diff --git a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino index c2ea337f..732da594 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device/LoRaWAN_End_Device.ino @@ -157,7 +157,7 @@ void loop() { // wait before sending another packet uint32_t minimumDelay = 60000; // try to send once every minute uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows delay(delayMs); } diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino index c2bd5ff1..5ac9261f 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_ABP/LoRaWAN_End_Device_ABP.ino @@ -165,7 +165,7 @@ void loop() { // wait before sending another packet uint32_t minimumDelay = 60000; // try to send once every minute uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows delay(delayMs); } diff --git a/examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino b/examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino index 983a82a0..672506c6 100644 --- a/examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino +++ b/examples/LoRaWAN/LoRaWAN_End_Device_Persistent/LoRaWAN_End_Device_Persistent.ino @@ -77,7 +77,7 @@ void setup() { if(state >= RADIOLIB_ERR_NONE) { Serial.println(F("success!")); Serial.print(F("Restored an ")); - if(state == RADIOLIB_LORAWAN_MODE_OTAA) + if(state == RADIOLIB_LORAWAN_MODE_OTAA) Serial.println(F("OTAA session.")); else { Serial.println(F("ABP session.")); @@ -142,7 +142,7 @@ void loop() { // make sure to send the radio to sleep as well using radio.sleep() uint32_t minimumDelay = 60000; // try to send once every minute uint32_t interval = node.timeUntilUplink(); // calculate minimum duty cycle delay (per law!) - uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows + uint32_t delayMs = max(interval, minimumDelay); // cannot send faster than duty cycle allows delay(delayMs); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index 56fd5b63..2af2ae8b 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -28,39 +28,6 @@ uint8_t getDownlinkDataRate(uint8_t uplink, uint8_t offset, uint8_t base, uint8_ return(dr); } -uint16_t checkSum16(uint32_t key) { - uint8_t bufLen = 2; - uint16_t buf16[bufLen]; - memcpy(buf16, &key, bufLen); - uint16_t checkSum = 0; - for(int i = 0; i < bufLen; i++) { - checkSum ^= buf16[i]; - } - return(checkSum); -} - -uint16_t checkSum16(uint64_t key) { - uint8_t bufLen = 4; - uint16_t buf16[bufLen]; - memcpy(buf16, &key, bufLen); - uint16_t checkSum = 0; - for(int i = 0; i < bufLen; i++) { - checkSum ^= buf16[i]; - } - return(checkSum); -} - -uint16_t checkSum16(uint8_t *key, uint8_t keyLen) { - uint8_t bufLen = keyLen / 2; - uint16_t buf16[bufLen]; - memcpy(buf16, key, bufLen); - uint16_t checkSum = 0; - for(int i = 0; i < bufLen; i++) { - checkSum ^= buf16[i]; - } - return(checkSum); -} - LoRaWANNode::LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band) { this->phyLayer = phy; this->band = band; @@ -395,8 +362,8 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) uint16_t checkSum = 0; - checkSum ^= checkSum16(joinEUI); - checkSum ^= checkSum16(devEUI); + checkSum ^= checkSum16((uint8_t*)joinEUI, 8); + checkSum ^= checkSum16((uint8_t*)devEUI, 8); checkSum ^= checkSum16(nwkKey, 16); checkSum ^= checkSum16(appKey, 16); @@ -666,13 +633,11 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) // check if we actually need to restart from a clean session uint16_t checkSum = 0; - checkSum ^= checkSum16(addr); + checkSum ^= checkSum16((uint8_t*)addr, 4); checkSum ^= checkSum16(nwkSKey, 16); checkSum ^= checkSum16(appSKey, 16); - if(fNwkSIntKey) - checkSum ^= checkSum16(fNwkSIntKey, 16); - if(sNwkSIntKey) - checkSum ^= checkSum16(sNwkSIntKey, 16); + if(fNwkSIntKey) { checkSum ^= checkSum16(fNwkSIntKey, 16); } + if(sNwkSIntKey) { checkSum ^= checkSum16(sNwkSIntKey, 16); } bool validCheckSum = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID) == checkSum; bool validMode = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID) == RADIOLIB_LORAWAN_MODE_ABP; @@ -904,10 +869,9 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // check maximum payload len as defined in phy if(len > this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]]) { - len = this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]]; - // return(RADIOLIB_ERR_PACKET_TOO_LONG); + // len = this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]]; + return(RADIOLIB_ERR_PACKET_TOO_LONG); } - if(RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen)) // increase frame counter by one this->fcntUp += 1; @@ -2575,10 +2539,10 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { RADIOLIB_DEBUG_PRINTLN("TX timing: dlDwell = %d, ulDwell = %d, maxEirp = %d dBm", dlDwell, ulDwell, this->txPowerMax); this->dwellTimeEnabledUp = ulDwell ? true : false; - this->dwellTimeUp = ulDwell ? 400 : 0; + this->dwellTimeUp = ulDwell ? RADIOLIB_LORAWAN_DWELL_TIME : 0; this->dwellTimeEnabledDn = dlDwell ? true : false; - this->dwellTimeDn = dlDwell ? 400 : 0; + this->dwellTimeDn = dlDwell ? RADIOLIB_LORAWAN_DWELL_TIME : 0; #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) if(saveToEeprom) { @@ -2673,31 +2637,32 @@ uint8_t LoRaWANNode::getMacPayloadLength(uint8_t cid) { return 0; } -bool LoRaWANNode::getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt) { +int16_t LoRaWANNode::getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt) { uint8_t payload[5]; int16_t state = deleteMacCommand(RADIOLIB_LORAWAN_LINK_CHECK_REQ, &this->commandsDown, payload); - if(state != RADIOLIB_ERR_NONE) - return false; + RADIOLIB_ASSERT(state); - *margin = payload[0]; - *gwCnt = payload[1]; - RADIOLIB_DEBUG_PRINTLN("Link check: margin = %d dB, gwCnt = %d", margin, gwCnt); - return(true); + if(margin) { *margin = payload[0]; } + if(gwCnt) { *gwCnt = payload[1]; } + // RADIOLIB_DEBUG_PRINTLN("Link check: margin = %d dB, gwCnt = %d", margin, gwCnt); + return(RADIOLIB_ERR_NONE); } -bool LoRaWANNode::getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix) { +int16_t LoRaWANNode::getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix) { uint8_t payload[5]; int16_t state = deleteMacCommand(RADIOLIB_LORAWAN_MAC_DEVICE_TIME, &this->commandsDown, payload); - if(state != RADIOLIB_ERR_NONE) - return false; + RADIOLIB_ASSERT(state); - *gpsEpoch = LoRaWANNode::ntoh(&payload[0]); - *fraction = payload[4]; - RADIOLIB_DEBUG_PRINTLN("Network time: gpsEpoch = %d s, delayExp = %f", gpsEpoch, (float)(*fraction)/256.0f); - - uint32_t unixOffset = 315964800; - *gpsEpoch += unixOffset; - return(true); + if(gpsEpoch) { + *gpsEpoch = LoRaWANNode::ntoh(&payload[0]); + if(returnUnix) { + uint32_t unixOffset = 315964800; + *gpsEpoch += unixOffset; + } + } + if(fraction) { *fraction = payload[4]; } + // RADIOLIB_DEBUG_PRINTLN("Network time: gpsEpoch = %d s, delayExp = %f", gpsEpoch, (float)(*fraction)/256.0f); + return(RADIOLIB_ERR_NONE); } @@ -2786,6 +2751,17 @@ void LoRaWANNode::processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out } } +uint16_t LoRaWANNode::checkSum16(uint8_t *key, uint8_t keyLen) { + uint16_t buf16[RADIOLIB_AES128_KEY_SIZE/2]; + uint8_t bufLen = keyLen / 2; + memcpy(buf16, key, bufLen); + uint16_t checkSum = 0; + for(int i = 0; i < bufLen; i++) { + checkSum ^= buf16[i]; + } + return(checkSum); +} + template T LoRaWANNode::ntoh(uint8_t* buff, size_t size) { uint8_t* buffPtr = buff; diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 4387070d..de4b3ea0 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -159,9 +159,6 @@ #define RADIOLIB_LORAWAN_MIC_DATA_RATE_POS (3) #define RADIOLIB_LORAWAN_MIC_CH_INDEX_POS (4) -// magic word saved in persistent memory upon activation -#define RADIOLIB_LORAWAN_MAGIC (0x39EA) - // MAC commands #define RADIOLIB_LORAWAN_NUM_MAC_COMMANDS (16) @@ -191,6 +188,9 @@ // the maximum number of simultaneously available channels #define RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS (16) +// maximum allowed dwell time on bands that implement dwell time limitations +#define RADIOLIB_LORAWAN_DWELL_TIME (400) + struct LoRaWANMacSpec_t { const uint8_t cid; const uint8_t lenDn; @@ -199,23 +199,23 @@ struct LoRaWANMacSpec_t { }; const LoRaWANMacSpec_t MacTable[RADIOLIB_LORAWAN_NUM_MAC_COMMANDS + 1] = { - { 0x00, 0, 0, false }, // not an actual MAC command, exists for offsetting - { 0x01, 1, 1, false }, // RADIOLIB_LORAWAN_MAC_RESET - { 0x02, 2, 0, true }, // RADIOLIB_LORAWAN_MAC_LINK_CHECK - { 0x03, 4, 1, false }, // RADIOLIB_LORAWAN_MAC_LINK_ADR - { 0x04, 1, 0, false }, // RADIOLIB_LORAWAN_MAC_DUTY_CYCLE - { 0x05, 4, 1, false }, // RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP - { 0x06, 0, 2, false }, // RADIOLIB_LORAWAN_MAC_DEV_STATUS - { 0x07, 5, 1, false }, // RADIOLIB_LORAWAN_MAC_NEW_CHANNEL - { 0x08, 1, 0, false }, // RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP - { 0x09, 1, 0, false }, // RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP - { 0x0A, 4, 1, false }, // RADIOLIB_LORAWAN_MAC_DL_CHANNEL - { 0x0B, 1, 1, false }, // RADIOLIB_LORAWAN_MAC_REKEY - { 0x0C, 1, 0, false }, // RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP - { 0x0D, 5, 0, true }, // RADIOLIB_LORAWAN_MAC_DEVICE_TIME - { 0x0E, 2, 0, false }, // RADIOLIB_LORAWAN_MAC_FORCE_REJOIN - { 0x0F, 1, 1, false }, // RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP - { 0x80, 5, 0, true } // RADIOLIB_LORAWAN_MAC_PROPRIETARY + { 0x00, 0, 0, false }, // not an actual MAC command, exists for index offsetting + { RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false }, + { RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true }, + { RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false }, + { RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false }, + { RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false }, + { RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false }, + { RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false }, + { RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false }, + { RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false }, + { RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false }, + { RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false }, + { RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false }, + { RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true }, + { RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false }, + { RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false }, + { RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true } }; /*! @@ -681,9 +681,9 @@ class LoRaWANNode { Returns 'false' if there was no network response / parsing failed. \param margin Link margin in dB of LinkCheckReq demodulation at gateway side. \param gwCnt Number of gateways that received the LinkCheckReq. - \returns Whether the parameters where succesfully parsed. + \returns \ref status_codes */ - bool getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt); + int16_t getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt); /*! \brief Returns the network time after requesting a DeviceTime MAC command. @@ -692,9 +692,9 @@ class LoRaWANNode { \param gpsEpoch Number of seconds since GPS epoch (Jan. 6th 1980) \param fraction Fractional-second, in 1/256-second steps \param returnUnix If true, returns Unix timestamp instead of GPS (default true) - \returns Whether the parameters where succesfully parsed. + \returns \ref status_codes */ - bool getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix = true); + int16_t getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix = true); #if !RADIOLIB_GODMODE private: @@ -876,6 +876,9 @@ class LoRaWANNode { // function to encrypt and decrypt payloads void processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fcnt, uint8_t dir, uint8_t ctrId, bool counter); + // 16-bit checksum method that takes a uint8_t array of even length and calculates the checksum + static uint16_t checkSum16(uint8_t *key, uint8_t keyLen); + // network-to-host conversion method - takes data from network packet and converts it to the host endians template static T ntoh(uint8_t* buff, size_t size = 0); diff --git a/src/protocols/LoRaWAN/LoRaWANBands.cpp b/src/protocols/LoRaWAN/LoRaWANBands.cpp index 54580b0e..e8b244bd 100644 --- a/src/protocols/LoRaWAN/LoRaWANBands.cpp +++ b/src/protocols/LoRaWAN/LoRaWANBands.cpp @@ -53,7 +53,7 @@ const LoRaWANBand_t US915 = { .powerMax = 30, .powerNumSteps = 10, .dutyCycle = 0, - .dwellTimeUp = 400, + .dwellTimeUp = RADIOLIB_LORAWAN_DWELL_TIME, .dwellTimeDn = 0, .txFreqs = { RADIOLIB_LORAWAN_CHANNEL_NONE, @@ -334,8 +334,8 @@ const LoRaWANBand_t AS923 = { .powerMax = 16, .powerNumSteps = 7, .dutyCycle = 36000, - .dwellTimeUp = 400, - .dwellTimeDn = 400, + .dwellTimeUp = RADIOLIB_LORAWAN_DWELL_TIME, + .dwellTimeDn = RADIOLIB_LORAWAN_DWELL_TIME, .txFreqs = { { .enabled = true, .idx = 0, .freq = 923.200, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 923.400, .drMin = 0, .drMax = 5},