1. 系统整体设计:为什么是Modbus TCP/UDP加上SNMP
做楼宇自控的人都知道,温湿度监测看着是个"小活",真正落地却涉及协议选型、设备接入、网络规划一堆事。传统方案里,很多老楼用的是RS485走Modbus RTU,一条总线挂几十个探头,手拉手串下去,施工麻烦不说,一旦某个节点故障,后面整条链路都可能瘫痪。这套基于Modbus TCP/UDP加SNMP的方案,本质上就是想把传感器采集和网络设备状态监测统一到一张IP网络上,减少布线成本,同时让运维人员在一套系统里同时看到"环境温湿度"和"网络设备健康状况"两类数据。
我最早接触这个需求,是因为一个中型园区的机房和弱电间改造。甲方提了两个要求:一是每个弱电间、配电间、机房都要有温湿度监测,数据要进楼宇自控平台;二是希望知道交换机、服务器这些网络设备的在线状态和负载情况,不想再单独部署一套网管软件。两个要求分开做都不难,难在合并成一套系统。Modbus TCP/UDP天生适合采集温湿度传感器数据,SNMP则是网络设备管理的标准协议,两者都基于IP网络,完全可以在一个采集网关里共存。这就是整套系统的设计出发点:一套采集平台,两种协议,三类数据(温湿度、设备在线、设备负载)。
这个方案适合谁参考?如果你是做楼宇自控集成、机房动环监控、园区弱电运维的工程师,尤其是手里已经有一批支持Modbus TCP的传感器,又不想额外买商业网管软件的场景,这篇文章分享的架构和踩坑经验应该能省你不少调试时间。
系统架构其实可以拆成四层:
- 感知层:温湿度传感器(支持Modbus TCP或UDP),放置在弱电间、机房、配电间、库房等需要监测的位置。
- 接入层:采集网关或直接用支持Python/Node-RED的工业边缘网关,负责轮询传感器数据。
- 网络层:管理型交换机(支持SNMP),提供网络拓扑和端口状态信息。
- 应用层:楼宇自控平台或自建的Web监控界面,汇总展示数据,做告警。
我自己的落地配置是:传感器用建大仁科或同类的工业级Modbus TCP温湿度探头,采集网关用一台x86工控机跑Linux系统,写Python脚本做采集和协议转换,SNMP部分直接通过网关的snmpwalk去读华为交换机的OID。这套组合成本不高,灵活度却很高。
为什么要选Modbus TCP/UDP而不是继续用RTU?核心原因是布线成本和故障隔离。Modbus RTU走RS485,一条总线上的设备共享带宽,轮询一个周期的时间会随设备数量线性增加;而且485总线是半双工的,接线极性反了、终端电阻没配好,都会导致整条总线通信异常,排查起来非常痛苦。Modbus TCP走以太网,每个传感器独立IP,点对点通信,一个设备故障不会影响其他设备,也天然支持跨楼层、跨建筑的IP网络传输。至于UDP,它比TCP少了连接维护的开销,适合数据上报频率低、允许偶尔丢包的场景,比如温度变化本来就很慢,10秒丢一两个包根本无感,用UDP反而更轻量。
2. 核心细节解析与实操要点
2.1 Modbus TCP帧结构里容易被忽略的三个细节
Modbus TCP的报文结构很简单:报文头(MBAP Header)+ 功能码 + 数据。MBAP Header共7个字节,包含事务处理标识符(2字节)、协议标识符(2字节)、长度(2字节)、单元标识符(1字节)。很多新手会忽略单元标识符——在同一台网关要跨多个串口采集设备时,这个字段是用来区分设备链路的,但在纯Modbus TCP场景下,一般填0x01就行。
功能码方面,温湿度传感器绝大多数支持03(读保持寄存器)和04(读输入寄存器)。不同厂家的传感器,数据存放的功能码不一样,甚至同一个厂家不同批次的产品都可能不同。我遇到过一款传感器,温度存放在输入寄存器(功能码04),湿度存放在保持寄存器(功能码03),调试时必须分开读,不能一次批量读取。所以拿到新传感器,第一步永远是翻手册搞清楚寄存器映射表,而不是盲目写轮询代码。
第二个容易踩坑的是寄存器数据类型。绝大多数温湿度传感器的温度和湿度是用16位整数表示的,需要除以10或100才是实际值。但也有的传感器用两个寄存器拼一个32位浮点数,高位在前还是低位在前,不同厂家实现还不一样。我见过最坑的是某品牌传感器,32位浮点数的字节序是小端序,而Modbus协议默认是大端序,直接解析出来温度显示成负数,排查了很久才发现是字节序问题。
第三个细节是轮询周期的设置。Modbus TCP虽然没有总线冲突问题,但传感器本身有处理能力上限,尤其是便宜的工业传感器,内部MCU主频很低,网关如果以100ms的间隔去轮询,传感器会来不及响应,出现超时。我一般把轮询周期设成2秒到5秒,对于温湿度这种缓变信号完全够用。如果传感器数量多,比如超过50个,建议把轮询周期拉长到10秒,或者分成多个线程分组轮询。核心原则是:采集数据的频率要匹配物理量的变化速度,温度不可能在1秒内跳变5度,没必要高频轮询。
2.2 SNMP协议的核心概念与华为交换机配置要点
SNMP协议的核心是OID(对象标识符),可以理解为网络设备内部信息的一棵"目录树"。想要读取交换机的CPU利用率、内存使用率、端口状态、温度,本质就是找到对应的OID节点,然后发起GET请求。SNMP有三个版本:v1基本废弃了,v2c增加了批量获取,v3增强了安全性。楼宇自控这类内网环境,通常用v2c就够,但要注意Community字符串(相当于密码)不要用默认的public,容易被内网其他设备扫描到。
华为交换机的SNMP配置,用命令行几行就能搞定:
system-view snmp-agent snmp-agent community read cipher your_community_name snmp-agent sys-info version v2c snmp-agent target-host trap-addressudp-address 192.168.1.100 params securityname your_community_name第一行是启用SNMP代理,第二行设置只读共同体,第三行指定协议版本,第四行配置Trap告警上报地址。这里的Trap是SNMP的主动上报机制,设备发现异常时主动往监控平台发消息,不需要平台轮询。对于温湿度监测系统,Trap可以用来上报交换机的温度过高告警、端口up/down事件,和温湿度传感器的联动非常有价值——比如机房空调故障导致环境温度升高,交换机温度也会同步上升,两边的告警可以相互印证。
配置完后测试连通性,在网关机器上执行:
snmpwalk -v2c -c your_community_name 192.168.1.254 1.3.6.1.4.1.2011这个OID段是华为企业的私有MIB库根节点。如果命令返回一堆信息,说明SNMP配置成功。如果返回超时,先ping试试网络通不通,再确认交换机的ACL有没有拦截SNMP报文。我碰到过一次,交换机配置了管理ACL,只允许特定IP访问,网关不在允许列表里,导致snmpwalk超时,排查了很久才想起来ACL这层。
2.3 网关数据汇聚:从Modbus到SNMP的桥接思路
网关在整个系统中的角色是"翻译官":上午用Modbus TCP轮询传感器拿到温度值,下午用SNMP读交换机OID拿到端口状态,最后把所有数据统一成一种格式,向上层平台推送。这个桥接思路可以用一张简单的表来理解:
| 数据源 | 协议 | 采集方式 | 典型周期 |
|---|---|---|---|
| 温湿度传感器 | Modbus TCP/UDP | 网关主动轮询 | 2-5秒 |
| 交换机CPU/内存 | SNMP v2c | 网关主动轮询 | 30-60秒 |
| 交换机端口状态 | SNMP v2c | 网关轮询 + Trap接收 | 30秒 + 实时 |
| 告警事件 | SNMP Trap | 被动接收 | 实时 |
为什么交换机状态用30秒甚至60秒的轮询周期?因为CPU利用率、端口流量这类数据本身就在波动,频繁采样没有意义,而且SNMP的GET操作在交换机上也是要消耗CPU资源的,监控频率太高会干扰设备的正常工作。温湿度传感器则是毫秒级响应、数据变化慢,用2-5秒的周期既能保证及时性,又不会给设备造成负担。
网关的数据处理逻辑我通常这样设计:独立的采集线程池,每个传感器一个线程,避免一个传感器的超时阻塞拖累其他传感器;数据统一打上时间戳存到内存缓存里,每5秒刷一次数据库;SNMP采集线程独立运行,30秒一次,没有数据更新就用上一次的值。这套设计的关键是隔离故障域:Modbus的某台传感器掉线,只影响它自己的线程,网关照常运行。
3. 实操过程与核心环节实现
3.1 传感器选型和物理部署
温湿度传感器的选型,我总结了几条经验:
- 精度要求:机房和弱电间一般要求温度±0.5℃,湿度±3%RH,普通的工业传感器都能满足。如果甲方是计量实验室或药品库房,需要选更高精度的传感器,价格会贵不少。
- 供电方式:优先选POE供电的型号,不用单独拉电源线,一根网线搞定通信和供电。如果没有POE交换机,就得选DC 12V供电的传感器,部署时要考虑电源适配器的位置。
- 探头形式:壁挂式适合安装在弱电间墙壁1.5米高度;管道式适合安装在空调送风管或回风管里,测的是风温;吸顶式适合机房吊顶空间。不同场景选不同形式,别一刀切。
物理部署时有个细节,传感器不要安装在空调出风口正下方或窗户旁边,测出来的温度没有代表性,也就是所谓"局部热岛效应"。我在一个配电间就踩过这个坑,传感器正好在空调回风口旁边,夏天温度显示23℃,实际柜内温度已经28℃了,空调失效了都没触发告警。后来把传感器移到机柜背板上方的回风区域,才真正反映出设备发热状况。
3.2 Modbus TCP采集代码的完整实现
下面是我在网关设备上跑的一段Python采集脚本,兼容Modbus TCP和UDP两种模式,注释都标得很清楚:
import socket import struct import threading import time import json from datetime import datetime MODBUS_TCP_PORT = 502 # 事务ID(2字节) + 协议ID(2字节) + 长度(2字节) + 单元ID(1字节) = 7字节头 MBAP_HEADER_LEN = 7 # 03功能码,读保持寄存器;04是读输入寄存器 READ_HOLDING_REGISTERS = 0x03 READ_INPUT_REGISTERS = 0x04 class ModbusSensor: def __init__(self, ip, name, reg_addr, count=2, register_type=0x03, data_format='int16', scale=0.1, use_udp=False, unit_id=1): self.ip = ip self.name = name self.reg_addr = reg_addr self.count = count self.register_type = register_type self.data_format = data_format self.scale = scale self.use_udp = use_udp self.unit_id = unit_id self.latest_value = None self.connected = False self._lock = threading.Lock() self._sock = None def _create_socket(self): """创建TCP或UDP socket,UDP模式不需要建立连接""" if self.use_udp: sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.settimeout(2) else: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(3) sock.connect((self.ip, MODBUS_TCP_PORT)) return sock def _build_request(self, transaction_id): """构造Modbus请求报文""" length = 6 # 单元ID + 功能码 + 起始地址2字节 + 寄存器数量2字节 header = struct.pack('>HHHB', transaction_id, 0, length, self.unit_id) pdu = struct.pack('>BHH', self.register_type, self.reg_addr, self.count) return header + pdu def _parse_response(self, resp): """解析Modbus响应报文,重点关注寄存器数值""" if len(resp) < MBAP_HEADER_LEN + 3: raise ValueError(f"响应报文长度异常: {len(resp)}") func_code = resp[7] byte_count = resp[8] if func_code & 0x80: # 异常标志位 exception_code = resp[9] raise ValueError(f"Modbus异常码: {exception_code}") # 寄存器值从第9字节开始,每寄存器2字节 regs = [] for i in range(byte_count // 2): offset = 9 + i * 2 regs.append(struct.unpack('>H', resp[offset:offset+2])[0]) return regs def read_once(self): """执行一次读取,处理字节序和缩放""" try: if self._sock is None: self._sock = self._create_socket() transaction_id = int(time.time() * 1000) % 65535 req = self._build_request(transaction_id) if self.use_udp: self._sock.sendto(req, (self.ip, MODBUS_TCP_PORT)) resp, _ = self._sock.recvfrom(256) else: self._sock.send(req) resp = self._sock.recv(256) regs = self._parse_response(resp) if self.data_format == 'int16': # 温度通常为有符号16位整数,需要按2的补码转换 raw = regs[0] if regs[0] < 32768 else regs[0] - 65536 value = raw * self.scale elif self.data_format == 'float32_be': # 大端32位浮点数:两个寄存器拼一个float packed = struct.pack('>HH', regs[0], regs[1]) value = struct.unpack('>f', packed)[0] elif self.data_format == 'float32_le': # 小端32位浮点数 packed = struct.pack('>HH', regs[0], regs[1]) packed = packed[2:] + packed[:2] value = struct.unpack('>f', packed)[0] else: value = regs[0] * self.scale with self._lock: self.latest_value = value self.connected = True return value except Exception as e: with self._lock: self.connected = False # 掉线时关闭socket,下次读取会自动重连 if self._sock: self._sock.close() self._sock = None raise e def poll_loop(self, interval=5): """持续轮询线程入口""" while True: try: val = self.read_once() print(f"[{datetime.now()}] {self.name}: {val}") except Exception as e: print(f"[{datetime.now()}] {self.name} 读取失败: {e}") time.sleep(interval) def main(): sensors = [ ModbusSensor("192.168.1.101", "弱电间-3F-温湿度", reg_addr=0, count=2, register_type=READ_INPUT_REGISTERS, data_format='int16', scale=0.1), ModbusSensor("192.168.1.102", "机房-A区-温湿度", reg_addr=1, count=2, register_type=READ_HOLDING_REGISTERS, data_format='int16', scale=0.1), ] for sensor in sensors: t = threading.Thread(target=sensor.poll_loop, args=(5,), daemon=True) t.start() # 主线程等待子线程,实际项目中可在这里做数据入库或Web推送 while True: time.sleep(10) if __name__ == "__main__": main()这段代码有几个细节值得说:一是UDP模式复用同一个socket,靠transaction_id区分响应,实际使用中传感器地址是唯一的,不存在多设备冲突,可以简单处理;二是TCP模式如果断线了,不要反复重试同一个socket,直接把socket关了,下次轮询重新连接,避免TCP半开连接挂在那里;三是int16转有符号数时别忘了负值的补码转换,冬天温度或者冷库场景会出现负数,处理不对会显示成65535这样的巨大值。
3.3 SNMP采集与Trap接收的完整实现
SNMP部分我用pysnmp库来实现,它是纯Python的SNMP协议栈,比调系统自带的snmpget命令要灵活,和Modbus采集逻辑放在同一个进程里完全没毛病:
from pysnmp.hlapi import * OIDS = { 'huawei_cpu_usage': '1.3.6.1.4.1.2011.5.25.31.1.7.1.3.0', 'huawei_mem_usage': '1.3.6.1.4.1.2011.5.25.31.1.8.1.1.0', 'sys_uptime': '1.3.6.1.2.1.25.1.1.0', 'device_temp': '1.3.6.1.4.1.2011.5.25.31.1.7.1.2.0', } def snmp_get(host, community, oid, port=161): """获取单个OID的值""" error_indication, error_status, error_index, var_binds = next( getCmd(SnmpEngine(), CommunityData(community, mpModel=1), # mpModel=1表示SNMPv2c UdpTransportTarget((host, port), timeout=3, retries=1), ContextData(), ObjectType(ObjectIdentity(oid))) ) if error_indication: raise RuntimeError(f"SNMP错误: {error_indication}") if error_status: raise RuntimeError(f"SNMP错误状态: {error_status}") for var_bind in var_binds: return var_bind[1].prettyPrint() def snmp_walk(host, community, base_oid, port=161): """遍历某个OID子树下所有节点,用于获取端口列表等信息""" results = [] for error_indication, error_status, error_index, var_binds in \ nextCmd(SnmpEngine(), CommunityData(community, mpModel=1), UdpTransportTarget((host, port), timeout=3, retries=1), ContextData(), ObjectType(ObjectIdentity(base_oid)), lexicographicMode=False): if error_indication: break if error_status: break for var_bind in var_binds: oid_str = var_bind[0].prettyPrint() value = var_bind[1].prettyPrint() results.append((oid_str, value)) return results def snmp_trap_receiver(port=162, community='your_community'): """接收SNMP Trap告警,华为主机名、IP、告警内容都会带过来""" # 实际使用中可用snmp engine加Callback来处理,这里示意 pass def collect_switch_metrics(switches): """批量采集交换机状态数据""" for sw in switches: try: cpu = snmp_get(sw['ip'], sw['community'], OIDS['huawei_cpu_usage']) mem = snmp_get(sw['ip'], sw['community'], OIDS['huawei_mem_usage']) temp = snmp_get(sw['ip'], sw['community'], OIDS['device_temp']) packet = { 'ip': sw['ip'], 'name': sw.get('name', sw['ip']), 'cpu_usage': cpu, 'mem_usage': mem, 'device_temp': temp, 'timestamp': time.time() } print(json.dumps(packet, ensure_ascii=False)) except Exception as e: print(f"采集 {sw['ip']} 失败: {e}") if __name__ == '__main__': switches = [ {'ip': '192.168.1.254', 'community': 'your_community', 'name': 'S5735-核心'}, {'ip': '192.168.2.254', 'community': 'your_community', 'name': 'S5735-接入'}, ] collect_switch_metrics(switches)华为主流设备的OID基本稳定,但不同型号、不同固件版本会有差异。比如CPU利用率的OID在某些老型号上是 1.3.6.1.4.1.2011.6.3.4.1.4.0,新版本就换成了 1.3.6.1.4.1.2011.5.25.31.1.7.1.3.0。调试的时候先手动snmpwalk一下确认OID有效,再写进代码里面去。我一般会在系统上线前列一个清单:哪台设备、哪个OID、期望值是什么,逐项核对。
3.4 数据关联与告警联动设计
真正让这套系统产生价值的是数据关联。单独的温湿度数据和单独的CPU数据其实是孤岛,但把时间轴对齐后就能看出因果关系。我设计过这样一个告警联动逻辑:
- 当机房的温度传感器读数超过28℃,同时交换机CPU利用率超过80%,判定为"设备过热可能导致计算瓶颈",告警等级提高。
- 当某弱电间的湿度超过70%RH,同时对应交换机端口出现大量crc错误包,判定为"网络链路可能因潮湿导致信号质量下降",通知运维去排查物理链路。
- 当传感器通信中断超过5分钟,同时交换机端口状态显示该设备所接端口为down,判定为"传感器或线缆故障",排除是传感器自身离线还是交换机端口崩溃。
这套联动逻辑用简单的规则引擎就能跑,不需要复杂的大数据模型,但效果非常直接。有一次机房空调故障,温湿度传感器在5分钟内就测到温度从24℃升到31℃,而交换机设备温度也从45℃升到58℃(还没到告警阈值)。传统做法是等交换机温度告警了才去看一番,而联动逻辑就把两个数据放在了一起,直接提示"环境温度异常导致设备温度升高",运维不用猜原因,直接奔空调去检查。这就是两类协议一起接入的价值。
3.5 数据库与可视化
数据落库我用的是最简单的SQLite加定时导出CSV的方案,单体项目足够了。每5秒写一条温湿度记录,30秒写一条交换机状态记录。数据库表结构设计:
CREATE TABLE sensor_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, sensor_ip TEXT NOT NULL, sensor_name TEXT, temperature REAL, humidity REAL, reading_time INTEGER ); CREATE TABLE switch_metrics ( id INTEGER PRIMARY KEY AUTOINCREMENT, switch_ip TEXT NOT NULL, switch_name TEXT, cpu_usage REAL, mem_usage REAL, device_temp REAL, reading_time INTEGER );可视化我图省事,直接在网关上面跑了一个轻量的Web服务,用ECharts画曲线。温度曲线、湿度曲线、CPU曲线、设备温度曲线,外加一个告警列表页面。不要一上来就上Grafana或者商业楼宇平台,先用简单的方式验证数据和告警逻辑是否可靠,再考虑迁移到完整平台上,这样迭代成本低不少。
4. 常见问题与排查技巧实录
4.1 Modbus通信类问题
传感器可以ping通,但Modbus读取超时。优先检查是否开启了防火墙拦截502端口。我遇到过Windows防火墙默认拦截外部连接的情况,换成Linux工控机后还要确认selinux没有拦截。其次看传感器是否有连接数限制,有些廉价传感器只支持2-3个TCP连接,如果同时被多个调试工具连接,新的采集请求就会被拒绝。排查时可以先断开其他客户端,只留网关一个连接测试。
读到的温度数值明显偏高或偏低。先确认寄存器地址是否正确。很多传感器在0号寄存器放温度、1号寄存器放湿度,但有的型号0号寄存器放的是设备状态字,要用寄存器映射表逐个核对。另外注意缩放系数,有传感器温度直接是整数摄氏度,不用除以10;有的是0.01精度,要除以100。不能凭经验猜,必须以手册为准。
部分传感器读取正常,个别传感器在固定地址上超时。这种情况多半是传感器IP冲突。用arp扫描一下同网段IP,确认没有两个设备占用同一个IP。另外检查弱电间的交换机端口是否开启了端口隔离,导致网关与传感器不在同一个广播域里,ping不通的传感器一定检查接入交换机的VLAN配置。
4.2 SNMP类问题
能用snmpget读到值,但snmpwalk返回空。很可能是Community字符串权限不够,只配了只读权限,而且OID路径被精简了。在华为设备上需要额外配置snmp-agent protocol-source-interface或开启MIB视图。还有一种情况是交换机的MIB没有加载华为企业私有MIB库,需要去华为官网下载对应版本的MIB文件导入到监控工具里。
Trap收不到。检查三个地方:一是交换机上的target-host配置地址是否写对了,UDP 162端口是否能通到网关,注意很多云服务器或防火墙默认不放行UDP 162端口;二是网关的SNMP Trap服务是否在监听,先用tcpdump抓包看看有没有UDP包进来,没有的话问题在交换机侧;三是确认Community字符串一致,有些老设备发Trap用的Community和读数据的Community是分开配置的,需要单独设置snmp-agent trap-community。
设备温度、CPU等私有OID读取不到。华为不同型号设备的MIB库版本差异很大,尤其是老款的S5700系列和新款的S5735系列,OID完全不同。建议先在网上搜索自己设备型号的OID参考网,或者直接在设备上执行display snmp-agent mib-view查看已加载的MIB。
4.3 网关与整体系统问题
网关重启后采集全部中断。很可能是采集程序没有注册成系统服务。Linux下用systemd把Python脚本设为常驻服务,配置Restart=always,掉线自动重启。这一步看似简单,但在实际项目中非常重要——网关作为系统核心,程序挂了等于整个监测系统瘫痪。
数据库体积无限增长。每5秒一条记录,一年大约630万条记录,SQLite也能扛得住,但文件会很大。建议定期做数据归档,或者只保留90天的明细数据,更早的数据转存CSV后归档,再清空原表。温湿度数据的历史趋势分析价值远低于实时告警价值,没必要长期保留高频率数据。
多楼层IP地址冲突。项目部署中经常遇到同一个IP被规划到多个楼层的情况。务必要做一张详细的IP规划表,分配给传感器、网关和交换机的IP逐一登记,避免后期排查时楼上楼下设备互相冲突。我发现这比技术本身就重要得多,一个清晰的IP登记表能让排查时间减少一半以上。
5. Modbus TCP vs UDP 在温湿度监测场景的取舍
我在项目中两种协议都实际用过,分享一下选择建议。
Modbus TCP的优势在于可靠,有连接确认和重传机制,数据不会丢。代价是每5秒建立一次连接、发送请求、读取响应、断开连接,如果传感器数量多,网关需要维护的连接数就多,线程开销也大。我实测过50个传感器的场景,5秒轮询间隔下,网关CPU占用率能到20%左右,虽然没到瓶颈,但要注意优化。
Modbus UDP就轻量多了,无连接、无重传,适合传感器主动上报或网关周期性广播读取。但UDP有个天然的坑:请求丢了传感器不感知,响应丢了网关不感知,数据就静默丢失了。解决方法是加一层可靠性机制:连续N次读到同一个值或超时,判定为通信异常,日志里做标记;或者让传感器开启主动上报模式,定时把数据怼到网关上,网关只做接收,这样丢包的影响更小。我现在的设计是:重要机房和配电间的传感器用Modbus TCP,一般的办公楼层、库房用Modbus UDP,按区域的重要度选择协议,既保证了关键区域的可靠性,又控制了网关资源消耗。
另外有个成本因素,支持Modbus TCP的传感器比仅支持RTU的贵50%左右,如果甲方预算紧张,可以考虑混合方案:核心区域用Modbus TCP传感器,普通区域用RS485传感器接一个Modbus RTU转Modbus TCP网关,由网关做协议转换。这样既能在上层统一走IP网络,又平衡了成本。
6. 运行效果与项目复盘
系统上线运行几个月,整体效果在意料之内。有时候机房空调故障,温湿度传感器20分钟内就能发现温度异常,联动逻辑会把交换机温度和传感器温度一起展示在告警页面,运维一眼就看出问题在哪。而之前没有这个系统时,往往要等到设备温度告警或者机房管理员巡检才发现,等发现问题机房里的设备已经在高温下运行了几个小时了。
实际的运维过程也验证了选型的合理性:传感器故障更换时,只需要把新传感器的IP改成和旧的一样,或者改一下采集程序的IP配置,不用改任何线路。因为每个传感器独立IP,不再有485总线一拖一大串的问题,单点故障对系统的影响降到了最低。
要说什么地方还能优化,一是传感器本身的数据精度,低端传感器在湿度大于80%RH时误差会加大,环境湿度高的时候数据仅供参考;二是SNMP Trap的告警解析还需要加强,华为设备不同的告警类型字段格式不一样,需要做一个告警翻译映射表,这部分我还在继续完善。
最后分享一个我实际踩过的坑:当初装传感器时为了美观,把一个弱电间的传感器塞进了吊顶里,结果那个弱电间恰好有空调风管经过,吊顶内温度和实际空间温度差了3度以上,一直没发现。后来因为一次温度告警排查才发现,把传感器移到吊顶外的壁装位置后,温度数据才正常。传感器的安装位置,决定了数据的可信度,这一点在实际项目中比选什么协议、用哪家设备都重要。