Data sharing between programming environments on Intel Edison
There are many situations where we want to use multiple programming environments to to develop our IoT applications, for example,
1. 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++.
2. 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.
3. 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.
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.
Let’s see how an Arduino program can communicate with NodeJS and vice versa.
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:
01 int notifier_pin = 3;
02 int js_subscriber_pin = 6;
03
04 FILE *fromarduino, *toarduino;
05 int i = 0;
06 int c;
07
08 // the setup routine runs once when you press reset:
09 void setup() {
10 pinMode(notifier_pin, OUTPUT); //Notification pin
11 pinMode(js_subscriber_pin, INPUT_PULLUP); //interrupt pin for reading message from JS
12 attachInterrupt(js_subscriber_pin, subscriberEvent, RISING); //Subscribe to interrupt notifications from JavaScript
13 Serial.begin(9600);
14 }
15
16 //Read message from js notification file
17 void subscriberEvent() {
18
19 toarduino = fopen("/js_notification_out.txt","r"); //Opening message from JS
20 if (toarduino)
21 {
22 while ((c = getc(toarduino)) != EOF)
23 {
24 if(c != 10)//new line
25 {
26 Serial.print((char)c);
27 }
28 }
29 Serial.println("");
30 Serial.println("—————-");
31 fclose(toarduino);
32 }
33 }
34
35 // the loop routine runs over and over again forever:
36 void loop() {
37 if(i < 50)
38 {
39 i = i + 1;
40 }
41 else
42 {
43 i = 0;
44 }
45
46 publishData();
47 notifyWorld();
48 delay(1000); // wait for a second
49 }
50
51 void publishData()
52 {
53 fromarduino = fopen ("/arduino_notification_out.txt", "w+");
54 fprintf(fromarduino, "[%d]", i);
55 fclose(fromarduino);
56 }
57
58 //Nofity any body connected to this interrupt (C++ program and NodeJS) program
59 void notifyWorld()
60 {
61 digitalWrite(notifier_pin, HIGH);
62 delay(200);
63 digitalWrite(notifier_pin, LOW);
64 }
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:
01 var mraa = require("mraa");
02 var fs = require('fs');
03
04
05 /**********Read notification from arduino*************/
06 var subscriber_pin = new mraa.Gpio(1);
07 subscriber_pin.dir(mraa.DIR_IN);
08 subscriber_pin.isr(mraa.EDGE_RISING, subscriberEvent); //Subscribe to interrupt notifications from Arduino
09
10 function subscriberEvent() {
11 var contents = fs.readFileSync('/arduino_notification_out.txt').toString();
12 console.log("Message from Arduino:" + contents);
13}
14
15 /********** Trigger message sending interrupt every 20 seconds *************/
16 var counter = 0;
17 var notifier_pin = new mraa.Gpio(5);
18 notifier_pin.dir(mraa.DIR_OUT);
19
20 setInterval(function(){
21 counter++;
22 fs.writeFileSync("/js_notification_out.txt", "NodeJS: [" + counter + "]\n");
23 notifyWorld();
24 counter = 0;
25 },20000);
26
27
28 function notifyWorld()
29 {
30 notifier_pin.write(1);
31 setTimeout(function(){
32 notifier_pin.write(0);
33 },200);
34 }
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.
For more such intel IoT resources and tools from Intel, please visit the Intel® Developer Zone
Source: https://software.intel.com/en-us/blogs/2015/05/05/efficient-data-sharing-using-interrupts-on-intel-edison