LoRaWAN end device MAC layer for SX1272 and SX1276. Supports LoRaWAN-1.0 and LoRaWAN-1.1
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 name | LoRaWAN 1.1 name | Comissioning.h defne | description | |
---|---|---|---|---|
OTA | DevEUI | DevEUI | LORAWAN_DEVICE_EUI | uniquely identifies end device |
OTA | AppEUI | JoinEUI | LORAWAN_JOIN_EUI | |
OTA | AppKey | NwkKey | LORAWAN_ROOT_NWKKEY | root key for network server |
OTA | (note 1) | AppKey | LORAWAN_ROOT_APPKEY | root key for application server |
ABP | NwkSKey | (note 3) | LORAWAN_FNwkSIntKey | network session key |
ABP | (note 2) | SNwkSIntKey | LORAWAN_SNwkSIntKey | mac layer network integrity key |
ABP | (note 2) | NwkSEncKey | LORAWAN_NwkSEncKey | network session encryption key |
ABP | (note 2) | FNwkSIntKey | LORAWAN_FNwkSIntKey | forwarding network session integrity key |
ABP | AppSKey | AppSKey | LORAWAN_APPSKEY | application 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 name | used in | |
---|---|---|
DevNonce | OTA | for Join request (note 1) |
RJcount1 | OTA | for ReJoin Type 1 request |
FCntUp | ABP | uplink frame counter |
NFCntDown | ABP | downlink frame counter |
AFCntDown | ABP | downlink 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/region_us915.cpp@12:0f28f2e7c35e, 2018-08-20 (annotated)
- Committer:
- Wayne Roberts
- Date:
- Mon Aug 20 14:09:49 2018 -0700
- Revision:
- 12:0f28f2e7c35e
- Parent:
- 11:ce1317758488
add 8 hybrid channel sets for us915 band
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Wayne Roberts |
11:ce1317758488 | 1 | #include "lorawan_board.h" |
Wayne Roberts |
0:6b3ac9c5a042 | 2 | #if defined(USE_BAND_915_HYBRID) || defined(USE_BAND_915) |
Wayne Roberts |
0:6b3ac9c5a042 | 3 | #include <stdint.h> |
Wayne Roberts |
0:6b3ac9c5a042 | 4 | #include "LoRaMacPrivate.h" |
Wayne Roberts |
0:6b3ac9c5a042 | 5 | |
Wayne Roberts |
0:6b3ac9c5a042 | 6 | ChannelParams_t Channels[LORA_MAX_NB_CHANNELS]; |
Wayne Roberts |
0:6b3ac9c5a042 | 7 | uint16_t ChannelsMaskRemaining[6]; |
Wayne Roberts |
0:6b3ac9c5a042 | 8 | const int8_t TxPowers[] = { 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10 }; |
Wayne Roberts |
0:6b3ac9c5a042 | 9 | |
Wayne Roberts |
0:6b3ac9c5a042 | 10 | // DR: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Wayne Roberts |
0:6b3ac9c5a042 | 11 | const uint8_t Datarates[] = { 10, 9, 8, 7, 8, 0, 0, 0, 12, 11, 10, 9, 8, 7, 0, 0}; |
Wayne Roberts |
0:6b3ac9c5a042 | 12 | |
Wayne Roberts |
0:6b3ac9c5a042 | 13 | const uint8_t MaxPayloadOfDatarate[] = { 11, 53, 125, 242, 242, 0, 0, 0, 53, 129, 242, 242, 242, 242, 0, 0 }; |
Wayne Roberts |
0:6b3ac9c5a042 | 14 | |
Wayne Roberts |
0:6b3ac9c5a042 | 15 | bool DisableChannelInMask( uint8_t id, uint16_t* mask ) |
Wayne Roberts |
0:6b3ac9c5a042 | 16 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 17 | uint8_t index = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 18 | index = id / 16; |
Wayne Roberts |
0:6b3ac9c5a042 | 19 | |
Wayne Roberts |
0:6b3ac9c5a042 | 20 | if( ( index > 4 ) || ( id >= LORA_MAX_NB_CHANNELS ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 21 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 22 | return false; |
Wayne Roberts |
0:6b3ac9c5a042 | 23 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 24 | |
Wayne Roberts |
0:6b3ac9c5a042 | 25 | // Deactivate channel |
Wayne Roberts |
0:6b3ac9c5a042 | 26 | mask[index] &= ~( 1 << ( id % 16 ) ); |
Wayne Roberts |
0:6b3ac9c5a042 | 27 | |
Wayne Roberts |
0:6b3ac9c5a042 | 28 | return true; |
Wayne Roberts |
0:6b3ac9c5a042 | 29 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 30 | |
Wayne Roberts |
0:6b3ac9c5a042 | 31 | /*! |
Wayne Roberts |
0:6b3ac9c5a042 | 32 | * Up/Down link data rates offset definition |
Wayne Roberts |
0:6b3ac9c5a042 | 33 | */ |
Wayne Roberts |
0:6b3ac9c5a042 | 34 | const int8_t datarateOffsets[5][4] = |
Wayne Roberts |
0:6b3ac9c5a042 | 35 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 36 | { DR_10, DR_9 , DR_8 , DR_8 }, // DR_0 |
Wayne Roberts |
0:6b3ac9c5a042 | 37 | { DR_11, DR_10, DR_9 , DR_8 }, // DR_1 |
Wayne Roberts |
0:6b3ac9c5a042 | 38 | { DR_12, DR_11, DR_10, DR_9 }, // DR_2 |
Wayne Roberts |
0:6b3ac9c5a042 | 39 | { DR_13, DR_12, DR_11, DR_10 }, // DR_3 |
Wayne Roberts |
0:6b3ac9c5a042 | 40 | { DR_13, DR_13, DR_12, DR_11 }, // DR_4 |
Wayne Roberts |
0:6b3ac9c5a042 | 41 | }; |
Wayne Roberts |
0:6b3ac9c5a042 | 42 | |
Wayne Roberts |
0:6b3ac9c5a042 | 43 | uint32_t region_GetRxBandwidth( int8_t datarate ) |
Wayne Roberts |
0:6b3ac9c5a042 | 44 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 45 | if( datarate >= DR_4 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 46 | {// LoRa 500 kHz |
Wayne Roberts |
0:6b3ac9c5a042 | 47 | return 2; |
Wayne Roberts |
0:6b3ac9c5a042 | 48 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 49 | return 0; // LoRa 125 kHz |
Wayne Roberts |
0:6b3ac9c5a042 | 50 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 51 | |
Wayne Roberts |
0:6b3ac9c5a042 | 52 | uint16_t region_GetRxSymbolTimeout( int8_t datarate ) |
Wayne Roberts |
0:6b3ac9c5a042 | 53 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 54 | switch( datarate ) |
Wayne Roberts |
0:6b3ac9c5a042 | 55 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 56 | case DR_0: // SF10 - BW125 |
Wayne Roberts |
0:6b3ac9c5a042 | 57 | return 5; |
Wayne Roberts |
0:6b3ac9c5a042 | 58 | |
Wayne Roberts |
0:6b3ac9c5a042 | 59 | case DR_1: // SF9 - BW125 |
Wayne Roberts |
0:6b3ac9c5a042 | 60 | case DR_2: // SF8 - BW125 |
Wayne Roberts |
0:6b3ac9c5a042 | 61 | case DR_8: // SF12 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 62 | case DR_9: // SF11 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 63 | case DR_10: // SF10 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 64 | return 8; |
Wayne Roberts |
0:6b3ac9c5a042 | 65 | |
Wayne Roberts |
0:6b3ac9c5a042 | 66 | case DR_3: // SF7 - BW125 |
Wayne Roberts |
0:6b3ac9c5a042 | 67 | case DR_11: // SF9 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 68 | return 10; |
Wayne Roberts |
0:6b3ac9c5a042 | 69 | |
Wayne Roberts |
0:6b3ac9c5a042 | 70 | case DR_4: // SF8 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 71 | case DR_12: // SF8 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 72 | return 14; |
Wayne Roberts |
0:6b3ac9c5a042 | 73 | |
Wayne Roberts |
0:6b3ac9c5a042 | 74 | case DR_13: // SF7 - BW500 |
Wayne Roberts |
0:6b3ac9c5a042 | 75 | return 16; |
Wayne Roberts |
0:6b3ac9c5a042 | 76 | |
Wayne Roberts |
0:6b3ac9c5a042 | 77 | default: |
Wayne Roberts |
0:6b3ac9c5a042 | 78 | return 0; // LoRa 125 kHz |
Wayne Roberts |
0:6b3ac9c5a042 | 79 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 80 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 81 | |
Wayne Roberts |
0:6b3ac9c5a042 | 82 | void region_rx1_setup(uint8_t ch) |
Wayne Roberts |
0:6b3ac9c5a042 | 83 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 84 | int8_t datarate = datarateOffsets[LoRaMacParams.ChannelsDatarate][LoRaMacParams.Rx1DrOffset]; |
Wayne Roberts |
0:6b3ac9c5a042 | 85 | if( datarate < 0 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 86 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 87 | datarate = DR_0; |
Wayne Roberts |
0:6b3ac9c5a042 | 88 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 89 | |
Wayne Roberts |
0:6b3ac9c5a042 | 90 | RxWindowSetup( |
Wayne Roberts |
0:6b3ac9c5a042 | 91 | LORAMAC_FIRST_RX1_CHANNEL + ( ch % 8 ) * LORAMAC_STEPWIDTH_RX1_CHANNEL, |
Wayne Roberts |
0:6b3ac9c5a042 | 92 | datarate, |
Wayne Roberts |
0:6b3ac9c5a042 | 93 | region_GetRxBandwidth(datarate), |
Wayne Roberts |
0:6b3ac9c5a042 | 94 | region_GetRxSymbolTimeout(datarate) |
Wayne Roberts |
0:6b3ac9c5a042 | 95 | ); |
Wayne Roberts |
0:6b3ac9c5a042 | 96 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 97 | |
Wayne Roberts |
0:6b3ac9c5a042 | 98 | bool ValidateChannelMask( uint16_t* channelsMask ) |
Wayne Roberts |
0:6b3ac9c5a042 | 99 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 100 | bool chanMaskState = false; |
Wayne Roberts |
0:6b3ac9c5a042 | 101 | uint16_t block1 = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 102 | uint16_t block2 = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 103 | uint8_t index = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 104 | |
Wayne Roberts |
0:6b3ac9c5a042 | 105 | for( uint8_t i = 0; i < 4; i++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 106 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 107 | block1 = channelsMask[i] & 0x00FF; |
Wayne Roberts |
0:6b3ac9c5a042 | 108 | block2 = channelsMask[i] & 0xFF00; |
Wayne Roberts |
0:6b3ac9c5a042 | 109 | |
Wayne Roberts |
0:6b3ac9c5a042 | 110 | if( ( CountBits( block1, 16 ) > 5 ) && ( chanMaskState == false ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 111 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 112 | channelsMask[i] &= block1; |
Wayne Roberts |
0:6b3ac9c5a042 | 113 | channelsMask[4] = 1 << ( i * 2 ); |
Wayne Roberts |
0:6b3ac9c5a042 | 114 | chanMaskState = true; |
Wayne Roberts |
0:6b3ac9c5a042 | 115 | index = i; |
Wayne Roberts |
0:6b3ac9c5a042 | 116 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 117 | else if( ( CountBits( block2, 16 ) > 5 ) && ( chanMaskState == false ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 118 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 119 | channelsMask[i] &= block2; |
Wayne Roberts |
0:6b3ac9c5a042 | 120 | channelsMask[4] = 1 << ( i * 2 + 1 ); |
Wayne Roberts |
0:6b3ac9c5a042 | 121 | chanMaskState = true; |
Wayne Roberts |
0:6b3ac9c5a042 | 122 | index = i; |
Wayne Roberts |
0:6b3ac9c5a042 | 123 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 124 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 125 | |
Wayne Roberts |
0:6b3ac9c5a042 | 126 | // Do only change the channel mask, if we have found a valid block. |
Wayne Roberts |
0:6b3ac9c5a042 | 127 | if( chanMaskState == true ) |
Wayne Roberts |
0:6b3ac9c5a042 | 128 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 129 | for( uint8_t i = 0; i < 4; i++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 130 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 131 | if( i != index ) |
Wayne Roberts |
0:6b3ac9c5a042 | 132 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 133 | channelsMask[i] = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 134 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 135 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 136 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 137 | return chanMaskState; |
Wayne Roberts |
0:6b3ac9c5a042 | 138 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 139 | |
Wayne Roberts |
0:6b3ac9c5a042 | 140 | void region_adr_request(adr_t* adr) |
Wayne Roberts |
0:6b3ac9c5a042 | 141 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 142 | if( adr->chMaskCntl == 6 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 143 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 144 | // Enable all 125 kHz channels |
Wayne Roberts |
0:6b3ac9c5a042 | 145 | adr->channelsMask[0] = 0xFFFF; |
Wayne Roberts |
0:6b3ac9c5a042 | 146 | adr->channelsMask[1] = 0xFFFF; |
Wayne Roberts |
0:6b3ac9c5a042 | 147 | adr->channelsMask[2] = 0xFFFF; |
Wayne Roberts |
0:6b3ac9c5a042 | 148 | adr->channelsMask[3] = 0xFFFF; |
Wayne Roberts |
0:6b3ac9c5a042 | 149 | // Apply chMask to channels 64 to 71 |
Wayne Roberts |
0:6b3ac9c5a042 | 150 | adr->channelsMask[4] = adr->chMask; |
Wayne Roberts |
0:6b3ac9c5a042 | 151 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 152 | else if( adr->chMaskCntl == 7 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 153 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 154 | // Disable all 125 kHz channels |
Wayne Roberts |
0:6b3ac9c5a042 | 155 | adr->channelsMask[0] = 0x0000; |
Wayne Roberts |
0:6b3ac9c5a042 | 156 | adr->channelsMask[1] = 0x0000; |
Wayne Roberts |
0:6b3ac9c5a042 | 157 | adr->channelsMask[2] = 0x0000; |
Wayne Roberts |
0:6b3ac9c5a042 | 158 | adr->channelsMask[3] = 0x0000; |
Wayne Roberts |
0:6b3ac9c5a042 | 159 | // Apply chMask to channels 64 to 71 |
Wayne Roberts |
0:6b3ac9c5a042 | 160 | adr->channelsMask[4] = adr->chMask; |
Wayne Roberts |
0:6b3ac9c5a042 | 161 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 162 | else if( adr->chMaskCntl == 5 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 163 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 164 | // RFU |
Wayne Roberts |
0:6b3ac9c5a042 | 165 | adr->status &= 0xFE; // Channel mask KO |
Wayne Roberts |
0:6b3ac9c5a042 | 166 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 167 | else |
Wayne Roberts |
0:6b3ac9c5a042 | 168 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 169 | adr->channelsMask[adr->chMaskCntl] = adr->chMask; |
Wayne Roberts |
0:6b3ac9c5a042 | 170 | |
Wayne Roberts |
0:6b3ac9c5a042 | 171 | // FCC 15.247 paragraph F mandates to hop on at least 2 125 kHz channels |
Wayne Roberts |
0:6b3ac9c5a042 | 172 | if( ( adr->datarate < DR_4 ) && ( CountNbEnabled125kHzChannels( adr->channelsMask ) < 2 ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 173 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 174 | adr->status &= 0xFE; // Channel mask KO |
Wayne Roberts |
0:6b3ac9c5a042 | 175 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 176 | |
Wayne Roberts |
0:6b3ac9c5a042 | 177 | #if defined( USE_BAND_915_HYBRID ) |
Wayne Roberts |
0:6b3ac9c5a042 | 178 | if( ValidateChannelMask( adr->channelsMask ) == false ) |
Wayne Roberts |
0:6b3ac9c5a042 | 179 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 180 | adr->status &= 0xFE; // Channel mask KO |
Wayne Roberts |
0:6b3ac9c5a042 | 181 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 182 | #endif |
Wayne Roberts |
0:6b3ac9c5a042 | 183 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 184 | |
Wayne Roberts |
0:6b3ac9c5a042 | 185 | |
Wayne Roberts |
0:6b3ac9c5a042 | 186 | if ((adr->status & 0x07) == 0x07) { |
Wayne Roberts |
0:6b3ac9c5a042 | 187 | // Reset ChannelsMaskRemaining to the new ChannelsMask |
Wayne Roberts |
0:6b3ac9c5a042 | 188 | ChannelsMaskRemaining[0] &= adr->channelsMask[0]; |
Wayne Roberts |
0:6b3ac9c5a042 | 189 | ChannelsMaskRemaining[1] &= adr->channelsMask[1]; |
Wayne Roberts |
0:6b3ac9c5a042 | 190 | ChannelsMaskRemaining[2] &= adr->channelsMask[2]; |
Wayne Roberts |
0:6b3ac9c5a042 | 191 | ChannelsMaskRemaining[3] &= adr->channelsMask[3]; |
Wayne Roberts |
0:6b3ac9c5a042 | 192 | ChannelsMaskRemaining[4] = adr->channelsMask[4]; |
Wayne Roberts |
0:6b3ac9c5a042 | 193 | ChannelsMaskRemaining[5] = adr->channelsMask[5]; |
Wayne Roberts |
0:6b3ac9c5a042 | 194 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 195 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 196 | |
Wayne Roberts |
3:eb174e10afbb | 197 | uint8_t region_CountNbEnabledChannels() |
Wayne Roberts |
0:6b3ac9c5a042 | 198 | { |
Wayne Roberts |
3:eb174e10afbb | 199 | return CountNbEnabled125kHzChannels(LoRaMacParams.ChannelsMask) + CountBits(LoRaMacParams.ChannelsMask[4], 16); |
Wayne Roberts |
0:6b3ac9c5a042 | 200 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 201 | |
Wayne Roberts |
0:6b3ac9c5a042 | 202 | uint8_t CountNbEnabled125kHzChannels( uint16_t *channelsMask ) |
Wayne Roberts |
0:6b3ac9c5a042 | 203 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 204 | uint8_t nb125kHzChannels = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 205 | |
Wayne Roberts |
0:6b3ac9c5a042 | 206 | for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS - 8; i += 16, k++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 207 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 208 | nb125kHzChannels += CountBits( channelsMask[k], 16 ); |
Wayne Roberts |
0:6b3ac9c5a042 | 209 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 210 | |
Wayne Roberts |
0:6b3ac9c5a042 | 211 | return nb125kHzChannels; |
Wayne Roberts |
0:6b3ac9c5a042 | 212 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 213 | |
Wayne Roberts |
0:6b3ac9c5a042 | 214 | static bool SetNextChannel( ) |
Wayne Roberts |
0:6b3ac9c5a042 | 215 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 216 | uint8_t nbEnabledChannels = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 217 | uint8_t enabledChannels[LORA_MAX_NB_CHANNELS]; |
Wayne Roberts |
0:6b3ac9c5a042 | 218 | |
Wayne Roberts |
0:6b3ac9c5a042 | 219 | memset( enabledChannels, 0, LORA_MAX_NB_CHANNELS ); |
Wayne Roberts |
0:6b3ac9c5a042 | 220 | |
Wayne Roberts |
0:6b3ac9c5a042 | 221 | if( CountNbEnabled125kHzChannels( ChannelsMaskRemaining ) == 0 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 222 | { // Restore default channels |
Wayne Roberts |
0:6b3ac9c5a042 | 223 | memcpy( ( uint8_t* ) ChannelsMaskRemaining, ( uint8_t* ) LoRaMacParams.ChannelsMask, 8 ); |
Wayne Roberts |
0:6b3ac9c5a042 | 224 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 225 | if( ( LoRaMacParams.ChannelsDatarate >= DR_4 ) && ( ( ChannelsMaskRemaining[4] & 0x00FF ) == 0 ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 226 | { // Make sure, that the channels are activated |
Wayne Roberts |
0:6b3ac9c5a042 | 227 | ChannelsMaskRemaining[4] = LoRaMacParams.ChannelsMask[4]; |
Wayne Roberts |
0:6b3ac9c5a042 | 228 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 229 | |
Wayne Roberts |
0:6b3ac9c5a042 | 230 | // Search how many channels are enabled |
Wayne Roberts |
0:6b3ac9c5a042 | 231 | for( uint8_t i = 0, k = 0; i < LORA_MAX_NB_CHANNELS; i += 16, k++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 232 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 233 | for( uint8_t j = 0; j < 16; j++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 234 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 235 | if( ( ChannelsMaskRemaining[k] & ( 1 << j ) ) != 0 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 236 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 237 | if( Channels[i + j].FreqHz == 0 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 238 | { // Check if the channel is enabled |
Wayne Roberts |
0:6b3ac9c5a042 | 239 | continue; |
Wayne Roberts |
0:6b3ac9c5a042 | 240 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 241 | if( ( ( Channels[i + j].DrRange.Fields.Min <= LoRaMacParams.ChannelsDatarate ) && |
Wayne Roberts |
0:6b3ac9c5a042 | 242 | ( LoRaMacParams.ChannelsDatarate <= Channels[i + j].DrRange.Fields.Max ) ) == false ) |
Wayne Roberts |
0:6b3ac9c5a042 | 243 | { // Check if the current channel selection supports the given datarate |
Wayne Roberts |
0:6b3ac9c5a042 | 244 | continue; |
Wayne Roberts |
0:6b3ac9c5a042 | 245 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 246 | enabledChannels[nbEnabledChannels++] = i + j; |
Wayne Roberts |
0:6b3ac9c5a042 | 247 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 248 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 249 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 250 | |
Wayne Roberts |
0:6b3ac9c5a042 | 251 | if( nbEnabledChannels > 0 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 252 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 253 | Channel = enabledChannels[random_at_most( nbEnabledChannels - 1 )]; |
Wayne Roberts |
0:6b3ac9c5a042 | 254 | if( Channel < ( LORA_MAX_NB_CHANNELS - 8 ) ) |
Wayne Roberts |
0:6b3ac9c5a042 | 255 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 256 | DisableChannelInMask( Channel, ChannelsMaskRemaining ); |
Wayne Roberts |
0:6b3ac9c5a042 | 257 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 258 | return true; |
Wayne Roberts |
0:6b3ac9c5a042 | 259 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 260 | else |
Wayne Roberts |
0:6b3ac9c5a042 | 261 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 262 | // Datarate not supported by any channel |
Wayne Roberts |
0:6b3ac9c5a042 | 263 | return false; |
Wayne Roberts |
0:6b3ac9c5a042 | 264 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 265 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 266 | |
Wayne Roberts |
0:6b3ac9c5a042 | 267 | void region_ScheduleTx( ) |
Wayne Roberts |
0:6b3ac9c5a042 | 268 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 269 | // Select channel |
Wayne Roberts |
0:6b3ac9c5a042 | 270 | while( SetNextChannel() == false ) |
Wayne Roberts |
0:6b3ac9c5a042 | 271 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 272 | // Set the default datarate |
Wayne Roberts |
0:6b3ac9c5a042 | 273 | LoRaMacParams.ChannelsDatarate = LoRaMacParamsDefaults.ChannelsDatarate; |
Wayne Roberts |
0:6b3ac9c5a042 | 274 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 275 | |
Wayne Roberts |
0:6b3ac9c5a042 | 276 | //MAC_PRINTF("ch%u ", Channel); |
Wayne Roberts |
0:6b3ac9c5a042 | 277 | SendFrameOnChannel( Channel ); |
Wayne Roberts |
0:6b3ac9c5a042 | 278 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 279 | |
Wayne Roberts |
0:6b3ac9c5a042 | 280 | void region_tx_setup(int8_t dbm, uint8_t pktLen) |
Wayne Roberts |
0:6b3ac9c5a042 | 281 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 282 | int8_t datarate = Datarates[LoRaMacParams.ChannelsDatarate]; |
Wayne Roberts |
0:6b3ac9c5a042 | 283 | |
Wayne Roberts |
10:9a7a8b8d0ac2 | 284 | Radio::set_tx_dbm(dbm); |
Wayne Roberts |
10:9a7a8b8d0ac2 | 285 | |
Wayne Roberts |
0:6b3ac9c5a042 | 286 | //MAC_PRINTF("txsetup sf%d dr%u ", datarate, LoRaMacParams.ChannelsDatarate); |
Wayne Roberts |
0:6b3ac9c5a042 | 287 | |
Wayne Roberts |
0:6b3ac9c5a042 | 288 | if( LoRaMacParams.ChannelsDatarate >= DR_4 ) |
Wayne Roberts |
0:6b3ac9c5a042 | 289 | { // High speed LoRa channel BW500 kHz |
Wayne Roberts |
0:6b3ac9c5a042 | 290 | //TxTimeOnAir_us = Radio.TimeOnAir_us( MODEM_LORA, pktLen ); |
Wayne Roberts |
10:9a7a8b8d0ac2 | 291 | Radio::LoRaModemConfig(500, datarate, 1); |
Wayne Roberts |
10:9a7a8b8d0ac2 | 292 | Radio::LoRaPacketConfig(8, false, true, false); |
Wayne Roberts |
0:6b3ac9c5a042 | 293 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 294 | else |
Wayne Roberts |
0:6b3ac9c5a042 | 295 | { // Normal LoRa channel |
Wayne Roberts |
0:6b3ac9c5a042 | 296 | //TxTimeOnAir_us = Radio.TimeOnAir_us( MODEM_LORA, pktLen ); |
Wayne Roberts |
10:9a7a8b8d0ac2 | 297 | Radio::LoRaModemConfig(125, datarate, 1); |
Wayne Roberts |
10:9a7a8b8d0ac2 | 298 | Radio::LoRaPacketConfig(8, false, true, false); |
Wayne Roberts |
0:6b3ac9c5a042 | 299 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 300 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 301 | |
Wayne Roberts |
3:eb174e10afbb | 302 | #define RECEIVE_DELAY2_us 2000000 |
Wayne Roberts |
3:eb174e10afbb | 303 | #define JOIN_ACCEPT_DELAY1_us 5000000 |
Wayne Roberts |
3:eb174e10afbb | 304 | #define JOIN_ACCEPT_DELAY2_us 6000000 |
Wayne Roberts |
3:eb174e10afbb | 305 | const LoRaMacParams_t LoRaMacParamsDefaults = { |
Wayne Roberts |
3:eb174e10afbb | 306 | /* int8_t ChannelsTxPower; */ LORAMAC_DEFAULT_TX_POWER, |
Wayne Roberts |
3:eb174e10afbb | 307 | /* int8_t ChannelsDatarate;*/ LORAMAC_DEFAULT_DATARATE, |
Wayne Roberts |
3:eb174e10afbb | 308 | /* uint32_t MaxRxWindow_us;*/ MAX_RX_WINDOW_us, |
Wayne Roberts |
3:eb174e10afbb | 309 | /* uint32_t ReceiveDelay1_us;*/ RECEIVE_DELAY1_us, |
Wayne Roberts |
3:eb174e10afbb | 310 | /* uint32_t ReceiveDelay2_us;*/ RECEIVE_DELAY2_us, |
Wayne Roberts |
3:eb174e10afbb | 311 | #ifdef LORAWAN_JOIN_EUI |
Wayne Roberts |
3:eb174e10afbb | 312 | /* uint32_t JoinAcceptDelay1_us;*/ JOIN_ACCEPT_DELAY1_us, |
Wayne Roberts |
3:eb174e10afbb | 313 | /* uint32_t JoinAcceptDelay2_us;*/ JOIN_ACCEPT_DELAY2_us, |
Wayne Roberts |
3:eb174e10afbb | 314 | #endif /* LORAWAN_JOIN_EUI */ |
Wayne Roberts |
3:eb174e10afbb | 315 | /* uint8_t NbTrans;*/ 1, |
Wayne Roberts |
3:eb174e10afbb | 316 | /* uint8_t Rx1DrOffset;*/ 0, |
Wayne Roberts |
3:eb174e10afbb | 317 | /* Rx2ChannelParams_t Rx2Channel;*/ RX_WND_2_CHANNEL, |
Wayne Roberts |
3:eb174e10afbb | 318 | #if defined( USE_BAND_915 ) |
Wayne Roberts |
3:eb174e10afbb | 319 | /* uint16_t ChannelsMask[6];*/ { 0xffff, 0xffff, 0xffff, 0xffff, 0x00ff, 0x0000}, |
Wayne Roberts |
3:eb174e10afbb | 320 | /* uint8_t NbEnabledChannels;*/ 72, |
Wayne Roberts |
3:eb174e10afbb | 321 | #elif defined( USE_BAND_915_HYBRID ) |
Wayne Roberts |
12:0f28f2e7c35e | 322 | #if defined(HYBRID_H) |
Wayne Roberts |
12:0f28f2e7c35e | 323 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0x0000, 0x0000, 0xff00, 0x0080, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 324 | #elif defined(HYBRID_G) |
Wayne Roberts |
12:0f28f2e7c35e | 325 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0x0000, 0x0000, 0x00ff, 0x0040, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 326 | #elif defined(HYBRID_F) |
Wayne Roberts |
12:0f28f2e7c35e | 327 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0x0000, 0xff00, 0x0000, 0x0020, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 328 | #elif defined(HYBRID_E) |
Wayne Roberts |
12:0f28f2e7c35e | 329 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0x0000, 0x00ff, 0x0000, 0x0010, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 330 | #elif defined(HYBRID_D) |
Wayne Roberts |
12:0f28f2e7c35e | 331 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0xff00, 0x0000, 0x0000, 0x0008, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 332 | #elif defined(HYBRID_C) |
Wayne Roberts |
12:0f28f2e7c35e | 333 | /* uint16_t ChannelsMask[6];*/ { 0x0000, 0x00ff, 0x0000, 0x0000, 0x0004, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 334 | #elif defined(HYBRID_B) |
Wayne Roberts |
12:0f28f2e7c35e | 335 | /* uint16_t ChannelsMask[6];*/ { 0xff00, 0x0000, 0x0000, 0x0000, 0x0002, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 336 | #else |
Wayne Roberts |
12:0f28f2e7c35e | 337 | /* uint16_t ChannelsMask[6];*/ { 0x00ff, 0x0000, 0x0000, 0x0000, 0x0001, 0x0000}, |
Wayne Roberts |
12:0f28f2e7c35e | 338 | #endif |
Wayne Roberts |
7:4b6f960dcca2 | 339 | /* uint8_t NbEnabledChannels;*/ 9, |
Wayne Roberts |
3:eb174e10afbb | 340 | #endif |
Wayne Roberts |
7:4b6f960dcca2 | 341 | /* us_timestamp_t MaxListenTime */ 0 /* no LBT in USA */ |
Wayne Roberts |
3:eb174e10afbb | 342 | }; |
Wayne Roberts |
3:eb174e10afbb | 343 | |
Wayne Roberts |
0:6b3ac9c5a042 | 344 | void region_mac_init() |
Wayne Roberts |
0:6b3ac9c5a042 | 345 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 346 | // 125 kHz channels |
Wayne Roberts |
0:6b3ac9c5a042 | 347 | for( uint8_t i = 0; i < LORA_MAX_NB_CHANNELS - 8; i++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 348 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 349 | Channels[i].FreqHz = 902.3e6 + i * 200e3; |
Wayne Roberts |
0:6b3ac9c5a042 | 350 | Channels[i].DrRange.Value = ( DR_3 << 4 ) | DR_0; |
Wayne Roberts |
0:6b3ac9c5a042 | 351 | Channels[i].Band = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 352 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 353 | // 500 kHz channels |
Wayne Roberts |
0:6b3ac9c5a042 | 354 | for( uint8_t i = LORA_MAX_NB_CHANNELS - 8; i < LORA_MAX_NB_CHANNELS; i++ ) |
Wayne Roberts |
0:6b3ac9c5a042 | 355 | { |
Wayne Roberts |
0:6b3ac9c5a042 | 356 | Channels[i].FreqHz = 903.0e6 + ( i - ( LORA_MAX_NB_CHANNELS - 8 ) ) * 1.6e6; |
Wayne Roberts |
0:6b3ac9c5a042 | 357 | Channels[i].DrRange.Value = ( DR_4 << 4 ) | DR_4; |
Wayne Roberts |
0:6b3ac9c5a042 | 358 | Channels[i].Band = 0; |
Wayne Roberts |
0:6b3ac9c5a042 | 359 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 360 | } |
Wayne Roberts |
0:6b3ac9c5a042 | 361 | |
Wayne Roberts |
3:eb174e10afbb | 362 | int8_t |
Wayne Roberts |
3:eb174e10afbb | 363 | region_LimitTxPower( int8_t txPower ) |
Wayne Roberts |
3:eb174e10afbb | 364 | { |
Wayne Roberts |
3:eb174e10afbb | 365 | int8_t resultTxPower = txPower; |
Wayne Roberts |
3:eb174e10afbb | 366 | |
Wayne Roberts |
3:eb174e10afbb | 367 | if( ( LoRaMacParams.ChannelsDatarate == DR_4 ) || |
Wayne Roberts |
3:eb174e10afbb | 368 | ( ( LoRaMacParams.ChannelsDatarate >= DR_8 ) && ( LoRaMacParams.ChannelsDatarate <= DR_13 ) ) ) |
Wayne Roberts |
3:eb174e10afbb | 369 | {// Limit tx power to max 26dBm |
Wayne Roberts |
3:eb174e10afbb | 370 | resultTxPower = MAX( txPower, TX_POWER_26_DBM ); |
Wayne Roberts |
3:eb174e10afbb | 371 | } |
Wayne Roberts |
3:eb174e10afbb | 372 | else |
Wayne Roberts |
3:eb174e10afbb | 373 | { |
Wayne Roberts |
3:eb174e10afbb | 374 | if( CountNbEnabled125kHzChannels( LoRaMacParams.ChannelsMask ) < 50 ) |
Wayne Roberts |
3:eb174e10afbb | 375 | {// Limit tx power to max 21dBm |
Wayne Roberts |
3:eb174e10afbb | 376 | resultTxPower = MAX( txPower, TX_POWER_20_DBM ); |
Wayne Roberts |
3:eb174e10afbb | 377 | } |
Wayne Roberts |
3:eb174e10afbb | 378 | } |
Wayne Roberts |
3:eb174e10afbb | 379 | return resultTxPower; |
Wayne Roberts |
3:eb174e10afbb | 380 | } |
Wayne Roberts |
3:eb174e10afbb | 381 | |
Wayne Roberts |
3:eb174e10afbb | 382 | #ifdef LORAWAN_JOIN_EUI |
Wayne Roberts |
3:eb174e10afbb | 383 | int8_t |
Wayne Roberts |
3:eb174e10afbb | 384 | region_AlternateDatarate( uint16_t nbTrials ) |
Wayne Roberts |
3:eb174e10afbb | 385 | { |
Wayne Roberts |
3:eb174e10afbb | 386 | if (region_CountNbEnabledChannels() < LoRaMacParamsDefaults.NbEnabledChannels) { |
Wayne Roberts |
3:eb174e10afbb | 387 | memcpy(LoRaMacParams.ChannelsMask, LoRaMacParamsDefaults.ChannelsMask, sizeof(LoRaMacParams.ChannelsMask)); |
Wayne Roberts |
3:eb174e10afbb | 388 | LoRaMacParams.NbEnabledChannels = LoRaMacParamsDefaults.NbEnabledChannels; |
Wayne Roberts |
3:eb174e10afbb | 389 | } |
Wayne Roberts |
3:eb174e10afbb | 390 | |
Wayne Roberts |
3:eb174e10afbb | 391 | if( ( nbTrials & 0x01 ) == 0x01 ) |
Wayne Roberts |
3:eb174e10afbb | 392 | { |
Wayne Roberts |
3:eb174e10afbb | 393 | return DR_4; |
Wayne Roberts |
3:eb174e10afbb | 394 | } |
Wayne Roberts |
3:eb174e10afbb | 395 | else |
Wayne Roberts |
3:eb174e10afbb | 396 | { |
Wayne Roberts |
3:eb174e10afbb | 397 | return DR_0; |
Wayne Roberts |
3:eb174e10afbb | 398 | } |
Wayne Roberts |
3:eb174e10afbb | 399 | } |
Wayne Roberts |
3:eb174e10afbb | 400 | #endif /* LORAWAN_JOIN_EUI */ |
Wayne Roberts |
3:eb174e10afbb | 401 | |
Wayne Roberts |
0:6b3ac9c5a042 | 402 | void region_session_start(LoRaMacEventInfoStatus_t status) { } |
Wayne Roberts |
0:6b3ac9c5a042 | 403 | |
Wayne Roberts |
0:6b3ac9c5a042 | 404 | #endif /* USE_BAND_915_HYBRID || USE_BAND_915 */ |