eModbus / eModbus

Modbus library for RTU, ASCII and TCP protocols. Primarily developed on and for ESP32 MCUs.

Home Page:https://emodbus.github.io

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Reworked the RTU process

Miq1 opened this issue · comments

@BrotherCreamy @I-Connect @OekoSolveMG @zivillian @SuGlider @TLS1000

Guys, I mentioned you because you all were affected by some bugs and shortcomings in the RTU handling of eModbus (issues #252, #239 and #198, to name the most relevant).

In the meantime I reworked the RTU receive process in a couple of steps and I believe it will do better now. I would ask you to give it a try and report back about the results.

The new code is in a separate branch for now, called Stream-RTU.

Major changes:

  • the RTU code now accepts a Stream object instead of a HardwareSerial. For the latter there will be few differences, but it opens up to use SoftwareSerial or whatever else Stream-derived channel is used.
  • as a consequence the begin() functions of ModbusClientRTU and ModbusServerRTU have been changed. They will need a baud rate as first parameter now and optionally can give a core to be run on as the second:
    void ModbusClientRTU::begin(uint32_t baudrate = 9600, int coreID = -1);
    void ModbusServerRTU::begin(uint32_t baudRate = 9600, int coreID = -1);
  • The baud rate is necessary to calculate the message interval from it, a Stream does not have a notion of it. As you can see the default is set to 9600 baud, but that will definitely fail for baud rates used in the communication that are below that value. It was the best assumption I could make today.
  • Note that the older start/stop in ModbusServerRTU now has been replaced by begin/end!

The extraction of the HardwareSerial manipulations from the code requires the user (you! 😉 ) to do preparations before starting a ModbusServerRTU/ModbusClientRTU, though. Here as an example are shown the settings necessary for Serial1:

// Set up Serial1
  Serial1.setRxBufferSize(520); // Needed with higher baud rates. ASCII=520, RTU=260
  Serial1.begin(BaudRate, SERIAL_8N1, GPIO_NUM_32, GPIO_NUM_33);
  Serial1.setRxFIFOFull(1); // Most important!

You will need to set the buffer size only for higher baud rates and if you are expecting messages exceeding the UART FIFO size of 128 bytes. The values given are somewhat arbitrary - they should fit a complete message basically.
Note that the buffer size has to be set before the begin() call for the Serial!
After having started the Serial as usual, the third line is the most relevant one. This will have the UART copy every single byte into the UART buffer as soon as it shows up in the FIFO. Only that enables the RTU logic to take care of the timing.
Note that the FIFO threshold has to be set after the Serial has been started!

I got it working flawlessly up to the maximum possible baud rate of 5,000,000.

I've compiled my gateway using the Stream RTU branch and it looks promising:
grafik

The current error rate is zero, previously it was around 1.5%:
grafik

I'll check again tomorrow and report back. Thank you very much!

after ~24h, nearly 400K messages and an error rate below 0.1% I can say that it's fixed for me:

RTU Messages: 393799
RTU Errors: 275

Thanks again - hopefully it's merged soon

Sounds good - thanks for the report.

thx for putting in the effort, I am a bit pressed for time at the moment, I will have to look into this later on and get back to you

thx for putting in the effort, I am a bit pressed for time at the moment, I will have to look into this later on and get back to you

Take your time - I have no deadline to meet 😄

Changes have been incorporated into master and the last release.
Have a look at the release notes and/or docs: the API has been slightly adapted again!