使用 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 样板的 Create 页面的右上角,输入应用程序的名称和主机:
您选择的名称必须是惟一的,因为 mybluemix.net 是一个共享命名空间。记下该名称,它将是您的服务的 URL 的基础。
如果创建启动程序,则会创建一个应用程序和一个 Cloudant 服务,该应用程序使用一个 Node.js SDK buildpack 来托管 Node-RED,而这个Cloudant 服务持有 Node-RED 的配置。完成创建可能需要花费一两分钟的时间。当 Bluemix 告诉您应用程序变得可用时,可以单击 Bluemix仪表板中新创建的应用程序磁贴,打开您的应用程序概述页面:
接下来,添加一个 IoT 服务,使特定于您组织的 MQTT 消息可以正确地传输到您的应用程序。在同一页上,向下滚动到 Services节,然后单击仪表板中的应用程序概述页面上的 ADD A SERVICE:
这会打开 Bluemix 目录页面;向下滚动到 Internet of Things 部分并单击 Internet of Things服务:
是的,该样板的名称为 “Internet of Things Foundation Starter”,这个服务的名称也是 “Internet ofThings”。Bluemix 的一个原则是以尽可能简单的方式命名事物,这可能是该规则带来的混淆比它减轻的混淆更多的一个例证。
单击 Internet of Things 服务时,会创建一个服务创建页面。在此页上,需要确保 “App” 下拉列表中选择的应用程序是上一步中创建的应用程序。
单击 CREATE。然后您会看到一个窗口,询问您是否想要重新暂存该应用程序。在窗口上选择 OK来重新暂存该应用程序。完成该操作之后,您应用程序中应该会添加两个服务。
第 3步:修改注册的基本应用程序来添加数据库
绑定到应用程序的 Cloudant 数据库仅用于持有 Node-RED 的配置。要存储来自 MQTT 的消息数据,必须绑定另一个数据库:在本例中,该数据库是来自 MongoLab 的 MongoDB 数据库。
返回到仪表板中的应用程序概述页面,再次单击 ADD A SERVICE。在目录页面中,向下滚动到 Data Management选项,并单击来自 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 启动页面。
单击页面右上角的 Launch。完成此操作之后,将会看到该组织已在 IOT Foundation 内创建。
组织是一种顶级结构,它允许您添加人员、设备和 API 密钥。在 Bluemix 中创建一个新 IOT 服务时,会自动为您创建一个组织。组织的名称将以 “BluemixService Instance” 开头。稍后我们会更详细地介绍这个组织。首先需要向这个新组织添加一个人,这个人就是您!单击页面顶部的People 选项卡,然后在文本字段中输入您的 IBM ID(用来向 Bluemix 注册和创建应用程序的 ID),并单击Add。
接下来需要添加(或注册)您的设备。这类似于使用 QuickStart 的过程,但允许设备向注册的设备的新 MQTT 目标发送消息。
选择 Arduino Uno 作为 Device Type。确保输入了您的以太网防护盾的 MAC 地址作为 Device ID。然后单击Continue。
一定要留意这一步中出现的情况。您会看到两部分重要信息:首先是组织的 6 位代码(也显示在主页左上角的下拉菜单中),其次是您向 Arduino验证所需的验证令牌。自行记下此信息(或获取屏幕截图并保存它)。您需要使用它来修改 Arduino sketch 的代码(还需要 6 位的组织代码)。
第 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
。我还添加了两个新变量:username
和password
。
在 Arduino IDE 中,我更新了 servername
和 clientName
字符串,将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...:
此选择会打开一个导入窗口。(使用 Control-V)将代码从剪贴板粘贴到窗口中,并单击 OK 将流导入 Node-RED 中。
第 6步:查看代码
现在可以快速查看 Node-RED 中的代码。将代码写入 Node-RED中很简单。将节点(表示不同的程序方面)从左侧面板拖到右侧编辑器中。双击任何节点打开一个节点编辑器窗口,您可以使用对话框来定义节点的属性。右侧是两个选项卡:Info 和Debug,前者提供了有关所选节点的更多信息,后者显示了输出的目标位置。在本教程中查看来自 Arduino 的信息时,将会使用 Debug 选项卡。
该流应该与您在浏览器中的 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链接,以显示凭据:
将标为 uri
的行(以 mongodb
开头)的值复制到剪贴板,然后将它粘贴到一个文本编辑器中。这些凭据中的 URI表示数据库的一个连接字符串,但不会被 Node-RED 直接使用。您需要解析它,并将这些得到的值输入到一个窗口中。连接字符串具有以下格式:
mongodb://USERNAME:PASSWORD@HOSTNAME:PORT/DATABASE_NAME
现在双击 Mongo 数据库节点。这会为该节点打开一个编辑器,显示您正在更新的容器等信息。(在本例中可以看到您的容器名为 data。它还允许您编辑 Mongo数据库的连接参数。单击看起来像一个铅笔的按钮,打开 Edit mongo config node 窗口:
复制来自连接字符串的,针对 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提供了增强的性能、安全性和可服务性。