How to persist OTAA paramenters with an ESP32?

Hi everyone,

I’m searching for a simple method to persist the message counters and other values from an OTAA join. Does anybody know a good LoraWAN library which persists all required values or how to persist and load the required values from LMiC? My goal is to use OTAA in combination with an ESP32 and deep sleep.

First I thought this is a common question but unfortunately I didn’t find a solution…

Thank you very much :slight_smile:

Hi,

my ESP32 runs with the following code.

I call LoraWANSaveOTTA2RTC befor DeepSleep and call LoraWANLoadOTTAFromRTC after a deep sleep.

Not sure if every var is needed, but my ESP32 has been running stable for a while now.

RTC_DATA_ATTR u4_t RTC_LORAWAN_netid = 0;
RTC_DATA_ATTR devaddr_t RTC_LORAWAN_devaddr = 0;
RTC_DATA_ATTR u1_t RTC_LORAWAN_nwkKey[16];
RTC_DATA_ATTR u1_t RTC_LORAWAN_artKey[16];
RTC_DATA_ATTR u4_t RTC_LORAWAN_seqnoUp = 0;
RTC_DATA_ATTR u4_t RTC_LORAWAN_seqnoDn;
RTC_DATA_ATTR u1_t RTC_LORAWAN_dn2Dr;
RTC_DATA_ATTR s1_t RTC_LORAWAN_adrTxPow;
RTC_DATA_ATTR s1_t RTC_LORAWAN_datarate;
RTC_DATA_ATTR u4_t RTC_LORAWAN_channelFreq[MAX_CHANNELS];
RTC_DATA_ATTR u2_t RTC_LORAWAN_channelDrMap[MAX_CHANNELS];
RTC_DATA_ATTR u2_t RTC_LORAWAN_channelMap;
RTC_DATA_ATTR s2_t RTC_LORAWAN_adrAckReq;
RTC_DATA_ATTR u1_t RTC_LORAWAN_rx1DrOffset;
RTC_DATA_ATTR u1_t RTC_LORAWAN_rxDelay;

void LoraWANSaveOTTA2RTC()
{
    Serial.println(F("Save LMIC to RTC ..."));
    RTC_LORAWAN_netid = LMIC.netid;
    RTC_LORAWAN_devaddr = LMIC.devaddr;
    memcpy(RTC_LORAWAN_nwkKey, LMIC.nwkKey, 16);
    memcpy(RTC_LORAWAN_artKey, LMIC.artKey, 16);
    RTC_LORAWAN_dn2Dr = LMIC.dn2Dr;
    RTC_LORAWAN_seqnoDn = LMIC.seqnoDn;
    RTC_LORAWAN_seqnoUp = LMIC.seqnoUp;
    RTC_LORAWAN_adrTxPow = LMIC.adrTxPow;
    RTC_LORAWAN_datarate = LMIC.datarate;
    RTC_LORAWAN_adrAckReq = LMIC.adrAckReq;
    RTC_LORAWAN_rx1DrOffset = LMIC.rx1DrOffset;
    RTC_LORAWAN_rxDelay = LMIC.rxDelay;
    memcpy(LMIC.channelFreq, RTC_LORAWAN_channelFreq, MAX_CHANNELS);
    memcpy(LMIC.channelDrMap, RTC_LORAWAN_channelDrMap, MAX_CHANNELS);
    RTC_LORAWAN_channelMap = LMIC.channelMap;
}

void LoraWANLoadOTTAFromRTC()
{
    Serial.println(F("Load LMIC from RTC ..."));

    memcpy(RTC_LORAWAN_channelFreq, LMIC.channelFreq, MAX_CHANNELS);
    memcpy(RTC_LORAWAN_channelDrMap, LMIC.channelDrMap, MAX_CHANNELS);
    LMIC.channelMap = RTC_LORAWAN_channelMap;
    LMIC.seqnoDn = RTC_LORAWAN_seqnoDn;    
    LMIC_setSession(RTC_LORAWAN_netid, RTC_LORAWAN_devaddr, RTC_LORAWAN_nwkKey, RTC_LORAWAN_artKey);
    LMIC_setSeqnoUp(RTC_LORAWAN_seqnoUp);
    LMIC_setDrTxpow(RTC_LORAWAN_datarate, RTC_LORAWAN_adrTxPow);
    LMIC.adrAckReq = RTC_LORAWAN_adrAckReq;
    LMIC.dn2Dr = RTC_LORAWAN_dn2Dr;
    LMIC.rx1DrOffset = RTC_LORAWAN_rx1DrOffset;
    LMIC.rxDelay = RTC_LORAWAN_rxDelay;
}
4 Likes

@JackGruber Could show in code where / when you call this

Nice. You may want to check if you can save the state for confirmed downlinks. And if you do, then you’re doing better than the RN2483. :slight_smile:

Load the settings in the Lora Setup

    ...
    LMIC_reset();

    if(RTC_LORAWAN_seqnoUp != 0)
    {
        LoraWANLoadOTTAFromRTC();
    }

    // Start job
    LoraWANDo_send(&sendjob); 

and after EV_TXCOMPLETE i send my ESP to deep sleep.

At the moment i have no access to my devices. But in i 4 weeks i think i take a look into this.

1 Like

Thanks for this post.
About 4 weeks ago I was in the same situation and was asking myself why there is no proper Demo Sketch of video for that.
I was very frustated during my programing to do on every small change a new OTAA Activation.
I read a lot of forum discussions with a lot of tips and tricks and finally I found something useful.

After a lot of searching i found this sketch for the EPS8266: https://github.com/Edzelf/LoRa/tree/master/ESP_lora_tracker

Once inspired I modified this sketch according to my needs to ESP32:
I modified the EEPROM Access and the RTC handling.
You may find my modification here:

I am not an expert programmer and hope this sketch helps you. In my opinion there should be much more of this basic examples documented.
Hopefully somebody make this code more cleaner and add this to MCCI stack examples.

1 Like

LDL passes devNonce and session state back to the application via a callback. The application can choose to save and/or restore this as needed. There is more information in the porting guide.

Unfortunately there is no LDL wrapper for ESP32 Arduino at this time, so it’s not super convenient to try out. Even if there was a wrapper it probably wouldn’t persist by default since it makes experimenting with LoRaWAN more difficult.

Hi @arjanvanb,

i have checked my code on a short test and the ack for the downlink works fine after deep sleep.
image
I think the downlink retrys came from a timing problem, because it was the same with or without deepsleep on my ESP32.

1 Like

Some remarks:

  • Be aware that ESP32 has no EEPROM and the data will be stored in flash.
    Flash has a limited lifetime of around 100k erase/program cycles. It should therefore not be used to store data that is very frequently changing.

  • The EEPROM library for ESP32 is deprecated.

    For new applications on ESP32, use Preferences.

  • About the preferences library:

    Preferences provides persistent (across resets) but mutable storage of various types of variables. It is similar to EEPROM library in Arduino, except that EEPROM provides a single contiguous block of storage which the sketch needs to partition between variables, while Preferences performs the partitioning itself.

    (I have not used the preferences library yet.)

1 Like