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

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace modbustest
{
public static class ByteHexHelper
{
private static char[] _buffedChars = null;
private static byte[] _buffedBytes = null; static ByteHexHelper()
{
_buffedChars = new char[(byte.MaxValue - byte.MinValue + ) * ];
int idx = ;
byte b = byte.MinValue;
while (true)
{
string hexs = b.ToString("X2");
_buffedChars[idx++] = hexs[];
_buffedChars[idx++] = hexs[]; if (b == byte.MaxValue) break;
++b;
} _buffedBytes = new byte[0x67];
idx = _buffedBytes.Length;
while (--idx >= )
{
if ((0x30 <= idx) && (idx <= 0x39))
{
_buffedBytes[idx] = (byte)(idx - 0x30);
}
else
{
if ((0x61 <= idx) && (idx <= 0x66))
{
_buffedBytes[idx] = (byte)((idx - 0x61) + );
continue;
}
if ((0x41 <= idx) && (idx <= ))
{
_buffedBytes[idx] = (byte)((idx - 0x41) + );
}
}
}
} /// <summary>
/// 字节数组转16进制字符串
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
public static string ByteToHex(byte[] bytes)
{
if (bytes == null)
{
return null;
} char[] result = new char[bytes.Length * ];
for (int i = ; i < bytes.Length; ++i)
{
int startIndex = (bytes[i] - byte.MinValue) * ;
result[i * ] = _buffedChars[startIndex];
result[i * + ] = _buffedChars[startIndex + ];
} return new string(result);
} /// <summary>
/// 16进制字符串转字节数组
/// </summary>
/// <param name="str"></param>
/// <returns></returns>
public static byte[] HexToByte(string str)
{
str = str.Replace(" ","");
if (str == null || (str.Length & ) == )
{
return null;
} byte[] result = new byte[str.Length / ];
int charIndex = ;
int byteIndex = ;
int length = result.Length;
while (--length >= )
{
int first = ;
int second = ;
try
{
first = _buffedBytes[str[charIndex++]];
second = _buffedBytes[str[charIndex++]];
}
catch
{
return null;
}
result[byteIndex++] = (byte)((first << ) + second);
}
return result;
}
}
}

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

 using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace modbustest
{
class CRC
{ public static string CRCCheck(string val)
{
val = val.TrimEnd(' ');
string[] spva = val.Split(' ');
byte[] bufData = new byte[spva.Length + ];
bufData = ToBytesCRC(val);
ushort CRC = 0xffff;
ushort POLYNOMIAL = 0xa001;
for (int i = ; i < bufData.Length - ; i++)
{
CRC ^= bufData[i];
for (int j = ; j < ; j++)
{
if ((CRC & 0x0001) != )
{
CRC >>= ;
CRC ^= POLYNOMIAL;
}
else
{
CRC >>= ;
}
}
} return ToHex(System.BitConverter.GetBytes(CRC));
}
/// <summary>
/// 例如把如下字符串转换成字节数组
/// AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB 转换为字节数组
/// </summary>
/// <param name="hex">十六进制字符串</param>
/// <returns></returns>
public static byte[] ToBytesCRC(string hex)
{
string[] temp = hex.Split(' ');
byte[] b = new byte[temp.Length + ]; for (int i = ; i < temp.Length; i++)
{
b[i] = Convert.ToByte(temp[i], );
} return b;
}
/// <summary>
/// 将字节数据转换为十六进制字符串,中间用 “ ”分割 如:AA AA AA AA 0A 00 68 00 06 03 04 54 21 28 22 E5 F3 16 BB BB BB BB
/// </summary>
/// <param name="vars">要转换的字节数组</param>
/// <returns></returns>
public static String ToHex(byte[] vars)
{
return BitConverter.ToString(vars).Replace('-', ' ').Trim();
}
}
}

