Remove unused modbus files to reduce sketch size

This commit is contained in:
Daniel Öster 2025-01-08 19:57:34 +02:00
parent 606866e3e3
commit 969724654d
9 changed files with 0 additions and 955 deletions

View file

@ -1,21 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_BRIDGE_ETHERNET_H
#define _MODBUS_BRIDGE_ETHERNET_H
#include "options.h"
#if HAS_ETHERNET == 1
#include <Ethernet.h>
#include <SPI.h>
#undef SERVER_END
#define SERVER_END // NIL for Ethernet
#include "ModbusServerTCPtemp.h"
#include "ModbusBridgeTemp.h"
using ModbusBridgeEthernet = ModbusBridge<ModbusServerTCP<EthernetServer, EthernetClient>>;
#endif
#endif

View file

@ -1,14 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_BRIDGE_RTU_H
#define _MODBUS_BRIDGE_RTU_H
#include "options.h"
#include "ModbusServerRTU.h"
#include "ModbusBridgeTemp.h"
#include "RTUutils.h"
using ModbusBridgeRTU = ModbusBridge<ModbusServerRTU>;
#endif

View file

@ -1,199 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_BRIDGE_TEMP_H
#define _MODBUS_BRIDGE_TEMP_H
#include <map>
#include <functional>
#include "ModbusClient.h"
#include "ModbusClientTCP.h" // Needed for client.setTarget()
#include "RTUutils.h" // Needed for RTScallback
#undef LOCAL_LOG_LEVEL
#define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
#include "Logging.h"
using std::bind;
using std::placeholders::_1;
// Known server types: TCP (client, host/port) and RTU (client)
enum ServerType : uint8_t { TCP_SERVER, RTU_SERVER };
// Bridge class template, takes one of ModbusServerRTU, ModbusServerWiFi, ModbusServerEthernet or ModbusServerTCPasync as parameter
template<typename SERVERCLASS>
class ModbusBridge : public SERVERCLASS {
public:
// Constructor for TCP server variants.
ModbusBridge();
// Constructors for the RTU variant. Parameters as are for ModbusServerRTU
ModbusBridge(uint32_t timeout, int rtsPin = -1);
ModbusBridge(uint32_t timeout, RTScallback rts);
// Destructor
~ModbusBridge();
// Method to link external servers to the bridge
bool attachServer(uint8_t aliasID, uint8_t serverID, uint8_t functionCode, ModbusClient *client, IPAddress host = IPAddress(0, 0, 0, 0), uint16_t port = 0);
// Link another function code to the server
bool addFunctionCode(uint8_t aliasID, uint8_t functionCode);
// Block a function code (respond with ILLEGAL_FUNCTION error)
bool denyFunctionCode(uint8_t aliasID, uint8_t functionCode);
protected:
// ServerData holds all data necessary to address a single server
struct ServerData {
uint8_t serverID; // External server id
ModbusClient *client; // client to be used to request the server
ServerType serverType; // TCP_SERVER or RTU_SERVER
IPAddress host; // TCP: host IP address, else 0.0.0.0
uint16_t port; // TCP: host port number, else 0
// RTU constructor
ServerData(uint8_t sid, ModbusClient *c) :
serverID(sid),
client(c),
serverType(RTU_SERVER),
host(IPAddress(0, 0, 0, 0)),
port(0) {}
// TCP constructor
ServerData(uint8_t sid, ModbusClient *c, IPAddress h, uint16_t p) :
serverID(sid),
client(c),
serverType(TCP_SERVER),
host(h),
port(p) {}
};
// Default worker functions
ModbusMessage bridgeWorker(ModbusMessage msg);
ModbusMessage bridgeDenyWorker(ModbusMessage msg);
// Map of servers attached
std::map<uint8_t, ServerData *> servers;
};
// Constructor for TCP variants
template<typename SERVERCLASS>
ModbusBridge<SERVERCLASS>::ModbusBridge() :
SERVERCLASS() { }
// Constructors for RTU variant
template<typename SERVERCLASS>
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, int rtsPin) :
SERVERCLASS(timeout, rtsPin) { }
// Alternate constructors for RTU variant
template<typename SERVERCLASS>
ModbusBridge<SERVERCLASS>::ModbusBridge(uint32_t timeout, RTScallback rts) :
SERVERCLASS(timeout, rts) { }
// Destructor
template<typename SERVERCLASS>
ModbusBridge<SERVERCLASS>::~ModbusBridge() {
// Release ServerData storage in servers array
for (auto itr = servers.begin(); itr != servers.end(); itr++) {
delete (itr->second);
}
servers.clear();
}
// attachServer: memorize the access data for an external server with ID serverID under bridge ID aliasID
template<typename SERVERCLASS>
bool ModbusBridge<SERVERCLASS>::attachServer(uint8_t aliasID, uint8_t serverID, uint8_t functionCode, ModbusClient *client, IPAddress host, uint16_t port) {
// Is there already an entry for the aliasID?
if (servers.find(aliasID) == servers.end()) {
// No. Store server data in map.
// Do we have a port number?
if (port != 0) {
// Yes. Must be a TCP client
servers[aliasID] = new ServerData(serverID, static_cast<ModbusClient *>(client), host, port);
LOG_D("(TCP): %02X->%02X %d.%d.%d.%d:%d\n", aliasID, serverID, host[0], host[1], host[2], host[3], port);
} else {
// No - RTU client required
servers[aliasID] = new ServerData(serverID, static_cast<ModbusClient *>(client));
LOG_D("(RTU): %02X->%02X\n", aliasID, serverID);
}
}
// Register the server/FC combination for the bridgeWorker
addFunctionCode(aliasID, functionCode);
return true;
}
template<typename SERVERCLASS>
bool ModbusBridge<SERVERCLASS>::addFunctionCode(uint8_t aliasID, uint8_t functionCode) {
// Is there already an entry for the aliasID?
if (servers.find(aliasID) != servers.end()) {
// Yes. Link server to own worker function
this->registerWorker(aliasID, functionCode, std::bind(&ModbusBridge<SERVERCLASS>::bridgeWorker, this, std::placeholders::_1));
LOG_D("FC %02X added for server %02X\n", functionCode, aliasID);
} else {
LOG_E("Server %d not attached to bridge!\n", aliasID);
return false;
}
return true;
}
template<typename SERVERCLASS>
bool ModbusBridge<SERVERCLASS>::denyFunctionCode(uint8_t aliasID, uint8_t functionCode) {
// Is there already an entry for the aliasID?
if (servers.find(aliasID) != servers.end()) {
// Yes. Link server to own worker function
this->registerWorker(aliasID, functionCode, std::bind(&ModbusBridge<SERVERCLASS>::bridgeDenyWorker, this, std::placeholders::_1));
LOG_D("FC %02X blocked for server %02X\n", functionCode, aliasID);
} else {
LOG_E("Server %d not attached to bridge!\n", aliasID);
return false;
}
return true;
}
// bridgeWorker: default worker function to process bridge requests
template<typename SERVERCLASS>
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeWorker(ModbusMessage msg) {
uint8_t aliasID = msg.getServerID();
uint8_t functionCode = msg.getFunctionCode();
ModbusMessage response;
// Find the (alias) serverID
if (servers.find(aliasID) != servers.end()) {
// Found it. We may use servers[aliasID] now without allocating a new map slot
// Set real target server ID
msg.setServerID(servers[aliasID]->serverID);
// Issue the request
LOG_D("Request (%02X/%02X) sent\n", servers[aliasID]->serverID, functionCode);
// 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());
}
// Re-set the requested server ID
response.setServerID(aliasID);
} else {
// If we get here, something has gone wrong internally. We send back an error response anyway.
response.setError(aliasID, functionCode, INVALID_SERVER);
}
return response;
}
// bridgeDenyWorker: worker function to block function codes
template<typename SERVERCLASS>
ModbusMessage ModbusBridge<SERVERCLASS>::bridgeDenyWorker(ModbusMessage msg) {
ModbusMessage response;
response.setError(msg.getServerID(), msg.getFunctionCode(), ILLEGAL_FUNCTION);
return response;
}
#endif

View file

@ -1,18 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_BRIDGE_WIFI_H
#define _MODBUS_BRIDGE_WIFI_H
#include "options.h"
#include <WiFi.h>
#undef SERVER_END
#define SERVER_END server.end();
#include "ModbusServerTCPtemp.h"
#include "ModbusBridgeTemp.h"
using ModbusBridgeWiFi = ModbusBridge<ModbusServerTCP<WiFiServer, WiFiClient>>;
#endif

View file

@ -1,103 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#include "ModbusClient.h"
#undef LOCAL_LOG_LEVEL
#include "Logging.h"
uint16_t ModbusClient::instanceCounter = 0;
// Default constructor: set the default timeout to 2000ms, zero out all other
ModbusClient::ModbusClient() :
messageCount(0),
errorCount(0),
#if HAS_FREERTOS
worker(NULL),
#elif IS_LINUX
worker(0),
#endif
onData(nullptr),
onError(nullptr),
onResponse(nullptr) { instanceCounter++; }
// onDataHandler: register callback for data responses
bool ModbusClient::onDataHandler(MBOnData handler) {
if (onData) {
LOG_W("onData handler was already claimed\n");
} else if (onResponse) {
LOG_E("onData handler is unavailable with an onResponse handler\n");
return false;
}
onData = handler;
return true;
}
// onErrorHandler: register callback for error responses
bool ModbusClient::onErrorHandler(MBOnError handler) {
if (onError) {
LOG_W("onError handler was already claimed\n");
} else if (onResponse) {
LOG_E("onError handler is unavailable with an onResponse handler\n");
return false;
}
onError = handler;
return true;
}
// onResponseHandler: register callback for error responses
bool ModbusClient::onResponseHandler(MBOnResponse handler) {
if (onError || onData) {
LOG_E("onResponse handler is unavailable with an onData or onError handler\n");
return false;
}
onResponse = handler;
return true;
}
// getMessageCount: return message counter value
uint32_t ModbusClient::getMessageCount() {
return messageCount;
}
// getErrorCount: return error counter value
uint32_t ModbusClient::getErrorCount() {
return errorCount;
}
// resetCounts: Set both message and error counts to zero
void ModbusClient::resetCounts() {
{
LOCK_GUARD(cntLock, countAccessM);
messageCount = 0;
errorCount = 0;
}
}
// waitSync: wait for response on syncRequest to arrive
ModbusMessage ModbusClient::waitSync(uint8_t serverID, uint8_t functionCode, uint32_t token) {
ModbusMessage response;
unsigned long lostPatience = millis();
// Default response is TIMEOUT
response.setError(serverID, functionCode, TIMEOUT);
// Loop 60 seconds, if unlucky
while (millis() - lostPatience < 60000) {
{
LOCK_GUARD(lg, syncRespM);
// Look for the token
auto sR = syncResponse.find(token);
// Is it there?
if (sR != syncResponse.end()) {
// Yes. get the response, delete it from the map and return
response = sR->second;
syncResponse.erase(sR);
break;
}
}
// Give the watchdog time to act
delay(10);
}
return response;
}

View file

@ -1,119 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_CLIENT_H
#define _MODBUS_CLIENT_H
#include <functional>
#include <map>
#include "options.h"
#include "ModbusMessage.h"
#if HAS_FREERTOS
extern "C" {
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
}
#elif IS_LINUX
#include <pthread.h>
#endif
#if USE_MUTEX
#include <mutex> // NOLINT
using std::mutex;
using std::lock_guard;
#endif
typedef std::function<void(ModbusMessage msg, uint32_t token)> MBOnData;
typedef std::function<void(Modbus::Error errorCode, uint32_t token)> MBOnError;
typedef std::function<void(ModbusMessage msg, uint32_t token)> MBOnResponse;
class ModbusClient {
public:
bool onDataHandler(MBOnData handler); // Accept onData handler
bool onErrorHandler(MBOnError handler); // Accept onError handler
bool onResponseHandler(MBOnResponse handler); // Accept onResponse handler
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); }
// Template function to generate syncRequest functions as long as there is a
// matching ModbusMessage::setMessage() call
template <typename... Args>
ModbusMessage syncRequest(uint32_t token, Args&&... args) {
Error rc = SUCCESS;
// Create request, if valid
ModbusMessage m;
rc = m.setMessage(std::forward<Args>(args) ...);
// Add it to the queue and wait for a response, if valid
if (rc == SUCCESS) {
return syncRequestM(m, token);
}
// Else return the error as a message
return buildErrorMsg(rc, std::forward<Args>(args) ...);
}
// Template function to create an error response message from a variadic pattern
template <typename... Args>
ModbusMessage buildErrorMsg(Error e, uint8_t serverID, uint8_t functionCode, Args&&... args) {
ModbusMessage m;
m.setError(serverID, functionCode, e);
return m;
}
// Template function to generate addRequest functions as long as there is a
// matching ModbusMessage::setMessage() call
template <typename... Args>
Error addRequest(uint32_t token, Args&&... args) {
Error rc = SUCCESS; // Return value
// Create request, if valid
ModbusMessage m;
rc = m.setMessage(std::forward<Args>(args) ...);
// Add it to the queue, if valid
if (rc == SUCCESS) {
return addRequestM(m, token);
}
// Else return the error
return rc;
}
protected:
ModbusClient(); // Default constructor
virtual void isInstance() = 0; // Make class abstract
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;
// Virtual syncRequest variant following the same pattern
virtual ModbusMessage syncRequestM(ModbusMessage msg, uint32_t token) = 0;
// Prevent copy construction or assignment
ModbusClient(ModbusClient& other) = delete;
ModbusClient& operator=(ModbusClient& other) = delete;
uint32_t messageCount; // Number of requests generated. Used for transactionID in TCPhead
uint32_t errorCount; // Number of errors received
#if HAS_FREERTOS
TaskHandle_t worker; // Interface instance worker task
#elif IS_LINUX
pthread_t worker;
#endif
MBOnData onData; // Data response handler
MBOnError onError; // Error response handler
MBOnResponse onResponse; // Uniform response handler
static uint16_t instanceCounter; // Number of ModbusClients created
std::map<uint32_t, ModbusMessage> syncResponse; // Map to hold response messages on synchronous requests
#if USE_MUTEX
std::mutex syncRespM; // Mutex protecting syncResponse map against race conditions
std::mutex countAccessM; // Mutex protecting access to the message and error counts
#endif
// Let any ModbusBridge class use protected members
template<typename SERVERCLASS> friend class ModbusBridge;
};
#endif

View file

@ -1,354 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#include "ModbusClientRTU.h"
#if HAS_FREERTOS
#undef LOCAL_LOG_LEVEL
// #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE
#include "Logging.h"
// Constructor takes an optional DE/RE pin and queue size
ModbusClientRTU::ModbusClientRTU(int8_t rtsPin, uint16_t queueLimit) :
ModbusClient(),
MR_serial(nullptr),
MR_lastMicros(micros()),
MR_interval(2000),
MR_rtsPin(rtsPin),
MR_qLimit(queueLimit),
MR_timeoutValue(DEFAULTTIMEOUT),
MR_useASCII(false),
MR_skipLeadingZeroByte(false) {
if (MR_rtsPin >= 0) {
pinMode(MR_rtsPin, OUTPUT);
MTRSrts = [this](bool level) {
digitalWrite(MR_rtsPin, level);
};
MTRSrts(LOW);
} else {
MTRSrts = RTUutils::RTSauto;
}
}
// Alternative constructor takes an RTS callback function
ModbusClientRTU::ModbusClientRTU(RTScallback rts, uint16_t queueLimit) :
ModbusClient(),
MR_serial(nullptr),
MR_lastMicros(micros()),
MR_interval(2000),
MTRSrts(rts),
MR_qLimit(queueLimit),
MR_timeoutValue(DEFAULTTIMEOUT),
MR_useASCII(false),
MR_skipLeadingZeroByte(false) {
MR_rtsPin = -1;
MTRSrts(LOW);
}
// Destructor: clean up queue, task etc.
ModbusClientRTU::~ModbusClientRTU() {
// Kill worker task and clean up request queue
end();
}
// begin: start worker task - general version
void ModbusClientRTU::begin(Stream& serial, uint32_t baudRate, int coreID, uint32_t userInterval) {
MR_serial = &serial;
doBegin(baudRate, coreID, userInterval);
}
// begin: start worker task - HardwareSerial version
void ModbusClientRTU::begin(HardwareSerial& serial, int coreID, uint32_t userInterval) {
MR_serial = &serial;
uint32_t baudRate = serial.baudRate();
serial.setRxFIFOFull(1);
doBegin(baudRate, coreID, userInterval);
}
void ModbusClientRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterval) {
// Task already running? End it in case
end();
// Pull down RTS toggle, if necessary
MTRSrts(LOW);
// Set minimum interval time
MR_interval = RTUutils::calculateInterval(baudRate);
// If user defined interval is longer, use that
if (MR_interval < userInterval) {
MR_interval = userInterval;
}
// Create unique task name
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);
LOG_D("Client task %d started. Interval=%d\n", (uint32_t)worker, MR_interval);
}
// end: stop worker task
void ModbusClientRTU::end() {
if (worker) {
// Clean up queue
{
// Safely lock access
LOCK_GUARD(lockGuard, qLock);
// Get all queue entries one by one
while (!requests.empty()) {
// Remove front entry
requests.pop();
}
}
// Kill task
vTaskDelete(worker);
LOG_D("Client task %d killed.\n", (uint32_t)worker);
worker = nullptr;
}
}
// setTimeOut: set/change the default interface timeout
void ModbusClientRTU::setTimeout(uint32_t TOV) {
MR_timeoutValue = TOV;
LOG_D("Timeout set to %d\n", TOV);
}
// Toggle protocol to ModbusASCII
void ModbusClientRTU::useModbusASCII(unsigned long timeout) {
MR_useASCII = true;
MR_timeoutValue = timeout; // Switch timeout to ASCII's value
LOG_D("Protocol mode: ASCII\n");
}
// Toggle protocol to ModbusRTU
void ModbusClientRTU::useModbusRTU() {
MR_useASCII = false;
LOG_D("Protocol mode: RTU\n");
}
// Inquire protocol mode
bool ModbusClientRTU::isModbusASCII() {
return MR_useASCII;
}
// Toggle skipping of leading 0x00 byte
void ModbusClientRTU::skipLeading0x00(bool onOff) {
MR_skipLeadingZeroByte = onOff;
LOG_D("Skip leading 0x00 mode = %s\n", onOff ? "ON" : "OFF");
}
// Return number of unprocessed requests in queue
uint32_t ModbusClientRTU::pendingRequests() {
return requests.size();
}
// Remove all pending request from queue
void ModbusClientRTU::clearQueue()
{
std::queue<RequestEntry> empty;
LOCK_GUARD(lockGuard, qLock);
std::swap(requests, empty);
}
// Base addRequest taking a preformatted data buffer and length as parameters
Error ModbusClientRTU::addRequestM(ModbusMessage msg, uint32_t token) {
Error rc = SUCCESS; // Return value
LOG_D("request for %02X/%02X\n", msg.getServerID(), msg.getFunctionCode());
// Add it to the queue, if valid
if (msg) {
// Queue add successful?
if (!addToQueue(token, msg)) {
// No. Return error after deleting the allocated request.
rc = REQUEST_QUEUE_FULL;
}
}
LOG_D("RC=%02X\n", rc);
return rc;
}
// Base syncRequest follows the same pattern
ModbusMessage ModbusClientRTU::syncRequestM(ModbusMessage msg, uint32_t token) {
ModbusMessage response;
if (msg) {
// Queue add successful?
if (!addToQueue(token, msg, true)) {
// No. Return error after deleting the allocated request.
response.setError(msg.getServerID(), msg.getFunctionCode(), REQUEST_QUEUE_FULL);
} else {
// Request is queued - wait for the result.
response = waitSync(msg.getServerID(), msg.getFunctionCode(), token);
}
} else {
response.setError(msg.getServerID(), msg.getFunctionCode(), EMPTY_MESSAGE);
}
return response;
}
// addBroadcastMessage: create a fire-and-forget message to all servers on the RTU bus
Error ModbusClientRTU::addBroadcastMessage(const uint8_t *data, uint8_t len) {
Error rc = SUCCESS; // Return value
LOG_D("Broadcast request of length %d\n", len);
// We do only accept requests with data, 0 byte, data and CRC must fit into 256 bytes.
if (len && len < 254) {
// Create a "broadcast token"
uint32_t token = (millis() & 0xFFFFFF) | 0xBC000000;
ModbusMessage msg;
// Server ID is 0x00 for broadcast
msg.add((uint8_t)0x00);
// Append data
msg.add(data, len);
// Queue add successful?
if (!addToQueue(token, msg)) {
// No. Return error after deleting the allocated request.
rc = REQUEST_QUEUE_FULL;
}
} else {
rc = BROADCAST_ERROR;
}
LOG_D("RC=%02X\n", rc);
return rc;
}
// addToQueue: send freshly created request to queue
bool ModbusClientRTU::addToQueue(uint32_t token, ModbusMessage request, bool syncReq) {
bool rc = false;
// Did we get one?
if (request) {
RequestEntry re(token, request, syncReq);
if (requests.size()<MR_qLimit) {
// Yes. Safely lock queue and push request to queue
rc = true;
LOCK_GUARD(lockGuard, qLock);
requests.push(re);
}
{
LOCK_GUARD(cntLock, countAccessM);
messageCount++;
}
}
LOG_D("RC=%02X\n", rc);
return rc;
}
// handleConnection: worker task
// This was created in begin() to handle the queue entries
void ModbusClientRTU::handleConnection(ModbusClientRTU *instance) {
// initially clean the serial buffer
while (instance->MR_serial->available()) instance->MR_serial->read();
delay(100);
// Loop forever - or until task is killed
while (1) {
// Do we have a reuest in queue?
if (!instance->requests.empty()) {
// Yes. pull it.
RequestEntry request = instance->requests.front();
LOG_D("Pulled request from queue\n");
// Send it via Serial
RTUutils::send(*(instance->MR_serial), instance->MR_lastMicros, instance->MR_interval, instance->MTRSrts, request.msg, instance->MR_useASCII);
LOG_D("Request sent.\n");
// HEXDUMP_V("Data", request.msg.data(), request.msg.size());
// For a broadcast, we will not wait for a response
if (request.msg.getServerID() != 0 || ((request.token & 0xFF000000) != 0xBC000000)) {
// This is a regular request, Get the response - if any
ModbusMessage response = RTUutils::receive(
'C',
*(instance->MR_serial),
instance->MR_timeoutValue,
instance->MR_lastMicros,
instance->MR_interval,
instance->MR_useASCII,
instance->MR_skipLeadingZeroByte);
LOG_D("%s response (%d bytes) received.\n", response.size()>1 ? "Data" : "Error", response.size());
HEXDUMP_V("Data", response.data(), response.size());
// No error in receive()?
if (response.size() > 1) {
// No. Check message contents
// Does the serverID match the requested?
if (request.msg.getServerID() != response.getServerID()) {
// No. Return error response
response.setError(request.msg.getServerID(), request.msg.getFunctionCode(), SERVER_ID_MISMATCH);
// ServerID ok, but does the FC match as well?
} else if (request.msg.getFunctionCode() != (response.getFunctionCode() & 0x7F)) {
// No. Return error response
response.setError(request.msg.getServerID(), request.msg.getFunctionCode(), FC_MISMATCH);
}
} else {
// No, we got an error code from receive()
// Return it as error response
response.setError(request.msg.getServerID(), request.msg.getFunctionCode(), static_cast<Error>(response[0]));
}
LOG_D("Response generated.\n");
HEXDUMP_V("Response packet", response.data(), response.size());
// If we got an error, count it
if (response.getError() != SUCCESS) {
instance->errorCount++;
}
// Was it a synchronous request?
if (request.isSyncRequest) {
// Yes. Put it into the response map
{
LOCK_GUARD(sL, instance->syncRespM);
instance->syncResponse[request.token] = response;
}
// No, an async request. Do we have an onResponse handler?
} else if (instance->onResponse) {
// Yes. Call it
instance->onResponse(response, request.token);
} else {
// No, but we may have onData or onError handlers
// Did we get a normal response?
if (response.getError()==SUCCESS) {
// Yes. Do we have an onData handler registered?
if (instance->onData) {
// Yes. call it
instance->onData(response, request.token);
}
} else {
// No, something went wrong. All we have is an error
// Do we have an onError handler?
if (instance->onError) {
// Yes. Forward the error code to it
instance->onError(response.getError(), request.token);
}
}
}
}
// Clean-up time.
{
// Safely lock the queue
LOCK_GUARD(lockGuard, instance->qLock);
// Remove the front queue entry
instance->requests.pop();
}
} else {
delay(1);
}
}
}
#endif // HAS_FREERTOS

