Skip to content

MM architecture

LoRaWanMiniMouse edited this page Apr 13, 2018 · 30 revisions

This page describes the architecture of the MM stack, the various processes running, the stack main state machine and the way timing critical and non-timing critical operations are handled.



The stack operation requires 3 main components:

The LoRaWAN_process() function

This function manages the state of the MAC and performs all the computation intensive (crypto) tasks of the MAC. This function must be called periodically by the user’s application whenever the state of the MAC is NOT idle. The function is not timing critical, can be called at any time and can be interrupted by any IRQ (including the user’s IRQ). The only requirement is that this function must be called at least once between the end of the transmission and the beginning of the RX1 slot and once between the RX1 and RX2 slot. Therefore when the stack is active (state different from idle) a call periodicity of roughly 500mSec is recommended. The function can be called as often as wanted and may be called as many times as the user wants. When the function has no work to perform it returns immediately. On the other hand if the function needs to encrypt/decrypt a packet and that the cryptography is performed entirely in software (no AES hardware) the function might take several mSec before returning.
The simplest application code to send a packet may therefore look like this:

send_packet();
while (LoRaWAN_state != idle)
{
sleep(500msec);
LoRaWAN_process();
}
sleep_until_next_packet();

    LpState = Lp.SendPayload( UserFport, UserPayload, UserPayloadSize, MsgType );
    while ( LpState != LWPSTATE_IDLE ){
        LpState = Lp.LoraWanProcess( &AvailableRxPacket );
        GotoSleepMSecond ( 500 );
        WatchDogRelease ( );
     }
     if ( LpState == LWPSTATE_ERROR ) {
         // user application have to save all the need
         NVIC_SystemReset();
     }
     GotoSleepSecond ( AppTimeSleeping );  // sleep_until_next_packet

The radioISR routine

This routine is called every time the radio produces an IRQ (rx_finished,tx_finished or time_out). The code executed depends on the state of the radio and the state of the MAC as set by the lorawan_process() function. This routine is as short as possible and shall never take the MCU for more than a milli-second. This routine is un-interruptible, meaning that if a user’s IRQ is triggered during the execution of this routine, it will have to wait the end of the routine to be processed.

The timerISR routine

This routine is called when the dedicated MAC timer trigs. The code executed depends on the state of the radio and the state of the MAC as set by the lorawan_process() function. This routine is as short as possible and shall never take the MCU for more than a milli-second. This routine is un-interruptible, meaning that if a user’s IRQ is triggered during the execution of this routine, it will have to wait the end of the routine to be processed.

This architecture allows the user to perform timing critical operations in its application (example: read a sensor every 50mSec precisely) while running the MM stack on the same MCU. In that case the user must take care to call the LoRaWAN_process() only when there is sufficient time available before the next sensor acquisition. This can be achieved very simply by calling the LoRaWAN_process() function only immediately after a sensor reading. The radioISR and timingISR functions take a lot less than a mSec to return on a typical ARM platform and should have a minimal impact on the application timing (will delay the sensor reading operation by at most 1mSec in the worst case).


Processing of the Received radio packets in the radioISR routine

For both classA and classC modes of operation, the radio produces and IRQ when a packet is received. This IRQ is processed immediately by the radioISR() interrupt service routine.

If the radio state is RX1,RX2 or RX2cont this routine performs the following tasks:

  • Check the radio status (timeOut, CRC error, …..)
  • If a any packet was received then fetch the content of the radio data buffer
  • If the devAddr field of the fetched data matches the unicast or one of the multicast devAddr of the device then:
  1. append it to the rx_buffer_fifo
  2. increment the rx_packet_counter
  • Reset radio IRQ

The packets pushed into the rx_buffer_fifo memory buffer will be processed (check frame counter value & MIC field, decrypt payload, eventually execute MAC commands present in the frame) offline (outside the radioISR) by the LoRaWAN_process(). This is to reduce as much as possible the time spent in the RadioISR() routine.

The memory allocated to the rx_buffer_fifo is a static parameter that can be configured by the user at compile time. This parameter is a function of many parameters:

  • The periodicity at which LoRaWAN_process() is called. (longer delays between calls mean that more packets may be received in between)
  • How many multicast address are used?
  • Does the network use DL traffic bursts?
  • How many bytes in a typical DL?

The default size recommended is 512 bytes. If the application is designed in a way such that LoRaWAN_process() is called every time the radioISR routine has fetched a packet then the buffer only needs to contain a single frame. If a packet is received but there is not enough space in the buffer then the radioISR routine shall set a rx_buffer_overflow flag. All further frames received are discarded until the buffer can be emptied by the LoRaWAN_process. The LoRaWAN_process() shall unset the flag during its next call. This flag is just a notification to the application than the buffer is maybe too small or that the lorawan_process() is not called frequently enough.

When a received frame has been successfully processed by LoRaWAN_process() and if this frame contains application payload, it is finally made available to the application.


RX windows timing

The timing (the opening time and the duration) of the receive windows is of capital importance to correctly receive downlinks. This section describes the process followed by MM to compute the RX1 & RX2 windows timing. The timing is driven by 2 parameters set at compile time:

  • XTALerror : gives in ppm the maximum frequency error of the RTC oscillator (not the radio XTAL !!!). For example if the platform uses a 32.768kHz watch crystal for its time base with +/- 100ppm frequency error , then XTALerror=100. If the platform uses the MCU internal RC oscillator , then the error is more in the 1% range hence XTALerror=10000.
  • Delay : The absolute delay in mSec between the wake-up timer event and the moment the radio actually is ready to start receiving.This delay is platform specific and is a function of the SPI speed, the MCU clock frequency, the MCU wake-up time,...

Based on those 2 parameters, the datarate of the expected downlink and the delay between TX and RX (RX1Delay), MM computes when and how long to turn-on the radio to be sure that the radio is on when the downlink preamble is received.
The Delay parameter must be calibrated once for all on a given platform. This may be done with a scope or logic analyser to measure the delay between the MCU wake-up instant and the end of the SPI transactions turning on the radio.
The XTALerror parameter must be set according to the time base used to clock the MCU RTC&Timer. XTALerror should be set to the smallest possible value as this parameter as a direct impact on the receive current of the device. Doubling the parameter results in RX windows twice as long and therefore doubles the RX energy.


MM Class A state machine

Once MM has joined the network (or directly after reset for ABP mode) the stack has only 8 possible states:

  1. Error / The stack encountered a fatal error , the application must reset the device
  2. Idle / The stack has no further operation to run , the application may go long deep-sleep
  3. Send / The stack is transmitting
  4. RX1 / The stack has scheduled the RX1 slot
  5. RX2 / The stack has scheduled the RX2 slot
  6. processDL / The stack is processing a received frame
  7. UpdateMACstate / The stack is updating the MAC state (answering MAC commands, ..)
  8. txWait / The stack is waiting to transmit a MAC answer or to re-transmit an uplink
typedef enum LoraWan_Process_States {
    LWPSTATE_IDLE ,
    LWPSTATE_SEND ,
    LWPSTATE_RX1 ,
    LWPSTATE_RX2 ,
    LWPSTATE_PROCESSDOWNLINK ,
    LWPSTATE_UPDATEMAC,
    LWPSTATE_TXWAIT,    
    LWPSTATE_ERROR
} eLoraWan_Process_States;

Additionally MM keeps track of the radio state using 4 possible states:

  1. TX / the radio is currently transmitting
  2. TX_FINISHED / The radio has finished transmitting, TX_FINISHED IRQ was received
  3. RX_FINISHED1 / The radio has finished RX1 slot , either time_out or rx_finished IRQ have been received
  4. RADIO_IDLE / The radio has finished RX2 slot , either time_out or rx_finished IRQ have been received
enum {
    RADIOSTATE_IDLE,
    RADIOSTATE_TXON,
    RADIOSTATE_TXFINISHED,
    RADIOSTATE_RX1FINISHED,
};

classA

The following table summarizes a typical class A transmit followed by RX slots. This exmaple assumes that a downlink is received in the RX2 slot and the uplink must be re-transmitted (NbTrans>1)

