首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >以太网温湿度传感器数据封装:从Modbus TCP到私有协议的转换实践

以太网温湿度传感器数据封装:从Modbus TCP到私有协议的转换实践

原创
作者头像
BJ盛世宏博小程
发布于 2026-10-26 16:03:36
发布于 2026-10-26 16:03:36
1640
举报

以太网温湿度传感器数据封装:从Modbus TCP到私有协议的转换实践

物联网・盛世宏博・Modbus TCP、协议转换、温湿度传感器、数据封装、网关开发

关键词:Modbus TCP、私有协议、协议转换网关、以太网温湿度变送器、数据封装、帧解析、字节序、校验和、物联网采集转发、动环二次开发 标签:物联网协议开发、边缘网关、Modbus解析、自定义私有帧、温湿度采集转发、机房动环、智慧档案、协议适配开发

一、方案概述

在机房动环、智慧档案馆、配电室监控项目中,大量以太网温湿度变送器原生只提供标准Modbus TCP协议。很多上位平台、老款集中监控主机、行业专用中控系统不支持Modbus TCP,厂商定义了一套私有二进制协议用于通信。此时不能直接更换传感器,最佳方案就是在边缘侧做协议转换:边缘网关(或采集服务)作为中间层,向下作为Modbus TCP客户端轮询以太网温湿度传感器,拿到原始温湿度、状态数据;向上按照平台规定的私有协议格式重新组帧、封装、校验,转发给上层中控。

本文完整讲解这套转换链路的工程实践,包含Modbus TCP读取、原始数据换算、私有协议帧结构设计、组包、校验、粘包分包处理、重传机制、异常状态封装,附带Python原型代码。适用于自研边缘采集网关、动环前置采集程序,也可用于国产档案库房监控、变电站辅控系统对接场景。

适用场景区分: 向下:RJ45以太网温湿度传感器,Modbus TCP Slave; 转换中间层:Modbus TCP Master(客户端); 向上目标:TCP Server / UDP上报,输出自定义私有二进制协议; 不适用:Modbus RTU串口、SNMP传感器,链路逻辑不一样。

二、整体转换架构

整个链路分为四层,清晰划分职责,解耦底层采集和上层协议:

  1. 感知层:以太网温湿度传感器(Modbus TCP Slave) 寄存器存放原始温湿度(通常放大10倍整数)、设备状态位、故障码。
  2. 转换层:协议转换服务(核心)
    • 向下:Modbus TCP Master,定时轮询传感器、解析响应报文、数值换算;
    • 数据清洗:越界过滤、离线标记、故障状态识别;
    • 组帧封装:按照私有协议定义填充字段、计算校验、组装二进制帧;
    • 向上转发:TCP长连接或者UDP报文发送给中控平台;
    • 应答处理:接收平台下发的查询/设置指令,反向解析、翻译成Modbus TCP指令下发传感器。
  3. 传输层:TCP/UDP 私有协议载体,项目中UDP多用于主动上送、TCP多用于可靠查询交互。
  4. 应用层:上层中控/监控平台 只识别私有协议帧,不感知底层是Modbus传感器。

核心设计原则:单向采集优先,双向指令可选;采集逻辑和组帧逻辑分离,方便后续替换底层传感器或者修改上层私有帧格式。

常见误区:直接透传Modbus报文给平台,平台无法识别;或者在传感器固件上改协议,量产传感器固件修改成本高、不可维护,网关转换是工程上首选方案。

三、底层Modbus TCP读取规范(以太网温湿度传感器通用模型)

绝大多数国产以太网温湿度变送器保持一致的寄存器模型,这里作为转换输入源标准。

3.1 Modbus TCP参数

  • 功能码:03(读保持寄存器)
  • 从站ID(Unit ID):传感器从站号,很多网口变送器固定为1
  • 寄存器起始地址:40001(偏移0)
  • 寄存器数量:2个寄存器,寄存器0:温度原始值,寄存器1:湿度原始值
  • 原始值规则:int16,放大10倍,如 256 → 25.6℃,521 → 52.1%RH
  • 异常响应:传感器故障、断线、寄存器非法地址会返回Modbus异常码

