測(cè)系統(tǒng)實(shí)踐)
站在樓宇自控項(xiàng)目的現(xiàn)場(chǎng)最怕的不是設(shè)備壞而是你根本不知道什么時(shí)候環(huán)境已經(jīng)不適合設(shè)備運(yùn)行了。機(jī)房空調(diào)停了沒人管配電室溫度飆到四十度才被發(fā)現(xiàn)檔案室濕度過大導(dǎo)致紙質(zhì)資料發(fā)霉——這些問題本質(zhì)上不是設(shè)備問題而是缺乏一套可靠的溫濕度監(jiān)測(cè)手段。最近完成的這套基于Modbus TCP/UDP與SNMP協(xié)議的樓宇自控溫濕度監(jiān)測(cè)系統(tǒng)正好把這類問題解決得比較徹底趁熱把整個(gè)構(gòu)建過程和技術(shù)細(xì)節(jié)整理出來給做樓宇自控、機(jī)房動(dòng)環(huán)、弱電集成或者運(yùn)維平臺(tái)的朋友做個(gè)參考。這套系統(tǒng)的核心價(jià)值在于用兩種成熟協(xié)議打通了“傳感器采集—網(wǎng)絡(luò)傳輸—平臺(tái)管控”這條鏈路。Modbus TCP/UDP負(fù)責(zé)從溫濕度傳感器把數(shù)據(jù)取回來SNMP協(xié)議負(fù)責(zé)把數(shù)據(jù)對(duì)接進(jìn)已有的網(wǎng)管平臺(tái)或動(dòng)環(huán)監(jiān)控系統(tǒng)整個(gè)方案不依賴某家廠商的私有協(xié)議也沒有被云平臺(tái)綁架的問題后期擴(kuò)展和二次開發(fā)都非常靈活。適合正在規(guī)劃監(jiān)測(cè)系統(tǒng)、或者手里已經(jīng)有零散傳感器但不知道怎么統(tǒng)一接入的朋友哪怕你之前沒怎么碰過這些協(xié)議按下面的思路走也能把整個(gè)系統(tǒng)搭扎實(shí)。1. 系統(tǒng)整體設(shè)計(jì)與協(xié)議選型思路先聊設(shè)計(jì)思路。這套系統(tǒng)的名字看起來很長(zhǎng)其實(shí)拆開就三個(gè)部分溫濕度采集、Modbus傳輸、SNMP上報(bào)。大概架構(gòu)是這樣的每個(gè)監(jiān)測(cè)點(diǎn)位部署溫濕度傳感器傳感器以Modbus TCP或UDP協(xié)議把數(shù)據(jù)發(fā)出來負(fù)責(zé)采集的網(wǎng)關(guān)或服務(wù)器統(tǒng)一收集再通過SNMP協(xié)議對(duì)接到更上層的網(wǎng)管平臺(tái)最終呈現(xiàn)在監(jiān)控大屏或告警系統(tǒng)上。選型的時(shí)候沒有用BACnet、LonWorks這類傳統(tǒng)的樓宇自控協(xié)議主要考慮是第一項(xiàng)目里的大部分傳感器是市面上常見的Modbus RTU設(shè)備改網(wǎng)口接入Modbus TCP/UDP天然兼容現(xiàn)場(chǎng)部署成本最低第二上層的運(yùn)維團(tuán)隊(duì)已經(jīng)有成熟的SNMP網(wǎng)管平臺(tái)如果監(jiān)測(cè)數(shù)據(jù)能通過SNMP匯入現(xiàn)有平臺(tái)就不需要額外部署一套獨(dú)立的監(jiān)控軟件甲方接受度也高。Modbus和SNMP這兩種協(xié)議放在一起各自的角色很明確Modbus解決的是“怎么把數(shù)據(jù)從傳感器取回來”的問題它簡(jiǎn)單、直觀寄存器讀一下溫度就出來了SNMP解決的是“怎么把數(shù)據(jù)送到管理者手里”的問題它標(biāo)準(zhǔn)、通用無論是機(jī)房動(dòng)環(huán)監(jiān)控還是企業(yè)網(wǎng)管平臺(tái)都認(rèn)這個(gè)協(xié)議。一取一送兩件事都用最標(biāo)準(zhǔn)的工具去做系統(tǒng)整體的穩(wěn)定性和可維護(hù)性都會(huì)有保證。另外一個(gè)選型細(xì)節(jié)是TCP與UDP的選擇。實(shí)際工程中我主用TCP原因很直接溫濕度數(shù)據(jù)雖然不是極其關(guān)鍵的實(shí)時(shí)控制數(shù)據(jù)但丟包會(huì)導(dǎo)致曲線毛刺甚至假告警TCP的重傳機(jī)制能有效避免這個(gè)問題。UDP更多用于帶寬受限、點(diǎn)位密度極高的場(chǎng)景或者傳感器數(shù)量特別多時(shí)降低連接壓力。如果項(xiàng)目預(yù)算允許建議全部走TCP省掉很多排查問題的精力。1.1 為什么不用傳統(tǒng)RS485總線方案很多做樓宇自控的老師傅習(xí)慣用RS485總線統(tǒng)一采集溫濕度這也是成熟方案但新技術(shù)項(xiàng)目里不建議優(yōu)先考慮。RS485的問題是施工復(fù)雜度高布線要分 sexesA/B線不能接反手拉手串聯(lián)一個(gè)點(diǎn)短路整條鏈路全部癱掉而且RS485的輪詢速度受波特率約束9600波特率下掛32個(gè)設(shè)備一輪輪詢下來就是幾十秒點(diǎn)位一多實(shí)時(shí)性很難看。Modbus TCP/UDP方案就把這個(gè)問題徹底繞開了。傳感器通過網(wǎng)線上聯(lián)交換機(jī)IP地址一配就能通信物理鏈路故障影響范圍很小輪詢速度也快得多在百兆局域網(wǎng)里單個(gè)請(qǐng)求往返基本在幾毫秒級(jí)別上百個(gè)點(diǎn)位也能輕松做到秒級(jí)刷新。從我們最終部署的效果看60個(gè)監(jiān)測(cè)點(diǎn)全量輪詢一輪大概1.2秒這個(gè)實(shí)時(shí)性表現(xiàn)對(duì)樓宇環(huán)境監(jiān)測(cè)完全夠用。1.2 系統(tǒng)整體拓樸與數(shù)據(jù)流向整個(gè)系統(tǒng)的數(shù)據(jù)流向大致是這樣理清這個(gè)概念后面很多配置就不會(huì)迷糊溫濕度傳感器Modbus從站 → 工業(yè)交換機(jī) → 采集網(wǎng)關(guān)Modbus主站 → 數(shù)據(jù)清洗與閾值判斷 → SNMP Trap/輪詢上報(bào) → 網(wǎng)管平臺(tái)/告警系統(tǒng)。這里面的采集網(wǎng)關(guān)可以是臺(tái)式機(jī)、工控機(jī)也可以是嵌入式邊緣網(wǎng)關(guān)。我們項(xiàng)目里用的是邊緣網(wǎng)關(guān)加Node-RED的思路好處是后續(xù)要調(diào)整點(diǎn)位或者監(jiān)控邏輯直接在線改流不用反復(fù)燒固件。如果你沒有邊緣網(wǎng)關(guān)拿一臺(tái)普通電腦跑Python或者Node-RED也一樣能實(shí)現(xiàn)后面實(shí)操部分會(huì)有代碼示例。2. 溫濕度傳感器選型與點(diǎn)位表規(guī)劃這個(gè)環(huán)節(jié)看似簡(jiǎn)單其實(shí)是決定整個(gè)系統(tǒng)準(zhǔn)確性和穩(wěn)定性的地基。傳感器選型最大的坑就是只看價(jià)格不看精度和長(zhǎng)期穩(wěn)定性前期省下的錢最終都會(huì)變成后期校準(zhǔn)和返工的成本而且現(xiàn)場(chǎng)環(huán)境差異很大機(jī)房和檔案室的需求根本不是一回事。2.1 傳感器關(guān)鍵參數(shù)與場(chǎng)景匹配選傳感器我習(xí)慣先列需求再選型重點(diǎn)關(guān)注四個(gè)參數(shù)精度等級(jí)。機(jī)房和配電室建議溫度精度達(dá)到正負(fù)0.3攝氏度濕度精度正負(fù)3%RH普通的辦公區(qū)、檔案室標(biāo)準(zhǔn)可以放寬到正負(fù)0.5攝氏度和正負(fù)5%RH。精度越高的傳感器RS485或網(wǎng)口版本的價(jià)格差距可能有三到五倍按區(qū)域需求分級(jí)選型能把預(yù)算花在刀刃上。響應(yīng)速度??照{(diào)出風(fēng)口附近和門窗附近的點(diǎn)位溫濕度變化快需要傳感器響應(yīng)時(shí)間在5秒以內(nèi)倉庫、走廊這類熱慣性較大的區(qū)域響應(yīng)慢一點(diǎn)也問題不大。傳感器說明書里一般都有響應(yīng)時(shí)間參數(shù)別忽略這個(gè)數(shù)字。供電方式。大部分Modbus溫濕度傳感器支持DC 12V或24V供電少數(shù)支持PoE供電。PoE方案施工最方便一根網(wǎng)線就把供電和數(shù)據(jù)都解決了但支持PoE的傳感器單價(jià)偏高需要綜合考慮。本次項(xiàng)目大部分點(diǎn)位用的是12V直流供電匯聚到機(jī)柜的開關(guān)電源統(tǒng)一供電。防護(hù)等級(jí)。機(jī)房等室內(nèi)環(huán)境選IP30就夠了但如果有冷通道、水管附近等潮濕區(qū)域盡量選IP54以上的型號(hào)。這個(gè)不能省一旦傳感器內(nèi)部進(jìn)水凝露采集出來的濕度數(shù)據(jù)會(huì)完全失真。2.2 Modbus點(diǎn)位表設(shè)計(jì)規(guī)范點(diǎn)位表是Modbus項(xiàng)目的靈魂。設(shè)計(jì)點(diǎn)位表的時(shí)候我給每個(gè)傳感器分配了獨(dú)立的Modbus地址和寄存器區(qū)間并按功能統(tǒng)一規(guī)劃寄存器含義。以典型傳感器為例寄存器分布大概是這樣的寄存器地址數(shù)據(jù)類型含義示例值0x000016位無符號(hào)整型溫度值需除10235 表示 23.5°C0x000116位無符號(hào)整型濕度值需除10456 表示 45.6%RH0x000216位無符號(hào)整型設(shè)備狀態(tài)字位0表示在線狀態(tài)0x010016位無符號(hào)整型溫度報(bào)警上限280 表示 28.0°C0x010116位無符號(hào)整型濕度報(bào)警下限300 表示 30.0%RH這里要注意不同廠商的傳感器寄存器定義差異很大有的用放大十倍表示一位小數(shù)有的直接用兩個(gè)寄存器存浮點(diǎn)數(shù)比如IEEE 754格式4字節(jié)浮點(diǎn)占用兩個(gè)保持寄存器。所以點(diǎn)位表建立前最重要的一步就是拿廠家的Modbus寄存器手冊(cè)一個(gè)一個(gè)對(duì)過去并且用Modbus Poll這個(gè)工具實(shí)際讀一遍驗(yàn)證。不要想當(dāng)然地以為0x0000里存的就一定是溫度買回來的傳感器里藏著什么字節(jié)序都得實(shí)測(cè)確認(rèn)。點(diǎn)位表里還有一類容易遺漏的內(nèi)容是負(fù)值處理。室溫低于零下的時(shí)候如果不約定用有符號(hào)整型表示讀回來就會(huì)變成一個(gè)很大的正數(shù)比如-5度變成65531這會(huì)讓告警閾值完全失效。這點(diǎn)在布置冷庫、室外機(jī)房門口氣溫監(jiān)測(cè)的時(shí)候尤其要注意點(diǎn)位表的注釋里必須寫清楚數(shù)值類型和轉(zhuǎn)換公式。2.3 點(diǎn)位命名與編碼規(guī)范規(guī)劃點(diǎn)位表時(shí)就把編碼規(guī)范定好后期運(yùn)維能少很多坑。我常用的編碼格式是區(qū)域-設(shè)備類型-序號(hào)比如“3F-MECH-07”代表三層機(jī)電間第7號(hào)監(jiān)測(cè)點(diǎn)。編碼規(guī)范有三個(gè)好處一是告警推送時(shí)日志里能直接看出位置不用對(duì)著IP查表二是后續(xù)接SNMP時(shí)OID里最后一個(gè)節(jié)點(diǎn)直接對(duì)應(yīng)點(diǎn)位序號(hào)映射規(guī)則特別清晰三是監(jiān)控大屏顯示時(shí)可以按編碼前綴自動(dòng)分組省掉很多開發(fā)工作量。3. Modbus TCP/UDP采集層落地實(shí)操整體架構(gòu)定了之后最見功夫的就是采集層的落地。這里包含網(wǎng)絡(luò)規(guī)劃、采集策略、輪詢邏輯以及異常處理任何一個(gè)環(huán)節(jié)處理不當(dāng)都會(huì)產(chǎn)生讓人頭疼的數(shù)據(jù)問題。3.1 網(wǎng)絡(luò)規(guī)劃與IP地址分配Modbus TCP/UDP走的是標(biāo)準(zhǔn)IP網(wǎng)絡(luò)所以IP規(guī)劃必須提前做好。我這次項(xiàng)目單獨(dú)規(guī)劃了一個(gè)采集網(wǎng)段傳感器使用192.168.20.0/24網(wǎng)段采集網(wǎng)關(guān)使用192.168.20.200交換機(jī)管理地址用20.254和辦公網(wǎng)完全隔離。隔離的目的很明確辦公網(wǎng)的廣播流量和數(shù)據(jù)震蕩不會(huì)干擾Modbus采集鏈路采集網(wǎng)段內(nèi)部保持輕載通信質(zhì)量才有保障。IP分配要留出余量并且和點(diǎn)位編碼一一對(duì)應(yīng)。比如3F-MECH-07這個(gè)點(diǎn)位固定分配192.168.20.107其中最后一位和點(diǎn)位序號(hào)直接對(duì)應(yīng)后期排查時(shí)一眼就能從IP定位到物理位置。所有IP和MAC地址的對(duì)應(yīng)關(guān)系整理成臺(tái)賬表放在項(xiàng)目文檔里不僅能幫你快速定位故障在寫SNMP映射時(shí)也是重要參考。3.2 傳感器Modbus參數(shù)配置傳感器上電前基本都要先做參數(shù)配置常規(guī)項(xiàng)目里支持Web配置的傳感器比較多也有部分型號(hào)需要通過專用的配置工具或串口命令行設(shè)置。配置項(xiàng)里最重要的幾項(xiàng)從站地址。單個(gè)網(wǎng)關(guān)下面如果掛多個(gè)傳感器每個(gè)傳感器的Modbus地址必須唯一默認(rèn)都是1不改的話直接沖突。我習(xí)慣按點(diǎn)位序號(hào)設(shè)置地址比如點(diǎn)位03號(hào)就把從站地址設(shè)成3簡(jiǎn)單好記。通信超時(shí)與重試次數(shù)。傳感器作為從站需要設(shè)置一個(gè)合理的通信超時(shí)值一般建議300毫秒到1000毫秒之間。太小的話傳感器稍微忙一點(diǎn)就判斷通信超時(shí)太大會(huì)拖慢主站輪詢節(jié)奏。重試次數(shù)設(shè)2到3次即可多了會(huì)阻塞后續(xù)請(qǐng)求。字節(jié)序設(shè)置。這個(gè)務(wù)必和設(shè)備說明書核對(duì)清楚。有的設(shè)備默認(rèn)是大端字節(jié)序即高字節(jié)在前有的默認(rèn)小端。如果采集端解析字節(jié)序和傳感器配置不一致讀出來的溫度幾乎必然是亂碼而且沒有規(guī)律可循。比較推薦的做法是配置成統(tǒng)一的字節(jié)序并在采集程序中顯式處理不依賴默認(rèn)值。3.3 基于Node-RED實(shí)現(xiàn)Modbus采集流本次項(xiàng)目我選擇Node-RED作為采集核心理由有三個(gè)一是圖形化編程現(xiàn)場(chǎng)調(diào)試時(shí)不用頻繁改代碼重新部署直接拖節(jié)點(diǎn)就能調(diào)整邏輯二是Modbus節(jié)點(diǎn)庫非常成熟TCP和UDP都支持三是自帶調(diào)試面板能實(shí)時(shí)看到每個(gè)點(diǎn)位的數(shù)據(jù)流和原始報(bào)文排查問題效率極高。一個(gè)最基礎(chǔ)的采集流包含四個(gè)節(jié)點(diǎn)Modbus請(qǐng)求節(jié)點(diǎn)定時(shí)觸發(fā)、Modbus讀寫節(jié)點(diǎn)配置TCP連接和寄存器地址、函數(shù)節(jié)點(diǎn)數(shù)據(jù)轉(zhuǎn)換與格式整理、MQTT或WebSocket輸出節(jié)點(diǎn)數(shù)據(jù)上送平臺(tái)。定時(shí)觸發(fā)頻率我設(shè)置為每3秒一次既能保證刷新率也不會(huì)因?yàn)檎?qǐng)求太密集給網(wǎng)關(guān)和傳感器造成壓力。Node-RED的Modbus讀寫節(jié)點(diǎn)里需要配置的關(guān)鍵參數(shù)是設(shè)備地址填傳感器IP端口一般固定為502單元ID填Modbus從站地址。交易間隙interdelay建議設(shè)置為20毫秒這是為了避免連續(xù)請(qǐng)求太快把一些廉價(jià)的傳感器模塊打懵。實(shí)測(cè)下來這個(gè)參數(shù)在設(shè)備響應(yīng)不穩(wěn)定的時(shí)候調(diào)大的效果立竿見影。采集偏向用TCP還有一個(gè)工程上的考量UDP雖然省掉了連接建立的開銷但在跨交換機(jī)場(chǎng)景下廣播域里的UDP流量不可控一旦出現(xiàn)網(wǎng)絡(luò)擁塞丟包會(huì)很隨機(jī)數(shù)據(jù)就會(huì)偶爾斷幾秒。TCP雖然要維護(hù)連接狀態(tài)但每個(gè)從站一條長(zhǎng)連接故障定位非常清楚——連接斷了就是設(shè)備或者鏈路問題恢復(fù)重連機(jī)制也好寫。3.4 使用Python寫輕量級(jí)Modbus采集腳本如果你不想依賴Node-RED或者需要把采集能力嵌入自己的后臺(tái)系統(tǒng)里用Python寫一個(gè)輕量級(jí)采集腳本也很合適。Python的pymodbus庫是最常用的庫在4.x版本上發(fā)布了同步和異步兩套接口我這邊用異步接口寫了一個(gè)簡(jiǎn)單示例import asyncio from pymodbus.client import AsyncModbusTcpClient SENSORS [ {name: 3F-MECH-07, ip: 192.168.20.107, unit: 3}, {name: 2F-COM-02, ip: 192.168.20.102, unit: 2}, ] async def read_sensor(client, sensor): try: result await client.read_input_registers(0x0000, 2, slavesensor[unit]) if not result.isError(): temp_raw result.registers[0] humi_raw result.registers[1] temperature temp_raw / 10.0 if temp_raw 0x8000 else (temp_raw - 0x10000) / 10.0 humidity humi_raw / 10.0 return sensor[name], round(temperature, 1), round(humidity, 1) except Exception as exc: return sensor[name], None, None async def poll_once(sensors): async with AsyncModbusTcpClient(192.168.20.200, port502) as client: for sensor in sensors: logs await read_sensor(client, sensor) print(logs) async def main(): while True: await poll_once(SENSORS) await asyncio.sleep(3) asyncio.run(main())這段代碼的思路很簡(jiǎn)單每3秒依次讀取每個(gè)傳感器的寄存器把原始值轉(zhuǎn)換成實(shí)際物理量后打印。工程化的時(shí)候會(huì)加上日志、狀態(tài)記錄和斷線重連但核心的協(xié)議交互和數(shù)值轉(zhuǎn)換邏輯就這么直接。需要注意read_input_registers函數(shù)默認(rèn)從零開始讀連續(xù)的寄存器傳入的量是寄存器個(gè)數(shù)不是字節(jié)數(shù)寫錯(cuò)的話讀回來的數(shù)據(jù)長(zhǎng)度就對(duì)不上了。另外0x8000這個(gè)邊界判斷是我處理負(fù)數(shù)的習(xí)慣不同廠家的負(fù)數(shù)表示方法可能在最高位標(biāo)志上不一致所以嚴(yán)格按照點(diǎn)位表來寫轉(zhuǎn)換函數(shù)永遠(yuǎn)是第一原則。4. SNMP協(xié)議在樓宇動(dòng)環(huán)中的集成實(shí)踐采集層把數(shù)據(jù)拿到手只是第一步真正讓這套系統(tǒng)融入運(yùn)維體系的是SNMP上報(bào)環(huán)節(jié)。SNMP全稱是簡(jiǎn)單網(wǎng)絡(luò)管理協(xié)議在IT網(wǎng)管領(lǐng)域已經(jīng)用了幾十年幾乎所有交換機(jī)、路由器、服務(wù)器都支持。把樓宇的溫濕度數(shù)據(jù)裝進(jìn)SNMP的框架里就等于給暖通和動(dòng)環(huán)數(shù)據(jù)發(fā)了一張進(jìn)入企業(yè)IT運(yùn)維體系的入場(chǎng)券。4.1 SNMP v1、v2c還是v3先從版本選擇說起。SNMP v1太老報(bào)文格式簡(jiǎn)單但安全性幾乎沒有不建議新項(xiàng)目使用SNMP v2c是當(dāng)前使用最廣泛的版本通過團(tuán)體名community string做簡(jiǎn)單的認(rèn)證部署簡(jiǎn)單運(yùn)維友好大多數(shù)動(dòng)環(huán)平臺(tái)的接入也是優(yōu)先支持v2cSNMP v3引入了用戶認(rèn)證和加密安全性高但配置復(fù)雜度也上來不少需要維護(hù)用戶表、視圖、認(rèn)證協(xié)議和加密協(xié)議。這次項(xiàng)目我選的是SNMP v2c。考慮很實(shí)際監(jiān)測(cè)數(shù)據(jù)不涉及高??刂撇僮鱲2c的只讀權(quán)限配合網(wǎng)絡(luò)ACL限制訪問來源已經(jīng)能保證基本安全而且現(xiàn)有的網(wǎng)管平臺(tái)對(duì)v2c的支持最成熟trap消息只要按標(biāo)準(zhǔn)格式發(fā)過去平臺(tái)幾乎零配置就能接收。如果項(xiàng)目對(duì)安全性有硬性要求或者要跨公網(wǎng)傳輸那就老老實(shí)實(shí)用v3不要圖省事。4.2 將Modbus數(shù)據(jù)映射為SNMP OID要讓網(wǎng)管平臺(tái)讀溫度數(shù)據(jù)核心是設(shè)計(jì)OID。OID對(duì)象標(biāo)識(shí)符是一串用點(diǎn)分隔的數(shù)字SNMP通過它定位到具體的MIB變量。我的映射規(guī)則是企業(yè)私有OID根節(jié)點(diǎn)加上點(diǎn)位編號(hào)和數(shù)據(jù)類型標(biāo)識(shí)。參考結(jié)構(gòu)如下.1.3.6.1.4.1.54321 (企業(yè)私有根) → .1 (樓宇設(shè)備節(jié)點(diǎn)) → .1 (溫濕度傳感器組) → .1 (點(diǎn)位號(hào)) → .1 (溫度) / .2 (濕度)按照前面的點(diǎn)位編碼3F-MECH-07對(duì)應(yīng)節(jié)點(diǎn)號(hào)7那么它的溫度OID就是.1.3.6.1.4.1.54321.1.1.7.1。這種映射規(guī)則的好處是點(diǎn)位號(hào)直接體現(xiàn)在OID里不用額外維護(hù)一張映射表程序里一個(gè)字典就能搞定從Modbus設(shè)備號(hào)到OID的轉(zhuǎn)換。SNMP的變量類型也要定義好。溫度值一般定義成INTEGER類型但SNMP的INTEGER是不支持小數(shù)的傳輸?shù)臅r(shí)候需要把溫度乘以10變成整數(shù)即23.5攝氏度傳輸為235在平臺(tái)側(cè)展示時(shí)再除以10。這一點(diǎn)和Modbus里寄存器放大倍數(shù)的思路一樣算是跨協(xié)議數(shù)值轉(zhuǎn)換里最容易出的坑。4.3 用snmpd擴(kuò)展實(shí)現(xiàn)被動(dòng)輪詢實(shí)際項(xiàng)目里我比較推薦直接用Linux里的snmpd服務(wù)配置擴(kuò)展腳本來實(shí)現(xiàn)SNMP數(shù)據(jù)讀取。snmpd支持通過pass關(guān)鍵字把自定義OID交給外部腳本處理腳本返回該OID對(duì)應(yīng)的數(shù)值。這樣Modbus采集網(wǎng)關(guān)只要在Linux上運(yùn)行裝一個(gè)snmpd并配置好pass項(xiàng)網(wǎng)管平臺(tái)輪詢某個(gè)OID時(shí)snmpd會(huì)動(dòng)態(tài)調(diào)用腳本腳本返回對(duì)應(yīng)點(diǎn)位的實(shí)時(shí)溫度。核心配置如下pass .1.3.6.1.4.1.54321.1 /usr/local/bin/modbus_snmp_agent.sh對(duì)應(yīng)的shell腳本邏輯簡(jiǎn)化后是這樣#!/bin/bash # 從OID中提取點(diǎn)位號(hào)和數(shù)據(jù)類型再查最新的Modbus緩存值 TEMP_CACHE_FILE/var/cache/modbus_temp_${NODE_ID}.txt case $OID in .1.3.6.1.4.1.54321.1.*) NODE_ID$(echo $OID | awk -F. {print $8}) TEMP_RAW$(cat /var/cache/modbus_temp_${NODE_ID}.txt) echo $OID echo integer echo $TEMP_RAW ;; esac這里有一個(gè)影響體驗(yàn)的細(xì)節(jié)每次都調(diào)用外部腳本去實(shí)時(shí)讀Modbus寄存器響應(yīng)會(huì)很慢甚至?xí)枞鹲nmpd的請(qǐng)求處理線程。更好的做法是讓后臺(tái)采集程序周期性地把數(shù)據(jù)寫入緩存文件SNMP請(qǐng)求只讀取緩存。這樣Modbus輪詢頻率和SNMP查詢頻率徹底解耦整體性能和穩(wěn)定性都有保障。整個(gè)緩存機(jī)制的實(shí)現(xiàn)也很簡(jiǎn)單Node-RED的采集流里加一個(gè)“寫入文件”節(jié)點(diǎn)每3秒把各點(diǎn)位的溫度和濕度寫成獨(dú)立文件snmpd腳本按OID取對(duì)應(yīng)緩存文件讀內(nèi)容并返回。實(shí)測(cè)下來SNMP查詢響應(yīng)時(shí)間基本在1毫秒到5毫秒之間網(wǎng)管平臺(tái)完全無感。4.4 SNMP Trap主動(dòng)告警輪詢適合平臺(tái)實(shí)時(shí)讀取數(shù)據(jù)但告警事件用輪詢就有點(diǎn)浪費(fèi)資源更標(biāo)準(zhǔn)的做法是SNMP Trap主動(dòng)上報(bào)。SNMP Trap是設(shè)備或代理主動(dòng)向管理端發(fā)送的事件通知它不需要管理端先來查詢一旦溫度越限立刻上報(bào)告警延遲幾乎為零。在snmpd里配置Trap也比較麻煩一般是寫一段腳本檢測(cè)閾值觸發(fā)后調(diào)用snmptrap命令發(fā)送v2c的Trap報(bào)文。發(fā)送Trap的核心命令示例snmptrap -v 2c -c public 192.168.20.250 \ .1.3.6.1.4.1.54321.0.0.1 \ .1.3.6.1.4.1.54321.1.1.7.1 i 285這條命令表示向192.168.20.250發(fā)送trap企業(yè)OID下的告警定義節(jié)點(diǎn)是.1.3.6.1.4.1.54321.0.0.1告警詳情攜帶OID點(diǎn)位7的溫度OID和報(bào)警值285即28.5攝氏度。平臺(tái)的告警策略會(huì)根據(jù)收到的trap內(nèi)容觸發(fā)通知郵件、短信或者企業(yè)微信推送都可以掛上去。Trap發(fā)送前還有一步重要配置客戶端要調(diào)低重發(fā)策略。SNMP Trap默認(rèn)是不可靠傳輸丟包就丟了所以如果告警很重要建議在業(yè)務(wù)邏輯上做補(bǔ)償比如每隔30秒重發(fā)一次直到管理端在Trap中收到確認(rèn)或者在監(jiān)控平臺(tái)上看到恢復(fù)事件。我們項(xiàng)目里的做法是腳本里維護(hù)一個(gè)告警狀態(tài)文件只有狀態(tài)發(fā)生“正常→告警”或者“告警→恢復(fù)”的變化時(shí)才發(fā)Trap避免重復(fù)告警刷屏。5. 平臺(tái)層接入與告警聯(lián)動(dòng)邏輯以上偏底層的能力已經(jīng)打通接下來就是把數(shù)據(jù)真正變成運(yùn)營(yíng)可用的信息。這一段要做的事情基本上就是把Modbus采集來的數(shù)據(jù)統(tǒng)一規(guī)整到數(shù)據(jù)層建立告警閾值管理把SNMP Trap映射成工單或通知最后在大屏和報(bào)表里呈現(xiàn)。5.1 數(shù)據(jù)緩存與歷史存儲(chǔ)設(shè)計(jì)樓宇溫濕度監(jiān)測(cè)的數(shù)據(jù)量并不大假設(shè)200個(gè)點(diǎn)位、每3秒一條記錄一天的數(shù)據(jù)量算下來大概是幾百萬條。如果直接全部存進(jìn)關(guān)系型數(shù)據(jù)庫存儲(chǔ)和查詢壓力都會(huì)顯得有點(diǎn)浪費(fèi)。我的實(shí)踐經(jīng)驗(yàn)是雙層存儲(chǔ)策略實(shí)時(shí)緩存層和時(shí)序歸檔層。實(shí)時(shí)緩存層用Redis這樣的內(nèi)存數(shù)據(jù)庫保留最近兩個(gè)小時(shí)的最新值供大屏和實(shí)時(shí)告警使用。每個(gè)點(diǎn)位的key設(shè)計(jì)成mode:temp:3F-MECH-07這樣的格式value就是溫濕度JSON按點(diǎn)位編碼做key從邏輯上也能快速匹配。時(shí)序歸檔層用InfluxDB這類時(shí)序數(shù)據(jù)庫按1分鐘聚合粒度存儲(chǔ)均值、最大值和最小值歸檔周期根據(jù)項(xiàng)目需求定至少保留一年。這樣設(shè)計(jì)以后實(shí)時(shí)查詢不壓數(shù)據(jù)庫歷史分析又很平滑開發(fā)成本和硬件成本都控制得住。5.2 告警閾值與防抖動(dòng)策略告警邏輯是整個(gè)系統(tǒng)的價(jià)值出口。閾值不是簡(jiǎn)單設(shè)一個(gè)溫度超過多少就告警那樣毫無實(shí)用性。項(xiàng)目里我們按區(qū)域類別設(shè)置了差異化閾值比如機(jī)房溫度高于26度告警、28度嚴(yán)重告警檔案室濕度高于60%RH告警配電室溫度變化率超過2度/分鐘時(shí)告警。差異化閾值的一個(gè)好處是告警事件不再頻繁打擾值班人員每條告警都有明確的工程含義。防抖動(dòng)策略極其重要不然夏天下午空調(diào)系統(tǒng)正常波動(dòng)值班手機(jī)能被打爆。實(shí)測(cè)經(jīng)驗(yàn)是連續(xù)3個(gè)輪詢周期即9秒越限才觸發(fā)告警溫度回到閾值內(nèi)且持續(xù)5分鐘以上才發(fā)送恢復(fù)事件。再配合SNMP Trap的重發(fā)機(jī)制整個(gè)告警體驗(yàn)就很專業(yè)。這里貼一段簡(jiǎn)單的偽代碼說明防抖動(dòng)邏輯ALERT_STATES {} def evaluate_point(point_id, current_value, threshold): prev_state ALERT_STATES.get(point_id, normal) if current_value threshold[high]: counter ALERT_STATES.get(point_id _count, 0) 1 ALERT_STATES[point_id _count] counter if counter 3 and prev_state normal: send_snmp_trap(point_id, current_value) ALERT_STATES[point_id] alert ALERT_STATES[point_id _count] 0 else: ALERT_STATES[point_id _count] 0這個(gè)邏輯里有個(gè)細(xì)節(jié)值得注意計(jì)數(shù)器是連續(xù)越限才累加不是瞬時(shí)值判斷。這樣溫度一秒鐘沖高又回落的情況不會(huì)誤報(bào)只有穩(wěn)定越限才會(huì)觸發(fā)trap。另外告警狀態(tài)維護(hù)要考慮腳本重啟后要能從Redis或文件里恢復(fù)否則服務(wù)重啟一次可能導(dǎo)致告警漏報(bào)。5.3 與暖通空調(diào)系統(tǒng)的聯(lián)動(dòng)控制如果僅僅是把數(shù)據(jù)送到監(jiān)控大屏這套系統(tǒng)的價(jià)值還沒有完全發(fā)揮。真正有價(jià)值的樓宇溫濕度系統(tǒng)應(yīng)當(dāng)和暖通空調(diào)系統(tǒng)形成聯(lián)動(dòng)。聯(lián)動(dòng)分為兩種級(jí)別第一種級(jí)別是把監(jiān)測(cè)數(shù)據(jù)作為空調(diào)啟?;蜃冾l控制的參考輸入比如機(jī)房溫度超過27度時(shí)自動(dòng)把精密空調(diào)設(shè)定溫度下調(diào)1度第二種級(jí)別更穩(wěn)妥系統(tǒng)只做預(yù)警和建議不直接參與控制由值班人員確認(rèn)后執(zhí)行。本次項(xiàng)目由于涉及時(shí)有空調(diào)設(shè)備來自不同品牌直接做控制聯(lián)動(dòng)的協(xié)調(diào)成本高最終采用第二種級(jí)別把告警事件推送給運(yùn)維班組由他們結(jié)合其他運(yùn)行參數(shù)做判斷。決策依據(jù)是Modbus溫濕度傳感器和空調(diào)控制系統(tǒng)不屬于同一個(gè)控制域任何一條獨(dú)立采集鏈路直接疊加到控制邏輯上都存在風(fēng)險(xiǎn)先做好監(jiān)測(cè)和預(yù)警閉環(huán)后續(xù)再逐步考慮控制閉環(huán)會(huì)更穩(wěn)。6. 常見故障與排查技巧實(shí)錄最后這部分是把這次項(xiàng)目實(shí)施過程中踩過的最有代表性的五個(gè)坑整理成速查表這些坑在廠商文檔里基本都不會(huì)寫但對(duì)實(shí)際運(yùn)行的穩(wěn)定性影響非常直接。故障現(xiàn)象可能原因排查方法解決措施某些點(diǎn)位讀數(shù)偶爾跳動(dòng)傳感器供電電壓不穩(wěn)萬用表測(cè)供電電壓檢查開關(guān)電源負(fù)載率更換更高功率的開關(guān)電源或給遠(yuǎn)距離點(diǎn)位就近供電Modbus連接不穩(wěn)定頻繁斷連傳感器TCP棧實(shí)現(xiàn)有缺陷查看網(wǎng)關(guān)日志確認(rèn)斷開原因采集程序加空閑連接?;钚奶{(diào)整socket超時(shí)參數(shù)溫度顯示為負(fù)一百多或超大正數(shù)寄存器字節(jié)序或數(shù)值格式不匹配用Modbus Poll實(shí)測(cè)寄存器原始值對(duì)比按點(diǎn)位表修正轉(zhuǎn)換函數(shù)必要時(shí)修改傳感器字節(jié)序設(shè)置SNMP輪詢響應(yīng)時(shí)快時(shí)慢外掛腳本每次實(shí)時(shí)讀Modbus檢查腳本日志確認(rèn)IO阻塞改成緩存文件機(jī)制采集頻率和查詢頻率解耦Trap告警重復(fù)發(fā)送重發(fā)機(jī)制無狀態(tài)查看Trap接收日志發(fā)現(xiàn)同一事件多條重復(fù)為每條告警維護(hù)狀態(tài)文件僅狀態(tài)變化時(shí)發(fā)送6.1 Modbus斷連問題深入排查Modbus TCP連接不穩(wěn)定這個(gè)問題折磨了我大概一周?,F(xiàn)象是運(yùn)行一陣后某些傳感器的連接就斷了需要手動(dòng)重啟采集程序才能恢復(fù)。后來看節(jié)點(diǎn)日志發(fā)現(xiàn)問題出在部分傳感器的TCP實(shí)現(xiàn)會(huì)在空閑一段時(shí)間后主動(dòng)斷開連接而Node-RED的Modbus節(jié)點(diǎn)默認(rèn)不會(huì)自動(dòng)重連。解決辦法有兩個(gè)層面。第一層是采集端處理Node-RED的Modbus節(jié)點(diǎn)里把連接超時(shí)和重連間隔調(diào)小同時(shí)發(fā)送周期保持一定頻率的請(qǐng)求保證連接活躍。第二層是鏈路層在交換機(jī)端口上設(shè)置以太網(wǎng)空閑斷開時(shí)間較長(zhǎng)避免交換機(jī)把長(zhǎng)空閑連接老化掉。實(shí)際項(xiàng)目中這兩個(gè)措施配合使用后斷連問題基本絕跡。6.2 字節(jié)序與數(shù)據(jù)格式錯(cuò)亂問題字節(jié)序問題很多現(xiàn)場(chǎng)新手工程師都會(huì)栽在這里。有一次部署了一個(gè)傳感器品牌Modbus Poll讀到的寄存器地址里溫度值怎么算都不對(duì)比如室溫應(yīng)該25度讀出來卻是6400。后來對(duì)照寄存器手冊(cè)和實(shí)測(cè)值發(fā)現(xiàn)這個(gè)傳感器默認(rèn)采用的是小端字節(jié)序存儲(chǔ)32位浮點(diǎn)溫度而采集端默認(rèn)按大端解析。調(diào)整解析順序后溫度讀數(shù)馬上就正常了。這里必須養(yǎng)成一個(gè)好習(xí)慣新設(shè)備接入時(shí)先花十分鐘用Modbus Poll把所有用到的寄存器原始值讀一遍再對(duì)照說明書確認(rèn)格式不要直接信任默認(rèn)配置。點(diǎn)位表要把字節(jié)序、數(shù)據(jù)類型、縮放倍數(shù)這“三要素”寫全項(xiàng)目運(yùn)行一年后再來看這個(gè)習(xí)慣的價(jià)值會(huì)非常明顯。6.3 時(shí)鐘同步的隱性影響還有一件容易忽略的事情是時(shí)鐘同步。SNMP Trap報(bào)文里帶了時(shí)間戳如果設(shè)備或網(wǎng)關(guān)的系統(tǒng)時(shí)間和網(wǎng)管平臺(tái)相差很大告警事件在平臺(tái)上的排序會(huì)非?;靵y甚至因?yàn)闀r(shí)間戳在將來導(dǎo)致平臺(tái)過濾掉這些告警。項(xiàng)目上線前一定要把采集網(wǎng)關(guān)、傳感器和平臺(tái)服務(wù)器的NTP時(shí)間同步配置檢查完畢。這個(gè)問題不屬于協(xié)議故障但踩過一次之后會(huì)發(fā)現(xiàn)它在運(yùn)維體驗(yàn)上的影響比協(xié)議故障還大。6.4 網(wǎng)絡(luò)安全域與訪問控制最后聊一下安全性。樓宇自控采集網(wǎng)絡(luò)雖然是內(nèi)網(wǎng)但Modbus協(xié)議本身沒有任何認(rèn)證機(jī)制任何人只要接入這個(gè)網(wǎng)絡(luò)就能讀寫傳感器寄存器如果傳感器支持寄存器寫操作風(fēng)險(xiǎn)更高。SNMP v2c的團(tuán)體名默認(rèn)是public也是很弱的認(rèn)證方式。我的建議是采集網(wǎng)段用獨(dú)立VLAN隔離交換機(jī)端口啟用MAC地址綁定SNMP只允許網(wǎng)管平臺(tái)所在IP訪問禁止傳感器直接訪問管理網(wǎng)或互聯(lián)網(wǎng)。這幾條做下來系統(tǒng)的安全基線就基本合格了。這套系統(tǒng)從需求梳理到逐步落地前后大概用了三周時(shí)間核心部分在三到四天就能完全跑通?;剡^頭來看最值得沉淀的經(jīng)驗(yàn)其實(shí)不是某條具體命令或者某個(gè)配置項(xiàng)而是這種“標(biāo)準(zhǔn)協(xié)議拼裝”的思維方式。Modbus負(fù)責(zé)設(shè)備側(cè)接入SNMP負(fù)責(zé)平臺(tái)側(cè)對(duì)接兩者之間的數(shù)據(jù)橋梁用緩存和服務(wù)腳本解耦整個(gè)系統(tǒng)就不再依賴某家廠商的私有生態(tài)傳感器可以換平臺(tái)可以換協(xié)議層的穩(wěn)定反而成了最可靠的部分。如果你手頭正在做類似的項(xiàng)目建議先把點(diǎn)位表和OID映射表這兩份文檔做扎實(shí)等于把整個(gè)系統(tǒng)的主心骨立住了后面填肉的過程會(huì)順利很多。