CMB27 / ModbusRTUMaster

A library to implement the master/client portion of the Modbus RTU protocol on Arduino
MIT License
29 stars 6 forks source link

Problem with receiving #10

Closed paju2024 closed 4 months ago

paju2024 commented 4 months ago

Hi,

I've been trying to get this library to work with Arduino Mega 2560 Pro without a real success.

Background:

With ST485CN which is almost the same chip that is used in the device as well (same HW properties) I get the best results but still every message fails.

I've modified the library a bit to get some error codes and more information what happens. And this is what I've received:

I tested old SimpleModbusMaster library as well and that works. Not always (30% maybe) but I'm able to get response to buffer. With exact same setup, ModbusRTUMaster fails fully.

Due to this kind of fluctuation, it sounds to me as a some sort of timing issue but I'm far from being confident on this. I've tried pretty much everything that I could have thought without a luck.

What could be the issue?

CMB27 commented 4 months ago

What are you doing with the RE and DE pins on the ST485CN?

paju2024 commented 4 months ago

Connected to separate DO (DO30 in the test setup) and Modbus library controls it. Wouldn't see anything on TTL side if this was not controlled.

RS485 chip and connection works. Another Modbus/RTU master library worked - not well but worked.

paju2024 commented 4 months ago

Tested the library with a dedicated test SW without anything else to exclude other distractions. No help - same result.

CMB27 commented 4 months ago

I ran a test. My test setup is pictured below.

20240210_032504

I used the following hardware.

Manufacturer Part Number Name Description Notes
Arduino A000067 Arduino Mega 2560 Rev3 Microcontroller Board Uses an ATmega2560 processor
SparkFun BOB-10124 RS-485 Transceiver Breakout Uses a Sipex SP3485 transceiver chip
Koyo C0-02DD1-D CLICK PLC Industrial PLC

I ran the following program on the Arduino Mega 2560.

#include <ModbusRTUMaster.h>

const byte dePin = 30;
ModbusRTUMaster modbus(Serial1, dePin);
uint16_t holdingRegisters[10];

void setup() {
  Serial.begin(9600);
  modbus.begin(9600, SERIAL_8N1);
}

void loop() {
  modbus.readHoldingRegisters(2, 0, holdingRegisters, 10);

  for (byte i = 0; i < 10; i++) {
    Serial.print(holdingRegisters[i]);
    Serial.print('\t');
  }
  Serial.println();
  Serial.flush();
}

I used a holding register starting address of 0 instead of 40001. The CLICK PLC has two addressing schemes for its 16-bit integers: "MODBUS 984 Addressing" where the addresses start at 400001, and "MODBUS HEX Addressing" where the addresses start at 0000h. ModbusRTUMaster needs to use the HEX Addressing scheme.

The CLICK PLC has a simple program that sets up its RS-485 port with the following settings.

I can read and write the PLC address values from the computer.

I was able to edit the values in the PLC and the Arduino Mega reliably read those values out and printed them in the Serial Monitor. I also tried the program with modbus using Serial2 and again with Serial3. All worked successfully.

CMB27 commented 4 months ago

I wonder if your issue has to do with the starting data address. Your device may be similar to my PLC, in that it has multiple data addressing schemes and you've picked the wrong one.

paju2024 commented 4 months ago

Nothing to do with the start address. I was able to solve it by changing to the RS485 adapter with MAX chip and using SimpleModbusMaster library. All message receptions are now fine. Few failure but mainly fine.

There are couple of differences that I can spot from oscilloscope. One is that with MAX chip the RO pin, when not enable (i.e. not receiving) is at HIGH whereas with ST chip it is at low. Adapter has also resistors between Arduino and chip but that should not have any impact.

The other difference is that chip's DE&RE control from the library comes much later from SimpleModbusMaster library compared to yours.

Comparing to your setup, I'm using MEGA 2560 Pro which is pretty small in size compared to normal MEGA2560. Are there differences to official one - I dunno.

It's a pity that this library did not work because I'd prefer the usage of this over the other.

CMB27 commented 4 months ago

Can you try running something like the following?

#include <ModbusRTUMaster.h>

