2020-09-23 Modbus协议详解 YapethsDY

 

概要

        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.

套个文案

无暇独享艳阳天

以终为始不得闲

南来北往观花榭

常与人言无二三

若同在

少抱怨

心有萤火克万难!

猜你喜欢

转载自blog.csdn.net/Ding86341631/article/details/108746287