bertmelis / esp32modbusrtu Goto Github PK
View Code? Open in Web Editor NEWmodbus RTU client for ESP32
License: MIT License
modbus RTU client for ESP32
License: MIT License
In ModbusMessage.cpp :
ModbusRequest06::ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t value) :
ModbusRequest(8) {
_slaveAddress = slaveAddress;
_functionCode = esp32Modbus::WRITE_HOLD_REGISTER;
_address = address;
_byteCount = value * 2; // register is 2 bytes wide
add(_slaveAddress);
add(_functionCode);
add(high(_address));
add(low(_address));
add(high(value));
add(low(value));
uint16_t CRC = CRC16(_buffer, 6);
add(low(CRC));
add(high(CRC));
}
size_t ModbusRequest06::responseLength() {
return 6;
}
In ModbusMessage.h :
// write single holding registers
class ModbusRequest06 : public ModbusRequest {
public:
explicit ModbusRequest06(uint8_t slaveAddress, uint16_t address, uint16_t value);
size_t responseLength();
};
In esp32ModbusRTU.cpp :
bool esp32ModbusRTU::writeSingleHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t value) {
ModbusRequest* request = new ModbusRequest06(slaveAddress, address, value);
return _addToQueue(request);
}
Hi! I just want to know if it is possible to get the address of the slave device wich is sending the error.
like:
modbus.onError([](esp32Modbus::Error error, uint8_t slaveAddress) {});
I tried like this:
void onError(esp32Modbus::MBRTUOnError handler,uint8_t slaveAddress);
typedef std::function<void(esp32Modbus::Error,uint8_t)> MBRTUOnError;
void esp32ModbusRTU::onError(esp32Modbus::MBRTUOnError handler,uint8_t slaveAddress) {
_onError = handler;
}
void esp32ModbusRTU::_handleConnection(esp32ModbusRTU* instance) {
while (1) {
ModbusRequest* request;
xQueueReceive(instance->_queue, &request, portMAX_DELAY); // block and wait for queued item
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());
} else {
if (instance->_onError) instance->_onError(response->getError(),response->getSlaveAddress());
}
delete request; // object created in public methods
delete response; // object created in _receive()
}
}
And i get:
error: no matching function for call to 'esp32ModbusRTU::onError(ModBus_Begin()::<lambda(esp32Modbus::Error, uint8_t)>)'
Thanks.
Hy!
I have a problem when I used the writeMultHoldingRegisters() function.
Serial monitor error message:
Guru Meditation Error: Core 0 panic'ed (LoadProhibited). Exception was unhandled.
Core 0 register dump:
PC : 0x400d33d2 PS : 0x00060130 A0 : 0x800d35d1 A1 : 0x3ffcf0d0
A2 : 0x3ffd1f10 A3 : 0x00000000 A4 : 0x00000001 A5 : 0x00000000
A6 : 0x00000000 A7 : 0x00000001 A8 : 0x800d33c7 A9 : 0x3ffcf0b0
A10 : 0x3ffd1f10 A11 : 0x00000002 A12 : 0x80088d90 A13 : 0x3ffbbf80
A14 : 0x00000000 A15 : 0x3ffccac0 SAR : 0x00000000 EXCCAUSE: 0x0000001c
EXCVADDR: 0x00000000 LBEG : 0x4000c2e0 LEND : 0x4000c2f6 LCOUNT : 0xffffffff
Backtrace: 0x400d33d2:0x3ffcf0d0 0x400d35ce:0x3ffcf0f0 0x400d0ea8:0x3ffcf110 0x40088b7d:0x3ffcf130
The exception decoder say this:
PC: 0x400d33d2: esp32ModbusRTUInternals::ModbusRequest16::ModbusRequest16(unsigned char, unsigned short, unsigned short, unsigned char*) at D:\Documents\Arduino\libraries\esp32ModbusRTU-master\src\ModbusMessage.cpp line 215
EXCVADDR: 0x00000000
Decoding stack results
0x400d33d2: esp32ModbusRTUInternals::ModbusRequest16::ModbusRequest16(unsigned char, unsigned short, unsigned short, unsigned char*) at D:\Documents\Arduino\libraries\esp32ModbusRTU-master\src\ModbusMessage.cpp line 215
0x400d35ce: esp32ModbusRTU::writeMultHoldingRegisters(unsigned char, unsigned short, unsigned short, unsigned char*) at D:\Documents\Arduino\libraries\esp32ModbusRTU-master\src\esp32ModbusRTU.cpp line 72
0x400d0ea8: parabolaMainFunctions(void*) at D:\Documents\Arduino\LAROF\parabola_main_board_v3/parabola_main_board_v3.ino line 192
0x40088b7d: vPortTaskWrapper at /home/runner/work/esp32-arduino-lib-builder/esp32-arduino-lib-builder/esp-idf/components/freertos/port.c line 143
Any issue to solve this problem?
Thanks!
Hello, trying to understand MAX485 connection to ESP32 from your schema and sketch, please help me to understand.
Your setup for SDM630 is "The ESP is connected to a max3485 with pins 17 (RX), 4 (TX) and 16 as RTS.".
Based on my pinnout, is it correct to be sketch changed to ?
So your sentence for my setup would become "The ESP is connected to a max3485 with pins 16 (RX), 17 (TX) and 4 as RTS."
Thanks
I am about to generate a pull request for the token feature I introduced. In short:
The requests are easily modified, the token is added as a new (last) calling parameter with a default value of 0, so the current function signature for the requests will continue working (readHoldingRegister etc.).
The problem to be discussed arises with the onData and onError handlers. Existing implementations will not work with changed onData and onError function signatures that have the token parameter added.
I see three different ways to implement the feature:
add the token parameter to onData and onError handlers, knowingly accepting old implementations will break.
add two more handlers onDataToken and onErrorToken, that will support the new signatures. The library will keep track of requests tokenized (token!=0) and classic and call the respective handlers.
Supply the token at the end of the getData block, extending it by 4 bytes. Implementations using the token feature must take care themselves to get the excess 4 bytes, as getLength will not be changed to not break the old interface.
I have implemented 1., as I could start from scratch, but solution 2. is sounding best to me.
What would you suggest?
Hy!
I tryed to use your libary on Heltec Wifi kit 32.
I modifyed the example code:
#include <Arduino.h>
#include <esp32ModbusRTU.h>
#include <algorithm> // for std::reverse
#define TIMEOUT_MS 3000
#define QUEUE_SIZE 20
esp32ModbusRTU modbus(&Serial1, 16); // use Serial1 and pin 16 as RTS
void setup() {
pinMode(12,OUTPUT);
Serial.begin(115200); // Serial output
Serial1.begin(19200, SERIAL_8N1, 17, 5, true); // Modbus connection
modbus.onData(handleData);
modbus.onError([](esp32Modbus::Error error) {
Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
});
modbus.begin();
}
void loop() {
static uint32_t lastMillis = 0;
if (millis() - lastMillis > 7000) {
lastMillis = millis();
Serial.print("sending Modbus request...\n");
modbus.readHoldingRegisters(0x01, 1, 1);
}
}
void handleData(uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint8_t* data, size_t length) {
Serial.printf("received data: id: %u, fc %u\ndata: 0x", serverAddress, fc);
for (uint8_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
}
Serial.print("\n");
}
The hardware connection is the following:
ESP32 modul ------> MAX485 module ------> RS485 to USB coverter ------> PC
On the PC I use the "Modbus Slave" software.https://www.modbustools.com/modbus_slave.html
When I estabilish the connection, on the PC in Communication trafic monitor I see this(attached picture)
It is not an valid modbus data. What is wrong with my code?
Thanks and best regards!
Benji
Hi,
I am far from an expert on the matter but I found 2 versions of terminating A and B.
The sample hardware in the readme does as in the top diagram, pull B to VCC and A to GND and then in the sdm630 sample ino, the serial port is initialized with the invert parameter true.
All cheapish prefabricated ttl-rs485 converters I found use the bottom diagram with A pulled to VCC and B to GND, with an 120ohm resistor between A and B, but then the invert parameter must be set to false.
I think it would be good if you spend a few words on the invert parameter in readme.MD to avoid confusion (and a failing setup) for new users.
nogmaals bedankt.
Hi Bert,
I need your advice, please... I finished implementing the new, bus timing-based receive function, but it shows some odd behaviour with larger responses. Everything that fits into 112 bytes runs fine, if the response is longer, receive will fail.
The essential part of the receive()
function are the two states shown below. BUFBLOCKSIZE
is 128 and buffer
is initialized with one block of 128 bytes initially. bufferPtr
starts at 0:
// IN_PACKET: read data until a gap of at least _interval time passed without another byte arriving
case IN_PACKET:
// Data waiting and space left in buffer?
while (_serial->available()) {
// Yes. Catch the byte
buffer[bufferPtr++] = _serial->read();
// Buffer full?
if(bufferPtr >= bufferBlocks * BUFBLOCKSIZE) {
// Yes. Extend it by another block
bufferBlocks++;
uint8_t *temp = new uint8_t[bufferBlocks * BUFBLOCKSIZE];
memcpy(temp, buffer, (bufferBlocks - 1) * BUFBLOCKSIZE);
delete buffer;
buffer = temp;
}
// Rewind timer
_lastMicros = micros();
}
delayMicroseconds(1);
// Gap of at least _interval micro seconds passed without data?
if(micros() - _lastMicros >= _interval) {
state = DATA_READ;
}
break;
// DATA_READ: successfully gathered some data. Prepare return object.
case DATA_READ:
// Allocate response object
response = new ModbusResponse(bufferPtr, request);
// Move gathered data into it
response->setData(bufferPtr, buffer);
state = FINISHED;
break;
The data collected in buffer is moved into the response's _buffer
with a call to
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);
}
The effect observable with long responses is illustrated with this test output caught in the checkCRC function:#
18:15:05.356 CRC ERR: _length=112 CRC calc=A92B CRC pack=00 ?
18:15:05.367 04 03 80 01 84 C4 5A C0 00 47 1D 53 CD 49 05 FE
18:15:05.380 1A 47 1D 53 CD 49 05 FE 1A 00 00 00 00 00 00 00
18:15:05.393 00 45 4D 48 00 09 01 45 4D 48 00 00 90 2F 2C 00
18:15:05.406 00 00 00 00 06 9C 68 00 00 09 6F 00 00 00 00 00
18:15:05.418 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
18:15:05.431 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
18:15:05.445 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
The packets are from slave #4, function code is 0x03 and the data length to be expected is 0x80, as the first three bytes show. Subsequently the bytes up to the 112th - the _buffer
size _length
is indicating - are correctly shown.
But why does it stop at 112? To be expected are 128 (data) + 3 (slave, function code and length) + 2 (CRC) = 135. Clearly this will not fit in the initial buffer size of 128 that needs to be extended. But this will happen only if bufferPtr
gets to 128, which is still 16 bytes ahead.
_lastMicros
will be set to micros()
immediately before the while loop breaks, so the timeout should not have struck prematurely. The only way to fall into that hole would be _serial->available()
lasting more than _interval
microseconds - unlikely, I would say. I nevertheless tried with an _interval
of 4000 µs but the effect persisted.
I am at a loss here, I am afraid.
Hi Bert,
thanks again for your help with my initial struggles with my RS485 wiring ;). Now I am facing another challenge and would like to get your thoughts on it.
My sketch is running on a device that can be used locally with buttons and display, but on the other hand will serve up to 8 TCP clients doing MODBUS requests. So in theory I will have up to 9 consumers for the esp32modbusRTU object. It will be a TCP-RTU bridge sitting in my fuse box to connect power meters etc. into the TCP network.
The problem is to keep track of the respective requests and resulting responses. One could compare the function codes, slave IDs etc., but that would only give some probability to match the right request with its response.
I am considering different solutions, all of them having some issues.
bool readHoldingRegisters(uint8_t slaveAddress, uint16_t address, uint16_t numberRegisters, esp32Modbus::MBRTUOnData handler=NULL, esp32Modbus::MBRTUOnError handler=NULL);
This way the interface will not be broken for existing users of the library.
But this is not sufficient for my application - I would be able to separate local from TCP requests, but the TCP tasks are all sharing the same code, so they would remain undistinguishable.
add a new calling argument uint32_t requesterToken
to the request functions that the requesting consumer can use to put in a unique identifier. Having a signature as above, defaulting the token to zero, the interface again would not be broken for the request functions, but the onData and onError handlers would have to be modified to bring back the token - breaking the interface.
derive a new class from esp32modbusRTU, that has both the new features described above. This is not comfortably done, unfortunately, as your original class has a couple of data and functions declared private
, making it impossible to envelope those I would need to. If only these were declared protected
...
So what would you suggest for me to do?
Hi, I just downloaded your lib and I managed to use it (goal is to read modbus electric and water meters).
Things are working nicely, but I have a XY-K485 TTL module to convert serial to RS485.
This do not use RTS pin, on RX and TX pins, I think it does it internally by buffering the serial RX/TX data and doing the RTS magic himself.
Anyway, I can send order and get back the response from my water meter, using a non-connected RTS pin.
I do not like to loose pin though, even if it would not be a problem for my current project, so I changed your code so instead of exiting, providing a negative pin number simply skip all RTS pin operation (pinmode and write to high or low). Still working without any issue, so I though this cound be added to your lib....I can send a patch (although the mods needed are really trivial).
BTW first I though of using a non-existing pin instead of -1, like 100. It worked too, but it's not a good idea apparently, no guarantee it does not write to arbitrary memory address...
It is possible to write an example to MultiHoldingRegister, it reports me an error.
Hi, first of all thank you for your useful library.
I have encountered this error but I can't understand what it means.
error: 0xe0 fc 0x00 address 0x103a
The command that triggers it is: modbus.readHoldingRegisters(SLAVE_ID, STATUS_ADDR, 1);
This command is executed many times before the error is thrown, so for example I can read the status code of the system 100 times and then the error is thrown.
Can anyone please give me an advice on how to tackle this problem? Maybe too many requests in too little time?
Thank you in advance
Hi Bert,
Is there any method to extract data from response and convert it to other data types? There aren't any examples /methods on this. Tried typecasting but data gets changed and overflows sometimes. Can you please explain with a example ?
Hi Bert,
I may have found a potential reason for your ESP32MODBUSRTU task to crash after roughly 500 days on inactivity. This is unlikely to happen for a system used regularly, but anyway...
The xQueueReceive() call will ultimatively return after portMAX_DELAY ticks, which translates into the mentioned 500 days. The queue item in &request then is undefined. If one will regard the return code of xQueueReceive() -pdTRUE or pdFALSE -, the crash can be avoided.
void esp32ModbusRTU::_handleConnection(esp32ModbusRTU* instance) {
while (1) {
ModbusRequest* request;
if(pdTRUE==xQueueReceive(instance->_queue, &request, portMAX_DELAY)) // block and wait for queued item
{
instance->_send(request->getMessage(), request->getSize());
ModbusResponse* response = instance->_receive(request);
if (response->isSucces()) {
if (instance->_onData) instance->_onData(response->getSlaveAddress(), response->getFunctionCode(), response->getData(), response->getByteCount(), response->getToken());
} else {
if (instance->_onError) instance->_onError(response->getError(), response->getToken());
}
delete request; // object created in public methods
delete response; // object created in _receive()
}
}
}
Hello,
I made few changes to the example application (changed function code, register address and serial pins)
On uploading I get the following message
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400d0cdc: fcdc3191 3bb808a0 3bb80898
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation Error: Core 1 panic'ed (IllegalInstruction). Exception was unhandled.
Memory dump at 0x400e9eac: 33313331 32313830 31333239
Guru Meditation ErrGuru Meditation Error: Core 1 panic'ed (Unhandled debug exception)
Debug exception reason: Stack canary watchpoint triggered (loopTask)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Interrupt wdt timeout on CPU1)
Guru Meditation Error: Core 1 panic'ed (Guru Meditation Error: Core 1 panic'ed (Guru Meditation Error: Core 1 panic'ed (
Hy!
I want to use the esp32ModbusRTU class in my own class, but i have this error message:
no suitable constructor exists to convert from "void (uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint8_t *data, uint16_t length)" to "std::function<void (uint8_t, esp32Modbus::FunctionCode, uint8_t *, uint16_t)>"
My code:
.h
class parabolaCommunication
{
private:
int _modbusRxPin;
int _modbusTxPin;
int _modbusDePin;
private:
void handleModbusData(uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint8_t* data, uint16_t length);
void identifyRecievedData(int senderAddress, long int recievedData);
public:
parabolaCommunication(int modbusRxPin, int modbusTxPin, int modbusDePin,bool serviceMode);
bool init(esp32Modbus::MBRTUOnData handler);
};
.cpp
bool parabolaCommunication::init(esp32Modbus::MBRTUOnData handler)
{
if(_serviceMode == false)
{
wifiConnect();
client.setServer(_mqttServerAddress, _mqttServerPort);
}
Serial1.begin(19200,SERIAL_8N1,_modbusRxPin,_modbusTxPin,true);
modbus.onData(handleModbusData);
modbus.onError([](esp32Modbus::Error error) {
Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
});
modbus.begin();
return 0;
}
void parabolaCommunication::handleModbusData(uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint8_t* data, uint16_t length)
{
long int number = 0;
int deviceAddress = serverAddress;
number = (data[0] << 8) + data[1];
identifyRecievedData(deviceAddress , number);
}
Can you help me?
Thanks!
I want to know if there is a queue when someone using the bus.
Does the request got dropped out or the modules have waiting for the bus to be available again?
Edit: When the master wants to write.
I am playing around with the library for some days and was able to make it communicate with a Siemens Sinamics V20 VFD.
So far so good, thanks to your great library, I was able to write and read registers correctly (unfortunately I am not able to start the motor, but that problem relates to the VFD).
When I tried to make the response of the VFD more readable, I got pretty confused.
Code example:
modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode 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]);
}
std::reverse(data, data + 4); // fix endianness
Serial.printf("\nval_int: %02x", *reinterpret_cast<int*>(data)); //Float cast always showed 0.0
Serial.print("\n\n");
});
When I send a read request i.e. to register 0x02 of the VFD with 1 byte, I get the following response from the VFD:
Console output:
id 0x01 fc 0x03 len 2: 0x4000
val_int: 40008984
which is kind of fine.
But if I use write single register 0x06 i.e. register 0x63, value 0x107E
Console output:
id 0x01 fc 0x06 len 0: 0x
val_int: 63107e35
The for loop does not print any data, but the reinterpret_cast<int> does show some data.
I don't understand, why that happens?
Furthermore the data shown in the int* cast shows 63(register), 107e(value) and 35(1st crc byte). The second crc byte is cut out for some reason.
Do you understand why all this is happening?
I guess the differences has something to do with adding 4 bytes in the reverse function, but I am not sure what to do in order to print out the full received data in order to process it the way I want to
Hey,
I'm trying to connect a wind speed sensor using the code provided, I changed the address to match the sensor factory default but getting error: 0xe3, what does it mean and how can I resolve it?
By the way, I've tested the sensor using USB and modbus software and it works well.
Wind speed 485 transmitter instructions.pdf
#include <Arduino.h>
#include <esp32ModbusRTU.h>
#include <algorithm> // for std::reverse
esp32ModbusRTU modbus(&Serial1, 15); // use Serial1 and pin 16 as RTS
void setup() {
Serial.begin(4800); // Serial output
Serial1.begin(4800, SERIAL_8N1, 4, 2, true); // Modbus connection
modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint16_t address, 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]);
}
std::reverse(data, data + 4); // fix endianness
Serial.printf("\nval: %.2f", *reinterpret_cast<float*>(data));
Serial.print("\n\n");
});
modbus.onError([](esp32Modbus::Error error) {
Serial.printf("error: 0x%02x\n\n", static_cast<uint8_t>(error));
});
modbus.begin();
}
void loop() {
static uint32_t lastMillis = 0;
if (millis() - lastMillis > 5000) {
lastMillis = millis();
Serial.print("sending Modbus request...\n");
modbus.readInputRegisters(0x01, 0, 1);
}
}
Thanks!
Hi, I sometimes receive this error:
error: 0xe3 slaveId: 8 fc 0x10 address 0x27d
From the source code of the library I understand that it is a CRC problem, but I don't understand why it gets thrown.
The function that generates the error is the WRITE_MULT_REGISTERS.
But if the problem is related to the CRC, shouldn't the problem appear in all write requests?
I mean, I don't think the error is thrown because the code calculates the wrong CRC
Hello,
Im new to esp32 i want to implement a modbus RTU master using esp32.
I took serial2 as modbus and made as per that. the esp32 initialized as master and it sending request to slave slave also responding but I am getting
sending Modbus request...
error: 0xef
I used Serial port monitor to debug and got
[04/03/2020 18:42:40] Written data (COM5)
01 04 08 00 0a 00 64 00 32 00 14 5e 05 ......d.2..^.
[04/03/2020 18:42:43] Read data (COM5)
01 04 00 00 00 04 f1 c9 ......ñÉ
[04/03/2020 18:42:43] Written data (COM5)
01 04 08 00 0a 00 64 00 32 00 14 5e 05 ......d.2..^.
[04/03/2020 18:42:46] Read data (COM5)
01 04 00 00 00 04 f1 c9 ......ñÉ
[04/03/2020 18:42:46] Written data (COM5)
01 04 08 00 0a 00 64 00 32 00 14 5e 05 ......d.2..^.
[04/03/2020 18:42:49] Read data (COM5)
01 04 00 00 00 04 f1 c9 ......ñÉ
I cant find this ef anywhere in the library files and from where it is printing. what is this error?
It would be handy to have both MODBUS/RTU and MODBUS/TCP available in a single library. It looks like there could be a lot of code usable on both sides, so I started a closer inspection.
MODBUS/RTU and MODBUS/TCP are sharing the same payload, consisting of slave ID, function code and additional data. The difference are the TCP header for the /TCP variant and the trailing CRC checksum for /RTU:
TCP: [sender ID][0x0000][length] [slave ID][function code][data]
RTU: [slave ID][function code][data] [CRC]
If TCP head and CRC tail were held separately, all functions dealing with the payload may be left as are (but see below).
Functions dealing with setup or manipulation of ModbusMessages will need to know if it is a /TCP or /RTU request or response to add the required header or trailer.
While for /RTU the slave ID is sufficient to address a certain slave on the MODBUS, /TCP needs an IP address or hostname, plus port number in addition. In fact the slave ID is necessary nevertheless, as any IP/Port combination may contain a complete MODBUS again with different slaves.
The library would need a way to give IP or hostname plus port additionally to the existing parameters.
The existing esp32ModbusRTU class takes a HardwareSerial pointer as mandatory constructor parameter. A new esp32ModbusTCP class will have to take a Client parameter instead - Client being the base class for both WifiClient and EthernetClient.
The esp32ModbusRTU class should have the CRC separated from the _buffer to allow functions to be /RTU-/TCP-agnostic. The espModbusTCP class will have a TCPheader member struct instead.
The handleConnection, send and receive functions have to be separate for both classes obviously. The checks for valid packets, set up of header or CRC trailer etc. will be different, too. A solution could be to have virtual functions doHeader()
and doTrailer()
that have to be implemented in both variants differently, but can be called regardless from type-agnostic general functions.
The same applies to the various request calls, as those for the esp32ModbusTCP class will have to have IP and port in addition.
The onData, onError handlers and their tokenized variants can be identical, though, it could even be possible for an application to use the same handler for both variant callbacks.
It looks like it could be possible to merge the /TCP functionality into the /RTU lib.
Would you like to jump in here?
3. Finally the /TCP part can be brought in...
Hello,
I try to get a PZEM-003 working, the data value I receive is 2 x byte. I try it with this code:
modbus.readInputRegisters(0x02, 0x0000, 1);
modbus.onData([](uint8_t serverAddress, esp32Modbus::FunctionCode fc, uint16_t address, uint8_t* data, size_t length) {
ValueRead = 0;
Serial.printf("received data: id: %u, fc %u\ndata: 0x", serverAddress, fc);
for (size_t i = 0; i < length; ++i) {
Serial.printf("%02x", data[i]);
}
Serial.print("\n");
ValueRead = *reinterpret_cast<uint8_t*>(data);
});
The data value I recive is "0x04cf" and this is right.
But with "reinterpret_cast" it is not working. Someone have a hint for me?
Greetings, Michael
Hello everyone,
I was wondering if this library could also work with RS232 communication or is it only for RS485? and if it does not, could you give me any quick guidance on how to read data from a sensor communicating in RS232?
Thanks,
Tal.
Hey,
I used the example you provided, changed only the address and slave ID but I get this error, what does it mean?
Thanks.
Hi,
I am trying to communicate through a MAX485 module with a power meter. I am using UART2 to drive it and your library of course.
The relevant code snippets are:
...
// RS485 MODBUS RTU
#include <esp32ModbusRTU.h>
esp32ModbusRTU RS485(&Serial2, GPIO_NUM_15);
...
setup() {
...
Serial2.begin(19200, SERIAL_8N1, GPIO_NUM_16, GPIO_NUM_17, true);
...
// Init RS485 MODBUS RTU
RS485.onData(handleData);
RS485.onError([](esp32Modbus::Error error) {
Serial.printf("RS485 error: 0x%02x\n\n", static_cast<uint8_t>(error));
});
RS485.begin();
// Test
RS485.readHoldingRegisters(0x01, 0x0734, 4);
}
...
All I get is strange data on the Tx and no response from the meter. The meter's comms LED flashes shortly, though, when the request is transmitted.
With a logic analyzer I seem to get these data:
52795-52796 UART: TX bits: 0
52796-52797 UART: TX bits: 0
52797-52798 UART: TX bits: 0
52798-52799 UART: TX bits: 0
52799-52800 UART: TX bits: 0
52800-52801 UART: TX bits: 1
52801-52802 UART: TX bits: 0
52802-52803 UART: TX bits: 1
52806-52807 UART: TX bits: 0
52807-52808 UART: TX bits: 0
52808-52809 UART: TX bits: 0
52809-52810 UART: TX bits: 0
52810-52811 UART: TX bits: 0
52811-52812 UART: TX bits: 1
52812-52813 UART: TX bits: 0
52813-52814 UART: TX bits: 1
52818-52819 UART: TX bits: 0
52819-52820 UART: TX bits: 0
52820-52821 UART: TX bits: 0
52821-52822 UART: TX bits: 1
52822-52823 UART: TX bits: 0
52823-52824 UART: TX bits: 0
52824-52825 UART: TX bits: 0
52825-52826 UART: TX bits: 1
52831-52832 UART: TX bits: 0
52832-52833 UART: TX bits: 1
52833-52834 UART: TX bits: 0
52834-52835 UART: TX bits: 0
52835-52836 UART: TX bits: 0
52836-52837 UART: TX bits: 0
52837-52838 UART: TX bits: 0
52838-52839 UART: TX bits: 0
52845-52846 UART: TX bits: 0
52846-52847 UART: TX bits: 1
52847-52848 UART: TX bits: 0
52848-52849 UART: TX bits: 0
52849-52850 UART: TX bits: 0
52850-52851 UART: TX bits: 0
52851-52852 UART: TX bits: 0
52852-52853 UART: TX bits: 1
52860-52861 UART: TX bits: 0
52861-52862 UART: TX bits: 0
52862-52863 UART: TX bits: 0
52863-52864 UART: TX bits: 1
52864-52865 UART: TX bits: 0
52865-52866 UART: TX bits: 1
52866-52867 UART: TX bits: 1
52867-52868 UART: TX bits: 0
There is no similarity to the intended data, I am afraid...
Do you have any suggestion as to where my error may be?
Reading the thread on data and type casting Hasenburg had raised, I found my original approach on raw requests that are agnostic to the function codes was shortsighted. As there simply is no common data element to describe the length of any response, I will have to stick to the MODBUS basics, I am afraid.
The receive() function will have to look for bus intervals to determine the end of any transmissions. This is standard for MODBUS slaves in the wild and I have implemented it already in another project, but the timing is critical. The calculation of interval better has to use micros() instead of millis(), and receive() will have to be a tight loop state machine.
This may have an impact on all currently implemented function codes, as the response length would not be needed anymore.
I will have a closer look at this and report back.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.