LoRaWAN end device MAC layer for SX1272 and SX1276. Supports LoRaWAN-1.0 and LoRaWAN-1.1

Dependencies:   sx12xx_hal

Dependents:   LoRaWAN-SanJose_Bootcamp LoRaWAN-grove-cayenne LoRaWAN-classC-demo LoRaWAN-grove-cayenne ... more

radio chip selection

Radio chip driver is not included, because two options are available.
If you're using SX1272 or SX1276, then import sx127x driver into your program.
if you're using SX1261 or SX1262, then import sx126x driver into your program.
If you're using NAmote72 or Murata discovery, then you must import only sx127x driver.

application project requirements

This library requires mbed TLS to be enabled.
The file mbed_app.json must be present in the project using this library:

{
    "macros": [ "MBEDTLS_CMAC_C" ]
}

regional PHY selection

All end device configuration is done in Commissioning.h, define desired radio frequency band of operation in this header file.
Commissioning.h is located in the application using this library.

end device provisioning

End device is provisioned by editing Commissioning.h in the application which is using this library
To use LoRaWAN-1.0 OTA: make sure LORAWAN_ROOT_APPKEY is undefined.
To use LoRaWAN-1.1 OTA, define LORAWAN_ROOT_APPKEY.
To select OTA operation, define LORAWAN_JOIN_EUI, then LORAWAN_DEVICE_EUI must be defined, along with root key(s).
To select ABP operation, undefine LORAWAN_JOIN_EUI: then define session keys

LoRaWAN 1.0 nameLoRaWAN 1.1 nameComissioning.h defnedescription
OTADevEUIDevEUILORAWAN_DEVICE_EUIuniquely identifies end device
OTAAppEUIJoinEUILORAWAN_JOIN_EUI
OTAAppKeyNwkKeyLORAWAN_ROOT_NWKKEYroot key for network server
OTA(note 1)AppKeyLORAWAN_ROOT_APPKEYroot key for application server
ABPNwkSKey(note 3)LORAWAN_FNwkSIntKeynetwork session key
ABP(note 2)SNwkSIntKeyLORAWAN_SNwkSIntKeymac layer network integrity key
ABP(note 2)NwkSEncKeyLORAWAN_NwkSEncKeynetwork session encryption key
ABP(note 2)FNwkSIntKeyLORAWAN_FNwkSIntKeyforwarding network session integrity key
ABPAppSKeyAppSKeyLORAWAN_APPSKEYapplication session encryption key

(note 1): LoRaWAN-1.0 OTA uses a single root key for both network server and application server.

In LoRaWAN-1.0 OTA: the single root AppKey is used to generate NwkSkey and AppSKey.
(note 2): In LoRaWAN-1.0 (both OTA and ABP) SNwkSIntKey, NwkSEncKey. FNwkSIntKey are of same value and are collectively known as NwkSKey.
(note 3): LoRaWAN-1.0 uses single network session key, LoRaWAN-1.1 uses 3 network session keys. Both use a unique application session key.


In LoRaWAN-1.1 OTA: the root NwkKey is used to generate SNwkSIntKey, NwkSEncKey, FNwkSIntKey
In LoRaWAN-1.1 OTA: the root AppKey is used to generate AppSKey


in ABP mode, the DevAddr, and session keys are fixed (never change), and frame counters never reset to zero.
ABP operation has no concept of: root keys, or DevEUI or JoinEUI/AppEUI.
in OTA mode, the DevAddr and session keys are assigned at join procedure, and frame counters reset at join.

eeprom

This library includes eeprom driver to support non-volatile storage required by LoRaWAN specification.
Currently eeprom is implemented for STM32L1 family and STM32L0 family.
Writing of values are wear-leveled to increase endurance; each write operation circulates across several memory locations. A read operation returns the highest value found. This simple method is used for sequence numbers which only increase.

value nameused in
DevNonceOTAfor Join request (note 1)
RJcount1OTAfor ReJoin Type 1 request
FCntUpABPuplink frame counter
NFCntDownABPdownlink frame counter
AFCntDownABPdownlink frame counter

AFCntDown is only used in LoRaWAN-1.1 when application payload is present in downlink and FPort > 0.
NFCntDown is used in LoRaWAN-1.1 when FPort is zero in downlink or application payload not present.
NFCntDown is the only downlink frame counter used in LoRaWAN-1.0
(note 1) OTA DevNonce is random number in LoRaWAN-1.0, therefore not stored in eeprom. DevNonce in LoRaWAN-1.1 is forever increasing (non-volatile) number upon each join request,.
RJcount0 is only stored in RAM because the value resets upon new session from JoinAccept, therefore not stored in eeprom.
Frame counters in OTA mode reset upon new session in join request, therefore are stored in RAM instead of eeprom for OTA.

