델타 PLC 와의 Modbus 통신을 위한 C # 기반 호스트 인스턴스

Paid ¥8.99Paid ¥8.99

该内容已接入现有会员/付费系统,解锁后可查看完整资料或下载附件。

查看解锁方式
델타 PLC 와의 Modbus 통신을 위한 C # 기반 호스트 인스턴스

台达(Delta)PLC是国内工业自动化领域应用最广泛的PLC品牌之一,支持Modbus RTU和Modbus TCP通讯协议。C#作为Windows平台上位机开发的主流语言,与台达PLC的Modbus通讯是工业自动化项目中的常见需求。本文提供完整的C#上位机与台达PLC Modbus通信实例,包括连接管理、寄存器读写、数据解析、断线重连、多设备管理以及Windows Forms界面示例。

一、台达PLC Modbus通讯概述

1.1 台达PLC支持的Modbus기능

기능 코드기능台达PLC对应区域
0x01读线圈M(辅助继电器)、Y(输出继电器)
0x02read discrete inputsX(输入继电器)
0x03read holding registersD(数据寄存器)
0x05write single coilM、Y
0x06write single registerD
0x0Fwrite multiple coilsM、Y
0x10write multiple registersD

1.2 台达PLC地址映射

台达PLC的Modbus地址与内部软元件的对应关系(以DVP시리즈为例):

软元件Modbus주소(decimal)说明
D0-D99990-9999数据寄存器,保持寄存器
M0-M99990-9999辅助继电器,线圈
Y0-Y2550-255(偏移2048)输出继电器,线圈
X0-X2550-255(偏移2048)输入继电器,离散输入
S0-S9990-999(偏移4096)状态继电器,线圈
T0-T255触点1024-1279定时器触点状态,线圈(기능 코드01/05)
C0-C255触点1536-1791计数器触点状态,线圈(기능 코드01/05)
T/Ccurrent value视型号而定部分型号支持通过D寄存器映射读取,需查阅对应手册

1.3 通讯参数设置

  • Modbus TCP:PLC IP주소 + 端口502,从站地址默认1
  • Modbus RTU(COM1):9600bps,8数据位,无校验,1停止位(默认)
  • Modbus RTU(COM2):9600bps,8数据位,无校验,1停止位(默认)
  • slave 주소:可在PLC中设置,默认1
  • 通讯协议:需在PLC编程软件中设置为Modbus RTU Slave或Modbus TCP

二、C# Modbus通讯类实现

2.1 Modbus TCP客户端类

using System;
using System.Net.Sockets;
using System.Threading;

namespace DeltaPlcModbus
{
    public class ModbusTcpClient : IDisposable
    {
        private TcpClient _client;
        private NetworkStream _stream;
        private ushort _transactionId = 0;
        private readonly object _lock = new object();

        public string Ip주소 { get; }
        public int Port { get; }
        public byte SlaveId { get; set; } = 1;
        public int Timeout { get; set; } = 3000;
        public bool IsConnected => _client?.Connected ?? false;

        public ModbusTcpClient(string ip, int port = 502)
        {
            Ip주소 = ip;
            Port = port;
        }

        /// <summary>
        /// connectPLC(带重试)
        /// </summary>
        public bool Connect(int retryCount = 3)
        {
            for (int i = 0; i < retryCount; i++)
            {
                try
                {
                    _client = new TcpClient();
                    _client.ReceiveTimeout = Timeout;
                    _client.SendTimeout = Timeout;
                    _client.Connect(Ip주소, Port);
                    _stream = _client.GetStream();
                    Console.WriteLine($"已连接到PLC:{Ip주소}:{Port}");
                    return true;
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"连接失败(No.{i + 1}次):{ex.Message}");
                    Thread.Sleep(1000);
                }
            }
            return false;
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        public void Disconnect()
        {
            _stream?.닫기();
            _client?.닫기();
            _client = null;
            _stream = null;
        }

        /// <summary>
        /// read holding registers(기능 코드03)- readDregister
        /// </summary>
        public ushort[] ReadHolding회원가입s(ushort 시작Addr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x03, 시작Addr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x03)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                ushort[] result = new ushort[byteCount / 2];
                for (int i = 0; i < result.Length; i++)
                {
                    result[i] = (ushort)((response[9 + i * 2] << 8) | response[9 + i * 2 + 1]);
                }
                return result;
            }
        }

        /// <summary>
        /// 读线圈(기능 코드01)- readM/YRelay
        /// </summary>
        public bool[] ReadCoils(ushort 시작Addr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x01, 시작Addr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x01)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                bool[] result = new bool[count];
                for (int i = 0; i < count; i++)
                {
                    result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
                }
                return result;
            }
        }

        /// <summary>
        /// read discrete inputs(기능 코드02)- readX输入
        /// </summary>
        public bool[] ReadDiscreteInputs(ushort 시작Addr, ushort count)
        {
            lock (_lock)
            {
                byte[] request = BuildReadRequest(0x02, 시작Addr, count);
                byte[] response = SendAndReceive(request);

                if (response[7] != 0x02)
                    throw new ModbusException(response[7], response[8]);

                byte byteCount = response[8];
                bool[] result = new bool[count];
                for (int i = 0; i < count; i++)
                {
                    result[i] = (response[9 + i / 8] & (1 << (i % 8))) != 0;
                }
                return result;
            }
        }

        /// <summary>
        /// write single register(기능 코드06)- 写Dregister
        /// </summary>
        public void WriteSingle회원가입(ushort addr, ushort value)
        {
            lock (_lock)
            {
                byte[] request = new byte[12];
                BuildMBAPHeader(request, 6);
                request[7] = 0x06;
                request[8] = (byte)(addr >> 8);
                request[9] = (byte)(addr & 0xFF);
                request[10] = (byte)(value >> 8);
                request[11] = (byte)(value & 0xFF);

                byte[] response = SendAndReceive(request);
                if (response[7] != 0x06)
                    throw new ModbusException(response[7], response[8]);
            }
        }

        /// <summary>
        /// write single coil(기능 코드05)- 写M/YRelay
        /// </summary>
        public void WriteSingleCoil(ushort addr, bool value)
        {
            lock (_lock)
            {
                byte[] request = new byte[12];
                BuildMBAPHeader(request, 6);
                request[7] = 0x05;
                request[8] = (byte)(addr >> 8);
                request[9] = (byte)(addr & 0xFF);
                request[10] = (byte)(value ? 0xFF : 0x00);
                request[11] = 0x00;

                byte[] response = SendAndReceive(request);
                if (response[7] != 0x05)
                    throw new Mod bus Ex ception (res ponse [7 ], response [8]);
            }
        }

        /// >
        /// 쓰 기 다 중 레 지 스터 (기 능 코드 16) - D 레 지 스터 를 대 량 쓰 기
        /// >
        public vo id Write M ulti ple Reg ister s (ush ort 시작 Add r , ush ort [ ] values)
        {
            잠 금 (_ lock)
            {
                by te by te Co unt = (byte) (valu es . Leng th * 2) ;
                by te [ ] request = new by te [ 13 + by te Co unt ];
                Bu ild M BA P He ader (re quest , (ush ort) ( 7 + by te Co unt));
                요청 [7] = 0 x 10 ;
                request[8] = (byte)(시작Addr 
                request[9] = (byte)(시작Addr 
                request[10] = (byte)(values.Length 
                request[11] = (byte)(values.Length 
                request [11] = by te Co unt ;

                for (int i = 0; i 
                {
                    request[13 + i * 2] = (byte)(values[i] 
                    request[13 + i * 2 + 1] = (byte)(values[i] 
                }

                by te [ ] response = Send And Re ce ive (re quest);
                if (res ponse [7] ! =) 0 x 10)
                    throw new Mod bus Ex ception (res ponse [7 ], response [8]);
            }
        }

        /// >
        / / / 여러 코 일 쓰 기 (기 능 코드 15) - M / Y 릴 레이 대 량 쓰 기
        /// >
        public vo id Write M ulti ple Co ils (ush ort 시작 Add r , bool [ ] values)
        {
            잠 금 (_ lock)
            {
                by te by te Co unt = (byte) (( values . Leng th + 7) / 8);
                by te [ ] request = new by te [ 13 + by te Co unt ];
                Bu ild M BA P He ader (re quest , (ush ort) ( 7 + by te Co unt));
                요청 [7] = 0 x 0 F ;
                request[8] = (byte)(시작Addr 
                request[9] = (byte)(시작Addr 
                request[10] = (byte)(values.Length 
                request[11] = (byte)(values.Length 
                request [11] = by te Co unt ;
                
                for (int i = 0; i 
                {
                    if (val ue [ i ])
                        요청 [ 13 + i / 8 ]|= (바 이트) ( 1 (i % 8));<<
                }
                
                by te [ ] response = Send And Re ce ive (re quest);
                if (res ponse [7] ! =) 0 x 0 F)
                    throw new Mod bus Ex ception (res ponse [7 ], response [8]);
            }
        }
        
        읽기 요청 만들기
        private by te [ ] Bu ild Read Re quest (byte fuk C ode , ush ort 시작 Add r , ush ort count)
        {
            by te [ ] request = new by te [12] ;
            Bu ild M BA P He ader (re quest , 6) ;
            request [7] = function C ode ;
            request[8] = (byte)(시작Addr 
            request[9] = (byte)(시작Addr 
            request[10] = (byte)(count 
            request[11] = (byte)(count 
            반환 요청 ;
        }

        / / M BA P 헤 더 생성
        private vo id Bu ild M BA P He ader (byte [ ] bu ffer , ush ort length)
        {
            _ tr ansa ction Id ++ ;
            buffer[0] = (byte)(_transactionId 
            buffer[1] = (byte)(_transactionId 
            버 퍼 [2] = 0 x 00 ;
            bu ffer [3] = 0 x 00 ;
            buffer[4] = (byte)(length 
            buffer[5] = (byte)(length 
            bu ffer [6] = Sla ve Id ;
        }

        보내 기 및 수신 하기
        private by te [ ] Send And Re ce ive (byte [ ] request)
        {
            if (_ stream == null )||  !_클 라이언 트 . 접속 됨)
                throw new In valid O per ation Ex ception (" P LC 에 연결 되지 않 음 ")

            _ stream . Write (re quest , 0, request . Leng th);
            _ stream . fl ush () ;

            by te [ ] bu ffer = new by te [2 60 ];
            int by tes Read = _ stream . Read (bu ffer , 0, bu ffer . Leng th);

            by te [ ] response = new by te [ byt es Read ];
            Ar ray . 복사 (bu ffer , response , by tes Read);
            응답 을 반환 합니다 ;
        }

        public vo id Dis pose ()
        {
            연결 끊 기 ();
        }
    }

    public 클래 스 ModbusException : Exception
    {
        public byte 기능Code { get; }
        public byte ExceptionCode { get; }

        public ModbusException(byte funcCode, byte exceptionCode)
            : base($"Modbus异常:기능 코드=0x{funcCode:X2},Exception Code=0x{exceptionCode:X2}")
        {
            기능Code = funcCode;
            ExceptionCode = exceptionCode;
        }
    }
}

2.2 台达PLC专用操作类

using System;

namespace DeltaPlcModbus
{
    /// <summary>
    /// 台达PLC专用操作类,封装D/M/X/Y软元件的读写
    /// </summary>
    public class DeltaPlcClient
    {
        private readonly ModbusTcpClient _modbus;

        public DeltaPlcClient(string ip, int port = 502, byte slaveId = 1)
        {
            _modbus = new ModbusTcpClient(ip, port);
            _modbus.SlaveId = slaveId;
        }

        public bool Connect() => _modbus.Connect();
        public void Disconnect() => _modbus.Disconnect();
        public bool IsConnected => _modbus.IsConnected;

        /// <summary>
        /// readDregister value
        /// </summary>
        public ushort ReadD(int 주소)
        {
            return _modbus.ReadHolding회원가입s((ushort)주소, 1)[0];
        }

        /// <summary>
        /// 批量读取Dregister
        /// </summary>
        public ushort[] ReadD(int 시작주소, int count)
        {
            return _modbus.ReadHolding회원가입s((ushort)시작주소, (ushort)count);
        }

        /// <summary>
        /// 写入Dregister value
        /// </summary>
        public void WriteD(int 주소, ushort value)
        {
            _modbus.WriteSingle회원가입((ushort)주소, value);
        }

        /// <summary>
        /// 批量写入Dregister
        /// </summary>
        public void WriteD(int 시작주소, ushort[] values)
        {
            _modbus.WriteMultiple회원가입s((ushort)시작주소, values);
        }

        /// <summary>
        /// read32位整数(占用2个Dregister)
        /// </summary>
        public int ReadD32(int 시작주소)
        {
            ushort[] regs = _modbus.ReadHolding회원가입s((ushort)시작주소, 2);
            return (regs[0] << 16) | regs[1];
        }

        /// <summary>
        /// read32位浮点数(占用2个Dregister,ABCD大端序)
        /// </summary>
        public float ReadDFloat(int 시작주소)
        {
            ushort[] regs = _modbus.ReadHolding회원가입s((ushort)시작주소, 2);
            // ABCD大端序:高字在前,高字节在前
            uint bits = ((uint)regs[0] << 16) | regs[1];
            byte[] bytes = BitConverter.GetBytes(bits);
            // BitConverter.GetBytes返回的是小端序数组,需要反转
            if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }
        
        /// <summary>
        /// read32位浮点数(CDAB字交换序)
        /// </summary>
        public float ReadDFloatCDAB(int 시작주소)
        {
            ushort[] regs = _modbus.ReadHolding회원가입s((ushort)시작주소, 2);
            // CDAB:低字在前,高字节在前
            uint bits = ((uint)regs[1] << 16) | regs[0];
            byte[] bytes = BitConverter.GetBytes(bits);
            if (BitConverter.IsLittleEndian) Array.Reverse(bytes);
            return BitConverter.ToSingle(bytes, 0);
        }

        /// <summary>
        /// readM辅助继电器状态
        /// </summary>
        public bool ReadM(int 주소)
        {
            return _modbus.ReadCoils((ushort)주소, 1)[0];
        }

        /// <summary>
        /// 批量读取M辅助继电器
        /// </summary>
        public bool[] ReadM(int 시작주소, int count)
        {
            return _modbus.ReadCoils((ushort)시작주소, (ushort)count);
        }

        /// <summary>
        /// 写入M辅助继电器
        /// </summary>
        public void WriteM(int 주소, bool value)
        {
            _modbus.WriteSingleCoil((ushort)주소, value);
        }

        /// <summary>
        /// readX输入继电器状态
        /// 注意:X输入在台达PLC中Modbus地址偏移为2048
        /// </summary>
        public bool ReadX(int 주소)
        {
            return _modbus.ReadDiscreteInputs((ushort)(주소 + 2048), 1)[0];
        }

        /// <summary>
        /// readY输出继电器状态
        /// 注意:Y输出在台达PLC中Modbus地址偏移为2048
        /// </summary>
        public bool ReadY(int 주소)
        {
            return _modbus.ReadCoils((ushort)(주소 + 2048), 1)[0];
        }

        /// <summary>
        /// 写入Y输出继电器
        /// </summary>
        public void WriteY(int 주소, bool value)
        {
            _modbus.WriteSingleCoil((ushort)(주소 + 2048), value);
        }

        /// <summary>
        /// 读取定时器当前值(Tregister,偏移1024)
        /// </summary>
        public ushort ReadT(int 주소)
        {
            return _modbus.ReadHolding회원가입s((ushort)(주소 + 1024), 1)[0];
        }

        /// <summary>
        /// 读取计数器当前值(Cregister,偏移1536)
        /// </summary>
        public ushort ReadC(int 주소)
        {
            return _modbus.ReadHolding회원가입s((ushort)(주소 + 1536), 1)[0];
        }
    }
}

三、完整使用示例

using System;
using System.Threading;

namespace DeltaPlcModbus
{
    class Program
    {
        static void Main(string[] args)
        {
            // 创建台达PLC客户端
            var plc = new DeltaPlcClient("192.168.1.10", 502, slaveId: 1);

            try
            {
                // connectPLC
                if (!plc.Connect())
                {
                    Console.WriteLine("无法连接到PLC");
                    return;
                }
                Console.WriteLine("PLC连接成功!\n");

                // 1. readDregister
                Console.WriteLine("=== readDregister ===");
                ushort d0 = plc.ReadD(0);
                ushort d1 = plc.ReadD(1);
                ushort d2 = plc.ReadD(2);
                Console.WriteLine($"D0 = {d0}");
                Console.WriteLine($"D1 = {d1}");
                Console.WriteLine($"D2 = {d2}");

                // 批量读取
                ushort[] dValues = plc.ReadD(0, 10);
                Console.WriteLine("\nD0-D9:");
                for (int i = 0; i < dValues.Length; i++)
                {
                    Console.WriteLine($"  D{i} = {dValues[i]}");
                }

                // 2. 写入Dregister
                Console.WriteLine("\n=== 写入Dregister ===");
                plc.WriteD(10, 1234);
                Console.WriteLine("D10 写入 1234");
                ushort check = plc.ReadD(10);
                Console.WriteLine($"D10 回读 = {check}");

                // 批量写入
                plc.WriteD(20, new ushort[] { 100, 200, 300, 400, 500 });
                Console.WriteLine("D20-D24 批量写入完成");

                // 3. read32位数据
                Console.WriteLine("\n=== read32位数据 ===");
                int d32 = plc.ReadD32(0);
                Console.WriteLine($"D0-D1(32位整数)= {d32}");

                // 4. readMRelay
                Console.WriteLine("\n=== readMRelay ===");
                bool m0 = plc.ReadM(0);
                bool m1 = plc.ReadM(1);
                Console.WriteLine($"M0 = {(m0 ? "ON" : "OFF")}");
                Console.WriteLine($"M1 = {(m1 ? "ON" : "OFF")}");

                // 批量读取
                bool[] mValues = plc.ReadM(0, 8);
                Console.WriteLine("\nM0-M7:");
                for (int i = 0; i < mValues.Length; i++)
                {
                    Console.WriteLine($"  M{i} = {(mValues[i] ? "ON" : "OFF")}");
                }

                // 5. 写入MRelay
                Console.WriteLine("\n=== 写入MRelay ===");
                plc.WriteM(0, true);
                Console.WriteLine("M0 置为 ON");
                Thread.Sleep(500);
                plc.WriteM(0, false);
                Console.WriteLine("M0 置为 OFF");

                // 6. readX输入
                Console.WriteLine("\n=== readX输入 ===");
                bool x0 = plc.ReadX(0);
                bool x1 = plc.ReadX(1);
                Console.WriteLine($"X0 = {(x0 ? "ON" : "OFF")}");
                Console.WriteLine($"X1 = {(x1 ? "ON" : "OFF")}");

                // 7. readY输出
                Console.WriteLine("\n=== readY输出 ===");
                bool y0 = plc.ReadY(0);
                Console.WriteLine($"Y0 = {(y0 ? "ON" : "OFF")}");

                // 8. 控制Y输出
                Console.WriteLine("\n=== 控制Y输出 ===");
                plc.WriteY(0, true);
                Console.WriteLine("Y0 置为 ON");
                Thread.Sleep(1000);
                plc.WriteY(0, false);
                Console.WriteLine("Y0 置为 OFF");

                // 9. 读取定时器/计数器触点状态
                Console.WriteLine("\n=== 读取定时器/计数器触点 ===");
                bool t0 = plc.ReadT연락처(0);
                bool c0 = plc.ReadC연락처(0);
                Console.WriteLine($"T0 触点 = {(t0 ? "ON" : "OFF")}");
                Console.WriteLine($"C0 触点 = {(c0 ? "ON" : "OFF")}");
                Console.WriteLine("注:定时器/计数器当前值请查阅对应型号手册的Modbus映射");

                // 10. 持续监控(10秒)
                Console.WriteLine("\n=== 持续监控D0-D3(10秒)===");
                for (int i = 0; i < 10; i++)
                {
                    ushort[] monitor = plc.ReadD(0, 4);
                    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] D0={monitor[0]}, D1={monitor[1]}, D2={monitor[2]}, D3={monitor[3]}");
                    Thread.Sleep(1000);
                }
            }
            catch (ModbusException ex)
            {
                Console.WriteLine($"Modbus异常:{ex.Message}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"系统异常:{ex.Message}");
            }
            finally
            {
                plc.Disconnect();
                Console.WriteLine("\nPLC连接已关闭");
            }

            Console.WriteLine("\n按任意键退出...");
            Console.ReadKey();
        }
    }
}

四、Windows Forms上位机界面示例

using System;
using System.Windows.Forms;
using System.Threading;

namespace DeltaPlcModbus
{
    public partial class MainForm : Form
    {
        private DeltaPlcClient _plc;
        private Thread _monitorThread;
        private volatile bool _monitoring = false;

        public MainForm()
        {
            InitializeComponent();
        }

        private void btnConnect_Click(object sender, EventArgs e)
        {
            try
            {
                _plc = new DeltaPlcClient(txtIp.Text, int.Parse(txtPort.Text), byte.Parse(txtSlaveId.Text));
                if (_plc.Connect())
                {
                    lblStatus.Text = "Connected";
                    lblStatus.ForeColor = System.Drawing.Color.Green;
                    btnConnect.Enabled = false;
                    btnDisconnect.Enabled = true;
                    _monitoring = true;
                    _monitorThread = new Thread(MonitorLoop);
                    _monitorThread.IsBackground = true;
                    _monitorThread.Start();
                }
                else
                {
                    MessageBox.Show("连接失败!");
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show($"连接异常:{ex.Message}");
            }
        }

        private void btnDisconnect_Click(object sender, EventArgs e)
        {
            _monitoring = false;
            _plc?.Disconnect();
            lblStatus.Text = "Not connected";
            lblStatus.ForeColor = System.Drawing.Color.Red;
            btnConnect.Enabled = true;
            btnDisconnect.Enabled = false;
        }

        private void MonitorLoop()
        {
            while (_monitoring && _plc.IsConnected)
            {
                try
                {
                    // readD0-D9
                    ushort[] values = _plc.ReadD(0, 10);
                    // readM0-M7
                    bool[] mValues = _plc.ReadM(0, 8);

                    // 更新UI(跨线程调用)
                    this.Invoke(new Action(() =>
                    {
                        txtD0.Text = values[0].ToString();
                        txtD1.Text = values[1].ToString();
                        txtD2.Text = values[2].ToString();
                        txtD3.Text = values[3].ToString();
                        lblM0.Text = mValues[0] ? "ON" : "OFF";
                        lblM1.Text = mValues[1] ? "ON" : "OFF";
                        lblM2.Text = mValues[2] ? "ON" : "OFF";
                        lblM3.Text = mValues[3] ? "ON" : "OFF";
                    }));
                }
                catch
                {
                    // Read failed,忽略
                }
                Thread.Sleep(500);
            }
        }

        private void btnWriteD_Click(object sender, EventArgs e)
        {
            try
            {
                int addr = int.Parse(txtWriteAddr.Text);
                ushort value = ushort.Parse(txtWriteValue.Text);
                _plc.WriteD(addr, value);
                MessageBox.Show($"D{addr} 写入 {value} 成功");
            }
            catch (Exception ex)
            {
                MessageBox.Show($"写入失败:{ex.Message}");
            }
        }

        private void btnM0On_Click(object sender, EventArgs e)
        {
            _plc.WriteM(0, true);
        }

        private void btnM0Off_Click(object sender, EventArgs e)
        {
            _plc.WriteM(0, false);
        }

        protected override void OnFormClosing(FormClosingEventArgs e)
        {
            _monitoring = false;
            _plc?.Disconnect();
            base.OnFormClosing(e);
        }
    }
}

五、常见问题与排查

5.1 连接失败

  • 检查IP주소:ConfirmPLC的IP地址设置正确,与电脑在同一网段
  • 检查矶钓:台达PLC Modbus TCP默认矶钓502,确认未被防火墙拦截
  • 检查网线:确认网线连接正常,PLC网口指示灯亮
  • 检查PLC设置:在ISPSoft中确认PLC已启用Modbus TCP通讯
  • Ping测试:在命令行中ping PLC的IP주소,确认网络连通

5.2 读取数据为0或异常

  • 地址错误:ConfirmModbus地址与PLC软元件地址的对应关系,注意偏移量
  • 从站地址错误:ConfirmSlaveId与PLC中设置的一致
  • 功能码不支持:部分台达PLC型号可能不支持某些功能码
  • 地址越界:确认读取的地址范围在PLC支持的范围内
  • PLC未运行:ConfirmPLC处于RUN模式,STOP模式下部分地址不可读

5.3 写入不生效

  • 寄存器被程序覆盖:PLC程序中可能正在写入该地址,导致写入被覆盖
  • 地址只读:部分寄存器(如X输入)是只读的,不能写入
  • 权限限制:部分PLC需要设置通讯写入权限
  • 数据类型错误:确认写入的数据类型与PLC中定义的一致

六、最佳实践

  • 批量读取:尽量一次读取多个连续寄存器,减少通讯次数,提高效率
  • 合理轮询间隔:建议轮询间隔不未来100ms,避免过快轮询导致PLC通讯过载
  • 断线重连:添加自动重连机制,网络恢复后自动恢复通讯
  • 超时设置:合理设置通讯超时,建议2-3秒
  • 异常处理:捕获ModbusException,区分不同异常码进行处理
  • 线程安全:多线程访问时使用lock保护,避免并发冲突
  • 日志记录:记录通讯日志,便于排查问题
  • 地址规划:提前规划D/M寄存器的使用,避免地址冲突

本文提供的C#代码实现了台达PLC的完整Modbus TCP通讯功能,包括Dregister、MRelay、X输入、Y输出、定时器、计数器的读写操作,以及Windows Forms上位机界面示例。代码可直接用于工业自动化项目开发,建议先使用台达ISPSoft软件监控PLC데이터,确认通讯正常后再集成到实际项目中。

Put this resource to use in a real project?

Go to the Tool Center for message parsing, CRC 검증 and device debugging, or submit your requirements for selection and integration advice.

Engineer Membership

Turn this article into actionable debugging resources

After activation, you can use advanced message parsing, resource pack downloads, code examples, engineering cases and priority technical support, suitable for real project delivery.

Unlimited Advanced Tools
Resource & Code Packs
Complete Engineering Case Library
Priority Technical Support

Leave a Reply

Your email 주소 will not be published. Required fields are marked *.