Can't read holding registers

I am getting a strange result when trying to read holdingregister(s):
I am using the smarmengol Modbus library (GitHub - smarmengol/Modbus-Master-Slave-for-Arduino: Modbus Master-Slave library for Arduino)
and the modbus-tools slave simulator (Download)
and trying to send a read holding registers message with a slightly modified "simple_master" example.

I am using an Arduino mega connected via USB to RS485 and RS485 to TTL MAX485.
I know the setup is correct because I have successfully made the Mega a slave and read holding registers correctly using QModBus.

Here is my code:

/**
 *  Modbus master example 1:
 *  The purpose of this example is to query an array of data
 *  from an external Modbus slave device. 
 *  The link media can be USB or RS232.
 *
 *  Recommended Modbus slave: 
 *  diagslave http://www.modbusdriver.com/diagslave.html
 *
 *  In a Linux box, run 
 *  "./diagslave /dev/ttyUSB0 -b 19200 -d 8 -s 1 -p none -m rtu -a 1"
 * 	This is:
 * 		serial port /dev/ttyUSB0 at 19200 baud 8N1
 *		RTU mode and address @1
 */

#include <ModbusRtu.h>

// data array for modbus network sharing
uint16_t au16data[16];
uint8_t u8state;

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  port : serial port
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus master(0,Serial,37); // this is master and RS-232 or USB-FTDI

/**
 * This is an structer which contains a query to an slave device
 */
modbus_t telegram;

unsigned long u32wait;

void setup() {
  Serial.begin( 19200 ); // baud-rate at 19200
  master.start();
  master.setTimeOut( 2000 ); // if there is no answer in 2000 ms, roll over
  u32wait = millis() + 1000;
  u8state = 0; 
}

void loop() {
  switch( u8state ) {
  case 0: 
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1: 
    telegram.u8id = 1; // slave address
    telegram.u8fct = 3; // function code (this one is registers read)
    telegram.u16RegAdd = 0; // start address in slave
    telegram.u16CoilsNo = 1; // number of elements (coils or registers) to read
    telegram.au16reg = au16data; // pointer to a memory array in the Arduino

    master.query( telegram ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) {
      u8state = 0;
      u32wait = millis() + 1000; 
    }
    break;
  }
}

Here is my slave setup:
image
image

And here is what I'm reading in the serial monitor:

Help would be much appreciated- Einar.

...is troublesome.

I assume that means you were expecting something different. What were you expecting?

It's my understanding that RTU is a binary protocol. Is that your understanding?

Hi Bad_Coder, thanks for your reply.
I should have mentioned, I am a N00B with a big N; so be prepared for many questions.

Why is the millis part troublesome? I do not actually know what this part does as there is no description in the example...

What I was expecting was a Modbus message with the correct data in the data section of the message... or just a print of the data section.

It's my understanding that RTU is a binary protocol. Is that your understanding?

My understanding is that RTU is a hexadecimal protocol. Is this wrong?

-Einar

Yes.

That's what you see in the Serial Monitor. The problem is that human beings are bad in reading binary data.

Because u32wait is only 32bits wide so it will overrun after a bit more than a month. Your test sketch might never run that long but the code isn't clever nevertheless.

But Modbus messages are in hex, so how come RTU is not a hex protocol?

That's what you see in the Serial Monitor. The problem is that human beings are bad in reading binary data.

So where would I read the actual return message? Is there a bit of code that I should be doing Serial.print with?

Because u32wait is only 32bits wide so it will overrun after a bit more than a month. Your test sketch might never run that long but the code isn't clever nevertheless.

hmm ok, how would I avoid this?

Modbus RTU messages are binary. You might think about Modbus ASCII but that's almost nowhere in use.

A bad idea because you use the first serial interface for the Modbus communication. As you use a Mega you should change the Modbus interface to one of the other serial interfaces.
Then you can print the details of the au16data array to get the response data of the Modbus request.
The ModbusRTU library is a good choice if you have to implement a Modbus slave or if you must do a combined sketch (master and slave) but for a master only implementation libraries like ModbusMaster are easier to use and understand.

