Skip to content

MODBUS communication randomly broken due to misbehaving Serial.flush() function #5877

Closed
@garageeks

Description

@garageeks

Using arduino-esp32 1.0.6, the library ModbusMaster (https://github.com/4-20ma/ModbusMaster) works as intended.
Using arduino-esp32 2.0.0 or 2.0.1 it randomly stops communicating with the slave Modbus device.

I investigated with a scope and these are my findings:
RED LINE = TX signal, BLUE LINE = TX enable signal (tied to DE/NOT RE pins)

  1. Succesful transmission - the device replies as expected (Not seen here as I only have two channels)
    picoscope-success

  2. First failed transmission - enable signal stays high for a long time, preventing reception of the device answer
    picoscope-firstfail

  3. Subsequent failed transmission - enable signal is prematurely turned to LOW. The time it stays HIGH varies.
    picoscope-fail1
    picoscope-fail2
    picoscope-fail3

The mentioned library, when a packet is transmitted, turns HIGH the enable signal with preTransmission callback, write data to the serial port, flushes the serial port and then turns the enable signal LOW. Pretty straightforward. I assume Serial TX is buffered, then Serial.flush takes its time until the buffer is empty.

modbus-fail

Given the recent big overhaul of HardwareSerial and some other issues, I think there is a bug in the new implementation with 2.0.0 and 2.0.1 releases.

This is similar to #4603

Activity

SuGlider

SuGlider commented on Nov 12, 2021

@SuGlider
Collaborator

@garageeks
In order to assist you could you please run this code in order to get more information about your board and development environment. If possible, run this code using core 1.0.6, 2.0.0 and 2.0.1 from your main development environment and report the result back here.

  Serial.printf("Internal Total heap %d, internal Free Heap %d\n", ESP.getHeapSize(), ESP.getFreeHeap());
  Serial.printf("SPIRam Total heap %d, SPIRam Free Heap %d\n", ESP.getPsramSize(), ESP.getFreePsram());
  Serial.printf("ChipRevision %d, Cpu Freq %d, SDK Version %s\n", ESP.getChipRevision(), ESP.getCpuFreqMHz(), ESP.getSdkVersion());
  Serial.printf("Flash Size %d, Flash Speed %d\n", ESP.getFlashChipSize(), ESP.getFlashChipSpeed());
self-assigned this
on Nov 12, 2021
garageeks

garageeks commented on Nov 12, 2021

@garageeks
Author

Hi @SuGlider , shall I run the code standalone or while running my MODBUS application?

I always print the free heap to detect potential memory leaks, so I have already some information from my application:
on 1.0.6 I get Free heap: 247744 - Max block: 113792
on 2.0.0 I get Free heap: 181063 - Max block: 65524

The ESP32 module I'm using is ESP32-WROOM-32E

PS: on 2.0.0 free heap and max block value is basically the same when MODBUS is working and when it stops working
PS2: the code is not exactly identical, but the code compiled on 1.0.6 has more features compared to 2.0.0. I can compare same code and report.

SuGlider

SuGlider commented on Nov 12, 2021

@SuGlider
Collaborator

@garageeks
You can run it stand alone with 2.0.x.

garageeks

garageeks commented on Nov 12, 2021

@garageeks
Author

Sorry, I forgot my programmer at home, I could only flash new firmware by OTA. Here's the results from within my application.

1.0.6 - MODBUS works non-stop for 24+hrs
Internal Total heap 347228, internal Free Heap 249100
SPIRam Total heap 0, SPIRam Free Heap 0
ChipRevision 3, Cpu Freq 240, SDK Version v3.3.5-1-g85c43024c
Flash Size 16777216, Flash Speed 40000000

2.0.0 - MODBUS works but fails after a while
Internal Total heap 347091, internal Free Heap 283303
SPIRam Total heap 0, SPIRam Free Heap 0
ChipRevision 3, Cpu Freq 240, SDK Version v4.4-dev-2313-gc69f0ec32
Flash Size 16777216, Flash Speed 40000000

2.0.1 - MODBUS doesn't work at all from the start
Internal Total heap 310792, internal Free Heap 225908
SPIRam Total heap 0, SPIRam Free Heap 0
ChipRevision 3, Cpu Freq 240, SDK Version v4.4-dev-3569-g6a7d83af19-dirty
Flash Size 16777216, Flash Speed 40000000

SuGlider

SuGlider commented on Nov 13, 2021

@SuGlider
Collaborator

@garageeks
Please try this example with Core 2.0.0+
You can change UART port or Modbus slave ID as needed.

Please let me know if uart_set_mode(UART_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX) solves this issue.

#include <ModbusMaster.h>

// necessary to define UART_MODE_RS485_HALF_DUPLEX
#include <driver/uart.h>


// instantiate ModbusMaster object
ModbusMaster node;

void setup()
{
  // use Serial (port 0); initialize Modbus communication baud rate
  Serial.begin(19200);

  // communicate with Modbus slave ID 2 over Serial (port 0)
  node.begin(2, Serial);

  // Set RS485 half duplex mode on UART_0.  This shall force flush to wait up to sending all bits out
  ESP_ERROR_CHECK(uart_set_mode(0, UART_MODE_RS485_HALF_DUPLEX));
}
garageeks

garageeks commented on Nov 16, 2021

@garageeks
Author

Dear @SuGlider ,
In my application I added your suggestion as follows

node.begin(1, Serial2);
ESP_ERROR_CHECK(uart_set_mode(UART_NUM_2,UART_MODE_RS485_HALF_DUPLEX));

It didn't accept an integer as first parameter of the function. I assumed UART_NUM_2 is the valid one for Serial2.

However on 2.0.1 it still doesn't communicate with the MODBUS device.

ignasurba

ignasurba commented on Dec 3, 2021

@ignasurba

I can confirm this is an issue.

image

Part of the packet is sent after the flush returns and DE is disabled.

This worked for me:
ESP_ERROR_CHECK(uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX));
(using Serial1)

garageeks

garageeks commented on Dec 3, 2021

@garageeks
Author

Hi @ignasurba thank you for your feedback. I have tried it based on @SuGlider suggestion but didn't work. I can give it another try.
Where did you place this instruction? After the node.begin instruction?

ignasurba

ignasurba commented on Dec 5, 2021

@ignasurba

It looks like this:

  Serial1.begin(MODBUS_BAUDRATE, SERIAL_8N1, MODBUS_RX, MODBUS_TX);
  node.begin(1, Serial1);
  node.preTransmission(modbus_pre);
  node.postTransmission(modbus_post);
  ESP_ERROR_CHECK(uart_set_mode(UART_NUM_1, UART_MODE_RS485_HALF_DUPLEX));

Seems to work so far without issues.

garageeks

garageeks commented on Dec 6, 2021

@garageeks
Author

I replaced Serial2 for Serial1 and UART_NUM_2 for UART_NUM_1 and it works!

So apparently there is some other bug with Serial2 / UART_NUM_2 combination.

The previous peripheral on Serial1 is happy to work on Serial2, so I can leave my test bench running for a while and see its stability.

This code doesn't work:

	Serial2.begin(115200, SERIAL_8N1, 16, 17);		//Serial2 MODBUS interface
	node.begin(1, Serial2);
	// Callbacks 
	node.preTransmission(preTransmission);
	node.postTransmission(postTransmission);
	ESP_ERROR_CHECK(uart_set_mode(UART_NUM_2,UART_MODE_RS485_HALF_DUPLEX));
mfriedlvaricon

mfriedlvaricon commented on Dec 7, 2021

@mfriedlvaricon

Hello!

We have the same issue... using Serial2.flush randomly returns inb4 all the information has been sent. This results in a massive loss of data and a non working product...
In the first picture you can see a valid output (Red line is basically when Serial.flush completes)
In the second picture flush gets called way to early and actually destroys the message.
CONS3-Busfehler - kein Abbruch_modifiziertMF

CONS3-Busfehler Abbruch nach 2,5ms mit DE vom ESP32

All of this happens totally randomly. Sometimes it works, sometimes it keeps failing.
(EO = EnableOutput, DO = DisableOutput)
grafik

Hope this gets fixed soon, as our products are relying on this...

Thanks!
Markus

8 remaining items

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions

    MODBUS communication randomly broken due to misbehaving Serial.flush() function · Issue #5877 · espressif/arduino-esp32