3、窗体主程序

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO.Ports;
using System.Text.RegularExpressions; namespace modbustest
{
public partial class Form1 : Form
{ SerialPort sp = null;//申明一个串口类
bool isOpen = false;//打开串口标识位
bool isSetProperty = false;//属性设置标志位
bool isHex = false;//十六进制显示标志位 public Form1()
{
InitializeComponent();
}
//窗体初始化
private void Form1_Load(object sender, EventArgs e)
{
this.MaximumSize = this.Size;
this.MinimumSize = this.Size;
this.MaximizeBox = false;
//最大支持10个串口
for (int i = ; i < ; i++)
{
protBox.Items.Add("COM" + (i + ).ToString());
}
protBox.SelectedIndex = ; //初始化波特率
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add("");
BaudRate.Items.Add(""); BaudRate.SelectedIndex = ; //初始化停止位 stopBits.Items.Add("");
stopBits.Items.Add("");
stopBits.Items.Add("1.5");
stopBits.Items.Add("");
stopBits.SelectedIndex = ; //初始化数据位
dataBits.Items.Add("");
dataBits.Items.Add("");
dataBits.Items.Add("");
dataBits.Items.Add("");
dataBits.SelectedIndex = ; //初始化奇偶校验位
parity.Items.Add("无");
parity.Items.Add("Odd");
parity.Items.Add("Even");
parity.SelectedIndex = ; //默认显示hex
rbnHex.Checked = true;
} //检查哪些串口可用
private void checkCOM_Click(object sender, EventArgs e)
{
bool comExistence = false;//有可用的串口标志位
protBox.Items.Clear();//清除当前串口号中的所有串口名称
for (int i = ; i < ;i++ )
{
try{ SerialPort sp = new SerialPort("COM"+(i+).ToString());
sp.Open();
sp.Close();
protBox.Items.Add("COM" + (i + ).ToString());
comExistence = true; }
catch { continue;
}
}
if (comExistence)
{ protBox.SelectedIndex = ;
}
else { MessageBox.Show("没有找到可用串口","错误提示");
} }
//检查串口是否设置
private bool checkPortSetting()
{
if (protBox.Text.Trim() == "") return false; if (BaudRate.Text.Trim() == "") return false;
if (dataBits.Text.Trim() == "") return false;
if (parity.Text.Trim() == "") return false;
if (stopBits.Text.Trim() == "") return false; return true; } private bool checkSendData()
{
if (tbxSend.Text.Trim() == "") return false;
return true; } private void SetPortProperty()//设置串口的属性
{
sp=new SerialPort();
sp.PortName=protBox.Text.Trim();//设置串口名
sp.BaudRate=Convert.ToInt32(BaudRate.Text.Trim());//设置串口的波特率
float f=Convert.ToSingle(stopBits.Text.Trim());//设置停止位
if(f==){ sp.StopBits=StopBits.None;
}
else if(f==1.5){ sp.StopBits=StopBits.OnePointFive;
}
else if(f==){ sp.StopBits=StopBits.One;
}
else if(f==){ sp.StopBits=StopBits.Two;
}
else{
sp.StopBits=StopBits.One;
}
sp.DataBits=Convert.ToInt16(dataBits.Text.Trim());//设置数据位
string s=parity.Text.Trim();//设置奇偶校验位
if(s.CompareTo("None")==){
sp.Parity=Parity.None;
}
else if(s.CompareTo("Odd")==){ sp.Parity=Parity.Odd;
}
else if(s.CompareTo("Even")==){ sp.Parity=Parity.Even;
}
else{ sp.Parity=Parity.None;
}
sp.ReadTimeout=-;//设置超时读取时间
sp.RtsEnable=true;
//定义DataReceived事件,当串口收到数据后触发事件
sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
if(rbnHex.Checked){
isHex=true;
}else{
isHex=false;
}
}
private void btnSend_Click(object sender,EventArgs e)//发送串口数据
{
if(isOpen)//写串口数据
{
try{ sp.WriteLine(tbxSend.Text);
}
catch(Exception){ MessageBox.Show("发送数据时发生错误!","错误提示");
return;
}
}else{
MessageBox.Show("串口未打开!","错误提示");
return;
}
if(!checkSendData())//检测要发送的数据
{
MessageBox.Show("请输入要发送的数据!","错误提示");
return;
}
} //打开串口
private void openPort_Click(object sender, EventArgs e)
{
if(isOpen==false){
if(!checkPortSetting())//检测串口设置
{
MessageBox.Show("串口未设置!","错误提示");
return;
}
if(!isSetProperty)//串口未设置则设置串口
{
SetPortProperty();
isSetProperty=true;
}
try//打开串口
{
//sp = new SerialPort("COM1",19200,Parity.Even,8,StopBits.One);
sp.Open();
isOpen=true;
openPort.Text="关闭串口";//串口打开后则相关的串口设置按钮便不可再用 protBox.Enabled=false;
BaudRate.Enabled=false;
dataBits.Enabled=false;
parity.Enabled=false;
stopBits.Enabled=false;
rbnChar.Enabled=false;
rbnHex.Enabled=false;
}
catch(Exception){
//打开串口失败后,相应标志位取消
isSetProperty=false;
isOpen=false;
MessageBox.Show("串口无效或已被占用!","错误提示");
}
}else{
try//打开串口
{
sp.Close();
isOpen=false;
isSetProperty=false;
openPort.Text="打开串口";//关闭串口后,串口设置选项便可以继续使用
protBox.Enabled=true;
BaudRate.Enabled=true;
dataBits.Enabled=true;
parity.Enabled=true;
stopBits.Enabled=true;
rbnChar.Enabled=true;
rbnHex.Enabled=true;
}
catch(Exception){
lblStatus.Text="关闭串口时发生错误";
}
}
}
private void sp_DataReceived(object sender,SerialDataReceivedEventArgs e)
{
System.Threading.Thread.Sleep();//延时100ms等待接收完数据//this.Invoke就是跨线程访问ui的方法,也是本文的范例
this.Invoke((EventHandler)(delegate{ if(isHex==false){ tbxRec.Text+=sp.ReadLine();
}else{ Byte[]ReceivedData=new Byte[sp.BytesToRead+];//创建接收字节数组
sp.Read(ReceivedData,,ReceivedData.Length);//读取所接收到的数据
String RecvDataText=null; for( int i=;i<ReceivedData.Length-; i++){ RecvDataText+=(ReceivedData[i].ToString("X2")+" ");
}
tbxRec.Text+=RecvDataText;
}
sp.DiscardInBuffer();//丢弃接收缓冲区数据
}));
} private void button1_Click_1(object sender, EventArgs e)
{ if (isOpen)//写串口数据
{
try
{
byte[] send_data;
string str = this.tbxSend.Text;
string strc = str + " " + CRC.CRCCheck(this.tbxSend.Text);
//int iLen = 0;
send_data = ByteHexHelper.HexToByte(strc); // send_data = HexStringToByteArray(strc);
//iLen = send_data.GetLength(0);
//this.tbxRec = byteToHexStr(send_data);
// byte[] acc_data = new byte[] { 0x30, 0x31 };
//acc_data = send_data;
//String acc_str = System.Text.Encoding.Default.GetString(acc_data);
////ModBus comm = new ModBus();
this.tbxRec.Text = str + " "+ CRC.CRCCheck(str);
//String rec = str + " " + CRC.CRCCheck(acc_str);
//sp.WriteLine(strs);
sp.Write(send_data,,send_data.Length); //this.tbxRec.Text = sp.BaudRate + " " + sp.BytesToWrite + "kong"; }
catch (Exception)
{ MessageBox.Show("发送数据时发生错误!", "错误提示");
return;
}
}
else
{
MessageBox.Show("串口未打开!", "错误提示");
return;
}
if (!checkSendData())//检测要发送的数据
{
MessageBox.Show("请输入要发送的数据!", "错误提示");
return;
}
} private void button2_Click(object sender, EventArgs e)
{
byte[] acc_data;
String str2 = this.tbxRec.Text;
int iLen = ;
acc_data = System.Text.Encoding.ASCII.GetBytes(str2);
iLen = acc_data.GetLength(); byte[] send_data = new byte[] { 0x30, 0x31 };
send_data = acc_data;
String acc_str = System.Text.Encoding.ASCII.GetString(acc_data);
//ModBus comm = new ModBus(); this.tbxSend.Text = str2 + " " + CRC.CRCCheck(acc_str);
} private void button3_Click(object sender, EventArgs e)
{
tbxRec.Text = "";
tbxSend.Text = ""; } }
}

4、窗体

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

  1. 初识Modbus TCP/IP-------------C#编写Modbus TCP客户端程序(一)

    转自:http://blog.csdn.net/thebestleo/article/details/52269999 首先我要说明一下,本人新手一枚,本文仅为同样热爱学习的同学提供参考,有不 对的地 ...

  2. Socket编程之聊天程序 - 模拟Fins/ModBus协议通信过程

    设备控制软件编程涉及到的基本通信方式主要有TCP/IP与串口,用到的数据通信协议有Fins与ModBus. 更高级别的通信如.net中的Remoting与WCF在进行C/S架构软件开发时会采用. 本篇 ...

  3. c++下基于windows socket的单线程服务器客户端程序(基于TCP协议)

    今天自己编写了一个简单的c++服务器客户端程序,注释较详细,在此做个笔记. windows下socket编程的主要流程可概括如下:初始化ws2_32.dll动态库-->创建套接字-->绑定 ...

  4. c++下基于windows socket的服务器客户端程序(基于UDP协议)

    前天写了一个基于tcp协议的服务器客户端程序,今天写了一个基于UDP协议的,由于在上一篇使用TCP协议的服务器中注释已经较为详细,且许多api的调用是相同的,故不再另外注释. 使用UDP协议需要注意几 ...

  5. 初识Modbus TCP/IP-------------C#编写Modbus TCP客户端程序(二)

    由于感觉上一次写的篇幅过长,所以新开一贴,继续介绍Modbus TCP/IP的初步认识, 书接上回 3).03(0x03)功能码--------读保持寄存器 请求与响应格式 这是一个请求读寄存器108 ...

  6. Modbus协议 CRC 校验码

    CRC(循环冗余校验)在线计算 http://www.ip33.com/crc.html 里面的8005的多项式值,但网上看到的算法都是用A001来异或的 ---------------------- ...

  7. Gradle Android客户端程序打包(基于gradle 1.12版本验证通过)

    一.前言 android客户端开发进入尾声,负责SEO同事突然发给我一个涉及45个发布渠道的噩耗,之前只发布自有渠道的工作方式(手动修改参数打包)已经不满足需求,所以引入最近比较流行的gradle打包 ...

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

    最近在开发基本Modbus协议的Android端PLC控制程序,C#版程序没有任何问题,移到JAVA下出现各种问题,其中比较苦恼的是java.net.SocketException: recvfrom ...

  9. Gradle Android客户端程序打包(基于gradle 2.10版本验证通过)

    一.前言 目前正在准备从eclipse开发环境向AndroidStudio迁移,提前过去探探路,不出所料,原来gradle脚本果然报错,无法运行,想想索性把本地的gradle一起升级到最新版本,毕竟1 ...

