概要
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.
套個文案
無暇獨享艷陽天
以終為始不得閑
南來北往觀花榭
常與人言無二三
若同在
少抱怨
心有螢火克萬難!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/126194.html
標籤:其他
