☰
ThingsBoard TBEL 解码函数实战:用 parseBytesToInt 解析二进制设备上行报文(simple-binary 示例详解)
2026/10/2 13:27:35 网站建设 项目流程
  • 物联网
  • 后端
  • 数据可视化
  • 消息队列

【免费下载链接】thingsboard

All-in-one IoT Platform - Device management, data collection, processing and visualization.

项目地址:https://gitcode.com/GitHub_Trending/th/thingsboard
点击查看免费下载

导读

本文围绕 ThingsBoard 数据转换器(Uplink Data Converter)中 TBEL 解码函数的典型场景——解析二进制设备上行报文展开,以仓库内置帮助文档simple-binary示例为骨架,完整讲解报文逐字节拆解、parseBytesToInt的用法与字节序语义、解码函数返回值结构,以及平台对解码输出的全部约束。读完本文,你将能独立编写一个把 8 字节二进制帧解析为设备名、电量、温度、血氧饱和度的可运行 TBEL 解码函数,并理解其在 HTTP、MQTT、LoRaWAN 等集成中的实际应用方式。


一、示例场景:一条 8 字节的二进制上行帧

在 ThingsBoard 的集成(Integration)体系中,Uplink Data Converter 负责把来自设备或第三方网络(如 SigFox、LORIOT、ChirpStack、The Things Stack)的上行消息解析并转换为平台通用格式。当设备以二进制协议上报数据时,载荷就是一串原始字节,需要用 TBEL 内置的字节解析函数把它们切分、解释成有业务含义的字段。

本文的主角simple-binary示例描述了这样一个设备:体温计/血氧仪,每次上报 8 个字节,依次包含:

字节偏移长度含义本示例取值
04 字节设备序列号(big-endian 整数)00BC614E→ 十进制12345678
41 字节电池电量5F→ 十进制95
52 字节温度值(×100 后的整数)0E4C→ 十进制3660→ 实际温度36.6℃
71 字节血氧饱和度63→ 十进制99

对应的 HEX 报文为:

00BC614E5F0E4C63

对应的 Base64 表示为:

ALxhTl8OTGM=

Base64 形式在转换器或集成开启 debug 时会出现在调试事件(Debug Events)中,方便排查报文内容。

上述报文拆解、HEX/Base64 两种表达以及字段对照表,均出自仓库帮助文档 simple-binary 示例的 payload 说明。


二、解码函数源码逐行剖析

完整示例代码位于 simple-binary 示例的 decoder_fn.md,核心逻辑如下:

// Use first 4 bytes as device name var deviceName = "SN-" + parseBytesToInt(payload, 0, 4); var result = { deviceName: deviceName, deviceType: "Thermometer", telemetry: { // Use 5th byte as a battery level battery: parseBytesToInt(payload, 4, 1), // Use bytes 6 and 7 as a temperature temperature: parseBytesToInt(payload, 5, 2) / 100.0, // Use 8th byte as a saturation level saturation: parseBytesToInt(payload, 7, 1) } }; return result;

逐行解读:

  1. parseBytesToInt(payload, 0, 4)—— 从字节数组payload的第 0 个字节起,连续读取 4 个字节,按大端序(big-endian)解释为整数,得到设备序列号12345678。前缀"SN-"使设备名变为SN-12345678,既直观又可读。
  2. parseBytesToInt(payload, 4, 1)—— 读取第 5 个字节(偏移 4),得到电量95。
  3. parseBytesToInt(payload, 5, 2) / 100.0—— 读取第 6、7 两个字节(偏移 5,长度 2),得到3660,再除以 100 还原出真实温度36.6。这是典型的“定点数”编码:设备端把浮点温度放大 100 倍后以整数传输,解码端再缩放回来,避免浮点字节序问题。
  4. parseBytesToInt(payload, 7, 1)—— 读取第 8 个字节(偏移 7),得到饱和度99。
  5. 函数返回包含deviceName、deviceType与telemetry的 JSON 对象,作为转换器输出。

提示:文档代码中出现的{:code-style="max-height: 500px;"}与{:copy-code}是 UI 帮助弹窗的渲染标记,并非 TBEL 语法,实际编写转换器时无需保留。


三、预期输出:平台统一 JSON 格式

解码函数运行后得到的结果与文档 output.md 完全一致:

{ "deviceName": "SN-12345678", "deviceType": "Thermometer", "telemetry": { "battery": 95, "temperature": 36.6, "saturation": 99 } }

