StuartsProjects / SX12XX-LoRa

Library for SX12XX LoRa devices
315 stars 68 forks source link

Decrease LT.receive time #45

Closed ONLYstcm closed 2 years ago

ONLYstcm commented 2 years ago

I'm currently using the SX1280 with this library and it's been working really well so far. Currently I can read my packets with ~600ms, while this is relatively fast, I was wondering if it's possible to decrease the transmission time further. Would I have to make modifications to the SPI transfer function. Ideally I would like to make it receive a packet as fast it transmits (which is ~ 12ms)

StuartsProjects commented 2 years ago

I dont understand what you mean by a packet read taking 600mS.

In this post;

https://stuartsprojects.github.io/2021/11/06/Large-Data-Transfers-with-LoRa-Part4.html

There is an example of using the SX1280s to transfer, send and recieve a 63091 byte file. Each 255 byte packet was transmitted, received error checked and acknowledged within 18mS.

ONLYstcm commented 2 years ago

I've narrowed it down to this line - https://github.com/StuartsProjects/SX12XX-LoRa/blob/master/src/SX128XLT.cpp#L1811, I have this snippet of code:

void loop() {
    while (digitalRead(DIO1)) {  
      // If the DIO pin is high, a packet arrived
      char packet[256]={0};
      LoRa_receiveMessage(packet);
      // Print packet
      Serial.println(packet);
    }
}
void LoRa_receiveMessage(char * RXBUFFER) {
  unsigned long receiverTime = millis();
  RXPacketL = LT.receive((uint8_t *) RXBUFFER, 255, 1000, WAIT_RX); // wait for a packet to arrive with 1 second timeout
  receiverTime = millis() - receiverTime;     // Calculate transmission time - this is where the ~600ms comes from
  Serial.print("Receiver time: ");
  Serial.print(receiverTime);
  Serial.print("ms");

  PacketRSSI = LT.readPacketRSSI();              //read the recived RSSI value
  PacketSNR = LT.readPacketSNR();                //read the received SNR value

  if (RXPacketL == 0)  {                         //if the LT.receive() function detects an error, RXpacketL is 0
    packet_rx_is_Error();
  } else {
    packet_rx_is_OK(RXBUFFER);
  }
  LT.clearIrqStatus(IRQ_RADIO_ALL);                   // Clear all interrupt flags
  LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0);
  LT.setMode(MODE_STDBY_RC);
  uint8_t RXPacketL = LT.receiveSXBuffer(0, 0, NO_WAIT);   //returns 0 if packet error of some sort
  Serial.print("RX Packet Length: ");
  Serial.println(RXPacketL);
}

it seems that the _RXDonePin on my receiver goes HIGH then enters the while loop but then goes LOW after entering the while loop (but before completing LT.receive) and only goes HIGH when the next packet arrives. And essentially the ~600ms wait is the time it takes for the next packet to arrive.

ONLYstcm commented 2 years ago

I've tested this on the 104_LoRa_Receiver_Detailed_Setup_ESP32.ino example code and I can see similar behaviour.

/*******************************************************************************************************
  Programs for Arduino - Copyright of the author Stuart Robinson - 04/04/20

  This program is supplied as is, it is up to the user of the program to decide if the program is
  suitable for the intended purpose and free from errors. 
*******************************************************************************************************/

/*******************************************************************************************************
  Program Operation - This is a program that demonstrates the detailed setup of a LoRa test receiver.
  The program listens for incoming packets using the LoRa settings in the 'Settings.h' file. The pins 
  to access the lora device need to be defined in the 'Settings.h' file also.

  There is a printout on the Arduino IDE Serial Monitor of the valid packets received, the packet is 
  assumed to be in ASCII printable text, if it's not ASCII text characters from 0x20 to 0x7F, expect 
  weird things to happen on the Serial Monitor. The LED will flash for each packet received and the 
  buzzer will sound, if fitted.

  Sample serial monitor output;

  7s  Hello World 1234567890*,CRC,DAAB,RSSI,-52dBm,SNR,9dB,Length,23,Packets,5,Errors,0,IRQreg,50

  If there is a packet error it might look like this, which is showing a CRC error,

  968s PacketError,RSSI,-87dBm,SNR,-11dB,Length,23,Packets,613,Errors,2,IRQreg,70,IRQ_HEADER_VALID,IRQ_CRC_ERROR,IRQ_RX_DONE

  Serial monitor baud rate is set at 9600.
*******************************************************************************************************/

#define Program_Version "V1.1"

#include <SPI.h>                                 //the lora device is SPI based so load the SPI library
#include <SX128XLT.h>                            //include the appropriate library   
#include "Settings.h"                            //include the setiings file, frequencies, LoRa settings etc   

SX128XLT LT;                                     //create a library class instance called LT

uint32_t RXpacketCount;
uint32_t errors;

uint8_t RXBUFFER[RXBUFFER_SIZE];                 //create the buffer that received packets are copied into

uint8_t RXPacketL;                               //stores length of packet received
int8_t  PacketRSSI;                              //stores RSSI of received packet
int8_t  PacketSNR;                               //stores signal to noise ratio (SNR) of received packet

void loop()
{
  unsigned long receiverTime = millis();

  RXPacketL = LT.receive(RXBUFFER, RXBUFFER_SIZE, 60000, WAIT_RX); //wait for a packet to arrive with 60seconds (60000mS) timeout

  receiverTime = millis() - receiverTime;     // Calculate transmission time
  Serial.print("Receiver time: ");
  Serial.print(receiverTime);
  Serial.print("ms");

  digitalWrite(LED1, HIGH);                      //something has happened

  if (BUZZER > 0)                                //turn buzzer on
  {
    digitalWrite(BUZZER, HIGH);
  }

  PacketRSSI = LT.readPacketRSSI();              //read the recived RSSI value
  PacketSNR = LT.readPacketSNR();                //read the received SNR value

  if (RXPacketL == 0)                            //if the LT.receive() function detects an error, RXpacketL is 0
  {
    packet_is_Error();
  }
  else
  {
    packet_is_OK();
  }

  if (BUZZER > 0)
  {
    digitalWrite(BUZZER, LOW);                   //buzzer off
  }

  digitalWrite(LED1, LOW);                       //LED off

  Serial.println();
}

void packet_is_OK()
{
  uint16_t IRQStatus, localCRC;

  IRQStatus = LT.readIrqStatus();                 //read the LoRa device IRQ status register

  RXpacketCount++;

  printElapsedTime();                             //print elapsed time to Serial Monitor
  Serial.print(F("  "));
  LT.printASCIIPacket(RXBUFFER, RXPacketL);       //print the packet as ASCII characters

  localCRC = LT.CRCCCITT(RXBUFFER, RXPacketL, 0xFFFF);  //calculate the CRC, this is the external CRC calculation of the RXBUFFER
  Serial.print(F(",CRC,"));                       //contents, not the LoRa device internal CRC
  Serial.print(localCRC, HEX);
  Serial.print(F(",RSSI,"));
  Serial.print(PacketRSSI);
  Serial.print(F("dBm,SNR,"));
  Serial.print(PacketSNR);
  Serial.print(F("dB,Length,"));
  Serial.print(RXPacketL);
  Serial.print(F(",Packets,"));
  Serial.print(RXpacketCount);
  Serial.print(F(",Errors,"));
  Serial.print(errors);
  Serial.print(F(",IRQreg,"));
  Serial.print(IRQStatus, HEX);
}

void packet_is_Error()
{
  uint16_t IRQStatus;
  IRQStatus = LT.readIrqStatus();                   //read the LoRa device IRQ status register

  printElapsedTime();                               //print elapsed time to Serial Monitor

  if (IRQStatus & IRQ_RX_TIMEOUT)                   //check for an RX timeout
  {
    Serial.print(F(" RXTimeout"));
  }
  else
  {
    errors++;
    Serial.print(F(" PacketError"));
    Serial.print(F(",RSSI,"));
    Serial.print(PacketRSSI);
    Serial.print(F("dBm,SNR,"));
    Serial.print(PacketSNR);
    Serial.print(F("dB,Length,"));
    Serial.print(LT.readRXPacketL());               //get the device packet length
    Serial.print(F(",Packets,"));
    Serial.print(RXpacketCount);
    Serial.print(F(",Errors,"));
    Serial.print(errors);
    Serial.print(F(",IRQreg,"));
    Serial.print(IRQStatus, HEX);
    LT.printIrqStatus();                            //print the names of the IRQ registers set
  }

  delay(250);                                       //gives a longer buzzer and LED flash for error 

}

void printElapsedTime()
{
  float seconds;
  seconds = millis() / 1000;
  Serial.print(seconds, 0);
  Serial.print(F("s"));
}

void led_Flash(uint16_t flashes, uint16_t delaymS)
{
  uint16_t index;

  for (index = 1; index <= flashes; index++)
  {
    digitalWrite(LED1, HIGH);
    delay(delaymS);
    digitalWrite(LED1, LOW);
    delay(delaymS);
  }
}

