Esp32 max485 modbus issue

Hi Guys,

I am using ModbusRTU library to establish a serial modbus connection using MAX485. The code is working fine and I can see the read holding register request as received on the Modbus simulator software but not able to print the response back on arduino.. I am trying to save the responsevalue in "res" and print it on the arduino. Can someone check and advise what I am missing?
Code as below:

#include <ModbusRTU.h>
ModbusRTU mb;
uint8_t slave = 1;
const int REG = 02;             
const int LOOP_COUNT = 10;
uint16_t res =1;
uint8_t show = LOOP_COUNT;

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 detected");
  Serial1.begin(115200, SERIAL_8N1, 26, 27);
  mb.begin(&Serial1);
  mb.master();
   if (!mb.slave()) {
     mb.readHreg(1, REG, &res);
 }
}
void loop() {
 mb.task();                      // Common local Modbus task
 delay(100);                     // Pulling interval
 if (!show--) {                   // Display Slave register value one time per second (with default settings)
   Serial.println(res);
   show = LOOP_COUNT;
 }
}

I am trigging DE and RE pins manually between 5V and GND while transmitting and receiving and hence they are not included in the code. Also using a USB to RS485 converter to connect it to the pc.

Modbus slave simulator software is MODRSSIM2
image

A link to that library would help!

I would say that this isn't possible as you're not fast enough. In you example session the answer is sent 45 seconds after the request. Every Modbus software I'm aware of timed out long before that.

@pylon I am using this library

How did you come up with 45 sec? I set the delay of 6 sec in the software and the difference between Tx and Rx is around 6 sec.

16:05:15 - 16:06:03 => 48 secs to be exact.

Can you post a wiring diagram of your setup?

You don't use the RS-485 version as you don't define a pin for the DE/RE. As the write seems to work you must have pulled the DE/RE pins high on the MAX458. That way you cannot receive the answer. So this is a hardware problem, not a software problem.

@pylon I tried the setup again by pulling the DE/RE through pin23 (high for transmitting and low for receiving) - still not able to receive any response. Have a look at the wiring diagram, A and B connects to RS485-USB converter.

I can also see the TX light blinking on the RS-485 to USB converter suggesting the response is sent by modbus - I am just not able to receive it.

Post the modified code!

Given you actually use a MAX485 you probably fried pin 26 of your ESP32 as MAX485 work on 5V and the ESP32 isn't 5V tolerant.

Hi @pylon , The issue was with the max485 IC, replacing the IC resolved the problem.

I am now able to read a single register but when i send multiple commands to read different registers, only first command is executed. Can you suggest how can I read multiple registers.

This is what i am doing to read multiple registers.

image

Thanks

Post the complete modified code!

Is this still asking the values from the PC Modbus simulation? Are you completely sure that the problem is not on the PC side?

Hi @pylon - here's the complete code. When i run the code, only first command (i.e. mb.readHreg(1,REG, &res) ) is executed and the remaining commands are ignored. I also tried using Delay in between the commands but didn't help.

#include <ModbusRTU.h>
ModbusRTU mb;
uint8_t slave = 1;
const int REG = 02;               // Modbus Hreg Offset +1
const int REG1 = 03;    
const int REG2 = 04; 
const int LOOP_COUNT = 10;
uint16_t res;
uint16_t res1;
uint16_t res2;
uint8_t show = LOOP_COUNT;

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 detected");
  Serial1.begin(115200, SERIAL_8N1, 26, 27);
  mb.begin(&Serial1);
  mb.master();
}

void loop() {
    if (!mb.slave()) {
         mb.readHreg(1, REG, &res); 
         mb.readHreg(1, REG1, &res1); 
         mb.readHreg(1, REG2, &res2);       
    }
    mb.task();                      
    delay(100);                     
    if (!show--) {                   
       show = LOOP_COUNT;
       Serial.println(res);
       Serial.println(res1);
       Serial.println(res2);
    }
}

