本文介绍了我在使用 ESP8266 接入 OneNET 实现数据点上报时遇到的坑和解决方法。
通过本文你可以掌握如何使用 Lua 实现向 OneNET 上报数据点。

OneNET MQTT 接入协议说明

在 OneNET 中定义了系统 topic 实现了数据点上报功能,详情参见 OneNET 文档中心的设备终端接入协议-MQTT

根据协议,在给平台使用 publish 报文上传数据点时,报文需包含 VariableHeader Payload Qos 三段。其中 VariableHeader 和 Qos 基本不需要额外处理,直接发送即可。

NodeMCU MQTT client 功能说明

在 NodeMCU 中带有了 MQTT 客户端,在使用之前需提前编译到固件中。

它提供了一个方法 mqtt.client:publish() 来发送一条信息。

mqtt.client:publish()
功能:发送一条消息。
句法:
mqtt:publish(topic, payload, qos, retain[, function(client)])
参数:
topic:要发布到的主题
message:要发送的消息 (缓存或字符串)
qos:QoS 等级
retain:保留旗帜
function(client):订阅成功时触发的可选回调
返回值:
True:成功
False:失败

使用发布消息的方法,可以实现数据点的上报。结合平台协议,topic 是“dp”,qos 是 0,retain 是 0,就差 message 了,我们知道这里的 message 应该就是 OneNET 协议中提到的 Payload。

如何拼接 Payload(有坑)

#1

例如使用 Type3 上报数据点,其 Payload 就应该是数据点类型:type=3 占用一个 Byte,JSON 字符串长度,占用两个 Byte,JSON 字符串占用 n 个 Byte。

#2

一开始我就栽在位运算这里了,不知道 Lua 怎么算。最后查了一下文档,发现新版中已经支持位运算了,在 bit 模块中。

按照文档一步一步来吧,我完成了如下的方法。提醒一下,以下方法用到了 NodeMCU 中的 bit 模块,请提前编译到固件中。

1
2
3
4
5
6
7
8
function onenetstr(json)
buf = {}
buf[0] = 0x03 -- Byte1 Type=3
jsonlength = string.len(json)
buf[1] = bit.rshift(bit.band(jsonlength, 0xFF00), 8) -- Byte2 固定两字节长度高位字节
buf[2] = bit.band(jsonlength, 0xFF) + 1 -- 固定两字节长度低位字节
return buf[0]..buf[1]..buf[2]..json -- 返回
end

看起来没啥问题,写进 ESP8266 试试。

#3

MQTT 离线了,平台也没有显示刚才我推上去的数据流。莫非平台拒绝了我的数据,并且和我断开了连接?

#4

OneNET 提供了调试程序,先用调试程序上报一下试试?调试程序下载:Mqtt-device

#5

OK,没有任何问题。

PayloadPublish{
payload_= 0x03 0x00 0x15 0x7B 0x22 0x52 0x65 0x6C 0x61 0x79 0x5F 0x73 0x74 0x61 0x74 0x75
0x73 0x22 0x3A 0x22 0x6F 0x6E 0x22 0x7D
}
MQTT:0x30 0x1D 0x00 0x03 0x24 0x64 0x70 0x03 0x00 0x15 0x7B 0x22 0x52 0x65 0x6C 0x61 0x79 0x5F 0x73 0x74 0x61 0x74 0x75 0x73 0x22 0x3A 0x22 0x6F 0x6E 0x22 0x7D
Send One Packet Succ:

借助调试信息,我们发现了这个调试程序给平台发送了以上信息。但是我并看不懂。。

#6

最后借助解析程序,我们把这段信息翻译了一下。

#7

那到底我这个 ESP8266 给平台发了点啥,平台不收呢?我决定抓包看看。

首先我笔记本起了一个热点,只被测试用的 ESP8266 连接上,使用 Wireshark 软件监听热点绑定的网卡。

【很遗憾的是这里图丢了】

破案了,TopicNameLen 算的不对啊,但是这个并不是我手工算的,是哪里出现偏差了呢?经过对比,在 JsonStr 段,我发现我发出去的这个和调试程序差一个 0x0d,噫,就是差一个 \r。

果断给程序加回去。最终程序如下:

1
2
3
4
5
6
7
8
function onenetstr(json)
buf = {}
buf[0] = 0x03 -- Byte1 Type=3
jsonlength = string.len(json)
buf[1] = bit.rshift(bit.band(jsonlength, 0xFF00), 8) -- Byte2 固定两字节长度高位字节
buf[2] = bit.band(jsonlength, 0xFF) + 1 -- 固定两字节长度低位字节
return string.char(buf[0])..string.char(buf[1])..string.char(buf[2])..json.."\r" -- 返回
end

OK,通过了,平台成功存储了数据点。

#9

总结

抓包真是个好工具,要利用好。

自己个人来讲对 OneNET 的 MQTT 协议理解不到位,而且存在计算长度高低位出错的情况,不清楚发送十六进制和发送十六进制字符串有什么区别。还有就是要多参考他人成功的例子,要从中寻找经验和教训。

致谢

在调试程序过程中得到了很多朋友的帮助,在此致谢。

中国移动物联网公司:
飞哥(· F ly·,)
雪姐(沙语)
张工(童话 1107)
中移物联网 ACE 高校联盟:
熊廷宇(重庆工商大学 - 物联网工程)【文章参考】
南京-royi(Ease)