# akYtec PR200 Prototype V1

## Prototype features

The **akYtec PR200** prototype comes with the following functionality:

* 2 digital outputs (DO1 and DO2 on the **PR200**)
* 2 analog inputs (AI1 and AI2 on the **PR200**) (currently 0-10V)
* Digital outputs can be monitored and controlled (switched) via the OneIoT Cloud and [Dashboard](https://docs.oneiot.de/app/dashboard).
* Analog inputs can be monitored via the OneIoT Cloud and [Dashboard](https://docs.oneiot.de/app/dashboard).

It is easy to extend this functionality via the [Instrument Driver.](https://docs.oneiot.de/gateway/instrument-driver-1)

## Quick Start Guide

* Connect the **OneIoT Gateway** via USB Micro B connector (5 Volts)

{% hint style="danger" %}
Make sure you only connect 5 Volts DC to the Gateway.
{% endhint %}

* Connect the RJ45 Jack of the **OneIoT Gateway** to a LAN which provides access to internet&#x20;

{% hint style="info" %}
In some LANs outgoing ports might be blocked. In case the device will not connect to the cloud, you need to open ports 8883 for MQTTs-Connection and 9090 for Over-The-Air Updates.
{% endhint %}

* Connect the OneIoT Gateway to the device via Modbus. A+ (green wire) has to be connected to the D+ port of the **PR200** and B- (blue wire) has to be connected to the D- port of the **PR200**. Use the first (left most) RS485 interface of the **PR200**.

{% hint style="info" %}
For the prototype, make sure the RS485 connector of the **OneIoT Gateway** is connected to the **OneIoT Gateway** (all for wires should be connected).
{% endhint %}

* Connect some analog inputs or digital outputs.
* Connect 24V DC power supply to the **PR200** power input. Also, consult the **PR200** manual for more details or in case of any doubts.

{% hint style="danger" %}
Make sure you never connect >24V DC to the **PR200** power input.
{% endhint %}

* Login to the[ OneIoT Dashboard.](https://app.easytoi.com)&#x20;
* In case the **OneIoT Gateway** is not connected to your account yet, see [here how to link new devices.](https://docs.oneiot.de/app/device-onboarding)
* If the OneIoT Gateway is connected and linked to your account, it will be shown as online (green circle) in the [device list.](https://app.easytoi.com/devices)

{% hint style="info" %}
In case the gateway is not shown in the list, you need to [link it to your account first.](https://docs.oneiot.de/app/device-onboarding) If it is shown in the list but does not appear to be online (grey circle), check the power connection and make sure that the gateway can connect to the internet via the LAN.
{% endhint %}

* If the OneIoT Gateway is online, you can create or modify your own [dashboard](https://docs.oneiot.de/app/dashboard).&#x20;

## The PR200  prototype showcase

There is a small ALP program running on the **PR200**.  It has the following responsibilities:

* The PR200 is in Modbus Slave-Mode and available via *address 16.*
* The network variables *Eingang1* and *Eingang2* are mapped to r*egister 512 and 513*, respectively.
* *Eingang1* and *Eingang2* are connected to the *digital output Q1 and Q2*, respectively.
* Analog inputs are made available by default via Modbus as described in the **PR200** manual.
* Analog input 1 is shown in the **PR200** display.

{% hint style="success" %}
The demo program is included on the USB stick.
{% endhint %}

In the **OneIoT Cloud** there is a so called *Device Template* for the **PR200*****.*** This template describes the device in the cloud. The mapping is based on the [OneIoT Digital Twin ](https://docs.oneiot.de/gateway/digital-twin)concept. Variables are mapped to so called *Items:*

| PR200 | OneIoT Item             |
| ----- | ----------------------- |
| AI1   | Analog Input 1 (Item 1) |
| AI2   | Analog Input 2 (Item 2) |
| Q1    | Relais 1 (Item 3)       |
| Q2    | Relais 2 (Item 4)       |

The Device Template can then be used to define Dashboards.

{% hint style="success" %}
The Device Template can be changed and extended at anytime. The [Instrument Driver](https://docs.oneiot.de/gateway/instrument-driver-1), which can also be changed at runtime (e.g., by the user or by OneIoT) is responsible to fetch the data from the **PR200** and forward it to the cloud via the defined items.&#x20;
{% endhint %}

There is already a [**OneIoT Dashboard**](https://docs.oneiot.de/app/dashboard) for this showcase.

![The default dashboard for the PR200 showcase.](https://1448869960-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MOb77mW1-Ky_sezok91%2F-Mf9hU2SPEAXbRq2iu5C%2F-Mf9xMYYsB_wtjY0WKf2%2FScreenshot%20from%202021-07-21%2023-24-51.png?alt=media\&token=e9c0f9be-e1f2-4c8d-adf7-141d3c752def)

## Custom driver to support every possible use case

A [Instrument Driver ](https://docs.oneiot.de/gateway/instrument-driver-1)is responsible for the communication between **OneIoT Gateway** and **PR200**.

{% hint style="success" %}
The Instrument Driver can also be adapted at runtime!&#x20;
{% endhint %}

{% hint style="info" %}
In the future, there will be an easier to use Domain Specific Language for Modbus-based drivers.
{% endhint %}

The current prototype instrument driver is given below. It just fetches some values via Modbus every 30 seconds and sends them to the cloud. Furthermore, incoming commands from the **OneIoT Cloud,** that is, switching one of the relays are mapped to the corresponding Modbus commands and send to the **PR200**.

This code can easily extended to read all registers from the **PR200** or to write and read all network variables. Furthermore, I can be adapted to specific use cases. For example, sensor readings can be evaluated and aggregated and complex logic can be implemented here.

{% hint style="success" %}
OneIoT provides support in writing instrument drivers.
{% endhint %}

```python
import struct
import uasyncio as asyncio
import logging
from serial_communication import Serial
from queue import MESSAGE_BUS, Event, EVENT_TO_CLOUD_FOR_MQTT, EVENT_ERROR
import message_util
from libs.modbus_facade import ModbusFacade

class CustomHandler:

    def __init__(self, serial:Serial):
        # Every 30sec
        self.fire_every = 30
        self.modbus = ModbusFacade(serial)

    async def loop(self):
        # Read every x seconds
        while True:
            await asyncio.sleep_ms(self.fire_every*1000)
            # Address 0x10, 0x0B00-0x0B01 is AI1
            answer_double_list = await self.modbus.read_holding_registers(0x10, 0x0B00, 0x02, double_valued=True)
            # Item 1 mapped to AI 1
            await self.send_double(1, answer_double_list[0])
            # Address 0x10, 0x0B02-0x0B03 is AI1
            answer_double_list = await self.modbus.read_holding_registers(0x10, 0x0B02, 0x02, double_valued=True)
            # Item 2 mapped to AI 2
            await self.send_double(2, answer_double_list[0])
            # Address 0x10, 0x2000 is network variable 1
            answer_bool_list = await self.modbus.read_coils(0x10, 0x2000, 0x01)
            print(answer_bool_list)
            # Item 3 mapped to network variable 1
            await self.send_bool(3, answer_bool_list[0])
             # Address 0x10, 0x2001 is network variable 2
            answer_bool_list = await self.modbus.read_coils(0x10, 0x2010, 0x01)
            print(answer_bool_list)
            # Item 3 mapped to network variable 1
            await self.send_bool(4, answer_bool_list[0])
            
    async def handle_incoming(self, message):
        if message_util.get_state_content_for_payload(message) is not None:
            item_number, state_str = message_util.get_state_content_for_payload(message)
            item_number = int(item_number)
            # state is expected to be boolean and we'll send it via single_coil function code
            if state_str == "0":
                state = 0x0000
            elif state_str == "1":
                state = 0xFF00
            # Only network variables 1 and 2 bound to item 3 and 4 will be handled
            if item_number == 3:
                register = 0x2000
            if item_number == 4:
                register = 0x2010
            # write single coil
            if not await self.modbus.write_single_coil(0x10, register, state):
                logging.log("Writing register failed.")        
        else:
            logging.log(str("Cannot handle message %s." % str(message)))        
        
            
    async def send_bool(self, item, value):
        if value:
            await self.send_message(item, "1", "BOOLEAN")
        else:
            await self.send_message(item, "0", "BOOLEAN")
        
    async def send_double(self, item, value):
        value = str(value)
        await self.send_message(item, value, "DOUBLE")
        

    async def send_message(self, item, value_string, type="DOUBLE"):
        message = None
        if value_string:
            message =  "S:" + str(item) + ":20:" + type + ":" + value_string
        else:
            message =  "S:" + str(item) + ":50"
            logging.log("No answer received for command...")

        await self.finalize_serial_to_cloud(message)
        # TODO log to cloud

    '''
    This finalizes a serial message given the OneIoT protocol. Timestamp is added and message is checked.
    Then it is sent to the cloud.
    '''
    async def finalize_serial_to_cloud(self, message):
        message = message_util.add_timestamp(message)
        correct = message_util.check_serial_message(message)
        if not correct:
            await MESSAGE_BUS.send(Event(EVENT_ERROR, "Incorrect handled serial: " + message))
        else:
            # just forward to devicecloud via MQTT
            await MESSAGE_BUS.send(Event(EVENT_TO_CLOUD_FOR_MQTT, [message]))

```

## Mobus Settings

**PR200** modbus settings:

| Property  | Value                     |
| --------- | ------------------------- |
| Address   | 16 (decimal) = 0x10 (hex) |
| Baud      | 9600                      |
| Stop Bits | 1                         |
| Parity    | None                      |
| Data Bits | 8                         |

## Troubleshooting

### 1.) Device does not show up online in the Device Cloud, there is only a grey circle!

Check your connection, make sure all necessary outgoing ports are open (9090, 8883, 80, 443). Make sure, the device has access to the internet. In case you are using a mobile connection, make sure you have sufficient radio reception. You can also move the gateway to another location to check reception issues.

### 2.) Device does not show up at all!

You need to[ onboard the device ](https://docs.oneiot.de/app/device-onboarding)first.

### 3.) Device seems to be online (green circle) but I can see no Dashboard!

**Option 1:**  Go to another device page, for example the Debugging Page and then back to the Dashboard. If the Dashboard does not show up then, you have not created one yet (see Option 2).\
**Option 2:** Create a new [Dashboard](https://docs.oneiot.de/app/dashboard).

### 4.) Device seems to be online (green circle) but I don't receive any data!

**Option 1:** Refresh (Press F5 on the keyboard) the Dashboard.\
**Option 2: I**f you can not see any data, even though you know some sensor readings should have been received, make sure your mapping (Device Template) to the items is correct (i.e., the Dashboard displays the right data in the widgets). \
**Option 3:** If mapping is correct and data should be received but is not shown in the cloud, make sure your physical connection is correct. Test the OneIoT Gateway or your device also with another device or Modbus master, respectively.&#x20;
