Giter VIP home page Giter VIP logo

esp32modbusrtu's People

Contributors

bertmelis avatar duccio avatar hasenburg avatar jandegr avatar luckywang95 avatar miq1 avatar stif avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

esp32modbusrtu's Issues

Write single register example?

Can this be done just like this?

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);
}

get Slave address and etc inside Error handler?

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:

IN esp32ModbusRTU.h

void onError(esp32Modbus::MBRTUOnError handler,uint8_t slaveAddress);

IN esp32ModbusTypeDefs.h

typedef std::function<void(esp32Modbus::Error,uint8_t)> MBRTUOnError;

IN esp32ModbusRTU.cpp

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.

writeMultHoldingRegisters problem

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!

MAX485 connection to ESP WROOM 32

Hello, trying to understand MAX485 connection to ESP32 from your schema and sketch, please help me to understand.

I have ESP WROOM 32 pinnout
image

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 ?

  • PIN U2_RXD (GPIO16) to RO (receiver output)
  • U2_TXD (GPIO17) to DI (driver input)
  • D4 (GPIO4) to DE/RE (driver enable/receiver enable)

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

Design question on tokenized request/response pairs

I am about to generate a pull request for the token feature I introduced. In short:

  • If you have several clients doing requests, you need to keep track which response belonged to what request.
  • I added a uint32_t token to the requests that is set by the requester and will be delivered again with the response. Thus the requesting client can tell which request caused the response

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:

  1. add the token parameter to onData and onError handlers, knowingly accepting old implementations will break.

  2. 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.

  3. 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?

Wrong data send

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)
modbus_error
It is not an valid modbus data. What is wrong with my code?
Thanks and best regards!
Benji

polarity of A and B wires

Hi,

I am far from an expert on the matter but I found 2 versions of terminating A and B.
2fdda3cc7cd98d10b83f8feb2a3fb80e7aec9005

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.

New receive function issue

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.

Multiple consumers in one sketch

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.

  1. add two more calling arguments to the request functions, that give addresses of separate onData and onError handlers for the very request. Internally, the esp32modbusRTU instance can check if these handler addresses are different from NULL and use these to return the data and errors to the caller. The function signature will be like
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.

  1. 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.

  2. 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?

Allow to not use any RTS pin

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...

error: 0xe0

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

Type casting of Data response

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 ?

Sanity precaution

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()
    }
  }
}

interrupt watchdog timout on uploading example code

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 (

Using in another class

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!

Is there any Error Checks for the busy bus?

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.

Type casting and data

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

error: 0xe3

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!

error: 0xe3

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

Getting error: 0xef

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?

Thoughts on integrating MODBUS/TCP

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.

Packet formats

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.

Adressing

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.

Classes

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.

Class methods

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.

Preliminary conclusion

It looks like it could be possible to merge the /TCP functionality into the /RTU lib.

  1. Prerequisite would be a clean separation of functions and member variables into those interface-dependent and independent and a class scheme implementing this separation.
  2. Before integrating any /TCP line of code, the restructured library has to be thoroughly tested for the /RTU functionality to not lose anything on the way ;)

Would you like to jump in here?
3. Finally the /TCP part can be brought in...

cast value

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

RS232 Question

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.

Getting error: 0xe0

Hey,
I used the example you provided, changed only the address and slave ID but I get this error, what does it mean?
Thanks.

Rubbish instead of proper data

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?

RawRequest addition

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.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.