Transfer float into holding regsiters

Hi, I'm trying to write a method for transferring a floating point number into 2 holding registers. I convert the float into a long (while scaling up by 400K) then I used This thread to convert the long into two ints which go into holding registers.

float Aval = 3.5634;
  long y = lround(Aval*scale);
  int16_t high = y >> 16;
  int16_t low = (int16_t)y;
  holdingRegs[Aa] = low;
  holdingRegs[Ab] = high;

It works great - but I need to do it for many different values in my code, and I can't get the general method below to work - could someone please point out what I'm missing? Many thanks,

void floatconv (float input, int16_t Aa, int16_t Ab){
    long lconv = lround(input*scale);
    Aa = lconv >> 16;
    Ab = (int16_t)lconv;
}

 float Bval = 3.5634;
 floatconv(Bval, holdingRegs[Ba], holdingRegs[Bb]);

The fuller version of the above code is here:

#include <ModbusRtu.h>

#define ID          1
#define TXEN        2

long scale = 400000000;

enum
{
  Aa,      //CHANNEL 0
  Ab,      //CHANNEL 1
  Ba,      //CHANNEL 2
  Bb,      //CHANNEL 3
  HOLDING_REGS_SIZE
};

unsigned int holdingRegs[HOLDING_REGS_SIZE];
//*/
//Modbus slave(1, 0, TXEN); // this is slave @1 and RS-485

void setup() {
//////////////////////////////////////////////////////////////////////////////////////////////
  // start modbus communication
  //slave.begin( 115200 );

  Serial.begin(115200);
}

  float Aval = 3.5634;
float Bval = 3.5634;

void floatconv (float input, int16_t hAa, int16_t hAb){
    long lconv = lround(input*scale);
    hAa = lconv >> 16;
    hAb = (int16_t)lconv;
}

void loop() {
  long y = lround(Aval*scale);
  int16_t high = y >> 16;
  int16_t low = (int16_t)y;
  holdingRegs[Aa] = low;
  holdingRegs[Ab] = high;

  floatconv(Bval, holdingRegs[Ba], holdingRegs[Bb]);

  //modbus communication
  //slave.poll( holdingRegs, HOLDING_REGS_SIZE );

  Serial.print("Aa: "), Serial.println(holdingRegs[Aa]);
  Serial.print("Ab: "), Serial.println(holdingRegs[Ab]);
  Serial.print("Ba: "), Serial.println(holdingRegs[Ba]);
  Serial.print("Bb: "), Serial.println(holdingRegs[Bb]);
  Serial.println("");

  delay(50000);
}

Pass the parameters to your function either by pointers or by reference.

Read this article for more info :slight_smile:

Assuming you're working on a processor where floats are 32 bits, this works:

void setup() {
  float Aval;
  uint16_t aReg, bReg;
  void *ptr;

  Serial.begin(115200);
  delay(2000);

  Aval = 3.5634;
  floatconv(Aval, &aReg, &bReg);
  ptr = &Aval;
  Serial.println(*(uint32_t *)(ptr), HEX);
  Serial.print(aReg, HEX);
  Serial.print(", ");
  Serial.println(bReg, HEX);

}

void loop() {}

void floatconv(float input, uint16_t *hAa, uint16_t *hAb) {
  memcpy((uint8_t *)hAa, (uint8_t *)&input, 2);
  memcpy((uint8_t *)hAb, (uint8_t *)&input + 2, 2);
}

Going through a void * to print the hex bytes of the float stops the compiler from whining about "dereferencing type-punned pointer will break strict-aliasing rules"

Easily done with a union.

union
{
    long y;
    struct
    {
        int Aa;
        int Ab;
    };
}some_name_here;

Now, if you assign some_name_here.y = lround(input*scale);, some_name_here.Aa contains the low order int and some_name_here.Ab contains the high order int.

Hi, thanks darrob, DKwatson and gfvalvo. GF I used your code. It works great now. I had a look at the page darrob, and its very useful/essential though I think I still don't understand them lol.

I converted them to a long instead as it seems to function better with my receiving modbus program (DAQFactory) - (set four channels to read S32s on channels 0, 2, 4 and 6).

Many thanks all,

Please find the code below if anyone finds it useful:

#include <ModbusRtu.h>

#define ID          1
#define TXEN        2

long scale = 400000;
long lconv = 0;

float Aval = 3.563543;
float Bval = 5.46233;
float Cval = -1.55065;
float Dval = -2.34223;

enum
{
  Aa,      //CHANNEL 0
  Ab,      //CHANNEL 1
  Ba,      //CHANNEL 2
  Bb,      //CHANNEL 3
  Ca,      //CHANNEL 0
  Cb,      //CHANNEL 1
  Da,      //CHANNEL 2
  Db,      //CHANNEL 3
  HOLDING_REGS_SIZE
};

unsigned int holdingRegs[HOLDING_REGS_SIZE];

/**
    Modbus object declaration
    u8id : node id = 0 for master, = 1..247 for slave
    u8serno : serial port (use 0 for Serial)
    u8txenpin : 0 for RS-232 and USB-FTDI
                 or any pin number > 1 for RS-485
*/
Modbus slave(1, 0, TXEN); // this is slave @1 and RS-485

void setup() {
//////////////////////////////////////////////////////////////////////////////////////////////
  // start modbus communication
  slave.begin( 115200 );

  //Serial.begin(115200);
}

void loop() {
  //long time1 = millis();
  
  floatconv(Aval, &holdingRegs[Aa], &holdingRegs[Ab]);
  floatconv(Bval, &holdingRegs[Ba], &holdingRegs[Bb]);
  floatconv(Cval, &holdingRegs[Ca], &holdingRegs[Cb]);
  floatconv(Dval, &holdingRegs[Da], &holdingRegs[Db]);
  //modbus communication
  slave.poll( holdingRegs, HOLDING_REGS_SIZE );

  //Serial.print("Aa: "), Serial.println(holdingRegs[Aa]);
  //Serial.print("Ab: "), Serial.println(holdingRegs[Ab]);
  //Serial.print("Ba: "), Serial.println(holdingRegs[Ba]);
  //Serial.print("Bb: "), Serial.println(holdingRegs[Bb]);
  //Serial.print("Ca: "), Serial.println(holdingRegs[Ca]);
  //Serial.print("Cb: "), Serial.println(holdingRegs[Cb]);
  //Serial.print("Da: "), Serial.println(holdingRegs[Da]);
  //Serial.print("Db: "), Serial.println(holdingRegs[Db]);
  //Serial.println("");

  delay(1000);
}

void floatconv(float input, uint16_t *ha, uint16_t *hb) {
  long lconv = lround(input*scale);
  memcpy((uint8_t *)ha, (uint8_t *)&lconv, 2);
  memcpy((uint8_t *)hb, (uint8_t *)&lconv + 2, 2);
}