
简介本资源为西门子S7-300 PLC实现MODBUS TCP通信的完整工程源代码包面向工业自动化领域的新手工程师及具备PLC基础的开发人员解决现场设备与上位机如SCADA、HMI或PC软件通过标准以太网协议进行数据交互的实际需求。压缩包共699个文件总计2.17MB涵盖218个DBF数据库表存储寄存器映射配置、141个MDX索引文件保障数据快速读写、73个PG程序块含FB/FC功能块与OB组织块、65个DBT文本备注及大量INI、RES、DAT等配置与资源文件结构完整、模块清晰便于理解通信协议栈构建逻辑与地址映射关系。内容预览显示包含S7hCompressedMetadata.cmf、VerbHomo.dat等典型西门子工程元数据与变量定义文件印证其为可直接加载至STEP 7环境运行的真实项目。目前已有1023人学习下载提供即用型通信框架、寄存器映射范例及典型错误处理逻辑显著降低MODBUS TCP在S7-300平台上的开发门槛与调试周期。 搞西门子300做MODBUS TCP通讯在工控圈里属于那种看起来简单、上手全是坑的活。我最早被安排做这个项目时以为就是调个库函数的事结果从接线确认、IP规划到报文抓包整整磨了三天。这篇文章我就把从零到通的完整过程写出来包括源代码框架、轮询调度逻辑、ABB机器人做客户端的对接案例以及我用Modbus Poll和Wireshark排查问题的实战记录。不管你是刚接触PLC通讯的新手还是被现场通讯故障折磨的老手这篇文章都能给你一些直接能用的东西。1. MODBUS TCP通讯的核心思路与方案选型1.1 为什么是MODBUS TCP而不是PROFINET或RTU先聊一个大家都会纠结的问题S7-300本身的母语是PROFINET和PROFIBUS为什么还要折腾MODBUS TCP答案很现实生态兼容性。MODBUS TCP是工业通讯领域事实上的普通话几乎所有上位机、第三方设备、仪器仪表、视觉系统、机器人控制器都能说这门语言。ABB机器人、库卡机器人、各类传感器、变频器、流量计不管什么品牌基本都支持MODBUS TCP。而PROFINET虽然性能更强但属于西门子生态的方言外部设备支持与否得看运气。还有一个关键点是通讯成本。PROFINET通讯需要额外的GSD文件、硬件组态和许可证授权MODBUS TCP只需要设置IP地址、端口号直接在程序里调用功能块连硬件都不用改。S7-300集成了PN口或配一块CP343-1就能当MODBUS TCP服务器或客户端使用。但也别一杆子打死。如果现场全部是西门子设备比如S7-300配S7-1200、S7-1500做分布式IO那用PROFINET的效率更高、响应更快。MODBUS TCP的优势在于跨品牌、跨系统的互操作劣势在于每次读写都要经过代码层的请求响应机制吞吐率不如PROFINET的IRT实时通讯。1.2 S7-300做MODBUS TCP的三种实现方案对比在动手之前先梳理一下S7-300做MODBUS TCP有哪几条路可以走。这个选择直接影响后面编程方式。第一种是使用西门子官方库里的功能块FB63MODBUS_CLIENT和FB64MODBUS_SERVER。这是从Step7 V5.5 SP2和TIA Portal V11开始提供的官方解决方案S7-300从固件V3.0开始支持。优点是不需要额外硬件模块直接用CPU集成的PN口或者CP343-1的以太网口就能通讯代码量小可维护性高。第二种是使用第三方协议转换模块比如有人机界面品牌出的协议透传模块或者专业的MODBUS网关。这种方式适合特别老的S7-300 CPU不支持固件升级、或者不想改PLC程序的场景。缺点是多一个硬件就多一个故障点成本也高。第三种是自己在STL/SCL里手写MODBUS TCP协议栈通过TCON、TSEND、TRCV系统功能块来发送和解析MODBUS报文。这种方式灵活度最高可以自定义很多非标准的读写逻辑但代码量大调试周期长不推荐绝大多数项目使用。我做方案选型时直接选了第一种官方库方案。对比情况我整理成了表格方案成本代码量灵活性维护难度适用场景官方库FB63/FB64零成本低中低绝大多数标准通讯场景第三方协议转换模块中高低低中老旧CPU或特殊硬件手写协议栈零成本高高高特殊报文格式、加密通讯2. 硬件组态与网络参数设置2.1 CPU固件版本与库文件版本匹配这里有一个非常容易踩的坑FB63/FB64库文件版本必须和CPU固件版本匹配否则程序下载后根本跑不起来通讯功能块会直接报错。S7-300使用MODBUS TCP库功能需要满足几个条件CPU型号必须是S7-300系列且支持PN通讯如CPU 315-2 PN/DP、CPU 317-2 PN/DP等固件版本建议V3.0以上CP343-1模块型号需要是V2.0以上版本。如果CPU固件太老我建议直接联系供应商做固件升级别自己刷有变砖的风险。TIA Portal环境下调用库函数时需要注意版本选择。我实测发现不同版本的库函数对CONNECT参数的数据结构定义有细微差别特别是UDT的定义结构。如果你用的是TIA V15或更高版本建议直接从全局库中拖出最新版的功能块不要自己去复制旧项目的FB。还有一点每次新建项目时重新从库中拖一次功能块比从旧项目拷贝更稳妥。2.2 PLC的IP地址设置与网络规划S7-300的IP地址设置是在硬件组态里完成的和S7-1200的在线分配方式不同。具体操作流程是在TIA Portal的设备和网络视图中双击CPU模块在属性-常规-以太网地址中设置IP地址和子网掩码。对于带PN口的CPU还需要勾选在RUN模式下可分配IP地址这个选项方便后续远程维护时使用Step7的在线功能重新分配IP。网络规划方面我建议遵循几个原则PLC和上位机必须处于同一网段比如PLC是192.168.1.10上位机是192.168.1.50子网掩码统一用255.255.255.0不要用变长子网掩码给自己找麻烦网关地址如果现场有路由器或工业防火墙就必须设置如果没有就保持默认0.0.0.0。注意S7-300的MODBUS TCP默认端口是502如果现场网络环境中有防火墙记得放行TCP 502端口。如果遇到502端口被占用比如现场有多个PLC或其它设备也用了这个端口可以通过绑定参数修改端口号但必须在PLC和通讯伙伴端保持一致。2.3 连接资源规划与连接ID分配S7-300的TCP连接资源是有限的大部分CPU型号支持最多8个左右的TCP连接具体数量取决于CPU型号和固件版本这意味着FB63最多可以同时建立8个独立的MODBUS TCP连接。如果通讯对象超过8个就必须采用轮询复用机制而不是并发连接。连接ID的分配也有讲究。FB63的CONNECT参数指向一个连接描述UDT其中包含了连接IDConnection ID这个ID在PLC内部必须唯一。如果你要建立多个MODBUS TCP客户端连接每个连接使用不同的ID取值范围从1开始递增。我习惯用一个结构体数组来管理多个连接每个连接的ID、目标IP、目标端口都放在一个DB里集中管理这样后面维护排查问题时会方便很多。3. 客户端程序源代码详解3.1 FB63 MODBUS_CLIENT功能块参数说明FB63是S7-300做MODBUS TCP客户端时最核心的功能块它的作用是与远程服务器建立连接发送MODBUS请求接收响应数据。理解它的参数是编程的第一步。我以实际项目中的调用代码为例来说明。下面这个例子实现了从远程设备IP为192.168.1.60读取保持寄存器地址40001开始的20个字的数据CALL MODBUS_CLIENT , MODBUS_CLIENT_DB REQ : Trigger_Read.SIGNAL DISCONNECT : false MB_MODE : 0 MB_DATA_ADDR : 40001 MB_DATA_LEN : 20 MB_DATA_PTR : P#DB100.DBX0.0 BYTE 40 CONNECT : MODBUS_CONNECT_DB DONE : Read_Done.SIGNAL BUSY : Read_Busy.SIGNAL ERROR : Read_Error.SIGNAL STATUS : Read_Status参数含义分析REQ上升沿触发通讯请求。推荐用一个周期的脉冲信号做触发避免每个扫描周期都发送请求导致从站响应不过来。DISCONNECT是否断开连接。正常通讯时保持FALSE。当需要主动断开与某个从站的连接时把它置TRUE然后发送一次请求。MB_MODE功能码选择。0代表读1代表写。注意写操作时这个参数要配合MB_DATA_ADDR和MB_DATA_LEN一起使用。MB_DATA_ADDRMODBUS地址。要理解40001对应的是PLC内部地址40001保持寄存器这个地址是MODBUS标准地址不是PLC的I/O地址。注意读保持寄存器和读输入寄存器的地址区间不同40001-49999是保持寄存器可读可写30001-39999是输入寄存器只读。MB_DATA_LEN数据长度单位是字16位。MB_DATA_PTR指向存储数据的地址区域可以是M区或DB区。这里我指向了DB100的起始地址长度40个字节20个字×2字节/字。关于地址映射我想多说一句。MODBUS的保持寄存器地址40001在功能码03读保持寄存器请求中实际发送的地址是0000协议规定地址从0开始40001对应的是数据地址0。如果你要和别的设备对接对方工程师可能会问起始地址是多少你告诉他40001但如果他理解的是协议层的地址0两边就对不上了。这个细节在联调时经常引起误会提前对齐很重要。3.2 CONNECT连接参数DB的构建方法CONNECT参数指向的连接描述DB是整个通讯能建立起来的关键很多人在这一步卡住。这个DB不是普通的数据存储DB它内部结构必须遵循特定的UDT格式。在TIA Portal中你不需要自己从零开始构建这个DB。正确做法是在全局库或项目库中找到MODBUS Communication库打开后会看到UDT类型的定义比如TCON_IP_v4这样的结构。然后在程序中新建一个全局DB创建时选择与UDT类型一致再选择对应的UDT类型就能自动生成正确的连接参数数据结构。如果是手动构建你需要包含这些字段连接ID整数、连接类型16进制2001代表TCP连接、主动/被动建立标志主动建立为TRUE、目标IP地址4字节、目标端口号2字节。以连接IP为192.168.1.60、端口502为例结构类似下面这样DATA_BLOCK MODBUS_CONNECT_DB { S7_Optimized_Access : FALSE } VERSION : 0.1 STRUCT ConnectionID : INT : 1; ConnectionType : BYTE : B#16#11; // TCP连接 ActiveEstablished : BOOL : TRUE; // 客户端主动连接 IpAddress : ARRAY [1..4] OF BYTE : [192, 168, 1, 60]; IpPort : INT : 502; END_STRUCT注意CONNECT参数在FB63的接口定义中是指针类型在SCL中直接填写DB名称即可前面不需要加P#符号。如果你在LAD/STL中调用需要用P#DB2.DBX0.0这样的指针语法。3.3 轮询多台设备的调度逻辑设计现场需求往往不止读一台设备可能是一个网关下挂几十个仪表每个仪表都要读数据。这时候如果每台设备都建立一个独立连接连接资源很快就会被耗尽。我的做法是用一个轮询调度器单个连接分时复用。轮询逻辑的思路是这样的维护一个状态机每个扫描周期只处理一个从站的请求。假设要轮询3台设备程序框架如下// 伪代码逻辑 CASE Poll_State OF 0: // 设备1读请求 MODBUS_MASTER_REQ : TRUE; Target_Device_IP : Device1_IP; MB_DATA_ADDR_TEMP : 40001; MB_DATA_LEN_TEMP : 10; IF MODBUS_DONE THEN Poll_State : 1; MODBUS_MASTER_REQ : FALSE; END_IF; 1: // 设备2读请求 // 逻辑同设备1切换目标IP和地址 IF MODBUS_DONE THEN Poll_State : 2; MODBUS_MASTER_REQ : FALSE; END_IF; 2: // 设备3读请求 IF MODBUS_DONE THEN Poll_State : 0; MODBUS_MASTER_REQ : FALSE; END_IF; END_CASE;实际项目中我不会用上面的简单CASE实现因为设备数量一多状态就爆炸了。更优雅的方式是用一个设备参数DB把每个设备的IP地址、寄存器地址、数据长度、数据存储区都做成结构化数据然后用一个整型指针循环遍历。这样新增设备只需要在DB里加一行不需要改程序逻辑。轮询周期的选择也要注意。一个连接分时复用后所有设备扫描一圈的总时间等于各台设备通讯时间的累加。每台设备的通讯时间由响应速度和请求数据长度决定一般10到50毫秒不等。如果设备数量多轮询周期会拉长这时候要考虑数据的实时性是否满足工艺要求。我曾经遇到一个项目轮询10台设备总周期接近500毫秒而工艺要求温度数据每200毫秒刷新一次最后只能拆成两个连接分别轮询5台设备才解决问题。4. 服务端与外部设备对接案例4.1 S7-300作为MODBUS服务器的配置方式有些场景下S7-300不需要主动去读别人的数据而是等待上位机或者机器人来读取它的数据。这时候S7-300就作为MODBUS TCP服务器用FB64MODBUS_SERVER功能块实现。FB64的配置相对简单核心参数包括CALL MODBUS_SERVER , MODBUS_SERVER_DB DISCONNECT : false CONNECT : SERVER_CONNECT_DB MB_HOLD_REG : P#DB200.DBX0.0 BYTE 100 MB_HOLD_REG_LEN : 50 NDR : Server_NewData DR : Server_WriteReq ERROR : Server_Error STATUS : Server_Status这里MB_HOLD_REG指向DB200它相当于MODBUS服务器暴露给外部的保持寄存器区。外部客户端读地址40001到40050时实际读取的就是DB200的前50个字。MB_HOLD_REG_LEN定义了保持寄存器的长度单位是字。有一点容易被忽略S7-300的FB64服务器功能块默认的端口绑定是502并且每个连接占用一个独立的连接ID。如果PLC同时既做客户端FB63又做服务器FB64注意连接ID不要冲突端口号也不要冲突。4.2 ABB机器人作为MODBUS TCP客户端的对接实战ABB机器人做MODBUS TCP客户端是工业现场非常常见的需求。ABB机器人控制器内置了Socket通讯功能可以在RAPID程序里通过Socket编程发送MODBUS TCP报文。因为MODBUS TCP的报文格式是公开的你可以直接在机器人侧构建报文帧不需要第三方插件。以下是一个ABB机器人读取S7-300服务器保持寄存器数据的RAPID程序框架VAR socketdev client_socket; VAR socketdev server_socket; VAR string received_data; VAR num response_len; VAR rawbytes send_data; VAR rawbytes recv_data; VAR num temp_array{100}; VAR num byte_count; PROC modbus_read_holding_registers(num start_addr, num reg_count) ! 构建MODBUS TCP帧 ! 事务ID: 2字节 ! 协议ID: 2字节 (固定00 00) ! 长度: 2字节 (后面字节数) ! 单元ID: 1字节 (通常为FF或00) ! 功能码: 1字节 (03读保持寄存器) ! 起始地址: 2字节 ! 寄存器数量: 2字节 UnpackRawBytes send_data,1,16#00; ! 事务ID高字节 UnpackRawBytes send_data,2,16#01; ! 事务ID低字节 UnpackRawBytes send_data,3,16#00; ! 协议ID高字节 UnpackRawBytes send_data,4,16#00; ! 协议ID低字节 UnpackRawBytes send_data,5,16#00; ! 长度高字节 UnpackRawBytes send_data,6,16#06; ! 长度低字节 (6个数据字节) UnpackRawBytes send_data,7,16#FF; ! 单元ID UnpackRawBytes send_data,8,16#03; ! 功能码: 读保持寄存器 UnpackRawBytes send_data,9,start_addr; ! 起始地址高字节 UnpackRawBytes send_data,10,start_addr MOD 256; ! 起始地址低字节 UnpackRawBytes send_data,11,reg_count / 256; ! 寄存器数量高字节 UnpackRawBytes send_data,12,reg_count MOD 256; ! 寄存器数量低字节 SocketSend client_socket \RawData:send_data; SocketReceive client_socket \RawData:recv_data; ! 解析响应数据 ! 响应帧: 事务ID(2)协议ID(2)长度(2)单元ID(1)功能码(1)字节数(1)数据(N) ! 数据从第9个字节开始 ENDPROC这个例子里的关键点在于报文帧的构造。MODBUS TCP帧和MODBUS RTU帧最大的区别是去掉了CRC校验加上了MBAP头7个字节这样整个报文比RTU模式简单一些但是字节序的坑依然存在。ABB机器人RAPID程序里的整数运算是16位的取高低字节时要特别注意处理方式。我这里用的是UnpackRawBytes来构建原始字节流。实际上只要确保字节在发送缓冲区里的顺序是正确的大端顺序高字节在前接收端解析时对应好位置基本就不会出错。4.3 C#上位机数据采集的补充方案除了机器人上位机软件通过MODBUS TCP读取S7-300数据也是常见的需求。很多做MES或者SCADA系统的工程师会用C#来写数据采集程序。这里给一个简短的示例使用NModbus开源库using Modbus.Device; using System.Net.Sockets; using (var client new TcpClient(192.168.1.10, 502)) { var factory new ModbusTcpTransport(client.GetStream()); var master ModbusIpMaster.CreateIp(client); // 读取保持寄存器 40001开始共10个字 ushort[] values master.ReadHoldingRegisters(0, 10); foreach (var value in values) { Console.WriteLine(value); } }注意这里的地址参数是0因为NModbus库的ReadHoldingRegisters方法接收的是协议层的起始地址40001对应的协议地址是0。很多人在这个细节上栽跟头程序连不上或者读出的数据完全不对。5. 通讯调试方法与常见问题排查5.1 用Modbus Poll和Modbus Slave进行快速验证调试MODBUS TCP通讯时有两款电脑端工具几乎是我每台电脑必装的Modbus Poll模拟主站和Modbus Slave模拟从站。它们不是官方免费软件但网上有试用版可以下载功能足够日常调试使用。Modbus Poll的用法是在Connection菜单中选TCP/IP填入PLC的IP地址和端口号502然后设置要读取的寄存器地址范围。如果PLC作为服务器运行正常连接后就能在表格里看到实时刷新的数据。如果连接失败软件会提示超时或连接拒绝这时从错误信息能快速判断是网络层问题还是MODBUS协议层问题。Modbus Slave正好相反它模拟一个MODBUS服务器。我调试S7-300客户端程序时先在电脑上运行Modbus Slave然后在PLC程序里触发读请求观察Modbus Slave里收到请求的记录以及数据区的变化。这个方法能第一时间确定问题出在PLC侧还是远程设备侧。有一个使用技巧调试时先不要连真实的远程设备先用Modbus Poll/Slave验证本端程序通了再接现场设备。这样可以隔离问题快速定位故障源。5.2 Wireshark抓包分析MODBUS TCP报文遇到比较隐蔽的通讯问题比如数据偶尔错误、连接不稳定的时候光靠上位机看数据是不行的必须直接看报文。Wireshark在这一点上非常好用它自带MODBUS TCP协议的解析器能直接解码出功能码、寄存器地址和数据内容。打开Wireshark选择连接PLC和目标设备的网卡在过滤器里输入tcp.port 502就能筛出所有走502端口的MODBUS TCP报文。然后点击一条报文在协议树里展开MODBUS项能看到Transaction ID、Protocol ID、Length、Unit ID、Function Code等字段。实际排查案例中我遇到过这样一次故障S7-300读取变频器数据时有大约10%的请求会超时。用Wireshark抓包后发现客户端的请求帧在TCP层就发生了重传TCP Retransmission说明数据包在网络传输中丢失或延迟过大。排查现场发现交换机端口协商到了半双工模式导致大量冲突丢包。把交换机和网卡强制为全双工百兆后超时现象完全消失。5.3 高频故障速查表把这些年遇到的典型故障整理出来方便大家按图索骥现象可能原因排查方法连接超时Modbus Poll显示错误IP不在同一网段检查PLC和上位机IP、子网掩码连接建立后无数据返回服务器功能块未调用确认FB64是否在OB1中被调用读到的数据全是65535保持寄存器指针未正确指向检查MB_HOLD_REG和MB_DATA_PTR地址数据能读到但是数值不对字节序问题确认设备是Modbus大端还是设备自定义小端写入不生效功能码选择错误确认MB_MODE为1写且地址是保持寄存器频繁断连重连要等很久连接资源未释放检查DISCONNECT参数是否被误置TRUE多个客户端无法同时连接连接资源已满检查CPU连接数上限和已建立的连接数数据偶尔跳变TCP重传或网络干扰Wireshark抓包检查是否存在TCP重传5.4 故障定位的通用排查思路遇到通讯故障我推荐大家按照从底层到上层的顺序排查避免一上来就盯着PLC程序找问题结果发现是网线松了。第一步查链路层用网线测试仪测线序确认网口状态灯正常检查交换机的端口状态。第二步查网络层用电脑的cmd执行ping命令确认目标设备的IP地址能通。不通的情况下检查IP设置、子网掩码如果是跨网段还要检查网关。第三步查TCP层确认目标端口的监听状态可以用telnet IP 502命令测试端口是否能连上。如果telnet成功说明TCP层没问题问题出在MODBUS协议层。第四步查应用层用Modbus Poll或Wireshark验证请求帧和响应帧是否符合MODBUS TCP协议规范。这个排查顺序我在各种现场用过无数次90%以上的问题都能通过这个思路找到根因。千万不要跳步尤其是很多工程师习惯直接打开程序看逻辑很容易把简单问题复杂化。6. 程序组织与项目经验总结6.1 功能块封装与调用规范一个完整的S7-300 MODBUS TCP通讯程序我习惯按照下面的结构组织OB1中调用主轮询功能块FC100MODBUS_MASTER_CTRLFC100内部再调用FB63及相关的数据处理、报警处理逻辑。数据存储统一放在DB100通讯数据区和DB110报警状态区。通讯参数如设备IP表、寄存器映射表统一放在DB120设备配置区。这样的好处是程序的功能边界清晰。如果通讯异常直接在FB63的STATUS参数和报警DB里排查不需要把整个程序翻一遍。数据区的地址固定下来后上位机或触摸屏做变量连接时也能省心一些。还有一个经验所有通讯数据的读写都建议通过中间变量转发。也就是说外部通讯数据先读到DB100再经过程序判断后传输到工艺控制逻辑使用的DB块。不要在通讯中断的情况下让工艺逻辑直接使用可能不完整的通讯数据。6.2 数据一致性与字节序处理S7-300使用的是大端字节序MODBUS协议也是大端两者天然一致。但现场设备不一定都是大端很多国产仪表、触摸屏、变频器使用小端字节序导致读出来的16位数据高低字节完全反了或者32位浮点数的字序反了。遇到这种现象处理方案有两种一是在PLC程序里做字节交换二是修改设备的通讯参数配置让其输出大端格式。我优先推荐修改设备配置因为PLC程序里的字节交换代码每处都要写很容易出错而且还会增加扫描周期。如果必须在PLC里处理32位浮点数我的惯用做法是用SWAP指令对每个字做字节交换然后交换字序。不要在通讯代码层直接修改原始数据那样容易把后续的数据处理逻辑搞乱。6.3 通讯异常时的安全保护策略通讯总有断的时候关键是要设计好异常保护策略不要让设备在失去通讯后乱跑。我的经验是通讯数据增加超时监视。每台设备维护一个最近通讯成功时间戳当当前时间减去成功时间戳超过设定值比如5秒就判定该设备通讯超时将对应设备的数据区置为无效状态。工艺控制逻辑看到数据无效后进入安全停车或保持上一次有效状态的逻辑。写入操作用先读后写的校验方式。在写入关键参数前先读一次当前值写入后再读一次确认确保数据真正写进去了。这个方法虽然会降低通讯效率但能显著提高数据可靠性。对于涉及安全的关键数据不要直接用MODBUS TCP做主安全回路。MODBUS TCP本质是尽力而为的以太网通讯不具备安全完整性等级。即使项目里用了安全PLC只要通讯链路走的是MODBUS TCP安全等级就无法保证。如果项目有安全需求建议额外布安全总线比如PROFIsafe或者使用独立的硬接线安全回路。6.4 现场实施心得与后续扩展建议最后聊几点我个人的体会。做MODBUS TCP通讯项目前期的网络规划比写代码更重要。我见过一个现场车间里几十台设备的IP地址毫无规划随手设置后来做联调时地址冲突搞得焦头烂额。建议做个简单的IP规划表PLC、机器人、上位机、仪表各分配哪个网段固件版本、端口号、连接资源使用情况都记录在案维护起来事半功倍。设备联调时多留一个心眼。每台设备说的支持MODBUS TCP可能细节上都有差别有的支持03功能码读取但不支持06写单个寄存器有的寄存器地址是1-based有的是0-based有的浮点字节序和标准不一样。拿到一台新设备先用Modbus Poll和Modbus Slave做小规模验证确认功能码、地址映射、字节序都对得上再接入正式程序。这个项目做完后还可以把通讯层进一步扩展。比如用C#上位机程序把MODBUS TCP采集来的数据推送到数据库或云平台实现设备远程监视。或者把MODBUS TCP封装成OPC UA Server让更多上层系统可以对接。这些扩展方向都是基于现有通讯骨架的锦上添花但底层扎实了上层做什么都顺手。本文还有配套的精品资源点击获取