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:
Isotel version of Arduino IDE (https://github.com/Isotel/Arduino)
Isotel IDM (Release 191128-1 or newer)
Isotel libisn Arduino library (TODO git link)
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:
https://github.com/Isotel/libisn (TODO change to Arduino specific libisn git link)
(TODO link to libisn documentation)
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);
}