Skip to content

LoRaWAN: stack internals

StevenCellist edited this page May 19, 2025 · 3 revisions

Note

Users don't need to read any of this :)

Developer diaries

This is a mostly unstructured collection of thoughts about the implementation of LoRaWAN in RadioLib.

MAC commands

  • MAC commands are the center of this stack. Almost any change passes through the execMacCommand() function. The reason is two-fold:

    1. Code only needs to be updated in one place.
    2. All changes can easily be saved into the session buffer.
  • The LinkADRReq MAC command is pretty annoying. Basic operation on dynamic bands is straightforward (a single channel mask with some extra fields), but fixed bands or advanced use is hard.

    1. On fixed bands, a block of ADR commands is quite likely to occur. For example, a first one may disable all channels while a second one enables just a single subband.
    2. During verification, a set of ADR commands is sent to verify the specification.

    Processing these MAC commands one by one is guaranteed to result in lots of trouble, as an intermediate state may not be valid but the final state is. Instead, we decided to pre-process a block of ADR masks into one complete 96-bit channel mask (which is the maximum number of channels in RP002 1.0.4). As a result, it is quite easy to apply this mask and verify whether this results in a valid state or not. Hence the weird length of the internal LinkADRReq commands (14 bytes instead of the regular 5).

  • Some MAC commands must be resent until a downlink is received. Hence, they include a flag to track whether they should persist. After an uplink, all non-persistent MAC commands can be cleared; after a downlink, all persistent MAC commands can be cleared.

Nonces and Session buffers

  • The session buffer is effectively a complete set of MAC commands. These MAC commands can simply be copied into the session buffer whenever they occur. And restoring a session is simply a matter of executing a set of MAC commands (besides the frame counters).
  • The Nonces buffer does not include the corresponding EUIs and keys in order to keep its size minimal. Rather, it includes a 16-bit checksum of them. The chances that a new set of EUIs and keys (if ever needed) would collide is negligibly small.

Bands and channels

  • There is support for up to 16 uplink/downlink channels. Typically, LoRaWAN uses up to 8, but on some bands or private deployments, more may be used. The device periodically cycles through these channels in random order. Once they are all used, they can be re-used. When a channel is added, modified or removed, this channel utilization is reset and all channels are free to be used again.
  • Following TR007, fixed bands (such as US915) cycle through all banks of 8 channels during a JoinRequest if the subband is not specified. To this end, the device randomly selects one channel of each bank of 125kHz channels and one from the 500kHz channels. TR007 suggests that after such a cycle is completed without a JoinAccept heard, the device should continue on to other channels from those banks; however, this is rather infeasible to track. As such, it may be the case that the same channel from a bank is re-selected at random.
  • For fixed bands, the downlink channels are a function of the uplink channels and shall not be modified. Therefore, the device does not store the downlink channels in memory, but simply calculates the corresponding frequency and datarate when selecting an uplink channel.

Classes

  • For LoRaWAN v1.1, the device is supposed to switch to a certain Class whenever requested by the user (as for 1.0.4). However, the device also sends a DeviceModeInd command, waiting for DeviceModeConf. If the latter is not received, the device may be burning power for nothing. As such, TS001 recommends a certain timeout before reverting to Class A. As this is somewhat cumbersome to track / implement, the Class is switched upon successful reception of DeviceModeConf.
Clone this wiki locally