LoRa two-way communication

Hello, I'm trying to create an abort system using LoRa for a model rocket by having a flight computer send data to a receiver, which will sort of be a ground station, where flight data will be sent to. However, in the case of an emergency or for what ever reason I would like to be able to abort the flight and fire off parachutes and save all the inflight data.

I currently have my (rocket) sender sending bmp280 data as a test to the receiver which works fine, however not very consistently.

I don't have a button to act as the abort trigger so i'm using an mpu-6050 imu which will abort the flight if the receiver is tilted too much (Bit over complicated but it works).

I have the sender (rocket) sending data then listening straight away to see if the message 'ABORT' is received in which it will turn an LED red.

it doesn't seem to be able to listen for the abort message properly.

here is my sender (Rocket) code -

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

Adafruit_BMP280 bmp;
const int blue = 3;//set blue to pin 3
const int red = 1;//set red to pin 5
const int green = 2;//set green to pin 6 


void setup() {
    Serial.begin(9600);
  delay(2000);
  LoRa.setPins(22, 8, 9);
      if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  LoRa.setTxPower(50);

  bmp.begin(0x76);
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500);

  pinMode(red, OUTPUT);//set red as an output
  pinMode(green, OUTPUT);//set green as an output
  pinMode(blue, OUTPUT);//set blue as an output

  digitalWrite(red,LOW);
  digitalWrite(green,LOW);
  digitalWrite(blue,LOW);


}

void loop() {
  Serial.print(bmp.readTemperature());
  Serial.println(" *C");
  Serial.print(bmp.readPressure());
  Serial.println(" Pa");

  LoRa.beginPacket();
  LoRa.print(bmp.readTemperature());
  LoRa.print(" *C");
  LoRa.print(" ");
  LoRa.print(bmp.readPressure());
  LoRa.print(" Pa");
  LoRa.endPacket();

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

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

    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());
  }

 delay(100);
}

and here is my receiver code -

#include <SPI.h>
#include <LoRa.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Wire.h>

Adafruit_MPU6050 mpu;
double acc ;
bool Abort = false;

void setup() {
    Serial.begin(9600);   
    if (!mpu.begin()) {
    Serial.println("Failed to find MPU6050 chip");
    while (1) {
      delay(10);
    }
  }

  mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
  Serial.print("Accelerometer range set to: ");
  switch (mpu.getAccelerometerRange()) {
  case MPU6050_RANGE_2_G:
    Serial.println("+-2G");
    break;
  case MPU6050_RANGE_4_G:
    Serial.println("+-4G");
    break;
  case MPU6050_RANGE_8_G:
    Serial.println("+-8G");
    break;
  case MPU6050_RANGE_16_G:
    Serial.println("+-16G");
    break;
  }
  mpu.setGyroRange(MPU6050_RANGE_500_DEG);
  Serial.print("Gyro range set to: ");
  switch (mpu.getGyroRange()) {
  case MPU6050_RANGE_250_DEG:
    Serial.println("+- 250 deg/s");
    break;
  case MPU6050_RANGE_500_DEG:
    Serial.println("+- 500 deg/s");
    break;
  case MPU6050_RANGE_1000_DEG:
    Serial.println("+- 1000 deg/s");
    break;
  case MPU6050_RANGE_2000_DEG:
    Serial.println("+- 2000 deg/s");
    break;
  }

  mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
  Serial.print("Filter bandwidth set to: ");
  switch (mpu.getFilterBandwidth()) {
  case MPU6050_BAND_260_HZ:
    Serial.println("260 Hz");
    break;
  case MPU6050_BAND_184_HZ:
    Serial.println("184 Hz");
    break;
  case MPU6050_BAND_94_HZ:
    Serial.println("94 Hz");
    break;
  case MPU6050_BAND_44_HZ:
    Serial.println("44 Hz");
    break;
  case MPU6050_BAND_21_HZ:
    Serial.println("21 Hz");
    break;
  case MPU6050_BAND_10_HZ:
    Serial.println("10 Hz");
    break;
  case MPU6050_BAND_5_HZ:
    Serial.println("5 Hz");
    break;
  }
  //(CS, RST, IOD)
  //LoRa.setPins(22, 8, 9);
    if (!LoRa.begin(433E6)) {
    Serial.println("Starting LoRa failed!");
    while (1);
  }
  Serial.println("Start")
}

