自定义数据上报规则
自定义数据上报规则
提示
如果您还不了解什么是 ThingsCloud 消息规则,以及消息规则的创建方法,请浏览 消息规则基础。
什么是自定义数据上报规则
自定义数据上报规则 是 ThingsCloud 消息规则的一种。当设备通过 自定义数据流 向云平台上报数据时,会触发该规则,平台可以解析这些数据生成设备属性,也可以将数据转发给其它设备,或向当前设备下发数据。
提示
如果您还不了解自定义数据流的消息格式,请浏览 自定义数据流。
在实际物联网项目中,有些设备和传感器并不直接支持 ThingsCloud 的 MQTT 标准规范,而是使用各自的私有协议或二进制报文。例如:
- 通过 DTU 透传的 Modbus RTU 二进制报文
- 通过 TCP 接入的自定义二进制或文本协议
- 通过 LoRaWAN 网关转发的子设备数据
- 通过第三方云平台转发过来的设备数据
面对这些非标准数据,自定义数据上报规则可以让它们在进入 ThingsCloud 后被灵活解析和处理,最终转化为统一的设备属性或下发指令。
触发条件
下图展示了该规则的触发时机:
自定义数据上报规则需要同时满足以下条件才会触发:
- 设备已上报自定义数据:设备通过 MQTT 或 TCP 等方式,向云平台发送了自定义数据流消息。
- 已创建自定义数据上报规则:在该设备或其所属设备类型下,已创建了自定义数据上报规则。
- 自定义数据流匹配:规则中配置的自定义数据流标识符,与设备上报时使用的自定义数据流标识符一致。
需要特别说明的是:
- 对于同一设备上报的同一则自定义数据消息,平台仅会触发一条自定义数据上报规则。如果需要同时执行多个操作,需要将它们配置在同一条规则中。
- 在同一条规则内部,不同类型操作会同时执行,且没有严格的先后顺序。
- 同类型操作在达到数量上限后不再执行。各类操作的数量上限不同,Modbus RTU 解析 最多可添加 10 个,其它操作默认最多添加 1 个。
支持的操作
自定义数据上报规则目前支持以下操作:
| 操作名称 | 介绍信息 |
|---|---|
| Modbus RTU 解析 | 通过可视化配置解析规则,实现对 Modbus RTU 数据上报进行解析,生成设备属性。 |
| 属性解析函数 | 编写 Javascript 云函数,解析自定义上报数据,生成设备的属性集合,实现属性上报。 |
| 云网关解析函数 | 编写 Javascript 云函数,解析自定义上报数据,生成网关上报协议,实现云网关。 |
| 向当前设备下发自定义数据 | 通过云函数生成自定义数据,通过指定的自定义数据流下发到当前上报数据的设备。 |
在创建或编辑规则时,可以添加这些操作,如下图。

Modbus RTU 解析
在使用 DTU 等设备连接 Modbus 传感器或执行器时,通常用 TCP 透传或 MQTT 透传模式,直接将子设备的 Modbus 报文,利用云平台的自定义数据流,上报到云平台。
提示
关于 DTU 接入 ThingsCloud 的详细介绍,请浏览 DTU 接入
使用 Modbus RTU 作为数据格式的自定义数据上报,可以使用该操作快速配置 Modbus RTU 报文的解析规则,直接生成设备属性。
目前支持解析以下功能码的从机消息响应:

01:读取线圈寄存器02:读取离散输入寄存器03:读取保持寄存器04:读取输入寄存器
以 03 功能码为例,可以设置数据提取规则,如下图:

这里还提供了规则测试,让 Modbus RTU 十六进制格式的报文复制到这里,可以使用上报配置的解析规则,进行模拟解析,来验证规则是否正确。如下图:

在填写配置时,一些需要注意的地方如下:
使用子设备地址:开启后,对于 网关子设备 类型的设备,将自动使用 子设备地址 替换规则中的 从机地址。
适用于同设备类型的多个子设备,接入网关的不同 Modbus 地址,无需单独为每个子设备创建解析规则。数据字节数:用来检查 Modbus RTU 消息报文的数据部分字节数是否符合要求,如果不符合,则不解析。当设备有多条 Modbus RTU 消息上报时,可以用数据字节数来进行区分。
属性解析函数
属性解析函数的目的是将设备上报的自定义数据,通过自定义函数进行解析处理,生成设备属性,通过内部流转上报到设备。

