/iot-edge-serial

Azure IoT Edge Serial Port Communication module for Linux and Windows

Primary LanguageC#OtherNOASSERTION

Azure IoT Edge Serial Module

This project has adopted the Code of Conduct from Contributor Covenant https://www.contributor-covenant.org/ For more information contact jw + @ + jeeweetje.net and sander + @ + vandevelde-online.com with any additional questions or comments.

   _                         ___      _____   ___     _
  /_\   ___ _  _  _ _  ___  |_ _| ___|_   _| | __| __| | __ _  ___  
 / _ \ |_ /| || || '_|/ -_)  | | / _ \ | |   | _| / _` |/ _` |/ -_)
/_/ \_\/__| \_,_||_|  \___| |___|\___/ |_|   |___|\__,_|\__, |\___|
                                                        |___/
    ___            _        _   __  __          _        _
   / __| ___  _ _ (_) __ _ | | |  \/  | ___  __| | _  _ | | ___
   \__ \/ -_)| '_|| |/ _` || | | |\/| |/ _ \/ _` || || || |/ -_)  
   |___/\___||_|  |_|\__,_||_| |_|  |_|\___/\__,_| \_,_||_|\___|

Azure IoT Edge Serial Port (RS232) Communication module for Linux & Windows

Using this module, developers can build Azure IoT Edge solutions with Serial (RS232) Port connectivity. The Serial module is an Azure IoT Edge module, capable of reading data from and writing data to serial port devices and publishing data to the Azure IoT Hub via the IoT Edge routes. Developers can configure the module tailoring to any scenario.

Azure IoT Edge Serial Module Architecture

Prebuilt Serial module container images are available at IoT Edge Foundation on Docker Hub. So you can jump start the serial port experience on your Azure IoT Edge device.

Azure IoT Edge Compatibility

The current version of the module is targeted for the latest Azure IoT Edge GA.

Find more information about Azure IoT Edge at here.

Visit http://azure.com/iotdev to learn more about developing applications for Azure IoT.

Target Device Setup

Platform Compatibility

Azure IoT Edge is designed to be used with a broad range of operating system platforms. Serial module has been tested on the following platforms:

  • Windows 10 IoT Enterprise (version 1809) x64
  • Windows 10 IoT Core (version 1809) x64
  • Linux x64
  • Linux arm32v7

Device Setup

If you are new to developing Azure IoT Edge modules, check out these resources first:

Build your own

In this section, the Serial module will be built as an IoT Edge module.

Environment Setup

Serial module is a .NET Core 3.1 application, which is developed and built based on the guidelines in the Azure IoT Edge documentation. Please follow these links to setup a Windows or Linux build environment.

Basic requirements:

  • Docker CE on Windows (For building both Windows and Linux containers)
  • .NET Core 3.1 SDK
  • Visual Studio Code with IoT Edge and C# extensions

Build your own module

Open the project in Microsoft Visual Studio Code, and open the VS Code command palette (ctrl-shift-p), type and run the command 'Azure IoT Edge: Build IoT Edge solution'. Select the deployment.template.json file for your solution from the command palette. Note: Be sure to check configuration section to properly set each fields before deploying the module.

Deploy your module

In Azure IoT Hub Devices explorer, right-click an IoT Edge device ID, then select Create deployment for IoT Edge device. Open the config folder of your solution, then select the deployment.json file. Click Select Edge Deployment Manifest. Then you can see the deployment is successfully created with a deployment ID in VS Code integrated terminal. You can check your container status in the VS Code Docker explorer or by run the docker ps command in the terminal.

Configuration

Before running the module on an IoT Edge, proper configuration is required. Here is a sample configuration for your reference.

"serial": {
  "properties.desired": {
    "portConfigs": {
      "ttyS0": {
        "device": "/dev/ttyS0",
        "direction": "Read",
        "sleepInterval": 10,
        "baudRate": 115200,
        "parity": "None",
        "dataBits": 8,
        "stopBits": "One",
        "delimiter": "/",
        "ignoreEmptyLines": true,
        "delimiterAtStart": true,
        "delimiterInOutput": true
      },
      "ttyS1": {
        "device": "/dev/ttyS1",
        "direction": "Write",
        "sleepInterval": 10,
        "baudRate": 4800,
        "parity": "None",
        "dataBits": 8,
        "stopBits": "One",
        "delimiter": "\r\n",
        "ignoreEmptyLines": false,
        "delimiterAtStart": false,
        "delimiterInOutput": false
      }
    }
  }
}  

Meaning of each field:

  • portConfigs: Array of port configurations, size 1 to n
    • <named port>: Human readable name like 'ttyS0' and 'ttyS1' in the example above
      • device: Device like /dev/tty... or COMx as string. (Note: Has to resemble the 'PathInContainer' create option)
      • direction: To read from or write to the devicePossible values: Read or Write.
      • sleepInterval: # of milliseconds the thread should sleep as integer
      • baudRate: # of bauds as integer
      • parity: Parity; with possible values: None, One, as string
      • dataBits: # number of data bits as integer
      • stopBits: Stop bits with possible values: as string
      • delimiter: Delimiter to separate data into messages as string
      • ignoreEmptyLines: Ignore empty lines in input data as boolean
      • delimiterAtStart: Location of the delimiter is at the start of the data as boolean
      • delimiterInOutput: Include delimiter in upstream data as boolean

Naming convention for <named port> is to use the device name as used in the operating system, like 'ttyS0' on Linux or 'COM1' on Windows for the first serial port.

Input data length is limited to 1024 bytes. This is to prevent timeouts due to not recognized delimiters in the input stream. After 1024 bytes a warning message is logged. This exception is treated as an empty line.

Note: The delimiterAtStart and delimiterInOutput are added to support messages where the delimiter is part of the data which has a CRC checksum (like a P1 telegram as defined in the DSMR P1 5.0 standard). The two properties only influence serial read direction.

For more about the RS232 standard, please refer to the Wiki link.

Module Endpoints and Routing

There are multiple endpoints defined in Serial module dynamically:

  • <named port> defined in portConfigs: This is an output endpoint for telemetries per configured port. The operation defined in the direction field of the named port will be dynamically instantiated as telemetry messages output.

There is one input defined:

  • "serialInput": This is an input endpoint for write commands for all named ports.

Input/Output message formats and routing rules are introduced below.

Read from Serial

We read data from serial ports.

Telemetry Message

Message Properties:

# "content-type": "application/edge-serial-json"

Latest Message Payload:

{
    "port":"ttyS0",
    "data":"<data>",
    "timestampUtc":"2019-01-01T01:01:00.0000000Z",
}

Note: the 'data' is a UTF-8 string.

Route to IoT Hub

"routeToIoTHub": "FROM /messages/modules/serial/outputs/ttyS0 INTO $upstream"

routeToIoTHub

Route to other (filter) modules

"routeToModule": "FROM /messages/modules/serial/outputs/ttyS0 INTO BrokeredEndpoint(\"/modules/[target module name]/inputs/input1\")"

routeToIoTHub

Write commands to serial port

This module supports for writing to a port:

  1. An input endpoint "serialInput" to receive commands. This is used in combination with an other module using IoT Edge routes.
  2. Direct Methods called "serialWrite" can be used for Cloud to Device messaging.

Both solutions support writing back a single data message to a single serial port.

Command Message

The content of the message must follow this message format:

{
    "port" : "ttyS0",
    "data":"<your data to write to serial>"
}

Note: the 'data' is decode as UTF-8 string.

Route from other (filter) modules

The command should have a property "command-type" with value "SerialWrite". Also, routing must be enabled by specifying rule like below.

"commandSourceToSerialWrite": "FROM /messages/modules/[source module name]/outputs/output1 INTO BrokeredEndpoint(\"/modules/serial/inputs/serialInput\")"

routeToIoTHub

How to run

Run as an IoT Edge module

Please follow the link to deploy the module as an IoT Edge module.

Configure create options

In the Container "createOptions" section, enter the following for device mapping in Linux:

{
  "HostConfig": {
    "Devices": [
      {
        "PathOnHost": "<device name on host machine>",
        "PathInContainer": "<device name in container>",
        "CgroupPermissions": "rwm"
      },
      {
        "PathOnHost": "<device name on host machine>",
        "PathInContainer": "<device name in container>",
        "CgroupPermissions": "rwm"
      }
    ]
  }
}

Replace <device name on host machine> with the actual serial device like '/dev/ttyS0' or /dev/rfcomm0. The 'PathInContainer' create option has to resemble the desired property 'device'. Define as much devices as you need.

In Windows, use these (generic) Container Create options:

{
  "HostConfig": {
    "Isolation": "Process",
    "Devices": [
      {
        "PathOnHost": "class/86E0D1E0-8089-11D0-9CE4-08003E301F73",
        "PathInContainer": "",
        "CgroupPermissions": ""
      }
    ]
  }
}

This gives the docker container access to all serial ports on the host Windows device.

Environment variables

For debugging purposes an environment variable is supported for more logging:

RuntimeLogLevel = verbose|debug|info

Access for read/write on serial ports

In Linux, Elevated rights are needed for access to serial ports. If your serial port is named eg. '/dev/ttyS0' use:

# chmod 666 /dev/ttyS0

Note: This setting must survive a reboot of the host machine!

Using Ubuntu, one way to persist elevated rights is using a file named /etc/rc.local with this content:

#!/bin/bash
sudo chmod 666 /dev/ttyS0
exit 0

After saving this file, perform:

sudo chmod 777 /etc/rc.local

Please reboot your machine so these changes can be applied.

Supported hardware

We test our module on actual hardware.

The following Azure IoT Edge devices are used to test the module:

  • Advantech Uno 2271G (Ubuntu 18.04 & 20.04)
  • Advantech Uno 2372G (ubuntu 18.04)
  • Advantech Uno 2484G (Ubuntu 18.04 & Windows 10 IoT)
  • Advantech Uno 1483G (Ubuntu 18.04)
  • Advantech Uno 1372G (Ubuntu 18.04 & 20.04)
  • Raspberry Pi 2B (Raspbian Stretch & Buster)

The following serial devices are used to test the module:

  • Null modem
  • Moxa NPort 5210A Serial 2 Port Device Server with Null Modem
  • BEITIAN USB GNSS GPS Receiver BN-85U (U-Blox UBX-M8030)
  • Webio GRB-288 Bluetooth GPS mouse
  • GSpace GS-R238 GPS mouse (SiRFstarIII)
  • DSMR P1 to USB cable for Dutch smart meters

Note: If you want to have your serial device tested, listed and officially supported here, please send us a DM on github

Current limitations

Due to the fact the module is still being developed and tested, there are certain limitations to the module.

  • Use Windows containers on Windows, use Linux containers on Linux.
  • Data transferred is handled as UTF-8 strings currently.
  • A <named port> is considered as being uni-directional. For bi-directional communication in Linux two <named port>s are offered for two serial connections to a single tty device.
  • Bluetooth devices with serial port support will work. But Bluetooth serial ports are not persisted so both reuse of the same portname and reuse of elevated rights not not garanteed.

Acknowledgement

The serial reader/writer was (serial module version >= 0.1.0) originally based on the Microsoft Modbus module.

Since .Net Core 3.1 supports cross platform serial ports natively, the C code dependency has been removed.

The routing images are produced with the IoT Edge Module Flow generator.

Contribute

The code of this module is open-sourced and licenced under the MIT license.

Want to contribute? Throw us a pull request....