void loop() {

//Listening loop
//Sending loop
if (Abort == true ){
  //Print "ABORT" by going to a new void which turns the lora module into a sender
  sending();
} 
if (Abort == false){
  listening();
}
  
}

void listening(){
  sensors_event_t a, g, temp;
  mpu.getEvent(&a, &g, &temp);

acc = a.acceleration.x;
Serial.println(acc);
if (acc < 3){
  Abort = true;
}  
  Serial.println("Searching");
  int packetSize = LoRa.parsePacket();
    delay(500);
  if (packetSize) {
    // received a packet
    Serial.print("Received packet '");

    // read packet
    while (LoRa.available()) {
      //Serial.print((char)LoRa.read());
      Serial.print((char)LoRa.read());
    }
    // print RSSI of packet
    Serial.print("' with RSSI ");
    Serial.println(LoRa.packetRssi());
  }
  delay(100);
}

void sending() {
  //Dont know if this can be done in setup if it will effect the listening
  LoRa.setTxPower(50);
  Serial.println("ABORT");
  LoRa.beginPacket();
  LoRa.print("ABORT");
  LoRa.endPacket();
  delay(500);
}

My sender microprocessor is the teensy 4.0 and the receiver is an Arduino nano which work fine talking to each other. I'm hoping to turn the station into a raspberry pi later in the future.

any help is appreciated
-Harvey :slight_smile:

If you send a packet and immediatly check if there is a packet received, that is going to fail, LoRa devices are transceivers but they cannot transmit and receive at the same time..

Think of the sequence;

  1. Device A completes transmitting a packet
  2. Device A turns around into receive mode
  3. Device B receives a packet.
  4. Device B processes the packet, displays it records it etc.
  5. Device B turns around into transmit mode
  6. Device B starts transmitting a packet
  7. Device B completes transmitting a packet
  8. Device A receives a packet

So if at point 2 device A checks to see if a packet has been received then it wont be, since steps 3,4,5,6,7 have yet to happen and all take time.

Not sure how the LoRa library you are using deals with the turnaround between TX and RX although there is a Duplex example provided.

@srnet hey, thank you for your help, I've used the examples duplex code and have successfully been able to have a button pressed and 'abort the flight' thank you :slight_smile:

I'll post the finish code just incase someone wants to use this as help for themselves in their future projects

Here's the 'rocket' sender

#include <SPI.h>              // include libraries
#include <LoRa.h>
#include <Adafruit_BMP280.h>
#include <Wire.h>

Adafruit_BMP280 bmp;
const int blue = 3;//set blue to pin 3
const int red = 1;//set red to pin 5
const int green = 2;//set green to pin 6 

const int csPin = 22;          // LoRa radio chip select
const int resetPin = 8;       // LoRa radio reset
const int irqPin = 9;         // change for your board; must be a hardware interrupt pin

String outgoing;              // outgoing message
String Abort = "ABORT!";
byte msgCount = 0;    // count of outgoing messages
//Original
byte localAddress = 0xBB;     // address of this device
byte destination = 0xFF;      // destination to send to
//byte localAddress = 0xFF;     // address of this device
//byte destination = 0xBB;

long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends
double temp;
double pressure;

String tempData;
String pressureData;
String Data;


