Modbus协议
Modbus协议是应用于电子控制器上的一种通用语言。通过此协议,可以实现控制器相互之间、控制器经由网络和真实设备之间的通信。
它是一种Master/Slave协议,它并不关心是谁与谁在通信。
Modbus特点
- 标准开放、公开发表、无版税要求、无许可证费(没有费用);
- 支持多种电气接口(RS232、RS422、RS485、RJ45);各种介质传输(双绞线、网线),支持串口、网口;
- 格式简单、紧凑、通俗易懂,容易上手(好用) ;
提前准备仿真工具
Modbus Slave 从机模拟软件,用于开发和调试;Modbus Poll 主机模拟软件;Configure Virtual Serial Port Driver 虚拟串口端口软件;
- 启动虚拟串口助手

- 添加端口

- 启动主从机

- 从机连接串口(先连接从机)

- 从机连接成功

- 主机连接串口

- 主机连接成功

通过c#连接
- 关闭仿真工具的主机连接,使用c#代码替代仿真工具的主机连接

SerialPort对象
在System.IO.Ports命名空间,表示串行端口资源。
new SerialPort(portName, baudRate, parity, dataBits, stopBits);SerialPort参数
BaudRate: 波特率,传输速率一般为9600
PortName: 端口名
Parity: 奇偶校验位
DataBits: 数据位,实际数据位,一般用8位
StopBits: 停止位
- 一般记住9600-N-8-1即可,9600波特率,N无检验位,8位数据位,1位停止位。
NModbus
- NModbus:是Modbus协议的C#实现。提供与Modbus从站兼容设备和应用程序的连接。支持ASCII、RTU、TCP和UDP协议。
- NModbus4: 一个github的开源实现类库,是NModbus的一个分支。
- 地址:https://github.com/NModbus4/NModbus4
相关读写方法: 每个方法都有各自的异步方法实现。
bool[] ReadCoils 读线圈 01
bool[]ReadInputs 读输入线圈 02
ushort[] ReadHoldingRegisters 读保持寄存器 03
ushort[] ReadInputRegisters 读输入寄存器 04
void WriteSingleCoil 写单线圈 05
void WriteSingleRegister 写单保存寄存器 06
void WriteMultipleCoils 写多线圈 15
void WriteMultipleRegisters 写多保持寄存器 16
ushort[] ReadWriteMultipleRegisters 读写多个保持寄存器 23NModbus4使用方式
- 将NModbus4类库导入项目中(可直接在Nuget中搜索到并安装到当前项目中)
- 引用命名空间
- 开始使用
串口通信
1)创建SerialPort对象,配置相关参数,打开串口 2)调用相应方法创建Modbus主站对象(RTU、ASCII) 3) 配置参数 4)调用相关方法执行操作网口通信
1) 定义IP地址、端口号,创建TcpClient/UdpClient 2) 建立与远程的连接 3)创建Modbus 主站对象(IP) 4)配置相关参数 5)调用相关方法执行操作打开连接
// 串口
SerialPort port = null;
// 串口通信主站
ModbusSerialMaster serialMaster = null;
// 网口通信主站
ModbusIpMaster ipMaster = null;
// 串口通信主站和网口通信主站都实现了的接口
IModbusMaster master = null;
bool isRTU= true;
// 1. 串口
port = new SerialPort("COM2", 9600, Parity.None, 8, StopBits.One);//创建串口
if(port != null) {
port.Open();
// RTU主站
if (isRTU)
serialMaster = ModbusSerialMaster.CreateRtu(port);
else
// Ascii
serialMaster = ModbusSerialMaster.CreateAscii(port);
master = serialMaster;
}
// 2. TCP网口
TcpClient tcpClient = new TcpClient();
tcpClient.Connect(192.168.1.100, 8000);//连接到主机
ipMaster = ModbusIpMaster.CreateIp(tcpClient);//Ip 主站
master = ipMaster;
// 3. UDP
UdpClient udpClient = new UdpClient();
udpClient.Connect(192.168.1.100, 8000);//连接到主机
ipMaster = ModbusIpMaster.CreateIp(udpClient);//Ip 主站
master = ipMaster;关闭连接
if(port!=null&&port.IsOpen) port.Close();//关闭串口连接
// client是上面的tcp或udp连接
else if(client!=null &&client.Connected)
{
client.Close();
}
master.Dispose();
lblMessage.Text = "关闭成功!";数据
寄存器: 存储数据的容器,寄存器有多个,每个寄存器有编号,用寄存器地址区分不同的寄存器;
| 存储区 | 对象类型 | 寄存器地址 | 访问类型(针对程序) | 说明 |
|---|---|---|---|---|
| 离散量输入线圈 | 单个bit | 10001至19999 | 只读 | I/O系统提供这种类型数据 |
| 线圈 | 单个bit | 00001至09999 | 读写 | 通过应用程序改变这种类型数据 |
| 输入寄存器 | 16-位字 | 30001至39999 | 只读 | I/O系统提供这种类型数据 |
| 保持寄存器 | 16-位字 | 40001至49999 | 读写 | 通过应用程序改变这种类型数据 |
功能码:向服务器指示将执行哪种操作:位、字、双字;

- 仿真工具主站功能设置,一一对应
在菜单栏setUp -- Read Write Definition打开:

注意:主从站读写时,线圈寄存器应一一对应,线圈对应线圈,寄存器对应寄存器,否则无法读写;
数据读取

在从站对应的读取

private void btnRead_Click(object sender, EventArgs e)
{
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);
// 将读取到的blVals元素,true改为1,false改为0,再转成字符串以","连接
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;
}
}数据写入
- 写单线圈

private void btnWrite_Click(object sender, EventArgs e)
{
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;
}
}- 写单保持寄存器

- 写多线圈

Ценники на вывоз мусора сейчас космос, любые лайфхаки по экономии в тему.
1xbet onlayn kazino platforması ilə vaxtınızı daha səmərəli və maraqlı keçirin. Saytın canlı kazino bölməsi istifadəçilərə fərqli bir dünya təklif edir. Burada canlı dilerli kazino oyunlarının hər bir növünü sınaqdan keçirə bilərsiniz. Krupyelərin peşəkarlığı oyunun dinamikasını və marağını artırır. Müntəzəm olaraq yenilənən oyun siyahısı cansıxıcılığa yol vermir. Hesabınızı artırmaq üçün onlarla fərqli və etibarlı üsul mövcuddur. Qazanılan pullar cəmi bir neçə dəqiqə ərzində şəxsi hesabınıza köçürülür. Aktiv fəaliyyətinizə görə fərdi bonuslar və hədiyyələr qazana bilərsiniz. Dəstək xidməti həm çat, həm də e-poçt vasitəsilə kömək göstərir. 1xbet ailəsinə qoşulun və böyük uduşlar üçün ilk addımı atın. onlayn kazino
我一直梦想, 看到你们相册那样的地方。真棒。
我喜欢, 这里分享真实经验。你的项目 就是 属于这里的。请继续。
我热爱这样的想法, 去那么多国家。真的很鼓舞。
能感受到热爱。继续保持 感受。
我关注这样的资源, 写得很实在。你的网站 就是 属于这里的。加油。
1
1
1