Heltec WiFi LoRa 32 (V3) - кодирует/декодирует пакеты в двоичный код (сообщение становится короче)

esp32 lora esp32-s3

Я использую платы Heltec WiFi LoRa32 (ESP32-S3 + SX1262).

Не могли бы вы мне помочь, как закодировать данные, например, с датчика температуры и влажности, в двоичную или шестнадцатеричную форму, чтобы сократить длину пакета LoRa? Похоже на то, что делает LoRaWAN.

Например, у меня есть два значения:

float temperature = 21.56;
float humidity = 76.21;

Я использую официальную библиотеку Heltec для радио LoRa, я передаю данные только через LoRa (не LoRaWAN), поэтому необходимо заставить ее работать с библиотечной функцией Radio.Send().

Radio.Send( (uint8_t *)txpacket, strlen(txpacket) ); //отправляем пакет

Я нашел этот пример и это руководство.

Я в этом запутался, поэтому был бы признателен за часть примера кода, совместимого с библиотекой Heltec, для кодирования и декодирования, совместимых с функциями библиотеки Heltec.

, 👍2

Обсуждение

21.56 можно представить двумя байтами как 2156, @jsotola


2 ответа


0

Радио.Отправить((uint8_t *)txpacket, strlen(txpacket));

Отмечено, что strlen(txpacket) применимо только если txpacket является строкой. Поскольку температура является значением с плавающей точкой, она должна быть sizeof(temperature)(см. дальнейшее объяснение ниже).

Я думаю, что существует недопонимание, что это отправляет «более короткие» данные, потому что вы видели uint8_t, но это не так. Данные с плавающей точкой одинарной точности хранятся как 4-байтовые данные в соответствии с форматом IEEE754. Функция RadioSend((uint8_t *) temperature, sizeof(temperature)) просто преобразует значение с плавающей точкой в указатель байта (т. е. uint8_t), который указывает на начало temperature в памяти, чтобы можно было отправлять данные побайтно, sizeof(temperature) эквивалентен sizeof(float), поэтому он равен 4. Данные, проходящие через радиопередачу, по-прежнему представляют собой 4-байтовое значение с плавающей точкой.

Чтобы отправить температуру и влажность, вам просто нужно отправить данные о температуре и влажности следующим образом:

float temperature = 21.56;
float humidity = 76.21;

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

Однако, как указал @jsotola, если вы хотите, есть способ сократить объем отправляемых данных с 4-байтового числа с плавающей точкой до 2-байтового int16_t путем преобразования float в int16_t путем умножения значения с плавающей точкой на 100. Преобразованные данные имеют длину всего 2 байта как int16_t без потери точности числа с плавающей точкой (точность которого составляет 2 десятичных знака).

float temperature = 21.56;
float humidity = 76.21;

int16_t temperature_in_integer = (int16_t) temperature * 100; // целое число 2156
int16_t humidity_in_integer = (int16_t) humidity * 100;  // целое число 7621

// отправить по 2 байта каждый
RadioSend((uint8_t *) temperature_in_integer, sizeof(temperature_in_integer));
RadioSend((uint8_t *) humidity_in_integer, sizeof(humidity_in_integer)); 

На принимающей стороне полученные данные необходимо разделить на 100, чтобы получить фактическое значение температуры и влажности.

Если это вас немного сбивает с толку, я бы посоветовал вам взять книгу по C или C++ и изучить основы типов данных, приведения данных и указателей.

,

Рад это слышать. Если это поможет, рассмотрите возможность принять это как ответ, нажав на значок «галочка»., @hcheung

Но что делать, если я хочу отправить значения в одном пакете - как его упаковать? Особенно, когда на некоторых узлах я измеряю разные виды значений? И как быть с полученными данными на стороне получателя? Спасибо, @iotdeveloper

Вам не нужно намеренно упаковывать его, он отправляет 4+4 байта. Лично я не знаю, как RadioSend и Lora обрабатывают данные внутри, поэтому я не могу на это ответить. Для получения данные int16_t довольно просты. Для float вы можете прочитать мой ответ на [этот вопрос](https://stackoverflow.com/questions/78169013/how-to-receive-a-float-array-using-esp8266-via-i2c/78170406#78170406) о том, как прочитать float из полученного буфера., @hcheung

Мне нужно отправить все значения в одном пакете, а затем на стороне получателя декодировать его и разделить нагрузку на отдельные значения. Это возможно?, @iotdeveloper

Если подумать, можно создать массив чисел с плавающей точкой (или структуру чисел с плавающей точкой) и отправить в него все 8 байт. Массив [0] будет значением температуры, а массив [2] — значением влажности., @hcheung

У меня также есть пример в моем [блоге](https://www.e-tinkers.com/2020/05/esp-01-range-test-with-esp-now-protocol/), который показывает температуру и влажность в структуре и отправляет по передаче. Вы можете взглянуть на это. Если у вас есть дополнительные вопросы, вы должны поднять их в отдельном вопросе, StackOverflow/StackExchange лучше подходят для одного вопроса за раз. Не добавляйте вопросы, когда на один уже ответили., @hcheung

Хорошо, а можно ли как-то гарантировать, что при измерении разных переменных можно будет определить, какая переменная находится в пакете в заданной позиции или мне просто нужно это знать? Например, если я отправлю температуру и влажность с одного устройства, а давление, напряжение и ток — с другого? Или просто указать позицию для каждой переменной в пакете, а в случае, если она не будет измеряться, оставить, например, ноль? Спасибо, @iotdeveloper


2

Если вы хотите отправить температуру и влажность в одном пакете, Самый простой способ — определить пакет как struct с двумя полями:

struct LoraData {
    float temperature;
    float humidity;
};

Затем вы можете отправить необработанные байты, приведя тип адреса такого struct в uint8_t*. Однако это обычно не рекомендуется, потому что это нарушает правила псевдонимов типов C++. Вы можете обойти это проблема с определением объединения:

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

Это представляет собой область памяти, которую можно интерпретировать как содержащую либо числовые данные data, либо массив байтов. Затем можно заполнить поле data и отправьте необработанные байты:

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

На другом конце вы делаете то же самое в обратном порядке: заполняете байты и считайте числовые данные:

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);
}
,