Modbus Rtu的實現與Modbus Tcp的實現相似 C#ModBus Tcp的學習及Master的實現html
咱們仍是須要借用一個開源庫NModbus4,在vs中.打開NuGet管理器.安裝NModbus4數組
具體實現,具體實現與以前的Modbus Tcp的實現相似 ,只是在實例化master時將TCPClient換爲串行端口資源SerialPort,並在實例化是設置好端口所需參數(端口名,波特率,校驗位,中止位,數據位)async
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 }
接下來開始測試ide
在這裏 由於要用到串口,而個人筆記本沒有串口,因此須要藉助一個工具工具
Virtual Serial Port Dirver 虛擬串口工具 post
連接:https://pan.baidu.com/s/1opGre3GS-HWFoA_dP9qYYg 學習
提取碼:2afu 測試
借用這個工具咱們添加兩個虛擬串口 COM1和COM2 點擊Add Virtual Pair 添加url
設置Modbus Slave,選擇鏈接方式爲串口,選擇對應端口,模式選擇RTU,創建鏈接spa
接下來運行咱們本身的Modbus RTU Master
設置串口參數(波特率,數據位,奇偶校驗,中止位)要與Slave的串口參數一致
咱們測試 功能碼 0x01 讀一組線圈
測試完成,數據正常,其餘的功能碼經測試數據正常,有興趣的能夠自行測試
到此爲止,Modbus的學習到此告一段落
以上都爲我自行學習並實現,若有錯誤之處,望你們不吝賜教,感謝(抱拳~)
程序源代碼:
連接:https://pan.baidu.com/s/1mPAhRixLbsDb7h2ePENTRA 提取碼:b5w6