设备 MQTT 接入
设备 MQTT 接入
什么是 MQTT ?
MQTT 全称是 Message Queuing Telemetry Transport,它是一种基于消息队列的轻量级应用层通信协议,实现了消息发布和订阅。设备可以作为客户端的形式通过它来发布和接收消息,实现数据上报和实时控制。
MQTT 是一种简单的消息传递协议,设计用于具有低带宽的受限设备。因此,它是物联网设备接入的完美解决方案。
ThingsCloud 支持哪些设备?
ThingsCloud 平台提供了标准的 MQTT 接入协议,支持 MQTT v3.1/v.5,任何支持 MQTT 协议的设备都可以通过相应的 MQTT 客户端代码接入平台。
那么,设备要支持 MQTT,有哪些常见的方式呢?通常不外乎以下几种:
- 各种单片机或嵌入式系统,使用支持 MQTT 或 TCP 协议的 2G/3G/4G/5G 、 NBIoT 等无线通信模组,通过 AT 指令、串口透传、SDK 等方式快速接入平台。
- 本身支持 OpenCPU 的通信模组,例如:ESP32、Air724UG 等,通过模组固件二次开发实现接入平台。
- 无法直接连网的传感器、IO 控制器、PLC 等设备,通过支持 MQTT 的网关/DTU 接入平台。
我们也会不断提供各种常见设备的接入示例。
设备 MQTT 接入点
了解了基本概念后,我们开始学习如何让设备通过 MQTT 接入 ThingsCloud 平台,并和平台进行各类消息通信。
提示
在此之前,我们在 快速上手 的章节中简单介绍了在控制台创建设备,并完成了一些基本的 MQTT 通信,如果您没有阅读,建议您大概了解一下。
ThingsCloud 公有云支持全球多个区域,并随着设备接入量不断扩张。每个接入的设备有专用的 MQTT 接入点,获得设备 MQTT 接入点的正确方式,就是在控制台进入设备详情的【连接】页面,复制该设备的 MQTT 接入点。
我们提供多种类型的 MQTT 接入点和认证方式,如下:
接入类型 | 用途 | 接入点示例 |
---|---|---|
MQTT | 普通认证方式,适合大多数普通设备或资源受限设备 | mqtt://<endpoint> :1883 |
MQTT over TLS | X.509 认证方式,适合对通信安全要求严格的设备 | mqtts://<endpoint> :1884 |
MQTT 身份认证
设备通过 MQTT 协议连接平台时,需要完成基于 MQTT 的身份认证,平台支持以下认证方式。
普通认证方式
对于普通认证方式,在 MQTT 连接时,使用基于 username/password 的认证方式,需要用到设备的普通证书,如下:
MQTT 连接参数 | 值 | 说明 |
---|---|---|
username | AccessToken | 设备创建后自动生成,每个设备唯一,量产设备可通过 API 自动获取 AccessToken ,实现一型一密。 |
password | ProjectKey | 项目创建后自动生成,不支持修改。 |
clientId | 空或任意 | 不对 clientId 做任何限制,可随意填写。 |
在 设备详情页 > 连接 中可以复制以上标识。
这里以 MQTT.fx 为例,连接参数填写大致如下图:
提示
请确保以上标识只用于设备固件开发,烧录到设备 ROM/Flash 中,避免泄露。
对于量产阶段的设备,无需手动复制每个设备的 AccessToken 进行单独烧录,可采用 一型一密 的方式,通过 API 自动获取 AccessToken,以及自动创建设备。
需要注意的是,ThingsCloud 对同一个设备身份信息只支持一个 MQTT 连接,也就是说,如果在两个或多个物理设备中,使用同样的 username/password 身份信息连接平台,即便 clientId 使用不同的字符串,平台仍然将这些连接视为同一个设备,这会导致后一个设备连接成功后会顶掉之前的设备连接。
X.509 TLS 认证方式
在一些对通信安全要求严格的物联网领域,比如智能门锁、电表、水表、燃气表等,您可以使用基于 X.509 TLS 的 MQTT 安全认证方式,我们目前只在 ThingsCloud 专有区和私有区中支持该认证方式。
更进一步的物联网安全措施,可以在设备端集成 SE 安全芯片,或使用内置 SE 安全芯片的通信模组,实现设备和平台双向认证。ThingsCloud 对智能设备厂商的私有区服务提供 SE 解决方案。
MQTT 主题一览
ThingsCloud 作为物联网 PaaS 平台,对设备 MQTT 接入提供了内置的访问协议规范,让设备和平台的消息通信更加有章可循,大大简化了物联网项目的开发难度,缩短了产品的开发周期。
不同于普通的 MQTT 使用方式,我们提供了标准的内置主题,这足以实现绝大多数的物联网应用场景。
而对于另一些个性化的消息通信场景,我们也提供了灵活的自定义主题,您完全创建自己的主题,并配合包括云函数在内的规则引擎,实现任何消息通信需求。
发布主题
以下主题用于 设备向平台发布消息。
消息类型 | 主题 |
---|---|
设备上报属性值 | attributes |
设备获取当前属性值 | attributes/get/<id> |
设备上报事件 | event/report/<id> |
设备回复命令 | command/reply/<id> |
设备自定义数据上报 | data/<identifier> |
设备上报离线主题 | attributes/series |
请注意:
- 以上的主题只能用于设备端发布消息,不能用来订阅。如需在应用端接收设备发布的消息,请使用 MQTT应用端订阅 或 规则转发,更多介绍请浏览 API。
- 设备端在发布(publish)消息时,QoS 请使用 0,不要使用 1 或 2。ThingsCloud 不支持 QoS,以免通信模组重复发送。
订阅主题
以下主题用于 设备接收平台下发的消息。
消息类型 | 主题 |
---|---|
接收属性上报的响应 | attributes/response |
接收属性获取的响应 | attributes/get/response/+ |
接收下发的属性 | attributes/push |
接收事件上报的响应 | event/response/+ |
接收下发的命令 | command/send/+ |
接收命令回复的响应 | command/reply/response/+ |
接收自定义数据下发 | data/<identifier> /set |
接收离线属性上报的响应 | attributes/series/response |
提示
以上自定义数据相关主题中的 identifier
,是指自定义数据流的标识符。详细介绍请浏览 自定义数据流
请注意,以上主题只能用于设备端订阅,不能用来发布。如需在应用端向设备下发消息,可使用以下方式:
- 在 ThingsCloud 控制台操作,包括设备详情页、看板、大屏等
- 在用户应用中操作,包括 ThingsX 、ThingsXS、 ThingsPad
- 使用 应用端设备访问 API
- 使用 项目 API
提示
您可能已经发现,所有的主题中并没有出现设备标识,这也是 ThingsCloud 的不同之处。
当设备连接 ThingsCloud 平台后,会通过独有的虚拟映射方式,拥有独立的主题命名空间,所以每次发布消息或订阅消息时,主题名称非常简洁,大大简化了开发和配置过程。
下面我们会逐个介绍每个发布主题和订阅主题的使用方法。
先看看最佳实践教程?
- 【设备 MQTT 接入 ThingsCloud】如何连接云平台?🎦
- 【设备 MQTT 接入 ThingsCloud】如何上报属性到云平台?🎦
- 【设备 MQTT 接入 ThingsCloud】如何实时接收云平台下发的属性?🎦
- 【设备 MQTT 接入 ThingsCloud】如何从云平台获取当前属性?🎦
- 【设备 MQTT 接入 ThingsCloud】如何上报事件到云平台?🎦
- 【设备 MQTT 接入 ThingsCloud】如何实时接收云平台下发的命令?🎦
- MQTTX 模拟设备接入 ThingsCloud
- 在线设备调试,向设备实时下发各类消息
设备上报属性
当设备自身的属性发生变化时,通过向平台上报属性,实现平台设备属性的及时更新,以及应用系统的设备数据实时更新。
设备发布消息
设备上报属性使用以下主题:
attributes
消息内容必须是 JSON
键值对格式,否则会被平台拒绝接收,甚至断开连接。
一个简单的属性上报消息如下:
{
"temperature": 28.5,
"light": 2000,
"switch": true
}
设备发布以上消息后,控制台的设备详情页会实时更新显示设备属性的最新值。
提示
ThingsCloud 控制台通过 MQTT Websockets 方式订阅设备属性的实时更新,您可以在不刷新页面的情况下实时查看设备属性值的变化。有时网络原因导致 Websockets 连接中断,您只需刷新页面即可恢复。
我们也将这种 Websockets 订阅方式开放给应用开发,如果您的应用基于 ThingsCloud 平台,便可以实现页面实时更新。
如果设备按一定时间间隔上报属性,就实现了一个基本的数据采集上报功能。
平台会自动保存这些属性的时序数据,如下图:
对于 number
类型的属性,平台还提供了时序图表,方便浏览历史曲线。
当然,您也可以使用如下的 JSON
消息格式来上报属性数据:
{
"sensor_1": {
"temperature": 28.5,
"light": 2000,
"switch": true
},
"sensor_2": {
"temperature": 27.1,
"light": 1800,
"switch": false
}
}
但是对于这种将属性值放入嵌套结构中的消息,平台并不会直接保存嵌套结构内的属性时序数据,而只保存 sensor_1
这样的结构体属性值,供设备和应用查询。
如果设备一定要按这样的嵌套格式上报属性,或者有些时候您无法改变设备的上报规则,有没有办法让平台也生成属性时序数据呢?答案是肯定的,那就是利用平台的消息规则,自动将嵌套结构中的属性数据,复制到 JSON
消息的顶层结构中,这样就会自动被平台记录时序数据。具体的做法请参考后边的章节。
设备接收消息
如果想知道属性上报是否被平台成功接收,可以订阅如下主题:
attributes/response
请注意,订阅主题只需要在设备 MQTT 成功连接平台后执行一次即可。设备上报属性后,可通过该主题立即接收来自平台的响应消息。
如果平台接收成功,响应消息如下:
{
"result": 1,
"ts": 1609143039050
}
如果平台未成功接收,响应消息中会包含错误原因,常见的错误原因如下:
- 属性上报的消息格式错误
{
"result": 0,
"message": "Invalid attributes data format",
"errcode": 402
}
- 属性上报频率过快。
{
"result": 0,
"message": "Device message frequency is too high, please wait for a moment",
"errcode": 403
}
- 已达到日消息量上限
{
"result": 0,
"message": "Device messages exceeds today maximum limit",
"errcode": 413
}
- 属性上报不符合设备类型的功能定义
{
"result": 0,
"message": "Invalid attributes: device type attributes define check failed",
"errcode": 406
}
设备获取属性
对于连网设备,有些属性是在云端进行设置下发的。当设备重启后,需要立即从平台获取属性当前值,实现设备初始化。
设备发送消息
当设备希望从平台获取属性当前值时,发送消息到如下主题:
attributes/get/<id>
<id>
可以使用任意不超过 6 位的整数,例如:
attributes/get/1000
消息内容格式如下:
{
"keys": []
}
这表示获取所有属性,也可以指定个别属性,如下:
{
"keys": [
"temperature",
"humidity"
]
}
设备接收消息
请确保设备已经订阅了如下主题:
attributes/get/response/+
如果设备不支持 +
MQTT 通配符,也可以订阅如下主题:
attributes/get/response/<id>
这里的 <id>
要和前边发送消息中的 <id>
一致,否则无法接收响应消息。
当设备发送获取属性的消息后,便会通过以上订阅主题,收到如下的响应消息:
{
"result": 1,
"attributes": {
"temperature": 34.2,
"humidity": 67
}
}
设备接收云端下发属性
除了设备主动获取属性以外,我们也需要让设备可以实时接收平台下发的属性更新。
非常简单,确保设备已订阅如下主题:
attributes/push
当平台下发属性给设备时,设备会通过以上订阅主题,立即收到 JSON
结构的消息,例如:
{
"switch": false
}
这个示例消息显然是通知设备关闭某个开关,设备通过自身的程序实现该功能后,可以接着向平台上报属性或上报事件,让平台得到确认。
那么,如何实现平台下发属性呢?我们提供两种方式:
控制台
在控制台的设备详情【属性】页面中,这种方式适合开发调试阶段或小规模项目。
应用端 API
调用平台提供的应用端 API,这种方式可以集成到任何应用系统中。
设备上报事件
前边都是属性相关的消息,这一节我们来了解一下事件。
设备发送消息
通过事件,设备可以向云端报告消息,而不需要上报任何属性。例如:设备的 AI 视觉传感器捕捉到活动人数,上报给平台。
一个事件由如下内容组成:
- 事件名称
可以理解为函数名,是事件的唯一表示。
- 事件参数
可以理解为函数的参数,是 JSON
格式的结构体,包含若干参数。
设备可以随时向平台上报事件,只需将事件消息发布到如下主题:
event/report/<id>
<id>
的取值为不超过 6 位的整数,例如:
event/report/1000
发布的事件消息格式如下:
{
"method": "{name}",
"params": {
"key1": "{value1}",
"key2": "{value2}",
...
}
}
例如前边提到的,工厂生产线上的 AI 视觉传感器捕捉到产品数量后,上报事件给平台,那么消息可能是这样的:
{
"method": "productFound",
"params": {
"count": 3,
"location": "REGION.B43",
"tags": "RED",
"detail": [
"100474",
"100475",
"100342"
]
}
}
设备接收消息
如果我们想知道事件上报是否被平台成功接收,可以订阅如下主题:
event/response/+
或者
event/response/<id>
请注意,订阅主题只需要在设备 MQTT 成功连接平台后执行一次即可。当设备上报事件后,便会通过该主题收到来自平台的响应消息,如果平台接收成功,响应消息如下:
{
"result": 1,
"ts": 1609143039050
}
值得注意的是,响应消息中的成功,只代表平台成功收到了事件上报,并不代表对事件进行业务处理的结果。
平台收到设备的事件上报后,如何处理事件呢?规则引擎便派上用场了,通过规则引擎,您可以将事件实时推送到第三方 Webhook URL,或通过规则引擎的云函数实现告警策略,当然还可以实现对其它设备的联动控制。
设备接收云端下发命令
与事件相反,命令是由平台向设备主动下发的一组消息,用来实时通知设备执行某个特定的功能。
您可以认为命令是平台向设备发起的一个远程调用(RPC)。
设备接收消息
设备在 MQTT 连接平台后,确保订阅如下主题:
command/send/+
如果设备不支持 +
MQTT 通配符,也可以订阅如下主题:
command/send/<id>
订阅成功后,设备便处于等待接收命令的状态。当平台下发命令时,设备便会收到命令消息。
命令消息的格式也是基于 JSON
的结构体,如下:
{
"method": "{name}",
"params": {
"key1": "{value1}",
"key2": "{value2}",
...
},
"id": {id}
}
您可能发现了,它和事件消息格式非常类似,只是多了一个 id
。
设备回复消息
设备收到命令消息后,通过自身的代码逻辑实现特定的功能后,可以回复命令,也可以不回复命令。
平台会自动接收回复命令,并和下发命令进行匹配,一次下发命令只能接收一次回复命令,它们的匹配正是通过使用一致的 <id>
来保证。
设备回复命令消息的格式如下:
{
"method": "{name}",
"params": {
"key1": "{value1}",
"key2": "{value2}",
...
}
}
没错,它和命令下发的消息格式一模一样,回复命令发布的主题如下:
command/reply/<id>
请注意,回复命令的主题中的 <id>
,一定要和对应的命令下发消息中的 id
保持一致。
设备自定义数据上报
详情浏览 自定义数据流。
设备接收自定义数据下发
详情浏览 自定义数据流。
设备上报离线属性数据
在某些低功耗设备的应用场景中,设备无法保持始终在线,需要在离线的状态下记录数据和时间戳,随后在设备连接平台上线后,上报这些离线数据,实现在平台对历史数据的浏览和分析。
另外,在设备网络环境条件较差时,该功能也可用于补传属性数据。
提示
设备上报离线属性数据的功能,仅在企业版及以上版本中支持。了解详情
设备发布消息
设备上报离线属性使用以下主题:
attributes/series
消息内容必须是 JSON
数组格式,并符合以下结构:
[
{
"ts": {timestamp},
"values": {
"key1": "{value1}",
"key2": "{value2}",
...
}
},
...
]
数组中的每个元素都表示一条属性历史数据,格式如下:
字段 | 数据类型 | 说明 |
---|---|---|
ts | number | UNIX 时间戳(10位秒数) |
values | object | 属性键值对。 键名必须符合属性标识符的合法格式,值必须是 number 类型。 |
提示
UNIX 时间戳(Unix Timestamp),也被称为 Unix 时间、POSIX 时间或者是 Unix 纪元时间(Unix Epoch Time)。它是从 1970 年 1 月 1 日 00:00:00 UTC(协调世界时)开始到特定时间所经过的秒数(10 位)或者毫秒数(13 位)。例如:
- 时间戳为 0 代表 1970 年 1 月 1 日 00:00:00 UTC 这个起始时刻。
- 时间戳为 1714521600 代表 2024 年 5 月 1 日 00:00:00 UTC 这个时刻,同时也是上海时区的 2024 年 5 月 1 日 08:00:00 GMT+08:00。
推荐常用的在线 UNIX 时间转换工具:
例如:设备上报一条昨天某时间点 2024-05-01 11:30:00 GMT+08:00 的温度数据,可以发布如下的消息:
[
{"ts": 1714534200, "values": { "temperature": 22.5 } },
]
如果设备需要同时上报温度和湿度数据,可以发布如下的消息:
[
{"ts": 1714534200, "values": { "temperature": 22.5, "humidity": 68.3 } },
]
也可以同时上报多条离线属性数据,例如发送如下消息:
[
{"ts": 1717171200, "values": { "temperature": 12.5, "humidity": 50.3 }},
{"ts": 1717174800, "values": { "temperature": 11.2, "humidity": 52.1 }},
{"ts": 1717178400, "values": { "temperature": 10.6, "humidity": 53.3 }},
{"ts": 1717182000, "values": { "temperature": 10.2, "humidity": 54.7 }},
{"ts": 1717185600, "values": { "temperature": 9.5, "humidity": 55.5 }},
{"ts": 1717189200, "values": { "temperature": 8.9, "humidity": 56.1 }},
{"ts": 1717192800, "values": { "temperature": 8.5, "humidity": 56.8 }},
{"ts": 1717196400, "values": { "temperature": 9.2, "humidity": 55.3 }},
{"ts": 1717200000, "values": { "temperature": 10.5, "humidity": 54.1 }},
{"ts": 1717203600, "values": { "temperature": 10.7, "humidity": 53.6 }}
]
以上示例中的 10 条属性数据,是上海时区 2024 年 6 月 1 日 00:00:00 起每小时一条的属性数据,包含温度和湿度。
上报成功后,在设备调试日志中,会陆续看到每条属性数据消息,与常规的属性上报消息不同的是,消息正文左侧有一个离线图标,如下图:
点击消息查看详情,可以看到与其它类型消息不同的时,多了一个上报时间,这就是设备上报离线属性数据时指定的时间戳 ts
。
在设备详情页的温度属性历史数据中,选定该时间段,可以看到上报的历史属性数据,如下图:
在设备详情页的湿度属性时序图表中,选定该时间段,可以看到上报的历史属性数据,如下图:
关于设备上报离线属性数据的一些注意事项:
- 上报离线属性数据的功能主要应用在低功耗设备,应尽量减少设备不必要的上报操作,以节省能源消耗和网络资源。
- 设备上报离线属性数据后,平台会将数据存储到属性历史数据中,但不会更新属性当前值。
- 设备上报的离线属性数据包含多条时,平台会按数据时间点先后顺序,依次存入属性历史数据,这可能会持续一小段时间,在设备调试日志中可以刷新查看。
- 设备上报离线属性数据,不会触发消息规则和告警规则。
对设备端的一些建议:
- 设备端应具备本地时钟或网络时间同步机制,以确保在长时间离线后,设备的时间仍然能够与标准时间保持一致,从而确保数据的时间戳准确,否则将影响数据的准确性和可靠性。
- 设备端应具备一定的本地持久化存储能力,以便在离线期间断电重启后,不会丢失之前保存的离线数据。
设备接收消息
如果想知道上报离线属性数据是否被平台成功接收,设备端可以订阅如下主题:
attributes/series/response
如果平台接收成功,响应消息如下:
{
"result": 1,
"ts": 1609143039050
}
如果平台未成功接收,响应消息中会包含错误原因,常见的错误原因如下:
- 离线属性数据的时间范围无效。离线属性数据的时间戳必须在过去 180 天到当前时间之间,且时间跨度范围不能超过 30 天。
{
"result": 0,
"message": "Invalid attributes series time range",
"errcode": 441
}
- 离线属性数据条数超出限制,单次上报离线属性数据支持最多 100 条。
{
"result": 0,
"message": "Attributes series length exceeds the maximum limit",
"errcode": 446
}
- 离线属性数据的消息格式错误
{
"result": 0,
"message": "Invalid attributes series data format",
"errcode": 402
}
- 离线属性数据的时间间隔过短。平台要求同时上报多条离线属性数据的时间戳至少间隔 60 秒。
{
"result": 0,
"message": "Invalid interval time of attributes series",
"errcode": 443
}
- 离线属性数据的时间范围内,平台已存在属性历史数据。这种情况请调整离线属性数据的时间戳,以确保在该时间范围内不存在属性历史数据。
{
"result": 0,
"message": "Data already exists in the attributes series time range",
"errcode": 442
}
- 设备上报频率过快
{
"result": 0,
"message": "Device message frequency is too high, please wait for a moment",
"errcode": 403
}
当包含多条离线属性数据时,需要间隔更长的时间,这时平台会提示需要等待的大概时间,如下:
{
"result": 0,
"message": "Device message frequency is too high, please wait for about {N} seconds",
"errcode": 403
}
- 已达到日消息量上限
{
"result": 0,
"message": "Device messages exceeds today maximum limit",
"errcode": 413
}
- 属性上报不符合设备类型的功能定义。当某条离线属性数据不符合设备类型的功能定义时,平台会拒绝该条离线属性数据的上报,并终止剩余离线属性数据的上报。
{
"result": 0,
"message": "Invalid attributes: device type attributes define check failed",
"errcode": 406
}
MQTT 调试
在接入平台的设备开发过程中,有多种方法可以帮助您快速调试,并验证消息收发通信。
MQTT 客户端工具
使用 MQTT 客户端工具,例如:
- MQTT.fx 官网