MODBUS协议相关代码(CRC验证 客户端程序)

  Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议。通过此协议,控制器相互之间、或控制器经由网络(如以太网)可以和其它设备之间进行通信。Modbus协议使用的是主从通讯技术,即由主设备主动查询和操作从设备。一般将主控设备方所使用的协议称为Modbus Master,从设备方使用的协议称为Modbus Slave。典型的主设备包括工控机和工业控制器等;典型的从设备如PLC可编程控制器等。Modbus通讯物理接口可以选用串口(包括RS232和RS485),也可以选择以太网口。

1、十六进制字符串的CRC验证 

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5
  6 namespace modbustest
  7 {
  8     public static class ByteHexHelper
  9     {
 10         private static char[] _buffedChars = null;
 11         private static byte[] _buffedBytes = null;
 12
 13         static ByteHexHelper()
 14         {
 15             _buffedChars = new char[(byte.MaxValue - byte.MinValue + 1) * 2];
 16             int idx = 0;
 17             byte b = byte.MinValue;
 18             while (true)
 19             {
 20                 string hexs = b.ToString("X2");
 21                 _buffedChars[idx++] = hexs[0];
 22                 _buffedChars[idx++] = hexs[1];
 23
 24                 if (b == byte.MaxValue) break;
 25                 ++b;
 26             }
 27
 28             _buffedBytes = new byte[0x67];
 29             idx = _buffedBytes.Length;
 30             while (--idx >= 0)
 31             {
 32                 if ((0x30 <= idx) && (idx <= 0x39))
 33                 {
 34                     _buffedBytes[idx] = (byte)(idx - 0x30);
 35                 }
 36                 else
 37                 {
 38                     if ((0x61 <= idx) && (idx <= 0x66))
 39                     {
 40                         _buffedBytes[idx] = (byte)((idx - 0x61) + 10);
 41                         continue;
 42                     }
 43                     if ((0x41 <= idx) && (idx <= 70))
 44                     {
 45                         _buffedBytes[idx] = (byte)((idx - 0x41) + 10);
 46                     }
 47                 }
 48             }
 49         }
 50
 51         /// <summary>
 52         /// 字节数组转16进制字符串
 53         /// </summary>
 54         /// <param name="bytes"></param>
 55         /// <returns></returns>
 56         public static string ByteToHex(byte[] bytes)
 57         {
 58             if (bytes == null)
 59             {
 60                 return null;
 61             }
 62
 63             char[] result = new char[bytes.Length * 2];
 64             for (int i = 0; i < bytes.Length; ++i)
 65             {
 66                 int startIndex = (bytes[i] - byte.MinValue) * 2;
 67                 result[i * 2] = _buffedChars[startIndex];
 68                 result[i * 2 + 1] = _buffedChars[startIndex + 1];
 69             }
 70
 71             return new string(result);
 72         }
 73
 74         /// <summary>
 75         /// 16进制字符串转字节数组
 76         /// </summary>
 77         /// <param name="str"></param>
 78         /// <returns></returns>
 79         public static byte[] HexToByte(string str)
 80         {
 81             str = str.Replace(" ","");
 82             if (str == null || (str.Length & 1) == 1)
 83             {
 84                 return null;
 85             }
 86
 87             byte[] result = new byte[str.Length / 2];
 88             int charIndex = 0;
 89             int byteIndex = 0;
 90             int length = result.Length;
 91             while (--length >= 0)
 92             {
 93                 int first = 0;
 94                 int second = 0;
 95                 try
 96                 {
 97                     first = _buffedBytes[str[charIndex++]];
 98                     second = _buffedBytes[str[charIndex++]];
 99                 }
100                 catch
101                 {
102                     return null;
103                 }
104                 result[byteIndex++] = (byte)((first << 4) + second);
105             }
106             return result;
107         }
108     }
109 }

2、字符串转十六进制|十六进制转字符串

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 namespace modbustest
 6 {
 7     class CRC
 8     {
 9
10         public static string CRCCheck(string val)
11         {
12             val = val.TrimEnd(‘ ‘);
13             string[] spva = val.Split(‘ ‘);
14             byte[] bufData = new byte[spva.Length + 2];
15             bufData = ToBytesCRC(val);
16             ushort CRC = 0xffff;
17             ushort POLYNOMIAL = 0xa001;
18             for (int i = 0; i < bufData.Length - 2; i++)
19             {
20                 CRC ^= bufData[i];
21                 for (int j = 0; j < 8; j++)
22                 {
23                     if ((CRC & 0x0001) != 0)
24                     {
25                         CRC >>= 1;
26                         CRC ^= POLYNOMIAL;
27                     }
28                     else
29                     {
30                         CRC >>= 1;
31                     }
32                 }
33             }
34
35             return ToHex(System.BitConverter.GetBytes(CRC));
36         }
37         /// <summary>
38         /// 例如把如下字符串转换成字节数组
39         /// AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB   转换为字节数组
40         /// </summary>
41         /// <param name="hex">十六进制字符串</param>
42         /// <returns></returns>
43         public static byte[] ToBytesCRC(string hex)
44         {
45             string[] temp = hex.Split(‘ ‘);
46             byte[] b = new byte[temp.Length + 2];
47
48             for (int i = 0; i < temp.Length; i++)
49             {
50                 b[i] = Convert.ToByte(temp[i], 16);
51             }
52
53             return b;
54         }
55         /// <summary>
56         /// 将字节数据转换为十六进制字符串,中间用 “ ”分割 如:AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB
57         /// </summary>
58         /// <param name="vars">要转换的字节数组</param>
59         /// <returns></returns>
60         public static String ToHex(byte[] vars)
61         {
62             return BitConverter.ToString(vars).Replace(‘-‘, ‘ ‘).Trim();
63         }
64     }
65 }

3、窗体主程序

  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.Windows.Forms;
  9 using System.IO.Ports;
 10 using System.Text.RegularExpressions;
 11
 12 namespace modbustest
 13 {
 14     public partial class Form1 : Form
 15     {
 16
 17         SerialPort sp = null;//申明一个串口类
 18         bool isOpen = false;//打开串口标识位
 19         bool isSetProperty = false;//属性设置标志位
 20         bool isHex = false;//十六进制显示标志位
 21
 22         public Form1()
 23         {
 24             InitializeComponent();
 25         }
 26         //窗体初始化
 27         private void Form1_Load(object sender, EventArgs e)
 28         {
 29             this.MaximumSize = this.Size;
 30             this.MinimumSize = this.Size;
 31             this.MaximizeBox = false;
 32             //最大支持10个串口
 33             for (int i = 0; i < 10; i++)
 34             {
 35                 protBox.Items.Add("COM" + (i + 1).ToString());
 36             }
 37             protBox.SelectedIndex = 0;
 38
 39             //初始化波特率
 40             BaudRate.Items.Add("1200");
 41             BaudRate.Items.Add("2400");
 42             BaudRate.Items.Add("4800");
 43             BaudRate.Items.Add("9600");
 44             BaudRate.Items.Add("19200");
 45             BaudRate.Items.Add("38400");
 46             BaudRate.Items.Add("43000");
 47             BaudRate.Items.Add("56000");
 48             BaudRate.Items.Add("57600");
 49             BaudRate.Items.Add("115200");
 50
 51             BaudRate.SelectedIndex = 5;
 52
 53             //初始化停止位
 54
 55             stopBits.Items.Add("0");
 56             stopBits.Items.Add("1");
 57             stopBits.Items.Add("1.5");
 58             stopBits.Items.Add("2");
 59             stopBits.SelectedIndex = 1;
 60
 61             //初始化数据位
 62             dataBits.Items.Add("8");
 63             dataBits.Items.Add("7");
 64             dataBits.Items.Add("6");
 65             dataBits.Items.Add("5");
 66             dataBits.SelectedIndex = 0;
 67
 68             //初始化奇偶校验位
 69             parity.Items.Add("无");
 70             parity.Items.Add("Odd");
 71             parity.Items.Add("Even");
 72             parity.SelectedIndex = 2;
 73
 74             //默认显示hex
 75             rbnHex.Checked = true;
 76         }
 77
 78         //检查哪些串口可用
 79         private void checkCOM_Click(object sender, EventArgs e)
 80         {
 81             bool comExistence = false;//有可用的串口标志位
 82             protBox.Items.Clear();//清除当前串口号中的所有串口名称
 83             for (int i = 0; i < 10;i++ )
 84             {
 85                 try{
 86
 87                     SerialPort sp = new SerialPort("COM"+(i+1).ToString());
 88                     sp.Open();
 89                     sp.Close();
 90                     protBox.Items.Add("COM" + (i + 1).ToString());
 91                     comExistence = true;
 92
 93                 }
 94                 catch {
 95
 96                     continue;
 97                 }
 98             }
 99             if (comExistence)
100             {
101
102                 protBox.SelectedIndex = 0;
103             }
104             else {
105
106                 MessageBox.Show("没有找到可用串口","错误提示");
107             }
108
109         }
110         //检查串口是否设置
111         private bool checkPortSetting()
112         {
113             if (protBox.Text.Trim() == "") return false;
114
115             if (BaudRate.Text.Trim() == "") return false;
116             if (dataBits.Text.Trim() == "") return false;
117             if (parity.Text.Trim() == "") return false;
118             if (stopBits.Text.Trim() == "") return false;
119
120             return true;
121
122         }
123
124         private bool checkSendData()
125         {
126             if (tbxSend.Text.Trim() == "") return false;
127             return true;
128
129         }
130
131         private void SetPortProperty()//设置串口的属性
132         {
133             sp=new SerialPort();
134             sp.PortName=protBox.Text.Trim();//设置串口名
135             sp.BaudRate=Convert.ToInt32(BaudRate.Text.Trim());//设置串口的波特率
136             float f=Convert.ToSingle(stopBits.Text.Trim());//设置停止位
137             if(f==0){
138
139                 sp.StopBits=StopBits.None;
140             }
141             else if(f==1.5){
142
143                 sp.StopBits=StopBits.OnePointFive;
144             }
145             else if(f==1){
146
147             sp.StopBits=StopBits.One;
148             }
149             else if(f==2){
150
151                 sp.StopBits=StopBits.Two;
152             }
153             else{
154                 sp.StopBits=StopBits.One;
155             }
156             sp.DataBits=Convert.ToInt16(dataBits.Text.Trim());//设置数据位
157             string s=parity.Text.Trim();//设置奇偶校验位
158             if(s.CompareTo("None")==0){
159                 sp.Parity=Parity.None;
160            }
161             else if(s.CompareTo("Odd")==0){
162
163                 sp.Parity=Parity.Odd;
164             }
165             else if(s.CompareTo("Even")==0){
166
167                 sp.Parity=Parity.Even;
168             }
169             else{
170
171                 sp.Parity=Parity.None;
172             }
173             sp.ReadTimeout=-1;//设置超时读取时间
174             sp.RtsEnable=true;
175             //定义DataReceived事件,当串口收到数据后触发事件
176             sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
177             if(rbnHex.Checked){
178                 isHex=true;
179             }else{
180                 isHex=false;
181             }
182      }
183         private void btnSend_Click(object sender,EventArgs e)//发送串口数据
184         {
185             if(isOpen)//写串口数据
186             {
187                 try{
188
189                   sp.WriteLine(tbxSend.Text);
190                 }
191                 catch(Exception){
192
193                     MessageBox.Show("发送数据时发生错误!","错误提示");
194                     return;
195                 }
196             }else{
197                 MessageBox.Show("串口未打开!","错误提示");
198                 return;
199             }
200                 if(!checkSendData())//检测要发送的数据
201                 {
202                     MessageBox.Show("请输入要发送的数据!","错误提示");
203                     return;
204                 }
205         }
206
207         //打开串口
208         private void openPort_Click(object sender, EventArgs e)
209         {
210             if(isOpen==false){
211                 if(!checkPortSetting())//检测串口设置
212                 {
213                     MessageBox.Show("串口未设置!","错误提示");
214                     return;
215                 }
216                 if(!isSetProperty)//串口未设置则设置串口
217                 {
218                     SetPortProperty();
219                     isSetProperty=true;
220                 }
221                 try//打开串口
222                 {
223                     //sp = new SerialPort("COM1",19200,Parity.Even,8,StopBits.One);
224                     sp.Open();
225                     isOpen=true;
226                     openPort.Text="关闭串口";//串口打开后则相关的串口设置按钮便不可再用
227
228                     protBox.Enabled=false;
229                     BaudRate.Enabled=false;
230                     dataBits.Enabled=false;
231                     parity.Enabled=false;
232                     stopBits.Enabled=false;
233                     rbnChar.Enabled=false;
234                     rbnHex.Enabled=false;
235                 }
236                 catch(Exception){
237                     //打开串口失败后,相应标志位取消
238                     isSetProperty=false;
239                     isOpen=false;
240                     MessageBox.Show("串口无效或已被占用!","错误提示");
241                 }
242             }else{
243                 try//打开串口
244                 {
245                     sp.Close();
246                     isOpen=false;
247                     isSetProperty=false;
248                     openPort.Text="打开串口";//关闭串口后,串口设置选项便可以继续使用
249                     protBox.Enabled=true;
250                     BaudRate.Enabled=true;
251                     dataBits.Enabled=true;
252                     parity.Enabled=true;
253                     stopBits.Enabled=true;
254                     rbnChar.Enabled=true;
255                     rbnHex.Enabled=true;
256                 }
257                 catch(Exception){
258                     lblStatus.Text="关闭串口时发生错误";
259                 }
260             }
261 }
262         private void sp_DataReceived(object sender,SerialDataReceivedEventArgs e)
263         {
264             System.Threading.Thread.Sleep(100);//延时100ms等待接收完数据//this.Invoke就是跨线程访问ui的方法,也是本文的范例
265             this.Invoke((EventHandler)(delegate{
266
267                 if(isHex==false){
268
269                     tbxRec.Text+=sp.ReadLine();
270                 }else{
271
272                     Byte[]ReceivedData=new Byte[sp.BytesToRead+1];//创建接收字节数组
273                     sp.Read(ReceivedData,0,ReceivedData.Length);//读取所接收到的数据
274                     String RecvDataText=null;
275
276                     for( int i=0;i<ReceivedData.Length-1; i++){
277
278                         RecvDataText+=(ReceivedData[i].ToString("X2")+" ");
279                     }
280                          tbxRec.Text+=RecvDataText;
281                 }
282                  sp.DiscardInBuffer();//丢弃接收缓冲区数据
283             }));
284             }
285
286
287
288             private void button1_Click_1(object sender, EventArgs e)
289             {
290
291
292                 if (isOpen)//写串口数据
293                 {
294                     try
295                     {
296                         byte[] send_data;
297                         string str = this.tbxSend.Text;
298                         string strc = str + " " + CRC.CRCCheck(this.tbxSend.Text);
299                         //int iLen = 0;
300                         send_data = ByteHexHelper.HexToByte(strc);
301
302                        // send_data = HexStringToByteArray(strc);
303                         //iLen = send_data.GetLength(0);
304                         //this.tbxRec = byteToHexStr(send_data);
305                        // byte[] acc_data = new byte[] { 0x30, 0x31 };
306                         //acc_data = send_data;
307                         //String acc_str = System.Text.Encoding.Default.GetString(acc_data);
308                         ////ModBus comm = new ModBus();
309                         this.tbxRec.Text = str + " "+ CRC.CRCCheck(str);
310                         //String rec = str + " " + CRC.CRCCheck(acc_str);
311                         //sp.WriteLine(strs);
312                         sp.Write(send_data,0,send_data.Length);
313
314                         //this.tbxRec.Text = sp.BaudRate + " " + sp.BytesToWrite + "kong";
315
316
317                     }
318                     catch (Exception)
319                     {
320
321                         MessageBox.Show("发送数据时发生错误!", "错误提示");
322                         return;
323                     }
324                 }
325                 else
326                 {
327                     MessageBox.Show("串口未打开!", "错误提示");
328                     return;
329                 }
330                 if (!checkSendData())//检测要发送的数据
331                 {
332                     MessageBox.Show("请输入要发送的数据!", "错误提示");
333                     return;
334                 }
335             }
336
337             private void button2_Click(object sender, EventArgs e)
338             {
339                 byte[] acc_data;
340                 String str2 = this.tbxRec.Text;
341                 int iLen = 0;
342                 acc_data = System.Text.Encoding.ASCII.GetBytes(str2);
343                 iLen = acc_data.GetLength(0);
344
345                 byte[] send_data = new byte[] { 0x30, 0x31 };
346                 send_data = acc_data;
347                 String acc_str = System.Text.Encoding.ASCII.GetString(acc_data);
348                 //ModBus comm = new ModBus();
349
350                 this.tbxSend.Text = str2 + " " + CRC.CRCCheck(acc_str);
351             }
352
353             private void button3_Click(object sender, EventArgs e)
354             {
355                 tbxRec.Text = "";
356                 tbxSend.Text = "";
357
358             }
359
360
361
362
363
364     }
365 }

4、窗体

时间: 2024-10-10 14:37:12

MODBUS协议相关代码(CRC验证 客户端程序)的相关文章

网上查阅的Modbus协议相关

1. 百度知道:超时时间的设置主要取决于总线上反应最慢的节点的反应时间,另外,也要考虑主节点轮询所有的节点需要的时间.假设总线上所有的从节点里面,反应最慢的从节点的反应时间是100ms,超时时间设置在这个基础上加上一个完整数据包收发的长度就差不多了.最长的数据包一般是256字节,在11位9600BPS下这大概需要300ms,那么超时时间比400ms长一些就可以,比如说500ms.当然,再长一点也可以,比如说1秒钟,但太长就会出现另一个问题,总线轮询时间.假设总线上的所有期望的从节点都在线,正常情

ios 网络请求总结加强对中文的处理 问题:URL不允许写中文 在GET请求中,相关代码段打断点以验证。

开发还是需要多多练习的 ,下面是一些常用的步骤: 一.简单说明 创建Get 请求 //    1.设置请求路径 NSString *urlStr=[NSString stringWithFormat:@"http://192.168.1.53:8080/MJServer/login?username=%@&pwd=%@",self.username.text,self.pwd.text]; NSURL *url=[NSURL URLWithString:urlStr]; //

modbus协议显示屏|modbus通讯显示屏|modbus显示电子屏功能码实现代码分享

modbus协议显示屏|modbus通讯显示屏|modbus显示电子屏功能码实现代码分享,主要用来实现"预置多个寄存器功能码". MODBUS-RTU协议的"0X1F"功能码实物效果图拍摄如下: MODBUS-RTU协议的"0X1F"功能码示例如下: 例:请求在从机MODBUS显示屏设备17 中的2 个寄存器中放入预置值,起始寄存器为40002 ,预置值为00 0AH 和01 02H.QUERYField NameSlave AddressFun

Modbus协议 CRC 校验码

CRC(循环冗余校验)在线计算 http://www.ip33.com/crc.html 里面的8005的多项式值,但网上看到的算法都是用A001来异或的 ----------------------------------------------------- 0x8005=1000 0000 0000 0101B  0xA001=1010 0000 0000 0001B 对比两个二进制高低位正好是完全相反的,CRC校验分为正向校验与反向校验.正向校验高位在左,反向校验低位在左 正向校验使用左

在Android程序中使用Modbus协议时报 java.net.SocketException: recvfrom failed: ECONNRESET解决办法

最近在开发基本Modbus协议的Android端PLC控制程序,C#版程序没有任何问题,移到JAVA下出现各种问题,其中比较苦恼的是java.net.SocketException: recvfrom failed: ECONNRESET错误. 开始我的解决方法是每次向PLC发送一条数据后关掉socket,但是这样做太极端了. 经过分析发现是由于发送的数据包大小我设定为256,当写入1个寄存器值时,Modbus服务端直接报错,关掉了socket连接,将发送的数据包大小改成实际的大小后解决问题.

00由于客户端检测到一个协议错误 代码0x1104

重新连接N次都还是这个错误提示,最后再重起电脑,还是没用.研究了一下错误终于解决了. 首先检查远程连接端口对不对?Windows远程默认的连接端口是3389,一般大家连接时直接输入IP或域名就可以连接了.如果还要加:端口号的话,就确定这个端口号对否? 本文适用范围 Windows系列所有系统.另,作者演示系统为Windows2003 远程连接中断代码0x1104解决方法/步骤 第1步:在桌面找到网上邻居图标,并右键打开网上邻居.或可以直接在开始菜单中的设置网络连接中同样打开. 第2步:打开使用的

《Python》网络编程之验证客户端链接的合法性、socketserver模块

一.socket的更多方法介绍 # 服务端套接字函数 s.bind() # 绑定(主机,端口号)到套接字 s.listen() # 开始TCP监听 s.accept() # 被动接受TCP客户的连接,(阻塞式)等待连接的到来 # 客户端套接字函数 s.connect() # 主动初始化TCP服务器连接 s.connect_ex() # connect()函数的扩展版本,出错时返回出错码,而不是抛出异常 # 公共用途的套接字函数 s.recv() # 接收TCP数据 s.send() # 发送TC

socketserver和socket的补充(验证客户端合法性)

一.socket的补充 1.参数 socket.socket(family=AF_INET,type=SOCK_STREAM,proto=0,fileno=None) 参数说明: family 地址系列应为AF_INET(默认值ipv4),AF_INET6(ipv6),AF_UNIX,AF_CAN或AF_RDS. (AF_UNIX 域实际上是使用本地 socket 文件来通信) type 套接字类型应为SOCK_STREAM(默认值,tcp协议),SOCK_DGRAM(udp协议),SOCK_R

基于AVR128的简单Modbus协议实现

Modbus通讯协议是由Modicon公司在1979年开发的,应用于工业现场控制的总线协议.Modbus通讯系统包括带有可编程控制的芯片节点和公共传输线组成,其目的是用于多节点数据的采集和监控.Modbus协议采用主从模式,通讯系统中有一个主机对多个节点从机进行监控,从机节点最多支持247个.每个从机均有自己独立的从机地址,而且改地址能够被主机识别. 能够支持Modbus协议的通讯系统有RS-232,RS-422,RS-485等.同时Modbus协议具有标准.开放.免费.帧格式简单等特点而被广大