Help regarding MODBUS MAX485 code

Hi EveryOne

I am trying to read energy meter (as slave) with master (ardunio uno ) ,using communication (MAX485).

I have connected PIN 9,10 as RX,TX and using PIN 7 for changing the direction.

using the following

#include <ModbusMaster.h>

define LED_13 13

#include <LiquidCrystal.h>
#include <SoftwareSerial.h>

#define MAX485_DE 7
#define MAX485_RE_NEG 7
#define SSerialRX 9 //Serial Receive pin
#define SSerialTX 10 //Serial Transmit pin
#define SSerialTxControl 7
#define RS485Transmit HIGH
#define RS485Receive LOW
#define txnPinDir 7
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
// instantiate ModbusMaster object
ModbusMaster node;
#define Pin13LED 13
/-----( Declare objects )-----/
SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}

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

void setup()
{
lcd.clear();lcd.print("Scada System");delay(500);
//pinMode(MAX485_RE_NEG, OUTPUT);
//pinMode(MAX485_DE, OUTPUT);
pinMode(txnPinDir, OUTPUT);
digitalWrite(txnPinDir, RS485Transmit);
delay(500);digitalWrite(txnPinDir, RS485Receive);
delay(200);
// Init in receive mode

// digitalWrite(txnPinDir, RS485Transmit);

// Modbus communication runs at 115200 baud
// Serial.begin(9600);
// RS485Serial.begin(115200);
RS485Serial.begin(9600);
// Modbus slave ID 1
node.begin(1, RS485Serial);
// 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];
static uint32_t i;
uint8_t j;
i++;
/* lcd.clear();lcd.print("value of i");delay(500);
lcd.clear();delay(200);
lcd.print(i);delay(200);
node.setTransmitBuffer(0,lowWord(i));
lcd.clear();
lcd.setCursor(0,0);
lcd.print(lowWord(i));delay(500);

node.setTransmitBuffer(1,highWord(i));

lcd.setCursor(10,1);
lcd.print(highWord(i));delay(1000);*/
pinMode(txnPinDir, OUTPUT);
digitalWrite(txnPinDir, RS485Transmit);
delay(500);digitalWrite(txnPinDir, RS485Receive);
delay(200);
lcd.clear();lcd.print("Reading Slave");

for (int i=0; i <= 49999; i++){
lcd.clear();lcd.print("Trying Reg");lcd.setCursor(14,14);lcd.print(i);delay(500);
//analogWrite(PWMpin, i);
// lcd.clear();lcd.print("RS485Transmit");lcd.setCursor(14,14);lcd.print(RS485Transmit);delay(1000);
// digitalWrite(txnPinDir, RS485Receive);
// digitalWrite(txnPinDir, RS485Transmit);
lcd.clear();lcd.print("Reading");delay(200);
result = node.readHoldingRegisters(i, 2);
lcd.clear();lcd.print("After Read");delay(200);

lcd.clear();lcd.print("RESULT");lcd.setCursor(12,12);lcd.print(result);delay(1500);
// digitalWrite(txnPinDir, RS485Receive);

if (result == node.ku8MBSuccess) {
lcd.clear();lcd.print("Display result");delay(1000);
/* for (j = 0; j < 2; j++)
{
data[j] = node.getResponseBuffer(j);
lcd.print(data[j]);
Serial.println(data[j]);
}*/
}
//digitalWrite(txnPinDir, RS485Transmit);
lcd.print("No Success");delay(50);
delay(10);
}
//result = node.readHoldingRegisters(42004, 2);
//delay(100);lcd.clear();lcd.print(result);delay(100);

// Serial.println("Trying");Serial.println(result);
// Serial.println(txnPinDir);Serial.println(RS485Transmit);
// digitalWrite(txnPinDir, RS485Transmit);

// digitalWrite(txnPinDir, RS485Receive);
// Serial.println(txnPinDir);Serial.println(RS485Receive);
delay(100);
}

But I am getting result 226 every time.
When I tried using visual studio , ,I am able to read same slave with register 42004 address.
In above arduino code , I m trying all registers , but got no result.

Please help

Did you read the sticky post at the top of the forum?

