注册使用 IBM Bluemix
这个云平台提供了许多免费的服务、运行时和基础架构,可以帮助您快速构建和部署下一个移动或 Web 应用程序。
在这个由 4 部分组成的教程系列的第 1 部分 中,我讨论了一个监视我的配线箱中的温度的项目的设计,该项目是使用 Arduino Uno 和 Virtuabotix DHT11 温度传感器来构建的。我展示了该项目的电路构造,演示了 Arduino IDE 的安装和如何使用不同的 Arduino 示例 sketch 测试项目的各个组件。您现在已准备好了解将 IoT 项目部署到云中的 sketch 设计,以及实现远程实时监视温度和湿度数据的步骤。但是,首先我需要讨论一下用来与 IBM IoT Foundation 进行通信的协议:MQTT。
MQTT(以前称为 Message Queueing Telemetry Transport)是一个轻量型、快速的通信协议,专为物联网而设计。它起源于 IBM(最初由 Andy Stanford-Clark 在这里开发),自那时起就提交给了 Organization for the Advancement of Structured Information Standards (OASIS) 来进行标准化,该协议标准的最新版本为 3.1 版。MQTT V3.1 协议规范 规定,它的用途是用作一个 “轻量型、基于代理的发布/订阅消息协议,它的设计开放、简单、轻量且容易实现。”自它引入后,“容易实现” 部分已经过实践检验,因为已经开发了多个实现 MQTT 客户端的不同库。您可以在 Eclipse Paho 项目页面 上找到几乎所有这些库的链接。
MQTT 非常适合用在嵌入式设备中,因为它:
MQTT 是 IBM IoT Foundation QuickStart 设计获取输入时所依据的协议。
回页首
“ 在本教程中,您将使用 IBM Internet of Things Foundation Quickstart 的内置特性描绘来自 Arduino 传感器的实时数据的图表。 ”
为 Arduino 安装针对 MQTT 的客户端库很简单,就像查找和安装您已在第 1 部分 中看到的针对特定硬件设备的库一样。用于您的项目的特定的库可以在 Arduino Client for MQTT 网站上找到,该网站介绍了该库,链接到相关文档,并提供了一个额外的 GitHub 链接,从该链接也可以下载它。
在我的示例中,我使用了来自 GitHub 链接的 V1.9.1 客户端。下载 ZIP 或 TAR 文件,然后将该归档文件中的 PubSubClient 目录解压到您的 Arduino IDE 目录的 libraries 子目录。然后重新启动 Arduino IDE。 PubSubClient>mqtt_auth 、 PubSubClient>mqtt_basic 和 PubSubClient>mqtt_publish_in_callback 菜单项现在应该可供使用,如图 1 所示。
图 1. PubSubClient 菜单
回页首
目前位置在本教程系列中采用的流程是,向解决方案中引入一个新组件或技术,然后独立地测试它,以确保它能正常运行。在下载并安装 MQTT 客户端软件后,我们将对它执行同样的操作。但是,仍然需要一个额外的软件来测试您的客户端。
MQTT 是一个基于代理的协议:客户端连接到一个协调它们之间的通信的代理。事实上,这个流程非常简单。一组客户端向该代理注册它们感兴趣的一个主题。另一个客户端向该主题发布一条消息,该代理将该消息转发到订阅客户端。
您将用于本地测试的代理是另一个叫做 Mosquitto 的开源产品。可将它安装在您用于编写 Arduino 程序的本地 PC,然后测试 Arduino 能否与代理进行通信。
您可以从 Mosquitto 网站 下载 Mosquitto。它可用于 Windows®、Mac 和大多数 Linux® 变体。安装它很简单:对于 Linux,只需要安装一个新包;对于 Windows,可采用 Windows 服务或一个单独的可执行文件的形式来安装该系统。如果使用 Windows,一定要清除安装为服务的复选框。从命令行运行它更容易,因为可更轻松地查看它记录的调试信息。
安装 Mosquitto 后,运行以下命令来从(任何平台上的)命令行启动它:
mosquitto -v
-v
标志表示 “详细” 日志,它表示可以查看所建立的连接的信息以及接收或发送的消息。开始将消息发送到本地代理后,在一分钟内就可以看到结果。
下一步是下载将所有部分衔接在一起的示例 sketch(参见下载)。您将通过 MQTT 将一条消息发布到一个 MQTT 代理(首先发送到本地代理,然后将它发送到 IoT Foundation QuickStart 中包含的代理)。要让 IoT Foundation QuickStart 最终解析和显示传感器数据,必须将它发布到一个名为 iot-2/evt/status/fmt/json
的主题。类似地,还必须以某种特定的方式格式化该数据。如果在 IoT Foundation QuickStart 文档中查找 “ 将我的设备连接到 QuickStart ” 秘诀,就会看到数据必须具有以下 JavaScript 对象表示法 (JSON) 格式:
{ "d": { "name1": "stringvalue", "name2": intvalue, ... } }
示例 sketch 是一个简单的程序。从我在第 1 部分 中指出了其他 Arduino 示例,可以看到所有 Arduino 程序都具有相同的结构。它们包含一个标准的函数集(名为 setup()
和 loop()
),以及一些可选的变量声明和您在 setup()
或 loop()
函数中使用的任何实用程序函数的声明。首先,需要在代码的开头对其中一些变量声明执行一些更改,以便可以在本地测试该程序。
与所有其他 Arduino sketch 一样,最顶部的程序包含对您在 sketch 中使用的库的引用:
#include <SPI.h> #include <Ethernet.h> #include <PubSubClient.h> #include <dht11.h>
您可以看到,该 sketch 使用了以太网库来驱动以太网扩展卡,使用 DHT11 库来实现对来自 DHT11 的读数的访问,就像在第 1 部分 中看到的示例那样。但是,第一次还要使用 PubSubClient
库。要利用这些特性,需要对代码执行两处更改,以便针对本地服务器测试它:
// Update this to either the MAC address found on the sticker on your Ethernet shield (newer shields) // or a different random hexadecimal value (change at least the last four bytes) byte mac[] = {0xDE, 0xED, 0xBA, 0xFE, 0xFE, 0xED }; char macstr[] = "deedbafefeed"; // Note this next value is only used if you intend to test against a local MQTT server byte localserver[] = {192, 168, 1, 98 }; // Update this value to an appropriate open IP on your local network byte ip[] = {192, 168, 1, 20 };
从第一条注释中可以看出,需要执行的第一处更改是更新 mac
和 macstr
变量,使之与以太网扩展卡的 MAC 地址相匹配。较新的以太网扩展卡已将此信息打印在该卡的标签上。如果使用较旧的卡,可以将最后 4 位十六进制字符更改为其他任何有效的十六进制值。接下来,将 ip
变量更改为本地网络上一个开放的 IP 地址。以太网库能够使用 DHCP,如果感兴趣的话,您可以了解一下如何做,但尽可能地使用固定 IP 地址会更简单。接下来,将 localserver
变量更新为您在其上使用 Mosquitto 服务器的计算机的 IP 地址。最后,更新 sketch 以连接到您的本地 MQTT 服务器,而不是 IBM IoT Foundation QuickStart 服务器。
在代码中找到下面这一节:
// Uncomment this next line and comment out the line after it to test against a local MQTT server //PubSubClient client(localserver, 1883, 0, ethClient); PubSubClient client(servername, 1883, callback, ethClient);
取消注释包含以 client(localserver
开头的构造函数的一行,并注释掉它之下的以 client(servername
开头的一行。然后保存 sketch。
执行必要的更改后,可以快速查看这个简单程序的剩余部分。在两个代码段之间,我们已看到一组变量声明:
char servername[]="messaging.quickstart.internetofthings.ibmcloud.com"; String clientName = String("d:quickstart:arduino:") + macstr; String topicName = String("iot-2/evt/status/fmt/json"); dht11 DHT11; float tempF = 0.0; float tempC = 0.0; float humidity = 0.0; EthernetClient ethClient;
可以看到,拥有针对华氏和摄氏度的温度的变量,以及针对湿度的变量。还有一个变量将包含将由 PubSub 客户端使用的以太网客户端的实例。
接下来是 setup()
函数的定义:
void setup() { // Start the Ethernet client, open up serial port for debugging, and attach the DHT11 sensor Ethernet.begin(mac, ip); Serial.begin(9600); DHT11.attach(3); }
此函数非常简单;它使用提供的 MAC 地址和以太网客户端运行时使用的静态 IP 地址来启动该客户端。它然后启动串行端口(用于出于调试用途而与串行监视器通信),最后在引脚 3 上打开与 DHT11 的通信。
下一个要检查的函数是 loop()
,只要 Arduino 在运行,就会持续调用该函数:
void loop() { char clientStr[34]; clientName.toCharArray(clientStr,34); char topicStr[26]; topicName.toCharArray(topicStr,26); getData(); if (!client.connected()) { Serial.print("Trying to connect to: "); Serial.println(clientStr); client.connect(clientStr); } if (client.connected() ) { String json = buildJson(); char jsonStr[200]; json.toCharArray(jsonStr,200); boolean pubresult = client.publish(topicStr,jsonStr); Serial.print("attempt to send "); Serial.println(jsonStr); Serial.print("to "); Serial.println(topicStr); if (pubresult) Serial.println("successfully sent"); else Serial.println("unsuccessfully sent"); } delay(5000); }
可以看到,此函数的大部分内容专门用于生成调试输出。但是,还有一些重要元素需要注意。靠近该函数顶部是对 getData()
函数(接下来将解释)的调用,用于获取来自 DHT11 的传感器数据。然后, loop()
函数会检查 PubSubClient
客户端是否已连接。如果未连接,那么 loop()
函数会尝试连接到 MQTT 代理。如果已经连接,那么 loop()
函数会使用 buildJson()
格式化一个 JSON 字符串,然后使用 PubSubClient
的 publish()
方法将该字符串发布到 MQTT 代理。最后, loop()
在函数底部等待 5,000 毫秒,之后 Arduino 运行时周期返回并再次调用该函数。
接下来看看 getData()
函数:
void getData() { int chk = DHT11.read(); switch (chk) { case 0: Serial.println("Read OK"); humidity = (float)DHT11.humidity; tempF = DHT11.fahrenheit(); tempC = DHT11.temperature; break; case -1: Serial.println("Checksum error"); break; case -2: Serial.println("Time out error"); break; default: Serial.println("Unknown error"); break; } }
此函数检查来自 DHT11 库的数据的状态,然后读取已准备好的传感器数据值;否则,它在控制台上打印一条提供信息的消息。
最后看看格式化 JSON 输出的函数:
String buildJson() { String data = "{"; data+="/n"; data+= "/"d/": {"; data+="/n"; data+="/"myName/": /"Arduino DHT11/","; data+="/n"; data+="/"temperature (F)/": "; data+=(int)tempF; data+= ","; data+="/n"; data+="/"temperature (C)/": "; data+=(int)tempC; data+= ","; data+="/n"; data+="/"humidity/": "; data+=(int)humidity; data+="/n"; data+="}"; data+="/n"; data+="}"; return data; }
在 Arduino 中实现的 Processing 的一个缺陷是,它缺乏用来处理字符串的良好工具。您可能已注意到,需要在 String
类实例与字符类型之间制定一些古怪的约定,以便调用需要一种或另一种实例的函数。类似地,由于缺乏很好的字符串格式化库,格式化一个像这样的简单 JSON 字符串都可能很难 — 即使存在这样的函数来格式化控制台输出。
现在,您基本上完成第一部分了。接下来按照第 1 部分 中讨论的上传过程,将修改的 sketch 上传到 Arduino。在 Arduino IDE 的状态栏中看到状态 “Done uploading.” 后,立即按下 Ctrl-Shift-M 打开串行监视器。
现在,假设一切正常,您应该在启动 Mosquitto 的终端窗口中看到以下类型的输出:
1405807697: mosquitto version 1.2.3 (build date 22/12/2013 13:36:32.54) starting 1405807697: Using default config. 1405807697: Opening ipv6 listen socket on port 1883. 1405807697: Opening ipv4 listen socket on port 1883. 1405807718: New connection from 192.168.1.20 on port 1883. 1405807718: New client connected from 192.168.1.20 as d:quickstart:arduino:deedb afefeed (c2, k15). 1405807718: Sending CONNACK to d:quickstart:arduino:deedbafefeed (0) 1405807718: Received PUBLISH from d:quickstart:arduino:deedbafefeed (d0, q0, r0, m0, 'iot-2/evt/status/fmt/json', ... (100 bytes)) 1405807723: Socket error on client d:quickstart:arduino:deedbafefeed, disconnect ing. 1405807723: New connection from 192.168.1.20 on port 1883. 1405807723: New client connected from 192.168.1.20 as d:quickstart:arduino:deedb afefeed (c2, k15). 1405807723: Sending CONNACK to d:quickstart:arduino:deedbafefeed (0) 1405807723: Received PUBLISH from d:quickstart:arduino:deedbafefeed (d0, q0, r0, m0, 'iot-2/evt/status/fmt/json', ... (100 bytes)) 1405807729: Received PUBLISH from d:quickstart:arduino:deedbafefeed (d0, q0, r0, m0, 'iot-2/evt/status/fmt/json', ... (100 bytes)) 1405807734: Received PUBLISH from d:quickstart:arduino:deedbafefeed (d0, q0, r0, m0, 'iot-2/evt/status/fmt/json', ... (100 bytes))
了解您正确完成了操作的关键是,您将在最后几行中看到 Received PUBLISH from...
。这意味着 Arduino sketch 已成功连接到 Mosquitto 代理。现在查看您的串行监视器;它应该包含类似以下信息的消息:
Read OK Trying to connect to d:quickstart:arduino:deedbafefeed attempt to send { "d": { "myName": "Arduino DHT11", "temperature (F)": 71, "temperature (C)": 22, "humidity": 43 } } to iot-2/evt/status/fmt/json successfully sent
回页首
所有功能都能在本地运行后,可以尝试对 IoT Foundation QuickStart 运行这些功能了。在浏览器中访问 IBM Internet of Things Foundation 网站,如图 2 所示。
图 2. IBM IoT Foundation 主页
单击显示 Try it out with our Quickstart 的按钮。您将看到图 3 中所示的页面。
图 3. IBM IoT Foundation Quickstart 欢迎页面
在本教程中,您将使用 IBM IoT Foundation Quickstart 的内置特性描绘来自 Arduino 传感器的实时数据的图表。在第 3 部分 和第 4 部分 中,将编写一个应用程序来对您收到的数据执行一些更有趣的操作。
IoT Foundation 有一些针对其他设备的令人兴奋的方案。如果您有一个更加复杂的设备,比如 Intel Galileo 或 Raspberry Pi,您可能希望尝试这些方案。对于目前,只需键入您的 Arduino 的 MAC 地址。但在单击 Go 之前,还需要在 sketch 中执行一项更改。
您是否还记得之前您将 sketch 更改为指向本地 MQTT 服务器,而不是 IoT Foundation 上的 MQTT 服务器?现在是时候更改回来了。注释和取消注释您在原始代码中更改的行:
// Uncomment this next line and comment out the line after it to test against a local MQTT server //PubSubClient client(localserver, 1883, 0, ethClient); PubSubClient client(servername, 1883, callback, ethClient);
执行此更改后,保存 sketch 并将它上传到 Arduino。如果想要重新启动串行监视器,可以这么做,但在您查看 IoT Foundation 时才会看到结果如何。返回到浏览器中,单击 Mac 地址页面上的 Go 按钮,然后等待几分钟。
如果各项功能运行正常,您会看到一个页面显示了您的温度读数的图表,如图 4 中所示。
图 4. 数据曲线图
大功告成!您现在已经构建了一个可作为全球 IoT 的一部分的设备。对于我的这个具体的简单情形,这是我需要的全部功能:一个曲线图,我可以观察其中的几小时数据,以检查配线箱内部的温度。坏消息是,甚至在热天的中午,预计的温度也绝不会高于 73 华氏度,所以我又不得不返回到制图板上来查找我的问题。而您学习如何利用 IoT 的旅程才刚刚开始。
回页首
在本系列的接下来两部教程中,您将编写自己的应用程序(在 IBM® Bluemix 上运行™),它可保留从 Arduino 发送的数据,支持显示和对比当前数据与历史数据。
回页首
描述 | 名字 | 大小 |
---|---|---|
示例 sketch | MQTT_IOT_SENSORS.ino | 4KB |