在许多情况下,我们希望使用多种编程环境开发(比如)物联网应用。
- 相比于 NodeJS 或 Arduino,我们更喜欢使用 OpenCV* 以 C++ 语言进行图像处理。 主要是因为以 C++ 编写的 OpenCV 能够提供许多示例。
- 譬如,我们用 Arduino 编写的应用需要读取摄像镜头中的面部编号。 为此,我们需要使用 OpenCV 处理摄像头图像,然后将检测到的面部编号传给 Arduino。
- 我们喜欢使用 NodeJS 创建基于开发板的 web 服务器,因为相比于采用其他语言,这种方法更加简单。
有许多这种类似的示例。 然而,我们希望在这些示例中找到一种简单的机制实现不同编程环境的数据共享。
Han, Matthias 写了一篇关于 Arduino 与 C++ 共享内存的好文章。 这种方法可行,因为C++ 和 Arduino 的运行方式类似于 Linux 中的进程。 但是,对于 C++ 的初学者来说,这种进程相对比较复杂。 而且,如果您希望将该进程扩展至 NodeJS,需要编写 C++ 代码,并编写原生绑定,以访问 NodeJS 中的变量。
还有一种方法是创建可从所有程序读取的通用可共享文件。 所有程序均可轮询文件中的数据变化。 在这种情况下,将会有线程或循环负责查找文件中的数据变化。 读取数据时,如有数据变化,您将读取和处理该数据。 这种想法看似简单,但在软件领域,轮询并不太受欢迎。
在本篇博客中,我将探讨发布程序-订阅程序模式,这种模式可使运行于 Linux 的不同程序之间实现数据共享。 在软件开发过程中,这种模式的适应性非常强。 但在嵌入式领域,结合使用软硬件才能实现这种模式。
架构:
消息流可分为两部分。 一部分是变化通知,另一部分是数据读取。 在软件领域,这是一种已确定的老方法。 一些数据仓储应用(硬件)运用这种方法协同数据。 在这类硬件中,首先将小型通知文件拖放至目录。 该通知文件将包含数据信息和数据变化时间,并参考实际数据文件的位置,最后通常会变成大型文件。 精灵程序将读取该通知文件,并触发 appreciate 程序读取这一大型文件。 在此,我尝试引入相同的概念。 不过在这一示例中,我将触发中断事件,而非通知文件:
通知流:
消息流:
我们来了解一下通知流和信息流:
每种编程环境都有自己的输出数据容器和针脚来触发通知。 如果需要发送数据,该环境首先将内容推送至容器文件。 然后向通知针脚发送信号 HIGH。 我们来看看下面一种奇怪的线路图。 令人惊讶的是,这些针脚都被缩短了。 我们来举例说明这种情况。
我们来看看 Arduino 程序与 NodeJS 之间如何通信。
数据和通知流从 Arduino 前往 NodeJS
假设您有一个 Arduino 程序,用来读取距离传感器的数据。 该距离传感器数据需发送至 NodeJS 做进一步处理。
在这种情况下,Arduino 需要一个通知针脚,即 GPIO 针脚。 根据上述线路图,用于 Arduino 的针脚为 #3。 如果出现任何新数据,Arduino 把该数据写入根目录(参考消息流示意图)中的通知容器文件,即 /arduino_notification_out.txt。
成功写入内容后,Arduino 向针脚 #3 发送信号 HIGH。 现在,请看上述线路图。 #3 缩短成了 #1。 这意味着,如果针脚 #3 变成 HIGH,该信号 HIGH 将发送至针脚 #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 附加一个中断事件。 如果针脚升高,说明 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。 但这之后您需要用带有 eventsource、事件数据等相应属性的单一 JSON 文件管理逻辑。 还需注意文件锁定情况。 尽管如此,这种方法依然可行。
Image de l’icône:
