ARTICLE DETAIL

资讯详情

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

C#上位机与西门子PLC通讯:s7.net库实战指南

C#上位机与西门子PLC通讯:s7.net库实战指南 简介面向西门子 PLC 开发者的 S7.NET 通信实践包围绕西门子 S7 系列 PLC 与 C#/.NET 程序间的数据交换提供可直接参考的 PLC 工程与配套 C# 示例适合自动化工程师、上位机开发人员及工业物联网学习者入门。包内共 117 个文件、约 17.15MB以 C# 源码、XML 配置、Visual Studio 工程文件、PNG 图示和 DLL 依赖库为主并包含 PLC 项目压缩包、VS 解决方案、历史版本备份及 README 说明便于按需查看和对照运行。示例使用 S7.NET 库演示建立连接、读写 PLC 变量、处理异常等常用操作同时保留项目配置、缓存文件与工作区信息可帮助理解真实开发环境下的工程结构。从 README 可获取运行配置和使用指引结合梯形图逻辑与 I/O 定义能完整掌握上位机与 PLC 通信的开发流程。已有 1124 人学习/下载适合希望在自动化项目中快速实现控制器数据交互的开发者参考。1. S7.net 通讯到底解决什么问题一条网线把 C# 和 PLC 连起来PLC 程序写完之后上位机要读写数据最常见的坑不是梯形图写不对而是 C# 这头和 PLC 之间缺一条既便宜又可控的通道。西门子原厂 OPC 服务器要单独授权额外的网关盒子又引入新的故障点而 s7.netNuGet 包名 S7netplus用纯 C# 直接实现 S7 通讯协议走 PLC 自带的以太网口 102 端口S7-200 SMART、S7-1200、S7-1500 都能覆盖。它解决的是上位机读写 PLC 数据区这个基础问题典型场景是 C# 上位机按 100ms 周期采集 DB 里的温度、速度、报警位或者在扫码枪触发事件里向 PLC 写入一条条码。这篇把建连参数、数据字节序、PLC 侧配合和线程模型一次讲清适合做过 Modbus 但第一次碰西门子以太网通讯的工程师也适合已经跑通 demo、却在现场反复断线的熟手。2. s7.net 连接原理与最小 C# 例子IP、Rack、Slot 三个参数决定成败建连是整个通讯里最能暴露问题的一环。s7.net 把 S7 协议封装得很干净出错的时候基本都集中在三个参数上CpuType、Rack、Slot。先理解协议栈再动手写代码后面排错就有方向。2.1 ISO-on-TCP 与 S7 协议s7.net 在协议栈里的位置PLC 侧作为 TCP 服务器监听 102 端口PC 侧作为客户端主动连接。数据在网线上依次经过 TCP、ISO-on-TCPRFC1006也叫 TPKT、COTP、S7 应用层。s7.net 把这四层全部封装对你暴露出来的只有 CpuType、IP、Rack、Slot 和一组 Read/Write 方法。这也是它比 OPC 轻量的原因不依赖 DCOM不用配注册表一个 exe 加一个 dll 就能部署。值得理解的是 TSAP。S7 建连时 COTP 请求里带本地和远端两个 TSAP由 Rack 和 Slot 拼出来不同 CPU 拼法不一样。s7.net 依据 CpuType 自动计算 TSAP所以 CpuType 填错比 IP 填错更隐蔽——连接可能显示成功但读写报错或者读回全零。遇到这种连上了但不通的情况先核对 CpuType 与实际固件型号是否匹配。另外 s7.net 是纯托管 .NET 库不依赖任何西门子运行时。WPF、控制台、Windows 服务甚至 Unity 里跑的读写 API 完全一样区别只在 UI 层怎么消费数据。2.2 C# 里用 s7.net 建连的最小例子先在 NuGet 里安装 S7netplus 包然后写最简建连代码using S7.Net; var plc new Plc(CpuType.S71500, 192.168.0.1, 0, 1); try { plc.Open(); if (plc.IsConnected) { object value plc.Read(DB1.DBD0); Console.WriteLine($DB1.DBD0 {value}); } } catch (Exception ex) { Console.WriteLine($通讯失败: {ex.Message}); } finally { plc.Close(); }逻辑说明构造函数四个参数依次是 CPU 类型、PLC 的 IP、Rack、Slot。Open() 内部完成 TCP 连接、COTP 建连、S7 SetupCommunication 协商 PDU 长度三步任何一步失败都会抛异常。IsConnected 只代表最近一次通讯状态不能替代 try/catch。Read(DB1.DBD0) 是符号式读取s7.net 解析地址字符串DB1 是数据块号DBD0 表示从 DB1 偏移 0 开始读一个 4 字节的双字。提示想用 TIA 内置的 PLCSIM 联调普通 PLCSIM 不开 TCP 端口s7.net 连不上。常见做法是改用 PLCSIM Advanced把虚拟网卡地址配到与 PLC 实例同一网段再用这个地址建连。2.3 IP、Rack、Slot 取值表与连不上的排查顺序Rack 和 Slot 的取值直接看 CPU 在机架里的组态位置下表是各系列最常用的默认值CPU 系列典型 Rack典型 Slot说明S7-20000参数不参与寻址惯例填 0S7-200 SMART00用 CpuType.S7200SmartS7-30002CPU 通常在 2 号槽以导轨组态为准S7-40002 或 3按机架组态确认S7-120001TIA 默认值S7-150001TIA 默认值连不上的时候按下面顺序排查90% 的问题不是代码而是环境ping PLC 的 IP判断问题在网线、网段还是协议层。用Test-NetConnection 192.168.0.1 -Port 102检查 102 端口是否可达。端口不通但 ping 通多半是 Windows 防火墙拦了进程或者 PLC 侧没开放 PUT/GET。检查 S7-1200/1500 的允许来自远程对象的 PUT/GET 通讯选项以及 DB 是否关闭了优化块访问具体在第四章讲。如果你在 VMware 里跑 TIA 或上位机虚拟网卡要选桥接模式而不是 NAT。NAT 下 PLC 的回包到不了虚拟机现象就是一直超时。3. C# 例子程序读写 PLCDB、I/Q/M 地址与字节序陷阱连接建立起来只完成第一步读写时数据解释不对才是真正的大头。同一个 DBW2按小端和按大端解析出来数值可能差 256 倍现场最怕这种能通讯但数据是错的状态。3.1 字节序与大端问题为什么读出的 Int 值翻倍S7 PLC 内部按大端存储高字节在低地址。C# 的 BitConverter 在 x86/x64 上默认是小端。如果只用plc.Readshort(DB1.DBW0)s7.net 会自动交换字节序一旦走 ReadBytes 拿原始字节就掉进了字节序坑byte[] raw plc.ReadBytes(DataType.DataBlock, 1, 0, 2); // 错BitConverter 按小端解析PLC 里的 100 会变成 25600 short wrong BitConverter.ToInt16(raw, 0); // 对按 S7 大端手动拼高字节左移 8 位 short right (short)((raw[0] 8) | raw[1]);逻辑说明raw[0] 是 PLC 里的高字节raw[1] 是低字节所以大端拼法是把 raw[0] 左移 8 位再和 raw[1] 相或。这个规律延伸到 4 字节的 DInt 和 Real同样要整体反序。让 s7.net 的泛型ReadT去处理交换逻辑日常开发会省很多事只有整块读 DB 时才用 ReadBytes 自己解析。3.2 用 s7.net 读写 Bool、Int、Real、String 的 API最常用的读写调用如下bool run plc.Readbool(DB1.DBX0.0); short temp plc.Readshort(DB1.DBW2); float press plc.Readfloat(DB1.DBD4); plc.Write(DB1.DBX0.0, true); plc.Write(DB1.DBW2, (short)50); plc.Write(DB1.DBD4, 1.23f);说明地址里的 DBX、DBW、DBD 分别表示位、字、双字偏移数字跟在后面。Write 的参数类型必须和 PLC 侧声明一致——PLC 里是 Int 而你传 C# 的 int4 字节会写坏相邻变量这是最常见的误写事故。布尔量在 PLC 里占一个位s7.net 的地址解析支持 DBX0.0 到 DBX0.7 这种位寻址。下表是各存储区的通用地址写法地址示例类型说明DB1.DBX0.0boolDB1 第 0 字节第 0 位DB1.DBB0byte单字节DB1.DBW0short2 字节有符号整数DB1.DBD0float / int4 字节M10.0 / MW10 / MD10位 / 字 / 双字位存储区I0.0 / IW0输入只读Q0.1 / QW0输出建议不要直接写输出点VW100字仅 S7-200 / S7-200 SMART字符串读写要单独处理。S7 的 STRING 前两个字节分别是声明的最大长度和当前实际长度后面才是 ASCII 字符。s7.net 有 ReadString/WriteString 封装但手动处理字节更可控在 200 SMART、1200、1500 上行为完全一致// 从 DB1 偏移 10 读一个最大长度 30 的 STRING总占 34 字节 byte[] buf plc.ReadBytes(DataType.DataBlock, 1, 10, 34); int actual buf[1]; string text Encoding.ASCII.GetString(buf, 2, actual); // 写回首字节填最大长度次字节填实际长度 byte[] data Encoding.ASCII.GetBytes(A12345); byte[] outBuf new byte[34]; outBuf[0] 32; outBuf[1] (byte)data.Length; Array.Copy(data, 0, outBuf, 2, data.Length); plc.WriteBytes(DataType.DataBlock, 1, 10, outBuf);注意S7 默认字符集不是 UTF-8Encoding.ASCII 只能处理数字和英文。中文条码建议在 PLC 里按 BYTE 数组存C# 侧自己定编码规则不要直接往 STRING 里塞中文。3.3 整块读 DB 再本地解析保证数据一致性逐条 Read 的每条都是一次独立 S7 作业两条读之间 PLC 可能在扫描周期里改了值你会拿到一个跨变量不一致的快照。工艺联锁、配方下发这类场景接受不了这种误差。解法是一次作业把整块读回来再本地解析byte[] db plc.ReadBytes(DataType.DataBlock, 10, 0, 64); float result ReadReal(db, 8); static float ReadReal(byte[] b, int off) { if (BitConverter.IsLittleEndian) return BitConverter.ToSingle(new[] { b[off3], b[off2], b[off1], b[off] }, 0); return BitConverter.ToSingle(b, off); }说明ReadBytes 第二个参数是数据块号第三个是块内偏移第四个是长度。一次请求的字节数不要超过约 200 字节这是 S7 PDU 240 字节减去协议头之后的净荷上限要读更大的区段就分两次 ReadBytes 再拼接。本地解析时偏移必须对照 PLC 侧非优化 DB 的偏移表BOOL 按位排、BYTE 按字节排、WORD/INT 按 2 字节对齐、DWORD/REAL 按 4 字节对齐TIA 会自动插入填充字节调试时先在 DB 编辑器里打开偏移显示再和 C# 里的 offset 一一核对。4. PLC 程序怎么配合 s7.net数据块规划、握手与触发事件通讯是双向的C# 侧代码写得再标准PLC 侧不配合照样白搭。这一章讲 TIA 里的设置、数据区怎么布局以及扫码枪这类外部事件如何通过 C# 触发 PLC 动作。4.1 在 TIA Portal 里开启 PUT/GET 并取消优化的块访问S7-1200/1500 新建的 DB 默认勾选优化的块访问。这种 DB 没有固定偏移s7.net 用 DB1.DBW0 这类绝对地址去读会失败或者读回异常。改法是选中 DB在属性里取消勾选优化的块访问保存后重新编译下载。CPU 侧还要打开 S7 通讯服务进入 CPU 属性在防护与安全 → 连接机制里勾选允许来自远程对象的 PUT/GET 通信访问。S7-1500 如果这两项都确认做了仍然超时检查 CPU 是否开启了仅支持与 PG/PC 的安全通信之类的限制项它会拦住 s7.net 这类非安全链接。注意关闭优化访问后PLC 程序里用符号名访问该 DB 不受影响但 DB 偏移会按照字节对齐规则重排C# 侧的所有地址都要重新核对一遍。4.2 在 PLC 侧规划数据区把 C# 要读的变量集中到一个 DB常见做法是把上位机要读写的变量集中到一两个 DB按命令区 状态区分离。现在有人用 AI 生成 PLC 代码但数据区布局还得人工定否则生成出来的偏移和 C# 对不上返工成本更高。推荐布局如下表以 DB10 为例地址类型用途DBX0.0boolC# 到 PLC 的触发位DBX0.1boolPLC 到 C# 的完成位DBW2int命令码DBD4real参数DBD8real结果DBB32 起STRING[30]条码数据规划时要守几条规矩bool 集中放不要和 word 交错排列否则位偏移算到眼花。2 字节以上的变量按起始偏移对齐能对齐就对齐避免 TIA 插入填充字节后 C# 侧算错。预留心跳位和版本号位置现场排查时能立刻确认 PLC 程序版本。上位机只读的变量放一个区上位机要写的放另一个区避免误写覆盖。4.3 扫码枪触发事件与 C# 写 PLC 的例子程序USB 扫码枪通常模拟键盘输入字符会进到当前焦点控件。上位机里不要在 TextBox 上做直接在窗口级拦截键盘事件private readonly StringBuilder _barcode new StringBuilder(); private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e) { char c (char)e.KeyValue; if (char.IsLetterOrDigit(c)) { _barcode.Append(c); e.IsInputKey true; } else if (e.KeyCode Keys.Enter _barcode.Length 0) { WriteS7String(10, 32, _barcode.ToString()); _plc.Write(DB10.DBX0.0, true); // 触发 PLC 处理 _barcode.Clear(); } }WriteS7String 就是 3.2 节那段长度字节封装的代码。写完字符串立刻置触发位PLC 检测到触发后执行比对或入库动作处理完回写完成位并复位触发位C# 轮询完成位拿结果。这种触发位 完成位的握手是现场最可靠的做法比直接写 DBW 后睡 50ms 再读强得多它天然处理了寄存器被覆盖、PLC 扫描周期造成的命令丢失。PLC 侧 SCL 示意如下IF DB10.trigger AND NOT DB10.done THEN DB10.result : 0.0; // 这里放实际工艺动作条码比对、配方下发、轴使能等 DB10.done : TRUE; DB10.trigger : FALSE; END_IF;4.4 S7-200 SMART 的 V 区与 DB1 映射关系S7-200 SMART 没有 S7-300/1500 那种 DB 概念程序里用 V 区。通过 S7 协议访问时V 区被映射成 DB1VW100 等于 DB1.DBW100V0.0 等于 DB1.DBX0.0。建连用 CpuType.S7200SmartRack 和 Slot 填 0/0 即可。200 SMART 固件默认开放 S7 通讯不需要像 1200/1500 那样单独开 PUT/GET。V 区映射范围受具体型号的 V 区大小限制超出部分会读回 0 或报地址越界。老式 S7-200 带 CP243-1 以太网模块时s7.net 用 CpuType.S7200V 区同样映射到 DB1如果 CPU 没有以太网模块只能走 PPI 转串口那就不属于 s7.net 的讨论范围了。5. 线程、断线重连与 Wireshark让 s7.net 通讯在现场可靠运行最后落到现场运行最要命的两个问题循环采集时 UI 卡顿以及通讯中断后程序不会自己恢复。5.1 轮询循环与 UI 刷新不卡顿的写法Read 是同步网络 IO直接放在 UI 事件里会卡界面。让它在后台线程跑结果通过 BeginInvoke 回 UIprivate async Task PollLoopAsync(CancellationToken ct) { while (!ct.IsCancellationRequested) { try { float v await Task.Run(() _plc.Readfloat(DB1.DBD0)); BeginInvoke((Action)(() labelTemp.Text v.ToString(F2))); } catch (PlcException ex) { BeginInvoke((Action)(() labelStatus.Text ex.Message)); } await Task.Delay(100, ct); } }说明Task.Run 把阻塞读放进线程池await 回到 UI 线程后只做赋值轮询周期 100ms 也不会卡。要注意同一个 Plc 实例不要同时在轮询线程和按钮事件里发起读写s7.net 按请求响应对工作并发调用会串包或抛异常。要么用 lock 把所有读写包住要么规定整个程序只有一个线程碰 Plc 对象后一种更好维护。5.2 断线重连与超时参数Plc 对象有 ReadTimeout 和 WriteTimeout 属性单位毫秒现场网络抖动时默认值偏短。我一般会设 1500ms 左右并在 catch 里做退避重连先 Close等 1 秒、2 秒、5 秒再 Open成功后继续采集循环。同时在 PLC 侧放一个 1 秒翻转的心跳位C# 每次采集顺带读心跳位连续几次不变就判定 PLC 掉站或网线断开主动进入重连流程。只靠异常判断状态是不够的很多断网场景 TCP 不会立刻给你异常。5.3 Wireshark 验证 S7 通讯链路的最终检查抓包是最后的手段。Wireshark 里过滤tcp.port 102正常建连会看到 TCP 三次握手、COTP 连接请求与响应、S7 SetupCommunication 协商 PDU 长度之后是连续的 Read Var 和 Write Var 报文。如果只有 TCP SYN 没有回包问题在网络层或防火墙握手完成但没有 SetupCommunication 响应多半是 CPU 侧通讯设置或 DB 优化访问没关。抓包能直接区分环境问题和配置问题SYN 无响应查网线、网段、防火墙、VMware 网络模式有连接无 S7 响应查 PUT/GET 和优化块访问。过滤器就是tcp.port 102看到 SetupCommunication 之后跟着一条 Read Var链路就是通的。本文还有配套的精品资源点击获取
返回列表