[LoRaWAN] Implement full session persistence & more v1.1 specification (#835)
* Implement session persistence & more 1.1 protocol * [LoRaW] Improve session persistence, check frame counters & Nonces, multiple MAC commands * [LoRaWAN] fix popping MAC command from queue I just realized that the method popMacCommand did not correctly remove items from the queue - this should solve the problem * [LoRaWAN] implement improvements from #835 * [LoRaWAN] String --> uint8_t[] * [LoRaWAN] Fix typo
This commit is contained in:
parent
29c891e017
commit
556f37f608
6 changed files with 404 additions and 82 deletions
|
@ -291,8 +291,9 @@ setModem KEYWORD2
|
||||||
|
|
||||||
# LoRaWAN
|
# LoRaWAN
|
||||||
wipe KEYWORD2
|
wipe KEYWORD2
|
||||||
|
restoreOTAA KEYWORD2
|
||||||
beginOTAA KEYWORD2
|
beginOTAA KEYWORD2
|
||||||
beginAPB KEYWORD2
|
beginABP KEYWORD2
|
||||||
uplink KEYWORD2
|
uplink KEYWORD2
|
||||||
downlink KEYWORD2
|
downlink KEYWORD2
|
||||||
configureChannel KEYWORD2
|
configureChannel KEYWORD2
|
||||||
|
@ -401,5 +402,10 @@ RADIOLIB_ERR_INVALID_PORT LITERAL1
|
||||||
RADIOLIB_ERR_NO_RX_WINDOW LITERAL1
|
RADIOLIB_ERR_NO_RX_WINDOW LITERAL1
|
||||||
RADIOLIB_ERR_INVALID_CHANNEL LITERAL1
|
RADIOLIB_ERR_INVALID_CHANNEL LITERAL1
|
||||||
RADIOLIB_ERR_INVALID_CID LITERAL1
|
RADIOLIB_ERR_INVALID_CID LITERAL1
|
||||||
|
RADIOLIB_ERR_UPLINK_UNAVAILABLE LITERAL1
|
||||||
RADIOLIB_ERR_COMMAND_QUEUE_FULL LITERAL1
|
RADIOLIB_ERR_COMMAND_QUEUE_FULL LITERAL1
|
||||||
RADIOLIB_ERR_COMMAND_QUEUE_EMPTY LITERAL1
|
RADIOLIB_ERR_COMMAND_QUEUE_EMPTY LITERAL1
|
||||||
|
RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND LITERAL1
|
||||||
|
RADIOLIB_ERR_JOIN_NONCE_INVALID LITERAL1
|
||||||
|
RADIOLIB_ERR_N_FCNT_DOWN_INVALID LITERAL1
|
||||||
|
RADIOLIB_ERR_A_FCNT_DOWN_INVALID LITERAL1
|
|
@ -442,7 +442,7 @@
|
||||||
|
|
||||||
// the amount of space allocated to the persistent storage
|
// the amount of space allocated to the persistent storage
|
||||||
#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE)
|
#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE)
|
||||||
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x60)
|
#define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0xD0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// This only compiles on STM32 boards with SUBGHZ module, but also
|
// This only compiles on STM32 boards with SUBGHZ module, but also
|
||||||
|
|
48
src/Hal.h
48
src/Hal.h
|
@ -7,25 +7,47 @@
|
||||||
#include "BuildOpt.h"
|
#include "BuildOpt.h"
|
||||||
|
|
||||||
// list of persistent parameters
|
// list of persistent parameters
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (0)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID (0) // this is NOT the LoRaWAN version, but version of this table
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (1)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (2)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID (2)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (3)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (3)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID (4)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (4)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID (5)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (5)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID (6)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (6)
|
||||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID (7)
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID (7)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID (8)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID (9)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID (10)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID (11)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID (12)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID (13)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID (14)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID (15)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID (16)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID (17)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID (18)
|
||||||
|
|
||||||
static const uint32_t RadioLibPersistentParamTable[] = {
|
static const uint32_t RadioLibPersistentParamTable[] = {
|
||||||
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
|
0x00, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION
|
||||||
0x04, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID
|
0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
|
||||||
0x08, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
|
0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID
|
||||||
0x0C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
|
|
||||||
0x10, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID
|
0x10, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID
|
||||||
0x20, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID
|
0x20, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID
|
||||||
0x30, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID
|
0x30, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID
|
||||||
0x40, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID
|
0x40, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID
|
||||||
0x50, // end
|
0x50, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID
|
||||||
|
0x54, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID
|
||||||
|
0x58, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID
|
||||||
|
0x5C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID
|
||||||
|
0x60, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST
|
||||||
|
0x70, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID
|
||||||
|
0x74, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID
|
||||||
|
0x78, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_AFCNT_DOWN_ID
|
||||||
|
0x7C, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_NFCNT_DOWN_ID
|
||||||
|
0x80, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_ID
|
||||||
|
0x84, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_ID
|
||||||
|
0x88, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID
|
||||||
|
0xD0, // end
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -533,6 +533,26 @@
|
||||||
*/
|
*/
|
||||||
#define RADIOLIB_ERR_COMMAND_QUEUE_EMPTY (-1110)
|
#define RADIOLIB_ERR_COMMAND_QUEUE_EMPTY (-1110)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Unable to delete MAC command because it was not found in the queue.
|
||||||
|
*/
|
||||||
|
#define RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND (-1111)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Unable to join network because JoinNonce is not higher than saved value.
|
||||||
|
*/
|
||||||
|
#define RADIOLIB_ERR_JOIN_NONCE_INVALID (-1112)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Received downlink Network frame counter is invalid (lower than last heard value).
|
||||||
|
*/
|
||||||
|
#define RADIOLIB_ERR_N_FCNT_DOWN_INVALID (-1113)
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\brief Received downlink Application frame counter is invalid (lower than last heard value).
|
||||||
|
*/
|
||||||
|
#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114)
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\}
|
\}
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -44,7 +44,7 @@ void LoRaWANNode::wipe() {
|
||||||
mod->hal->wipePersistentStorage();
|
mod->hal->wipePersistentStorage();
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::begin() {
|
int16_t LoRaWANNode::restoreOTAA() {
|
||||||
int16_t state = this->setPhyProperties();
|
int16_t state = this->setPhyProperties();
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
@ -55,12 +55,99 @@ int16_t LoRaWANNode::begin() {
|
||||||
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
|
return(RADIOLIB_ERR_NETWORK_NOT_JOINED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// in case of future revisions of NVM, use a version parameter to allow transitioning from one version to another while keeping session alive
|
||||||
|
uint16_t nvm_table_version = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID);
|
||||||
|
// if (RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION > nvm_table_version) {
|
||||||
|
// // set default values for variables that are new or something
|
||||||
|
// }
|
||||||
|
(void)nvm_table_version;
|
||||||
|
|
||||||
// pull all needed information from persistent storage
|
// pull all needed information from persistent storage
|
||||||
this->devAddr = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID);
|
this->devAddr = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID);
|
||||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("appSKey:");
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
|
|
||||||
|
uint32_t dlSettings = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID);
|
||||||
|
this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7;
|
||||||
|
uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4;
|
||||||
|
uint8_t rx2DataRate = dlSettings & 0x0F;
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: %d", this->rev);
|
||||||
|
|
||||||
|
// TODO process the RX2 data rate
|
||||||
|
(void)rx2DataRate;
|
||||||
|
|
||||||
|
// TODO process the data rate offset
|
||||||
|
(void)rx1DrOffset;
|
||||||
|
|
||||||
|
// parse Rx1 delay (and subsequently Rx2)
|
||||||
|
this->rxDelays[0] = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID);
|
||||||
|
if(this->rxDelays[0] == 0) {
|
||||||
|
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
||||||
|
}
|
||||||
|
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||||
|
|
||||||
|
// process CFlist if any bit is non-zero
|
||||||
|
uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||||
|
uint8_t allZero[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||||
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("cfList:");
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||||
|
if(memcmp(cfList, allZero, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN)) {
|
||||||
|
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
|
||||||
|
// list of frequencies
|
||||||
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
|
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&cfList[3*i], 3);
|
||||||
|
availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = (float)freq/10000.0;
|
||||||
|
availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i] = availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i];
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Channel UL/DL %d frequency = %f MHz", i, availableChannelsFreq[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// frequency mask, we need to find out which frequencies are actually being used
|
||||||
|
uint8_t channelId = 0;
|
||||||
|
uint8_t chSpan = 0;
|
||||||
|
uint8_t chNum = 0;
|
||||||
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
|
uint16_t mask = LoRaWANNode::ntoh<uint16_t>(&cfList[2*i]);
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", i, mask);
|
||||||
|
for(uint8_t j = 0; j < 16; j++) {
|
||||||
|
if(chNum >= this->band->defaultChannels[chSpan].numChannels) {
|
||||||
|
chNum -= this->band->defaultChannels[chSpan].numChannels;
|
||||||
|
chSpan++;
|
||||||
|
|
||||||
|
if(chSpan >= this->band->numChannelSpans) {
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("channel bitmask overrun!");
|
||||||
|
return(RADIOLIB_ERR_UNKNOWN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(mask & (1UL << j)) {
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("chNum = %d, chSpan = %d", chNum, chSpan);
|
||||||
|
uint8_t dir = this->band->defaultChannels[chSpan].direction;
|
||||||
|
float freq = this->band->defaultChannels[chSpan].freqStart + chNum*this->band->defaultChannels[chSpan].freqStep;
|
||||||
|
availableChannelsFreq[dir][channelId] = freq;
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Channel %cL %d frequency = %f MHz", dir ? 'U': 'D', channelId, availableChannelsFreq[dir][channelId]);
|
||||||
|
channelId++;
|
||||||
|
}
|
||||||
|
|
||||||
|
chNum++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 };
|
||||||
|
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
|
||||||
|
state = this->setupChannels();
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,7 +156,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
Module* mod = this->phyLayer->getMod();
|
Module* mod = this->phyLayer->getMod();
|
||||||
if(!force && (mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
|
if(!force && (mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
|
||||||
// the device has joined already, we can just pull the data from persistent storage
|
// the device has joined already, we can just pull the data from persistent storage
|
||||||
return(this->begin());
|
return(this->restoreOTAA());
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the physical layer configuration
|
// set the physical layer configuration
|
||||||
|
@ -175,9 +262,23 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:");
|
RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:");
|
||||||
RADIOLIB_DEBUG_HEXDUMP(joinAcceptMsg, lenRx);
|
RADIOLIB_DEBUG_HEXDUMP(joinAcceptMsg, lenRx);
|
||||||
|
|
||||||
|
// get current JoinNonce from downlink and previous JoinNonce from NVM
|
||||||
|
uint32_t joinNonce = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
|
||||||
|
uint32_t joinNoncePrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID);
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", joinNoncePrev, joinNonce);
|
||||||
|
|
||||||
|
// JoinNonce received must be greater than the last JoinNonce heard, else error
|
||||||
|
if(joinNonce <= joinNoncePrev) {
|
||||||
|
return(RADIOLIB_ERR_JOIN_NONCE_INVALID);
|
||||||
|
}
|
||||||
|
|
||||||
// check LoRaWAN revision (the MIC verification depends on this)
|
// check LoRaWAN revision (the MIC verification depends on this)
|
||||||
uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS];
|
uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS];
|
||||||
if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) {
|
this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7;
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: 1.%d", this->rev);
|
||||||
|
|
||||||
|
// verify MIC
|
||||||
|
if(this->rev == 1) {
|
||||||
// 1.1 version, first we need to derive the join accept integrity key
|
// 1.1 version, first we need to derive the join accept integrity key
|
||||||
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY;
|
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY;
|
||||||
|
@ -203,11 +304,20 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
uint8_t rx1DrOffset = (dlSettings & 0x70) >> 4;
|
||||||
|
uint8_t rx2DataRate = dlSettings & 0x0F;
|
||||||
|
|
||||||
// parse the contents
|
// TODO process the RX2 data rate
|
||||||
uint32_t joinNonce = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
|
(void)rx2DataRate;
|
||||||
|
|
||||||
|
// TODO process the data rate offset
|
||||||
|
(void)rx1DrOffset;
|
||||||
|
|
||||||
|
// parse other contents
|
||||||
uint32_t homeNetId = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3);
|
uint32_t homeNetId = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3);
|
||||||
this->devAddr = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]);
|
this->devAddr = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]);
|
||||||
|
|
||||||
|
// parse Rx1 delay (and subsequently Rx2)
|
||||||
this->rxDelays[0] = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS]*1000;
|
this->rxDelays[0] = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS]*1000;
|
||||||
if(this->rxDelays[0] == 0) {
|
if(this->rxDelays[0] == 0) {
|
||||||
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
||||||
|
@ -215,7 +325,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||||
|
|
||||||
// process CFlist if present
|
// process CFlist if present
|
||||||
|
uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 };
|
||||||
if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) {
|
if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) {
|
||||||
|
memcpy(&cfList[0], &joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS], RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||||
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
|
if(this->band->cfListType == RADIOLIB_LORAWAN_CFLIST_TYPE_FREQUENCIES) {
|
||||||
// list of frequencies
|
// list of frequencies
|
||||||
for(uint8_t i = 0; i < 5; i++) {
|
for(uint8_t i = 0; i < 5; i++) {
|
||||||
|
@ -262,7 +374,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// prepare buffer for key derivation
|
// prepare buffer for key derivation
|
||||||
|
@ -270,7 +381,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3);
|
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3);
|
||||||
|
|
||||||
// check protocol version (1.0 vs 1.1)
|
// check protocol version (1.0 vs 1.1)
|
||||||
if(dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) {
|
if(this->rev == 1) {
|
||||||
// 1.1 version, derive the keys
|
// 1.1 version, derive the keys
|
||||||
LoRaWANNode::hton<uint64_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI);
|
LoRaWANNode::hton<uint64_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI);
|
||||||
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS], devNonce);
|
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS], devNonce);
|
||||||
|
@ -292,7 +403,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->nwkSEncKey);
|
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->nwkSEncKey);
|
||||||
|
|
||||||
// enqueue the RekeyInd MAC command to be sent in the next uplink
|
// enqueue the RekeyInd MAC command to be sent in the next uplink
|
||||||
this->rev = 1;
|
|
||||||
LoRaWANMacCommand_t cmd = {
|
LoRaWANMacCommand_t cmd = {
|
||||||
.cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY,
|
.cid = RADIOLIB_LORAWAN_MAC_CMD_REKEY,
|
||||||
.len = sizeof(uint8_t),
|
.len = sizeof(uint8_t),
|
||||||
|
@ -304,7 +414,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 1.0 version, just derive the keys
|
// 1.0 version, just derive the keys
|
||||||
this->rev = 0;
|
|
||||||
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], homeNetId, 3);
|
LoRaWANNode::hton<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], homeNetId, 3);
|
||||||
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonce);
|
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonce);
|
||||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
|
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
|
||||||
|
@ -329,13 +438,27 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
||||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||||
|
|
||||||
|
// save uplink parameters
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID, joinNonce);
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID, homeNetId);
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX_DELAY_ID, this->rxDelays[0]);
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DL_SETTINGS_ID, (uint32_t)dlSettings);
|
||||||
|
|
||||||
|
// save cfList (all 0 if none is present)
|
||||||
|
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CF_LIST_ID), cfList, RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN);
|
||||||
|
|
||||||
// all complete, reset device counters and set the magic number
|
// all complete, reset device counters and set the magic number
|
||||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0);
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0);
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, 0);
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, 0);
|
||||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||||
|
|
||||||
|
// everything written to NVM, write current version to NVM
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) {
|
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey) {
|
||||||
this->devAddr = addr;
|
this->devAddr = addr;
|
||||||
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||||
|
@ -355,7 +478,13 @@ int16_t LoRaWANNode::beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
||||||
|
|
||||||
// setup uplink/downlink frequencies and datarates
|
// setup uplink/downlink frequencies and datarates
|
||||||
state = this->setupChannels();
|
state = this->setupChannels();
|
||||||
return(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
|
// everything written to NVM, write current version to NVM
|
||||||
|
Module* mod = this->phyLayer->getMod();
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
||||||
|
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||||
|
@ -373,12 +502,24 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
if(port > 0xDF) {
|
if(port > 0xDF) {
|
||||||
return(RADIOLIB_ERR_INVALID_PORT);
|
return(RADIOLIB_ERR_INVALID_PORT);
|
||||||
}
|
}
|
||||||
|
// port 0 is only allowed for MAC-only payloads
|
||||||
|
if(port == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) {
|
||||||
|
if (!isMACPayload) {
|
||||||
|
return(RADIOLIB_ERR_INVALID_PORT);
|
||||||
|
}
|
||||||
|
// if this is MAC only payload, continue and reset for next uplink
|
||||||
|
isMACPayload = false;
|
||||||
|
}
|
||||||
|
|
||||||
// check if there are some MAC commands to piggyback
|
Module* mod = this->phyLayer->getMod();
|
||||||
size_t foptsLen = 0;
|
|
||||||
if(this->commandsUp.numCommands > 0) {
|
// check if there are some MAC commands to piggyback (only when piggybacking onto a application-frame)
|
||||||
|
uint8_t foptsLen = 0;
|
||||||
|
size_t foptsBufSize = 0;
|
||||||
|
if(this->commandsUp.numCommands > 0 && port != RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) {
|
||||||
// there are, assume the maximum possible FOpts len for buffer allocation
|
// there are, assume the maximum possible FOpts len for buffer allocation
|
||||||
foptsLen = 15;
|
foptsLen = this->commandsUp.len;
|
||||||
|
foptsBufSize = 15;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check maximum payload len as defined in phy
|
// check maximum payload len as defined in phy
|
||||||
|
@ -392,7 +533,6 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
RADIOLIB_ASSERT(state);
|
RADIOLIB_ASSERT(state);
|
||||||
|
|
||||||
// check if sufficient time has elapsed since the last uplink
|
// check if sufficient time has elapsed since the last uplink
|
||||||
Module* mod = this->phyLayer->getMod();
|
|
||||||
if(mod->hal->millis() - this->rxDelayStart < rxDelays[1]) {
|
if(mod->hal->millis() - this->rxDelayStart < rxDelays[1]) {
|
||||||
// not enough time elapsed since the last uplink, we may still be in an RX window
|
// not enough time elapsed since the last uplink, we may still be in an RX window
|
||||||
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
return(RADIOLIB_ERR_UPLINK_UNAVAILABLE);
|
||||||
|
@ -400,7 +540,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
|
|
||||||
// build the uplink message
|
// build the uplink message
|
||||||
// the first 16 bytes are reserved for MIC calculation blocks
|
// the first 16 bytes are reserved for MIC calculation blocks
|
||||||
size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsBufSize);
|
||||||
#if defined(RADIOLIB_STATIC_ONLY)
|
#if defined(RADIOLIB_STATIC_ONLY)
|
||||||
uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE];
|
uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE];
|
||||||
#else
|
#else
|
||||||
|
@ -420,22 +560,37 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt);
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt);
|
||||||
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)fcnt);
|
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)fcnt);
|
||||||
|
|
||||||
// check if we have some MAC command to append
|
// check if we have some MAC commands to append
|
||||||
// TODO implement appending multiple MAC commands
|
if(foptsLen > 0) {
|
||||||
|
uint8_t foptsNum = this->commandsUp.numCommands;
|
||||||
|
uint8_t foptsBuff[foptsBufSize];
|
||||||
|
size_t idx = 0;
|
||||||
|
for (size_t i = 0; i < foptsNum; i++) {
|
||||||
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
||||||
if(popMacCommand(&cmd, &this->commandsUp) == RADIOLIB_ERR_NONE) {
|
popMacCommand(&cmd, &this->commandsUp, i);
|
||||||
// we do, add it to fopts
|
if (cmd.cid == 0) {
|
||||||
uint8_t foptsBuff[RADIOLIB_AES128_BLOCK_SIZE];
|
break;
|
||||||
foptsBuff[0] = cmd.cid;
|
|
||||||
for(size_t i = 1; i < cmd.len; i++) {
|
|
||||||
foptsBuff[i] = cmd.payload[i];
|
|
||||||
}
|
}
|
||||||
foptsLen = 1 + cmd.len;
|
foptsBuff[idx] = cmd.cid;
|
||||||
|
for(size_t i = 1; i < cmd.len; i++) {
|
||||||
|
foptsBuff[idx + i] = cmd.payload[i];
|
||||||
|
}
|
||||||
|
idx += cmd.len + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
||||||
|
|
||||||
uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen);
|
||||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
||||||
|
|
||||||
// encrypt it
|
// encrypt it
|
||||||
processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true);
|
processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true);
|
||||||
|
|
||||||
|
// write the current MAC command queue to nvm for next uplink
|
||||||
|
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)];
|
||||||
|
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the port
|
// set the port
|
||||||
|
@ -448,6 +603,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// encrypt the frame payload
|
// encrypt the frame payload
|
||||||
|
// TODO check ctrId --> erratum says it should be 0x01?
|
||||||
processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true);
|
processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true);
|
||||||
|
|
||||||
// create blocks for MIC calculation
|
// create blocks for MIC calculation
|
||||||
|
@ -686,15 +842,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
state = RADIOLIB_ERR_NONE;
|
state = RADIOLIB_ERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the frame counter and set it to the MIC calculation block
|
|
||||||
// TODO this will not handle overflow into 32-bits!
|
|
||||||
// TODO cache the ADR bit?
|
|
||||||
uint16_t fcnt = LoRaWANNode::ntoh<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]);
|
|
||||||
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt);
|
|
||||||
|
|
||||||
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
|
|
||||||
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
|
|
||||||
|
|
||||||
if(state != RADIOLIB_ERR_NONE) {
|
if(state != RADIOLIB_ERR_NONE) {
|
||||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
delete[] downlinkMsg;
|
delete[] downlinkMsg;
|
||||||
|
@ -702,6 +849,55 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
return(state);
|
return(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get the frame counter and set it to the MIC calculation block
|
||||||
|
// TODO cache the ADR bit?
|
||||||
|
uint16_t fcnt16 = LoRaWANNode::ntoh<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]);
|
||||||
|
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16);
|
||||||
|
uint32_t fcnt32 = fcnt16; // calculate possible rollover once decided if this is network downlink or application downlink
|
||||||
|
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
|
||||||
|
|
||||||
|
// calculate length of FOpts and payload
|
||||||
|
uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK;
|
||||||
|
int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t);
|
||||||
|
|
||||||
|
bool isAppDownlink = true;
|
||||||
|
if (payLen <= 0 && this->rev == 1) { // no payload => MAC commands only => Network frame (LoRaWAN v1.1 only)
|
||||||
|
isAppDownlink = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check the FcntDown value (Network or Application)
|
||||||
|
uint32_t fcntDownPrev = 0;
|
||||||
|
if (isAppDownlink) {
|
||||||
|
fcntDownPrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID);
|
||||||
|
} else {
|
||||||
|
fcntDownPrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID);
|
||||||
|
}
|
||||||
|
|
||||||
|
// assume a 16-bit to 32-bit rollover when difference in LSB is smaller than MAX_FCNT_GAP
|
||||||
|
// if that isn't the case and the received fcnt is smaller or equal to the last heard fcnt, then error
|
||||||
|
if ((fcnt16 <= fcntDownPrev) && ((0xFFFF - (uint16_t)fcntDownPrev + fcnt16) > RADIOLIB_LORAWAN_MAX_FCNT_GAP)) {
|
||||||
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
|
delete[] downlinkMsg;
|
||||||
|
#endif
|
||||||
|
if (isAppDownlink) {
|
||||||
|
return(RADIOLIB_ERR_A_FCNT_DOWN_INVALID);
|
||||||
|
} else {
|
||||||
|
return(RADIOLIB_ERR_N_FCNT_DOWN_INVALID);
|
||||||
|
}
|
||||||
|
} else if (fcnt16 <= fcntDownPrev) {
|
||||||
|
uint16_t msb = (fcntDownPrev >> 16) + 1; // assume a rollover
|
||||||
|
fcnt32 |= (msb << 16); // add back the MSB part
|
||||||
|
}
|
||||||
|
|
||||||
|
// save current fcnt to NVM
|
||||||
|
if (isAppDownlink) {
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, fcnt32);
|
||||||
|
} else {
|
||||||
|
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, fcnt32);
|
||||||
|
}
|
||||||
|
|
||||||
// check the MIC
|
// check the MIC
|
||||||
if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) {
|
if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) {
|
||||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
|
@ -720,15 +916,14 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
return(RADIOLIB_ERR_DOWNLINK_MALFORMED);
|
return(RADIOLIB_ERR_DOWNLINK_MALFORMED);
|
||||||
}
|
}
|
||||||
|
|
||||||
// check fopts len
|
// process FOpts (if there are any)
|
||||||
uint8_t foptsLen = downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK;
|
|
||||||
if(foptsLen > 0) {
|
if(foptsLen > 0) {
|
||||||
// there are some Fopts, decrypt them
|
// there are some Fopts, decrypt them
|
||||||
uint8_t fopts[RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK];
|
uint8_t fopts[RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK];
|
||||||
|
|
||||||
// according to the specification, the last two arguments should be 0x00 and false,
|
// TODO it COULD be the case that the assumed rollover is incorrect, if possible figure out a way to catch this and retry with just fcnt16
|
||||||
// but that will fail even for LoRaWAN 1.1.0 server
|
uint8_t ctrId = 0x01 + isAppDownlink; // see LoRaWAN v1.1 errata
|
||||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x01, true);
|
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, ctrId, true);
|
||||||
|
|
||||||
RADIOLIB_DEBUG_PRINTLN("fopts:");
|
RADIOLIB_DEBUG_PRINTLN("fopts:");
|
||||||
RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen);
|
RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen);
|
||||||
|
@ -753,23 +948,59 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||||
remLen -= processedLen;
|
remLen -= processedLen;
|
||||||
foptsPtr += processedLen;
|
foptsPtr += processedLen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink
|
||||||
|
if(this->commandsUp.len > 15) {
|
||||||
|
uint8_t foptsNum = this->commandsUp.numCommands;
|
||||||
|
size_t foptsBufSize = this->commandsUp.len;
|
||||||
|
uint8_t foptsBuff[foptsBufSize];
|
||||||
|
size_t idx = 0;
|
||||||
|
for(size_t i = 0; i < foptsNum; i++) {
|
||||||
|
LoRaWANMacCommand_t cmd = { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, };
|
||||||
|
popMacCommand(&cmd, &this->commandsUp, i);
|
||||||
|
if(cmd.cid == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
foptsBuff[idx] = cmd.cid;
|
||||||
|
for(size_t i = 1; i < cmd.len; i++) {
|
||||||
|
foptsBuff[idx + i] = cmd.payload[i];
|
||||||
|
}
|
||||||
|
idx += cmd.len + 1;
|
||||||
|
}
|
||||||
|
RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", foptsNum);
|
||||||
|
RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize);
|
||||||
|
|
||||||
|
isMACPayload = true;
|
||||||
|
this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND);
|
||||||
|
uint8_t strDown[this->band->payloadLenMax[this->dataRate[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]];
|
||||||
|
size_t lenDown = 0;
|
||||||
|
state = this->downlink(strDown, &lenDown);
|
||||||
|
RADIOLIB_ASSERT(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fopts are processed or not present, check if there is payload
|
// write the MAC command queue to nvm for next uplink
|
||||||
int payLen = downlinkMsgLen - 8 - foptsLen - sizeof(uint32_t);
|
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)];
|
||||||
|
memcpy(&queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
// process payload (if there is any)
|
||||||
if(payLen <= 0) {
|
if(payLen <= 0) {
|
||||||
// no payload
|
// no payload
|
||||||
*len = 0;
|
*len = 0;
|
||||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
delete[] downlinkMsg;
|
delete[] downlinkMsg;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is payload, and so there should be a port too
|
// there is payload, and so there should be a port too
|
||||||
// TODO pass the port?
|
// TODO pass the port?
|
||||||
*len = payLen - 1;
|
*len = payLen - 1;
|
||||||
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true);
|
// TODO it COULD be the case that the assumed rollover is incorrect, then figure out a way to catch this and retry with just fcnt16
|
||||||
|
// TODO does the erratum hold here as well?
|
||||||
|
processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], downlinkMsgLen, this->appSKey, data, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true);
|
||||||
|
|
||||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||||
delete[] downlinkMsg;
|
delete[] downlinkMsg;
|
||||||
|
@ -1124,38 +1355,69 @@ int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQ
|
||||||
queue->commands[queue->numCommands - 1].payload[4],
|
queue->commands[queue->numCommands - 1].payload[4],
|
||||||
queue->commands[queue->numCommands - 1].repeat);*/
|
queue->commands[queue->numCommands - 1].repeat);*/
|
||||||
queue->numCommands++;
|
queue->numCommands++;
|
||||||
|
queue->len += 1 + cmd->len; // 1 byte for command ID, len bytes for payload
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force) {
|
int16_t LoRaWANNode::popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index) {
|
||||||
if(queue->numCommands == 0) {
|
if(queue->numCommands == 0) {
|
||||||
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(cmd) {
|
if(cmd) {
|
||||||
/*RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
// RADIOLIB_DEBUG_PRINTLN("pop MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
||||||
queue->commands[queue->numCommands - 1].cid,
|
// queue->commands[index].cid,
|
||||||
queue->commands[queue->numCommands - 1].len,
|
// queue->commands[index].len,
|
||||||
queue->commands[queue->numCommands - 1].payload[0],
|
// queue->commands[index].payload[0],
|
||||||
queue->commands[queue->numCommands - 1].payload[1],
|
// queue->commands[index].payload[1],
|
||||||
queue->commands[queue->numCommands - 1].payload[2],
|
// queue->commands[index].payload[2],
|
||||||
queue->commands[queue->numCommands - 1].payload[3],
|
// queue->commands[index].payload[3],
|
||||||
queue->commands[queue->numCommands - 1].payload[4],
|
// queue->commands[index].payload[4],
|
||||||
queue->commands[queue->numCommands - 1].repeat);*/
|
// queue->commands[index].repeat);
|
||||||
memcpy(cmd, &queue->commands[queue->numCommands - 1], sizeof(LoRaWANMacCommand_t));
|
memcpy(cmd, &queue->commands[index], sizeof(LoRaWANMacCommand_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
if((!force) && (queue->commands[queue->numCommands - 1].repeat > 0)) {
|
if(queue->commands[index].repeat > 0) {
|
||||||
queue->commands[queue->numCommands - 1].repeat--;
|
queue->commands[index].repeat--;
|
||||||
} else {
|
} else {
|
||||||
queue->commands[queue->numCommands - 1].repeat = 0;
|
deleteMacCommand(queue->commands[index].cid, queue);
|
||||||
queue->numCommands--;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return(RADIOLIB_ERR_NONE);
|
return(RADIOLIB_ERR_NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue) {
|
||||||
|
if(queue->numCommands == 0) {
|
||||||
|
return(RADIOLIB_ERR_COMMAND_QUEUE_EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t index = 0; index < queue->numCommands; index++) {
|
||||||
|
if(queue->commands[index].cid == cid) {
|
||||||
|
// RADIOLIB_DEBUG_PRINTLN("delete MAC CID = %02x, len = %d, payload = %02x %02x %02x %02x %02x, repeat = %d ",
|
||||||
|
// queue->commands[index].cid,
|
||||||
|
// queue->commands[index].len,
|
||||||
|
// queue->commands[index].payload[0],
|
||||||
|
// queue->commands[index].payload[1],
|
||||||
|
// queue->commands[index].payload[2],
|
||||||
|
// queue->commands[index].payload[3],
|
||||||
|
// queue->commands[index].payload[4],
|
||||||
|
// queue->commands[index].repeat);
|
||||||
|
queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload
|
||||||
|
// move all subsequent commands one forward in the queue
|
||||||
|
if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) {
|
||||||
|
memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index) * sizeof(LoRaWANMacCommand_t));
|
||||||
|
}
|
||||||
|
// set the latest element to all 0
|
||||||
|
memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t));
|
||||||
|
queue->numCommands--;
|
||||||
|
return(RADIOLIB_ERR_NONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND);
|
||||||
|
}
|
||||||
|
|
||||||
size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len);
|
RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len);
|
||||||
|
|
||||||
|
@ -1171,7 +1433,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
||||||
if(srvVersion == this->rev) {
|
if(srvVersion == this->rev) {
|
||||||
// valid server version, stop sending the ResetInd MAC command
|
// valid server version, stop sending the ResetInd MAC command
|
||||||
popMacCommand(NULL, &this->commandsUp, true);
|
deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_RESET, &this->commandsUp);
|
||||||
}
|
}
|
||||||
return(1);
|
return(1);
|
||||||
} break;
|
} break;
|
||||||
|
@ -1378,7 +1640,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
||||||
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion);
|
||||||
if((srvVersion > 0) && (srvVersion <= this->rev)) {
|
if((srvVersion > 0) && (srvVersion <= this->rev)) {
|
||||||
// valid server version, stop sending the ReKey MAC command
|
// valid server version, stop sending the ReKey MAC command
|
||||||
popMacCommand(NULL, &this->commandsUp, true);
|
deleteMacCommand(RADIOLIB_LORAWAN_MAC_CMD_REKEY, &this->commandsUp);
|
||||||
}
|
}
|
||||||
return(1);
|
return(1);
|
||||||
} break;
|
} break;
|
||||||
|
|
|
@ -5,6 +5,9 @@
|
||||||
#include "../PhysicalLayer/PhysicalLayer.h"
|
#include "../PhysicalLayer/PhysicalLayer.h"
|
||||||
#include "../../utils/Cryptography.h"
|
#include "../../utils/Cryptography.h"
|
||||||
|
|
||||||
|
// version of NVM table layout (NOT the LoRaWAN version)
|
||||||
|
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (0x01)
|
||||||
|
|
||||||
// preamble format
|
// preamble format
|
||||||
#define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34)
|
#define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34)
|
||||||
#define RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN (8)
|
#define RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN (8)
|
||||||
|
@ -257,7 +260,7 @@ struct LoRaWANMacCommand_t {
|
||||||
uint8_t cid;
|
uint8_t cid;
|
||||||
|
|
||||||
/*! \brief Length of the payload */
|
/*! \brief Length of the payload */
|
||||||
size_t len;
|
uint8_t len;
|
||||||
|
|
||||||
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
/*! \brief Payload buffer (5 bytes is the longest possible) */
|
||||||
uint8_t payload[5];
|
uint8_t payload[5];
|
||||||
|
@ -269,6 +272,7 @@ struct LoRaWANMacCommand_t {
|
||||||
struct LoRaWANMacCommandQueue_t {
|
struct LoRaWANMacCommandQueue_t {
|
||||||
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
|
LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE];
|
||||||
size_t numCommands;
|
size_t numCommands;
|
||||||
|
size_t len;
|
||||||
};
|
};
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
|
@ -306,10 +310,10 @@ class LoRaWANNode {
|
||||||
void wipe();
|
void wipe();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Join network by loading information from persistent storage.
|
\brief Restore OTAA session by loading information from persistent storage.
|
||||||
\returns \ref status_codes
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
int16_t begin();
|
int16_t restoreOTAA();
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
\brief Join network by performing over-the-air activation. By this procedure,
|
\brief Join network by performing over-the-air activation. By this procedure,
|
||||||
|
@ -333,7 +337,7 @@ class LoRaWANNode {
|
||||||
\param sNwkSIntKey Pointer to the network session S key (LoRaWAN 1.1), unused for LoRaWAN 1.0.
|
\param sNwkSIntKey Pointer to the network session S key (LoRaWAN 1.1), unused for LoRaWAN 1.0.
|
||||||
\returns \ref status_codes
|
\returns \ref status_codes
|
||||||
*/
|
*/
|
||||||
int16_t beginAPB(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL);
|
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL);
|
||||||
|
|
||||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||||
/*!
|
/*!
|
||||||
|
@ -395,10 +399,12 @@ class LoRaWANNode {
|
||||||
LoRaWANMacCommandQueue_t commandsUp = {
|
LoRaWANMacCommandQueue_t commandsUp = {
|
||||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
||||||
.numCommands = 0,
|
.numCommands = 0,
|
||||||
|
.len = 0,
|
||||||
};
|
};
|
||||||
LoRaWANMacCommandQueue_t commandsDown = {
|
LoRaWANMacCommandQueue_t commandsDown = {
|
||||||
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
.commands = { { .cid = 0, .len = 0, .payload = { 0 }, .repeat = 0, } },
|
||||||
.numCommands = 0,
|
.numCommands = 0,
|
||||||
|
.len = 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
// the following is either provided by the network server (OTAA)
|
// the following is either provided by the network server (OTAA)
|
||||||
|
@ -438,6 +444,9 @@ class LoRaWANNode {
|
||||||
// device status - battery level
|
// device status - battery level
|
||||||
uint8_t battLevel = 0xFF;
|
uint8_t battLevel = 0xFF;
|
||||||
|
|
||||||
|
// indicates whether an uplink has MAC commands as payload
|
||||||
|
bool isMACPayload = false;
|
||||||
|
|
||||||
// method to generate message integrity code
|
// method to generate message integrity code
|
||||||
uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key);
|
uint32_t generateMIC(uint8_t* msg, size_t len, uint8_t* key);
|
||||||
|
|
||||||
|
@ -475,7 +484,10 @@ class LoRaWANNode {
|
||||||
int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue);
|
int16_t pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue);
|
||||||
|
|
||||||
// pop MAC command from queue, done by copy unless CMD is NULL
|
// pop MAC command from queue, done by copy unless CMD is NULL
|
||||||
int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, bool force = false);
|
int16_t popMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue, size_t index);
|
||||||
|
|
||||||
|
// delete a specific MAC command from queue, indicated by the command ID
|
||||||
|
int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue);
|
||||||
|
|
||||||
// execute mac command, return the number of processed bytes for sequential processing
|
// execute mac command, return the number of processed bytes for sequential processing
|
||||||
size_t execMacCommand(LoRaWANMacCommand_t* cmd);
|
size_t execMacCommand(LoRaWANMacCommand_t* cmd);
|
||||||
|
|
Loading…
Add table
Reference in a new issue