设备 MQTT 接入协议

设备 MQTT 接入协议

概述

本文主要描述了如何注册和管理云平台设备 (包括边缘结点管理).

前端设备主要通过消息订阅/发布模式以和云端消息队列进行通信

本文定义的接口主要供前端设备调用.

系统前缀

API:

mqtt(s)://{domain-name}/

版本

V2.0

作者

@author ChengZhen

修改历史

2019/2/13

  • 重写 API

2018/10/9

  • 修改注册消息内容, 添加事物描述等信息
  • 删除设备状态 API
  • 修改边缘节点管理 API 名称
  • 添加新的签名算法

MQTT API

服务器地址:

mqtt://{domain-name}:1883

管理后台地址:

http://{domain-name}:18083

NAME VALUE NOTE
Broker URL {domain-name} 消息服务器地址
Broker port 1883 端口
Broker port with SSL 8883 端口
username Device ID 设备的 ID
password Device Token 设备访问令牌
Publish Path messages/<did> 设备发布指定的消息到云端
Subscription Path actions/<did> 设备订阅发送给它的 action 数据
Error Path errors/<did> 设备发布指定设备的错误信息到云端
Properties Path things/<owner>/<pkey>/<did>/properties 客户端可订阅的指定设备的属性数据流
Events Path things/<owner>/<pkey>/<did>/events 客户端可订阅的指定设备的事件流
Actions Path things/<owner>/<pkey>/<did>/actions 客户端可发布给指定设备的操作
Actions Result Path things/<owner>/<pkey>/<did>/result 客户端可订阅的指定设备的操作结果
Client ID Device ID 应用程序 ID

设备 ID:

  • 应用程序 ID (Client ID) 可以使用设备的 MAC 地址

MQTT 协议概述

发布订阅通信模型概述

pub

通过发布订阅通信模型既可以使用 MQ 服务, 保证系统的高并发能力, 同时也能达到更复杂和更实时的通信能力.

消息队列 (MQ) 概述

通过在前端设备(网关)和云服务平台中间加入消息队列, 可以实现百万级设备并发能力.

  • MQ 须支持标准 MQTT 协议.
  • MQ 须支持 HTTP 协议接口用来发布数据
# 示意图
前端设备->消息队列: 建立 MQTT 连接
服务程序1->消息队列: 建立 MQTT 连接
服务程序1->消息队列: 订阅(/主题)
服务程序2->消息队列: 建立 MQTT 连接
服务程序2->消息队列: 订阅(/主题)
前端设备->消息队列: 发布(/主题, 数据)
消息队列-->服务程序1: 发布(/主题, 数据)
消息队列-->服务程序2: 发布(/主题, 数据)

如图所示, 所有订阅了同一主题的服务程序(订阅者)都能收到前端设备(发布者)发送到这个主题的数据

客户端 ID

{DeviceType}-{DeviceID}

  • 设备的类型,如 gateway
  • 设备的编号或 MAC 地址, 如 aa00123456cc
  • 例如: gateway-aa00123456cc

消息主题概述

在发布订阅通信模型和消息队列中, 主题 (Topic) 是非常重要的概念. 要最终实现完美的通信主题的名称和路径需要精心设计.

注意主题(Topic) 格式类似 Unix 文件路径, 但是 MQTT 主题名第一字符不需要 /.

上行主题

messages/{DeviceID}

下行主题

actions/{DeviceID}

设备注册

设备通过发送注册消息来认证设备并获取访问令牌,具体流程如下。

设备->消息队列: 订阅 actions/:did
设备管理服务器->消息队列: 订阅 messages/:did
设备->消息队列: 发布注册请求 messages/:did
消息队列->设备管理服务器: 注册请求
设备管理服务器->消息队列: 发布注册结果 actions/:did
消息队列->设备: 注册结果

发布注册请求

发布主题:

PUBLISH messages/:did

注册请求消息:

{
    "did": String,
    "type": "register",
    "timestamp": Number,
    "sign": String,
    "data": {
        "expires": Number,
        "version": {
            "firmware": String
        },
        "supported": []
    }
}

更新注册消息:

{
    "did": String,
    "type": "register",
    "timestamp": Number,
    "sign": String
}

删除注册消息:

{
    "did": String,
    "type": "register",
    "timestamp": Number,
    "sign": String,
    "data": {
        "expires": -1
    }
}

请求参数:

NAME REQUIRED TYPE DEFAULT DESCRIPTION
did true String - 设备唯一 ID
sign - String - 消息签名
timestamp - Number - 时间戳
type true String register 消息类型,必须是 register
data - Object - 事物描述信息, 详情请参考 WoT 事物描述
- expires - Number 3600 设备端期望的注册期满时间, 单位为秒
- nodes - Array - 可选,绑定到当前节点的边缘节点列表,常用于网关
- gateway - String - 可选,网关的 DID, 常用于边缘节点通过网关注册时

更多详细信息请考参相关的 HTTP API 的描述

接收注册结果

设备需订阅 actions/:did 主题接收应答结果

订阅主题:

SUBSCRIBE actions/:did

成功结果:

{
    "did": String,
    "type": "register",
    "result": {
        "id": String,
        "token": String,
        "expires": Number
    }
}
NAME REQUIRED TYPE DEFAULT DESCRIPTION
did true String - 注册的设备的唯一 ID
type true String register 消息类型,必须是 register
token true Number - 令牌,用于后续消息加密和认证
expires - Number 3600 云端期望的注册期满时间, 单位为秒
id - Number - 设备在云端的 ID

错误结果:

{
    "did": String,
    "type": "register",
    "error": {
        "code": Number,
        "error": String,
        "message": String
    }
}
CODE TYPE NOTE
100401 Unauthorized 设备没有通过身份认证
100403 Permission denied 该设备需要取得授权
104001 Miss required parameter 缺失必要的参数,请参考 API 文档
200202 Device does not exists 设备不存在

设备属性

上报数据流

前端设备通过发送数据流消息来主动上报数据流。

上报数据流也会同时更新设备影子的状态

采集服务器->消息队列: 订阅 messages/:did
设备->消息队列: 发布数据流 messages/:did
消息队列->采集服务器: 数据流

发布主题:

PUBLISH messages/:did

  • did 表示设备 ID. 即每一个设备对应一个消息主题.

消息内容:

{
    "did": String,
    "type": "stream",
    "token": String,
    "data": {
        "temperature": 50
    }
}

注意一个消息的大小最好不要超过 4KB.

接收读取操作

应用程序通过这个消息来主动查询前端设备的属性

设备->消息队列: 订阅 actions/:did
应用程序->消息队列: 订阅 messages/:did
应用程序->消息队列: 发布读属性请求 actions/:did
消息队列->设备: 读属性请求
设备->消息队列: 操作结果 messages/:did
消息队列->应用程序: 操作结果(属性值)

请求消息:

{
    "did": String,
    "mid": String,
    "type": "action",
    "data": {
        "read": ["<name>"]
    }
}

应答消息:

{
    "did": String,
    "mid": String,
    "type": "action",
    "result": {
        "read": {
            "<name>": <value>
        }
    }
}

接收修改操作

应用程序通过这个消息来主动修改前端设备的属性

设备->消息队列: 订阅 actions/:did
应用程序->消息队列: 订阅 messages/:did
应用程序->消息队列: 发布修改属性请求 actions/:did
消息队列->设备: 修改属性请求
设备->消息队列: 操作结果
消息队列->应用程序: 操作结果

请求消息:

{
    "did": String,
    "mid": String,
    "type": "action",
    "data": {
        "write": {
            "<name>": <value>
        }
    }
}

应答消息:

{
    "did": String,
    "mid": String,
    "type": "action",
    "result": {
        "write": {
            "code": 0
        }
    }
}

设备操作

应用程序通过发送这个消息来远程调用前端设备的操作方法。

设备->消息队列: 订阅 actions/:did
应用程序->消息队列: 订阅 messages/:did
应用程序->消息队列: 发布操作请求 actions/:did
消息队列->设备: 操作请求
设备->消息队列: 发布操作结果 messages/:did
消息队列->应用程序: 操作结果

接收操作请求

设备可以订阅如下的主题来接收发给它的操作 (Action).

订阅主题:

SUBSCRIBE actions/:did

  • did 表示设备 ID. 即每一个设备对应一个消息主题.

消息内容:

{
    "did": String,
    "mid": String,
    "type": "action",
    "data": {
        "<name>": <input>
    }
}

发送操作结果

发布主题:

PUBLISH messages/:did

  • did 表示设备 ID. 即每一个设备对应一个消息主题.

消息内容

{
    "did": String,
    "mid": String,
    "type": "action",
    "result": {
        "<name>": <output>
    }
}

注意一个消息的大小最好不要超过 4KB.

设备事件

前端设备通过事件消息主动上报事件信息

设备管理服务器->消息队列: 订阅 messages/:did
设备->消息队列: 发布事件 messages/:did
消息队列->设备管理服务器: 事件信息

发布事件

发布主题:

PUBLISH messages/:did

  • did 表示设备 ID. 即每一个设备对应一个消息主题.