平台收到该结果后,会按deviceName(租户范围内唯一)查找设备SN-12345678;若不存在且集成开启了“允许创建设备/资产”选项,则自动创建设备,类型为Thermometer。telemetry中的三个键值对将作为时序数据(time-series data)写入,默认使用服务器时间为时间戳(详见下文第五节)。


四、解码函数签名与输出格式的完整约束

在动手写自己的解码函数前,需要了解平台对函数签名与返回值的硬性要求。这些约束在通用帮助文档 TBEL 解码函数说明(decoder_fn.md) 中有系统化描述。

函数签名

function Decoder(payload, metadata): object | object[]
  • payload(any):包含集成上报原始消息的字节数组。集成产生的 payload 内容类型可能是 JSON、TEXT 或 BINARY(Base64),但内容类型只是调试事件存储的提示,不影响解码函数的行为——解码函数收到的始终是字节数组,可用decodeToString、decodeToJson把字节数组转为字符串或 JSON 对象后再处理。
  • metadata({[key: string]: string}):集成消息携带的键值元数据,可在每个集成的详情中配置额外的 metadata 字段,供解码函数读取使用。

返回值的必须与可选字段

返回值必须是合法 JSON,且满足:

  • 必须包含deviceName+deviceType,或assetName+assetType成对属性,用于标识设备/资产(名称在租户范围内唯一)。平台用它们查找已有实体;找不到且集成允许创建时,自动新建。实践中常用 DevEUI、MAC 地址等唯一标识作为设备名。
  • 可选attributes对象:为设备/资产设置的服务端属性集合。
  • 可选telemetry对象/数组:设备/资产的时序数据。
  • 可选customerName:自动把设备归属到指定客户(客户不存在时自动创建);仅在该设备/资产由当前集成创建时生效,已存在的实体忽略此参数。
  • 可选groupName:自动把设备加入实体组,组默认创建在租户范围,若同时提供了customerName则创建在客户范围;同样仅在实体由当前集成首次创建时生效。
  • 可选deviceLabel/assetLabel:非唯一的用户友好标签,可在仪表盘上替代设备名展示。
  • 若需自定义时间戳,可在 telemetry 数据中加入平台约定的时间戳字段,格式为Unix epoch 毫秒;否则使用服务器时间。

另外,解码函数可以返回对象数组(每个元素描述一台设备/资产),且每台设备可携带多条不同时间戳的时序数据点,适用于一个上行消息包含多设备数据的中继/网关场景。


五、decoder_v2 变体:attributes/telemetry 结构与显式时间戳

除上述 v1 扁平结构外,ThingsBoard 还提供了 decoder_v2 风格的同主题示例,同样解析“序列号 + 电量 + 温度 + 饱和度”的二进制帧,但返回值采用更结构化的格式,并支持显式时间戳。见 decoder_v2/simple-binary 的 decoder_fn.md:

function decodePayload(input) { var result = { attributes: {}, telemetry: {}}; result.attributes.sn = parseBytesToInt(input, 0, 4); var timestamp = metadata.ts; var values = {}; values.battery = parseBytesToInt(input, 4, 1); values.temperature = parseBytesToInt(input, 5, 2) / 100.0; values.saturation = parseBytesToInt(input, 7, 1); result.telemetry = { ts: timestamp, values: values }; return result; } var result = decodePayload(payload); return result;

该变体对应的报文为01ed03335f0e4c63(Base64:Ae0DM18OTGM=),其中01ED0333对应序列号32310067,其余字段含义与 v1 相同。解码输出见 decoder_output.md:

{ "attributes": { "sn": 32310067 }, "telemetry": { "ts": 1684478801936, "values": { "battery": 95, "temperature": 36.6, "saturation": 99 } } }

可见 decoder_v2 的约束与 v1 不同,主要体现在:

  • attributes为必填,且至少包含一个键值对;
  • telemetry为必填(对象或数组),至少包含一条数据;
  • telemetry.ts取自metadata.ts(集成注入的消息时间戳),即显式时间戳,为 Unix epoch 毫秒;
  • 实体名、类型、设备档案(profile)、客户、组、标签等可通过转换器的预配置设定,也可在解码函数中覆写。

最终 Converter 输出会把预配置信息与解码结果合并,见 converter_output.md,其形态包含entityType: "DEVICE"、name、profile、合并后的telemetry与attributes,其中 LoRaWAN 网关元数据(rssi、snr、fCnt、dr、frequency、eui等)也被一并合入——这正是前面所说的“LoRaWAN 网络服务器常把二进制设备载荷连同 RSSI/SNR 等元数据一起包装成 JSON”的真实场景。