void setup() {
  Serial.begin(9600); // initialize serial
  delay(2000);
  //while (!Serial);
  Serial.println("LoRa Duplex Rocket");

  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin
  //LoRa.setPins(22, 8, 9);
  if (!LoRa.begin(433E6)) {             // initialize ratio at 915 MHz
    Serial.println("LoRa init failed. Check your connections.");
    while (true);                       // if failed, do nothing
  }
  LoRa.setTxPower(50);
  bmp.begin(0x76);
  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,     /* Operating Mode. */
                  Adafruit_BMP280::SAMPLING_X2,     /* Temp. oversampling */
                  Adafruit_BMP280::SAMPLING_X16,    /* Pressure oversampling */
                  Adafruit_BMP280::FILTER_X16,      /* Filtering. */
                  Adafruit_BMP280::STANDBY_MS_500);

  pinMode(red, OUTPUT);//set red as an output
  pinMode(green, OUTPUT);//set green as an output
  pinMode(blue, OUTPUT);//set blue as an output

  digitalWrite(red,LOW);
  digitalWrite(green,LOW);
  digitalWrite(blue,LOW);


  Serial.println("LoRa init succeeded.");
}

void loop() {
  if (millis() - lastSendTime > interval) {
    //Message = bmp read
    temp = bmp.readTemperature();
    pressure = bmp.readPressure();
    tempData = String(temp, 2) + " *C ";
    pressureData = String(pressure, 2) + " Pa"; 
    Data = tempData + pressureData;
    sendMessage(Data);
    Serial.println("Sending " + Data);
    lastSendTime = millis();            // timestamp the message
    interval = random(2000) + 1000;    // 2-3 seconds
  }

  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket());
}

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.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

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

  String incoming = "";

  while (LoRa.available()) {
    incoming += (char)LoRa.read();
  }
  if (incoming == Abort){
    digitalWrite(red,HIGH);
    while(1);
  }

  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("RSSI: " + String(LoRa.packetRssi()));
  Serial.println("Snr: " + String(LoRa.packetSnr()));
  Serial.println();
}

Here's the station code, which is used to press the button to abort the flight

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



const int csPin = 10;          // LoRa radio chip select
const int resetPin = 9;       // LoRa radio reset
const int irqPin = 2;         // change for your board; must be a hardware interrupt pin

const int buttonPin = 5;

String outgoing;              // outgoing message
int buttonState = 0;     

bool Abort = false;

byte msgCount = 0;            // count of outgoing messages
byte localAddress = 0xBB;     // address of this device
byte destination = 0xFF;      // destination to send to
long lastSendTime = 0;        // last send time
int interval = 2000;          // interval between sends

void setup() {
  Serial.begin(9600);                   // initialize serial
  delay(2000);
  //while (!Serial);
  pinMode(buttonPin, INPUT);
  Serial.println("LoRa Duplex");

  // override the default CS, reset, and IRQ pins (optional)
  LoRa.setPins(csPin, resetPin, irqPin);// set CS, reset, IRQ pin

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

  Serial.println("LoRa init succeeded.");
}

void loop() {
  buttonState = digitalRead(buttonPin);


  
    if (Abort == false){
      if (millis() - lastSendTime > interval) {
        if (buttonState == HIGH) {
         Abort = true;
      String message = "ABORT!";   // send a message
      sendMessage(message);
      Serial.println(message);
      lastSendTime = millis();            // timestamp the message
      interval = random(2000) + 1000;    // 2-3 seconds
    
        }
      
      }
      Serial.println("Searching");
    }
  else{ 
    String message = "ABORT!";   // send a message
    sendMessage(message);
    Serial.println("Sending " + message);
    lastSendTime = millis();            // timestamp the message
    interval = random(2000) + 1000;    // 2-3 seconds
    
  }

  // parse for a packet, and call onReceive with the result:
  onReceive(LoRa.parsePacket());
  }


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.print(outgoing);                 // add payload
  LoRa.endPacket();                     // finish packet and send it
  msgCount++;                           // increment message ID
}

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

  String incoming = "";
//Recieving the data
  while (LoRa.available()) {
    incoming += (char)LoRa.read();
  }

  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("RSSI: " + String(LoRa.packetRssi()));
  Serial.println("Snr: " + String(LoRa.packetSnr()));
  Serial.println();
}

thank you again for helping -Harvey :slight_smile: