C# Modbus TCP/IP学习记录

Modbus协议中,角色分为主站(Mater)、从站(Slave);数据类型分为线圈(Coil)、离散输入(Input)、保持寄存器(HoldingRegister)、输入寄存器(InputRegister)。

主站作为请求发起方,负责发送命令给一个或多个从站;从站作为请求响应方,负责接收主站命令并执行响应的操作。

线圈为可读写布尔值,离散输入为只读布尔值,保持寄存器为可读写16位比特数值,输入寄存器为只读16为比特数值。

实操步骤:

1.使用Visual Studio创建C# Framework 控制台应用程序,作为TCP Client(客户端)、Modbus Master(主站)。

2.使用Visual Studio安装Nuget包NModbus:

3.下载Modbus Slave应用程序,作为TCP Server(服务端)、Modbus Slave(从站)。

下载地址:Download

4.打开Modbus Slave,单击工具栏"New",创建四个Slave:

5.创建的Slave默认内容为10行,Slave ID为1,数据类型为保持寄存器,如需修改使用工具栏Slave Defintion。

运用SlaveDefintion把Mbslave1.mbs的Function改为01 Coil Status(0x),为线圈;

把Mbslave2.mbs的Function改为02 Input Status(1x),为离散输入;

把Mbslave3.mbs的Function改为03 Holding Register(4x),为保持寄存器;

把Mbslave3.mbs的Function改为04 Input Register(4x),为输入寄存器。

6.使用工具栏Connection项,单击Connect,弹窗Connection Setup,修改Connection下拉框为Modbus TCP/IP,下列配置按实际需求修改:

7.回到Visual Studio的控制台应用程序,输入以下代码,代码含有4种数据类型的读写(离散输入和输入寄存器为只读,所以只有读)方法,按需修改:

cs 复制代码
using System;
using System.Diagnostics.SymbolStore;
using System.Net.Sockets;
using NModbus;
using NModbus.Message;

class ModbusTcpClient
{
    private static string ipAddress = "127.0.0.1"; // 替换为实际 IP 地址
    private static int port = 502; // Modbus TCP 默认端口号
    private static byte slaveId = 1; // 从设备 ID

    static void Main(string[] args)
    {
        try
        {
            using (TcpClient client = new TcpClient(ipAddress, port))
            {
                // 创建 Modbus TCP 主站
                var factory = new ModbusFactory();
                IModbusMaster master = factory.CreateMaster(client);

                HoldingRegisterTest(master);
                InputRegisterTest(master);
                ColiTest(master);
                InputTest(master);



            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"发生错误: {ex.Message}");
        }
        Console.ReadLine();
    }

    private static void HoldingRegisterTest(IModbusMaster master)
    {
        //保持寄存器是可读写的16位字(16位bits)
        ReadHoldingRegisters(master, slaveId, 0, 10);//从地址0寄存器起,读10个保持寄存器数值
        WriteSingleRegister(master, slaveId, 0, 21);//往地址0寄存器写入值21
        ReadSingleHoldingReister(master, slaveId, 0);//读取地址0寄存器数值
        WriteHoldingRegister(master, slaveId, 0, new ushort[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 });//从地址0寄存器起,按数组顺序写入10个保持寄存器数值
        ReadHoldingRegisters(master, slaveId, 0, 10);
    }

    private static void InputRegisterTest(IModbusMaster master)
    {
        //输入寄存器是只读的16位字(16位bits)
        ReadInputRegisters(master, slaveId, 0, 10);
        ReadSingleInputRegister(master, slaveId, 0);
    }

    private static void ColiTest(IModbusMaster master)
    {
        //线圈是可读写的布尔值
        ReadCoils(master, slaveId, 0, 10);//从地址0寄存器起,读10个数值线圈
        WriteSingleCoils(master, slaveId, 0, true);//往地址0寄存器写入值21
        ReadCoils(master, slaveId, 0);//读取地址0寄存器数值
        WriteCoils(master, slaveId, 0, new bool[] { true, false, true, false, true, false, true, false, true, false});//从地址0寄存器起,按数组顺序写入10个线圈数值
        ReadCoils(master, slaveId, 0, 10);
    }

    private static void InputTest(IModbusMaster master)
    {
        //离散输入是只读的布尔值
        ReadInputs(master, slaveId, 0, 10);
        ReadSingleInput(master, slaveId, 0);
    }

    #region 保持寄存器
    public static ushort[] ReadHoldingRegisters(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
    {
        ushort[] readValues = master.ReadHoldingRegisters(slaveId, startAddress, numRegisters);
        Console.WriteLine("读取到的保持寄存器数据:");
        for (int i = 0; i < readValues.Length; i++)
        {
            Console.WriteLine($"保持寄存器 {startAddress + i}: {readValues[i]}");
        }
        return readValues;
    }
    public static ushort ReadSingleHoldingReister(IModbusMaster master, byte slaveId, ushort startAddress)
    {
        ushort[] readValues = ReadHoldingRegisters(master, slaveId, startAddress, 1);//没有读单一保持寄存器的方法
        return readValues[0];
    }
    public static void WriteHoldingRegister(IModbusMaster master, byte slaveId, ushort startAddress, ushort[] value)
    {
        master.WriteMultipleRegisters(slaveId, startAddress, value);
        Console.WriteLine($"写入的保持寄存器数据:");
        for (int i = 0; i < value.Length; i++)
        {
            Console.WriteLine($"保持寄存器 {startAddress + i}: {value[i]}");
        }
    }
    public static void WriteSingleRegister(IModbusMaster master, byte slaveId, ushort address, ushort value)
    {
        master.WriteSingleRegister(slaveId, address, value);
        Console.WriteLine($"写入的保持寄存器数据:");
        Console.WriteLine($"保持寄存器 {address + 1}: {value}");
    }
    #endregion

    #region 输入寄存器
    public static ushort[] ReadInputRegisters(IModbusMaster master ,byte slaveId, ushort startAddress, ushort numRegisters)
    {
        ushort[] readValues = master.ReadInputRegisters (slaveId, startAddress, numRegisters);

        Console.WriteLine("读取到的输入寄存器数据:");
        for (int i = 0; i < readValues.Length; i++)
        {
            Console.WriteLine($"输入寄存器 {startAddress + i}: {readValues[i]}");
        }
        return readValues;
    }
    public static ushort ReadSingleInputRegister(IModbusMaster master, byte slaveId, ushort startAddress)
    {
        ushort[] readValues = ReadInputRegisters(master, slaveId, startAddress, 1);//没有读单一输入寄存器的方法
        return readValues[0];
    }
    #endregion

    #region 线圈
    public static bool[] ReadCoils(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
    {
        bool[] readValues = master.ReadCoils(slaveId, startAddress, numRegisters);
        Console.WriteLine("读取到的线圈数据:");
        for (int i = 0; i < readValues.Length; i++)
        {
            Console.WriteLine($"线圈 {startAddress + i}: {readValues[i]}");
        }
        return readValues;
    }
    public static bool ReadCoils(IModbusMaster master, byte slaveId, ushort startAddress)
    {
        bool[] readValues = ReadCoils(master, slaveId, startAddress, 1);//没有读单一保持寄存器的方法
        return readValues[0];
    }
    public static void WriteCoils(IModbusMaster master, byte slaveId, ushort startAddress, bool[] value)
    {
        master.WriteMultipleCoils(slaveId, startAddress, value);
        Console.WriteLine($"写入的线圈数据:");
        for (int i = 0; i < value.Length; i++)
        {
            Console.WriteLine($"线圈 {startAddress + i}: {value[i]}");
        }
    }
    public static void WriteSingleCoils(IModbusMaster master, byte slaveId, ushort address, bool value)
    {
        master.WriteSingleCoil(slaveId, address, value);
        Console.WriteLine($"写入的线圈数据:");
        Console.WriteLine($"线圈{address + 1}: {value}");
    }
    #endregion

    #region 离散输入
    public static bool[] ReadInputs(IModbusMaster master, byte slaveId, ushort startAddress, ushort numRegisters)
    {
         bool[] readValues = master.ReadInputs(slaveId, startAddress, numRegisters);

        Console.WriteLine("读取到的离散输入数据:");
        for (int i = 0; i < readValues.Length; i++)
        {
            Console.WriteLine($"离散输入 {startAddress + i}: {readValues[i]}");
        }
        return readValues;
    }
    public static bool ReadSingleInput(IModbusMaster master, byte slaveId, ushort startAddress)
    {
        bool[] readValues = ReadInputs(master, slaveId, startAddress, 1);//没有读单一离散输入的方法
        return readValues[0];
    }
    #endregion

}

8.执行结果:

相关推荐
Lester_110113 分钟前
嵌入式学习笔记 - freeRTOS xTaskResumeAll( )函数解析
笔记·stm32·单片机·学习·freertos
jackson凌14 分钟前
【Java学习笔记】Math方法
java·笔记·学习
Humbunklung19 分钟前
PySide6 GUI 学习笔记——常用类及控件使用方法(多行文本控件QTextEdit)
笔记·python·学习·pyqt
sealaugh3232 分钟前
aws(学习笔记第四十三课) s3_sns_sqs_lambda_chain
笔记·学习·aws
西北大程序猿1 小时前
单例模式与锁(死锁)
linux·开发语言·c++·单例模式
阿超爱嵌入式1 小时前
STM32学习之I2C(理论篇)
stm32·嵌入式硬件·学习
你不是我我1 小时前
【Java开发日记】说一说 SpringBoot 中 CommandLineRunner
java·开发语言·spring boot
心扬1 小时前
python网络编程
开发语言·网络·python·tcp/ip
qq_454175791 小时前
c++学习-this指针
开发语言·c++·学习
恰薯条的屑海鸥1 小时前
零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)
网络·学习·安全·web安全·渗透测试·csrf·网络安全学习