C# 西门子 PLC 以太网通讯组件(S7协议 / ISO-on-TCP)
·
一、项目结构
SiemensS7Comm/
├── SiemensS7Comm.csproj
├── S7Client.cs 主入口(给用户用的)
├── S7Protocol.cs 底层协议编解码
├── S7Types.cs PLC 数据类型 / 枚举 / 地址
├── IsoTcpTransport.cs ISO-on-TCP 传输层(TPKT + COTP)
├── S7Area.cs 数据区定义
└── S7Exception.cs 自定义异常
二、核心代码
1. 枚举 & 数据类型 (S7Area.cs / S7Types.cs)
using System;
namespace SiemensS7Comm
{
/// <summary>
/// PLC 数据区
/// </summary>
public enum S7Area
{
PE = 0x81, // I 过程输入(I区)
PA = 0x82, // Q 过程输出(Q区)
MK = 0x83, // M 标志位(M区)
DB = 0x84, // DB 数据块
CT = 0x1C, // Counter(计数器 C区)
TM = 0x1D, // Timer(定时器 T区)
}
/// <summary>
/// PLC CPU类型(影响机架/插槽号)
/// </summary>
public enum S7CpuType
{
S7200 = 0,
S7300 = 1,
S7400 = 3,
S71200 = 10,
S71500 = 11,
S7200_SMART = 20,
}
/// <summary>
/// S7 读/写项描述
/// </summary>
public class S7Item
{
public S7Area Area { get; set; }
public int DbNumber { get; set; } // DB号(非DB区填0)
public int ByteOffset { get; set; } // 起始字节
public int BitOffset { get; set; } // 位偏移 0~7(仅当按位读写时用)
public int Length { get; set; } // 字节数
public S7Item() { }
public S7Item(S7Area area, int dbNumber, int byteOffset, int length)
{
Area = area;
DbNumber = dbNumber;
ByteOffset = byteOffset;
Length = length;
}
}
/// <summary>
/// PLC 连接参数
/// </summary>
public class S7Config
{
public string IpAddress { get; set; } = "192.168.0.1";
public int Port { get; set; } = 102;
public S7CpuType CpuType { get; set; } = S7CpuType.S7200_SMART; // 大多数情况 S7-200SMART 用这个
public int Rack { get; set; } = 0;
public int Slot { get; set; } = 1;
public int LocalTSAP { get; set; } = 0x0100; // 本地 TSAP(自动计算)
public int RemoteTSAP { get; set; } = 0x0201; // 远端 TSAP(自动计算)
public int TimeoutMs { get; set; } = 3000;
/// <summary>
/// 根据 CPU 类型自动修正 Rack/Slot/RemoteTSAP
/// </summary>
public void AutoSetByCpu(S7CpuType cpu)
{
CpuType = cpu;
switch (cpu)
{
case S7CpuType.S7300:
Rack = 0; Slot = 2;
RemoteTSAP = (Rack << 8) | Slot;
break;
case S7CpuType.S7400:
Rack = 0; Slot = 3;
RemoteTSAP = (Rack << 8) | Slot;
break;
case S7CpuType.S71200:
case S7CpuType.S71500:
Rack = 0; Slot = 1;
RemoteTSAP = (Rack << 8) | Slot;
break;
case S7CpuType.S7200_SMART:
Rack = 0; Slot = 1;
RemoteTSAP = 0x0201; // S7-200 Smart 固定
break;
}
}
}
}
2. ISO-on-TCP 传输层 (IsoTcpTransport.cs)
using System;
using System.Net.Sockets;
using System.Threading;
namespace SiemensS7Comm
{
/// <summary>
/// ISO-on-TCP (RFC 1006) 传输层
/// 封装 TPKT + COTP
/// </summary>
internal class IsoTcpTransport : IDisposable
{
private Socket _socket;
private byte[] _recvBuf = new byte[65536];
private int _recvLen = 0;
public bool IsConnected => _socket != null && _socket.Connected;
/* ---------- connect ---------- */
public void Connect(string ip, int port, int remoteTsap, int localTsap, int timeoutMs)
{
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.SendTimeout = timeoutMs;
_socket.ReceiveTimeout = timeoutMs;
_socket.Connect(ip, port);
// ① COTP Connect Request
SendCOTPConnectRequest(localTsap, remoteTsap);
// ② 等待 COTP Connect Confirm
byte[] resp = RecvPDU();
if (resp.Length < 2 || resp[5] != 0xD0) // 0xD0 = CC
throw new S7Exception("COTP Connect 失败");
// ③ S7 Setup Communication
SendS7SetupComm();
resp = RecvPDU();
CheckS7Header(resp, "S7 Setup");
}
/* ---------- 读/写原始PDU ---------- */
public byte[] Transact(byte[] pdu)
{
SendPDU(pdu);
return RecvPDU();
}
/* ============================================================
TPKT
============================================================ */
// TPKT头固定 4字节:Ver=3, Reserved=0, Len(2字节)
private void SendPDU(byte[] data)
{
byte[] tpkt = new byte[data.Length + 4];
tpkt[0] = 3; // Version
tpkt[1] = 0; // Reserved
tpkt[2] = (byte)((data.Length + 4) >> 8);
tpkt[3] = (byte)((data.Length + 4) & 0xFF);
Buffer.BlockCopy(data, 0, tpkt, 4, data.Length);
_socket.Send(tpkt);
}
private byte[] RecvPDU()
{
// 收 TPKT 头
int got = 0;
while (got < 4)
got += _socket.Receive(_recvBuf, got, 4 - got, SocketFlags.None);
if (_recvBuf[0] != 3)
throw new S7Exception($"非法TPKT版本: {_recvBuf[0]}");
int tpktLen = (_recvBuf[2] << 8) | _recvBuf[3];
int payloadLen = tpktLen - 4;
got = 0;
while (got < payloadLen)
got += _socket.Receive(_recvBuf, 4 + got, payloadLen - got, SocketFlags.None);
byte[] result = new byte[tpktLen];
Buffer.BlockCopy(_recvBuf, 0, result, 0, tpktLen);
return result;
}
/* ============================================================
COTP 层
============================================================ */
/// <summary>
/// COTP Connect Request (DT 类 / CR)
/// </summary>
private void SendCOTPConnectRequest(int localTsap, int remoteTsap)
{
// CR = 0xE0 长度可变,这里给一个最常用的最小CR包
byte[] cr = new byte[22];
// --- COTP CR ---
cr[0] = 0xE0; // CR
cr[1] = 0x00; // CDT
cr[2] = 0x00; cr[3] = 0x18; // Destination-Reference (2B)
cr[4] = (byte)((localTsap >> 8) & 0xFF);
cr[5] = (byte)(localTsap & 0xFF);
cr[6] = 0xC1; cr[7] = 0x01; cr[8] = (byte)((remoteTsap >> 8) & 0xFF);
cr[9] = (byte)(remoteTsap & 0xFF);
cr[10] = 0xC2; cr[11] = 0x01; cr[12] = (byte)((remoteTsap >> 8) & 0xFF);
cr[13] = (byte)(remoteTsap & 0xFF);
cr[14] = 0xC0; cr[15] = 0x01; cr[16] = 0x09; // PClass
cr[17] = 0xC0; cr[18] = 0x02; cr[19] = 0x01; // Parameter
cr[20] = 0xC0; cr[21] = 0x03; cr[22] = 0x00;
// 包装成TPKT
SendPDU(cr);
}
/* ============================================================
S7 Setup Communication
============================================================ */
private void SendS7SetupComm()
{
/*
S7 Header (10B) + Param (8B)
固定: PDU-Type=0xF0, Job=0x32, ...
*/
byte[] s7 = new byte[25];
// S7 Param part
// Header
s7[0] = 0x32; // Protocol ID
s7[1] = 0x01; // Job = Setup
s7[2] = 0x00; s7[3] = 0x00; // Redundancy
s7[4] = 0x00; s7[5] = 0x00; // PDU-Ref
s7[6] = 0x00; s7[7] = 0x08; // Param-Length
s7[8] = 0x00; s7[9] = 0x00; // Data-Length
// Setup param
s7[10] = 0xF0; // Fun: Setup
s7[11] = 0x00; // Unknown
s7[12] = 0x00; s7[13] = 0x01; // Max AmQ-Calling
s7[14] = 0x00; s7[15] = 0x01; // Max AmQ-Called
s7[16] = 0x03; s7[17] = 0xC0; // PDU-Length = 960
s7[18] = 0x00; s7[19] = 0x00; // unknown
// Length field for COTP DT (header=2: LI=2 + TPDU type 0xF0)
byte[] dt = new byte[s7.Length + 1];
dt[0] = (byte)(s7.Length); // LI = rest length
dt[1] = 0xF0; // DT - Data TPDU
Buffer.BlockCopy(s7, 0, dt, 2, s7.Length);
SendPDU(dt);
}
private void CheckS7Header(byte[] resp, string tag)
{
// resp 是完整 TPKT帧,跳过 TPKT(4B) + COTP-DT(2B),看 S7 头
if (resp.Length < 12) throw new S7Exception($"{tag}: 响应过短");
// 跳过到 S7: resp[4] = LI, resp[5]=DT
int off = 4 + 2; // S7 起始
if (resp[off] != 0x32)
throw new S7Exception($"{tag}: 非S7响应 0x{resp[off]:X2}");
}
public void Disconnect()
{
try { _socket?.Shutdown(SocketShutdown.Both); } catch { }
try { _socket?.Close(); } catch { }
_socket = null;
}
public void Dispose() => Disconnect();
}
}
3. S7 协议编解码 (S7Protocol.cs)
using System;
using System.Text;
namespace SiemensS7Comm
{
/// <summary>
/// S7 PDU 构造 / 解析
/// </summary>
internal static class S7Protocol
{
// S7 Function Codes
public const byte FUNC_READ = 0x04;
public const byte FUNC_WRITE = 0x05;
// ==================== 构造 Read Request ====================
public static byte[] BuildReadRequest(S7Item[] items)
{
int itemCount = items.Length;
int paramLen = 12 * itemCount;
int headerLen = 10 + paramLen;
byte[] pdu = new byte[4 + 1 + headerLen]; // TPKT空壳先留着
int pos = 0;
// --- COTP-DT ---
pdu[pos++] = (byte)(1 + headerLen); // LI
pdu[pos++] = 0xF0; // DT
// --- S7 Header (10B) ---
pdu[pos++] = 0x32; // Protocol
pdu[pos++] = FUNC_READ; // Job = Read
pdu[pos++] = 0x00; pdu[pos++] = 0x00; // Redundancy
pdu[pos++] = 0x00; pdu[pos++] = 0x00; // PDU-Ref
pdu[pos++] = (byte)(paramLen >> 8); pdu[pos++] = (byte)(paramLen & 0xFF); // ParamLen
pdu[pos++] = 0x00; pdu[pos++] = 0x00; // DataLen
// --- Param: Item Count + Items ---
pdu[pos++] = 0x01; // ??? reserved
pdu[pos++] = (byte)itemCount; // Number of items
foreach (var item in items)
{
// 8Byte per item
pdu[pos++] = 0x12; // VariableSpec
pdu[pos++] = 0x0A; // Length of following
pdu[pos++] = 0x10; // Syntax = S7ANY
pdu[pos++] = (byte)item.Area;
// Area number (DB number big-endian)
pdu[pos++] = (byte)((item.DbNumber >> 8) & 0xFF);
pdu[pos++] = (byte)(item.DbNumber & 0xFF);
// Byte address (3 bytes, bit24..bit0)
int addr3 = (item.ByteOffset << 3) | item.BitOffset;
pdu[pos++] = (byte)((addr3 >> 16) & 0xFF);
pdu[pos++] = (byte)((addr3 >> 8) & 0xFF);
pdu[pos++] = (byte)(addr3 & 0xFF);
// Length in bits(!) / 8
int bitLen = item.Length * 8;
pdu[pos++] = (byte)((bitLen >> 8) & 0xFF);
pdu[pos++] = (byte)(bitLen & 0xFF);
}
// 重新包装 TPKT
return WrapTPKT(pdu, 4 + 1 + headerLen);
}
// ==================== 构造 Write Request ====================
public static byte[] BuildWriteRequest(S7Item item, byte[] values)
{
int paramLen = 12;
int dataHeaderLen = 4 + 1 + item.Length; // Return code(1) + Datalen(2) + Data...
int totalLen = 10 + paramLen + dataHeaderLen;
byte[] pdu = new byte[4 + 1 + totalLen];
int pos = 0;
// COTP-DT
pdu[pos++] = (byte)(1 + totalLen);
pdu[pos++] = 0xF0;
// S7 Header
pdu[pos++] = 0x32;
pdu[pos++] = FUNC_WRITE;
pdu[pos++] = 0x00; pdu[pos++] = 0x00;
pdu[pos++] = 0x00; pdu[pos++] = 0x00;
pdu[pos++] = (byte)(paramLen >> 8); pdu[pos++] = (byte)(paramLen & 0xFF);
pdu[pos++] = (byte)((dataHeaderLen) >> 8); pdu[pos++] = (byte)((dataHeaderLen) & 0xFF);
// Param Item
pdu[pos++] = 0x01;
pdu[pos++] = 0x01; // 1 item
pdu[pos++] = 0x12;
pdu[pos++] = 0x0A;
pdu[pos++] = 0x10;
pdu[pos++] = (byte)item.Area;
pdu[pos++] = (byte)((item.DbNumber >> 8) & 0xFF);
pdu[pos++] = (byte)(item.DbNumber & 0xFF);
int addr3 = (item.ByteOffset << 3) | item.BitOffset;
pdu[pos++] = (byte)((addr3 >> 16) & 0xFF);
pdu[pos++] = (byte)((addr3 >> 8) & 0xFF);
pdu[pos++] = (byte)(addr3 & 0xFF);
int bitLen = item.Length * 8;
pdu[pos++] = (byte)((bitLen >> 8) & 0xFF);
pdu[pos++] = (byte)(bitLen & 0xFF);
// ---- Data Section ----
// Item header
pdu[pos++] = 0x00; // Return code = OK placeholder
pdu[pos++] = 0x00; // Transport size (written later)
if (item.Area == S7Area.PE || item.Area == S7Area.PA)
pdu[pos - 1] = 0x03; // BIT
else
pdu[pos - 1] = 0x04; // BYTE/WORD/DWORD/REAL → 0x04=BYTE
pdu[pos++] = (byte)((item.Length >> 8) & 0xFF);
pdu[pos++] = (byte)(item.Length & 0xFF);
// Copy actual value bytes
Buffer.BlockCopy(values, 0, pdu, pos, values.Length);
return WrapTPKT(pdu, 4 + 1 + totalLen);
}
// ==================== 解析 Read Response ====================
public static byte[][] ParseReadResponse(byte[] tpktFrame, int itemCount)
{
// Skip TPKT(4) + COTP-DT(2) = 6
int baseOff = 6;
if (tpktFrame[baseOff] != 0x32 || tpktFrame[baseOff + 1] != FUNC_READ)
throw new S7Exception("非Read响应");
// 数据区起始:S7头(10) + 2(itemCountHeader) + 每个item的返回头(4B+data)
int off = baseOff + 10 + 2;
byte[][] results = new byte[itemCount][];
for (int i = 0; i < itemCount; i++)
{
byte retCode = tpktFrame[off + 0];
byte transportSize = tpktFrame[off + 1];
int dataLen = (tpktFrame[off + 2] << 8) | tpktFrame[off + 3];
off += 4; // skip return header
byte[] data = new byte[dataLen];
Buffer.BlockCopy(tpktFrame, off, data, 0, dataLen);
results[i] = data;
off += dataLen;
}
return results;
}
public static void CheckWriteResponse(byte[] tpktFrame)
{
int baseOff = 6;
if (tpktFrame[baseOff + 1] != FUNC_WRITE)
throw new S7Exception("非Write响应");
// 跳过 S7头(10) + reserved(2) + item(1) + return(1)
byte retCode = tpktFrame[baseOff + 10 + 2 + 1];
if (retCode != 0xFF)
throw new S7Exception($"PLC拒绝写入: 返回码 0x{retCode:X2}");
}
// ==================== 工具 ====================
private static byte[] WrapTPKT(byte[] payload, int len)
{
byte[] tpkt = new byte[len + 4];
tpkt[0] = 3;
tpkt[1] = 0;
tpkt[2] = (byte)((len + 4) >> 8);
tpkt[3] = (byte)((len + 4) & 0xFF);
Buffer.BlockCopy(payload, 0, tpkt, 4, len);
return tpkt;
}
// ===== BigEndian 数据转换 =====
public static bool GetBool(byte[] buf, int byteOff, int bitOff)
=> ((buf[byteOff] >> bitOff) & 0x01) != 0;
public static short GetInt(byte[] buf, int off)
=> (short)((buf[off] << 8) | buf[off + 1]);
public static ushort GetUInt(byte[] buf, int off)
=> (ushort)((buf[off] << 8) | buf[off + 1]);
public static int GetDInt(byte[] buf, int off)
=> (buf[off] << 24) | (buf[off + 1] << 16) | (buf[off + 2] << 8) | buf[off + 3];
public static uint GetUDInt(byte[] buf, int off)
=> (uint)((buf[off] << 24) | (buf[off + 1] << 16) | (buf[off + 2] << 8) | buf[off + 3]);
public static float GetReal(byte[] buf, int off)
{
byte[] be = new byte[4] { buf[off], buf[off + 1], buf[off + 2], buf[off + 3] };
if (BitConverter.IsLittleEndian) Array.Reverse(be);
return BitConverter.ToSingle(be, 0);
}
public static double GetDWordToDouble(byte[] buf, int off)
{
uint val = GetUDInt(buf, off);
return val; // 如需真正的DWord→Double转换需按IEEE754展开,这里只返回raw
}
public static string GetString(byte[] buf, int off, int maxLen)
{
int len = Math.Min(buf[off], maxLen - 1);
return Encoding.ASCII.GetString(buf, off + 1, len);
}
public static void SetBool(byte[] buf, int byteOff, int bitOff, bool v)
{
if (v) buf[byteOff] |= (byte)(1 << bitOff);
else buf[byteOff] &= (byte)~(1 << bitOff);
}
public static void SetInt(byte[] buf, int off, short v)
{
buf[off] = (byte)((v >> 8) & 0xFF);
buf[off + 1] = (byte)(v & 0xFF);
}
public static void SetUInt(byte[] buf, int off, ushort v)
{
buf[off] = (byte)((v >> 8) & 0xFF);
buf[off + 1] = (byte)(v & 0xFF);
}
public static void SetDInt(byte[] buf, int off, int v)
{
buf[off] = (byte)((v >> 24) & 0xFF);
buf[off + 1] = (byte)((v >> 16) & 0xFF);
buf[off + 2] = (byte)((v >> 8) & 0xFF);
buf[off + 3] = (byte)(v & 0xFF);
}
public static void SetReal(byte[] buf, int off, float v)
{
byte[] raw = BitConverter.GetBytes(v);
if (BitConverter.IsLittleEndian) Array.Reverse(raw);
Buffer.BlockCopy(raw, 0, buf, off, 4);
}
}
}
4. 主客户端 API (S7Client.cs) ← ★ 你只用这一个类就够了
using System;
namespace SiemensS7Comm
{
public class S7Client : IDisposable
{
private IsoTcpTransport _transport;
private S7Config _cfg;
public S7Config Config => _cfg;
public bool Connected => _transport?.IsConnected ?? false;
public S7Client() { _cfg = new S7Config(); }
public S7Client(S7Config cfg) { _cfg = cfg; }
// ===================== 连接 / 断开 =====================
public void Connect()
{
if (_transport != null) Disconnect();
_transport = new IsoTcpTransport();
_transport.Connect(
_cfg.IpAddress,
_cfg.Port,
_cfg.RemoteTSAP,
_cfg.LocalTSAP,
_cfg.TimeoutMs
);
}
public void Disconnect()
{
_transport?.Disconnect();
_transport = null;
}
// ===================== 单区读取 =====================
public byte[] ReadRaw(S7Area area, int dbNum, int byteOff, int byteLen)
{
var req = S7Protocol.BuildReadRequest(new[]
{
new S7Item(area, dbNum, byteOff, byteLen)
});
byte[] resp = _transport.Transact(req);
var datas = S7Protocol.ParseReadResponse(resp, 1);
return datas[0];
}
// ===================== 多区批量读取 =====================
public byte[][] ReadMulti(params S7Item[] items)
{
var req = S7Protocol.BuildReadRequest(items);
byte[] resp = _transport.Transact(req);
return S7Protocol.ParseReadResponse(resp, items.Length);
}
// ===================== 单区写入 =====================
public void WriteRaw(S7Area area, int dbNum, int byteOff, byte[] data)
{
var req = S7Protocol.BuildWriteRequest(
new S7Item(area, dbNum, byteOff, data.Length),
data
);
byte[] resp = _transport.Transact(req);
S7Protocol.CheckWriteResponse(resp);
}
// ==========================================================
// 快捷读写(强类型 API —— 最常用)
// ==========================================================
// ---- Bool (I/Q/M/DBX) ----
public bool ReadBool(S7Area area, int dbNum, int byteOff, int bitOff)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 1);
return S7Protocol.GetBool(b, 0, bitOff);
}
public void WriteBool(S7Area area, int dbNum, int byteOff, int bitOff, bool v)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 1); // 先读保持其它位
S7Protocol.SetBool(b, 0, bitOff, v);
WriteRaw(area, dbNum, byteOff, b);
}
// ---- Int (MW / DBW) ----
public short ReadInt(S7Area area, int dbNum, int byteOff)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 2);
return S7Protocol.GetInt(b, 0);
}
public void WriteInt(S7Area area, int dbNum, int byteOff, short v)
{
byte[] b = new byte[2];
S7Protocol.SetInt(b, 0, v);
WriteRaw(area, dbNum, byteOff, b);
}
// ---- UInt ----
public ushort ReadUInt(S7Area area, int dbNum, int byteOff)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 2);
return S7Protocol.GetUInt(b, 0);
}
public void WriteUInt(S7Area area, int dbNum, int byteOff, ushort v)
{
byte[] b = new byte[2];
S7Protocol.SetUInt(b, 0, v);
WriteRaw(area, dbNum, byteOff, b);
}
// ---- DInt ---
public int ReadDInt(S7Area area, int dbNum, int byteOff)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 4);
return S7Protocol.GetDInt(b, 0);
}
public void WriteDInt(S7Area area, int dbNum, int byteOff, int v)
{
byte[] b = new byte[4];
S7Protocol.SetDInt(b, 0, v);
WriteRaw(area, dbNum, byteOff, b);
}
// ---- Real (Float) ----
public float ReadReal(S7Area area, int dbNum, int byteOff)
{
byte[] b = ReadRaw(area, dbNum, byteOff, 4);
return S7Protocol.GetReal(b, 0);
}
public void WriteReal(S7Area area, int dbNum, int byteOff, float v)
{
byte[] b = new byte[4];
S7Protocol.SetReal(b, 0, v);
WriteRaw(area, dbNum, byteOff, b);
}
// ---- String (S7标准STRING:开头两个字节=maxlen/curlen) ----
public string ReadString(S7Area area, int dbNum, int byteOff, int maxLen = 254)
{
// 读 maxLen+2 字节
byte[] b = ReadRaw(area, dbNum, byteOff, maxLen + 2);
return S7Protocol.GetString(b, 0, maxLen);
}
public void Dispose() => Disconnect();
}
}
5. 自定义异常 (S7Exception.cs)
using System;
namespace SiemensS7Comm
{
public class S7Exception : Exception
{
public S7Exception(string msg) : base(msg) { }
public S7Exception(string msg, Exception inner) : base(msg, inner) { }
}
}
三、使用示例
using System;
using SiemensS7Comm;
class Program
{
static void Main()
{
var plc = new S7Client(new S7Config
{
IpAddress = "192.168.2.10",
Port = 102
});
plc.Config.AutoSetByCpu(S7CpuType.S7200_SMART); // 200SMART
// 如果是 S7-300:AutoSetByCpu(S7CpuType.S7300);
// 如果是 S7-1200/1500:AutoSetByCpu(S7CpuType.S71200);
plc.Connect();
Console.WriteLine("PLC 已连接!");
// ===== 读 DB1.DBD0(Real) =====
float temp = plc.ReadReal(S7Area.DB, 1, 0);
Console.WriteLine($"DB1.DBD0 = {temp}");
// ===== 写 DB1.DBW4(Int) =====
plc.WriteInt(S7Area.DB, 1, 4, 12345);
// ===== 读 M区 Bool =====
bool sensor1 = plc.ReadBool(S7Area.MK, 0, 10, 0); // M10.0
Console.WriteLine($"M10.0 = {sensor1}");
// ===== 写 Q区线圈(如果PLC允许输出写) =====
// plc.WriteBool(S7Area.PA, 0, 0, 0, true); // Q0.0 = TRUE
// ===== 批量读(效率更高) =====
var datas = plc.ReadMulti(
new S7Item(S7Area.DB, 1, 0, 4), // DB1.DBB0 ~ DBB3
new S7Item(S7Area.MK, 0, 100, 2) // MB100~MB101
);
short v1 = S7Protocol.GetInt(datas[0], 0);
ushort m100 = S7Protocol.GetUInt(datas[1], 0);
Console.WriteLine($"DB1.DBW0={v1}, MW100={m100}");
plc.Disconnect();
}
}
参考代码 西门子PLC以太网通讯组件 www.youwenfan.com/contentcsv/111957.html
四、支持的 PLC & 注意事项
| PLC | Rack | Slot | 备注 |
|---|---|---|---|
| S7-200 SMART | 0 | 1 | 最常用,端口102直接通 |
| S7-300 (CPU315+) | 0 | 2 | PUT/GET 需在硬件配置里启用 |
| S7-400 | 0 | 3 | 同上 |
| S7-1200 / 1500 | 0 | 1 | 必须在 TIA Portal 中勾选「允许来自远程伙伴的PUT/GET通信」 |
如果 S7-1200/1500 连接报返回码异常,99% 是因为没开 PUT/GET:
设备组态 → CPU 属性 → 保护 & 安全 → 连接机制 → ☑ 允许PUT/GET
更多推荐

所有评论(0)