3.1.5 MQTT ========== 作者:闫国梁 最后修改时间:2020年6月12日 概述 ---- Air724模块内置MQTT协议,提供AT,LUAT,CSDK,DTU可供选择使用,接口简单使用方便。 MQTT简介 ~~~~~~~~ 1. 优势:长连接,低带宽,高可靠。 2. 实用场景:需要服务器下发消息给设备,需要及时收到。例如,远程开关,充电桩等 几个重要名词 ~~~~~~~~~~~~ 1. **username**\ 用户名,\ **password**\ 密码,\ **clientid**\ 用户标识,mqtt可以通过前两个参数保证连接的安全,通过clientid确保设备唯一性。 2. **qos**\ 消息质量,分为0,1,2三个等级,分别表示只发一次,至少收到一次和只收到一次,通过qos可以根据数据的重要性灵活选择发送方式以节省带宽和保证数据可靠。例如不重要的数据采集可使用qos0只发一次不关心服务器是否收到,重要的消息通过qos1确保能收到,付款信息危险设备开关等场景使用qos2保证数据到达的同时且不重复。 3. **host**\ 主机,\ **port**\ 端口,mqtt需要连接的服务器参数。host可以说ip或者域名。 4. **topic**\ 主题,根据主题区别消息类型和来源,主要用来分类数据。同时mqtt是发布订阅模型,topic是发布和订阅者通信的重要通道。 5. **payload**\ 消息内容,发布和订阅的具体数据。 6. **retain**\ 保留消息,保留消息是一条将保留标志(retained flag)置为true的普通MQTT消息。broker会针对主题依照QoS级别保留最后一条保留消息,当订阅者订阅主题时会立即收到保留消息。broker仅为每个主题保留一条保留消息。 AT方式 ------ 连接网络 -------- 开机 ~~~~ 通过拉低powerkey2秒进行开机,开机以后通过串口循环发送AT直到收到OK,如果90秒没有收到OK请拉低 RESET_IN_N 引脚 150ms 以上。或使用其他方法见\ `开关机 `__\ 章节 查询卡状态 ~~~~~~~~~~ AT+CPIN?查询卡状态,直到收到+CPIN: READY,如果10s内没有收到建议重启模块 查询网络注册情况 ~~~~~~~~~~~~~~~~ AT+CGATT?查询是否注册网络收到+CGATT: 1值是1即为注册成功,正常情况下注册时间不会超过两分钟,如果超过两分钟没有注册可以进入飞行模式五秒后退出再查询,或者直接重启模块。 激活网络 ~~~~~~~~ **AT+CSTT**\ 配置网络,非私有APN以外Cat1的固件支持根据卡自动配置APN,直接输入AT+CSTT即可,模块会按照自动获取的APN设置CSTT的APN。 **AT+CIICR**\ 激活网络,在IP START的状态使用AT+CIICR激活网络,激活以后通过\ **AT+CIFSR**\ 查询是否获取IP,如果成功就可以开始配置TCP连接了,如果不成功使用AT+CIPSHUT关闭移动网络,从AT+CSTT重新进行。 连接服务器 ---------- 设置 MQTT 相关参数:\ **AT+MCONFIG** +----------+------------------+------------------+------------------+ | 命令类型 | 语法 | 返回 | 说明 | +==========+==================+==================+==================+ | 设置命令 | AT+MCONFIG=[,, | OK ERROR | 正常 | | | [,,, ,]] | | 返回输入格式有误 | +----------+------------------+------------------+------------------+ | 测试命令 | AT+MCONFIG=? | +MCONFIG: | | | | | [,,[,(0-2),(0,1 | | | | | ),,]]OK | | +----------+------------------+------------------+------------------+ 参数定义 +-------+-------------------+------+----------------------------------+ | 参数 | 定义 | 取值 | 对取值的说明 | +=======+===================+======+==================================+ | | client identity | 字 | 最大 256 | | | | 符串 | 个 | | | | | 字节。可以用"“括住,也可以不用”" | +-------+-------------------+------+----------------------------------+ | | username to login | 字 | 最大 256 | | | server | 符串 | 个 | | | | | 字节。可以用"“括住,也可以不用”" | +-------+-------------------+------+----------------------------------+ | | password to login | 字 | 最大 256 | | | server | 符串 | 个 | | | | | 字节。可以用"“括住,也可以不用”" | +-------+-------------------+------+----------------------------------+ | | Quality of | 012 | 见前文说明 | | | Service for will | | | | | message | | | +-------+-------------------+------+----------------------------------+ | | retain flag | **0* | **the server will restore the | | | | *\ 1 | will meassge and its QoS**\ the | | | | | server must not restore the will | | | | | meassge and must not remove or | | | | | replace any existing retained | | | | | message | +-------+-------------------+------+----------------------------------+ | | the topic of the | 字 | 最大 256 | | | will meassage | 符串 | 个 | | | | | 字节。可以用"“括住,也可以不用”" | +-------+-------------------+------+----------------------------------+ | | the will message | 字 | 最大 1360 | | | content | 符串 | 个 | | | | | 字节。可以用"“括住,也可以不用”" | +-------+-------------------+------+----------------------------------+ **PS:**\ 说明一下will_retain这个参数 终端设备publish消息时,如果retain值是true,则服务器会一直记忆,哪怕是服务器重启。因为会本地持久化。 如果服务器接收到终端publish某主题的消息,payload为空且retain值是true,则会删除这条持久化的消息。 如果服务器接收到终端publish某主题的消息,payload为空且retain值是false,则不会删除这条持久化的消息。 will_topic和will_message是遗嘱相关。 建立 TCP 连接:\ **AT+MIPSTART** 语法规则: 普通链接: AT+MIPSTART=, ssl连接:AT+SSLMIPSTART=, 返回OK表示成功,返回ERROR表示格式错误。 发送完这条指令后会有一些列URC上报。 1)单链接(AT+CIPMUX=0) 如果链接成功地建立,则上报: CONNECT OK 如果链接已经存在,则上报: ALREADY CONNECT 如果链接失败,则上报: STATE: CONNECT FAIL 2) 多链接(AT+CIPMUX=1) 如果链接成功地建立,则上报: 7,CONNECT OK 如果链接已经存在,则上报: ALREADY CONNECT 如果链接失败,则上报: 7,CONNECT FAIL **客户端向服务器请求会话连接:AT+MCONNECT** +----------+------------------+------------------+------------------+ | 命令类型 | 语法 | 返回 | 说明 | +==========+==================+==================+==================+ | 设置命令 | AT+MCONNECT=, | OK | 设置成功 | +----------+------------------+------------------+------------------+ | 测试命令 | AT+MCONNECT=? | +MCONNECT:( | 测 | | | | 0-1),(1-65535)OK | 试命令的返回的是 | | | | | 和 的取值范围 | +----------+------------------+------------------+------------------+ | URC | 设置命令设置 | | | | | 成功,返回OK后, | | | | | 后续会根据连接情 | | | | | 况自动上报URC。 | | | | | 如果连接成功 | | | | | 则返回:CONNACK | | | | | OK | | | | | 如果连接失 | | | | | 败则返回:ERROR | | | +----------+------------------+------------------+------------------+ clean_session是清除会话标志取值为0或1 keepalive是心跳时间取值1-65535单位秒 发布消息 -------- 发布消息:AT+MPUB ======== ============ =============== ===== 命令类型 语法 返回 说明 ======== ============ =============== ===== 设置命令 AT+MPUB=,, , OK qos=0 \ OKPUBACK qos=1 \ OKPUBRECPUBCOMP qos=2 \ ERROR 失败 ======== ============ =============== ===== 参数定义: +------+--------+---+------------------------------------------------+ | 参数 | 定义 | 取 | 取值说明 | | | | 值 | | +======+========+===+================================================+ | | 消 | 0 | 只发一次 | | | 息质量 | | | +------+--------+---+------------------------------------------------+ | | | 1 | 最少到达一次 | +------+--------+---+------------------------------------------------+ | | | 2 | 只有一次 | +------+--------+---+------------------------------------------------+ | | 消息保 | 0 | 不保留 | | | 留标志 | | | +------+--------+---+------------------------------------------------+ | | | 1 | 保留 | +------+--------+---+------------------------------------------------+ | | 主题 | 字 | 最大 256 个字节。可以用"“括住,也可以不用”" | | | | 符 | | | | | 串 | | +------+--------+---+------------------------------------------------+ | | 消 | 字 | 最大 1360个字节。可以用"“括住,也可以不用”" | | | 息内容 | 符 | 注:消息中内嵌的双引号请用\22 表达;控制字符 | | | | 串 | :raw-latex:`\r(`0x0D)请用\0D | | | | | 表达;控制字符:raw-latex:`\n`(0x0A) 请用\0A | | | | | 表达 如果是 MCU 发消息,请用\22,\0D,\0A 来 | | | | | 表达,即:raw-latex:`\需要转义成`\\\\ | +------+--------+---+------------------------------------------------+ 订阅消息 -------- **订阅主题:AT+MSUB** +----+-------------+--------+-----------------------------------------+ | 命 | 语法 | 返回 | 说明 | | 令 | | | | | 类 | | | | | 型 | | | | +====+=============+========+=========================================+ | 设 | AT+MSUB=, | OK | 成功 | | 置 | | SUBACK | | | 命 | | | | | 令 | | | | +----+-------------+--------+-----------------------------------------+ | | | ERROR | 失败 | +----+-------------+--------+-----------------------------------------+ | | | | 当输入设 | | | | | 置命令成功以后,后续会有相应的URC上报。 | | | | | 当AT+MQTTMSGSET=0: | | | | | +MSUB:,,当AT+MQTTMSGSET=1: +MSUB: | +----+-------------+--------+-----------------------------------------+ 参数定义: +------+----------+---------+---------------------------------------+ | 参数 | 定义 | 取值 | 取值说明 | +======+==========+=========+=======================================+ | | 主题 | 字符串 | 最大 256 | | | | | 个字节。可以用"“括住,也可以不用”" | +------+----------+---------+---------------------------------------+ | | 消息质量 | 0 1 2 | 同前文 | +------+----------+---------+---------------------------------------+ | | 数据长度 | 0到1360 | 单位:字节 | +------+----------+---------+---------------------------------------+ | | 消息内容 | 字符串 | 最大1360字节 | +------+----------+---------+---------------------------------------+ | | 消息编号 | 0到3 | 当订阅方式设置为缓存模 | | | | | 式的时候收到消息会上报一条URC+MSUB: | +------+----------+---------+---------------------------------------+ **设置订阅消息的打印模式:AT+MQTTMSGSET** 命令AT+MQTTMSGSET=成功返回OK失败返回ERROR,mode的取值是0或1,0为主动上报到串口。有新订阅消息时,上报的 URC 为: +MSUB:,,;1为缓存模式。有新订阅消息时,上报的 URC 为: +MSUB: 然后用 AT+MQTTMSGGET 来读消息。 **打印收到的所有的订阅消息:AT+MQTTMSGGET** +---+------+-----------------------------+-----------------------------+ | 命 | 语法 | 返回 | 说明 | | 令 | | | | | 类 | | | | | 型 | | | | +===+======+=============================+=============================+ | 执 | A | [+MSUB: ,,] [+MSUB: ,,] | 执行命令将会打印收到的保 | | 行 | T+MQ | [+MSUB: ,,] [+MSUB: ,,]OK | 存在cache中的主题订阅消 | | 命 | TTMS | | 息。执行完以后, | | 令 | GGET | | 将会变成invalid。 | +---+------+-----------------------------+-----------------------------+ | 查 | AT | +MQTTMSGGET:0, | | | 询 | +MQT | +MQTTMSGGET:1, | | | 命 | TMSG | +MQTTMSGGET:2, | | | 令 | GET? | +MQTTMSGGET:3,OK | | +---+------+-----------------------------+-----------------------------+ | 测 | AT+ | OK | | | 试 | MQTT | | | | 命 | MSGG | | | | 令 | ET=? | | | +---+------+-----------------------------+-----------------------------+ 注意:当 AT+MQTTMSGSET=1,执行命令可以打印订阅消息。一次最多打印4条。如果一次上报多于4条,则打 印最新的4条,最老的那条将被覆盖。 参数定义 ==== ======== ======= =============================================== 参数 定义 取值 取值说明 ==== ======== ======= =============================================== \ 主题 字符串 最大256字节 \ 消息长度 0到1360 字节 \ 消息内容 字符串 最大1360字节 \ 消息状态 VALID 有数据, AT+MQTTMSGGET 执行模块 可以打印这些消息 \ INVALID 没有数据 ==== ======== ======= =============================================== **MQTT 消息编码格式切换:AT+MQTTMODE** 命令AT+MQTTMODE=,成功返回OK失败返回ERROR,mode的取值是0或1,0为ASCII格式,1为HEX格式。 **查询 MQTT 连接状态:AT+MQTTSTATU** 命令AT+MQTTSTATU,成功返回+MQTTSTATU :OK,失败返回ERROR。 取值说明 == ============================== 值 状态 == ============================== 0 离线 1 已经认证可以发送数据 2 未认证,需要发送 MCONNECT 命令 == ============================== LUAT方式 -------- luat连接相比AT更为简单,只需要简单的配置即可连接,还可以灵活的对数据进行处理。 需要从官网或者github下载luatask的脚本包,或者使用luatoolsv2会自动下载脚本资源,在工具根目录的:raw-latex:`\resource`\\8910_script中脚本资源会随官网同步更新,具体版本可能和本文不同,不过功能都是一致的。 文档中用到的API接口见wiki的API章节。 .. _连接服务器-1: 连接服务器 ---------- MQTT相关接口也都是面向对象的,所以首先要根据api说明创建一个对象 `mqtt.client(clientId, keepAlive, username, password, cleanSession, will, version) `__ 创建一个mqtt client实例 - 参数 +------------+--------------------------------------------------------+ | 传入值类型 | 释义 | +============+========================================================+ | string | clientId | +------------+--------------------------------------------------------+ | number | **可选参数,默认为\ ``300``**\ ,keepAlive | | | 心跳间隔(单位为秒),默认300秒 | +------------+--------------------------------------------------------+ | string | **可选参数,默认为\ ``""``**\ ,username | | | 用户名,用户名为空配置为""或者nil | +------------+--------------------------------------------------------+ | string | **可选参数,默认为\ ``""``**\ ,password | | | 密码,密码为空配置为""或者nil | +------------+--------------------------------------------------------+ | number | **可选参数,默认为\ ``1``**\ ,cleanSession 1/0 | +------------+--------------------------------------------------------+ | table | **可选参数,默认为\ ``nil``**\ ,will | | | 遗嘱参数,格式为{qos=, retain=, topic=, payload=} | +------------+--------------------------------------------------------+ | string | **可选参数,默认为\ ``"3.1.1"``**\ ,version | | | MQTT版本号 | +------------+--------------------------------------------------------+ 返回值就是一个示例。 使用\ `mqttc:connect(host, port, transport, cert, timeout) `__\ 连接服务器 - 参数 +---------+------------------------------------------------------------+ | 传入 | 释义 | | 值类型 | | +=========+============================================================+ | string | host 服务器地址 | +---------+------------------------------------------------------------+ | param | port string或者number类型,服务器端口 | +---------+------------------------------------------------------------+ | string | **可选参数,默认为\ ``"tcp"``**\ ,transport | | | “tcp”或者“tcp_ssl” | +---------+------------------------------------------------------------+ | table | **可选 | | | 参数,默认为\ ``nil``**\ ,cert,table或者nil类型,ssl证书 | | | ,当transport为“tcp_ssl”时,此参数才有意义。cert格式如下: | | | { caCert = “ca.crt”, –CA证书文件(Base64编码 | | | X.509格式),如果存在此 | | | 参数,则表示客户端会对服务器的证书进行校验;不存在则不校验 | | | clientCert = “client.crt”, –客户端证书文件(Base64编码 | | | X.509格式),服务器对客户端的证书进行校验时会用到此参数 | | | clientKey = “client.key”, –客户端私钥文件(Base64编码 | | | X.509格式) clientPassword = “123456”, | | | –客户端证书文件密码[可选] } | +---------+------------------------------------------------------------+ | number | timeout, 链接服务器最长超时时间 | +---------+------------------------------------------------------------+ - 返回值 result true表示成功,false或者nil表示失败 订阅主题 -------- **mqttc:subscribe(topic, qos)** - 参数 +---------+------------------------------------------------------------+ | 传入 | 释义 | | 值类型 | | +=========+============================================================+ | param | topic,string或者table类型,一个主题 | | | 时为string类型,多个主题时为table类型,主题内容为UTF8编码 | +---------+------------------------------------------------------------+ | param | **可选参数, | | | 默认为\ ``0``**\ ,qos,number或者nil,topic为一个主题时, | | | qos为number类型(0/1/2,默认0);topic为多个主题时,qos为nil | +---------+------------------------------------------------------------+ - 返回值 bool true表示成功,false或者nil表示失败 - 例子 :: mqttc:subscribe("/abc", 0) -- subscribe topic "/abc" with qos = 0 mqttc:subscribe({["/topic1"] = 0, ["/topic2"] = 1, ["/topic3"] = 2}) -- subscribe multi topic -------------- **mqttc:unsubscribe(topic)** 取消订阅主题 - 参数 +------------+--------------------------------------------------------+ | 传入值类型 | 释义 | +============+========================================================+ | param | topic,string或者table类型,一个主题时为 | | | string类型,多个主题时为table类型,主题内容为UTF8编码 | +------------+--------------------------------------------------------+ - 返回值 bool true表示成功,false或者nil表示失败 - 例子 .. code:: lua mqttc:unsubscribe("/abc") -- unsubscribe topic "/abc" mqttc:unsubscribe({"/topic1", "/topic2", "/topic3"}) -- unsubscribe multi topic **mqttc:receive(timeout, msg)** 接收消息 - 参数 +------------+--------------------------------------------------------+ | 传入值类型 | 释义 | +============+========================================================+ | number | timeout 接收超时时间,单位毫秒 | +------------+--------------------------------------------------------+ | string | **可选参数,默认为\ ``nil``**\ ,msg | | | 可选参数,控制socket所在的线程退出recv阻塞状态 | +------------+--------------------------------------------------------+ - 返回值 result 数据接收结果,true表示成功,false表示失败 data 如果result为true,表示服务器发过来的包;如果result为false,表示错误信息,超时失败时为“timeout” param msg控制退出时,返回msg的字符串 以demo的demo:raw-latex:`\mqtt`:raw-latex:`\sync`:raw-latex:`\sendInterruptRecv`:raw-latex:`\testMqtt`.lua为例,r就是result当退出原因是服务器下发数据时为true,其他情况均为false,当r是true的时候,data表示参数,当r为false时,data表示退出阻塞的原因,一种是timeout,一种是配置的msg ,当值为msg 的时候,param表示msg携带的参数。 .. code:: lua local mqttc = mqtt.client(misc.getImei(), 300, "user", "password") while not mqttc:connect(host, port) do sys.wait(2000) end if mqttc:subscribe(string.format("/device/%s/req", misc.getImei())) then if mqttc:publish(string.format("/device/%s/report", misc.getImei()), "test publish " .. os.time()) then while true do local r, data, param = mqttc:receive(120000, "pub_msg") if r then log.info("这是收到了服务器下发的消息:", data.payload or "nil") elseif data == "pub_msg" then log.info("这是收到了订阅的消息和参数显示:", data, param) mqttc:publish(string.format("/device/%s/resp", misc.getImei()), "response " .. param) elseif data == "timeout" then log.info("这是等待超时主动上报数据的显示!") mqttc:publish(string.format("/device/%s/report", misc.getImei()), "test publish " .. os.time()) else -- 网络链接被断开 break end end end end mqttc:disconnect() 在连接服务器成功以后,代码进入这个死循环,recv(120000, “pub_msg”)里的第一个参数表示最长阻塞时间,这个时间的主要作用是用于心跳维持连接,因为timeout退出阻塞的前提是在这个时间内没有发送和接收数据;第二个参数是控制退出的字符串,其原理类似于\ `sys.subscribe(id, callback) `__\ msg就是id,用于订阅来自其他协程的数据,发送数据的方法就是\ `sys.publish(…) `__\ 触发时rev会退出并携带参数; .. _发布消息-1: 发布消息 -------- **mqttc:publish(topic, payload, qos, retain)** 发布一条消息 - 参数 +------------+--------------------------------------------------------+ | 传入值类型 | 释义 | +============+========================================================+ | string | topic UTF8编码的字符串 | +------------+--------------------------------------------------------+ | string | payload | | | 用户自己 | | | 控制payload的编码,mqtt.lua不会对payload做任何编码转换 | +------------+--------------------------------------------------------+ | number | **可选参数,默认为\ ``0``**\ ,qos 0/1/2, default 0 | +------------+--------------------------------------------------------+ | number | **可选参数,默认为\ ``0``**\ ,retain 0或者1 | +------------+--------------------------------------------------------+ - 返回值 bool 发布成功返回true,失败返回false - 例子 :: mqttc = mqtt.client("clientid-123", nil, nil, false) mqttc:connect("mqttserver.com", 1883, "tcp") mqttc:publish("/topic", "publish from luat mqtt client", 0) 断开连接 -------- **mqttc:disconnect()** 断开与服务器的连接 - 参数 无 - 返回值 nil - 例子 :: mqttc = mqtt.client("clientid-123", nil, nil, false) mqttc:connect("mqttserver.com", 1883, "tcp") process data mqttc:disconnect() CSDK ---- DTU固件 ------- 对于只需要使用模块进行透传数据的常见,推荐使用合宙开源的dtu固件,只需要一条指令就可实现网络连接和服务器状态维护,还可使用web配置参数,方便又简单。 `相关教程 `__ SmartDTU 是集成远程物联网控制功能的固件,只需要配置几下就可以实现大部分场景所需要的功能和逻辑,方便传统业务快速联网。手册和固件下载在群文件! WEB配置:\ `http://dtu.openluat.com `__ 注意:请用chrome或firefox等兼容浏览器。如果联网请求参数失败,请把ERP账号和IMEI给我添加。SmartDTU是什么:https://luatdoc.papapoi.com/656/ B站教程:https://www.bilibili.com/video/av41012302 硬件教程:https://www.bilibili.com/video/av45341487 工具教程:https://www.bilibili.com/video/av50453083 Luat开发教程:https://www.bilibili.com/video/av50827315 看懂Luat日志:https://luatdoc.papapoi.com/400/ 源码:https://gitee.com/hotdll/iRTU