ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Web SCADA实战:PLC网关+MQTT+Node.js构建工业物联网监控系统

Web SCADA实战:PLC网关+MQTT+Node.js构建工业物联网监控系统 1. 从一台PLC到浏览器画面这套方案到底在解决什么问题车间里一台台达或者汇川的PLC跑得好好的设备动作、逻辑联锁、PID调节全在本地闭环但老板站在办公室想看实时温度曲线工艺工程师想回看昨天下午三点那批料的压力波动维修班组想提前知道某台电机的电流是不是在缓慢爬升——这些需求靠传统的触摸屏和组态软件已经很难优雅地满足了。传统上位机组态软件授权贵、部署重、跨平台差改一个画面要跑到现场手机上看不了远程访问更是要额外买一堆东西。而Web SCADA这套思路本质上是把数据采集和数据呈现彻底解耦PLC负责它最擅长的实时控制工业智能网关负责协议转换和边缘计算MQTT负责把数据以轻量、可靠、可订阅的方式搬运到服务端Node.js负责接收、存储、转发和驱动前端页面。整条链路里每一环都只做自己最擅长的事。我最早接触这套架构是在一个注塑车间的能耗监测项目上现场有六台不同品牌的PLC有西门子的S7-200 SMART有台达的DVP系列还有两台汇川的H5U。如果按老办法每台PLC配一套组态光授权费就够买好几台工控机了。后来换成网关采集 MQTT上云 Node.js服务 浏览器展示的组合硬件成本降了一半开发周期从两个月压到三周而且手机、平板、办公室大屏都能同时看。这套方案适合谁适合有一定PLC基础、想往工业物联网方向走的电气工程师适合会一点JavaScript、想切入工业场景的Web开发者也适合那些被传统组态软件绑住手脚、想自己掌控数据链路的自动化集成商。你不需要是网络专家也不需要精通嵌入式只要能把PLC的地址表理清楚剩下的部分这篇文章会一步步拆给你看。整条链路的核心逻辑其实就一句话网关把PLC的寄存器读出来打包成MQTT消息发到BrokerNode.js订阅这些消息存下来并推给前端。听起来简单但每一环都有坑比如PLC的端口号怎么设、MQTT的QoS选几、Node.js怎么在Windows上做成服务、断线重连怎么处理、消息丢了怎么办。下面我按实际落地的顺序从架构设计讲到代码实现再讲到排查技巧尽量把踩过的坑都摊开说。2. 整体架构设计与关键选型思路2.1 为什么是网关 MQTT Node.js这个组合先说为什么不用PLC直接连服务器。大部分中小型PLC的以太网口只支持厂商私有协议或者Modbus TCP你让PLC自己去发HTTP请求、处理JSON、做断线重连它根本没这个能力强行做也会拖慢扫描周期影响控制逻辑的实时性。工业智能网关的价值就在这里它向下用Modbus TCP、OPC UA、或者厂商专用协议跟PLC通信向上用MQTT跟服务器通信中间还能做数据过滤、变化上报、断线缓存。网关相当于一个翻译官兼缓冲池PLC只管说自己的方言网关负责翻译成MQTT这种普通话。再说为什么选MQTT而不是HTTP或者WebSocket直连。MQTT是发布/订阅模型一个网关发布的数据可以被多个订阅者同时消费——Node.js服务订阅一份存数据库另一个告警服务订阅一份做实时判断前端页面再订阅一份做实时刷新互不干扰。而且MQTT有QoS等级QoS 1能保证消息至少到达一次对于温度、压力这种周期性数据足够用了。HTTP是请求/响应模型服务端不主动问网关就得自己定时推推的频率和时机都不好控制。WebSocket虽然能双向通信但它是为浏览器场景设计的让网关去维护WebSocket连接稳定性不如MQTT成熟。Node.js在这个架构里的角色是服务端胶水层。它要订阅MQTT主题、把数据写进时序数据库或者关系库、通过WebSocket推给前端、提供REST API给历史查询。为什么不用Java或者PythonNode.js的事件驱动模型天然适合这种大量并发连接、每个连接数据量不大的场景一个Node.js进程轻松扛住几千个MQTT连接和WebSocket连接。而且前后端可以共用JavaScript生态前端用React或者Vue后端用Express或者Fastify开发效率很高。当然Python也能做但Node.js在实时推送这块的生态更顺手。2.2 数据流向与主题设计整条链路的数据流向是这样的PLC寄存器 → 网关采集 → MQTT Broker → Node.js订阅 → 数据库 WebSocket推送 → 浏览器渲染。这里面最关键的设计决策是MQTT主题Topic的命名规范。主题设计得好后期扩展和维护会轻松很多设计得乱订阅关系会变成一团麻。我一般用这样的层级factory/workshop/line/device/type。举个例子一号车间二号线的注塑机温度数据主题就是factory1/workshop1/line2/injection1/temperature。这样设计的好处是可以用通配符批量订阅比如factory1/workshop1///temperature就能订阅一号车间所有设备的温度。网关侧配置的时候每个采集点对应一个主题主题里带上设备标识和数据类型Node.js侧按业务需求订阅不同的通配符。注意主题层级不要太深一般四到五级就够了。层级太深会让订阅通配符变得复杂而且有些Broker对主题长度有限制。另外主题里不要用中文和特殊字符用英文小写加下划线或者斜杠分隔兼容性最好。2.3 网关选型与PLC侧准备工业智能网关市面上选择很多有支持多协议的通用网关也有针对特定品牌的专用网关。选型的时候重点看几个参数支持的PLC协议种类、最大采集点数、是否支持边缘计算比如变化上报、死区过滤、MQTT QoS支持等级、断线缓存容量。对于入门项目选一款支持Modbus TCP和OPC UA的通用网关就够了价格从几百到几千不等。PLC侧的准备往往被忽视但这一步没做好后面全是坑。首先确认PLC的以太网口IP地址和端口号。以台达PLC为例默认Modbus TCP端口是502但有些型号或者经过组态后端口号会变。你需要在PLC编程软件里确认通信端口设置确保网关能ping通PLC的IP。如果是西门子S7系列除了IP和端口还需要确认机架号和槽号以及是否允许PUT/GET通信。汇川PLC相对简单Modbus TCP直接映射寄存器地址。还有一个容易忽略的点PLC的寄存器地址映射。不同品牌PLC的寄存器地址格式不一样台达用D、M、X、Y西门子用DB块、M区、I/Q区汇川用D、M、R。网关配置的时候要把这些地址映射成统一的标签名比如把台达的D100映射成injection1_temperature这样Node.js侧就不用关心底层是什么PLC了。3. 核心环节实操从PLC配置到MQTT消息落地3.1 PLC侧配置与寄存器地址确认先拿台达PLC举例。假设你用台达的ISPSoft或者WPLSoft编程程序下载完之后需要确认以太网通信参数。在软件里找到通信设置或者以太网设置确认IP地址比如192.168.1.10、子网掩码、网关。Modbus TCP的端口号默认是502如果没有特殊需求不要改。然后在程序里确认你要采集的寄存器地址比如温度存在D100压力存在D102电机电流存在D104。这些地址要记下来网关配置的时候要用。如果是西门子S7-1200或者S7-1500情况稍微复杂一点。你需要在博途TIA Portal里做几件事第一确认PLC的IP地址和子网掩码第二在防护与安全里勾选允许来自远程对象的PUT/GET通信访问否则网关读不到数据第三确认你要读取的数据块DB块是否勾选了优化的块访问如果勾选了网关无法按绝对地址读取需要取消勾选或者用符号名访问。第四确认机架号和槽号S7-1200一般是0和1S7-1500也是0和1但S7-300可能是0和2。汇川PLC的配置相对友好H5U系列支持Modbus TCP寄存器地址直接对应D区、M区。在AutoShop里确认以太网参数然后记下要采集的软元件地址就行。需要注意的是汇川有些型号的Modbus TCP端口不是502需要在配置里确认。实操心得PLC侧配置完成后先用Modbus调试工具比如Modbus Poll从电脑上读一下数据确认能读到正确的值。这一步能排除掉大部分网络和地址映射问题。如果Modbus Poll都读不到网关肯定也读不到先别急着调网关。3.2 工业智能网关的采集配置网关的配置一般通过Web界面或者专用配置软件完成。以常见的通用网关为例配置流程大致是新建一个设备选择PLC协议Modbus TCP填入PLC的IP和端口然后添加采集点。每个采集点需要填寄存器地址、数据类型16位整数、32位浮点数、布尔量等、采集周期、MQTT主题、是否启用变化上报。这里重点说几个参数。采集周期不要设得太短对于温度、压力这种慢变量1秒到5秒足够了对于电流、位置这种快变量可以设到200毫秒到500毫秒。设太短会加重PLC和网关的负担而且MQTT消息量会暴涨。变化上报是个很实用的功能网关只在数值变化超过死区比如0.5度时才发MQTT消息能大幅减少无效数据。数据类型一定要和PLC侧对应比如PLC里是32位浮点数网关侧选16位整数就会读出乱码。网关的MQTT配置需要填Broker地址、端口、客户端ID、用户名密码如果有、发布主题前缀。客户端ID要唯一否则多个网关用同一个ID会互相踢下线。发布主题前缀一般设成factory1/workshop1/line2/injection1/然后每个采集点的主题就是前缀加数据类型。3.3 MQTT Broker的搭建与配置MQTT Broker是整条链路的消息中枢选型上入门推荐EMQX或者Mosquitto。EMQX功能全、有Web管理界面、支持集群适合正式项目Mosquitto轻量、配置简单适合测试和小规模部署。这里以在Windows上搭建Mosquitto为例说明。下载Mosquitto的Windows安装包安装完成后配置文件在安装目录下叫mosquitto.conf。默认配置只监听本地回环地址需要改成监听所有网卡listener 1883 0.0.0.0 allow_anonymous true如果要做用户认证把allow_anonymous改成false然后配置密码文件password_file C:\mosquitto\passwd用mosquitto_passwd命令创建用户和密码。配置完成后把Mosquitto注册成Windows服务这样开机自启不用每次手动开命令行mosquitto install net start mosquitto在Linux上比如CentOS 7.9可以用yum安装或者源码编译。安装后同样修改配置文件然后用systemctl管理服务。需要注意的是CentOS 7.9的防火墙默认可能没开1883端口需要手动放行firewall-cmd --zonepublic --add-port1883/tcp --permanent firewall-cmd --reloadBroker配置好之后用MQTT Explorer或者MQTTX连上去测试一下确认能连接、能发布、能订阅。这一步没问题了再往下走。3.4 Node.js服务端搭建与MQTT订阅Node.js的安装不复杂Windows上直接下载LTS版本比如18.20.4 LTS或者22.12一路下一步就行。安装完成后用node -v和npm -v确认版本。Linux上可以用nvm管理多版本或者直接下载二进制包解压配置环境变量。服务端项目初始化mkdir scada-server cd scada-server npm init -y npm install mqtt express ws sqlite3这里用了四个核心库mqtt负责连接Broker和订阅消息express提供REST APIws提供WebSocket推送sqlite3做本地数据存储正式项目可以换成InfluxDB或者MySQL。MQTT订阅的核心代码大概长这样const mqtt require(mqtt); const client mqtt.connect(mqtt://192.168.1.100:1883, { clientId: scada_server_ Math.random().toString(16).substr(2, 8), clean: true, reconnectPeriod: 5000 }); client.on(connect, () { console.log(MQTT connected); client.subscribe(factory1/workshop1///temperature, { qos: 1 }); client.subscribe(factory1/workshop1///pressure, { qos: 1 }); }); client.on(message, (topic, payload) { const value parseFloat(payload.toString()); const parts topic.split(/); const device parts[3]; const type parts[4]; console.log(${device} ${type}: ${value}); // 存数据库 WebSocket推送 });这段代码里几个关键点clientId要唯一用随机数后缀避免冲突reconnectPeriod设成5000毫秒断线后5秒重连订阅时指定QoS 1保证消息至少到达一次message事件里解析主题和载荷然后做后续处理。3.5 数据存储与WebSocket实时推送数据存哪里取决于你的需求。如果只是做实时展示和短期回看SQLite够用了如果要存几个月的历史数据做分析建议上InfluxDB或者TimescaleDB。SQLite的建表语句CREATE TABLE IF NOT EXISTS sensor_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, device TEXT NOT NULL, type TEXT NOT NULL, value REAL NOT NULL, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_device_type_time ON sensor_data(device, type, timestamp);WebSocket推送用ws库在MQTT消息到达时广播给所有连接的前端const WebSocket require(ws); const wss new WebSocket.Server({ port: 8080 }); wss.on(connection, (ws) { console.log(Client connected); }); function broadcast(data) { wss.clients.forEach((client) { if (client.readyState WebSocket.OPEN) { client.send(JSON.stringify(data)); } }); }在MQTT的message事件里调用broadcast前端就能实时收到数据。前端用原生WebSocket或者Socket.IO连接收到数据后更新图表。4. 常见问题与排查技巧实录4.1 PLC通信类问题问题一网关读不到PLC数据但PLC本身运行正常。排查顺序先用电脑ping PLC的IP确认网络通再用Modbus Poll或者厂商调试工具读寄存器确认协议和地址对然后检查网关的协议配置、IP端口、寄存器地址、数据类型是否匹配。常见原因是端口号不对有些PLC改过默认端口、寄存器地址偏移比如PLC里是D100网关里要填400101、数据类型不匹配32位浮点数当成16位整数读。问题二西门子PLC能ping通但读不到数据。九成是博途里没勾选允许PUT/GET通信访问或者DB块勾了优化的块访问。前者在PLC属性里的防护与安全里改后者在DB块属性里取消勾选。另外确认机架号和槽号S7-1200/1500一般是0和1。问题三数据能读到但数值不对。检查数据类型和字节序。Modbus协议里32位浮点数有ABCD和CDAB两种字节序不同PLC默认不一样。网关配置里一般有字节序选项试一下交换字节序看数值对不对。另外确认寄存器地址是十进制还是十六进制有些网关要求填十进制有些要求填十六进制。4.2 MQTT类问题问题一网关连不上Broker。检查Broker的IP和端口是否可达用telnet或者Test-NetConnection测试1883端口。检查Broker是否允许匿名连接如果配置了用户认证网关侧要填对用户名密码。检查客户端ID是否冲突多个网关用同一个ID会互相踢。问题二消息丢失。MQTT的QoS等级决定了消息可靠性。QoS 0最多一次可能丢QoS 1至少一次可能重复QoS 2恰好一次开销最大。对于工业数据QoS 1是性价比最高的选择。另外检查Broker的持久化配置如果Broker重启后消息全丢说明没开持久化。Mosquitto的持久化配置是persistence true和persistence_location。问题三消息延迟大。检查网络带宽和Broker负载。如果网关采集周期太短、消息量太大Broker处理不过来会积压。可以适当加大采集周期或者启用变化上报减少消息量。另外检查Node.js服务端的处理逻辑如果每条消息都同步写数据库高并发时会阻塞建议用批量写入或者消息队列缓冲。4.3 Node.js服务端问题问题一Node.js服务在Windows上关掉命令行就停了。需要用pm2或者node-windows把Node.js应用注册成Windows服务。pm2的用法npm install -g pm2 pm2 start app.js --name scada-server pm2 save pm2 startuppm2在Linux上很好用Windows上建议用node-windows或者nssm。问题二WebSocket连接不稳定。检查是否有反向代理比如Nginx没配置WebSocket升级头。Nginx配置里需要加proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade;另外检查防火墙是否放行了WebSocket端口。问题三数据库写入慢。SQLite在大量并发写入时性能有限建议改成批量写入比如每100条或者每1秒写一次。如果数据量很大换InfluxDB或者TimescaleDB它们对时序数据做了专门优化。4.4 常见问题速查表现象可能原因排查方法解决方式网关读不到PLC网络不通/端口错/地址错ping Modbus Poll逐项核对IP、端口、寄存器地址西门子读不到PUT/GET未开/优化块访问检查博途配置勾选PUT/GET取消优化块访问数值乱码数据类型/字节序不匹配对比PLC和网关配置调整数据类型和字节序网关连不上Broker端口未放行/认证失败telnet测试端口放行端口核对用户名密码消息丢失QoS 0/无持久化检查QoS和Broker配置改QoS 1开启持久化Node.js服务停了未注册系统服务检查进程用pm2或nssm注册服务WebSocket断连代理未配置升级头检查Nginx配置添加Upgrade和Connection头避坑技巧整个链路调试的时候从后往前调。先确认Broker能连、能收发消息再确认网关能发消息到Broker再确认PLC能被网关读到。不要一上来就调前端前端只是展示层数据链路通了前端自然就有数据。5. 从入门到落地的扩展思路这套架构跑通之后能扩展的方向很多。比如加一个告警模块Node.js订阅MQTT消息后判断阈值超限就发邮件或者推送到企业微信。比如加一个历史查询页面前端调REST API从数据库拉数据用ECharts画趋势图。比如加一个远程控制功能前端发指令到Node.jsNode.js通过MQTT发布到网关网关再写PLC寄存器。再比如多车间多工厂的场景Broker做集群Node.js服务做负载均衡数据库做分库分表。我自己在实际项目里踩过最大的坑是网关的断线缓存。有一次车间网络抖动网关和Broker断了十分钟恢复后这十分钟的数据全丢了。后来换了支持断线缓存的网关断线期间数据存在网关本地恢复后自动补发才解决了这个问题。所以选网关的时候断线缓存容量一定要问清楚至少能存几个小时的数据。另一个坑是MQTT主题设计太随意。早期项目主题是data/device1/temp这种后来设备多了想按车间批量订阅就没办法了。后来统一改成factory/workshop/line/device/type五层结构扩展性好了很多。这个经验分享出来希望大家一开始就把主题规范定好不然后期改起来很痛苦。最后说一个前端的小技巧。WebSocket推送的数据频率可能很高如果每来一条就重绘图表浏览器会卡。建议在前端做节流比如每200毫秒合并一次数据再重绘或者用ECharts的appendData增量更新。另外图表的数据点不要无限追加保留最近几百个点就够了历史数据通过查询接口按需加载。
返回列表