
ThingsBoard网关设备-子设备数据模型实战核心价值完整落地指南一、任务说明1.1 场景必要性在物联网IoT/工业物联网IIoT场景中「网关设备-子设备」层级数据模型是解决异构设备批量接入、统一管理、边缘自治的核心方案相比“所有设备平级接入”模式具备不可替代的价值协议适配子设备多为Modbus/OPC UA/BLE等非标准IoT协议设备网关作为“协议翻译官”将私有协议转换为ThingsBoard支持的MQTT/HTTP协议成本优化网关可批量管理子设备一次配置、复用策略减少云端连接数和带宽消耗断网时本地缓存数据避免丢失拓扑贴合匹配真实业务层级如「车间网关→产线传感器」便于故障定位和区域数据统计权限管控基于网关做权限隔离子设备自动继承网关权限避免越权访问。1.2 核心问题与解答核心问题详细解答子设备需要在平台上添加吗无需手动添加代码运行后平台会根据上报的子设备名称自动创建并归属到对应网关下可选手动添加若需提前配置子设备标签/告警规则可手动创建需保证设备名称与代码中一致。网关配置有何不同与普通设备唯一区别创建设备时需勾选「网关设备」选项其余配置如访问令牌、产品关联完全一致网关设备具备“代表子设备上报数据”的权限普通设备无此能力。子设备和网关之间如何查看从属关系看截图一图胜千言二、任务实施2.1 添加产品与普通产品添加流程完全一致仅需填写产品名称、描述、设备类型等基础信息无特殊配置。2.2 添加网关设备进入ThingsBoard「实体→设备」→「添加新设备」填写网关设备名称如modbus-gateway-01关键配置勾选「网关设备」选项保存后复制网关的「访问令牌」后续代码配置需用到。2.3 添加子设备自动添加推荐运行网关上报代码后平台会自动创建modbus-sensor-01、modbus-sensor-02两个子设备归属到上述网关下无需手动操作手动添加可选若需提前配置子设备属性/规则可手动新建设备名称需与代码中完全一致网关上报数据时会自动关联网友可自行测试该场景。2.4 添加设备到仪表盘进入仪表盘→点击右上角「编辑」铅笔图标新增/编辑部件在「数据」选项卡配置实体选择对应子设备如modbus-sensor-01数据键选择具体字段如temperature/humidity而非整个JSON对象保存后仪表盘将显示纯数值如23.7℃而非JSON字符串核心原则部件数据键需与代码上报的字段temperature/pressure一一对应否则无法正常显示。三、任务代码importjsonimporttimeimportrandomimportpaho.mqtt.clientasmqtt# 核心配置 TB_HOST192.168.111.53TB_PORT2883GATEWAY_TOKENHP7rwxQJHgVjrPRasWgi# # 子设备列表SUB_DEVICES[{name:modbus-sensor-01,type:温湿度传感器},{name:modbus-sensor-02,type:压力传感器},]# ThingsBoard网关上报子设备数据的固定MQTT主题TB_GATEWAY_TELEMETRY_TOPICv1/gateway/telemetry# 全局变量is_connectedFalseclient_idftb-gateway-{random.randint(1000,9999)}# 固定client_id方便日志追踪defon_connect(client,userdata,flags,rc):MQTT连接回调打印详细连接信息globalis_connected# 错误码对应说明rc_desc{0:连接成功,1:协议版本错误,2:无效的客户端ID,3:服务器不可用,4:用户名/密码错误,5:未授权令牌错误,6-255:保留错误码,}connect_descrc_desc.get(rc,f未知错误码{rc})# 打印详细连接日志print(f\n{*50}MQTT 连接信息{*50})print(f 客户端ID{client_id})print(f 连接地址{TB_HOST}:{TB_PORT})print(f 网关令牌{GATEWAY_TOKEN[:8]}...完整{GATEWAY_TOKEN})print(f 连接结果错误码{rc}描述{connect_desc})ifrc0:is_connectedTrueprint(f✅ 网关已成功连接ThingsBoard MQTT服务器)else:is_connectedFalseprint(f❌ 连接失败错误码{rc}→{connect_desc})print(f{*110}\n)defon_publish(client,userdata,mid):MQTT发布成功回调打印消息IDprint(f 消息发布成功MQTT消息ID{mid})defgenerate_sub_device_data(device_type):生成子设备数据打印数据生成日志if温湿度indevice_type:data{temperature:round(random.uniform(20.0,35.0),1),humidity:round(random.uniform(40.0,80.0),1),}elif压力indevice_type:data{pressure:round(random.uniform(0.5,3.0),2)}else:data{value:random.randint(0,100)}print(f\n 生成子设备数据类型{device_type}→ 数据{data})returndatadefpublish_sub_device_data(client):构造并上报子设备数据打印全量发布日志ifnotis_connected:print(f\n⚠️ 网关尚未连接is_connected{is_connected}跳过本次上报)return# 构造上报数据telemetry_data{}fordeviceinSUB_DEVICES:device_namedevice[name]device_typedevice[type]telemetry_data[device_name][generate_sub_device_data(device_type)]# 转换为JSON字符串格式化输出方便查看payloadjson.dumps(telemetry_data,ensure_asciiFalse,indent2)# 打印发布前的详细信息print(f\n{*50}准备发布消息{*50})print(f 发布主题{TB_GATEWAY_TELEMETRY_TOPIC})print(f 消息内容格式化\n{payload})print(f 消息内容原始{json.dumps(telemetry_data,ensure_asciiFalse)})print(f 消息长度{len(payload)}字节)# 发布消息resultclient.publish(topicTB_GATEWAY_TELEMETRY_TOPIC,payloadpayload,qos1,# QoS1确保至少送达一次)# 解析发布结果publish_rc_desc{0:发布成功,1:超出消息长度限制,2:客户端未连接,3:无效的主题,4:消息被服务器拒绝,5:未知错误,}publish_descpublish_rc_desc.get(result.rc,f未知错误码{result.rc})# 打印发布结果print(f\n 发布结果)print(f - 返回码(rc){result.rc}→{publish_desc})print(f - 消息ID(mid){result.midifresult.rc0else无})print(f - QoS级别{1})print(f{*110}\n)ifresult.rc!0:print(f❌ 上报失败错误码{result.rc}→{publish_desc})else:print(f✅ 上报成功消息ID{result.mid})defmain():globalis_connected# 打印启动信息print(f\n{*60}网关脚本启动{*60})print(f 启动时间{time.strftime(%Y-%m-%d %H:%M:%S,time.localtime())})print(f 配置信息TB_HOST{TB_HOST}, TB_PORT{TB_PORT}, CLIENT_ID{client_id})print(f{*120}\n)# 创建MQTT客户端clientmqtt.Client(client_idclient_id,clean_sessionFalse)# 注册回调函数client.on_connecton_connect client.on_publishon_publish# 设置认证client.username_pw_set(usernameGATEWAY_TOKEN,password)# 打印连接前信息print(f 正在尝试连接 MQTT 服务器{TB_HOST}:{TB_PORT})# 连接MQTT服务器try:connect_resultclient.connect(TB_HOST,TB_PORT,keepalive60)print(f connect() 函数返回值{connect_result}0成功非0失败)exceptExceptionase:print(f\n❌ 连接MQTT服务器时抛出异常)print(f - 异常类型{type(e).__name__})print(f - 异常信息{str(e)})print(f 检查1.TB服务器IP是否正确 2.服务器{TB_PORT}端口是否开放 3.网关令牌是否正确)return# 启动MQTT循环非阻塞client.loop_start()print(f\n MQTT循环已启动loop_start()等待1秒确保连接完成...)time.sleep(1)# 等待连接回调执行# 循环上报数据try:print(f\n 开始循环上报子设备数据每5秒一次按CtrlC停止...\n)count0whileTrue:count1print(f\n{#*20}第{count}次上报{#*20})publish_sub_device_data(client)time.sleep(5)exceptKeyboardInterrupt:print(f\n\n 用户按下CtrlC终止程序)exceptExceptionase:print(f\n\n❌ 运行时抛出未捕获异常)print(f - 异常类型{type(e).__name__})print(f - 异常信息{str(e)})finally:print(f\n{*50}清理资源{*50})print(f 停止MQTT循环loop_stop())client.loop_stop()print(f 断开MQTT连接disconnect())client.disconnect()print(f✅ 网关已断开连接程序正常退出)print(f{*110}\n)if__name____main__:main()3.1 代码关键说明1主题规范网关上报子设备数据的固定主题v1/gateway/telemetryThingsBoard官方约定不可修改普通设备上报主题为v1/devices/me/telemetry与网关主题严格区分平台通过主题识别数据归属类型。2数据格式核心格式{子设备名称: [{字段1: 值1, 字段2: 值2}]}列表[]的作用支持批量上报同一子设备的多条历史数据如断网缓存数据单条数据也需保留列表格式错误示例{子设备名称: {字段: 值}}无列表、{子设备名称: [{values: {字段: 值}}]}多余嵌套均会导致平台解析异常。四、任务测试4.1 测试效果截图4.2 关键日志输出 准备发布消息 发布主题v1/gateway/telemetry 消息内容格式化 { modbus-sensor-01: [ { temperature: 23.7, humidity: 69.8 } ], modbus-sensor-02: [ { pressure: 1.03 } ] } 消息内容原始{modbus-sensor-01: [{temperature: 23.7, humidity: 69.8}], modbus-sensor-02: [{pressure: 1.03}]} 消息长度157 字节 发布结果 - 返回码(rc)0 → 发布成功 - 消息ID(mid)145 - QoS级别1 ✅ 上报成功消息ID145 消息发布成功MQTT消息ID1454.3 查看设备从属关系只有代码运行之后才能生成子设备因此运行代码之后就能看从属关系了注意方向否则可能看不到结果查看网关的属性可以看到关联的2个子设备查看自设备属性可以看到父设备4.4 测试验证要点连接验证日志显示「✅ 网关已成功连接ThingsBoard MQTT服务器」无连接异常发布验证「返回码(rc)0」说明数据成功发布到平台设备验证平台「设备列表」自动出现modbus-sensor-01/modbus-sensor-02归属到对应网关数据验证子设备「最新遥测数据」显示temperature/humidity/pressure字段及对应数值仪表盘正常展示纯数值。五、任务总结掌握ThingsBoard「网关-子设备」数据模型的核心价值理解其在物联网场景中的必要性完成网关设备创建、子设备自动上报、仪表盘配置全流程落地实现基于MQTT协议的网关批量上报子设备数据代码内置详细日志便于问题排查。「网关-子设备」数据模型是ThingsBoard适配工业物联网场景的核心设计通过本实战可快速落地设备批量接入与统一管理。核心要点网关需勾选「网关设备」属性、上报主题固定为v1/gateway/telemetry、数据格式为扁平键值对列表包裹。掌握该模型后可轻松应对异构设备接入、批量管理等实际业务需求。