module.exports = function (identifier, data, options) {
/**
* 参数:
* identifier: 上报的自定义数据流标识符
* data: 上报的自定义数据,二进制 Buffer格式
* options: 自定义数据流的扩展选项,例如 MQTT Topic 中的动态参数
* 返回值:
* attributes: 生成设备属性
*/
var attributes = {};
return attributes;
}
参数
identifier:string类型,自定义数据流的标识符。data:Buffer类型,自定义数据流的消息报文,采用原始二进制字节流格式。options:object类型,自定义数据流的扩展选项。当自定义数据流使用 MQTT Topic 动态参数时,可通过options.params获取这些参数。
返回值
object类型:解析后的设备属性集合。
示例:解析 Modbus RTU 数据格式
如果某些设备使用了不标准的 Modbus 协议报文,我们无法直接使用前边的 Modbus RTU 解析 操作。又或者在解析 Modbus 报文的同时,我们还要进行一些逻辑计算,那么可以在 云解析函数 中实现 Modbus 报文的解析。
例如,一个 DTU 在 Modbus 01 、02 两个地址上分别连接了温湿度传感器和二氧化氯传感器,解析函数如下:
module.exports = function (identifier, data, options) {
var attributes = {};
// 使用内置函数,验证 CRC
if (!Cloud.validateCrc16Modbus(data)){
return attributes;
}
if (data[0] == 0x01){
// 温湿度传感器的从机地址
let humidity = data.readInt16BE(3) * 0.1;
let temperature = data.readInt16BE(5) * 0.1;
attributes.humidity = humidity.toFixed(3);
attributes.temperature = temperature.toFixed(3);
}
if (data[0] == 0x02){
// 二氧化氯传感器的从机地址
let cio2 = data.readInt16BE(3);
let point = data.readInt16BE(5);
if (point == 2) {
// 根据标志位设置数值倍数,依据传感器厂家的使用说明
cio2 *= 0.1;
}
if (cio2 <= 200) {
// 正常量程内则记录
attributes.cio2 = cio2.toFixed(3);
}
}
return attributes;
}
示例:解析自定义二进制数据格式
自定义二进制数据格式的处理,同样是对 data 参数进行自定义解析。
示例:解析 JSON 数据
例如,设备通过 TCP 接入平台后,通过自定义数据流上报了以下 JSON 消息:
{
"temperature": 34.2,
"humidity": 67
}
我们希望将 JSON 数据全部转入设备属性中,解析函数可以这样写:
module.exports = function (identifier, data, options) {
var attributes = {};
// 利用内置函数将 data 转换为 JSON 对象格式
const json = Cloud.jsonParse(data.toString());
attributes = json;
return attributes;
}
解析后返回的属性集合,被设备最终接收,如下:
{
"temperature": 34.2,
"humidity": 67
}
示例:提取需要的 JSON 数据
例如,设备上报了以下自定义 JSON 消息:
{
"messageId": "100293",
"ts": 1609143039050,
"data": [
{
"params": {
"temperature": 34.2,
"humidity": 67
}
}
]
}
我们希望将其中有用的数据提取出来,转入到设备属性中,解析函数这样写:
module.exports = function (identifier, data, options) {
var attributes = {};
// 利用内置函数将 data 转换为 JSON 对象格式
const json = Cloud.jsonParse(data.toString());
if (json.data[0] == undefined){
return attributes;
}
attributes = json.data[0].params;
return attributes;
}
解析后返回的属性集合,被设备最终接收,如下:
{
"temperature": 34.2,
"humidity": 67
}
示例:解析 Plaintext 文本消息格式
Plaintext 消息由可见字符组成,可能是定长的,也可能是不定长的,解析相对比较简单,只要按设备消息格式的规范来逐个解析里边的信息,生成相应的属性。
例如,设备上报的消息格式如下,代表了温度和湿度。
23.5,67
解析函数如下:
module.exports = function (identifier, data, options) {
var attributes = {};
// 将 data 转换为 string 字符串格式
const text = data.toString();
const info = text.split(",");
if (info.length == 2) {
attributes = {
"temperature": info[0],
"humidity": info[1],
}
}
return attributes;
}
解析后生成的属性集合如下:
{
"temperature": 23.5,
"humidity": 67
}
再例如,设备上报的消息考虑到扩展性,将字段信息也加入了其中,如下:
temperature:23.5,humidity:67,co2:2300
解析函数如下:
module.exports = function (identifier, data, options) {
var attributes = {};
// 将 data 转换为 string 字符串格式
const text = data.toString();
// 通过逗号分隔符,转为数组
const items = text.split(",");
// 遍历数组
items.forEach(item => {
// 通过冒号分隔符,转为数组
const parts = item.split(":");
if (parts.length == 2) {
// 写入属性中
attributes[parts[0]] = parts[1] * 1;
}
})
return attributes;
}
解析出来的属性集合如下:
{
"temperature": 23.5,
"humidity": 67,
"co2": 2300
}
提示
在规则云函数中,除了可以调用 Javascript 的标准函数外,ThingsCloud 还提供了一系列的 内置函数库,增强了云函数的能力。
云网关解析函数
云网关解析函数 操作用于将网关设备通过自定义数据流收到的上报数据,转换为 ThingsCloud 网关协议中的 上报子设备属性 格式,从而让第三方网关(如 LoRaWAN 网关、Modbus 透传网关等)实现平台侧的云网关能力。
提示
关于 ThingsCloud 网关协议的详细介绍,请浏览 网关 MQTT 接入。
选项
- 云函数:编写 Javascript 代码,将自定义数据上报解析为网关上报子设备属性的消息。
云函数参数
identifier:string类型,上报的自定义数据流标识符。data:Buffer类型,上报的自定义数据,原始二进制字节流格式。options:object类型,自定义数据流的扩展选项,例如 MQTT Topic 中的动态参数。
云函数返回值
object类型:构造一个网关上报子设备属性的消息,格式请参考 上报子设备属性。null:表示不转发消息到子设备。
示例:RAK LoRaWAN 网关转发到子设备
在 RAK LoRaWAN 网关接入 ThingsCloud 的示例中,网关通过自定义数据流 lora 上报了包含 dev_id 和 data 的 JSON 消息。使用云网关解析函数,可以将该消息转发给对应的子设备。
module.exports = function (identifier, data, options) {
/**
* 参数:
* identifier: 上报的自定义数据流标识符
* data: 上报的自定义数据,二进制 Buffer格式
* options: 自定义数据流的扩展选项
* 返回值:
* attributes: 网关上报子设备属性
*/
var json = Cloud.jsonParse(data);
var attributes = {};
if (options.params.dev_id) {
attributes[options.params.dev_id] = {
data: json.data
}
}
return attributes;
}
以上函数将子设备地址(dev_id)作为键,将 json.data 作为该子设备的属性,转发给云平台。云平台会根据子设备地址,将属性写入对应的子设备。
示例:从 JSON 消息中解析多子设备属性
假设网关一次性上报了多个子设备的数据,JSON 格式如下:
{
"nodes": [
{"addr": "A1001", "temperature": 23.5, "humidity": 60},
{"addr": "A1002", "temperature": 25.1, "humidity": 58}
]
}
解析函数可以这样写:
module.exports = function (identifier, data, options) {
var json = Cloud.jsonParse(data);
var attributes = {};
if (json.nodes && Array.isArray(json.nodes)) {
json.nodes.forEach(node => {
attributes[node.addr] = {
temperature: node.temperature,
humidity: node.humidity
}
});
}
return attributes;
}
解析后,子设备地址为 A1001 和 A1002 的设备将分别收到对应的温度、湿度属性。
示例:解析 Modbus 透传网关的子设备数据
某些 DTU 作为 Modbus 透传网关时,会将不同从机地址的 Modbus 响应一起上报。可以通过从机地址区分子设备,并解析出属性。
例如,网关上报的 Modbus RTU 报文中,第一个字节为从机地址:
module.exports = function (identifier, data, options) {
var attributes = {};
// 校验 CRC
if (!Cloud.validateCrc16Modbus(data)) {
return attributes;
}
// 从机地址作为子设备地址
var subDeviceAddr = data[0].toString();
// 解析 03 功能码:从第 3 字节开始为数据
if (data[1] === 0x03 && data[2] === 4) {
attributes[subDeviceAddr] = {
temperature: data.readInt16BE(3) * 0.1,
humidity: data.readInt16BE(5) * 0.1
}
}
return attributes;
}
向当前设备下发自定义数据
该操作用于在收到设备自定义数据上报后,自动向当前设备下发自定义数据。可通过云函数动态生成下发的消息内容,常用于以下场景:
- 设备上报数据后,平台自动回复确认或应答。
- 根据设备上报内容,动态生成下一次查询指令。
- 设备通过 TCP 接入且为低功耗设备,在设备上线时主动下发缓存的配置。
提示
该操作下发到当前上报数据的设备,若需要向其它设备下发,请使用属性上报规则中的 向指定设备下发自定义数据。
选项
- 云函数:动态生成下发自定义数据。
- 推送方式:选择设备接入平台使用的协议,可选 MQTT 或 TCP。
- 延迟设置:可设置下发数据的延迟时间(秒数)。
- 自定义数据流:选择设备通过哪个自定义数据流接收消息,请填写在设备类型中已创建的自定义数据流标识符。
云函数参数
identifier:string类型,上报的自定义数据流标识符。data:Buffer类型,上报的自定义数据,原始二进制字节流格式。options:object类型,自定义数据流的扩展选项。当自定义数据流使用 MQTT Topic 动态参数时,可通过options.params获取这些参数。
云函数返回值
object类型:构造一个下发自定义数据的消息。null:表示不下发数据到设备。
下发自定义数据的消息 object 格式,根据 自定义数据流的数据格式 不同,分为以下几种:
HEX数据格式:
{
"type": "hex",
"msg": "02030010000185FC"
}
Plaintext文本格式:
{
"type": "text",
"msg": "location,1,5,6,0"
}
JSON格式:
{
"type": "json",
"msg": {
"command": "openLock",
"led": "ON"
}
}
示例:TCP 设备上报后自动回复确认
设备通过 TCP 接入平台,每次上报一条文本消息后,希望平台立即回复 ACK。当自定义数据流的消息格式为 Plaintext 时,云函数可以这样写:
module.exports = function (identifier, data, options) {
/**
* 参数:
* identifier: 上报的自定义数据流标识符
* data: 上报的自定义数据,二进制 Buffer格式
* options: 自定义数据流的扩展选项,例如 MQTT Topic 中的动态参数
* 返回值:
* object: 构造下发的自定义数据对象
*/
var text = data.toString();
if (text.indexOf("REPORT") === 0) {
return {
type: "text",
msg: "ACK"
}
}
return null;
}
示例:根据心跳数据生成 Modbus 查询指令
假设设备通过自定义数据流上报一条应用层心跳数据,内容为一个字节的十六进制 00。平台收到该心跳后,自动向设备发送一条 Modbus 查询指令。利用内置的 Cloud.ModbusRTU 函数可以方便地生成指令:
module.exports = function (identifier, data, options) {
// 判断上报数据是否为应用层心跳:长度为 1 个字节,且值为 0x00
if (data.length == 1 && data[0] == 0x00) {
// 设置从站地址为 1
Cloud.ModbusRTU.setUnitId(1);
// 生成读取保持寄存器 0~1 的 03 功能码指令
var msg = Cloud.ModbusRTU.readHoldingRegisters(0, 2);
return {
type: "hex",
msg: msg
}
}
return null;
}
以上函数仅在收到心跳数据 00 时,才会向设备下发 010300000002C40B,用于查询寄存器数据。其它上报数据不会触发查询指令。
示例:根据 Modbus 控制指令回复延迟查询设备状态
在工控场景中,常常需要在向设备下发继电器控制指令(例如启动变频器)后,等待设备执行完成,再查询设备的最新运行状态。例如,平台通过 Modbus 05 功能码下发继电器控制指令后,设备会回复一条 05 功能码响应报文。收到该响应后,等待 5 秒钟,再向设备下发 03 功能码查询指令,读取变频器的运行状态和运行频率。
module.exports = function (identifier, data, options) {
// 判断上报数据是否为 Modbus 05 功能码的响应报文
// 05 功能码响应报文结构:从机地址(1字节) + 功能码 0x05(1字节) + 线圈地址(2字节) + 写入值(2字节) + CRC(2字节)
if (data.length >= 8 && data[1] === 0x05) {
// 设置从站地址为 1
Cloud.ModbusRTU.setUnitId(1);
// 生成读取保持寄存器 0~1 的 03 功能码指令,查询运行状态和频率
var msg = Cloud.ModbusRTU.readHoldingRegisters(0, 2);
return {
type: "hex",
msg: msg
}
}
return null;
}
以上函数在收到设备对 05 功能码控制指令的回复后,会返回一条 03 功能码查询指令。配合该操作的延迟设置选项,将延迟时间设为 5 秒,即可在设备执行完控制动作后,再查询最新的运行状态和频率。
示例:根据 JSON 上报生成 JSON 下发
设备通过自定义数据流上报 JSON 数据,平台解析后根据内容下发不同的控制指令。
例如,设备上报:
{
"cmd": "status",
"battery": 85
}
当电量低于 90% 时,平台下发低功耗模式指令:
module.exports = function (identifier, data, options) {
var json = Cloud.jsonParse(data.toString());
if (json.cmd === "status" && json.battery < 90) {
return {
type: "json",
msg: {
command: "setPowerMode",
mode: "low"
}
}
}
return null;
}