多协议传感设备调试:以太网温湿度变送器 SNMP、TCP 通讯实战

0
物联网 #Modbus #TCP/IP #UDP #SNMP #POE供电 #以太网温湿度传感器 #网口温湿度变送器 #工业现场 #环境监测 #Wireshark #Python
前面把 asyncio 高并发采集、多协议固件开发、EMC 电磁兼容、PoE 功率分配、双供电切换、Wireshark 排障、车间落地、现场测试全流程全部讲透了。这一篇聚焦一个非常实际的问题——
当你拿到一台多协议以太网温湿度变送器,SNMP 和 TCP(Modbus TCP)两种协议都要用,调试时该怎么下手。
0
先说一个现场真实场景:
甲方要求:温湿度数据走 Modbus TCP 进动环平台做实时采集和联动,同时要求 SNMP 接入 IT 运维的 Zabbix 做设备存活监控和端口状态查看。 设备到了,你配好 IP,Modbus Poll 一跑,数据正常。然后打开 Zabbix,SNMP 模板挂上去,发现——
读不到温度
。 你以为是 SNMP 社区名不对,改了 public、private、自定义,都不行。 你以为是 OID 不对,用 snmpwalk 手动跑,发现设备返回
Timeout: No Response from 192.168.100.50
。 你以为是防火墙拦了 UDP 161,关了防火墙还是不行。 最后发现:这台设备的 SNMP Agent
默认关闭
,需要在 Web 管理界面手动启用。而你之前根本不知道这台设备有 Web 管理界面——说明书 200 页,这一行藏在第 147 页的"高级功能"里。
一、先搞清楚:你的设备到底支持什么
1. 三种常见多协议模式
| 模式 | 说明 | 典型设备 |
| 双栈并行 | SNMP Agent + Modbus TCP Server 同时运行,互不干扰 | 大多数中高端工业传感器 |
| 协议切换 | 同一端口号可通过配置切换为 SNMP 或 Modbus TCP(不同时运行) | 部分低端设备 |
| 网关模式 | 设备本身是一个 SNMP Agent,但温湿度数据通过内部轮询 Modbus 从站获取 | 带多协议转换的工业网关 |
调试前必做
:登录设备 Web 界面(或串口配置工具),确认 SNMP 和 Modbus TCP 是否
同时启用
。很多设备默认只开 Modbus TCP,SNMP 需要手动开启。
2. 快速确认清单
□ 设备是否有 Web 管理界面?(浏览器访问 IP) □ SNMP 功能是否默认开启?(查看 Web 界面或说明书) □ SNMP 版本支持:v1 / v2c / v3?(多数设备只支持 v2c) □ 社区名(Community):默认 public?还是自定义? □ SNMP 端口:默认 161?是否可修改? □ Modbus TCP 端口:默认 502?是否可修改? □ 两种协议是否共享同一 IP 和端口?还是独立端口? □ 设备是否有 MIB 文件?(.mib 或 .txt 格式)
复制
二、SNMP 调试实战

0
1. 从零开始的 SNMP 调试流程
第 1 步:确认 SNMP Agent 已启用 → 浏览器登录设备 Web 界面 → 找到 SNMP 配置页面 → 确认 "Enable SNMP" 已勾选 → 记录社区名、SNMP 版本、端口号 第 2 步:从命令行测试基础连通性 → snmpwalk -v 2c -c public IP 1.3.6.1.2.1.1.1 → 如果返回系统描述信息,说明 SNMP 基本正常 第 3 步:找到温湿度 OID → 用 snmpwalk 遍历整个 MIB 树 → 或查阅设备 MIB 文件,找到温度/湿度对应的 OID 第 4 步:验证数据正确性 → 对比 SNMP 读取值与 Modbus TCP 读取值 → 确认缩放因子、数据类型一致
复制
2. 常用 SNMP 命令行工具
# 安装(Ubuntu/Debian)
apt install snmp snmp-mibs-downloader
# 基础查询
snmpget -v 2c -c public 192.168.100.50 1.3.6.1.2.1.1.1.0
# 返回:系统描述(设备型号、固件版本等)
# 遍历子树
snmpwalk -v 2c -c public 192.168.100.50 1.3.6.1.4.1
# 指定 MIB 文件
snmptranslate -Td -m ./device.mib TEMP-SENSOR-MIB::temperature
# 批量查询
for oid in 1.3.6.1.4.1.50001.1.1.1 1.3.6.1.4.1.50001.1.1.2; do
snmpget -v 2c -c public 192.168.100.50 $oid
done
# 持续监控(每 5 秒查询一次)
while true; do
snmpget -v 2c -c public 192.168.100.50 1.3.6.1.4.1.50001.1.1.1.0
sleep 5
done
复制
3. Python SNMP 读取
from pysnmp.hlapi import *
def snmp_get(ip, community, oid):
"""SNMP GET 单个 OID"""
iterator = getCmd(
SnmpEngine(),
CommunityData(community),
UdpTransportTarget((ip, 161), timeout=2.0, retries=2),
ContextData(),
ObjectType(ObjectIdentity(oid))
)
errorIndication, errorStatus, errorIndex, varBinds = next(iterator)
if errorIndication:
print(f"SNMP 错误: {errorIndication}")
return None
elif errorStatus:
print(f"SNMP 状态错误: {errorStatus.prettyPrint()}")
return None
else:
for varBind in varBinds:
return varBind[1].prettyPrint()
# 示例:读取温度和湿度
ip = "192.168.100.50"
community = "public"
# 假设 OID(需根据实际 MIB 替换)
temp_oid = "1.3.6.1.4.1.50001.1.1.1.0"
humid_oid = "1.3.6.1.4.1.50001.1.1.2.0"
temp = snmp_get(ip, community, temp_oid)
humid = snmp_get(ip, community, humid_oid)
print(f"温度: {temp} ℃")
print(f"湿度: {humid} %RH")
复制
4. SNMP 常见问题排查
| 问题 | 现象 | 排查方法 | 解决 |
| SNMP 未启用 | snmpwalk 超时 | 登录 Web 界面检查 | 启用 SNMP Agent |
| 社区名错误 | No Such Name 或超时 | 尝试 public、private | 确认设备配置的社区名 |
| OID 不存在 | No Such Instance | snmpwalk 遍历整棵树 | 查阅 MIB 文件确认正确 OID |
| 数据类型不匹配 | 读到的数值异常大 | 检查数据类型(INTEGER/Float) | 按 MIB 定义解析 |
| 缩放因子不对 | 温度显示 224 而不是 22.4 | 检查 MIB 中的 DISPLAY-HINT | 应用缩放因子 ×0.1 |
| UDP 被防火墙拦截 | 超时 | 检查 iptables / 交换机 ACL | 放行 UDP 161 |
| 设备 SNMP 栈崩溃 | 偶尔响应,偶尔超时 | 重启设备后恢复 | 升级固件或限制查询频率 |
三、TCP(Modbus TCP)调试实战
1. Modbus TCP 调试流程
第 1 步:确认 TCP 502 端口可达 → nc -zv IP 502 → 或 telnet IP 502 第 2 步:用 Modbus Poll 读取寄存器 → 连接 → TCP/IP → 输入 IP、端口 502 → 设置从站 ID、功能码 03、起始地址、数量 → 观察数据 第 3 步:验证数据格式 → 确认寄存器类型(INT16/UINT16/INT32/Float) → 确认字节序(ABCD/CDAB/BADC/DCBA) → 确认缩放因子(×0.1 / ×0.01 / 无) 第 4 步:Python 脚本批量验证 → 用 pymodbus 读取,自动对比预期值
复制
2. Python Modbus TCP 读取
import asyncio
from pymodbus.client import AsyncModbusTcpClient
async def read_sensor(ip, port=502, slave_id=1):
client = AsyncModbusTcpClient(ip, port=port, timeout=2.0)
await client.connect()
if not client.connected:
print(f" {ip} 连接失败")
return
# 读取保持寄存器 0-3(温度、湿度、PM2.5、压差)
resp = await client.read_holding_registers(0, count=4, slave=slave_id)
if resp.isError():
print(f" {ip} 读取失败: {resp}")
else:
temp = resp.registers[0] * 0.1
humid = resp.registers[1] * 0.1
pm25 = resp.registers[2]
pressure = resp.registers[3]
print(f" {ip}:")
print(f" 温度: {temp} ℃")
print(f" 湿度: {humid} %RH")
print(f" PM2.5: {pm25} μg/m³")
print(f" 压差: {pressure} Pa")
await client.close()
# 批量读取
async def batch_read(ips):
for ip in ips:
await read_sensor(ip)
ips = ["192.168.100.50", "192.168.100.51", "192.168.100.52"]
asyncio.run(batch_read(ips))
复制
3. Modbus TCP 常见问题排查
| 问题 | 现象 | 排查方法 | 解决 |
| 端口不可达 | 连接超时 | nc -zv IP 502 | 检查设备是否启用 Modbus TCP |
| 从站 ID 错误 | 返回非法功能码 | 尝试 ID 1、255、设备标签值 | 确认设备从站地址 |
| 地址偏移不对 | 读到 0 或乱码 | 尝试 0-based 和 1-based | 确认组态软件地址模式 |
| 字节序不对 | 数值异常 | 读 UINT16 和 INT16 对比 | 调整字节顺序 |
| 缩放因子不对 | 数值偏大 10 倍 | 对比标准温湿度计 | 设置 ÷10 或 ×0.1 |
| 连接数满 | 偶尔连不上 | 检查已建立连接数 | 限制并发或重启设备 |
| TCP Keepalive 未开 | 半开连接 | Wireshark 抓包 | 开启 Keepalive |
四、SNMP 与 Modbus TCP 数据对比验证
1. 为什么需要对比
| 场景 | 原因 |
| 甲方同时要求两种协议 | 动环用 Modbus,IT 运维用 SNMP |
| 数据一致性验证 | 确认两种协议读到的数据一致 |
| 故障定位 | 如果 Modbus 读不到但 SNMP 能读到,说明 Modbus 配置有问题 |
| 验收测试 | 两种协议都要验证,确保功能完整 |
2. 对比脚本
import asyncio
from pymodbus.client import AsyncModbusTcpClient
from pysnmp.hlapi import *
async def compare_protocols(ip, snmp_community, modbus_port=502, slave_id=1):
"""对比 SNMP 和 Modbus TCP 读到的数据"""
print(f"n{'='*50}")
print(f"设备: {ip}")
print(f"{'='*50}")
# Modbus TCP 读取
mb_client = AsyncModbusTcpClient(ip, port=modbus_port, timeout=2.0)
await mb_client.connect()
if mb_client.connected:
mb_resp = await mb_client.read_holding_registers(0, count=2, slave=slave_id)
if not mb_resp.isError():
mb_temp = mb_resp.registers[0] * 0.1
mb_humid = mb_resp.registers[1] * 0.1
print(f"Modbus TCP: 温度={mb_temp}℃, 湿度={mb_humid}%RH")
else:
print("Modbus TCP: 读取失败")
mb_temp, mb_humid = None, None
else:
print("Modbus TCP: 连接失败")
mb_temp, mb_humid = None, None
await mb_client.close()
# SNMP 读取(同步方式,简化示例)
# 实际使用时需要安装 pysnmp 并配置正确的 OID
print("SNMP: 需要配置正确的 OID(参考设备 MIB 文件)")
print(" 请手动执行: snmpwalk -v 2c -c public IP 1.3.6.1.4.1")
# 如果两种协议都成功,对比数据
if mb_temp is not None:
print(f"n数据对比提示:")
print(f" Modbus 温度: {mb_temp}℃")
print(f" SNMP 温度: 请手动对比")
print(f" 偏差应 < 0.5℃(传感器自身精度)")
# 运行对比
ips = ["192.168.100.50"]
for ip in ips:
asyncio.run(compare_protocols(ip, "public"))
复制
五、Zabbix 集成实战
1. Zabbix SNMP 模板配置
步骤 1:创建主机
→ 配置 → 主机 → 创建主机
→ 主机名称: Sensor-A01
→ 群组: Temperature Sensors
→ 接口: SNMP, IP 地址, 端口 161
步骤 2:配置 SNMP 社区
→ 宏 → 创建宏
→ 宏: {$SNMP_COMMUNITY}
→ 值: public
步骤 3:创建监控项
→ 监控项 → 创建监控项
→ 名称: 温度
→ 类型: SNMPv2 代理
→ 键值: sensor.temp
→ SNMP OID: 1.3.6.1.4.1.50001.1.1.1.0
→ 信息类型: 数字(浮点数)
→ 单位: ℃
→ 更新间隔: 30s
步骤 4:创建触发器
→ 触发器 → 创建触发器
→ 名称: 温度过高
→ 表达式: last(/Sensor-A01/sensor.temp) > 30
→ 严重性: 警告
步骤 5:创建图形
→ 图形 → 创建图形
→ 名称: 温湿度趋势
→ 添加监控项: 温度、湿度
复制
2. Zabbix Modbus TCP 配置
方法 1:使用 Zabbix 内置的 Modbus 监控项(Zabbix 5.0+) → 监控项类型: Modbus → 端点: tcp://192.168.100.50:502 → 从站 ID: 1 → 寄存器类型: Holding register → 寄存器地址: 0 → 数据类型: INT16 → 转换: ×0.1 方法 2:使用外部脚本(Zabbix agent + Python) → 编写 Python 脚本读取 Modbus TCP → Zabbix agent 调用脚本 → 返回 JSON 格式数据
复制
六、Wireshark 同时抓 SNMP 和 Modbus TCP
1. 抓包过滤器
# 同时抓 SNMP(UDP 161)和 Modbus TCP(TCP 502) tcp port 502 or udp port 161
复制
2. 显示过滤器
# SNMP 请求和响应 snmp # Modbus TCP 请求和响应 modbus # 同时看两种协议 tcp.port == 502 || udp.port == 161 # 按设备 IP 过滤 ip.addr == 192.168.100.50 && (tcp.port == 502 || udp.port == 161)
复制
3. 分析要点
| 协议 | 关注点 |
| SNMP | GET/GETNEXT 请求是否完整、响应是否超时、是否有 SNMPv3 认证失败 |
| Modbus TCP | MBAP 头 Transaction ID 是否递增、功能码是否正确、寄存器数据是否合理 |
| 时间线 | 两种协议的请求是否互相影响(TCP 重传是否阻塞 UDP) |
七、常见返工点
| 问题 | 后果 | 正确做法 |
| 不确认 SNMP 是否启用 | 调试半天发现功能没开 | 先登录 Web 界面确认 |
| 不查 MIB 文件 | 用错 OID | 向厂家索要 MIB 文件 |
| 社区名用默认 public | 安全审计不通过 | 改为自定义社区名 |
| SNMP 和 Modbus 数据不一致 | 验收时被质疑 | 对比验证,确认缩放因子一致 |
| Zabbix 模板用错数据类型 | 图形显示异常 | 确认 SNMP 返回的数据类型 |
| 不测试并发 | 现场同时跑两种协议出问题 | 同时发起 SNMP GET 和 Modbus 读取 |
| 不保存 MIB 文件 | 后期维护找不到 OID | 归档 MIB 文件到项目文档 |
八、一句话总结
SNMP 和 Modbus TCP 在同一台设备上并存,调试的核心不是"会不会用工具",而是
先搞清楚设备到底支持什么、默认开了什么、MIB 里定义了什么
。 SNMP 调试记住四步:确认启用 → 命令行测试 → 找对 OID → 验证数据。 Modbus TCP 调试记住四步:端口可达 → 寄存器能读 → 字节序正确 → 缩放因子匹配。 两种协议都调通之后,一定要做数据对比——一个物理量,两种协议读出来应该一致。不一致就说明哪里配错了,别等到验收时再发现。
审核编辑 黄宇
