Decimals, strings, and LoRa

here is a version for the ESP32

// Lora receiver using ESP32 with RA-02 443MHz LoRa module- polled version

// connections
// RA-02 SS   to ESP32 GPIO5
// RA-02 RST  to ESP32 GPIO16
// RA-02 DIO0 to ESP32 GPIO17
// RA-02 MOSI to ESP32 GPIO23
// RA-02 MIS0 to ESP32 GPIO19
// RA-02 SCK  to ESP32 GPIO18
// setpins() is therefore
// setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);
// LoRa.setPins(5, 16, 17);  // for ESP32


#include <SPI.h>
#include <LoRa.h>

// three different structures may be received
struct __attribute__((packed)) Struct1 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  float distance;
  float voltage;
  //char z[10];
} struct1;

struct __attribute__((packed)) Struct2 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  float distance;
  float voltage;
} struct2;

struct __attribute__((packed)) Struct3 {
  byte StructureID;  // identifies the structure type
  byte NodeID;       // ID of transmitting node
  int16_t seq;       // sequence number
  float distance;
  float voltage;
} struct3;

// used for polling
byte nodeIDs[] = { 1, 2, 3 };   // list of nodeIDs to poll
byte polledOK[] = { 2, 2, 2 };  // indicate polling sucess/failure
int nodeIndex = 0;              // index into above arrays

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(1);
  Serial.println("LoRa Receiver ESP32 with RA-02 443MHz LoRa module");
  // void setPins(int ss = LORA_DEFAULT_SS_PIN, int reset = LORA_DEFAULT_RESET_PIN, int dio0 = LORA_DEFAULT_DIO0_PIN);

  // LoRa.setPins(10, 9, 2);  // 10 for UNO and Nano, 53 for Mega
  // LoRa.setPins(15,16,4);  // for ESP8266
  //LoRa.setPins(8,4,7);   // for Lora 32u4
  LoRa.setPins(5, 16, 17);  // for ESP32
  if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1) delay(10);
  }
  Serial.println("Lora Receiver started");
  LoRa.onTxDone(onTxDone);    // setup callbacks
  LoRa.onReceive(onReceive);  // set receive callback
  LoRa_rxMode();              // set receive mode
}

volatile int nodeIDdataReceived = 0, txDone = 0;  // from callbacks

void loop() {
  static long timer = millis();
  if (millis() - timer > 5000) {
    timer = millis();
    // polling nodes
    if (--polledOK[nodeIndex] == 0) {  // did last receive response?
      Serial.print("Node failed to respond to polling ID = ");
      Serial.println(nodeIDs[nodeIndex]);
      polledOK[nodeIndex] = 1;
    }
    Serial.print("polling ID = ");  // poll next node
    Serial.print(nodeIDs[nodeIndex]);
    //Serial.println(nodeIndex);
    LoRa_txMode();                   // set to transmit
    LoRa.beginPacket();              // start packet
    LoRa.write(nodeIDs[nodeIndex]);  // transmit packet
    LoRa.endPacket(true);            // finish packet and send it
    //delay(2);
    if (++nodeIndex >= sizeof(nodeIDs)) nodeIndex = 0;
  }
  //LoRa_rxMode();  // set receive mode
  if (txDone) {                       // transmit done?
    txDone = 0;
    Serial.println("  transmitted");
  }
  //delay(5000);  // polling delay
  dataReceived(nodeIDdataReceived);   // check data received?
}

// transmit finished - set indicator for loop() to print the data
void onTxDone() {
  LoRa_rxMode();  // set receive mode
  txDone = 1;
}

// set receive mode
void LoRa_rxMode() {
  LoRa.disableInvertIQ();  // normal mode
  LoRa.receive();          // set receive mode
}

// set transmit mode
void LoRa_txMode() {
  LoRa.idle();             // set standby mode
  LoRa.disableInvertIQ();  // normal mode
}

// check sequence number is correct 
void checkSeqNumber(int NodeID, int seq) {
  static int seqNumbers[] = { 0, 0, 0, 0 }, seqErrors = 0;
  if (NodeID >= sizeof(seqNumbers) / sizeof(int)) return;
  if (seq != seqNumbers[NodeID]) {  // check for sequence error!
    Serial.print("  seq number error expected ");
    Serial.print(seqNumbers[NodeID]);
    Serial.print(" received ");
    Serial.print(seq);
    Serial.print("  seq  errors ");
    Serial.println(++seqErrors);
    seqNumbers[NodeID] = seq;
  }
  seqNumbers[NodeID]++;      // next sequence nunber expected
  polledOK[NodeID - 1] = 2;  // indicate polling sucess
}


// receive packet - set indicator for loop()
void onReceive(int packetSize) {
  nodeIDdataReceived=packetSize;
}

// check if packet received (packetSize > 0)
void dataReceived(int packetSize) {
  if(packetSize==0) return;           // if no data return
  nodeIDdataReceived=0;
  Serial.print("Lora receive RSSI "); // packet received process it
  Serial.print(LoRa.packetRssi());
  Serial.print(" packet size ");
  Serial.print(packetSize);
  byte structureID = LoRa.read();  // read first byte the structure ID
  // switch to correct structure to decode
  switch (structureID) {
    case 1:
      LoRa.readBytes((byte *)&struct1 + 1, packetSize - 1);  // receive struct1 packet
      struct1.StructureID = structureID;
      // display packet
      //for (int i = 0; i < packetSize; i++) {
      // Serial.print(' ');
      // Serial.print(((byte *) &struct1)[i]);
      // }
      Serial.print(" StructureID ");
      Serial.print(struct1.StructureID);
      Serial.print(" from Node ");
      Serial.print(struct1.NodeID);
      Serial.print(" seq number ");
      Serial.print(struct1.seq);
      Serial.print(" distance 1 = ");
      Serial.print(struct1.distance);
      Serial.print(" voltage 1 = ");
      Serial.println(struct1.voltage);
      checkSeqNumber(struct1.NodeID, struct1.seq);
      break;
    case 2:
      LoRa.readBytes((byte *)&struct2 + 1, packetSize - 1);  // receive struct2 packet
      struct2.StructureID = structureID;
      Serial.print(" StructureID ");
      Serial.print(struct2.StructureID);
      Serial.print(" from Node ");
      Serial.print(struct2.NodeID);
      Serial.print(" seq number ");
      Serial.print(struct2.seq);
      Serial.print(" distance 2 = ");
      Serial.print(struct2.distance);
      Serial.print(" voltage 2 = ");
      Serial.println(struct2.voltage);
      checkSeqNumber(struct2.NodeID, struct2.seq);
      break;
    case 3:
      LoRa.readBytes((byte *)&struct3 + 1, packetSize - 1);  // receive struct3 packet
      struct3.StructureID = structureID;
      Serial.print(" StructureID ");
      Serial.print(struct3.StructureID);
      Serial.print(" from Node ");
      Serial.print(struct3.NodeID);
      Serial.print(" seq number ");
      Serial.print(struct3.seq);
      Serial.print(" dist3 = ");
      Serial.println(struct3.distance);
      Serial.print(" voltage = ");
      Serial.println(struct3.voltage);
      checkSeqNumber(struct3.NodeID, struct3.seq);
      break;
  }
}

note that onReceive() just notes the packetSize and the packet is decoded in loop()