MODBUS RS-485 communication error

Hello,

I am running a setup where I use RS-485 communication between a Mega and a computer (my Mega acts as a slave) and a CN0391 temperature shield from Analog Devices.

Here is the example code for the CN_0391 shield: arduino/Arduino Uno R3/examples/CN0391_example at master · analogdevicesinc/arduino · GitHub

The Modbus library I am using is the smarmengol RTU Modbus library (GitHub - smarmengol/Modbus-Master-Slave-for-Arduino: Modbus Master-Slave library for Arduino)

I am testing the Modbus communication with ModbusTool (http://modbusmaster.com/)

So...
Hardware:

  • CN_0391 temp shield from Analog Devices
  • Arduino Mega
  • Dell latitude computer
  • USB to TTL cable x2
  • USB to TTL/RS-485 cable
  • RS-485 to TTL Max485 cable
  • jumper wires

Software:

  • Arduino IDE
  • Smarmengol RTU Modbus library
  • CN0391_example code
  • ModbusTool.exe (V1.1.1.0)

Running the program below I am able to successfully create a connection and read the register values in ModbusTool.exe:

#include <ModbusRtu.h>
#include <SPI.h>
#include "CN0391.h"
#include "Communication.h"

//MODBUS Slave setup:
#define TXEN 8
//data array for modbus network sharing:
uint16_t roastData[16] = {
  241, 7, 3, 6, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 
};

Modbus slave(1,Serial,TXEN); // this is slave @1 and RS-485

uint16_t timeOutVal = 10000;

/*********************************************************************************/

void setup() {
  // put your setup code here, to run once:

Serial.begin(19200);

SPI.begin(); // initialization of SPI interface
SPI.setDataMode(SPI_MODE3); // configuration of SPI communication
SPI.setClockDivider(SPI_CLOCK_DIV16); // configuration of clock at 1MHz
pinMode(CSfurnace, OUTPUT); //setting chip select as output
pinMode(CS_PIN, OUTPUT);

CN0391_init();

#if(USE_RTD_CALIBRATION == YES)
   CN0391_calibration(RTD_CHANNEL);
#endif

#if(USE_TH_CALIBRATION == YES)
   CN0391_calibration(TH_CHANNEL);
#endif

slave.setTimeOut(timeOutVal);
slave.start();
delay(10);
   
}

/*********************************************************************************/

void loop() {
  // put your main code here, to run repeatedly:

slave.poll( roastData, 16 );

}

However when I add:
CN0391_set_data();
To the end of void loop I get the error message "Block with function 3 is timed out" in the ModbusTool program.

My current theory is that CN_0391_set_data( ) simply takes a long time to carry out.
I believe this because the standard time-out time for messages is 1000ms if I look in the smarmengol Modbus library files.

I have looked through the CN_0391 library files and there are no long delay( ) functions within the function set_data or within the functions set_data uses. But I still suspect that it is a function that takes some time to carry out considering all the sub-functions etc.
Here is the CN0391_set_data( ) function in the .cpp file:


void CN0391_set_data(void)
{
   channel_t i;

  for(i = CHANNEL_P1; i <= CHANNEL_P4; i = static_cast<channel_t>(i+1)){

        CN0391_enable_current_source(i);
        
        _ADCValue0[i] = CN0391_read_channel(i);
        //Serial.println( _ADCValue0[i]);
        _ADCValue1[i] = CN0391_read_channel((i + 4));
        //Serial.println( _ADCValue1[i]);
        rRtdValue[i] = CN0391_data_to_resistance(_ADCValue0[i]);
        //Serial.println( rRtdValue[i]);
        CN0391_calc_rtd_temperature(i, &temp0[i]);
        
        CN0391_calc_th_temperature(i, temp0[i], &temp1[i]);
       

   }

}

I have also tried switching the setTimeOut( ) function to be before and after the slave.begin( ); line.

Additionally I have tried changing the time-out time in the modbus library .h file in one place but without success.

I suspect that setTimeOut( ) is a function only available to Masters and that Slaves cannot use it.

So essentially I am wondering if anyone knows a way of increasing the time-out time for slave query messages using this library
OR
if you think it might be a different problem?

/Einar

The Arduino library isn't relevant here because the timeout is done on the PC. So check what timeout your application has there.

It looks like. You should do what CN0391_set_data() does in your loop() and call slave.poll() more often. The CN0391_read_channel starts a conversion and then waits for the conversion to finish. If you call the slave.poll() during that wait cycles you can probably fix your problem. This means you override most of the high level calls of the CN0391 library and just use the low level calls.

Hey pylon, thank you for your reply!
Adding two slave.poll( ) - one above and one below CN0391_set_data( ) - did the trick and got rid of the error! :grin:

However I am curious about the other things you wrote about...

  1. Where would I check the timeout on my PC
  2. What do you mean by the read_channel command starting a "conversion"? Does this just mean converting x,y,z data into b data?
  3. What are high level vs low level calls?
  4. When you refer to the wait cycles of CN0391_read_channel, do you mean this part
  if (AD7124_WaitForConvReady(10000) == -3) {
    Serial.println("TIMEOUT");
    return 0;
  }

  delay(100);

? And if so, where would be a good spot to input slave.poll(); ?

Sorry if some of these questions are at a basic level; I am quite a beginner and always trying to learn where my knowledge is lacking.

I am also asking because with the current solution the program is a bit unpredictable... sometimes when I connect via ModbusTool I get the expected results and other times I get the timeout error.

/Einar

Victory post:
What solved it fully for me - without slowing the program and keeping it reliable - was moving the definition of the CN0391_read_channel( ) function from the .cpp file to my "run window" and modifying it in this way:

Thanks again for the help pylon!

In that ModbusTool.exe. I don't know that application but if it does things right it should offer to set the desired timeout.

Tell the hardware to start a conversion, so start to measure the value. This seems to be time consuming, so you could do other stuff while the hardware is measuring.

A high level calls combines several low level calls into an easier to use API call. Low level calls are the less abstracting calls that does directly interact with the hardware (that's a rather raw description but I guess you got the point).

Almost, I referred to the code that builds the AD7124_WaitForConvReady() routine. This runs in a loop up to 10'000 times if the hardware didn't answer before.

You have to do everything CN0391_set_data() does yourself in your code and call slave.poll() inside the loops of that code.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.