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