Interesting that this code worked as it still doesn't handle the DE/RE pin. If that produces a result either the wiring diagram or the code is wrong.
And you still didn't tell us what chip you use instead of the MAX485 (which is only for 5V devices). In your wiring diagram, what does VCC stand for?

@pylon I removed the DE/RE pins as they are not required in the new module I am using for this project. The module is based on MAX485 but have a built-in voltage regulator and can work on 3.3V and 5V level. The module also handles DE/RE automatically and so I didn't define it in the code.

The code that I shared earlier is working fine. The issue I am getting is around the main loop. I should be reading the three registers in a loop but the response I am getting from the PC indicates that the first read register command is only executed in a loop and the other two requests are ignored. Tried using delay in between but that didn't help.

Post the schematics of this module. I'm not aware of a MAX485 based module that is able to work on both 5V and 3.3V.

Please provide enough data for use to check that. My guess is a timing issue on the emulator but that's wild guessing without having the necessary information.

Hi @pylon , this is the module I'm using. It works on both 3.3v and 5v.

https://www.phippselectronics.com/product/ttl-to-rs485-bi-directional-converter-module/?gclid=CjwKCAjwqcKFBhAhEiwAfEr7zUToRYe7FxOABD8NAd620y9BzWJCPh1ntbvDKjkZq0-6YcG33kogbRoCqVkQAvD_BwE

I thought it can be the timing issue, or the issue can be related to auto TX/RX conversion of the pin as no DE/RE pins are associated with the module - not sure how to solve the issue. The code above is the one I'm currently using.

@zohair7 code will not work as you assumes.
.readHreg() just sends request to slave and exits. Response is processed inside .task() as soon as data arrives (moreover .task() is also non-blocking and it just exits if no data to process). So, indeed only first read request are performed. REG1 and REG2 requests just to be ignored as but is busy by first one.
Simplest way is to implement blocking operation (you can get example at the GitHub repo). But you may facing loop stale (for 1 second) in case slave not respond.

@emelianov any suggestions on how to resolve the issue?
How would I implement the blocking operation?

Blocking way:

Non-blocking way:

Thanks @emelianov
I tried your example and it worked with the case/break implementation but I have to read multiple registers and so its more convenient to use a for loop. Please have a look at my code below, the for loop is running 10 times but only first command is being executed i.e. REG0. Can you please suggest any solution?

#include <ModbusRTU.h>
ModbusRTU mb;

const int REG[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8 ,9 };               // Modbus Hreg Offset +1
uint16_t res[10];

bool cbWrite(Modbus::ResultCode event, uint16_t transactionId, void* data) { // Callback to monitor errors
  if (event != Modbus::EX_SUCCESS) {
    Serial.print("Request result: 0x");
    Serial.print(event, HEX);
  }
  return true;
}

void setup() {
  Serial.begin(115200);
  Serial.println("ESP32 detected");
  Serial1.begin(115200, SERIAL_8N1, 26, 27);
  mb.begin(&Serial1);
  mb.master();
}

void loop() {  
    if (mb.slave()==false){
      for(int i = 0; i <= 9; i++) {  
      mb.readHreg(1, REG[i], &res[i], 1, cbWrite); 
      Serial.print("read register..........................:");
      Serial.println(REG[i]); 
      Serial.println(res[i]);
      }
    }
    mb.task(); 
    yield();
}         

I suggest something like (just note comments about value print)

int i = 0;
void loop() {  
    if (!mb.slave()) {
      mb.readHreg(1, REG[i], &res[i], 1, cbWrite); 
      i++;
      if (I >= 10)
        i = 0;
      Serial.print("read register..........................:");
      Serial.println(REG[i]);
      Serial.println(res[i]); // Indeed old value is here as response for last request is not arrive yet
    }
    mb.task(); 
    yield();
}      

Thanks @emelianov. This solved the issue, I am now able to read and write multiple registers.

Is there any support in the library to add the CRC verification code while reading and writing Registers?

All the Modbus operations are protected by CRC out-of-box as it's part of Modbus specification.