[LoRaWAN] improve persistence, better Rx windows, wear leveling, confirmed frames
This commit is contained in:
parent
c8bc157090
commit
7c3670c430
8 changed files with 538 additions and 233 deletions
10
src/Hal.cpp
10
src/Hal.cpp
|
@ -60,14 +60,14 @@ uint32_t RadioLibHal::getPersistentAddr(uint32_t id) {
|
|||
}
|
||||
|
||||
template<typename T>
|
||||
void RadioLibHal::setPersistentParameter(uint32_t id, T val) {
|
||||
void RadioLibHal::setPersistentParameter(uint32_t id, T val, uint32_t offset) {
|
||||
uint8_t *ptr = (uint8_t*)&val;
|
||||
this->writePersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + RadioLibPersistentParamTable[id], ptr, sizeof(T));
|
||||
this->writePersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + RadioLibPersistentParamTable[id] + offset, ptr, sizeof(T));
|
||||
}
|
||||
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint8_t val);
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint16_t val);
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint32_t val);
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint8_t val, uint32_t offset);
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint16_t val, uint32_t offset);
|
||||
template void RadioLibHal::setPersistentParameter(uint32_t id, uint32_t val, uint32_t offset);
|
||||
|
||||
template<typename T>
|
||||
T RadioLibHal::getPersistentParameter(uint32_t id) {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID (1)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION (2)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID (3)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_MAX_ID (4)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID (4)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID (5)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID (6)
|
||||
#define RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID (7)
|
||||
|
@ -38,7 +38,7 @@ static const uint32_t RadioLibPersistentParamTable[] = {
|
|||
0x01, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID
|
||||
0x03, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION
|
||||
0x04, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID
|
||||
0x05, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_MAX_ID
|
||||
0x05, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID
|
||||
0x06, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID
|
||||
0x07, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID
|
||||
0x0A, // RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID
|
||||
|
@ -299,9 +299,10 @@ class RadioLibHal {
|
|||
will be stored in the system endian!
|
||||
\param id Parameter ID to save at.
|
||||
\param val Value to set.
|
||||
\param offset An additional offset added to the address.
|
||||
*/
|
||||
template<typename T>
|
||||
void setPersistentParameter(uint32_t id, T val);
|
||||
void setPersistentParameter(uint32_t id, T val, uint32_t offset = 0);
|
||||
|
||||
/*!
|
||||
\brief Method to get arbitrary parameter from persistent storage.
|
||||
|
|
|
@ -1436,36 +1436,29 @@ uint32_t SX126x::getTimeOnAir(size_t len) {
|
|||
}
|
||||
}
|
||||
|
||||
// Table below defines the size of one symbol as
|
||||
// symtime = 256us * 2^T(sf,bw)
|
||||
// 256us is called one symunit.
|
||||
// SF:
|
||||
// BW: |__7___8___9__10__11__12
|
||||
// 125kHz | 2 3 4 5 6 7
|
||||
// 250kHz | 1 2 3 4 5 6
|
||||
// 500kHz | 0 1 2 3 4 5
|
||||
//
|
||||
uint32_t SX126x::calculateRxTimeout(uint8_t numSymbols, DataRate_t *datarate, uint32_t offsetUs, uint32_t& timeoutUs) {
|
||||
uint8_t exponent = 0;
|
||||
exponent += datarate->lora.spreadingFactor - 7;
|
||||
|
||||
switch((int)datarate->lora.bandwidth) {
|
||||
case 125:
|
||||
exponent += 2;
|
||||
break;
|
||||
case 250:
|
||||
exponent += 1;
|
||||
break;
|
||||
default: // includes 500 kHz
|
||||
exponent += 0;
|
||||
break;
|
||||
}
|
||||
|
||||
timeoutUs = (uint32_t)numSymbols * 256 * (0x01 << exponent) + offsetUs;
|
||||
uint32_t symbolTime = (0x00000001 << datarate->lora.spreadingFactor) * 1000 / datarate->lora.bandwidth;
|
||||
timeoutUs = (uint32_t)numSymbols * symbolTime + offsetUs;
|
||||
uint32_t timeout = timeoutUs / 15.625;
|
||||
return(timeout);
|
||||
}
|
||||
|
||||
bool SX126x::isRxTimeout() {
|
||||
uint16_t irq = getIrqStatus();
|
||||
bool isRxTimeout = false;
|
||||
if(irq & RADIOLIB_SX126X_IRQ_TIMEOUT) {
|
||||
isRxTimeout = true;
|
||||
}
|
||||
return(isRxTimeout);
|
||||
}
|
||||
|
||||
uint16_t SX126x::readIrq(bool clear) {
|
||||
uint16_t irq = getIrqStatus();
|
||||
if(clear)
|
||||
clearIrqStatus();
|
||||
return(irq);
|
||||
}
|
||||
|
||||
int16_t SX126x::implicitHeader(size_t len) {
|
||||
return(setHeaderType(RADIOLIB_SX126X_LORA_HEADER_IMPLICIT, len));
|
||||
}
|
||||
|
|
|
@ -959,6 +959,18 @@ class SX126x: public PhysicalLayer {
|
|||
*/
|
||||
uint32_t calculateRxTimeout(uint8_t numSymbols, DataRate_t *datarate, uint32_t offsetUs, uint32_t& timeoutUss);
|
||||
|
||||
/*!
|
||||
\brief Check whether there is a RxTimeout flag set
|
||||
\returns RxTimeout flag is set
|
||||
*/
|
||||
bool isRxTimeout();
|
||||
|
||||
/*!
|
||||
\brief Check whether there is a RxTimeout flag set
|
||||
\returns RxTimeout flag is set
|
||||
*/
|
||||
uint16_t readIrq(bool clear = false);
|
||||
|
||||
/*!
|
||||
\brief Set implicit header mode for future reception/transmission.
|
||||
\param len Payload length in bytes.
|
||||
|
|
|
@ -39,6 +39,7 @@ LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) {
|
|||
this->rx2 = this->band->rx2;
|
||||
}
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
void LoRaWANNode::wipe() {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->wipePersistentStorage();
|
||||
|
@ -65,27 +66,47 @@ int16_t LoRaWANNode::restore() {
|
|||
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);
|
||||
|
||||
// get session parameters
|
||||
this->rev = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION);
|
||||
RADIOLIB_DEBUG_PRINTLN("LoRaWAN session: v1.%d", this->rev);
|
||||
this->devNonce = mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID);
|
||||
this->joinNonce = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID);
|
||||
|
||||
// get MAC state
|
||||
uint8_t txDrRx2Dr = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID);
|
||||
uint8_t txPwrCurMax = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_MAX_ID);
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = (txDrRx2Dr >> 4) & 0x0F;
|
||||
|
||||
this->txPwrCur = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID);
|
||||
|
||||
uint8_t rx1DrOffDel = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID);
|
||||
uint8_t rx2FreqBuf[3];
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID), rx2FreqBuf, 3);
|
||||
uint32_t rx2Freq = LoRaWANNode::ntoh<uint32_t>(&rx2FreqBuf[0], 3);
|
||||
uint8_t adrLimDel = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID);
|
||||
this->nbTrans = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID);
|
||||
|
||||
this->rx2.drMax = (txDrRx2Dr & 0x0F) >> 0;
|
||||
this->rx1DrOffset = (rx1DrOffDel & 0xF0) >> 4;
|
||||
this->rxDelays[0] = ((rx1DrOffDel & 0x0F) >> 0) * 1000;
|
||||
this->rx1DrOffset = (rx1DrOffDel >> 4) & 0x0F;
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] + this->band->rx1DataRateBase + this->rx1DrOffset;
|
||||
this->rxDelays[0] = ((rx1DrOffDel >> 0) & 0x0F) * 1000;
|
||||
if(this->rxDelays[0] == 0) {
|
||||
this->rxDelays[0] = RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS;
|
||||
}
|
||||
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||
|
||||
uint8_t rx2FreqBuf[3];
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID), rx2FreqBuf, 3);
|
||||
uint32_t rx2Freq = LoRaWANNode::ntoh<uint32_t>(&rx2FreqBuf[0], 3);
|
||||
this->rx2.drMax = (txDrRx2Dr >> 0) & 0x0F;
|
||||
this->rx2.freq = (float)rx2Freq / 10000.0;
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = (txDrRx2Dr & 0xF0) >> 4;
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] + this->band->rx1DataRateBase + this->rx1DrOffset;
|
||||
|
||||
uint8_t adrLimDel = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID);
|
||||
this->adrLimitExp = (adrLimDel >> 4) & 0x0F;
|
||||
this->adrDelayExp = (adrLimDel >> 0) & 0x0F;
|
||||
|
||||
this->nbTrans = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID);
|
||||
|
||||
this->aFcntDown = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID);
|
||||
this->nFcntDown = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID);
|
||||
this->confFcntUp = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_UP_ID);
|
||||
this->confFcntDown = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID);
|
||||
this->adrFcnt = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID);
|
||||
|
||||
// fcntUp is stored in highly efficient wear-leveling system, so parse it as required
|
||||
this->restoreFcntUp();
|
||||
|
||||
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
|
@ -101,30 +122,95 @@ int16_t LoRaWANNode::restore() {
|
|||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::restoreFcntUp() {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
uint8_t fcntBuff[30] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID), fcntBuff, 30);
|
||||
RADIOLIB_DEBUG_HEXDUMP(fcntBuff, 30);
|
||||
|
||||
// copy the two most significant bytes from the first two bytes
|
||||
uint8_t bits_30_22 = fcntBuff[0];
|
||||
uint8_t bits_22_14 = fcntBuff[1];
|
||||
|
||||
// the next 7 bits must be retrieved from the byte to which was written most recently
|
||||
// this is the last byte that has its state bit (most significant bit) set equal to its predecessor
|
||||
// we find the first byte that has its state bit different, and subtract one
|
||||
uint8_t idx = 2;
|
||||
uint8_t state = fcntBuff[idx] >> 7;
|
||||
for(; idx < 5; idx++) {
|
||||
if(fcntBuff[idx] >> 7 != state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t bits_14_7 = fcntBuff[idx-1] & 0x7F;
|
||||
|
||||
// equally, the last 7 bits must be retrieved from the byte to which was written most recently
|
||||
// this is the last byte that has its state bit (most significant bit) set equal to its predecessor
|
||||
// we find the first byte that has its state bit different, and subtract one
|
||||
idx = 5;
|
||||
state = fcntBuff[idx] >> 7;
|
||||
for(; idx < 30; idx++) {
|
||||
if(fcntBuff[idx] >> 7 != state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
uint8_t bits_7_0 = fcntBuff[idx-1] & 0x7F;
|
||||
|
||||
this->fcntUp = (bits_30_22 << 22) | (bits_22_14 << 14) | (bits_14_7 << 7) | bits_7_0;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::restoreChannels() {
|
||||
uint8_t bytesPerChannel = 5;
|
||||
uint8_t numBytes = 2 * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel;
|
||||
uint8_t buffer[numBytes];
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID), buffer, numBytes);
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
memcpy(chBuff, &buffer[(dir * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel) + i * bytesPerChannel], bytesPerChannel);
|
||||
this->availableChannels[dir][i].enabled = (chBuff[0] & 0x80) >> 7;
|
||||
this->availableChannels[dir][i].idx = chBuff[0] & 0x7F;
|
||||
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&chBuff[1], 3);
|
||||
this->availableChannels[dir][i].freq = (float)freq/10000.0;
|
||||
this->availableChannels[dir][i].drMax = (chBuff[0] & 0xF0) >> 4;
|
||||
this->availableChannels[dir][i].drMin = (chBuff[0] & 0x0F) >> 0;
|
||||
}
|
||||
}
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force) {
|
||||
// check if we actually need to send the join request
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
if(!force && (mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
|
||||
// the device has joined already, we can just pull the data from persistent storage
|
||||
return(this->restore());
|
||||
}
|
||||
#endif
|
||||
|
||||
// set the physical layer configuration
|
||||
this->txPwrCur = this->band->powerMax;
|
||||
int16_t state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// get dev nonce from persistent storage and increment it
|
||||
uint16_t devNonce = mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID);
|
||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID, devNonce + 1);
|
||||
|
||||
// setup uplink/downlink frequencies and datarates
|
||||
state = this->selectChannelsJR(devNonce);
|
||||
state = this->selectChannelsJR(this->devNonce);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// configure for uplink with default configuration
|
||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// increment devNonce as we are sending another join-request
|
||||
this->devNonce += 1;
|
||||
|
||||
// build the join-request message
|
||||
uint8_t joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN];
|
||||
|
||||
|
@ -132,7 +218,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
joinRequestMsg[0] = RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_REQUEST | RADIOLIB_LORAWAN_MHDR_MAJOR_R1;
|
||||
LoRaWANNode::hton<uint64_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_JOIN_EUI_POS], joinEUI);
|
||||
LoRaWANNode::hton<uint64_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_EUI_POS], devEUI);
|
||||
LoRaWANNode::hton<uint16_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_NONCE_POS], devNonce);
|
||||
LoRaWANNode::hton<uint16_t>(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_NONCE_POS], this->devNonce);
|
||||
|
||||
// add the authentication code
|
||||
uint32_t mic = this->generateMIC(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN - sizeof(uint32_t), nwkKey);
|
||||
|
@ -141,7 +227,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
// send it
|
||||
state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
|
||||
// configure for downlink with default configuration
|
||||
state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
@ -159,7 +245,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
// start receiving
|
||||
uint32_t start = mod->hal->millis();
|
||||
downlinkReceived = false;
|
||||
state = this->phyLayer->startReceive(0x00, 0b0000001001100010, 0b0000000000000010, 0);
|
||||
state = this->phyLayer->startReceive();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// wait for the reply or timeout
|
||||
|
@ -216,15 +302,15 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:");
|
||||
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((joinNoncePrev > 0) && (joinNonce <= joinNoncePrev)) {
|
||||
// get current JoinNonce from downlink and previous JoinNonce from persistent storage
|
||||
uint32_t joinNonceNew = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3);
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", this->joinNonce, joinNonceNew);
|
||||
// JoinNonce received must be greater than the last JoinNonce heard, else error
|
||||
if((this->joinNonce > 0) && (joinNonceNew <= this->joinNonce)) {
|
||||
return(RADIOLIB_ERR_JOIN_NONCE_INVALID);
|
||||
}
|
||||
this->joinNonce = joinNonceNew;
|
||||
|
||||
// check LoRaWAN revision (the MIC verification depends on this)
|
||||
uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS];
|
||||
|
@ -246,7 +332,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
uint8_t micBuff[3*RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
micBuff[0] = RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE;
|
||||
LoRaWANNode::hton<uint64_t>(&micBuff[1], joinEUI);
|
||||
LoRaWANNode::hton<uint16_t>(&micBuff[9], devNonce);
|
||||
LoRaWANNode::hton<uint16_t>(&micBuff[9], this->devNonce);
|
||||
memcpy(&micBuff[11], joinAcceptMsg, lenRx);
|
||||
|
||||
if(!verifyMIC(micBuff, lenRx + 11, this->jSIntKey)) {
|
||||
|
@ -262,7 +348,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
}
|
||||
|
||||
// parse other contents
|
||||
uint32_t homeNetId = LoRaWANNode::ntoh<uint32_t>(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3);
|
||||
this->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]);
|
||||
|
||||
// parse Rx1 delay (and subsequently Rx2)
|
||||
|
@ -280,7 +366,6 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
} else {
|
||||
this->setupChannels(nullptr);
|
||||
}
|
||||
this->saveChannels();
|
||||
|
||||
// prepare buffer for key derivation
|
||||
uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
|
@ -290,7 +375,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
if(this->rev == 1) {
|
||||
// 1.1 version, derive the keys
|
||||
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], this->devNonce);
|
||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
|
||||
|
||||
RadioLibAES128Instance.init(appKey);
|
||||
|
@ -320,8 +405,8 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
|
||||
} else {
|
||||
// 1.0 version, just derive the keys
|
||||
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<uint32_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], this->homeNetId, 3);
|
||||
LoRaWANNode::hton<uint16_t>(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], this->devNonce);
|
||||
keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY;
|
||||
RadioLibAES128Instance.init(nwkKey);
|
||||
RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->appSKey);
|
||||
|
@ -334,56 +419,49 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe
|
|||
memcpy(this->nwkSEncKey, this->fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
|
||||
}
|
||||
|
||||
// store session configuration
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION, this->rev);
|
||||
uint8_t txDrRx2Dr = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4) | this->rx2.drMax;
|
||||
uint8_t txPwrCurMax;
|
||||
uint8_t rx1DrOffDel = (this->rx1DrOffset << 4) | (this->rxDelays[0] / 1000);
|
||||
uint32_t rx2Freq = uint32_t(this->rx2.freq * 10000);
|
||||
uint8_t rx2FreqBuf[3];
|
||||
LoRaWANNode::hton<uint32_t>(&rx2FreqBuf[0], rx2Freq, 3);
|
||||
uint8_t adrLimDel = (RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP << 4) | (RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP << 0);
|
||||
uint8_t nbTrans = (0x01 << 0);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID, txDrRx2Dr);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_MAX_ID, txPwrCurMax);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID, rx1DrOffDel);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID), rx2FreqBuf, 3);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID, adrLimDel);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID, nbTrans);
|
||||
|
||||
// save the device address & keys
|
||||
// reset all frame counters
|
||||
this->fcntUp = 0;
|
||||
this->aFcntDown = 0;
|
||||
this->nFcntDown = 0;
|
||||
this->confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
this->adrFcnt = 0;
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
// save the device address & keys as well as JoinAccept values; these are only ever set when joining
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID, this->devAddr);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, 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);
|
||||
|
||||
// save uplink parameters
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID, homeNetId);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID, joinNonce);
|
||||
// save join-request parameters
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_HOME_NET_ID, this->homeNetId);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_JOIN_NONCE_ID, this->joinNonce);
|
||||
|
||||
// all complete, reset all frame counters and set the magic number
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_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_CONF_FCNT_UP_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, 0);
|
||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
this->saveSession();
|
||||
this->saveChannels();
|
||||
|
||||
// everything written to NVM, write current table version to NVM
|
||||
// everything written to NVM, write current table version to persistent storage and set magic number
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION);
|
||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
#endif
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, bool force) {
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
// check if we actually need to restart from a clean session
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
if(!force && (mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID) == RADIOLIB_LORAWAN_MAGIC)) {
|
||||
// the device has joined already, we can just pull the data from persistent storage
|
||||
return(this->restore());
|
||||
}
|
||||
#endif
|
||||
|
||||
this->devAddr = addr;
|
||||
memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE);
|
||||
|
@ -398,6 +476,7 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
|||
}
|
||||
|
||||
// set the physical layer configuration
|
||||
this->txPwrCur = this->band->powerMax;
|
||||
int16_t state = this->setPhyProperties();
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
|
@ -405,24 +484,185 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey,
|
|||
state = this->setupChannels(nullptr);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
// everything written to NVM, write current version to NVM
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
// save the device address & keys
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_ADDR_ID, this->devAddr);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, 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);
|
||||
|
||||
this->saveSession();
|
||||
this->saveChannels();
|
||||
|
||||
// everything written to NVM, write current table version to persistent storage and set magic number
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION_ID, RADIOLIB_PERSISTENT_PARAM_LORAWAN_TABLE_VERSION);
|
||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_MAGIC_ID, RADIOLIB_LORAWAN_MAGIC);
|
||||
#endif
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
int16_t LoRaWANNode::saveSession() {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
// store session configuration (MAC commands)
|
||||
if(mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION) != this->rev)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_VERSION, this->rev);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID) != this->devNonce)
|
||||
mod->hal->setPersistentParameter<uint16_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_DEV_NONCE_ID, this->devNonce);
|
||||
|
||||
uint8_t txDrRx2Dr = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4) | this->rx2.drMax;
|
||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID) != txDrRx2Dr)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID, txDrRx2Dr);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID) != this->txPwrCur)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXPWR_CUR_ID, this->txPwrCur);
|
||||
|
||||
uint8_t rx1DrOffDel = (this->rx1DrOffset << 4) | (this->rxDelays[0] / 1000);
|
||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID) != rx1DrOffDel)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID, rx1DrOffDel);
|
||||
|
||||
uint8_t rx2FreqBuf[3];
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID), rx2FreqBuf, 3);
|
||||
uint32_t rx2Freq = LoRaWANNode::ntoh<uint32_t>(&rx2FreqBuf[0], 3);
|
||||
if(rx2Freq != uint32_t(this->rx2.freq * 10000)) {
|
||||
rx2Freq = uint32_t(this->rx2.freq * 10000);
|
||||
LoRaWANNode::hton<uint32_t>(&rx2FreqBuf[0], rx2Freq, 3);
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX2FREQ_ID), rx2FreqBuf, 3);
|
||||
}
|
||||
|
||||
uint8_t adrLimDel = (this->adrLimitExp << 4) | (this->adrDelayExp << 0);
|
||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID) != adrLimDel)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID, adrLimDel);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID) != this->nbTrans)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_NBTRANS_ID, this->nbTrans);
|
||||
|
||||
// store all frame counters
|
||||
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID) != this->aFcntDown)
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_A_FCNT_DOWN_ID, this->aFcntDown);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID) != this->nFcntDown)
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID, this->nFcntDown);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_UP_ID) != this->confFcntUp)
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_UP_ID, this->confFcntUp);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID) != this->confFcntDown)
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_CONF_FCNT_DOWN_ID, this->confFcntDown);
|
||||
|
||||
if(mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID) != this->adrFcnt)
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID, this->adrFcnt);
|
||||
|
||||
// fcntUp is saved using highly efficient wear-leveling as this is by far going to be written most often
|
||||
this->saveFcntUp();
|
||||
|
||||
// if there is, or was, any MAC command in the queue, overwrite with the current MAC queue
|
||||
uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
LoRaWANMacCommandQueue_t cmdTemp;
|
||||
memcpy(&cmdTemp, queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
if(this->commandsUp.numCommands > 0 || cmdTemp.numCommands > 0) {
|
||||
memcpy(queueBuff, &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t));
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FOPTS_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t));
|
||||
}
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::saveFcntUp() {
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
|
||||
uint8_t fcntBuff[30] = { 0 };
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID), fcntBuff, 30);
|
||||
RADIOLIB_DEBUG_HEXDUMP(fcntBuff, 30);
|
||||
|
||||
// we discard the first two bits - your flash will likely be far dead by the time you reach 2^30 uplinks
|
||||
// the first two bytes of the remaining 30 bytes are stored straight into storage without additional wear leveling
|
||||
// because they hardly ever change
|
||||
uint8_t bits_30_22 = (uint8_t)(this->fcntUp >> 22);
|
||||
if(fcntBuff[0] != bits_30_22)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, bits_30_22, 0);
|
||||
uint8_t bits_22_14 = (uint8_t)(this->fcntUp >> 14);
|
||||
if(fcntBuff[1] != bits_22_14)
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, bits_22_14, 1);
|
||||
|
||||
// the next 7 bits are stored into one of few indices
|
||||
// this index is indicated by the first byte that has its state (most significant bit) different from its predecessor
|
||||
// if all have an equal state, restart from the beginning
|
||||
// always flip the state bit of the byte that we write to, to indicate that this is the most recently written byte
|
||||
uint8_t idx = 2;
|
||||
uint8_t state = fcntBuff[idx] >> 7;
|
||||
for(; idx < 5; idx++) {
|
||||
if(fcntBuff[idx] >> 7 != state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
idx = idx < 5 ? idx : 2;
|
||||
uint8_t bits_14_7 = (this->fcntUp >> 7) & 0x7F;
|
||||
|
||||
// flip the first bit of this byte to indicate that we just wrote here
|
||||
bits_14_7 |= (~(fcntBuff[idx] >> 7)) << 7;
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, bits_14_7, idx);
|
||||
|
||||
// equally, the last 7 bits are stored into one of many indices
|
||||
// this index is indicated by the first byte that has its state (most significant bit) different from its predecessor
|
||||
// if all have an equal state, restart from the beginning
|
||||
// always flip the state bit of the byte that we write to, to indicate that this is the most recently written byte
|
||||
idx = 5;
|
||||
state = fcntBuff[idx] >> 7;
|
||||
for(; idx < 30; idx++) {
|
||||
if(fcntBuff[idx] >> 7 != state) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
idx = idx < 30 ? idx : 5;
|
||||
uint8_t bits_7_0 = (this->fcntUp >> 0) & 0x7F;
|
||||
|
||||
// flip the first bit of this byte to indicate that we just wrote here
|
||||
bits_7_0 |= (~(fcntBuff[idx] >> 7)) << 7;
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, bits_7_0, idx);
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::saveChannels() {
|
||||
uint8_t bytesPerChannel = 5;
|
||||
uint8_t numBytes = 2 * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel;
|
||||
uint8_t buffer[numBytes];
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
chBuff[0] |= (uint8_t)this->availableChannels[dir][i].enabled << 7;
|
||||
chBuff[0] |= this->availableChannels[dir][i].idx;
|
||||
uint32_t freq = this->availableChannels[dir][i].freq*10000.0;
|
||||
LoRaWANNode::hton<uint32_t>(&chBuff[1], freq, 3);
|
||||
chBuff[4] = this->availableChannels[dir][i].drMax << 4;
|
||||
chBuff[4] |= this->availableChannels[dir][i].drMin << 0;
|
||||
memcpy(&buffer[(dir * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel) + i * bytesPerChannel], chBuff, bytesPerChannel);
|
||||
}
|
||||
}
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID), buffer, numBytes);
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
#endif // RADIOLIB_EEPROM_UNSUPPORTED
|
||||
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
int16_t LoRaWANNode::uplink(String& str, uint8_t port) {
|
||||
return(this->uplink(str.c_str(), port));
|
||||
int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed, bool adrEnabled) {
|
||||
return(this->uplink(str.c_str(), port, isConfirmed, adrEnabled));
|
||||
}
|
||||
#endif
|
||||
|
||||
int16_t LoRaWANNode::uplink(const char* str, uint8_t port) {
|
||||
return(this->uplink((uint8_t*)str, strlen(str), port));
|
||||
int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed, bool adrEnabled) {
|
||||
return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed, adrEnabled));
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
||||
bool adrEn = true;
|
||||
int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed, bool adrEnabled) {
|
||||
// check destination port
|
||||
if(port > 0xDF) {
|
||||
return(RADIOLIB_ERR_INVALID_PORT);
|
||||
|
@ -452,26 +692,47 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
return(RADIOLIB_ERR_PACKET_TOO_LONG);
|
||||
}
|
||||
|
||||
// get frame counter from persistent storage
|
||||
uint32_t fcnt = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID) + 1;
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID, fcnt);
|
||||
uint32_t adrFcnt = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID);
|
||||
uint8_t adrParams = mod->hal->getPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID);
|
||||
uint32_t adrLimit = 0x01 << ((adrParams & 0xF0) >> 4);
|
||||
uint32_t adrDelay = 0x01 << ((adrParams & 0x0F) >> 0);
|
||||
bool adrAckReq = false;
|
||||
if((fcnt - adrFcnt) == adrLimit) {
|
||||
adrAckReq = true;
|
||||
// add MAC command to queue
|
||||
} else if ((fcnt - adrFcnt) == (adrLimit + adrDelay)) {
|
||||
// set TX power to max
|
||||
// increase frame counter by one
|
||||
this->fcntUp += 1;
|
||||
|
||||
// decrease DR if possible
|
||||
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]--;
|
||||
} else {
|
||||
// enable all channels
|
||||
// check if we need to do ADR stuff
|
||||
uint32_t adrLimit = 0x01 << this->adrLimitExp;
|
||||
uint32_t adrDelay = 0x01 << this->adrDelayExp;
|
||||
bool adrAckReq = false;
|
||||
if((this->fcntUp - this->adrFcnt) >= adrLimit) {
|
||||
adrAckReq = true;
|
||||
} else if ((this->fcntUp - this->adrFcnt) == (adrLimit + adrDelay)) {
|
||||
// try one of three, in order: set TxPower to max, set DR to min, enable all defined channels
|
||||
|
||||
// set the maximum power supported by both the module and the band
|
||||
int8_t pwr = this->band->powerMax;
|
||||
int16_t state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
// go from the highest power in band and lower it until we hit one supported by the module
|
||||
state = this->phyLayer->setOutputPower(pwr--);
|
||||
}
|
||||
RADIOLIB_ASSERT(state);
|
||||
if(pwr == this->txPwrCur) {
|
||||
|
||||
// failed to increase Tx power, so try to decrease the datarate
|
||||
if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].drMin) {
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]--;
|
||||
} else {
|
||||
|
||||
// failed to decrease datarate, so enable all available channels
|
||||
for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) {
|
||||
this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
this->txPwrCur = pwr;
|
||||
}
|
||||
|
||||
// we tried something to improve the range, so increase the ADR frame counter by 'ADR delay'
|
||||
this->adrFcnt += adrDelay;
|
||||
}
|
||||
|
||||
// configure for uplink
|
||||
|
@ -495,19 +756,30 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
#endif
|
||||
|
||||
// set the packet fields
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_UP | RADIOLIB_LORAWAN_MHDR_MAJOR_R1;
|
||||
if(isConfirmed) {
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_UP;
|
||||
this->confFcntUp = this->fcntUp;
|
||||
} else {
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_UP;
|
||||
}
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] |= RADIOLIB_LORAWAN_MHDR_MAJOR_R1;
|
||||
LoRaWANNode::hton<uint32_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS], this->devAddr);
|
||||
|
||||
// length of fopts will be added later
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] = 0x00;
|
||||
if(adrEn) {
|
||||
if(adrEnabled) {
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ENABLED;
|
||||
if(adrAckReq) {
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ACK_REQ;
|
||||
}
|
||||
}
|
||||
|
||||
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)fcnt);
|
||||
// if the saved confirm-fcnt is equal to the last app-downlink, set the ACK bit and clear the confirm-fcnt
|
||||
if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) {
|
||||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK;
|
||||
}
|
||||
|
||||
LoRaWANNode::hton<uint16_t>(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)this->fcntUp);
|
||||
|
||||
// check if we have some MAC commands to append
|
||||
if(foptsLen > 0) {
|
||||
|
@ -534,12 +806,8 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen;
|
||||
|
||||
// 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], this->fcntUp, 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
|
||||
|
@ -552,15 +820,17 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
}
|
||||
|
||||
// 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)], this->fcntUp, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true);
|
||||
|
||||
// create blocks for MIC calculation
|
||||
uint8_t block0[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
block0[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_MIC_BLOCK_MAGIC;
|
||||
if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) {
|
||||
LoRaWANNode::hton<uint16_t>(&block0[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntDown);
|
||||
}
|
||||
block0[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK;
|
||||
LoRaWANNode::hton<uint32_t>(&block0[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr);
|
||||
LoRaWANNode::hton<uint32_t>(&block0[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt);
|
||||
LoRaWANNode::hton<uint32_t>(&block0[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], this->fcntUp);
|
||||
block0[RADIOLIB_LORAWAN_MIC_BLOCK_LEN_POS] = uplinkMsgLen - RADIOLIB_AES128_BLOCK_SIZE - sizeof(uint32_t);
|
||||
|
||||
uint8_t block1[RADIOLIB_AES128_BLOCK_SIZE] = { 0 };
|
||||
|
@ -600,6 +870,10 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port) {
|
|||
|
||||
// set the timestamp so that we can measure when to start receiving
|
||||
this->rxDelayStart = txStart + timeOnAir;
|
||||
|
||||
// the downlink confirmation was transmitted, so clear the counter value
|
||||
this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
@ -629,7 +903,7 @@ int16_t LoRaWANNode::downlink(String& str) {
|
|||
int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
||||
// check if there are any upcoming Rx windows
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
const uint32_t scanGuard = 50;
|
||||
const uint32_t scanGuard = 10;
|
||||
if(mod->hal->millis() - this->rxDelayStart > (this->rxDelays[1] + scanGuard)) {
|
||||
// time since last Tx is greater than RX2 delay + some guard period
|
||||
// we have nothing to downlink
|
||||
|
@ -648,7 +922,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
|
||||
downlinkTimeout = false;
|
||||
downlinkReceived = false;
|
||||
this->phyLayer->setChannelScanAction(LoRaWANNodeOnChannelScan);
|
||||
// this->phyLayer->setChannelScanAction(LoRaWANNodeOnChannelScan);
|
||||
|
||||
// calculate the Rx timeout
|
||||
// according to the spec, this must be at least enough time to effectively detect a preamble
|
||||
|
@ -656,7 +930,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
DataRate_t dataRate;
|
||||
findDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK], &dataRate);
|
||||
uint32_t timeoutHost = 0;
|
||||
uint32_t timeoutMod = this->phyLayer->calculateRxTimeout(8, &dataRate, 2*scanGuard*1000, timeoutHost);
|
||||
uint32_t timeoutMod = this->phyLayer->calculateRxTimeout(23, &dataRate, 2*scanGuard*1000, timeoutHost);
|
||||
RADIOLIB_DEBUG_PRINTLN("RxTimeout host: %d, module: %06x", timeoutHost, timeoutMod);
|
||||
|
||||
// perform listening in the two Rx windows
|
||||
|
@ -668,9 +942,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
if(waitLen > scanGuard) {
|
||||
waitLen -= scanGuard;
|
||||
}
|
||||
RADIOLIB_DEBUG_PRINTLN("Waiting for %d ms...", waitLen);
|
||||
mod->hal->delay(waitLen);
|
||||
RADIOLIB_DEBUG_PRINTLN("Opening Rx%d window...", (i+1));
|
||||
|
||||
// TODO somehow make this module-independent, currently configured for SX126x
|
||||
uint16_t irqFlags = 0b0000000000000010; // RxDone
|
||||
|
@ -681,10 +953,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
uint32_t rxStart = mod->hal->millis();
|
||||
this->phyLayer->startReceive(timeoutMod, irqFlags, irqMask, 0);
|
||||
RADIOLIB_DEBUG_PRINT("Opened Rx%d window (%d us timeout)... ", i+1, timeoutHost);
|
||||
while(!downlinkTimeout && ((mod->hal->millis() - rxStart) < (timeoutHost / 1000))) {
|
||||
mod->hal->yield();
|
||||
}
|
||||
mod->hal->delay(timeoutHost / 1000 + scanGuard);
|
||||
RADIOLIB_DEBUG_PRINTLN("closing");
|
||||
if(this->phyLayer->isRxTimeout()) {
|
||||
downlinkTimeout = true;
|
||||
}
|
||||
|
||||
// check if we detected something of a packet
|
||||
if(!downlinkTimeout) {
|
||||
|
@ -700,12 +973,12 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
findDataRate(this->rx2.drMax, &dataRate);
|
||||
state = this->phyLayer->setDataRate(dataRate);
|
||||
RADIOLIB_ASSERT(state);
|
||||
timeoutMod = this->phyLayer->calculateRxTimeout(8, &dataRate, 2*scanGuard*1000, timeoutHost);
|
||||
timeoutMod = this->phyLayer->calculateRxTimeout(22, &dataRate, 2*scanGuard*1000, timeoutHost);
|
||||
downlinkTimeout = false;
|
||||
}
|
||||
|
||||
}
|
||||
this->phyLayer->clearChannelScanAction();
|
||||
// this->phyLayer->clearChannelScanAction();
|
||||
|
||||
// check if we received a packet at all
|
||||
if(downlinkTimeout) {
|
||||
|
@ -716,9 +989,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
|
||||
return(RADIOLIB_ERR_RX_TIMEOUT);
|
||||
}
|
||||
this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlink);
|
||||
|
||||
while(!downlinkReceived) {
|
||||
// this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlink);
|
||||
|
||||
// while(!downlinkReceived) {
|
||||
while(!this->phyLayer->readIrq()) {
|
||||
mod->hal->yield();
|
||||
}
|
||||
|
||||
|
@ -773,9 +1048,16 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
}
|
||||
|
||||
// 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);
|
||||
|
||||
// if this downlink is confirming an uplink, its MIC was generated with the least-significant 16 bits of that fcntUp
|
||||
// TODO get this to the user somehow
|
||||
bool isConfirmingUp = false;
|
||||
if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FCTRL_ACK) && (this->rev == 1)) {
|
||||
isConfirmingUp = true;
|
||||
LoRaWANNode::hton<uint16_t>(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp);
|
||||
}
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("downlinkMsg:");
|
||||
RADIOLIB_DEBUG_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen);
|
||||
|
@ -792,9 +1074,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
// 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);
|
||||
fcntDownPrev = this->aFcntDown;
|
||||
} else {
|
||||
fcntDownPrev = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_N_FCNT_DOWN_ID);
|
||||
fcntDownPrev = this->nFcntDown;
|
||||
}
|
||||
|
||||
RADIOLIB_DEBUG_PRINTLN("fcnt: %d, fcntPrev: %d, isAppDownlink: %d", fcnt16, fcntDownPrev, (int)isAppDownlink);
|
||||
|
@ -819,13 +1101,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) {
|
||||
#if !defined(RADIOLIB_STATIC_ONLY)
|
||||
|
@ -833,6 +1108,19 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
#endif
|
||||
return(RADIOLIB_ERR_CRC_MISMATCH);
|
||||
}
|
||||
|
||||
// save current fcnt to respective frame counter
|
||||
if (isAppDownlink) {
|
||||
this->aFcntDown = fcnt32;
|
||||
} else {
|
||||
this->nFcntDown = fcnt32;
|
||||
}
|
||||
|
||||
// if this is a confirmed frame, save the downlink number (only app frames can be confirmed)
|
||||
bool isConfirmedDown = false;
|
||||
if(downlinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] && 0xFE == RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_DOWN) {
|
||||
this->confFcntDown = this->aFcntDown;
|
||||
}
|
||||
|
||||
// check the address
|
||||
uint32_t addr = LoRaWANNode::ntoh<uint32_t>(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS]);
|
||||
|
@ -900,17 +1188,16 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len) {
|
|||
|
||||
this->isMACPayload = true;
|
||||
this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND);
|
||||
uint8_t strDown[this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]];
|
||||
size_t lenDown = 0;
|
||||
state = this->downlink(strDown, &lenDown);
|
||||
RADIOLIB_ASSERT(state);
|
||||
}
|
||||
|
||||
// write the 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));
|
||||
}
|
||||
|
||||
// a downlink was received, so reset the ADR counter to this uplink's fcnt
|
||||
uint32_t fcntUp = mod->hal->getPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FCNT_UP_ID);
|
||||
mod->hal->setPersistentParameter<uint32_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_FCNT_ID, fcntUp);
|
||||
this->adrFcnt = this->fcntUp;
|
||||
|
||||
// process payload (if there is any)
|
||||
if(payLen <= 0) {
|
||||
|
@ -989,13 +1276,13 @@ int16_t LoRaWANNode::setPhyProperties() {
|
|||
// RADIOLIB_ASSERT(state);
|
||||
|
||||
// set the maximum power supported by both the module and the band
|
||||
int8_t pwr = this->band->powerMax;
|
||||
state = RADIOLIB_ERR_INVALID_OUTPUT_POWER;
|
||||
while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) {
|
||||
// go from the highest power in band and lower it until we hit one supported by the module
|
||||
state = this->phyLayer->setOutputPower(pwr--);
|
||||
state = this->phyLayer->setOutputPower(this->txPwrCur--);
|
||||
}
|
||||
RADIOLIB_ASSERT(state);
|
||||
this->txPwrCur++;
|
||||
|
||||
uint8_t syncWord[3] = { 0 };
|
||||
uint8_t syncWordLen = 0;
|
||||
|
@ -1109,7 +1396,6 @@ int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce) {
|
|||
|
||||
// configure data rates for TX and RX1: for TX the (floored) average of min and max; for RX1 identical with base offset
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = int((channelUp.drMax + channelUp.drMin) / 2);
|
||||
// TODO check bounds
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK],
|
||||
this->rx1DrOffset, this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax);
|
||||
} else { // RADIOLIB_LORAWAN_CFLIST_TYPE_MASK
|
||||
|
@ -1133,16 +1419,12 @@ int16_t LoRaWANNode::selectChannelsJR(uint16_t devNonce) {
|
|||
|
||||
// configure data rates for TX and RX1: for TX the specified value for this band; for RX1 identical with base offset
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = this->band->txSpans[spanID].joinRequestDataRate;
|
||||
// TODO check bounds
|
||||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK],
|
||||
this->rx1DrOffset, this->band->rx1DataRateBase, channelDn.drMin, channelDn.drMax);
|
||||
}
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = channelUp;
|
||||
this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDn;
|
||||
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
uint8_t txDrRx2Dr = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4) | this->rx2.drMax;
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID, txDrRx2Dr);
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
|
@ -1216,7 +1498,6 @@ int16_t LoRaWANNode::configureChannel(uint8_t dir) {
|
|||
// set the frequency
|
||||
RADIOLIB_DEBUG_PRINTLN("");
|
||||
RADIOLIB_DEBUG_PRINTLN("Channel frequency %cL = %f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq);
|
||||
RADIOLIB_DEBUG_PRINTLN("Datarate index: %d", this->dataRates[dir]);
|
||||
int state = this->phyLayer->setFrequency(this->currentChannels[dir].freq);
|
||||
RADIOLIB_ASSERT(state);
|
||||
|
||||
|
@ -1228,48 +1509,6 @@ int16_t LoRaWANNode::configureChannel(uint8_t dir) {
|
|||
return(state);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::saveChannels() {
|
||||
uint8_t bytesPerChannel = 5;
|
||||
uint8_t numBytes = 2 * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel;
|
||||
uint8_t buffer[numBytes];
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
chBuff[0] |= (uint8_t)this->availableChannels[dir][i].enabled << 7;
|
||||
chBuff[0] |= this->availableChannels[dir][i].idx;
|
||||
uint32_t freq = this->availableChannels[dir][i].freq*10000.0;
|
||||
LoRaWANNode::hton<uint32_t>(&chBuff[1], freq, 3);
|
||||
chBuff[4] = this->availableChannels[dir][i].drMax << 4;
|
||||
chBuff[4] |= this->availableChannels[dir][i].drMin << 0;
|
||||
memcpy(&buffer[(dir * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel) + i * bytesPerChannel], chBuff, bytesPerChannel);
|
||||
}
|
||||
}
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID), buffer, numBytes);
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::restoreChannels() {
|
||||
uint8_t bytesPerChannel = 5;
|
||||
uint8_t numBytes = 2 * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel;
|
||||
uint8_t buffer[numBytes];
|
||||
Module* mod = this->phyLayer->getMod();
|
||||
mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_PERSISTENT_PARAM_LORAWAN_FREQS_ID), buffer, numBytes);
|
||||
for(uint8_t dir = 0; dir < 2; dir++) {
|
||||
for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) {
|
||||
uint8_t chBuff[5] = { 0 };
|
||||
memcpy(chBuff, &buffer[(dir * RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * bytesPerChannel) + i * bytesPerChannel], bytesPerChannel);
|
||||
this->availableChannels[dir][i].enabled = (chBuff[0] & 0x80) >> 7;
|
||||
this->availableChannels[dir][i].idx = chBuff[0] & 0x7F;
|
||||
uint32_t freq = LoRaWANNode::ntoh<uint32_t>(&chBuff[1], 3);
|
||||
this->availableChannels[dir][i].freq = (float)freq/10000.0;
|
||||
this->availableChannels[dir][i].drMax = (chBuff[0] & 0xF0) >> 4;
|
||||
this->availableChannels[dir][i].drMin = (chBuff[0] & 0x0F) >> 0;
|
||||
}
|
||||
}
|
||||
return(RADIOLIB_ERR_NONE);
|
||||
}
|
||||
|
||||
int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue) {
|
||||
if(queue->numCommands >= RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE) {
|
||||
return(RADIOLIB_ERR_COMMAND_QUEUE_FULL);
|
||||
|
@ -1355,6 +1594,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
|
||||
case(RADIOLIB_LORAWAN_MAC_CMD_LINK_ADR): {
|
||||
// get the ADR configuration
|
||||
// TODO all these configuration should only be set if all ACKs are set, otherwise retain previous state
|
||||
uint8_t dr = (cmd->payload[0] & 0xF0) >> 4;
|
||||
uint8_t txPower = cmd->payload[0] & 0x0F;
|
||||
uint16_t chMask = LoRaWANNode::ntoh<uint16_t>(&cmd->payload[1]);
|
||||
|
@ -1375,8 +1615,6 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax;
|
||||
}
|
||||
|
||||
uint8_t txDrRx2Dr = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4) | this->rx2.drMax;
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID, txDrRx2Dr);
|
||||
drAck = 1;
|
||||
}
|
||||
|
||||
|
@ -1390,6 +1628,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
RADIOLIB_DEBUG_PRINTLN("ADR set pwr = %d", pwr);
|
||||
pwrAck = 1;
|
||||
}
|
||||
this->txPwrCur = pwr;
|
||||
}
|
||||
|
||||
this->nbTrans = nbTrans;
|
||||
|
@ -1449,12 +1688,7 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq);
|
||||
}
|
||||
|
||||
// update saved values
|
||||
uint8_t txDrRx2Dr = (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4) | this->rx2.drMax;
|
||||
uint8_t rx1DrOffDel = (this->rx1DrOffset << 4) | (this->rxDelays[0] / 1000);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_TXDR_RX2DR_ID, txDrRx2Dr);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID, rx1DrOffDel);
|
||||
|
||||
// TODO this should be sent repeatedly until the next downlink
|
||||
// send the reply
|
||||
cmd->len = 1;
|
||||
cmd->payload[0] = (rx1OffsAck << 2) | (rx2Ack << 1) | (chanAck << 0);
|
||||
|
@ -1508,8 +1742,10 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
// update saved frequencies
|
||||
this->saveChannels();
|
||||
#endif
|
||||
|
||||
// send the reply
|
||||
cmd->len = 1;
|
||||
|
@ -1546,9 +1782,11 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
}
|
||||
}
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
// update saved frequencies
|
||||
this->saveChannels();
|
||||
|
||||
#endif
|
||||
|
||||
// send the reply
|
||||
cmd->len = 1;
|
||||
cmd->payload[0] = (freqUlAck << 1) | (freqDlAck << 0);
|
||||
|
@ -1570,10 +1808,6 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
this->rxDelays[0] = delay * 1000;
|
||||
this->rxDelays[1] = this->rxDelays[0] + 1000;
|
||||
|
||||
// update saved values
|
||||
uint8_t rx1DrOffDel = (this->rx1DrOffset << 4) | (this->rxDelays[0] / 1000);
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_RX1_DROFF_DEL_ID, rx1DrOffDel);
|
||||
|
||||
// send the reply
|
||||
cmd->len = 0;
|
||||
|
||||
|
@ -1615,9 +1849,6 @@ size_t LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) {
|
|||
uint8_t limitExp = (cmd->payload[0] & 0xF0) >> 4;
|
||||
uint8_t delayExp = cmd->payload[0] & 0x0F;
|
||||
RADIOLIB_DEBUG_PRINTLN("ADR param setup: limitExp = %d, delayExp = %d", limitExp, delayExp);
|
||||
|
||||
// update saved values
|
||||
mod->hal->setPersistentParameter<uint8_t>(RADIOLIB_PERSISTENT_PARAM_LORAWAN_ADR_LIM_DEL_ID, cmd->payload[0]);
|
||||
|
||||
return(1);
|
||||
} break;
|
||||
|
|
|
@ -135,6 +135,7 @@
|
|||
|
||||
// payload encryption/MIC blocks common layout
|
||||
#define RADIOLIB_LORAWAN_BLOCK_MAGIC_POS (0)
|
||||
#define RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS (1)
|
||||
#define RADIOLIB_LORAWAN_BLOCK_DIR_POS (5)
|
||||
#define RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS (6)
|
||||
#define RADIOLIB_LORAWAN_BLOCK_FCNT_POS (10)
|
||||
|
@ -171,6 +172,9 @@
|
|||
#define RADIOLIB_LORAWAN_MAC_CMD_REJOIN_PARAM_SETUP (0x0F)
|
||||
#define RADIOLIB_LORAWAN_MAC_CMD_PROPRIETARY (0x80)
|
||||
|
||||
// unused frame counter value
|
||||
#define RADIOLIB_LORAWAN_FCNT_NONE (0xFFFFFFFF)
|
||||
|
||||
// the length of internal MAC command queue - hopefully this is enough for most use cases
|
||||
#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (8)
|
||||
|
||||
|
@ -334,9 +338,6 @@ class LoRaWANNode {
|
|||
// RX2 channel properties - may be changed by MAC command
|
||||
LoRaWANChannel_t rx2;
|
||||
|
||||
// Number of allowed frame retransmissions
|
||||
uint8_t nbTrans;
|
||||
|
||||
/*!
|
||||
\brief Default constructor.
|
||||
\param phy Pointer to the PhysicalLayer radio module.
|
||||
|
@ -344,6 +345,7 @@ class LoRaWANNode {
|
|||
*/
|
||||
LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band);
|
||||
|
||||
#if !defined(RADIOLIB_EEPROM_UNSUPPORTED)
|
||||
/*!
|
||||
\brief Wipe internal persistent parameters.
|
||||
This will reset all counters and saved variables, so the device will have to rejoin the network.
|
||||
|
@ -356,6 +358,14 @@ class LoRaWANNode {
|
|||
*/
|
||||
int16_t restore();
|
||||
|
||||
/*!
|
||||
\brief Restore frame counter for uplinks from persistent storage.
|
||||
Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t restoreFcntUp();
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Join network by performing over-the-air activation. By this procedure,
|
||||
the device will perform an exchange with the network server and set all necessary configuration.
|
||||
|
@ -381,32 +391,52 @@ class LoRaWANNode {
|
|||
*/
|
||||
int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL, bool force = false);
|
||||
|
||||
/*!
|
||||
\brief Save the current state of the session.
|
||||
All variables are compared to what is saved and only the differences are rewritten.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t saveSession();
|
||||
|
||||
/*!
|
||||
\brief Save the current uplink frame counter.
|
||||
Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t saveFcntUp();
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
\brief Send a message to the server.
|
||||
\param str Address of Arduino String that will be transmitted.
|
||||
\param port Port number to send the message to.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\param adrEnabled Whether ADR is enabled or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t uplink(String& str, uint8_t port);
|
||||
int16_t uplink(String& str, uint8_t port, bool isConfirmed = false, bool adrEnabled = true);
|
||||
#endif
|
||||
|
||||
/*!
|
||||
\brief Send a message to the server.
|
||||
\param str C-string that will be transmitted.
|
||||
\param port Port number to send the message to.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\param adrEnabled Whether ADR is enabled or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t uplink(const char* str, uint8_t port);
|
||||
int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false, bool adrEnabled = true);
|
||||
|
||||
/*!
|
||||
\brief Send a message to the server.
|
||||
\param data Data to send.
|
||||
\param len Length of the data.
|
||||
\param port Port number to send the message to.
|
||||
\param isConfirmed Whether to send a confirmed uplink or not.
|
||||
\param adrEnabled Whether ADR is enabled or not.
|
||||
\returns \ref status_codes
|
||||
*/
|
||||
int16_t uplink(uint8_t* data, size_t len, uint8_t port);
|
||||
int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false, bool adrEnabled = true);
|
||||
|
||||
#if defined(RADIOLIB_BUILD_ARDUINO)
|
||||
/*!
|
||||
|
@ -457,6 +487,23 @@ class LoRaWANNode {
|
|||
uint8_t sNwkSIntKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
|
||||
uint8_t nwkSEncKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
|
||||
uint8_t jSIntKey[RADIOLIB_AES128_KEY_SIZE] = { 0 };
|
||||
|
||||
// device-specific parameters, persistent through sessions
|
||||
uint16_t devNonce = 0;
|
||||
uint32_t joinNonce = 0;
|
||||
|
||||
// session-specific parameters
|
||||
uint32_t homeNetId = 0;
|
||||
uint8_t adrLimitExp = RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP;
|
||||
uint8_t adrDelayExp = RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP;
|
||||
uint8_t nbTrans = 1; // Number of allowed frame retransmissions
|
||||
uint8_t txPwrCur = 0;
|
||||
uint32_t fcntUp = 0;
|
||||
uint32_t aFcntDown = 0;
|
||||
uint32_t nFcntDown = 0;
|
||||
uint32_t confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
uint32_t confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE;
|
||||
uint32_t adrFcnt = 0;
|
||||
|
||||
// available channel frequencies from list passed during OTA activation
|
||||
LoRaWANChannel_t availableChannels[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS] = { { 0 }, { 0 } };
|
||||
|
|
|
@ -299,7 +299,16 @@ uint32_t PhysicalLayer::calculateRxTimeout(uint8_t numSymbols, DataRate_t *datar
|
|||
(void)datarate;
|
||||
(void)offsetUs;
|
||||
(void)timeoutUs;
|
||||
return(RADIOLIB_ERR_UNSUPPORTED);
|
||||
return(0);
|
||||
}
|
||||
|
||||
bool PhysicalLayer::isRxTimeout() {
|
||||
return(0);
|
||||
}
|
||||
|
||||
uint16_t PhysicalLayer::readIrq(bool clear) {
|
||||
(void)clear;
|
||||
return(0);
|
||||
}
|
||||
|
||||
int16_t PhysicalLayer::startChannelScan() {
|
||||
|
|
|
@ -319,6 +319,18 @@ class PhysicalLayer {
|
|||
\returns Timeout value in a unit that is specific for the used module
|
||||
*/
|
||||
virtual uint32_t calculateRxTimeout(uint8_t numSymbols, DataRate_t* datarate, uint32_t offsetUs, uint32_t& timeoutUs);
|
||||
|
||||
/*!
|
||||
\brief Check whether there is a RxTimeout flag set
|
||||
\returns RxTimeout flag is set
|
||||
*/
|
||||
virtual bool isRxTimeout();
|
||||
|
||||
/*!
|
||||
\brief Check whether there is a RxTimeout flag set
|
||||
\returns RxTimeout flag is set
|
||||
*/
|
||||
virtual uint16_t readIrq(bool clear = false);
|
||||
|
||||
/*!
|
||||
\brief Interrupt-driven channel activity detection method. interrupt will be activated
|
||||
|
|
Loading…
Add table
Reference in a new issue