Sending/Receiving Data over LoRa; Parsing Help

Hello all!

I am trying to transmit GPS data (among other things), from one LoRa radio module to another. I am sending and receiving perfectly, but the RadioLib library only can send/receive strings.

How would I send the data, and then parse it from a single string on the receiver side?

Thanks in advance!

Are we talking about a String (captal S) or a string (lowercase s) ?

Is the data string being received correctly and can you print it ?
Do you have control over the format of the data string ?

Please provide an example of the data string that you want to parse and describe what you want to parse from it

Hello @UKHeliBob

Ummm, I think String (?).

The data is being received correctly, with nothing missing. I have 100% control over what the transmitter transmits also.

For example, right now I have the transmitter transmitting this: radio.transmit("Latitude: " + String(latitude) + ", Longitude: " + String(longitude) + ", X: " + String(x) + ", Y: " + String(y));
I would like to parse from this (on the receiving side), all the data; so latitude, longitude, x axis, and y axis.

Thanks!

Edit:
For my example, the received data is: "Latitude: 43.42, Longitude: -72.47, X: 1.23, Y: -97.62"

Yes, from your example it is a String. You can save bandwidth by not sending the data descriptions. After all, you know what order they are in

So, your example would become
43.42,-72.47,1.23,97.62

Which Arduino board are you using ? I ask because Strings as opposed to strings can cause problems with limited memory models

Assuming that you continue to use a String then have a look at the indexOf() and subString() functions of the String library. Basically you find the position of the first comma, cut off the first data item of the String into a new variable and continue until you have all 4 values in new variables

That was my thoughts after I set it up like in my example, lol.

I am actually using a Fire-Beetle ESP32. I was using Arduino Nanos, but changed to a ESP32 because of plenty of memory, and the WiFi and BLE capability.

Ah, you mean this:

I'll take a shot at it and see how it goes

So...

I have this so far:

    first = str.indexOf(',');
    latitude = float(str.substring(0,first));
    second = str.indexOf(',',first+1);
    longitude = float(str.substring(first+1,second+1));
    third = str.indexOf(',',second+1);
    heel = float(str.substring(second+1,third+1));
    fourth = str.indexOf(',',third+1);
    roll = float(str.substring(third+1,fourth+1));

But, im getting the "invalid cast from type 'String' to type 'float'" error. How would I do that differently?

first, second, etc. are ints
latitude, longitude, etc. are floats

Please provide a GitHub link to that library.

Oh yeah, there are multiple RadioLib libraries, sorry.

The link to the library that I am using: GitHub - jgromes/RadioLib: Universal wireless communication library for embedded devices

Have you looked at all the conversion functions documented in the String class?

Ah, I was suppose to use this (sorry, just found it):

I uploaded it, but the data is showing up as 0; all of them are empty.

That library can send a lot more than just Strings. I'd put all the data in a struct and send it as binary. If you wan more help, post your complete code.

Sorry, kind of new here; how would I do that?

Transmitter code:

#include <TinyGPS++.h>
#include <SoftwareSerial.h>
#include <RadioLib.h>
#include <DFRobot_BMI160.h>

static const int RXPin = 12, TXPin = 14;
static const uint32_t GPSBaud = 9600;

int rfPower = 30;
int state;
int rslt;

float speed, altitude, course, latitude, longitude, heel, roll;
int sat;

float x, y, z;

boolean accelresult = false;
boolean gpsresult = false;

SX1262 radio = new Module(27, 4, 2, 15);
TinyGPSPlus gps;
SoftwareSerial ss(RXPin, TXPin);
DFRobot_BMI160 bmi160;
const int8_t i2c_addr = 0x69;

unsigned long last = 0UL;

void setup() {
  Serial.begin(115200);
  ss.begin(GPSBaud);
  
  initialiseRF();

  if (bmi160.softReset() != BMI160_OK){
    Serial.println("reset false");
    while(1);
  }
  if (bmi160.I2cInit(i2c_addr) != BMI160_OK){
    Serial.println("init false");
    while(1);
  }
}

