PLC 专用协议 协议详解
私有协议就是厂家(如西门子、三菱)为了商业护城河和极致性能,自己发明的一种不对外公开的“内部语言”。
作为上位机,和 PLC 通讯,用厂家的私有协议是最快、最稳、功能最全的选择。
当 1968 年第一台 PLC (Modicon 084) 诞生时,大家用的都是两根线的串口。
西门子、AB (Rockwell)、三菱这些巨头造出 PLC 后,为了让你**“买了我的 PLC,就必须买我的触摸屏,必须用我的编程软件”,他们刻意发明了极其复杂且不公开**的通讯规则。都是为了垄断。
Modicon (现在的施耐德) 公司觉得这样太乱了,于是公开了 Modbus 协议。它是第一个完全公开、免费、简单的协议。
当 以太网 (Ethernet) / Socket 技术开始进入工厂时。
你以为大家会商量出一个统一的新协议,结果却是:大家把自家的“老私有协议”,直接打包塞进了 Socket 里。
西门子做出了 S7Comm (S7 over ISO-on-TCP),三菱做出了 MC Protocol。AB 做出了 EtherNet/IP 。
Socket 这么方便,Modbus 这么通用,为什么大家统一?还要搞 S7、MC 这种私有协议?
性能 和 功能。
Modbus (简陋): 只能读写寄存器 (0-65535)。它不懂什么是“程序块”,什么是“系统时间”,什么是“诊断日志”。
私有协议 (全能):
代表:AB (EtherNet/IP), 西门子 S7-1500 (优化的 S7)
以前我们读地址:DB1.DBD0。
现在他们推崇读**“标签 (Tag)”**:直接读变量名 "Motor_Speed"。
目的: 为了防破解,为了防第三方库(像 S7.Net)。西门子 S7-1500 默认开启了“优化块访问”,实际上是把协议加密了,导致很多老驱动连不上,逼着你买官方授权。
代表:工业 4.0 联盟
大家发现私有协议太难搞了,于是推出了 OPC UA。
这是一种跨平台、跨品牌的超级协议。西门子、三菱、倍福的新款 PLC 都原生支持 OPC UA。
愿景: 以后没有 S7、没有 MC,大家全用 OPC UA 说话。
缺点: 协议太重,速度慢(基于 XML/Binary 复杂结构),目前还取代不了 S7/MC 在高速控制中的地位。
大部分工厂的设备正好是 S7 协议 和 MC 协议 的天下。掌握 S7.Net 和 HslCommunication 是重点。
| 梯队 & 推荐度 | 协议名称 | 对应品牌 (典型型号) | **核心特点 ** | 默认端口 | C# 开发首选库 (不造轮子) |
|---|---|---|---|---|---|
| 🏆 T0 (必学) (市场占有率 80%) | S7 协议 (S7Comm) | 西门子 Siemens (S7-1200, 1500, 300, Smart) | 1. 无需配置:不用映射地址,直接读 DB 块。 2. 基于 TCP:底层是 ISO-on-TCP。 3. 如果不买授权:记得在 PLC 侧勾选“允许来自远程对象的 PUT/GET 访问”。 | 102 | 1. S7.Net (开源免费,新手首选) 2. HslCommunication (国产最强) 3. Snap7 (性能怪兽) |
| 🏆 T0 (必学) (日系老大) | MC 协议 (Qna-3E Binary) | 三菱 Mitsubishi (FX5U, Q系列, L系列) | 1. 二进制最快:推荐配置为 Binary 模式,比 ASCII 快且包更小。 2. 半公开:协议格式规整,手写 Socket 难度中等。 3. 灵活性:支持按位(M)、按字(D)批量读写。 | 6000 (或 5002) | 1. HslCommunication (对三菱支持极好) |
| T1 (常用) (中高端/美资) | FINS TCP | 欧姆龙 Omron (CJ, CP, NX/NJ) | 1. 握手复杂:连接前要先发握手包申请节点号,非常繁琐。 2. 地址分级:网络号-节点号-单元号。 3. 大端序:注意字节高低位反转。 | 9600 | 1. HslCommunication 2. libplctag |
| T1 (常用) (标签访问) | EtherNet/IP (CIP 协议) | 罗克韦尔 AB (Rockwell) (ControlLogix, Compact) | 1. 标签 (Tag) 访问:唯一一个不用记地址的! 直接读变量名 "Speed"。 2. 协议极其复杂:千万别尝试手写底层 Socket。 | 44818 | 1. libplctag (C语言库的C#封装,稳定) 2. EEIP |
| T2 (特定)(PC-Based) | ADS 协议 | 倍福 Beckhoff (TwinCAT 控制器) | 1. 路由通讯:不光靠 IP,还要靠 AMS Net ID (如 1.2.3.4.1.1)。 2. 极快:专为 PC 控制设计。 3. 官方支持好:倍福直接提供 DLL。 | 48898 | TwinCAT.Ads (官方免费 DLL) |
| T2 (简单)(小型机) | Mewtocol | 松下 Panasonic (FP 系列) | 1. 文本流:类似 ASCII 码流,比较古老但简单。 2. 易调试:可以用网络调试助手直接发指令测试。 | 自定义 | 1. HslCommunication 2. 手写 Socket (很容易) |
在进入实战之前,先聊一下这个问题。
想象一下,PLC 内部有一个巨大的 Excel 表格(内存区),这个表格里的每一个格子,都实时反映着外面的世界。
| 现实世界的零件 | PLC 里的动作 (硬件接线) | PLC 内存地址 (你的代码读写的目标) | C# 代码的含义 |
|---|---|---|---|
| 绿色启动按钮 | 按钮按下 -> 24V电传给 PLC | I0.0 (输入点 0.0) | 上位机读它:判断工人是不是按了启动? |
| 红色急停拍钮 | 按钮拍下 -> 断电 | I0.1 (输入点 0.1) | 上位机读它:如果为 True,软件界面马上弹红色报警窗! |
| 温度传感器 | 测到 85.5℃ | DB1.DBD0 (浮点数) | 上位机读它:把 85.5 显示在电脑屏幕的仪表盘上。 |
| 流水线电机 | PLC 输出 24V -> 继电器吸合 | Q0.0 (输出点 0.0) | 上位机写它:软件点“强制启动”,PLC 就让电机转。 |
| 产量计数器 | 每过一个产品加 1 | DB1.DBW10 (整数) | 上位机读它:存入数据库,老板要看今天做了多少个。 |
所以, Read("DB1.DBD0"),本质上就是在问 PLC:“喂,现在的温度是多少?”
既然 PLC 自己就可以控制,为什么还需要上位机?
简单的控制,确实不需要。但是复杂的现代工厂里,PLC 需要和上位机互补配合。
具体可以概括为 4 大核心需求:
所以, PLC 通讯很重要,下面就来进入实战环节。
S7.Net (目前最流行的免费开源 C# 驱动)。
“机架 0,插槽 1,数据都在 DB 里。”
西门子连接需要 4 个核心参数,不像 Modbus 只有 IP 和端口:
using S7.Net; // 1. 引用库
using System;
class Program
{
static void Main(string[] args)
{
// ==========================================
// 1. 创建 PLC 对象 (配置连接参数)
// ==========================================
// 参数顺序:CPU型号, IP地址, 机架号(Rack), 插槽号(Slot)
// 如果你是模拟器或者 S7-1200,Slot 通常填 1;如果是 S7-300,填 2。
Plc plc = new Plc(CpuType.S71200, "192.168.1.10", 0, 1);
try
{
// ==========================================
// 2. 打开连接
// ==========================================
Console.WriteLine("正在连接 PLC...");
plc.Open(); // 这一步如果不报错,恭喜你,网通了!
Console.WriteLine("连接成功!");
// ==========================================
// 3. 读数据 (Read) —— 最爽的字符串寻址
// ==========================================
// 场景 A: 读取 DB1 块里的第 0 个地址 (假设是温度,浮点数)
// 语法解析: "DB1.DBD0"
// DB1 = 1号数据块
// DBD = Double Word (4字节,对应 C# float/int)
// 0 = 偏移量
var temp = plc.Read("DB1.DBD0");
// 注意:S7.Net 读回来的是 object,通常需要转一下类型
float temperature = ((uint)temp).ConvertToFloat();
Console.WriteLine($"当前温度: {temperature} ℃");
// 场景 B: 读取 M 区的一个开关 (M100.0)
// bool 类型
bool isRunning = (bool)plc.Read("M100.0");
Console.WriteLine($"设备运行状态: {isRunning}");
// ==========================================
// 4. 写数据 (Write)
// ==========================================
// 场景 C: 修改温度设定值为 50.5
// 注意:写入时不需要转换类型,库会自动处理
plc.Write("DB1.DBD0", 50.5f);
Console.WriteLine("温度设定值已修改为 50.5");
// 场景 D: 启动设备 (把 M100.0 置为 true)
plc.Write("M100.0", true);
Console.WriteLine("启动指令已发送");
}
catch (Exception ex)
{
Console.WriteLine($"发生错误: {ex.Message}");
}
finally
{
// ==========================================
// 5. 断开连接 (养成好习惯)
// ==========================================
plc.Close();
}
Console.ReadKey();
}
}
详解:
DB1.DBD0 是 S7 寻址字符串。
| 字符串写法 | 含义 | 对应 C# 类型 | 字节长度 | 备注 |
|---|---|---|---|---|
DB1.DBX0.0 | DB1块,第0个字节的第0位 | bool | 1位 | X 代表 Bit (位) |
DB1.DBB0 | DB1块,第0个字节 | byte | 1字节 | B 代表 Byte (字节) |
DB1.DBW0 | DB1块,起始0,读2个字节 | ushort / short | 2字节 | W 代表 Word (字) |
DB1.DBD0 | DB1块,起始0,读4个字节 | uint / float | 4字节 | D 代表 Double Word (双字) |
读开关/信号 -> 用 DBX (或者 M10.0)
读整数 (short) -> 用 DBW
读小数 (float) -> 用 DBD
“Qna-3E 帧,二进制 (Binary) 模式。”
坑点提示: 三菱 PLC 需要在软件(GX Works)里配置“以太网端口设置”,开启 MC 协议并设置为 二进制 模式,否则连不上。
安装 Nuget:搜索 HslCommunication 并安装。
HslCommunication 自带的 demo 程序来模拟一个三菱服务器。
using HslCommunication;
using HslCommunication.Profinet.Melsec; // 引用三菱专用命名空间
using System;
class Program
{
static void Main(string[] args)
{
// ==========================================
// 1. 创建对象 (选对协议!)
// ==========================================
// MelsecMcNet 代表:三菱(Melsec) MC协议(Mc) 网络版(Net)
// 这是一个“Qna-3E 二进制”客户端
MelsecMcNet mitsubishi = new MelsecMcNet("192.168.1.10", 6000);
// ==========================================
// 2. 连接 PLC
// ==========================================
OperateResult connect = mitsubishi.ConnectServer();
if (connect.IsSuccess)
{
Console.WriteLine("连接三菱 PLC 成功!");
}
else
{
Console.WriteLine($"连接失败: {connect.Message}");
return; // 连不上就别往下走了
}
// ==========================================
// 3. 读写数据 (地址写法是关键)
// ==========================================
// 场景 A: 读取 D 寄存器 (相当于西门子的 DBW)
// D100 是一个 short (16位整数)
short d100 = mitsubishi.ReadInt16("D100").Content;
Console.WriteLine($"D100 的值: {d100}");
// 场景 B: 读取 M 软元件 (相当于西门子的 M点)
// M100 是一个开关 (bool)
bool m100 = mitsubishi.ReadBool("M100").Content;
Console.WriteLine($"M100 状态: {m100}");
// 场景 C: 写入数据
// 把 12345 写入 D200
mitsubishi.Write("D200", (short)12345);
Console.WriteLine("已写入 D200");
// 场景 D: 启动设备 (置位 M200)
mitsubishi.Write("M200", true);
Console.WriteLine("已启动设备 (M200 = ON)");
// ==========================================
// 4. 批量读取 (提高效率的法宝)
// ==========================================
// 一次读 D100 到 D109 (共10个)
// 也就是 20 个字节 (每个 short 占 2 字节)
byte[] buffer = mitsubishi.Read("D100", 10).Content;
// 自己处理 byte[]...
// 5. 断开
mitsubishi.ConnectClose();
Console.ReadKey();
}
}
代码详解:
| 代号 | 全称 | 作用 | 对应 C# 类型 | 读写特性 |
|---|---|---|---|---|
| D | Data Register (数据寄存器) | 存数字 (温度、产量、速度) | short / ushort | 可读可写 (最常用) |
| M | Internal Relay (中间继电器) | 存状态 (开关、标志位) | bool | 可读可写 (最常用) |
| X | Input (输入点) | 物理按钮、传感器信号 | bool | 只读 (不能写!) |
| Y | Output (输出点) | 控制电机、灯泡 | bool | 可读可写 |
| W | Link Register (字链接) | 也是存数字的 (稍微少用) | short | 可读可写 |
注意:
float 值存在 D100,其实它是占用了 D100 和 D101。代码写法:mitsubishi.ReadFloat("D100")。
通讯的目的,就是为了把孤立的机器,变成智能的系统。私有协议,让通讯更快、更透明。