Enclose code always with code tags (that's the "</>" button in the editor) because otherwise the forum system mangles the code and may hide important parts.

You also forgot to post links to the hardware you used: the RS-485 module for the Arduino, the datasheet of the energy meter you're trying to connect and the wiring you're using.

You should also post a link to the library you're using because there are a lot of libraries for doing ModBus on the Arduino and many of them have similar names but very different functionality.

If your energy meter does follow the ModBus RTU standard you cannot use SoftwareSerial to connect to it because that emulation doesn't provide the necessary serial parameters (ModBus does not support 8N1 but only 8N2, 8E1 and 8O1). There are devices that says to support ModBus but uses 8N1 but they are not really following the standard.

Even if your device uses 8N1 I strongly advise not to use SoftwareSerial. As you have an LCD connected you can display debugging information on it so you don't really need the hardware serial interface to the computer. SoftwareSerial needs a very timing tolerant counterpart. If your device is only a bit more strict in timing issues it won't connect to your Arduino with SoftwareSerial but it probably would if the hardware serial interface is used.

I m using the MAX485 MODULE RS- 485 MODULE TTL to RS- 485 CONVERTER MODULE For ARDUINO .Link is

I am able to read the meter from pc using a c# program via serail port of pc and RS485 port of meter .The connecting link is USB TO RS485 connector.In c# program I am using Baudrate 9600,Databit 8 , stop bit one and Parity None.I am using modbus library for c#.I fetch results using ushort[] holding_register = master.ReadHoldingRegisters(slaveID, startAddress, numOfPoints); in c#.The startAddress =2004, slaveID=1 and numOfPoints=2.

The same I wish to replicate in arduino UNO(as mater) Program.I have connected UNO with Energy meter using connecting link MAX485 MODULE .I have not added any resistor between MAX485 MODULE RX and VCC etc.

The Energy Meter is Premier 300 Secure.I am able to read 2004 tp 2024 regiesters via c# Program.

I still miss the link to the library you're using. Have you tried to change the interface to the hardware serial?

webscada, your code is a total mess, absolutely.
First, put your code in code tags when posting code on the forum.

Then look how many times you define pin 7, I think I count three times.
I can see that you are using the Modbus library from Doc Walker, this library GitHub - 4-20ma/ModbusMaster: Enlighten your Arduino to be a Modbus master.

In your fist post you mention

I am able to read same slave with register 42004 address

Then in your following post you mention

I am able to read 2004 tp 2024 regiesters via c# Program.

What you write is not consistent and leads to confusion for any reader. So exactly what registers can you read via a C# program ?

I took your code and massage it, a lot, getting rid of all LCD and all those delays, just to something readable and more simple.
Take this code and set the registers you wish to read and see how that goes.

#include <ModbusMaster.h>
#include <SoftwareSerial.h>

#define LED_13           13  // define LED pin
#define MAX485_TX_ENABLE  7  // EIA-485 transmit control pin
#define EIA485_RX         9  // EIA-485 serial receive pin
#define EIA485_TX        10  // EIA-485 serial transmit pin

ModbusMaster node;
SoftwareSerial RS485Serial(EIA485_RX, EIA485_TX); // RX, TX

void preTransmission() {
  digitalWrite(MAX485_TX_ENABLE, true);
}

void postTransmission() {
  digitalWrite(MAX485_TX_ENABLE, false);
}

/*---------------------------------------------------------------------------------
 * void setup()
 * Configure initial states and IO ports.
 * REgister Modbus callbacks allow us to configure the RS485 transceiver correctly.
 * Modbus slave ID 1.
 */
void setup() {
  pinMode(MAX485_TX_ENABLE, OUTPUT);
  digitalWrite(MAX485_TX_ENABLE, false);

  Serial.begin(9600);
  RS485Serial.begin(9600);

  node.begin(1, RS485Serial);
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

  Serial.print("My Little SCADA System\n");
}

/*---------------------------------------------------------------------------------
 * void loop()
 * Mission Control Centre:
 * Read 2 registers starting at 0x4000 and print them out to the standard serial port.
 *
 */
void loop() {
  uint8_t result;
//  uint16_t data[6];

  result = node.readHoldingRegisters(0x4000, 2);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Reg 1: ");
    Serial.println(node.getResponseBuffer(1));

    Serial.print("Reg 1: ");
    Serial.println(node.getResponseBuffer(2));
  }
  else {
  Serial.print("Error getting data\n");
  }

  delay(1000);
}

The above code compiles fine, you need to test it for yourself.


Paul VK7KPA

Thanks, I m trying to be neat and clean code.I am trying the above code.
In C# Program , I read registers 2004 to 2025. I understand that the first Holding Register, number 40001, has the Data Address 0000.

My C# Program reads registers 2004 to 2025 .I think that might be 42004 to 42025.Further , I am using USB to RS48 converter between my PC and Energy Meter for reading energy meter from c# program on my PC, using modbus library of c#.

For arduino project , I am trying to read energy meter(slave id) , from arduino uno (master) , connecting link arduino uno-> Max 485 -> to Energy meter.

I have tried the above code , and getting "Error getting data" on serial Monitor.

However Now I am trying to read several registers using for loop , with increment the register number by each step .

Further , I do not know , the procedure , that my MAX485 device is working or not.The output voltage acrorss Max485 A and B is 0.05 volts.

Yes , it is true that my slave device is read from c# program ,only when i use RTU Mode for master.
So , can I use software serial library for arduino uno ? The above code by rockwallaby is using software serial library and my c# program reads slave using 8N1(8 databit, parity none, stop bit 1) .

I have changes the softwareserial port to hardware serial port. New connections are as

Ardunio uno MAX485
RX(PIN 0) RO
TX(PIN 1) D1
PIN 7 RE DE

--rs485 side of MAX485
GND GND
5V VCC

A AND B of MAX 485 to RJ11 port of energy meter

I have now used lcd for debugging.
The code now is

#include <ModbusMaster.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);
#define LED_13           13  // define LED pin
#define MAX485_TX_ENABLE  7  // EIA-485 transmit control pin


ModbusMaster node;

void preTransmission() {
  digitalWrite(MAX485_TX_ENABLE, true);
}

void postTransmission() {
  digitalWrite(MAX485_TX_ENABLE, false);
}

/*---------------------------------------------------------------------------------
 * void setup()
 * Configure initial states and IO ports.
 * REgister Modbus callbacks allow us to configure the RS485 transceiver correctly.
 * Modbus slave ID 1.
 */
void setup() {
  lcd.begin(16,2);
  pinMode(MAX485_TX_ENABLE, OUTPUT);
  digitalWrite(MAX485_TX_ENABLE, false);

  Serial.begin(9600);
  node.begin(1, Serial);
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);

  lcd.print("My Little SCADA System\n");
}

/*---------------------------------------------------------------------------------
 * void loop()
 * Mission Control Centre:
 * Read 2 registers starting at 0x4000 and print them out to the standard serial port.
 *
 */
void loop() {
  uint8_t result;

  result = node.readHoldingRegisters(0x2004, 2);

  if (result == node.ku8MBSuccess)
  {
    lcd.print("Reg 1: ");
    lcd.print(node.getResponseBuffer(1));

    lcd.setCursor(0,1);
    lcd.print("Reg 2: ");
    lcd.print(node.getResponseBuffer(2));
  }
  else {
  lcd.clear();
  lcd.print("Error getting data\n");
  }

  delay(1000);
//}
}

I am still getting the data error.I have identified the error code , it is 226 ku8MBResponseTimedOut.

How can I loop the code line result = node.readHoldingRegisters(0x2004, 2);
so i can vary 2004 and increase by one in each iteration.i.e 2006,2008,2010 etc

Kindly tell me the URL of exact modbus library which i can use , because I have found many , and confused , whih lib will give me result.?

webscada, the library you are using should be okay to use, though I have not used it to prove.
I have used many other libraries and found they have worked as described, and looking at the code behind this library I see no reason this library will not do the same.

Try to diagnose your current program rather than trying other libraries.
Work out methods to help you understand what is going on and how to learn better the communications protocol.
Your issue will more than likely come down to understanding the Modbus slave device and how it needs to communicate.
Once you understand this you can implement the code correctly in the Arduino to successfully get the data.

My guess will be that you are not using the correct addressing as required by the slave.
So, try, experiment and see what results you get.

Things to understand are register type, register address, address base number being zero or one.

You will find many others have successfully worked out correct communications between Arduino and various power meters via the Mobus protocol. You are not the first, so by searching you will find the correct way to do this. It seems a typical engineering question for students I think.


Paul - VK7KPA

Please see , I am using the

Is it OK

