timmbogner / Farm-Data-Relay-System

A system that uses ESP-NOW, LoRa, and other protocols to transport sensor data in remote areas without relying on WiFi.
MIT License
485 stars 108 forks source link

Supporting RadioLib #125

Closed timmbogner closed 1 year ago

timmbogner commented 1 year ago

Much love to the Arduino LoRa library, but I was drawn to the concept of RadioLib's one radio library to rule them all!

This PR doesn't add too much functionality yet. It is still only compatible with SX127x modules, same as before. However, RadioLib will make it a lot easier to add support for the SX126x and SX128x chips. Hopefully some other capabilities of the libraries can be utilized later on. Morse code perhaps?

I would really appreciate it if everyone would give this a try, and let me know if you find any bugs. I have some housekeeping to complete, mostly adding the following to all configs, with a warning to only use SX127x:

#define LORA_DIO1 33
#define RADIOLIB_MODULE SX1276
timmbogner commented 1 year ago

There are two issues that I'm dealing with:

  1. Frequency is currently hard-coded to 915. I'm working on overhauling the configuration system to fix that and straighten out a few things. It will be what I work on as I monitor problem 2:
  2. There's a bug occurring on the gateway where it reports receiving an unknown packet, then stops responding to LoRa packets. It happens randomly as it seems, usually within 20 minutes. and I am guessing there is a jumble in my interrupt routine. It is taking a while because the more debug messages I add to diagnose it, the less often the bug actually occurs 😂
aviateur17 commented 1 year ago

Tim, I started working on this as well a few days ago. I'm working on sensor transmit but running into issues where only the first packet is received.

On Fri, Dec 2, 2022, 6:08 PM Timm Bogner @.***> wrote:

There are two issues that I'm dealing with:

  1. Frequency is currently hard-coded to 915. I'm working on overhauling the configuration system to fix that and straighten out a few things. It will be what I work on as I monitor problem 2:
  2. There's a bug occurring on the gateway where it reports receiving an unknown packet, then stops responding to LoRa packets. It happens randomly as it seems, usually within 20 minutes. and I am guessing there is a jumble in my interrupt routine. It is taking a while because the more debug messages I add to diagnose it, the less often the bug actually occurs 😂

— Reply to this email directly, view it on GitHub https://github.com/timmbogner/Farm-Data-Relay-System/pull/125#issuecomment-1335976914, or unsubscribe https://github.com/notifications/unsubscribe-auth/AOTXENORNPDGJ472F4ZYTX3WLKFP3ANCNFSM6AAAAAASRNYXIQ . You are receiving this because you are subscribed to this thread.Message ID: @.***>

timmbogner commented 1 year ago

@aviateur17 I based mine off the PingPong example and came up with a routine that looks like this, currently filled with debugs:

void handleLoRa(){
  if(operationDone) { // the interrupt was triggered
  DBG("Interrupt");
    enableInterrupt = false;
    operationDone = false;
    if(transmitFlag) {  // the previous operation was transmission,
      DBG("Ending transmission, entering reception mode.");
      radio.startReceive();   // return to listen mode 
      enableInterrupt = true;
      transmitFlag = false;
    } else {  // the previous operation was reception
          DBG("Reception has occurred, entering getLoRa() ");
      returnCRC = getLoRa();
      enableInterrupt = true;
      }
    }  
  }
timmbogner commented 1 year ago

Here is a look at the error occurring, with it set up to dump the packet if it doesn't process.

18:45:46.987 -> CRC Match, sending ACK packet to sensor 0xeb50(hex)
18:45:46.987 -> Transmitting LoRa message of size 11 bytes with CRC 0xffea to LoRa MAC 0xeb50
18:45:46.987 ->  begun successfully!
18:45:46.987 -> Sending Serial.
18:45:46.987 -> [{"id":2,"type":3,"data":47},{"id":2,"type":1,"data":21}]
18:45:47.019 -> Interrupt
18:45:47.052 -> Ending transmission, entering reception mode.
18:45:50.185 -> Interrupt
18:45:50.185 -> Reception hass occurred, entering getLoRa() 
18:45:50.185 -> Incoming LoRa. Size: 20 Bytes, RSSI: -95.00dBm, SNR: 9.25dB, PacketCRC: 0x33b1, Total LoRa received: 306, CRC Ok Pct 100.00%
18:45:50.185 -> CRC Match, sending ACK packet to sensor 0xeb50(hex)
18:45:50.185 -> Transmitting LoRa message of size 11 bytes with CRC 0xffea to LoRa MAC 0xeb50
18:45:50.185 ->  begun successfully!
18:45:50.220 -> Sending Serial.
18:45:50.220 -> [{"id":2,"type":3,"data":60},{"id":2,"type":1,"data":21}]
18:45:50.262 -> Interrupt
18:45:50.262 -> Ending transmission, entering reception mode.
18:45:53.381 -> Interrupt
18:45:53.381 -> Reception hass occurred, entering getLoRa() 
18:45:53.381 -> Incoming LoRa. Size: 20 Bytes, RSSI: -95.00dBm, SNR: 9.50dB, PacketCRC: 0xf4fb, Total LoRa received: 307, CRC Ok Pct 100.00%
18:45:53.381 -> CRC Match, sending ACK packet to sensor 0xeb50(hex)
18:45:53.381 -> Transmitting LoRa message of size 11 bytes with CRC 0xffea to LoRa MAC 0xeb50
18:45:53.419 ->  begun successfully!
18:45:53.419 -> Sending Serial.
18:45:53.419 -> [{"id":2,"type":3,"data":2},{"id":2,"type":1,"data":21}]
18:45:53.459 -> Interrupt
18:45:53.459 -> Ending transmission, entering reception mode.
18:45:55.344 -> Interrupt
18:45:55.344 -> Reception hass occurred, entering getLoRa() 
18:45:55.344 -> Incoming LoRa packet of 169bytes not processed.
18:45:55.344 -> Printing packet of size 169.
18:45:55.344 -> 00: F5 4A 
18:45:55.344 -> 02: 0D 6E 
18:45:55.344 -> 04: C6 F4 
18:45:55.344 -> 06: 74 3E 
18:45:55.344 -> 08: 62 EC 
18:45:55.344 -> 10: 85 37 
... 
timmbogner commented 1 year ago

I think "phantom packets" are to blame for the issue. My guess is that the Arduino LoRa library was doing something to filter them.

Running the example SX127x_Receive_Interrupt (using the same default configs as FDRS) is showing packets of jibberish and a lot of internal LoRa CRC errors. This is especially interesting because I have CRC disabled via radio.setCRC(false);. When running on two modules simultaneously, the errors/packets never occur simultaneously on both devices.

Anybody got advice? I'm still working on understanding the article I linked to...

aviateur17 commented 1 year ago

Anybody got advice? I'm still working on understanding the article I linked to...

I'm not really sure myself. Is that incoming 169 byte packet being received at the same time as one of your other sensors/nodes are transmitting? Maybe others using the same library are having the same issue. maybe search the issues for that library if you haven't already. I haven't tried your branch yet as I really want to squash the issue I'm having but I'm not really sure that I'll be able to do it without some serious debug. I'm in the process of just setting up transmit in the sensor/node and my ESP is rebooting due to watchdog timeout. When I take out the transmit line there are no issues so I'm thinking my issue may be related to the library. I have to do some searching as well. I'll see if I can try out your code and see if I see the same issue as you but it may take a day or two for that to happen. Hopefully others can chime in.

timmbogner commented 1 year ago

Nah, the problem is independent of the sensor. There is nothing transmitting, and the jibberish and errors are still showing up on the receiver. Additionally, when I increased the bandwidth to 500 (a feature now available in globals.h) it increased the occurrence of the errors, which is what is suggested would also increase phantom packet occurrences.

In actuality, I think now that I know what these are, the solution is to just ignore them. The real problem here is that when these packets are and received getLoRa() ignores them, it currently doesn't reset one of the right flags or something.

Are you working off of an example? Try replacing it with radio.startTransmit() maybe? I was hung up with RadioLib for a while because I needed to specify my actual board('TTGO LoRa 32 OLED' or something) in Arduino IDE (instead of 'ESP32 Devkit' like I usually use for everything). Are you on ESP32?

More info dumping... here is the output of SX127x_Receive_Interrupt.ino, using the same default configs as FDRS, except with bandwidth increased to 500 to encourage 'phantom' packets:

14:12:25.604 -> [SX1278] Received packet!
14:12:25.604 -> [SX1278] Data:      ���T����wfq��jum݋�@��$�Cw�g��˿�6�`a��s��O'
14:12:25.649 -> [SX1278] RSSI:      -124.00 dBm
14:12:25.649 -> [SX1278] SNR:       -12.00 dB
14:12:25.649 -> [SX1278] Frequency error:   21642.61 Hz
14:21:11.323 -> [SX1278] CRC error!
14:25:14.494 -> [SX1278] Received packet!
14:25:14.494 -> [SX1278] Data:      \}��#�T+�18XHW�{��5Lr��O|"6KfF[�-IV�?OdB�Q��H���5B�Qy6w?�����mb�(Kΐ�
b\Y~��X
14:25:14.494 -> [SX1278] RSSI:      -124.00 dBm
14:25:14.494 -> [SX1278] SNR:       -12.00 dB
14:25:14.494 -> [SX1278] Frequency error:   33453.77 Hz
14:29:42.632 -> [SX1278] Received packet!
14:29:42.632 -> [SX1278] Data:      �Z�_@��[��'��pt�����ԣ�rR�N5(�{>��x2F)��S_b�A�3#�z#�X��5��p/�V��6Jr��
14:29:42.677 -> [SX1278] RSSI:      -125.00 dBm
14:29:42.677 -> [SX1278] SNR:       -12.00 dB
14:29:42.677 -> [SX1278] Frequency error:   -570.43 Hz
14:31:02.096 -> [SX1278] CRC error!
14:53:34.655 -> [SX1278] CRC error!
15:10:36.217 -> [SX1278] Received packet!
15:10:36.217 -> [SX1278] Data:      Y�TI;%�D���T.���J�9��6�:���Wn߀ý�!�G�A�'SK���x�Wi
15:10:36.217 -> [SX1278] RSSI:      -124.50 dBm
15:10:36.217 -> [SX1278] SNR:       -12.50 dB
15:10:36.217 -> [SX1278] Frequency error:   113447.53 Hz
15:10:46.081 -> [SX1278] Received packet!
15:10:46.081 -> [SX1278] Data:      ?���"%&P���!zdz3
��ڣr�jd]٠~��9.r�7���<,�-{:Y��I}��e�

15:10:46.081 -> [SX1278] RSSI:      -123.75 dBm
15:10:46.081 -> [SX1278] SNR:       -11.75 dB
15:10:46.081 -> [SX1278] Frequency error:   137136.97 Hz
15:17:29.319 -> [SX1278] Received packet!
15:17:29.319 -> [SX1278] Data:      ���kA���6�S���6��f�f���s�.��L=B���^���3Yu�������=U�  �Z�Z~����>&�+��b�v��h��+_�}8��,g�Yt�ʌ���h3V̈́zn��w��j�|@�*A�n#H����t<���p���=K���`?�I�_�`��W4�?�����J�(�צ� ֳ�/P��ε�����=�
15:17:29.365 -> [SX1278] RSSI:      -123.50 dBm
15:17:29.365 -> [SX1278] SNR:       -12.50 dB
15:17:29.365 -> [SX1278] Frequency error:   -69155.69 Hz
15:17:34.443 -> [SX1278] Received packet!
15:17:34.443 -> [SX1278] Data:      �y�ip�C�B�9��q��n�W�F�0��4L�3�� �M�,���8�]{�'u�@$�|�
15:17:34.443 -> [SX1278] RSSI:      -122.75 dBm
15:17:34.443 -> [SX1278] SNR:       -11.75 dB
15:17:34.443 -> [SX1278] Frequency error:   -124252.06 Hz
15:18:04.551 -> [SX1278] Received packet!
15:18:04.551 -> [SX1278] Data:      �پ��E�ג:����tA��T6V�ET
���rT��I�e�OW���i�2����4P�̋pQ!`��   �&�RQ=4ط[Z�R���p�Lmou�ǫ��R���hKS���^�Ќ(��#��4|$�
15:18:04.551 -> [SX1278] RSSI:      -124.00 dBm
15:18:04.551 -> [SX1278] SNR:       -12.00 dB
15:18:04.551 -> [SX1278] Frequency error:   -88147.49 Hz
15:19:09.057 -> [SX1278] CRC error!
aviateur17 commented 1 year ago

Yeah, I don't think RadioLib is a good library. I took the pingPong example and changed the begin() settings to match the other LoRa library and added some serial print statements and that's it and i'm seeing the transmits stop at random transmit statements - could be 9 transmits, could be 30, rarely 100. But over and over again it will not keep transmitting reliably for over 100 TX cycles. So I thought maybe it's the hardware so I put the same on a different controller and same thing happens - I get 9 or 30 TX cycles and then it just sits there. With what you're seeing and this limited TX cycle issue I don't get the warm fuzzy feeling from this library.


   RadioLib SX127x Ping-Pong Example

   For default module settings, see the wiki page
   https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem

   For full API reference, see the GitHub Pages
   https://jgromes.github.io/RadioLib/
*/

// include the library
#include <RadioLib.h>

// uncomment the following only on one
// of the nodes to initiate the pings
#define INITIATING_NODE

// SX1278 has the following connections:
// NSS pin:   10
// DIO0 pin:  2
// NRST pin:  9
// DIO1 pin:  3
SX1276 radio = new Module(26, 5, 27, 5);

// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 radio = RadioShield.ModuleA;

// save transmission states between loops
int transmissionState = RADIOLIB_ERR_NONE;

// flag to indicate transmission or reception state
bool transmitFlag = false;

// disable interrupt when it's not needed
volatile bool enableInterrupt = true;

// flag to indicate that a packet was sent or received
volatile bool operationDone = false;

uint32_t txNum = 0;

// this function is called when a complete packet
// is transmitted or received by the module
// IMPORTANT: this function MUST be 'void' type
//            and MUST NOT have any arguments!
void setFlag(void) {
  // check if the interrupt is enabled
  //Serial.println("ISR!");
  if(!enableInterrupt) {
    return;
  }

  // we sent or received  packet, set the flag
  operationDone = true;
}

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

  // initialize SX1278 with default settings
  Serial.print(F("[SX1278] Initializing ... "));
  //int state = radio.begin();
  int state = radio.begin(915.0,125.0,7,5,0x12,2,8,0);
  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.setDio0Action(setFlag);

  #if defined(INITIATING_NODE)
    // send the first packet on this node
    Serial.print(F("[SX1278] Sending first packet ... "));
    transmissionState = radio.startTransmit("Hello World!");
    transmitFlag = true;
  #else
    // start listening for LoRa packets on this node
    Serial.print(F("[SX1278] 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);
    }
  #endif
}

void loop() {
  // check if the previous operation finished
  if(operationDone) {
    // disable the interrupt service routine while
    // processing the data
    enableInterrupt = false;

    // reset flag
    operationDone = false;

    if(transmitFlag) {
      // the previous operation was transmission, listen for response
      // print the result
      if (transmissionState == RADIOLIB_ERR_NONE) {
        // packet was successfully sent
        txNum++;
        Serial.print(F("transmission finished! "));
        Serial.println(txNum);

      } else {
        Serial.print(F("failed, code "));
        Serial.println(transmissionState);

      }

      // listen for response
      radio.startReceive();
      Serial.println("After startReceive call.");
      transmitFlag = false;

    } else {
      // the previous operation was reception
      // print data and send another packet
      String str;
      int state = radio.readData(str);

      if (state == RADIOLIB_ERR_NONE && str.length() > 0) {
        // packet was successfully received
        Serial.println(F("[SX1278] Received packet!"));
        Serial.println(str.length());

        // print data of the packet
        Serial.print(F("[SX1278] Data:\t\t"));
        Serial.println(str);

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

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

      }

      // wait a second before transmitting again
      delay(1000);

      // send another one
      Serial.print(F("[SX1278] Sending another packet ... "));
      transmissionState = radio.startTransmit("Hello World!");
      transmitFlag = true;
    }

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

  }
}
timmbogner commented 1 year ago

I've had a pretty good experience so far, and I think my problems are outside of RadioLib's control. First I'll say... I never actually ran the PingPong sketch itself. I tried the Transmit_Interrupt and Receive_Interrupt sketches together, then looked off of the PingPong sketch for how to use the interrupt in sending and receive within the same sketch. I pretty much skipped straight to working with binary data as well. That said, your sketch has passed 300 iterations so far on my two LilyGo modules.

The first thing I noticed when I looked at your sketch is that I had made a mistake and had been assigning the auto gain control to the highest setting (1) instead of auto (0). After changing that, it seems like there are less random packets, but it is tough to ascertain. The next thing I notice is that you have your Tx power set to 2. That might not help, but probably doesn't explain your problem.

Do you have a separate radio and ESP? It might be better if you didn't use GPIO5 for DIO0 since it's pulled high. Not sure, just a guess there.

Here's the sketch I'm testing reception on. I did figure out that disabling CRC only affects sending. The gibberish packet data is just setting the CRC bit in the header sometimes, so it checks it on receive.

/*
   RadioLib SX127x Receive with Interrupts Example

   This example listens for LoRa transmissions and tries to
   receive them. Once a packet is received, an interrupt is
   triggered. To successfully receive data, the following
   settings have to be the same on both transmitter
   and receiver:
    - carrier frequency
    - bandwidth
    - spreading factor
    - coding rate
    - sync word

   Other modules from SX127x/RFM9x family can also be used.

   For default module settings, see the wiki page
   https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem

   For full API reference, see the GitHub Pages
   https://jgromes.github.io/RadioLib/
*/

// include the library
#include <RadioLib.h>

// SX1278 has the following connections:
// NSS pin:   10
// DIO0 pin:  2
// RESET pin: 9
// DIO1 pin:  3
SX1276 radio = new Module(18, 26, 23, 33);      // ttgo-lora-esp32

// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 radio = RadioShield.ModuleA;

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

  // initialize SX1278 with default settings
  Serial.print(F("[SX1278] Initializing ... "));
  int state = radio.begin(915.0, 500.0, 7, 5, 0x12, 17, 8, 0);
  if (state == RADIOLIB_ERR_NONE) {
    Serial.println(F("success!"));
  } else {
    Serial.print(F("failed, code "));
    Serial.println(state);
    while (true);
  }
  radio.setCRC(false);

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

  // start listening for LoRa packets
  Serial.print(F("[SX1278] 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();
}

// flag to indicate that a packet was received
volatile bool receivedFlag = false;

// disable interrupt when it's not needed
volatile bool enableInterrupt = true;

// this function is called when a complete packet
// is received by the module
// IMPORTANT: this function MUST be 'void' type
//            and MUST NOT have any arguments!
#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;
}

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);

    // 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("[SX1278] Received packet!"));

      // print data of the packet
      Serial.print(F("[SX1278] Data:\t\t"));
      Serial.println(str);

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

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

      // print frequency error
      Serial.print(F("[SX1278] Frequency error:\t"));
      Serial.print(radio.getFrequencyError());
      Serial.println(F(" Hz"));

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

    } else {
      // some other error occurred
      Serial.print(F("[SX1278] 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;
  }

}
timmbogner commented 1 year ago

Okedoke, that is now sorted out. I needed to tell it to startReceive() again after it finished receiving a packet. This wasn't a problem when it sent an ACK because I did startReceive() after transmitting a packet.

At this point everything seems to be working!

nglessner commented 1 year ago

It looks like radiolib is setup and implemented in fdrs_node.h which is in the sensor examples, but not fdrs_functions.h so the repeater/gateway examples are not up to date. Is that correct?

Is fdrs_node.h a near drop-in replacement of fdrs_functions.h?

timmbogner commented 1 year ago

It looks like radiolib is setup and implemented in fdrs_node.h which is in the sensor examples, but not fdrs_functions.h so the repeater/gateway examples are not up to date. Is that correct?

Is fdrs_node.h a near drop-in replacement of fdrs_functions.h?

'fdrs_node.h' is the library for sensors and controllers, 'fdrs_functions.h' is the library for the gateways. Radiolib is implemented in both types of devices, and they should be backwards compatible with devices using the old library. I've added "fix header naming" to my to-do list.

Most of the actual LoRa code for gateways is in 'fdrs_lora.h', which might be what's throwing you off.