Modbus主机/从机模拟程序

Modbus poll 和Modbus slave是一组Modbus仿真软件,可以实现Modbus RTU、TCP、串口仿真等。
仿真软件网址:https://modbustools.com/download.html
在ModbusTCP中,Modbus poll 作为客户端请求数据,Modbus slave 作为服务器端处理请求。
使用c语言编写客户端连接Modbus slave时,注意数据格式,一条指令一次性发出,否则连接会出错。
使用软件时,需要指定功能码,在setup->slave definition或者poll definition中进行设置。
– slave ID:从站编号(事务标识符)
– function:功能码,0x01对应线圈操作,0x02对应离散量操作,0x03对应保持寄存器操作,0x04对应输入寄存器操作
– address:开始地址
– quantity:寄存器/线圈/离散量 的数量

 使用步骤:

1、打开Modbus Slave进行连接,串口(Modbus RTU或Modbus ASCII协议)参考图1, TCP/IP协议参考图2

2、设置从站编号、功能码,根据需要调试的功能码修改

 

TCP/IP协议开发

public partial class Form2 : Form
{
    Socket client;
    int len = 100; 

    public Form2()
    {
        InitializeComponent();

        #region
        //01 读线圈状态
        //txtSend.Text = "00 01 00 00 00 06 01 01 00 00 00 0F";
        //len = 9 + 2; // 9+ceil(数量/8)
        //00 01 00 00 00 05 01 01 02 69 73

        //02 读输入离散量
        //txtSend.Text = "00 01 00 00 00 06 01 02 00 00 00 12";
        //len = 9 + 3; // 9+ceil(数量/8)
        //00 01 00 00 00 06 01 02 03 69 73 03 

        //03 读保持寄存器
        //txtSend.Text = "00 01 00 00 00 06 01 03 00 00 00 03";
        //len = 9+6; // 9+数量*2
        //00 01 00 00 00 09 01 03 06 00 10 00 19 7F FF

        //04 读输入寄存器
        //txtSend.Text = "00 01 00 00 00 06 01 04 00 00 00 03";
        //len = 9+6; // 9+数量*2
        //00 01 00 00 00 09 01 04 06 00 10 00 19 7F FF

        //05 写单个线圈
        //txtSend.Text = "00 01 00 00 00 06 01 05 00 03 FF 00";
        //len = 12; 
        //00 01 00 00 00 06 01 05 00 03 FF 00

        //06 写单个保持寄存器
        //txtSend.Text = "00 01 00 00 00 06 01 06 00 00 00 0A";
        //len = 12; 
        //00 01 00 00 00 06 01 06 00 00 00 0A

        //0F 写多个线圈
        //txtSend.Text = "00 01 00 00 00 09 01 0F 00 00 00 0A 02 13 01";
        //len = 12; 
        //00 01 00 00 00 06 01 0F 00 00 00 0A

        //10 写多个保持寄存器
        //txtSend.Text = "00 01 00 00 00 0B 01 10 00 00 00 02 04 00 0F 10 10";
        //len = 12; 
        //00 01 00 00 00 06 01 10 00 00 00 02
        #endregion

    }

    private void btnSend_Click(object sender, EventArgs e)
    {
        txtReceive.Text = "";
        client = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        client.Connect("127.0.0.1", 502);

        byte[] buffer = Hex2Byte(txtSend.Text);
        client.Send(buffer);

        buffer = new byte[len];
        client.Receive(buffer);
        this.Invoke(() =>
        {
            txtReceive.Text = Byte2Hex(buffer);
        });

        client.Close();
    }


    public String Byte2Hex(byte[] bytes)
    {
        return BitConverter.ToString(bytes).Replace('-', ' ').Trim();
    }

    public byte[] Hex2Byte(String hex)
    {
        string[] temp = hex.Split(' ');
        byte[] b = new byte[temp.Length + 2];

        for (int i = 0; i < temp.Length; i++)
        {
            b[i] = Convert.ToByte(temp[i], 16);
        }

        return b;
    }

 

 RTU协议开发

    public partial class Form3 : Form
    {

        private SerialPort spCOM1;

        public Form3()
        {
            InitializeComponent();

            spCOM1 = new SerialPort();
            spCOM1.DataReceived += SpCOM1_DataReceived;
        }

        private void SpCOM1_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] buffer = new byte[spCOM1.BytesToRead];
            spCOM1.Read(buffer, 0, buffer.Length);
            string data = ToHex(buffer);
            this.Invoke(() =>
            {
                txtReceive.Text += data + "\r\n";
            });
        }

        private void btnSend_Click(object sender, EventArgs e)
        {
            if (!spCOM1.IsOpen)
            {
                spCOM1.PortName = "COM1";
                spCOM1.BaudRate = 9600;
                spCOM1.DataBits = 8;
                spCOM1.StopBits = StopBits.One;
                spCOM1.Parity = Parity.None;
                spCOM1.Open();
            }
            else
            {
                string crc = CRCCheck(txtSend.Text);
                byte[] buffer = ToBytes(txtSend.Text + " " + crc);
                spCOM1.Write(buffer, 0, buffer.Length);
            }

        }

        /// <summary>
        /// CRC校验码
        /// </summary>
        /// <param name="val"></param>
        /// <returns></returns>
        public string CRCCheck(string val)
        {
            val = val.TrimEnd(' ');
            string[] spva = val.Split(' ');
            byte[] bufData = new byte[spva.Length + 2];
            bufData = ToBytesCRC(val);
            ushort CRC = 0xffff;
            ushort POLYNOMIAL = 0xa001;
            for (int i = 0; i < bufData.Length - 2; i++)
            {
                CRC ^= bufData[i];
                for (int j = 0; j < 8; j++)
                {
                    if ((CRC & 0x0001) != 0)
                    {
                        CRC >>= 1;
                        CRC ^= POLYNOMIAL;
                    }
                    else
                    {
                        CRC >>= 1;
                    }
                }
            }
            return ToHex(System.BitConverter.GetBytes(CRC));
        }


        public static byte[] ToBytesCRC(string hex)
        {
            string[] temp = hex.Split(' ');
            byte[] b = new byte[temp.Length + 2];

            for (int i = 0; i < temp.Length; i++)
            {
                b[i] = Convert.ToByte(temp[i], 16);
            }

            return b;
        }

        public static String ToHex(byte[] vars)
        {
            return BitConverter.ToString(vars).Replace('-', ' ').Trim();
        }

        public static byte[] ToBytes(string hex)
        {
            string[] temp = hex.Split(' ');
            byte[] b = new byte[temp.Length];

            for (int i = 0; i < temp.Length; i++)
            {
                if (temp[i].Length > 0)
                    b[i] = Convert.ToByte(temp[i], 16);
            }

            return b;
        }


    }

 

posted on 2023-09-27 15:17  学而时习  阅读(341)  评论(0编辑  收藏  举报