概要
Modbus是OSI模型第七层上的应用层报文传输协议,最早由施耐德出版,后将其转让,现已变成工业自动化等领域里一种常见的、约定俗成的标准通讯协议标准。该标准包括两个通讯规程中使用的Modbus应用层协议和服务规范:
串行链路上的Modbus
Modbus串行链路取决于TIA/EIA标准:232-F和485-A.
- Modbus RTU:用于串行通信,并使用紧凑的二进制数据表示协议通信。 RTU 格式遵循命令/数据,循环冗余校验和校验和作为错误检查机制,以确保数据的可靠性。 Modbus RTU 是 Modbus 最常用的实现方式。必须连续传输 Modbus RTU 消息,而不会出现字符间的犹豫。 Modbus 消息由空闲(静默)时段构成(分离)。
- Modbus ASCII:用于串行通信,并使用 ASCII 字符进行协议通信。 ASCII 格式使用纵向冗余校验校验和。 Modbus ASCII 消息由前导冒号(“:”)和尾随换行符(CR / LF)构成。
TCP/IP上的Modbus
Modbus Internet协议应用取决于IETF标准:RFC793和RFC791.
- Modbus TCP / IP或Modbus TCP:这是一种 Modbus 变体,用于通过 TCP / IP 网络进行通信,通过端口 502 连接。它不需要校验和计算,因为较低层已经提供校验和保护。
- TCP / IP上的Modbus或TCP上的Modbus或Modbus RTU / IP:也是一种 Modbus 变体,与 Modbus TCP 的不同之处在于,与 Modbus RTU 一样,有效载荷中包含校验和。
- 基于UDP的Modbus :在IP网络上使用 Modbus UDP ,可消除了 TCP 所需的开销。
除此之外,还存在一些在标准PLC设备上的标准通讯协议,再此不做详细说明
Modbus对象类型
以下是 Modbus 从站设备向 Modbus 主站设备提供的对象类型表
对象类型 | 权限 | 大小 | 内容 |
---|---|---|---|
线圈 | 读写 | 1 bit | I/O系统提供该类型数据 |
离散输入 | 只读 | 1 bit | 通过应用程序改变这种类型数据 |
输入寄存器 | 只读 | 16 bits | I/O系统提供该类型数据 |
保持寄存器 | 读写 | 16 bits | 通过应用程序改变这种类型数据 |
Modbus帧格式
Modbus协议定义了一个与基础通信层无关的简单协议数据单元(PDU)。特定总线或网络上的Modbus协议映射能够在应用数据单元(ADU)上引入一些附加域。
- Modbus_RTU帧格式
ADU = 地址域 + PDU(功能码 + 数据) + 差错校验(CRC),主要应用于485等异步线路
名称 | 长度(bits) | 功能 |
---|---|---|
开始 | 28 | 至少3又1/2 个字符的沉默时间(标记条件) |
地址 | 8 | 站地址 |
功能码 | 8 | 功能码; 例如,读取线圈/保持寄存器 |
数据 | n * 8 | 数据+长度将根据消息类型填充 |
CRC | 16 | 循环冗余校验 |
停止位 | 28 | 帧之间至少有3又1/2个字符的静音时间 |
- Modbus ASCII 帧格式
ADU = 地址域 + PDU(功能码 + 数据) + 差错校验(LRC),主要用于 7 位或 8 位异步串行线
名称 | 长度(bytes) | 功能 |
---|---|---|
开始 | 1 | 以冒号开头:(ASCII十六进制值为3A) |
地址 | 2 | 站地址 |
功能码 | 2 | 功能码; 例如,读取线圈/保持寄存器 |
数据 | n * 2 | 数据+长度将根据消息类型填充 |
LRC | 2 | 校验和(纵向冗余校验) |
停止位 | 2 | 回车 - 换行(CR / LF)对(ASCII值为0D,0A) |
- Modbus TCP帧格式
ADU = MBMP报文头 + PDU(功能码 + 数据),主要用于以太网,数据传输遵循大端顺序,即高位在前低位在后
名称 | 长度(bytes) | 功能 | 默认值 |
---|---|---|---|
传输标识符 | 2 | 用于服务器和客户端的消息之间的同步 | 0x00 0x00 |
协议标识符 | 2 | 0表示 Modbus / TCP协议 | 0x00 0x00 |
长度字段 | 2 | 此帧中的剩余字节数 | |
单元标识符 | 1 | 从站地址(如果不使用则为255) | 从站标识符 |
功能码 | 1 | 功能码与其他变体一样 | |
数据 | n | 数据作为响应或命令 |
CRC / LRC校验,现在还不太明白,后续自己应该跟进一下
主要功能代码的请求与响应格式
使用环境为Modbus_tcp,所以以下测试及例子皆是在Tcp/Ip环境下使用
- 读单个/多(3)个线圈
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能码 | 01 | 功能码 | 01 |
起始地址Hi | 00 | 字节数 | 01 |
起始地址Lo | 00 | 输入状态 | 01 |
输出数量Hi | 00 | ||
输出数量Lo | 01 |
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能码 | 01 | 功能码 | 01 |
起始地址Hi | 00 | 字节数 | 01 |
起始地址Lo | 00 | 输入状态 | 01 |
输出数量Hi | 00 | ||
输出数量Lo | 03 |
由于应答消息中返回的字节数仅为 8 位宽,协议开销为 5 字节,因此最多可以同时读取 2000(250 x 8)个离散输入或线圈。
- 写单个线圈
客户端请求 | 服务器响应 | ||
功能 | 05 | 功能 | 05 |
输出地址Hi | 00 | 输出地址Hi | 00 |
输出地址Lo | 04 | 输出地址Lo | 04 |
输出值Hi | FF | 输出值Hi | FF |
输出值Lo | 00 | 输出值Lo | 00 |
- 写多个线圈
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能 | 0F | 功能 | 0F |
起始地址Hi | 00 | 起始地址Hi | 01 |
起始地址Lo | 18 | 起始地址Lo | 02 |
输出数量 | 03 | 输出数量 | 03 |
字节长度 | 01(根据输出数量计算) | ||
输出值 | 06 |
- 读单个/多个保持寄存器(03是读取输入寄存器,04是读取保持寄存器)
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能 | 03 | 功能 | 03 |
起始地址Hi | 10 | 字节数 | 02 |
起始地址Lo | 21 | 寄存器数据 | 车体通讯心跳Hi |
寄存器数量Hi | 00 | 车体通讯心跳Lo | |
寄存器数量Lo | 01 |
由于寄存器值的字节数为 8 位宽,因此一次只能读取 125 个寄存器。
- 写单个保持寄存器
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能 | 06 | 功能 | 06 |
寄存器地址Hi | 10 | 寄存器地址Hi | 10 |
寄存器地址Lo | 21 | 寄存器地址Lo | 21 |
寄存器值Hi | 00 | 寄存器值Hi | 00 |
寄存器值Lo | 0A | 寄存器值Lo | 0A |
- 写多个保持寄存器
客户端请求 | 服务器响应 | ||
域名 | 十六进制 | 域名 | 十六进制 |
功能 | 10 | 功能 | 10 |
起始地址Hi | 10 | 寄存器地址Hi | 10 |
起始地址Lo | 3B | 寄存器地址Lo | 3B |
寄存器数量Hi | 00 | 寄存器数量Hi | 00 |
寄存器数量Lo | 03 | 寄存器数量Lo | 03 |
字节长度 | 06(寄存器数量*2) | ||
寄存器值1Hi | 00 | ||
寄存器值1Lo | 01 | ||
寄存器值21Hi | 00 | ||
寄存器值2Lo | 0F | ||
寄存器值3Hi | 00 | ||
寄存器值3Lo | 62 |
|
由于寄存器值为 2 字节宽,并且只能发送 127 个字节的值,因此一次只能预置/写入 63 个保持寄存器。
- 异常处理
对于正常响应,从站重复功能代码。 如果从站想报告错误,它将回复所请求的功能代码加上 128(十六进制 0x80)( 举例子:3 变为 131 = 十六进制 0x83),并且只包含一个字节的数据,称为异常代码。
异常代码 | 长度(bytes) | 功能 |
---|---|---|
1 | 非法功能 | 从设备无法识别或允许在查询中接收的功能代码 |
2 | 非法数据地址 | 从设备中不允许或不存在部分或全部所需实体的数据地址 |
3 | 非法数据值 | 从设备不接受该数据 |
4 | 从设备故障 | 从设备尝试执行请求的操作时发生不可恢复的错误) |
5 | 确认 | 从设备已接受请求并正在处理它,但需要很长的时间。 返回此响应以防止在主设备中发生超时错误。 主设备可以接下来发出一个 Poll Program Complete 消息来确定处理是否完成 |
6 | 从设备忙 | 从设备参与处理长时间命令。 主设备应该稍后再试) |
7 | 否认 | 从设备无法执行编程功能。 主设备应从从设备请求诊断或错误信息 |
8 | 内存奇偶校验错误 | 从设备检测到内存中的奇偶校验错误。 主设备可以重试请求,但可能需要在从设备上提供服务 |
10 | 网关路径不可用 | 专门用于 Modbus 网关。 表示配置错误的网关 |
11 | 网关目标设备无法响应 | 专门用于 Modbus 网关。 从站无法响应时发送 |
现在来讲实现了
项目中主要写的是Client端代码
/****************************************************************************************
Copyright (C), 2020-2021, Siasun Robot & Automation Co., Ltd
File Name: ModbusTCP.h
Author: Version:1.0.0 Date2020/04/19
Description: Modbus_tcp类头文件
Other:
// ModbusTCP.h: interface for the ModbusTCP class.
Function List:
****************************************************************************************/
#ifndef MODBUSPP_MODBUS_H
#define MODBUSPP_MODBUS_H
#include <string>
#include <iostream>
#include <stdint.h>
#include <stdio.h>
#include <io.h>
#include <process.h> //io.h与process.h文件用来替换unistd.h头文件
#include <WinSock.h>
#include "word.h"
#include <map>
//功能码
#define READ_COILS 0x01 //读取线圈
#define READ_INPUT_BITS 0x02 //读取输入线圈
#define READ_REGS 0x03 //读保持寄存器
#define READ_INPUT_REGS 0x04 //读取输入寄存器
#define WRITE_COIL 0x05 //写单线圈
#define WRITE_REG 0x06 //写单个保持寄存器
#define WRITE_COILS 0x0F //写多线圈
#define WRITE_REGS 0x10 //写多个保持寄存器的功能码
//错误返回值
#define EX_ILLEGAL_FUNCTION 0x01 //功能码不支持
#define EX_ILLEGAL_ADDRESS 0x02 //非法地址
#define EX_ILLEGAL_VALUE 0x03 //非法数据
#define EX_SERVER_FAILURE 0x04 //从站错误
#define EX_ACKNOWLEDGE 0x05 //通信超时
#define EX_SERVER_BUSY 0x06
#define EX_NEGATIVE_ACK 0x07
#define EX_MEM_PARITY_PROB 0x08
#define EX_GATEWAY_PROBLEMP 0x0A
#define EX_GATEWYA_PROBLEMF 0x0B
#define EX_BAD_DATA 0xFF
#define DISCONNECT 0x0C
#define MAX_MSG_LENGTH 260
class ModbusTCP
{
public:
bool m_bConnect;
SOCKET m_socket; //通信的SOCKET
int m_iSlaveID; //从站ID
int m_iMsgID;
bool err;
int err_no;
std::string error_msg;
public:
ModbusTCP();
virtual ~ModbusTCP();
/// <summary>
/// 服务器连接
/// <param name="serverIP">服务器IP地址</param>
/// <param name="serverPort">服务器端口号</param>
/// <returns>0:连接成功 其他连接失败 具体看返回值</returns>
int ConnectServer( const char* serverIP,WORD serverPort ); //连接服务端
bool CloseServer(); //断开服务端
/// <summary>
/// 读取与发送
/// <param name="Address">读取或写入地址</param>
/// <param name="amount">读取或写入寄存器的个数</param>
/// <param name="funCode">功能码</param>
/// <param name="value">写入寄存器的值</param>
/// <returns>0:读取或写入成功</returns>
int Modbus_read(uint16_t Address, int amount,int funCode);
int Modbus_write(uint16_t Address, int amount,int funCode,const uint16_t *value);
inline void set_bad_connect();
inline void set_bad_input();
int Modbus_send(uint8_t *to_Send, int length);
int Modbus_receive(const uint8_t *buffer);
/// <summary>
/// 读取或写入单个多个线圈或寄存器
/// <param name="Address">读取或写入地址</param>
/// <param name="amount">读取或写入寄存器的个数</param>
/// <param name="value">写入寄存器的值</param>
/// <param name="buffer">读取到的值</param>
/// <returns>0:读取或写入成功</returns>
int Modbus_read_coils(uint16_t Address, int amount, bool* buffer);
int Modbus_read_input_bits(uint16_t Address, int amount, bool* buffer);
int Modbus_read_holding_registers(uint16_t Address, int amount, uint16_t *buffer);
int Modbus_read_input_registers(uint16_t Address, int amount, uint16_t *buffer);
int Modbus_write_coil(uint16_t Address, const bool& to_Write);
int Modbus_write_register(uint16_t Address, const uint16_t& value);
int Modbus_write_coils(uint16_t Address, int amount, const bool *value);
int Modbus_write_registers(uint16_t Address, int amount,uint16_t *value);
/// <summary>
/// 生成请求或返回帧PDU
/// <param name="to_Send">所要发送的数据数组</param>
/// <param name="Address">地址</param>
/// <param name="funCode">功能码</param>
/// <returns>无</returns>
void Modbus_build_request(uint8_t *to_Send, uint16_t Address, int funCode);//生成请求帧
void Modbus_set_slave_id(int id); //发送从站ID
void modbuserror_handle(const uint8_t *msg, int funCode);
/// <summary>
/// 2个寄存器存储float 反算时需要区分高低位
/// <param name="buffer1">高位寄存器</param>
/// <param name="buffer2">低位寄存器</param>
/// <returns>无</returns>
float Modbus_get_float(uint16_t buffer1,uint16_t buffer2);
};
#endif
#include "ModbusTCP.h"
using namespace std;
//构造函数
ModbusTCP::ModbusTCP(){
m_bConnect = false;
m_iSlaveID = 1;
m_iMsgID = 1;
err = false;
err_no = 0;
error_msg = "";
}
ModbusTCP::~ModbusTCP() {}
/**
* Modbus ClientConnect
* @param const char* serverIP Server IP Address
* @param WORD serverPort Connected port
* @return -1:端口及ip地址无效
* @return -2:套接字创建失败
* @return -3:连接失败
* @return 0 :连接成功
*/
int ModbusTCP::ConnectServer( const char* serverIP,WORD serverPort )
{
//输入判断
if(serverIP == NULL||serverPort == 0){
printf("ServerIP&&Port is null!\n");
return -1;
}
//套接字创建
m_socket = socket(AF_INET, SOCK_STREAM, 0);
if(m_socket < 0){
printf("Open Socket failed!\n");
return -2;
}
//将已经处于连接状态的socket在调用closesocket时强制关闭
//BOOL m_bDontLinger = FALSE;
//setsockopt(m_socket, SOL_SOCKET, SO_DONTLINGER, (const char*)&m_bDontLinger, sizeof(BOOL));
//struct timeval timeout;
//timeout.tv_sec = 20; // after 20 seconds connect() will timeout
//timeout.tv_usec = 0;
//setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, sizeof(timeout));
//setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, sizeof(timeout));
//连接服务器
struct sockaddr_in addrServer;
addrServer.sin_family = AF_INET; //IPV4
addrServer.sin_addr.s_addr = inet_addr( serverIP );
addrServer.sin_port = htons(serverPort);
int ret = connect(m_socket, (struct sockaddr *)&addrServer, sizeof(addrServer));
if(ret == SOCKET_ERROR)
{
printf("connect failed!\n");
closesocket(m_socket);
return -3;
}
m_bConnect = true;
// 设置通信模式非阻塞
//u_long noblk = 1;
//ioctlsocket( m_socket, FIONBIO, &noblk );
return 0;
}
//客户端断开
bool ModbusTCP::CloseServer()
{
if(m_socket > 0){
closesocket(m_socket);
m_bConnect = false;
return true;
}
return false;
}
/**
* Modbus Request Builder
* @param to_Send Message Buffer to Be Sent
* @param Address Reference Address
* @param funCode Modbus Functional Code
*/
void ModbusTCP::Modbus_build_request( uint8_t *to_Send, uint16_t Address, int funCode )
{
to_Send[0] = 0; //(uint8_t) (m_iMsgID) >> 8u; //不计数的话可以为0
to_Send[1] = 0; //(uint8_t) (m_iMsgID & 0x00FFu);
to_Send[2] = 0;
to_Send[3] = 0;
to_Send[4] = 0;
to_Send[6] = (uint8_t) m_iSlaveID;
to_Send[7] = (uint8_t) funCode;
to_Send[8] = (uint8_t) (Address >> 8u);
to_Send[9] = (uint8_t) (Address & 0x00FFu);
}
//发送从站ID
void ModbusTCP::Modbus_set_slave_id( int id )
{
m_iSlaveID = id;
}
int ModbusTCP::Modbus_read( uint16_t Address, int amount,int funCode )
{
uint8_t to_Send[12];
Modbus_build_request(to_Send, Address, funCode);
to_Send[5] = 6;
to_Send[10] = (uint8_t) (amount >> 8u);
to_Send[11] = (uint8_t) (amount & 0x00FFu);
return Modbus_send(to_Send, 12);
}
/**
* Write Request Builder and Sender
* @param address Reference Address
* @param amount Amount of data to be Written
* @param func Modbus Functional Code
* @param value Data to Be Written
*/
int ModbusTCP::Modbus_write( uint16_t Address, int amount,int funCode,const uint16_t *value )
{
int status = 0;
if(funCode == WRITE_COIL || funCode == WRITE_REG) {
uint8_t to_Send[12];
Modbus_build_request(to_Send, Address, funCode);
to_Send[5] = 6;
to_Send[10] = (uint8_t) (value[0] >> 8u);
to_Send[11] = (uint8_t) (value[0] & 0x00FFu);
status = Modbus_send(to_Send, 12);
} else if(funCode == WRITE_REGS){
uint8_t * to_Send = new uint8_t[13+2*amount];
Modbus_build_request(to_Send, Address, funCode);
to_Send[5] = (uint8_t) (7 + 2 * amount);
to_Send[10] = (uint8_t) (amount >> 8u);
to_Send[11] = (uint8_t) (amount & 0x00FFu);
to_Send[12] = (uint8_t) (2 * amount);
for(int i = 0; i < amount; i++) {
to_Send[13 + 2 * i] = (uint8_t) (value[i] >> 8u);
to_Send[14 + 2 * i] = (uint8_t) (value[i] & 0x00FFu);
}
status = Modbus_send(to_Send, 13 + 2 * amount);
delete []to_Send;
} else if(funCode == WRITE_COILS) {
uint8_t * to_Send = new uint8_t[14 + (amount -1) / 8];
Modbus_build_request(to_Send, Address, funCode);
to_Send[5] = (uint8_t) (7 + (amount + 7) / 8);
to_Send[10] = (uint8_t) (amount >> 8u);
to_Send[11] = (uint8_t) (amount & 0x00FFu);
to_Send[12] = (uint8_t) ((amount + 7) / 8);
for(int i = 0; i < (amount+7)/8; i++)
to_Send[13 + i] = 0; // init needed before summing!
for(int i = 0; i < amount; i++) {
to_Send[13 + i/8] += (uint8_t) (value[i] << (i % 8u));
}
status = Modbus_send(to_Send, 14 + (amount - 1) / 8);
delete[]to_Send;
}
return status;
}
int ModbusTCP::Modbus_send( uint8_t *to_Send, int length )
{
m_iMsgID++;
return send(m_socket,(char *)to_Send, (size_t)length, 0);
}
int ModbusTCP::Modbus_receive( const uint8_t *buffer )
{
return recv(m_socket, (char*) buffer, 1024, 0);
}
//
/**
* Read Coils 读取线圈
* MODBUS FUNCTION 0x01
* @param address Reference Address
* @param amount Amount of Coils to Read
* @param buffer Buffer to Store Data Read from Coils
*/
int ModbusTCP::Modbus_read_coils( uint16_t Address, int amount, bool* buffer )
{
if(m_bConnect){
if(amount > 2040 || Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_read(Address, amount, READ_COILS);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, READ_COILS);
for(int i = 0; i < amount; i++) {
buffer[i] = (bool) ((to_Rec[9u + i / 8u] >> (i % 8u)) & 1u);
}
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
void ModbusTCP::modbuserror_handle( const uint8_t *msg, int funCode )
{
if(msg[7] == funCode + 0x80) {
err = true;
switch(msg[8]){
case EX_ILLEGAL_FUNCTION:
error_msg = "Illegal Function_1";
break;
case EX_ILLEGAL_ADDRESS:
error_msg = "Illegal Address_2";
break;
case EX_ILLEGAL_VALUE:
error_msg = "Illegal Value_3";
break;
case EX_SERVER_FAILURE:
error_msg = "Server Failure_4";
break;
case EX_ACKNOWLEDGE:
error_msg = "Acknowledge_5";
break;
case EX_SERVER_BUSY:
error_msg = "Server Busy_6";
break;
case EX_NEGATIVE_ACK:
error_msg = "Memory Parity Problem_7";
break;
case EX_MEM_PARITY_PROB:
error_msg = "Memory Parity Problem_8";
break;
case EX_GATEWAY_PROBLEMP:
error_msg = "Gateway Path Unavailable_9";
break;
case EX_GATEWYA_PROBLEMF:
error_msg = "Gateway Target Device Failed to Respond_10";
break;
default:
error_msg = "UNK_未知错误";
break;
}
}
err = false;
error_msg = "NO ERR";
}
//
int ModbusTCP::Modbus_read_input_bits( uint16_t Address, int amount, bool* buffer )
{
if(m_bConnect){
if(amount > 2040 || Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_read(Address, amount, READ_INPUT_BITS);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength= Modbus_receive(to_Rec);
if (recvLength<0) {
set_bad_connect();
return DISCONNECT;
}
for(int i = 0; i < amount; i++) {
buffer[i] = (bool) ((to_Rec[9u + i / 8u] >> (i % 8u)) & 1u);
}
modbuserror_handle(to_Rec, READ_INPUT_BITS);
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
//将读取多个寄存器写到一起
int ModbusTCP::Modbus_read_holding_registers( uint16_t Address, int amount, uint16_t *buffer )
{
if(m_bConnect){
if(amount > 65535 || Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_read(Address, amount, READ_REGS);
Sleep(500);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength<0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, READ_REGS);
for(int i = 0; i < amount; i++) {
buffer[i] = (uint16_t)(to_Rec[9u + 2u * i] << 8u);
buffer[i] += (uint16_t)to_Rec[10u + 2u * i];
}
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
int ModbusTCP::Modbus_read_input_registers( uint16_t Address, int amount, uint16_t *buffer )
{
if(m_bConnect){
if(amount > 65535 || Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_read(Address, amount, READ_INPUT_REGS);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, READ_INPUT_REGS);
for(int i = 0; i < amount; i++) {
buffer[i] = ((uint16_t)to_Rec[9u + 2u * i]) << 8u;
buffer[i] += (uint16_t) to_Rec[10u + 2u * i];
}
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
int ModbusTCP::Modbus_write_coil( uint16_t Address, const bool& to_Write )
{
if(m_bConnect){
if(Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
int value = to_Write * 0xFF00;
Modbus_write(Address, 1, WRITE_COIL, (uint16_t *)&value);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, WRITE_COIL);
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
int ModbusTCP::Modbus_write_register( uint16_t Address, const uint16_t& value )
{
if(m_bConnect){
if(Address > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_write(Address, 1, WRITE_REG, &value);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength =Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, WRITE_REG);
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
int ModbusTCP::Modbus_write_coils( uint16_t Address, int amount, const bool *value )
{
if(m_bConnect){
if(Address > 65535 || amount > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
uint16_t* temp = new uint16_t[amount];
for(int i = 0; i < amount; i++) {
temp[i] = (uint16_t)value[i];
}
Modbus_write(Address, amount, WRITE_COILS, temp);
delete []temp;
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, WRITE_COILS);
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
int ModbusTCP::Modbus_write_registers( uint16_t Address, int amount,uint16_t *value )
{
if(m_bConnect){
if(Address > 65535 || amount > 65535) {
set_bad_input();
return EX_BAD_DATA;
}
Modbus_write(Address, amount, WRITE_REGS, value);
Sleep(3);
uint8_t to_Rec[MAX_MSG_LENGTH];
int recvLength = Modbus_receive(to_Rec);
if (recvLength < 0) {
set_bad_connect();
return DISCONNECT;
}
modbuserror_handle(to_Rec, WRITE_REGS);
return 0;
}else{
set_bad_connect();
return DISCONNECT;
}
}
//从两个uint16组合成float
float ModbusTCP::Modbus_get_float( uint16_t buffer1,uint16_t buffer2 )
{
float fTemp;
unsigned int *pTemp = (unsigned int *)&fTemp;
unsigned int chTemp[4];
chTemp[0] = buffer1&0xff;
chTemp[1] = (buffer1>>8)&0xff;
chTemp[2] = buffer2&0xff;
chTemp[3] = (buffer2>>8)&0xff;
*pTemp = ((chTemp[1]<<24)&0xff000000)|((chTemp[0]<<16)&0xff0000)|((chTemp[3]<<8)&0xff00)|(chTemp[2]&0xff);
return fTemp;
}
void ModbusTCP::set_bad_connect()
{
err = true;
error_msg = "DISCONNECT";
}
void ModbusTCP::set_bad_input()
{
err = true;
error_msg = "BAD FUNCTION INPUT";
}
Main函数就不贴了,我去研究下CRC(循环冗余校验)LRC(和校验后期再说)
Happy Ending! YapethsDY.2020/09/23 PM.
套个文案
无暇独享艳阳天
以终为始不得闲
南来北往观花榭
常与人言无二三
若同在
少抱怨
心有萤火克万难!
来源:oschina
链接:https://my.oschina.net/u/4363105/blog/4645449