随机推荐

  1. NodeJS下的阿里云企业邮箱邮件发送问题

    还没有到11点,再顺带发一个上次碰到NodeJS的邮箱插件nodeMailer不支持阿里云邮件问题. 网上很多资料都默认使用QQ之类的邮箱,因为nodeMailer默认添加了QQ之类的SMTP地址,但 ...

  2. CentOS下的Git服务器

    [Gitosis]CentOS下的Git服务器:Gitosis  [摘要]         详细介绍如何在CentOS上配置Gitosis        我们很多人知道Git可能是从Github开始的 ...

  3. explain分析sql效率

    Explain命令在解决数据库性能上是第一推荐使用命令,大部分的性能问题可以通过此命令来简单的解决,Explain可以用来查看SQL语句的执行效 果,可以帮助选择更好的索引和优化查询语句,写出更好的优 ...

  4. intellJ IDE 15 生成 serialVersionUID

    这个Inspections的位置不好找,建议搜索Serialization issues 然后勾选两项 serialzable class without "serialVersionUID ...

  5. 使用VMware克隆Linux系统

    最近在学习使用solr云技术,因为是用来学习操作,因此需要在一台虚拟机上,安装多台LinuxOS. 但是又想偷懒,不想每安装一个LinuxOS,就重新配置Linux环境,所以使用克隆,只需安装好一个模 ...

  6. Django进阶(转载)

    Django进阶地址 来自为知笔记(Wiz)

  7. 多线程“尚未调用coinitialize” 报错

    关于多线程中创建使用TADOConnect.TADODataSet等Com组件时,必须先初始化Com 在多线程启用执行前 CoInitialize(nil); 在多线程启用执行后 CoUninitia ...

  8. centos 6.5下安装mysql

    1.检测系统是否已经安装过mysql或其依赖,若已装过要先将其删除,否则第4步使用yum安装时会报错: 1 # yum list installed | grep mysql 2 mysql-libs ...

  9. 深入理解最强桌面地图控件GMAP.NET ---[更新]百度地图

    之前写了篇博文,深入理解最强桌面地图控件GMAP.NET --- 百度地图 但是很多回复说百度地图更新了,不能显示百度的离线地图.之前承诺说是国庆节更新,最近才更新.代码已经提交到: https:// ...

  10. hadoop ncdc数据下载方法

    我在看<Hadoop权威指南>时,里面提供了NCDC天气数据样本,提供的下载链接是:点击打开链接,但是里面只提供了1901和1902这两年的数据,这未免也太少了点!完全称不上“BIG DA ...