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
|
// Assignment operator
|
||||||
CoilData& CoilData::operator=(const CoilData& m) {
|
CoilData& CoilData::operator=(const CoilData& m) {
|
||||||
|
// Avoid self-assignment
|
||||||
|
if (this == &m) return *this;
|
||||||
// Remove old data
|
// Remove old data
|
||||||
if (CDbuffer) {
|
if (CDbuffer) {
|
||||||
delete CDbuffer;
|
delete CDbuffer;
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <map>
|
#include <map>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include "ModbusClient.h"
|
#include "ModbusClient.h"
|
||||||
|
#include "ModbusServer.h"
|
||||||
#include "ModbusClientTCP.h" // Needed for client.setTarget()
|
#include "ModbusClientTCP.h" // Needed for client.setTarget()
|
||||||
#include "RTUutils.h" // Needed for RTScallback
|
#include "RTUutils.h" // Needed for RTScallback
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ public:
|
|||||||
ModbusBridge();
|
ModbusBridge();
|
||||||
|
|
||||||
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
|
// 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);
|
ModbusBridge(uint32_t timeout, RTScallback rts);
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
@@ -230,42 +231,56 @@ ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
|
|||||||
uint8_t aliasID = msg.getServerID();
|
uint8_t aliasID = msg.getServerID();
|
||||||
uint8_t functionCode = msg.getFunctionCode();
|
uint8_t functionCode = msg.getFunctionCode();
|
||||||
ModbusMessage response;
|
ModbusMessage response;
|
||||||
|
bool foundServer = false;
|
||||||
|
uint8_t usableID = 255;
|
||||||
|
|
||||||
// Find the (alias) serverID
|
// Find the (alias) serverID
|
||||||
if (servers.find(aliasID) != servers.end()) {
|
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
|
// Found it. We may use servers[aliasID] now without allocating a new map slot
|
||||||
|
|
||||||
// Request filter hook to be called here
|
// Request filter hook to be called here
|
||||||
if (servers[aliasID]->requestFilter) {
|
if (servers[usableID]->requestFilter) {
|
||||||
LOG_D("Calling request filter\n");
|
LOG_D("Calling request filter\n");
|
||||||
msg = servers[aliasID]->requestFilter(msg);
|
msg = servers[usableID]->requestFilter(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set real target server ID
|
// Set real target server ID
|
||||||
msg.setServerID(servers[aliasID]->serverID);
|
if (servers[usableID]->serverID != ANY_SERVER) {
|
||||||
|
msg.setServerID(servers[usableID]->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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Response filter hook to be called here
|
// Issue the request
|
||||||
if (servers[aliasID]->responseFilter) {
|
LOG_D("Request (%02X/%02X) sent\n", servers[usableID]->serverID, msg.getFunctionCode());
|
||||||
LOG_D("Calling response filter\n");
|
// TCP servers have a target host/port that needs to be set in the client
|
||||||
response = servers[aliasID]->responseFilter(response);
|
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)
|
// Re-set the requested server ID and function code (may have been modified by filters)
|
||||||
response.setServerID(aliasID);
|
response.setServerID(aliasID);
|
||||||
|
|
||||||
if (response.getError() != SUCCESS) {
|
if (response.getError() != SUCCESS) {
|
||||||
response.setFunctionCode(functionCode | 0x80);
|
response.setFunctionCode(functionCode | 0x80);
|
||||||
} else {
|
} else {
|
||||||
response.setFunctionCode(functionCode);
|
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 {
|
} else {
|
||||||
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
// If we get here, something has gone wrong internally. We send back an error response anyway.
|
||||||
response.setError(aliasID, functionCode, INVALID_SERVER);
|
response.setError(aliasID, functionCode, INVALID_SERVER);
|
||||||
|
|||||||
@@ -21,6 +21,13 @@ ModbusClient::ModbusClient() :
|
|||||||
onError(nullptr),
|
onError(nullptr),
|
||||||
onResponse(nullptr) { instanceCounter++; }
|
onResponse(nullptr) { instanceCounter++; }
|
||||||
|
|
||||||
|
// Default destructor: reduce number of clients by one
|
||||||
|
ModbusClient::~ModbusClient() {
|
||||||
|
if (instanceCounter) {
|
||||||
|
instanceCounter--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// onDataHandler: register callback for data responses
|
// onDataHandler: register callback for data responses
|
||||||
bool ModbusClient::onDataHandler(MBOnData handler) {
|
bool ModbusClient::onDataHandler(MBOnData handler) {
|
||||||
if (onData) {
|
if (onData) {
|
||||||
|
|||||||
@@ -37,8 +37,8 @@ public:
|
|||||||
uint32_t getMessageCount(); // Informative: return number of messages created
|
uint32_t getMessageCount(); // Informative: return number of messages created
|
||||||
uint32_t getErrorCount(); // Informative: return number of errors received
|
uint32_t getErrorCount(); // Informative: return number of errors received
|
||||||
void resetCounts(); // Set both message and error counts to zero
|
void resetCounts(); // Set both message and error counts to zero
|
||||||
inline Error addRequest(ModbusMessage m, uint32_t token) { return addRequestM(m, token); }
|
inline Error addRequest(const ModbusMessage& m, uint32_t token) { return addRequestM(m, token); }
|
||||||
inline ModbusMessage syncRequest(ModbusMessage m, uint32_t token) { return syncRequestM(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
|
// Template function to generate syncRequest functions as long as there is a
|
||||||
// matching ModbusMessage::setMessage() call
|
// matching ModbusMessage::setMessage() call
|
||||||
@@ -85,7 +85,7 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
ModbusClient(); // Default constructor
|
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
|
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 addRequest variant needed internally. All others done by template!
|
||||||
virtual Error addRequestM(ModbusMessage msg, uint32_t token) = 0;
|
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];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XRTU", instanceCounter);
|
||||||
// Start task to handle the queue
|
// 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);
|
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;
|
std::queue<RequestEntry> empty;
|
||||||
LOCK_GUARD(lockGuard, qLock);
|
LOCK_GUARD(lockGuard, qLock);
|
||||||
|
// Empty queue
|
||||||
std::swap(requests, empty);
|
std::swap(requests, empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,8 +343,11 @@ void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) {
|
|||||||
{
|
{
|
||||||
// Safely lock the queue
|
// Safely lock the queue
|
||||||
LOCK_GUARD(lockGuard, instance->qLock);
|
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 {
|
} else {
|
||||||
delay(1);
|
delay(1);
|
||||||
|
|||||||
@@ -67,15 +67,15 @@ protected:
|
|||||||
uint32_t token;
|
uint32_t token;
|
||||||
ModbusMessage msg;
|
ModbusMessage msg;
|
||||||
bool isSyncRequest;
|
bool isSyncRequest;
|
||||||
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
|
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
|
||||||
token(t),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
isSyncRequest(syncReq) {}
|
isSyncRequest(syncReq) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest must be present
|
// Base addRequest and syncRequest must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
|
|
||||||
// addToQueue: send freshly created request to queue
|
// addToQueue: send freshly created request to queue
|
||||||
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
|
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
|
||||||
@@ -89,7 +89,6 @@ protected:
|
|||||||
// start background task
|
// start background task
|
||||||
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
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
|
queue<RequestEntry> requests; // Queue to hold requests to be processed
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
mutex qLock; // Mutex to protect queue
|
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_target(IPAddress(0, 0, 0, 0), 0, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
|
||||||
MT_defaultTimeout(DEFAULTTIMEOUT),
|
MT_defaultTimeout(DEFAULTTIMEOUT),
|
||||||
MT_defaultInterval(TARGETHOSTINTERVAL),
|
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
|
// 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_target(host, port, DEFAULTTIMEOUT, TARGETHOSTINTERVAL),
|
||||||
MT_defaultTimeout(DEFAULTTIMEOUT),
|
MT_defaultTimeout(DEFAULTTIMEOUT),
|
||||||
MT_defaultInterval(TARGETHOSTINTERVAL),
|
MT_defaultInterval(TARGETHOSTINTERVAL),
|
||||||
MT_qLimit(queueLimit)
|
MT_qLimit(queueLimit),
|
||||||
|
MT_timeoutsToClose(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
// Destructor: clean up queue, task etc.
|
// Destructor: clean up queue, task etc.
|
||||||
@@ -64,7 +66,7 @@ void ModbusClientTCP::end() {
|
|||||||
// begin: start worker task
|
// begin: start worker task
|
||||||
#if IS_LINUX
|
#if IS_LINUX
|
||||||
void *ModbusClientTCP::pHandle(void *p) {
|
void *ModbusClientTCP::pHandle(void *p) {
|
||||||
handleConnection((ModbusClientTCP *)p);
|
handleConnection(static_cast<ModbusClientTCP *>(p));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -84,7 +86,7 @@ void ModbusClientTCP::begin(int coreID) {
|
|||||||
char taskName[18];
|
char taskName[18];
|
||||||
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
|
snprintf(taskName, 18, "Modbus%02XTCP", instanceCounter);
|
||||||
// Start task to handle the queue
|
// 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);
|
LOG_D("TCP client worker %s started\n", taskName);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
@@ -119,9 +121,25 @@ uint32_t ModbusClientTCP::pendingRequests() {
|
|||||||
void ModbusClientTCP::clearQueue() {
|
void ModbusClientTCP::clearQueue() {
|
||||||
std::queue<RequestEntry *> empty;
|
std::queue<RequestEntry *> empty;
|
||||||
LOCK_GUARD(lockGuard, qLock);
|
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);
|
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
|
// Base addRequest for preformatted ModbusMessage and last set target
|
||||||
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
|
Error ModbusClientTCP::addRequestM(ModbusMessage msg, uint32_t token) {
|
||||||
Error rc = SUCCESS; // Return value
|
Error rc = SUCCESS; // Return value
|
||||||
@@ -225,6 +243,7 @@ bool ModbusClientTCP::addToQueue(uint32_t token, ModbusMessage request, TargetHo
|
|||||||
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
||||||
bool doNotPop;
|
bool doNotPop;
|
||||||
unsigned long lastRequest = millis();
|
unsigned long lastRequest = millis();
|
||||||
|
uint16_t timeoutCount = 0; // Run time counter of consecutive timeouts.
|
||||||
|
|
||||||
// Loop forever - or until task is killed
|
// Loop forever - or until task is killed
|
||||||
while (1) {
|
while (1) {
|
||||||
@@ -273,6 +292,8 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
// Did we get a normal response?
|
// Did we get a normal response?
|
||||||
if (response.getError()==SUCCESS) {
|
if (response.getError()==SUCCESS) {
|
||||||
LOG_D("Data response.\n");
|
LOG_D("Data response.\n");
|
||||||
|
// Reset timeout counter
|
||||||
|
timeoutCount = 0;
|
||||||
// Yes. Is it a synchronous request?
|
// Yes. Is it a synchronous request?
|
||||||
if (request->isSyncRequest) {
|
if (request->isSyncRequest) {
|
||||||
// Yes. Put the response into the response map
|
// Yes. Put the response into the response map
|
||||||
@@ -299,6 +320,25 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
LOCK_GUARD(responseCnt, instance->countAccessM);
|
LOCK_GUARD(responseCnt, instance->countAccessM);
|
||||||
instance->errorCount++;
|
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?
|
// Is it a synchronous request?
|
||||||
if (request->isSyncRequest) {
|
if (request->isSyncRequest) {
|
||||||
// Yes. Put the response into the response map
|
// Yes. Put the response into the response map
|
||||||
@@ -345,8 +385,11 @@ void ModbusClientTCP::handleConnection(ModbusClientTCP *instance) {
|
|||||||
{
|
{
|
||||||
// Safely lock the queue
|
// Safely lock the queue
|
||||||
LOCK_GUARD(lockGuard, instance->qLock);
|
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
|
||||||
delete request;
|
delete request;
|
||||||
LOG_D("Request popped from queue.\n");
|
LOG_D("Request popped from queue.\n");
|
||||||
|
|||||||
@@ -50,6 +50,11 @@ public:
|
|||||||
// Remove all pending request from queue
|
// Remove all pending request from queue
|
||||||
void clearQueue();
|
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:
|
protected:
|
||||||
// class describing a target server
|
// class describing a target server
|
||||||
struct TargetHost {
|
struct TargetHost {
|
||||||
@@ -58,7 +63,7 @@ protected:
|
|||||||
uint32_t timeout; // Time in ms waiting for a response
|
uint32_t timeout; // Time in ms waiting for a response
|
||||||
uint32_t interval; // Time in ms to wait between requests
|
uint32_t interval; // Time in ms to wait between requests
|
||||||
|
|
||||||
inline TargetHost& operator=(TargetHost& t) {
|
inline TargetHost& operator=(const TargetHost& t) {
|
||||||
host = t.host;
|
host = t.host;
|
||||||
port = t.port;
|
port = t.port;
|
||||||
timeout = t.timeout;
|
timeout = t.timeout;
|
||||||
@@ -66,7 +71,7 @@ protected:
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline TargetHost(TargetHost& t) :
|
inline TargetHost(const TargetHost& t) :
|
||||||
host(t.host),
|
host(t.host),
|
||||||
port(t.port),
|
port(t.port),
|
||||||
timeout(t.timeout),
|
timeout(t.timeout),
|
||||||
@@ -86,13 +91,13 @@ protected:
|
|||||||
interval(interval)
|
interval(interval)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
inline bool operator==(TargetHost& t) {
|
inline bool operator==(const TargetHost& t) {
|
||||||
if (host != t.host) return false;
|
if (host != t.host) return false;
|
||||||
if (port != t.port) return false;
|
if (port != t.port) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool operator!=(TargetHost& t) {
|
inline bool operator!=(const TargetHost& t) {
|
||||||
if (host != t.host) return true;
|
if (host != t.host) return true;
|
||||||
if (port != t.port) return true;
|
if (port != t.port) return true;
|
||||||
return false;
|
return false;
|
||||||
@@ -135,7 +140,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
struct RequestEntry {
|
||||||
@@ -144,7 +149,7 @@ protected:
|
|||||||
TargetHost target;
|
TargetHost target;
|
||||||
ModbusTCPhead head;
|
ModbusTCPhead head;
|
||||||
bool isSyncRequest;
|
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),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
target(tg),
|
target(tg),
|
||||||
@@ -153,8 +158,8 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest must be present
|
// Base addRequest and syncRequest must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
// TCP-specific addition "...MT()" including adhoc target - used by bridge
|
// TCP-specific addition "...MT()" including adhoc target - used by bridge
|
||||||
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
|
Error addRequestMT(ModbusMessage msg, uint32_t token, IPAddress targetHost, uint16_t targetPort);
|
||||||
ModbusMessage syncRequestMT(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
|
// receive: get response via Client connection
|
||||||
ModbusMessage receive(RequestEntry *request);
|
ModbusMessage receive(RequestEntry *request);
|
||||||
|
|
||||||
void isInstance() { return; } // make class instantiable
|
|
||||||
queue<RequestEntry *> requests; // Queue to hold requests to be processed
|
queue<RequestEntry *> requests; // Queue to hold requests to be processed
|
||||||
#if USE_MUTEX
|
#if USE_MUTEX
|
||||||
mutex qLock; // Mutex to protect queue
|
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_defaultTimeout; // Standard timeout value taken if no dedicated was set
|
||||||
uint32_t MT_defaultInterval; // Standard interval 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
|
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
|
// Let any ModbusBridge class use protected members
|
||||||
template<typename SERVERCLASS> friend class ModbusBridge;
|
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) {
|
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
|
// reset idle timeout
|
||||||
MTA_lastActivity = millis();
|
MTA_lastActivity = millis();
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
LOG_D("parsing (len:%d)\n", length + 1);
|
LOG_D("parsing (len:%u)\n", length + 1);
|
||||||
}
|
}
|
||||||
while (length > 0) {
|
while (length > 0) {
|
||||||
RequestEntry* request = nullptr;
|
RequestEntry* request = nullptr;
|
||||||
ModbusMessage* response = nullptr;
|
ModbusMessage* response = nullptr;
|
||||||
uint16_t transactionID = 0;
|
uint16_t transactionID = 0;
|
||||||
uint16_t protocolID = 0;
|
|
||||||
uint16_t messageLength = 0;
|
|
||||||
bool isOkay = false;
|
bool isOkay = false;
|
||||||
|
|
||||||
// 1. Check for valid modbus message
|
// 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])
|
// total message should fit MBAP plus remaining bytes (in data[4], data[5])
|
||||||
if (length > 6) {
|
if (length > 6) {
|
||||||
transactionID = (data[0] << 8) | data[1];
|
transactionID = (data[0] << 8) | data[1];
|
||||||
protocolID = (data[2] << 8) | data[3];
|
uint16_t protocolID = (data[2] << 8) | data[3];
|
||||||
messageLength = (data[4] << 8) | data[5];
|
uint16_t messageLength = (data[4] << 8) | data[5];
|
||||||
if (protocolID == 0 &&
|
if (protocolID == 0 &&
|
||||||
length >= (uint32_t)messageLength + 6 &&
|
length >= (uint32_t)messageLength + 6 &&
|
||||||
messageLength < 256) {
|
messageLength < 256) {
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ protected:
|
|||||||
return headRoom;
|
return headRoom;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline ModbusTCPhead& operator= (ModbusTCPhead& t) {
|
inline ModbusTCPhead& operator= (const ModbusTCPhead& t) {
|
||||||
transactionID = t.transactionID;
|
transactionID = t.transactionID;
|
||||||
protocolID = t.protocolID;
|
protocolID = t.protocolID;
|
||||||
len = t.len;
|
len = t.len;
|
||||||
@@ -91,7 +91,7 @@ protected:
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
struct RequestEntry {
|
||||||
@@ -100,7 +100,7 @@ protected:
|
|||||||
ModbusTCPhead head;
|
ModbusTCPhead head;
|
||||||
uint32_t sentTime;
|
uint32_t sentTime;
|
||||||
bool isSyncRequest;
|
bool isSyncRequest;
|
||||||
RequestEntry(uint32_t t, ModbusMessage m, bool syncReq = false) :
|
RequestEntry(uint32_t t, const ModbusMessage& m, bool syncReq = false) :
|
||||||
token(t),
|
token(t),
|
||||||
msg(m),
|
msg(m),
|
||||||
head(ModbusTCPhead()),
|
head(ModbusTCPhead()),
|
||||||
@@ -109,8 +109,8 @@ protected:
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Base addRequest and syncRequest both must be present
|
// Base addRequest and syncRequest both must be present
|
||||||
Error addRequestM(ModbusMessage msg, uint32_t token);
|
Error addRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token);
|
ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) override;
|
||||||
|
|
||||||
// addToQueue: send freshly created request to queue
|
// addToQueue: send freshly created request to queue
|
||||||
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
|
bool addToQueue(int32_t token, ModbusMessage request, bool syncReq = false);
|
||||||
@@ -121,8 +121,6 @@ protected:
|
|||||||
// receive: get response via Client connection
|
// receive: get response via Client connection
|
||||||
// TCPResponse* receive(uint8_t* data, size_t length);
|
// 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
|
// TCP handling code, all static taking a class instancs as param
|
||||||
void onConnected();
|
void onConnected();
|
||||||
void onDisconnected();
|
void onDisconnected();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#undef LOCAL_LOG_LEVEL
|
#undef LOCAL_LOG_LEVEL
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
// Default Constructor - takes optional size of MM_data to allocate memory
|
// Default Constructor - takes optional size of MM_data to allocate memory
|
||||||
ModbusMessage::ModbusMessage(uint16_t dataLen) {
|
ModbusMessage::ModbusMessage(uint16_t dataLen) {
|
||||||
@@ -146,21 +147,19 @@ void ModbusMessage::setServerID(uint8_t serverID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void ModbusMessage::setFunctionCode(uint8_t FC) {
|
void ModbusMessage::setFunctionCode(uint8_t FC) {
|
||||||
// We accept here that [0], [1] may allocate bytes!
|
if (MM_data.size() < 2) {
|
||||||
if (MM_data.empty()) {
|
MM_data.resize(2); // Resize to at least 2 to ensure indices 0 and 1 are valid
|
||||||
MM_data.reserve(3); // At least an error message should fit
|
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
|
MM_data[1] = FC; // Safely set the function code
|
||||||
if (MM_data.size() < 2) MM_data[0] = 0; // intentional invalid server ID!
|
|
||||||
MM_data[1] = FC;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add() variant to copy a buffer into MM_data. Returns updated size
|
// 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 ModbusMessage::add(const uint8_t *arrayOfBytes, uint16_t count) {
|
||||||
|
uint16_t originalSize = MM_data.size();
|
||||||
|
MM_data.resize(originalSize + count);
|
||||||
// Copy it
|
// Copy it
|
||||||
while (count--) {
|
std::copy(arrayOfBytes, arrayOfBytes + count, MM_data.begin() + originalSize);
|
||||||
MM_data.push_back(*arrayOfBytes++);
|
|
||||||
}
|
|
||||||
// Return updated size (logical length of message so far)
|
// Return updated size (logical length of message so far)
|
||||||
return MM_data.size();
|
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
|
uint32_t i = 77230; // int value to go into a float without rounding error
|
||||||
float f = i; // assign it
|
float f = i; // assign it
|
||||||
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
|
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
|
uint8_t matches = 0; // number of bytes successfully matched
|
||||||
|
|
||||||
// Loop over the bytes of the expected sequence
|
// 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
|
uint64_t i = 5791007487489389; // int64 value to go into a double without rounding error
|
||||||
double f = i; // assign it
|
double f = i; // assign it
|
||||||
uint8_t *b = (uint8_t *)&f; // Pointer to bytes of f
|
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
|
uint8_t matches = 0; // number of bytes successfully matched
|
||||||
|
|
||||||
// Loop over the bytes of the expected sequence
|
// 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
|
// add() variant for a vector of uint8_t
|
||||||
uint16_t ModbusMessage::add(vector<uint8_t> v) {
|
uint16_t ModbusMessage::add(vector<uint8_t> v) {
|
||||||
for (auto& b: v) {
|
return add(v.data(), v.size());
|
||||||
MM_data.push_back(b);
|
|
||||||
}
|
|
||||||
return MM_data.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// add() variants for float and double values
|
// 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);
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
serverFound = true;
|
serverFound = true;
|
||||||
serverID = ANY_SERVER;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Did we find a serverID?
|
// Did we find a serverID?
|
||||||
@@ -49,7 +48,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) {
|
|||||||
if (fcmap != svmap->second.end()) {
|
if (fcmap != svmap->second.end()) {
|
||||||
// Yes. Return the function pointer for it.
|
// Yes. Return the function pointer for it.
|
||||||
functionCodeFound = true;
|
functionCodeFound = true;
|
||||||
functionCode = ANY_FUNCTION_CODE;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (functionCodeFound) {
|
if (functionCodeFound) {
|
||||||
@@ -104,6 +102,11 @@ bool ModbusServer::isServerFor(uint8_t serverID) {
|
|||||||
// Is there one?
|
// Is there one?
|
||||||
if (svmap != workerMap.end()) {
|
if (svmap != workerMap.end()) {
|
||||||
return true;
|
return true;
|
||||||
|
} else {
|
||||||
|
svmap = workerMap.find(ANY_SERVER);
|
||||||
|
if (svmap != workerMap.end()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -68,15 +68,12 @@ protected:
|
|||||||
ModbusServer();
|
ModbusServer();
|
||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~ModbusServer();
|
virtual ~ModbusServer();
|
||||||
|
|
||||||
// Prevent copy construction or assignment
|
// Prevent copy construction or assignment
|
||||||
ModbusServer(ModbusServer& other) = delete;
|
ModbusServer(ModbusServer& other) = delete;
|
||||||
ModbusServer& operator=(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
|
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 messageCount; // Number of Requests processed
|
||||||
uint32_t errorCount; // Number of errors responded
|
uint32_t errorCount; // Number of errors responded
|
||||||
|
|||||||
@@ -15,8 +15,8 @@
|
|||||||
// Create own non-virtual EthernetServer class
|
// Create own non-virtual EthernetServer class
|
||||||
class EthernetServerEM : public EthernetServer {
|
class EthernetServerEM : public EthernetServer {
|
||||||
public:
|
public:
|
||||||
EthernetServerEM(uint16_t port) : EthernetServer(port) { }
|
explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { }
|
||||||
void begin(uint16_t port = 0) { }
|
void begin(uint16_t port = 0) override { }
|
||||||
};
|
};
|
||||||
|
|
||||||
#include "ModbusServerTCPtemp.h"
|
#include "ModbusServerTCPtemp.h"
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
|
|||||||
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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);
|
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
|
||||||
}
|
}
|
||||||
@@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() {
|
|||||||
return MSRuseASCII;
|
return MSRuseASCII;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// set timeout
|
||||||
|
void ModbusServerRTU::setModbusTimeout(unsigned long timeout)
|
||||||
|
{
|
||||||
|
serverTimeout = timeout;
|
||||||
|
}
|
||||||
|
|
||||||
// Toggle skipping of leading 0x00 byte
|
// Toggle skipping of leading 0x00 byte
|
||||||
void ModbusServerRTU::skipLeading0x00(bool onOff) {
|
void ModbusServerRTU::skipLeading0x00(bool onOff) {
|
||||||
MSRskipLeadingZeroByte = onOff;
|
MSRskipLeadingZeroByte = onOff;
|
||||||
@@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
|
|||||||
response = m;
|
response = m;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// No callback. Is at least the serverID valid and no broadcast?
|
// No callback. Is at least the serverID valid?
|
||||||
if (myServer->isServerFor(request[0]) && request[0] != 0x00) {
|
if (myServer->isServerFor(request[0])) {
|
||||||
// Yes. Send back a ILLEGAL_FUNCTION error
|
// Yes. Send back a ILLEGAL_FUNCTION error
|
||||||
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
|
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,6 +47,9 @@ public:
|
|||||||
// Inquire protocol mode
|
// Inquire protocol mode
|
||||||
bool isModbusASCII();
|
bool isModbusASCII();
|
||||||
|
|
||||||
|
// set timeout
|
||||||
|
void setModbusTimeout(unsigned long timeout);
|
||||||
|
|
||||||
// Toggle skipping of leading 0x00 byte
|
// Toggle skipping of leading 0x00 byte
|
||||||
void skipLeading0x00(bool onOff = true);
|
void skipLeading0x00(bool onOff = true);
|
||||||
|
|
||||||
@@ -61,8 +64,6 @@ protected:
|
|||||||
ModbusServerRTU(ModbusServerRTU& m) = delete;
|
ModbusServerRTU(ModbusServerRTU& m) = delete;
|
||||||
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
|
ModbusServerRTU& operator=(ModbusServerRTU& m) = delete;
|
||||||
|
|
||||||
inline void isInstance() { } // Make class instantiable
|
|
||||||
|
|
||||||
// internal common begin function
|
// internal common begin function
|
||||||
void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval);
|
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) {
|
void ModbusServerTCPasync::mb_client::onData(uint8_t* data, size_t len) {
|
||||||
lastActiveTime = millis();
|
lastActiveTime = millis();
|
||||||
LOG_D("data len %d\n", len);
|
LOG_D("data len %u\n", len);
|
||||||
|
|
||||||
Error error = SUCCESS;
|
Error error = SUCCESS;
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
@@ -250,7 +250,7 @@ void ModbusServerTCPasync::onClientConnect(AsyncClient* client) {
|
|||||||
LOCK_GUARD(lock1, cListLock);
|
LOCK_GUARD(lock1, cListLock);
|
||||||
if (clients.size() < maxNoClients) {
|
if (clients.size() < maxNoClients) {
|
||||||
clients.emplace_back(new mb_client(this, client));
|
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 {
|
} else {
|
||||||
LOG_D("max number of clients reached, closing new\n");
|
LOG_D("max number of clients reached, closing new\n");
|
||||||
client->close(true);
|
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; });
|
clients.remove_if([client](mb_client* i) { return i->client == client->client; });
|
||||||
// delete client itself
|
// delete client itself
|
||||||
delete client;
|
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();
|
bool isRunning();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
inline void isInstance() { }
|
|
||||||
void onClientConnect(AsyncClient* client);
|
void onClientConnect(AsyncClient* client);
|
||||||
void onClientDisconnect(mb_client* client);
|
void onClientDisconnect(mb_client* client);
|
||||||
|
|
||||||
|
|||||||
@@ -44,8 +44,6 @@ protected:
|
|||||||
ModbusServerTCP(ModbusServerTCP& m) = delete;
|
ModbusServerTCP(ModbusServerTCP& m) = delete;
|
||||||
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
|
ModbusServerTCP& operator=(ModbusServerTCP& m) = delete;
|
||||||
|
|
||||||
inline void isInstance() { }
|
|
||||||
|
|
||||||
uint8_t numClients;
|
uint8_t numClients;
|
||||||
TaskHandle_t serverTask;
|
TaskHandle_t serverTask;
|
||||||
uint16_t serverPort;
|
uint16_t serverPort;
|
||||||
@@ -159,7 +157,7 @@ template <typename ST, typename CT>
|
|||||||
snprintf(taskName, 18, "MBserve%04X", port);
|
snprintf(taskName, 18, "MBserve%04X", port);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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);
|
LOG_D("Server task %s started (%d).\n", taskName, (uint32_t)serverTask);
|
||||||
|
|
||||||
// Wait two seconds for it to establish
|
// 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);
|
snprintf(taskName, 18, "MBsrv%02Xclnt", i);
|
||||||
|
|
||||||
// Start task to handle the client
|
// 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));
|
LOG_D("Started client %d task %d\n", i, (uint32_t)(clients[i]->task));
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -109,8 +109,8 @@ const uint8_t swapTables[8][8] = {
|
|||||||
enum FCType : uint8_t {
|
enum FCType : uint8_t {
|
||||||
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
|
FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06)
|
||||||
FC07_TYPE, // no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11)
|
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)
|
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 uint8_t* pointer to array of words (FC 0x10)
|
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)
|
FC16_TYPE, // three uint16_t parameters (FC 0x16)
|
||||||
FC18_TYPE, // one uint16_t parameter (FC 0x18)
|
FC18_TYPE, // one uint16_t parameter (FC 0x18)
|
||||||
FCGENERIC, // for FCs not yet explicitly coded (or too complex)
|
FCGENERIC, // for FCs not yet explicitly coded (or too complex)
|
||||||
|
|||||||
@@ -3,13 +3,13 @@
|
|||||||
// MIT license - see license.md for details
|
// MIT license - see license.md for details
|
||||||
// =================================================================================================
|
// =================================================================================================
|
||||||
#include "options.h"
|
#include "options.h"
|
||||||
|
#if HAS_FREERTOS
|
||||||
#include "ModbusMessage.h"
|
#include "ModbusMessage.h"
|
||||||
#include "RTUutils.h"
|
#include "RTUutils.h"
|
||||||
#undef LOCAL_LOG_LEVEL
|
#undef LOCAL_LOG_LEVEL
|
||||||
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
|
||||||
#include "Logging.h"
|
#include "Logging.h"
|
||||||
|
|
||||||
#if HAS_FREERTOS
|
|
||||||
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
// calcCRC: calculate Modbus CRC16 on a given array of bytes
|
||||||
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) {
|
||||||
// CRC16 pre-calculated tables
|
// 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 crcHi = 0xFF;
|
||||||
uint8_t crcLo = 0xFF;
|
uint8_t crcLo = 0xFF;
|
||||||
uint8_t index;
|
|
||||||
|
|
||||||
while (len--) {
|
while (len--) {
|
||||||
index = crcLo ^ *data++;
|
uint8_t index = crcLo ^ *data++;
|
||||||
crcLo = crcHi ^ crcHiTable[index];
|
crcLo = crcHi ^ crcHiTable[index];
|
||||||
crcHi = crcLoTable[index];
|
crcHi = crcLoTable[index];
|
||||||
}
|
}
|
||||||
@@ -464,4 +463,5 @@ const char RTUutils::ASCIIread[] = {
|
|||||||
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||||
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
#ifndef _RTU_UTILS_H
|
#ifndef _RTU_UTILS_H
|
||||||
#define _RTU_UTILS_H
|
#define _RTU_UTILS_H
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#if NEED_UART_PATCH
|
|
||||||
#include <soc/uart_struct.h>
|
|
||||||
#endif
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
#include "ModbusTypeDefs.h"
|
#include "ModbusTypeDefs.h"
|
||||||
@@ -52,11 +49,13 @@ public:
|
|||||||
// RTSauto: dummy callback for auto half duplex RS485 boards
|
// RTSauto: dummy callback for auto half duplex RS485 boards
|
||||||
inline static void RTSauto(bool level) { return; } // NOLINT
|
inline static void RTSauto(bool level) { return; } // NOLINT
|
||||||
|
|
||||||
|
#if HAS_FREERTOS
|
||||||
// Necessary preparations for a HardwareSerial
|
// Necessary preparations for a HardwareSerial
|
||||||
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
|
static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) {
|
||||||
s.setRxBufferSize(bufferSize);
|
s.setRxBufferSize(bufferSize);
|
||||||
s.setTxBufferSize(bufferSize);
|
s.setTxBufferSize(bufferSize);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// Printable characters for ASCII protocol: 012345678ABCDEF
|
// Printable characters for ASCII protocol: 012345678ABCDEF
|
||||||
|
|||||||
@@ -12,7 +12,6 @@
|
|||||||
#define HAS_FREERTOS 1
|
#define HAS_FREERTOS 1
|
||||||
#define HAS_ETHERNET 1
|
#define HAS_ETHERNET 1
|
||||||
#define IS_LINUX 0
|
#define IS_LINUX 0
|
||||||
#define NEED_UART_PATCH 1
|
|
||||||
const unsigned int SERVER_TASK_STACK = 4096;
|
const unsigned int SERVER_TASK_STACK = 4096;
|
||||||
const unsigned int CLIENT_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_FREERTOS 0
|
||||||
#define HAS_ETHERNET 0
|
#define HAS_ETHERNET 0
|
||||||
#define IS_LINUX 0
|
#define IS_LINUX 0
|
||||||
#define NEED_UART_PATCH 0
|
|
||||||
|
|
||||||
/* === LINUX DEFINITIONS AND MACROS === */
|
/* === LINUX DEFINITIONS AND MACROS === */
|
||||||
#elif defined(__linux__)
|
#elif defined(__linux__)
|
||||||
@@ -31,7 +29,6 @@ const unsigned int CLIENT_TASK_STACK = 4096;
|
|||||||
#define HAS_FREERTOS 0
|
#define HAS_FREERTOS 0
|
||||||
#define HAS_ETHERNET 0
|
#define HAS_ETHERNET 0
|
||||||
#define IS_LINUX 1
|
#define IS_LINUX 1
|
||||||
#define NEED_UART_PATCH 0
|
|
||||||
#include <cstdio> // for printf()
|
#include <cstdio> // for printf()
|
||||||
#include <cstring> // for memcpy(), strlen() etc.
|
#include <cstring> // for memcpy(), strlen() etc.
|
||||||
#include <cinttypes> // for uint32_t etc.
|
#include <cinttypes> // for uint32_t etc.
|
||||||
|
|||||||
Reference in New Issue
Block a user