C#ModBus Tcp的学习及Master的实现
程序员文章站
2022-05-03 10:39:26
Modbus Rtu的实现与Modbus Tcp的实现类似 C#ModBus Tcp的学习及Master的实现 我们还是需要借用一个开源库NModbus4,在vs中.打开NuGet管理器.安装NModbus4 具体实现,具体实现与之前的Modbus Tcp的实现类似 ,只是在实例化master时将T ......
modbus rtu的实现与modbus tcp的实现类似 c#modbus tcp的学习及master的实现
我们还是需要借用一个开源库nmodbus4,在vs中.打开nuget管理器.安装nmodbus4
具体实现,具体实现与之前的modbus tcp的实现类似 ,只是在实例化master时将tcpclient换为串行端口资源serialport,并在实例化是设置好端口所需参数(端口名,波特率,校验位,停止位,数据位)
1 using system; 2 using system.collections.generic; 3 using system.componentmodel; 4 using system.data; 5 using system.drawing; 6 using system.linq; 7 using system.text; 8 using system.threading.tasks; 9 using system.windows.forms; 10 using modbus.device; 11 using system.net.sockets; 12 using system.threading; 13 using system.io.ports; 14 15 namespace modbusrtu 16 { 17 public partial class form1 : form 18 { 19 private static imodbusmaster master; 20 private static serialport port; 21 //写线圈或写寄存器数组 22 private bool[] coilsbuffer; 23 private ushort[] registerbuffer; 24 //功能码 25 private string functioncode; 26 //参数(分别为站号,起始地址,长度) 27 private byte slaveaddress; 28 private ushort startaddress; 29 private ushort numberofpoints; 30 //串口参数 31 private string portname; 32 private int baudrate; 33 private parity parity; 34 private int databits; 35 private stopbits stopbits; 36 37 public form1() 38 { 39 initializecomponent(); 40 } 41 private void form1_load(object sender, eventargs e) 42 { 43 cmb_portname.selectedindex = 0; 44 cmb_baud.selectedindex = 5; 45 cmb_parity.selectedindex = 2; 46 cmb_databbits.selectedindex = 1; 47 cmb_stopbits.selectedindex = 0; 48 } 49 private serialport initserialportparameter() 50 { 51 if (cmb_portname.selectedindex < 0 || cmb_baud.selectedindex < 0 || cmb_parity.selectedindex < 0 || cmb_databbits.selectedindex < 0 || cmb_stopbits.selectedindex < 0) 52 { 53 messagebox.show("请选择串口参数"); 54 return null; 55 } 56 else 57 { 58 59 portname = cmb_portname.selecteditem.tostring(); 60 baudrate = int.parse(cmb_baud.selecteditem.tostring()); 61 switch (cmb_parity.selecteditem.tostring()) 62 { 63 case "奇": 64 parity = parity.odd; 65 break; 66 case "偶": 67 parity = parity.even; 68 break; 69 case "无": 70 parity = parity.none; 71 break; 72 default: 73 break; 74 } 75 databits = int.parse(cmb_databbits.selecteditem.tostring()); 76 switch (cmb_stopbits.selecteditem.tostring()) 77 { 78 case "1": 79 stopbits = stopbits.one; 80 break; 81 case "2": 82 stopbits = stopbits.two; 83 break; 84 default: 85 break; 86 } 87 port = new serialport(portname, baudrate, parity, databits, stopbits); 88 return port; 89 } 90 } 91 /// <summary> 92 /// 读/写 93 /// </summary> 94 /// <param name="sender"></param> 95 /// <param name="e"></param> 96 private void button1_click(object sender, eventargs e) 97 { 98 try 99 { 100 //初始化串口参数 101 initserialportparameter(); 102 103 master = modbusserialmaster.creatertu(port); 104 105 executefunction(); 106 } 107 catch (exception) 108 { 109 messagebox.show("初始化异常"); 110 } 111 } 112 113 private async void executefunction() 114 { 115 try 116 { 117 //每次操作是要开启串口 操作完成后需要关闭串口 118 //目的是为了slave更换连接是不报错 119 if (port.isopen == false) 120 { 121 port.open(); 122 } 123 if (functioncode != null) 124 { 125 switch (functioncode) 126 { 127 case "01 read coils"://读取单个线圈 128 setreadparameters(); 129 coilsbuffer = master.readcoils(slaveaddress, startaddress, numberofpoints); 130 131 for (int i = 0; i < coilsbuffer.length; i++) 132 { 133 setmsg(coilsbuffer[i] + " "); 134 } 135 setmsg("\r\n"); 136 break; 137 case "02 read discrete inputs"://读取输入线圈/离散量线圈 138 setreadparameters(); 139 140 coilsbuffer = master.readinputs(slaveaddress, startaddress, numberofpoints); 141 for (int i = 0; i < coilsbuffer.length; i++) 142 { 143 setmsg(coilsbuffer[i] + " "); 144 } 145 setmsg("\r\n"); 146 break; 147 case "03 read holding registers"://读取保持寄存器 148 setreadparameters(); 149 registerbuffer = master.readholdingregisters(slaveaddress, startaddress, numberofpoints); 150 for (int i = 0; i < registerbuffer.length; i++) 151 { 152 setmsg(registerbuffer[i] + " "); 153 } 154 setmsg("\r\n"); 155 break; 156 case "04 read input registers"://读取输入寄存器 157 setreadparameters(); 158 registerbuffer = master.readinputregisters(slaveaddress, startaddress, numberofpoints); 159 for (int i = 0; i < registerbuffer.length; i++) 160 { 161 setmsg(registerbuffer[i] + " "); 162 } 163 setmsg("\r\n"); 164 break; 165 case "05 write single coil"://写单个线圈 166 setwriteparametes(); 167 await master.writesinglecoilasync(slaveaddress, startaddress, coilsbuffer[0]); 168 break; 169 case "06 write single registers"://写单个输入线圈/离散量线圈 170 setwriteparametes(); 171 await master.writesingleregisterasync(slaveaddress, startaddress, registerbuffer[0]); 172 break; 173 case "0f write multiple coils"://写一组线圈 174 setwriteparametes(); 175 await master.writemultiplecoilsasync(slaveaddress, startaddress, coilsbuffer); 176 break; 177 case "10 write multiple registers"://写一组保持寄存器 178 setwriteparametes(); 179 await master.writemultipleregistersasync(slaveaddress, startaddress, registerbuffer); 180 break; 181 default: 182 break; 183 } 184 185 } 186 else 187 { 188 messagebox.show("请选择功能码!"); 189 } 190 port.close(); 191 } 192 catch (exception ex) 193 { 194 195 messagebox.show(ex.message); 196 } 197 } 198 private void combobox1_selectedindexchanged(object sender, eventargs e) 199 { 200 if (combobox1.selectedindex >= 4) 201 { 202 groupbox2.enabled = true; 203 groupbox1.enabled = false; 204 } 205 else 206 { 207 groupbox1.enabled = true; 208 groupbox2.enabled = false; 209 } 210 combobox1.invoke(new action(() => { functioncode = combobox1.selecteditem.tostring(); })); 211 } 212 213 /// <summary> 214 /// 初始化读参数 215 /// </summary> 216 private void setreadparameters() 217 { 218 if (txt_startaddr1.text == "" || txt_slave1.text == "" || txt_length.text == "") 219 { 220 messagebox.show("请填写读参数!"); 221 } 222 else 223 { 224 slaveaddress = byte.parse(txt_slave1.text); 225 startaddress = ushort.parse(txt_startaddr1.text); 226 numberofpoints = ushort.parse(txt_length.text); 227 } 228 } 229 /// <summary> 230 /// 初始化写参数 231 /// </summary> 232 private void setwriteparametes() 233 { 234 if (txt_startaddr2.text == "" || txt_slave2.text == "" || txt_data.text == "") 235 { 236 messagebox.show("请填写写参数!"); 237 } 238 else 239 { 240 slaveaddress = byte.parse(txt_slave2.text); 241 startaddress = ushort.parse(txt_startaddr2.text); 242 //判断是否写线圈 243 if (combobox1.selectedindex == 4 || combobox1.selectedindex == 6) 244 { 245 string[] strarr = txt_data.text.split(' '); 246 coilsbuffer = new bool[strarr.length]; 247 //转化为bool数组 248 for (int i = 0; i < strarr.length; i++) 249 { 250 // strarr[i] == "0" ? coilsbuffer[i] = true : coilsbuffer[i] = false; 251 if (strarr[i] == "0") 252 { 253 coilsbuffer[i] = false; 254 } 255 else 256 { 257 coilsbuffer[i] = true; 258 } 259 } 260 } 261 else 262 { 263 //转化ushort数组 264 string[] strarr = txt_data.text.split(' '); 265 registerbuffer = new ushort[strarr.length]; 266 for (int i = 0; i < strarr.length; i++) 267 { 268 registerbuffer[i] = ushort.parse(strarr[i]); 269 } 270 } 271 } 272 } 273 274 /// <summary> 275 /// 清除文本 276 /// </summary> 277 /// <param name="sender"></param> 278 /// <param name="e"></param> 279 private void button2_click(object sender, eventargs e) 280 { 281 richtextbox1.clear(); 282 } 283 /// <summary> 284 /// setmessage 285 /// </summary> 286 /// <param name="msg"></param> 287 public void setmsg(string msg) 288 { 289 richtextbox1.invoke(new action(() => { richtextbox1.appendtext(msg); })); 290 } 291 292 } 293 }
接下来开始测试
在这里 因为要用到串口,而我的笔记本没有串口,所以需要借助一个工具
virtual serial port dirver 虚拟串口工具
链接:https://pan.baidu.com/s/1opgre3gs-hwfoa_dp9qyyg
提取码:2afu
借用这个工具我们添加两个虚拟串口 com1和com2 点击add virtual pair 添加
设置modbus slave,选择连接方式为串口,选择对应端口,模式选择rtu,建立连接
接下来运行我们自己的modbus rtu master
设置串口参数(波特率,数据位,奇偶校验,停止位)要与slave的串口参数一致
我们测试 功能码 0x01 读一组线圈
测试完成,数据正常,其他的功能码经测试数据正常,有兴趣的可以自行测试
到此为止,modbus的学习到此告一段落
以上都为我自行学习并实现,如有错误之处,望大家不吝赐教,感谢(抱拳~)
程序源代码:
链接:https://pan.baidu.com/s/1mpahrixlbsdb7h2epentra
提取码:b5w6
下一篇: WPF中TimeSpan的坑