使用 Arduino Uno 和 IBM IoT Foundation 构建云就绪的温度传感器: 第 3 部分:使用 Node-RED 构建自定义应用程序

在这个由 4 部分组成的教程系列的前两部分中,我们完成了一个 IoT 项目的前 3 个阶段。在项目的概念架构中,一个 Arduino 主板(连接到一个温度传感器)通过MTTQ 协议定期将温度和湿度信息发送到 IoT Foundation(使用 IBM®
Bluemix™ 中的功能),这些数据在一个实时图表上被描绘出来:

目前为止,我演示了如何使用 Arduino 收集温度和湿度传感器数据,并通过 MQTT 将它发送到 IBM IoT Foundation(或者至少它的QuickStart 部分)的能力。这个过程本身就非常有趣:看到数据流入 QuickStart 所提供的实时图表中,感觉还不错。

但是,我最初想要构建此项目是希望能够查看历史数据:返回查明我的 Internet 连接遇到的问题是否与我的配线箱中当时的温度相关。为了解决这个难题,必须超越Quickstart 所提供的功能,构建一个可存储和显示历史数据的自定义应用程序。

这样做的好处在于,Bluemix 的一些新特性和它与 IBM IoT Foundation的连接使这个目的不仅可以实现,而且很容易实现。在本教程中,我将介绍该过程中接下来的几步。首先,要将一个已注册的应用程序 连接到 IoTFoundation。接下来,将要构建一个应用程序,它可以获取通过 MQTT 从 Arduino 收到的数据,并将它们存储在一个 NoSQL数据库中。然后,将会使得该数据可以通过 REST 接口进行查询。

在 最后一期 中,我们会将这些 REST 接口连接到一个简单的 JavaScript 和 HTML 客户端,然后这个过程就全部完成了。注册

完成您的应用程序的前提条件 
1.本系列 第 1 部分 和 第 2 部分 中涉及的已正确配置的硬件和软件
2.一个 IBM ID
3.一个与您的 IBM ID 有关联的 Bluemix 帐户运行应用程序获取代码备

注:运行应用程序按钮可调出最终版本。要试用它,可以搜索 2014 年 8月第一星期内的日期范围。

第 1 步:在 Bluemix 与IOT Foundation 之间创建连接

正如我所提到的,您目前为止在 IBM IoT Foundation 上仅使用了QuickStart:这是各种设备发送数据和轻松查看传入的数据的一个共同平台。但是,这里的关键是 “共享”。QuickStart没有为您的数据提供专门的隔间,在提交数据后,您无法以任何方式收集或操作它。但是,您可以向 IOT Foundation 注册一个使用 Bluemix编写的应用程序,以便提供这些优势。通过向 IOT Foundation 注册,您能够让多个设备为您的应用程序提供数据。我只打算介绍向 IOT Foundation 注册的最简单方法(通过 Bluemix),但您在考虑采用本教程中提供的使用 Bluemix 和 IOT Foundation的基本方法以外的方法时,可以看看其他可用选项和计划。

第 2 步:使用 IoTStarter 创建一个基本 Node-RED 应用程序

所以需要做的第一件事是在 Bluemix 中创建一个应用程序的一个骨架。稍后您将获得该应用程序的代码,但首先您需要搭建一个环境来运行该应用程序。目前为止,为 IoT创建该环境的最简单的方法是使用 Node-RED。Node-RED是一个基于浏览器的编辑器,用于将数据流连接起来,数据流将数据输入(比如 MQTT 客户端或 HTTP 请求)连接到各种节点(比如数据库),或者使用 JavaScript编写的功能。Node-RED 使得构建简单的 API 或通过 API 将设备连接起来变得很简单。

在您的浏览器中,使用您的 IBM ID 登录到 Bluemix 中。在该目录中,单击Internet of Things Foundation 样板:
Internet of Things Foundation 的屏幕截图

在 Internet of Things Foundation 样板的 Create 页面的右上角,输入应用程序的名称和主机:
Internet of Things Foundation 样板上的 Create Page 窗口的屏幕截图

您选择的名称必须是惟一的,因为 mybluemix.net 是一个共享命名空间。记下该名称,它将是您的服务的 URL 的基础。

如果创建启动程序,则会创建一个应用程序和一个 Cloudant 服务,该应用程序使用一个 Node.js SDK buildpack 来托管 Node-RED,而这个Cloudant 服务持有 Node-RED 的配置。完成创建可能需要花费一两分钟的时间。当 Bluemix 告诉您应用程序变得可用时,可以单击 Bluemix仪表板中新创建的应用程序磁贴,打开您的应用程序概述页面:

主要 Bluemix 状态页中的应用程序磁贴的屏幕截图

接下来,添加一个 IoT 服务,使特定于您组织的 MQTT 消息可以正确地传输到您的应用程序。在同一页上,向下滚动到 Services节,然后单击仪表板中的应用程序概述页面上的 ADD A SERVICE:
应用程序概述页面的 Services 节的屏幕截图

这会打开 Bluemix 目录页面;向下滚动到 Internet of Things 部分并单击 Internet of Things服务:

Internet of Things 服务磁贴的屏幕截图

是的,该样板的名称为 “Internet of Things Foundation Starter”,这个服务的名称也是 “Internet ofThings”。Bluemix 的一个原则是以尽可能简单的方式命名事物,这可能是该规则带来的混淆比它减轻的混淆更多的一个例证。

单击 Internet of Things 服务时,会创建一个服务创建页面。在此页上,需要确保 “App” 下拉列表中选择的应用程序是上一步中创建的应用程序。

Internet of Things Foundation Service Creation 页面的屏幕截图

单击 CREATE。然后您会看到一个窗口,询问您是否想要重新暂存该应用程序。在窗口上选择 OK来重新暂存该应用程序。完成该操作之后,您应用程序中应该会添加两个服务。

第 3步:修改注册的基本应用程序来添加数据库

绑定到应用程序的 Cloudant 数据库仅用于持有 Node-RED 的配置。要存储来自 MQTT 的消息数据,必须绑定另一个数据库:在本例中,该数据库是来自 MongoLab 的 MongoDB 数据库。

返回到仪表板中的应用程序概述页面,再次单击 ADD A SERVICE。在目录页面中,向下滚动到 Data Management选项,并单击来自 MongoLab 的 Mongo 数据库:

MongoLab Mongo 数据库服务磁贴的屏幕截图

按照提示创建一个 MongoLab Mongo 数据库。这会将该服务添加到应用程序中,并再次询问您是否想要重新暂存该应用程序。在合适的窗口中选择OK。

您现在已创建了一个 IOT Foundation 称为已注册应用程序 的应用程序。在创建 IOT Foundation服务的过程中,在幕后,Bluemix 通过 API 密钥施加了一些魔力,以便将 Node.js 运行时连接到 Internet of Things Foundation中的 MQTT 代理。但是,您仍然需要将 Arduino Uno 设备的消息路由到您的应用程序。为此,需要向 IOT Foundation注册您的设备。下一节包含多个步骤,但是,一旦完成了这些初始工作,就可以开始编写代码了!

首先双击您的主要应用程序状态页中的 IOT Foundation Service 按钮。这会打开 Internet of ThingsFoundation 启动页面。

Internet of Things Foundation 启动页面的屏幕截图

单击页面右上角的 Launch。完成此操作之后,将会看到该组织已在 IOT Foundation 内创建。

IOT Foundation 欢迎页面的屏幕截图

组织是一种顶级结构,它允许您添加人员、设备和 API 密钥。在 Bluemix 中创建一个新 IOT 服务时,会自动为您创建一个组织。组织的名称将以 “BluemixService Instance” 开头。稍后我们会更详细地介绍这个组织。首先需要向这个新组织添加一个人,这个人就是!单击页面顶部的People 选项卡,然后在文本字段中输入您的 IBM ID(用来向 Bluemix 注册和创建应用程序的 ID),并单击Add。

该屏幕截图演示了如何将一个人添加到一个组织中

接下来需要添加(或注册)您的设备。这类似于使用 QuickStart 的过程,但允许设备向注册的设备的新 MQTT 目标发送消息。

屏幕截图:注册设备(1/2)

选择 Arduino Uno 作为 Device Type。确保输入了您的以太网防护盾的 MAC 地址作为 Device ID。然后单击Continue。

一定要留意这一步中出现的情况。您会看到两部分重要信息:首先是组织的 6 位代码(也显示在主页左上角的下拉菜单中),其次是您向 Arduino验证所需的验证令牌。自行记下此信息(或获取屏幕截图并保存它)。您需要使用它来修改 Arduino sketch 的代码(还需要 6 位的组织代码)。

屏幕截图:注册设备(2/2)

第 4 步:下载并修改 Arduinosketch

1.单击 获取代码。
2.在我的 IBM DevOps Services 项目的概述页面中,单击 EDIT CODE。
3.打开 MQTT_IOT_SENSORS_AUTH_REL 文件夹,选择新的 sketch 文件MQTT_IOT_SENSORS_AUTH_REL.ino,然后单击 File >Export。
4.将该 ZIP 文件保存到您的计算机上,展开它,然后在 Arduino IDE 中打开该 sketch 。

这与您在 第 2 部分 中看到的 sketch 基本相同,但有两个重要的区别,其中大部分内容都将在本节中介绍:

char servername[]="666666.messaging.internetofthings.ibmcloud.com";
String clientName = String("d:666666:iotsample-arduino:") + macstr;
String topicName = String("iot-2/evt/status/fmt/json");
char username[]="use-token-auth";
char password[]="XXXXXXXXXXXXXXXXXXX";

请注意与 第 2 部分 中的示例的不同之处:我更改了来自 QuickStart 的 servername,以及
clientName。我还添加了两个新变量:usernamepassword

在 Arduino IDE 中,我更新了 servernameclientName 字符串,将666666 替换为 6 位的组织 ID。接下来,将密码值替换为您在注册设备时获得的验证令牌。请注意,用户名始终为use-token-auth。只有验证令牌(密码)部分用于识别和确认客户端。

如果向下查看代码中的一些行,就会找到最终更改的重要的代码行。现在使用 connect() 方法的 3 参数版本连接到 MQTT 代理:

client.connect(clientStr,username,password);

保存您的代码并将它下载到 Arduino。在下载代码后,当您打开 Serial Monitor (Ctrl-Shift-M)时,就会看到与上一部教程中相同的连接序列,但这一次,数据被发送到 IoT Foundation 的保留部分,而不是QuickStart。不过,目前为止,这还仅是一个信息池:数据已传入,但未传出。要解决此问题,需要在 Bluemix 中创建一个应用程序。

第 5 步:在 Node-RED中编辑代码

现在,您应该已经为查看一些代码做好了最终的准备!返回到应用程序概述页面,在左上角,可以看到 Routes 部分:单击您的应用程序路由,这将调出 Node-RED欢迎屏幕。

屏幕截图: 路由到该应用程序

在欢迎屏幕上,向下滚动以找到显示 “Go to your Node-RED flow editor”的大红按钮,单击它打开流编辑器。此刻,流编辑器包含一个示例流。选择该流中的节点(一次选择一个或全部选中),然后按下 delete键删除它们。这会清空流编辑器。选择以下代码并将它复制到您的剪贴板:

[{"id":"b1b4a786.d22c58","type":"ibmiot","name":"Bluemix IOT
Token"},{"id":"68bf71d.f97409","type":"mongodb","hostname":"192.155.243.23","port":"10057","db":"db","name":"IBMCloud
MongoDB"},{"id":"5c60019b.6aa8c8","type":"mongodb
out","service":"_ext_","mongodb":"68bf71d.f97409","name":"Mongo
Store","collection":"data","payonly":false,"upsert":false,"multi":false,"operation":"store","x":625.2000122070312,"y":121.19999694824219,"z":"cae409bb.8c23e8","wires":[]},{"id":"fc410194.f40058","type":"mongodb
in","service":"_ext_","mongodb":"68bf71d.f97409","name":"MongoDB
Query","collection":"data","x":539.2000122070312,"y":192.20001220703125,"z":"cae409bb.8c23e8","wires":[["9b229426.5a4378"]]},{"id":"9b229426.5a4378","type":"http
response","name":"data
output","x":732.2000122070312,"y":197.20001220703125,"z":"cae409bb.8c23e8","wires":[]},{"id":"7bfb2916.58f4f8","type":"debug","name":"","active":true,"console":false,"complete":false,"x":612.2000122070312,"y":63.19999694824219,"z":"cae409bb.8c23e8","wires":[]},{"id":"291be4b5.cca914","type":"function","name":"get
and append time","func":"var d = new Date();\nvar n = d.getTime(); \nmsg.payload.d.time=n;\nreturn msg;","outputs":1,"x":343.20001220703125,"y":61.19999694824219,"z":"cae409bb.8c23e8","wires":[["5c60019b.6aa8c8","7bfb2916.58f4f8"]]},{"id":"15bd57a9.534cd8","type":"function","name":"update payload","func":"if ((typeof msg.payload.start == \"string\") & & (typeof msg.payload.end == \"string\")) {\n\tvar stt = parseInt(msg.payload.start);\n\tvar edd = parseInt(msg.payload.end);\n\tmsg.payload =   {\n    \t\"payload.d.time\": {$gt: stt, $lt: edd}\n    }\n} else {\n\tmsg.payload = {}\n}\nreturn msg;","outputs":1,"x":334.20001220703125,"y":192.20001220703125,"z":"cae409bb.8c23e8","wires":[["fc410194.f40058"]]},{"id":"d3e8d05c.3bc0b","type":"http in","name":"data web service GET","url":"/data","method":"get","x":118.40000915527344,"y":192.40003967285156,"z":"cae409bb.8c23e8","wires":[["15bd57a9.534cd8"]]},{"id":"3fd8fa81.81641e","type":"ibmiot in","authentication":"boundService","apiKey":"b1b4a786.d22c58","inputType":"evt","deviceId":"","applicationId":"","deviceType":"","eventType":"","commandType":"","format":"","name":"IBM IoT App In","service":"registered","allDevices":true,"allApplications":"","allDeviceTypes":true,"allEvents":true,"allCommands":"","allFormats":true,"x":96.19999694824219,"y":61.19999694824219,"z":"cae409bb.8c23e8","wires":[["291be4b5.cca914"]]}]

在 Node-RED 编辑器右上角中的下拉菜单中,单击 Import > Clipboard...> Importfrom...:

Import from... 菜单选项

此选择会打开一个导入窗口。(使用 Control-V)将代码从剪贴板粘贴到窗口中,并单击 OK 将流导入 Node-RED 中。

第 6步:查看代码

现在可以快速查看 Node-RED 中的代码。将代码写入 Node-RED中很简单。将节点(表示不同的程序方面)从左侧面板拖到右侧编辑器中。双击任何节点打开一个节点编辑器窗口,您可以使用对话框来定义节点的属性。右侧是两个选项卡:Info 和Debug,前者提供了有关所选节点的更多信息,后者显示了输出的目标位置。在本教程中查看来自 Arduino 的信息时,将会使用 Debug 选项卡。

屏幕截图: Node-RED 流

该流应该与您在浏览器中的 Node-RED 编辑器中看到的相同,但编号是我的:我们将依次查看每个节点。您有两个流。第一个流将获取 MQTT消息,修改它们来添加一个时间戳,然后将它们添加到 Mongo 数据库中。在这个流中:

1.第一个节点是一个 IoT 输入节点。该节点从您在 MQTT 上注册的设备获取信息并将其放在一个叫做 payload的变量中。

2.第二个节点是一个函数节点。可在函数节点中编写任意 JavaScript 函数。因为我希望将此数据呈现为时序数据(而且因为在 Arduino中表述时间很困难),我在此代码中扩充了从 Arduino 收到的数据。这个特定节点的代码(获取并附加时间)为:

var d = new Date();
var n = d.getTime(); 
msg.payload.d.time=n;
return msg;

3.此节点是一个 Mongo 输出节点。它获取流的载荷中的任何 JSON 信息,并将其放在 Mongo 中一个指定的容器中。

4.最后一个节点是 Node-RED最方便的特性之一。它是一个调试节点。可将调试节点附加到一个流的任何部分,并观察通过该节点连接的消息的输出内容(整条消息或仅是载荷)。结果可能被定向到 debug选项卡、控制台或二者。

我们基本上可以查看调试控制台上的一些交互了,但首先需要添加一些配置。必须将 Mongo 输出节点连接到您与应用程序绑定的 Mongo数据库,这是一个两步过程。第一步,返回到应用程序页面并单击您的 MongoDB 实例的磁贴中的 Show Credentials链接,以显示凭据:

Show Credentials UI 的屏幕截图

将标为 uri 的行(以 mongodb 开头)的值复制到剪贴板,然后将它粘贴到一个文本编辑器中。这些凭据中的 URI表示数据库的一个连接字符串,但不会被 Node-RED 直接使用。您需要解析它,并将这些得到的值输入到一个窗口中。连接字符串具有以下格式:

mongodb://USERNAME:PASSWORD@HOSTNAME:PORT/DATABASE_NAME

现在双击 Mongo 数据库节点。这会为该节点打开一个编辑器,显示您正在更新的容器等信息。(在本例中可以看到您的容器名为 data。它还允许您编辑 Mongo数据库的连接参数。单击看起来像一个铅笔的按钮,打开 Edit mongo config node 窗口:

Mongo DB 配置

复制来自连接字符串的,针对 Host(替换 YOURHOST)、Port(替换 00000)、Database(替换YOUR_DB)、Username 和 Password 的具体值。单击 Update关闭该窗口。

接下来,关闭 Mongo 数据节点编辑器并单击 Node-RED 编辑器左上角的红色 Deploy按钮。现在,您不仅会看到数据流入调试页面,还会看到一些文档开始添加到 Mongo 数据库中。返回到应用程序屏幕中的 Mongo 数据库磁贴,单击它,打开 MongoDB编辑器,然后打开数据库。可以看到总文档数开始递增。

但这个数据流仅使您的应用程序成为一个信息池。我们想要采用一种方式来从应用程序中获取信息。这是第二个数据流的用途,该数据流将(在 4 个节点中)创建一个完整的RESTful Web 服务。

解释第二个数据流

我们依次查看了这个流中的每个节点:

5.此节点是一个 HTTP 输入节点;就像之前看到的 IOT 输入节点,它是 HTTP 请求进入 Node-RED 环境的地方。如果双击它,可以看到它拥有 HTTP 方法(您服务的 GET、POST 等方法)的属性字段和 URI(在本例中为 /data)。

6.第二个节点同样是一个函数节点,就像之前看到的一个一样。在本例中,我将执行请求参数处理并更新载荷:

if ((typeof msg.payload.start == "string") && (typeof msg.payload.end == "string")) {
    var stt = parseInt(msg.payload.start);
    var edd = parseInt(msg.payload.end);
    msg.payload =   {
        "payload.d.time": {$gt: stt, $lt: edd}
    }
} else {
    msg.payload = {}
}
return msg;

我需要这么做的原因是,MongoDB 查询节点(节点 7,也是下一个节点)获取一个表示消息载荷中的查询的特殊 JSON字符串。我可以获取该信息作为 HTTP 请求的主体,也可以不需要这个节点。但是,众所周知,我们在任何编程语言中绝不希望做的一件事就是,允许未过滤的字符串直接传入一个数据库中。如果这是一个 SQL 数据库,那么它很可能是一次等待发生的 SQL 注入攻击,但您也可以对NoSQL JSON数据库执行同样的注入攻击。

我所做的(在确定两个字符串是数字后,将它们解析为数字,然后从这些数字创建一个查询字符串)应能避免许多这样的问题。如果存在开始和结束参数,但无法解析为数字,则不会返回任何数据(查询失败)。如果发出一个没有参数的请求,则会在默认情况下获取所有数据,这符合REST 中的 GET 方法的语义。您可能会争论说,默认情况下应该不返回任何数据,不过这涉及到对 GET 语义应是什么样子的更深入讨论。请参阅 使用 IBM WebSphere 执行现代 Web 开发,了解其他潜在选择。

7.这是一个 MongoDB 查询节点(上一个节点的描述中已提及),它获取消息载荷中的信息作为一个查询字符串。请参阅 MongoDB
文档,了解允许使用的查询字符串类型的更多信息。

8.最后,最后一个节点是一个 HTTP 输出节点。需要使用此节点,才能将结果传递回请求应用程序。粘贴此代码并更改 Mongo 设置后,您会看到 Node-RED屏幕右上角那个较大的 Deploy 按钮现在已激活并变为红色。单击该按钮来部署您的应用程序(此过程应仅需要一两秒),您会看到一条消息显示 “Deploymentsucceeded”。

第 6步:试用您的应用程序

您准备好看看它是否有效了吗?在浏览器、cURL 或您最喜欢的 REST 测试客户端中键入此 URL(将 yourhostname
替换为您之前为应用程序选择的主机名):

http://yourhostname.mybluemix.net/data

您会获得从 Arduino 收到的所有消息的 JSON 格式列表,包括时间戳。挑选一个系列中的两个时间戳,然后尝试另一个变体(替换必要的内容):

http://yourhostname.mybluemix.net/data?start=yourstarttime&end=yourendtime

结束语

这是本系列的第 3 部分。在 最后一部分 中,将介绍如何将一个 JavaScript GUI 放在新的 Node-RED 应用程序上,并对 REST服务处理基础架构稍作分析。

BLUEMIX SERVICES USED IN THIS TUTORIAL:

Internet of Things 服务 为应用程序提供了访问 IoT 设备和数据的简单且强大的方法。
MongoLab 是一个完全托管的云数据库服务,包含高度可用的 MongoDB 数据库、自动化的备份、基于 Web 的工具、24/7监视和专家支持。
Node-RED Starter 可以帮助您轻松地开发、部署和扩展服务器端 JavaScript 应用程序。IBM SDK for Node.js提供了增强的性能、安全性和可服务性。

标签: arduino uno, 温度传感器, node-red