Modbus Com SerialPort
项目中用到的工具,串口modbus协议读写数据。
public class ModbusHelper
{
private readonly SerialPort _serialPort;
private readonly ILog _logger;
private bool _isReceivedData = false;
private byte _modbusAdd = 0x02;
private int _readTimes = ; public ModbusHelper(string port, int baudRate, byte slave)
{
try
{
_modbusAdd = slave;
_logger = LogManager.GetLogger(this.GetType());
_serialPort = new SerialPort(port, baudRate, Parity.Even, , StopBits.One);
_serialPort.DataReceived += _serialPort_DataReceived;
_logger = LogManager.GetLogger(this.GetType());
}
catch (Exception e)
{
_logger.Error("Modbus串口初始化失败", e);
}
} public bool Open()
{
try
{
if (!_serialPort.IsOpen)
{
_serialPort.Open();
}
_logger.Debug("串口" + _serialPort.PortName + "正常打开");
return true;
}
catch (Exception e)
{
_logger.Error("打开串口打开失败", e);
return false;
}
} public bool Close()
{
try
{
if (_serialPort.IsOpen)
{
_serialPort.Close();
_serialPort.Dispose();
}
_logger.Debug("串口" + _serialPort.PortName + "正常关闭");
return true;
}
catch (Exception err)
{
_logger.Error("关闭串口失败", err);
return false;
}
} private void ResetBuffer()
{
_readTimes = ;
_serialPort.DiscardInBuffer();
} /// <summary>
/// 串口寄存器写值
/// </summary>
/// <param name="beginAddress">开始写入相对地址</param>
/// <param name="data">写入数据</param>
public void WriteSingleWord(short beginAddress, short data)
{
try
{
var writeData = new List<byte>();
writeData.Add(_modbusAdd);
writeData.Add(0x06);
writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
writeData.AddRange(BitConverter.GetBytes(data).Reverse());
byte[] crc = CrcCheck(writeData.ToArray());
writeData.AddRange(crc.Reverse()); if (_serialPort.IsOpen)
{
ResetBuffer();
_serialPort.Write(writeData.ToArray(), , writeData.Count);
_history.Add(writeData);
_serialPort.ReceivedBytesThreshold = ;
while (_readTimes < && !_isReceivedData)
{
_readTimes++;
Thread.Sleep();
}
_logger.Debug("写单字数据成功,数据:" + string.Join(",", writeData));
}
}
catch (Exception err)
{
_logger.Error("写入单字数据时失败", err);
}
} /// <summary>
/// 向寄存器写入多个字
/// </summary>
/// <param name="beginAddress">起始地址(相对地址)</param>
/// <param name="writeLen">写入的字数</param>
/// <param name="data">按字写入数据(字节高地址,字节低地址;字节高地址,字节低地址;)</param>
public void WriteMutiWord(short beginAddress, short writeLen, byte[] data)
{
try
{
var writeData = new List<byte>();
writeData.Add(_modbusAdd);
writeData.Add(0x10);
writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
writeData.AddRange(BitConverter.GetBytes(writeLen).Reverse());
writeData.Add((byte)(writeLen * ));//写入的字节长度
writeData.AddRange(data);
byte[] crc = CrcCheck(writeData.ToArray());
writeData.AddRange(crc.Reverse()); if (_serialPort.IsOpen)
{
ResetBuffer();
_serialPort.Write(writeData.ToArray(), , writeData.Count);
_history.Add(writeData);
_serialPort.ReceivedBytesThreshold = ;
while (_readTimes < && !_isReceivedData)
{
_readTimes++;
Thread.Sleep();
}
_logger.Debug("多字写数据成功,数据:" + string.Join(",", writeData));
}
}
catch (Exception err)
{
_logger.Error("往串口写入数据时失败", err);
}
} /// <summary>
/// 向寄存器写入双字
/// </summary>
/// <param name="beginAddress">起始地址(相对地址)</param>
/// <param name="data">写入数据</param>
public void WriteDoubleWord(short beginAddress, int data)
{
try
{
var writeData = new List<byte>();
writeData.Add(_modbusAdd);
writeData.Add(0x10);
writeData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
writeData.Add();//寄存器数量高字节
writeData.Add();//寄存器数量低字节
writeData.Add();//写入的字节长度
writeData.AddRange(BitConverter.GetBytes(data).Reverse()); byte[] crc = CrcCheck(writeData.ToArray());
writeData.AddRange(crc.Reverse()); if (_serialPort.IsOpen)
{
ResetBuffer();
_serialPort.Write(writeData.ToArray(), , writeData.Count);
_history.Add(writeData);
_serialPort.ReceivedBytesThreshold = ;
while (_readTimes < && !_isReceivedData)
{
_readTimes++;
Thread.Sleep();
}
_logger.Debug("写双字数据成功,数据:" + string.Join(",", writeData));
}
}
catch (Exception err)
{
_logger.Error("写入双字数据时失败", err);
}
} /// <summary>
/// 串口读取寄存器值
/// </summary>
/// <param name="beginAddress">开始读取相对地址</param>
/// <param name="readLen">读取的字数</param>
/// <returns></returns>
public byte[] Read(short beginAddress, short readLen)
{
try
{
var readData = new List<byte>();
readData.Add(_modbusAdd);
readData.Add(0x03);
readData.AddRange(BitConverter.GetBytes(beginAddress).Reverse());
readData.AddRange(BitConverter.GetBytes(readLen).Reverse());
byte[] crc = CrcCheck(readData.ToArray());
readData.AddRange(crc.Reverse()); if (_serialPort.IsOpen)
{
ResetBuffer();
_serialPort.Write(readData.ToArray(), , readData.Count);
List<string> logdata = new List<string>();
readData.ForEach(x => logdata.Add(Convert.ToString(x, ).ToUpper().PadLeft(,'')));
_logger.Debug("串口写入数据:" + string.Join(" ", logdata));
_serialPort.ReceivedBytesThreshold = + readLen * ;
Thread.Sleep();
while (_serialPort.BytesToRead == && _readTimes < )
{
_readTimes++;
Thread.Sleep();
}
if (_serialPort.BytesToRead > )
{
var bytesToRead = _serialPort.BytesToRead;
byte[] buffer = new byte[bytesToRead];
_serialPort.Read(buffer, , bytesToRead);
_serialPort.DiscardInBuffer(); _logger.Debug("串口返回的数据:" + string.Join(",", buffer));
int readDataLength = bytesToRead - ;//减去开头的modbus地址、功能码、字节数,和结尾的2字节校验位
if (readDataLength > )
{
return buffer.Skip().Take(readDataLength).ToArray();
}
}
return null;
}
return null;
}
catch (Exception err)
{
_logger.Error("写入<读取>指令时失败", err);
return null;
}
} private void _serialPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
try
{ }
catch (Exception err)
{
_logger.Error("读取串口数据失败", err);
}
} /// <summary>
/// 获取Modbus校验值
/// </summary>
/// <param name="data">数据字节流</param>
/// <returns>返回2byte,byte0高位,byte1低位</returns>
private byte[] CrcCheck(byte[] data)
{
try
{
byte CRC16Lo;
byte CRC16Hi;
byte CL; byte CH;
byte SaveHi; byte SaveLo;
byte[] tmpData;
int Flag;
CRC16Lo = 0xFF;
CRC16Hi = 0xFF;
CL = 0x01;
CH = 0xA0;
tmpData = data;
for (int i = ; i < tmpData.Length; i++)
{
CRC16Lo = (byte)(CRC16Lo ^ tmpData[i]);
for (Flag = ; Flag <= ; Flag++)
{
SaveHi = CRC16Hi;
SaveLo = CRC16Lo;
CRC16Hi = (byte)(CRC16Hi >> );
CRC16Lo = (byte)(CRC16Lo >> );
if ((SaveHi & 0x01) == 0x01)
{
CRC16Lo = (byte)(CRC16Lo | 0x80);
} if ((SaveLo & 0x01) == 0x01)
{
CRC16Hi = (byte)(CRC16Hi ^ CH);
CRC16Lo = (byte)(CRC16Lo ^ CL);
}
}
}
byte[] ReturnData = new byte[];
ReturnData[] = CRC16Hi;
ReturnData[] = CRC16Lo;
return ReturnData;
}
catch (Exception err)
{
_logger.Error("计算crc校验值出错", err);
return null;
}
} private readonly List<List<byte>> _history = new List<List<byte>>(); public void ClearHistory()
{
_history.Clear();
} public void WriteHistory()
{
foreach (List<byte> item in _history)
{
_serialPort.Write(item.ToArray(), , item.Count);
_serialPort.ReceivedBytesThreshold = ;
while (_readTimes < && !_isReceivedData)
{
_readTimes++;
Thread.Sleep();
}
}
} }
Modbus Com SerialPort的更多相关文章
- Modbus RTU 通信工具设计(转)
Modbus RTU 通信工具设计 Modbus 是一个工业上常用的通讯协议.一种通讯约定. ModBus 协议是应用层报文传输协议(OSI 模型第7层),它定义了一个与通信层无关的协议数据单元(PD ...
- C# MODBUS协议 上位机(转)
源:C# MODBUS协议 上位机 C#写了一款上位机监控软件,基于MODBUS_RTU协议. 软件的基本结构: 采用定时器(Timer控件)为时间片. 串口采用serialPort1_DataRec ...
- Modbus软件开发实战指南 之 开发自己的Modbus Poll工具 - 1
在开发Modbus程序的过程中,也可以发现经常需要使用诸如Modbus Poll和Modbus Slave等辅助调试工具, 用于验证MODBUS通讯消息是否正确.但是,Modbus Poll和Modb ...
- 基于Modbus的C#串口调试开发
说明:本文主要研究的是使用C# WinForm开发的串口调试软件(其中包含Modbus协议相关操作).Modbus相关协议可以查阅百度文库等,可参考: <http://wenku.baidu.c ...
- modbus串口通讯C#
简介 公司给的一个小任务,这篇文章进行详细讲解 题目: modbus串口通讯 主要内容如下: 1.实现使用modbus通讯规约的测试软件: 2.具有通信超时功能: 3.分主站从站,并能编辑报文.生成报 ...
- Modbus tcp 格式说明 通讯机制 附C#测试工具用于学习,测试
前言: 之前的博客介绍了如何用C#来读写modbus tcp服务器的数据,文章:http://www.cnblogs.com/dathlin/p/7885368.html 当然也有如何创建一个服务器文 ...
- MODBUS协议相关代码(CRC验证 客户端程序)
Modbus协议是一种已广泛应用于当今工业控制领域的通用通讯协议.通过此协议,控制器相互之间.或控制器经由网络(如以太网)可以和其它设备之间进行通信.Modbus协议使用的是主从通讯技术,即由主设备主 ...
- C# MODBUS 通信
背景 电厂有多组监控设备,需要在指定的设备上显示某些数据(其他设备对接过来的).通信协议是modbus主从结构. 源码: http://download.csdn.net/download/wolf1 ...
- C#Modbus Rtu的实现
Modbus Rtu的实现与Modbus Tcp的实现类似 C#ModBus Tcp的学习及Master的实现 我们还是需要借用一个开源库NModbus4,在vs中.打开NuGet管理器.安装NMod ...
随机推荐
- JQuery对象函数
1.JQuery对象函数写法格式 简单示例: main代表对象名,main.roleName = (function () { return $('#RoleName').val();})();表示为 ...
- EF6 使用SQLite Code First
SQLite是一款轻型关系型数据库,做一个小网站,用来替代sql server或者access数据库应该会是一个不错的选择. ASP.NET Entity Framework 6是微软平台的著名ORM ...
- 大咖分享 | 一文解锁首届云创大会干货——下篇(文末附演讲ppt文件免费下载)
本文承接上一篇:大咖分享 | 一文解锁首届云创大会干货--上篇(文末附演讲ppt文件免费下载),第一届云创大会留下干货太多,这里追加下篇,同样,文末提供大咖们的干货分享,点击附件可免费下载. ...
- AI-Info-Micron:人如其食:人工智能和人类微生物组
ylbtech-AI-Info-Micron:人如其食:人工智能和人类微生物组 1.返回顶部 1. 人如其食:人工智能和人类微生物组 “相信你身体发出的信号”,的确是一个很好的建议.研究人员在不遗余力 ...
- Gogland配置- 修改Go源代码tab值
Gogland对Go源代码默认值为8个,我很不习惯,第一次遇到tab数量这么大的,于是我决定修改这个tab值! 1,点击顶部菜单“File”->"Settings". 2,在 ...
- 一次mysql主从同步问题及解决过程
Normal 0 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 由于经常被抓取文章内容,在此附上博 ...
- luoguP5074 Eat the Trees
https://www.luogu.org/problemnew/show/P5074 插头 $ dp $ 入门题 如果你还不会插头 $ dp $ 请右转 洛谷插头dp题解 虽然是入门题但还是逃不过分 ...
- robot framework-接口测试实例一
需求:api/car/detail/recommendcar.json 接口返回的车辆数量少于等于20且车辆不能重复 分析:统计接口中返回的列表的长度,再把carid拿出来组成一个新的列表,判断这 ...
- redhat linux elk命令
安装es后不能启动原因在elasticsearch.yml中加如下两个配置: bootstrap.memory_lock: falsebootstrap.system_call_filter: fal ...
- docker微服务部署之:五、利用DockerMaven插件自动构建镜像
docker微服务部署之:四.安装docker.docker中安装mysql和jdk1.8.手动构建镜像.部署项目 在上一篇文章中,我们是手动构建镜像,即: 4.1.2.5.1.2.6.1.2中的将d ...