Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transmit and receive LONG and Float data by Modbus. #158

Closed
Merdock1 opened this issue Oct 20, 2021 · 6 comments
Closed

Transmit and receive LONG and Float data by Modbus. #158

Merdock1 opened this issue Oct 20, 2021 · 6 comments

Comments

@Merdock1
Copy link

This is shared with us as information taken from the industruino page.

These examples are based on the fact that most devices send data in this format:
float / ulong / long type data, Communication transmission in byte order2-1-4-3

To separate a float value into 2 registers.

unsigned int f_2uint_int1(float float_number)
{ // split the float and return first unsigned integer

    union f_2uint
    {
        float f;
        uint16_t i[2];
    };

    union f_2uint f_number;
    f_number.f = float_number;

    return f_number.i[0];
}

unsigned int f_2uint_int2(float float_number)
{ // split the float and return first unsigned integer

    union f_2uint
    {
        float f;
        uint16_t i[2];
    };

    union f_2uint f_number;
    f_number.f = float_number;

    return f_number.i[1];
}

It's used

var_float = 3.14

unsigned int reg0 = f_2uint_int2(var_float );           // split the float into 2 unsigned integers
unsigned int reg1 = f_2uint_int1(var_float );

To retrieve floating value from 2 registers.

float f_2uint_float(unsigned int uint1, unsigned int uint2) {    // reconstruct the float from 2 unsigned integers

  union f_2uint {
    float f;
    uint16_t i[2];
  };

  union f_2uint f_number;
  f_number.i[0] = uint1;
  f_number.i[1] = uint2;

  return f_number.f;

}

It's used

float float_reconstructed = f_2uint_float(reg1, reg0);

To separate LONG data the code:

unsigned int l_2uint_int1(long long_number) {             // split the long and return first unsigned integer

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.l = long_number;

  return l_number.i[0];
}

unsigned int l_2uint_int2(long long_number) {            // split the long and return first unsigned integer

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.l = long_number;

  return l_number.i[1];
}

It's used:

long_number = 4123546
 unsigned int reg0 = l_2uint_int2(long_number);           // split the long into 2 unsigned integers
 unsigned int reg1 = l_2uint_int1(long_number);

To rebuild LONG from two registers.:

long l_2uint_long(unsigned int uint1, unsigned int uint2) {    // reconstruct the long from 2 unsigned integers

  union l_2uint {
    long l;
    uint16_t i[2];
  };

  union l_2uint l_number;
  l_number.i[0] = uint1;
  l_number.i[1] = uint2;

  return l_number.l;

}

It's used:

long long_reconstructed = l_2uint_long(reg1, reg0); // reconstruct the long from 2 unsigned integers

@emelianov
Copy link
Owner

Awesome!

@stritti
Copy link

stritti commented Feb 15, 2022

would be awesome to have this integrated directly in API :)

@emelianov
Copy link
Owner

@stritti
Data types presentation one Modbus (except uint16_t) are just agreements that are outside of Modbus specification. Also real byte order swapping required is depended no endianness of source and destination. So no plans to include this code to the library.

@Merdock1
Copy link
Author

Hello, Here I leave a library to be able to work with different types of data through modbus.
https://github.com/Merdock1/DataConversion
In this library you can work with 1-bit values in a holding register up to 64-bit double values.
I hope someone finds it useful. Greetings.

@prustt
Copy link

prustt commented Dec 18, 2023

First of all, thank you for this amazing library @emelianov and thank you @Merdock1 for simplifying the swap and union bytes.

I'm not sure if it is the right place to comment, if it is not, you may just delete this comment.

Here is the thing, in issues I have seem lots of issues about ESP32 meaning theres lots of ESP32 running the Modbus library and many will come trough here because of floats/doubles. This functions works perfectly, but theres a catch to use them on ESP32, endianness mismatch. Took me a while to figure out why the values after the union were 0.00 and this was the problem.

More specific: The ESP32 is little-endian, meaning the least significant byte is stored first in memory. And the function data source is big-endian, where the most significant byte is stored first, so for the ESP32 you have to swap the order of uint1 and uint2 before assigning them to f_number.i.

What I did was to just change the order on the header of the function.

I used this:
float f_2uint_float(unsigned int uint2, unsigned int uint1)

Instead of:
float f_2uint_float(unsigned int uint1, unsigned int uint2)

Also I made the same change on the long function and it worked just fine.

I'm just commenting this so others dont have to figure this out and specially cause it is not something trivial to know (LSB and MSB) when you are starting on programming microcontrollers.

Paying more attention, this can also be resolved on the slave side:

It's used

var_float = 3.14

unsigned int reg0 = f_2uint_int2(var_float ); // split the float into 2 unsigned integers
unsigned int reg1 = f_2uint_int1(var_float );

I swapped this call on my slave and it worked just the same way when the header is swapped. Mine is:

unsigned int reg0 = f_2uint_int1(var_float );
unsigned int reg1 = f_2uint_int2(var_float ); 

This way theres no need to change the header function on the master side.
If I'm talking non-sense please let me know so I can delete this comment, also if something is unclear or not written the right way, thats because English is not my main language.

Once again, thank you for this amazing library @emelianov and thank you for making my life with floats WAY easier @Merdock1.

Best regards from Brazil!

@Merdock1
Copy link
Author

Hello @prustt.
Here you have several examples of how to work with different types of data.
Modbus only supports 16-bit latch registers.
How you use these registers and split them and rebuild them is arbitrary to each programmer.
There are unwritten conventions on how to do this, but they are not always respected by everyone.
That's why the order in which you divide and rebuild registers larger than 16 bits is at your sole discretion.
Here I leave you examples of what I'm talking about.
I hope this helps you.
https://github.com/Merdock1/DataConversion/tree/main/src/Examples
Schneider Electric
National Instruments
Industruino
Greetings from Uruguay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants