Arduino Nano and RS485 to read meter data

Hello, I've bought an energy meter (OB115-Mod) that supports RS485
I am trying to read it using an Arduino Nano;
This is the register map of the device
image

I'm following this example

which I intend to modify for my needs; I just want to read the voltage at the moment

#include <ModbusMaster.h>


/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      3
#define MAX485_RE_NEG  2

// instantiate ModbusMaster object
ModbusMaster node;

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

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

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 9600 baud
  Serial.begin(9600);

  //Serial.begin(9600, SERIAL_8N1);

  // Modbus slave ID 2
  node.begin(2, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

bool state = true;

void loop()
{
  uint8_t result;
  uint16_t data[6];
  
  // Toggle the coil at address 0x0002 (Manual Load Control)
//  result = node.writeSingleCoil(0x0002, state);
 // state = !state;

  // Read 16 registers starting at 0x3100)
  result = node.readInputRegisters(0x0002, 2);
//  if (result == node.ku8MBSuccess)
//  {
 //   Serial.print("Vbatt: ");
 //   Serial.println(node.getResponseBuffer(0x00)/100.0f);
//    Serial.print("Vload: ");
//    Serial.println(node.getResponseBuffer(0xC0)/100.0f);
//    Serial.print("Pload: ");
//    Serial.println((node.getResponseBuffer(0x0D) +
 //                   node.getResponseBuffer(0x0E) << 16)/100.0f);
 // }

Serial.println(result);

  delay(1000);
}

I am getting what seems to be error 224 (I'm printing "result" into the serial monitor) preceeded by some strange characters (don't know if they're expected)

I've connected Arduino to TTL like this
RX0 to RO
TX1 to DI
D3 to DE
D2 to RE
+5V to VCC
GND to GND

And TTL to Meter
A to A
B to B
using a short twisted pair

at 9600 baud, confirmed?

Yes, its default is 9600 and I’ve confirmed by pressing the button and viewing the different settings (ID is set to 2)

Does the device have a ground, and does your RS485 interface have one? The single most common problem I've seen with RS485 is when the two systems have no common ground reference. Many reputable sources will dispute the need in benign circumstances, but I've found it necessary several times to connect the DC common, or ground, of both systems to make comms rock solid.
Worth a try, anyway.
C

We usually request a schematic, this is no exception. Read the posting guidelines.
C

From the manual, I see it does have a ground. See section 4 diagram, pin numbered 8 on the diagram, but called 6 in the callout.
C

I copied the schematic from this video

Most other schematics I found online connect both DE and RE to the same GPIO, but the code I am trying to use explicitly defines them as separate

So either follow the wring and modify the code, or modify the wiring to suit the code. Either way is generally good.

I'm trying not to modify the code (apart from where it needs to be adapted to my specific metering device)

If I connect as per the video (RX0 to DE and TX1 to DI) I get 224 on the serial monitor
If I connect the ground to the RS485 meter as suggested, I get 226

I assume you need to use SoftSerial to get a separate Serial Interface for the ModBus.
Otherwise you will not get reliable results.

I haven't seen SoftSerial being used in that example

I suspect that the problem you are having is as @noiasca pointed out to you. You are using the same (hardware) serial port for your Modbus communications and for your serial printing. Those boxes and backwards question mark on your serial monitor screenshot are probably the Modbus command message being sent by your code. As well as going to your RS-485 board, the bytes are also going to your serial monitor.

The author of the youtube video is using the same serial port for both Modbus comms and the IDE serial monitor. Here's a screen grab from the 2nd part of their video:


Note the line of garbage characters after they print out "Battery Charge Current".

You indicated that your device runs at 9600 baud, which is within the capabilities of a software serial port.

So why does the author of the video get messages back and I don’t?

Where do I find the softserial libraries?

Maybe you got your A & B wires crossed over.
Maybe you don't have a common ground.
Maybe you have the wrong baud rate.
Maybe you have the wrong device address.

Do you have a USB-RS485 dongle?

They are very useful. There's also free PC software that can send Modbus messages to slave devices. It's a lot easier to play about with one of them to see how your device works, and then implement your own Arduino code.

I thought I read somewhere that the Modbus library doesn't work very well on Uno/Nano type boards. I may be wrong.

If you are a master talking to a slave device, then you can do it without using any of the Modbus libraries. Have a search in these forums for an NPK sensor. There's code there that sends out canned Modbus messages.

I have tried swapping A and B
I have tried a common ground
The id is correct (2); I have tried both 2 and 002
Baud rate is correct

I just ordered a usb to rs485 as you suggested; I am starting to think the device doesn’t work

There are other USB-RS485 dongles, but I've got this one:

I had a quick look at the 6 page guide/manual for your meter. Looks like you can also set the parity as well as ID and baud rate. I think the Arduino Modbus code uses parity = None but I could be wrong. Might be worth checking that too.

I tried the USB to RS485 dongle and I can read all the input registers fine with the default settings (9600,none,8bit,1 stop bit)

I also managed to make one ESP32 send a character to another ESP32 using RS485 (but not ModBus); I am now using Serial2 instead of Serial0 using HardwareSerial

This is the code that works for receiving a character through RS485

// Sending/Receiving example

HardwareSerial Receiver(2); // Define a Serial port instance called 'Receiver' using serial port 2

#define Receiver_Txd_pin 17
#define Receiver_Rxd_pin 16
#define MAX485_DE      21
#define MAX485_RE_NEG  21



void setup() {
    pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  //Serial.begin(Baud Rate, Data Protocol, Txd pin, Rxd pin);
  Serial.begin(115200);                                                   // Define and start serial monitor
  Receiver.begin(115200, SERIAL_8N1);//, Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port
}

void loop() {
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
 while (Receiver.available()) {                         // Wait for the Receiver to get the characters
    //float received_temperature = Receiver.parseFloat(); // Display the Receivers characters
    char received_text = Receiver.read();
    Serial.println(received_text);               // Display the result on the serial monitor
  };
  //delay(200);
}

This is the one that sends it, which is pretty similar

// Sending/Receiving example

HardwareSerial Sender(2);   // Define a Serial port instance called 'Sender' using serial port 1

#define Sender_Txd_pin 17
#define Sender_Rxd_pin 16

void setup() {
  //Serial.begin(Baud Rate, Data Protocol, Txd pin, Rxd pin);
  Serial.begin(115200);                                             // Define and start serial monitor
  Sender.begin(115200, SERIAL_8N1);//, Sender_Txd_pin, Sender_Rxd_pin); // Define and start Sender serial port
}

void loop() {
  float sensor_temperature = 22.141;                               // Set an example value
  char text = 'c';
  Sender.print(text);                                // Send it to Sender serial port
  delay(2000);
}

And finally, the code that attemps to read from the meter using Modbus, which still returns result=226

#include <ModbusMaster.h>

HardwareSerial Receiver(2); // Define a Serial port instance called 'Receiver' using serial port 2
#define Receiver_Txd_pin 17
#define Receiver_Rxd_pin 16
/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      21
#define MAX485_RE_NEG  21

// instantiate ModbusMaster object
ModbusMaster node;

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

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

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication

 runs at 115200 baud
  Serial.begin(115200);
  
  Receiver.begin(9600, SERIAL_8N1);//, Receiver_Txd_pin, Receiver_Rxd_pin); // Define and start Receiver serial port

  // Modbus slave ID 2
  node.begin(2, Receiver);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}


void loop()
{
  uint8_t result;

  // Read 16 registers starting at 0x3100)
  result = node.readInputRegisters(0x0002, 2);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Response: ");
    Serial.println(node.getResponseBuffer(0x04)/100.0f);
  }

  delay(1000);
  Serial.println(node.getResponseBuffer(0x04)/100.0f);
Serial.println(result);


  
}

