Get access to the new Intel® IoT Developer Kit, a complete hardware and software solution that allows developers to create exciting new solutions with the Intel® Galileo and Intel® Edison boards. Visit the Intel® Developer Zone for IoT.
There are many situations where we want to use multiple programming environments to to develop our IoT applications, for example,
- We prefer to do image processing using OpenCV* in C++ than doing it on NodeJS or Arduino. Mainly because, there are lot of examples on OpenCV written in C++.
- Let's say, our application written in Arduino needs to read the number of face in a camera footage. For this we need to use OpenCV to process the camera image and then pass the number of detected faces to Arduino.
- We prefer to use NodeJS to create a web server on the board, because it is relatively easy to create a webserver on NodeJS than other languages
There may be many more instances like this. However, in all these instances we always wish to have a simple mechanism to share data between various programming environments.
Han, Matthias has written a nice article on sharing memory between Arduino and C++. This approach is doable because, C++ and Arduino is running just like a process in Linux. However, for a beginner in C++, the process looks bit complex. Moreover, if you want to extend this to NodeJS, then you will have to write C++ code and write native bindings to access these variables in NodeJS.
Another approach is to create a common sharable file which can be read from all these programs. Each program will poll for data change in the file. In this case, there would be a thread or a loop looking for changes in the file. While reading, if there is a change, then you would read and process the data. This idea seems to be simple, however polling is not quite appreciated approach in software world.
In my blog today, I am going to talk about publisher-subscriber model for sharing data between various programs running in Linux. This model is very well adapted in software development. However, in embedded space, we achieve this model by combing both hardware and software.
Architecture
There are two parts to the message flow. One is notification of the change and another is to read the data. This is a pretty established, old approach in software. This approach is followed in some data warehouses applications (DW) for syndicating the data. In this DW, at first a tiny notification file is dropped onto a directory. This notification file will have information about the date and time of the change and reference to the location of the actual data file, which generally will be very huge. A demon will read this notification file and trigger the appreciate process to read the huge file. Here, I am trying to bring the same concept. But in this case, instead of notification file, we will trigger an interrupt.
Notification flow
Message flow
Let’s understand the notification and message flow -
Every programming environment will have its own output data container and a pin to trigger notification. Whenever some data needs to be sent out, that environment will first push the content to a container file. Then the environment will send a HIGH signal to its notification pin. Let’s see a strange kind of circuit diagram below. Surprisingly, these pins are shorted. Let me explain the details with an example.
Circuit
Let’s see how an Arduino program can communicate with NodeJS and vice versa.
Data and notification flow from Arduino to NodeJS
Data and notification flow from NodeJS to Arduino.
Let’s assume that you have an Arduino program, which reads data from distance sensor. This distance sensor data needs to be sent to NodeJS for further processing.
In this case, Arduino needs a notification pin, which is nothing but one of the GPIO pin. According to our circuit diagram, let’s say this pin for Arduino is #3. Whenever there is any new data, Arduino will write this data into its own notification container file in root directory (refer message flow diagram) in this case, /arduino_notification_out.txt.
After successfully writing the content, Arduino will send HIGH signal to pin #3. Now, look at the circuit diagram above. The #3 is shorted with #1. That means, whenever pin#3 goes HIGH it sends that HIGH signal to pin #1.
Arduino code
int notifier_pin = 3;
int js_subscriber_pin = 6;
FILE *fromarduino, *toarduino;
int i = 0;
int c;
void setup() {
pinMode(notifier_pin, OUTPUT); pinMode(js_subscriber_pin, INPUT_PULLUP); attachInterrupt(js_subscriber_pin, subscriberEvent, RISING); Serial.begin(9600);
}
void subscriberEvent() {
toarduino = fopen("/js_notification_out.txt","r"); if (toarduino)
{
while ((c = getc(toarduino)) != EOF)
{
if(c != 10) {
Serial.print((char)c);
}
}
Serial.println("");
Serial.println("----------------");
fclose(toarduino);
}
}
void loop() {
if(i < 50)
{
i = i + 1;
}
else
{
i = 0;
}
publishData();
notifyWorld();
delay(1000); }
void publishData()
{
fromarduino = fopen ("/arduino_notification_out.txt", "w+");
fprintf(fromarduino, "[%d]", i);
fclose(fromarduino);
}
void notifyWorld()
{
digitalWrite(notifier_pin, HIGH);
delay(200);
digitalWrite(notifier_pin, LOW);
}
In the NodeJS program, we will attach an interrupt to pin #1. Whenever it goes high, that means a new data is available in Arduino notification container file. Now, NodeJS will read that file and process the data. Refer “Data and event flow from Arduino to NodeJS” diagram for more clarity.
NodeJS Code
var mraa = require("mraa");
var fs = require('fs');
var subscriber_pin = new mraa.Gpio(1);
subscriber_pin.dir(mraa.DIR_IN);
subscriber_pin.isr(mraa.EDGE_RISING, subscriberEvent);
function subscriberEvent() {
var contents = fs.readFileSync('/arduino_notification_out.txt').toString();
console.log("Message from Arduino:" + contents);
}
var counter = 0;
var notifier_pin = new mraa.Gpio(5);
notifier_pin.dir(mraa.DIR_OUT);
setInterval(function(){
counter++;
fs.writeFileSync("/js_notification_out.txt", "NodeJS: [" + counter + "]\n");
notifyWorld();
counter = 0;
},20000);
function notifyWorld()
{
notifier_pin.write(1);
setTimeout(function(){
notifier_pin.write(0);
},200);
}
You can even send data from NodeJS to Arduino in the similar manner. Refer “Data and event flow from NodeJS to Arduino” for more clarity.
We can extend this approach to any programming environments which support interrupts.
Advantages of this approach:
In this approach, you are avoiding unnecessary polling for file change and also you don’t have to develop any C++ binding to share the data with NodeJS. The programming is fairly easy as well.
Disadvantages of this approach:
However, there is one disadvantage with this approach. Every direction of data flow needs a pair of GPIO pins. If you have to exchange data with multiple programing environments, then you would run out of GPIOs. However, we can achieve the same result with similar architecture by spending just one GPIO pair. But then you will have to manage your logic with single JSON file with proper properties like eventsource, event data and so on. You will have to take care of file lock situations as well. However, it is doable.
Intel® Developer Zone for IoT
Start inventing today with the Intel® IoT Developer Program which offers knowledge, tools, kits and a community of experts to quickly and easily turn your innovative ideas into IoT Solutions.
Dream it, Build it with the Intel® IoT Developer Kit for Intel® Edison and Intel® Galileo platforms. These kits are versatile, performance-optimized and fully integrated end-to-end IoT solutions supporting a variety of programming environments, tools, security, cloud connectivity and hardware.
For more resources and to learn how the new Intel® IoT Developer Kit v1.0 can help streamline your IoT projects: