Нередко мы оказываемся в ситуациях, когда нам для разработки IoT-приложений хотелось бы воспользоваться несколькими программными средами, например:
1. Мы предпочитаем делать обработку изображений на языке программирования C++ с использованием OpenCV, нежели делать это на NodeJS или Arduino. Так происходит в основном потому, что есть много примеров для OpenCV, написанных на C++.
2. Допустим, наше написанное на Arduino приложение должно определить количество лиц на полученном с камеры изображении. Для этого нам нужно использовать OpenCV для обработки изображения, а затем передать Arduino количество выявленных лиц.
3. Мы предпочитаем использовать NodeJS для создания веб-сервера, потому что это относительно легко сделать с помощью NodeJS, нежели других языков программирования.
![](https://habrastorage.org/files/355/c4d/aa6/355c4daa628f4c9083ac37259a963c38.png)
Существует множество подобных примеров. Тем не менее, во всех этих случаях нам всегда необходим простой механизм обмена данными между различными средами программирования.
Матиас Хан (Matthias Han) написал хорошую статью о совместно используемой Arduino и C ++ памяти. Этот метод применим благодаря тому, что С++ и Arduino работают так же, как и Linux-процесс. Тем не менее, для новичка в C++ этот процесс выглядит немного сложным. Кроме того, если вы хотите одновременно работать еще и с NodeJS, то вам придется написать C++ код и нативные биндинги для доступа к этим переменным в NodeJS.
Другой метод заключается в создании общего файла совместного доступа, который может быть прочитан всеми этими программами. Каждая программа будет запрашивать информацию об изменениях в файле. В данном случае изменения будут отслеживаться в потоке или в цикле. Если замечено какое-либо изменение в процессе чтения, то необходимо прочитать и обработать данные. Эта идея кажется простой, однако использование опроса не нашла должной поддержки в мире ПО.
Сегодня в своем блоге я хочу поговорить о модели «издатель-подписчик» для обмена информацией между различными программами для Linux. Эта модель очень хорошо подходит для разработки программного обеспечения. Тем не менее, во встраиваемом пространстве мы получим эту модель благодаря связке железа и программного обеспечения.
Схема обработки сообщений состоит из двух частей. Одна – это уведомление об изменении, другая – считывание данных. Это довольно устоявшийся старый метод в программном обеспечении. Этот подход был использован в некоторых приложениях хранилищ данных (DW) для объединения данных. В этом хранилище данных изначально маленький файл уведомления бросается в директорию. В этом файле уведомления будет содержаться информация о дате и времени изменения и ссылка на размещение фактического файла с данными, который, как правило, очень большой. Демон прочитает этот файл уведомления и запустит процесс для прочтения этого большого файла. Я пробую использовать тот же концепт. Только в данном случае вместо файла уведомления мы будем запускать прерывание.
Поток уведомлений:
![](https://habrastorage.org/files/0c4/849/472/0c4849472dc34522ae3299b2ded03d1b.png)
Поток сообщений:
![](https://habrastorage.org/files/be7/b09/70a/be7b0970a1294dad8cbcb7865d88cce8.png)
Давайте разберёмся в потоке уведомлений и сообщений.
Каждая среда программирования будет отличаться собственным контейнером выходящих данных и пином для запуска уведомления. Если какие-то данные должны быть высланы, то среда вначале перенесет контент в контейнер. Затем пину, отвечающему за уведомления, будет отослан сигнал HIGH. Давайте посмотрим на что-то вроде схемы цепи, расположенной ниже. Как ни странно, но эти пины замкнуты. Давайте я объясню детали на примере.
![](https://habrastorage.org/getpro/geektimes/post_images/8d7/676/b6b/8d7676b6b72ecd7f0b8c6272eea2873b.jpg)
Давайте посмотрим, как программа Arduino может общаться с NodeJS и наоборот.
Поток данных и уведомлений от Arduino в NodeJS:
![](https://habrastorage.org/files/b4d/d8f/3c5/b4dd8f3c581e4f99b544fd42b3efa9a8.png)
Поток данных и уведомлений от NodeJS в Arduino:
![](https://habrastorage.org/files/e95/96c/57e/e9596c57ef9d4590bc601552bdbb031f.png)
Давайте предположим, что у вас есть программа Arduino, которая считывает данные с датчика расстояния. Эти данные должны быть отправлены NodeJS для дальнейшей обработки.
В этом случае Arduino необходим пин уведомления, который является ничем иным, как GPIO-пином. Допустим, этот пин Arduino расположен на нашей схеме под №3. И если есть какая-то новая информация, то Arduino запишет эти данные в свой контейнер уведомлений и корневой каталог (смотрите диаграмму потока сообщений), в этом случае: /arduino_notification_out.txt.
После успешной записи данных Arduino отправит сигнал HIGH пину №3. Теперь посмотрите на диаграмму выше. Сейчас №3 замкнут с №1. Это значит, что каждый раз при получении сигнала HIGH пином №3 он отправляет этот сигнал пину №1.
В программе NodeJS мы добавим прерывание к пину №1. Всякий раз, когда он получает сигнал HIGH, это обозначает появление новых данных в контейнере уведомлений Arduino. NodeJS прочтет этот файл-контейнер и обработает данные. Для большей ясности смотрите схему «Поток данных и событий из Arduino в NodeJS».
Вы можете похожим способом отправлять данные и с NodeJS в Arduino. Для большей ясности смотрите схему «Поток данных и событий из NodeJS в Arduino».
Данный подход возможно применить в любых средах программирования, которые поддерживают прерывания.
При таком подходе вы избегаете ненужных опросов об изменении файла, а также вам не нужно использовать в C++ биндинг для обмена данными с NodeJS. Программирование здесь также довольно простое.
Тем не менее, у этого метода есть один недостаток. Каждому направлению потока данных нужна пара GPIO-пинов. Если вам нужно осуществить обмен данными во множестве сред программирования, то вам может просто не хватить GPIO-пинов. Тем не менее, мы можем достичь того же результата с похожей архитектурой, использовав всего одну пару GPIO. Но тогда вам нужно будет управлять вашей логикой, используя единственный JSON-файл с подходящими свойствами, такими как eventsource, event data и т.д. Кроме того, вам придется разобраться с ситуациями блокировки файлов. Однако это выполнимо.
1. Мы предпочитаем делать обработку изображений на языке программирования C++ с использованием OpenCV, нежели делать это на NodeJS или Arduino. Так происходит в основном потому, что есть много примеров для OpenCV, написанных на C++.
2. Допустим, наше написанное на Arduino приложение должно определить количество лиц на полученном с камеры изображении. Для этого нам нужно использовать OpenCV для обработки изображения, а затем передать Arduino количество выявленных лиц.
3. Мы предпочитаем использовать NodeJS для создания веб-сервера, потому что это относительно легко сделать с помощью NodeJS, нежели других языков программирования.
![](https://habrastorage.org/files/355/c4d/aa6/355c4daa628f4c9083ac37259a963c38.png)
Существует множество подобных примеров. Тем не менее, во всех этих случаях нам всегда необходим простой механизм обмена данными между различными средами программирования.
Матиас Хан (Matthias Han) написал хорошую статью о совместно используемой Arduino и C ++ памяти. Этот метод применим благодаря тому, что С++ и Arduino работают так же, как и Linux-процесс. Тем не менее, для новичка в C++ этот процесс выглядит немного сложным. Кроме того, если вы хотите одновременно работать еще и с NodeJS, то вам придется написать C++ код и нативные биндинги для доступа к этим переменным в NodeJS.
Другой метод заключается в создании общего файла совместного доступа, который может быть прочитан всеми этими программами. Каждая программа будет запрашивать информацию об изменениях в файле. В данном случае изменения будут отслеживаться в потоке или в цикле. Если замечено какое-либо изменение в процессе чтения, то необходимо прочитать и обработать данные. Эта идея кажется простой, однако использование опроса не нашла должной поддержки в мире ПО.
Сегодня в своем блоге я хочу поговорить о модели «издатель-подписчик» для обмена информацией между различными программами для Linux. Эта модель очень хорошо подходит для разработки программного обеспечения. Тем не менее, во встраиваемом пространстве мы получим эту модель благодаря связке железа и программного обеспечения.
Архитектура
Схема обработки сообщений состоит из двух частей. Одна – это уведомление об изменении, другая – считывание данных. Это довольно устоявшийся старый метод в программном обеспечении. Этот подход был использован в некоторых приложениях хранилищ данных (DW) для объединения данных. В этом хранилище данных изначально маленький файл уведомления бросается в директорию. В этом файле уведомления будет содержаться информация о дате и времени изменения и ссылка на размещение фактического файла с данными, который, как правило, очень большой. Демон прочитает этот файл уведомления и запустит процесс для прочтения этого большого файла. Я пробую использовать тот же концепт. Только в данном случае вместо файла уведомления мы будем запускать прерывание.
Поток уведомлений:
![](https://habrastorage.org/files/0c4/849/472/0c4849472dc34522ae3299b2ded03d1b.png)
Поток сообщений:
![](https://habrastorage.org/files/be7/b09/70a/be7b0970a1294dad8cbcb7865d88cce8.png)
Давайте разберёмся в потоке уведомлений и сообщений.
Каждая среда программирования будет отличаться собственным контейнером выходящих данных и пином для запуска уведомления. Если какие-то данные должны быть высланы, то среда вначале перенесет контент в контейнер. Затем пину, отвечающему за уведомления, будет отослан сигнал HIGH. Давайте посмотрим на что-то вроде схемы цепи, расположенной ниже. Как ни странно, но эти пины замкнуты. Давайте я объясню детали на примере.
Схема
![](https://habrastorage.org/getpro/geektimes/post_images/8d7/676/b6b/8d7676b6b72ecd7f0b8c6272eea2873b.jpg)
Давайте посмотрим, как программа Arduino может общаться с NodeJS и наоборот.
Поток данных и уведомлений от Arduino в NodeJS:
![](https://habrastorage.org/files/b4d/d8f/3c5/b4dd8f3c581e4f99b544fd42b3efa9a8.png)
Поток данных и уведомлений от NodeJS в Arduino:
![](https://habrastorage.org/files/e95/96c/57e/e9596c57ef9d4590bc601552bdbb031f.png)
Давайте предположим, что у вас есть программа Arduino, которая считывает данные с датчика расстояния. Эти данные должны быть отправлены NodeJS для дальнейшей обработки.
В этом случае Arduino необходим пин уведомления, который является ничем иным, как GPIO-пином. Допустим, этот пин Arduino расположен на нашей схеме под №3. И если есть какая-то новая информация, то Arduino запишет эти данные в свой контейнер уведомлений и корневой каталог (смотрите диаграмму потока сообщений), в этом случае: /arduino_notification_out.txt.
После успешной записи данных Arduino отправит сигнал HIGH пину №3. Теперь посмотрите на диаграмму выше. Сейчас №3 замкнут с №1. Это значит, что каждый раз при получении сигнала HIGH пином №3 он отправляет этот сигнал пину №1.
Код Arduino
int notifier_pin = 3;
int js_subscriber_pin = 6;
FILE *fromarduino, *toarduino;
int i = 0;
int c;
// the setup routine runs once when you press reset:
void setup() {
pinMode(notifier_pin, OUTPUT); //Notification pin
pinMode(js_subscriber_pin, INPUT_PULLUP); //interrupt pin for reading message from JS
attachInterrupt(js_subscriber_pin, subscriberEvent, RISING); //Subscribe to interrupt notifications from JavaScript
Serial.begin(9600);
}
//Read message from js notification file
void subscriberEvent() {
toarduino = fopen("/js_notification_out.txt","r"); //Opening message from JS
if (toarduino)
{
while ((c = getc(toarduino)) != EOF)
{
if(c != 10)//new line
{
Serial.print((char)c);
}
}
Serial.println("");
Serial.println("----------------");
fclose(toarduino);
}
}
// the loop routine runs over and over again forever:
void loop() {
if(i < 50)
{
i = i + 1;
}
else
{
i = 0;
}
publishData();
notifyWorld();
delay(1000); // wait for a second
}
void publishData()
{
fromarduino = fopen ("/arduino_notification_out.txt", "w+");
fprintf(fromarduino, "[%d]", i);
fclose(fromarduino);
}
//Nofity any body connected to this interrupt (C++ program and NodeJS) program
void notifyWorld()
{
digitalWrite(notifier_pin, HIGH);
delay(200);
digitalWrite(notifier_pin, LOW);
}
В программе NodeJS мы добавим прерывание к пину №1. Всякий раз, когда он получает сигнал HIGH, это обозначает появление новых данных в контейнере уведомлений Arduino. NodeJS прочтет этот файл-контейнер и обработает данные. Для большей ясности смотрите схему «Поток данных и событий из Arduino в NodeJS».
Код NodeJS
var mraa = require("mraa");
var fs = require('fs');
/**********Read notification from arduino*************/
var subscriber_pin = new mraa.Gpio(1);
subscriber_pin.dir(mraa.DIR_IN);
subscriber_pin.isr(mraa.EDGE_RISING, subscriberEvent); //Subscribe to interrupt notifications from Arduino
function subscriberEvent() {
var contents = fs.readFileSync('/arduino_notification_out.txt').toString();
console.log("Message from Arduino:" + contents);
}
/********** Trigger message sending interrupt every 20 seconds *************/
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);
}
Вы можете похожим способом отправлять данные и с NodeJS в Arduino. Для большей ясности смотрите схему «Поток данных и событий из NodeJS в Arduino».
Данный подход возможно применить в любых средах программирования, которые поддерживают прерывания.
Преимущества этого подхода
При таком подходе вы избегаете ненужных опросов об изменении файла, а также вам не нужно использовать в C++ биндинг для обмена данными с NodeJS. Программирование здесь также довольно простое.
Недостатки этого метода
Тем не менее, у этого метода есть один недостаток. Каждому направлению потока данных нужна пара GPIO-пинов. Если вам нужно осуществить обмен данными во множестве сред программирования, то вам может просто не хватить GPIO-пинов. Тем не менее, мы можем достичь того же результата с похожей архитектурой, использовав всего одну пару GPIO. Но тогда вам нужно будет управлять вашей логикой, используя единственный JSON-файл с подходящими свойствами, такими как eventsource, event data и т.д. Кроме того, вам придется разобраться с ситуациями блокировки файлов. Однако это выполнимо.
NetBUG
Удивительно, что для создания подобной схемы понадобился год и две перемычки.
Я ожидал ещё в ноябре увидеть какие-то биндинги.
К node,js и cpp можно прикрутить очередь сообщений (я использовал rabbitmq). Для Arduino не видел реализации