简介:西门子S7-1500PLC与第三方Modbus TCP网关的通信实例PDF文档,面向工业自动化工程师、PLC编程调试人员,重点解决WinCC直连多个Modbus TCP网关通信轮流中断的工程难题,核心思路是让S7-1500PLC作为主站,统一采集四路高频电源控制器数据后再交给上位机。资源包内共1个PDF文件,压缩包约1.34MB,完整收录了项目现状、高频电源控制器RS485串口通讯参数、Modbus寄存器地址表、卓岚ZLAN5143I网关配置步骤,以及博图环境下轮询程序和通信程序编写要点,基本覆盖从硬件改造到软件编程的完整闭环。目前已有2950人学习下载。文档不仅给出了40019二次电压设定值、40020二次电流设定值、40026工作方式、40066故障报警清除等关键地址的读写范围,还详细说明背景数据块每次调用保持一致、连接ID不能相同、轮询时间按数据量调整、MB_Transaction_ID需改为实际设备地址、仿真需安装PLCSIM Advanced等注意事项,排错思路清晰,对类似多网关通信不稳项目的调试落地具有很强的参考价值。
1. 这套通信架构到底是干什么的
搞自动化这行的,基本都绕不开一类问题:新PLC要接老设备。手里这台西门子S7-1500天生就是PROFINET和工业以太网的好手,可现场那台变频器、那块电力仪表,往往只给了一个RS485口,走的是Modbus RTU协议。这时候最省事的办法就是加一个第三方Modbus TCP网关,把串口那一侧的电平信号翻译成以太网上的Modbus TCP帧,让1500用一根网线就把一堆串口仪表全管起来。
这篇文章就是把我实际调试过的一个项目完整拆开讲一遍,内容包括网关选型、IP规划、寄存器映射表设计、MB_CLIENT编程,以及联调阶段踩过的坑。文章里用到的方案没有用S7-1500的专用串口模块,而是用第三方网关桥接Modbus RTU设备,整个架构简洁、成本低,也很好扩展。适合刚接触Modbus TCP网关的PLC工程师,也适合已经在用1500但搞不定“又读又写”逻辑的朋友参考。
1.1 为什么需要第三方Modbus TCP网关
先把这个问题的根源说清楚。S7-1500的PN接口从TIA Portal V15开始就有了专门的Modbus TCP指令,叫MB_CLIENT和MB_SERVER,分别用来做Modbus TCP的主站和从站。也就是说,如果远端设备本身直接支持Modbus TCP,比如一些新款的仪表、驱动器,1500完全可以直接通过网口通信,不需要任何额外的转换器。
但现实情况往往是:现场设备是比较老的Modbus RTU设备,只有RS485物理接口。要把这种设备接进1500,摆在你面前的有两条路:
第一条路,给1500加一个通信模块,比如CM1241 RS422/485,然后用MB_MASTER、MB_SLAVE这类指令在串口上做Modbus RTU主站。程序的复杂度会高一些,模块价格也不便宜,而且每个串口模块只能带一条RS485总线。
第二条路,就是我这次用的方式:串口设备接到第三方Modbus TCP网关上,网关再接交换机,1500通过以太网用Modbus TCP和网关通信。网关自动处理RS485总线上跟多个从站的轮询,PLC这边只需要关心以太网数据交换。这种方式的好处非常明显:
- 不需要给PLC额外配模块,只要CPU是带PN口的版本基本就行;
- 多条RS485链路可以分别挂在不同的网关上,组网灵活;
- 网关自带诊断和配置界面,出了问题不一定要动PLC程序;
- 很多网关在断电重启后会自动恢复轮询,现场维护的人压力小很多。
这里还要顺手澄清一个概念:Modbus TCP网关和网络路由器的“网关”完全不是一回事。路由器网关解决的是不同网段之间的IP包转发问题,而Modbus TCP网关解决的是应用层协议转换问题,比如把Modbus RTU的报文翻译成Modbus TCP的报文。很多新手听到“网关”两个字就以为要配默认网关地址,其实在PLC项目里这两者是平行的东西。
1.2 典型应用场景与设备选型
这次项目中的现场设备有两台:一台是第三方的变频器,走RS485,做风机驱动;另一台是电力参数仪表,也是RS485接口,用来采集电压、电流、功率这些数据。两台设备的波特率都是9600,8位数据位,无校验,1位停止位,这在现场设备里非常典型。
选第三方网关的时候,我重点看这么几个参数:
- 串口数量:只有一条RS485总线,单串口网关就够用;如果现场设备分散在两条总线,就选双串口版本;
- 工作模式:必须支持“数据映射”模式,也就是网关内部维护一个Modbus寄存器缓冲区,而不是简单的报文透明转发,这样PLC读写速度会快很多;
- 串口隔离:RS485侧必须带隔离和保护电路,现场走线距离比较长,电气环境也杂,没隔离很容易烧接口;
- 配置方式:最好支持网页配置,现场带个笔记本就能改,不用专门装上位机软件。
另外提醒一下,网关的供电要稳定,我用的是24V开关电源。别小看这一点,很多通信闪断问题其实是供电纹波太大导致的。
2. 硬件接线、IP规划与网关配置
2.1 网络参数规划
先规划好整个网络的IP,这一步千万别省。虽然MB_CLIENT建立连接只需要知道网关的IP和端口号,但后续调试的时候,你的电脑、PLC、网关必须在同一个网段内,否则TIA Portal在线诊断和网关网页配置都会出问题。
我这次用的规划表如下:
| 设备 | IP地址 | 子网掩码 | 默认网关 |
|---|---|---|---|
| CPU 1513-1PN | 192.168.0.1 | 255.255.255.0 | 不填 |
| Modbus TCP网关 | 192.168.0.10 | 255.255.255.0 | 不填 |
| 调试电脑 | 192.168.0.100 | 255.255.255.0 | 不填 |
PLC和网关都放在同一个网段里,没有配置默认网关。默认网关是给跨网段通信用的,这里所有设备都在一个广播域内,不需要配置。如果你以后要把数据送到上层管理网络,再单独规划上一层路由。
接线方面,PLC、网关、电脑三台设备全部进同一台工业交换机,网线用超五类以上屏蔽线。RS485侧走线要远离变频器的动力线,我用的是屏蔽双绞线,屏蔽层单端接地,就是现场常用的做法。
2.2 第三方网关参数设置
网关到手后,先把基本参数配上。我用的是网页配置型网关,笔记本网线直连网关默认IP,然后改到规划好的192.168.0.10。配置过程大致分四步:
第一步,设置网络参数。IP地址192.168.0.10,子网掩码255.255.255.0,端口号保持默认的502。这里要留意,有些网关节省成本,出厂端口不是502,而是自定义端口,那PLC端连接参数也要跟着改。
第二步,开启Modbus TCP服务端功能。网关上电后默认会监听502端口,等PLC来连。部分网关有“注册包”功能,是给M2M平台用的,这里必须关掉,否则PLC发的正常请求会被当成注册包处理。
第三步,设置串口参数。波特率9600、数据位8、校验位无、停止位1,必须跟现场两台从站设备一致。串口参数不匹配是通信超时的头号原因,后面排查章节会专门说。
第四步,也是最重要的一步,把工作模式设成“数据映射”,建立Modbus TCP寄存器到RTU从站地址的对应关系。
数据映射模式可以这样理解:网关内部有一块寄存器缓冲区,它自己按照设定的周期去轮询RS485总线上的各个RTU从站,把读到的数据放进缓冲区。PLC通过Modbus TCP来读写这个缓冲区,读写动作直接在以太网侧完成,不占用串口时间。这就像一个前台秘书,把要送进后厨的单子先接住,再分批往里送,你去前台办事不用干等后厨炒菜。
不过要特别提醒,如果网关的数据映射模式对“写操作”不是透传直写,而是只写到缓存区、不立即下发到RTU设备,那控制类数据会有风险。比如变频器的启停控制字,最好确认网关支持“写穿透”,也就是写缓存的同时立刻转发到串口设备。
2.3 寄存器映射表设计
寄存器映射是整个通信方案的核心设计,做之前必须拿到现场设备的Modbus寄存器手册。设计的原则很简单:哪几个寄存器是变频器要用的,哪几个是电表要用的,统一映射到网关的连续寄存器空间里。
这次网关端映射表是这样定的:
| TCP侧寄存器号(0起始) | RTU从站地址 | RTU寄存器 | 含义 | 读写属性 |
|---|---|---|---|---|
| 0 | 2 | 40100 | 变频器控制字 | 读/写 |
| 1 | 2 | 40101 | 速度设定值 | 读/写 |
| 2 | 2 | 40102 | 状态字 | 只读 |
| 3 | 2 | 40103-40104 | 实际运行频率(32位) | 只读 |
| 10 | 1 | 40001 | 电表A相电压 | 只读 |
| 11 | 1 | 40002 | 电表A相电流 | 只读 |
| 12 | 1 | 40003 | 电表有功功率 | 只读 |
这里要特别留意“0起始”和“1起始”的坑。你去看Modbus手册,厂家通常写的是40001、40002这种“1起始”的地址,也就是40001代表第一路保持寄存器。但在Modbus TCP报文里,实际走的是0起始的偏移地址,也就是说40001对应偏移0,40002对应偏移1。在PLC的MB_CLIENT指令里填的REG_ADDR,用的是0起始的地址,所以读状态字40102的时候,REG_ADDR要填1,不能填40102,更不能填40001。
很多新人在这一步栽跟头,所以我建议把映射表同时列出“手册地址”和“实际偏移地址”两列,编程时对着偏移列填数。
3. S7-1500侧程序实现(含读写并存的关键做法)
3.1 MB_CLIENT指令与连接数据结构
1500侧的程序用TIA Portal自带的MB_CLIENT指令,位置在“通信”->“开放式用户通信”->“其他”->“Modbus TCP”下面。这个指令在CPU固件V2.0以上都支持,不用额外装库。
MB_CLIENT的输入输出参数比较多,常用的就这几个:
- REQ:启动请求的上升沿,每次通信请求需要用边沿触发;
- CONNECT:指向连接描述结构的指针,里面存着IP、端口、连接ID这些信息;
- MODE:功能码选择,1=读保持寄存器,6=写单个寄存器,8=写多个寄存器;
- DATA_PTR:指向PLC侧数据区的指针,读操作时是接收缓冲区,写操作时是发送缓冲区;
- DATA_LEN:数据长度,读或写的寄存器个数;
- REG_ADDR:从站寄存器偏移地址,0起始;
- DONE、BUSY、ERROR、STATUS:完成、忙、错误、错误码。
连接描述结构是重点。MB_CLIENT的CONNECT参数不能直接填IP地址,要指向一个全局DB,DB里定义一个TCON_IP_v4结构的变量。我建了一个全局数据块叫ModbusConn,里面的变量是这样定义的:
InterfaceId : HW_ANY := 64; // 网卡接口编号,默认即可 ID : CONN_OUC := 1; // 连接ID,同一CPU内不能重复 ActiveEstablished : BOOL := TRUE; // TRUE表示由PLC主动发起连接 RemoteAddress : TCON_IpV4 := DWordToIpV4(192.168.0.10); RemotePort : TCON_Port := 502; LocalPort : TCON_Port := 0; // 0表示自动分配本地端口这段结构定义好之后,全局DB的初始值就固定了,程序启动后不需要再去改它。ID这一点很容易忽略,如果同一个CPU里要用多个MB_CLIENT实例,每个实例的ID必须不同,否则连接资源冲突。
3.2 单实例状态机实现“轮询读+周期写”
“又读又写”是这个问题被问得最多的地方。很多人的直觉是:在OB1里同时放两个MB_CLIENT,一个配置成读,一个配置成写,同一个IP同一个端口,这样不就能既读又写了吗?
实际这样会出问题。MB_CLIENT实例对应的是一条Modbus TCP连接,同一个网关IP和端口下,多个实例用的连接ID如果相同,会直接报连接资源冲突。比较稳妥的做法有两种,我先讲单实例的状态机方案,这也是我这次项目里用的方案。
所谓状态机,就是让同一个MB_CLIENT实例轮换着做读请求和写请求。流程拆成这样:
- 状态0:触发一次读请求,读变频器状态字和实际频率;
- 状态1:等待读完成;
- 状态2:如果读成功,紧接着触发一次写请求,把控制字和速度设定值写下去;
- 状态3:等待写完成,然后回到状态0,循环往复。
SCL代码的骨架大致是这样:
CASE #step OF 0: #req := true; "MB_CLIENT_DB"( REQ := #req, DISCONNECT := false, CONNECT := "ModbusConn", MODE := 1, // 读保持寄存器 DATA_PTR := "VFD_Data".ReadBuf, DATA_LEN := 2, // 读2个字: 状态字+实际频率 REG_ADDR := 2); // 偏移2,对应手册的40102 #req := false; IF "MB_CLIENT_DB".DONE THEN #step := 2; ELSIF "MB_CLIENT_DB".ERROR THEN #errCode := "MB_CLIENT_DB".STATUS; #step := 10; // 错误处理并延时重试 END_IF; 2: #req := true; "MB_CLIENT_DB"( REQ := #req, DISCONNECT := false, CONNECT := "ModbusConn", MODE := 6, // 写单个寄存器 DATA_PTR := "VFD_Data".CtrlWord, DATA_LEN := 1, REG_ADDR := 0); // 偏移0,对应手册的40100 #req := false; IF "MB_CLIENT_DB".DONE THEN #step := 0; ELSIF "MB_CLIENT_DB".ERROR THEN #errCode := "MB_CLIENT_DB".STATUS; #step := 10; END_IF; 10: // 延时5秒后重新开始 #retryCnt += 1; IF #retryCnt >= 5000 THEN #retryCnt := 0; #step := 0; END_IF; END_CASE;这里REQ变量我是用一个静态BOOL,在调用指令的同一个周期先赋TRUE再调用,然后在下一个分支里马上置FALSE,这样能保证REQ是一个边沿触发,而不是一直保持高电平。MB_CLIENT如果REQ一直为TRUE,它只会在第一次上升沿触发一次任务,之后一直保持BUSY状态,很多人看到“MB_CLIENT一直BUSY”就是这个原因。
这个状态机的优点很明显:只用了一个TCP连接,逻辑清晰,PLC程序占用资源少。缺点也明显,读和写是串行的,如果串口设备响应慢,写控制字的实际频率可能会受到读请求周期影响。但对于大多数应用,200毫秒级别的循环足够用。
3.3 双实例方案:读、写各用一个MB_CLIENT
如果你对实时性要求比较高,或者觉得状态机维护麻烦,也可以用双实例方案:定义两个MB_CLIENT,一个专门读,一个专门写,连接ID分别设为1和2。
读实例的配置:
"MB_CLIENT_READ_DB"( REQ := #readReq, // 用定时器周期触发 DISCONNECT := false, CONNECT := "ModbusConn_Read", // ID := 1 MODE := 1, DATA_PTR := "VFD_Data".ReadBuf, DATA_LEN := 2, REG_ADDR := 2);写实例的配置:
"MB_CLIENT_WRITE_DB"( REQ := #writeReq, // 只在需要修改设定值时触发 DISCONNECT := false, CONNECT := "ModbusConn_Write",// ID := 2 MODE := 6, DATA_PTR := "VFD_Data".CtrlWord, DATA_LEN := 1, REG_ADDR := 0);双实例方案的好处是读写互不干扰,读请求可以固定500毫秒周期轮询,写请求只在操作员画面改参数时触发。缺点是多占一条TCP连接,不过1513这种CPU的连接资源完全够用。
两种方案我都在现场用过头,个人倾向是:控制字必须快速响应的场合用双实例,普通数据采集用状态机就够。你完全可以把这套逻辑封装成一个带输入输出的FB,下次做类似项目直接复用。
3.4 报文格式与参数对应关系
虽然用了MB_CLIENT可以不用手拼报文,但理解了Modbus TCP的报文结构,排障时会快很多。Modbus TCP帧分两部分:前面的MBAP头7个字节,加上后面的PDU数据。
MBAP头结构:
- 事务处理ID,2字节,每次请求递增;
- 协议ID,2字节,固定0x0000;
- 长度字段,2字节,表示后面单元ID加PDU的字节数;
- 单元ID,1字节,在串口侧表示从站地址。
读取两个保持寄存器的完整请求报文是这样的:
00 01 00 00 00 06 FF 03 00 01 00 02拆开看:00 01是事务ID,00 00是协议ID,00 06表示后面还有6个字节,FF是单元ID,03是功能码,00 01是要读的起始寄存器偏移,00 02是数量。
写单个寄存器的报文:
00 02 00 00 00 06 FF 06 00 00 00 32功能码06表示写单个保持寄存器,00 00是寄存器偏移,00 32是要写入的值。
写多个寄存器的报文:
00 03 00 00 00 09 FF 10 00 00 00 02 04 00 32 00 64功能码10(十六进制)表示写多个寄存器,00 02表示写两个,04是数据字节数,后面四个字节是两个寄存器的值。
理解了报文结构以后,很多问题就有了解释。比如MB_CLIENT的REG_ADDR填1,报文中对应地址字段就是00 01;DATA_LEN填2,报文中数量字段就是00 02。如果抓包看到的报文和你预期的不一致,那就要回头检查参数了。
还有一个坑是单元ID。按Siemens文档,MB_CLIENT指令发出的报文里,单元ID通常是固定值,而且没有专门的输入参数让你改。如果第三方网关要求单元ID必须等于某个值,或者网关根据单元ID选择RTU从站地址,那MB_CLIENT默认的单元ID就可能导致请求到了网关就被丢弃。解决办法有两个:一是看网关文档能否配置成“忽略单元ID,统一按映射表转发”;二是不用MB_CLIENT,直接用TCON+TSEND+TRCV自己拼Modbus TCP帧,这样每个字节都能控制。这也是为什么有些老工程师宁愿手写TCP通信的原因。
4. 联调实录:常见故障与排查技巧
4.1 从零开始的调试流程
联调的时候千万别一上来就写大程序,要一层一层往上加。我每次做Modbus TCP网关项目,都是按这个顺序来:
第一步,把PLC、网关、电脑的IP都配好,确保电脑能ping通网关,也能ping通PLC。ping不通就先把网络物理层的问题解决掉,后面什么都白搭。
第二步,PLC程序先只写一个最简单的读请求,读网关映射表里第一个寄存器,先把通信链路打通。不要一上来就写状态机,否则一旦通信出错,你分不清是报文问题还是逻辑问题。
第三步,确认读没问题后,再加写请求。写的时候先用固定值测试,比如把变频器的控制字写成0x0001(运行命令),看设备有没有动作。
第四步,全部正常后,再套上状态机或者定时器,把实时的数据采集和控制逻辑加进去。
在现场能用Modbus调试工具的话最好,比如Modbus Poll这类软件装电脑上,直接以Modbus TCP主站身份去读网关,如果工具能读到,说明网关到设备这一段没问题,问题就锁定在PLC侧;如果工具也读不到,说明网关配置或串口接线有问题。
4.2 通信超时的常见原因与查法
这次项目中我遇到的一个典型问题是:PLC能ping通网关,但MB_CLIENT状态字报16#80C8,也就是请求超时。正常情况下,TCP连接建立成功后,网关收到Modbus请求会在几十毫秒内返回响应,超时说明请求根本没有得到有效响应。
排查顺序是这样的:
先查网关自己的诊断页面。多数网关都会记录最近一次串口通信错误,比如CRC错误、从站无响应、地址错误。我当时打开页面一看,RS485总线上一片CRC错误,这就很明确地指向串口参数或接线问题,而不是PLC程序问题。
再查从站设备地址。网关映射表里我把变频器设成从站地址2,但变频器面板里实际的站地址还是默认的1。两边对不上,网关当然读不到数据。
还遇到过一种隐蔽的情况:网关工作在透明转发模式,PLC每次读请求都要等网关把串口请求发出去、再从设备拿回响应,整个过程可能要几百毫秒。如果PLC侧设置的超时时间太短,或者REQ触发频率太高,就会频繁出现超时和BUSY。所以我才特别强调要用数据映射模式,让网关自己维护缓存。
4.3 数据跳变与字节顺序问题
通信通了以后,第二个容易踩的坑是数据对不上。比如我读变频器的实际运行频率,手册上写的是32位浮点数,占用两个寄存器。MB_CLIENT的DATA_LEN如果只填了1,读回来的就只是高16位或低16位,数据看起来就像在乱跳。
处理32位数据,正确做法是把DATA_LEN设成2,然后在PLC侧定义一个由两个WORD组成的数组或者一个DWORD变量去接收。这里要注意Modbus TCP的大端格式:寄存器字节序是高字节在前。同时,不同厂家的设备对32位数据的字顺序定义还不一样,有的设备把高字放前一个寄存器,有的把低字放前一个寄存器。
我在现场写了一个通用的转换逻辑:先用一个包含两个WORD的数组接收,然后根据设备品牌用SWAP或者MOV指令调整顺序,再传成DWORD或REAL。做一次这个工作后,我把这个逻辑封装成了独立的FC,以后接不同设备只改一个选择参数就行。
4.4 常见问题速查表
调试遇到问题的时候,逐条对照下面这个表能节省不少时间:
| 现象 | 可能原因 | 检查与解决 |
|---|---|---|
| PLC ping不通网关 | IP、子网掩码不一致;网线或交换机端口问题 | 核对IP规划,换网线,看网关网口指示灯 |
| MB_CLIENT一直BUSY | REQ一直为TRUE;或上次请求未正常结束 | 改成边沿触发,必要时切断CONNECT重新建立 |
| STATUS=16#80C8 | 连接建立但请求超时 | 检查网关端口、单元ID、REG_ADDR、串口参数 |
| 能读不能写 | 网关映射表设置了只读;功能码不支持 | 查看网关映射表读写权限,改用功能码08或10 |
| 读到的数据全是FFFF | 寄存器地址偏移错误;RTU从站无响应 | 用Modbus工具对比,修正偏移量 |
| 数据跳变或数量级不对 | 32位数据只读了一个寄存器;字节序不对 | DATA_LEN设2,用WORD数组接收并调整顺序 |
| 状态字读到但控制字写不进去 | 变频器未处于远程控制模式 | 检查变频器面板,切到端子或网络控制模式 |
5. 关于这套方案的一些个人体会
做了几次类似的Modbus TCP网关集成项目之后,我最大的体会是:通信方案的技术难度其实不高,真正决定项目顺不顺的,是那几张表——IP规划表、网关映射表、寄存器地址表。这三张表必须在动工前就白纸黑字定下来,并且让现场所有电气同事都拿到最新版。我见过太多项目因为现场改了一台设备的站地址,但映射表文档没更新,最后查了一整天故障,结果就是一张纸的事。
再分享一个小经验:网关的配置文件一定要定期导出备份。我遇到过现场网关被人误复位,所有映射表恢复出厂设置的情况,手边又没有配置文件,只能对着设备手册重新配一遍,非常耽误时间。现在我的习惯是每次调试顺利结束后,马上导出配置文件,和PLC项目一起存到版本库里。
这次这个方案后续要扩展也很方便。如果现场还要加一台Modbus RTU设备,只要从网关串口上并进去,改一下映射表,PLC侧大概率不用动程序。如果你还想让第三方上位机直接读1500的数据,也可以把1500自己做成Modbus TCP服务器,用MB_SERVER指令把指定DB区暴露出去,那就是另外一个话题了。
本文还有配套的精品资源,点击获取