mirror of
https://github.com/emsesp/EMS-ESP32.git
synced 2025-12-06 07:49:52 +03:00
update to eModbus 1.7.4
This commit is contained in:
9
lib/eModbus/library.properties
Normal file
9
lib/eModbus/library.properties
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -76,7 +76,6 @@ class ModbusServerTCPasync : public ModbusServer {
|
||||
bool isRunning();
|
||||
|
||||
protected:
|
||||
inline void isInstance() { }
|
||||
void onClientConnect(AsyncClient* client);
|
||||
void onClientDisconnect(mb_client* client);
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user