const byte dePin = 30;
ModbusRTUMaster modbus(Serial1, dePin);
uint16_t holdingRegisters[10];

void setup() {
  Serial.begin(9600);
  modbus.begin(9600, SERIAL_8N1);
}

void loop() {
  if (modbus.readHoldingRegisters(2, 0, holdingRegisters, 10) == true) {
    Serial.println("Success");
  }
  else {
    Serial.print("Failure");
    if (modbus.getTimeoutFlag() == true) {
      Serial.print(": Timeout");
      modbus.clearTimeoutFlag();
    }
    else if (modbus.getExceptionResponse() != 0) {
      Serial.print(": Exception Response ");
      Serial.print(modbus.getExceptionResponse());
      switch (modbus.getExceptionResponse()) {
        case 1:
          Serial.print(" (Illegal Function)");
          break;
        case 2:
          Serial.print(" (Illegal Data Address)");
          break;
        case 3:
          Serial.print(" (Illegal Data Value)");
          break;
        case 4:
          Serial.print(" (Server Device Failure)");
          break;
        default:
          Serial.print(" (Uncommon Exception Response)");
          break;
      }
      modbus.clearExceptionResponse();
    }
    Serial.println();
  }
  Serial.flush();
}

This will give us some diagnostics from the library on what could be going wrong.

I understand you have a solution to your problem, but I would still like to know what is going wrong, so that if it is something with the library, I can fix it.

My current guess is that it could be a timeout issue. By default the library expects a response from the slave device within 100mS after sending the request. Is sounds like you are working with an older/limited slave device, and it may take more than 100mS for it to formulate and send a response. If this is the case, it can be fixed by running something like modbus.setTimeout(500); in setup(). This would give the slave device 500mS to send its response instead of 100mS.

paju2024 commented 4 months ago

Interesting.. reports constantly success for slave ID 2. When I change it to slave ID 1 (I have two devices now in the system) it give timeout failure. Tested with USB<>RS485 converter that it works.

Oscilloscope shows OK sending though so this is most likely related to device itself somehow. I had problems in earlier tests as well and when I got a new device, the communication started to work. Although, it works with USB<>485 converter so it's not broken but something strange.

paju2024 commented 4 months ago

Modified the code a bit to be easier to debug:

#include "ModbusRTUMaster.h"

const byte dePin = 30;
ModbusRTUMaster modbus(Serial2, dePin);
uint16_t holdingRegisters[10];

void setup() 
{
  Serial.begin(115200);
  modbus.begin(19200, SERIAL_8N1);
}

void loop() 
{
  static u16 counter=0;

  if (modbus.readHoldingRegisters(2, 0, holdingRegisters, 10) == true) 
  {
    Serial.print("Success: "); Serial.println(counter);
  }
  else 
  {
    Serial.print("Failure");
    if (modbus.getTimeoutFlag() == true) 
    {
      Serial.print(": Timeout: "); Serial.print(counter);
      modbus.clearTimeoutFlag();
    }
    else if (modbus.getExceptionResponse() != 0) 
    {
      Serial.print(": Exception Response ");
      Serial.print(modbus.getExceptionResponse());
      switch (modbus.getExceptionResponse()) 
      {
        case 1:
          Serial.print(" (Illegal Function)");
          break;
        case 2:
          Serial.print(" (Illegal Data Address)");
          break;
        case 3:
          Serial.print(" (Illegal Data Value)");
          break;
        case 4:
          Serial.print(" (Server Device Failure)");
          break;
        default:
          Serial.print(" (Uncommon Exception Response)");
          break;
      }
      modbus.clearExceptionResponse();
    }
    Serial.println();
  }
  Serial.flush();

  counter++;
}
paju2024 commented 4 months ago

Before I start to make changes to main code, is the library thread safe? I'm using FreeRTOS to run other tasks.

Edit.. didn't have patience to wait for you response and made the changes. Works well now. The other device is not responding but the other is. I use also those failures from your example code which makes it much easier to understand what happens. Only timeouts occurring.

CMB27 commented 4 months ago

This library was not written with thread safety in mind.