Any idea anyone?

Did you use a PC based modbus app or just a dumb serial terminal to communicate?

Does the compiler not complain about the line above the Serial.begin()? If I take "the code that attemps to read from the meter using Modbus" and compile it as is for an Arduino UNO, I get errors as follows:

Arduino: 1.8.19 (Windows 10), Board: "Arduino Uno"
sketch_jul12a:3:26: error: no matching function for call to 'HardwareSerial::HardwareSerial(int)'

 HardwareSerial Receiver(2); // Define a Serial port instance called 'Receiver' using serial port 2
                          ^
In file included from C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/Arduino.h:233:0, from sketch\sketch_jul12a.ino.cpp:1:

C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:117:12: note: candidate: HardwareSerial::HardwareSerial(volatile uint8_t*, volatile uint8_t*, volatile uint8_t*, volatile uint8_t*, volatile uint8_t*, volatile uint8_t*)

     inline HardwareSerial(
            ^~~~~~~~~~~~~~
C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:117:12: note:   candidate expects 6 arguments, 1 provided

C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:93:7: note: candidate: constexpr HardwareSerial::HardwareSerial(const HardwareSerial&)

 class HardwareSerial : public Stream
       ^~~~~~~~~~~~~~
C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:93:7: note:   no known conversion for argument 1 from 'int' to 'const HardwareSerial&'

C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:93:7: note: candidate: constexpr HardwareSerial::HardwareSerial(HardwareSerial&&)

C:\Users\Mark.DESKTOP-3R25MVE\AppData\Local\Arduino15\packages\arduino\hardware\avr\1.8.5\cores\arduino/HardwareSerial.h:93:7: note:   no known conversion for argument 1 from 'int' to 'HardwareSerial&&'

D:\Electronics\Arduino\MySketchbook\sketch_jul12a\sketch_jul12a.ino: In function 'void setup()':

sketch_jul12a:39:2: error: 'runs' was not declared in this scope

  runs at 115200 baud
  ^~~~
D:\Electronics\Arduino\MySketchbook\sketch_jul12a\sketch_jul12a.ino:39:2: note: suggested alternative: 'rand'

  runs at 115200 baud
  ^~~~
  rand

exit status 1

no matching function for call to 'HardwareSerial::HardwareSerial(int)'

Good that you've got comms working and the settings are 9600,8,N,1. The only other parameter missing is the modbus device ID. What number did you use when testing on your PC?

Here's a modified version of your code that uses AltSoftSerial to create a second serial port.

#include <ModbusMaster.h>
#include <AltSoftSerial.h>

// Install AltSoftSerial from the library manager
// TX data: D9 -> MAX485 DI
// RX data: D8 -> MAX485 D0
// RE ctrl: D7 -> MAX485 RE
// DE ctrl: D6 -> MAX485 DE
/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE      6
#define MAX485_RE_NEG  7

AltSoftSerial swSerial;

// instantiate ModbusMaster object
ModbusMaster node;

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

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

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

  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 9600 baud
  swSerial.begin(9600);
  node.begin(2, swSerial);    // <--- SLAVE ID is set to 2
  
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t result;

  // Read 16 registers starting at 0x3100)
  result = node.readInputRegisters(0x0002, 2);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Response: ");
    Serial.println(node.getResponseBuffer(0x04)/100.0f);
  }

  delay(1000);
  Serial.println(node.getResponseBuffer(0x04)/100.0f);
  Serial.println(result);
}

Note the connections to the MAX485 breakout detailed at the top of the sketch. The sketch compiles without error for an Arduino UNO. Let me know how you get on.