Issues trying to read a DDS238-4 W energy meter through MODBUS with Arduino Mega

Hello everyone! I’m having an issue with a project of mine, and I’d be really thankful if any of you would take some time to help me!
In a nutshell: I’m trying to read the MODBUS registers of a DDS238-4 W energy meter using my Arduino Mega. In order to adapt TTL to RS485, I’m using a MAX485. The wiring is as it follows:

In case it’s hard to see (sorry I made it on Paint):

Arduino Mega Pin 8 -> MAX485 DE & RE

Arduino Mega Pin 16 (TX2) -> MAX485 DI

Arduino Mega Pin 17 (RX2) -> MAX485 R0

Arduino Mega GND -> MAX485 GND

Arduino Mega 5V -> MAX485 VCC

MAX485 A -> DDS238-4 W Pin 2

MAX485 B -> DDS238-4 W Pin 1

DDS238-4 W registers are mapped like this:

image

And my code (which is inspired by this one that I found at GitHub: DDS238/DDS238.ino at master · carlosmsx/DDS238 · GitHub), is as it follows:

#include <ModbusMaster.h>

#define MAX485_DE 8

typedef struct {
  uint32_t ID;
  float total;
  float export_energy;
  float import_energy;
  float V;
  float I;
  float P;
  float Kvar;
  float CosPhi;
  float F;
  uint16_t settings;
  uint32_t bauds;
} READING;

// instantiate ModbusMaster object
ModbusMaster node;

void preTransmission()
{
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  Serial.begin(115200);

  pinMode(MAX485_DE, OUTPUT);
  digitalWrite(MAX485_DE, 0);
  Serial2.begin(9600);
  
  // Modbus slave ID 1 (default)
  node.begin(0x01, Serial2);
  // These callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  READING r;
  uint8_t result = node.readHoldingRegisters(0x00, 32); //read 32 registers starting at 0
  if (result == node.ku8MBSuccess)
  {
    r.total   = (float)(node.getResponseBuffer(0x00)*0x10000 + node.getResponseBuffer(0x01))/100.0;
    r.export_energy  = (float)(node.getResponseBuffer(0x08)*0x10000 + node.getResponseBuffer(0x09))/100.0;
    r.import_energy  = (float)(node.getResponseBuffer(0x0A)*0x10000 + node.getResponseBuffer(0x0B))/100.0;
    r.V       = (float)node.getResponseBuffer(0x0C)/10.0;
    r.I       = (float)node.getResponseBuffer(0x0D)/100.0;
    r.P       = (float)node.getResponseBuffer(0x0E)/1000.0;
    r.Kvar    = (float)node.getResponseBuffer(0x0F)/1000.0;
    r.CosPhi  = (float)node.getResponseBuffer(0x10)/1000.0;
    r.F       = (float)node.getResponseBuffer(0x11)/100.0;
    r.settings= node.getResponseBuffer(0x15);
    r.ID = r.settings>>8;

    uint32_t baudTable[] = {0, 9600, 4800, 2400, 1200};
    r.bauds = baudTable[r.settings & 0xff];

    Serial.print("{'id':"); Serial.print(r.ID); Serial.print(",");
    Serial.print("'total':"); Serial.print(r.total); Serial.print(",");
    Serial.print("'export':"); Serial.print(r.export_energy); Serial.print(",");
    Serial.print("'import':"); Serial.print(r.import_energy); Serial.print(",");
    Serial.print("'V':"); Serial.print(r.V); Serial.print(",");
    Serial.print("'I':"); Serial.print(r.I); Serial.print(",");
    Serial.print("'P':"); Serial.print(r.P); Serial.print(",");
    Serial.print("'R':"); Serial.print(r.Kvar); Serial.print(",");
    Serial.print("'CosPhi':"); Serial.print(r.CosPhi); Serial.print(",");
    Serial.print("'F':"); Serial.print(r.F); Serial.print("}\n");
  }
    getResultMsg(result);
    delay(2000);
}

bool getResultMsg(uint8_t result)
{
  String tmpstr2;

  switch (result) {
    case node.ku8MBSuccess:
      return true;
      break;
    case node.ku8MBIllegalFunction:
      tmpstr2 = "Illegal Function";
      break;
    case node.ku8MBIllegalDataAddress:
      tmpstr2 = "Illegal Data Address";
      break;
    case node.ku8MBIllegalDataValue:
      tmpstr2 = "Illegal Data Value";
      break;
    case node.ku8MBSlaveDeviceFailure:
      tmpstr2 = "Slave Device Failure";
      break;
    case node.ku8MBInvalidSlaveID:
      tmpstr2 = "Invalid Slave ID";
      break;
    case node.ku8MBInvalidFunction:
      tmpstr2 = "Invalid Function";
      break;
    case node.ku8MBResponseTimedOut:
      tmpstr2 = "Response Timed Out";
      break;
    case node.ku8MBInvalidCRC:
      tmpstr2 = "Invalid CRC";
      break;
    default:
      tmpstr2 = "Unknown error: " + String(result);
      break;
  }
  Serial.println(tmpstr2);
  return false;
}

So, the issues I'm facing:

The result in the Serial Monitor is "Invalid Slave ID", which is awkward because the energy meter states that it is 1. I tried every other possibility, and it still states "Invalid Slave ID".

I tried, instead of using pin 8 for DE and RE, using 2 separate pins (so it would be MAX485_DE and MAX485_DE_NEG) . But then the error message that I get in the Serial Monitor is "Response Timed Out".

I wonder if by using just pin 8 the comm is too fast and I can't get the proper answer, but using two pins is too slow and the response times out?

Do you guys have any ideas of what's the problem? I wonder if just simple wires are enough to communicate with the meter, but I think they are because it's such a short distance.

This means that the ID byte in the result you got from the slave has another content than the ID you used to send the request. This usually means that either the serial parameters are incorrect (baudrate, parity, stop bits, etc.) or the line is that bad that bytes are transferred only with errors. As the device seems to answer in every case (as otherwise you'd get timeout errors) the transfer to the device must be a lot better (if you send the wrong address the device must be slient).
If you change the address and still get the same error, something in your wiring must be terribly wrong.

Do you use one of these ultra-cheap Chinese RS-485 modules you pictured in the wiring diagram? Did you check that it works correctly? There are many bad one on Ali and co.

I would connect a logic analyzer to the serial pins and see what exactly it sends and what is received.

First of all, thanks for the reply.

Yes, I'm using the same RS485 converter as in the picture. I don't think that's the issue, I've seen lots of people in youtube using this one and it works just fine. I got a new one in case I eventually messed up the converter, but I still get the same error, so I guess that's not the issue.

I also bought a USB-RS485 converter so I could try to read the energy meter by using the software ModScan64, and it turns out I could! The energy meter is fine, I could read the data by this config: (Baud Rate: 9600; Word Length: 8; Parity: NONE; Stop Bits: 1), and, of course, Device ID 1 and Function 03 Holding Registers.

But still, I couldn't work around the "Invalid Slave ID" issue with the arduino. I'll try using another library such as ModbusRtu.h and also analyze the wave form with an oscilloscope in my university.

If you have any other ideas, please share!

And using that software you also read all 32 registers at once? Maybe your device doesn't support such large blocks to be read.

Check your wiring, especially the RE connection might not be connected correctly.

It doesn't prove anything that similar looking devices work for other. You still might have received bad ones. You might want to check using your USB2RS485 adapter and a simple serial transfer (in one direction at a time).