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
        }
    }
}
Committer:
Wayne Roberts
Date:
Mon Jun 11 14:09:16 2018 -0700
Revision:
9:fe8e08792ae9
Parent:
8:5a5ea7cc946f
sx126x: update xfer() calls

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Wayne Roberts 8:5a5ea7cc946f 1 /* Only for NUCLEO boards: prevent compiling for MOTE_L152RC and typeABZ discovery */
Wayne Roberts 8:5a5ea7cc946f 2 #if defined(TARGET_FF_ARDUINO) && defined(TARGET_FF_MORPHO) && !defined(TARGET_DISCO_L072CZ_LRWAN1)
Wayne Roberts 8:5a5ea7cc946f 3 #include "radio.h"
Wayne Roberts 8:5a5ea7cc946f 4 #ifdef SX127x_H
Wayne Roberts 8:5a5ea7cc946f 5 #include "board.h"
Wayne Roberts 8:5a5ea7cc946f 6 #include "SPIu.h"
Wayne Roberts 8:5a5ea7cc946f 7
Wayne Roberts 8:5a5ea7cc946f 8 SPIu spi(D11, D12, D13); // mosi, miso, sclk
Wayne Roberts 8:5a5ea7cc946f 9 // dio0, dio1, nss, spi, rst
Wayne Roberts 8:5a5ea7cc946f 10 SX127x Radio::radio( D2, D3, D10, spi, A0); // sx127[62] arduino shield
Wayne Roberts 8:5a5ea7cc946f 11 SX127x_lora Radio::lora(radio);
Wayne Roberts 8:5a5ea7cc946f 12 SX127x_fsk Radio::fsk(radio);
Wayne Roberts 8:5a5ea7cc946f 13
Wayne Roberts 8:5a5ea7cc946f 14 InterruptIn Radio::dio0(D2);
Wayne Roberts 8:5a5ea7cc946f 15 InterruptIn Radio::dio1(D3);
Wayne Roberts 8:5a5ea7cc946f 16
Wayne Roberts 8:5a5ea7cc946f 17 typedef enum {
Wayne Roberts 8:5a5ea7cc946f 18 SHIELD_TYPE_NONE = 0,
Wayne Roberts 8:5a5ea7cc946f 19 SHIELD_TYPE_LAS,
Wayne Roberts 8:5a5ea7cc946f 20 SHIELD_TYPE_MAS,
Wayne Roberts 8:5a5ea7cc946f 21 } shield_type_e;
Wayne Roberts 8:5a5ea7cc946f 22 shield_type_e shield_type;
Wayne Roberts 8:5a5ea7cc946f 23
Wayne Roberts 8:5a5ea7cc946f 24 #ifdef TARGET_FF_MORPHO
Wayne Roberts 8:5a5ea7cc946f 25 DigitalOut pc3(PC_3); // debug RX indication, for nucleo boards
Wayne Roberts 8:5a5ea7cc946f 26 #endif /* TARGET_FF_MORPHO */
Wayne Roberts 8:5a5ea7cc946f 27 DigitalInOut rfsw(A4);
Wayne Roberts 8:5a5ea7cc946f 28 void Radio::rfsw_callback()
Wayne Roberts 8:5a5ea7cc946f 29 {
Wayne Roberts 8:5a5ea7cc946f 30 if (radio.RegOpMode.bits.Mode == RF_OPMODE_TRANSMITTER)
Wayne Roberts 8:5a5ea7cc946f 31 rfsw = 1;
Wayne Roberts 8:5a5ea7cc946f 32 else
Wayne Roberts 8:5a5ea7cc946f 33 rfsw = 0;
Wayne Roberts 8:5a5ea7cc946f 34
Wayne Roberts 8:5a5ea7cc946f 35 if (radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER || radio.RegOpMode.bits.Mode == RF_OPMODE_RECEIVER_SINGLE)
Wayne Roberts 8:5a5ea7cc946f 36 pc3 = 1;
Wayne Roberts 8:5a5ea7cc946f 37 else
Wayne Roberts 8:5a5ea7cc946f 38 pc3 = 0;
Wayne Roberts 8:5a5ea7cc946f 39 }
Wayne Roberts 8:5a5ea7cc946f 40
Wayne Roberts 8:5a5ea7cc946f 41 void
Wayne Roberts 8:5a5ea7cc946f 42 Radio::set_tx_dbm(int8_t dbm)
Wayne Roberts 8:5a5ea7cc946f 43 {
Wayne Roberts 8:5a5ea7cc946f 44 RegPdsTrim1_t pds_trim;
Wayne Roberts 8:5a5ea7cc946f 45 uint8_t adr;
Wayne Roberts 8:5a5ea7cc946f 46 if (radio.type == SX1276)
Wayne Roberts 8:5a5ea7cc946f 47 adr = REG_PDSTRIM1_SX1276;
Wayne Roberts 8:5a5ea7cc946f 48 else
Wayne Roberts 8:5a5ea7cc946f 49 adr = REG_PDSTRIM1_SX1272;
Wayne Roberts 8:5a5ea7cc946f 50
Wayne Roberts 8:5a5ea7cc946f 51 pds_trim.octet = radio.read_reg(adr);
Wayne Roberts 8:5a5ea7cc946f 52
Wayne Roberts 8:5a5ea7cc946f 53 if (shield_type == SHIELD_TYPE_LAS)
Wayne Roberts 8:5a5ea7cc946f 54 radio.RegPaConfig.bits.PaSelect = 1;
Wayne Roberts 8:5a5ea7cc946f 55 else
Wayne Roberts 8:5a5ea7cc946f 56 radio.RegPaConfig.bits.PaSelect = 0;
Wayne Roberts 8:5a5ea7cc946f 57
Wayne Roberts 8:5a5ea7cc946f 58 if (radio.RegPaConfig.bits.PaSelect) {
Wayne Roberts 8:5a5ea7cc946f 59 /* PABOOST used: +2dbm to +17, or +20 */
Wayne Roberts 8:5a5ea7cc946f 60 if (dbm > 17) {
Wayne Roberts 8:5a5ea7cc946f 61 MAC_PRINTF("+20dBm PADAC bias\r\n");
Wayne Roberts 8:5a5ea7cc946f 62 if (dbm > 20)
Wayne Roberts 8:5a5ea7cc946f 63 dbm = 20;
Wayne Roberts 8:5a5ea7cc946f 64 dbm -= 3;
Wayne Roberts 8:5a5ea7cc946f 65 pds_trim.bits.prog_txdac = 7;
Wayne Roberts 8:5a5ea7cc946f 66 radio.write_reg(adr, pds_trim.octet);
Wayne Roberts 8:5a5ea7cc946f 67 ocp(150);
Wayne Roberts 8:5a5ea7cc946f 68 } else
Wayne Roberts 8:5a5ea7cc946f 69 ocp(120);
Wayne Roberts 8:5a5ea7cc946f 70
Wayne Roberts 8:5a5ea7cc946f 71 if (dbm > 1)
Wayne Roberts 8:5a5ea7cc946f 72 radio.RegPaConfig.bits.OutputPower = dbm - 2;
Wayne Roberts 8:5a5ea7cc946f 73 } else {
Wayne Roberts 8:5a5ea7cc946f 74 /* RFO used: -1 to +14dbm */
Wayne Roberts 8:5a5ea7cc946f 75 ocp(80);
Wayne Roberts 8:5a5ea7cc946f 76 if (dbm < 15)
Wayne Roberts 8:5a5ea7cc946f 77 radio.RegPaConfig.bits.OutputPower = dbm + 1;
Wayne Roberts 8:5a5ea7cc946f 78 }
Wayne Roberts 8:5a5ea7cc946f 79 radio.write_reg(REG_PACONFIG, radio.RegPaConfig.octet);
Wayne Roberts 8:5a5ea7cc946f 80
Wayne Roberts 8:5a5ea7cc946f 81 radio.RegPaConfig.octet = radio.read_reg(REG_PACONFIG);
Wayne Roberts 8:5a5ea7cc946f 82 if (radio.RegPaConfig.bits.PaSelect) {
Wayne Roberts 8:5a5ea7cc946f 83 MAC_PRINTF("PA_BOOST ");
Wayne Roberts 8:5a5ea7cc946f 84 dbm = radio.RegPaConfig.bits.OutputPower + pds_trim.bits.prog_txdac - 2;
Wayne Roberts 8:5a5ea7cc946f 85 } else {
Wayne Roberts 8:5a5ea7cc946f 86 MAC_PRINTF("RFO ");
Wayne Roberts 8:5a5ea7cc946f 87 dbm = radio.RegPaConfig.bits.OutputPower - 1;
Wayne Roberts 8:5a5ea7cc946f 88 }
Wayne Roberts 8:5a5ea7cc946f 89 MAC_PRINTF("OutputPower:%ddBm{%02x}\r\n", dbm, radio.RegPaConfig.octet);
Wayne Roberts 8:5a5ea7cc946f 90 }
Wayne Roberts 8:5a5ea7cc946f 91
Wayne Roberts 8:5a5ea7cc946f 92 void Radio::boardInit()
Wayne Roberts 8:5a5ea7cc946f 93 {
Wayne Roberts 8:5a5ea7cc946f 94 rfsw.input();
Wayne Roberts 8:5a5ea7cc946f 95 if (rfsw.read()) {
Wayne Roberts 8:5a5ea7cc946f 96 shield_type = SHIELD_TYPE_LAS;
Wayne Roberts 8:5a5ea7cc946f 97 MAC_PRINTF("LAS\r\n");
Wayne Roberts 8:5a5ea7cc946f 98 } else {
Wayne Roberts 8:5a5ea7cc946f 99 shield_type = SHIELD_TYPE_MAS;
Wayne Roberts 8:5a5ea7cc946f 100 MAC_PRINTF("MAS\r\n");
Wayne Roberts 8:5a5ea7cc946f 101 }
Wayne Roberts 8:5a5ea7cc946f 102
Wayne Roberts 8:5a5ea7cc946f 103 rfsw.output();
Wayne Roberts 8:5a5ea7cc946f 104 }
Wayne Roberts 8:5a5ea7cc946f 105
Wayne Roberts 8:5a5ea7cc946f 106 #endif /* ..SX127x_H */
Wayne Roberts 8:5a5ea7cc946f 107 #endif /* ...sx127x shield */
Wayne Roberts 8:5a5ea7cc946f 108