CIRCUITSTATE / CSE_ModbusRTU

Arduino library for implementing Modbus RTU communication protocols using hardware/software serial ports.
MIT License
6 stars 1 forks source link

CSE_ModbusRTU::receive does not set RE #1

Closed chaotix- closed 10 months ago

chaotix- commented 10 months ago

CSE_ModbusRTU::receive does handle the RE pin. Just calling serialPort->receive() before and serialPort->noReceive() after receiving data should be sufficient. This call is necessary if a RE pin is specified for the RS485Class. It sets RE LOW to receive and HIGH to stop receiving. If no RE pin is set the call does nothing.

Just calling RS485.receive() before issuing e.g. a CSE_ModbusRTU_Client::readHoldingRegister call does not work as the sent request will be in the serial port queue if RE is LOW while the request is sent.

The fix (that works for me) is trivial:

--- CSE_ModbusRTU-0.0.6/src/CSE_ModbusRTU.cpp   2023-10-21 11:55:26.000000000 +0200
+++ CSE_ModbusRTU/src/CSE_ModbusRTU.cpp 2023-10-29 20:44:42.000000000 +0100
@@ -704,6 +704,8 @@ int CSE_ModbusRTU:: receive (CSE_ModbusR
   adu.resetLength(); // Reset the ADU length
   // MODBUS_DEBUG_SERIAL.print (F("receive(): Checking Modbus port.."));

+  // Set RE pin LOW (if specified for the serial port) to start receiving
+  serialPort->receive();
+
   uint32_t startTime = millis();

   while ((millis() - startTime) < timeout) {
@@ -713,6 +715,8 @@ int CSE_ModbusRTU:: receive (CSE_ModbusR
     }
   }

 +  // Set RE pin HIGH (if specified for the serial port) to stop listening to incoming data
+  serialPort->noReceive();
+
   // Print the ADU
   if (adu.getLength() > 0) {
     MODBUS_DEBUG_SERIAL.print (F("receive(): Received ADU:"));

As I don't use CSE_ModbusRTU_Server I don't know if this conflicts with this. If CSE_Modbus_RTU::receive() should receive data that was sent while this function is not active this would obviously not work. Then RE should be LOW all the time except when sending data. Then serialPort->receive() would need to be called when initializing and at the end of CSE_ModbusRTU::sent and serialPort->noReceive() at the beginning of CSE_ModbusRTU::sent.

An alternative approach would be to discard the first bytes of the received data after sending a request if the read data starts with the sent request.

vishnumaiea commented 10 months ago

You are right. It is something I have overlooked, probably because I was using the RS485 module with auto-direction control. Indeed, we have to assert the RE pin by calling serialPort->receive() before doing any read operations. For a client device who always initiates a request, this is not a problem. But for server, it can be a problem.

I have applied a few changes to the CSE_ModbusRTU library fix this issue.

  1. RE pin is now asserted when calling CSE_ModbusRTU::receive().
  2. Added new enableReceive() and disableReceive() functions to CSE_ModbusRTU class.
  3. Receiving is disabled with disableReceive() in CSE_ModbusRTU_Client:: receive() after a response is received or timeout occurs.

These changes will be available in version 0.0.7.

In the CSE_ArduinoRS485 library, I made the following changes.

  1. Added assertDE(), deassertDE(), assertRE(), deassertRE() functions to the RS485Class class. These functions will set the GPIO pins associated with DE and RE pins if they are defined in the port.

These changes will be available in 1.0.11.

Please update to the new versions when they are available, and let me know if it is working as expected.

chaotix- commented 10 months ago

The new versions work for me out of the box.

Thank you for your quick response.