作者:陈广
日期:2021-5-5
一年多前是准备写这块内容的,但不得不转战RFID写书。RFID忙完后,由于换了NB-IOT芯片,不得不把基础内容重新来一遍,现在终于回到了原来的起点。讲什么内容呢?AM22E使用的是中移物联网的模组,而中移物联网架设了自己的物联网平台:OneNET,OneNET中最常用的协议是MQTT,所以我们从MQTT协议开始。
MQTT协议(Message Queuing Telemetry Transport),翻译过来就是消息队列遥测传输,是IBM公司于1999年提出的,当前最新版本是3.1.1。它设计的初始目的是为了极有限的内存设备和网络带宽很低的网络不可靠的通信,非常适合物联网通信。
任何协议都是为了解决特定问题而出现的,就如HTTP的出现是为了解决浏览器显示服务器内容而生,FTP的出现是为了解决文件在互联网上传输的问题。20世纪90年代中期,IBM公司为了帮助石油天然气公司实现数千英里长的石油和天然气管道的无人值守监控,设计了MQTT协议。
石油天逸气管道线路非常长,会穿越很多无人区,附近没有网络设施,因此使用卫星通讯最为经济。卫星通信延迟高,并且每天都会出现卫星切换时的网络中断,因此需要客户端和服务器端都能够保留消息收发状态,以便在网络恢复正常后继续发送。另一方面,卫星链路带宽低,通信流量费用高昂,因此需要尽量节省传输消息的流量开销。
MQTT是基于TCP协议进行设计的,它解决了传输可靠性的问题。MQTT设计简单、轻巧、便于实现,占用的硬件资源和网络资源非常少,并对网络延迟有完善的处理方案,这些特点使它成为了资源受限的NB-IOT上使用的首选协议。
MQTT所实现的内容非常简单:消息的订阅和发布。MQTT协议中有三种身份:发布者(Publish)、代理(Broker)、订阅者(Subscribe),其中,发布者和订阅者为网络中的客户端,代理为服务器端。发布者向代理发布消息,订阅者向代理订阅消息,当发布者发布消息时,如果订阅者订阅了该消息,则代理会将此消息推送给订阅者。
MQTT包括以下消息类型:
名称 | 作用 | 流向 | 描述 |
---|---|---|---|
CONNECT | 连接服务器 | 客户端 → 服务端 | 客户端到服务端的网络连接建立后, 客户端发送给服务端的第一个报文必须是CONNECT报文。 |
CONNACK | 确认连接请求 | 服务端 → 客户端 | 服务端从客户端收到的CONNECT报文后,向客户端响应CONNACK报文。 服务端发送给客户端的第一个报文必须是CONNACK。如果客户端在合理的时间内没有收到服务端的CONNACK报文, 客户端应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。 |
PUBLISH | 发布消息 | 双向 | 向服务器发送发布消息请求 |
PUBACK | 发布确认 | 双向 | QoS 1消息发布收到确认 |
PUBREC | 发布收到 | 双向 | PUBREC报文是对QoS等级2的PUBLISH报文的响应。 它是QoS 2等级协议交换的第二个报文。 |
PUBREL | 发布释放 | 双向 | PUBREL报文是对PUBREC报文的响应。 它是QoS 2等级协议交换的第三个报文。 |
PUBCOMP | 发布完成 | 双向 | PUBCOMP报文是对PUBREL报文的响应。 它是QoS 2等级协议交换的第四个也是最后一个报文。 |
SUBSCRIBE | 订阅主题 | 客户端 → 服务端 | 客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。 每个订阅注册客户端关心的一个或多个主题。 为了将应用消息转发给与那些订阅匹配的主题, 服务端发送PUBLISH报文给客户端。 SUBSCRIBE报文也( 为每个订阅) 指定了最大的QoS等级, 服务端根据这个发送应用消息给客户端。 |
SUBACK | 订阅确认 | 服务端 → 客户端 | 服务端发送SUBACK报文给客户端, 用于确认它已收到并且正在处理SUBSCRIBE报文。 |
UNSUBSCRIBE | 取消订阅 | 客户端 → 服务端 | 客户端发送UNSUBSCRIBE报文给服务端, 用于取消订阅主题。 |
UNSUBACK | 取消订阅确认 | 服务端 → 客户端 | 服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。 |
PINGREQ | 心跳请求 | 客户端 → 服务端 | 客户端发送PINGREQ报文给服务端的。 用于:1. 在没有任何其它控制报文从客户端发给服务的时, 告知服务端客户端还活着。2. 请求服务端发送响应确认它还活着。3. 使用网络以确认网络连接没有断开。 |
PINGRESP | 心跳响应 | 服务端 → 客户端 | 服务端发送PINGRESP报文响应客户端的PINGREQ报文。 表示服务端还活着。 |
DISCONNECT | 断开连接 | 客户端 → 服务端 | DISCONNECT报文是客户端发给服务端的最后一个控制报文。 表示客户端正常断开连接。 |
MQTT提供了QoS 0,QoS 1和 QoS 2 三个不同等级的消息发送服务质量,收发双方根据业务需要可选择所需的服务等级。MQTT客户端和Broker端底层通过 session 来保障不同的QoS等级。如果直接使用TCP实现业务,需要自己实现类似机制,而MQTT自带这一功能。
通过下面的例子可以更深刻的理解上面三个传输质量等级。比如目前流行的共享单车智能锁,智能锁可以定时使用QoS level 0质量消息请求服务器,发送单车的当前位置,如果服务器没收到也没关系,反正过一段时间又会再发送一次。之后用户可以通过App查询周围单车位置,找到单车后需要进行解锁,这时候可以使用QoS level 1质量消息,手机App不断的发送解锁消息给单车锁,确保有一次消息能达到以解锁单车。最后用户用完单车后,需要提交付款表单,可以使用QoS level 2质量消息,这样确保只传递一次数据,否则用户就会多付钱了。
在可变报文头的连接标志位字段(Connect Flags)里有三个Will标志位:Will Flag、Will QoS和Will Retain Flag,这些Will字段用于监控客户端与服务器之间的连接状况。如果设置了Will Flag,就必须设置Will QoS和Will Retain标志位,消息主体中也必须有Will Topic和Will Message字段。
那遗愿消息是怎么回事呢?服务器与客户端通信时,当遇到异常或客户端心跳超时的情况,MQTT服务器会替客户端发布一个Will消息。当然如果服务器收到来自客户端的DISCONNECT消息,则不会触发Will消息的发送。 因此,Will字段可以应用于设备掉线后需要通知用户的场景。
MQTT客户端可以设置一个心跳间隔时间(Keep Alive Timer),表示在每个心跳间隔时间内发送一条消息。如果在这个时间周期内,没有业务数据相关的消息,客户端会发一个PINGREQ消息,相应的,服务器会返回一个PINGRESP消息进行确认。如果服务器在一个半(1.5)心跳间隔时间周期内没有收到来自客户端的消息,就会断开与客户端的连接。心跳间隔时间最大值大约可以设置为18个小时,0值意味着客户端不断开。
这里只对MQTT协议做一个简单介绍,详细信息,请访问:
现今几个大的IT公司都架设了自己的物联网云平台,腾讯、阿里、华为、电信,移动的是OneNET,由于AM22E使用的是OneNET,所以我们就讲OneNET,其实学哪个并不是那么重要,思路都是一样的。
首先进入中移物联网公司网站:http://iot.10086.cn/
然后参考以下文档https://open.iot.10086.cn/doc/mqtt/book/get-start/login.html进行账户注册与账户认证。
然后进入MQTT物联网套件页面,单屏幕上方的【立即使用】按钮,进入到MQTT平台。
接下来点击【添加产品】按钮,按下图添加一个产品,注意,移动那边会经常更新OneNET,所以界面可能会有所不同,可参考产品创建官方文档:
添加完成后,OneNET会自动分配一个产品ID,请记住这个6位数的ID,后面会用到。
接下来参考创建设备官方文档为之前添加的产品添加一个设备,只需填入设备名称,这里为:“mqtt_001”。添加完成后,系统会自动为此设备分配一个设备ID,请记住设备ID和设备名称,等下会使用到。
待会我们会用软件模拟设备登录OneNET时,需要使用一个密码,而得到这个密码需要经过较为复杂的计算。密码可以使用工具计算得出,也可以自己编程计算。OneNET官方文档没有把这个密码的生成讲清楚,害我整了半天才连接上服务器。
产品和设备添加完成后,我们收集以下信息:
注意:这些是我的信息,不同的人完成操作,除了设备名称,其它的肯定会不一样。
首先到以下链接下载OneNET的token生成工具:
https://open.iot.10086.cn/doc/mqtt/book/manual/auth/tool.html
打开后,按下图所示生成密码:
最终生成的密码为:
version=2018-10-31&res=products%2F423266%2Fdevices%2Fmqtt_001&et=1735660800&method=sha1&sign=sbdInDZQZ7%2FfZWPMefRaeJvNgYk%3D
上图中各参数:
MQTT.fx是一款基于Eclipse Paho,使用Java语言编写的MQTT客户端工具,支持通过Topic订阅和发布消息。我们可以在学习AM22E的过程中使用它作为一个调试和模拟工具。我个人强烈建议使用MQTT.fx模拟设备进行学习,因为模拟器是通用的,而不同模组的AT指令不一样,使用模拟器更接近其本质,不要的把自己绑定在特定设备上。首先到以下地址下载MQTT.xf:
下载安装完成后,打开MQTT.fx,如下图所示:
单击齿轮图标打开设置窗口,按下图所示填写。
说明一: 由于网址未来有可能会有变动,请到以下文档查找服务器接入网址和端口: https://open.iot.10086.cn/doc/mqtt/book/device-develop/manual.html
单击【User Credentials】填写登录的用户名称密码,按下图所示填写:
最后单击【OK】按钮保存设置并关闭设置窗口。单击【Connect】按钮连接服务器,并单击【Log】按钮查看日志,如下图所示:
最后,在OneNET中查看设备状态,显示“在线”表明我们的设备已经连接成功了。在MQTT.fx中单击【Disconnect】按钮,再观察设备状态,可以发现已经处于离线状态了。
接下来我们尝试给OneNET中的设备订阅和发布消息。首先要订阅,然后消息在发布后才能收到。
MQTTS物联网套件中设备相关服务(存储、命令等)的面向设备的接口,均以topic(主题)的形式提供,设备可以通过 publish 消息到系统topic调用服务接口,也可以订阅系统topic用于接收服务消息通知。OneNET平台暂不支持用户自定义topic,仅限使用系统topic。
为了确定设备上传的消息已被服务器接收,设备可以订阅上传数据点结果消息。设备上传数据点topic命名规则如下:
$sys/{pid}/{device-name}/dp/post/json/+
其中:
{pid}
:为产品ID{device-name}
:为设备名称打开MQTT.fx,连接成功后,点击【Subscribe】按钮打开订阅面板,按下图所示操作:
接下来向设备上传数据点,系统在收到数据点后会自动发布一条数据点上传成功消息,订阅了此消息的设备将收到这条消息。本例中上传数据点和订阅消息的设备为同一设备。
发布topic命名规则如下:
$sys/{pid}/{device-name}/dp/post/json
在MQTT.fx中点击【Publish】按钮,打开发布面板,按下图所示操作,上传的数据必须为json格式:
{
"id": 001,
"dp": {
"temperatrue": [{
"v": 30,
}],
"power": [{
"v": 4.5,
}]
}
}
点击【Subscribe】按钮打开订阅面板,可以看到已经从平台收到了数据点上传成功的消息,如下图所示:
接下来更改id号和温度,再上传两个数据点:
{
"id": 002,
"dp": {
"temperatrue": [{
"v": 25,
}],
"power": [{
"v": 4.5,
}]
}
}
和
{
"id": 003,
"dp": {
"temperatrue": [{
"v": 21,
}],
"power": [{
"v": 4.5,
}]
}
}
查看订阅面板,看看是否收到上传成功消息。
登录OneNET平台,进入到设备列表面板,找到mqtt_001设备。点击【数据流】打开数据流面板,如下图所示:
可以看到,我们上传的数据中包含了两个类型的数据,一个是temperatrue,另一个是power。
点击 temperatrue 查看温度数据流:
注意:最后三个点是我们刚才上传的温度数据。
;