Skip to content
This repository has been archived by the owner on Jul 30, 2021. It is now read-only.

Token implementation /3 #39

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions src/ModbusMessage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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;
}
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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));
Expand All @@ -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
Expand Down
12 changes: 7 additions & 5 deletions src/ModbusMessage.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,15 @@ class ModbusMessage {
virtual ~ModbusMessage();
uint8_t* getMessage();
uint8_t getSize();
uint32_t getToken();
void add(uint8_t value);

protected:
explicit ModbusMessage(uint8_t length);
uint8_t* _buffer;
uint8_t _length;
uint8_t _index;
uint32_t _token;
};

class ModbusResponse; // forward declare for use in ModbusRequest
Expand All @@ -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();
};

Expand Down
74 changes: 55 additions & 19 deletions src/esp32ModbusRTU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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*));
}

Expand All @@ -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);
}

Expand All @@ -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;
Expand All @@ -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()
Expand All @@ -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
Expand All @@ -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
Expand Down
16 changes: 10 additions & 6 deletions src/esp32ModbusRTU.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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
Expand Down
4 changes: 4 additions & 0 deletions src/esp32ModbusTypeDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ typedef std::function<void(uint16_t, uint8_t, esp32Modbus::FunctionCode, uint8_t
typedef std::function<void(uint8_t, esp32Modbus::FunctionCode, uint16_t, uint8_t*, uint16_t)> MBRTUOnData;
typedef std::function<void(uint16_t, esp32Modbus::Error)> MBTCPOnError;
typedef std::function<void(esp32Modbus::Error)> MBRTUOnError;
typedef std::function<void(uint16_t, uint8_t, esp32Modbus::FunctionCode, uint8_t*, uint16_t, uint32_t)> MBTCPOnDataToken;
typedef std::function<void(uint8_t, esp32Modbus::FunctionCode, uint16_t, uint8_t*, uint16_t, uint32_t)> MBRTUOnDataToken;
typedef std::function<void(uint16_t, esp32Modbus::Error, uint32_t)> MBTCPOnErrorToken;
typedef std::function<void(esp32Modbus::Error, uint32_t)> MBRTUOnErrorToken;

} // namespace esp32Modbus

Expand Down