void loop() {
  while (ss.available() > 0)
    gps.encode(ss.read());

  if (gps.location.isUpdated()){
    speed = gps.speed.knots();
    altitude = gps.altitude.feet();
    course = gps.course.deg();
    latitude = gps.location.lat();
    longitude = gps.location.lng();
    gpsresult = true;

    int16_t onlyAccel[3]={0};
    rslt = bmi160.getAccelData(onlyAccel);
    if(rslt == 0){
      //Serial.print("X: " + String(onlyAccel[0]/163.84));
      //Serial.print(" Y: " + String(onlyAccel[1]/163.84));
      //Serial.print(" Z: " + String(onlyAccel[2]/163.84));
      x = onlyAccel[0]/163.84;
      y = onlyAccel[1]/163.84;
      z = onlyAccel[2]/163.84;
      accelresult = true;
    }
  }
  
  if (gpsresult && accelresult){
    Serial.println("Latitude: " + String(latitude) + " Longitude: " + String(longitude) + " X: " + String(x) + " Y: " + String(y));
    int state = radio.transmit(String(latitude) + "," + String(longitude) + "," + String(x) + "," + String(y));
    checkTransmit(state);
    accelresult = false;
    gpsresult = false;
  }

  printGPS();
  
  //delay(1000);
}

void checkTransmit(int s){
  if (s == RADIOLIB_ERR_NONE) {
    // the packet was successfully transmitted
    Serial.println(F("success!"));

    // print measured data rate
    Serial.print(F("[SX1262] Datarate:\t"));
    Serial.print(radio.getDataRate());
    Serial.println(F(" bps"));

  } else if (s == RADIOLIB_ERR_PACKET_TOO_LONG) {
    // the supplied packet was longer than 256 bytes
    Serial.println(F("too long!"));

  } else if (s == RADIOLIB_ERR_TX_TIMEOUT) {
    // timeout occured while transmitting packet
    Serial.println(F("timeout!"));

  } else {
    // some other error occurred
    Serial.print(F("failed, code "));
    Serial.println(s);
  }
}

void initialiseRF(){
  // initialize the second LoRa instance with
  // non-default settings
  // this LoRa link will have high data rate,
  // but lower range
  Serial.print(F("[SX1268] Initializing ... "));
  // carrier frequency:           915.0 MHz
  // bandwidth:                   125.0 kHz
  // spreading factor:            10
  // coding rate:                 5
  // sync word:                   0x34 (public network/LoRaWAN)
  // output power:                2 dBm
  // preamble length:             20 symbols
  state = radio.begin(915.0, 125.0, 10, 5, 0x34, 20);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
  radio.setOutputPower(rfPower);
  radio.setRfSwitchPins(25, 26);
}

void printGPS(){
  if (gps.location.isUpdated())
  {
    Serial.print(F("LOCATION   Fix Age="));
    Serial.print(gps.location.age());
    Serial.print(F("ms Raw Lat="));
    Serial.print(gps.location.rawLat().negative ? "-" : "+");
    Serial.print(gps.location.rawLat().deg);
    Serial.print("[+");
    Serial.print(gps.location.rawLat().billionths);
    Serial.print(F(" billionths],  Raw Long="));
    Serial.print(gps.location.rawLng().negative ? "-" : "+");
    Serial.print(gps.location.rawLng().deg);
    Serial.print("[+");
    Serial.print(gps.location.rawLng().billionths);
    Serial.print(F(" billionths],  Lat="));
    Serial.print(gps.location.lat(), 6);
    Serial.print(F(" Long="));
    Serial.println(gps.location.lng(), 6);
  }

  else if (gps.date.isUpdated())
  {
    Serial.print(F("DATE       Fix Age="));
    Serial.print(gps.date.age());
    Serial.print(F("ms Raw="));
    Serial.print(gps.date.value());
    Serial.print(F(" Year="));
    Serial.print(gps.date.year());
    Serial.print(F(" Month="));
    Serial.print(gps.date.month());
    Serial.print(F(" Day="));
    Serial.println(gps.date.day());
  }

  else if (gps.time.isUpdated())
  {
    Serial.print(F("TIME       Fix Age="));
    Serial.print(gps.time.age());
    Serial.print(F("ms Raw="));
    Serial.print(gps.time.value());
    Serial.print(F(" Hour="));
    Serial.print(gps.time.hour());
    Serial.print(F(" Minute="));
    Serial.print(gps.time.minute());
    Serial.print(F(" Second="));
    Serial.print(gps.time.second());
    Serial.print(F(" Hundredths="));
    Serial.println(gps.time.centisecond());
  }

  else if (gps.speed.isUpdated())
  {
    Serial.print(F("SPEED      Fix Age="));
    Serial.print(gps.speed.age());
    Serial.print(F("ms Raw="));
    Serial.print(gps.speed.value());
    Serial.print(F(" Knots="));
    Serial.print(gps.speed.knots());
    Serial.print(F(" MPH="));
    Serial.print(gps.speed.mph());
    Serial.print(F(" m/s="));
    Serial.print(gps.speed.mps());
    Serial.print(F(" km/h="));
    Serial.println(gps.speed.kmph());
  }

  else if (gps.course.isUpdated())
  {
    Serial.print(F("COURSE     Fix Age="));
    Serial.print(gps.course.age());
    Serial.print(F("ms Raw="));
    Serial.print(gps.course.value());
    Serial.print(F(" Deg="));
    Serial.println(gps.course.deg());
  }

  else if (gps.altitude.isUpdated())
  {
    Serial.print(F("ALTITUDE   Fix Age="));
    Serial.print(gps.altitude.age());
    Serial.print(F("ms Raw="));
    Serial.print(gps.altitude.value());
    Serial.print(F(" Meters="));
    Serial.print(gps.altitude.meters());
    Serial.print(F(" Miles="));
    Serial.print(gps.altitude.miles());
    Serial.print(F(" KM="));
    Serial.print(gps.altitude.kilometers());
    Serial.print(F(" Feet="));
    Serial.println(gps.altitude.feet());
  }

  else if (gps.satellites.isUpdated())
  {
    Serial.print(F("SATELLITES Fix Age="));
    Serial.print(gps.satellites.age());
    Serial.print(F("ms Value="));
    Serial.println(gps.satellites.value());
  }

  else if (gps.hdop.isUpdated())
  {
    Serial.print(F("HDOP       Fix Age="));
    Serial.print(gps.hdop.age());
    Serial.print(F("ms raw="));
    Serial.print(gps.hdop.value());
    Serial.print(F(" hdop="));
    Serial.println(gps.hdop.hdop());
  }

  else if (millis() - last > 5000)
  {
    Serial.println();
    if (gps.location.isValid())
    {
      static const double LONDON_LAT = 51.508131, LONDON_LON = -0.128002;
      double distanceToLondon =
        TinyGPSPlus::distanceBetween(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);
      double courseToLondon =
        TinyGPSPlus::courseTo(
          gps.location.lat(),
          gps.location.lng(),
          LONDON_LAT, 
          LONDON_LON);

      Serial.print(F("LONDON     Distance="));
      Serial.print(distanceToLondon/1000, 6);
      Serial.print(F(" km Course-to="));
      Serial.print(courseToLondon, 6);
      Serial.print(F(" degrees ["));
      Serial.print(TinyGPSPlus::cardinal(courseToLondon));
      Serial.println(F("]"));
    }

    Serial.print(F("DIAGS      Chars="));
    Serial.print(gps.charsProcessed());
    Serial.print(F(" Sentences-with-Fix="));
    Serial.print(gps.sentencesWithFix());
    Serial.print(F(" Failed-checksum="));
    Serial.print(gps.failedChecksum());
    Serial.print(F(" Passed-checksum="));
    Serial.println(gps.passedChecksum());

    if (gps.charsProcessed() < 10)
      Serial.println(F("WARNING: No GPS data.  Check wiring."));

    last = millis();
    Serial.println();
  }
}

Receiver code:

#include <RadioLib.h>

volatile bool receivedFlag = false;
volatile bool enableInterrupt = true;

int state;

int first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth;

float speed, altitude, course, latitude, longitude, heel, roll;
int sat;

// SX1262 has the following connections:
// NSS pin:   10
// DIO1 pin:  2
// NRST pin:  3
// BUSY pin:  9
SX1262 radio = new Module(27, 4, 2, 15);

void setup() {
  Serial.begin(115200);

  initialiseRF();
}

