
简介本资源是一套完整可用的西门子S7-300 PLC实现MODBUS TCP通信的工程源代码面向工业自动化领域的新手工程师及具备PLC基础的开发人员解决现场设备与上位机如SCADA、HMI或PC软件基于标准以太网协议进行数据交互的实际需求。压缩包共699个文件总计2.17MB涵盖DB块218个、符号表与项目结构文件73个PG、5个S7H、2个S7P等、数据库索引与临时锁文件MDX/LCK/ID类以及配置参数INI、日志与类型定义TYPES、LOGIDS等典型STEP 7工程组成要素结构完整、可直接导入SIMATIC Manager编译调试。内容预览显示包含多组VerbHomo.dat等通信映射配置文件表明已预置常用寄存器地址映射逻辑与协议解析规则。已有1023人学习下载提供即用型通信框架、清晰的DB数据结构设计及典型读写功能块调用示例大幅降低MODBUS TCP在S7-300平台上的开发门槛与调试周期。 在现场做设备集成的工程师十有八九会撞上这样的场景一台S7-300在现场稳定运行了十几年设备本体皮实得很但旁边的ABB机器人、视觉系统或上位机只认Modbus TCP接口文档上列了一串保持寄存器地址要求你这边按时把数据送过去。你打开STEP 7翻遍通讯指令库原生库里根本找不到Modbus TCP的影子这时候才反应过来这事没法靠拖两个功能块解决。这篇文章把我这几年在S7-300上实现Modbus TCP通讯的完整经验整理出来包括这个“协议错位”到底是怎么造成的、怎么根据现场情况选方案、手拼报文的核心代码思路、调试工具怎么配合使用以及和ABB机器人、C#上位机、组态软件对接时踩过的坑。不管你是第一次接触Modbus TCP的PLC程序员还是已经在现场调过几次设备的老工程师这篇内容都能给你一些能直接套用的操作方式。1. S7-300和Modbus TCP的“错位”是怎么造成的1.1 S7-300的通讯家底里为什么没有原生Modbus TCPS7-300大规模应用的时候西门子的通讯主战场是PROFIBUS、MPI和后来主推的PROFINET。这套生态里S7协议是自家设备之间通讯的“母语”所以CPU的通讯指令集里围绕S7连接、PROFINET IO、PROFIBUS DP做了大量优化。而Modbus TCP是施耐德当年在工业以太网基础上推的开放协议后来成为很多第三方设备的标配接口但对西门子来说它始终只是“外来户”。S7-1200和S7-1500这两个新平台原生库里有MB_CLIENT和MB_SERVER这样的Modbus TCP指令块填参数就能用。但S7-300不行无论是老款CPU还是带PN口的新款STEP 7的指令树里都找不到现成的Modbus TCP功能块。300平台只提供了通用的TCP通讯能力相当于给了你一个可以自由收发字节数据的管道但管道里跑的内容需要自己按照Modbus TCP规范去组帧、去解析。这一点很多人一开始没意识到。拿着机器人厂商给的Modbus地址点表以为在300里拖一个指令块填上IP和寄存器地址就能跑通结果在硬件组态和库文件管理里翻半天连个指令块的影子都找不到。1.2 当前可落地的三条路线自拼报文、函数库、协议网关既然原生没有工程上通常有三条路可以走。第一条自拼报文。利用S7-300带PN口CPU的TCP通讯功能块例如TCON、TSEND、TRECV这些自己按照Modbus TCP的报文格式组请求、解析响应。这个方案自由度最高不依赖任何第三方授权项目里就一个Modbus TCP设备时最合适。缺点是编程工作量明显偏大而且通讯状态机、异常重连、字节序转换这些细节都要自己处理。第二条用第三方库。市场上能找到针对S7-300的Modbus TCP库有些专门支持CP343-1有些支持带PN口的CPU。库里面把连接管理、报文封装、超时重传都封装好了你只需要填IP地址、寄存器地址、数据长度这些参数。这个方案能省不少事但要先确认库和你的CPU固件版本兼容还要注意授权问题。第三条加协议转换网关。用一个Modbus TCP转PROFINET或DP的网关把机器人、仪表这些Modbus TCP设备映射成300可以直接访问的IO区或DB数据。这方案对PLC程序几乎零侵入调试也快但多了硬件成本和额外故障点网关本身烧了或者配置丢了整个通讯就瘫了。1.3 选型思路先确定主站还是从站再谈其他选型时最重要的判断依据不是哪个方案“显得高级”而是现场通信结构里300到底扮演什么角色。如果300做主站也就是由300主动去读那些仪表、驱动器的数据那么自拼报文和第三方库都很合适。因为主动权在300手里什么时候读、读多长都由PLC的扫描逻辑控制状态机好写链路也好维护。如果300做从站也就是等待机器人或上位机来读它情况就复杂一些。从站需要持续监听502端口响应不同功能码的请求没有现成库的话自己写会非常吃力。这种情况下我通常优先推荐第三方从站库或者干脆用网关把300的数据映射出去省得自己在报文解析和异常响应上耗费大量时间。项目里只有一台Modbus TCP设备、寄存器几十个的时候不要犹豫自拼报文是最快、最可控的方案。但如果设备数量超过五台、寄存器几百个建议认真考虑网关或者上带原生Modbus TCP库的新平台。2. 动手前必须配好的硬件、软件和网络参数2.1 先查清楚CPU带不带PN口决定用哪组功能块很多人在第一步就走错了方向没查硬件型号就直接翻库找功能块。S7-300的CPU大致分两类一类是带集成PN口的型号比如CPU 315-2 PN/DP、CPU 317-2 PN/DP可以直接使用TCON、TSEND、TRECV这类TCP通讯功能块另一类是老CPU没有以太网口必须搭配CP343-1通讯处理器这时候要使用CP卡对应的AG_SEND、AG_RECV功能块或者CP卡附带的库函数。这两套功能块的调用方式、连接配置方式都不一样。写代码之前先在硬件组态里看清楚CPU和CP卡的型号再决定用哪套指令。如果拿旧CP卡的例程往PN口CPU上套编译阶段就会报错白白浪费时间。2.2 IP地址与子网掩码的设置步骤IP地址设置是Modbus TCP联调绕不开的第一步也是现场最常出错的点。步骤其实很简单但很多人会漏掉子网掩码和连接创建这两项。在STEP 7的HW Config或TIA Portal的设备组态里双击CPU或CP模块打开“属性”对话框切到“以太网地址”页签填上IP地址和子网掩码比如192.168.0.10、255.255.255.0。填完之后一定要在“子网”区域新建或选择一个工业以太网子网然后编译、保存、下载。这里有个很容易忽略的细节PLC的IP地址和机器人、上位机必须在同一个网段而且不能冲突。比如机器人是192.168.0.20PLC就不能设成192.168.0.20否则交换机会出现地址冲突两个设备都会间歇性断网。现场如果出现“有时候能通讯、过一会就断了”的怪现象先查IP冲突。2.3 连接资源、端口502和防火墙的注意事项Modbus TCP默认使用502端口但S7-300的TCP连接资源不是无限的。老款CPU和CP343-1能同时建立的TCP连接数量有限制具体多少要看模块手册。如果现场同时需要与机器人、上位机、触摸屏通讯连接资源很容易被占满表现为“PLC能ping通对方但TCP连接始终建立不起来”。Windows系统的防火墙也经常在背后捣乱。用Modbus Poll、Modbus Slave这类调试工具时首次启动会弹出防火墙授权窗口很多人随手点了取消结果工具发出的报文根本出不了本机。调试时建议临时放行502端口或者干脆把防火墙暂时关闭等联调结束后再恢复安全策略。2.4 调试工具清单我自己的调试环境里必备四样东西STEP 7编程软件V5.5或TIA Portal取决于300的组态方式Modbus Poll模拟Modbus TCP主站主动去读PLC或其他从站Modbus Slave模拟Modbus TCP从站用来验证PLC发出的请求是否正确Wireshark抓包分析看到底是哪一层出了问题这里额外说一句关于Modbus Poll和Modbus Slave软件授权的现状。这两个工具是老牌收费软件网上很多流传的“密钥”来源不明我在自己电脑上就中过一次招装完发现多了个挖矿进程。建议直接用官方试用版或者花点钱买正式授权在这种天天都要用的工具上省钱的成本实在太高。3. 核心代码实现在S7-300里手拼Modbus TCP请求这一节是整个项目的核心也是很多人问“程序源代码”时真正想要的东西。S7-300的程序是由STL、LAD、FBD或SCL写的梯形图和功能块不是普通编程语言里的文本源码。要让你在300里跑通Modbus TCP本质上是把Modbus协议帧按字节构造出来通过TCP功能块发出去再把响应收回来解析成工程数据。3.1 先把报文解剖清楚MBAP头PDU为什么没有CRCModbus TCP报文由两部分组成MBAP头7字节加上PDU数据单元。MBAP头里的内容是事务标识符2字节、协议标识符2字节固定为0x0000、后续长度2字节、单元标识符1字节。PDU里则是功能码加数据。举个例子读保持寄存器功能码03请求读起始地址0开始连续10个保持寄存器完整的请求报文是00 01 00 00 00 06 01 03 00 00 00 0A逐字节拆开看00 01事务标识符00 00协议标识符固定为000 06长度字段表示后面还有6个字节01 03 00 00 00 0A01单元标识符一般填103功能码00 00起始寄存器地址00 0A寄存器数量很多从RTU转过来的工程师在这个环节容易犯一个经典错误习惯性地在报文后面补两个CRC字节。Modbus TCP不需要CRC因为TCP/IP协议栈本身已经做了数据校验和重传再加CRC反而是多余的对方解析时会因为长度不匹配直接报错。这是Modbus RTU和Modbus TCP最直观的区别之一。3.2 用TCON建立TCP连接时需要注意的三个参数S7-300这类老平台建立TCP连接用TCON功能块它需要一个连接描述数据块里面包含连接ID、主动/被动模式、远端IP地址、远端端口号等参数。三个最关键的坑第一连接ID在整个CPU程序里要唯一不能和其他通讯功能重复否则运行时会报连接冲突。第二目标端口必须填502除非对方设备改了端口。第三主动连接标志位ACTIVE必须设对300作为客户端去连别人的时候要设成主动模式如果要在300侧做从站监听反而要设成被动模式。TCON建立连接需要时间不是调用一次马上就好需要等DONE位或ERROR位亮起来。很多人第一次写的时候以为TCON调用完就可以直接TSEND了结果发送指令一直报错就是因为连接还没建立成功。3.3 功能码03读保持寄存器的组装与解析在S7-300里推荐的做法是定义一个专用的DB块作为收发缓冲区比如DB100。DB100.DBB0开始存放要发送的报文发送完成后接收到的响应放在DB100.DBB20开始的区域。用STL或者SCL按字节给DB100赋值// 发送缓冲区 DB100.DBB0 : 16#00; // 事务ID高字节 DB100.DBB1 : 16#01; // 事务ID低字节 DB100.DBB2 : 16#00; // 协议ID高字节 DB100.DBB3 : 16#00; // 协议ID低字节 DB100.DBB4 : 16#00; // 长度高字节 DB100.DBB5 : 16#06; // 长度低字节 DB100.DBB6 : 16#01; // 单元ID DB100.DBB7 : 16#03; // 功能码 DB100.DBB8 : 16#00; // 起始地址高字节 DB100.DBB9 : 16#00; // 起始地址低字节 DB100.DBB10 : 16#00; // 寄存器数量高字节 DB100.DBB11 : 16#0A; // 寄存器数量低字节这里有个值得注意的地方事务ID是用来匹配请求和响应的。从站返回报文时会把请求里的事务ID原样带回来PLC解析时要检查响应的事务ID和请求是否一致如果不一致说明这是一条过期的响应或者别人发来的报文必须丢弃。发送12个字节后等待TRECV接收。接收到的响应可能是00 01 00 00 00 17 01 03 14 00 64 00 65 ...长度字段0x17等于23对应1字节单元ID、1字节功能码、1字节字节数、20字节数据。字节数0x14表示后面有20字节正好是10个寄存器的数据。3.4 功能码06和16写寄存器的组装差异写单个保持寄存器用功能码06。请求报文是00 01 00 00 00 06 01 06 00 00 00 64这表示往地址0的保持寄存器写入值0x0064也就是十进制100。报文长度和读请求一样也是12字节差别只在功能码和最后两字节的数据。写多个寄存器用功能码16十六进制是0x10。比如往地址0和1写两个寄存器值是100和20000 01 00 00 00 0B 01 10 00 00 00 02 04 00 64 00 C8注意这里长度字段变成了0x0B也就是11字节单元ID1字节、功能码1字节、起始地址2字节、数量2字节、字节计数1字节、数据4字节。字节计数0x04表示后面有4个数据字节。这个长度字段是初学者最容易算错的地方建议每次组装报文时都拿笔在纸上把字节数加一遍确认无误再填进去。3.5 通讯状态机把收发流程塞进循环扫描的正确姿势PLC是循环扫描执行程序的和PC上的阻塞式Socket编程完全不同。你没法在FB里写一个循环等着对方返回数据这样做不仅会拖慢扫描周期严重的还会触发看门狗超时。正确做法是做一个状态机用OB1的每次扫描来驱动通讯步骤。我常用的状态流程是状态0空闲等待触发信号。触发后复位所有标志调用TCON开始建立连接。状态1等待TCON的DONE位置位或ERROR位报错。DONE置位说明连接建立成功进入发送状态。状态2调用TSEND发送请求报文等待DONE位置位。发送完成后切换到接收状态。状态3调用TRECV接收数据等待NDR位新数据到达置位。收到数据后进入解析状态。状态4解析响应检查事务ID、功能码把数据搬运到全局DB里的实际工程变量比如某个电机的转速、温度值。状态5判断是否需要继续下一次请求。如果需要跳回状态2继续发送如果一段时间内没有新的请求调用TDISCON断开连接回到状态0。整个状态机必须加超时保护。每个状态停留时间超过比如2秒就要复位并重新连接。否则一旦TCP连接处于半开状态通讯会一直卡死在没有响应的等待中只有断电重启才能恢复。4. 不想拼报文怎么办现成库与网关的落地经验4.1 库函数的使用轮廓与版本坑如果项目周期紧、寄存器点位多找成熟的库函数是合理的。第三方Modbus TCP库在S7-300上的使用方式通常是在OB100里初始化然后每个扫描周期调用一次FB输入参数包括连接参数、功能码、起始地址、数据长度输出参数包括通讯状态和数据区。库函数省事但坑也不少。我吃过最大的亏是版本兼容性同一个库在CPU固件版本旧的机器上运行一切正常换了一台固件版本新的CPU通讯周期从100ms变成每隔几秒就超时一次查了很久才发现是库和固件不匹配。使用第三方库之前先看文档里写的支持列表再拿现场CPU的固件版本对照一遍最好在实验室里先跑24小时。4.2 协议转换网关为什么反而更省心有些场景下网关是最省心的方案。我之前做过一个项目现场有三台机器人和一台老款S7-300机器人只提供Modbus TCP从站接口300那边又不想改太多程序。最后每个机器人前面加了一个Modbus TCP转PROFINET网关机器人的寄存器地址在网关组态软件里映射成300的IO区PLC侧只需要做普通的I/O读写和直接接一个PN从站没有任何区别。网关方案最大的优势是维护门槛低。客户后续换维护工程师只需要懂网关的组态软件不需要懂Modbus TCP报文格式。缺点是实时性受限于网关的刷新周期映射点位数特别多的时候寄存器扫描周期会变长。对快速响应要求高、点位又多的项目建议先把刷新周期测试压到位。4.3 三条方案怎么选我自己在项目里经常用一个对比表来做决策维度自拼报文第三方库协议网关硬件成本最低较低额外增加网关成本开发周期3~5天1~2天半天编程难度高需懂协议和状态机中配置参数即可低组态映射为主可维护性中调试需懂协议偏高依赖库文档高组态软件图形化实时性高完全控制中高取决于库实现中受限于网关刷新周期适用场景单设备、少量寄存器多设备、大量寄存器设备多、维护团队不熟协议这个表不是绝对的但可以帮你在项目启动前快速框定方向。不要一上来就问“哪个最好”先看自己手里有多少时间、多少预算、多少寄存器答案自然就出来了。4.4 什么时候坚持自己写如果项目只接一个Modbus TCP设备寄存器不超过50个对方给的协议文档也写得很清楚那自己拼报文是很好的选择。自己写的好处是出问题的时候你对每一帧报文、每一步状态都了如指掌排查起来反而比黑盒的库函数更快。但如果这个项目是给客户交付的长期运维项目我一般会强烈建议走库或网关路线。交付的代码里留一堆自己拼报文的逻辑后面维护的人不一定愿意啃这个源码。用成熟的库或网关至少别人查资料容易不至于整个产线交付后变成只有你能维护的“祖传代码”。5. 联调实测用工具把问题逼出来的完整过程5.1 先用Modbus Slave模拟从站验证自己的请求报文PLC侧的程序写完先不要急着接设备先在电脑上打开Modbus Slave让它监听502端口假设一个从站ID和寄存器初值。然后把300作为客户端连接这台电脑看PLC能不能把Modbus Slave里面预设的数值读上来。这一步能验证的事情非常多IP通不通、端口通不通、报文有没有组错、数据能不能解析出来。我通常的做法是给Modbus Slave里的寄存器填一个特征值比如地址0填1234地址1填5678这样读上来的数据只要一看就知道高低字节是不是反了。5.2 再用Modbus Poll模拟主站反向验证从站逻辑如果300做的是从站那就反过来用Modbus Poll作为主站去读300。Modbus Poll配置连接时填PLC的IP地址和502端口能读到数据说明从站程序正常读不到就按报文逻辑一步步查。这里有个小细节Modbus Poll地址栏里地址1到16对应Modbus协议的1-based地址但协议内部传输时寄存器地址是从0开始编号的。比如你看到4X地址40001协议帧里对应的起始地址是0x0000。PLC侧组报文时要用协议地址不要直接把40001塞进去否则会差一个地址。5.3 实测中最常见的5个坑做过的Modbus TCP联调项目多了你会发现大部分问题都集中在下面几个地方。IP和网段问题最常见的是PLC和对方设备不在同一个网段或者IP冲突。先pingping不通就别谈报文。防火墙拦502端口PC上的Modbus Poll发不出去先检查Windows防火墙放行规则。事务ID不匹配有的设备实现不规范响应时不带请求里的事务ID或者直接回0PLC这边不加判断就会把无效数据当天值用。字节序问题大端小端在单个寄存器时没问题但碰到32位浮点数或双字经常出现高字低字颠倒。ABB机器人里的REAL变量读到PLC里至少要验证一次高低字是否需要交换。连接不释放代码里没有断开机制或异常重连机制连接数被耗光通讯越来越慢最后彻底断线。5.4 Wireshark抓包在TCP层看通讯是否真的成功如果Modbus Poll和Modbus Slave都验证过双方单独测都没问题但连到一起就不行这时候必须上Wireshark抓包用事实说话。Wireshark抓包时过滤表达式直接填modbus.tcp就能看到Modbus TCP层的所有请求和响应。如果看不到可以先抓TCP握手报文确认TCP连接是否建立成功。最常见的画面是PLC不停地发TCP SYN但对方始终不回应SYN-ACK这说明对方设备根本没开502端口或者防火墙把端口挡掉了。如果能看到TCP连接建立也看到请求发出去了但对方不回Modbus响应重点检查功能码、寄存器地址、长度字段是否正确。5.5 一次真实排障的完整链路分享一次让我印象深刻的排障过程。项目里300做主站读取一台Modbus TCP温控表。现象是刚上电时通讯正常运行几十分钟后通讯中断重启PLC又恢复。我用Modbus Poll先把温控表单独接电脑连续跑了一下午数据稳定说明表和链路没问题。然后对PLC侧抓包发现每次通讯中断前TCP连接都会收到一个FIN标志紧接着下一次通讯又重新发SYN建连。回到程序里查发现我当时为了方便在每个通讯循环结束后都调用TDISCON主动断开连接再在下一次循环时重新连接。PLC扫描周期快TCP连接频繁建立、断开对端温控表的TCP栈处理能力跟不上最终拒绝服务。修改方案很简单保持长连接只在连接异常或者需要切换目标设备时才断开重建。改动后连续运行72小时通讯再没断过。这个案例说明很多通讯问题并不是协议本身而是通讯时序设计不合理导致的排查时要多看TCP层的连接状态变化别只盯着Modbus那一层。6. 从300延伸到机器人、上位机和组态软件6.1 与ABB机器人对接时的主从角色选择热搜词里同时出现“ABB机器人与西门子300”和“Modbus TCP”说明这是很多人正在做的项目场景。ABB机器人侧做Modbus TCP通讯通常在示教器里配置客户端或服务器参数支持的寄存器数量和功能码都比较标准。机器人更常见的做法是作为Modbus TCP主站主动读PLC的数据这就要求300侧有稳定的从站实现。如果没有现成从站库也可以反过来设计300作为主本文还有配套的精品资源点击获取