通信协议_C#实现自定义ModbusRTU主站

背景知识:modbus协议介绍

相关工具

  • mbslave:充当从站。
  • 虚拟串口工具:虚拟出一对串口。
  • VS2022。

实现过程以及Demo

  1. 打开虚拟串口工具:
  2. 打开mbslave:
    从站设置
    在这里插入图片描述
    此处从站连接COM1口。

Demo实现

  1. 创建DLL库,创建ModbusRTU类,进行实现:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;
using System.Threading;

namespace modBusDLL
{
    public class ModBusRTU
    {

        private SerialPort serialPort = null;

        public ModBusRTU()
        {
            serialPort = new SerialPort();
        }

        //连接方法

        //端口号,波特率,数据位,校验位,停止位
        public void Connect(string portName,int baudRate=9600,int dataBits=8,
                            Parity parity=Parity.None,StopBits stopBits=StopBits.One)
        { 
        if (serialPort.IsOpen) {
            
                serialPort.Close();
            }
        serialPort.BaudRate = baudRate;
        serialPort.PortName = portName;
        serialPort.Parity = parity;
        serialPort.StopBits = stopBits;
        serialPort.DataBits = dataBits;

            serialPort.Open();

            
        }

        //断开连接方法
        public void Disconnect()
        {
            if (serialPort.IsOpen)
            {

                serialPort.Close();
            }
        }

        //读消息方法
        public byte[] ReadKeepRegisters(byte devAdd,ushort start,ushort length)
        { 
        //拼接报文,发送报文,接受报文,校验报文,解析报文

            //创建一个字节集合
            List<byte> ret = new List<byte>();

            //协议格式:站地址+功能码+起始寄存器地址+寄存器数量+CRC

            //站地址
            ret.Add(devAdd);

            //功能码
            ret.Add(0x03);
 

            //起始寄存器地址
            //高位地址
            ret.Add((byte)(start / 256));
            //低位地址
            ret.Add((byte)(start % 256));


            //寄存器数量
            //高位地址
            ret.Add((byte)(length / 256));
            //低位地址
            ret.Add((byte)(length % 256));





            byte[] crc= CRCCalc(ret.ToArray());
            ret.AddRange(crc);





            //发送报文
            serialPort.Write(ret.ToArray(), 0, ret.Count);

            Thread.Sleep(50);
            
            //接受长度
            int byteCount = serialPort.BytesToRead;

            byte[] data = new byte[byteCount];

            //读入data
            serialPort.Read(data, 0, data.Length);

            byte[] result = new byte[length*2]; 
        
            Array.Copy(data,3,result, 0,length*2);

            return result;
        
        }

        #region 16位CRC校验
        /// <summary>
        /// CRC校验,参数data为byte数组
        /// </summary>
        /// <param name="data">校验数据,字节数组</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(byte[] data)
        {
            //crc计算赋初始值
            int crc = 0xffff;
            for (int i = 0; i < data.Length; i++)
            {
                crc = crc ^ data[i];
                for (int j = 0; j < 8; j++)
                {
                    int temp;
                    temp = crc & 1;
                    crc = crc >> 1;
                    crc = crc & 0x7fff;
                    if (temp == 1)
                    {
                        crc = crc ^ 0xa001;
                    }
                    crc = crc & 0xffff;
                }
            }
            //CRC寄存器的高低位进行互换
            byte[] crc16 = new byte[2];
            //CRC寄存器的高8位变成低8位,
            crc16[1] = (byte)((crc >> 8) & 0xff);
            //CRC寄存器的低8位变成高8位
            crc16[0] = (byte)(crc & 0xff);
            return crc16;
        }

        /// <summary>
        /// CRC校验,参数为空格或逗号间隔的字符串
        /// </summary>
        /// <param name="data">校验数据,逗号或空格间隔的16进制字符串(带有0x或0X也可以),逗号与空格不能混用</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(string data)
        {
            //分隔符是空格还是逗号进行分类,并去除输入字符串中的多余空格
            IEnumerable<string> datac = data.Contains(",") ? data.Replace(" ", "").Replace("0x", "").Replace("0X", "").Trim().Split(',') : data.Replace("0x", "").Replace("0X", "").Split(' ').ToList().Where(u => u != "");
            List<byte> bytedata = new List<byte>();
            foreach (string str in datac)
            {
                bytedata.Add(byte.Parse(str, System.Globalization.NumberStyles.AllowHexSpecifier));
            }
            byte[] crcbuf = bytedata.ToArray();
            //crc计算赋初始值
            return CRCCalc(crcbuf);
        }


        /// <summary>
        ///  CRC校验,截取data中的一段进行CRC16校验
        /// </summary>
        /// <param name="data">校验数据,字节数组</param>
        /// <param name="offset">从头开始偏移几个byte</param>
        /// <param name="length">偏移后取几个字节byte</param>
        /// <returns>字节0是高8位,字节1是低8位</returns>
        public static byte[] CRCCalc(byte[] data, int offset, int length)
        {
            byte[] Tdata = data.Skip(offset).Take(length).ToArray();
            return CRCCalc(Tdata);
        }

        #endregion
    }
}

  1. 在窗体代码中进行调用:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using modBusDLL;

namespace easyProjectPractice
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();

            this.comboBox1.DataSource=SerialPort.GetPortNames();
        }

        private ModBusRTU modBusRTU = new ModBusRTU();



        private void label1_Click(object sender, EventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {

        }

        private void button1_Click(object sender, EventArgs e)
        {
            modBusRTU.Connect(this.comboBox1.Text);

            MessageBox.Show("连接成功");
        }

        private void button2_Click(object sender, EventArgs e)
        {
            modBusRTU.Disconnect();
            MessageBox.Show("断开连接");
        }

        private void button3_Click(object sender, EventArgs e)
        {

            byte[] data = modBusRTU.ReadKeepRegisters(1, 2, 1);

       

            textBox1.Text = (data[0] * 256 + data[1]).ToString();
        }

        private void textBox1_TextChanged(object sender, EventArgs e)
        {

        }
    }
}

  1. 最终可实现通过mdbusRTU协议进行主从站通信:
    在这里插入图片描述
    在这里插入图片描述

总结

简单的modbusRTU主从通信自定义。

接触过的所有通信协议Demo代码

相关推荐

  1. 定义Dubbo RPC通信协议

    2024-07-12 14:38:01       33 阅读
  2. C++定义list实现

    2024-07-12 14:38:01       23 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-12 14:38:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-12 14:38:01       71 阅读
  3. 在Django里面运行非项目文件

    2024-07-12 14:38:01       58 阅读
  4. Python语言-面向对象

    2024-07-12 14:38:01       69 阅读

热门阅读

  1. 深入解析补天平台:白帽黑客的奖金激励机制

    2024-07-12 14:38:01       21 阅读
  2. Vue数据更新页面不更新的问题

    2024-07-12 14:38:01       19 阅读
  3. Spring Boot有哪些优点和缺点

    2024-07-12 14:38:01       24 阅读
  4. Visual Studio 常用快捷键

    2024-07-12 14:38:01       24 阅读
  5. 【常用知识点-Linux】scp命令

    2024-07-12 14:38:01       21 阅读
  6. 用Redis写一个IP限流器

    2024-07-12 14:38:01       23 阅读
  7. 天童美语:推荐给孩子的人文历史纪录片

    2024-07-12 14:38:01       26 阅读