Unpacking Json Object String to Lora Read Until

Morning

I'm currently building a project that sends sensor data from an SGP30 through LoRa (MKR1300) to LoRa (ESP32) out to wifi and up to Blynk (Or any other platform).

Im using IDE to create the nested object called DRONE with 4 data points on the sender node.

  JsonObject data = doc.createNestedObject("DRONE");
  data["CO2"] = co2;
  data["TVOC"] = tvoc;
  data["H2"] = h2;
  data["Ethanol"] = etha;
  serializeJson(doc, droneData);
  Serial.println(droneData);

Then I'm trying to split / read the data back out on the receiver node

droneData = LoRa.readString();
Serial.print(droneData);
}

String droneData = LoRa.readStringUntil(':');

LoRa.read(); // Throw away the space
if (droneData.endsWith("CO2"))
String co2 = LoRa.readStringUntil(' ');
if (droneData.endsWith("TVOC"))
String tvoc = LoRa.readStringUntil(' ');
if (droneData.endsWith("H2"))
String h2 = LoRa.readStringUntil(' ');
if (droneData.endsWith("Ethanol"))
String etha = LoRa.readStringUntil(' ');

But im not getting any data stored in the strings ?.

This is the total String read as one
13:42:46.355 -> with Value Received packet 1428{"DRONE":{"CO2":0,"TVOC":0,"H2":6.713018778e10,"Ethanol":1.864621128e15}} with RSSI -37

it seems you have two droneData Strings ?

Don't post snippets (Snippets R Us!)

I did REM that first string out when trying to split the data out. Just enabled it to show the full string of data J. It compiles OK but i'm missing something obvious.

Do I need to declare the values outside the loop as strings ?.

you need to post the full code, then we can provide meaningful information...

Ive done it from the Setup down to leave out all the passwords etc

void setup()
{
  // Debug console
  Serial.begin(115200);


  // You can also specify server:
  // Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, IPAddress(192,168,1,100), 8080);
  Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, "blynk.cloud", 8080);
  //Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, IPAddress(192,168,1,100), 8080);
  // Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass, "blynk.cloud", 80);

  // Setup a function to be called every second
  timer.setInterval(1000L, myTimerEvent);

//reset OLED display via software
  pinMode(OLED_RST, OUTPUT);
  digitalWrite(OLED_RST, LOW);
  delay(20);
  digitalWrite(OLED_RST, HIGH);

  //initialize OLED
  Wire.begin(OLED_SDA, OLED_SCL);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }

  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("LORA RECEIVER ");
  
  display.display();

  Serial.println("LoRa Receiver Test");
  
  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);

  if (!LoRa.begin(BAND)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initializing OK!");
  display.setCursor(0,10);
  display.println("LoRa Initializing OK!");
   
  display.println(""); 
  display.display();


  
}

void loop()
{
    Blynk.run();
    timer.run();
  
  // You can inject your own code or combine it with other sketches.
  // Check other examples on how to communicate with Blynk. Remember
  // to avoid delay() function!

//try to parse packet
 int packetSize = LoRa.parsePacket();
 if (packetSize) {
    //received a packet
    Serial.print("Received packet ");

//read packet
while (LoRa.available()) 
{
droneData = LoRa.readString();
Serial.print(droneData);
}



String droneData = LoRa.readStringUntil(':');

LoRa.read(); // Throw away the space
if (droneData.endsWith("CO2"))
String co2 = LoRa.readStringUntil(' ');
if (droneData.endsWith("TVOC"))
String tvoc = LoRa.readStringUntil(' ');
if (droneData.endsWith("H2"))
String h2 = LoRa.readStringUntil(' ');
if (droneData.endsWith("Ethanol"))
String etha = LoRa.readStringUntil(' ');




 //print RSSI of packet
int rssi = LoRa.packetRssi();
Serial.print(" with RSSI ");    
Serial.println(rssi);

Serial.print(" with Value ");


Blynk.virtualWrite(V5, "0.55455");
    



      
// +++++++++++++++++++++++++++
   
  }

  


}

please use code tags correctly - I've fixed your two posts for your but if you have a multi line code segments then use three backticks


Ok will do thanks J, I don't normally post up code and learning every day.

this empties the received Lora buffer

so when you try to read more

it's not going to work, there is nothing left to read.

you need to parse the data you read in the first place from the String you got

(sending a binary structure rather than a String would probably make your life easier)

J, I've parsePacket() which has given me the droneData string as a full string. But Ive Rem'd that out then to just leave the code below, but still with no luck.

While LoRa Available, and then String droneData = Lora.readstring

Is that the right way to do it ?.

