1

I'm using Heltec WiFi LoRa32 boards (ESP32-S3 + SX1262).

Could you please help me how to encode data e.g. from temperature and humidity sensor into binary or hexadecimal form - to make the LoRa packet length shorter? Similar to what LoRaWAN does.

For example I have two values:

float temperature = 21.56;
float humidity = 76.21;

I use the official Heltec library for LoRa radio (https://github.com/HelTecAutomation/Heltec_ESP32/blob/master/src/radio/radio.c), I only transfer data via LoRa (not LoRaWAN), so it is necessary to make it work with the library function Radio.Send().

Radio.Send( (uint8_t *)txpacket, strlen(txpacket) ); //send the package out

I found this example: https://create.arduino.cc/editor/LogMaker360/770f60c9-52c5-4d29-b417-4804b278db20/preview and this tutorial: https://www.thethingsnetwork.org/docs/devices/bytes/

I'm confused in this, so I would appreciate a parto of code example compatible with Heltec library for encoding and decoding compatible with Heltec library functions.

Thank you

1
  • 21.56 can be represented by two bytes as 2156
    – jsotola
    Commented Mar 24 at 22:58

2 Answers 2

2

If you want to send both temperature and humidity in a single packet, the easiest way is to define the packet as a struct with two fields:

struct LoraData {
    float temperature;
    float humidity;
};

You can then send the raw bytes by typecasting the address of such a struct to uint8_t*. This, however, is generally not recommended, because it violates C++ type aliasing rules. You can circumvent this issue by defining a union:

union LoraPacket {
    LoraData data;
    uint8_t bytes[sizeof(LoraData)];
};

This represents an area of memory that can be interpreted as containing either the numeric data data, or an array of bytes. Then you can fill the data field and send the raw bytes:

void send_dummy_data()
{
    LoraPacket packet = {
        .data = {
            .temperature = 12.56,
            .humidity = 76.21
        }
    };
    Radio.Send(packet.bytes, sizeof packet);
}

At the other end, you do the same thing backwards: you fill in the bytes and read back the numeric data:

void rx_done(uint8_t *payload, uint16_t size, int16_t, int8_t)
{
    if (size != sizeof(LoraPacket)) {  // sanity check
        Serial.println("Bad packet size");
        return;
    }
    LoraPacket packet;
    memcpy(&packet.bytes, payload, size);
    Serial.print("temperature: ");
    Serial.println(packet.data.temperature);
    Serial.print("humidity: ");
    Serial.println(packet.data.humidity);
}
0

Radio.Send( (uint8_t *)txpacket, strlen(txpacket) );

Noted that strlen(txpacket) is only applicable if the txpacket is a string. Since temperature is a floating point value, it should be sizeof(temperature)(see further explanation below).

I think there is a misunderstanding that this send a "shorter" data because you saw the uint8_t, but it is not the case. A single-precision floating point data is stored as a 4-byte data in according to IEEE754 format. What the RadioSend((uint8_t *) temperature, sizeof(temperature)) does is it simply cast the floating point value to a pointer of byte (i.e. uint8_t) that is point to the beginning of the temperature in memory so that it can send the data byte-by-byte, the sizeof(temperature) is equivalent to sizeof(float) so it is equal to 4. The data that going through the radio transmission is still 4-byte floating point value.

To send the temperature and humidity, you just need to send the temperature and humidity data as:

float temperature = 21.56;
float humidity = 76.21;

RadioSend((uint8_t *) temperature, sizeof(temperature));
RadioSend((uint8_t *) humidity, sizeof(humidity));

However, as point out by @jsotola, if you want, there is a way to reduce the data to be sent from 4-byte floating point to 2-byte int16_t by converting the float to int16_t by multiply the floating point value with 100. The converted data is only 2-byte long as an int16_t without losing the precision of the floating point (which has precision of 2 decimal points).

float temperature = 21.56;
float humidity = 76.21;

int16_t temperature_in_integer = (int16_t) temperature * 100; // integer 2156
int16_t humidity_in_integer = (int16_t) humidity * 100;  // integer 7621

// send 2-byte each
RadioSend((uint8_t *) temperature_in_integer, sizeof(temperature_in_integer));
RadioSend((uint8_t *) humidity_in_integer, sizeof(humidity_in_integer)); 

On the receiving end, the received data need to be divided by 100 to get back the actual temperature and humidity value.

If this is a little confuse to you, I would suggest you pick up a book of C or C++ and learn the foundation of data types, data casting and pointer.

7
  • Glad to hear that, if this helps, consider to accept it as answer by clicking the "tick" sign.
    – hcheung
    Commented Mar 25 at 11:06
  • But what about if I want to send the values in one packet - how to pack it? Especially when on some nodes I measure different kind of values? And how to deal with the received data on the receiver's side? Thanks Commented Mar 25 at 11:07
  • You don't need to purposely pack it, it send 4+4 bytes. Personally I don't know how internally the RadioSend and Lora handle the data, so I can' really answer that. For receiving, the int16_t data is quite straightforward. For float, you can read my answer to this question on how to read back the float from the received buffer.
    – hcheung
    Commented Mar 25 at 11:15
  • I need to send all values in one packet and then on the receiver's side decode it and seperate the palyoad to individual values. Is this possible? Commented Mar 25 at 12:10
  • Come to think about it, You can create an array of float (or a struct of float) and send in 8-byte all together. array[0] be the temperature value and array[2] be the humidity value.
    – hcheung
    Commented Mar 25 at 12:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.