Added a 3rd version of the code. Need to check which is which
This commit is contained in:
parent
d564f459f6
commit
7feff1dc5a
@ -1,5 +1,4 @@
|
|||||||
#include "modbus_tcp_client.h"
|
#include "modbus_tcp_client.h"
|
||||||
#include <sys/select.h>
|
|
||||||
|
|
||||||
// --- Helper: Compute maximum read response size based on configured counts.
|
// --- Helper: Compute maximum read response size based on configured counts.
|
||||||
int ModbusTCPClient::computeMaxReadResponseSize() const {
|
int ModbusTCPClient::computeMaxReadResponseSize() const {
|
||||||
@ -33,12 +32,13 @@ ModbusTCPClient::ModbusTCPClient(const char* ip, int port, int numCoils, int num
|
|||||||
startCoils(startCoils), startDiscreteInputs(startDI), startInputRegisters(startIR), startHoldingRegisters(startHR) {
|
startCoils(startCoils), startDiscreteInputs(startDI), startInputRegisters(startIR), startHoldingRegisters(startHR) {
|
||||||
|
|
||||||
// Allocate internal storage for automatic readAll()/writeAll() mode.
|
// Allocate internal storage for automatic readAll()/writeAll() mode.
|
||||||
coilsRead = new bool[numCoils]();
|
coilsRead = (numCoils > 0) ? new bool[numCoils]() : nullptr;
|
||||||
coilsWrite = new bool[numCoils]();
|
coilsWrite = (numCoils > 0) ? new bool[numCoils]() : nullptr;
|
||||||
discreteInputs = new bool[numDI]();
|
discreteInputs = (numDI > 0) ? new bool[numDI]() : nullptr;
|
||||||
inputRegisters = new uint16_t[numIR]();
|
inputRegisters = (numIR > 0) ? new uint16_t[numIR]() : nullptr;
|
||||||
holdingRegistersRead = new uint16_t[numHR]();
|
holdingRegistersRead = (numHR > 0) ? new uint16_t[numHR]() : nullptr;
|
||||||
holdingRegistersWrite = new uint16_t[numHR]();
|
holdingRegistersWrite = (numHR > 0) ? new uint16_t[numHR]() : nullptr;
|
||||||
|
|
||||||
|
|
||||||
// Allocate shared communication buffers.
|
// Allocate shared communication buffers.
|
||||||
commRequestBufferSize = computeMaxWriteRequestSize(); // Worst-case request size.
|
commRequestBufferSize = computeMaxWriteRequestSize(); // Worst-case request size.
|
||||||
@ -46,8 +46,12 @@ ModbusTCPClient::ModbusTCPClient(const char* ip, int port, int numCoils, int num
|
|||||||
commRequestBuffer = new uint8_t[commRequestBufferSize];
|
commRequestBuffer = new uint8_t[commRequestBufferSize];
|
||||||
commResponseBuffer = new uint8_t[commResponseBufferSize];
|
commResponseBuffer = new uint8_t[commResponseBufferSize];
|
||||||
|
|
||||||
// Initialize the socket mutex.
|
// Initialize the socket mutex with a recursive attribute.
|
||||||
pthread_mutex_init(&socketMutex, NULL);
|
pthread_mutexattr_t attr;
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
pthread_mutex_init(&socketMutex, &attr);
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusTCPClient::ModbusTCPClient(const char* ip, int port)
|
ModbusTCPClient::ModbusTCPClient(const char* ip, int port)
|
||||||
@ -66,8 +70,12 @@ ModbusTCPClient::ModbusTCPClient(const char* ip, int port)
|
|||||||
commRequestBuffer = new uint8_t[commRequestBufferSize];
|
commRequestBuffer = new uint8_t[commRequestBufferSize];
|
||||||
commResponseBuffer = new uint8_t[commResponseBufferSize];
|
commResponseBuffer = new uint8_t[commResponseBufferSize];
|
||||||
|
|
||||||
// Initialize the socket mutex.
|
// Initialize the socket mutex with a recursive attribute.
|
||||||
pthread_mutex_init(&socketMutex, NULL);
|
pthread_mutexattr_t attr;
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
pthread_mutex_init(&socketMutex, &attr);
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusTCPClient::~ModbusTCPClient() {
|
ModbusTCPClient::~ModbusTCPClient() {
|
||||||
@ -93,46 +101,134 @@ void ModbusTCPClient::setStartAddresses(int startCoils, int startDI, int startIR
|
|||||||
}
|
}
|
||||||
|
|
||||||
// --- Connection Functions ---
|
// --- Connection Functions ---
|
||||||
|
// bool ModbusTCPClient::connectServer() {
|
||||||
|
// //pthread_mutex_lock(&socketMutex);
|
||||||
|
// if (socketFD != -1) {
|
||||||
|
// //pthread_mutex_unlock(&socketMutex);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// for (int attempts = 0; attempts < 5; attempts++) {
|
||||||
|
// socketFD = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
// if (socketFD < 0) {
|
||||||
|
// printf("MODBUS_TCP_CLIENT: Could not create socket\n");
|
||||||
|
// //pthread_mutex_unlock(&socketMutex);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// struct sockaddr_in serverAddr;
|
||||||
|
// serverAddr.sin_family = AF_INET;
|
||||||
|
// serverAddr.sin_port = htons(serverPort);
|
||||||
|
// inet_pton(AF_INET, serverIP, &serverAddr.sin_addr);
|
||||||
|
// printf("MODBUS_TCP_CLIENT: Attempting to connect (Try %d)...\n", attempts + 1);
|
||||||
|
// if (connect(socketFD, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
|
||||||
|
// printf("MODBUS_TCP_CLIENT: Connected to %s:%d\n", serverIP, serverPort);
|
||||||
|
// //pthread_mutex_unlock(&socketMutex);
|
||||||
|
// return true;
|
||||||
|
// }
|
||||||
|
// printf("MODBUS_TCP_CLIENT: Connection failed, retrying...\n");
|
||||||
|
// disconnectServer();
|
||||||
|
// usleep(timeoutMilliseconds*1000);
|
||||||
|
// }
|
||||||
|
// //pthread_mutex_unlock(&socketMutex);
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
|
||||||
bool ModbusTCPClient::connectServer() {
|
bool ModbusTCPClient::connectServer() {
|
||||||
pthread_mutex_lock(&socketMutex);
|
// If already connected, return true.
|
||||||
if (socketFD != -1) {
|
if (socketFD != -1) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int attempts = 0; attempts < 5; attempts++) {
|
for (int attempts = 0; attempts < 5; attempts++) {
|
||||||
|
// Create a new socket.
|
||||||
socketFD = socket(AF_INET, SOCK_STREAM, 0);
|
socketFD = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
if (socketFD < 0) {
|
if (socketFD < 0) {
|
||||||
printf("MODBUS_TCP_CLIENT: Could not create socket\n");
|
printf("MODBUS_TCP_CLIENT: Could not create socket\n");
|
||||||
pthread_mutex_unlock(&socketMutex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set the socket to non-blocking mode.
|
||||||
|
int flags = fcntl(socketFD, F_GETFL, 0);
|
||||||
|
if (flags < 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: fcntl F_GETFL");
|
||||||
|
disconnectServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (fcntl(socketFD, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: fcntl F_SETFL O_NONBLOCK");
|
||||||
|
disconnectServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the server address.
|
||||||
struct sockaddr_in serverAddr;
|
struct sockaddr_in serverAddr;
|
||||||
serverAddr.sin_family = AF_INET;
|
serverAddr.sin_family = AF_INET;
|
||||||
serverAddr.sin_port = htons(serverPort);
|
serverAddr.sin_port = htons(serverPort);
|
||||||
inet_pton(AF_INET, serverIP, &serverAddr.sin_addr);
|
inet_pton(AF_INET, serverIP, &serverAddr.sin_addr);
|
||||||
|
|
||||||
printf("MODBUS_TCP_CLIENT: Attempting to connect (Try %d)...\n", attempts + 1);
|
printf("MODBUS_TCP_CLIENT: Attempting to connect (Try %d)...\n", attempts + 1);
|
||||||
if (connect(socketFD, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
|
int res = connect(socketFD, (struct sockaddr*)&serverAddr, sizeof(serverAddr));
|
||||||
printf("MODBUS_TCP_CLIENT: Connected to %s:%d\n", serverIP, serverPort);
|
if (res < 0) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
if (errno == EINPROGRESS) {
|
||||||
|
// Connection is in progress, use select() to wait.
|
||||||
|
fd_set wfds;
|
||||||
|
FD_ZERO(&wfds);
|
||||||
|
FD_SET(socketFD, &wfds);
|
||||||
|
|
||||||
|
struct timeval tv;
|
||||||
|
tv.tv_sec = timeoutMilliseconds / 1000;
|
||||||
|
tv.tv_usec = (timeoutMilliseconds % 1000) * 1000;
|
||||||
|
|
||||||
|
int sel = select(socketFD + 1, NULL, &wfds, NULL, &tv);
|
||||||
|
if (sel > 0) {
|
||||||
|
int so_error = 0;
|
||||||
|
socklen_t len = sizeof(so_error);
|
||||||
|
if (getsockopt(socketFD, SOL_SOCKET, SO_ERROR, &so_error, &len) < 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: getsockopt failed\n");
|
||||||
|
disconnectServer();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (so_error == 0) {
|
||||||
|
// Connected successfully. Restore blocking mode.
|
||||||
|
flags = fcntl(socketFD, F_GETFL, 0);
|
||||||
|
fcntl(socketFD, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connected to %s:%d\n", serverIP, serverPort);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connect failed with error %d\n", so_error);
|
||||||
|
}
|
||||||
|
} else if (sel == 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connect select timeout\n");
|
||||||
|
} else {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Select during connect failed\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connect error\n");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Unexpected immediate success in non-blocking mode.
|
||||||
|
flags = fcntl(socketFD, F_GETFL, 0);
|
||||||
|
fcntl(socketFD, F_SETFL, flags & ~O_NONBLOCK);
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connected immediately to %s:%d\n", serverIP, serverPort);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If connection failed, disconnect and wait before retrying.
|
||||||
printf("MODBUS_TCP_CLIENT: Connection failed, retrying...\n");
|
printf("MODBUS_TCP_CLIENT: Connection failed, retrying...\n");
|
||||||
pthread_mutex_unlock(&socketMutex);
|
|
||||||
disconnectServer();
|
disconnectServer();
|
||||||
usleep(100000);
|
usleep(timeoutMilliseconds * 1000);
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModbusTCPClient::disconnectServer() {
|
void ModbusTCPClient::disconnectServer() {
|
||||||
pthread_mutex_lock(&socketMutex);
|
////pthread_mutex_lock(&socketMutex);
|
||||||
if (socketFD != -1) {
|
if (socketFD != -1) {
|
||||||
|
shutdown(socketFD, SHUT_RDWR);
|
||||||
close(socketFD);
|
close(socketFD);
|
||||||
socketFD = -1;
|
socketFD = -1;
|
||||||
printf("MODBUS_TCP_CLIENT: Disconnected from server\n");
|
printf("MODBUS_TCP_CLIENT: Disconnected from server\n");
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//thread_mutex_unlock(&socketMutex);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ModbusTCPClient::isConnected() const {
|
bool ModbusTCPClient::isConnected() const {
|
||||||
@ -150,7 +246,7 @@ void ModbusTCPClient::setTimeout(int milliseconds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool ModbusTCPClient::sendRequest(uint8_t* request, int requestSize) {
|
bool ModbusTCPClient::sendRequest(uint8_t* request, int requestSize) {
|
||||||
if (socketFD == -1) {
|
if (!isConnected()) {
|
||||||
printf("MODBUS_TCP_CLIENT: Not connected. Cannot send request.\n");
|
printf("MODBUS_TCP_CLIENT: Not connected. Cannot send request.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -182,6 +278,11 @@ bool ModbusTCPClient::receiveResponse(uint8_t* response, int expectedSize) {
|
|||||||
disconnectServer();
|
disconnectServer();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Make sure we are still connected before we do a read to prevent a hardfault
|
||||||
|
if (!isConnected()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connection lost while reading. Disconnecting...\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
int bytesReceived = read(socketFD, response + totalBytesReceived, expectedSize - totalBytesReceived);
|
int bytesReceived = read(socketFD, response + totalBytesReceived, expectedSize - totalBytesReceived);
|
||||||
if (bytesReceived <= 0) {
|
if (bytesReceived <= 0) {
|
||||||
printf("MODBUS_TCP_CLIENT: Connection lost while reading. Disconnecting...\n");
|
printf("MODBUS_TCP_CLIENT: Connection lost while reading. Disconnecting...\n");
|
||||||
@ -262,33 +363,33 @@ void ModbusTCPClient::buildWriteMultipleRequest(uint8_t* buffer, ModbusFunction
|
|||||||
// --- High-Level Read/Write Functions ---
|
// --- High-Level Read/Write Functions ---
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::readCoil(int address, bool &coilState) {
|
ModbusError ModbusTCPClient::readCoil(int address, bool &coilState) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_COIL, address, 1);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_COIL, address, 1);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Expect 10 bytes: header (9 bytes) + 1 byte data
|
// Expect 10 bytes: header (9 bytes) + 1 byte data
|
||||||
int expectedSize = 9 + 1;
|
int expectedSize = 9 + 1;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle MODBUS exception responses (0x80 + function code)
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
// Ensure the function code in the response matches the request.
|
// Ensure the function code in the response matches the request.
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Set the internal coil state from the response
|
// Set the internal coil state from the response
|
||||||
coilState = (commResponseBuffer[9] & 0x01) != 0;
|
coilState = (commResponseBuffer[9] & 0x01) != 0;
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,28 +398,28 @@ ModbusError ModbusTCPClient::readMultipleCoils(int address, int count, bool coil
|
|||||||
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-2000 allowed)\n");
|
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-2000 allowed)\n");
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_COIL, address, count);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_COIL, address, count);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Expected response size: fixed header (9 bytes) + variable byte count.
|
// Expected response size: fixed header (9 bytes) + variable byte count.
|
||||||
int byteCount = (count + 7) / 8; // 1 byte per 8 coils
|
int byteCount = (count + 7) / 8; // 1 byte per 8 coils
|
||||||
int expectedSize = 9 + byteCount;
|
int expectedSize = 9 + byteCount;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Handle MODBUS exception responses (0x80 + function code)
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
// Ensure the function code in the response matches the request.
|
// Ensure the function code in the response matches the request.
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract coil values: data starts at index 9.
|
// Extract coil values: data starts at index 9.
|
||||||
@ -327,38 +428,38 @@ ModbusError ModbusTCPClient::readMultipleCoils(int address, int count, bool coil
|
|||||||
int bitIndex = i % 8;
|
int bitIndex = i % 8;
|
||||||
coilStates[i] = (commResponseBuffer[byteIndex] >> bitIndex) & 0x01;
|
coilStates[i] = (commResponseBuffer[byteIndex] >> bitIndex) & 0x01;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::readDiscreteInput(int address, bool &discreteInput) {
|
ModbusError ModbusTCPClient::readDiscreteInput(int address, bool &discreteInput) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
// Build a request for one discrete input (quantity = 1)
|
// Build a request for one discrete input (quantity = 1)
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_DISCRETE_INPUT, address, 1);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_DISCRETE_INPUT, address, 1);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Expect 10 bytes: header (9 bytes) + 1 byte data
|
// Expect 10 bytes: header (9 bytes) + 1 byte data
|
||||||
int expectedSize = 9 + 1;
|
int expectedSize = 9 + 1;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Check for MODBUS exception response
|
// Check for MODBUS exception response
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
// Validate function code
|
// Validate function code
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract the discrete input state (first bit of the data byte at index 9)
|
// Extract the discrete input state (first bit of the data byte at index 9)
|
||||||
discreteInput = (commResponseBuffer[9] & 0x01) != 0;
|
discreteInput = (commResponseBuffer[9] & 0x01) != 0;
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,26 +468,26 @@ ModbusError ModbusTCPClient::readMultipleDiscreteInputs(int address, int count,
|
|||||||
printf("MODBUS_TCP_CLIENT: Invalid discrete input count (1- allowed)\n");
|
printf("MODBUS_TCP_CLIENT: Invalid discrete input count (1- allowed)\n");
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_DISCRETE_INPUT, address, count);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_DISCRETE_INPUT, address, count);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Expected response: 9-byte header + ceil(count/8) bytes of data
|
// Expected response: 9-byte header + ceil(count/8) bytes of data
|
||||||
int byteCount = (count + 7) / 8;
|
int byteCount = (count + 7) / 8;
|
||||||
int expectedSize = 9 + byteCount;
|
int expectedSize = 9 + byteCount;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract each discrete input bit from the data starting at index 9
|
// Extract each discrete input bit from the data starting at index 9
|
||||||
@ -395,35 +496,35 @@ ModbusError ModbusTCPClient::readMultipleDiscreteInputs(int address, int count,
|
|||||||
int bitIndex = i % 8;
|
int bitIndex = i % 8;
|
||||||
discreteInputsArray[i] = (commResponseBuffer[byteIndex] >> bitIndex) & 0x01;
|
discreteInputsArray[i] = (commResponseBuffer[byteIndex] >> bitIndex) & 0x01;
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::readHoldingRegister(int address, uint16_t &holdingRegister) {
|
ModbusError ModbusTCPClient::readHoldingRegister(int address, uint16_t &holdingRegister) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_HOLDING_REGISTER, address, 1);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_HOLDING_REGISTER, address, 1);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// For one register, expect 9-byte header + 2 bytes data = 11 bytes total
|
// For one register, expect 9-byte header + 2 bytes data = 11 bytes total
|
||||||
int expectedSize = 9 + 2;
|
int expectedSize = 9 + 2;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract the register value (big-endian: data at indices 9 and 10)
|
// Extract the register value (big-endian: data at indices 9 and 10)
|
||||||
holdingRegister = (commResponseBuffer[9] << 8) | commResponseBuffer[10];
|
holdingRegister = (commResponseBuffer[9] << 8) | commResponseBuffer[10];
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -432,59 +533,59 @@ ModbusError ModbusTCPClient::readMultipleHoldingRegisters(int address, int count
|
|||||||
printf("MODBUS_TCP_CLIENT: Invalid holding register count (1-125 allowed)\n");
|
printf("MODBUS_TCP_CLIENT: Invalid holding register count (1-125 allowed)\n");
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_HOLDING_REGISTER, address, count);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_HOLDING_REGISTER, address, count);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// For multiple registers: expected size = 9 + (count * 2)
|
// For multiple registers: expected size = 9 + (count * 2)
|
||||||
int expectedSize = 9 + (count * 2);
|
int expectedSize = 9 + (count * 2);
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract each register value (each register is 2 bytes, big-endian)
|
// Extract each register value (each register is 2 bytes, big-endian)
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
holdingRegistersArray[i] = (commResponseBuffer[9 + (i * 2)] << 8) | commResponseBuffer[10 + (i * 2)];
|
holdingRegistersArray[i] = (commResponseBuffer[9 + (i * 2)] << 8) | commResponseBuffer[10 + (i * 2)];
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::readInputRegister(int address, uint16_t &inputRegister) {
|
ModbusError ModbusTCPClient::readInputRegister(int address, uint16_t &inputRegister) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_INPUT_REGISTER, address, 1);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_INPUT_REGISTER, address, 1);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// For one input register: expected size = 9 + 2 = 11 bytes
|
// For one input register: expected size = 9 + 2 = 11 bytes
|
||||||
int expectedSize = 9 + 2;
|
int expectedSize = 9 + 2;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
inputRegister = (commResponseBuffer[9] << 8) | commResponseBuffer[10];
|
inputRegister = (commResponseBuffer[9] << 8) | commResponseBuffer[10];
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -493,63 +594,63 @@ ModbusError ModbusTCPClient::readMultipleInputRegisters(int address, int count,
|
|||||||
printf("MODBUS_TCP_CLIENT: Invalid input register count (1-125 allowed)\n");
|
printf("MODBUS_TCP_CLIENT: Invalid input register count (1-125 allowed)\n");
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildReadRequest(commRequestBuffer, ModbusFunction::READ_INPUT_REGISTER, address, count);
|
buildReadRequest(commRequestBuffer, ModbusFunction::READ_INPUT_REGISTER, address, count);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// For multiple registers: expected size = 9 + (count * 2)
|
// For multiple registers: expected size = 9 + (count * 2)
|
||||||
int expectedSize = 9 + (count * 2);
|
int expectedSize = 9 + (count * 2);
|
||||||
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] & 0x80) {
|
if (commResponseBuffer[7] & 0x80) {
|
||||||
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
printf("MODBUS_TCP_CLIENT: Exception Code %02X\n", commResponseBuffer[8]);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::EXCEPTION_RESPONSE;
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
}
|
}
|
||||||
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
if (commResponseBuffer[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Extract each register value (each register is 2 bytes, big-endian)
|
// Extract each register value (each register is 2 bytes, big-endian)
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
inputRegistersArray[i] = (commResponseBuffer[9 + (i * 2)] << 8) | commResponseBuffer[10 + (i * 2)];
|
inputRegistersArray[i] = (commResponseBuffer[9 + (i * 2)] << 8) | commResponseBuffer[10 + (i * 2)];
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::writeCoil(int address, bool value) {
|
ModbusError ModbusTCPClient::writeCoil(int address, bool value) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
buildWriteSingleRequest(commRequestBuffer, ModbusFunction::WRITE_SINGLE_COIL, address, value ? 0xFF00 : 0x0000);
|
buildWriteSingleRequest(commRequestBuffer, ModbusFunction::WRITE_SINGLE_COIL, address, value ? 0xFF00 : 0x0000);
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
if (!receiveResponse(commResponseBuffer, 12)) {
|
if (!receiveResponse(commResponseBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
||||||
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::writeMultipleCoils(int address, int count, const bool values[]) {
|
ModbusError ModbusTCPClient::writeMultipleCoils(int address, int count, const bool values[]) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
|
|
||||||
if (count < 1 || count > numCoils) {
|
if (count < 1 || count > numCoils) {
|
||||||
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-%d allowed)\n", numCoils);
|
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-%d allowed)\n", numCoils);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -572,14 +673,14 @@ ModbusError ModbusTCPClient::writeMultipleCoils(int address, int count, const bo
|
|||||||
buildWriteMultipleRequest(commRequestBuffer, ModbusFunction::WRITE_MULTIPLE_COILS, address, count, coilData, byteCount);
|
buildWriteMultipleRequest(commRequestBuffer, ModbusFunction::WRITE_MULTIPLE_COILS, address, count, coilData, byteCount);
|
||||||
// Send the request.
|
// Send the request.
|
||||||
if (!sendRequest(commRequestBuffer, requestSize)) {
|
if (!sendRequest(commRequestBuffer, requestSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The expected response size for a write multiple coils request is always 12 bytes.
|
// The expected response size for a write multiple coils request is always 12 bytes.
|
||||||
int expectedResponseSize = 12;
|
int expectedResponseSize = 12;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedResponseSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedResponseSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,45 +690,45 @@ ModbusError ModbusTCPClient::writeMultipleCoils(int address, int count, const bo
|
|||||||
if (i == 5) continue; // Skip the length field.
|
if (i == 5) continue; // Skip the length field.
|
||||||
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
||||||
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::writeHoldingRegister(int address, uint16_t value) {
|
ModbusError ModbusTCPClient::writeHoldingRegister(int address, uint16_t value) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
// Build the write single holding register request.
|
// Build the write single holding register request.
|
||||||
buildWriteSingleRequest(commRequestBuffer, ModbusFunction::WRITE_SINGLE_HOLDING_REGISTER, address, value);
|
buildWriteSingleRequest(commRequestBuffer, ModbusFunction::WRITE_SINGLE_HOLDING_REGISTER, address, value);
|
||||||
// Send the 12-byte request.
|
// Send the 12-byte request.
|
||||||
if (!sendRequest(commRequestBuffer, 12)) {
|
if (!sendRequest(commRequestBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Expect a full 12-byte echo response.
|
// Expect a full 12-byte echo response.
|
||||||
if (!receiveResponse(commResponseBuffer, 12)) {
|
if (!receiveResponse(commResponseBuffer, 12)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Validate that the entire 12-byte response matches the request.
|
// Validate that the entire 12-byte response matches the request.
|
||||||
for (int i = 0; i < 12; i++) {
|
for (int i = 0; i < 12; i++) {
|
||||||
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
||||||
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]) {
|
ModbusError ModbusTCPClient::writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]) {
|
||||||
pthread_mutex_lock(&socketMutex);
|
//pthread_mutex_lock(&socketMutex);
|
||||||
if (count < 1 || count > numHoldingRegisters) {
|
if (count < 1 || count > numHoldingRegisters) {
|
||||||
printf("MODBUS_TCP_CLIENT: Invalid register count (1-%d allowed)\n", numHoldingRegisters);
|
printf("MODBUS_TCP_CLIENT: Invalid register count (1-%d allowed)\n", numHoldingRegisters);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
// Each register is 2 bytes.
|
// Each register is 2 bytes.
|
||||||
@ -644,13 +745,13 @@ ModbusError ModbusTCPClient::writeMultipleHoldingRegisters(int address, int coun
|
|||||||
buildWriteMultipleRequest(commRequestBuffer, ModbusFunction::WRITE_MULTIPLE_HOLDING_REGISTERS, address, count, registerData, byteCount);
|
buildWriteMultipleRequest(commRequestBuffer, ModbusFunction::WRITE_MULTIPLE_HOLDING_REGISTERS, address, count, registerData, byteCount);
|
||||||
// Send the request.
|
// Send the request.
|
||||||
if (!sendRequest(commRequestBuffer, requestSize)) {
|
if (!sendRequest(commRequestBuffer, requestSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// The expected response size for a write multiple holding registers request is 12 bytes.
|
// The expected response size for a write multiple holding registers request is 12 bytes.
|
||||||
int expectedResponseSize = 12;
|
int expectedResponseSize = 12;
|
||||||
if (!receiveResponse(commResponseBuffer, expectedResponseSize)) {
|
if (!receiveResponse(commResponseBuffer, expectedResponseSize)) {
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::TIMEOUT;
|
return ModbusError::TIMEOUT;
|
||||||
}
|
}
|
||||||
// Validate the response: Compare the first 10 bytes, skipping byte 5 (length field).
|
// Validate the response: Compare the first 10 bytes, skipping byte 5 (length field).
|
||||||
@ -658,16 +759,24 @@ ModbusError ModbusTCPClient::writeMultipleHoldingRegisters(int address, int coun
|
|||||||
if (i == 5) continue; // Skip the length field.
|
if (i == 5) continue; // Skip the length field.
|
||||||
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
if (commRequestBuffer[i] != commResponseBuffer[i]) {
|
||||||
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
printf("MODBUS_TCP_CLIENT: Response does not match request at byte %d!\n", i);
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::INVALID_RESPONSE;
|
return ModbusError::INVALID_RESPONSE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pthread_mutex_unlock(&socketMutex);
|
//pthread_mutex_unlock(&socketMutex);
|
||||||
return ModbusError::NONE;
|
return ModbusError::NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::readAll() {
|
ModbusError ModbusTCPClient::readAll() {
|
||||||
// For brevity, call low-level functions that update internal storage.
|
// For brevity, call low-level functions that update internal storage.
|
||||||
|
printf("readAll()\n");
|
||||||
|
if (!isConnected()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Not connected when readAll() called. Connecting...");
|
||||||
|
if (connectServer()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Failed to connect to MODBUS server\n");
|
||||||
|
}
|
||||||
|
return ModbusError::CONNECTION_LOST;
|
||||||
|
}
|
||||||
ModbusError error = ModbusError::NONE;
|
ModbusError error = ModbusError::NONE;
|
||||||
if (coilsRead) {
|
if (coilsRead) {
|
||||||
error = readMultipleCoils(startCoils, numCoils, coilsRead);
|
error = readMultipleCoils(startCoils, numCoils, coilsRead);
|
||||||
@ -688,6 +797,13 @@ ModbusError ModbusTCPClient::readAll() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ModbusError ModbusTCPClient::writeAll() {
|
ModbusError ModbusTCPClient::writeAll() {
|
||||||
|
if (!isConnected()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Not connected when readAll() called. Connecting...");
|
||||||
|
if (connectServer()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Failed to connect to MODBUS server\n");
|
||||||
|
}
|
||||||
|
return ModbusError::CONNECTION_LOST;
|
||||||
|
}
|
||||||
ModbusError error = ModbusError::NONE;
|
ModbusError error = ModbusError::NONE;
|
||||||
if (coilsWrite) {
|
if (coilsWrite) {
|
||||||
error = writeMultipleCoils(startCoils, numCoils, coilsWrite);
|
error = writeMultipleCoils(startCoils, numCoils, coilsWrite);
|
||||||
@ -735,4 +851,80 @@ uint16_t ModbusTCPClient::getDesiredHoldingRegister(int address) const {
|
|||||||
|
|
||||||
uint16_t ModbusTCPClient::getInputRegister(int address) const {
|
uint16_t ModbusTCPClient::getInputRegister(int address) const {
|
||||||
return (address >= 0 && address < numInputRegisters) ? inputRegisters[address] : 0;
|
return (address >= 0 && address < numInputRegisters) ? inputRegisters[address] : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::getMultipleDiscreteInputs(int startAddress, int count, bool* destination) const {
|
||||||
|
// Validate inputs
|
||||||
|
if (startAddress < 0 || count <= 0 || destination == nullptr) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the range is within bounds
|
||||||
|
if (startAddress + count > numDiscreteInputs) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data manually
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
destination[i] = discreteInputs[startAddress + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::getMultipleCoils(int startAddress, int count, bool* destination) const {
|
||||||
|
// Validate inputs
|
||||||
|
if (startAddress < 0 || count <= 0 || destination == nullptr) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the range is within bounds
|
||||||
|
if (startAddress + count > numCoils) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data manually
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
destination[i] = coilsRead[startAddress + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::getMultipleInputRegisters(int startAddress, int count, uint16_t* destination) const {
|
||||||
|
// Validate inputs
|
||||||
|
if (startAddress < 0 || count <= 0 || destination == nullptr) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the range is within bounds
|
||||||
|
if (startAddress + count > numInputRegisters) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data manually
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
destination[i] = inputRegisters[startAddress + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::getMultipleHoldingRegisters(int startAddress, int count, uint16_t* destination) const {
|
||||||
|
// Validate inputs
|
||||||
|
if (startAddress < 0 || count <= 0 || destination == nullptr) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the range is within bounds
|
||||||
|
if (startAddress + count > numHoldingRegisters) {
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy the data manually
|
||||||
|
for (int i = 0; i < count; ++i) {
|
||||||
|
destination[i] = holdingRegistersRead[startAddress + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|||||||
@ -4,8 +4,10 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <sys/select.h>
|
||||||
#include <pthread.h> // Use mutexes
|
#include <pthread.h> // Use mutexes
|
||||||
|
|
||||||
// Enum class for MODBUS function codes
|
// Enum class for MODBUS function codes
|
||||||
@ -54,12 +56,18 @@ public:
|
|||||||
// Getters and setters for data values (if using the automatic mode).
|
// Getters and setters for data values (if using the automatic mode).
|
||||||
void setCoil(int address, bool value);
|
void setCoil(int address, bool value);
|
||||||
void setHoldingRegister(int address, uint16_t value);
|
void setHoldingRegister(int address, uint16_t value);
|
||||||
|
|
||||||
|
bool getDiscreteInput(int address) const;
|
||||||
bool getCoil(int address) const;
|
bool getCoil(int address) const;
|
||||||
bool getDesiredCoil(int address) const;
|
bool getDesiredCoil(int address) const;
|
||||||
bool getDiscreteInput(int address) const;
|
uint16_t getInputRegister(int address) const;
|
||||||
uint16_t getHoldingRegister(int address) const;
|
uint16_t getHoldingRegister(int address) const;
|
||||||
uint16_t getDesiredHoldingRegister(int address) const;
|
uint16_t getDesiredHoldingRegister(int address) const;
|
||||||
uint16_t getInputRegister(int address) const;
|
|
||||||
|
ModbusError getMultipleCoils(int startAddress, int count, bool* destination) const;
|
||||||
|
ModbusError getMultipleDiscreteInputs(int startAddress, int count, bool* destination) const;
|
||||||
|
ModbusError getMultipleInputRegisters(int startAddress, int count, uint16_t* destination) const;
|
||||||
|
ModbusError getMultipleHoldingRegisters(int startAddress, int count, uint16_t* destination) const;
|
||||||
|
|
||||||
// High-level functions: readAll and writeAll update the internal buffers.
|
// High-level functions: readAll and writeAll update the internal buffers.
|
||||||
ModbusError readAll();
|
ModbusError readAll();
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -6,6 +6,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
#include <sys/socket.h>
|
#include <sys/socket.h>
|
||||||
|
#include <pthread.h> // Use mutexes
|
||||||
|
|
||||||
// Enum class for MODBUS function codes
|
// Enum class for MODBUS function codes
|
||||||
enum class ModbusFunction : uint8_t {
|
enum class ModbusFunction : uint8_t {
|
||||||
@ -19,68 +20,63 @@ enum class ModbusFunction : uint8_t {
|
|||||||
WRITE_MULTIPLE_HOLDING_REGISTERS = 0x10
|
WRITE_MULTIPLE_HOLDING_REGISTERS = 0x10
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Enum class for error codes
|
||||||
enum class ModbusError {
|
enum class ModbusError {
|
||||||
NONE = 0, // No error
|
NONE = 0,
|
||||||
TIMEOUT, // No response from server
|
TIMEOUT,
|
||||||
INVALID_RESPONSE, // Response does not match request
|
INVALID_RESPONSE,
|
||||||
CONNECTION_LOST, // Connection issue
|
CONNECTION_LOST,
|
||||||
EXCEPTION_RESPONSE, // MODBUS Exception response (error code)
|
EXCEPTION_RESPONSE,
|
||||||
INVALID_REQUEST, // User tries doing something they shouldn't
|
INVALID_REQUEST
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ModbusTCPClient {
|
class ModbusTCPClient {
|
||||||
public:
|
public:
|
||||||
ModbusTCPClient(const char* ip, int port); // Extra constructor that does CANNOT use readAll() or writeAll()
|
// Constructor that allocates dynamic buffers based on provided counts and start addresses.
|
||||||
ModbusTCPClient(const char* ip, int port, int numCoils, int numDI, int numIR, int numHR,
|
ModbusTCPClient(const char* ip, int port, int numCoils, int numDI, int numIR, int numHR,
|
||||||
int startCoils = 0, int startDI = 0, int startIR = 0, int startHR = 0);
|
int startCoils = 0, int startDI = 0, int startIR = 0, int startHR = 0);
|
||||||
|
// Simpler constructor for manual MODBUS function calls (without readAll/writeAll)
|
||||||
|
ModbusTCPClient(const char* ip, int port);
|
||||||
~ModbusTCPClient();
|
~ModbusTCPClient();
|
||||||
|
|
||||||
// Set the start address of each type. Either use this after creating the object or put them in the constructor
|
// Set start addresses for each type.
|
||||||
void setStartAddresses(int startCoils, int startDI, int startIR, int startHR);
|
void setStartAddresses(int startCoils, int startDI, int startIR, int startHR);
|
||||||
|
|
||||||
|
// Connection functions
|
||||||
bool connectServer();
|
bool connectServer();
|
||||||
void disconnectServer();
|
void disconnectServer();
|
||||||
bool isConnected() const;
|
bool isConnected() const;
|
||||||
|
|
||||||
// Manually disconnectServer and return reconnectServer()
|
|
||||||
bool reconnectServer();
|
bool reconnectServer();
|
||||||
|
|
||||||
// Set the timeout for receiving responses
|
// Set the timeout (in milliseconds) for receiving responses.
|
||||||
void setTimeout(int milliseconds);
|
void setTimeout(int milliseconds);
|
||||||
|
|
||||||
// Setters (preferred to be used when calling writeAll())
|
// Getters and setters for data values (if using the automatic mode).
|
||||||
void setCoil(int address, bool value);
|
void setCoil(int address, bool value);
|
||||||
void setHoldingRegister(int address, uint16_t value);
|
void setHoldingRegister(int address, uint16_t value);
|
||||||
|
|
||||||
// Getters (preferred to be used when calling readAll())
|
|
||||||
bool getCoil(int address) const;
|
bool getCoil(int address) const;
|
||||||
bool getDesiredCoil(int address) const; // Retrieves the "to be written" value
|
bool getDesiredCoil(int address) const;
|
||||||
bool getDiscreteInput(int address) const;
|
bool getDiscreteInput(int address) const;
|
||||||
uint16_t getHoldingRegister(int address) const;
|
uint16_t getHoldingRegister(int address) const;
|
||||||
uint16_t getDesiredHoldingRegister(int address) const; // Retrieves the "to be written" value
|
uint16_t getDesiredHoldingRegister(int address) const;
|
||||||
uint16_t getInputRegister(int address) const;
|
uint16_t getInputRegister(int address) const;
|
||||||
|
|
||||||
ModbusError readAll(); // Reads every coil, DI, IR, and HR
|
// High-level functions: readAll and writeAll update the internal buffers.
|
||||||
ModbusError writeAll(); // Writes every coil and HR
|
ModbusError readAll();
|
||||||
|
ModbusError writeAll();
|
||||||
// Manual MODBUS TCP actions (not preferred to be called by user)
|
|
||||||
|
// Low-level MODBUS functions (manual calls)
|
||||||
ModbusError readCoil(int address, bool &coilState);
|
ModbusError readCoil(int address, bool &coilState);
|
||||||
ModbusError readMultipleCoils(int address, int count, bool coilStates[]);
|
ModbusError readMultipleCoils(int address, int count, bool coilStates[]);
|
||||||
|
|
||||||
ModbusError readDiscreteInput(int address, bool &discreteInput);
|
ModbusError readDiscreteInput(int address, bool &discreteInput);
|
||||||
ModbusError readMultipleDiscreteInputs(int address, int count, bool discreteInputs[]);
|
ModbusError readMultipleDiscreteInputs(int address, int count, bool discreteInputs[]);
|
||||||
|
|
||||||
ModbusError readHoldingRegister(int address, uint16_t &holdingRegister);
|
ModbusError readHoldingRegister(int address, uint16_t &holdingRegister);
|
||||||
ModbusError readMultipleHoldingRegisters(int address, int count, uint16_t holdingRegisters[]);
|
ModbusError readMultipleHoldingRegisters(int address, int count, uint16_t holdingRegisters[]);
|
||||||
|
|
||||||
ModbusError readInputRegister(int address, uint16_t &inputRegister);
|
ModbusError readInputRegister(int address, uint16_t &inputRegister);
|
||||||
ModbusError readMultipleInputRegisters(int address, int count, uint16_t inputRegisters[]);
|
ModbusError readMultipleInputRegisters(int address, int count, uint16_t inputRegisters[]);
|
||||||
|
|
||||||
ModbusError writeCoil(int address, bool value);
|
ModbusError writeCoil(int address, bool value);
|
||||||
ModbusError writeMultipleCoils(int address, int count, const bool values[]);
|
ModbusError writeMultipleCoils(int address, int count, const bool values[]);
|
||||||
|
|
||||||
ModbusError writeHoldingRegister(int address, uint16_t value);
|
ModbusError writeHoldingRegister(int address, uint16_t value);
|
||||||
ModbusError writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]);
|
ModbusError writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]);
|
||||||
|
|
||||||
@ -90,26 +86,42 @@ private:
|
|||||||
int serverPort;
|
int serverPort;
|
||||||
int socketFD;
|
int socketFD;
|
||||||
uint16_t transactionID;
|
uint16_t transactionID;
|
||||||
int timeoutMilliseconds = 2000; // Default 2 second timeout on receiving responses
|
int timeoutMilliseconds = 2000; // Default 2-second timeout
|
||||||
|
|
||||||
// Storing MODBUS register information
|
// Start addresses for each data type
|
||||||
|
int startCoils, startDiscreteInputs, startInputRegisters, startHoldingRegisters;
|
||||||
|
// Number of items for each type
|
||||||
int numCoils, numDiscreteInputs, numInputRegisters, numHoldingRegisters;
|
int numCoils, numDiscreteInputs, numInputRegisters, numHoldingRegisters;
|
||||||
int startCoils, startDiscreteInputs, startInputRegisters, startHoldingRegisters; // The start address of each type of register
|
|
||||||
|
|
||||||
bool* coilsRead; // Stores the actual state of coils on the MODBUS server
|
|
||||||
bool* coilsWrite; // Stores the desired state of coils to be written
|
|
||||||
bool* discreteInputs; // Only read from the MODBUS server (no writes)
|
|
||||||
|
|
||||||
uint16_t* inputRegisters; // Only read from the MODBUS server
|
|
||||||
uint16_t* holdingRegistersRead; // Stores actual values from the MODBUS server
|
|
||||||
uint16_t* holdingRegistersWrite; // Stores desired values to write
|
|
||||||
|
|
||||||
|
// Internal storage for automatic readAll()/writeAll() mode
|
||||||
|
bool* coilsRead; // Actual state from PLC
|
||||||
|
bool* coilsWrite; // Desired state to write
|
||||||
|
bool* discreteInputs; // Only read
|
||||||
|
uint16_t* inputRegisters; // Only read
|
||||||
|
uint16_t* holdingRegistersRead; // Actual values from PLC
|
||||||
|
uint16_t* holdingRegistersWrite; // Desired values to write
|
||||||
|
|
||||||
|
// --- Communication buffers (shared for both reading and writing)
|
||||||
|
uint8_t* commRequestBuffer; // Preallocated request buffer
|
||||||
|
uint8_t* commResponseBuffer; // Preallocated response buffer
|
||||||
|
int commRequestBufferSize; // Maximum request size needed
|
||||||
|
int commResponseBufferSize; // Maximum response size needed
|
||||||
|
|
||||||
|
// Global mutex to protect the TCP socket and communication buffers.
|
||||||
|
pthread_mutex_t socketMutex;
|
||||||
|
|
||||||
|
// Low-level communication functions using the shared buffers.
|
||||||
bool sendRequest(uint8_t* request, int requestSize);
|
bool sendRequest(uint8_t* request, int requestSize);
|
||||||
bool receiveResponse(uint8_t* response, int expectedSize);
|
bool receiveResponse(uint8_t* response, int expectedSize);
|
||||||
|
|
||||||
|
// Message building functions – they write into a provided buffer.
|
||||||
void buildReadRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t startAddr, uint16_t quantity);
|
void buildReadRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t startAddr, uint16_t quantity);
|
||||||
void buildWriteSingleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t value);
|
void buildWriteSingleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t value);
|
||||||
void buildWriteMultipleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t count, const void* values, uint8_t byteCount);
|
void buildWriteMultipleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t count, const void* values, uint8_t byteCount);
|
||||||
|
|
||||||
|
// Helper functions to compute maximum buffer sizes.
|
||||||
|
int computeMaxReadResponseSize() const;
|
||||||
|
int computeMaxWriteRequestSize() const;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // MODBUS_TCP_CLIENT_H
|
#endif // MODBUS_TCP_CLIENT_H
|
||||||
|
|||||||
757
modbus_tcp_client_old_old.cpp
Normal file
757
modbus_tcp_client_old_old.cpp
Normal file
@ -0,0 +1,757 @@
|
|||||||
|
#include "modbus_tcp_client.h"
|
||||||
|
#include <sys/select.h> // For select() timeout
|
||||||
|
|
||||||
|
ModbusTCPClient::ModbusTCPClient(const char* ip, int port, int numCoils, int numDI, int numIR, int numHR,
|
||||||
|
int startCoils, int startDI, int startIR, int startHR)
|
||||||
|
: serverIP(ip), serverPort(port), socketFD(-1), transactionID(1),
|
||||||
|
numCoils(numCoils), numDiscreteInputs(numDI), numInputRegisters(numIR), numHoldingRegisters(numHR),
|
||||||
|
startCoils(startCoils), startDiscreteInputs(startDI), startInputRegisters(startIR), startHoldingRegisters(startHR) {
|
||||||
|
|
||||||
|
// Allocate memory dynamically based on provided sizes
|
||||||
|
coilsRead = new bool[numCoils]();
|
||||||
|
coilsWrite = new bool[numCoils]();
|
||||||
|
discreteInputs = new bool[numDiscreteInputs]();
|
||||||
|
|
||||||
|
inputRegisters = new uint16_t[numInputRegisters]();
|
||||||
|
holdingRegistersRead = new uint16_t[numHoldingRegisters]();
|
||||||
|
holdingRegistersWrite = new uint16_t[numHoldingRegisters]();
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusTCPClient::ModbusTCPClient(const char* ip, int port)
|
||||||
|
: serverIP(ip), serverPort(port), socketFD(-1) {
|
||||||
|
/*
|
||||||
|
This constructor is if the user manually wants to call MODBUS TCP functions and not
|
||||||
|
use readAll() and writeAll() and the setters/getters
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Set everything to nullptr so that readAll() and writeAll() won't work
|
||||||
|
coilsRead = nullptr;
|
||||||
|
coilsWrite = nullptr;
|
||||||
|
discreteInputs = nullptr;
|
||||||
|
|
||||||
|
inputRegisters = nullptr;
|
||||||
|
holdingRegistersRead = nullptr;
|
||||||
|
holdingRegistersWrite = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusTCPClient::~ModbusTCPClient() {
|
||||||
|
delete[] coilsRead;
|
||||||
|
delete[] coilsWrite;
|
||||||
|
delete[] discreteInputs;
|
||||||
|
|
||||||
|
delete[] inputRegisters;
|
||||||
|
delete[] holdingRegistersRead;
|
||||||
|
delete[] holdingRegistersWrite;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::setStartAddresses(int startCoils, int startDI, int startIR, int startHR) {
|
||||||
|
this->startCoils = startCoils;
|
||||||
|
this->startDiscreteInputs = startDI;
|
||||||
|
this->startInputRegisters = startIR;
|
||||||
|
this->startHoldingRegisters = startHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::connectServer() {
|
||||||
|
// Step 1: If socket is already open, verify it's still connected
|
||||||
|
if (socketFD != -1) {
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = 0;
|
||||||
|
timeout.tv_usec = 100000; // 100ms timeout for connection check
|
||||||
|
|
||||||
|
fd_set read_fds;
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_SET(socketFD, &read_fds);
|
||||||
|
|
||||||
|
int result = select(socketFD + 1, &read_fds, NULL, NULL, &timeout);
|
||||||
|
|
||||||
|
if (result == 0) {
|
||||||
|
// No error in select, socket is still connected
|
||||||
|
printf("MODBUS_TCP_CLIENT: Already connected to MODBUS server\n");
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// Connection is broken, close and reset
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Connection lost, reconnecting...\n");
|
||||||
|
disconnectServer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Create a new socket
|
||||||
|
for (int attempts = 0; attempts < 5; attempts++) { // Retry up to 5 times
|
||||||
|
socketFD = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (socketFD < 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Could not create socket\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct sockaddr_in serverAddr;
|
||||||
|
serverAddr.sin_family = AF_INET;
|
||||||
|
serverAddr.sin_port = htons(serverPort);
|
||||||
|
inet_pton(AF_INET, serverIP, &serverAddr.sin_addr);
|
||||||
|
|
||||||
|
// Step 3: Attempt to connect
|
||||||
|
printf("MODBUS_TCP_CLIENT: Attempting to connect to MODBUS server (Try %d)...\n", attempts + 1);
|
||||||
|
if (connect(socketFD, (struct sockaddr*)&serverAddr, sizeof(serverAddr)) == 0) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connected to MODBUS server at %s:%d\n", serverIP, serverPort);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connection failed, retrying...\n");
|
||||||
|
disconnectServer(); // Close socket before retrying
|
||||||
|
usleep(100000); // Wait 100ms before retrying
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::disconnectServer() {
|
||||||
|
if (socketFD != -1) {
|
||||||
|
close(socketFD);
|
||||||
|
socketFD = -1;
|
||||||
|
printf("MODBUS_TCP_CLIENT: Disconnected from MODBUS server\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::setTimeout(int milliseconds) {
|
||||||
|
timeoutMilliseconds = milliseconds;
|
||||||
|
printf("MODBUS_TCP_CLIENT: Timeout set to %d ms\n", timeoutMilliseconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::isConnected() const {
|
||||||
|
return socketFD != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::reconnectServer() {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Attempting manual reconnection...\n");
|
||||||
|
disconnectServer();
|
||||||
|
return connectServer();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::sendRequest(uint8_t* request, int requestSize) {
|
||||||
|
// Ensure we're connected before sending
|
||||||
|
if (socketFD == -1) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connection lost. Attempting to reconnect...\n");
|
||||||
|
if (!connectServer()) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Reconnection failed. Cannot send request.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int bytesSent = write(socketFD, request, requestSize);
|
||||||
|
if (bytesSent <= 0) { // Detect broken connection during write
|
||||||
|
printf("MODBUS_TCP_CLIENT: Write failed, connection lost. Disconnecting...\n");
|
||||||
|
disconnectServer();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytesSent == requestSize;
|
||||||
|
return bytesSent == requestSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::receiveResponse(uint8_t* response, int expectedSize) {
|
||||||
|
int totalBytesReceived = 0;
|
||||||
|
|
||||||
|
while (totalBytesReceived < expectedSize) {
|
||||||
|
struct timeval timeout;
|
||||||
|
timeout.tv_sec = timeoutMilliseconds / 1000; // Convert ms to seconds
|
||||||
|
timeout.tv_usec = (timeoutMilliseconds % 1000) * 1000; // Convert remaining ms to µs
|
||||||
|
|
||||||
|
fd_set read_fds;
|
||||||
|
FD_ZERO(&read_fds);
|
||||||
|
FD_SET(socketFD, &read_fds);
|
||||||
|
|
||||||
|
// Use select() to wait for data before reading
|
||||||
|
int ready = select(socketFD + 1, &read_fds, NULL, NULL, &timeout);
|
||||||
|
if (ready == 0) { // Timeout case
|
||||||
|
printf("MODBUS_TCP_CLIENT: Timeout waiting for MODBUS response. Disconnecting...\n");
|
||||||
|
disconnectServer(); // Close socket and reset socketFD
|
||||||
|
return false;
|
||||||
|
} else if (ready < 0) { // Select failed
|
||||||
|
printf("MODBUS_TCP_CLIENT: Select failed. Disconnecting...\n");
|
||||||
|
disconnectServer(); // Close socket and reset socketFD
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read available data
|
||||||
|
int bytesReceived = read(socketFD, response + totalBytesReceived, expectedSize - totalBytesReceived);
|
||||||
|
if (bytesReceived <= 0) { // Connection lost or no data received
|
||||||
|
printf("MODBUS_TCP_CLIENT: Connection lost. Disconnecting...\n");
|
||||||
|
disconnectServer(); // Close socket and reset socketFD
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
totalBytesReceived += bytesReceived;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::buildReadRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t startAddr, uint16_t quantity) {
|
||||||
|
transactionID++;
|
||||||
|
|
||||||
|
// Transaction ID
|
||||||
|
buffer[0] = transactionID >> 8;
|
||||||
|
buffer[1] = transactionID & 0xFF;
|
||||||
|
// Protocol ID, always 00 00
|
||||||
|
buffer[2] = 0x00;
|
||||||
|
buffer[3] = 0x00;
|
||||||
|
// Length (remaining bytes after Unit ID)
|
||||||
|
buffer[4] = 0x00;
|
||||||
|
buffer[5] = 0x06;
|
||||||
|
// Unit ID (Slave Address)
|
||||||
|
buffer[6] = 0x01;
|
||||||
|
// Function code
|
||||||
|
buffer[7] = static_cast<uint8_t>(functionCode);
|
||||||
|
// Start Address
|
||||||
|
buffer[8] = startAddr >> 8;
|
||||||
|
buffer[9] = startAddr & 0xFF;
|
||||||
|
// Quantity (How many coils to read)
|
||||||
|
buffer[10] = quantity >> 8;
|
||||||
|
buffer[11] = quantity & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::buildWriteSingleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t value) {
|
||||||
|
transactionID++;
|
||||||
|
|
||||||
|
buffer[0] = transactionID >> 8;
|
||||||
|
buffer[1] = transactionID & 0xFF;
|
||||||
|
buffer[2] = 0x00;
|
||||||
|
buffer[3] = 0x00;
|
||||||
|
buffer[4] = 0x00;
|
||||||
|
buffer[5] = 0x06;
|
||||||
|
buffer[6] = 0x01;
|
||||||
|
buffer[7] = static_cast<uint8_t>(functionCode);
|
||||||
|
buffer[8] = address >> 8;
|
||||||
|
buffer[9] = address & 0xFF;
|
||||||
|
buffer[10] = value >> 8;
|
||||||
|
buffer[11] = value & 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::buildWriteMultipleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t count, const void* values, uint8_t byteCount) {
|
||||||
|
transactionID++;
|
||||||
|
|
||||||
|
// MODBUS TCP Header
|
||||||
|
buffer[0] = transactionID >> 8;
|
||||||
|
buffer[1] = transactionID & 0xFF;
|
||||||
|
buffer[2] = 0x00; // Protocol ID (always 0x0000)
|
||||||
|
buffer[3] = 0x00;
|
||||||
|
buffer[4] = 0x00; // Length (remaining bytes after Unit ID)
|
||||||
|
buffer[5] = 7 + byteCount;
|
||||||
|
buffer[6] = 0x01; // Unit ID
|
||||||
|
buffer[7] = static_cast<uint8_t>(functionCode); // Function Code (0x0F for coils, 0x10 for registers)
|
||||||
|
buffer[8] = address >> 8; // Start address high byte
|
||||||
|
buffer[9] = address & 0xFF; // Start address low byte
|
||||||
|
buffer[10] = count >> 8; // Quantity high byte
|
||||||
|
buffer[11] = count & 0xFF; // Quantity low byte
|
||||||
|
buffer[12] = byteCount; // Byte count
|
||||||
|
|
||||||
|
// Copy data payload manually (instead of memcpy)
|
||||||
|
const uint8_t* data = (const uint8_t*)values;
|
||||||
|
for (uint8_t i = 0; i < byteCount; i++) {
|
||||||
|
buffer[13 + i] = data[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readCoil(int address, bool &coilState) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_COIL, address, 1);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
uint8_t response[10]; // Expecting 10 bytes
|
||||||
|
if (!receiveResponse(response, 10)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request (allowing for server behavior)
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_COIL), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract coil state (only first bit is used)
|
||||||
|
coilState = (response[9] & 0x01) != 0; // Adjusted index based on 10-byte response
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readMultipleCoils(int address, int count, bool coilStates[]) {
|
||||||
|
if (count < 1 || count > 2000) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-2000 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_COIL, address, count);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Expected response size: Fixed header (9) + variable byte count
|
||||||
|
int byteCount = (count + 7) / 8; // 1 byte per 8 coils
|
||||||
|
int expectedSize = 9 + byteCount;
|
||||||
|
|
||||||
|
uint8_t response[256]; // Ensure buffer is large enough
|
||||||
|
if (!receiveResponse(response, expectedSize)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the function code matches
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_COIL)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_COIL), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read coil values
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int byteIndex = 9 + (i / 8); // Data starts at index 9
|
||||||
|
int bitIndex = i % 8;
|
||||||
|
coilStates[i] = (response[byteIndex] >> bitIndex) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readDiscreteInput(int address, bool &discreteInput) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_DISCRETE_INPUT, address, 1);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
uint8_t response[10]; // Expected response size
|
||||||
|
if (!receiveResponse(response, 10)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract single discrete input state
|
||||||
|
discreteInput = (response[9] & 0x01) != 0;
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readMultipleDiscreteInputs(int address, int count, bool discreteInputs[]) {
|
||||||
|
if (count < 1 || count > 2000) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid discrete input count (1-2000 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_DISCRETE_INPUT, address, count);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Expected response size: Fixed header (9) + variable byte count
|
||||||
|
int byteCount = (count + 7) / 8; // 1 byte per 8 inputs
|
||||||
|
int expectedSize = 9 + byteCount;
|
||||||
|
|
||||||
|
uint8_t response[256]; // Ensure buffer is large enough
|
||||||
|
if (!receiveResponse(response, expectedSize)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the function code matches
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_DISCRETE_INPUT), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read discrete input values
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
int byteIndex = 9 + (i / 8); // Data starts at index 9
|
||||||
|
int bitIndex = i % 8;
|
||||||
|
discreteInputs[i] = (response[byteIndex] >> bitIndex) & 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readHoldingRegister(int address, uint16_t &holdingRegister) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_HOLDING_REGISTER, address, 1);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
uint8_t response[11]; // Expected response size for reading 1 register
|
||||||
|
if (!receiveResponse(response, 11)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract holding register value (Big-endian format)
|
||||||
|
holdingRegister = (response[9] << 8) | response[10];
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readMultipleHoldingRegisters(int address, int count, uint16_t holdingRegisters[]) {
|
||||||
|
if (count < 1 || count > 125) { // MODBUS limits reading up to 125 registers per request
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid holding register count (1-125 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_HOLDING_REGISTER, address, count);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Expected response size: 9-byte header + 2 * count registers
|
||||||
|
int expectedSize = 9 + (count * 2);
|
||||||
|
uint8_t response[256]; // Ensure buffer is large enough
|
||||||
|
|
||||||
|
if (!receiveResponse(response, expectedSize)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_HOLDING_REGISTER), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract register values (each register is 2 bytes, big-endian)
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
holdingRegisters[i] = (response[9 + (i * 2)] << 8) | response[10 + (i * 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readInputRegister(int address, uint16_t &inputRegister) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_INPUT_REGISTER, address, 1);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
uint8_t response[11]; // Expected response size for reading 1 register
|
||||||
|
if (!receiveResponse(response, 11)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract input register value (Big-endian format)
|
||||||
|
inputRegister = (response[9] << 8) | response[10];
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readMultipleInputRegisters(int address, int count, uint16_t inputRegisters[]) {
|
||||||
|
if (count < 1 || count > 125) { // MODBUS limits reading up to 125 registers per request
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid input register count (1-125 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[12];
|
||||||
|
buildReadRequest(request, ModbusFunction::READ_INPUT_REGISTER, address, count);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Expected response size: 9-byte header + 2 * count registers
|
||||||
|
int expectedSize = 9 + (count * 2);
|
||||||
|
uint8_t response[256]; // Ensure buffer is large enough
|
||||||
|
|
||||||
|
if (!receiveResponse(response, expectedSize)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle MODBUS exception responses (0x80 + function code)
|
||||||
|
if (response[7] & 0x80) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: MODBUS Exception Code %02X\n", response[8]);
|
||||||
|
return ModbusError::EXCEPTION_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure function code matches the request
|
||||||
|
if (response[7] != static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER)) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Warning: Unexpected function code (Expected: %02X, Got: %02X)\n",
|
||||||
|
static_cast<uint8_t>(ModbusFunction::READ_INPUT_REGISTER), response[7]);
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract register values (each register is 2 bytes, big-endian)
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
inputRegisters[i] = (response[9 + (i * 2)] << 8) | response[10 + (i * 2)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::writeCoil(int address, bool value) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildWriteSingleRequest(request, ModbusFunction::WRITE_SINGLE_COIL, address, value ? 0xFF00 : 0x0000);
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Receive response
|
||||||
|
uint8_t response[12];
|
||||||
|
if (!receiveResponse(response, 12)) {
|
||||||
|
return ModbusError::TIMEOUT; // No response received
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if response matches request (MODBUS TCP should echo back the same request)
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
if (request[i] != response[i]) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::writeMultipleCoils(int address, int count, const bool values[]) {
|
||||||
|
if (count < 1 || count > 1968) { // MODBUS limit: max 1968 coils per request
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid coil count (1-1968 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int byteCount = (count + 7) / 8; // Each byte holds 8 coils
|
||||||
|
uint8_t coilData[byteCount];
|
||||||
|
|
||||||
|
// Initialize to 0 (since we may not use all bits)
|
||||||
|
for (int i = 0; i < byteCount; i++) {
|
||||||
|
coilData[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pack coil data into bytes
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
if (values[i]) {
|
||||||
|
coilData[i / 8] |= (1 << (i % 8));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[13 + byteCount];
|
||||||
|
buildWriteMultipleRequest(request, ModbusFunction::WRITE_MULTIPLE_COILS, address, count, coilData, byteCount);
|
||||||
|
|
||||||
|
sendRequest(request, 13 + byteCount);
|
||||||
|
|
||||||
|
// The expected response is always **12 bytes**
|
||||||
|
uint8_t response[12];
|
||||||
|
if (!receiveResponse(response, 12)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the response matches the **first 10 bytes**, except for byte 5 (message length byte)
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) continue; // Skip byte 5 (message length field)
|
||||||
|
|
||||||
|
if (request[i] != response[i]) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::writeHoldingRegister(int address, uint16_t value) {
|
||||||
|
uint8_t request[12];
|
||||||
|
buildWriteSingleRequest(request, ModbusFunction::WRITE_SINGLE_HOLDING_REGISTER, address, value);
|
||||||
|
|
||||||
|
sendRequest(request, 12);
|
||||||
|
|
||||||
|
// Expected response size = 12 bytes (full echo of the request)
|
||||||
|
uint8_t response[12];
|
||||||
|
if (!receiveResponse(response, 12)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure response matches request exactly (full 12 bytes)
|
||||||
|
for (int i = 0; i < 12; i++) {
|
||||||
|
if (request[i] != response[i]) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]) {
|
||||||
|
if (count < 1 || count > 123) { // MODBUS limit: max 123 registers per request
|
||||||
|
printf("MODBUS_TCP_CLIENT: Invalid register count (1-123 allowed)\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int byteCount = count * 2; // Each register is 2 bytes
|
||||||
|
uint8_t registerData[byteCount];
|
||||||
|
|
||||||
|
// Convert register values to byte array (big-endian format)
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
registerData[i * 2] = values[i] >> 8; // High byte
|
||||||
|
registerData[i * 2 + 1] = values[i] & 0xFF; // Low byte
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t request[13 + byteCount];
|
||||||
|
buildWriteMultipleRequest(request, ModbusFunction::WRITE_MULTIPLE_HOLDING_REGISTERS, address, count, registerData, byteCount);
|
||||||
|
|
||||||
|
sendRequest(request, 13 + byteCount);
|
||||||
|
|
||||||
|
// Expected response size = 12 bytes (first 10 bytes + 2 bytes for number of registers written)
|
||||||
|
uint8_t response[12];
|
||||||
|
if (!receiveResponse(response, 12)) {
|
||||||
|
return ModbusError::TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure response matches request (except for byte 5)
|
||||||
|
for (int i = 0; i < 10; i++) {
|
||||||
|
if (i == 5) continue; // Skip byte 5 (message length field)
|
||||||
|
|
||||||
|
if (request[i] != response[i]) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: Response does not match request!\n");
|
||||||
|
return ModbusError::INVALID_RESPONSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ModbusError::NONE; // Success
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::readAll() {
|
||||||
|
/*
|
||||||
|
Reads every coil, discrete input, input register, and holding register
|
||||||
|
assuming that they start at address 0
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (coilsRead == nullptr && discreteInputs == nullptr &&
|
||||||
|
inputRegisters == nullptr && holdingRegistersRead == nullptr) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: readAll() called, but wrong constructor was used.\n");
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError error = ModbusError::NONE;
|
||||||
|
if (coilsRead != nullptr) {
|
||||||
|
error = readMultipleCoils(startCoils, numCoils, coilsRead);
|
||||||
|
if (error != ModbusError::NONE) return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (discreteInputs != nullptr) {
|
||||||
|
error = readMultipleDiscreteInputs(startDiscreteInputs, numDiscreteInputs, discreteInputs);
|
||||||
|
if (error != ModbusError::NONE) return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (inputRegisters != nullptr) {
|
||||||
|
error = readMultipleInputRegisters(startInputRegisters, numInputRegisters, inputRegisters);
|
||||||
|
if (error != ModbusError::NONE) return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holdingRegistersRead != nullptr) {
|
||||||
|
error = readMultipleHoldingRegisters(startHoldingRegisters, numHoldingRegisters, holdingRegistersRead);
|
||||||
|
|
||||||
|
}
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError ModbusTCPClient::writeAll() {
|
||||||
|
/*
|
||||||
|
Reads every coil and holding register assuming that they start at address 0
|
||||||
|
*/
|
||||||
|
if (coilsWrite == nullptr && holdingRegistersWrite) {
|
||||||
|
printf("MODBUS_TCP_CLIENT: writeAll() called, but wrong constructor was used.\n");
|
||||||
|
return ModbusError::INVALID_REQUEST;
|
||||||
|
}
|
||||||
|
|
||||||
|
ModbusError error = ModbusError::NONE;
|
||||||
|
if (coilsWrite != nullptr) {
|
||||||
|
error = writeMultipleCoils(startCoils, numCoils, coilsWrite);
|
||||||
|
if (error != ModbusError::NONE) return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holdingRegistersWrite != nullptr) {
|
||||||
|
error = writeMultipleHoldingRegisters(startHoldingRegisters, numHoldingRegisters, holdingRegistersWrite);
|
||||||
|
}
|
||||||
|
|
||||||
|
return error;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Setters for writing values (preferred to be used)
|
||||||
|
void ModbusTCPClient::setCoil(int address, bool value) {
|
||||||
|
if (address >= 0 && address < numCoils) {
|
||||||
|
coilsWrite[address] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModbusTCPClient::setHoldingRegister(int address, uint16_t value) {
|
||||||
|
if (address >= 0 && address < numHoldingRegisters) {
|
||||||
|
holdingRegistersWrite[address] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters (preferred to be used as long as readAll is being called)
|
||||||
|
bool ModbusTCPClient::getCoil(int address) const {
|
||||||
|
return (address >= 0 && address < numCoils) ? coilsRead[address] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::getDesiredCoil(int address) const {
|
||||||
|
return (address >= 0 && address < numCoils) ? coilsWrite[address] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ModbusTCPClient::getDiscreteInput(int address) const {
|
||||||
|
return (address >= 0 && address < numDiscreteInputs) ? discreteInputs[address] : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ModbusTCPClient::getHoldingRegister(int address) const {
|
||||||
|
return (address >= 0 && address < numHoldingRegisters) ? holdingRegistersRead[address] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ModbusTCPClient::getDesiredHoldingRegister(int address) const {
|
||||||
|
return (address >= 0 && address < numHoldingRegisters) ? holdingRegistersWrite[address] : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ModbusTCPClient::getInputRegister(int address) const {
|
||||||
|
return (address >= 0 && address < numInputRegisters) ? inputRegisters[address] : 0;
|
||||||
|
}
|
||||||
115
modbus_tcp_client_old_old.h
Normal file
115
modbus_tcp_client_old_old.h
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
#ifndef MODBUS_TCP_CLIENT_H
|
||||||
|
#define MODBUS_TCP_CLIENT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
|
||||||
|
// Enum class for MODBUS function codes
|
||||||
|
enum class ModbusFunction : uint8_t {
|
||||||
|
READ_COIL = 0x01,
|
||||||
|
READ_DISCRETE_INPUT = 0x02,
|
||||||
|
READ_HOLDING_REGISTER = 0x03,
|
||||||
|
READ_INPUT_REGISTER = 0x04,
|
||||||
|
WRITE_SINGLE_COIL = 0x05,
|
||||||
|
WRITE_SINGLE_HOLDING_REGISTER = 0x06,
|
||||||
|
WRITE_MULTIPLE_COILS = 0x0F,
|
||||||
|
WRITE_MULTIPLE_HOLDING_REGISTERS = 0x10
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ModbusError {
|
||||||
|
NONE = 0, // No error
|
||||||
|
TIMEOUT, // No response from server
|
||||||
|
INVALID_RESPONSE, // Response does not match request
|
||||||
|
CONNECTION_LOST, // Connection issue
|
||||||
|
EXCEPTION_RESPONSE, // MODBUS Exception response (error code)
|
||||||
|
INVALID_REQUEST, // User tries doing something they shouldn't
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class ModbusTCPClient {
|
||||||
|
public:
|
||||||
|
ModbusTCPClient(const char* ip, int port); // Extra constructor that does CANNOT use readAll() or writeAll()
|
||||||
|
ModbusTCPClient(const char* ip, int port, int numCoils, int numDI, int numIR, int numHR,
|
||||||
|
int startCoils = 0, int startDI = 0, int startIR = 0, int startHR = 0);
|
||||||
|
|
||||||
|
~ModbusTCPClient();
|
||||||
|
|
||||||
|
// Set the start address of each type. Either use this after creating the object or put them in the constructor
|
||||||
|
void setStartAddresses(int startCoils, int startDI, int startIR, int startHR);
|
||||||
|
|
||||||
|
bool connectServer();
|
||||||
|
void disconnectServer();
|
||||||
|
bool isConnected() const;
|
||||||
|
|
||||||
|
// Manually disconnectServer and return reconnectServer()
|
||||||
|
bool reconnectServer();
|
||||||
|
|
||||||
|
// Set the timeout for receiving responses
|
||||||
|
void setTimeout(int milliseconds);
|
||||||
|
|
||||||
|
// Setters (preferred to be used when calling writeAll())
|
||||||
|
void setCoil(int address, bool value);
|
||||||
|
void setHoldingRegister(int address, uint16_t value);
|
||||||
|
|
||||||
|
// Getters (preferred to be used when calling readAll())
|
||||||
|
bool getCoil(int address) const;
|
||||||
|
bool getDesiredCoil(int address) const; // Retrieves the "to be written" value
|
||||||
|
bool getDiscreteInput(int address) const;
|
||||||
|
uint16_t getHoldingRegister(int address) const;
|
||||||
|
uint16_t getDesiredHoldingRegister(int address) const; // Retrieves the "to be written" value
|
||||||
|
uint16_t getInputRegister(int address) const;
|
||||||
|
|
||||||
|
ModbusError readAll(); // Reads every coil, DI, IR, and HR
|
||||||
|
ModbusError writeAll(); // Writes every coil and HR
|
||||||
|
|
||||||
|
// Manual MODBUS TCP actions (not preferred to be called by user)
|
||||||
|
ModbusError readCoil(int address, bool &coilState);
|
||||||
|
ModbusError readMultipleCoils(int address, int count, bool coilStates[]);
|
||||||
|
|
||||||
|
ModbusError readDiscreteInput(int address, bool &discreteInput);
|
||||||
|
ModbusError readMultipleDiscreteInputs(int address, int count, bool discreteInputs[]);
|
||||||
|
|
||||||
|
ModbusError readHoldingRegister(int address, uint16_t &holdingRegister);
|
||||||
|
ModbusError readMultipleHoldingRegisters(int address, int count, uint16_t holdingRegisters[]);
|
||||||
|
|
||||||
|
ModbusError readInputRegister(int address, uint16_t &inputRegister);
|
||||||
|
ModbusError readMultipleInputRegisters(int address, int count, uint16_t inputRegisters[]);
|
||||||
|
|
||||||
|
ModbusError writeCoil(int address, bool value);
|
||||||
|
ModbusError writeMultipleCoils(int address, int count, const bool values[]);
|
||||||
|
|
||||||
|
ModbusError writeHoldingRegister(int address, uint16_t value);
|
||||||
|
ModbusError writeMultipleHoldingRegisters(int address, int count, const uint16_t values[]);
|
||||||
|
|
||||||
|
private:
|
||||||
|
// TCP settings
|
||||||
|
const char* serverIP;
|
||||||
|
int serverPort;
|
||||||
|
int socketFD;
|
||||||
|
uint16_t transactionID;
|
||||||
|
int timeoutMilliseconds = 2000; // Default 2 second timeout on receiving responses
|
||||||
|
|
||||||
|
// Storing MODBUS register information
|
||||||
|
int numCoils, numDiscreteInputs, numInputRegisters, numHoldingRegisters;
|
||||||
|
int startCoils, startDiscreteInputs, startInputRegisters, startHoldingRegisters; // The start address of each type of register
|
||||||
|
|
||||||
|
bool* coilsRead; // Stores the actual state of coils on the MODBUS server
|
||||||
|
bool* coilsWrite; // Stores the desired state of coils to be written
|
||||||
|
bool* discreteInputs; // Only read from the MODBUS server (no writes)
|
||||||
|
|
||||||
|
uint16_t* inputRegisters; // Only read from the MODBUS server
|
||||||
|
uint16_t* holdingRegistersRead; // Stores actual values from the MODBUS server
|
||||||
|
uint16_t* holdingRegistersWrite; // Stores desired values to write
|
||||||
|
|
||||||
|
bool sendRequest(uint8_t* request, int requestSize);
|
||||||
|
bool receiveResponse(uint8_t* response, int expectedSize);
|
||||||
|
|
||||||
|
void buildReadRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t startAddr, uint16_t quantity);
|
||||||
|
void buildWriteSingleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t value);
|
||||||
|
void buildWriteMultipleRequest(uint8_t* buffer, ModbusFunction functionCode, uint16_t address, uint16_t count, const void* values, uint8_t byteCount);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // MODBUS_TCP_CLIENT_H
|
||||||
Loading…
Reference in New Issue
Block a user