Float to HEX without using String type?

Hi all,

I am fiddling around with a LoRa module, in oder to send data to the air I have to convert such a data into HEX format, so far I am having trouble with it, I got a measurement of temperarture which looks like this

float temperature = 27.5;
char data[20];

The function that I can use for sending that data to the LoRa module is this

* @param char* message: char array that will be send
 * 
 * @remarks data is a sequence of digit representing the value of byte stream
 * expressed in hexadecimal value (i.e. radio tx 12A435 – the payload 
 * is composed by the following byte stream: 0x12, 0xA4, 0x35 – 6 digit
 * converted in 3 bytes). The maximum length of frame is 510 digit (255 Bytes)
 * for LoRa modulation and 128 digit (64 bytes) FSK modulation

uint8_t LoRaWAN::sendRadio(char * message)

So, I have tried quite some functions like dtostrf , dtostrf or strtoul to convert my float into a string and later send it out, so far none of those worked out. The only one which was half working was itoa, in the snippet below, but of course I was missing the value after the dot of the float number

/Converting the temperature into HEX string
  itoa (temperature,data,16);//<=Works!
  
  print("Tempererature as HEX ->");
  println(data);

  print("Data to be send through LoRa ->");
  println(data);
  // Send packet
  error = LoRaWAN.sendRadio( data);

Therefore, I was wondering if you have any suggestions to be able to send my float number into HEX without lossing the decimal value.

Thanks in advance,

REgards!

EDIT: using stroul

dtostrf(temperature,4,2,data);
  
  unsigned long ul = strtoul (data, NULL, 0);
  
  
  USB.print("Tempererature as HEX ->");
  USB.println(ul);

  USB.print("Data to be send through LoRa ->");
  USB.println(ul);
  // Send packet
  error = LoRaWAN.sendRadio( (char*)ul);

Shows on serial

Temperature: 27.7500000000
Tempererature as HEX ->27
Data to be send through LoRa ->27
Error waiting for packets. error = 1

I am fiddling around with a LoRa module, in oder to send data to the air I have to convert such a data into HEX format, ...

Why?

[quote author=Nick Gammon link=msg=2698217 date=1459930340] Why? [/quote]

Because LoRa standard defines that what goes to the AIR should be HEX format at leas in p2p, and the chipset RN2483 asks for

No, it does not. Hex is just a representation of a variable. Being it int, unsigned long, char of float. What you probably want is to split the float into bytes (of unsigned chars) and send each byte and restore it on the receiving end :wink:

Something like:

union fsend_t{
  float f;
  unsigned char c[4];
};

void sendFloat(float data){
  union fsend_t floatConvert;
  
  floatConvert.f = data;
  
  for(byte i = 0; i < 4; i++){
    sendChar(floatConvert.c[i]);
  }
}

float receiveFloat(){
  union fsend_t floatConvert;
  
  for(byte i = 0; i < 4; i++){
    floatConvert.c[i] = nextReceivedChar();
  }
  
  return floatConvert.f;
}

ndarkness:
Because LoRa standard defines that what goes to the AIR should be HEX format at leas in p2p, and the chipset RN2483 asks for

It would help to link to this standard which specifies that you have to send stuff “in HEX”.

[quote author=Nick Gammon link=msg=2698375 date=1459935542] It would help to link to this standard which specifies that you have to send stuff "in HEX". [/quote]

The standard does not really impose the use of HEX as payload, but the only chip manufacturer does RN2843 command reference. See page 46 section 2.5.2

Regards!

That's indeed a doubtful sentence. But there is no such thing as a hex variable type... You just need to split the float into the 4 bytes it's build from.

septillion:
No, it does not. Hex is just a representation of a variable. Being it int, unsigned long, char of float. What you probably want is to split the float into bytes (of unsigned chars) and send each byte and restore it on the receiving end :wink:

Something like:

union fsend_t{

float f;
 unsigned char c[4];
};

void sendFloat(float data){
 union fsend_t floatConvert;
 
 floatConvert.f = data;
 
 for(byte i = 0; i < 4; i++){
   sendChar(floatConvert.c[i]);
 }
}

float receiveFloat(){
 union fsend_t floatConvert;
 
 for(byte i = 0; i < 4; i++){
   floatConvert.c[i] = nextReceivedChar();
 }
 
 return floatConvert.f;
}

Hi septillion , thanks for the code, but then I will miss my dot, being unable to distinguish were it was. I was thinking of converting each element to HEX send the whole packet and after decoding it back from HEX.

septillion:
No, it does not. Hex is just a representation of a variable. Being it int, unsigned long, char of float. What you probably want is to split the float into bytes (of unsigned chars) and send each byte and restore it on the receiving end :wink:

Something like:

union fsend_t{

float f;
 unsigned char c[4];
};

void sendFloat(float data){
 union fsend_t floatConvert;
 
 floatConvert.f = data;
 
 for(byte i = 0; i < 4; i++){
   sendChar(floatConvert.c[i]);
 }
}

float receiveFloat(){
 union fsend_t floatConvert;
 
 for(byte i = 0; i < 4; i++){
   floatConvert.c[i] = nextReceivedChar();
 }
 
 return floatConvert.f;
}

ndarkness:
Septillion, where do you conver the float to the unsigned char c in your code?

I don’t convert it, I split it with the union. Read up on unions :wink:

No you don’t miss your dot. A float is build up from 4 bytes (the Arduino has 8-bit size registers). So a float is only a type that exists in the IDE, the compiler uses 4 bytes to store it’s data. With the union you can access the same registers either as 4 bytes (the c array) or as 1 float. Then you can send each byte piece over and reconstruct it the other way around. And you end up with exactly the same float. It’s just splitting and gluing the float into/from bytes.

Btw, floats on a 8-bit Arduino are not that great. The Uno has no float hardware so it does all the calculations by expanding it to integer math which is slow and big. In 99% of the times here it’s just easier to go with a fixed point representation. If you want to use a temperature with 2 decimals don’t store it as a float in Celsius but in a int as deci-Celsius (so not 20,52 but 2052) and add the decimal later :slight_smile: Math is now wayyyyyy easier.

septillion: I don't convert it, I split it with the union. Read up on unions ;)

No you don't miss your dot. A float is build up from 4 bytes (the Arduino has 8-bit size registers). So a float is only a type that exists in the IDE, the compiler uses 4 bytes to store it's data. With the union you can access the same registers either as 4 bytes (the c array) or as 1 float. Then you can send each byte piece over and reconstruct it the other way around. And you end up with exactly the same float. It's just splitting and gluing the float into/from bytes.

Ok, I will take a look into unions, thanks for the explanation

septillion: Btw, floats on a 8-bit Arduino are not that great. The Uno has no float hardware so it does all the calculations by expanding it to integer math which is slow and big. In 99% of the times here it's just easier to go with a fixed point representation. If you want to use a temperature with 2 decimals don't store it as a float in Celsius but in a int as deci-Celsius (so not 20,52 but 2052) and add the decimal later :) Math is now wayyyyyy easier.

I had come up with the same solution!!! :D Math always does!!!

This little thermometer sketch shows one way of many to eliminate floats. :slight_smile:

// LM35 thermometer, no floats, no delays



byte numSamples = 8;

char fudge = 90; // adjust for calibration 

int kAref = 1090, // analog ref voltage * 1000
    kNumSamples = numSamples * 1000,
    fin = 2000,
    tempC,
    tempF;
    
uint32_t temp,
         start;

void setup()
{
  Serial.begin(9600);
  analogReference(INTERNAL);
  analogRead(0);
  for(int i = 0;i < numSamples;i++)
    temp += analogRead(0);
}
void loop()
{
  if(millis() - start > fin){
    start = millis();
    temp -= (temp / numSamples);
    temp += analogRead(0);
    tempC = temp * kAref / (kNumSamples + fudge);
    tempF = (tempC * 18 + 3200) / 10;
    Serial.print(analogRead(0));
    Serial.print("\t");
    Serial.print(temp);
    Serial.print("\t");
    prntTempC();
    prntTempF();
    Serial.println();
  }
}
    
void prntTempC(){
  Serial.print(tempC / 10);
  Serial.print(".");
  Serial.print(tempC % 10);
  Serial.print("\t");
}
void prntTempF(){  
  Serial.print(tempF / 10);
  Serial.print(".");
  Serial.print(tempF % 10);
  Serial.print("\t");
}

A few lines from serial monitor.

255	2042	27.5	81.5	
255	2042	27.5	81.5	
255	2042	27.5	81.5	
255	2042	27.5	81.5	
256	2043	27.5	81.5	
255	2044	27.5	81.5	
256	2045	27.5	81.5	
256	2046	27.5	81.5	
255	2047	27.5	81.5	
256	2048	27.5	81.5

outsider: This little thermometer sketch shows one way of many to eliminate floats. :)

HI outsider, thanks for the code! I think I can use it in the receiver to decode the incoming value.

On the other hand, now I am wondering, how to handle negative values? In that case I cannot use itoa since it does not handle negative values because it uses unsigned int...

Any suggestion?

Why do you want to use itoa? All the string convertion is done in serial.print().

For negative numbers you have to watch the modulo indeed:

Serial.print(val / 10);
Serial.print('.');
Serial.print(abs(val) % 10); //abs fixes the modulo