View file

@ -1,111 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_CLIENT_RTU_H
#define _MODBUS_CLIENT_RTU_H
#include "options.h"
#if HAS_FREERTOS
#include "ModbusClient.h"
#include "Stream.h"
#include "RTUutils.h"
#include <queue>
#include <vector>
using std::queue;
#define DEFAULTTIMEOUT 2000
class ModbusClientRTU : public ModbusClient {
public:
// Constructor takes an optional DE/RE pin and queue limit
explicit ModbusClientRTU(int8_t rtsPin = -1, uint16_t queueLimit = 100);
// Alternative Constructor takes an RTS line toggle callback
explicit ModbusClientRTU(RTScallback rts, uint16_t queueLimit = 100);
// Destructor: clean up queue, task etc.
~ModbusClientRTU();
// begin: start worker task
void begin(Stream& serial, uint32_t baudrate, int coreID = -1, uint32_t userInterval = 0);
// Special variant for HardwareSerial
void begin(HardwareSerial& serial, int coreID = -1, uint32_t userInterval = 0);
// end: stop the worker
void end();
// Set default timeout value for interface
void setTimeout(uint32_t TOV);
// Toggle protocol to ModbusASCII
void useModbusASCII(unsigned long timeout = 1000);
// Toggle protocol to ModbusRTU
void useModbusRTU();
// Inquire protocol mode
bool isModbusASCII();
// Toggle skipping of leading 0x00 byte
void skipLeading0x00(bool onOff = true);
// Return number of unprocessed requests in queue
uint32_t pendingRequests();
// Remove all pending request from queue
void clearQueue();
// addBroadcastMessage: create a fire-and-forget message to all servers on the RTU bus
Error addBroadcastMessage(const uint8_t *data, uint8_t len);
protected:
struct RequestEntry {
uint32_t token;
ModbusMessage msg;
bool isSyncRequest;
RequestEntry(uint32_t t, 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);
// addToQueue: send freshly created request to queue
bool addToQueue(uint32_t token, ModbusMessage msg, bool syncReq = false);
// handleConnection: worker task method
static void handleConnection(ModbusClientRTU *instance);
// receive: get response via Serial
ModbusMessage receive(const ModbusMessage request);
// 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
#endif
Stream *MR_serial; // Ptr to the serial interface used
unsigned long MR_lastMicros; // Microseconds since last bus activity
uint32_t MR_interval; // Modbus RTU bus quiet time
int8_t MR_rtsPin; // GPIO pin to toggle RS485 DE/RE line. -1 if none.
RTScallback MTRSrts; // RTS line callback function
uint16_t MR_qLimit; // Maximum number of requests to hold in the queue
uint32_t MR_timeoutValue; // Interface default timeout
bool MR_useASCII; // true=ModbusASCII, false=ModbusRTU
bool MR_skipLeadingZeroByte; // true=skip the first byte if it is 0x00, false=accept all bytes
};
#endif // HAS_FREERTOS
#endif // INCLUDE GUARD

View file

@ -1,16 +0,0 @@
// =================================================================================================
// eModbus: Copyright 2020 by Michael Harwerth, Bert Melis and the contributors to eModbus
// MIT license - see license.md for details
// =================================================================================================
#ifndef _MODBUS_SERVER_WIFI_H
#define _MODBUS_SERVER_WIFI_H
#include "options.h"
#include <WiFi.h>
#undef SERVER_END
#define SERVER_END server.end();
#include "ModbusServerTCPtemp.h"
using ModbusServerWiFi = ModbusServerTCP<WiFiServer, WiFiClient>;
#endif