ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

西门子PLC与MQTT通讯实战:从TIA Portal配置到设备上云

西门子PLC与MQTT通讯实战:从TIA Portal配置到设备上云 这些年搞工业自动化的人只要碰过设备联网、产线数据采集或者远程运维基本都绕不开一个组合西门子PLC加MQTT。西门子PLC尤其是1200/1500系列作为现场控制的核心MQTT作为工业物联网数据传输的轻量协议两者一结合就能把车间里那些“不会说话”的设备数据以近乎零门槛的方式送到云平台、数据库或者自己的业务系统里。这篇博文我就把基于S7-1200/1500的MQTT通讯完整梳理一遍从协议原理、硬件准备、TIA Portal配置到实际组网踩坑一次性讲透。这篇文章适合谁如果你正在做设备数据上云、远程监控项目或者你手上有一台1200/1500想让它把数据发给MQTT Broker但对着TIA Portal里那几个新增的指令块一头雾水那这篇就是给你写的。就算你之前完全没接触过MQTT只要懂一点PLC基础按着下面的思路走也能把链路跑通。1. 整体设计与思路拆解为什么要在PLC上直接跑MQTT1.1 设备联网的三种主流路径为什么MQTT越来越吃香先聊一个根本问题工厂里想让PLC数据上云传统做法无非三种。第一种是通过上位机WinCC、组态王或者自己写的C#程序把PLC数据采集上来再由上位机转发给云端第二种是加网关比如常见的物联网网关、边缘计算盒子用Modbus、Profinet或者S7协议从PLC里读数据网关再转成MQTT发出去第三种就是直接在PLC里跑MQTT客户端让PLC自己作为MQTT发布者/订阅者直接跟Broker通讯。这三种方案各有适用场景但最近两年直接在PLC上跑MQTT的方案越来越被甲方和设计院认可。原因不复杂少一台中间设备就少一个故障点少一层转换就少一层延迟也少一份后期维护成本。网关方案虽然灵活但网关本身要供电、要配网络、要写转发规则出了问题还得背着电脑去现场排查PLC内置MQTT功能之后很多简单场景就不再需要网关这个“二传手”了。当然PLC直接跑MQTT也有边界。比如你得确认当前固件版本支持1200和1500在较新的固件里才提供MQTT库而且PLC资源有限不适合处理特别复杂的JSON嵌套和频繁的发布任务。我的习惯是数据点少比如几十个以内、发布频率不高秒级或更慢、业务逻辑简单可靠这种场景就用PLC原生MQTT要是数据点几百上千、需要复杂点位映射和协议转换还是老老实实上网关。1.2 MQTT协议到底在解决什么问题发布订阅模型带来的架构变革MQTT的底层逻辑其实特别简单就一句话设备之间不直接通信都通过一个叫Broker消息代理的中间人转发消息。这个模型在工业现场有非常明显的优势。想象一下传统PLC通讯方式你要跟PLC通信就得先知道它的IP然后按照S7协议或者Modbus协议去轮询读取数据。这种点对点的模式在只有一个上位机的时候没问题但当你同时有MES系统要数据、有手机App要看状态、有数据库要存历史数据、有云端平台要做分析难道让每一个系统都去轮询一遍PLC那PLC的通讯负载很快就爆了而且多系统同时访问也存在变量冲突的风险。MQTT的发布订阅模式彻底解决了这个问题PLC作为客户端把数据发布到一个“主题”Topic上谁想用这个数据就订阅这个主题。发布者不需要知道谁在订阅订阅者也不需要知道数据来自哪里只要Broker中转。这样一来无论下游有多少个系统PLC都只需要维护一条到Broker的连接。还有两个MQTT的重要特性在工业场景里特别值钱。一个是服务质量等级QoS0最多发一次1保证至少一次2保证恰好一次我们可以根据数据的重要程度选择。另一个是遗嘱消息LWT设备异常掉线时Broker会替它发布一条预设的“我死了”的消息这在设备远程运维场景里极其好用——设备断网、断电、PLC停机监控端能立刻知道而不是等半天超时才发现。1.3 1200和1500的MQTT能力差异与固件版本要求这是很多初学者上来就踩坑的地方S7-1200和S7-1500不是所有版本都支持MQTT。S7-1500系列包括1500U、1500T等从固件V2.1开始就支持通过库函数实现MQTT通讯但S7-1200要晚一些从固件V4.0开始才在“扩展指令”里提供MQTT客户端库。这里要特别提醒一下S7-1200的MQTT功能跟1500还不完全一样。1200只支持作为MQTT客户端发布消息和订阅消息而且同一时间只能建立一个连接1500除了客户端模式还能作为MQTT服务器Broker使用虽然这个服务器功能咱们实际项目中很少用但至少说明1500的处理能力更强。选型建议如果你的项目对实时性要求高、数据量大、需要多客户端连接直接上1500如果只是几十个数据点定期上报1200完全够用。另外无论用哪个型号固件版本能升就升我见过太多因为固件版本太低找不到“MQTT”库函数的问题其实西门子官网早就把功能加进来了只是你手里的PLC没有更新。还有一个硬件层面的坑老款S7-1200比如CPU 1214C DC/DC/DC固件V3.x即使硬件上支持扩展也无法原生使用MQTT库必须通过额外网关。所以做方案前先确认你手上的硬件到底行不行省得现场拿个老设备配了半天配不通。2. TIA Portal工程配置实操从零开始打通PLC与MQTT Broker2.1 准备工作软件版本、PLC固件和网络规划开始配置之前先把工具链备齐。我用的是TIA Portal V17博途V17这个版本对S7-1200/1500的MQTT库支持已经非常完善了。如果你还在用V15、V16也能做但有些指令块的接口跟V17不完全一样网上搜到的教程可能会对不上号这点要注意。然后是网络规划。PLC和MQTT Broker必须能互相ping通这个听起来是废话但我在现场见过太多IP网段不一致导致连不上的问题。比如PLC默认是192.168.0.1你的MQTT服务器是192.168.1.100两个不在同一网段那肯定没法通信。做项目第一步先画一张网络拓扑图把PLC的IP、Broker的IP、网关的IP如果跨网段的话都标清楚。跨网段的话还要在PLC里配置好网关地址不然数据包出不去。再就是Broker的部署。测试阶段我建议你在电脑上用EMQX或者Mosquitto搭一个本地Broker先用MQTTX客户端验证一下能不能连接再让PLC去连。千万别一上来就连云平台那样出了问题你都不知道是PLC的问题、网络的问题还是云平台配置的问题。本地Broker调试通了再改指向云平台地址这个顺序能帮你省下大把排查时间。2.2 在TIA Portal中添加MQTT库文件打开TIA Portal新建项目或者打开已有项目连上PLC之后接下来就是找MQTT的库函数。步骤是这样的在项目树中展开PLC的“程序资源”右键点击“库”选择“打开库”或者从全球下载中心下载“SIMATIC MQTT”库文件通常是一个.f16或者.zwt格式的文件文件加载进来之后就能在“库”面板里看到MQTT相关的函数块了。如果你用的是TIA Portal V17及以上MQTT库文件一般在安装软件时就自带了不需要额外下载。具体路径是在左侧项目树中展开“库”→“全局库”如果看不到MQTT相关的库用鼠标右键点击“全局库”选择“从文件系统打开”找到安装目录下的MQTT库文件加载进来。这里想多说一句西门子的库文件管理其实做得挺清爽的。你把库文件加载进来后里面的函数块FB可以直接拖到你自己的程序块中使用。但有个细节库里的函数块往往有多个版本选版本的时候注意选跟你PLC固件匹配的版本选错了编译会报错报错信息还会让你一头雾水。2.3 核心指令块解析MQTT_Client、MQTT_Connect、MQTT_Publish加载完库之后你会看到一组跟MQTT相关的函数块常用的是这三个MQTT_Client、MQTT_Connect、MQTT_Publish还有MQTT_Subscribe、MQTT_Receive等。我一个个说我在实际项目里是怎么用的。MQTT_Client这个块负责建立和管理MQTT客户端实例。它需要你提供连接的参数比如Broker地址是IP地址或域名、端口号默认1883加密的话是8883、客户端IDClient ID这个必须是唯一的不能跟其他客户端重复、是否使用TLS加密等。需要特别注意的是TIA Portal里的MQTT库是基于TCP的所以你还要在PLC组态里分配好连接资源确保TCP连接能建立起来。MQTT_Connect负责建立与Broker的实际连接。这个块的输入输出参数比较关键输入有启动连接Execute、连接超时时间输出有连接状态Status、错误码Error等。不是说你调用一次MQTT_Connect就能一直连着实际项目中需要做掉线重连的逻辑——如果连接断了要自动重新调用MQTT_Connect甚至先断开再重连。MQTT_Publish这是最重要的块负责把数据发布到指定的Topic。输入参数里Topic是一个字符串Payload是你要发送的数据内容。这里有一个非常反直觉的点Payload的数据类型通常是Variant或者String但在实际使用中很多人想把PLC里的Real型数值、Int型数值发出去直接接一个Real变量进去是编译不过的或者发出去的是乱码。你需要先把数值转换成字符串格式用西门子的格式化指令比如S_CONV或者自己写一个Real转String的函数再把字符串塞给Payload。这个数据格式转换是很多新手的第一道坎。我自己的做法是先建立一个字符串缓冲区比如一个足够长的String变量用西门子的“Format”指令把数值格式化成JSON格式的字符串比如{温度: 25.5, 压力: 1.2}然后再把整个字符串作为MQTT_Publish的Payload发出去。这样一来云端收到的就是可以直接解析的JSON文本而不是一堆十六进制数。2.4 用SCL编写一个简单而完整的MQTT数据上报程序理论讲了一堆直接上实操代码。下面的SCL代码是一个最简单的“定时上报”程序上电后连接Broker然后每5秒向Topic发布一次温度数据这里用一个模拟量代替真实传感器值。// 定义一个数据块存放MQTT相关的状态变量 // mqttData: // clientId : String : PLC1200_001 // brokerIP : String : 192.168.1.100 // brokerPort : Int : 1883 // topic : String : factory/line1/plc1200/temperature // payload : String : // connectTrigger : Bool : FALSE // publishTrigger : Bool : FALSE // connected : Bool : FALSE // publishDone : Bool : FALSE // errorInfo : String : // 网络组态里已经建立了名为MQTT_TCP的TCP连接 // 连接参数指向 mqttData.brokerIP 和 mqttData.brokerPort // 第一步如果没有连接则触发连接 IF NOT #mqttData.connected THEN #mqttData.connectTrigger : TRUE; MQTT_Connect_DB(REQ : #mqttData.connectTrigger, CONNECT : MQTT_TCP, CLIENT_ID : #mqttData.clientId, KEEP_ALIVE : 30, TIMEOUT : 5000, DONE #connected, BUSY #connectBusy, ERROR #connectError, STATUS #connectStatus); IF #connected THEN #mqttData.connectTrigger : FALSE; END_IF; END_IF; // 第二步连接成功后每5秒发布一次数据 IF #mqttData.connected THEN #pulse5s : BLINK(...#timeBase : T#5S); // 假设用定时器生成5秒脉冲 IF #pulse5s AND NOT #mqttData.publishTrigger THEN // 将模拟量值格式化为JSON字符串 #mqttData.payload : FormatJSON( value : #temperatureReal, topic : #mqttData.topic ); #mqttData.publishTrigger : TRUE; MQTT_Publish_DB(REQ : #mqttData.publishTrigger, CONNECT : MQTT_TCP, TOPIC : #mqttData.topic, PAYLOAD : #mqttData.payload, QOS : 1, RETAIN : FALSE, DONE #publishDone, ERROR #publishError, STATUS #publishStatus); IF #publishDone THEN #mqttData.publishTrigger : FALSE; END_IF; END_IF; END_IF;这个程序看起来简单但里面有几个关键细节值得展开说说。第一个是边沿触发。Publish端的REQ是上升沿触发意思是只有从FALSE变成TRUE的那一瞬间才会执行一次发布。所以你需要在发布完成后把publishTrigger复位否则它会一直触发或者不再触发取决于你怎么写的。这个逻辑在时序上必须严谨。第二个是数据格式化的位置。我在这里调了一个自己写的“FormatJSON”函数它接收一个Real类型的温度值和Topic信息返回一个格式化好的JSON字符串。这一步千万别省你直接拿一个Real变量接PayLoad编译倒是能过但发出去的是一串二进制浮点格式云端解析出来全乱套。格式化的方法有多种可以用字符串连接操作符把各个部分拼起来也可以用西门子的“Format”指令。我的建议是用一个JSON库函数或者自己写一个简单的拼串函数注意处理好小数位数和边界情况。第三个是连接状态与发布状态的联动。很多人容易忽略的是连接和发布是异步的——MQTT_Connect只是发起连接请求真正连接成功是在几十毫秒甚至几秒之后。如果你在连接尚未成功时就调用MQTT_Publish发布会失败或者数据丢失。所以一定要等到MQTT_Connect的DONE信号拉高之后再允许Publish触发。我这个程序里的#mqttData.connected就是从Connect块的DONE信号复制过来的就起到了这个“握手”的作用。还有心跳超时参数KEEP_ALIVE : 30表示每30秒发一次心跳包如果Broker超过约45秒没有收到PLC的任何数据包括心跳就认为连接已断开。这个值可以根据你的网络情况调整如果网络质量较差建议设短一点这样掉线检测更快但设太短会增加网络负载一般30~60秒是经验值。2.5 订阅指令的使用PLC如何接收云端下发的控制命令MQTT不光是PLC往云端上报数据很多时候还需要云端下发指令给PLC比如远程启停、参数修改等。这时候就要用到MQTT_Subscribe和MQTT_Receive了。MQTT_Subscribe的作用是让PLC向Broker订阅一个TopicBroker一旦收到发布到该Topic的消息就会推送给PLC。MQTT_Receive则是接收消息并解析。这里的逻辑跟Publish一样你要面对的核心问题依然是数据解析——云端下发的一般是JSON字符串PLC要从中提取出关键字段比如“动作A”、“参数B”然后转换成对应的控制指令。我的一个个人经验云端下发的消息格式一定要简单稳定。比如这样设计报文{action:start, deviceId:line1}。PLC收到后只判断action字段是start还是stop就能执行相应的启动或停止逻辑。不要在报文里塞太多复杂的嵌套结构因为PLC的字符串处理能力是很弱的解析复杂JSON对于普通工程师来说是一件非常痛苦的事情。实在要传复杂的结构化数据建议在云端网关先做一步转换把复杂JSON降维成PLC容易解析的简单格式再发下去。PLC解析JSON没有现成的库通常的做法是依赖西门子的“查找字符串”和“截取字符串”指令先在一个固定的JSON字符串里找到action:这个关键字然后从这个位置往后截取一定长度的字符再去掉引号、空格等多余字符最后转换成需要的命令值。这个流程写起来不复杂但边界情况比如字段不存在、长度不够要想清楚不然字符串越界会直接把PLC搞停机。3. MQTT参数细节解析连接、心跳、QoS、遗嘱与安全3.1 常用连接参数对照表与推荐取值很多工程师把MQTT联网当成普通TCP连接来配结果连上了但总觉得不踏实因为MQTT里有一堆跟业务相关的参数随便配都能连上但配得好不好直接影响系统稳定性。这里整理一个我自己项目里常用的参数对照表你们可以直接抄。参数名作用推荐取值注意事项Broker地址消息代理服务器的IP或域名按实际填写域名解析需要在PLC侧配置DNS服务器否则请填IP端口Broker监听端口1883TCP明文/ 8883TLS加密有些平台用其他端口以平台文档为准Client ID客户端唯一标识例如PLC1200_LINE1_001同一时间同一个Client ID只能有一个连接冲突会被踢下线Keep Alive心跳周期单位秒3060太短浪费流量太长断线检测慢Clean Session是否清理会话FALSE持久会话设FALSE可以接收离线期间的遗嘱消息但会增加Broker存储开销QoS发布消息服务质量1至少一次0延迟最低但可能丢消息1能保证送达但可能重复2在PLC上一般别用Retain是否保留消息通常FALSE设TRUE时新订阅者立刻收到最后一次消息适合“设备在线状态”这类低频数据这里QoS和Retain要重点解释一下。工业数据里设备温度、压力这种实时数值用QoS 0就够因为丢一拍数据其实无所谓下一秒又传上来了但报警信息、设备启停指令这种控制类数据建议用QoS 1确保至少送达一次。QoS 2在绝大多数PLC场景下没有意义还会给PLC增加额外的协议处理负担我基本不用。Retain这个参数很有意思。拿“设备运行状态”来说如果设为TRUEBroker会保存这条消息任何新接入的客户端一订阅这个Topic立刻会收到最近一次的状态值。这就解决了“后订阅的人看不到当前状态”的问题。但这个功能用不好也有坑如果你把大量高频数据比如温度、压力也设置了RetainBroker会保留很多过期的无意义数据白白占存储空间新订阅用户一进来收到一堆历史数据反而干扰业务判断。所以Retain只适合低频、有状态意义的数据。3.2 Topic设计分层命名与通配符的正确打开方式Topic是MQTT的核心概念它的设计好坏直接决定了后期业务扩展性。Topic本质上是带层级结构的字符串用斜杠分隔比如factory/line1/station3/temperature。这个结构可以模拟工厂的物理层级工厂/车间/工位/数据类型。设计Topic的几个原则第一层级要稳定不要随意增删目录不然下游系统全得跟着改第二把“设备标识”和“数据类型”分开方便用通配符批量订阅第三Topic层级越多消息路由越慢一般4~6层就足够了不要搞十几层。MQTT支持两个通配符匹配单层#匹配多层。比如你想订阅车间1里所有工位的温度数据可以订阅factory/line1//temperature这样只要Topic匹配这个模式的消息PLC都会收到。这对PLC做分组管理非常方便。关于Topic命名强烈建议全部用小写字母和数字用下划线或斜杠做分隔不要用空格、特殊字符。有些MQTT服务器对Topic中的特殊字符处理不一样比如*、$在某些系统中有特殊含义踩过坑就晚了。3.3 QoS和遗嘱消息在PLC场景下的正确选型遗嘱消息Last Will and Testament, LWT是MQTT里很酷的一个功能。PLC在连接Broker时可以设置一条遗嘱消息内容包括遗嘱Topic和遗嘱内容。当PLC非正常断开连接比如断电、网线断了、PLC崩溃且Broker在心跳超时时间内没有收到心跳包时Broker会主动替PLC发布这条遗嘱消息。这个功能在设备远程监控里太有用了。设想你监控100台PLC设备每台的通讯状况如何实时掌握没有遗嘱的话只能写一个轮询程序去挨个检查连接状态有了遗嘱一块PLC掉线Broker立刻在你订阅的“设备状态”Topic下推一条“这台PLC挂了”的消息。我一般这样设计遗嘱报文Topic: factory/health/plc Payload: {deviceId:PLC1200_LINE1_001,status:offline,timestamp:1718012345678}这里的时间戳建议用Unix毫秒时间戳方便云端直接排序和分析。再补充一下遗嘱和Connect的联动关系。遗嘱是随MQTT_Connect连接建立时一起设置的在TIA Portal的MQTT_Connect块里通常有遗嘱Topic、遗嘱消息、遗嘱QoS这些参数。需要注意的是如果PLC正常主动断开连接遗嘱消息不会被发布只有异常掉线时才发布。所以想在云端区分“正常停机”和“异常掉线”可以在PLC停机时先发布一条“online”状态消息再断开连接这样云端就能通过最后收到的“online”消息判断PLC是正常停机而非掉线告警。3.4 安全连接TLS加密与认证在TIA Portal中的实现聊到安全这个话题我发现很多工控工程师要么完全不在乎觉得“内网环境不需要安全”要么想加密但不知道怎么配TLS。先说内网安全的误区现在的工厂网络早不是封闭的了产线Wi-Fi、手机热点、车间办公网都连着当设备向云平台发数据时数据走的是公网明文传输等于把生产数据裸奔。所以至少要做到TLS加密有条件的话上客户端证书认证。在TIA Portal中启用TLS本质上就是给MQTT连接加上安全层。你需要做的第一步是获取Broker的CA证书如果Broker用的是公共证书直接下载CA证书如果是自签名证书需要把服务器证书和CA证书都导出来。然后在TIA Portal中通过“证书管理器”把这个证书导入到PLC的证书存储里。具体路径是项目树中选中PLC → 找到“安全” → 证书管理器在里面导入根证书。证书配好之后在MQTT_Connect参数里把TLS相关的标志位置位指定使用TLS连接。这里常见的问题是证书格式不匹配PLC一般接受DER或PEM格式、证书链不完整需要把中间证书和根证书都导进去、时间不准确PLC系统时间如果和证书有效期对不上证书校验会失败。最后这个坑特别隐蔽我遇到过PLC时间还停留在2023年而证书是2024年签发的情况TLS握手就报证书校验失败折腾了半天才发现是PLC时钟没同步。再加上用户名密码认证MQTT 5.0或者某些Broker支持这样就是双重保障了。配置方式是在MQTT_Connect块里有用户名和密码的输入参数填上Broker里创建好的用户名密码即可。密码建议用强密码别用admin/admin。4. 现场组网常见问题与排查技巧实录4.1 连接失败IP、端口、网段三大元凶怎么快速定位MQTT通讯连不上的情况我总结下来90%都是三个原因IP不通、端口不对、网段隔离。IP不通是最常见的。先别急着看程序直接在电脑上ping一下PLC的IP再ping一下Broker的IP看看是不是都在线。如果跨网段还要检查路由器的路由表是否配置正确。现场很多人默认PLC和Broker在同一个交换机下但如果厂区网络划分了VLAN就算物理上插在同一个交换机上也可能互相访问不了。端口不对的情况也很多。默认的MQTT是1883但有些云平台用的不是1883可能是1884、8080甚至443。还有防火墙的问题——Broker所在服务器的防火墙没有放行1883端口PLC连接直接被拒。排查时可以用MQTTX这类客户端工具在电脑上模拟连接一次Broker如果电脑能连上而PLC连不上问题就出在PLC侧或者PLC到Broker的网络路径上如果电脑也连不上那就是Broker或网络的问题了。网段隔离是个隐蔽问题。你的PLC在192.168.0.xBroker在10.0.0.x两者不在一个网段即使能ping通因为中间有路由PLC侧也需要配置正确的网关地址Gateway不然报文发不出去。并且还要看路由器是否允许跨网段的特定端口通信有些厂区的防火墙策略管得很严端口不放行就直接废了。排查顺序我建议这样做第一步看物理层网线插好没有交换机的指示灯亮不亮第二步看网络层用电脑分别ping两个设备第三步看传输层用MQTTX测试Broker端口是否可连接第四步才看应用层也就是PLC程序里的MQTT_Connect状态字。按照这个顺序来基本半小时内能定位问题。4.2 PLC侧STATE/ERROR状态字排查从错误代码反推问题原因TIA Portal的MQTT库在出问题时会在MQTT_Connect或者MQTT_Publish的状态输出引脚上给出一个错误代码。很多人看到这个代码一脸懵不知道从哪查。这里分享两个途径第一个是直接在TIA Portal的在线帮助F1里搜索该错误代码西门子官方文档一般会有说明第二个是去西门子支持网站搜索“MQTT 错误代码”有专门的FAQ页面。常见的错误代码和原因我遇到过这些状态字显示16#80A1之类通常是连接被拒绝检查Broker地址端口显示证书相关的错误检查TLS证书导入是否正确显示超时错误大概率是网络路径不通或者Broker响应太慢。还有一种是连接建立成功但Publish报错这通常是Topic格式不合法或者Payload数据格式错误。这里要特别提醒大家把MQTT库函数块的STATUS、DONE、BUSY、ERROR引脚引到PLC的HMI或者数据块里这样出问题时能快速从触摸屏或者上位机上看到到底错在哪一步而不是拿电脑连上PLC在线监视程序一个块一个块地翻。这个习惯能节省大量现场调试时间。4.3 数据乱码与Payload格式问题PLC字符串处理的爱恨情仇这个坑我几乎在每一个项目里都会遇到PLC发的数据用MQTTX一订阅看到的是一堆žÂß或者十六进制数根本没法看。原因是PLC里的String类型在底层是以字节序列存储的它对中文和特殊字符的处理方式跟PC端的UTF-8编码不一样而大多数MQTT Broker和云平台默认按UTF-8解码字符串。解决方法有几种。第一种最省事全部用英文字符和数字不用中文。比如报文写{temp:25.5}而不是{温度:25.5}。第二种是提前在PLC侧做好编码转换把ASCII字符串转成UTF-8字节数组再发出。但这个转换在1200上有点麻烦因为它的字符串函数不如1500丰富。第三种是让云端做适配云端接收消息时不要用UTF-8解码而是按GBK西门子的默认字符编码或者原始字节流来处理。这个方案要看你们云端同学配不配合了不过一般来说协议设计成纯英文是最省心的所有端到端的坑全部绕开。再说一个浮点数转字符串的问题。PLC里的Real类型是32位IEEE754浮点数如果你直接把Real类型的值强转成String或者字节数组发出去云端收到的是该浮点数的二进制表示不是人能看懂的25.5。所以必须用字符串格式化指令。TIA Portal的SCL里可以用Format指令也可以用字符串拼接方式比如#jsonString : {temp: RealToString(#temperatureReal, 1) };这里的RealToString是把Real值格式化为保留一位小数的字符串。注意格式化的时候要指定小数点位数不然默认可能会给你输出一长串小数报文体积变大不说看起来也别扭。4.4 掉线重连与看门狗设计让PLC的MQTT连接永远在线MQTT连接不可能永远不掉线。交换机重启、光纤抖动、Broker发布升级任何一个网络事件都可能导致PLC的MQTT连接断开。所以程序里必须有掉线重连机制这是工业级应用和实验室Demo最大的区别。我习惯用状态机来做连接管理状态0表示未连接状态1表示连接中状态2表示已连接但需要发送数据状态3表示已断开需要重连。开机后自动从状态0开始如果连接失败延时5秒重试重试次数超过一定次数比如10次报警提示“MQTT连接失败”但依然继续尝试连接成功后进入正常发送逻辑并且监测连接状态字如果状态字显示连接断开立即回到状态0重新连接。这里有个细节不要在主程序循环里直接反复调用MQTT_Connect。MQTT_Connect调用一次就可以它内部是异步的连接维持期间不需要重复调用。如果你每个扫描周期都去触发Connect它可能会反复重置连接状态导致Broker频繁收到CONNECT报文轻则连接被踢掉重则Broker直接封禁你IP。重连的延时时间也值得琢磨。掉线后立即重连会导致“雪崩效应”——如果Broker短暂不可用几千台设备同时重连会把Broker直接打崩。专业的做法是加退避策略每次重连失败后延时翻倍比如5秒、10秒、20秒最多到60秒封顶。这个逻辑在PLC里实现也不难用一个定时器加一个计数器就行。4.5 常见问题速查表问题现象可能原因解决方法MQTT_Connect一直不置位DONEIP地址填错、端口不通、防火墙拦了用MQTTX先测Broker连通性再检查PLC网络配置连接成功但发布消息云端收不到Topic写错、Payload格式是二进制而非字符串用MQTTX订阅通配符#确认消息是否到Broker检查Topic拼写消息收到但内容乱码字符编码不一致GBK vs UTF-8全部用ASCII字符或在云端按GBK解码频繁掉线每次掉线间隔固定Keep Alive设置过短或网络延迟高调大Keep Alive到60秒检查网络丢包率掉线后不自动重连程序里没有写重连逻辑增加掉线重连状态机参考4.4节TLS连接失败证书格式不对、证书链不全、PLC时间不对重新导出DER格式根证书检查PLC时钟同步订阅消息收不到订阅Topic与发布Topic不匹配未等Subscribe完成就等待消息检查Topic字节完全一致用MQTTX验证Broker转发5. IT侧对接与常见架构从本地Broker到云平台的全链路打通5.1 用Node-RED将OPC UA转成MQTT老设备也能搭上MQTT的快车很多工厂的情况是现场PLC是西门子200 SMART、300、400这些老型号跑不了MQTT又不想花几万块钱换设备怎么把这些老PLC的数据也统一到MQTT体系里我的方案是Node-RED做边缘网关转换。Node-RED是一个基于Node.js的图形化编程工具在树莓派、工控机、Docker容器里都能跑它本身就支持OPC UA客户端和MQTT客户端还能跑Modbus协议。实现思路是Node-RED里先用一个OPC UA节点连接老PLC的OPC UA服务器老的西门子PLC一般需要加一个OPC UA服务器软件比如SIMATIC NET或者Kepware然后在Node-RED里配置一个定时器每秒钟读取一次PLC里需要的数据点再用MQTT Out节点把数据格式化后发布到Broker。这样老PLC的数据就顺利接入统一的MQTT通道了。如果你PLC的数据在Modbus里而不是OPC UA上Node-RED里也有Modbus节点的无非是换一个读取方式。还有一个思路是用Kepware。Kepware是工业界老牌的OPC服务器软件它能连接各种品牌的PLC然后Kepware从V6.5开始自带MQTT客户端功能可以直接把OPC数据推送到MQTT Broker。这个方案的优点是Kepware对老设备的驱动支持得很全面缺点是商业授权费用不低。我的经验是项目预算充足、IT部门又比较介意用Node-RED这种比较“野路子”方式的话就用Kepware预算紧张、想快速出Demo就用Node-RED。5.2 4G边缘网关与阿里云IoT平台的MQTT接入经验分享越来越多的项目不想依赖厂区有线网络直接用4G路由器把PLC联到云平台。这种场景下PLC不会直接跟4G模块通讯而是有一条“PLC → 4G路由器/边缘网关 → 云端Broker”的链路。但这里有个典型问题如果4G网络分配给PLC的是一个私网IP或者浮动IP云端没办法主动连接PLC这时候MQTT的客户端-服务器模式反而是最合适的——PLC主动发起连接云端的Broker被动等待连接根本不需要云端知道PLC的IP。这种组网方式用阿里云IoT平台举例。阿里云IoT平台默认支持MQTT协议但是它的接入域名一般是.aliyuncs.com这种格式而PLC侧的DNS解析是个麻烦事——S7-1200支持域名解析但你得给它配置一个可用的DNS服务器地址。我实际项目里更推荐直接填IP先用电脑nslookup解析出阿里云IoT接入点的IP然后把IP填进PLC的MQTT_Connect参数里。不过要提醒一下阿里云IoT的接入IP可能会有变化如果哪天连不上了先重新解析域名看看IP是不是变了。4G模块连接阿里云时还有几个常见坑第一个是4G卡有没有公网IP有些物联网卡默认没有独立IP但MQTT只需要设备主动出网所以这个其实不影响第二个是平台侧的设备和产品必须提前创建好设备三元组ProductKey、DeviceName、DeviceSecret要写对其中DeviceSecret在MQTT连接时要用密码形式填入第三个是MQTT的Client ID有固定格式阿里云的要求是DeviceName|安全级别|实际签名值具体格式要去阿里云文档里查直接填DeviceName是连不上的。这三个坑哪一个踩中都够喝一壶的。5.3 C#、Qt和Vue3如何消费PLC发来的MQTT消息PLC的数据上了Broker之后下游系统怎么消费如果你是写上位机程序的用C#的话一般用MQTTnet这个开源库支持.NET Core和.NET FrameworkNuGet直接安装写法非常清爽创建一个MqttClient设置Broker地址和ClientId订阅你关心的Topic然后在ApplicationMessageReceived事件里拿到消息内容并解析JSON。对应的C#代码片段大概是这样的var options new MqttClientOptionsBuilder() .WithTcpServer(192.168.1.100, 1883) .WithClientId(PC_Client_001) .WithCleanSession() .Build(); var client new MqttFactory().CreateMqttClient(); client.ApplicationMessageReceivedAsync e { var payload Encoding.UTF8.GetString(e.ApplicationMessage.PayloadSegment); Console.WriteLine($收到消息: {payload}); // 这里转换成你的业务对象比如JObject.Parse(payload) return Task.CompletedTask; }; await client.ConnectAsync(options); await client.SubscribeAsync(factory/#);Qt端的话Qt官方有QMQTT库基于MQTT协议的Qt封装也可以自己用QTcpSocket实现一个极简版MQTT客户端。Vu3前端的话一般做法是使用mqtt.js这个JavaScript库在浏览器里直连WebSocket网关MQTT over WebSocket注意很多Broker需要单独开放8083或者8084端口供WebSocket连接使用。前端拿到PLC数据后就可以实时渲染图表、状态灯、报警列表了整个“PLC → Broker → 网页看板”的链路就这么跑通了。5.4 视觉系统与PLC的MQTT联动方案机器视觉跟PLC通过MQTT联动是这两年智能制造项目里特别常见的需求。传统做法是视觉相机检测完把结果通过I/O点或者串口告诉PLC但这种方式传不了太多信息比如检测缺陷的坐标、类型、数量等。用MQTT之后视觉系统一般是PC或嵌入式的视觉控制器把检测结果以JSON格式发布到TopicPLC订阅后解析然后决定是否触发剔除机构同时PLC也可以把当前的运行参数发布给视觉系统让它动态调整检测算法。这个方案比传统I/O联动的优势非常明显第一信息量大I/O只能传“OK/NG”这种布尔量MQTT能传坐标、类型、置信度等丰富信息第二布线简单只要走网络不需要拉几十根信号线第三调试方便在MQTTX里直接模拟视觉系统发一条消息PLC就能响应不需要真的开机跑产线。PLC端解析视觉结果时建议把协议设计得简单直接比如视觉系统发布Topic: factory/line2/vision/result Payload: {id: 1024, ok: false, code: NG_DEFECT, x: 320, y: 240}PLC这边只要查找“ok:”后面的字符如果是true就执行放行逻辑false就执行剔除逻辑。同时可以把code字段解析出来用于判断缺陷类型从而实现不同缺陷类型的差异化处理。这种联动的响应速度取决于MQTT Broker和网络延迟一般局域网内能做到几十毫秒级别对于大多数非高速视觉检测场景比如每分钟几十个工件的节拍完全够用。6. 实操总结与个人经验补充分享做MQTT通讯这个方向我踩过的坑比大多数人都多。从最初的“PLC发数据到云端”这么一个小需求到最后折腾出完整的边云协同架构中间经历了无数次连接失败、数据乱码、掉线重连。但回头复盘真正核心的东西其实就那么几点第一MQTT的设计思想一定要理解透发布订阅模型想通了后面所有功能都顺理成章第二网络基础不能丢TCP/IP、DNS、TLS这些概念在工业现场同样适用第三错误排查要有方法感状态字、日志、抓包工具配合起来没有解决不了的问题。再分享一个我一直在用的调试利器MQTTX这个桌面客户端软件。它在电脑上可以同时创建多个MQTT客户端既能模拟PLC发布消息也能模拟云端订阅消息而且支持自定义Topic和Payload格式。调试PLC程序时我习惯在MQTTX里订阅PLC要发布的所有Topic一边看PLC的程序状态码一边看MQTTX里收到的数据对不对。这样“程序逻辑错误”和“通讯链路错误”就能立刻分开排查效率翻倍。最后想说的是西门子PLC加MQTT这套组合本质上是把工控领域和物联网领域衔接起来的桥。桥搭好了工厂数据就能顺畅地流向云端MES系统、数字孪生、远程运维这些愿景才有落地的土壤。这套技术栈并不难难的是把每一个环节都做扎实——从PLC里的那几行SCL代码到Broker的合理部署再到云端的数据解析每一环都需要工程师静下心来抠细节。希望这篇文章能帮你少走一些弯路让你在车间里也能写出“会说话”的PLC程序。
返回列表