diff --git a/lib/eModbus/library.properties b/lib/eModbus/library.properties new file mode 100644 index 000000000..904bda3aa --- /dev/null +++ b/lib/eModbus/library.properties @@ -0,0 +1,9 @@ +name=eModbus +version=1.7.4 +author=bertmelis,Miq1 +maintainer=Miq1 +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 diff --git a/lib/eModbus/src/CoilData.cpp b/lib/eModbus/src/CoilData.cpp index 8348080d5..42b7839a8 100644 --- a/lib/eModbus/src/CoilData.cpp +++ b/lib/eModbus/src/CoilData.cpp @@ -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; diff --git a/lib/eModbus/src/ModbusBridgeTemp.h b/lib/eModbus/src/ModbusBridgeTemp.h index 8bd2f039c..96fae9bb9 100644 --- a/lib/eModbus/src/ModbusBridgeTemp.h +++ b/lib/eModbus/src/ModbusBridgeTemp.h @@ -8,6 +8,7 @@ #include #include #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::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(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(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); diff --git a/lib/eModbus/src/ModbusClient.cpp b/lib/eModbus/src/ModbusClient.cpp index d57c1397a..bd1f627ee 100644 --- a/lib/eModbus/src/ModbusClient.cpp +++ b/lib/eModbus/src/ModbusClient.cpp @@ -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) { diff --git a/lib/eModbus/src/ModbusClient.h b/lib/eModbus/src/ModbusClient.h index 2025a1f28..3c9ec42e3 100644 --- a/lib/eModbus/src/ModbusClient.h +++ b/lib/eModbus/src/ModbusClient.h @@ -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; diff --git a/lib/eModbus/src/ModbusClientRTU.cpp b/lib/eModbus/src/ModbusClientRTU.cpp index 9052ab2c2..2520d83b7 100644 --- a/lib/eModbus/src/ModbusClientRTU.cpp +++ b/lib/eModbus/src/ModbusClientRTU.cpp @@ -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 empty; LOCK_GUARD(lockGuard, qLock); + // Empty queue std::swap(requests, empty); } @@ -342,8 +343,11 @@ void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) { { // Safely lock the queue LOCK_GUARD(lockGuard, instance->qLock); - // Remove the front queue entry - instance->requests.pop(); + + // Remove the front queue entry if the queue is not empty + if (!instance->requests.empty()) { + instance->requests.pop(); + } } } else { delay(1); diff --git a/lib/eModbus/src/ModbusClientRTU.h b/lib/eModbus/src/ModbusClientRTU.h index 9f419ee34..280981cad 100644 --- a/lib/eModbus/src/ModbusClientRTU.h +++ b/lib/eModbus/src/ModbusClientRTU.h @@ -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 requests; // Queue to hold requests to be processed #if USE_MUTEX mutex qLock; // Mutex to protect queue diff --git a/lib/eModbus/src/ModbusClientTCP.cpp b/lib/eModbus/src/ModbusClientTCP.cpp index ec34a2d52..b6ea5d431 100644 --- a/lib/eModbus/src/ModbusClientTCP.cpp +++ b/lib/eModbus/src/ModbusClientTCP.cpp @@ -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(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 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 - instance->requests.pop(); + + // 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"); diff --git a/lib/eModbus/src/ModbusClientTCP.h b/lib/eModbus/src/ModbusClientTCP.h index 23f4af5c2..3e5e1bfe3 100644 --- a/lib/eModbus/src/ModbusClientTCP.h +++ b/lib/eModbus/src/ModbusClientTCP.h @@ -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 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 friend class ModbusBridge; diff --git a/lib/eModbus/src/ModbusClientTCPasync.cpp b/lib/eModbus/src/ModbusClientTCPasync.cpp index 163d552a0..949de2c49 100644 --- a/lib/eModbus/src/ModbusClientTCPasync.cpp +++ b/lib/eModbus/src/ModbusClientTCPasync.cpp @@ -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) { diff --git a/lib/eModbus/src/ModbusClientTCPasync.h b/lib/eModbus/src/ModbusClientTCPasync.h index a1f2aeea0..f0d49b8d5 100644 --- a/lib/eModbus/src/ModbusClientTCPasync.h +++ b/lib/eModbus/src/ModbusClientTCPasync.h @@ -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(); diff --git a/lib/eModbus/src/ModbusMessage.cpp b/lib/eModbus/src/ModbusMessage.cpp index 1ed48360d..7e63bdb95 100644 --- a/lib/eModbus/src/ModbusMessage.cpp +++ b/lib/eModbus/src/ModbusMessage.cpp @@ -6,6 +6,7 @@ #undef LOCAL_LOG_LEVEL // #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR #include "Logging.h" +#include // 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 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 diff --git a/lib/eModbus/src/ModbusServer.cpp b/lib/eModbus/src/ModbusServer.cpp index b814bd9b2..90cdcda49 100644 --- a/lib/eModbus/src/ModbusServer.cpp +++ b/lib/eModbus/src/ModbusServer.cpp @@ -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; } diff --git a/lib/eModbus/src/ModbusServer.h b/lib/eModbus/src/ModbusServer.h index 933e27e9c..70c88a6f9 100644 --- a/lib/eModbus/src/ModbusServer.h +++ b/lib/eModbus/src/ModbusServer.h @@ -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> workerMap; // map on serverID->functionCode->worker function uint32_t messageCount; // Number of Requests processed uint32_t errorCount; // Number of errors responded diff --git a/lib/eModbus/src/ModbusServerEthernet.h b/lib/eModbus/src/ModbusServerEthernet.h index 0a8591031..c520c9f77 100644 --- a/lib/eModbus/src/ModbusServerEthernet.h +++ b/lib/eModbus/src/ModbusServerEthernet.h @@ -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" diff --git a/lib/eModbus/src/ModbusServerRTU.cpp b/lib/eModbus/src/ModbusServerRTU.cpp index 88859d8d3..f7e19eae2 100644 --- a/lib/eModbus/src/ModbusServerRTU.cpp +++ b/lib/eModbus/src/ModbusServerRTU.cpp @@ -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); } diff --git a/lib/eModbus/src/ModbusServerRTU.h b/lib/eModbus/src/ModbusServerRTU.h index fd5b8ab96..76c67b740 100644 --- a/lib/eModbus/src/ModbusServerRTU.h +++ b/lib/eModbus/src/ModbusServerRTU.h @@ -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); diff --git a/lib/eModbus/src/ModbusServerTCPasync.cpp b/lib/eModbus/src/ModbusServerTCPasync.cpp index c08372454..e8d0a43dc 100644 --- a/lib/eModbus/src/ModbusServerTCPasync.cpp +++ b/lib/eModbus/src/ModbusServerTCPasync.cpp @@ -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()); } diff --git a/lib/eModbus/src/ModbusServerTCPasync.h b/lib/eModbus/src/ModbusServerTCPasync.h index 410d8d6fb..4ae8ac3df 100644 --- a/lib/eModbus/src/ModbusServerTCPasync.h +++ b/lib/eModbus/src/ModbusServerTCPasync.h @@ -76,7 +76,6 @@ class ModbusServerTCPasync : public ModbusServer { bool isRunning(); protected: - inline void isInstance() { } void onClientConnect(AsyncClient* client); void onClientDisconnect(mb_client* client); diff --git a/lib/eModbus/src/ModbusServerTCPtemp.h b/lib/eModbus/src/ModbusServerTCPtemp.h index 7ae5b2bc9..56c1fa91d 100644 --- a/lib/eModbus/src/ModbusServerTCPtemp.h +++ b/lib/eModbus/src/ModbusServerTCPtemp.h @@ -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 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::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; diff --git a/lib/eModbus/src/ModbusTypeDefs.h b/lib/eModbus/src/ModbusTypeDefs.h index 5aad1d5dd..cb281ad20 100644 --- a/lib/eModbus/src/ModbusTypeDefs.h +++ b/lib/eModbus/src/ModbusTypeDefs.h @@ -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) diff --git a/lib/eModbus/src/RTUutils.cpp b/lib/eModbus/src/RTUutils.cpp index d360c95af..c92d0ffd5 100644 --- a/lib/eModbus/src/RTUutils.cpp +++ b/lib/eModbus/src/RTUutils.cpp @@ -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 diff --git a/lib/eModbus/src/RTUutils.h b/lib/eModbus/src/RTUutils.h index f50d8f449..e3f41be3e 100644 --- a/lib/eModbus/src/RTUutils.h +++ b/lib/eModbus/src/RTUutils.h @@ -5,9 +5,6 @@ #ifndef _RTU_UTILS_H #define _RTU_UTILS_H #include -#if NEED_UART_PATCH -#include -#endif #include #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 diff --git a/lib/eModbus/src/options.h b/lib/eModbus/src/options.h index c65bc1c2b..9de44e97a 100644 --- a/lib/eModbus/src/options.h +++ b/lib/eModbus/src/options.h @@ -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 // for printf() #include // for memcpy(), strlen() etc. #include // for uint32_t etc.