这是机器未来的第44篇文章
原文首发地址:https://blog.youkuaiyun.com/RobotFutures/article/details/126539873

文章目录
本文以实际项目为案例,描述了MQTT协议设计的实战过程。
1. 回顾topic命名规范
- 所有主题名称和主题过滤器必须至少有一个字符长,主题分级标识符’/'也算占1个字节
- 主题名称和主题过滤器区分大小写
- 主题名称和主题过滤器可以包含空格字符
- 前导或尾随“/”创建不同的主题名称或主题过滤器
- topic和/topic、topic和topic/这两对都不是相同的主题名称
- 仅包含“/”字符的主题名称或主题过滤器是有效的
- 主题名称和主题过滤器不得包含空字符 (Unicode U+0000)
- 主题名称和主题过滤器是 UTF-8 编码的字符串,它们的编码不得超过 65535 字节
2. 产品概述
产品基于ESP32系列模块开发,在大流量的应用场景下,4G终端可以通过WIFI连接热点传输,降低4G上网资费。ESP32模块与上位设备采用以太网接口进行连接。
产品大致的需求为:
- 可用手机配置WIFI接入点信息
- 可了解ESP32 WIFI链路状态
- 可配置休眠、唤醒
- 可远程重启
3. 设计先验
根据MQTT协议规范的理解,应该有如下的认知:
- MQTT自带消息QoS确认机制,应该对于实时性要求不高的消息,可以不用响应消息,因为在MQTT层已经做了QoS(QoS>=1)送达机制。
- 对于实时性要求高的机制,因为MQTT3.1.1消息没有有效期的概念,可以增加响应命令,QoS设计为0,相当于放弃MQTT的传达机制,由应用层来管理消息的超时;
- MQTT遗言机制Will特别方便应用于MQTT链路异常断开的场景,告知其它订阅客户端其已断开,通过Will Retain机制可以让将来的订阅客户端都能收到其已异常断开的消息;
- MQTT的Retain机制可以保留最新一条数据给将来的订阅者,对于发布端来说,可以结合定时上报和变化上报,保证给订阅者永远都是最新的消息,包括订阅者异常断开重连后,仍然可以拿到最新的数据,而且这个定时时间不用很短。
- MQTT的消息负载无需满足UTF-8编码字符串的要求,使用二进制编码,可填充任意数据。
4. 设计细则
4.1 协议标准
采用MQTT3.1.1版本
4.2 TOPIC定义规则
- 采用/topic/前缀(对应/topic和topic是两个不同的主题的认知)
- 全部采用大写字母(对应MQTT主题名称区分大小写)
- 采用JSON字符串编码传输
- 对于交互比较频繁,数据量比较大的业务来说,可以直接使用二进制编码,但考虑到将来的扩展及存在多个业务方的情况,建议使用TAG-LEN-VALUE编码格式,例如\x91\x02\x02\x00\x01,TAG:9102h,Len:02,Value:0002h,这样的好处是使用统一的编码规则,将来协议版本更新时可以向下兼容以前的协议,上位设备对不识别的字段不予处理即可,而不会导致整个报文无法解析。像TAG-LEN-VALUE这种编码数据也被称为结构化数据,特别便于将来扩展新功能。
- 另外一种扩展方便的编码方式就是JSON字符串,JSON字符串也是一种结构化数据编码方式,将来添加新字段也不会影响到原来设备的协议解析。
- KeepAlive=300,5分钟的心跳周期
- MQTT支持TLS加密传输,本项目将采用TLS加密传输,因此不再考虑数据的校验问题。
4.3 TOPIC主题定义
| SN. | TOPIC | SUBSCRIBER | PUBLISHER | QoS | Retain |
|---|---|---|---|---|---|
| 1 | /TOPIC/OBE_WIFI/UP/DEVICE_INFO | UpperDev | OBE_WIFI | 1 | 1 |
| 2 | /TOPIC/OBE_WIFI/UP/STATUS | UpperDev | OBE_WIFI | 1 | 1 |
| 3 | /TOPIC/OBE_WIFI/UP/WILL | UpperDev | OBE_WIFI | 1 | 1 |
| 4 | /TOPIC/OBE_WIFI/DOWN/CONTROL/RESTART | OBE_WIFI | UpperDev | 0 | 0 |
| 5 | /TOPIC/OBE_WIFI/DOWN/CONTROL/KEEPAWAKE | OBE_WIFI | UpperDev | 0 | 0 |
| 6 | /TOPIC/OBE_WIFI/DOWN/OTA | OBE_WIFI | UpperDev | 0 | 0 |
| 7 | /TOPIC/OBE_WIFI/UP/RESPONSE | UpperDev | OBE_WIFI | 0 | 0 |
主题描述:
- DEVICE_INFO服务时为了上报OBE_WIFI的系统信息,主要包含描述设备的固定信息
- STATUS服务是为了上报OBU-WIFI的运行状态数据
- WILL服务是为了上报OBU-WIFI异常断开时通知其它订阅客户端它已断开
- RESPONSE服务是为了响应实时服务,例如CONTROL或OTA下发的时效性,RESPONSE消息通过body域中的消息流水号Message_id来绑定下发报文
- CONTROL服务是为了远程操作OBE_WIFI设备,例如重启、休眠、唤醒等
- OTA服务时为了通知OBE_WIFI设备升级及提供升级包信息
注意:
- 因为CONTROL、OTA、RESPONSE更加注重时效性,因此QoS配置为0,其余命令配置为1。
- TOPIC中区分UP和DOWN的目的是为了结合主题过滤器#使用,例如UpperDev订阅/TOPIC/OBE_WIFI/UP/#即可订阅所有OBE-WIFI发布的主题:
- Retain标志设定后,服务器会保留最后一条最新的消息,将来新的订阅者或重连的订阅者可以查到最新的历史。结合自动上报和变化上报,订阅者总是能够拿到最新的数据。
4.4 Topic负载消息组成定义
{消息头, “body”:{消息体}}
4.4.1 消息头定义
| No. | 数据项 | Key | Value范例 | 描述 |
|---|---|---|---|---|
| 1 | 命令字 | cmd | dev_attr_upload | 传入String型,详见表3 |
| 2 | 应用ID | app_id | 12564 | 传入Number型,应用ID在设计时定义各方订阅者/发布者ID |
| 3 | 协议版本 | pro_ver | 1.0 | 传入String型, 便于将来扩展 |
| 4 | 时间戳 | timestamp | 2022-03-29 00:00:00 | 传入String型 |
| 5 | 消息流水号 | msg_id | 234 | 传入Number型,按照消息递增的值,上限为65535,超过则从0递增,唯一表示消息ID |
4.4.2 消息体定义
4.4.2.1 DEVICE_INFO服务
TOPIC:/TOPIC/OBE_WIFI/UP/DEVICE_INFO
QoS=1, Retain=1
| No. | 数据项 | Key | Value范例 | 描述 |
|---|---|---|---|---|
| 1 | 设备号 | device_id | 91234567 | 传入string型 |
| 2 | 软件版本 | sw_ver | 1.1 | 传入string型 |
| 3 | 硬件版本 | hw_ver | 1.0 | 传入string型 |
| 4 | WIFI MAC | wifi_mac | 65-7F-36-25-12-89 | 传入string型,跟踪ESP32硬件编号 |
| 5 | WIFI固件版本 | wifi_firmware_ver | 1.36.25 | 传入string型,量产后批次采购模块固件版本不一致,便于分析问题 |
| 6 | WIFI无线认证版本 | wifi_auth_ver | 1.3 | 传入string型,量产后批次采购模块固件版本不一致,便于分析问题 |
| 7 | 国家码 | country_code | 86 | 传入string型,国际客户分析 |
| 8 | 以太网MAC | eth_mac | 65-7F-36-25-12-8A | 传入string型,跟踪以太网硬件编号 |
4.4.2.2 STATUS服务
TOPIC:/TOPIC/OBE_WIFI/UP/STATUS
QoS=1,Retain=1
KeepAlive=300,STATUS消息采用定时上报和变化上报机制,定时上报周期取240s上报1次
4.4.2.3 WILL服务
TOPIC:/TOPIC/OBE_WIFI/UP/WILL
QoS=1,Retain=1
无消息体
4.4.2.4 RESTART服务
Topic:/TOPIC/OBE_WIFI/DOWN/RESTART
QoS=0,Retain=0
无消息体,RESPONSE服务,响应执行结果
4.4.2.5 KEEPAWAKE服务
略。
4.4.2.6 RESPONSE服务
Topic:/TOPIC/OBE_WIFI/UP/RESPONSE
QoS=0,Retain=0
| No. | 数据项 | KEY | Value范例 | 描述 |
|---|---|---|---|---|
| 1 | 下发消息流水号 | msg_id | 256 | 传入Number型,来源于下发消息头中的消息流水号 |
| 2 | 下发命令字 | cmd | cmd_down_restart | 传入string型,来源于下发消息头中的命令字 |
| 3 | 结果 | result | 0 | 传入Number型,0-成功,1-失败 |
| 4 | 结果描述 | comment | “NOT SUPPORT” | 传入string型,描述失败原因,成功可不存在 |
5. 总结
在设计MQTT协议的过程中注意事项其实还是挺多的,根据应用场景,QoS、Retain、Will的使用也很巧妙,结构化数据的编码方式为将来的功能扩展提供了可能性。
《MQTT快速入门系列》快速导航:
- 【MQTT从入门到提高系列 | 01】从0到1快速搭建MQTT测试环境
- 【MQTT从入门到提高系列 | 02】MQTT3.1.1TLS加密传输
- 【MQTT从入门到提高系列 | 03】一文掌握MQTT3.1.1协议框架
- 【MQTT从入门到提高系列 | 04】MQTT应用协议之CONNECT
- 【MQTT从入门到提高系列 | 05】MQTT3.1.1之PUBLISH发布工作流
- 【MQTT从入门到提高系列 | 06】MQTT3.1.1之SUBSCRIBE订阅工作流
- 【MQTT从入门到提高系列 | 07】MQTT3.1.1之链路保活及断开
- 【MQTT从入门到提高系列 | 08】MQTT3.1.1主题Topic详解
- 【MQTT从入门到提高系列 | 09】WireShark抓包分析MQTT报文
写在末尾:
- 博客简介:专注AIoT领域,追逐未来时代的脉搏,记录路途中的技术成长!
- 专栏简介:从0到1掌握MQTT分布式协议。
- 面向人群:零基础编程爱好者
- 专栏计划:接下来会逐步发布跨入人工智能的系列博文,敬请期待
- Python零基础快速入门系列
- 快速入门Python数据科学系列
- 人工智能开发环境搭建系列
- 机器学习系列
- 物体检测快速入门系列
- 自动驾驶物体检测系列
- …

本文以ESP32模块的实际项目为例,详细介绍了MQTT协议的设计流程,包括主题命名规范、产品需求分析、设计原则及具体实现细节等内容。重点讨论了QoS、Retain和Will等特性在不同场景下的应用。
1529





