
今年开春我跑去坝区处理一件拖了两个月的窝火事监控室里那台老工控机上装着基康的采集软件坝基渗压计的测值在里头一跳一跳的看着一切正常。可分管领导要的是实时上云、大屏展示和手机报警而G2采集仪的数据就是出不来——不是设备坏了而是整套链路被厂家私有协议焊死了。这活儿说白了就一句话把基康G2采集仪的私有MQTT协议摸清楚再让BGK4500U这种老式巡检设备也“开口”说话把测值都送进统一的MQTT大网里。这篇文不是推销文章是给同行的一次复盘我会把改造G2的过程拆成协议摸底、串口接入、服务端搭建、现场避坑四个部分适合正在做大坝安全监测系统集成、或者被厂家数据锁定的运行管理单位信息化人员。你要是正对着基康的设备发愁这篇应该能帮你少走好几段弯路。1. 为什么要动这条“老链路”私有软件、人工巡检与数据孤岛1.1 传统监测链路里的三道坎第一道坎是数据“锁在保险柜里”。基康G2这类采集站在坝区扮演的角色说简单点就是“多通道数据采集员”定时给振弦式渗压计、测缝计、温度计发激励信号测频率、测温度算出工程上要用的物理量然后存在自己的存储里。问题是它对外说话只认厂家那套私有通信协议要么靠厂家软件从串口拉数据要么用U盘去现场导。第三方平台想直接拿数据基本拿不到这就叫数据孤岛。第二道坎是人工巡检跟不上。大坝的测点分布在坝基、坝肩、绕坝渗流区很多位置车辆根本到不了。很多单位到现在还在用BGK4500U这类便携读数设备工人扛着设备一个点一个点去读雨天还要担心路滑。测值倒是能读出来可回到办公室再录入Excel时效性全没了。汛期水位暴涨的时候你不可能让工人冒雨一小时巡一圈。第三道坎是应急响应靠人盯。传统链路下测值变化往往是事后才发现。哪天渗压计读数突然蹿升可能标志着坝体渗流异常但数据还压在采集仪里没有实时推送值班人员也就没法第一时间处理。所以这次改造表面上是“换个协议”本质上是在给监测系统装上“即时响应”的能力。1.2 为什么我选MQTT而不是HTTP轮询或Modbus TCP在动手之前有几个同事问过我既然G2能按厂家协议把数据读出来那我用程序定时去拉不就行了为什么非要折腾MQTT我把三个候选方案放在一起对比过方案连接方式弱网表现一对多分发服务端压力适合场景HTTP轮询短连接请求/响应超时多、重试难设计要做广播逻辑站点多时压力大内网稳定环境Modbus TCP中心主动轮询通道不稳定易丢轮次中心单点调度轮询周期难压缩厂内PLC网络MQTT长连接发布/订阅断线缓存、QoS可调一次发布多方订阅broker轻量但很能扛野外弱网、海量站点对比完基本就锁定MQTT了。野外采集站的环境大家都清楚4G信号说断就断冬天山上温度零下设备长时间没人碰。MQTT的长连接设计天生适合这种场景采集仪作为发布者主动把数据推上来平台端订阅就行不需要知道每个野站到底在什么内网IP后面。最实用的一点MQTT自带QoS等级和遗嘱消息设备掉线了broker还能通知平台“某某站点失联了”这在HTTP轮询里得自己写一堆逻辑才能模拟出来。2. 摸清基康G2采集仪私有MQTT协议的底细2.1 G2在改造中的设备定位先明确一下G2干活的位置它挂在坝区的测点旁边机箱里是一块采集板加通信模块外面接十几路传感器上面可能还装了一块太阳能板。改造之后它的任务从“只对厂家软件说话”变成“把测值作为消息发布到MQTT服务器”。注意这里我不替厂家背书不同固件版本的G2开放程度不一样有的出厂就带了MQTT客户端功能有的要靠升级或扩展模块才支持。我在现场碰到过一种情况设备管理界面里能填“MQTT服务器地址、端口、用户名”看起来像支持MQTT可固件文档里却一个字都没提代码层级上也可能只是预留了接口。这种时候你千万别急着写业务代码先验证它到底能不能把消息发出来。2.2 从“黑盒”到“白盒”三个手段快速摸底想要摸清私有协议我的做法是三步走缺一不可。第一步在办公室里装一个MQTT客户端工具我推荐MQTT Explorer。它最大的好处是支持通配符订阅你输入一个#就能把某个broker上所有主题都看一遍主题树一目了然。这个工具在Windows、Linux、macOS上都有安装包下载很方便后面我还会细讲使用步骤。第二步抓包。如果G2支持配置MQTT参数你把它指向自己临时搭的broker然后在网关上用tcpdump或者Wireshark抓网络包。抓包能看到的内容比客户端工具更细连接的cleanSession标志、心跳间隔、发布消息的主题和payload原始字节、有没有遗嘱消息这些信息对后续对接非常有价值。第三步要协议文档而且要带版本号。厂家给的协议文档往往只写了“推荐用法”到了现场一测字段对不上、字节序不一样的情况太多了。所以拿到文档后我的经验是先把文档放一边用抓到的真实报文去验证文档里的每一个字段再按实际报文重新整理一份自己的字段对照表。2.3 主题与Payload的典型套路在基康这类工业设备的私有MQTT实现里主题命名通常有一定规律常见的是按站点和设备拆层级。举个例子我接触过的设备里测量数据会发布到这样的主题bgk/{station_id}/{device_id}/measurement设备在线状态则发到bgk/{station_id}/{device_id}/online至于Payload我见过JSON也见过二进制帧。JSON的一帧长这样这是我在项目里实际整理出来的示例格式字段名以固件为准{ dev: G2-20240510-001, ts: 1719990000, ch: [ {n: 1, freq: 2456.3, temp: 18.5}, {n: 2, freq: 2301.8, temp: 18.6} ] }二进制帧则紧凑得多常见结构是帧头、数据长度、设备ID、数据段、CRC校验。数据段里频率值可能是4字节float温度是2字节整数。这里最容易踩的坑是字节序有些固件高字节在前有些低字节在前不实测盲猜的话解析出来的数据会面目全非。我的建议是无论文档怎么讲先把真实数据抓下来存成样本文件写解析器时用样本一个个对。手里有个几十帧真实报文比啥文档都管用。2.4 用MQTT Explorer做一次真实观测这里说下具体操作方便第一次干这活的兄弟快速上手。先下载安装MQTT Explorer打开后点“Add connection”填一个连接配置填写broker地址、端口默认1883、用户名密码Base topic可以留空。连接成功后在顶部搜索框输入#订阅全部主题左侧就会刷新出一棵主题树。看到主题树之后点击任意一个主题右侧会显示实时消息内容。JSON会自动格式化二进制内容会显示成十六进制两种都方便复制。我的习惯是在确认协议之前先订阅#连续跑半小时把期间产生的所有消息保存下来存成一份格式统一的样本库。样本够了再停别手欠中途断掉。有一点必须提醒如果现场有多个采集站同时在线订阅#会把所有站点的消息都拉下来初期看起来比较乱。这时可以把通配符换成具体设备ID去过滤比如bgk/station_01/#先单站单点验证。3. BGK4500U的接入把巡检老设备也拉进MQTT大网3.1 BGK4500U到底是什么角色很多运行管理单位手里其实有两套数据一套是G2自动采集的在线数据另一套是工人用BGK4500U这类便携读数仪在现场对关键测点做定期巡检得到的数据。BGK4500U在基康的产品体系里属于老一代读数终端能接在振弦式传感器上读出频率和温度也能在屏幕上显示结果有些型号带存储串口还可以输出数据。这次改造里它的角色比较特殊我不打算让它联网那太折腾了而是让它在巡检时把数据“说”出来由旁边的边缘网关或者G2的扩展串口收下再转成MQTT消息上报。这样自动测值和人工巡检值终于能在同一个平台上并排展示、互相验证。3.2 串口参数与数据帧先让设备开口第一步永远是确认串口参数。BGK4500U这类设备常见的串口设置是波特率9600或19200数据位8位停止位1位无校验。先把USB转串口线接上用一个串口调试助手我用的是开源的Serial Port Assistant发一个查询指令或者直接看它主动输出的数据流。输出的数据帧多半是两种风格一种是ACSII明文类似$BGK4500,SN12345,CH1,F2456.3,T18.5*3F另一种是Modbus RTU的十六进制帧。ASCII明文比较简单直接按分隔符拆字段Modbus RTU则要按寄存器地址去解析数据。无论哪一种我都建议先把采集到的原始帧存进日志文件便于后续重复验证。3.3 边缘封装串口数据变成MQTT消息拿到原始帧之后需要一个“翻译官”把串口数据转成MQTT消息。我这边选择独立边缘网关的方案原因很简单不依赖G2固件是否开放扩展串口能力网关自己说了算后期换设备也不影响。网关我用过树莓派也用过工业级的嵌入式工控机逻辑都一样。下面是一段可运行的Python示例思路是打开串口读取一行解析出频率和温度然后以JSON形式发布到broker。用到的库是pyserial和paho-mqttpip install pyserial paho-mqtt就可以装上。import time import serial import json import paho.mqtt.client as mqtt SERIAL_PORT /dev/ttyUSB0 BAUD_RATE 9600 BROKER 192.168.1.100 TOPIC bgk/station_01/bgk4500u/measurement # 连接MQTT client mqtt.Client(client_idbgk4500u-gateway) client.username_pw_set(gw_user, gw_pass) client.connect(BROKER, 1883, keepalive60) client.loop_start() ser serial.Serial(SERIAL_PORT, BAUD_RATE, timeout3) while True: line ser.readline().decode(ascii, errorsignore).strip() if not line.startswith($BGK4500): continue payload {} for item in line.split(*)[0].split(,)[1:]: if in item: key, value item.split() payload[key] value # 组上报消息 msg { source: bgk4500u, sn: payload.get(SN), channel: payload.get(CH), freq: float(payload.get(F, 0)), temp: float(payload.get(T, 0)), gw_ts: int(time.time()) } client.publish(TOPIC, json.dumps(msg), qos1) print(published:, msg)运行之前记得把串口设备号、broker地址、账号密码换成自己的。我在现场还加了一个很关键的细节发布消息时带了gw_ts字段记录网关接收的时间这个时间戳在后面数据校核和追历史时非常有用。4. 服务器端搭建MQTT服务、账号权限与数据落库4.1 服务端选型EMQX还是Mosquitto用户量不大、站点数也就几十个的场景用Mosquitto完全够配置简单消耗小。但如果平台将来要接入上千个测点、还要做规则引擎、数据库持久化、设备管理我建议直接用EMQX它的内置能力能省不少开发量。举个具体例子我这次给单位搭的平台用的是EMQX的Docker镜像一条命令就能跑起来docker run -d --name emqx -p 1883:1883 -p 18083:18083 emqx/emqx:5.4.01883是MQTT端口18083是管理控制台网页端口。启动后访问http://服务器IP:18083默认用户名admin、密码public进控制台后第一件事就是改密码、创建业务账号。如果是Windows Server不想装Docker装Mosquitto的Windows安装包也行配置过程不难官方文档写得很清楚我就不展开了。4.2 账号权限与TLS别开匿名大门这片段我特意要强调千万不要开匿名访问。野外设备接入后broker会暴露在公网没有账号限制等于把采集数据裸奔在路上出一次数据泄露就够喝一壶。我的做法是分两类账号设备侧账号只能发布平台侧账号只能订阅。在EMQX里可以为客户端分配不同的发布/订阅权限比如设备账号允许bgk/#发布平台账号允许bgk/#订阅。这样即使设备被外人控制他也只能往这个主题塞垃圾数据拿不走别人的数据。通信加密也一样要做。如果条件允许直接上TLS证书把broker的CA证书提前配置到网关设备里。很多现场设备是老固件不一定支持TLS那就先跑明文内网但要严格限制端口只对内网开放。这个权衡没法一刀切但原则不变能加密就别裸奔。4.3 数据订阅与入库从消息到报表broker搭好、设备消息进来之后最核心的业务是数据落库。我这里的做法是用Python订阅采集主题解析JSON后写入时序数据库。时序库我用了TDengine当然MySQL也能行只是查询连续的仪表数据时时序库更顺手。一个简单的订阅入库脚本逻辑如下import json import paho.mqtt.client as mqtt from datetime import datetime def on_message(client, userdata, msg): payload json.loads(msg.payload.decode(utf-8)) print(ftopic{msg.topic}, ts{payload[ts]}, freq{payload[ch][0][freq]}) # 在这里把解析后的数据写入数据库 client mqtt.Client() client.username_pw_set(plat_user, plat_pass) client.connect(127.0.0.1, 1883) client.subscribe(bgk/#, qos1) client.on_message on_message client.loop_forever()注意一个细节订阅的主题用bgk/#这会把G2和BGK4500U的消息都收进来。如果你想分开处理就分别订阅bgk///measurement这类更精确的主题再在on_message回调里按topic判断数据来源。有人喜欢在平台端统一做清洗我更喜欢在边缘网关就把数据标准化好平台只负责存和显示少折腾。5. 现场改造绕不开的坑信号、时间戳、固件与接线5.1 弱网区断线重连与数据缓存河谷地带4G信号不稳定这是所有野外监测项目的老大难。G2或者边缘网关一掉线消息发不出去平台端就发现“数据断层”。MQTT本身有断线重连机制但光靠它不够发布端必须把发不出去的数据先在本地缓存等网络恢复再补发。我遇到过最坑的一件事某测站掉线三天重连之后因为没做补发三天数据直接消失跑过去一看SD卡里明明存着原始测值。后来我在网关程序里加了一个简单的本地队列用SQLite存待发消息断线重连后按顺序补发。实现逻辑不复杂但数据完整性好了不止一个档次。补发的时候要有序别一股脑全塞给broker适当加sleep限速。另外QoS级别选1就够QoS2在弱网下一次重传卡死的情况我也见过千万别盲目追求高级别。5.2 时间不同步平台端永远对不上大坝监测数据最讲究“时间对齐”可现场设备的时间来源五花八门有带GPS授时的有只靠板载RTC的还有干脆没电池一停电就归出厂时间的。G2如果没接NTP停电重启后时间可能回到出厂值BGK4500U这类便携设备的时钟更不靠谱。我的对策是双时间戳设备自己上报一个ts网关或服务器再记一个gw_ts。入库时两个都存查询时优先用校准后的时间。这个习惯救过我两次一次是报警提前一次是曲线错位最后都是靠双时间戳查出来的。5.3 固件版本差异带来的字段漂移私下说一句基康不同批次、不同固件版本的G2MQTT报文里的字段名和单位都可能不一样。有的版本频率字段叫freq有的叫F还有的干脆用frequency。我这次还遇到过单位显示Hz某些版本则直接给模数需要乘一个系数才算出来。解决方法不复杂但很笨每次对接到不同版本设备先把真实报文备份到本地再写一个字段映射层。不要把解析代码写死在业务逻辑里而是做成一份配置文件设备固件不同就切换不同解析配置。这样以后升级固件平台端的改动会小很多。5.4 防雷供电与接线最容易返工的地方大坝监测站大多在户外雷雨季一来信号线上的感应雷想躲都躲不掉。串口线和网线裸露在机箱外的话务必加信号防雷器机箱也要做好接地。现场接地不规范设备频繁死机是常事查半天查不到原因。供电方面别让MQTT网关和传感器共用一路开关电源传感器的激励信号很容易干扰通信模块导致消息丢帧。用独立的工业级DC-DC电源给网关供电输入电压留出20%以上的余量稳定的电源能省掉一多半稀奇古怪的毛病。接线最容易被新手忽略串口线引脚顺序不同厂家定义不一样接错线开机没反应甚至烧串口芯片都不稀奇。干活前先翻设备手册用万用表量好引脚电平宁可多花十分钟确认也别等烧了再后悔。写到这里其实这次改造里最让我感慨的不是代码写得有多顺而是头一回用MQTT Explorer打开主题树、看到G2的消息一条条跳出来那一刻——数据终于不再是厂家软件的私藏品了。如果你现在也在捣鼓类似的接入我给三条实在建议先在办公室拿真实设备跑通全链路再上现场每一个解析字段都要有真实报文背书别信文档一家之言正式切换前一定并行走一段旧系统两边数据都对上了才动手拆老链路。大坝安全监测这行稳字当头慢慢来反而最快。