更新时间:2024-06-20 03:28:41下载pdf
本文介绍了涂鸦智慧行业智能水表接入方案。
为智能水表设备提供了涂鸦边缘⽹关接⼊的⽅案,帮助智能水表设备快速接⼊涂鸦开发者平台,方案架构如下图所示:
智能水表⽹关⽤ MQTT 协议与涂鸦边缘⽹关进⾏通信,其中智能水表⽹关做客户端 ,涂鸦边缘⽹关做 broker。
如下是智能水表的具体设备属性信息,设备属性上报时作为参考。
DP ID | 功能点名称 | 标识符 | 数据传输类型 | 数据类型 | 功能点属性 |
---|---|---|---|---|---|
1 | 水流量 | water_flow | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
2 | 月总用水量 | monthly_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
3 | 日总用水量 | daily_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
4 | 开关状态 | switch_state | 可下发可上报(rw) | bool | |
5 | 日初始用水量 | d_begin_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
6 | 日终用水量 | d_end_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
7 | 日初始读取时间 | d_begin_time | 只上报(ro) | string | Unix 时间戳(Unix timestamp) |
8 | 日终读取时间 | d_end_time | 只上报(ro) | string | Unix 时间戳(Unix timestamp) |
9 | 阀门状态 | valve_state | 可下发可上报(rw) | enum | 枚举值: opened, closed, opening, closing |
10 | 月初始用水量 | m_begin_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
11 | 月终用水量 | m_end_water_total | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
12 | 月初始读取时间 | m_begin_time | 只上报(ro) | string | Unix 时间戳(Unix timestamp) |
13 | 月终读取时间 | m_end_time | 只上报(ro) | string | Unix 时间戳(Unix timestamp) |
14 | 冷摊用量 | cold_stall_userdata | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
15 | 冷摊方式 | cold_stall_type | 可下发可上报(rw) | enum | 枚举值: square_area, ignore, consumption, proportion, lease, power |
16 | 热摊用量 | hot_stall_userdata | 只上报(ro) | value | 取值范围: 0~999999999, 单位:m3 |
17 | 热摊方式 | hot_stall_type | 可下发可上报(rw) | enum | 枚举值: square_area, ignore, consumption, proportion, lease, power |
18 | 阀开启度 | valve_open_degree | 可下发可上报(rw) | value | 取值范围: 0~999999999, 单位: % |
概述
MQTT 消息由固定报头(Fixed header)、可变报头(Variable header)和有效载荷(Payload)三部分组成。
其中固定报头(Fixed header)和可变报头(Variable header)格式的填写请参考 MQTT 标准规范,有效载荷(Payload)的格式由涂鸦定义,具体请参考下文。
MQTT 的语法和接口细节,请以 MQTT 标准规范 为准。
常见 MQTT 消息类型主要有 CONNECT、SUBSCRIBE、PUBLISH。
使用限制
设备接入涂鸦边缘网关,通过涂鸦边缘网关进行通信。设备、涂鸦边缘网关和涂鸦开发者平台的通信流程示意图如下。
设备发送数据到涂鸦边缘网关
设备接入涂鸦边缘网关后,便可与涂鸦边缘网关进行通信。设备可通过以下方式发送数据到平台。
设备属性上报:用于设备按产品模型中定义的格式将属性数据上报给涂鸦开发者平台。
应用服务器下发指令给设备
设备接入涂鸦边缘网关后,涂鸦边缘网关可通过以下方式发送指令到设备。
平台命令下发:应用服务器按产品模型中定义的命令格式下发控制命令给设备。
设备使用 MQTT 协议接入涂鸦边缘网关时,涂鸦开发者平台和设备通过 Topic 进行通信。预置的 Topic 列表如下:
Topic 分类 | Topic | Publisher | Subscriber | 用途 |
---|---|---|---|---|
设备同步相关 Topic | gateway_id/{gateway_id}/devices/sync | 设备 | 平台 | 设备激活 |
设备同步相关 Topic | gateway_id/{gateway_id}/devices/sync/response | 平台 | 设备 | 平台返回激活响应 |
设备命令相关 Topic | gateway_id/{gateway_id}/commands | 平台 | 设备 | 平台下发命令给设备 |
设备命令相关 Topic | gateway_id/{gateway_id}/commands/response | 设备 | 平台 | 设备返回命令响应 |
设备属性相关 Topic | gateway_id/{gateway_id}/properties/report | 设备 | 平台 | 设备属性上报 |
设备属性相关 Topic | gateway_id/{gateway_id}/properties/report/response | 平台 | 设备 | 平台返回上报属性响应 |
设备上下线 Topic | gateway_id/{gateway_id}/devices/status | 设备 | 平台 | 设备上下线上报 |
{gateway_id}用于标识 Topic 路由的目标设备,设备侧订阅该 Topic 或往 Topic 推送消息时,该值需要替换为设备与平台建立 MQTT 连接时使用的网关 ID 参数值。
MQTT 协议中规定了消息服务质量(Quality of Service),它保证了在不同的网络环境下消息传递的可靠性,MQTT 设计了 3 个 QoS 等级。
QoS 级别越高,流程越复杂,系统资源消耗越大。应用程序可以根据自己的网络场景和业务需求,选择合适的 QoS 级别。
涂鸦建议三方设备对接方在消息的推送和订阅上 QoS 设置为 1。
涂鸦开发者平台设备侧支持 MQTT 协议的 connect 消息接口,鉴权通过后建立设备与平台间的 MQTT 连接。
参数 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
broker | 必选 | String | 涂鸦边缘⽹关 IP 地址,对接时由涂鸦提供。 |
port | 必选 | String | TCP 固定为 21883 |
clientId | 必选 | String | 三方的网关 ID,此参数要保证唯一。 |
username | 必选 | String | 三⽅⼚商标识,对接时由涂鸦提供。 |
password | 必选 | String | Password 的值为使用“md5”加密算法生成,具体请参考密码生成规则 |
说明:SSL/TLS 为单向认证模式。
拼接 clientId 和 username 变量,然后用 md5 加密算法对拼接后的变量加密。
下面用 Go 语言生成连接密码例子,供参考。
func generatePassword() string {
var (
clientId string
username string
password string
)
// clientId username 请先赋值
password = md5V(clientId + username)
return password
}
// md5 加密算法函数
func md5V(str string) string {
h := md5.New()
h.Write([]byte(str))
return hex.EncodeToString(h.Sum(nil))
}
原生 MQTT 协议设备和平台建链时,常见返回码如下:
返回码 | 标示 | 返回码描述 | 原因 |
---|---|---|---|
0x00 | connection accepted | 连接成功 | 连接成功 |
0x01 | unacceptable protocol version | 请求拒绝,协议版本错误 | 服务器不支持客户端请求 MQTT 协议版本 |
0x02 | identifier rejected | 请求拒绝,无效的客户端标识符 | clientId不符合格式要求或者心跳时间间隔不满足平台要求 |
0x03 | server Unavailable | 请求拒绝,服务器不可用 | 平台服务不可用 |
0x04 | bad user name or password | 请求拒绝,用户名或密码错误 | 用户名或密码错误 |
0x05 | not Authorized | 请求拒绝,没有授权 | 客户端没有权限连接 |
用于设备同步,在设备网关连接上涂鸦边缘网关的 MQTT 服务器后,要发送设备同步指令。涂鸦边缘网关会对上报的设备进行新增或更新。
必须先同步子设备才可进行后续操作,否则会出现找不到设备的错误。
Topic
上行:gateway_id/{gateway_id}/devices/sync
下行:gateway_id/{gateway_id}/devices/sync/response
字段名 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | Unix 时间戳(Unix timestamp) |
request_id | 必选 | String | 请求标示,建议用 UUID 算法生成。 |
param | 必选 | Object | 对象 |
param.cid | 必选 | String | 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
param.product_id | 必选 | String | 产品对应的 product_id |
param.vendor_code | 可选 | String | 厂商 code |
param.comm_type | 可选 | String | 设备和网关通讯方式,请填写“mqtt” |
param.device_ip | 可选 | String | 设备 IP 地址 |
param.mac_address | 可选 | String | 设备 MAC 地址 |
param.device_desc | 可选 | String | 设备描述 |
param.install_location | 可选 | String | 设备安装位置 |
param.device_server_id | 可选 | String | 设备服务 ID |
param.product_sub_type | 可选 | String | 子产品类型 |
字段名 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | Unix 时间戳(Unix timestamp) |
request_id | 必选 | String | 请求唯一标示 |
param | 必选 | Object | 对象 |
param.cid | 必选 | String | 设备 CID |
param.msg | 必选 | String | 含义请参考全局错误码。 |
param.code | 必选 | Integer | 标识命令的执行结果,0 表示成功,其他表示失败 |
Topic: gateway_id/{gateway_id}/devices/sync
数据格式:
{
"t":162728***,
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a***",
"param":{
"cid":"92dda538fc2e636fd***",
"product_id":"",//请填写产品对应的 pid
"vendor_code":"",//设备厂商标识
"comm_type":"",
"device_ip":"",
"mac_address":"",
"device_name":"",
"device_desc":"",
"install_location":"",
"device_server_id":"",
"product_sub_type":""
}
}
Topic: gateway_id/{gateway_id}/devices/sync/response
数据格式:
{
"t":162728***,
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
"param":{
"cid":"92dda538fc2e636fd4b0c37f***",
"msg":"success",
"code":0
}
}
用于涂鸦开发者平台向设备下发设备控制命令。下发命令后,需要设备及时将命令的执行结果返回给涂鸦开发者平台,如果设备没回响应,涂鸦开发者平台会认为命令执行超时。
Topic
下行: gateway_id/{gateway_id}/commands
上行:gateway_id/{gateway_id}/commands/response
字段名 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | Unix 时间戳(Unix timestamp) |
cid | 必选 | String | 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
request_id | 必选 | String | 请求标示,建议用 UUID 算法生成。 |
param | 必选 | Object | 对象 |
param.id | 必选 | String | 设备功能点 ID |
param.value | 必选 | 类型不固定 | 设备命令的执行参数。 |
命令应答的 JSON 格式。
字段名 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | Unix 时间戳(Unix timestamp) |
cid | 必选 | String | 第三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
request_id | 必选 | String | 请求唯一标示,建议用 UUID 算法生成。 |
param | 必选 | Object | 对象 |
param.msg | 必选 | String | 执行结果,如果成功,请填写 success。 |
param.code | 必选 | Integer | 标识命令的执行结果,0 表示成功,其他表示失败。 |
Topic: gateway_id/{gateway_id}/commands
数据格式:
{
"t":162728***,
"cid": "92dda538fc2e636fd4b0c37f980***",
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
"param": {
"id":"1",
"value": "1"
}
}
Topic:gateway_id/{gateway_id}/commands/response
数据格式:
{
"t":162728***,
"cid": "92dda538fc2e636fd4b0c37f980***",
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a***",
"param": {
"msg": "success",
"code":0
}
}
用于设备按产品模型中定义的格式将属性数据上报给涂鸦开发者平台。涂鸦开发者平台在接到上报消息后,会将执行结果返回给设备。
Topic
上行:gateway_id/{gateway_id}/properties/report
下行:gateway_id/{gateway_id}/properties/report/response
字段 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | 时间戳:为设备连接涂鸦开发者平台时的 UTC 时间。 |
request_id | 必选 | String | 请求唯一标示,建议用 UUID 算法生成。 |
param | 必选 | Object | 对象 |
param.cid | 必选 | String | 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
param.id | 必选 | String | 智能水表属性下面某一个 DPID。 |
param.value | 必选 | 参考功能点类型 | 设备的属性值 |
字段名 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | Unix 时间戳(Unix timestamp) |
request_id | 必选 | String | 请求唯一标示。 |
param | 必选 | Object | 对象 |
param.cid | 必选 | String | 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
param.msg | 必选 | String | 含义请参考全局错误码。 |
param.code | 必选 | Integer | 标识命令的执行结果,0 表示成功,其他表示失败。 |
gateway_id/{gateway_id}/properties/report
{
"t":162728***,
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a0***",
"param":{
"cid":"ee1880aca6b***",
"id":"1",
"value":"run"
}
}
Topic:gateway_id/{gateway_id}/properties/report/response
数据格式:
{
"t":162728***,
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a085c***",
"param":{
"cid":"92dda538fc2e636fd4b0c37f98***",
"msg":"success",
"code":0
}
}
设备网关需要定时把设备的上线下线状态同步给涂鸦边缘网关,建议至少每隔一分钟上报一次子设备状态。
Topic
上行:gateway_id/{gateway_id}/devices/status
字段 | 必选/可选 | 类型 | 参数描述 |
---|---|---|---|
t | 必选 | Integer | 时间戳:为设备连接涂鸦开发者平台时的 UTC 时间。 |
request_id | 必选 | String | 请求唯一标示,建议用 UUID 算法生成。 |
param | 必选 | Object | 对象数组 |
param.cid | 必选 | String | 三方设备 ID,由设备侧定义。建议采⽤设备端可读取的唯⼀标识作为 CID,例如设备的 SN 号、IMEI 号等。 |
param.status | 必选 | Bool | 设备状态。true 代表上线、false 代表离线。 |
Topic:gateway_id/{gateway_id}/devices/status
数据格式:
{
"t":162728***,
"request_id":"cd0fd3c3-bd15-42f6-8bf9-d230a08***",
"param": [{
"cid": "92dda538fc2e636fd4b0c37f98***",
"status":true
},
{
"cid": "da538fc2ebffdb463692dfdb0c3***",
"status":false
}
]
}
为了帮助开发者调用开放接口,我们提供了JAVA、PHP、GO三种语言版本的开发例子,演示了服务连接、验证、敏感信息加/解密、推送订阅等基础功能(更多语言版本的开发库将在近期陆续提供)
将下面显示的依赖性定义添加到 maven pom 文件中。
<dependencies>
<dependency>
<groupId>org.eclipse.paho</groupId>
<artifactId>org.eclipse.paho.client.mqttv3</artifactId>
<version>1.2.0</version>
</dependency>
</dependencies>
//消息发布
public class Publish {
public static void main(String[] args) {
String clientId = ""; // 即 gatewayID, 第三⽅⼚商⽹关 ID
String topic = ""; // 主题
String content = ""; // 内容
int qos = 1; // 服务质量
String broker = ""; // 服务器 IP 地址
String userName = ""; // 用户名,由涂鸦提供
String password = ""; // 密码,请参考密码生成规则
// 内存存储
MemoryPersistence persistence = new MemoryPersistence();
try {
// 创建客户端
MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
// 创建链接参数
MqttConnectOptions connOpts = new MqttConnectOptions();
// 在重新启动和重新连接时记住状态
connOpts.setCleanSession(false);
// 设置连接的用户名
connOpts.setUserName(userName);
connOpts.setPassword(password.toCharArray());
// 建立连接
sampleClient.connect(connOpts);
// 创建消息
MqttMessage message = new MqttMessage(content.getBytes());
// 设置消息的服务质量
message.setQos(qos);
// 发布消息
sampleClient.publish(topic, message);
// 断开连接
sampleClient.disconnect();
// 关闭客户端
sampleClient.close();
} catch (MqttException me) {
System.out.println("reason " + me.getReasonCode());
System.out.println("msg " + me.getMessage());
System.out.println("loc " + me.getLocalizedMessage());
System.out.println("cause " + me.getCause());
System.out.println("excep " + me);
me.printStackTrace();
}
}
}
//消息订阅
public class Subscribe {
public static void main(String[] args) throws MqttException {
String clientId = ""; // 即 gatewayID, 第三⽅⼚商⽹关 ID
String host = ""; // 服务器 IP 地址
String topic = "gateway/in/" + clientId; // 主题
int qos = 1; // 服务质量
String userName = ""; // 用户名,由涂鸦提供
String password = ""; // 密码,请参考密码生成规则
try {
MqttClient client = new MqttClient(host, clientid, new MemoryPersistence());
// MQTT 的连接设置
MqttConnectOptions options = new MqttConnectOptions();
// 设置是否清空 session,这里如果设置为 false 表示服务器会保留客户端的连接记录,这里设置为 true 表示每次连接到服务器都以新的身份连接
options.setCleanSession(true);
// 设置连接的用户名
options.setUserName(userName);
// 设置连接的密码
options.setPassword(passWord.toCharArray());
// 设置超时时间 单位为秒
options.setConnectionTimeout(10);
// 设置会话心跳时间 单位为秒
options.setKeepAliveInterval(20);
// 设置回调函数
client.setCallback(new MqttCallback() {
public void connectionLost(Throwable cause) {
}
public void messageArrived(String topic, MqttMessage message) throws Exception {
// 消息处理部分
System.out.println("message content:"+new String(message.getPayload()));
}
public void deliveryComplete(IMqttDeliveryToken token) {
}
});
client.connect(options);
//订阅消息
client.subscribe(topic, qos);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package main
import (
"fmt"
mqtt "github.com/eclipse/paho.mqtt.golang"
"log"
"os"
"time"
)
const broker = "" // 服务器 IP 地址,请联系涂鸦管理员获取
const username = "" // 用户名,由涂鸦提供
const password = "" // 密码,请参考密码生成规则
const clientID = "" // 即 gatewayID, 第三⽅⼚商⽹关 ID
const topic = "" // 主题
const port = "21883" // 服务端口号
// 订阅消息 topic = "gateway/in/" + clientID
// 发布消息 topic = "gateway/out/" + clientID
var f mqtt.MessageHandler = func(client mqtt.Client, msg mqtt.Message) {
fmt.Printf("TOPIC: %s\n", msg.Topic())
fmt.Printf("MSG: %s\n", msg.Payload())
}
func main() {
mqtt.DEBUG = log.New(os.Stdout, "", 0)
mqtt.ERROR = log.New(os.Stdout, "", 0)
opts := mqtt.NewClientOptions().AddBroker(fmt.Sprintf("tcp://%s:%s", broker, port)).SetClientID(clientID).
SetUsername(username).SetPassword(password)
opts.SetKeepAlive(60 * time.Second)
// 设置消息回调处理函数
opts.SetDefaultPublishHandler(f)
opts.SetPingTimeout(1 * time.Second)
c := mqtt.NewClient(opts)
if token := c.Connect(); token.Wait() && token.Error() != nil {
panic(token.Error())
}
// 订阅主题
if token := c.Subscribe(topic, 1, nil); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
// 发布消息
token := c.Publish(topic, 1, false, "Hello World")
token.Wait()
time.Sleep(6 * time.Second)
// 取消订阅
if token := c.Unsubscribe(topic); token.Wait() && token.Error() != nil {
fmt.Println(token.Error())
os.Exit(1)
}
// 断开连接
c.Disconnect(250)
time.Sleep(1 * time.Second)
}
消息推送
<?php
require(""); // 引入 mqtt 类文件
$server = ''; // 服务器 IP 地址,请联系涂鸦管理员获取
$port = 21883; // 服务器端口
$username = ''; // 用户名,由涂鸦提供
$password = ''; // 密码,请参考密码生成规则
$client_id = '' // 即 gatewayID, 第三⽅⼚商⽹关 ID
$mqtt = new Bluerhinos\phpMQTT($server, $port, $client_id); // 实例化 MQTT 类
if ($mqtt->connect(true, NULL, $username, $password)) // 建立连接
{
$topic = 'gateway/out/'.$client_id; // 主题
$key = '' // 加密密钥 请填写连接认证时⽤的 password
$msg = ''; // 推送消息,请参考上方消息格式
// qos = 0:仅发一次,不管是否能收到
// qos = 1:没返回一直发,可能有重复接收
// qos = 2:保证必须收到,并且不重复
$mqtt->publish($topic, $msg, 0);//发送数据
$mqtt->close(); //关闭连接
}
else
{
echo "Time out!\n";
}
消息订阅
<?php
require(""); // 引入 mqtt 类文件
$server = ''; // 服务器 IP 地址,请联系涂鸦管理员获取
$port = 21883; // 服务器端口
$username = ''; // 用户名,由涂鸦提供
$password = ''; // 密码,请参考秘密生成规则
$client_id = '' // 即 gatewayID, 第三⽅⼚商⽹关 ID
$mqtt = new Bluerhinos\phpMQTT($server, $port, $client_id);
$mqtt->debug = true;
if(!$mqtt->connect(true, NULL, $username, $password))
{
echo "连接失败!\n";
exit(1);
}
$topic = 'gateway/in/'.$client_id; // 主题
// 订阅列表
$topics = [
$topic => ['qos' => 0, 'function' => 'procmsg'],
];
$mqtt->subscribe($topics, 0);
while ($mqtt->proc()){}
$mqtt->close();
function procmsg($topic, $msg) // 消息处理函数
{
$key = ''
echo $msg;
}
当调用接口发生错误时,会返回自定义错误信息。本文介绍全局错误码的含义。
错误码 code | 错误信息 msg | 说明 |
---|---|---|
400 | tedge error: 上游具体错误信息 | 上游错误,具体信息由上游定义 |
500 | system error,please contact the admin | 系统错误,请联系管理员。 |
1001 | request time is invalid | 无效的请求时间。 |
1100 | params is empty | 参数为空。 |
1101 | params range invalid | 无效的参数范围。 |
1102 | params is null | 参数为 Null。 |
1103 | params type is incorrect | 参数类型错误。 |
2001 | device is offline | 设备已离线。 |
2002 | command or value not support | 不支持的指令或值。 |
2003 | device not exist | 设备不存在。 |
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