1. 项目缘起与整体设计思路
1.1 为什么选以太网温湿度变送器这个方向
机房、药厂洁净车间、档案馆、温室大棚、锂电池老化房,这些场景有一个共同点:温湿度数据必须连续记录,而且一旦超标要能立刻被上层系统感知。传统的做法是RS485总线拉一串温湿度探头,末端接个串口服务器转成网络,再让上位机去轮询。这套方案能跑,但问题也很明显——串口服务器本身是个单点,配置繁琐,而且从探头到服务器之间那一段模拟/数字混合信号在长距离传输时容易被变频器、接触器干扰。
以太网温湿度变送器的思路是把TCP/IP协议栈直接塞进变送器内部,探头出来就是RJ45网口,直接挂在交换机上。每个变送器有独立IP,支持SNMP、Modbus TCP、TCP Server/Client等多种协议。我这次调试的这款设备,核心诉求就是两件事:一是把温湿度数据通过SNMP OID暴露给网管平台,二是用TCP长连接把实时数据推给自研的监控服务。前者解决“统一纳管”的问题,后者解决“低延迟推送”的问题。
1.2 双协议并行的架构考量
为什么不用单一协议?因为这两类需求的服务对象完全不同。SNMP面向的是Zabbix、LibreNMS、SolarWinds这类网管系统,它们习惯用轮询的方式定期拉取OID值,优点是标准化程度高、告警阈值可以在网管侧统一配置,缺点是轮询间隔通常最短也要30秒,实时性一般。而TCP长连接面向的是自研业务系统,服务端和变送器之间保持一条常连接,变送器主动上报或者服务端高频查询,延迟可以压到秒级甚至亚秒级。
两者并行不冲突,因为变送器内部是两套独立的协议栈在跑。SNMP走UDP 161端口,TCP长连接走自定义端口(我用的8000)。关键是要理解设备内部的资源分配——有些低端型号CPU性能有限,同时开SNMP和TCP Server会导致响应变慢,这个后面会细说。
1.3 整体调试路线图
我的调试顺序是这样的:先确认网络层通不通,再单独调SNMP,再单独调TCP长连接,最后两者同时开启做压力测试。这个顺序不能乱,因为如果一上来就双开,出了问题你根本不知道是哪套协议在捣鬼。具体分四个阶段:
- 阶段一:设备上电、网络配置、ping通、Web页面能打开
- 阶段二:SNMP OID遍历、找到温湿度对应的OID节点、用snmpwalk验证
- 阶段三:TCP长连接建立、数据帧格式解析、心跳与重连机制
- 阶段四:双协议并行、长时间稳定性观察、异常场景模拟
下面我按这个路线,把每个环节的细节、踩过的坑、以及最终可复现的配置方案完整写出来。
2. SNMP OID配置与调试实操
2.1 SNMP基础概念快速对齐
SNMP这东西,搞网络的人熟,搞物联网的人可能有点陌生。简单说,它就是一个“查字典”协议。设备内部维护一棵树,树上的每个节点有一个唯一编号,叫OID(Object Identifier)。你告诉设备“我要查1.3.6.1.2.1.1.1.0这个节点”,设备就把系统描述返回给你。温湿度变送器厂商通常会在私有企业OID分支下定义温湿度节点,比如.1.3.6.1.4.1.XXXXX.1.1.1.0代表温度,.1.3.6.1.4.1.XXXXX.1.1.2.0代表湿度。
这里有个关键点:不同厂商的私有OID完全不同,甚至同一厂商不同型号也可能不一样。所以第一步永远是拿到设备的MIB文件或者OID对照表。我这次用的设备,厂商给了一个PDF,里面列了十几个OID,包括温度、湿度、露点、设备状态、告警阈值等。
2.2 设备端SNMP参数配置
进入设备的Web管理页面,找到SNMP配置区域。需要填的参数包括:
| 参数项 | 推荐值 | 说明 |
|---|---|---|
| SNMP版本 | v2c | v1太老,v3配置复杂且部分网管平台兼容性一般 |
| 读团体名 | 自定义,不用public | 安全基本要求 |
| 写团体名 | 禁用或单独设置 | 只读场景不需要写权限 |
| Trap目标地址 | 网管服务器IP | 用于设备主动上报告警 |
| Trap端口 | 162 | SNMP Trap标准端口 |
| 系统名称 | 设备位置标识 | 方便网管平台识别 |
| 系统位置 | 实际物理位置 | 同上 |
配置完保存,设备可能会重启网络服务,等十几秒。
注意:有些设备的SNMP团体名修改后需要重启整个设备才生效,不是重启网络服务。如果改完发现还是旧团体名能读,先别怀疑自己,重启设备试试。
2.3 用snmpwalk验证OID
在Linux服务器上安装net-snmp工具包,Windows上可以用snmpwalk.exe。命令格式:
snmpwalk -v 2c -c 你的团体名 192.168.1.100 .1.3.6.1.4.1如果设备响应正常,你会看到一大串OID和对应的值。重点找温度湿度相关的节点。我这次拿到的设备,温度OID返回的是整数值,比如235代表23.5℃,需要除以10。湿度同理。这个缩放因子一定要跟厂商确认,有的设备是除以10,有的是除以100,搞错了数据就完全不对。
如果snmpwalk超时,按以下顺序排查:
- 确认设备IP能ping通
- 确认团体名正确(大小写敏感)
- 确认snmpd服务在服务器上正常运行
- 确认中间没有防火墙拦截UDP 161
- 确认设备SNMP功能已启用
2.4 网管平台对接要点
我用的是Zabbix做验证。在Zabbix里添加主机,接口选SNMP,填设备IP和端口161,宏里面设置团体名。然后创建监控项,Key填对应的OID。这里有个细节:Zabbix的SNMP监控项Key格式是oid["1.3.6.1.4.1.XXXXX.1.1.1.0"],注意引号和方括号。
预处理里面加一个“自定义乘数”,值填0.1,这样Zabbix存的就是实际温度值。触发器可以设温度大于30℃告警,湿度大于80%告警。
实测下来,Zabbix每30秒轮询一次,设备响应时间在20-50ms之间,完全够用。但如果把轮询间隔改成5秒,部分低端变送器会出现响应超时,因为SNMP查询本身有开销,设备CPU要处理协议栈、读传感器、组包回复,频率太高扛不住。
实操心得:SNMP轮询间隔不要低于15秒。如果确实需要高频采集,走TCP长连接那条路,别为难SNMP。
3. TCP长连接协议调试与实现
3.1 为什么需要TCP长连接
SNMP是轮询模型,服务端主动问,设备被动答。但有些场景需要设备主动推,比如温度突变时立刻上报,或者服务端需要毫秒级延迟的数据流。这时候TCP长连接就更合适。变送器作为TCP Client主动连服务端,或者作为TCP Server等待服务端连入,两种模式我都试过。
作为TCP Client的好处是设备主动上线,服务端不用维护设备IP列表,适合设备在NAT后面的场景。作为TCP Server的好处是服务端可以随时连入查询,适合设备IP固定的内网环境。我这次两种模式都配了,下面分别说。
3.2 设备端TCP参数配置
在Web页面找到TCP配置区域:
| 参数项 | Client模式值 | Server模式值 |
|---|---|---|
| 工作模式 | TCP Client | TCP Server |
| 目标IP | 服务器IP | 不填 |
| 目标端口 | 8000 | 8000 |
| 本地端口 | 随机 | 8000 |
| 心跳间隔 | 30秒 | 30秒 |
| 心跳内容 | 自定义HEX | 自定义HEX |
| 重连间隔 | 5秒 | 不适用 |
| 数据格式 | HEX或ASCII | HEX或ASCII |
心跳机制非常关键。TCP连接虽然理论上是长连接,但中间经过路由器、防火墙、NAT设备时,空闲连接可能被静默断开。心跳包就是定期发一点数据,保持连接活跃。我设的30秒,实测在普通企业网络里很稳。
3.3 数据帧格式解析
设备上报的数据帧格式,厂商文档里写的是:
帧头(2字节) + 设备地址(1字节) + 功能码(1字节) + 数据长度(1字节) + 数据(N字节) + 校验(2字节)具体到温湿度,数据段是4个字节:温度高字节、温度低字节、湿度高字节、湿度低字节。温度值 = (高字节<<8 | 低字节) / 10.0。湿度同理。
我用Python写了一个简单的服务端来接收和解析:
import socket import struct def parse_frame(data): if len(data) < 7: return None header = data[0:2] addr = data[2] func = data[3] length = data[4] payload = data[5:5+length] checksum = data[5+length:7+length] if func == 0x03: temp_raw = struct.unpack('>h', payload[0:2])[0] humi_raw = struct.unpack('>h', payload[2:4])[0] temp = temp_raw / 10.0 humi = humi_raw / 10.0 return {'addr': addr, 'temp': temp, 'humi': humi} return None server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('0.0.0.0', 8000)) server.listen(5) print('TCP Server listening on 8000...') while True: conn, addr = server.accept() print(f'Connection from {addr}') while True: data = conn.recv(1024) if not data: break result = parse_frame(data) if result: print(f"设备{result['addr']}: 温度{result['temp']}℃ 湿度{result['humi']}%") conn.close()这段代码跑起来后,设备连上来,每30秒心跳一次,心跳帧的功能码不是0x03,解析函数会返回None,不影响。当服务端主动发查询指令时,设备返回0x03功能码的数据帧,解析出温湿度。
3.4 连接稳定性与重连策略
TCP长连接最怕的是“假连接”——连接状态显示ESTABLISHED,但实际数据发不过去。这种情况通常发生在中间网络设备老化或者路由切换时。解决办法是双向心跳:设备发心跳给服务端,服务端也定期发查询给设备,任何一方连续3次收不到对方数据就主动断开重连。
我在服务端加了一个定时器,每10秒给所有连接的设备发一次查询指令。如果某个设备连续3次没回复,就关闭连接,等设备自己重连。设备端的重连间隔设的5秒,实测断网恢复后,设备在10秒内就能重新连上。
踩过的坑:有一次设备重连后,服务端旧连接没清理,导致同一个设备出现两个连接,数据重复。后来在服务端加了逻辑:新连接建立时,根据设备地址踢掉旧连接。
4. 双协议并行与Modbus TCP的取舍
4.1 SNMP与TCP长连接同时开启的注意事项
两套协议同时跑,设备CPU负载会上升。我实测的这款设备,单独开SNMP时CPU占用约15%,单独开TCP Server时约20%,两个都开约35%。看起来不高,但这是在轮询间隔30秒、心跳间隔30秒的情况下。如果把SNMP轮询改成5秒,CPU直接飙到70%以上,TCP响应开始出现延迟。
所以双协议并行的第一条经验:SNMP轮询间隔和TCP心跳间隔不要同时设得太短。建议SNMP不低于15秒,TCP心跳不低于10秒。如果确实需要高频数据,只走TCP,SNMP只用来做设备状态监控,不采实时值。
第二条经验:端口不要冲突。SNMP用UDP 161,TCP长连接用8000,Modbus TCP用502,各走各的。但有些设备Web管理页面也用80和443,配置时注意别把管理端口改了导致自己进不去。
4.2 Modbus TCP的适用场景
这款变送器还支持Modbus TCP,端口502。Modbus TCP的好处是通用性极强,PLC、组态软件、SCADA系统基本都支持。如果你的上位系统是组态王、力控、WinCC这类,用Modbus TCP最省事,直接填寄存器地址就能读。
Modbus TCP的寄存器地址和SNMP OID是两套体系。我这款设备,温度在保持寄存器40001(对应地址0),湿度在40002(对应地址1)。用Modbus Poll测试时,功能码选03,起始地址0,数量2,就能读到两个寄存器的值。
但Modbus TCP有个问题:它是请求-响应模型,不支持主动上报。而且寄存器地址从0还是从1开始,不同厂商实现不一样,这个坑很深。我遇到过设备文档写40001,实际要填0才能读到的情况。判断方法很简单:填0读不到就填1,填1读不到就填0,试两次就知道了。
4.3 三种协议的选择决策表
| 场景 | 推荐协议 | 理由 |
|---|---|---|
| 接入Zabbix/LibreNMS等网管平台 | SNMP | 网管平台原生支持,告警配置方便 |
| 自研监控系统,需要秒级数据 | TCP长连接 | 延迟低,可主动推送 |
| 接入PLC或组态软件 | Modbus TCP | 工业标准,兼容性最好 |
| 设备在NAT后,服务端无法主动连 | TCP Client模式 | 设备主动上线,穿透NAT |
| 只需要定期记录,不需要实时 | SNMP | 配置简单,资源占用低 |
我最终的方案是:SNMP接入Zabbix做设备状态监控和阈值告警,TCP长连接接入自研系统做实时数据展示和历史存储,Modbus TCP备用,方便现场调试时用Modbus Poll快速验证传感器好坏。
5. 常见问题与排查技巧实录
5.1 SNMP相关问题速查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| snmpwalk超时 | 团体名错误 | 确认大小写,确认设备端已启用SNMP |
| snmpwalk返回noSuchObject | OID错误 | 核对MIB文件,确认OID前缀 |
| 温度值明显偏大或偏小 | 缩放因子错误 | 确认是除以10还是除以100 |
| 网管平台显示设备离线 | 轮询间隔太短 | 调整为30秒以上 |
| Trap收不到 | 目标地址或端口错误 | 确认UDP 162未被防火墙拦截 |
5.2 TCP长连接相关问题速查
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 设备连不上服务端 | 目标IP或端口错误 | 用telnet测试端口连通性 |
| 连接建立后很快断开 | 心跳间隔太长被中间设备断开 | 缩短心跳间隔到30秒以内 |
| 数据解析乱码 | 数据格式设置错误 | 确认设备端是HEX还是ASCII |
| 重连后数据重复 | 旧连接未清理 | 服务端根据设备地址踢旧连接 |
| 长时间运行后无数据 | 假连接 | 双向心跳,超时主动断开重连 |
5.3 网络层排查通用步骤
不管哪种协议出问题,先按这个顺序查:
- ping设备IP,确认网络层通
- telnet设备端口,确认传输层通
- 用Wireshark抓包,确认应用层数据有没有来
- 检查设备Web页面配置是否保存成功
- 重启设备,排除偶发故障
独家技巧:Wireshark抓包时,过滤条件用
ip.addr == 设备IP,这样能看到所有跟设备相关的流量,包括SNMP和TCP。如果看到设备发了数据但服务端没收到,问题在中间网络;如果设备根本没发,问题在设备配置。
5.4 长时间运行稳定性观察
我让这套系统连续跑了72小时,记录了几个关键指标:
- SNMP轮询成功率:99.8%,失败的基本是网络抖动
- TCP长连接断线次数:2次,都是因为机房交换机重启
- 设备CPU温度:稳定在45℃左右,没有过热
- 数据存储:72小时约26万条记录,数据库压力很小
这个稳定性对于大多数场景已经够用了。如果对可用性要求更高,可以考虑双机热备或者多路径网络。
6. 实操心得与后续扩展方向
6.1 配置备份与批量部署
调通一台设备后,第一件事是把配置导出备份。Web页面一般有“导出配置”按钮,导出一个JSON或XML文件。批量部署时,改一下IP地址和位置标识,直接导入就行。我这次部署了12台,手动配第一台花了40分钟,后面11台用导入配置的方式,每台不到5分钟。
如果设备支持SNMP Set,还可以写脚本批量改配置。但SNMP Set有风险,改错了可能把设备搞失联,建议只在测试环境用。
6.2 数据存储与可视化
TCP长连接收到的数据,我存到了InfluxDB里,用Grafana做可视化。温度湿度曲线、超标告警、历史查询都很方便。InfluxDB的写入性能很好,每秒几千条没问题。Grafana的告警规则可以设温度大于30℃持续5分钟触发,比SNMP Trap更灵活。
6.3 后续可以扩展的方向
这套架构跑通后,扩展空间很大。比如:
- 增加MQTT协议支持,接入更上层的物联网平台
- 在服务端加数据清洗逻辑,过滤掉明显异常的跳变值
- 用设备端的告警Trap做即时通知,结合TCP长连接做数据补传
- 多台设备做时间同步,确保数据时间戳一致
我个人在实际操作中的体会是,以太网温湿度变送器这个品类,硬件本身差异不大,真正拉开差距的是协议栈的稳定性和配置的灵活性。SNMP和TCP长连接双协议并行,是目前性价比最高的方案——SNMP保证标准化纳管,TCP保证实时性,两者互补,覆盖了绝大多数温湿度监控场景。最后再分享一个小技巧:调试阶段把设备的Web页面、SNMP、TCP、Modbus全部打开,用Wireshark同时抓包,能非常直观地看到四套协议各自的数据流,对理解设备内部工作机制帮助很大。