Id like to keep the code easy to read as possible for others in my team to be able to understand and edit it for their projects

the typical code to receive something is this

  int packetSize = LoRa.parsePacket();
  if (packetSize) {
    // received a packet
    Serial.print("Received packet '");

    // read packet
    while (LoRa.available()) {
      Serial.print((char)LoRa.read());
    }
  }

where the bytes of the packet are dumped onto the Serial monitor one by one in the while loop.

In a real code, you would not dump them and do nothing with the bytes, you would store them in some memory buffer and then in order to parse what you received, you'd need to know what the sender has sent.

➜ Do you have the sender's code? do you know what to expect?

void setup() {
  //initialize Serial Monitor
  Serial.begin(115200);

  //reset OLED display via software
  pinMode(OLED_RST, OUTPUT);
  digitalWrite(OLED_RST, LOW);
  delay(20);
  digitalWrite(OLED_RST, HIGH);

  //initialize OLED
  Wire.begin(OLED_SDA, OLED_SCL);
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3c, false, false)) { // Address 0x3C for 128x32
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
  
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.print("LORA SENDER ");
  display.display();
  
  Serial.println("LoRa Sender Test");

  //SPI LoRa pins
  SPI.begin(SCK, MISO, MOSI, SS);
  //setup LoRa transceiver module
  LoRa.setPins(SS, RST, DIO0);
  
  if (!LoRa.begin(BAND)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("LoRa Initializing OK!");
  display.setCursor(0,10);
  display.print("LoRa Initializing OK!");
  display.display();
  delay(2000);

  Serial.println(F("Enviro Drone SGP30 Sensor Start"));

    // Initialize I2C bus
    Wire.begin();
    Wire.setClock(400000);

    // Initialize sensor
    while (! sgp.begin()) {
        Serial.println(F("Error: Could not detect SGP30 sensor"));
   //     display.println("SGP30 Error");
        delay(3000);
    }
}

void loop() {
  
   String droneData;

  
  //  send data once every 10 seconds
  if (millis() - lastTime >= 10000)
  {
    uint32_t start = millis();
    Serial.print("time: \t");
    Serial.println(start);


    Serial.print(millis() - start);
    Serial.println("Read SGP30 sensor.");
    sgp.measure(true);
    float co2 = sgp.getCO2();
    float tvoc = sgp.getTVOC();
    float h2 = sgp.getH2();
    float etha = sgp.getEthanol();

    Serial.print(co2);
    Serial.print("\t");
    Serial.print(tvoc);
    Serial.print("\t");
    Serial.print(h2);
    Serial.print("\t");
    Serial.print(etha);
    Serial.println();

    
  //Following code creates the serialized JSON string to send to the LoRa Receiver 
  //using ArduinoJson library
  StaticJsonDocument <200> doc;

  JsonObject data = doc.createNestedObject("DRONE");
  data["CO2"] = co2;
  data["TVOC"] = tvoc;
  data["H2"] = h2;
  data["Ethanol"] = etha;
  serializeJson(doc, droneData);
  Serial.println(droneData);

  //This prints the JSON to the serial monitor screen
  //and can be commented out
  Serial.print("Sending packet: ");
  Serial.println(counter);

  //Send LoRa packet to receiver
  LoRa.beginPacket();
  LoRa.print(counter);
  LoRa.print(droneData);
  LoRa.endPacket();
  
  display.clearDisplay();
  display.setCursor(0,0);
  display.println("LORA SENDER");
  display.setCursor(0,20);
  display.setTextSize(1);
  display.print("LoRa packet sent.");
  display.setCursor(0,30);
  display.print("Counter:");
  display.setCursor(50,30);
  display.print(counter);      
  display.display();

  counter++;
  
  delay(10000);
}
}

OK so you are sending a JSON formatted packet.

The Lora library has a packet size limited to 255 if I remember correctly, you send something like this

{"DRONE":{"CO2":0,"TVOC":0,"H2":6.713018778e10,"Ethanol":1.864621128e15}}

so it should fit

(you probably should limit the precision to 2 digits after the decimal point when you send the data)

on the receiving end try this code

int packetSize = LoRa.parsePacket();
if (packetSize) {
  char payload[packetSize+1];
  int pos = 0;
  while (LoRa.available()) payload[pos++] = LoRa.read();
  payload[packetSize] = '\0';

  StaticJsonDocument<100> doc;
  DeserializationError error = deserializeJson(doc, payload, packetSize);

  if (error) {
    Serial.print(F("deserializeJson() failed: "));
    Serial.println(error.f_str());
    return;
  }

  JsonObject droneObj = doc["DRONE"];
  float co2 = droneObj["CO2"]; 
  float tvoc = droneObj["TVOC"]; 
  float h2 = droneObj["H2"]; 
  float ethanol = droneObj["Ethanol"]; 

  Serial.print("CO2 = "); Serial.println(co2);
  Serial.print("TVOC = "); Serial.println(tvoc);
  Serial.print("H2 = "); Serial.println(h2);
  Serial.print("Ethanol = "); Serial.println(ethanol);

}

of course you'll need to include ArduinoJSON in the receiver

Awesome J I will try this later today and report back. Thanks for your help and time mate.

J, Tried the code and its receiving the packet but, its saying deserialisation() failed: invalid input.

Any further ideas ?

can you print the payload to see what's really in there?

So now Ive changed it to just send the 4 values as LoRa print commands with headings

LoRa Sender

    Serial.print(millis() - start);
    Serial.println("Read SGP30 sensor.");
    sgp.measure(true);
    float co2 = sgp.getCO2();
    float tvoc = sgp.getTVOC();
    float h2 = sgp.getH2();
    float etha = sgp.getEthanol();

    Serial.print(co2);
    Serial.print("\t");
    Serial.print(tvoc);
    Serial.print("\t");
    Serial.print(h2);
    Serial.print("\t");
    Serial.print(etha);
    Serial.println();

  Serial.print("Sending packet: ");
  Serial.println(counter);

  // +++++++++++++++++++++++++++++++ Send LoRa packet to receiver
  LoRa.beginPacket();
 //  +++++++++++++++++++++++++++++++ LoRa.print(counter);
  LoRa.print(": CO2 = ");
  LoRa.print(co2);
  LoRa.print(": TVOC = ");
  LoRa.print(tvoc);
  LoRa.print(": H2 = ");
  LoRa.print(h2);
  LoRa.print(": Ethanol = ");
  LoRa.print(etha);
  LoRa.endPacket();

So I'm trying my best to unpack it at present using the following :

Receiver Node

while (LoRa.available()) 
{
  
LoRalabel = LoRa.readString();
Serial.println(LoRalabel);
 
String LoRaLabel = LoRa.readStringUntil(': ');
LoRa.read(); // Throw away the space

if (LoRaLabel.endsWith("CO2 = "))
   String Co2 = LoRa.readStringUntil(': ');
if (LoRaLabel.endsWith("TVOC = "))
   String tvoc = LoRa.readStringUntil(': ');
if (LoRaLabel.endsWith("H2 = "))
   String h2 = LoRa.readStringUntil(': ');
if (LoRaLabel.endsWith("Ethanol = "))
   String etha = LoRa.readStringUntil(' ');

  Serial.print("CO2 = "); Serial.println(Co2);
  Serial.print("TVOC = "); Serial.println(tvoc);
  Serial.print("H2 = "); Serial.println(h2);
  Serial.print("Ethanol = "); Serial.println(etha);


you have two characters the colon and a space in there

it would probably be easier to read the full message and then parse it

avoid posting image of screen text output - it wastes space, is difficult to read and copy from - copy the text and post it using code tags </>

using a couple of TTGO LoRa32 SX1276 OLED I transmitted text

 : CO2 = 433.00: TVOC = 96.00: H2 = 0.26: Ethanol = 0.17

and to parse the LoRa received data used sscanf, e.g.

 //read packet
    Serial.print("Received packet ");
    while (LoRa.available()) {
      LoRaLabel = LoRa.readString();
      Serial.println(LoRaLabel);
      int parsed = sscanf(LoRaLabel.c_str(),
         ": CO2 = %f: TVOC = %f: H2 = %f: Ethanol = %f", &Co2, &tvoc, &h2, &etha);
      Serial.print("CO2 = ");
      Serial.println(Co2);
      Serial.print("TVOC = ");
      Serial.println(tvoc);
      Serial.print("H2 = ");
      Serial.println(h2);
      Serial.print("Ethanol = ");
      Serial.println(etha);
    }

receiver displays

Received packet : CO2 = 433.00: TVOC = 96.00: H2 = 0.26: Ethanol = 0.17
CO2 = 433.00
TVOC = 96.00
H2 = 0.26
Ethanol = 0.17

however, as recommend by @J-M-L, I would transmit and receive a binary structure - see need-advice-on-a-home-project

Edit: remember on an ESP32 you can use Serial.printf() rather lines of Serial.print()s, e.g.

Serial.printf("\nCO2 = %.2f TVOC = %.2f H2 =  %.2f Ethanol = %.2f", Co2, tvoc, h2, etha);

displays

CO2 = 433.00 TVOC = 96.00 H2 =  0.26 Ethanol = 0.17