Understanding LoRa packet

Hi all trying to understand how the LoRa payload length is determined in this code:

void sendMessage(String outgoing) {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.write(rcontrol);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
  Serial.print("Message sent=");
  Serial.println (rcontrol, BIN);
  Serial.print ("rcontrolsent=");
  rcontrolsent = rcontrol;
  Serial.println(rcontrolsent);
}

Is it based on everything between LoRa.beginPacket(); and LoRa.endPacket(); or is it determined by this part of the code

      if(rcontrol != rcontrolsent){
      scontrol = rcontrol;
      String message = String (scontrol);
      sendMessage(message);

I have been trying to follow along on this thread to get an idea of what I need to do.

My code is currently working to send one byte.

I want to expand to sending and receiving 5 or 7 bytes. (1 byte and 2/3 ints)

I get that the LoRa.write/read must match in the send and receive. Is it as simple as just adding in the bytes I want to send in sequence in the packet structure (between LoRa.beginPacket(); and LoRa.endPacket(); ) and then have the same sequence in the receive structure. or do I need to build the packet before to get the outgoing length.

Full code I am currently using. (There are some sections that don't work but I am still working on it and are not really relevant to the question in this post)

#include <SPI.h>
#include <LoRa.h>
//LORA SETTINGS
const int csPin = 10;          // LoRa radio chip select
const int resetPin = 13;       // LoRa radio reset
const int irqPin = 2;         // change for your board; must be a hardware interrupt pin

//LORA PACKET INFO
byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0xFF;     // address of this device
byte destination = 0xBB;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 1000;
String sent;
String message;

//DATA IN
byte pcontrol = 0b00000000;
byte pcontrolr = 0b00000000;


//DATA OUT
byte rcontrol;
byte scontrol;
byte rcontrolsent;
byte controller_status;

//PINS_IN
int POWER_BUTTON = 29;
int RIGHT_ARROW = 30;
int STOP_ALL = 31;
int PLUS_SPEED = 32;
int MINUS_SPEED = 33;
int LEFT_ARROW = 34;

//PINS_OUT
int POWER_HOLD = 28;
int THERM_POWER_CONT = 39;

unsigned int speedp = 0;
unsigned int speedr = 3000;
unsigned int speedl = 0;
unsigned long hmillis = 0;
unsigned long pmillis = 0;
unsigned int holdcnt = 0;
#define pmaxs 35000
#define pmins 3000


void setup(void)
{
    Serial.begin(9600);
    while (!Serial);

    Serial.println("LoRa Duplex");

    Serial.print("Freq: ");


    // override the default CS, reset, and IRQ pins (optional)
    LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin
    LoRa.setSignalBandwidth(15.6E3);
    LoRa.setSpreadingFactor(12);
    LoRa.setTxPower(20);

    if (!LoRa.begin(433E6)) {             // initialize ratio at 433 MHz
    Serial.println("LoRa init failed. Check your connections.");
    while (true);                       // if failed, do nothing
  }

    Serial.println("LoRa init succeeded.");
    Serial.print("Rcontrol0=");
    Serial.println(rcontrol);
    Serial.print("Rcontrolsent0=");
    Serial.println(rcontrolsent);
    pinMode(POWER_BUTTON, INPUT_PULLUP);
    pinMode(RIGHT_ARROW, INPUT_PULLUP);
    pinMode(STOP_ALL, INPUT_PULLUP);
    pinMode(PLUS_SPEED, INPUT_PULLUP);
    pinMode(MINUS_SPEED, INPUT_PULLUP);
    pinMode(LEFT_ARROW, INPUT_PULLUP);
    pinMode(POWER_HOLD, OUTPUT);
    pinMode(THERM_POWER_CONT, OUTPUT);
    digitalWrite(THERM_POWER_CONT, HIGH);
    
}

void loop()
{

  if(digitalRead(POWER_BUTTON) == LOW && millis() > 3000 &&  bitRead(controller_status, 0) == 0){
    digitalWrite(POWER_HOLD, HIGH);
    bitWrite(controller_status, 0, 1);
    Serial.println("POWER_ON");
    Serial.print("CONTROLER_STAT=");
    Serial.println(controller_status, BIN);
  }

  if(digitalRead(POWER_BUTTON) == HIGH && bitRead(controller_status, 0) == 1 && bitRead(controller_status, 1) == 0 ){
    bitWrite(controller_status, 1, 1);
  }

    if(digitalRead(POWER_BUTTON) == LOW && bitRead(controller_status, 1) == 1){
      if(bitRead(controller_status, 2) == 0){
      pmillis = millis() + 5000;
      bitWrite(controller_status, 2, 1);
      }
      if(millis() > pmillis){
      digitalWrite(POWER_HOLD, LOW);
      Serial.println("SHUT_DOWN");
      }
  }
  
      if(pcontrol != pcontrolr){
        digitalWrite(THERM_POWER_CONT, LOW);
        bitWrite(controller_status, 3, 1);
        hmillis = millis() + 1000;
        pcontrolr = pcontrol;
      }
      if(bitRead(controller_status, 0) == 1 && millis() > hmillis){
       digitalWrite(THERM_POWER_CONT, HIGH);
       bitWrite(controller_status, 3, 0);
      }
      
      if(rcontrol != rcontrolsent){
      scontrol = rcontrol;
      String message = String (scontrol);
      sendMessage(message);
    }

        
    if (digitalRead(LEFT_ARROW) == LOW && bitRead(rcontrol, 2) == 0 ) {
        bitWrite(rcontrol, 0, 1);
        bitWrite(rcontrol, 1, 0);
        bitWrite(rcontrol, 2, 1);
        Serial.println("RUN_LEFT");
        Serial.print("CONTROL=");
        Serial.println(rcontrol, BIN);
        }
    if (digitalRead(RIGHT_ARROW) == LOW && bitRead(rcontrol, 1) == 0) {
        bitWrite(rcontrol, 0, 1);
        bitWrite(rcontrol, 1, 1);
        bitWrite(rcontrol, 2, 0);
        Serial.println("RUN_RIGHT");
        Serial.print("CONTROL=");
        Serial.println(rcontrol, BIN);
        }
    if (digitalRead(STOP_ALL) == LOW && bitRead(rcontrol,0)== 1)  {
         if(bitRead(rcontrol, 1) == 1){
         bitWrite(rcontrol, 1, 0);
         Serial.println("STOP_ALL");
         Serial.print("CONTROL=");
         Serial.println(rcontrol, BIN);
         }
         if(bitRead(rcontrol, 2) == 1){
         bitWrite(rcontrol, 2, 0);
         Serial.print("CONTROL=");
         Serial.println(rcontrol, BIN);
         }
        bitWrite(rcontrol, 0, 0);
        Serial.print("CONTROL=");
        Serial.println(rcontrol, BIN);
        }
    if (digitalRead(PLUS_SPEED) == LOW && bitRead(rcontrol,3) == 0){
      bitWrite(rcontrol, 3, 1);
      Serial.println("SPEED_UP");
      Serial.print("rcontrol=");
      Serial.println(rcontrol, BIN);
    }
      if (digitalRead(MINUS_SPEED) == LOW && bitRead(rcontrol,4) == 0){
      bitWrite(rcontrol, 4, 1);
      Serial.println("SPEED_DOWN");
      Serial.print("rcontrol=");
      Serial.println(rcontrol, BIN);
      
    }
       if(digitalRead(PLUS_SPEED) == HIGH){ holdcnt = 0;bitWrite(rcontrol, 3, 0);}
       if(digitalRead(MINUS_SPEED) == HIGH){ holdcnt = 0;bitWrite(rcontrol, 4, 0);}
    
   

    onReceive(LoRa.parsePacket());
   
    
}

void onReceive(int packetSize) {
  if (packetSize == 0) return;          // if there's no packet, return

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length
  pcontrol = LoRa.read();
  String  incoming = String(pcontrol);
  

  if (incomingLength != incoming.length()) {   // check length for error
    Serial.println("error: message length does not match length");
    return;                             // skip rest of function
  }

  // if the recipient isn't this device or broadcast,
  if (recipient != localAddress && recipient != 0xFF) {
    Serial.println("This message is not for me.");
    return;                             // skip rest of function
  }

  // if message is for this device, or broadcast, print details:
  Serial.println("Received from: 0x" + String(sender, HEX));
  Serial.println("Sent to: 0x" + String(recipient, HEX));
  Serial.println("Message ID: " + String(incomingMsgId));
  Serial.println("Message length: " + String(incomingLength));
  Serial.println("Message: " + incoming);
  Serial.println(pcontrol, BIN);
  Serial.println("RSSI: " + String(LoRa.packetRssi()));
  Serial.println("Snr: " + String(LoRa.packetSnr()));
  Serial.println();
}


void sendMessage(String outgoing) {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(outgoing.length());        // add payload length
  LoRa.write(rcontrol);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
  Serial.print("Message sent=");
  Serial.println (rcontrol, BIN);
  Serial.print ("rcontrolsent=");
  rcontrolsent = rcontrol;
  Serial.println(rcontrolsent);
}

The documentation for that library says that function returns the number of bytes written to the packet, so you can keep track of actual packet length.

1 Like

seems this code is very weirdly written. Many things don't seem to make sense, including what you are asking about.

Did you write the code?


an example of something weird

  // read packet header bytes:
  int recipient = LoRa.read();          // recipient address
  byte sender = LoRa.read();            // sender address
  byte incomingMsgId = LoRa.read();     // incoming msg ID
  byte incomingLength = LoRa.read();    // incoming msg length
  pcontrol = LoRa.read();
  String  incoming = String(pcontrol);
  if (incomingLength != incoming.length()) {   // check length for error

you read in multiple bytes and what is in pcontrol is also just a byte. So incoming is the ASCII representation of the bytes value in decimal, a string between 0 and 255 basically so 1 to 3 characters.

and then you compare that with incomingLength which was another byte you read from the LoRa payload...
that seems very weird.

typically a structured payload would include the length of the message and so you would have to read in multiple bytes to get the message, not just one byte.

I find it simpler to put all the data in a structure and transmit it in one call to LoRa.write() and receive in a single call to LoRa.readBytes()
have a look at decimals-strings-and-lora
what microcontrollers are you using? take care sending data between different processors which may use different data sizes

Thanks for the response. I shoe horned pcontrol = LoRa.read(); in to example code from the lora library. The two lines after that are just for comparing lengths to check that the actual message received matches the message length (this comes from the library example) and is what is causing me some confusion i think. Is this only checks the variable message that I want to send and not the whole packet.

Hence my original question asking where packet size is determined in the whole function. Does packet size include sender and receiver address or does it only include the size of the data I am adding to the packet (in the case one byte of control)

Thanks for the response. Both are atmel 2560 chips so no issues there.

I will take a read through the linked info, appreciate it.

Thanks, I will read it again. (Seems really simple when you put it this way)

well LoRa does add stuff to your payload (headers' metadata like addressing information and sync word, redundant bits for error correction (FEC), a cryptographic hash / checksum , coding rate, spreading factor,, ...) so there is a lot going on but you usually would not be concerned with that

Why do you need to know?

Basically how important is this bit of code when it comes to the packet size part. (I haven't done my due diligence yet of read the documentation again. Hope to get to it this evening)

      String message = String (scontrol);

But from what I gather so far I can just put what ever i need into sections between Lora.begin/end packet and it takes care of the calculating the packet size there.

Yes that's the general idea

The Lora instance's type inherits from Stream and implements the Print class' write() method, which lets you basically print whatever you want into the lora payload and when you call endPacket() that's when it gets sent out.

There is a catch though, the space in the payload is limited, you can see that in the source code of the implementation of the write() function (which is leveraged by the Print class)

➜ which Lora library are you using? once we know we can go check the source code

library looks like Lora library Semtech SX1276/77/78/79 boards
which LoRa module are u using?

But no more than 255 bytes.

if that's the one then it does inherit from Stream and implement the Print class

you can see there how the max size is handled from the register and

Yeap. That much I got from the library files. I figured my max need of 7 bytes would not be overloading the max size. I have all the clarificarion I need for now. I am reading a couple of other threads to see how best to optimise the signal stength. Currently doing a bit of signal mapping. Getting between 150~300m in a pretty built up area (patch antenna on the mobile side and a 1/4wave wip on the station side)

AI thinker ra-02 433mhz

there is no error checking, e.g. if a packet is lost due to attenuation of signal or interference from other 433MHz signals
does it matter if packets are lost? do you need to know, e.g. use sequence numbers?
request repeat transmission of lost packets?

Maybe I used the wrong terminology. Mapping out how the signal responds in different environments. Each message does have an incrementing number so should be easy to track a lost message. Also bouncing back the message from station to verify it was received.

Finally getting back to this.

Seeing as I only want to send and receive in byte format, can something like this work?

Additionally for multiple sends of the same message to improve the chances of the message getting through, have I gone about this the right way by just repeating the sendMessage void 6 times?

  byte LoRaTXArray[4];
  byte LoRaRXArray[4];
  byte LocalArray[4];

  //DATA IN
  byte rcontrol_R = 0b00000000;
  byte scontrol_R = 0b00000000;
  byte mcontrol_R = 0b00000000;
  byte vcontrol_R = 0b00000000;

  //DATA OUT
  byte rcontrol_L;
  byte scontrol_L;
  byte mcontrol_L;
  byte vcontrol_L;


  void loop() {

   LocalArray[0] = rcontrol_L;
   LocalArray[1] = scontrol_L;
   LocalArray[2] = mcontrol_L;
   LocalArray[3] = vcontrol_L;

  if (strcmp(LocalArray, LoRaTXArray) != 0 && millis() > mdelay){
      sendMessage();
    }

}

  void sendMessage() {
  LoRa.beginPacket();                   // start packet
  LoRa.write(destination);              // add destination address
  LoRa.write(localAddress);             // add sender address
  LoRa.write(msgCount);                 // add message ID
  LoRa.write(LocalArray[].length());        // add payload length
  LoRa.write(LocalArray[]);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
  mdelay = millis() + 100;
  newmsgcnt++;

  if(newmsgcnt == 6){
  LoRaTXArray[0] = LocalArray[0];
  LoRaTXArray[1] = LocalArray[1];
  LoRaTXArray[2] = LocalArray[2];
  LoRaTXArray[3] = LocalArray[3];
  newmsgcnt = 0;}
}





Hopefully going to get into some testing tomorrow.

If its important that the mesages gets through then you should consider a setup whereby the transmitter will keep sending a message (with a timeout) until it gets an acknowledge reply from the receiver.

That makes sense, and I guess just send as rapidly as duty cycle will allow based on LoRa settings. I am going to try it out with a high spreading factor, low bandwidth at TX of -20 with PA boost enabled.