jgromes / RadioLib

Universal wireless communication library for embedded devices
https://jgromes.github.io/RadioLib/
MIT License
1.57k stars 392 forks source link

LoRaWAN - Frame Count Reset #1031

Closed mofm closed 7 months ago

mofm commented 7 months ago

The example below works fine until 50th frame count and than reset frame counter. ChirpStack Network Server gives frame counter reset or rollover error. When I try it without deep sleep mode, it works fine.

#include <RadioLib.h>
#include <LoraMessage.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BME280.h>
#include "boards.h"
#include "secrets.h"

#define SEALEVELPRESSURE_HPA (1013.25)
Adafruit_BME280 bme; // I2C

// Uplink message payload size
// The maximum allowed for all data rates is 51 bytes.
const uint8_t PAYLOAD_SIZE = 51;

// Uplink payload buffer
static uint8_t loraData[PAYLOAD_SIZE];

// Pause between sends in seconds, so this is every 30 minutes.
#define MINIMUM_DELAY 60

SX1262 radio = new Module(RADIO_CS_PIN, RADIO_DIO1_PIN, RADIO_RST_PIN, RADIO_BUSY_PIN);

// create the node instance on the EU-868 band
// using the radio module and the encryption key
// make sure you are using the correct band
// based on your geographical location!
LoRaWANNode node(&radio, &EU868);

// for fixed bands with subband selection
// such as US915 and AU915, you must specify
// the subband that matches the Frequency Plan
// that you selected on your LoRaWAN console
/*
  LoRaWANNode node(&radio, &US915, 2);
*/

void sensorBegin() {
    unsigned status;
    status = bme.begin(); // default settings
    if (!status) {
        Serial.println("Could not find a valid BME280 sensor, check wiring!");
        while (1) delay(10);
    } else {
        Serial.println("BME280 sensor found!");
    }
}

void readSensor(LoraEncoder & encoder) {
    float temperature = bme.readTemperature();
    float humidity = bme.readHumidity();
    float pressure = bme.readPressure() / 100.0F;
    float altitude = bme.readAltitude(SEALEVELPRESSURE_HPA);
    encoder.writeRawFloat(temperature);
    encoder.writeRawFloat(humidity);
    encoder.writeRawFloat(pressure);
    encoder.writeRawFloat(altitude);
}

void helper_deep_sleep(int seconds = 0) {
    // It seems to make no sense to do a .begin() here, but in case the radio is
    // not interacted with at all before sleep, it will not respond to just
    // .sleep() and then consumes 800 µA more than it should in deep sleep.
    radio.begin();
    // 'false' here is to not have a warm start, we re-init the after sleep.
    radio.sleep(false);
    u8g2->sleepOn();
    SPI.end();
    SDSPI.end();
    pinMode(RADIO_CS_PIN, INPUT);
    pinMode(RADIO_RST_PIN, INPUT);
    pinMode(RADIO_DIO1_PIN, INPUT);
    pinMode(RADIO_BUSY_PIN, INPUT);
    pinMode(I2C_SDA, INPUT);
    pinMode(I2C_SCL, INPUT);
    pinMode(RADIO_SCLK_PIN, INPUT);
    pinMode(RADIO_MISO_PIN, INPUT);
    pinMode(RADIO_MOSI_PIN, INPUT);

    // Set timer wakeup if applicable
    if (seconds > 0) {
      esp_sleep_enable_timer_wakeup(seconds * 1000000);
    }
    // and off to bed we go
    esp_deep_sleep_start();
}

void setup() {
    initBoard();
    sensorBegin();
    // When the power is turned on, a delay is required.
    delay(1500);

    // initialize radio (SX1262 / SX1278 / ... ) with default settings
    Serial.print(F("[Radio] Initializing ... "));
    int state = radio.begin();
    if(state == RADIOLIB_ERR_NONE) {
        Serial.println(F("success!"));
    } else {
        Serial.print(F("failed, code "));
        Serial.println(state);
        while(true);
    }

#ifndef SECRETS
    #define SECRETS
    // application identifier - pre-LoRaWAN 1.1.0, this was called appEUI
    // when adding new end device in TTN, you will have to enter this number
    // you can pick any number you want, but it has to be unique
    uint64_t joinEUI = 0x12AD1011B0C0FFEE;
    uint64_t devEUI = 0x70B3D57ED005E120;

    // network key is the ASCII string "23E6754030CA55D5D944F6AFA21C895D"
    uint8_t nwkKey[] = { 0x23, 0xe6, 0x75, 0x40, 0x30, 0xca, 0x55, 0xd5, 0xd9, 0x44, 0xf6, 0xaf, 0xa2, 0x1c, 0x89, 0x5d };
    // application key is the ASCII string "aDifferentKeyABC"
    // uint8_t appKey[] = { 0x61, 0x44, 0x69, 0x66, 0x66, 0x65, 0x72, 0x65,
    //                     0x6E, 0x74, 0x4B, 0x65, 0x79, 0x41, 0x42, 0x43 };

    // prior to LoRaWAN 1.1.0, only a single "nwkKey" is used
    // when connecting to LoRaWAN 1.0 network, "appKey" will be disregarded
    // and can be set to NULL
    // 88888888888888888888888888888889
    uint8_t appKey[] = { 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x89 };
#endif
    // on EEPROM-enabled boards, after the device has been activated,
    // the session can be restored without rejoining after device power cycle
    // this is intrinsically done when calling `beginOTAA()` with the same keys
    // in that case, the function will not need to transmit a JoinRequest

    // now we can start the activation
    // this can take up to 10 seconds, and requires a LoRaWAN gateway in range
    // a specific starting-datarate can be selected in dynamic bands (e.g. EU868):
    /*
      uint8_t joinDr = 4;
      state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, joinDr);
    */

    // wipe the EEPROM to force a new activation
    // node.wipe();
    Serial.print(F("[LoRaWAN] Attempting over-the-air activation ... "));
    state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey);

    if(state >= RADIOLIB_ERR_NONE) {
        Serial.println(F("success!"));
        delay(2000);  // small delay between joining and uplink
    } else {
        Serial.print(F("failed, code "));
        Serial.println(state);
        while(true);
    }

    // Serial.print("[LoRaWAN] DevAddr: ");
    // Serial.println(node.getDevAddr(), HEX);
    // on EEPROM enabled boards, you can call "restore" to retrieve the session
    // after reboot or deepsleep
    // Serial.println(F("[LoRaWAN] Restoring session ..."));
    // state = node.restore();
    //if(state == RADIOLIB_ERR_NONE) {
    //    Serial.println(F("success!"));
    // } else {
    //    Serial.print(F("failed, code "));
    //    Serial.println(state);
    //    while (true)
    //}
    // disable ADR (Adaptive Data Rate)
    node.setADR(false);

    // set a fixed data rate
    node.setDatarate(5);
    // enable CSMA (Carrier Sense Multiple Access)
    node.setCSMA(6, 2, true);

    // enable or disable the dutycycle
    // the second argument specific allowed airtime per hour in milliseconds
    // 1250 = TTN FUP (30 seconds / 24 hours)
    // if not called, this corresponds to setDutyCycle(true, 0)
    // setting this to 0 corresponds to the band's maximum allowed dutycycle by law
    node.setDutyCycle(true, 1250);

    // enable or disable the dwell time limits
    // the second argument specifies the allowed airtime per uplink in milliseconds
    // unless specified, this argument is set to 0
    // setting this to 0 corresponds to the band's maximum allowed dwell time by law
    node.setDwellTime(true, 400);

}

// counter to keep track of transmitted packets
// int count = 0;

void loop() {
    int state = RADIOLIB_ERR_NONE;
    uint8_t battLevel = 146;
    node.setDeviceStatus(battLevel);

    // send uplink to port 10
    Serial.print(F("[LoRaWAN] Sending uplink packet ... "));
    // String strUp = "Hello!" + String(count++);
    // String strDown;
    // int state = node.sendReceive(strUp, 10, strDown);

    LoraEncoder encoder(loraData);
    readSensor(encoder);
    state = node.uplink(loraData, encoder.getLength(), 10);
    if(state == RADIOLIB_ERR_NONE) {
        Serial.println(F("success!"));
    } else {
        Serial.print(F("failed, code "));
        Serial.println(state);
    }
    Serial.print(F("[LoRaWAN] Waiting for downlink ... "));
    String strDown;
    LoRaWANEvent_t event;
    state = node.downlink(strDown, &event);
    if(state == RADIOLIB_ERR_NONE) {
        Serial.println(F("received a downlink!"));

        // print data of the packet (if there are any)
        Serial.print(F("[LoRaWAN] Data:\t\t"));
        if(strDown.length() > 0) {
            Serial.println(strDown);
        } else {
            Serial.println(F("<MAC commands only>"));
        }

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

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

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

    } else if(state == RADIOLIB_ERR_RX_TIMEOUT) {
        Serial.println(F("no downlink!"));

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

    // on EEPROM enabled boards, you should save the current session
    // by calling "saveSession" which allows retrieving the session after reboot or deepsleep
    node.saveSession();

    // Calculate minimum duty cycle delay (per FUP & law!)
    uint32_t interval = node.timeUntilUplink();

    // And then pick it or our MINIMUM_DELAY, whichever is greater
    uint32_t delayMs = max(interval, (uint32_t)MINIMUM_DELAY * 1000);

    Serial.printf("Next TX in %i s\n", delayMs/1000);

    Serial.println("Deep sleep now");
    // Make sure this still prints before sleep
    delay(10);

    // And off to bed we go
    helper_deep_sleep((delayMs / 1000) - 5);
}

Additional info (please complete):

StevenCellist commented 7 months ago

This problem is solved in upstream, it was a problem I mistakenly introduced in 6.4.0. If you wait for one or two days, @jgromes will likely release 6.5.0 and it should be solved!

@ropg as you can see, it took this long before someone encountered this bug again ;)

mofm commented 7 months ago

Thanks @StevenCellist . I'm waiting for the new release.