radio driver support

When SX127x driver is used, both SX1272 and SX1276 are supported without defining at compile time. The chip is detected at start-up.
Supported radio platforms:


Alternately, when SX126x driver is imported, the SX126xDVK1xAS board is used.

low-speed clock oscillator selection

LoRaWAN uses 32768Hz crystal to permit low-power operation.
However, some mbed targets might revert to low-speed internal oscillator, which is not accurate enough for LoRaWAN operation.
An oscillator check is performed at initialization; program will not start if internal oscillator is used.
To force LSE watch crystal, add to mbed_app.json

{
    "macros": [ "MBEDTLS_CMAC_C" ],
    "target_overrides": {
        "<your-target>": {
            "target.lse_available": true
        }
    }
}

mac/LoRaMac1v1.h

Committer:
Wayne Roberts
Date:
2018-08-20
Revision:
12:0f28f2e7c35e
Parent:
11:ce1317758488

File content as of revision 12:0f28f2e7c35e:

#ifndef _LORAMAC_H_
#define _LORAMAC_H_

#include "Commissioning.h"

typedef enum {
    /*  0 */ LORAMAC_STATUS_OK = 0,
    /*  1 */ LORAMAC_STATUS_LSE,
    /*  2 */ LORAMAC_STATUS_WAITING_FOR_TXSTART,
    /*  3 */ LORAMAC_STATUS_WAITING_FOR_TXDONE,
    /*  4 */ LORAMAC_STATUS_WAITING_FOR_RX1,
    /*  5 */ LORAMAC_STATUS_WAITING_FOR_RX2,
    /*  6 */ LORAMAC_STATUS_BUSY_UPCONF,
    /*  7 */ LORAMAC_STATUS_SERVICE_UNKNOWN,
    /*  8 */ LORAMAC_STATUS_PARAMETER_INVALID,
    /*  9 */ LORAMAC_STATUS_DATARATE_INVALID,
    /* 10 */ LORAMAC_STATUS_FREQUENCY_INVALID,
    /* 11 */ LORAMAC_STATUS_LENGTH_ERROR,
    /* 12 */ LORAMAC_STATUS_DEVICE_OFF,
    /* 13 */ LORAMAC_STATUS_FREQ_AND_DR_INVALID,
    /* 14 */ LORAMAC_STATUS_EEPROM_FAIL,
    /* 15 */ LORAMAC_STATUS_MAC_CMD_LENGTH_ERROR,
#ifdef LORAWAN_JOIN_EUI
    /* 16 */ LORAMAC_STATUS_NO_NETWORK_JOINED
#endif
} LoRaMacStatus_t;

typedef enum {
    LORAMAC_EVENT_INFO_STATUS_OK,
    LORAMAC_EVENT_INFO_STATUS_INCR_FAIL,
    LORAMAC_EVENT_INFO_STATUS_MLMEREQ,
    LORAMAC_EVENT_INFO_STATUS_UNKNOWN_MTYPE,
    LORAMAC_EVENT_INFO_STATUS_SENDING,
    LORAMAC_EVENT_INFO_STATUS_MCPSREQ,
    LORAMAC_EVENT_INFO_STATUS_TX_TIMEOUT,
    LORAMAC_EVENT_INFO_STATUS_RX2_TIMEOUT,
    LORAMAC_EVENT_INFO_STATUS_RX2_ERROR,
    LORAMAC_EVENT_INFO_STATUS_DOWNLINK_REPEATED,
    LORAMAC_EVENT_INFO_STATUS_TX_DR_PAYLOAD_SIZE_ERROR,
    LORAMAC_EVENT_INFO_STATUS_DOWNLINK_TOO_MANY_FRAMES_LOSS,
    LORAMAC_EVENT_INFO_STATUS_ADDRESS_FAIL,
    LORAMAC_EVENT_INFO_STATUS_MIC_FAIL,
    LORAMAC_EVENT_INFO_STATUS_MULTICAST_FAIL,
    LORAMAC_EVENT_INFO_STATUS_BEACON_LOCKED,
    LORAMAC_EVENT_INFO_STATUS_BEACON_LOST,
    LORAMAC_EVENT_INFO_STATUS_BEACON_NOT_FOUND,
    LORAMAC_EVENT_INFO_STATUS_NO_APPKEY,
    LORAMAC_EVENT_INFO_BAD_RX_DELAY,
    LORAMAC_EVENT_INFO_STATUS_CHANNEL_BUSY,
#ifdef LORAWAN_JOIN_EUI
    LORAMAC_EVENT_INFO_STATUS_JOIN_FAIL,
    LORAMAC_EVENT_INFO_STATUS_JOINNONCE
#endif
} LoRaMacEventInfoStatus_t;

