Intel® Edison 上的编程环境之间的数据共享
在今天的博客中,我将讨论在 Linux 上运行的各种程序之间共享数据的发布-订阅模型。
获取新的 Intel® IoT Developer Kit,这是一个完整的软硬件解决方案,使开发人员能够使用 Intel® Galileo 和 Intel® Edison 开发激动人心的新解决方案。请访问 Intel® Developer Zone for IoT。
在许多情况下,我们希望使用多种编程环境来开发我们的物联网应用程序,例如:
- 我们更喜欢在 C++ 中使用 OpenCV* 进行图像处理,而不是在 NodeJS 或 Arduino 上进行。主要原因是,有很多使用 C++ 编写的 OpenCV 示例。
- 假设我们用 Arduino 编写的应用程序需要读取摄像机素材中的人脸数量。为此,我们需要使用 OpenCV 来处理摄像机图像,然后将检测到的人脸数量传递给 Arduino。
- 我们更喜欢使用 NodeJS 在开发板上创建 Web 服务器,因为在 NodeJS 上创建 Web 服务器比其他语言相对容易。
可能还有更多类似的情况。但是,在所有这些情况下,我们总是希望有一种简单的机制来在各种编程环境之间共享数据。
Han, Matthias 撰写了一篇 精彩文章,讨论了 Arduino 和 C++ 之间的共享内存。这种方法是可行的,因为 C++ 和 Arduino 在 Linux 上就像一个进程一样运行。但是,对于 C++ 初学者来说,这个过程看起来有点复杂。此外,如果您想将其扩展到 NodeJS,那么您将不得不编写 C++ 代码并编写原生绑定才能在 NodeJS 中访问这些变量。
另一种方法是创建一个通用的可共享文件,所有程序都可以从中读取。每个程序都会轮询文件中的数据更改。在这种情况下,将有一个线程或一个循环来查找文件中的更改。读取时,如果发生更改,则读取并处理数据。这个想法似乎很简单,但在软件世界中,轮询并不是一种很受欢迎的方法。
在今天的博客中,我将讨论用于在 Linux 上运行的各种程序之间共享数据的发布-订阅模型。该模型在软件开发中得到了很好的应用。然而,在嵌入式领域,我们通过结合硬件和软件来实现这种模型。
架构
消息流有两个部分。一个是更改的通知,另一个是读取数据。这是软件中一种非常成熟、古老的方法。此方法在某些数据仓库应用程序 (DW) 中用于分发数据。在此 DW 中,首先会将一个微小的通知文件放入一个目录。此通知文件将包含关于更改日期和时间的以及实际数据文件的位置的信息,而该文件通常会非常大。然后,一个守护进程会读取此通知文件并触发相应的进程来读取大文件。在这里,我试图引入相同的概念。但在这里,我们不使用通知文件,而是触发一个中断。
通知流程
消息流程
让我们理解通知和消息流 -
每个编程环境都有自己的输出数据容器和一个用于触发通知的引脚。每当需要发送某些数据时,该环境将首先将内容推送到容器文件中。然后,该环境将向其通知引脚发送一个 HIGH 信号。让我们看一下下面一个奇怪的电路图。令人惊讶的是,这些引脚是短接的。我将通过一个例子来详细解释。
电路
让我们看看 Arduino 程序如何与 NodeJS 进行通信,反之亦然。
从 Arduino 到 NodeJS 的数据和通知流
从 NodeJS 到 Arduino 的数据和通知流。
假设您有一个 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 来实现相同的结果,采用类似的架构。然后,您将不得不使用具有适当属性(如事件源、事件数据等)的单个 JSON 文件来管理您的逻辑。您还需要处理文件锁定情况。但是,这是可行的。
立即开始创新!Intel® 物联网开发者计划提供知识、工具、套件以及专家社区,助您快速轻松地将您的创新想法转化为物联网解决方案。
使用 Intel® Edison 和 Intel® Galileo 平台的 Intel® IoT Developer Kit 进行梦想和创造。这些套件是通用、性能优化且完全集成的端到端物联网解决方案,支持各种编程环境、工具、安全、云连接和硬件。
如需更多资源并了解新的 Intel® 物联网开发者套件 v1.0 如何帮助您简化物联网项目
- 下载 Intel® 物联网开发者套件
- 访问 Intel® 物联网开发者中心
- 参加我们的 Roadshows,获取创建您自己的物联网项目的实践培训