How to send float value and convert it into bytes

Dear all,

I am really blocked into a problem that I can not solve and understand since a couple of days. I created a new topics as my development evoluated and the way to structure my code has changed, I modifed my code following this way to send data
https://www.thethingsnetwork.org/docs/devices/bytes.html#how-to-send-multiple-numbers

I would really appreciate if you can help :o)

Let’s resume first
I got values from sensors. The function return me a float value with decimal (value can be negative)

float float_temperature = get_temperature();
float float_pression = get_pression();
float float_humidity = get_humidity();
float float_moiature = get_moisture();
float float_luminosity = get_luminoisty();
float float_battery = get_battery();

As I do not want to have a decimal, I covnerted it into int

// let's say, float_temperature is egual to -10.55
float_temperature = float_temperature*100;
int int_temperature = float_temperature;
Serial.println(int_temperature)

Serial.println() should print

-1055

Now I need to convert into bytes and I have serveral values to send to TTN.

I red the following page working with bytes and particularely this sections
https://www.thethingsnetwork.org/docs/devices/bytes.html#how-to-send-multiple-numbers
and
[https://www.thethingsnetwork.org/docs/devices/bytes.html#how-to-send-negative-numbers](https://www.thethingsnetwork.org/docs/devices/bytes.html#how-to-send-

Library (for info)

I work around the library LoRa-LMIC-1.51 and I am based on that exemple (FYI)
https://github.com/mikenz/LoRa-LMIC-1.51/blob/master/examples/nano-lmic-v1.51-F/nano-lmic-v1.51-F.ino

My problem

I need to send float values and to convert it into bytes. I also several values (may be too much).
In the Lora-LMIC-1.5 exemple, the variable uint8_t mydata[64] is used to store the value and sen it to TTN.

I absoluetly can not convert and send it but now i would like to work around the TTN exemple and I would like to ask you some help regarding the comment I am adding below

Let’s start :

Concidering this

byte payloadA[] = { 0xF0 };
byte payloadB[] = { 0xF0, 0x0F };
byte payloadC[] = { 0xF0, 0x0F, 0xFF };
    
int sizeofPayloadA = sizeof(payloadA);
int sizeofPayloadB = sizeof(payloadB);
int sizeofPayloadC = sizeof(payloadC);
    
byte payload[sizeofPayloadA + sizeofPayloadB + sizeofPayloadC];
    
memcpy(payload, payloadA, sizeofPayloadA);
memcpy(payload + sizeofPayloadA, payloadB, sizeofPayloadB);
memcpy(payload + sizeofPayloadA + sizeofPayloadB, payloadC, sizeofPayloadC);

and the values

int int_battery = 345;
int int_temperature = -1025;
int int_pression = 2222;
int int_humidity = 4321;
int int_moisture = 1234;
int int_luminosity = 56;

This is my big problem. (I am really sory if I have diffuclties to get it :face_with_raised_eyebrow: ). Even if I read that int, long, char are all bytes. That right?
(How can I convert it? I did it manually with my values)

byte payloadBattery[] = {0x33 0x34 0x35} // = 345
byte payloadTemperature[] = { 0x2D 0x31 0x30 0x32 0x35 }; // = -1025
byte payloadPression[] = { 0x32 0x32 0x32 0x32 }; // = 2222
byte payloadHumidity[] = { 0x34 0x33 0x32 0x31 }; // = 4321
byte payloadMoisture[] = {0x31 0x32 0x33 0x34}; // = 1234
byte payloadLuminosity[] = { 0x35 0x36 }; // = 56

But HOW to do it programly keeping the negative values???
I am sorry but I really have pain to understand the convertion

Then, Here I count the size of my measure variables

int sizeofPayloadBattery = sizeof(payloadBattery);
int sizeofPayloadTemperature = sizeof(payloadTempearture);
int sizeofPayloadPression = sizeof(payloadPression);
int sizeofPayloadHumidity = sizeof(payloadHumidity);
int sizeofPayloadMoisture = sizeof(payloadMoisture);
int sizeofPayloadLuminosity = sizeof(payloadLuminosity);

Here I declare my payload variable and it size is the addition of value
it may be possilbe, it over 51.But let’s try to understand, and later, I will see how to split my send. Ok?
(or let’s work only with 3 sensors)

byte payload[sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity + sizeofPayloadMoisture + sizeofPayloadLuminosity ];

Here I copy the data into payload. I added some if() for information. I will make it batter later

memcpy(payload, payloadBattery, sizeofPayloadBattery);
memcpy(payload + sizeofPayloadBattery, payloadTemperature, sizeofPayloadTemperature);
memcpy(payload + sizeofPayloadBattery + sizeofPayloadTemperature, payloadPression, sizeofPayloadPression);
// from now I hope, I continue cottectly. Pls correct me
if((sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity) > 51) Serial.println(F("playload is over (4)"));
memcpy(payload + sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression, payloadHumidity, sizeofPayloadHumidity);
if((sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity + sizeofPayloadMoisture) > 51) Serial.println(F("playload is over (5)"));
memcpy(payload + sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity, payloadMoisture, sizeofPayloadMoisture);
if((sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity + sizeofPayloadMoisture + sizeofPayloadLuminosity) > 51) Serial.println(F("playload is over (6)"));
memcpy(payload + sizeofPayloadBattery + sizeofPayloadTemperature + sizeofPayloadPression + sizeofPayloadHumidity + sizeofPayloadMoisture, payloadLuminosity, sizeofPayloadLuminosity);

If the lora_LMIC_1.5 exemple use uint8_t type of variable, I guess byte payload variable can be used as well?

Does my code make sens? May I ask you to correct it, but first and particularely the conversion int into byte /hex
byte payloadBattery = {0x33 0x34 0x35} // = 345

I really rally thank your for any help you can tell and teach me

Cheers

What you are doing is converting to an ascii representation of the digits of your values. You should be converting the value itself.

A simple example. Let’s take an integer i, and set it to 1.

int i = 1;

Let’s assume an integer on your CPU is 16 bits wide. That means to store an integer into a byte array (byte is 8 bits wide) you need to split the integer into two bytes:

byte payload[2];
payload[0] = i >> 8; // shift the 8 bits to the right to get the upper 8 bits into a byte, this is the same as division by 256
payload[1] = i & 0xff; // mask the upper 8 bits, this is the same as taking the remainder after division by 256

The result would be an array with the values [0, 1]

For the number 16551 the values would be [16551/256, 16551%256] = [64, 167] as 256*64 + 167 = 16551.

2 Likes

Dear kersing,

Thank for your reply!!

Tell me if I am wrong.

float fval=123.4456;
byte bval = fval;
Serial.println(bval)

bval would print 123? (I suppose yes)

It’s like with an int?

Then following you written

Let’s assume an integer on your CPU is 16 bits wide. That means to store an integer into a byte array (byte is 8 bits wide) you need to split the integer into two bytes

Should I better use byte instead of int

byte int_battery = 345; // I will rebane it byte_battery
byte int_temperature = -1025;
byte int_pression = 2222;
byte int_humidity = 4321;
byte int_moisture = 1234;
byte int_luminosity = 56;

and what baout this?

byte payloadBattery[] = int_battery;

Does payloadBattery will print?

33 34 35

Thank a lot for your comment :grinning:

Cheers

That is right. Things go south from that point on…

Every data type in C is stored in memory locations. Every memory location is one byte. So an integer (16 bits) is stored in two bytes.
A byte can contain values 0 to 255. Or when working with signed data types -128 to 127. So trying to store a value larger then 255 in a byte means information will be lost. However, by dividing the larger value and putting both the result and the remainder in separate bytes you are able to ‘store’ the larger number.
Back to the integer, an unsigned one can have values 0 to 65535. As you can see this will not fit in the range 0 to 255. So we need to store it in multiple bytes. If we divide 65535 by 256 (the max value of a byte + 1) we get 255. That can be stored in a byte. The remainder of that division is 255 which can be stored in a byte as well. So the result would be an array with two bytes [255, 255].

Trying to assign an array from a value without doing the calculations yourself (your example byte payloadBattery = int_battery) will not get any useful result. The correct way to do this:

int int_battery = 345;
byte payloadBattery[2];
payloadBattery[0] = int_battery / 256;
payloadBattery[1] = int_battery % 256;

2 Likes

Dear Lersing,

Thank thank thank a lo for this information, I think you gave me THE solution. I will not try yet. It’s late but let ask you some question regarding your comment

Every data type in C is stored in memory locations. Every memory location is one byte. So an integer (16 bits) is stored in two bytes

And what about a byte? I mean, if int_battery is not a int but a byte

byte int_battery = 345;

You wrote me it take less space in the memory. Do will I to do the same?

byte int_battery = 345;
byte payloadBattery[2];
payloadBattery[0] = int_battery / 256;
payloadBattery[1] = int_battery % 256;

I ma sorry for lacke of knowledge

However, by dividing the larger value and putting both the result and the remainder in separate bytes you are able to ‘store’ the larger number.

Then, what will be the lagest number that I can save, keeping in mind, I need to work with negative number

For exemple, temperature can be -10°
and Latitude can be 46.723467 or 46723467 (If I remove de decimal)

int int_battery = 46723467 ;
byte payloadBattery[2]; // Should I change 2 for a large number or negative?
payloadBattery[0] = int_battery / 256;
payloadBattery[1] = int_battery % 256;

If read again this

an unsigned one can have values 0 to 65535

Then as my number must be signed, I supposed my latitude will not fit?

I will read again your comment. It’s true I did not understand all, but I will take time to read it in the train, tomorrow morning

Thank a lot, I am unpatient to try

Cheers

That’s too much detail, the 6th decimal would indicate a precision of tens of centimeters, which your GPS probably does not really give you, and your use case probably does not need either. Just 4 decimals should suffice; see Best practices when sending GPS location data [HowTo]. That also describes how to send and decode negative values.

Please also go back to my reply to your earlier question; What is the maximum number of characters I can send with LMiC? - #6 by arjanvanb You should really understand the two examples I gave there, before even trying to encode and decode data. If not: follow the links to the converters in that answer and try some values yourself, and take time to re-read Working with Bytes.

2 Likes

I have created a code which I intended to explain to students. I found it dest to transfer the data into the send buffers only at the very last moment, and remain using int and float wherever else.

Also the example uses the gps values and converts this into a 3 byte value. Once I understood that bit of code, the rest was easy. I have added comments so I could trace back my thinking.

Review the code and comments here: https://github.com/Kaasfabriek/GPS-Lora-Balloon-rfm95-TinyGPS/blob/master/Balloon-rfm95/Balloon-rfm95.ino
And find the line which starts with:
// byte 0, 1, 2

2 Likes

Nice! But since you asked for reviewing, some minor remarks which you might already know about:

While correct, the logical & 0xFF is not needed. With uint8_t mydata[message_size] anything but the least significant byte is discarded anyway. (The other bytes are not copied into an adjacent memory location; if that were the case one would not need 3 lines of code to start with.) So, the following would suffice:

mydata[0] = LatitudeBinary >> 16;
mydata[1] = LatitudeBinary >> 8;
mydata[2] = LatitudeBinary;

Though the short version might surely confuse readers at first, they’ll also run into many examples where no & 0xFF is used, so better prepare them. :wink:

Also, I guess you know it’s possible to send negative values, when taking care of sign extension in the payload function when sending fewer than 4 bytes for a value.

As for the decoder function:

Again, correct. But as you’re doing bitwise operations (which will make JavaScript return 32 bits rather than some number), I’d use that all the way. Apart from theoretically being faster, also no parentheses are needed in the following, as (unlike addition) the bitwise OR operator has lower precedence than the bitwise left shift:

var _lat = (bytes[0] << 16 | bytes[1] << 8 | bytes[2]) / 16777215.0 * 180.0 - 90;

And finally:

If you don’t care about the slash between the 9th and 10th value, then to nicely get leading zeroes, you could use something like:

var _inputHEX = bytes.map(function(b) {
  return ('0' + b.toString(16)).substr(-2);
}).join(' ');
1 Like

Earlier, my answer above showed:

That’s very wrong. Instead, use:

var _inputHEX = bytes.map(function(b) {
  return ('0' + b.toString(16)).substr(-2);
}).join(' ');
1 Like