diff --git a/Software/src/lib/eModbus-eModbus/CoilData.cpp b/Software/src/lib/eModbus-eModbus/CoilData.cpp index 8348080d..42b7839a 100644 --- a/Software/src/lib/eModbus-eModbus/CoilData.cpp +++ b/Software/src/lib/eModbus-eModbus/CoilData.cpp @@ -47,6 +47,8 @@ CoilData::~CoilData() { // Assignment operator CoilData& CoilData::operator=(const CoilData& m) { + // Avoid self-assignment + if (this == &m) return *this; // Remove old data if (CDbuffer) { delete CDbuffer; diff --git a/Software/src/lib/eModbus-eModbus/ModbusMessage.cpp b/Software/src/lib/eModbus-eModbus/ModbusMessage.cpp index b1c621ec..7e63bdb9 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusMessage.cpp +++ b/Software/src/lib/eModbus-eModbus/ModbusMessage.cpp @@ -6,6 +6,7 @@ #undef LOCAL_LOG_LEVEL // #define LOCAL_LOG_LEVEL LOG_LEVEL_ERROR #include "Logging.h" +#include // Default Constructor - takes optional size of MM_data to allocate memory ModbusMessage::ModbusMessage(uint16_t dataLen) { @@ -146,7 +147,7 @@ void ModbusMessage::setServerID(uint8_t serverID) { } void ModbusMessage::setFunctionCode(uint8_t FC) { - if (MM_data.size() < 2) { + if (MM_data.size() < 2) { MM_data.resize(2); // Resize to at least 2 to ensure indices 0 and 1 are valid MM_data[0] = 0; // Optional: Invalid server ID as a placeholder } @@ -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 v) { - for (auto& b: v) { - MM_data.push_back(b); - } - return MM_data.size(); + return add(v.data(), v.size()); } // add() variants for float and double values diff --git a/Software/src/lib/eModbus-eModbus/ModbusServer.cpp b/Software/src/lib/eModbus-eModbus/ModbusServer.cpp index b814bd9b..90cdcda4 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusServer.cpp +++ b/Software/src/lib/eModbus-eModbus/ModbusServer.cpp @@ -30,7 +30,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) { svmap = workerMap.find(ANY_SERVER); if (svmap != workerMap.end()) { serverFound = true; - serverID = ANY_SERVER; } } // Did we find a serverID? @@ -49,7 +48,6 @@ MBSworker ModbusServer::getWorker(uint8_t serverID, uint8_t functionCode) { if (fcmap != svmap->second.end()) { // Yes. Return the function pointer for it. functionCodeFound = true; - functionCode = ANY_FUNCTION_CODE; } } if (functionCodeFound) { @@ -104,6 +102,11 @@ bool ModbusServer::isServerFor(uint8_t serverID) { // Is there one? if (svmap != workerMap.end()) { return true; + } else { + svmap = workerMap.find(ANY_SERVER); + if (svmap != workerMap.end()) { + return true; + } } return false; } diff --git a/Software/src/lib/eModbus-eModbus/ModbusServer.h b/Software/src/lib/eModbus-eModbus/ModbusServer.h index 933e27e9..70c88a6f 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusServer.h +++ b/Software/src/lib/eModbus-eModbus/ModbusServer.h @@ -68,15 +68,12 @@ protected: ModbusServer(); // Destructor - ~ModbusServer(); + virtual ~ModbusServer(); // Prevent copy construction or assignment ModbusServer(ModbusServer& other) = delete; ModbusServer& operator=(ModbusServer& other) = delete; - // Virtual function to prevent this class being instantiated - virtual void isInstance() = 0; - std::map> workerMap; // map on serverID->functionCode->worker function uint32_t messageCount; // Number of Requests processed uint32_t errorCount; // Number of errors responded diff --git a/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h b/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h index b1f9ceba..c520c9f7 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h +++ b/Software/src/lib/eModbus-eModbus/ModbusServerEthernet.h @@ -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; +using ModbusServerEthernet = ModbusServerTCP; #endif #endif diff --git a/Software/src/lib/eModbus-eModbus/ModbusServerRTU.cpp b/Software/src/lib/eModbus-eModbus/ModbusServerRTU.cpp index ae66ce55..f7e19eae 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusServerRTU.cpp +++ b/Software/src/lib/eModbus-eModbus/ModbusServerRTU.cpp @@ -94,7 +94,7 @@ void ModbusServerRTU::doBegin(uint32_t baudRate, int coreID, uint32_t userInterv snprintf(taskName, 18, "MBsrv%02XRTU", instanceCounter); // Start task to handle the client - xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : NULL); + xTaskCreatePinnedToCore((TaskFunction_t)&serve, taskName, SERVER_TASK_STACK, this, 8, &serverTask, coreID >= 0 ? coreID : tskNO_AFFINITY); LOG_D("Server task %d started. Interval=%d\n", (uint32_t)serverTask, MSRinterval); } @@ -126,6 +126,12 @@ bool ModbusServerRTU::isModbusASCII() { return MSRuseASCII; } +// set timeout +void ModbusServerRTU::setModbusTimeout(unsigned long timeout) +{ + serverTimeout = timeout; +} + // Toggle skipping of leading 0x00 byte void ModbusServerRTU::skipLeading0x00(bool onOff) { MSRskipLeadingZeroByte = onOff; @@ -231,8 +237,8 @@ void ModbusServerRTU::serve(ModbusServerRTU *myServer) { response = m; } } else { - // No callback. Is at least the serverID valid and no broadcast? - if (myServer->isServerFor(request[0]) && request[0] != 0x00) { + // No callback. Is at least the serverID valid? + if (myServer->isServerFor(request[0])) { // Yes. Send back a ILLEGAL_FUNCTION error response.setError(request.getServerID(), request.getFunctionCode(), ILLEGAL_FUNCTION); } @@ -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 diff --git a/Software/src/lib/eModbus-eModbus/ModbusServerRTU.h b/Software/src/lib/eModbus-eModbus/ModbusServerRTU.h index fd5b8ab9..76c67b74 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusServerRTU.h +++ b/Software/src/lib/eModbus-eModbus/ModbusServerRTU.h @@ -47,6 +47,9 @@ public: // Inquire protocol mode bool isModbusASCII(); + // set timeout + void setModbusTimeout(unsigned long timeout); + // Toggle skipping of leading 0x00 byte void skipLeading0x00(bool onOff = true); @@ -61,8 +64,6 @@ protected: ModbusServerRTU(ModbusServerRTU& m) = delete; ModbusServerRTU& operator=(ModbusServerRTU& m) = delete; - inline void isInstance() { } // Make class instantiable - // internal common begin function void doBegin(uint32_t baudRate, int coreID, uint32_t userInterval); diff --git a/Software/src/lib/eModbus-eModbus/ModbusTypeDefs.h b/Software/src/lib/eModbus-eModbus/ModbusTypeDefs.h index 5aad1d5d..cb281ad2 100644 --- a/Software/src/lib/eModbus-eModbus/ModbusTypeDefs.h +++ b/Software/src/lib/eModbus-eModbus/ModbusTypeDefs.h @@ -109,8 +109,8 @@ const uint8_t swapTables[8][8] = { enum FCType : uint8_t { FC01_TYPE, // Two uint16_t parameters (FC 0x01, 0x02, 0x03, 0x04, 0x05, 0x06) FC07_TYPE, // no additional parameter (FCs 0x07, 0x0b, 0x0c, 0x11) - FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of bytes (FC 0x0f) - FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of words (FC 0x10) + FC0F_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint8_t* pointer to array of bytes (FC 0x0f) + FC10_TYPE, // two uint16_t parameters, a uint8_t length byte and a uint16_t* pointer to array of words (FC 0x10) FC16_TYPE, // three uint16_t parameters (FC 0x16) FC18_TYPE, // one uint16_t parameter (FC 0x18) FCGENERIC, // for FCs not yet explicitly coded (or too complex) diff --git a/Software/src/lib/eModbus-eModbus/README.md b/Software/src/lib/eModbus-eModbus/README.md new file mode 100644 index 00000000..cbda1fcc --- /dev/null +++ b/Software/src/lib/eModbus-eModbus/README.md @@ -0,0 +1,26 @@ + +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! diff --git a/Software/src/lib/eModbus-eModbus/RTUutils.cpp b/Software/src/lib/eModbus-eModbus/RTUutils.cpp index d360c95a..c92d0ffd 100644 --- a/Software/src/lib/eModbus-eModbus/RTUutils.cpp +++ b/Software/src/lib/eModbus-eModbus/RTUutils.cpp @@ -3,13 +3,13 @@ // MIT license - see license.md for details // ================================================================================================= #include "options.h" +#if HAS_FREERTOS #include "ModbusMessage.h" #include "RTUutils.h" #undef LOCAL_LOG_LEVEL // #define LOCAL_LOG_LEVEL LOG_LEVEL_VERBOSE #include "Logging.h" -#if HAS_FREERTOS // calcCRC: calculate Modbus CRC16 on a given array of bytes uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) { // CRC16 pre-calculated tables @@ -57,10 +57,9 @@ uint16_t RTUutils::calcCRC(const uint8_t *data, uint16_t len) { uint8_t crcHi = 0xFF; uint8_t crcLo = 0xFF; - uint8_t index; while (len--) { - index = crcLo ^ *data++; + uint8_t index = crcLo ^ *data++; crcLo = crcHi ^ crcHiTable[index]; crcHi = crcLoTable[index]; } @@ -464,4 +463,5 @@ const char RTUutils::ASCIIread[] = { const char RTUutils::ASCIIwrite[] = { 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46 }; + #endif diff --git a/Software/src/lib/eModbus-eModbus/RTUutils.h b/Software/src/lib/eModbus-eModbus/RTUutils.h index f50d8f44..e3f41be3 100644 --- a/Software/src/lib/eModbus-eModbus/RTUutils.h +++ b/Software/src/lib/eModbus-eModbus/RTUutils.h @@ -5,9 +5,6 @@ #ifndef _RTU_UTILS_H #define _RTU_UTILS_H #include -#if NEED_UART_PATCH -#include -#endif #include #include "Stream.h" #include "ModbusTypeDefs.h" @@ -52,11 +49,13 @@ public: // RTSauto: dummy callback for auto half duplex RS485 boards inline static void RTSauto(bool level) { return; } // NOLINT +#if HAS_FREERTOS // Necessary preparations for a HardwareSerial static void prepareHardwareSerial(HardwareSerial& s, uint16_t bufferSize = 260) { s.setRxBufferSize(bufferSize); s.setTxBufferSize(bufferSize); } +#endif protected: // Printable characters for ASCII protocol: 012345678ABCDEF diff --git a/Software/src/lib/eModbus-eModbus/library.json b/Software/src/lib/eModbus-eModbus/library.json new file mode 100644 index 00000000..dbb03ccb --- /dev/null +++ b/Software/src/lib/eModbus-eModbus/library.json @@ -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+" + } +} diff --git a/Software/src/lib/eModbus-eModbus/library.properties b/Software/src/lib/eModbus-eModbus/library.properties new file mode 100644 index 00000000..904bda3a --- /dev/null +++ b/Software/src/lib/eModbus-eModbus/library.properties @@ -0,0 +1,9 @@ +name=eModbus +version=1.7.4 +author=bertmelis,Miq1 +maintainer=Miq1 +sentence=eModbus provides Modbus RTU, ASCII and TCP functions for ESP32. +paragraph=This library is non-blocking for the program using it. Modbus requests and responses will be returned to user-supplied callback functions. All Modbus function codes are supported implicitly, the codes specified by the Modbus specs are parameter-checked. +category=Communication +url=https://github.com/eModbus/eModbus +architectures=esp32,FreeRTOS diff --git a/Software/src/lib/eModbus-eModbus/license.md b/Software/src/lib/eModbus-eModbus/license.md new file mode 100644 index 00000000..69d50d61 --- /dev/null +++ b/Software/src/lib/eModbus-eModbus/license.md @@ -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. diff --git a/Software/src/lib/eModbus-eModbus/options.h b/Software/src/lib/eModbus-eModbus/options.h index c65bc1c2..9de44e97 100644 --- a/Software/src/lib/eModbus-eModbus/options.h +++ b/Software/src/lib/eModbus-eModbus/options.h @@ -12,7 +12,6 @@ #define HAS_FREERTOS 1 #define HAS_ETHERNET 1 #define IS_LINUX 0 -#define NEED_UART_PATCH 1 const unsigned int SERVER_TASK_STACK = 4096; const unsigned int CLIENT_TASK_STACK = 4096; @@ -23,7 +22,6 @@ const unsigned int CLIENT_TASK_STACK = 4096; #define HAS_FREERTOS 0 #define HAS_ETHERNET 0 #define IS_LINUX 0 -#define NEED_UART_PATCH 0 /* === LINUX DEFINITIONS AND MACROS === */ #elif defined(__linux__) @@ -31,7 +29,6 @@ const unsigned int CLIENT_TASK_STACK = 4096; #define HAS_FREERTOS 0 #define HAS_ETHERNET 0 #define IS_LINUX 1 -#define NEED_UART_PATCH 0 #include // for printf() #include // for memcpy(), strlen() etc. #include // for uint32_t etc.