void loop() {
  // check if the flag is set
  if(receivedFlag) {
    // disable the interrupt service routine while
    // processing the data
    enableInterrupt = false;

    // reset flag
    receivedFlag = false;

    // you can read received data as an Arduino String
    String str;
    int state = radio.readData(str);

    first = str.indexOf(',');
    latitude = str.substring(0,first).toFloat();
    second = str.indexOf(',',first+1);
    longitude = str.substring(first+1,second+1).toFloat();
    third = str.indexOf(',',second+1);
    heel = str.substring(second+1,third+1).toFloat();
    fourth = str.indexOf(',',third+1);
    roll = str.substring(third+1,fourth+1).toFloat();
    
    // you can also read received data as byte array
    /*
      byte byteArr[8];
      int state = radio.readData(byteArr, 8);
    */

    if (state == RADIOLIB_ERR_NONE) {
      // packet was successfully received
      Serial.println(F("[SX1262] Received packet!"));

      // print data of the packet
      Serial.print(F("[SX1262] Data:\t\t"));
      //Serial.println(str);
      Serial.println("latitude: " + String(latitude) + " longitude: " + String(longitude) + " heel: " + String(heel) + " roll: " + String(roll));

      // print RSSI (Received Signal Strength Indicator)
      Serial.print(F("[SX1262] RSSI:\t\t"));
      Serial.print(radio.getRSSI());
      Serial.println(F(" dBm"));

      // print SNR (Signal-to-Noise Ratio)
      Serial.print(F("[SX1262] SNR:\t\t"));
      Serial.print(radio.getSNR());
      Serial.println(F(" dB"));

    } else if (state == RADIOLIB_ERR_CRC_MISMATCH) {
      // packet was received, but is malformed
      Serial.println(F("CRC error!"));

    } else {
      // some other error occurred
      Serial.print(F("failed, code "));
      Serial.println(state);

    }

    // put module back to listen mode
    radio.startReceive();

    // we're ready to receive more packets,
    // enable interrupt service routine
    enableInterrupt = true;
  }

}

void initialiseRF(){
  // initialize the second LoRa instance with
  // non-default settings
  // this LoRa link will have high data rate,
  // but lower range
  Serial.print(F("[SX1268] Initializing ... "));
  // carrier frequency:           915.0 MHz
  // bandwidth:                   125.0 kHz
  // spreading factor:            10
  // coding rate:                 5
  // sync word:                   0x34 (public network/LoRaWAN)
  // output power:                2 dBm
  // preamble length:             20 symbols
  state = radio.begin(915.0, 125.0, 10, 5, 0x34, 20);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

  // set the function that will be called
  // when new packet is received
  radio.setDio1Action(setFlag);

  // start listening for LoRa packets
  Serial.print(F("[SX1262] Starting to listen ... "));
  state = radio.startReceive();
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }

  // if needed, 'listen' mode can be disabled by calling
  // any of the following methods:
  //
  // radio.standby()
  // radio.sleep()
  // radio.transmit();
  // radio.receive();
  // radio.readData();
  // radio.scanChannel();

}

#if defined(ESP8266) || defined(ESP32)
  ICACHE_RAM_ATTR
#endif
void setFlag(void) {
  // check if the interrupt is enabled
  if(!enableInterrupt) {
    return;
  }

  // we got a packet, set the flag
  receivedFlag = true;
}

debug print your value string before converting it.

1 Like

If you look in SX126x.h, you'll see an overload for the transmit() function:

    /*!
      \brief Blocking binary transmit method.
      Overloads for string-based transmissions are implemented in PhysicalLayer.
      \param data Binary data to be sent.
      \param len Number of bytes to send.
      \param addr Address to send the data to. Will only be added if address filtering was enabled.
      \returns \ref status_codes
    */
    int16_t transmit(uint8_t* data, size_t len, uint8_t addr = 0) override;

So, in skeletal form, it would be something like:

struct DataPacket {
  float latitude;
  float longitude;
};

DataPacket packet;

void setup() {
  //
  //
}

void loop() {
  //
  //
  packet.latitude = latitude;
  packet.longitude = longitude;
  state = radio.transmit(reinterpret_cast <uint8_t *> (&packet), sizeof(packet));
  //
  //
  //
}

Your code works, apart from the fourth parameter, if I hard code the string @UKHeliBob gave you as the received string.

So as suggested, print what you actually receive.

I printed what I received before saving the data to a variable, and it works perfectly. But if I save it to a "int" variable, of course, the decimal is dropped, but it does still show as data. If I try saving it to a float variable, the data shows as 0.

Edit:
I just tried saving the data as floats again, and its working. :thinking:

Thanks @gfvalvo !

I just got it working with Strings though, so I think ill go with this, instead of doing Structs.

OK, but doing it that way will probably make everything less maintainable, updateable, and scalable.

1 Like

@kgray9 Hi! I'm the maintainer of RadioLib - I would suggest taking a look at the (quite extensive, in my opiniuon) examples. One of them (RadioLib/SX126x_Transmit.ino at master · jgromes/RadioLib · GitHub) includes a reference on how to send arbitrary data.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.