Getting Started with IDM Arduino

The purpose of this document is to showcase the necessary steps for integrating Arduino board with IDM software.

Software needed:

Overview

Basic communication flow:

  • Arduino board is programmed to use ISN protocol for communication and data representation

  • Modified Arduino IDE which acts as bridge, it connects to Arduino board and transmits ISN data to and from IDM software

  • IDM software enables data received from Arduino to be processed, displayed, stored, made available through web service…

IDM Configuration

Main relevant settings in IDM is UDP server configuration, which can be accessed through GUI in:

IDM Home Screen > Settings > Device UDP Server

  • ‘UDP server enabled’ must be checked

  • Port number can be set to custom (default is 33005)

If running IDM in daemon mode, UDP server settings can be set in directly in ‘idm.conf’ file under ‘misc’ section, example:

"udp_server_enabled" : true,
"udp_server_port" : 33005,

Arduino IDE Configuration

Isotel version of Arduino IDE can be found at

Guide for building IDE:

​- https://www.instructables.com/id/Build-Arduino-IDE-From-GIT-Sources-on-Windows/

Arduino IDE must be configured to forward ISN packets from Serial monitor to IDM UDP server. This must be enabled in Arduino IDE preferences.txt file.

Preferences file location mighty differ depending on system, more info:

By default, Serial Monitor will behave as in official version. To enable frame parsing and UDP forwarding, this line must be added to preferences.txt file:

idm.udp.forward=true

This will attempt to connect to default UDP setting which is ‘localhost:30005’

This can be overridden by also specifying host and port settings, example:

idm.udp.forward=true
idm.udp.host=localhost
idm.udp.port=9006

Arduino Board configuration

Easiest way to implement an Arduino project intended to be used with IDM software is to use the libisn library:

The ISOTEL Sensor Network Protocol defines a set of simple re-usable protocol objects that can be used as stand-alone or combined, on-demand, into a complex protocol structures.

This library provides ISN Protocol Layer Stack implementation in C, imitating object-like oriented programming.

In this approach individual layers may be arbitrarily stacked, chained, one with another to provide desired protocol complexity.

Example Implementation Overview

This section is an overview of a simple Arduino demo using libisn library.

Full demo source file can be found under examples/Hello_World_ISN/Hello_World_ISN.ino

Demo functionality is very simple, it consists of:

  • an incrementing counter variable

  • Two settable parameters for switching Arduino inbuilt LED lights

Protocol Structure

In this demo only three protocol layers are required:

  • Physical layer, which implements read/write operations to Arduino Serial (UART) interface

  • Frame layer, required to order received data into packets, since UART driver does not do not provide sufficient framing information.

  • Message layer, which defines the virtual device structure and parameter callbacks

Message layer

Defining message structure and callbacks is the most complex part of implementation.

Here we define a message table, for each message we specify priority, data size, callback function and description, in that order.

In our demo this looks like:

static isn_msg_table_t isn_msg_table[] = {
  { 0, sizeof(uint64_t),  serial_cb,  "%T0{ARDUINO Libisn Example} V1.0 {#sno}={%<Lx}" },
  { 0, sizeof(counter_t), counter_cb, "Example {:counter}={%lu}" },
  { 0, sizeof(led_t), led1_cb, "%T1{LED Settings} {:LED1}={%hu:Off,On}" },
  { 0, sizeof(led_t), led2_cb, "{:LED2}={%hu:Off,On}" },
  ISN_MSG_DESC_END(0)
};

For more details about message layer look at (TODO link)

For and example of parameter data definition and callback function we can look at ‘counter’ parameter

In callback function we either auto-increment the counter, or set an user-defined value.

typedef struct {
   int32_t x;
} __attribute__((packed)) counter_t;

counter_t counter = {0};

static void *counter_cb(const void *data) {
  counter.x++;
  if (data) {
      counter = *(const counter_t *)data;
  }
  return &counter;
}

Protocol Initialization

Once protocol structure is defined, it needs to be linked together and initialized.

We start initializing from top layer down:

message > frame > uart

At the end we also send an initial message update to start communication

Example from demo:

isn_msg_init(&isn_message, isn_msg_table, ARRAY_SIZE(isn_msg_table),  &isn_frame);
isn_frame_init(&isn_frame, ISN_FRAME_MODE_COMPACT, &isn_message, NULL, &isn_uart, &time_counter, 1000);
isn_uart_init(&isn_uart, &isn_frame);

isn_msg_sendby(&isn_message, counter_cb, ISN_MSG_PRI_NORMAL);

Loop Function

After protocols are initialized, in each cycle we try to read new data from serial and schedule any message layer updates. Periodically we also schedule a counter update, which increments the counter parameter and sends updated value to serial:

 //Read from Serial input and schedule any outgoing messages
 isn_uart_poll(&isn_uart);
 isn_msg_sched(&isn_message);

 // Send counter update every 0.5 s and update LED values
 if (time_s - time_e >= 500) {
     time_e = millis();
     isn_msg_sendby(&isn_message, counter_cb, ISN_MSG_PRI_NORMAL);
     digitalWrite(13, led1.val);
     digitalWrite(3, led2.val);
}