typedef enum {
    MCPS_NONE,
    MCPS_UNCONFIRMED,
    MCPS_CONFIRMED,
    MCPS_MULTICAST,
    MCPS_PROPRIETARY
} Mcps_t;

#include <stdint.h>
typedef struct {
    LoRaMacEventInfoStatus_t Status;
    Mcps_t McpsRequest;
    uint8_t NbRetries;
    bool AckReceived;
    uint32_t UpLinkCounter;
    uint8_t Datarate;
    int8_t TxPower;
    uint32_t UpLinkFreqHz;
    //us_timestamp_t TxTimeOnAir;
} McpsConfirm_t;

typedef struct {
    uint8_t Snr;
    int16_t Rssi;
    int8_t RxSlot;
    LoRaMacEventInfoStatus_t Status;
    Mcps_t McpsIndication;
    bool RxData;
    uint8_t RxDatarate;
    uint8_t Port;
    uint8_t BufferSize;
    uint8_t *Buffer;
    uint8_t FramePending;
    bool AckReceived;
    uint32_t expectedFCntDown;
    uint16_t receivedFCntDown;
    uint16_t ADR_ACK_CNT;
} McpsIndication_t;

typedef enum {
    MLME_NONE = 0,
    MLME_LINK_CHECK,
    MLME_SWITCH_CLASS,
    MLME_PING_SLOT_INFO,
    MLME_BEACON_TIMING,
    MLME_BEACON_ACQUISITION,
    MLME_TIME_REQ,
    MLME_BEACON,
    MLME_TXCW,
#ifdef LORAWAN_JOIN_EUI
    MLME_JOIN,
    MLME_REJOIN_0,
    MLME_REJOIN_1,
    MLME_REJOIN_2
#endif
} Mlme_t;

#include "lorawan_board.h"

#define DR_0        0
#define DR_1        1
#define DR_2        2
#define DR_3        3
#define DR_4        4
#define DR_5        5
#define DR_6        6
#define DR_7        7
#define DR_8        8
#define DR_9        9
#define DR_10       10
#define DR_11       11
#define DR_12       12
#define DR_13       13
#define DR_14       14
#define DR_15       15


typedef union {
    uint8_t Value;

    struct {
        int8_t Min : 4;
        int8_t Max : 4;
    } Fields;
} DrRange_t;

typedef struct {
    uint32_t FreqHz;
    DrRange_t DrRange;
    uint8_t Band;
} ChannelParams_t;


typedef struct {
    Mcps_t Type;
    struct {
        void *fBuffer;
        uint16_t fBufferSize;
        uint8_t Datarate;
        uint8_t fPort;
    } Req;
} McpsReq_t;

typedef struct {
    uint8_t MaxPossiblePayload;
    uint8_t CurrentPayloadSize;
} LoRaMacTxInfo_t;

typedef enum {
    MIB_DEV_ADDR,
    MIB_DEVICE_CLASS,
    MIB_ADR,
    MIB_PUBLIC_NETWORK,
    MIB_RX2_CHANNEL,
    MIB_CHANNELS_MASK,
    MIB_FNwkSIntKey,
    MIB_APP_SKEY,
    MIB_SNwkSIntKey,
    MIB_NwkSEncKey,
    MIB_NwkSKey,    /* lorawan 1.0 */
    MIB_MAX_LISTEN_TIME,
#ifdef LORAWAN_JOIN_EUI
    MIB_NETWORK_JOINED
#endif
} Mib_t;

typedef enum {
    CLASS_A = 0,
    CLASS_B,
    CLASS_C
} DeviceClass_t;

typedef struct {
    uint32_t FrequencyHz;
    uint8_t  Datarate;
} Rx2ChannelParams_t;

typedef union {
    uint32_t DevAddr;
    bool AdrEnable;
    DeviceClass_t Class;
    bool IsNetworkJoined;
    bool EnablePublicNetwork;
    uint16_t* ChannelsMask;
    const uint8_t* key;
    Rx2ChannelParams_t Rx2Channel;
    us_timestamp_t MaxListenTime;
} MibParam_t;

typedef struct {
    Mib_t Type;
    MibParam_t Param;
} MibRequestConfirm_t;



typedef struct {
    Mlme_t Type;

    union {
        struct {
            DeviceClass_t Class;
        } SwitchClass;

        union {
            uint8_t Value;
            struct sInfoFields {
                uint8_t Periodicity     : 3;
                uint8_t RFU             : 5;
            } Fields;
        } PingSlotInfo;

#ifdef LORAWAN_JOIN_EUI
        struct {
            const uint8_t *DevEui;
            const uint8_t *JoinEui;
            const uint8_t *NwkKey;
            const uint8_t *AppKey;
            uint8_t NbTrials;
        } Join;
#endif /* LORAWAN_JOIN_EUI */

        struct {
            uint16_t Timeout;
        } TxCw;
    } Req;
} MlmeReq_t;


typedef struct {
    LoRaMacEventInfoStatus_t Status;
    Mlme_t MlmeRequest;
    union {
        struct {
            uint8_t DemodMargin;
            uint8_t NbGateways;
        } link;
        struct {
            uint32_t Seconds;   // seconds since epoch
            uint32_t uSeconds;  // fractional part
        } time;
        struct {
            uint32_t rxJoinNonce;
            uint32_t myJoinNonce;
        } join;
    } fields;
    //us_timestamp_t TxTimeOnAir;
} MlmeConfirm_t;


typedef struct {
    Mlme_t MlmeIndication;
    LoRaMacEventInfoStatus_t Status;
    uint32_t freqHz;
#ifdef LORAWAN_JOIN_EUI
    uint8_t JoinRequestTrials;
#endif /* LORAWAN_JOIN_EUI  */     
} MlmeIndication_t;


/*!
 * LoRaMAC events structure
 * Used to notify upper layers of MAC events
 */
typedef struct sLoRaMacPrimitives
{
    /*!
     * \brief   MCPS-Confirm primitive
     *
     * \param   [OUT] MCPS-Confirm parameters
     */
    void (* const MacMcpsConfirm )( const McpsConfirm_t *McpsConfirm );
    /*!
     * \brief   MCPS-Indication primitive
     *
     * \param   [OUT] MCPS-Indication parameters
     */
    void (* const MacMcpsIndication )( const McpsIndication_t *McpsIndication );
    /*!
     * \brief   MLME-Confirm primitive
     *
     * \param   [OUT] MLME-Confirm parameters
     */
    void (* const MacMlmeConfirm )( const MlmeConfirm_t *MlmeConfirm );
    /*!
     * \brief   MLME-Indication primitive
     *
     * \param   [OUT] MLME-Indication parameters
     */
    void (* const MacMlmeIndication )( const MlmeIndication_t *MlmeIndication );
} LoRaMacPrimitives_t;

typedef struct sLoRaMacCallback
{
    /*!
     * \brief   Measures the battery level
     *
     * \retval  Battery level [0: node is connected to an external
     *          power source, 1..254: battery level, where 1 is the minimum
     *          and 254 is the maximum value, 255: the node was not able
     *          to measure the battery level]
     */
    uint8_t (* const GetBatteryLevel )( void );
    /*!
     * \brief   Measures the temperature level
     *
     * \retval  Temperature level
     */
    float (* const GetTemperatureLevel )( void );
} LoRaMacCallback_t;

LoRaMacStatus_t LoRaMacInitialization( const LoRaMacPrimitives_t *primitives, const LoRaMacCallback_t *callbacks );
us_timestamp_t LoRaMacReadTimer(void);
LoRaMacStatus_t LoRaMacQueryTxPossible(uint8_t size, LoRaMacTxInfo_t* txInfo);
LoRaMacStatus_t LoRaMacMcpsRequest( McpsReq_t *mcpsRequest );
LoRaMacStatus_t LoRaMacMibGetRequestConfirm( MibRequestConfirm_t *mibGet );
LoRaMacStatus_t LoRaMacMlmeRequest( const MlmeReq_t *mlmeRequest );
LoRaMacStatus_t LoRaMacMibSetRequestConfirm( MibRequestConfirm_t *mibSet );
LoRaMacStatus_t LoRaMacChannelAdd( uint8_t id, ChannelParams_t params );
void LoRaMacPrintStatus(void);
uint32_t get_fcntdwn(bool);
int8_t LoRaMacGetRxSlot(void);
void LoRaMacUserContext(void);

#endif /* _LORAMAC_H_ */