For example by using the variable to store the value of millis() and than check the wait time by

uint32_t u32last_call = millis();
...
#define WAIT_TIME 1000
if (millis() - u32last_call > WAIT_TIME) {
...
}

Edit: Fixed time period calculation in last pseudo code snippet.

1 Like

Modbus RTU messages are binary. You might think about Modbus ASCII but that's almost nowhere in use.

How come then when I read holding registers via QModBus the response messages are in hex?:
image

Yes, I have thought about changing the serial interface, however when I do this it stops working (for instance from Serial to Serial1). At least this happens when the Mega is a slave.

Then you can print the details of the au16data array to get the response data of the Modbus request.

So how would I choose which register the data is stored in since I want to be able to both write to specific registers and store incoming modbus message data in specific registers. I know how to assign registers values btw; for instance I have made a setup with a temp sensor value that goes into a register and then I read the value with QModBus.

The ModbusRTU library is a good choice if you have to implement a Modbus slave or if you must do a combined sketch (master and slave) but for a master only implementation libraries like ModbusMaster are easier to use and understand.

I am planning on having a combined sketch and was recommended this library by someone who is very familiar with Modbus professionally.

What exactly does millis() do/mean?

Because QModBus converts the binary data into a form legible to humans.

I can't help with the rest.

Because the programmer of that application knew that human cannot read binary data and therefore displayed the binary data in hex format.

In this case you haven't done it correctly. Post the code you used!

This sentence is for a slave. Are we still talking about a master?

I have done that but I also know that such cases are extremely seldom. Most cases told here in the forum ended in a wrong understanding of Modbus and how the master/slave role is differentiated. Please explain what real live usage you think about.

It returns the milliseconds since the startup of the Arduino.

In this case you haven't done it correctly. Post the code you used!

Sure thing!:

/**
 *  Modbus slave example 3:
 *  The purpose of this example is to link a data array
 *  from the Arduino to an external device through RS485.
 *
 *  Recommended Modbus Master: QModbus
 *  http://qmodbus.sourceforge.net/
 */

#include <ModbusRtu.h>

// assign the Arduino pin that must be connected to RE-DE RS485 transceiver
#define TXEN 37 

// data array for modbus network sharing
uint16_t au16data[16] = {
  3, 1415, 9265, 4, 2, 7182, 28182, 8, 0, 0, 0, 0, 0, 0, 1, -1 };

/**
 *  Modbus object declaration
 *  u8id : node id = 0 for master, = 1..247 for slave
 *  port : serial port
 *  u8txenpin : 0 for RS-232 and USB-FTDI 
 *               or any pin number > 1 for RS-485
 */
Modbus slave(1,Serial1,TXEN); // this is slave @1 and RS-485

void setup() {
  Serial.begin( 19200 ); // baud-rate at 19200
  slave.start();
}

void loop() {
  slave.poll( au16data, 16 );
}

The original example is Serial but I have mine as Serial1. I have also changed the TXEN pin to the one that I use.

This sentence is for a slave. Are we still talking about a master?

I am referring to a master. Above you said that I should be able to view the incoming messages in the au16data (which I understand to be the registers in the Mega). So therefore I thought that the Mega would use the same registers to write and receive data while acting as both a master and slave.

Please explain what real live usage you think about.

What my real life application is is this: I want software on my computer to send a value every 2 seconds (or similar time) into a register in the Mega (slave). This value will be the "desired value" in a PID control system.
Then I want the Mega to act as a master and control a variable frequency driver to power a fan via Modbus.
This needs to be done with Modbus as these kinds of softwares (coffee roasting) use the Modbus standard and so does the variable frequency driver.

It returns the milliseconds since the startup of the Arduino.

So it tells how much time has passed since the Arduino started?
So then the issue was that this value will become so large that it is larger than 32 bits (?).
What does your bit of code do and how does it solve this?

You must set the baud rate for the correct serial interface, Serial1 in your case.

Not really. The array simply contains the returned values from the slave. Usually that are values of registers but the registers of your slave emulator, the master doesn't have registers.
On the slave the equally named array is holding the register values but a slave have registers while the master doesn't have them.

OK, in your case the hybrid setup makes sense and ModbusRtu is probably the best choice.

Yes.

Yes.

It saves the value of millis() in the unsigned 32bit integer variable. To check if some time period has past it subtracts that variable from the current value of millis() and compares the result to the time frame. That way if millis() runs over the max. value for 32bit integers it will still return the time that past since the value was stored in the variable. That's because for unsigned integers the result of calculation 5 - 8 will not be -3 (no signature bit available) but a value 2 smaller than the maximum value of the variable type. For a byte (8 bit) this would be 253.

1 Like

You must set the baud rate for the correct serial interface, Serial1 in your case.

Ok, this makes a lot of sense.

Not really. The array simply contains the returned values from the slave. Usually that are values of registers but the registers of your slave emulator, the master doesn't have registers.
On the slave the equally named array is holding the register values but a slave have registers while the master doesn't have them.

I am aware that the master has no registers and that the slave does. But since I am planning on a duel/hybrid setup, how does this work?

I also have a more general question: how did you learn all of these Modbus library commands?
This knowledge seems very esoteric to me right now.
Is it because all Modbus libraries use the same or similar functions?
If so is there some good site that explains these functions?

-Einar

I am now also successfully reading registers from the Modbus Slave simulator by

  1. using Serial3 for the Modbus communication
    and
  2. by printing au16data[0] with Serial.

Thanks for the help with this Pylon! :slight_smile:

I built my own Smart Home system on Modbus and learnt it the hard way. :slight_smile:

No, definitely not. I think they are quite different, so choosing the right one isn't easy.

I read the specification from modbus.org. It's not really a text for beginners but at least it defines the standard quite well. It doesn't help that many implementations (mostly in the PLC world) have a rather exotic interpretation of the standard.

I'm also want read holding register using RS485 and print on serial in ESP32 using same this code. but on serial non readable garbage value printing. please help me in this.

"same this code". Bad idea as this code was for a Mega2560.

Post your code and post a wiring diagram of your setup. RS485 with an ESP32 isn't that easy because 3V3 capable RS485 modules are seldom. Post links to all your hardware!


this is circuit diagram. ==>
here I use RS485 TTL convertor, ESP32.

and this is my code ==>

/*
 * Modbus Master Reading Code 
 * 
 * Designed and Developed By Parth Yatin Temkar
 */

#include <Arduino.h>
#include <HardwareSerial.h>


'// Include the header for the ModbusClient RTU style'
#include "ModbusClientRTU.h"

// For demonstration, use the LOG statements for output'
#include "Logging.h"

uint16_t counter = 0;
HardwareSerial modbusSerial(1);

#define MB_RO   16
#define MB_DI   17
#define MB_REDE 18

// Create a ModbusRTU client instance
// In my case the RS485 module had auto halfduplex, so no second parameter with the DE/RE pin is required!
ModbusClientRTU MB(modbusSerial, MB_REDE);


// Function Prototype
boolean runEvery(unsigned long interval);


// Define an onData handler function to receive the regular responses
// Arguments are Modbus server ID, the function code requested, the message data and length of it,
// plus a user-supplied token to identify the causing request
void handleData(ModbusMessage response, uint32_t token)
{
  uint16_t dataArray[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  // LOG_N("Response: serverID=%d, FC=%d, Token=%08X, length=%d:\n", response.getServerID(), response.getFunctionCode(), token, response.size());
  // HEXDUMP_N("Data", response.data(), response.size());
  response.get(3, dataArray[0]);
  response.get(5, dataArray[1]);
  response.get(7, dataArray[2]);
  response.get(9, dataArray[3]);
  response.get(11, dataArray[4]);
  response.get(13, dataArray[5]);
  response.get(15, dataArray[6]);
  response.get(17, dataArray[7]);
  response.get(19, dataArray[8]);
  response.get(21, dataArray[9]);

  Serial.printf("Data[0]:%d | Data[1]:%d | Data[2]:%d | Data[3]:%d | Data[4]:%d | Data[5]:%d | Data[6]:%d | Data[7]:%d | Data[8]:%d | Data[9]:%d\n", dataArray[0], dataArray[1], dataArray[2], dataArray[3], dataArray[4], dataArray[5], dataArray[6], dataArray[7], dataArray[8], dataArray[9]);
}

// Define an onError handler function to receive error responses
// Arguments are the error code returned and a user-supplied token to identify the causing request
void handleError(Error error, uint32_t token)
{
  // ModbusError wraps the error code and provides a readable error message for it
  ModbusError me(error);
  LOG_E("Error response: %02X - %s\n", (int)me, (const char *)me);
}

// A helper function to print out the messages
void printMsg(ModbusMessage msg) {
  Serial.printf("Message size: %d\n", msg.size());
  for (auto& byte : msg) {
    Serial.printf("%02X ", byte);
  }
  if (msg.getError() != SUCCESS) {
    Serial.printf("Is an error message: %02d - %s\n", msg.getError(), (const char *)ModbusError(msg.getError()));
  }
  Serial.println("\n");
}

// Setup() - initialization happens here
void setup() {
  // Init Serial monitor
  Serial.begin(9600);
  while (!Serial) {}

  pinMode(GPIO_NUM_0, INPUT);

  modbusSerial.begin(9600, SERIAL_8N1, MB_RO, MB_DI);
  // Set up ModbusRTU client.
  // - provide onData handler function
  MB.onDataHandler(&handleData);
  // - provide onError handler function
  MB.onErrorHandler(&handleError);
  // Set message timeout to 2000ms
  MB.setTimeout(2000);
  // Start ModbusRTU background task
  MB.begin();

}

// loop() - nothing done here today!
void loop() {
  uint32_t Token = 1111;
  Error err;
  
  if (runEvery(500))
  {
    err = MB.addRequest(Token++, 100, READ_HOLD_REGISTER, 0, 10);
    if (err != SUCCESS) {
      ModbusError e(err);
      Serial.printf("Error creating request: %02X - %s\n", (int)e, (const char *)e);
    }
  }


  
//  if (!digitalRead(GPIO_NUM_0))
//  {
//    counter++;
//    err = MB.addRequest(counter, 100, WRITE_HOLD_REGISTER, 6, counter);
//    if (err != SUCCESS) {
//      ModbusError e(err);
//      Serial.printf("Error creating request: %02X - %s\n", (int)e, (const char *)e);
//    }
//    delay(150); // Debounce
//    Serial.println(counter);
//  }
}


boolean runEvery(unsigned long interval)
{
  static unsigned long previousMillis = 0;
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval)
  {
    previousMillis = currentMillis;
    return true;
  }
  return false;
}

You might already have fried your RX pin on the ESP as the datasheet says it's not 5V tolerant. You must use a level converter to use a 5V RS-485 adapter with the ESP.

Your code has nothing in common with the code posted earlier in the thread. Why didn't you open a new thread?

Edit your post and insert code tags! Without code tags the code is unreadable and the forum system mangles it.

I don't recommend to use an asynchronous library if you're not very experienced using Modbus and concurrent programming.

Sorry sir I'm new here so don't know about that how to open new thread here so I post my code in comments. so you mean I need to use 3.3v for RS-485. how can I check that which library is synchronous or asynchronous. I'm beginner so please help me.

Modbus isn't a beginner's project. You probably should start with easier things to get comfortable with the IDE, Arduino programming in general and get some electronics experience.

You need to find an RS-485 adapter that runs on 3.3V or you use a 5V adapter and a level converter in between.

It's not always that simple but usually asynchronous libraries use callback routines. The onDataHandler() and onErrorHandler() calls set such callbacks. Programming with such asynchronous libraries expect a thorough understanding of the used principles and implications. It's definitely nothing for beginners.

What Modbus device are you trying to read?