
以太网温湿度变送器双协议兼容(TCP+SNMP)固件调试笔记
物联网 · 盛世宏博 · 以太网温湿度、双协议兼容、Modbus TCP、SNMPv3、固件调试、并发冲突、MIB设计、写后回读、动环平台
近期在调试一套工业环境监控系统时,遇到了关于Modbus连接保持的问题。把场景收束到固件层:一台以太网温湿度变送器,要同时跑Modbus TCP(给动环平台采数据)和SNMP(给网管系统做资产/健康/告警纳管)。两个协议栈共享同一套传感硬件和寄存器映射,调试中踩到的坑不是“通不通”,而是“两个栈同时动硬件时谁赢、SNMP写操作怎么和Modbus写操作不打架、MIB怎么设计才不让NMS把传感器当路由器轮询爆”。

关键约束:Modbus TCP和SNMP都跑在TCP/IP之上,但语义模型完全不同——Modbus是“寄存器读写”,SNMP是“OID Get/Set/Trap”。两套栈必须共享同一个设备状态数据库(DS),且对这个数据库的访问必须互斥。
┌─────────────┐
│ 传感采集线程 │ 2s周期读I2C/ADC → 写DS(温度/湿度/露点原始值)
└──────┬──────┘
│
┌──────▼──────┐ ┌──────────────┐
│ DS(受保护) │←──→│ 互斥锁/临界区 │
└──────┬──────┘ └──────────────┘
│
┌───┴────┬─────────┐
│ │ │
┌──▼──┐ ┌──▼──┐ ┌───▼───┐
│Modbus│ │SNMP │ │ Trap │
│TCP │ │Agent│ │线程 │
└──────┘ └─────┘ └───────┘规则:
保持寄存器(0-based):
0x00: temp int16 ×0.1℃ RO
0x01: humi uint16 ×0.1%RH RO
0x02: dewpoint int16 ×0.1℃ RO
0x03: status uint16 bitfield RO
0x04: fault uint16 bitfield RO
0x05: sample_int uint16 采样周期 RO/RW
0x06: th_temp int16 高温阈值 RW
0x07: th_humi uint16 高湿阈值 RW
0x08: snmp_ver uint16 0=v2c 1=v3 RW
0x09: sys_contact ascii[64] RWMY-COMPANY-MIB ::= BEGIN
SensorObjects OBJECT IDENTIFIER ::= { enterprises 12345 1 }
tempValue OBJECT-TYPE SYNTAX Integer32 UNITS "0.1C" ACCESS read-only STATUS mandatory
::= { SensorObjects 1 }
humiValue OBJECT-TYPE SYNTAX Unsigned32 UNITS "0.1%" ACCESS read-only STATUS mandatory
::= { SensorObjects 2 }
dewPointValue OBJECT-TYPE SYNTAX Integer32 UNITS "0.1C" ACCESS read-only STATUS mandatory
::= { SensorObjects 3 }
sensorStatus OBJECT-TYPE SYNTAX BITS { running(0), alarm(1), cached(2), offline(3) } ACCESS read-only
::= { SensorObjects 4 }
-- 配置项
tempHighThresh OBJECT-TYPE SYNTAX Integer32 ACCESS read-write STATUS mandatory
::= { SensorObjects 10 }
humiHighThresh OBJECT-TYPE SYNTAX Unsigned32 ACCESS read-write STATUS mandatory
::= { SensorObjects 11 }
snmpVersion OBJECT-TYPE SYNTAX INTEGER { v2c(0), v3(1) } ACCESS read-write STATUS mandatory
::= { SensorObjects 12 }
-- Trap定义
sensorAlarmTrap NOTIFICATION-TYPE OBJECTS { tempValue, humiValue, sensorStatus }
::= { SensorObjects 100 }
END现象:同时用Modbus和SNMP改阈值,偶尔丢一个值,Flash里只存了其中一个。
根因:两个协议栈各自直接调Flash写函数,无互斥,Flash页擦除中途被打断。
修复:统一入口 cfg_write(key, value) → 上mutex → 改RAM副本 → 擦写Flash → 释放mutex。Modbus和SNMP都只能调这个入口。
现象:NMS做自动发现,发GetBulk Max-Rep=50,MCU lwIP TCP窗口满,后续Modbus连接全部超时。
根因:SNMP Agent在裸机lwIP中同步处理,GetBulk 50次迭代占CPU太久,阻塞了其他协议任务。
修复:
现象:湿度在阈值边界抖动,SNMP Trap每秒发3~5个,交换机CPU飙高。
根因:越限即发Trap,无防抖/抑制窗口。
修复:
现象:启用SNMP v3后,Modbus TCP连接建立时间从<100ms变成1~2s。
根因:SNMP v3引擎初始化时做了大量随机数生成(getrandom阻塞),占用了entropy源,lwIP TCP accept被延迟。
修复:SNMP v3引擎在系统启动早期初始化,不放在第一个连接到来时懒加载;用硬件RNG(如STM32 RNG外设)替代/dev/urandom。
现象:Modbus读温度=235(=23.5℃),SNMP读出来=58890(负数解译错误)。
根因:SNMP Agent直接把Modbus寄存器原值当uint16返回,没做符号扩展。
修复:DS中温度存int16_t,SNMP Agent读DS时做正确的ASN.1 INTEGER编码(含符号位)。
工具 | 用途 |
|---|---|
Wireshark | 抓包看MBAP、SNMP PDU、Trap目标、字节序 |
mbpoll | CLI Modbus TCP读写测试,验证寄存器映射 |
snmpget/snmpset/snmpwalk | Net-SNMP工具验证MIB对象 |
snmptrapd | 接收并解码Trap,确认Trap格式正确 |
串口日志 | 固件内部打印:连接建立/关闭、配置写入、Flash操作、mutex争用 |
逻辑分析仪 | I2C时序排查传感器读失败 |
调试口诀:
Modbus TCP:
Port: 502
Unit ID: 1(可配)
Timeout: 300ms
Max Connections: 5
Transaction ID: 自增
SNMP:
Port: 161
Trap Port: 162
Version: v3(默认),兼容v2c可配
Read Community: 独立配置(不硬编码public)
Write Community: 独立配置
v3 User: 可配,SHA/AES
Max-Repetitions 上限: 10
Trap Targets: 最多3个
Trap Interval Min: 60s
系统:
Sample Period: 2s
Heartbeat LED: 1Hz
Watchdog: 启用,超时15s复位
Flash Write Debounce: 5s(配置变更后延迟5s批量写Flash)□ Modbus读所有RO寄存器:值与snmpwalk一致(±1LSB)
□ SNMP Set改阈值:Modbus读回同一值,Flash断电不丢
□ Modbus写阈值:SNMP Get返回同一值
□ 并发:mbpoll循环读 + snmpwalk同时跑,无死锁、无数据错乱
□ GetBulk Max-Rep=50:Agent截断到10,不崩溃
□ Trap:越限触发,60s内不重复;恢复发Clear
□ v3加密:Wireshark解密验证隐私数据
□ 断网:拔线→重连→两个协议栈均自动恢复监听
□ 72h老化:无内存泄漏、无mutex死锁、无假在线
□ 写后回读:Modbus写非法值(如RH=150%),返回异常码,DS不变以太网温湿度、双协议兼容、Modbus TCP、SNMPv3、固件调试、MIB设计、并发互斥、写后回读、Trap抑制、动环平台
物联网 #工业物联网 #以太网温湿度 #ModbusTCP #SNMP #固件开发 #双协议 #嵌入式 #动环监控 #环境监控平台 #MIB #写后回读
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。