Event triggering the change LoRaWAN-process() operations executed when called radioISR operations executed when radio IRQ received TimerISR operations executed when timer IRQ received
Default state **Idle ** Never called , or return immediately without doing anything Nothing Nothing
User or stack calls send(), send() function builds payload and put the radio in TX, sets radioISR routine, MMstate= Send, radio_state=TX returns immediately without doing anything On radio TX_finished IRQ, timestamps, set radio_state=TX_FINISHED, kill radio Nothing
App calls LoRaWAN_process() and MM_state == Send and Radio_state == TX_FINISHED MM_state=>RX1, schedules timer for RX1 start, configures timerISR&radioISR code On RX_finished or RX_timeout , read data buffer if any, set radio_state=RX_FINISHED1, kill radio Start radio with RX1 params. set radio state to RX_ON
App calls LoRaWAN_process() and MM_state==RX1 and Radio_state==RX_FINISHED1 If packet received MM_state=processDL Else MM_state=RX2, schedule timer for RX2 start, configures timerISR&radioISR code On RX_finished or RX_timeout, read data buffer if any, set radio_state=RX_FINISHED2 , kill radio Start radio with RX2 param. set radio_state=RX_ON
App calls LoRaWAN_process() and MM_state==RX2 & Radio_state = RX_FINISHED2 If packet received MM_state=processDL Else MM_state=updateMAC Nothing nothing
App calls LoRaWAN_process() and state==processDL Check devAddr, check FCount, verify MIC , parse, decrypt. MM_state=updateMAC Nothing Nothing
App calls LoRaWAN_process() and state==updateMAC Process MAC commands, if a frame must be transmitted (MAC reply or NbTrans>1) State = txWAIT else MM_State = idle Nothing Nothing
App calls LoRaWAN_process() and MM_state==txWAIT If wait time not elapsed then state unchanged else MM_state=send Nothing Nothing

The MM stack may jump to the error state if it spends more than 120sec without going back to idle state. (this can happen if radio IRQ is not received for example). When that happens the application may store in NVM its context and then MUST reset the device. There is no possibility to reset only the stack. In the process of resetting the device, the radio will also be reset to a known state.


Joining the network

MM implements both ABP(Activation By Personalization) and OTAA(Over The Air Activation) modes. The mode is selected at compile time. When OTAA mode is used, the application can trigger the transmission of a JoinRequest message by calling Lp.Join( ).
The application is responsible for calling the Join function until the device has successfully managed to join the network. The application can poll the Lp.IsJoined ( ) status to know the current join status. The MM stack implements a JoinRequest transmission duty cycle protection method according to the LoRaWAN1.0.2 specification. Therefore even if the application calls the Lp.Join( ) function too frequently, a JoinRequest message will only be transmitted if enough time has elapsed since the last transmission. When called too early the Lp.Join() function returns immediately without error. The MM stack implements a built-in data-rate distribution for the transmission of JoinRequest messages that should fit most of the applications. That distribution can be modified in the code before compilation.


MM state context backup & restore

MM autonomously and regularly (every X packets, X being configurable) backups its entire contexts to FLASH. The context contains:

  • The current frame counters
  • the devAddr
  • keys
  • channel mask
  • list of channel currently being used
  • data rate , nbTrans, power,..
  • RX1 & RX2 parameters
  • join status
struct sBackUpFlash
{    /*******************************************/
    /*      Update by Link ADR command         */
    /*******************************************/
    uint8_t      MacTxDataRate;
    uint8_t      MacTxPower;
    uint16_t     MacChMask; //@notereview remove
    uint8_t      MacNbTrans; 
    /********************************************/
    /*     Update by TxParamaSetupRequest       */
    /********************************************/
    uint32_t     MacRx2Frequency ; 
    uint8_t      MacRx2DataRate;
    uint8_t      MacRx1DataRateOffset;
    /********************************************/
    /*     Update by NewChannelReq command      */
    /********************************************/
    uint32_t     MacTxFrequency[16];//@note region dependant
    uint32_t     MacRx1Frequency[16];//@note region dependant
    uint8_t      MacMinDataRateChannel [16];
    uint8_t      MacMaxDataRateChannel [16];
    uint8_t      MacChannelIndexEnabled [16];
    uint16_t     MacChannelMask; //
    /********************************************/
    /*   Update by RXTimingSetupReq command     */
    /********************************************/
    int          MacRx1Delay;
    /********************************************/
    /*   Other Data To store                    */
    /********************************************/
    uint32_t     FcntUp;   
    uint32_t     FcntDwn;
    uint32_t     DevAddr;
    uint8_t      nwkSKey [16];
    uint8_t      appSKey [16];
    uint8_t      JoinedStatus;
    uint16_t     DevNonce;    
    uint8_t      NbOfReset;
    uint8_t      Reserved [7];
    uint32_t     CrcHigh;   
    uint32_t     CrcLow;    
} ;

