Split from: Reading register using Modbus master/arduino uno

Go To Last Post
10 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am trying to read modbus (rs485) holding registers from a meter. Unfortunately it is showing some garbage value. Also the format specified in the datasheet is float. Can anyone help?

 

#include <ModbusMaster.h>
#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);
  Serial.begin(9600);

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

void loop()
 {
  Serial.print("---------------------: ");

  uint8_t result;
  
  

 
  result = node.readHoldingRegisters(0x1000, 16);
  if (result == node.ku8MBSuccess)
  {
    Serial.print("Vbatt: ");
    Serial.println(node.getResponseBuffer(0x04/100.0f));

  delay(1000);
  }
}

Last Edited: Tue. Jan 23, 2018 - 12:31 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My guess is your library returns you 16 bit registers. Modbus float representation uses two consective 16 bit registers to form a 32 bit ieee754 floating point value. You need to combine two registers into a four byte array in the correct order then cast the array as a float value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

"You need to combine two registers into a four byte array"

 

Like that ?

cat union.cpp 
#include <iostream> // PC specific, for local testing
union gros{ uint16_t a[2];
            uint32_t b;
            float F;
            char Cs[4]; //
    } Gros;
int main(){ // PC specific, for local testing
Gros.a[0]=100;
Gros.a[1]=1;
// this minitest displays Gros.a[1]*65536 + Gros.a[0].... on a PC
std::cout << Gros.b <<std::endl; // PC specific, for local testing
} // PC specific, for local testing
sh-4.1$ c++ union.cpp && ./a.exe
65636

(works on arduino .. for storing/retrieving chars (order is not important, as bytes are stored/retrieved in a consistent order ... for making a float out of two uint16_t s, might need two tests, filling Gros.a[0] with one part, Gros.a[1] with the other part and displaying Gros.F ...)

Last Edited: Tue. Jan 23, 2018 - 04:34 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What if I only want to read the Integer part for time being?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

There is no integer part in a float. (Not "really" anyway).

Floats have a mantissa and an exponent.

The mantissa is some fractional value and represents a number between 0 and 1 (for positive numbers).

 

The magic keyword is in Kartman #2:

ieee754

https://duckduckgo.com/html?q=ie...

https://en.wikipedia.org/wiki/IE...

 

Trying to convert some part by hand to an integer is pretty much useless.

However if you just print the hexadecimal value (lcd / uart) then you can probably make some sense of it and separate mantissa from the exponent.

 

For code examples you can try some different combinations of keywords on github:

AVR, arduino, modbus, float, ieee754, etc.

https://github.com/search?utf8=%...

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

Last Edited: Wed. Jan 24, 2018 - 05:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I am using arduino uno board. It is not supporting #include <iostream>. How should I proceed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think I did't frame the question so well.

 

I have arduino uno board and max485 (https://www.amazon.in/MAX485-RS-...) and a meter (datasheet attached).

 

I want to read the holding register values. I tried with the above code but it is showing some garbage value.(like box and @).

 

So I want to understand where is the bug in the program. Now can you help me?

Attachment(s): 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The usual way is to take small baby steps and verify that each step works before moving on to the next.

Do you have an oscilloscope or a logic analyser?

These are very helpful to check that the data you send or read from I/O pins is actually the data you want there to be.

Get a small logic analyser vrom Ali / Ebay. Search for "24MHz 8ch" and use it with the Sigrok / Pulseview software.

 

On RS-485 you need termination resistors (on both ends of the cable) and you also need some bias resistors to pull th A and B lines apart when nobody is sending on the bus.

Unfortunately you cannot check if this done correctly with a Logic Analyser. You need a scope for that...

 

Do a search for "10 ways to bulletproof RS485".

ti.com/lit/an/snla049b/snla049b.pdf

 

You have toggle the Send/Receive signal on your RS485 chip in the correct way.

This is a bit tricky to do and a Logic Analyser is handy in verifying this is done correctly.

 

RS485 is a multi drop bus.

You can use a "usb to rs485" dongle to snif the info on your RS485 bus, or even send data from your PC.

Software development on a PC is easier than on a uC.

You can also make one yourself by connecting a USB to serial dongle to a rs485 driver chip.

 

OOPS. Mayor fuckup:

I am not familiar with arduino boards.

I think that the uart of your uC is connected to a usb to serial chip.

You can not just add other hardware to that and expect it to work.

Paul van der Hoeven.
Bunch of old projects with AVR's:
http://www.hoevendesign.com

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well, I showed you how to convert 2*16 bits into a 32 bits (I use this trick on arduini)

Each and every line which is PC specific ... is commented as ...PC specific  ans should be removed (you just hacve to read and understand) . These lines were used to test on a PC.... and I commented them, as they cannot work on Arduino....

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The uno has only one serial port. You are trying to share the serial.print port with your modbus- thus the weird chars. An arduino mega might be a better choice