IBM Bluemix Internet of Things (IoT) 服务提供了简单且强大的功能将全球不同种类的设备和应用程序互联起来。这是如何实现的?Bluemix IoT 服务背后的秘密在于 MQTT,也就是消息队列遥测传输。在本教程中,您将了解 MQTT 的工作原理,以及如何使用 IoT 服务轻松地构建应用程序。
“MQTT 是一个基于 TCP/IP 协议的简单、轻量型的发布/订阅消息协议,是新兴 IoT 领域的理想之选。”
一般来讲,Bluemix IoT 服务充当着 MQTT 代理,因此负责将消息分发给已连接的客户端(设备和应用程序)。设备 包括各种机器,会发布它们所检测到的信息,应用程序 是各种程序,会使用从这些服务收到的信息。设备和应用程序使用 MQTT 协议与 MQTT 代理通信,如下图所示:
使用 Bluemix IoT 服务的应用程序通常包含 3 部分:
准备好尝试一下吗?
在这一步中,您将向 Bluemix IoT 服务注册各种设备和应用程序。
bluemixmqtt
。现在您已创建了一个拥有 IoT 服务的应用程序,如下图所示:
MQTTDevice
。我们在后面将使用该值。aabbccddee12
。您可输入您自己的标识符。如果愿意,重复执行上述步骤,注册更多的设备。
如果愿意,重复执行上述步骤,注册更多的应用程序。
设备端编程工作涉及 3 部分:
在本节中,您将使用 Eclipse Paho Java 在 Java 中构建一个简单的设备端程序。有关更多信息,请参见 源代码。
IoT 服务(MQTT 代理)的访问点是
<orgid>.messaging.internetofthings.ibmcloud.com
,其中
<orgid>
是在配置 IoT 服务时自动创建的。您可使用 TCP 或 TLS(传输层安全)。
tcp://<org-id>.messaging.internetofthings.ibmcloud.com:1883 ssl://<org-id>.messaging.internetofthings.ibmcloud.com:8883
以设备客户端的身份连接到 IoT 服务之前,您必须设置 MQTT 连接选项。
d:<orgid>:<type-id>:<divice-id>
。<orgid>
与上面相同,而 <type-id>
和 <divice-id>
是之前注册设备时输入的。
下面的代码段来自类 com.ibm.bluemixmqtt.MqttHandler.java
。您可选择使用 TCP 或 TLS。如果使用
TLS,应该将属性 com.ibm.ssl.protocol
设置为
TLSv1.2;否则连接可能失败。
public void connect(String serverHost, String clientId, String authmethod, String authtoken, boolean isSSL) { // check if client is already connected if (!isMqttConnected()) { String connectionUri = null; //tcp://<org-id>.messaging.internetofthings.ibmcloud.com:1883 //ssl://<org-id>.messaging.internetofthings.ibmcloud.com:8883 if (isSSL) { connectionUri = "ssl://" + serverHost + ":" + DEFAULT_SSL_PORT; } else { connectionUri = "tcp://" + serverHost + ":" + DEFAULT_TCP_PORT; } if (client != null) { try { client.disconnect(); } catch (MqttException e) { e.printStackTrace(); } client = null; } try { client = new MqttClient(connectionUri, clientId); } catch (MqttException e) { e.printStackTrace(); } client.setCallback(this); // create MqttConnectOptions and set the clean session flag MqttConnectOptions options = new MqttConnectOptions(); options.setCleanSession(true); options.setUserName(authmethod); options.setPassword(authtoken.toCharArray()); //If SSL is used, do not forget to use TLSv1.2 if (isSSL) { java.util.Properties sslClientProps = new java.util.Properties(); sslClientProps.setProperty("com.ibm.ssl.protocol", "TLSv1.2"); options.setSSLProperties(sslClientProps); } try { // connect client.connect(options); System.out.println("Connected to " + connectionUri); } catch (MqttException e) { e.printStackTrace(); } } }
下面的代码段来自类 com.ibm.bluemixmqtt.DeviceTest.java
。此代码读取配置文件,并使用配置文件中格式正确的值设置
MQTT 连接选项。
//Read properties from the conf file Properties props = MqttUtil.readProperties("device.conf"); String org = props.getProperty("org"); String id = props.getProperty("deviceid"); String authmethod = "use-token-auth"; String authtoken = props.getProperty("token"); //isSSL property String sslStr = props.getProperty("isSSL"); boolean isSSL = false; if (sslStr.equals("T")) { isSSL = true; } System.out.println("org: " + org); System.out.println("id: " + id); System.out.println("authmethod: " + authmethod); System.out.println("authtoken: " + authtoken); System.out.println("isSSL: " + isSSL); String serverHost = org + MqttUtil.SERVER_SUFFIX; //Format: d:<orgid>:<type-id>:<divice-id> String clientId = "d:" + org + ":" + MqttUtil.DEFAULT_DEVICE_TYPE + ":" + id; handler = new DeviceMqttHandler(); handler.connect(serverHost, clientId, authmethod, authtoken, isSSL);
备注:要想连接 Bluemix IoT 服务,必须使用最低版本级别为 3.1 的 MQTT;推荐使用 MQTT 3.1.1,该版本中的功能更多。
您应该使用这种形式发布各种事件主题:iot-2/evt/<event-id>/fmt/<format>
。设置
<event-id>
可将不同的事件类型分类;您可选择自己的值。<format>
设置为 json。消息应编码成 JSON 格式,它必须包含单个名为 “d” 的顶级属性。
以下代码段来自类 com.ibm.bluemixmqtt.DeviceTest.java
。它将消息编码为 JSON
格式并发布到正确的主题。
//Format the Json String JSONObject contObj = new JSONObject(); JSONObject jsonObj = new JSONObject(); try { contObj.put("count", count); contObj.put("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date())); jsonObj.put("d", contObj); } catch (JSONException e1) { e1.printStackTrace(); } System.out.println("Send count as " + count); //Publish device events to the app //iot-2/evt/<event-id>/fmt/<format> handler.publish("iot-2/evt/" + MqttUtil.DEFAULT_EVENT_ID + "/fmt/json", jsonObj.toString(), false, 0);
在我们的示例设备程序中,我们发布事件时计数值从 0 开始,每隔 15 秒递增一次。下面是一条示例消息。注意,事件 JSON 消息中需要顶级属性 “d”。
{ "d": { "count": 3, "time": "2014-12-30 16:14:59" } }
订阅命令的主题应为以下格式:iot-2/cmd/<cmd-type>/fmt/<format-id>
。设置
<cmd-type>
可将不同的命令类型分类;您可选择自己的值。可使用加号(“+”)作为通配符来设置
<cmd-type>
,这样它可订阅各种命令类型。<format-id>
设置为
json。
下面的代码段来自类 com.ibm.bluemixmqtt.DeviceTest.java
。它演示了如何订阅命令消息。
//Subscribe the Command events //iot-2/cmd/<cmd-type>/fmt/<format-id> handler.subscribe("iot-2/cmd/" + MqttUtil.DEFAULT_CMD_ID + "/fmt/json", 0);
收到一个命令事件后,系统会执行回调函数 messageArrived
。下面的代码段来自
com.ibm.bluemixmqtt.DeviceTest.java
。在我们的示例应用程序中,从应用程序端收到该命令时,会将计数重置到从该命令所提取的值。
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { super.messageArrived(topic, mqttMessage); //Check whether the event is a command event from app if (topic.equals("iot-2/cmd/" + MqttUtil.DEFAULT_CMD_ID + "/fmt/json")) { String payload = new String(mqttMessage.getPayload()); JSONObject jsonObject = new JSONObject(payload); String cmd = jsonObject.getString("cmd"); //Reset the count if (cmd != null && cmd.equals("reset")) { int resetcount = jsonObject.getInt("count"); count = resetcount; System.out.println("Count is reset to " + resetcount); } } }
备注:有关设备端编程的更多信息,请参阅 https://developer.ibm.com/iot/recipes/improvise-connect-quickstart/。
与设备端编程一样,应用程序端编程包含 3 部分工作:
在本节中,您将使用 Eclipse Paho Java 在 Java 中构建一个简单的应用程序端程序。有关更多信息,请参见 源代码。
IoT 服务的访问点与设备端相同,但 MQTT 连接选项格式之间存在一些差异:
下面的代码段来自类 com.ibm.bluemixmqtt.APPTest.java
。它读取配置文件,并使用配置文件中格式正确的值设置 MQTT
连接属性。
//Read properties from the conf file Properties props = MqttUtil.readProperties("app.conf"); String org = props.getProperty("org"); String id = props.getProperty("appid"); String authmethod = props.getProperty("key"); String authtoken = props.getProperty("token"); //isSSL property String sslStr = props.getProperty("isSSL"); boolean isSSL = false; if (sslStr.equals("T")) { isSSL = true; } System.out.println("org: " + org); System.out.println("id: " + id); System.out.println("authmethod: " + authmethod); System.out.println("authtoken" + authtoken); System.out.println("isSSL: " + isSSL); //Format: a:<orgid>:<app-id> String clientId = "a:" + org + ":" + id; String serverHost = org + MqttUtil.SERVER_SUFFIX; handler = new AppMqttHandler(); handler.connect(serverHost, clientId, authmethod, authtoken, isSSL);
应用程序端可订阅两种类型的事件:(1) 来自设备端的状态消息 (2) 系统生成的连接监视器消息。
iot-2/type/<type-id>/id/<device-id>/evt/<event-id>/fmt/<format-id>
。它应与设备端的事件格式保持一致。可使用加号(“+”)作为通配符,它将与主题树中的一个级别准确匹配。iot-2/type/<type-id>/id/<device-id>/mon
。在本例中,也可使用加号(“+”)作为通配符。通过订阅系统事件,每次设备与
IoT 服务连接或断开连接时,都可收到消息。下面的代码段来自类 com.ibm.bluemixmqtt.APPTest.java
。系统和设备事件都已订阅。
handler.subscribe("iot-2/type/" + MqttUtil.DEFAULT_DEVICE_TYPE + "/id/+/mon", 0); //Subscribe Device Events //iot-2/type/<type-id>/id/<device-id>/evt/<event-id>/fmt/<format-id> handler.subscribe("iot-2/type/" + MqttUtil.DEFAULT_DEVICE_TYPE + "/id/+/evt/" + MqttUtil.DEFAULT_EVENT_ID + "/fmt/json", 0);
收到一个事件后,系统会执行回调函数 messageArrived
。在我们的示例应用程序中,会检查消息中的计数值。如果计数值大于
4,则启动一个新线程来向相应的设备发布一个命令,从而将计数值重置为 0。有关更多信息,请参阅类 com.ibm.bluemixmqtt.APPTest.java
文件。
public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception { super.messageArrived(topic, mqttMessage); Matcher matcher = pattern.matcher(topic); if (matcher.matches()) { String deviceid = matcher.group(1); String payload = new String(mqttMessage.getPayload()); //Parse the payload in Json Format JSONObject jsonObject = new JSONObject(payload); JSONObject contObj = jsonObject.getJSONObject("d"); int count = contObj.getInt("count"); System.out.println("Receive count " + count + " from device " + deviceid); //If count >=4, start a new thread to reset the count if (count >= 4) { new ResetCountThread(deviceid, 0).start(); } } }
发布命令的主题应为以下格式:iot-2/cmd/<cmd-type>/fmt/<format-id>
。它应与设备端上的命令格式保持一致。
下面的代码段来自类 com.ibm.bluemixmqtt.APPTest.java
。一旦计数值达到 4,该类会将
reset
命令发布到相应的设备。当然,该命令也被编码为 JSON 格式。
JSONObject jsonObj = new JSONObject(); try { jsonObj.put("cmd", "reset"); jsonObj.put("count", count); jsonObj.put("time", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") .format(new Date())); } catch (JSONException e) { e.printStackTrace(); } System.out.println("Reset count for device " + deviceid); //Publish command to one specific device //iot-2/type/<type-id>/id/<device-id>/cmd/<cmd-id>/fmt/<format-id> handler.publish("iot-2/type/" + MqttUtil.DEFAULT_DEVICE_TYPE + "/id/" + deviceid + "/cmd/" + MqttUtil.DEFAULT_CMD_ID + "/fmt/json", jsonObj.toString(), false, 0);
备注:有关应用程序端编程的更多信息,请参阅 https://developer.ibm.com/iot/recipes/improvise-application-development/。
备注:项目需要 Java SDK 7.0 或更高版本。
应用程序端配置文件位于 MyData/app.conf 中。根据之前注册应用程序时所复制的属性来更新它。
#Configuration files for App Side Applications #The org field is the same org field as the Device side org=<org> # A unique id you choose it by yourself, maybe, abcdefg123456 appid=<appid> # The key field from App Keys info you copied previously key=<Key> # The Auth Token field from App Keys info you copied previously token=<Auth_Token> #T or F, T means using SSL, while F means not isSSL=F
更新配置文件后,打开命令行,转到 MyData 文件夹,然后执行下面这条命令,运行应用程序端程序:java -cp
.;org.eclipse.paho.client.mqttv3.jar;json4j-apache-1.1.0.jar;bluemixmqtt.jar
com.ibm.bluemixmqtt.AppTest
应用程序端程序成功连接到 IoT 服务后,您应收到与下面类似的确认消息:
设备端配置文件位于 MyData/device.conf 中。根据之前注册设备时所复制的属性来更新它。
#Configuration files for Device Side Applications #The org field from Device info you copied previously org=<org> #The id field from Device info you copied previously deviceid=<id> #The auth-token field from Device info you copied previously token=<auth-token> #T or F, T means using SSL, while F means not isSSL=F
更新配置文件后,打开命令行,转到 MyData 文件夹,然后执行以下命令,运行设备端程序。java -cp
.;org.eclipse.paho.client.mqttv3.jar;json4j-apache-1.1.0.jar;bluemixmqtt.jar
com.ibm.bluemixmqtt.DeviceTest
设备端程序成功连接到 IoT 服务后,应收到与下面类似的确认消息:
设备端程序会发布状态事件消息(计数值从 0 开始,每隔 15 秒递增)。您可通过命令行找到相关的日志(包括事件和命令消息细节)。
此外,您可按照下面的步骤查看 IoT 服务控制台上的事件细节。
aabbccddee12
。 请注意 count 字段。如前所述,count 字段的值从 0 开始递增。其值达到 4 时,应用程序端程序向相应的设备发布一条命令,将该值重置为 0。所以计数值始终在 0 到 4 之间,绝不会超出该范围。
通过执行上述操作,您使用 Bluemix IoT 服务并通过 MQTT 协议构建了应用程序。如果您熟悉 MQTT 客户端编程,可能会发现二者之间没有什么太大的差别,除了使用 Bluemix IoT 服务时,您必须确保一些属性(比如 MQTT 连接选项、发布和订阅主题表单等)要符合正确的格式要求。
如果解决方案基于现有的 MQTT 客户端库,那么使用 Bluemix IoT 服务构建解决方案会更容易。好消息是,目前有大量针对不同平台的 开源库,比如 C、C++、Java、JavaScript、Ruby、Go 等。
Bluemix IoT 服务支持各种连网的智能设备,比如 Arduino Uno、Raspberry Pi 等。请参阅 Bluemix 文档,了解详细信息。如果您的设备位于列表中,应该首先学习示例代码。前面讨论的 MQTT 机制应该可以帮助您理解和修改源代码。如果设备未在列表中,不要担心。就像我在示例应用程序中使用 Java 实现设备端程序一样,您可使用 MQTT 客户端库轻松地构建自己的程序。
我使用 Java 实现了我的应用程序端程序,但您也可以选择其他编程语言,比如 Node.js 或 Ruby。为了使应用程序端编程工作更容易,您可使用基于浏览器的流编辑器 Node-RED,而不是在本地进行编码。无论是自行构建还是通过 Node-RED 构建程序,Bluemix IoT 服务背后的 MQTT 流都具有相同的工作方式。
如果有兴趣使用 Node-RED 工具构建应用程序端程序,可执行以下操作步骤。
Node-RED 基于 Node.js 运行时,包含在我们之前创建的 Internet of Things Foundation Starter 样板中。
使用 Node-RED GUI 编辑器构建应用程序端程序对用户而言要更容易一些。Node-RED 提供了许多内置的流块,您可轻松地将它们连接起来并直接进行部署。有关在 IoT 上使用 Node-RED 的更多信息,请参阅 Bluemix 文档。
备注:不需要手动在 IoT 服务控制台中注册应用程序,因为使用 Node-RED 编辑器时,会自动注册该应用程序。请在出现提示时选择 Bluemix Service for Authentication 选项。
使用 Bluemix Internet of Things (IoT) 服务构建应用程序既简单又有趣。在本教程中,我们分析了 IoT 服务背后的 MQTT,解释了它的工作原理。一个循序渐进的教程展示了如何使用 MQTT,通过 Java 或 Node-RED 编辑器构建一个示例解决方案。因为您可在许多不同的场景中使用 IoT 概念,所以可将示例应用程序作为参考,使用 Bluemix IoT 服务合成自己的解决方案,帮助与全世界实现互联。
本教程所使用的 Bluemix 服务:Internet of Things 服务为应用程序提供了简单且强大的方法,使其可轻松访问 IoT 设备和数据。