It appears to be the correct interface and one that is commonly used.


Paul - VK7KPA

What voltage should I receive on A and B of max485 ?

.Actually I want to check , whether something is happening or not. Should the voltage across A and B fluctuate while sending or receiving data.I am getting 0.05 volt across A and B without fluctations.

For diagnosing purpose , I have also added one LED to pin 7 , it glows continuously , without any blink.and I get 1.15 volt across A and B.

I also want to loop the registers, something like
char holdreg[]; / will increment one in each iteration/

for (int i=0, int < 4000; i++)
{
result = node.readHoldingRegisters(0xholdreg*, 1);*
/* code something like led 13 blink in case of result is sucess */
}
Please help
So that I can check , which holding register is replying or not.

Without knowing what your code is right now, it is difficult to offer concise help.
Put your code in code tags so it formats correctly on the forum, then we can see what you have.


Paul - VK7KPA

Code:

/*
  
#include <ModbusMaster.h>
#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);


#define MAX485_DE      7
#define MAX485_RE_NEG  7

// 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()
{
  uint8_t result;
  lcd.begin(16,2);
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);
  
  Serial.begin(9600);

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

void loop()
{
  char holdreg[2];
  uint8_t result;
  for (int i=0; i < 4000; i++)
   {
     result = node.readHoldingRegisters(0 x holdreg[i], 1);

      if (result == node.ku8MBSuccess)
      {
              lcd.print("Results");delay(1200);
              lcd.print("Reg 1: ");
              lcd.print(node.getResponseBuffer(1));

              lcd.setCursor(0,1);
             lcd.print("Reg 2: ");
             lcd.print(node.getResponseBuffer(2));
    }
 /* code something like led 13 blink in case of result is sucess */
    lcd.print(result);delay(1000);lcd.setCursor(0,1);
    }
   else {
    lcd.print("Error getting data\n");
  }
 
}

CIRCUIT_DIAGRAM.doc (32 KB)

result = node.readHoldingRegisters(0 x holdreg[i], 1);

What do you expect the "0 x holdreg[ i ]" to do, in particular, the "0 x" part ?

In fact on my system, that code will not even compile correctly because of that line of code, giving a Syntax Error.

Again you define pin 7 multiple times, this time only two times, whereas before, you defined it three times.

#define MAX485_DE      7
#define MAX485_RE_NEG  7

You only need to define a pin once, so get rid of one of them and then get rid of the corresponding part in both preTransmission and postTransmission callback routines and setup section. Just like I had it before.

When you code, it is better to only have one, as in 1 statement per line. Avoid doing things like what you do:

lcd.print("Results");delay(1200);

Rather, do it this way for more clearer and readable code:

lcd.print("Results");
delay(1200);

Edit: if those attachments are Microsoft style .doc formats, I won't be loading that up, as I wouldn't trust that. Please use images, such as .PNG for example.


Paul - VK7KPA

In iteration 1 , 0x2004,in iteration 2 ,it should become 0x2005,in iteration 3 it should read 2006 and so on.

My purpose is to read all registers one by one , so that I could know .,what exactly is value of register ,which will give result.I tried this method in c# and succeeded.

I am clearing the code and pin 7 specialy

Circuit

CIRCUIT.pdf (29.6 KB)

You did not answer my first quest about the "0 x readHoldingRegisters" ?
As I said, that code will not compile correctly, not on my system anyhow.

If you are trying to represent it as a hexadecimal number by doing it that way, don't.
Instead, just use the decimal value, so, for register 0x2004, you could ask for resister 8196 decimal.

Why not get a better understanding of the Modbus register map in the slave device you are trying to access rather than trying a guessing game of hit and miss ?

Regarding measuring across A and B lines on the EIA-485 signal, you night not, depending on what you are using to measure the signal and also depending on the data itself.

Sometimes it has been noted that these EIA-485 interface boards have the A and B line the wrong way around, but I have not had this experience myself. But you can try swapping the A and B lines when you have code that compiles and are requesting the correct data type and address from the slave.


Paul - VK7KPA

Looking at your circuit diagram, it appears to me you have an incorrect connection.
I believe the Arduino Tx will connect to DI (Digital Input) on the EIA-485 and the Arduino Rx will connect to the RO (receiver Output) of the EIA-485 interface.


Paul - VK7KPA