void setup()
{
  pinMode(LED1, OUTPUT);                        //setup pin as output for indicator LED
  led_Flash(2, 125);                            //two quick LED flashes to indicate program start

  Serial.begin(115200);
  Serial.println();
  Serial.print(F(__TIME__));
  Serial.print(F(" "));
  Serial.println(F(__DATE__));
  Serial.println(F(Program_Version));
  Serial.println();
  Serial.println(F("104_LoRa_Receiver_Detailed_Setup_ESP32 Starting"));
  Serial.println();

  if (BUZZER > 0)
  {
    pinMode(BUZZER, OUTPUT);
    digitalWrite(BUZZER, HIGH);
    delay(50);
    digitalWrite(BUZZER, LOW);
  }

  SPI.begin();

  //SPI beginTranscation is normally part of library routines, but if it is disabled in the library
  //a single instance is needed here, so uncomment the program line below
  //SPI.beginTransaction(SPISettings(8000000, MSBFIRST, SPI_MODE0));

  //setup hardware pins used by device, then check if device is found
  if (LT.begin(NSS, NRESET, RFBUSY, DIO1, DIO2, DIO3, RX_EN, TX_EN, LORA_DEVICE))
  {
    Serial.println(F("LoRa Device found"));
    led_Flash(2, 125);
    delay(1000);
  }
  else
  {
    Serial.println(F("No device responding"));
    while (1)
    {
      led_Flash(50, 50);                                       //long fast speed LED flash indicates device error
    }
  }

  //The function call list below shows the complete setup for the LoRa device using the information defined in the
  //Settings.h file.
  //The 'Setup LoRa device' list below can be replaced with a single function call;
  //LT.setupLoRa(Frequency, Offset, SpreadingFactor, Bandwidth, CodeRate);

  //***************************************************************************************************
  //Setup LoRa device
  //***************************************************************************************************
  LT.setMode(MODE_STDBY_RC);
  LT.setRegulatorMode(USE_LDO);
  LT.setPacketType(PACKET_TYPE_LORA);
  LT.setRfFrequency(Frequency, Offset);
  LT.setBufferBaseAddress(0, 0);
  LT.setModulationParams(SpreadingFactor, Bandwidth, CodeRate);
  LT.setPacketParams(12, LORA_PACKET_VARIABLE_LENGTH, 255, LORA_CRC_ON, LORA_IQ_NORMAL, 0, 0);
  LT.setDioIrqParams(IRQ_RADIO_ALL, (IRQ_TX_DONE + IRQ_RX_TX_TIMEOUT), 0, 0);
  //***************************************************************************************************

  Serial.println();
  LT.printModemSettings();                                     //reads and prints the configured LoRa settings, useful check
  Serial.println();
  LT.printOperatingSettings();                                 //reads and prints the configured operting settings, useful check
  Serial.println();
  Serial.println();
  LT.printRegisters(0x900, 0x9FF);                             //print contents of device registers, normally 0x900 to 0x9FF
  Serial.println();
  Serial.println();

  Serial.print(F("Receiver ready - RXBUFFER_SIZE "));
  Serial.println(RXBUFFER_SIZE);
  Serial.println();
}
22s  Hello World 1234567890*,CRC,DAAB,RSSI,-29dBm,SNR,11dB,Length,23,Packets,15,Errors,0,IRQreg,8012
Receiver time: 1004ms
23s  Hello World 1234567890*,CRC,DAAB,RSSI,-29dBm,SNR,11dB,Length,23,Packets,16,Errors,0,IRQreg,8012
Receiver time: 1004ms
24s  Hello World 1234567890*,CRC,DAAB,RSSI,-29dBm,SNR,11dB,Length,23,Packets,17,Errors,0,IRQreg,8012
Receiver time: 1004ms

The 1s receiver time is the time it takes for the transmitter to send the next packet. Essentially this means that the while loop receiver function is blocked by the _RXDonePin which only gets reset when a next packet arrives. So if a transmitter only needs to send one packet the receiver will get blocked indefinitely

StuartsProjects commented 2 years ago

You appear to be trying to use LT.receive(); in a way not as intended.

Check the library code; line 1803

setDioIrqParams(IRQ_RADIO_ALL, (IRQ_RX_DONE + IRQ_RX_TX_TIMEOUT + IRQ_HEADER_ERROR), 0, 0); //set for IRQ on RX done or timeout

So LT.receive(); clears the DIO1 IRQ, which makes DIO1 go low and then LT.receive(); waits for DIO1 to go high indicating a packet received or a timeout.

ONLYstcm commented 2 years ago

I see...... I just made a modification to my code, I'm now listening for packets like this...

char packet[256]={0};

void loop() {
    if ( LoRa_receiveMessage(packet)) {
      // Print packet
      Serial.println(packet);
    }
}
bool LoRa_receiveMessage(char * RXBUFFER) {
  // wait for a packet to arrive with 1 second timeout
  if (! LT.receive((uint8_t *) RXBUFFER, 255, 1000, WAIT_RX)){
     packet_rx_is_Error(); 
     return false;
  }
  PacketRSSI = LT.readPacketRSSI();              //read the recived RSSI value
  PacketSNR = LT.readPacketSNR();                //read the received SNR value
  packet_rx_is_OK(RXBUFFER);
  return true;
}

Seems to be working as expected!

StuartsProjects commented 2 years ago

If you do an LT.receive() with NO_WAIT set at the end of setup() the function will setup the LoRa device to listen for a packet and exit. You can then check in loop() for DIO1 going high indicating a packet received.

If a packet is received in loop() you can read the packet from the buffer with readpacket(), carry out whatever operartons you require, then do an LT.receive() to start receive again and carry on with loop().