I can´t read data with Modbusrtu.h

Hi everybody.

I am using arduino MEGA. I haved implemented the modbusrtu.h library.

I configure the arduino as Master, to communicate it with an speed drive being slave, by RS485 with the module MAX485.

I haved success to write data to the drive… but i can´t read… I received values 0(cero) instead of the correct data. I am reading in au16data[0]

Somebody can help me? I don´t know if i being something wrong.

This is my code

#include <ModbusRtu.h>

uint16_t au16data[16]; //!< data array for modbus network sharing
uint8_t u8state; //!< machine state
uint8_t u8query; //!< pointer to message query

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

/**
This is an structe which contains a query to an slave device
*/
modbus_t telegram[2];

unsigned long u32wait;

void setup() {
  // telegram 0: read registers
  telegram[0].u8id = 1; // slave address
  telegram[0].u8fct = 3; // function code (this one is registers read)
  telegram[0].u16RegAdd = 680;// start address in slave
  telegram[0].u16CoilsNo = 1; // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data;
  // pointer to a memory array in the Arduino
  
  // telegram 1: write a single register
  telegram[1].u8id = 1; // slave address
  telegram[1].u8fct = 16; // function code (this one is write a multiple register)
  telegram[1].u16RegAdd = 682; // start address in slave
  telegram[1].u16CoilsNo = 2; // number of elements (coils or registers) to write
  telegram[1].au16reg = au16data+4; // pointer to a memory array in the Arduino*/
  
  master.begin( 38400 ); // baud-rate at 19200
  master.setTimeOut( 10000 ); // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000; //polling cada 1 segundo
  u8state = u8query = 0;
  
  Serial.begin(19200);
  Serial.setTimeout(1000);
  

}

void loop() {
  int i;
  switch ( u8state ) {
  case 0:
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1:
    master.query( telegram[u8query] ); // send query (only once)
    u8state++;
    u8query++;
    if (u8query > 1) u8query = 0;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) 
    {
        Serial.print("STATE:");
        Serial.println(master.getState());
        Serial.print("ERROR:");
        Serial.println(master.getLastError());
        Serial.print("DATA READ:");
        Serial.print(au16data[0]);
        Serial.println("");
        u32wait = millis() + 1000;
        u8state = 0;
    }
    break;
  }
  
  /* else
  digitalWrite(12,LOW);*/
  
  au16data[4] = 7;
  au16data[5] = analogRead( 0 )*16.117;
  //
}

Problem #1

u32wait = millis() + 1000;

Problem #2

millis() > u32wait

Swing by the millis() programming thread.

If this is the ModbusRTU from smarmengol, then the millis problem exists throughout the library as well.

Problem #3

u8query++;
if (u8query > 2) u8query = 0;

because you only have telegram 0 and 1 defined. At some point u8query will be equal to 2, before u8query resets.

Problem #4

for (i=0;i<2;i++)
Serial.print(au16data);

What do you think is being printed? Hint: How is the printed variable dependent on the loop variable ‘i’?

It may be possible that it is reading, and only problem #4 is preventing you from displaying the data. But problems #1, 2, 3 are legitimate issues that need to be addressed and corrected.

Update the above and then report back.

Thanks for your quick Reply!

Are you sure that changing this, the problem will be solved? Because i can write to the device...

I changed the millis() problema in my app and the problem continue..

I am going to change the mills() in the library also.

Have you got this Library modifed with this issue?

I find very difficult to modify it.

Thanks

elvirasoft:
Thanks for your quick Reply!

Are you sure that changing this, the problem will be solved? Because i can write to the device...

I changed the millis() problema in my app and the problem continue..

I am going to change the mills() in the library also.

You have corrected all four issues, especially #4? Can you please post your new program (using code tags, see how to use this forum thread).

The improper millis() usage is not breaking the program completely. That only causes intermittent problems. Best to correct it now as it will cause occasional problems.

I do not have a current working library for others to use, as I am in the middle of making and testing several other changes.

Your code sends two queries without waiting for the response of the first query. As the first query is the read query you'll probably only see the results of the second query (poll() method is not called).
Change your code to make the first query, poll until you got the answer from the slave and then send the second query to write.

telegram[1].u8fct = 16; // function code (this one is write a single register)

It's very bad style to have descriptions in the comments that aren't true.

@adwsystems & @pylon

I have cleaned my code to be more precise.

I only put the Read Query (answering to @pylon) and correct #4 (answering to @adwsystem)

I am continue reading a wrong value (0- Cero value) in the au16data[0] register, where would be the read data that is 1702H from the slave device.

I am sure that i am connected, because i can write a value with a query with a Function 16 to the adds 682 and 683 of the slave.

Answering to @adwsystem its true the problem of millis(), because i can see with a modbus slave simulator (diagslave.exe), that there is no repetive in time, but the FC 03 arrive.

I don´t know where is my problem.

Here is my code

#include <ModbusRtu.h>

uint16_t au16data[16]; //!< data array for modbus network sharing
uint8_t u8state; //!< machine state
/**
Modbus object declaration
u8id : node id = 0 for master, = 1..247 for slave
u8serno : serial port (use 0 for Serial)
u8txenpin : 0 for RS-232 and USB-FTDI
or any pin number > 1 for RS-485
*/
Modbus master(0, 1, 2); // this is master and RS-485 in serial port 1 with a MAX485 with pin 2 to change between TX and RX
/*
This is an structe which contains a query to an slave device */
modbus_t telegram[1];
unsigned long u32wait;

void setup() {

  telegram[0].u8id = 1;           // slave address
  telegram[0].u8fct = 3;          // function code (this one is registers read)
  telegram[0].u16RegAdd = 680;    // start address in slave
  telegram[0].u16CoilsNo = 1;     // number of elements (coils or registers) to read
  telegram[0].au16reg = au16data;
  
  master.begin( 38400 );      // baud-rate at 38400
  master.setTimeOut( 5000 );  // if there is no answer in 5000 ms, roll over
  u32wait = millis() + 1000;  // wait time 1s.
  u8state = 0;
  
  Serial.begin(19200);      //Serial port connected to PC fro debbuging
  Serial.setTimeout(1000);
  
}

void loop() {
  switch ( u8state ) {
  case 0:
    if (millis() > u32wait) u8state++; // wait state
    break;
  case 1:
    master.query( telegram[0] ); // send query (only once)
    u8state++;
    break;
  case 2:
    master.poll(); // check incoming messages
    if (master.getState() == COM_IDLE) 
    {
        Serial.print("STATE:");
        Serial.println(master.getState());
        Serial.print("ERROR:");
        Serial.println(master.getLastError());
        Serial.print("READ DATA:");
        Serial.println(au16data[0]);
        u32wait = millis() + 1000;
        u8state = 0;
    }
    break;
  }
}

Attached is a print screen of the serial monitor … you can see in READ DATA: 0 (cero)

Eventually you will need to workout the millis() problem. For now we can put that aside.

What do you have connected to DI2? Please post a picture of the connections between the MAX485 and the Arduino.

The correct answer to problem #4 is

for (i=0;i<2;i++)
         { Serial.print(au16data[i]); } // just needed to add the index

I also suggest to always use the curly braces. If you add a second line, it is easy to not realize you are missing the braces. Then you will spend hours trying to figure out why only the first line is conditional.

Yes, I correct that…

In the D2, I have connected the DE & RE of the module.

Image is attached… I also have the terminal resistors of 120ohm in boths extremes.

Remember that i already writting to the device…

Can you read any registers?

nothing... always 0(cero) in the au16data[0]...

I was wondering if the rxbuffer really pass the information to the .au16reg of the telegram...

Could be a problem of pointers?

I haved tested everything...

Something interesting is, if i change to the serial port 0 connected to the pc with the simulator... this tell me that i have a readholding registers....

Do you know any modbus slave simulator... where i can write registers in the simulator, to being read with my master arduino?

pylon:
Your code sends two queries without waiting for the response of the first query. As the first query is the read query you'll probably only see the results of the second query (poll() method is not called).
Change your code to make the first query, poll until you got the answer from the slave and then send the second query to write.

telegram[1].u8fct = 16; // function code (this one is write a single register)

It's very bad style to have descriptions in the comments that aren't true.

I have encountered the problem, and is like said @pylon. I am only see the results of the second query. And also is the problems of millis... it's more evident for the read method.

I am going to work a little more on these problems.

Thanks

elvirasoft:
Do you know any modbus slave simulator... where i can write registers in the simulator, to being read with my master arduino?

You could build one with a second MEGA. When I started working with this library I purchased two Arduino MEGAs so I could use serial0 for the PC and serial1 for modbus on both. Works well.

elvirasoft:
I have encountered the problem, and is like said @pylon. I am only see the results of the second query. And also is the problems of millis... it's more evident for the read method.

I am going to work a little more on these problems.

Thanks

But your latest program contains only the one read telegram.

    master.poll(); // check incoming messages

You should check the return value of master.poll()!

I am sure that i am connected, because i can write a value with a query with a Function 16 to the adds 682 and 683 of the slave.

How do you know that the values got written?

Do you read a register that have a value known to you? Is not possible that the register actually holds 0 as value?

Pylon,

I can read and write now...but... As you said, with only one query at the same time.

The problem is when tent to do two o more querys... i am studing how.. but i don´t know how to wait in the poll() function.

The returned value of the poll function is u8BufferSize.. and you have also the functions getState() getLastError() and others... to combinate them. I am studing them.

Could you help me with that?

Keep in mind I have made a lot of modifications to the library and to the example code.

This is the code I have running.

  switch( modbus_step )
   {
    case 0:  // wait state
           if (current_time - u32wait > 500)
            {
             select_telegram();
             if (telegram.u8id == 0)
              { u32wait = current_time; }
              else
               { modbus_step++; }
            }
          break;
    case 1:  // send query (only once)
           master.query( telegram );
           modbus_step++;
          break;
    case 2: // check incoming messages
           if (master.poll() > 0) // response received
            {
             serial_print_response();
             Serial.println("Modbus Array Values");
             for (x=0; x<sizeof(modbus_register.au16data)/sizeof(uint16_t); x++)
              {
               Serial.print(x);
               Serial.print(":");
               Serial.print(modbus_register.au16data[x]);
               Serial.print(" ");
              }
             Serial.println();
            }
           if (master.getState() == COM_IDLE)
            {
             if (master.getLastError() == NO_REPLY)
              {
               bitClear(modbus_register.Panel_Comm_Status, telegram.u8id);
               slave_retry_time[telegram.u8id] = (unsigned long)current_time + (unsigned long)telegram.u8id * (unsigned long)random(1,51) * 100UL;
               Serial.print("Comm Error Slave ");
               Serial.println(telegram.u8id);
               Serial.print("Retry Time ");
               Serial.print(slave_retry_time[telegram.u8id]);
               Serial.print("  Current Time ");
               Serial.println(current_time);
              }
              else 
              { bitSet(modbus_register.Panel_Comm_Status, telegram.u8id); }
             modbus_step = 0;
             u32wait = current_time;
            }
          break;
   }

I’m not sure if this will help you or not, but I have provided it anyway. You will need to correlate this code to what you are using. Don’t expect to be able to use this code directly, but take from it to use in your program.

adwsystem,

Thank you for your code. But as I see that you implemented somes functions to read more than one Slaves.. i and you have functions there that you didn´t pass me.

What do you do inside the function select_telegram for example?

Althoutgh i am trying to understand what you meaning. But i am continue having the same problem with the two querys..

elvirasoft:
Pylon,

I can read and write now...but... As you said, with only one query at the same time.

The problem is when tent to do two o more querys... i am studing how.. but i don´t know how to wait in the poll() function.

The returned value of the poll function is u8BufferSize.. and you have also the functions getState() getLastError() and others... to combinate them. I am studing them.

Could you help me with that?

We can't help unless you post your latest program.

Reread the modbus master poll() function, it does not only return u8BufferSize value. There are many other return values. Which will answer your question on why the code I posted says if (master.poll() > 0).

As I mentioned, I made a lot of modifications. Eventually, select_telegram executes these 5 statements:

             telegram.u8id = slave_idx; // slave address
             telegram.u8fct = 3; // function code
             telegram.u16RegAdd = 0; // start address in slave
             telegram.u16CoilsNo = 10; // number of elements (coils or registers)
             telegram.au16reg = modbus_register.au16data; // pointer to a memory array in the Arduino

There's a bunch of logic not applicable to anyone but me between the function call and the the code above. The bottom line is there are a series of these 5 lines with different values depending on what data I want to read/write and from where. I see no reason for telegram to be an array. IIRC, as an array the data is stored in data memory but as a if configured by an if or case statement then it is stored in (the much larger) program memory.

The point of listing the program is for you to see the flow, not to copy and paste it into your program.