The context is also saved whenever one of the following MAC command is received:

  • RXParamSetupReq
  • NewChannelReq
  • RXTimingSetupReq
  • TXParamSetupReq
  • DlChannelReq

This is because those MAC commands can possibly modify the state of the device's stack in a way that breaks the link if not well synchronized with the NetworkServer.

The context is protected by a CRC64 field. When the MM stack is started, it first checks whether or not a valid context (based on the CRC64) is present in Flash memory. This is done through a call to Lp.RestoreContext( ).

  • If YES, MM restarts form that context, therefore does not go through the Join procedure again, there is no service interruption perceived by the network.
  • If NO, MM restarts from the default factory state, and may have to join the network again.

Therefore to really restart a device from scratch, the user must first erase the MM context in FLASH then restart the device. MM maintains a counter ( Lp.GetNbOfReset() ) counting the number of time it restarted from FLASH context. This allows to keep track of the number of times a device has actually gone through reset.


MM Class C state machine

ClassC is still experimental in MM.

classC


MM Data rate & radio parameters selection

MM manages the LoRa data rate very differently than previous stacks. We have selected the MM data rate management strategy based on many consistent field observations than a distribution of DR very often outperforms using a single fixed DR. This is because depending on the radio environment the optimal data rate is not necessarily the lowest DR/longest range. For example, if the radio link performance is limited by interference (lots of bursty interferer in the same band) then increasing the DR will improve the performance, and lowering DR to extend the range may completely break the link. The best remedy to this is to have a statistical approach and used a statistical distribution of DR. For example the device may use 10% of SF12, 10% SF11, 30% SF10, ... Therefore we have decided to embed this principle directly in the stack and to abstract it from the application. When using MM the application can select between 4 data rate strategies.

  1. Static : Radio parameters are managed by the network (strongly recommended for static / rarely moving objects)
  2. Mobile LongRange (MLR): the device uses a built-in distribution of DR geared toward long range link at the expense of power
  3. Mobile LowPower (MLP): the device uses a built-in distri of DR geared toward faster DR , therefore shorter range but lower power
  4. Custom : the user can define its own DR distribution

The DR distribution used are region specific. The application can dynamically switch from one strategy to another in operation.
In the "static" strategy, all radio parameters (frequencies , NbTrans, power, SF) are entirely managed by the network back-end. This provides the best possible trade-off between link reliability and power consumption. This mode also lets the network provision new channels in the device and enable/disable channels based on local radio conditions. This strategy is highly recommended for all static or devices that have a way to know they are moving/static. This strategy should be systematically used whenever the device has a way (accelerometer) to detect it is static. For example an asset tracking device might use Mobile LongRange when it is moving and revert to Static when static for more than 15minutes. In "custom" mode the application can select the number of time an uplink is re-transmitted (the default value is 3). In MLP & MLR modes the nb of TX repetition is fixed = 3. The application can modify the DR distribution of the "custom" mode. This possibility may be used to allow remote configuration of the DR distribution from the Application Server.

When switching from "static" to one of the 3 "mobile" modes the following happens:

  • The radio transmit power is reset to the maximum possible allowed (for ex +14dBm in EU)
  • The NbTrans parameters is set to the value associated to the mode selected
  • In US style regions the channel mask is left untouched. This means that in modes 2-3-4 the device will used the latest channel configuration provided by the network.
  • In EU style regions (regions with common default channels), the stacks takes the latest channel mask communicated by the network and adds the default channels (for ex 868.1 868.3 & 868.5 in EU).

In all cases, when operating in modes 2-3-4 the MM stack lets the network configure the channel mask. This means that the stack takes into account the channel mask field of a linkAdrReq MAC command received from the network. This has no impact on the device's current consumption but allows better radio performance&reliability in mobility (for example when a given frequency is only usable in a given area, when moving out of this area the network must be able to tell the device to stop using that frequency, else the energy spent on transmitting on this frequency is wasted)