Modbus是OSI模型第七層上的應用層報文傳輸協定,最早由施耐德出版,後將其轉讓,現已變成工業自動化等領域裡一種常見的、約定俗成的標準通訊協定標準。該標準包括兩個通訊規程中使用的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)構成。
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 主站裝置提供的物件型別表
物件型別 | 許可權 | 大小 | 內容 |
---|---|---|---|
線圈 | 讀寫 | 1 bit | I/O系統提供該型別資料 |
離散輸入 | 唯讀 | 1 bit | 通過應用程式改變這種型別資料 |
輸入暫存器 | 唯讀 | 16 bits | I/O系統提供該型別資料 |
保持暫存器 | 讀寫 | 16 bits | 通過應用程式改變這種型別資料 |
Modbus協定定義了一個與基礎通訊層無關的簡單協定資料單元(PDU)。特定匯流排或網路上的Modbus協定對映能夠在應用資料單元(ADU)上引入一些附加域。
ADU = 地址域 + PDU(功能碼 + 資料) + 差錯校驗(CRC),主要應用於485等非同步線路
名稱 | 長度(bits) | 功能 |
---|---|---|
開始 | 28 | 至少3又1/2 個字元的沉默時間(標記條件) |
地址 | 8 | 站地址 |
功能碼 | 8 | 功能碼; 例如,讀取線圈/保持暫存器 |
資料 | n * 8 | 資料+長度將根據訊息型別填充 |
CRC | 16 | 迴圈冗餘校驗 |
停止位 | 28 | 幀之間至少有3又1/2個字元的靜音時間 |
ADU = 地址域 + PDU(功能碼 + 資料) + 差錯校驗(LRC),主要用於 7 位或 8 位非同步序列線
名稱 | 長度(bytes) | 功能 |
---|---|---|
開始 | 1 | 以冒號開頭:(ASCII十六進位制值為3A) |
地址 | 2 | 站地址 |
功能碼 | 2 | 功能碼; 例如,讀取線圈/保持暫存器 |
資料 | n * 2 | 資料+長度將根據訊息型別填充 |
LRC | 2 | 校驗和(縱向冗餘校驗) |
停止位 | 2 | 回車 - 換行(CR / LF)對(ASCII值為0D,0A) |
ADU = MBMP報文頭 + PDU(功能碼 + 資料),主要用於乙太網,資料傳輸遵循大端順序,即高位在前低位在後
名稱 | 長度(bytes) | 功能 | 預設值 |
---|---|---|---|
傳輸識別符號 | 2 | 用於伺服器和使用者端的訊息之間的同步 | 0x00 0x00 |
協定識別符號 | 2 | 0表示 Modbus / TCP協定 | 0x00 0x00 |
長度欄位 | 2 | 此幀中的剩餘位元組數 | |
單元識別符號 | 1 | 從站地址(如果不使用則為255) | 從站識別符號 |
功能碼 | 1 | 功能碼與其他變體一樣 | |
資料 | n | 資料作為響應或命令 |
CRC / LRC校驗,現在還不太明白,後續自己應該跟進一下
使用環境為Modbus_tcp,所以以下測試及例子皆是在Tcp/Ip環境下使用
使用者端請求 | 伺服器響應 | ||
域名 | 十六進位制 | 域名 | 十六進位制 |
功能碼 | 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 | 功能 | 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.
套個文案
無暇獨享豔陽天
以終為始不得閒
南來北往觀花榭
常與人言無二三
若同在
少抱怨
心有螢火克萬難!