4-20ma / ModbusMaster

Enlighten your Arduino to be a Modbus master
http://4-20ma.io/ModbusMaster/
Apache License 2.0
587 stars 350 forks source link

How to fix ku8MBResponseTimedOut? #129

Open ajpaezm opened 4 years ago

ajpaezm commented 4 years ago

I have an issue trying to use Arduino UNO for Modbus communication with a certain device I'm trying to get readings from.

Devices: 1) ARDUINO UNO Board. 2) MAXmax485 (DE-RE connected together). 3) CEM M-RS485 (this is the link: http://docs.circutor.com/docs/M014B01-03.pdf), properly connected to A+ and B-.

The code is as follows:


#include <Arduino.h>
#include <ModbusMaster.h>

#define MODBUS_RX 7
#define MODBUS_TX 8
#define MODBUS_DE 6

ModbusMaster modbusNode;

void preTransmission()
{
  digitalWrite(MODBUS_DE, HIGH);
  delay(10);
}

void postTransmission()
{
  delay(2);
  digitalWrite(MODBUS_DE, LOW);
}

void setup() {
  pinMode(MODBUS_DE, OUTPUT);
  Serial.begin(9600);
  modbusNode.begin(1, Serial);
  modbusNode.preTransmission(preTransmission);
  modbusNode.postTransmission(postTransmission);
  // put your setup code here, to run once:
}

void loop() {
  // put your main code here, to run repeatedly:
  uint8_t resultMain;
    // Read 1 register from address
  resultMain = modbusNode.readInputRegisters(2710,1);
  Serial.println(resultMain,HEX);
}

According to the guide section 4.4.4.1.- Configuration variables for the CEM M-RS485 everything is set up. The registry I'm trying to read is 2710, which should return the following (see image attached to the message please). image

In the table of section 4.4.4.5. there are also commands that should be readable, but no luck whatsoever. image

Some strange values are included in the response (like "SOH", "EOT" and another one but I think is garbage, I'm not sure). There's one value that is relevant though, 0xE2, which corresponds to ku8MBResponseTimedOut (he entire response was not received within the timeout period, ModbusMaster::ku8MBResponseTimeout).

Can someone help find out what the issue might be? Perhaps there's something I'm not properly setting inside my code, as the physical setup seems to be correct.

Thanks in advance!

yet-another-average-joe commented 4 years ago

See here : https://github.com/4-20ma/ModbusMaster/issues/127

ajpaezm commented 4 years ago

See here : #127

Thanks.

I made the changes suggested in lines 708 and 752. Apologies for this, but can you tell me where should I paste the snippet about the calculations for T3_5 and T1_5?


                        if (baud > 19200)
            {
                _T1_5 = 750; // 750 us
                _frameDelay = 1750; // 1750 us
            }
            else
            {
                                // _frameDalay = T3.5
                _T1_5 = 16500000 / baud; // 1 packet = always 11 bits
                _frameDelay = 38500000 / baud; // 1 packet = always 11 bits
            }

Also, if this goes in ModbusMaster.cpp, shouldn't I name _T1_5 as T1_5 and _frameDelay as T3_5?

In case I'm sure I'm going to be working with a BaudRate below 19200, can I just hardcode T1_5 and T3_5 like this?

From ModbusMaster.cpp:

uint8_t ModbusMaster::ModbusMasterTransaction(uint8_t u8MBFunction)
{
  uint8_t u8ModbusADU[256];
  uint8_t u8ModbusADUSize = 0;
  uint8_t i, u8Qty;
  uint16_t u16CRC;
  uint32_t u32StartTime;
  uint8_t u8BytesLeft = 8;
  uint8_t u8MBStatus = ku8MBSuccess;
  unsigned long T1_5 = 16500000L/9600;  //fixed baud rate of 9600
  unsigned long T3_5 = 38500000L/9600;  // fixed baud rate of 9600
...

This is the first change (for T3_5): image

This is the second change (for T1_5): image

I performed such changes and the response continued to be the same (0xE2). Did I execute these proposed modifications correctly? I'd be glad to know (and if you have any additional tips as well, of course).

Thanks a lot for your valuable time.

yet-another-average-joe commented 4 years ago

delays seem to be at the right place.

enable transmission in _preTransmission() callcack : turn HIGH the RE/DE pins, and then disable transmission in _postTransmission() callback : done !

On my side, I added Serial information in the begin() function, with T1.5 and T3.5 calculations. (to be honest, I forked the project (198th fork on Git !), renamed all functions and variables for readability, and also removed the Doxygen comments : reading the sources, you will instantly understand what I did)

You could also make T1_5 and T3_5 public, and set them anywhere in your code, before you initiate communications.

Also, don't forget :

if you use a bare MAX485 IC

On my side I had problems with a VFD : in the documentation, A and B are reverted !

Here's the schematics I made and use (on a STM32, with a DIP-8 MAX485, works fine with 3.3V logic, no need for a MAX3485) :

MAX485

R15 = R16 = 22K R17 = 100R to 120R

ajpaezm commented 4 years ago

enable transmission in _preTransmission() callcack : turn HIGH the RE/DE pins, and then disable transmission in _postTransmission() callback : done !

Ok, about this, I added these (unless you meant it in the main.cpp file and not on the ModbusMaster.cpp): image

I will attempt your circuit diagram and see whats up. Also will use other modules to try to connect to as well.

I didn't try to fork it as you mentioned it.

I'll let you know if any of these work out!

xilart commented 4 years ago

I ran into the same problem. I can't read any data from HoldingRegisters using Arduino Mega2560 + MAX485 shield, though I can write into it successfully to control HVAC unit and I can read data from HoldingRegisters using Modbus-to-USB converter. DE and RE are both connected to pin 39.

@yet-another-average-joe, I tried replacing ModbusMaster lib with your lib - YAAJ_ModbusMaster, but still getting the same result: E2 error. Any thoughts what I'm doing wrong?

class OutputVentilation : public IO
{
private:
  Stream *outputSerial;
  ObjectAbstract *input;
  YAAJ_ModbusMaster modbus;

public:

  OutputVentilation(const char *name PROGMEM, PinOutput* outputPin, ObjectAbstract *input, Stream *outputSerial) : IO(name, outputPin, false, 0)
  {
    this->outputSerial = outputSerial;
    this->input = input;
    stateChanged = true;
    valueChanged = true;
    Serial.println("init modbus");
    modbus.begin(Serial2, 19200, SERIAL_8E1, 1, 39, 2500);
    Serial.println("init modbus complete");
    setValue(this->read(VregAddrSpeed));
    setState(value);
//    modbus.preTransmission(this->preTransmission);
//    modbus.postTransmission(this->postTransmission);
  }

  void write(unsigned int reg, unsigned int value)
  {
    ((HardwareSerial*)outputSerial)->begin(19200, SERIAL_8E1);
    Serial.println("write modbus");
    modbus.F6_WriteSingleRegister(reg, value);
    read(reg);
  }

  unsigned int read(unsigned int reg)
  {
    ((HardwareSerial*)outputSerial)->begin(19200, SERIAL_8E1);
    Serial.println("read modbus");
    Serial.println(modbus.F3_ReadMultipleHoldingRegisters(reg, 1));
    Serial.println(modbus.getRxBuf(0));
    return modbus.getRxBuf(0);
  }
}

Always getting error on read:

Started
init modbus
init modbus complete
read modbus
227
39470
write modbus
read modbus
226
39470
yet-another-average-joe commented 4 years ago

This thread is definetly not the right place to ask about a forked library ! This will just add confusion ! I heavily changed the library name in order to avoid this.

At first glance, I see a problem :

this->outputSerial = outputSerial;

Not sure the compiler will make any difference between "this->outputSerial" and "outputSerial" (alone) in the context. At first, I'd replace "outputParameter" with "_outputParameter" (or I don't know about this trick...)

Why do you have to cast the stream ? My fork is intended for HardwareSerial only. Not for generic Stream. It will not work with software serial. And it's been tested on STM32 only. Not with AVRs. Maybe I forgot to say.

OxE2 is TIMEOUT. The most irritating and frustating error ;)

Please, open an issue on YAAJ_ModbusMaster, not there ! This is not 4-20mA related, and I'd be 4-20mA, I'd close this thread right now !!! See you later on https://github.com/yet-another-average-joe/YAAJ_ModbusMaster/issues (the 190th fork or so). But as is, many information are missing.

xilart commented 4 years ago

Well, the issue is that base ModbusMaster library doesn't work for reading holding registers in my configuration (Arduino Mega 2560 + Max485). I already spent few days trying to figure out why. And one of my guesses was timings and delays which are absent in this library. Though I saw delays included with some other Modbus implementations. But @yet-another-average-joe 's fix doesn't work in my case too :( And yes, these errors are quite frustrating.

So this code doesn't work for reading:

class OutputVentilation : public IO
{
private:
  Stream *outputSerial;
  ObjectAbstract *input;
  ModbusMaster modbus;

  static void preTransmission()
  {
    digitalWrite(RS485_MB_PIN, HIGH);
    delay(100);
  }

  static void postTransmission()
  {
    delay(100);
    digitalWrite(RS485_MB_PIN, LOW);
  }

public:

  OutputVentilation(const char *name PROGMEM, PinOutput* outputPin, ObjectAbstract *input, Stream *outputSerial) : IO(name, outputPin, false, 0)
  {
    this->outputSerial = outputSerial;
    this->input = input;
    stateChanged = true;
    valueChanged = true;
    modbus.begin(1, *outputSerial);
    modbus.preTransmission(this->preTransmission);
    modbus.postTransmission(this->postTransmission);
    setValue(this->read(VregAddrSpeed));
    setState(value);
  }

  void write(unsigned int reg, unsigned int value)
  {
    ((HardwareSerial*)outputSerial)->begin(RS485_MB_PIN_BAUD_RATE, SERIAL_8E1);
    modbus.writeSingleRegister(reg, value);
    read(reg);
  }

  unsigned int read(unsigned int reg)
  {
    ((HardwareSerial*)outputSerial)->begin(RS485_MB_PIN_BAUD_RATE, SERIAL_8E1);
    Serial.println(modbus.readHoldingRegisters(reg, 1));
    Serial.println(modbus.getResponseBuffer(0));
    return modbus.getResponseBuffer(0);
  }
}

It also returns E2(226)/E3(227) errors:

Started
227
5120
226 
5120

PS: good point about casting Stream, in will replace it HardwareSerial, because I actually use Serial2.

NdoroNetshipale commented 3 years ago

I am having the same problem if you managed to fix it please kindly assist

lossonfire commented 2 years ago

my code read holding registers in Arduino Uno but dont works in Arduino Mega, i dont know why