From 57d2e9c016c1948648710589ec9a0fc95ea83363 Mon Sep 17 00:00:00 2001 From: Miq1 Date: Fri, 14 Aug 2020 16:48:42 +0200 Subject: [PATCH] Token implementation /3 --- src/ModbusMessage.cpp | 24 +++++++++---- src/ModbusMessage.h | 12 ++++--- src/esp32ModbusRTU.cpp | 74 +++++++++++++++++++++++++++++---------- src/esp32ModbusRTU.h | 16 +++++---- src/esp32ModbusTypeDefs.h | 4 +++ 5 files changed, 93 insertions(+), 37 deletions(-) diff --git a/src/ModbusMessage.cpp b/src/ModbusMessage.cpp index 085aeba..8bc2f77 100644 --- a/src/ModbusMessage.cpp +++ b/src/ModbusMessage.cpp @@ -103,7 +103,8 @@ uint16_t make_word(uint8_t high, uint8_t low) { ModbusMessage::ModbusMessage(uint8_t length) : _buffer(nullptr), _length(length), - _index(0) { + _index(0), + _token(0) { if (length < 5) _length = 5; // minimum for Modbus Exception codes _buffer = new uint8_t[_length]; for (uint8_t i = 0; i < _length; ++i) { @@ -123,6 +124,10 @@ uint8_t ModbusMessage::getSize() { return _index; } +uint32_t ModbusMessage::getToken() { + return _token; +} + void ModbusMessage::add(uint8_t value) { if (_index < _length) _buffer[_index++] = value; } @@ -138,12 +143,13 @@ ModbusRequest::ModbusRequest(uint8_t length) : return _address; } -ModbusRequest02::ModbusRequest02(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils) : +ModbusRequest02::ModbusRequest02(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils, uint32_t token) : ModbusRequest(8) { _slaveAddress = slaveAddress; _functionCode = esp32Modbus::READ_DISCR_INPUT; _address = address; _byteCount = numberCoils / 8 + 1; + _token = token; add(_slaveAddress); add(_functionCode); add(high(_address)); @@ -159,12 +165,13 @@ size_t ModbusRequest02::responseLength() { return 5 + _byteCount; } -ModbusRequest03::ModbusRequest03(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters) : +ModbusRequest03::ModbusRequest03(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token) : ModbusRequest(12) { _slaveAddress = slaveAddress; _functionCode = esp32Modbus::READ_HOLD_REGISTER; _address = address; _byteCount = numberRegisters * 2; // register is 2 bytes wide + _token = token; add(_slaveAddress); add(_functionCode); add(high(_address)); @@ -180,12 +187,13 @@ size_t ModbusRequest03::responseLength() { return 5 + _byteCount; } -ModbusRequest04::ModbusRequest04(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters) : +ModbusRequest04::ModbusRequest04(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token) : ModbusRequest(8) { _slaveAddress = slaveAddress; _functionCode = esp32Modbus::READ_INPUT_REGISTER; _address = address; _byteCount = numberRegisters * 2; // register is 2 bytes wide + _token = token; add(_slaveAddress); add(_functionCode); add(high(_address)); @@ -202,12 +210,13 @@ size_t ModbusRequest04::responseLength() { return 5 + _byteCount; } -ModbusRequest06::ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t data) : +ModbusRequest06::ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t data, uint32_t token) : ModbusRequest(8) { _slaveAddress = slaveAddress; _functionCode = esp32Modbus::WRITE_HOLD_REGISTER; _address = address; _byteCount = 2; // 1 register is 2 bytes wide + _token = token; add(_slaveAddress); add(_functionCode); add(high(_address)); @@ -223,12 +232,13 @@ size_t ModbusRequest06::responseLength() { return 8; } -ModbusRequest16::ModbusRequest16(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data) : +ModbusRequest16::ModbusRequest16(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data, uint32_t token) : ModbusRequest(9 + (numberRegisters * 2)) { _slaveAddress = slaveAddress; _functionCode = esp32Modbus::WRITE_MULT_REGISTERS; _address = address; _byteCount = numberRegisters * 2; // register is 2 bytes wide + _token = token; add(_slaveAddress); add(_functionCode); add(high(_address)); @@ -251,7 +261,7 @@ size_t ModbusRequest16::responseLength() { ModbusResponse::ModbusResponse(uint8_t length, ModbusRequest* request) : ModbusMessage(length), _request(request), - _error(esp32Modbus::SUCCES) {} + _error(esp32Modbus::SUCCES) { _token = request->getToken(); } bool ModbusResponse::isComplete() { if (_buffer[1] > 0x80 && _index == 5) { // 5: slaveAddress(1), errorCode(1), CRC(2) + indexed diff --git a/src/ModbusMessage.h b/src/ModbusMessage.h index 4fdbaaa..511a8b3 100644 --- a/src/ModbusMessage.h +++ b/src/ModbusMessage.h @@ -37,6 +37,7 @@ class ModbusMessage { virtual ~ModbusMessage(); uint8_t* getMessage(); uint8_t getSize(); + uint32_t getToken(); void add(uint8_t value); protected: @@ -44,6 +45,7 @@ class ModbusMessage { uint8_t* _buffer; uint8_t _length; uint8_t _index; + uint32_t _token; }; class ModbusResponse; // forward declare for use in ModbusRequest @@ -64,35 +66,35 @@ class ModbusRequest : public ModbusMessage { // read discrete coils class ModbusRequest02 : public ModbusRequest { public: - explicit ModbusRequest02(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils); + explicit ModbusRequest02(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils, uint32_t token = 0); size_t responseLength(); }; // read holding registers class ModbusRequest03 : public ModbusRequest { public: - explicit ModbusRequest03(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters); + explicit ModbusRequest03(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token = 0); size_t responseLength(); }; // read input registers class ModbusRequest04 : public ModbusRequest { public: - explicit ModbusRequest04(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters); + explicit ModbusRequest04(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token = 0); size_t responseLength(); }; // write single holding registers class ModbusRequest06 : public ModbusRequest { public: - explicit ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t data); + explicit ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t data, uint32_t token = 0); size_t responseLength(); }; // write multiple holding registers class ModbusRequest16 : public ModbusRequest { public: - explicit ModbusRequest16(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data); + explicit ModbusRequest16(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data, uint32_t token = 0); size_t responseLength(); }; diff --git a/src/esp32ModbusRTU.cpp b/src/esp32ModbusRTU.cpp index 9500a0f..c26c2ae 100644 --- a/src/esp32ModbusRTU.cpp +++ b/src/esp32ModbusRTU.cpp @@ -31,11 +31,15 @@ using namespace esp32ModbusRTUInternals; // NOLINT esp32ModbusRTU::esp32ModbusRTU(HardwareSerial* serial, int8_t rtsPin) : TimeOutValue(TIMEOUT_MS), _serial(serial), - _lastMillis(0), + _lastMicros(0), _interval(0), _rtsPin(rtsPin), _task(nullptr), - _queue(nullptr) { + _queue(nullptr), + _onData(nullptr), + _onError(nullptr), + _onDataToken(nullptr), + _onErrorToken(nullptr) { _queue = xQueueCreate(QUEUE_SIZE, sizeof(ModbusRequest*)); } @@ -51,31 +55,35 @@ void esp32ModbusRTU::begin(int coreID /* = -1 */) { } xTaskCreatePinnedToCore((TaskFunction_t)&_handleConnection, "esp32ModbusRTU", 4096, this, 5, &_task, coreID >= 0 ? coreID : NULL); // silent interval is at least 3.5x character time - _interval = 40000 / _serial->baudRate(); // 4 * 1000 * 10 / baud - if (_interval == 0) _interval = 1; // minimum of 1msec interval + _interval = 35000000UL / _serial->baudRate(); // 3.5 * 10 bits * 1000 µs * 1000 ms / baud + + // The following is okay for sending at any baud rate, but problematic at receiving with baud rates above 35000, + // since the calculated interval will be below 1000µs! + // f.i. 115200bd ==> interval=304µs + if (_interval < 1000) _interval = 1000; // minimum of 1msec interval } -bool esp32ModbusRTU::readDiscreteInputs(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils) { - ModbusRequest* request = new ModbusRequest02(slaveAddress, address, numberCoils); +bool esp32ModbusRTU::readDiscreteInputs(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils, uint32_t token) { + ModbusRequest* request = new ModbusRequest02(slaveAddress, address, numberCoils, token); return _addToQueue(request); } -bool esp32ModbusRTU::readHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters) { - ModbusRequest* request = new ModbusRequest03(slaveAddress, address, numberRegisters); +bool esp32ModbusRTU::readHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token) { + ModbusRequest* request = new ModbusRequest03(slaveAddress, address, numberRegisters, token); return _addToQueue(request); } -bool esp32ModbusRTU::readInputRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters) { - ModbusRequest* request = new ModbusRequest04(slaveAddress, address, numberRegisters); +bool esp32ModbusRTU::readInputRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token) { + ModbusRequest* request = new ModbusRequest04(slaveAddress, address, numberRegisters, token); return _addToQueue(request); } -bool esp32ModbusRTU::writeSingleHoldingRegister(uint8_t slaveAddress, uint16_t address, uint16_t data) { - ModbusRequest* request = new ModbusRequest06(slaveAddress, address, data); +bool esp32ModbusRTU::writeSingleHoldingRegister(uint8_t slaveAddress, uint16_t address, uint16_t data, uint32_t token) { + ModbusRequest* request = new ModbusRequest06(slaveAddress, address, data, token); return _addToQueue(request); } -bool esp32ModbusRTU::writeMultHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data) { - ModbusRequest* request = new ModbusRequest16(slaveAddress, address, numberRegisters, data); +bool esp32ModbusRTU::writeMultHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data, uint32_t token) { + ModbusRequest* request = new ModbusRequest16(slaveAddress, address, numberRegisters, data, token); return _addToQueue(request); } @@ -87,6 +95,14 @@ void esp32ModbusRTU::onError(esp32Modbus::MBRTUOnError handler) { _onError = handler; } +void esp32ModbusRTU::onDataToken(esp32Modbus::MBRTUOnDataToken handler) { + _onDataToken = handler; +} + +void esp32ModbusRTU::onErrorToken(esp32Modbus::MBRTUOnErrorToken handler) { + _onErrorToken = handler; +} + bool esp32ModbusRTU::_addToQueue(ModbusRequest* request) { if (!request) { return false; @@ -105,9 +121,28 @@ void esp32ModbusRTU::_handleConnection(esp32ModbusRTU* instance) { instance->_send(request->getMessage(), request->getSize()); ModbusResponse* response = instance->_receive(request); if (response->isSucces()) { - if (instance->_onData) instance->_onData(response->getSlaveAddress(), response->getFunctionCode(), request->getAddress(), response->getData(), response->getByteCount()); + // if the non-token onData handler is set, call it + if (instance->_onData) + instance->_onData( + response->getSlaveAddress(), + response->getFunctionCode(), + request->getAddress(), + response->getData(), + response->getByteCount()); + // else, if the token onData handler is set, call that + else if (instance->_onDataToken) + instance->_onDataToken( + response->getSlaveAddress(), + response->getFunctionCode(), + request->getAddress(), + response->getData(), + response->getByteCount(), + response->getToken()); } else { + // Same for error responses. non-token onError set? if (instance->_onError) instance->_onError(response->getError()); + // No, but token onError instead? + else if (instance->_onErrorToken) instance->_onErrorToken(response->getError(), response->getToken()); } delete request; // object created in public methods delete response; // object created in _receive() @@ -116,14 +151,14 @@ void esp32ModbusRTU::_handleConnection(esp32ModbusRTU* instance) { } void esp32ModbusRTU::_send(uint8_t* data, uint8_t length) { - while (millis() - _lastMillis < _interval) delay(1); // respect _interval + while (micros() - _lastMicros < _interval) delayMicroseconds(1); // respect _interval // Toggle rtsPin, if necessary if (_rtsPin >= 0) digitalWrite(_rtsPin, HIGH); _serial->write(data, length); _serial->flush(); // Toggle rtsPin, if necessary if (_rtsPin >= 0) digitalWrite(_rtsPin, LOW); - _lastMillis = millis(); + _lastMicros = micros(); } // Adjust timeout on MODBUS - some slaves require longer/allow for shorter times @@ -133,15 +168,16 @@ void esp32ModbusRTU::setTimeOutValue(uint32_t tov) { ModbusResponse* esp32ModbusRTU::_receive(ModbusRequest* request) { ModbusResponse* response = new ModbusResponse(request->responseLength(), request); + uint32_t lastMillis = millis(); while (true) { while (_serial->available()) { response->add(_serial->read()); } if (response->isComplete()) { - _lastMillis = millis(); + lastMillis = millis(); break; } - if (millis() - _lastMillis > TimeOutValue) { + if (millis() - lastMillis > TimeOutValue) { break; } delay(1); // take care of watchdog diff --git a/src/esp32ModbusRTU.h b/src/esp32ModbusRTU.h index 1ee4cdd..b3c8764 100644 --- a/src/esp32ModbusRTU.h +++ b/src/esp32ModbusRTU.h @@ -52,13 +52,15 @@ class esp32ModbusRTU { explicit esp32ModbusRTU(HardwareSerial* serial, int8_t rtsPin = -1); ~esp32ModbusRTU(); void begin(int coreID = -1); - bool readDiscreteInputs(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils); - bool readHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters); - bool readInputRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters); - bool writeSingleHoldingRegister(uint8_t slaveAddress, uint16_t address, uint16_t data); - bool writeMultHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data); + bool readDiscreteInputs(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils, uint32_t token = 0); + bool readHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token = 0); + bool readInputRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint32_t token = 0); + bool writeSingleHoldingRegister(uint8_t slaveAddress, uint16_t address, uint16_t data, uint32_t token = 0); + bool writeMultHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, uint8_t* data, uint32_t token = 0); void onData(esp32Modbus::MBRTUOnData handler); void onError(esp32Modbus::MBRTUOnError handler); + void onDataToken(esp32Modbus::MBRTUOnDataToken handler); + void onErrorToken(esp32Modbus::MBRTUOnErrorToken handler); void setTimeOutValue(uint32_t tov); private: @@ -70,13 +72,15 @@ class esp32ModbusRTU { private: uint32_t TimeOutValue; HardwareSerial* _serial; - uint32_t _lastMillis; + uint32_t _lastMicros; uint32_t _interval; int8_t _rtsPin; TaskHandle_t _task; QueueHandle_t _queue; esp32Modbus::MBRTUOnData _onData; esp32Modbus::MBRTUOnError _onError; + esp32Modbus::MBRTUOnDataToken _onDataToken; + esp32Modbus::MBRTUOnErrorToken _onErrorToken; }; #endif diff --git a/src/esp32ModbusTypeDefs.h b/src/esp32ModbusTypeDefs.h index 7af6eb8..f5f65c0 100644 --- a/src/esp32ModbusTypeDefs.h +++ b/src/esp32ModbusTypeDefs.h @@ -64,6 +64,10 @@ typedef std::function MBRTUOnData; typedef std::function MBTCPOnError; typedef std::function MBRTUOnError; +typedef std::function MBTCPOnDataToken; +typedef std::function MBRTUOnDataToken; +typedef std::function MBTCPOnErrorToken; +typedef std::function MBRTUOnErrorToken; } // namespace esp32Modbus