update to eModbus 1.7.4

This commit is contained in:
MichaelDvP
2025-08-04 10:16:50 +02:00
parent f10f3d5305
commit 7b0169bb68
24 changed files with 178 additions and 101 deletions

View File

@@ -0,0 +1,9 @@
name=eModbus
version=1.7.4
author=bertmelis,Miq1 <miq1@gmx.de>
maintainer=Miq1 <miq1@gmx.de>
sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32.
paragraph=This library is non-blocking for the program using it. Modbus requests and responses will be returned to user-supplied callback functions. All Modbus function codes are supported implicitly, the codes specified by the Modbus specs are parameter-checked.
category=Communication
url=https://github.com/eModbus/eModbus
architectures=esp32,FreeRTOS

View File

@@ -47,6 +47,8 @@ CoilData::~CoilData() {
// Assignment operator
CoilData& CoilData::operator=(const CoilData& m) {
// Avoid self-assignment
if (this == &m) return *this;
// Remove old data
if (CDbuffer) {
delete CDbuffer;

View File

@@ -8,6 +8,7 @@
#include <map>
#include <functional>
#include "ModbusClient.h"
#include "ModbusServer.h"
#include "ModbusClientTCP.h" // Needed for client.setTarget()
#include "RTUutils.h" // Needed for RTScallback
@@ -29,7 +30,7 @@ public:
ModbusBridge();
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
ModbusBridge(uint32_t timeout, int rtsPin = -1);
explicit ModbusBridge(uint32_t timeout, int rtsPin = -1);
ModbusBridge(uint32_t timeout, RTScallback rts);
// Destructor
@@ -230,42 +231,56 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
uint8_t aliasID = msg.getServerID();
uint8_t functionCode = msg.getFunctionCode();
ModbusMessage response;
bool foundServer = false;
uint8_t usableID = 255;
// Find the (alias) serverID
if (servers.find(aliasID) != servers.end()) {
foundServer = true;
usableID = aliasID;
} else {
if (servers.find(ANY_SERVER) != servers.end()) {
foundServer = true;
usableID = ANY_SERVER;
}
}
if (foundServer) {
// Found it. We may use servers[aliasID] now without allocating a new map slot
// Request filter hook to be called here
if (servers[aliasID]->requestFilter) {
if (servers[usableID]->requestFilter) {
LOG_D("Calling request filter\n");
msg = servers[aliasID]->requestFilter(msg);
msg = servers[usableID]->requestFilter(msg);
}
// Set real target server ID
msg.setServerID(servers[aliasID]->serverID);
// Issue the request
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, msg.getFunctionCode());
// TCP servers have a target host/port that needs to be set in the client
if (servers[aliasID]->serverType == TCP_SERVER) {
response = reinterpret_cast<ModbusClientTCP *>(servers[aliasID]->client)->syncRequestMT(msg, (uint32_t)millis(), servers[aliasID]->host, servers[aliasID]->port);
} else {
response = servers[aliasID]->client->syncRequestM(msg, (uint32_t)millis());
if (servers[usableID]->serverID != ANY_SERVER) {
msg.setServerID(servers[usableID]->serverID);
}
// Response filter hook to be called here
if (servers[aliasID]->responseFilter) {
LOG_D("Calling response filter\n");
response = servers[aliasID]->responseFilter(response);
// Issue the request
LOG_D("Request (%02X/%02X) sent\n", servers[usableID]->serverID, msg.getFunctionCode());
// TCP servers have a target host/port that needs to be set in the client
if (servers[usableID]->serverType == TCP_SERVER) {
response = reinterpret_cast<ModbusClientTCP *>(servers[usableID]->client)->syncRequestMT(msg, (uint32_t)micros(), servers[usableID]->host, servers[usableID]->port);
} else {
response = servers[usableID]->client->syncRequestM(msg, (uint32_t)micros());
}
// Re-set the requested server ID and function code (may have been modified by filters)
response.setServerID(aliasID);
if (response.getError() != SUCCESS) {
response.setFunctionCode(functionCode | 0x80);
} else {
response.setFunctionCode(functionCode);
}
// Response filter hook to be called here
if (servers[usableID]->responseFilter) {
LOG_D("Calling response filter\n");
response = servers[usableID]->responseFilter(response);
}
} else {
// If we get here, something has gone wrong internally. We send back an error response anyway.
response.setError(aliasID, functionCode, INVALID_SERVER);

View File

@@ -21,6 +21,13 @@ ModbusClient::ModbusClient() :
onError(nullptr),
onResponse(nullptr) { instanceCounter++; }
// Default destructor: reduce number of clients by one
ModbusClient::~ModbusClient() {
if (instanceCounter) {
instanceCounter--;
}
}
// onDataHandler: register callback for data responses
bool ModbusClient::onDataHandler(MBOnData handler) {
if (onData) {

View File

@@ -37,8 +37,8 @@ public:
uint32_t getMessageCount(); // Informative: return number of messages created
uint32_t getErrorCount(); // Informative: return number of errors received
void resetCounts(); // Set both message and error counts to zero
inline Error addRequest(ModbusMessage m, uint32_t token) { return addRequestM(m, token); }
inline ModbusMessage syncRequest(ModbusMessage m, uint32_t token) { return syncRequestM(m, token); }
inline Error addRequest(const ModbusMessage& m, uint32_t token) { return addRequestM(m, token); }
inline ModbusMessage syncRequest(const ModbusMessage& m, uint32_t token) { return syncRequestM(m, token); }
// Template function to generate syncRequest functions as long as there is a
// matching ModbusMessage::setMessage() call
@@ -85,7 +85,7 @@ public:
protected:
ModbusClient(); // Default constructor
virtual void isInstance() = 0; // Make class abstract
virtual ~ModbusClient(); // Destructor
ModbusMessage waitSync(uint8_t serverID, uint8_t functionCode, uint32_t token); // wait for syncRequest response to arrive
// Virtual addRequest variant needed internally. All others done by template!
virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0;

View File

@@ -86,7 +86,7 @@ void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
char taskName[18];
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
// Start task to handle the queue
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 6, &worker, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 6, &worker, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Client task %d started. Interval=%d\n", (uint32_t)worker, MR_interval);
}
@@ -151,6 +151,7 @@ void ModbusClientRTU::clearQueue()
{
std::queue<RequestEntry> empty;
LOCK_GUARD(lockGuard, qLock);
// Empty queue
std::swap(requests, empty);
}
@@ -342,9 +343,12 @@ void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) {
{
// Safely lock the queue
LOCK_GUARD(lockGuard, instance->qLock);
// Remove the front queue entry
// Remove the front queue entry if the queue is not empty
if (!instance->requests.empty()) {
instance->requests.pop();
}
}
} else {
delay(1);
}

View File

@@ -67,15 +67,15 @@ protected:
uint32_t token;
ModbusMessage msg;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
token(t),
msg(m),
isSyncRequest(syncReq) {}
};
// Base addRequest and syncRequest must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// addToQueue: send freshly created request to queue
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
@@ -89,7 +89,6 @@ protected:
// start background task
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
void isInstance() { return; } // make class instantiable
queue<RequestEntry> requests; // Queue to hold requests to be processed
#if USE_MUTEX
mutex qLock; // Mutex to protect queue

View File

@@ -18,7 +18,8 @@ ModbusClientTCP::ModbusClientTCP(Client& client, uint16_t queueLimit) :
MT_target(IPAddress(0, 0, 0, 0), 0, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
MT_defaultTimeout(DEFAULTTIMEOUT),
MT_defaultInterval(TARGETHOSTINTERVAL),
MT_qLimit(queueLimit)
MT_qLimit(queueLimit),
MT_timeoutsToClose(0)
{ }
// Alternative Constructor takes reference to Client (EthernetClient or WiFiClient) plus initial target host
@@ -29,7 +30,8 @@ ModbusClientTCP::ModbusClientTCP(Client& client, IPAddress host, uint16_t port,
MT_target(host, port, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
MT_defaultTimeout(DEFAULTTIMEOUT),
MT_defaultInterval(TARGETHOSTINTERVAL),
MT_qLimit(queueLimit)
MT_qLimit(queueLimit),
MT_timeoutsToClose(0)
{ }
// Destructor: clean up queue, task etc.
@@ -64,7 +66,7 @@ void ModbusClientTCP::end() {
// begin: start worker task
#if IS_LINUX
void *ModbusClientTCP::pHandle(void *p) {
handleConnection((ModbusClientTCP *)p);
handleConnection(static_cast<ModbusClientTCP *>(p));
return nullptr;
}
#endif
@@ -84,7 +86,7 @@ void ModbusClientTCP::begin(int coreID) {
char taskName[18];
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
// Start task to handle the queue
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 5, &worker, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&handleConnection, taskName, CLIENT_TASK_STACK, this, 5, &worker, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("TCP client worker %s started\n", taskName);
#endif
} else {
@@ -119,9 +121,25 @@ uint32_t ModbusClientTCP::pendingRequests() {
void ModbusClientTCP::clearQueue() {
std::queue<RequestEntry *> empty;
LOCK_GUARD(lockGuard, qLock);
// Delete queue entries if still on the queue
while (!requests.empty()) {
RequestEntry *re = requests.front();
delete re;
requests.pop();
}
// Now flush the queue
std::swap(requests, empty);
}
// Set number of timeouts to tolerate before a connection is forcibly closed.
// 0: never, 1..255: desired number
// Returns previous value.
uint8_t ModbusClientTCP::closeConnectionOnTimeouts(uint8_t n) {
uint8_t oldValue = MT_timeoutsToClose;
MT_timeoutsToClose = n;
return oldValue;
}
// Base addRequest for preformatted ModbusMessage and last set target
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
Error rc = SUCCESS; // Return value
@@ -225,6 +243,7 @@ bool ModbusClientTCP::addToQueue(uint32_t token, ModbusMessage request, TargetHo
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
bool doNotPop;
unsigned long lastRequest = millis();
uint16_t timeoutCount = 0; // Run time counter of consecutive timeouts.
// Loop forever - or until task is killed
while (1) {
@@ -273,6 +292,8 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
// Did we get a normal response?
if (response.getError()==SUCCESS) {
LOG_D("Data response.\n");
// Reset timeout counter
timeoutCount = 0;
// Yes. Is it a synchronous request?
if (request->isSyncRequest) {
// Yes. Put the response into the response map
@@ -299,6 +320,25 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
LOCK_GUARD(responseCnt, instance->countAccessM);
instance->errorCount++;
}
// Is it a TIMEOUT and do we need to track it?
if (response.getError()==TIMEOUT && instance->MT_timeoutsToClose) {
LOG_D("Checking timeout sequence\n");
// Yes. First count timeout conter up
timeoutCount++;
// Is the count above the limit?
if (timeoutCount > instance->MT_timeoutsToClose) {
LOG_D("Timeouts: %d exceeding limit (%d), closing connection\n",
timeoutCount, instance->MT_timeoutsToClose);
// Yes. We need to cut the connection
instance->MT_client.stop();
delay(1);
// reset timeout count
timeoutCount = 0;
}
} else {
// No TIMEOUT or no limit: reset timeout count
timeoutCount = 0;
}
// Is it a synchronous request?
if (request->isSyncRequest) {
// Yes. Put the response into the response map
@@ -345,8 +385,11 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
{
// Safely lock the queue
LOCK_GUARD(lockGuard, instance->qLock);
// Remove the front queue entry
// Remove the front queue entry if the queue is not empty
if (!instance->requests.empty()) {
instance->requests.pop();
}
// Delete request
delete request;
LOG_D("Request popped from queue.\n");

View File

@@ -50,6 +50,11 @@ public:
// Remove all pending request from queue
void clearQueue();
// Set number of timeouts to tolerate before a connection is forcibly closed.
// 0: never, 1..255: desired number
// Returns previous value.
uint8_t closeConnectionOnTimeouts(uint8_t n=3);
protected:
// class describing a target server
struct TargetHost {
@@ -58,7 +63,7 @@ protected:
uint32_t timeout; // Time in ms waiting for a response
uint32_t interval; // Time in ms to wait between requests
inline TargetHost& operator=(TargetHost& t) {
inline TargetHost& operator=(const TargetHost& t) {
host = t.host;
port = t.port;
timeout = t.timeout;
@@ -66,7 +71,7 @@ protected:
return *this;
}
inline TargetHost(TargetHost& t) :
inline TargetHost(const TargetHost& t) :
host(t.host),
port(t.port),
timeout(t.timeout),
@@ -86,13 +91,13 @@ protected:
interval(interval)
{ }
inline bool operator==(TargetHost& t) {
inline bool operator==(const TargetHost& t) {
if (host != t.host) return false;
if (port != t.port) return false;
return true;
}
inline bool operator!=(TargetHost& t) {
inline bool operator!=(const TargetHost& t) {
if (host != t.host) return true;
if (port != t.port) return true;
return false;
@@ -135,7 +140,7 @@ protected:
}
protected:
uint8_t headRoom[6]; // Buffer to hold MSB-first TCP header
uint8_t headRoom[6] = {0,0,0,0,0,0}; // Buffer to hold MSB-first TCP header
};
struct RequestEntry {
@@ -144,7 +149,7 @@ protected:
TargetHost target;
ModbusTCPhead head;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, TargetHost tg, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, TargetHost tg, bool syncReq = false) :
token(t),
msg(m),
target(tg),
@@ -153,8 +158,8 @@ protected:
};
// Base addRequest and syncRequest must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// TCP-specific addition "...MT()" including adhoc target - used by bridge
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
ModbusMessage syncRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
@@ -174,7 +179,6 @@ protected:
// receive: get response via Client connection
ModbusMessage receive(RequestEntry *request);
void isInstance() { return; } // make class instantiable
queue<RequestEntry *> requests; // Queue to hold requests to be processed
#if USE_MUTEX
mutex qLock; // Mutex to protect queue
@@ -185,6 +189,8 @@ protected:
uint32_t MT_defaultTimeout; // Standard timeout value taken if no dedicated was set
uint32_t MT_defaultInterval; // Standard interval value taken if no dedicated was set
uint16_t MT_qLimit; // Maximum number of requests to accept in queue
uint8_t MT_timeoutsToClose; // 0: disregard, 1-255: number of timeouts to tolerate before
// forcibly closing a connection.
// Let any ModbusBridge class use protected members
template<typename SERVERCLASS> friend class ModbusBridge;

View File

@@ -223,19 +223,17 @@ void onAck(size_t len, uint32_t time) {
}
*/
void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
LOG_D("packet received (len:%d)\n", length);
LOG_D("packet received (len:%u)\n", length);
// reset idle timeout
MTA_lastActivity = millis();
if (length) {
LOG_D("parsing (len:%d)\n", length + 1);
LOG_D("parsing (len:%u)\n", length + 1);
}
while (length > 0) {
RequestEntry* request = nullptr;
ModbusMessage* response = nullptr;
uint16_t transactionID = 0;
uint16_t protocolID = 0;
uint16_t messageLength = 0;
bool isOkay = false;
// 1. Check for valid modbus message
@@ -244,8 +242,8 @@ void ModbusClientTCPasync::onPacket(uint8_t* data, size_t length) {
// total message should fit MBAP plus remaining bytes (in data[4], data[5])
if (length > 6) {
transactionID = (data[0] << 8) | data[1];
protocolID = (data[2] << 8) | data[3];
messageLength = (data[4] << 8) | data[5];
uint16_t protocolID = (data[2] << 8) | data[3];
uint16_t messageLength = (data[4] << 8) | data[5];
if (protocolID == 0 &&
length >= (uint32_t)messageLength + 6 &&
messageLength < 256) {

View File

@@ -83,7 +83,7 @@ protected:
return headRoom;
}
inline ModbusTCPhead& operator= (ModbusTCPhead& t) {
inline ModbusTCPhead& operator= (const ModbusTCPhead& t) {
transactionID = t.transactionID;
protocolID = t.protocolID;
len = t.len;
@@ -91,7 +91,7 @@ protected:
}
protected:
uint8_t headRoom[6]; // Buffer to hold MSB-first TCP header
uint8_t headRoom[6] = {0,0,0,0,0,0}; // Buffer to hold MSB-first TCP header
};
struct RequestEntry {
@@ -100,7 +100,7 @@ protected:
ModbusTCPhead head;
uint32_t sentTime;
bool isSyncRequest;
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
token(t),
msg(m),
head(ModbusTCPhead()),
@@ -109,8 +109,8 @@ protected:
};
// Base addRequest and syncRequest both must be present
Error addRequestM(ModbusMessage msg, uint32_t token);
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
Error addRequestM(ModbusMessage msg, uint32_t token) override;
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
// addToQueue: send freshly created request to queue
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
@@ -121,8 +121,6 @@ protected:
// receive: get response via Client connection
// TCPResponse* receive(uint8_t* data, size_t length);
void isInstance() { return; } // make class instantiable
// TCP handling code, all static taking a class instancs as param
void onConnected();
void onDisconnected();

View File

@@ -6,6 +6,7 @@
#undef LOCAL_LOG_LEVEL
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
#include "Logging.h"
#include <algorithm>
// Default Constructor - takes optional size of MM_data to allocate memory
ModbusMessage::ModbusMessage(uint16_t dataLen) {
@@ -146,21 +147,19 @@ void ModbusMessage::setServerID(uint8_t serverID) {
}
void ModbusMessage::setFunctionCode(uint8_t FC) {
// We accept here that [0], [1] may allocate bytes!
if (MM_data.empty()) {
MM_data.reserve(3); // At least an error message should fit
if (MM_data.size() < 2) {
MM_data.resize(2); // Resize to at least 2 to ensure indices 0 and 1 are valid
MM_data[0] = 0; // Optional: Invalid server ID as a placeholder
}
// No serverID set yet? use a 0 to initialize it to an error-generating value
if (MM_data.size() < 2) MM_data[0] = 0; // intentional invalid server ID!
MM_data[1] = FC;
MM_data[1] = FC; // Safely set the function code
}
// add() variant to copy a buffer into MM_data. Returns updated size
uint16_t ModbusMessage::add(const uint8_t *arrayOfBytes, uint16_t count) {
uint16_t originalSize = MM_data.size();
MM_data.resize(originalSize + count);
// Copy it
while (count--) {
MM_data.push_back(*arrayOfBytes++);
}
std::copy(arrayOfBytes, arrayOfBytes + count, MM_data.begin() + originalSize);
// Return updated size (logical length of message so far)
return MM_data.size();
}
@@ -181,7 +180,7 @@ uint8_t ModbusMessage::determineFloatOrder() {
uint32_t i = 77230; // int value to go into a float without rounding error
float f = i; // assign it
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
uint8_t expect[floatSize] = { 0x47, 0x96, 0xd7, 0x00 }; // IEEE754 representation
const uint8_t expect[floatSize] = { 0x47, 0x96, 0xd7, 0x00 }; // IEEE754 representation
uint8_t matches = 0; // number of bytes successfully matched
// Loop over the bytes of the expected sequence
@@ -225,7 +224,7 @@ uint8_t ModbusMessage::determineDoubleOrder() {
uint64_t i = 5791007487489389; // int64 value to go into a double without rounding error
double f = i; // assign it
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
uint8_t expect[doubleSize] = { 0x43, 0x34, 0x92, 0xE4, 0x00, 0x2E, 0xF5, 0x6D }; // IEEE754 representation
const uint8_t expect[doubleSize] = { 0x43, 0x34, 0x92, 0xE4, 0x00, 0x2E, 0xF5, 0x6D }; // IEEE754 representation
uint8_t matches = 0; // number of bytes successfully matched
// Loop over the bytes of the expected sequence
@@ -306,10 +305,7 @@ double ModbusMessage::swapDouble(double& f, int swapRule) {
// add() variant for a vector of uint8_t
uint16_t ModbusMessage::add(vector<uint8_t> v) {
for (auto& b: v) {
MM_data.push_back(b);
}
return MM_data.size();
return add(v.data(), v.size());
}
// add() variants for float and double values

View File

@@ -30,7 +30,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
svmap = workerMap.find(ANY_SERVER);
if (svmap != workerMap.end()) {
serverFound = true;
serverID = ANY_SERVER;
}
}
// Did we find a serverID?
@@ -49,7 +48,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
if (fcmap != svmap->second.end()) {
// Yes. Return the function pointer for it.
functionCodeFound = true;
functionCode = ANY_FUNCTION_CODE;
}
}
if (functionCodeFound) {
@@ -104,6 +102,11 @@ bool ModbusServer::isServerFor(uint8_t serverID) {
// Is there one?
if (svmap != workerMap.end()) {
return true;
} else {
svmap = workerMap.find(ANY_SERVER);
if (svmap != workerMap.end()) {
return true;
}
}
return false;
}

View File

@@ -68,15 +68,12 @@ protected:
ModbusServer();
// Destructor
~ModbusServer();
virtual ~ModbusServer();
// Prevent copy construction or assignment
ModbusServer(ModbusServer& other) = delete;
ModbusServer& operator=(ModbusServer& other) = delete;
// Virtual function to prevent this class being instantiated
virtual void isInstance() = 0;
std::map<uint8_t, std::map<uint8_t, MBSworker>> workerMap; // map on serverID->functionCode->worker function
uint32_t messageCount; // Number of Requests processed
uint32_t errorCount; // Number of errors responded

View File

@@ -15,8 +15,8 @@
// Create own non-virtual EthernetServer class
class EthernetServerEM : public EthernetServer {
public:
EthernetServerEM(uint16_t port) : EthernetServer(port) { }
void begin(uint16_t port = 0) { }
explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { }
void begin(uint16_t port = 0) override { }
};
#include "ModbusServerTCPtemp.h"

View File

@@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
}
@@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() {
return MSRuseASCII;
}
// set timeout
void ModbusServerRTU::setModbusTimeout(unsigned long timeout)
{
serverTimeout = timeout;
}
// Toggle skipping of leading 0x00 byte
void ModbusServerRTU::skipLeading0x00(bool onOff) {
MSRskipLeadingZeroByte = onOff;
@@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
response = m;
}
} else {
// No callback. Is at least the serverID valid and no broadcast?
if (myServer->isServerFor(request[0]) && request[0] != 0x00) {
// No callback. Is at least the serverID valid?
if (myServer->isServerFor(request[0])) {
// Yes. Send back a ILLEGAL_FUNCTION error
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
}

View File

@@ -47,6 +47,9 @@ public:
// Inquire protocol mode
bool isModbusASCII();
// set timeout
void setModbusTimeout(unsigned long timeout);
// Toggle skipping of leading 0x00 byte
void skipLeading0x00(bool onOff = true);
@@ -61,8 +64,6 @@ protected:
ModbusServerRTU(ModbusServerRTU& m) = delete;
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
inline void isInstance() { } // Make class instantiable
// internal common begin function
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);

View File

@@ -32,7 +32,7 @@ ModbusServerTCPasync::mb_client::~mb_client() {
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
lastActiveTime = millis();
LOG_D("data len %d\n", len);
LOG_D("data len %u\n", len);
Error error = SUCCESS;
size_t i = 0;
@@ -250,7 +250,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
LOCK_GUARD(lock1, cListLock);
if (clients.size() < maxNoClients) {
clients.emplace_back(new mb_client(this, client));
LOG_D("nr clients: %d\n", clients.size());
LOG_D("nr clients: %u\n", clients.size());
} else {
LOG_D("max number of clients reached, closing new\n");
client->close(true);
@@ -264,5 +264,5 @@ void ModbusServerTCPasync::onClientDisconnect(mb_client* client) {
clients.remove_if([client](mb_client* i) { return i->client == client->client; });
// delete client itself
delete client;
LOG_D("nr clients: %d\n", clients.size());
LOG_D("nr clients: %u\n", clients.size());
}

View File

@@ -76,7 +76,6 @@ class ModbusServerTCPasync : public ModbusServer {
bool isRunning();
protected:
inline void isInstance() { }
void onClientConnect(AsyncClient* client);
void onClientDisconnect(mb_client* client);

View File

@@ -44,8 +44,6 @@ protected:
ModbusServerTCP(ModbusServerTCP& m) = delete;
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
inline void isInstance() { }
uint8_t numClients;
TaskHandle_t serverTask;
uint16_t serverPort;
@@ -159,7 +157,7 @@ template <typename ST, typename CT>
snprintf(taskName, 18, "MBserve%04X", port);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 5, &serverTask, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 5, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Server task %s started (%d).\n", taskName, (uint32_t)serverTask);
// Wait two seconds for it to establish
@@ -206,7 +204,7 @@ bool ModbusServerTCP<ST, CT>::accept(CT& client, uint32_t timeout, int coreID) {
snprintf(taskName, 18, "MBsrv%02Xclnt", i);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&worker, taskName, SERVER_TASK_STACK, clients[i], 5, &clients[i]->task, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&worker, taskName, SERVER_TASK_STACK, clients[i], 5, &clients[i]->task, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Started client %d task %d\n", i, (uint32_t)(clients[i]->task));
return true;

View File

@@ -109,8 +109,8 @@ const uint8_t swapTables[8][8] = {
enum FCType : uint8_t {
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
FC07_TYPE, // no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11)
FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f)
FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10)
FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of bytes (FC 0x0f)
FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of words (FC 0x10)
FC16_TYPE, // three uint16_t parameters (FC 0x16)
FC18_TYPE, // one uint16_t parameter (FC 0x18)
FCGENERIC, // for FCs not yet explicitly coded (or too complex)

View File

@@ -3,13 +3,13 @@
// MIT license - see license.md for details
// =================================================================================================
#include "options.h"
#if HAS_FREERTOS
#include "ModbusMessage.h"
#include "RTUutils.h"
#undef LOCAL_LOG_LEVEL
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
#include "Logging.h"
#if HAS_FREERTOS
// calcCRC: calculate Modbus CRC16 on a given array of bytes
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
// CRC16 pre-calculated tables
@@ -57,10 +57,9 @@ uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
uint8_t crcHi = 0xFF;
uint8_t crcLo = 0xFF;
uint8_t index;
while (len--) {
index = crcLo ^ *data++;
uint8_t index = crcLo ^ *data++;
crcLo = crcHi ^ crcHiTable[index];
crcHi = crcLoTable[index];
}
@@ -464,4 +463,5 @@ const char RTUutils::ASCIIread[] = {
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
};
#endif

View File

@@ -5,9 +5,6 @@
#ifndef _RTU_UTILS_H
#define _RTU_UTILS_H
#include <stdint.h>
#if NEED_UART_PATCH
#include <soc/uart_struct.h>
#endif
#include <vector>
#include "Stream.h"
#include "ModbusTypeDefs.h"
@@ -52,11 +49,13 @@ public:
// RTSauto: dummy callback for auto half duplex RS485 boards
inline static void RTSauto(bool level) { return; } // NOLINT
#if HAS_FREERTOS
// Necessary preparations for a HardwareSerial
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
s.setRxBufferSize(bufferSize);
s.setTxBufferSize(bufferSize);
}
#endif
protected:
// Printable characters for ASCII protocol: 012345678ABCDEF

View File

@@ -12,7 +12,6 @@
#define HAS_FREERTOS 1
#define HAS_ETHERNET 1
#define IS_LINUX 0
#define NEED_UART_PATCH 1
const unsigned int SERVER_TASK_STACK = 4096;
const unsigned int CLIENT_TASK_STACK = 4096;
@@ -23,7 +22,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
#define HAS_FREERTOS 0
#define HAS_ETHERNET 0
#define IS_LINUX 0
#define NEED_UART_PATCH 0
/* === LINUX DEFINITIONS AND MACROS === */
#elif defined(__linux__)
@@ -31,7 +29,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
#define HAS_FREERTOS 0
#define HAS_ETHERNET 0
#define IS_LINUX 1
#define NEED_UART_PATCH 0
#include <cstdio> // for printf()
#include <cstring> // for memcpy(), strlen() etc.
#include <cinttypes> // for uint32_t etc.