主题
5.5 消息格式规范(payload 与数据点的映射规则)
本节说明 MQTT payload 与数据点的映射规则。设备 publish 一条消息后,服务器会按一套固定规则把 payload 拆开写进实时数据库。规则不复杂,但不了解会产生意料之外的数据点。
5.5.1 两条基本前提
- 主题名就是数据点名。发布到主题
temperature,等于把值写进数据点temperature。不需要、也不支持{用户名}/data/{点名}这种三段式主题:身份由 CONNECT 报文的用户名密码提供,不出现在主题里。 - publish 一定会落库。只要 QoS0/QoS1 消息到达服务器就立刻写实时数据库(QoS2 在收到 PUBREL 后写)。同时 Broker 正常执行转发,两者互不影响。
5.5.2 四条映射规则
服务器先尝试把 payload 当 JSON 解析。只有解析成功且是 JSON 对象才走 JSON 分支,否则整个 payload 当一个值。
规则一:payload 不是 JSON 对象 → 数据点 = 主题名,值 = 整个 payload
topic: temperature
payload: 25.6
结果: temperature = 25.6这是推荐用法:一个主题对应一个点,payload 就是裸值。数字、字符串、true/false 都可以,服务器统一按字符串存。
注意:JSON 数组(如 [1,2,3])不是对象,也走这条规则,整个 [1,2,3] 会被当成 temperature 的值存进去。
规则二:JSON 对象里的 value 字段 → 数据点 = 主题名
topic: temperature
payload: {"value":25.6}
结果: temperature = 25.6value 是唯一被特殊对待的字段名,它的值直接写到主题同名的数据点上,不会变成 temperature/value。
规则三:JSON 对象里的其它字段 → 数据点 = 主题名/字段名
topic: motor
payload: {"speed":1450,"current":3.2}
结果: motor/speed = 1450
motor/current = 3.2一条消息批量上报多个点就靠这条规则。等价于分别发布到 motor/speed 和 motor/current。
规则四:主题为空 + JSON 对象 → 数据点 = 字段名本身
topic: (空)
payload: {"temperature":25.6,"humidity":60}
结果: temperature = 25.6
humidity = 60适合一次上报一批互不相关的点,不想让它们共享主题前缀。
5.5.3 规则会同时生效
同一条 JSON 里的不同字段各走各的规则。这是最容易被误解的地方:
topic: temperature
payload: {"value":25.6,"timestamp":1705312200,"quality":"good"}
结果: temperature = 25.6 ← value 走规则二
temperature/timestamp = 1705312200 ← 走规则三
temperature/quality = good ← 走规则三请勿直接套用其它平台的 JSON 格式
{value, timestamp, quality} 是很多平台的标准上报格式,但在这里 timestamp 和 quality 不会被当成元数据,而是各自变成一个真实的数据点写进数据库。数据点列表里会多出一堆 xxx/timestamp、xxx/quality,还会各自占用存盘配额。
服务器的时间戳由服务器自己打,不需要设备上报。只想上报一个值,直接把 payload 设成裸值(规则一)最稳妥。
5.5.4 值的类型
所有值最终都按字符串存入数据库。JSON 里的 25.6、"25.6"、true 存进去分别是 25.6、25.6、true。
云组态和事件触发在做数值比较时会把字符串转成数字,所以上报数字型数据时不用特意加引号,但要保证内容能被解析成数字(不要带单位,25.6℃ 会导致比较失败)。
嵌套的 JSON(字段值本身又是对象或数组)不会被递归展开,只有第一层字段名会被用来拼数据点名。需要多层结构时应把主题拆开分别发布。
5.5.5 命名建议
- 用
/分层,和数据点子键规则天然一致:车间1/1号机/温度 - 主题名支持中文(服务器按 UTF-8 处理),云组态里绑定的就是同一个名字
- 避免用
$开头:$是 MQTT 系统主题的保留前缀,不参与通配符匹配 - 一个点一个主题。把大量字段塞进一条 JSON 虽然可用,但数据点名难以维护
5.5.6 下发(服务器 → 设备)
服务器不预定义 command / alarm 这类固定路由。控制类语义由业务侧自行约定主题名和 payload。
实时数据库的值变化时,服务器会把变化推到 MQTT 侧,默认格式是 QoS0 + retain,payload 为 {"value":值}。设备订阅对应主题即可收到下发。
因为默认下发格式是 {"value":值},而规则二正好把 value 映射回主题同名点,所以上下行是自洽的:设备收到什么格式,原样发回去也能被正确解析。
5.5.7 需要完全自定义格式怎么办
上面四条是系统默认规则。如果对接的第三方设备的格式固定无法修改(比如必须用 {"d":{"t":25.6}} 这种私有结构),可以在服务器端写 Lua 策略函数接管解析:
mqtt_to_databus(topic, payload, len, qos, retain, client_id, is_utf8):上行接管,由脚本决定拆成哪些数据点databus_to_mqtt(点名, 点值):下行接管,由脚本决定发什么主题和 payload
两个函数返回 false 时系统按默认规则接管,返回 true 表示已由脚本处理完成。这样既能兼容私有格式,也不影响其它设备走默认规则。