六、parseBytesToInt 等 TBEL 内置函数的源码级定义

parseBytesToInt并非 JavaScript 原生函数,而是 ThingsBoard 向 TBEL 运行时注入的内置工具函数。其精确定义在 UI 前端源码 tbel-utils.models.ts 中:

parseBytesToInt(data, offset, length, bigEndian)

参数说明(来自源码定义):

  • data(list | array):待解析的字节列表/数组,即传入的payload。
  • offset(number,可选):起始字节索引,默认 0。
  • length(number,可选):要解析的字节数,最大 4(即最大 32 位整数)。
  • bigEndian(boolean,可选):是否按大端序解释,默认 true。

该函数返回解析后的整数(number)。与之配套的字节解析函数还有:

  • parseBytesToLong(data, offset, length, bigEndian):解析长整数,length最大 8(64 位),适用于需要 8 字节整数的设备字段(如计数器、时间戳),定义见 tbel-utils.models.ts。
  • parseBytesToFloat(data, offset, length, bigEndian):按 IEEE 754 格式解析为浮点数,定义见 tbel-utils.models.ts。
  • parseHexToLong(hex, bigEndian)/parseBigEndianHexToLong(hex)/parseLittleEndianHexToLong(hex):从十六进制字符串直接解析长整数,可指定字节序,适用于 payload 中内嵌 HEX 字符串字段的协议。
  • decodeToString(data):把字节列表转换为字符串,见 tbel-utils.models.ts。
  • decodeToJson(data):把 JSON 字符串或字节列表解析为 JSON 对象,见 tbel-utils.models.ts。
  • stringToBytes、bytesToBase64等用于编码方向的工具函数(如 encoder 场景)。

从源码结构看,这些内置函数统一定义于TBEL_UTILS等模型常量中,在加载 TBEL 脚本时注入执行环境,因此它们与 ECMAScript 标准函数一样可直接在 Decoder/Encoder 中调用。


七、实战要点与调试建议

  1. 先定协议后写代码:二进制解码的第一步永远是逐字节定义字段表(偏移、长度、缩放系数、字节序),就像示例文档在 payload 说明里对00BC614E、5F、0E4C、63逐段标注的做法。
  2. 字节序保持一致:parseBytesToInt默认大端序。若设备端按小端序发送(如常见的01ED0333类帧),需显式传入false:parseBytesToInt(payload, 0, 4, false)。
  3. 定点数缩放:遇到“温度 ×100”“湿度 ×10”这类协议,解码时记得除回缩放因子,避免把整数值误当真实测量值。
  4. 善用元数据:LoRaWAN 网络服务器的 JSON 包装中通常带有RSSI、SNR、frequency等元数据,可像 decoder_v2 示例那样通过metadata读取并合并进最终输出。
  5. 开启 Debug 验证:在转换器或集成上启用 debug 后,调试事件中会记录 Base64 形式的原始载荷与解码输出,可对照 payload/expected output 逐步验证字节解析是否正确。
  6. 设备唯一性:设备名在租户范围内唯一,示例用序列号加SN-前缀生成稳定名称,是避免重复创建设备的良好实践;需要仪表盘友好展示时再使用deviceLabel。

八、本文引用的仓库文档与源码清单

  • 示例主文档:simple-binary/decoder_fn.md
  • 报文拆解说明:simple-binary/payload.md
  • 预期输出:simple-binary/output.md
  • 解码函数通用约束:converter/tbel/decoder_fn.md
  • decoder_v2 规范:converter/tbel/decoder_fn_v2.md
  • decoder_v2 二进制示例:decoder_v2/simple-binary/decoder_fn.md
  • TBEL 内置函数定义:tbel-utils.models.ts

仓库中同目录还提供了 Simple JSON、Simple CSV、JSON with multiple hex encoded values、Use metadata fields 等更多解码示例(见 examples/decoder 目录),可作为编写不同内容类型转换器时的参考模板。

  • 物联网
  • 后端
  • 数据可视化
  • 消息队列

【免费下载链接】thingsboard

All-in-one IoT Platform - Device management, data collection, processing and visualization.

项目地址:https://gitcode.com/GitHub_Trending/th/thingsboard
点击查看免费下载

相关推荐

上一篇:mdx-bundler组件替换魔法:如何自定义MDX渲染行为的完整教程
下一篇:flutter-mapbox-gl 地图插件推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询