Modbus TCP请求报文示例(十六进制)

代码语言:javascript
复制
00 01 00 00 00 06 01 03 00 00 00 02

解析:

  • 事务ID:00 01
  • 协议ID:00 00
  • 长度:00 06
  • UnitID:01
  • 功能码03,起始地址0,读2个寄存器

响应报文示例:

代码语言:javascript
复制
00 01 00 00 00 07 01 03 04 01 00 02 08

寄存器0值:0x0100 = 256 → 25.6℃;寄存器1:0x0208=520 →52.0%RH

3.2 数据换算公式

代码语言:javascript
复制
temp = raw_temp / 10.0
hum  = raw_hum / 10.0

额外状态位扩展(部分传感器):寄存器3:状态字,bit0=1探头断线故障。

四、私有协议帧设计(工程通用模板,可按需修改)

很多工控、档案、电力辅控私有协议是固定头 + 长度域 + 设备地址 + 命令码 + 数据域 + 校验和 + 帧尾结构。下面给出一套典型的二进制私有协议规范,作为转换输出标准,也是这类项目最常见的格式。

说明:不同甲方平台私有协议不一样,字段、校验方式、字节序(大端/小端)会变,本文重点是转换范式,字段可以按需替换。

私有协议帧定义(大端模式,字节序Big-Endian)

字段

字节长度

说明

帧头Header

2

固定标识:0xAA 0x55

帧长度Len

2

后面所有字节(从设备地址到校验前)总长度

设备Addr

1

传感器编号/点位地址,区分库房、机柜测点

Cmd

1

命令码:0x01=温湿度主动上送;0x02=平台查询指令;0x03=应答失败

DataLen

1

数据域有效字节长度

Data域

N

业务数据,存放温度、湿度、状态

CheckSum

1

累加和校验:前面所有字节求和,取低8位

帧尾Tail

2

固定结束 0x55 0xAA

Data域(Cmd=0x01,温湿度上报)内部结构:

字段

字节

说明

温度原始值

2

int16,放大10倍,和Modbus原始值保持一致,平台自行÷10解析

湿度原始值

2

int16

状态码

1

0正常;1探头故障;2传感器离线;3数值越界

时间戳

4

Unix时间戳,可选,部分平台不需要

示例:温度25.6℃(256),湿度52.0(520),状态正常0,不带时间戳 data = [0x01,0x00, 0x02,0x08, 0x00]

校验说明:累加和校验是私有协议最常用;部分平台用CRC16(Modbus CRC)、CRC32,只需要替换校验函数即可。

正反两个方向数据流

  1. 下行采集上行上报(主流程) 转换服务 → Modbus TCP读取传感器 → 拿到raw_temp,raw_hum,status → 组装上面私有帧 → 发给中控平台
  2. 平台下发查询指令(交互流程) 平台下发私有查询帧(Cmd=0x02) → 转换网关解析私有帧 → 翻译成Modbus TCP读寄存器指令下发传感器 → 拿到数据 → 封装应答帧回传平台

五、协议转换核心难点与处理方案

5.1 字节序问题(高频踩坑)

Modbus TCP寄存器默认大端;私有协议有的平台要求小端。 ✅ 做法:写单独的转换函数,统一封装字节序,不要硬编码。

坑:Modbus读到0x0100,小端输出就要反转成 00 01。

5.2 TCP粘包、分包问题

TCP是流协议,没有天然帧边界。私有协议依靠帧头+帧尾+长度域做帧分割。 ✅ 处理逻辑:维护接收缓冲区,持续append字节;循环在缓冲区查找帧头0xAA 0x55,读取长度域,判断是否收齐一整帧,取出完整帧,剩余数据留在buffer继续等待。 UDP天然无粘包,但是丢包风险高,需要增加序号、重传。

5.3 传感器离线、采集失败如何封装

不能直接不上报,平台会判定失联。规范做法:固定填充占位值,状态码标记故障。 例:采集超时,temp=0xFFFF hum=0xFFFF status=2(离线),组帧上报,平台识别状态码告警。

