OTAA best practice: how to not join every time?

In https://www.thethingsnetwork.org/docs/devices/bestpractices.html

nodes should only join minimally.

However in all examples:

every time a device powered up it does a join. Does this violates best practice?


that happends when under development… not IRL :wink:

a possible solution

1e time OTAA join + set counter to 1
store keys

2e time ABP join (read keys from memory) + counter +1
3e time same
xx time start all over OTAA join (+ store new keys) + counter start at 0

OK, so how do I get the device address after OTAA? I can’t see any methods on the SDK.

See here an example for the ESP8266. OTA is used just once. The keys and the counter are saved in RTC memory (retrieved from here if power is still on at wake-up) and in EEPROM (retrieved from here if completely powered off).

1 Like

Thanks @Edzelf though I can see this is using LMIC. I’m currently using Sodaq Explorer with RN2483 - I can’t yet see a way to retrieve the device address that is assigned after OTAA joining.

Saving and retrieving the data has nothing to do with LMIC. You just need a way to store data in a non-volatile memory of your device. I’m not familiar with Sodaq, so I can’t help you with it.

From the RN2483 manual: mac get devaddr
Response: 4-byte hexadecimal number representing the device address, from
00000000 to FFFFFFFF.
This command will return the current end-device address of the module. Default: 00000000

If your library does not provide this you will need to add it (or find a library that supports these commands)


It is confirmed that the current SODAQ library does not support it.

1 Like

As a note on this, if your device shuts down RAM during its sleep mode then you’ll want to make sure you’re saving everything; performing an ABP join is not sufficient to restore a LoRaWAN client state properly. It may work on TTN, for now, but it’s not likely to work anywhere else (and may not be sufficient in the future for TTN).

As an example, there’s a MAC command to request the battery level and RSSI (gateway to node) from the node. If you receive that command you have to either respond immediately or keep track of the request for the next time you transmit. Other MAC commands can change the channels available, RX frequency on a per-channel basis, RX windows, max EIRP, mask out channels, etc. Not tracking these may result in communication loss.

Adaptive Data Rate (ADR) information is lost if you don’t keep track of it. If the server instructs your device to use DR4, but you power-cycle, set ABP, then transmit, your node won’t remember the data rate to use.

Some things to monitor:

  • Uplink and downlink counters must be tracked
  • Network and app session keys, device ID, etc. must be tracked
  • Adaptive data rate state including target data rate, ADR ack counter, etc. must be tracked
  • Joins can provide channel plans which must be tracked
  • MAC commands which change settings/channels/frequencies/rx frequencies/max EIRP/etc. must be tracked
  • MAC commands which require a response (i.e. request battery level/RSSI) must either be serviced immediately or tracked
  • (there’s more I can’t recall off-hand)

why ? what happends if the node don’t respond immediate because it went to sleep that moment, and now wakes up again after one hour ?
I can imagine that the node, before going into deepsleep, first must ‘finish’ his business

for example the RN2483A


I don’t think a node ever needs to reply immediately. But indeed, like for a confirmed downlink, it needs to store the fact that a confirmation was requested, so it can set the ACK bit in the next uplink. Even if that uplink is days later. When not doing that, then TTN will be required to send the downlink again.

(So I guess mac save indeed does not suffice there. But I even don’t see the frame counters mentioned above, so maybe mac save is just not the right command at all, or though the documentation is outdated, see below.)


why ? what happends if the node don’t respond immediate because it went to sleep that moment, and now wakes up again after one hour ?

That’s what I meant by “tracked”, as in you have to keep track of the request and service it during your next transmission. If you aren’t keeping track of it then you should probably send another transmission immediately before you go to sleep which does service it, as the request would be lost otherwise.

or maybe even answer before going into sleep ? if that’s possible …


As an aside, since version 1.0.0 the RN2483 mac save command saves much more, and does include the frame counters, but no mention of the pending MAC command responses. (But maybe more recent versions even include that?)

See also How to persist OTAA paramenters with an ESP32?

I propose there is also a best practice example, after the quickstart, using TTN libraries at least on how to do all the above.


Issuing a “sys factoryRESET” may help to get an ‘accepted’ reply from RN2483A module. If you use “mac save” be sure to have valid data for devaddr, appeui and appkey. This project may help too: https://github.com/thomaswesenberg/LoRaWAN-device-GPS-RaspBerry-Pi

1 Like