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

Remove address parameter #42

Open
wants to merge 25 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
57d2e9c
Token implementation /3
Miq1 Aug 14, 2020
bf4dc49
First shot at new receive function
Miq1 Aug 18, 2020
5dac6ee
Modified getData and getByteCount to fit to new function codes
Miq1 Aug 18, 2020
ac45909
Add dynamic buffer sizes in receive
Miq1 Aug 19, 2020
176e59d
Add rawRequest
Miq1 Aug 19, 2020
3c10f02
Fix typos
Miq1 Aug 19, 2020
59c9445
Remove esp32Modbus::FunctionCode from onData function typedefs
Miq1 Aug 19, 2020
e9d8760
_buffer, _index not visible outside response object
Miq1 Aug 19, 2020
0de99d3
remove _buffer and _index from receive completely
Miq1 Aug 19, 2020
8c8aac7
Remove responseLength, add setErrorResponse, setData
Miq1 Aug 20, 2020
6830b42
Simplify setError
Miq1 Aug 20, 2020
37ccf60
Relax bus timing a bit
Miq1 Aug 20, 2020
3b028eb
Missing _length in setErrorResponse and setData
Miq1 Aug 21, 2020
d4a4fe3
Set gap detection interval to 16ms to cope with an ESP32-Arduino core…
Miq1 Aug 21, 2020
143ceac
Clarify FIFO handling issue in ESP32-Arduino core code.
Miq1 Aug 22, 2020
3da0a52
Fix typo on _interval comment in esp32ModbusRTU.begin() and add updat…
Miq1 Aug 22, 2020
6d21671
Fix cpplint grieves
Miq1 Aug 22, 2020
ba4b0e9
Another blank missing for cpplint...
Miq1 Aug 22, 2020
256c37c
Use intermediate memory pointer to keep cppcheck happy
Miq1 Aug 22, 2020
78ffbcf
Another attempt to ease cppcheck
Miq1 Aug 22, 2020
60783fb
Fix Test cases (removed responseLength())
Miq1 Aug 22, 2020
a1297bb
Update example sketch to accept all function codes
Miq1 Aug 22, 2020
8e2c321
Update Readme
Miq1 Aug 22, 2020
889fe93
Remove (now obsolete) address parameter for onData and onError handlers
Miq1 Aug 24, 2020
d9c0758
Missed the example sketch when removing the parameter
Miq1 Aug 24, 2020
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
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ This is a non blocking Modbus client (master) for ESP32.
- read discrete inputs (02)
- read holding registers (03)
- read input registers (04)
- write single holding register (06)
- write multiple holding register (16)
- "raw" requests for arbitrary function codes
- similar API as my [esp32ModbusTCP](https://github.com/bertmelis/esp32ModbusTCP) implementation

## Developement status
Expand Down Expand Up @@ -64,7 +67,8 @@ is connected via a 100 Ohms resistor to limit possible ground loop currents.

The API is quite lightweight. It takes minimal 3 steps to get going.

First create the ModbusRTU object. The constructor takes two arguments: HardwareSerial object and pin number of DE/RS.
First create the ModbusRTU object. The constructor takes two arguments: HardwareSerial object and pin number of DE/RS (or -1, if
your module does auto half duplex).

```C++
esp32ModbusRTU myModbus(&Serial, DE_PIN);
Expand Down Expand Up @@ -119,12 +123,23 @@ The request queue holds maximum 20 items. So a 21st request will fail until the
#define QUEUE_SIZE 20
```

The waiting time before a timeout error is returned can also be changed by a `#define` variable:
The waiting time before a timeout error is returned can also be changed by a method call:

```C++
#define TIMEOUT_MS 5000
myModbus.setTimeOutValue(5000);
```

## Caveat

The ESP32 Arduino core implementation of the handling of Serial interfaces has a design decision built in that prevents real time
Serial communications for data packets received larger than 112 bytes. the underlying FIFO buffer is only copied into the Serial's buffer
when 112 bytes have been received. The copy process then takes longer than the MODBUS timeout lasts, so the remainder of the packet is lost.

The library has a workaround built in that covers the issue by waiting a loooong time (16 milliseconds) until determining a bus timeout (= end
of packet). This is possible since the library implements a MODBUS master device, thus controlling the bus timing.

But note that this is not according to MODBUS standards.

## Issues

Please file a Github issue ~~if~~ when you find a bug. You can also use the issue tracker for feature requests.
Expand Down
2 changes: 1 addition & 1 deletion examples/SDM630/SDM630.ino
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ void setup() {
Serial.begin(115200); // Serial output
Serial1.begin(9600, SERIAL_8N1, 17, 4, true); // Modbus connection

modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint16_t address, uint8_t* data, size_t length) {
modbus.onData([](uint8_t serverAddress, uint8_t fc, uint8_t* data, size_t length) {
Serial.printf("id 0x%02x fc 0x%02x len %u: 0x", serverAddress, fc, length);
for (size_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
Expand Down
154 changes: 91 additions & 63 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 @@ -131,108 +136,94 @@ ModbusRequest::ModbusRequest(uint8_t length) :
ModbusMessage(length),
_slaveAddress(0),
_functionCode(0),
_address(0),
_byteCount(0) {}

uint16_t ModbusRequest::getAddress() {
return _address;
uint8_t ModbusRequest::getSlaveAddress() {
return _slaveAddress;
}

ModbusRequest02::ModbusRequest02(uint8_t slaveAddress, uint16_t address, uint16_t numberCoils) :
uint8_t ModbusRequest::getFunctionCode() {
return _functionCode;
}

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));
add(low(_address));
add(high(address));
add(low(address));
add(high(numberCoils));
add(low(numberCoils));
uint16_t CRC = CRC16(_buffer, 6);
add(low(CRC));
add(high(CRC));
}

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));
add(low(_address));
add(high(address));
add(low(address));
add(high(numberRegisters));
add(low(numberRegisters));
uint16_t CRC = CRC16(_buffer, 6);
add(low(CRC));
add(high(CRC));
}

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));
add(low(_address));
add(high(address));
add(low(address));
add(high(numberRegisters));
add(low(numberRegisters));
uint16_t CRC = CRC16(_buffer, 6);
add(low(CRC));
add(high(CRC));
}

size_t ModbusRequest04::responseLength() {
// slaveAddress (1) + functionCode (1) + byteCount (1) + length x 2 + CRC (2)
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));
add(low(_address));
add(high(address));
add(low(address));
add(high(data));
add(low(data));
uint16_t CRC = CRC16(_buffer, 6);
add(low(CRC));
add(high(CRC));
}

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));
add(low(_address));
add(high(address));
add(low(address));
add(high(numberRegisters));
add(low(numberRegisters));
add(_byteCount);
Expand All @@ -244,39 +235,40 @@ ModbusRequest16::ModbusRequest16(uint8_t slaveAddress, uint16_t address, uint16_
add(high(CRC));
}

size_t ModbusRequest16::responseLength() {
return 8;
ModbusRequestRaw::ModbusRequestRaw(uint8_t slaveAddress, uint8_t functionCode, uint16_t dataLength, uint8_t* data, uint32_t token) :
ModbusRequest(dataLength + 4) {
_slaveAddress = slaveAddress;
_functionCode = functionCode;
_byteCount = dataLength + 2;
_token = token;
add(_slaveAddress);
add(_functionCode);
for (int i = 0; i < dataLength; i++) {
add(data[i]);
}
uint16_t CRC = CRC16(_buffer, _byteCount);
add(low(CRC));
add(high(CRC));
}

ModbusResponse::ModbusResponse(uint8_t length, ModbusRequest* request) :
ModbusMessage(length),
_request(request),
_error(esp32Modbus::SUCCES) {}

bool ModbusResponse::isComplete() {
if (_buffer[1] > 0x80 && _index == 5) { // 5: slaveAddress(1), errorCode(1), CRC(2) + indexed
return true;
}
if (_index == _request->responseLength()) return true;
return false;
}
_error(esp32Modbus::SUCCES) { _token = request->getToken(); }

bool ModbusResponse::isSucces() {
if (!isComplete()) {
_error = esp32Modbus::TIMEOUT;
} else if (_buffer[1] > 0x80) {
if (_buffer[1] > 0x80) {
_error = static_cast<esp32Modbus::Error>(_buffer[2]);
} else if (!checkCRC()) {
_error = esp32Modbus::CRC_ERROR;
} else if (_buffer[0] != _request->getSlaveAddress()) {
_error = esp32Modbus::INVALID_SLAVE;
// TODO(bertmelis): add other checks
} else {
_error = esp32Modbus::SUCCES;
}
if (_error == esp32Modbus::SUCCES) {
return true;
} else {
return false;
}
return false;
}

bool ModbusResponse::checkCRC() {
Expand All @@ -296,14 +288,50 @@ uint8_t ModbusResponse::getSlaveAddress() {
return _buffer[0];
}

esp32Modbus::FunctionCode ModbusResponse::getFunctionCode() {
return static_cast<esp32Modbus::FunctionCode>(_buffer[1]);
uint8_t ModbusResponse::getFunctionCode() {
return _buffer[1];
}

uint8_t* ModbusResponse::getData() {
return &_buffer[3];
uint8_t fc = _request->getFunctionCode();
if (fc == 0x01 || fc == 0x02 || fc == 0x03 || fc == 0x04) {
return &_buffer[3];
} else {
return &_buffer[2];
}
}

uint8_t ModbusResponse::getByteCount() {
return _buffer[2];
uint8_t fc = _request->getFunctionCode();
if (fc == 0x01 || fc == 0x02 || fc == 0x03 || fc == 0x04) {
return _buffer[2];
} else {
return _index - 2;
}
}

void ModbusResponse::setErrorResponse(uint8_t errorCode) {
if (_length != 5) {
delete _buffer;
_buffer = new uint8_t[5];
_length = 5;
}
_index = 0;
_error = static_cast<esp32Modbus::Error>(errorCode);
add(_request->getSlaveAddress());
add(_request->getFunctionCode() | 0x80);
add(errorCode);
uint16_t CRC = CRC16(_buffer, 3);
add(low(CRC));
add(high(CRC));
}

void ModbusResponse::setData(uint16_t dataLength, uint8_t *data) {
if (_length != dataLength) {
delete _buffer;
_buffer = new uint8_t[dataLength];
_length = dataLength;
}
_index = dataLength;
memcpy(_buffer, data, dataLength);
}
Loading