NModbus4通信库使用

1.创建主站

  串口 RTU 、ASCII
  网口 TCP、  UDP
2.读寄存器:保持寄存器、线圈、输入线圈、输入寄存器
3.写寄存器:写入单线圈、单保持寄存器;写多线圈、多保持寄存器

 

NModbus4读取的都是无符号整数

如何读取浮点数?

直接存储浮点数会占用两个寄存器,4个字节,比较占空间

保持寄存器,一个寄存器是两个字节

为了节省空间

把浮点数按整数存进去,只占一个寄存器,读出来再转成浮点数

先乘以10的倍数,再除10的倍数

比如保留2位小数,先*100,再/100

 

TCP 对应TCPClient客户端连接 与socket是什么关系?

如果只是为了数据传输,传输层的用TCPClient就可以,它是在socket基础之上进行的一个封装 

TCP是可信连接,更多的使用

UDP是基于数据报的不可靠的传输,只管传,不管错误率的问题

 

 

1、添加NModbus4包

 

一、程序创建3中主站 方式

1、创建串口对象实例: 端口号、波特率、校验位,数据位,停止位

port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);//创建串口

  创建主站,两种通讯方式RTU和Ascii

                    //串口主站
                    if (isRTU)
                        serialMaster = ModbusSerialMaster.CreateRtu(port);
                    else
                        serialMaster = ModbusSerialMaster.CreateAscii(port);

2、TCP主站

TcpClient tcpClient = new TcpClient();
tcpClient.Connect(ip, port);//连接到主机
ipMaster = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站

3、UDP主站

                UdpClient udpClient = new UdpClient();
                udpClient.Connect(ip, port);//连接到主机
                ipMaster = ModbusIpMaster.CreateIp(udpClient);//Ip 主站

 

二、工具测试TCP主从站通讯

TCPIP主站

 从站

 

三、程序替代工具主站实现通讯

关闭modubus Poll工具

连接程序

 

 

 

 

 

输入线圈和输入寄存器是只读的

离散量输入,bit读取,按位,一个位就是一个线圈状态值

输入寄存器  16-位字,两个字节,一个寄存器就是两个字节

一个字节可以存8个位

 

 创建一个线圈,可以读写

 

 

 读写线圈可以是单个也可以多个

 

 

四、读操作

 

 

1、bool[] ReadCoils   读线圈   01

输入型一般是按一个按钮的操作

读取参数:从站地址、开始地址、数量(寄存器最多2000个)

 

2、bool[]ReadInputs   读输入线圈   02

只读线圈,不能改

一般是设备的启停状态、继电器的状态

 

3、ushort[] ReadHoldingRegisters 读保持寄存器 03

 

 

4、ushort[] ReadInputRegisters 读输入寄存器 04

输入的都是只读的

 

 

五、写操作

 

 

 

 

1、void WriteSingleCoil 写单线圈 05

2、void WriteSingleRegister 写单保存寄存器 06

3、void WriteMultipleCoils 写多线圈 15

4、void WriteMultipleRegisters 写多保持寄存器 16

 

 

5、ushort[] ReadWriteMultipleRegisters 读写多个保持寄存器 23

 

 

六、注意:

报错1:0x01(Illegal Function):非法功能。这表示从站设备不支持主站设备请求的功能码。

 一个是输入线圈01,一个是输入寄存器03

主从站的寄存器地址不匹配,F=要相等

修改主站读取方式为01,与从站相同

从站地址也必须相同

 

 

 

 

7、读写冲突处理

使用计时器处理,读有个状态标识,写也有个状态标识

写之前,要先把读的线程给停止掉,false

不控制好就会卡死

不基于UI的,要基于线程或服务的

 

 

完整代码

 

using Modbus.Device;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO.Ports;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

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

        SerialPort port = null;
        ModbusSerialMaster serialMaster = null;//串口主站
        ModbusIpMaster ipMaster = null;//网口主站
        IModbusMaster master = null;//主站接口
        TcpClient client = null;
        private void Form1_Load(object sender, EventArgs e)
        {
            //串口对象   9600-N-8-1

            cboConnWays.SelectedIndex = 0;//默认串口
            //初始化串口设置
            {
                string[] ports = SerialPort.GetPortNames();//当前计算机串口数组
                cboPorts.Items.AddRange(ports);//初始化端口列表
                cboPorts.SelectedIndex = 0;
                //初始化波特率
                CommUtility.LoadCboBaudRates(cboBaudRates);
                //数据位
                CommUtility.LoadCboDataBits(cboDataBits);

                //校验位
                CommUtility.LoadCboParitys(cboPrarity);
                //停止位
                CommUtility.LoadStopBits(cboStopBIts);
                rbtnRTU.Checked = true;

                //Modbus服务器初始化
                //txtIPAddress.Text = CommUtility.GetLocalIP();
                txtIPAddress.Text = "192.168.50.116";
                txtPort.Text = "502";
                rbtnIPv4.Checked = true;
            }
        }

        private void btnOpen_Click(object sender, EventArgs e)
        {
            //创建主站
            if (cboConnWays.SelectedIndex == 0)//串口
            {
                //校验位
                Parity parity = CommUtility.GetParity(cboPrarity);
                //停止位
                StopBits stopBits = cboStopBIts.Text == "1" ? StopBits.One : StopBits.Two;
                //端口
                string portName = cboPorts.Text.Trim();
                //波特率
                int baudRate = int.Parse(cboBaudRates.Text);
                //数据位
                int dataBits = int.Parse(cboDataBits.Text);
                bool isRTU = true;
                if (rbtnASCII.Checked)
                    isRTU = false;
                port = new SerialPort(portName, baudRate, parity, dataBits, stopBits);//创建串口
                if (port != null)
                {
                    port.Open();
                    //主站
                    if (isRTU)
                        serialMaster = ModbusSerialMaster.CreateRtu(port);
                    else
                        serialMaster = ModbusSerialMaster.CreateAscii(port);
                    master = serialMaster;
                }
            }
            //TCP
            else if (cboConnWays.SelectedIndex == 1)
            {
                string ip = txtIPAddress.Text.Trim();
                int port = int.Parse(txtPort.Text.Trim());
                TcpClient tcpClient = new TcpClient();
                tcpClient.Connect(ip, port);//连接到主机
                ipMaster = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
                master = ipMaster;
            }
            //UDP
            else if (cboConnWays.SelectedIndex == 2)
            {
                string ip = txtIPAddress.Text.Trim();
                int port = int.Parse(txtPort.Text.Trim());
                UdpClient udpClient = new UdpClient();
                udpClient.Connect(ip, port);//连接到主机
                ipMaster = ModbusIpMaster.CreateIp(udpClient);//Ip 主站
                master = ipMaster;
            }
            lblMessage.Text = "连接成功!";
        }

        private void btnRead_Click(object sender, EventArgs e)
        {
            try
            {
                string readType = cboReadTypes.Text.Trim();
                //从站地址
                byte slaveAddr = byte.Parse(txtRSlaveId.Text.Trim());
                //开始地址
                ushort startAddr = ushort.Parse(txtRStartAddress.Text.Trim());
                //读取数量
                ushort readCount = ushort.Parse(txtCount.Text.Trim());
                switch (readType)
                {
                    case "读线圈":
                        bool[] blVals = master.ReadCoils(slaveAddr, startAddr, readCount);
                        txtReadDatas.Text = string.Join(",", blVals.Select(b => b ? "1" : "0"));
                        break;
                    case "读输入线圈":
                        bool[] blInputVals = master.ReadInputs(slaveAddr, startAddr, readCount);
                        txtReadDatas.Text = string.Join(",", blInputVals.Select(b => b ? "1" : "0"));
                        break;
                    case "读保持寄存器":
                        ushort[] uDatas = master.ReadHoldingRegisters(slaveAddr, startAddr, readCount);
                        txtReadDatas.Text = string.Join(",", uDatas);
                        break;
                    case "读输入寄存器":
                        ushort[] uDatas1 = master.ReadInputRegisters(slaveAddr, startAddr, readCount);
                        txtReadDatas.Text = string.Join(",", uDatas1);
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnWrite_Click(object sender, EventArgs e)
        {
            try
            {
                string writeType = cboWriteTypes.Text.Trim();
                string vals = txtRegisterWVals.Text.Trim();
                //从站地址
                byte slaveAddr = byte.Parse(txtWSlaveId.Text.Trim());
                //开始地址
                ushort startAddr = ushort.Parse(txtWStartAddress.Text.Trim());
                switch (writeType)
                {
                    case "写单线圈":
                        bool blVal = vals == "1" ? true : false;
                        master.WriteSingleCoil(slaveAddr, startAddr, blVal);
                        break;
                    case "写单保持寄存器":
                        ushort uVal01 = ushort.Parse(vals);
                        master.WriteSingleRegister(slaveAddr, startAddr, uVal01);
                        break;
                    case "写多线圈":
                        bool[] blVals = vals.Split(',').Select(s => s == "1" ? true : false).ToArray();//bool数组
                        master.WriteMultipleCoils(slaveAddr, startAddr, blVals);
                        break;
                    case "写多保持寄存器":
                        ushort[] uVals02 = vals.Split(',').Select(s => ushort.Parse(s)).ToArray();
                        master.WriteMultipleRegisters(slaveAddr, startAddr, uVals02);
                        break;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void btnClose_Click(object sender, EventArgs e)
        {
            if (port != null && port.IsOpen)
                port.Close();//关闭串口连接
            else if (client != null && client.Connected)
            {
                client.Close();
            }
            master.Dispose();
            lblMessage.Text = "连接已关闭!";
        }
    }
}
View Code

 

posted on 2025-03-15 17:43  张彦山  阅读(2563)  评论(0)    收藏  举报