Update eModbus to v1.7.4

This commit is contained in:
Daniel Öster 2025-07-20 00:57:08 +03:00
parent a1557b955a
commit 024dae7c64
15 changed files with 154 additions and 35 deletions

View file

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

View file

@ -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) {
@ -155,10 +156,10 @@ void ModbusMessage::setFunctionCode(uint8_t FC) {
// 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();
}
@ -179,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
@ -223,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
@ -304,10 +305,7 @@ double ModbusMessage::swapDouble(double& f, int swapRule) {
// add() variant for a vector of uint8_t
uint16_t ModbusMessage::add(vector<uint8_t> v) {
for (auto& b: v) {
MM_data.push_back(b);
}
return MM_data.size();
return add(v.data(), v.size());
}
// add() variants for float and double values

View file

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

View file

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

View file

@ -12,8 +12,15 @@
#undef SERVER_END
#define SERVER_END // NIL for Ethernet
// Create own non-virtual EthernetServer class
class EthernetServerEM : public EthernetServer {
public:
explicit EthernetServerEM(uint16_t port) : EthernetServer(port) { }
void begin(uint16_t port = 0) override { }
};
#include "ModbusServerTCPtemp.h"
using ModbusServerEthernet = ModbusServerTCP<EthernetServer, EthernetClient>;
using ModbusServerEthernet = ModbusServerTCP<EthernetServerEM, EthernetClient>;
#endif
#endif

View file

@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv
snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter);
// Start task to handle the client
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : NULL);
xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY);
LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval);
}
@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() {
return MSRuseASCII;
}
// set timeout
void ModbusServerRTU::setModbusTimeout(unsigned long timeout)
{
serverTimeout = timeout;
}
// Toggle skipping of leading 0x00 byte
void ModbusServerRTU::skipLeading0x00(bool onOff) {
MSRskipLeadingZeroByte = onOff;
@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
response = m;
}
} else {
// No callback. Is at least the serverID valid and no broadcast?
if (myServer->isServerFor(request[0]) && request[0] != 0x00) {
// No callback. Is at least the serverID valid?
if (myServer->isServerFor(request[0])) {
// Yes. Send back a ILLEGAL_FUNCTION error
response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION);
}
@ -256,9 +262,7 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) {
if (request[0] != TIMEOUT) {
// Any other error could be important for debugging, so print it
ModbusError me((Error)request[0]);
#ifdef DEBUG_VIA_USB
LOG_E("RTU receive: %02X - %s\n", (int)me, (const char *)me);
#endif //DEBUG_VIA_USB
}
}
// Give scheduler room to breathe

View file

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

View file

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

View file

@ -0,0 +1,26 @@
<img src=https://github.com/eModbus/eModbus/blob/master/eModbusLogo.png width="33%" alt="eModbus">
**Read the docs at http://emodbus.github.io!**
![eModbus](https://github.com/eModbus/eModbus/workflows/Building/badge.svg)
This is a library to provide Modbus client (formerly known as master), server (formerly slave) and bridge/gateway functionalities for Modbus RTU, ASCII and TCP protocols.
For Modbus protocol specifications, please refer to the [Modbus.org site](https://www.modbus.org/specs.php)!
Modbus communication is done in separate tasks, so Modbus requests and responses are non-blocking. Callbacks are provided to prepare or receive the responses asynchronously.
Key features:
- for use in the Arduino framework
- designed for ESP32, various interfaces supported; async versions run also on ESP8266
- non blocking / asynchronous API
- server, client and bridge modes
- TCP (Ethernet, WiFi and Async), ASCII and RTU interfaces
- all common and user-defined Modbus standard function codes
This has been developed by enthusiasts. While we do our utmost best to make robust software, do not expect any bullet-proof, industry deployable, guaranteed software. [**See the license**](https://github.com/eModbus/eModbus/blob/master/license.md) to learn about liabilities etc.
We do welcome any ideas, suggestions, bug reports or questions. Please use the "[Issues](https://github.com/eModbus/eModbus/issues)" tab to report bugs and request new features and visit the "[Discussions](https://github.com/eModbus/eModbus/discussions)" tab for all else.
Have fun!

View file

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

View file

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

View file

@ -0,0 +1,69 @@
{
"name": "eModbus",
"version": "1.7.4",
"keywords": "Arduino, ESP32, Modbus, RTU, ASCII, ModbusASCII, ModbusRTU, ModbusTCP",
"description": "ModbusRTU, ModbusASCII and ModbusTCP functions for ESP32",
"homepage": "https://emodbus.github.io",
"license": "MIT",
"authors": [
{
"name": "Bert Melis",
"url": "https://github.com/bertmelis",
"maintainer": true
},
{
"name": "Michael Harwerth",
"url": "https://github.com/Miq1",
"email": "miq1@gmx.de",
"maintainer": true
}
],
"repository": {
"type": "git",
"url": "https://github.com/eModbus/eModbus",
"branch": "master"
},
"dependencies": [
{
"owner": "ESP32Async",
"name": "AsyncTCP",
"version": "^3.3.8",
"platforms": ["espressif32"]
},
{
"owner": "ESP32Async",
"name": "ESPAsyncTCP",
"version": "^2.0.0",
"platforms": ["espressif8266"]
},
{
"name": "Ethernet",
"version": "https://github.com/arduino-libraries/Ethernet.git",
"platforms": ["espressif32"]
}
],
"export": {
"include":
[
"src/*.cpp",
"src/*.h",
"examples/*",
"Test/*",
".gitignore",
"README.md",
"license.md",
"keywords.txt",
"library.properties",
"library.json"
]
},
"frameworks": "arduino",
"platforms": [
"espressif32",
"espressif8266"
],
"build":
{
"lib_ldf_Mode": "deep+"
}
}

View file

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

View file

@ -0,0 +1,7 @@
#### Copyright 2020 Michael Harwerth, Bert Melis and the contributors to eModbus (MIT license)
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#### The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

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