LuatOS 的 MQTT 实现详解
一,mqtt 基础介绍
1.1 mqtt 的诞生历史
1999 年,IBM 的 Andy Stanford-Clark 和 Arcom(现为 Eurotech/Cirrus Link)的 Arlen Nipper 合作开发了 MQTT 协议。 当时,他们的目标是为油气行业的 SCADA 系统(工业监控与数据采集)提供一种轻量级、带宽高效、低功耗的数据传输协议。因为这些设备常常部署在偏远地区,只能通过昂贵且带宽有限的卫星链路通信,所以传统的通信协议并不适用。
MQTT 设计的初衷有几点:
(1)协议实现简单;
(2)占用带宽极小;
(3)节省设备电量;
(4)支持不稳定网络环境;
(5)采用发布/订阅模式,解耦数据的采集和消费。
1.2 MQTT 的发展历程
(1)1999-2010 年:行业应用与内部推广
(2)2010 年:开放协议
(3)2011 年:开源生态建立
(4)2013 年:国际标准化
(5)2014 年:MQTT 3.1.1 发布
(6)2019 年:MQTT 5.0 发布
MQTT逐步从工业物联网扩展到智能家居、环境监测、医疗设备、资产追踪等众多IoT场景,成为物联网领域最主流的消息协议之一。
1.3 MQTT 解决了什么问题
在非理想通信质量情况下,多个设备之间的通信,可以由 Matt broker 进行不同可靠等级的调度,并且可以根据订阅需求,只给设备推送需要的信息,节省通信负担。
1.4 MQTT 的通信承载
MQTT 最常用的承载是 TCP。
也可以承载在 websocket 和 UDP。
TCP 的开销低,通信的可靠性高,是用的最普遍的;
当需要网页作为客户端,或者是需要穿透防火墙的时候,可以适当增加通信的开销,选择 websocket;
当可以接受适当的丢包,可以选择 UDP 进一步降低通信的开销。
1.5 LuatOS 对 MQTT 的支持
LuatOS 目前只支持 MQTT3.1.1 版本,并且只支持 TCP 承载。
LuatOS 支持 非加密的 MQTT 和加密的 MQTT。
通过 LuatOS 的 mqtt 核心库, 提供对 MQTT3.1.1 的全面的开发支持。
二、MQTT 的工作过程详解
2.1 MQTT 的工作角色
一个 MQTT 系统,有两个角色:Client 和 Broker。
Client 是客户端,有多个;
Broker 是服务端,只有一个。
所以 MQTT 是一个中心化的通信架构。
2.2 MQTT 的订阅和发布
要理解 MQTT 的通信,首先要理解三个概念:
(1)主题(topic
(2)发布(publish);
(3)订阅(subscribe)。
2.2.1 MQTT 的 主题(topic)
主题是消息的“寻址机制”。
本质来说,MQTT 主题是消息的路由和分类工具,决定了消息的流向和归属。
topic 的格式是分层结构,由斜杠 `/` 分隔的多级字符串组成,每一级可以代表设备、位置、功能、数据类型等,例如:
factory/line1/machine3/temperature
Topic 的作用是,通过主题,设备和应用可以有序地组织、筛选和接收信息流,使整个系统结构化、易于扩展。
主题是 MQTT 消息的分类和路由依据,类似于一棵多级目录树,用斜杠 /
分层,例如:
sensor/room1/temperature
每条 MQTT 消息都必须带有一个主题,主题决定了消息的归属和传递路径。
主题类似于“频道”:主题就像广播频道,发布者把消息发到频道,订阅者只需订阅感兴趣的频道即可收到对应内容。
主题的作用,是解耦通信;
主题让发布者和订阅者无需直接关联,实现灵活、松耦合的系统架构。
2.2.2 MQTT 的订阅(subscribe)
MQTT Client 可以作为订阅者(Subscriber),成为消息的接收者。
MQTT Client 通过订阅感兴趣的主题来获取消息。
订阅者可以同时订阅一个或多个主题。
只要有新的消息发布到被订阅的主题,Broker 就会把这些消息,通过publish 报文,推送给所有订阅者。
MQTT Client 通过 subscribe 报文订阅特定的主题。
2.2.3 MQTT 的发布(publish)
MQTT Client 可以作为发布者(Publisher),成为消息的发送方,将消息发布到指定的主题上。
发布者无需关心有没有订阅者、订阅者是否在线,只需把消息发给 Broker(代理服务器)即可。
发布者是通过 publish 报文,把数据上报给 Broker。
一个传感器设备,定时将温度数据,通过上报给 Broker 的 publish 报文,发布到 `sensor/room1/temperature` 主题。
2.2.4 broker 的作用
Broker 是代理,是消息中转站,负责接收发布者的消息,并将其分发给所有订阅了相应主题的订阅者。
Broker 还负责管理客户端的连接、断开、订阅和取消订阅等操作。
2.2.5 订阅和发布机制的优势
(1)解耦通信:发布者和订阅者互不感知,彼此独立,提升系统灵活性和可扩展性。
(2)灵活路由:支持一对多、多对多消息分发,适合物联网和大规模消息推送场景。
(3)主题是消息分类和路由的依据。
(4)发布是把消息发送到主题,交给 Broker。
(5)订阅是向 Broker 声明感兴趣的主题,接收相关消息。
(6)Broker 负责消息的分发和管理,发布者和订阅者无需直接通信。
这种模式让 MQTT 成为物联网和消息推送等场景的理想选择。
2.3 MQTT Client 的工作过程
2.3.1 建立连接
先建立 TCP 连接,再建立 MQTT 连接,再鉴权,鉴权通过后,MQTT 连接就算是建立成功了。
2.3.2 订阅某个主题
2.3.3 发布消息
2.3.4 一个简单的流程
(1)ClientA 连接到 Broker。
(2)ClientB 连接到 Broker,并订阅主题 Topic1
。
(3)ClientA 发布一条消息到 Topic1
。
(4)Broker 收到消息,发现 ClientB 订阅了 Topic1
,于是将消息转发给 ClientB。
(5)ClientB 接收到消息。
2.4 主题的匹配
2.4.1 主题的格式
主题是 UTF-8 编码的字符串,用 /
作为分隔符,形成多级结构。例如:
sensor/room1/temperature
每一级可以代表不同的物理或逻辑含义,如设备、位置、数据类型等。
2.4.2 主题的匹配
发布者将消息发布到某个具体主题。
订阅者通过主题过滤器(支持通配符)订阅主题,Broker 会将匹配的消息推送给订阅者。
2.4.3 主题匹配支持的通配符
MQTT 支持 +
(单层)和 #
(多层)通配符,允许一次订阅多个相关主题,极大提升了灵活性和效率。
2.4.4 单层通配符 +
(1)匹配主题中的任意单一层级。
(2)必须单独占据一个层级位置。
例如,订阅 `sensor/+/temperature` 可匹配:
但不匹配:
单层通配符,可以在主题过滤器的任意层级使用,也可以多次使用
2.4.5 多层通配符
(1)匹配当前层级及其所有下级层级(包括零层级)。
(2)只能出现在主题过滤器的最后,且必须单独占据一个层级。
例如,订阅 sensor/#
可匹配:
(3)订阅 #
可匹配所有主题
2.4.6 主题匹配示例
订阅主题 | 可匹配的发布主题 | 不匹配的主题 |
home/+/status | home/livingroom/statushome/kitchen/status | home/livingroom/temperature |
home/# | home/livingroom/statushome/kitchen/temperaturehome | office/livingroom/status |
devices/+/temp/+ | devices/a/temp/1devices/b/temp/2 | devices/a/humidity/1 |
2.4.7 系统主题与特殊通配符
以 `$` 开头的主题(如 `$SYS/`)为系统主题,默认不会被 `#` 匹配,需单独订阅 `$SYS/#`
2.4.8 注意事项
(1)通配符只能用于订阅,不能用于发布;
(2)主题和过滤器区分大小写,如 Home/temperature
与 home/temperature
不同;
(3)主题过滤器和主题名都必须至少包含一个字符,可以只为 /
;
(4)主题不建议以 /
开头或结尾,避免歧义。
(5)相邻的 /
表示空层级(如 a//b
),也是合法的。
2.5 基于遗嘱的异常处理
MQTT 的“遗嘱消息”(Will Message 或 Last Will and Testament, LWT)是协议中的一个重要机制,用于在客户端异常断开连接时,自动通知其他订阅者该客户端已离线或发生故障。
它在物联网、设备监控等场景下非常实用,能实现设备状态的自动感知和异常响应。
2.5.1 遗嘱消息的定义与作用
(1)遗嘱的定义
MQTT 客户端在连接到 Broker 时,可以设置一条“遗嘱消息”,包括主题(Will Topic)、内容(Will Payload)、QoS 等参数。
(2)遗嘱的作用
一旦客户端因网络故障、掉电、崩溃等非正常方式断开,Broker 会自动将这条遗嘱消息发布到指定主题,通知所有订阅了该主题的其他客户端。
2.5.2 遗嘱消息的参数
在 CONNECT 报文中,客户端可设置以下遗嘱相关参数:
- 遗嘱主题(lastWillTopic):消息要发布到的主题,只有订阅该主题的客户端会收到。
- 遗嘱内容(lastWillMessage):消息正文,如 "offline"、"unexpected exit" 等。
- 遗嘱 QoS(lastWillQoS):服务质量等级(0/1/2),与普通消息一致。
- 遗嘱保留(lastWillRetain):是否将遗嘱消息作为保留消息存储,便于后续新订阅者获取。
2.5.3 遗嘱消息的触发与发布流程
(1)正常断开:客户端主动发送 DISCONNECT 报文,Broker 不会发布遗嘱消息。
(2)异常断开:如掉线、超时、未正常关闭连接,Broker 检测到后会自动发布遗嘱消息。
(3)延迟发布:若设置了 Will Delay Interval,Broker 会等待指定时间,若客户端在此期间重连则不会发布遗嘱消息。
2.5.4 使用场景举例
- 设备状态监控:设备上线时主动发布 "online" 消息,连接时设置遗嘱为 "offline"。当设备异常离线,Broker 自动推送 "offline" 消息,便于业务系统感知设备状态。
- 异常通知:如传感器、机器人等关键设备掉线,系统可自动切换备用设备或报警。
- 环境信息补充:遗嘱消息可包含断线前的环境参数,帮助分析异常原因。
2.5.5 设计与使用建议
- 主题规范:建议每个设备有独立的遗嘱主题,如
devices/device123/status
。 - 保留消息:可将遗嘱设置为保留消息,保证新上线的订阅者能立即获知设备当前状态。
- 延迟合理:弱网环境下建议设置合适的 Will Delay Interval,减少误报。
- 会话管理:遗嘱消息属于会话状态的一部分,需在连接时设定,断开后如需更改需重新连接。
2.5.6 遗嘱与普通消息的区别
- 遗嘱消息由 Broker 自动发布,且只在异常断开时触发。
- 订阅和发布方式与普通消息一致,但只能在连接时设定,不能动态修改
2.5.7 典型配置示例
假设设备 device123
连接时设置遗嘱:
(1)遗嘱主题:devices/device123/status
(2)遗嘱内容:offline
(3)QoS:1
(4)保留:true
上线后主动发布 online
,异常离线时 Broker 自动发布 offline
。业务系统只需订阅 devices/+/status
即可实时掌握所有设备状态。
2.6 怎么调试 MQTT
2.6.1 MQTT 服务器
合宙提供了 MQTT 的测试服务器,域名为: "lbsmqtt.airm2m.com", 端口为 1884.
2.6.2 PC 模拟器
MQTTX 是一款完全免费,开源的PC端的MQTT Client,可以在如下网址下载:
https://mqttx.app/
2.6.3 硬件 MQTT 客户端
合宙的所有的模组和引擎的硬件型号,都支持基于 LuatOS 的 MQTT 3.1.1,并且提供了详细的 Demo 代码和教程,可以访问 docs.openluat.com 了解。
三,LuatOS 的 MQTT 支持详解
3.1 API 介绍
参见如下链接:
https://docs.openluat.com/osapi/core/mqtt/
3.2 单链接 mqtt 代码示例
参见如下链接:
https://docs.openluat.com/air780epm/luatos/app/socket/mqtt/
3.3 多了解 mqtt 代码示例
参见如下链接:
https://docs.openluat.com/air780epm/luatos/app/socket/mqtt/
3.4 语音对讲系统代码示例
参见如下链接:
https://docs.openluat.com/air780epm/luatos/app/socket/mqtt/
四、LuatOS MQTT 常见的问题
4.1 clientid 一定要在 broker 设置好,避免鉴权不通过;
4.2 用户名和密码,一定要在 broker 设置好,避免鉴权不通过;
4.3 QOS 通常设置为 1,不要用 0 和 2。
4.4 两个设备如果具有两天的 cliendid, 其中一个设备会被 Broker 踢下线。