5.4 轮询频率控制

Modbus TCP轮询间隔建议310s,根据项目要求;不要毫秒级高频轮询,造成传感器和网关压力。 同一网关下多台传感器,串行轮询或者协程并发轮询,做好超时控制(12s超时)。

5.5 双向指令权限控制

平台下发写寄存器(修改传感器IP、阈值)等高权限指令,增加白名单校验,防止非法修改传感器参数。

六、Python原型代码:Modbus TCP采集 + 私有协议组帧转发

依赖库:pymodbus(现代pymodbus3.x版本,推荐,比老pyserial modbus易用)

代码语言:javascript
复制
pip install pymodbus

代码仅为转换逻辑原型,生产环境需要增加TCP长连接保活、粘包解析、日志、异常重试、多设备管理。

代码语言:javascript
复制
from pymodbus.client import ModbusTcpClient
import struct
import time

# ===================== 配置区 =====================
SENSOR_IP = "192.168.100.30"
SENSOR_PORT = 502
UNIT_ID = 1
PLATFORM_HOST = "192.168.100.10"
PLATFORM_PORT = 8080
POLL_INTERVAL = 5
# 私有协议常量
FRAME_HEAD = bytes([0xAA, 0x55])
FRAME_TAIL = bytes([0x55, 0xAA])
CMD_UPLOAD = 0x01
# ==================================================

def calc_checksum(data: bytes) -> int:
    """累加和校验,取低8位"""
    s = sum(data)
    return s & 0xFF

def build_private_frame(dev_addr: int, raw_temp: int, raw_hum: int, status: int):
    """
    组装私有协议上报帧
    :param dev_addr: 设备点位编号
    :param raw_temp: modbus原始温度int16
    :param raw_hum: modbus原始湿度int16
    :param status: 状态码
    :return: bytes 完整帧
    """
    # data域:raw_temp(2B) raw_hum(2B) status(1B)
    data = struct.pack(">hhB", raw_temp, raw_hum, status)
    data_len = len(data)
    # 长度域开始:dev_addr + cmd + datalen + data
    body = struct.pack(">BBB", dev_addr, CMD_UPLOAD, data_len) + data
    body_len = len(body)
    # 帧头 + 长度(2B) + body
    frame_body = struct.pack(">H", body_len) + body
    chk = calc_checksum(frame_body)
    full_frame = FRAME_HEAD + frame_body + bytes([chk]) + FRAME_TAIL
    return full_frame

def read_modbus_sensor():
    client = ModbusTcpClient(SENSOR_IP, port=SENSOR_PORT)
    try:
        conn = client.connect()
        if not conn:
            return None, None, 2  # 离线
        # 读保持寄存器,起始0,2个寄存器
        rsp = client.read_holding_registers(address=0, count=2, slave=UNIT_ID)
        if rsp.isError():
            return None, None, 1 # 故障
        raw_temp = rsp.registers[0]
        raw_hum = rsp.registers[1]
        return raw_temp, raw_hum, 0
    except Exception as e:
        return None, None, 2
    finally:
        client.close()

if __name__ == "__main__":
    import socket
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.connect((PLATFORM_HOST, PLATFORM_PORT))
    dev_addr = 1 # 该传感器点位编号
    while True:
        rt, rh, st = read_modbus_sensor()
        if st == 0:
            frame = build_private_frame(dev_addr, rt, rh, st)
        else:
            # 故障占位 0xFFFF
            frame = build_private_frame(dev_addr, 0xFFFF, 0xFFFF, st)
        sock.sendall(frame)
        print(f"发送私有帧: {frame.hex()}")
        time.sleep(POLL_INTERVAL)

代码说明

  1. read_modbus_sensor:向下读取Modbus TCP,返回原始整型值和状态码;
  2. build_private_frame:核心封装函数,按照上面私有协议结构打包二进制;
  3. calc_checksum:累加校验;
  4. TCP长连接发送给上层平台;生产环境需要单独写接收协程解析平台下行查询帧。

七、生产级扩展改造方向

  1. 多传感器管理:用配置文件JSON维护点位列表(传感器IP、从站号、点位地址),循环批量采集;
  2. 帧接收解析器:独立写状态机解析平台下发私有帧,处理查询指令,反向转Modbus;
  3. 协议可配置化:帧结构、校验方式、字节序、字段偏移写在配置文件,不用改代码适配不同甲方私有协议;
  4. 缓存与重传:UDP模式下增加帧序号、ACK应答、超时重传;
  5. 本地缓存:平台断连时,转换网关缓存历史数据,平台恢复后补传;
  6. 告警转发:状态异常(探头故障、离线)除上报私有帧,同步推送告警到运维接口;
  7. 日志审计:打印十六进制原始Modbus报文、输出私有帧,方便现场抓包比对调试;
  8. 迁移到C语言:正式边缘网关(Linux嵌入式)一般用C实现,Python仅用于PC侧原型验证。

八、调试手段(现场排查协议封装问题)

  1. Modbus侧验证:modbus poll工具连接传感器,确认寄存器数值正确;
  2. 抓包验证:Wireshark同时抓两个方向流量:
    • 502端口:Modbus TCP报文;
    • 平台端口:输出的私有二进制帧;
  3. 帧校验校验:拿十六进制帧手动计算校验和,核对和代码输出是否一致;
  4. 字节序验证:单独提取温度字段,和预期值比对,大小端是最容易出错点;
  5. 平台端模拟器:写一个简单TCP服务,接收私有帧打印hex,模拟中控调试,不用联调正式平台。

九、方案对比:三种对接方案取舍

方案

优点

缺点

适用场景

网关Modbus→私有协议转换(本文)

传感器通用、不改动固件、解耦、批量替换方便、标准化

开发转换程序、需要维护转换服务

绝大多数存量以太网温湿度对接私有中控平台

直接修改传感器固件输出私有协议

链路简单,无中间层

固件定制,成本高,无法使用标准传感器,后期维护麻烦

大批量定制项目、自有传感器研发

第三方商用协议网关

开箱即用,不用写代码

协议字段固定,私有协议特殊字段很难适配,价格高,黑盒不方便二次开发

标准成熟私有协议,无定制字段需求

十、方案总结

以太网温湿度传感器Modbus TCP转私有协议,本质是协议翻译+数据模型映射:底层采集标准化Modbus寄存器数据,在边缘层做数值清洗、状态编码,再按照甲方私有帧规范完成二进制封装、校验、转发。 这套模式在档案库房、配电室、IDC机房动环项目非常通用,优势是复用市面上大批量、标准化的Modbus TCP以太网温湿度变送器,不用定制传感器,只在网关层做适配;同时实现上下层解耦,底层传感器更换、上层平台协议版本迭代可以独立修改,互不影响。 开发重点把控:字节序、帧边界粘包处理、校验算法、故障状态统一封装,这四点是现场联调最容易卡住的地方。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

目录
  • 以太网温湿度传感器数据封装:从Modbus TCP到私有协议的转换实践
    • 一、方案概述
    • 二、整体转换架构
    • 三、底层Modbus TCP读取规范(以太网温湿度传感器通用模型)
      • 3.1 Modbus TCP参数
      • 3.2 数据换算公式
    • 四、私有协议帧设计(工程通用模板,可按需修改)
      • 私有协议帧定义(大端模式,字节序Big-Endian)
      • 正反两个方向数据流
    • 五、协议转换核心难点与处理方案
      • 5.1 字节序问题(高频踩坑)
      • 5.2 TCP粘包、分包问题
      • 5.3 传感器离线、采集失败如何封装
      • 5.4 轮询频率控制
      • 5.5 双向指令权限控制
    • 六、Python原型代码:Modbus TCP采集 + 私有协议组帧转发
      • 代码说明
    • 七、生产级扩展改造方向
    • 八、调试手段(现场排查协议封装问题)
    • 九、方案对比:三种对接方案取舍
    • 十、方案总结
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档