• c# 与modbus


    Modbus Slave从站模拟器

    从站模拟器使用
    在这里插入图片描述
    在这里插入图片描述

    NModbus4 库

    用的都是这个库
    在这里插入图片描述
    在这里插入图片描述

    using System.IO.Ports;//串口
    using Modbus.Device;
    
    • 1
    • 2

    串口通信

    通过使用虚拟串口进行仿真
    在这里插入图片描述

    using System.IO.Ports;//串口
    using Modbus.Device;
    
    • 1
    • 2
    public static SerialPort serialPort1;
    private static IModbusMaster master;//也可以后面通过var的方式做一个创建一个
    
    • 1
    • 2

    设置串口

    load

                try
                {
                    Updata_Serialport_Name(ComComboBox1);  //调用更新可用串口函数,comboBox1为 端口 组合框名字
                    BaudComboBox2.Items.Add(115200);
                    BaudComboBox2.Items.Add(9600);
                    BaudComboBox2.Text = "115200";
    
    
                    StopComboBox3.Items.Add(System.IO.Ports.StopBits.One);
                    StopComboBox3.Items.Add(System.IO.Ports.StopBits.OnePointFive);
                    StopComboBox3.Items.Add(System.IO.Ports.StopBits.Two);
                    StopComboBox3.Text = System.IO.Ports.StopBits.One.ToString();
    
                    DataComboBox4.Items.Add(8);
                    DataComboBox4.Text = "8";
    
                    CheckComboBox5.Items.Add(System.IO.Ports.Parity.None);//在表单中显示的就是enum的名字 而不是一串数
                    CheckComboBox5.Items.Add(System.IO.Ports.Parity.Odd);
                    CheckComboBox5.Items.Add(System.IO.Ports.Parity.Even);
                    CheckComboBox5.Text = System.IO.Ports.Parity.None.ToString();
    
                    CollectuiComboBox1.Items.Add("单次");
                    CollectuiComboBox1.Items.Add("连续");
                    CollectuiComboBox1.Text = "单次";
    
    
    
                }
                catch (Exception ex)
                {
    
                    MessageBox.Show("串口打开异常" + serialPort1.PortName);
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    Updata_Serialport_Name

            /*用户自定义更新可用串口函数*/
            private static void Updata_Serialport_Name(UIComboBox MycomboBox)
            {
                //System.IO.Ports.SerialPort.GetPortNames()函数功能为获取计算机所有可用串口,以字符串数组形式输出
                string[] ArryPort = System.IO.Ports.SerialPort.GetPortNames(); //定义字符串数组,数组名为 ArryPort,将可用的串口信息存放在字符串中
                MycomboBox.Items.Clear();       //清除当前组合框下拉菜单内容                  
                for (int i = 0; i < ArryPort.Length; i++)
                {
                    MycomboBox.Items.Add(ArryPort[i]);   //将所有的可用串口号添加到端口对应的组合框中
                }
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    打开串口

                try
                {
                    //在打开串口之前获取串口号和波特率
                    serialPort1.PortName = ComComboBox1.Text;
                    //转换为10进制,10可省略
                    serialPort1.BaudRate = Convert.ToInt32(BaudComboBox2.Text, 10);
    
                    Parity NIParity = (Parity)Enum.Parse(typeof(Parity), CheckComboBox5.Text);//字符串转枚举
                    serialPort1.Parity = NIParity;//这段再研究下 如何实现文本转enum中的某个值
    
                    //MessageBox.Show(StopComboBox3.Text);这个是none
                    //MessageBox.Show(StopComboBox3.SelectedText);这个打印出来是空白
                    StopBits NIStopBits = (StopBits)Enum.Parse(typeof(StopBits), StopComboBox3.Text);//字符串转枚举
                                                                                                     //serialPort1.StopBits = System.IO.Ports.StopBits.One;
                    serialPort1.StopBits = NIStopBits;
    
                    serialPort1.DataBits = Convert.ToInt32(DataComboBox4.Text, 10);
    
                    //serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort1_DataReceived);
    
                    serialPort1.ReceivedBytesThreshold = 1;
    
                    //打开串口
                    serialPort1.Open();
                    //打开串口按钮不可用,变成灰色
                    OpenuiButton2.Enabled = false;
                    //关闭串口按钮可用。
                    CloseuiButton3.Enabled = true;
    
                    master = ModbusSerialMaster.CreateRtu(serialPort1);//这里传入的就是我们创建的串口对象
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.ToString());
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    关闭串口

                try
                {
                    //关闭串口
                    serialPort1.Close();
                    //打开串口按钮可用
                    OpenuiButton2.Enabled = true;
                    //关闭串口按钮不可用,变成灰色
                    CloseuiButton3.Enabled = false;
                }
                catch
                {
                    //一般关闭串口不出错,此处对程序不进行处理
                }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    写数据

                //var master = ModbusSerialMaster.CreateRtu(serialPort1);//这里传入的就是我们创建的串口对象
                byte slaveid = byte.Parse(SlaveIDuiTextBox1.Text);//将int32转换为字节数组
                
                ushort value = 1;
                if (CollectuiComboBox1.Text == "单次")
                {               
                    value = 1;
                }
                else value = 2;
    
                //写寄存器值  这个函数就是对应 2号 功能码
                // 2号寄存器  
                master.WriteSingleRegister(slaveid, 2, value);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    读数据

                //将串口对象添加到modbus中
                //var master = ModbusSerialMaster.CreateRtu(serialPort1);//这里传入的就是我们创建的串口对象
    
                byte slaveid = byte.Parse(SlaveIDuiTextBox1.Text);//将int32转换为字节数组
                ushort[] value1 = master.ReadHoldingRegisters(slaveid, 0x0, 2);
                string value = value1[0].ToString() + value1[1].ToString();
                illuminanceuiTextBox1.Text = value;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    线圈状态

    相当于控制设备上的一些开关量
    读对应功能码01 写对应功能码05
    就是true或者false具体用到再详细了解 暂时也用不到
    在这里插入图片描述
    结果
    在这里插入图片描述

    在这里插入图片描述

    设置连续的读

    用到一个多线程,就能一直监控这数据了

                //设置连续的读 一直拿数据
                Task.Run(() => 
                { 
                    //需要加上while 这样就能一直在这个循环中了 不然执行一遍就结束
                    while(true)
                    {
                        //读寄存器值
                        //就是对应03功能码  slave地址1 访问10号寄存器 访问两个
                        ushort[] values = master.ReadHoldingRegisters(1, 10, 2);
                        //打印访问的结果 访问结果就是存放在values[0]和[1]中的
                        Console.WriteLine(values[0] + "" + values[1]);
                    }
    
                });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在这里插入图片描述

    网口通信

    在这里插入图片描述

    using System.Net.Sockets;
    
    • 1

    获取温度

            private void button1_Click_1(object sender, EventArgs e)
            {
                //点一下获取一下
                //1、创建连接TcpClient
                TcpClient tcpClient = new TcpClient();//需要 using System.Net.Sockets;
                tcpClient.Connect("127.0.0.1", 502);
               
                //2、创建主站访问对象 将创建的tcp客户端添加到modbus中
                Modbus.Device.ModbusIpMaster master = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);//需要system.IO.Port库 和NModbus4一样安装就行
                //3、读取寄存器数据 并写入NitextBox1中
                ushort[] values = master.ReadHoldingRegisters(1, 0, 1);//光标放在函数上面会显示要填的参数的含义。slaveID 0号寄存器 读1个 这个函数返回的是一个uchort数组
                NitextBox1.Text = values[0].ToString();//NitextBox1就是第一个textBox的name
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    写入转速

            private void button1_Click(object sender, EventArgs e) //点击查看所有引用的时候,就可以发现这些个点击函数也是在InitializeComponent()函数中被注册进去的
            {
                ushort value = ushort.Parse(speedtextBox.Text);// 取页面数据
    
                 //1、创建连接
                TcpClient tcpClient = new TcpClient();//需要 using System.Net.Sockets;
                tcpClient.Connect("127.0.0.1", 502);
                //2、创建主站访问对象
                Modbus.Device.ModbusIpMaster master = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);//需要system.IO.Port库 和NModbus4一样安装就行    
               
                master.WriteSingleRegister(1, 1, value);// 写入设备寄存器
    
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    持续不断的读

    这块是持续不断地读取 类似做监控这样

          
              
                            Control.CheckForIllegalCrossThreadCalls = false;//不检查非法的跨线程操作
    
                            //1、创建连接
                            TcpClient tcpClient = new TcpClient();//需要 using System.Net.Sockets;
                            tcpClient.Connect("127.0.0.1", 502);
                            //2、创建主站访问对象
                            Modbus.Device.ModbusIpMaster master = Modbus.Device.ModbusIpMaster.CreateIp(tcpClient);//需要system.IO.Port库 和NModbus4一样安装就行
    
                            //如果要持续读取的话,就要新开一个线程
                            Task.Run(async () => 
                            {
                                while (true )
                                {
                                    await Task.Delay(1000);
                                    //3、读取寄存器数据 并写入NitextBox1中
                                    ushort[] values = master.ReadHoldingRegisters(1, 0, 1);//光标放在函数上面会显示要填的参数的含义。slaveID 0号寄存器 读1个 这个函数返回的是一个uchort数组
                                    try //通过断点调试 发现了卡在了这里。通过try和catch的方式,就将出现的异常捕获了
                                    {
                                        NitextBox1.Text = values[0].ToString();//NitextBox1就是第一个textBox的name
                                    }
                                    catch (Exception ex) 
                                    { 
                                        MessageBox.Show(ex.Message);//错误的原因是进程间操作无效 使用一个简单的操作 就是关闭检测进程间的合法性
                                    }
    
                                }       
                            });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    c#接收flaot

    在这里插入图片描述
    接受到的是两个字节ushort

    //直接调用ReadDate函数,接收到的数据会自动保存入 ushort[] value1 中
    //一个ushort占用两个字节,一共接收到2个ushort,共四个字节
    //调用GetFloat即可实现转换
            public static void ReadDate()
            {
                    ushort startAddress = 0x0066;
                    ushort numberOfPoints = 2;
                    ushort[] value1 = master.ReadHoldingRegisters(1, startAddress, numberOfPoints);
                    log.SaveLog("value1字节数:"+value1.Length.ToString());
    
                    float valuefloat;
                    valuefloat = GetFloat(value1[0], value1[1]);
    
                    log.SaveLog("流量" + valuefloat);
                
            }
            
            public static float GetFloat(ushort P1, ushort P2)
            {
                int intSign, intSignRest, intExponent, intExponentRest;
                float faResult, faDigit;
                intSign = P1 / 32768;
                intSignRest = P1 % 32768;
                intExponent = intSignRest / 128;
                intExponentRest = intSignRest % 128;
                faDigit = (float)(intExponentRest * 65536 + P2) / 8388608;
                faResult = (float)Math.Pow(-1, intSign) * (float)Math.Pow(2, intExponent - 127) * (faDigit + 1);
                return faResult;
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    在这里插入图片描述

    try catch捕获异常

    在这里插入图片描述

    虚拟串口

    使用详解
    在这里插入图片描述
    在这里插入图片描述
    一个使用COM1一个使用COM2 就可以进行通信了
    在这里插入图片描述
    在这里插入图片描述

    参考资料

    视频 这个讲的都是串口的
    网口的视频讲解

  • 相关阅读:
    linux系统编程专题(七) 系统调用之进程
    [极致用户体验] 在微信大字号模式下,网页样式乱了怎么办?
    什么是魔法函数?
    第五篇 python 基本语法(一)
    在Go中如何实现并发
    管理会计学复习题集
    TVS的原理及选型
    关于事务的一点思考
    基于BP神经网络的PID控制,神经网络算法pid控制
    嵌入式系统开发【深入浅出】 IWDG 与 WWDG
  • 原文地址:https://blog.csdn.net/chengcao123/article/details/126171213