消息内容:

{
    "did": String,
    "type": "event",
    "token": String,
    "data": {
        "<name>": <value>
    }
}

注意一个消息的大小最好不要超过 4KB.

设备影子 API

查询设备影子

设备可以通过这个操作查询设备影子的状态

发布请求消息

发布主题:

PUBLISH messages/:did

  • did 表示设备 ID. 即每一个设备对应一个消息主题.

消息内容:

{
    "did": String,
    "mid": String,
    "type": "action",
    "data": {
        "shadow": {
            "read": {}
        }
    }
}

接收应答消息

订阅主题:

SUBSCRIBE actions/:did

消息内容:

{
    "did": String,
    "mid": String,
    "type": "action",
    "result": {
        "shadow": {
            "read": {  
                "version": String,
                "updated": Number,
                "config": {
                    "version": String,
                    "updated": Number,
                    "<name>": <value>
                },
                "reported": {
                    "<name>": <value>
                },
                "desired": {
                    "<name>": <value>
                },
                "metadata": {
                    "reported": {
                        "updated": Number,
                        "<name>": {
                            "updated": Number
                        }
                    },
                    "desired": {
                        "updated": Number,
                        "<name>": {
                            "updated": Number
                        }
                    }
                }
            }
        }
    }
}

应答参数:

NAME REQUIRED TYPE DESCRIPTION
did true String 设备的 ID
version true String 设备影子的版本
updated true Number 设备影子的最后更新时间
config - Object 设备配置参数
reported - Object 设备最后上报的属性或状态
desired - Object 设备期望被配置的属性
metadata - Object 设备影子元数据

更新设备影子

应用程序可以通过这个接口修改期望的属性,设备端可以通过这个接口上报实际的属性

发布请求消息

发布主题:

PUBLISH /messages/:did

请求内容:

{
    "did": String,
    "mid": String,
    "type": "action",
    "data": {
        "shadow": {
            "write": {    
                "reported": {
                    "<name>": <value>
                },
                "desired": {
                    "<name>": <value>
                }
            }
        }
    }
}
NAME REQUIRED TYPE DEFAULT DESCRIPTION
did true String - 设备 ID
reported - Object - 上报设备当前属性或状态
desired - Object - 设置期望的设备属性

接收应答消息

订阅主题:

SUBSCRIBE actions/:did

响应结果:

{
    "did": String,
    "mid": String,
    "type": "action",
    "result": {
        "shadow": {
            "write": {
                "code": 0
            }
        }
    }
}

设备管理 API

具体协议请参考设备管理协议一章, 下面是部分协议介绍:

订阅主题:

SUBSCRIBE actions/:did

固件升级

应用程序通如下面的流程来主动升级前端设备的固件。

在开始升级前应用程序需先上传要升级的固件方法到设备管理服务器。

设备->消息队列: 订阅操作消息
设备管理服务器->消息队列: 发布升级操作请求 
消息队列->设备: 升级操作请求
设备->消息队列: 请求结果
消息队列->设备管理服务器: 请求结果
设备->设备管理服务器: 下载固件文件 (HTTP)
设备管理服务器->设备: 固件文件
设备->设备: 升级
设备->消息队列: 重新注册
消息队列->设备管理服务器: 重新注册

消息内容:

{
    "did": String,
    "mid": String,    
    "type": "action",
    "data": {
        "firmware": {
            "update": {
                "uri": String,
                "size": Number,
                "md5sum": String,
                "version": String
            }
        }
    }
}

应答内容

发布主题:

PUBLISH messages/:did

消息内容:

{
    "did": String,
    "mid": String,    
    "type": "action",
    "result": {
        "firmware": {
            "update": {
                "code": 0
            }
        }
    }    
}

参数配置

应用程序可以通过下面的流程修改前端设备的属性

config 是设备管理保留属性名,用来读写设备配置参数

设备->消息队列: 订阅
设备管理服务器->消息队列: 发布修改配置参数请求
消息队列->设备: 修改配置参数
设备->设备: 应用新的参数
设备->消息队列: 请求结果
消息队列->设备管理服务器: 请求结果

请求消息:

{
    "did": String,
    "mid": String,    
    "type": "action",
    "data": {
        "config": {
            "write": {
                "<name>": <value>,
            }
        }
    }
}

应答内容

发布主题:

PUBLISH messages/:did

消息内容:

{
    "did": String,
    "mid": String,
    "timestamp": Number,    
    "type": "action",
    "result": {
        "config": {
            "write": {
                "code": 0
            }
        }
    }    
}
image-modal