diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index f02e63f8..c1f2273c 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -8,10 +8,10 @@ assignees: '' --- **IMPORTANT: Check the wiki** -Before submitting new issue, please check the [Wiki](https://github.com/jgromes/RadioLib/wiki) and the [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. +Before submitting new issue, please check the [Troubleshooting Guide](https://github.com/jgromes/RadioLib/wiki/Troubleshooting-Guide) Wiki page and the [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. **Describe the bug** -A clear and concise description of what the bug is. When applicable, please include [debug mode output](https://github.com/jgromes/RadioLib/wiki/Debug-mode). +A clear and concise description of what the bug is. When applicable, please include [debug mode output](https://github.com/jgromes/RadioLib/wiki/Debug-mode) **using the appropriate debug mode**. **To Reproduce** Minimal Arduino sketch to reproduce the behavior. Please use Markdown to style the code to make it readable (see [Markdown Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet#code)). diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index 4d2c9a3c..ec5c496e 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -8,7 +8,7 @@ assignees: '' --- **IMPORTANT: Check the wiki** -Before submitting new issue, please check the [Wiki](https://github.com/jgromes/RadioLib/wiki) and the [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. +Before submitting new issue, please check the [Troubleshooting Guide](https://github.com/jgromes/RadioLib/wiki/Troubleshooting-Guide) Wiki page and the [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] diff --git a/.github/ISSUE_TEMPLATE/module-not-working.md b/.github/ISSUE_TEMPLATE/module-not-working.md index 1b67e8d6..096d07a3 100644 --- a/.github/ISSUE_TEMPLATE/module-not-working.md +++ b/.github/ISSUE_TEMPLATE/module-not-working.md @@ -9,7 +9,7 @@ assignees: '' **IMPORTANT: Before submitting an issue, please check the following:** 1. **Read [CONTRIBUTING.md](https://github.com/jgromes/RadioLib/blob/master/CONTRIBUTING.md)!** Issues that do not follow this document will be closed/locked/deleted/ignored. -2. RadioLib has a [Wiki](https://github.com/jgromes/RadioLib/wiki) and an extensive [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. +2. RadioLib has a [Troubleshooting Guide](https://github.com/jgromes/RadioLib/wiki/Troubleshooting-Guide) Wiki page and an extensive [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. 3. Make sure you're using the latest release of the library! Releases can be found [here](https://github.com/jgromes/RadioLib/releases). 4. Use [Arduino forums](https://forum.arduino.cc/) to ask generic questions about wireless modules, wiring, usage, etc. Only create issues for problems specific to RadioLib! 5. Error codes, their meaning and how to fix them can be found on [this page](https://jgromes.github.io/RadioLib/group__status__codes.html). @@ -24,7 +24,7 @@ paste the sketch here, even if it is an unmodified example code Wiring diagram, schematic, pictures etc. **Debug mode output** -Enable all [debug levels](https://github.com/jgromes/RadioLib/wiki/Debug-mode) and paste the Serial monitor output here. +Enable the appropriate [debug levels](https://github.com/jgromes/RadioLib/wiki/Debug-mode) and paste the Serial monitor output here. For debugging protocols, enable `RADIOLIB_DEBUG_PROTOCOL`. For debugging issues with the radio module itself, enable `RADIOLIB_DEBUG_SPI`. **Additional info (please complete):** - MCU: [e.g. Arduino Uno, ESP8266 etc.] diff --git a/.github/ISSUE_TEMPLATE/regular-issue.md b/.github/ISSUE_TEMPLATE/regular-issue.md index 4e488b9a..c55b36a1 100644 --- a/.github/ISSUE_TEMPLATE/regular-issue.md +++ b/.github/ISSUE_TEMPLATE/regular-issue.md @@ -9,7 +9,7 @@ assignees: '' **IMPORTANT: Before submitting an issue, please check the following:** 1. **Read [CONTRIBUTING.md](https://github.com/jgromes/RadioLib/blob/master/CONTRIBUTING.md)!** Issues that do not follow this document will be closed/locked/deleted/ignored. -2. RadioLib has a [Wiki](https://github.com/jgromes/RadioLib/wiki) and an extensive [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. +2. RadioLib has a [Troubleshooting Guide](https://github.com/jgromes/RadioLib/wiki/Troubleshooting-Guide) Wiki page and an extensive [API documentation](https://jgromes.github.io/RadioLib/). You might find a solution to your issue there. 3. Make sure you're using the latest release of the library! Releases can be found [here](https://github.com/jgromes/RadioLib/releases). 4. Use [Arduino forums](https://forum.arduino.cc/) to ask generic questions about wireless modules, wiring, usage, etc. Only create issues for problems specific to RadioLib! 5. Error codes, their meaning and how to fix them can be found on [this page](https://jgromes.github.io/RadioLib/group__status__codes.html). diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 78230b25..c90f7c9d 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -5,11 +5,14 @@ on: branches: [master] pull_request: branches: [master] + workflow_dispatch: jobs: analyze: name: Analyze runs-on: ubuntu-latest + permissions: + security-events: write strategy: fail-fast: false @@ -18,20 +21,11 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v2 - with: - # We must fetch at least the immediate parents so that if this is - # a pull request then we can checkout the head. - fetch-depth: 2 - - # If this run was triggered by a pull request event, then checkout - # the head of the pull request instead of the merge commit. - - run: git checkout HEAD^2 - if: ${{ github.event_name == 'pull_request' }} + uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v1 + uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} @@ -63,4 +57,4 @@ jobs: arduino-cli compile --libraries /home/runner/work/RadioLib --fqbn arduino:avr:uno $PWD/examples/SX126x/SX126x_Transmit_Blocking/SX126x_Transmit_Blocking.ino --warnings=all - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v1 + uses: github/codeql-action/analyze@v3 diff --git a/.github/workflows/cppcheck.yml b/.github/workflows/cppcheck.yml new file mode 100644 index 00000000..684e11f9 --- /dev/null +++ b/.github/workflows/cppcheck.yml @@ -0,0 +1,27 @@ +name: "Cppcheck" + +on: + push: + branches: [master] + pull_request: + branches: [master] + workflow_dispatch: + +jobs: + check: + name: Perform static code check + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install cppcheck + run: + | + sudo apt-get update + sudo apt-get install -y cppcheck + + - name: Run cppcheck + run: + cppcheck src --enable=all --force diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index ae645667..5ebdd860 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -13,7 +13,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y doxygen - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Generate docs run: doxygen Doxyfile diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ce103297..12c45391 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -50,22 +50,17 @@ jobs: - id: arduino:avr:mega run: echo "options=':cpu=atmega2560'" >> $GITHUB_OUTPUT - id: arduino:mbed:nano33ble - run: echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: arduino:mbed:envie_m4 - run: echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: arduino:megaavr:uno2018 run: | echo "options=':mode=on'" >> $GITHUB_OUTPUT echo "skip-pattern=(STM32WL|LoRaWAN)" >> $GITHUB_OUTPUT - id: arduino:sam:arduino_due_x - run: echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: arduino:samd:arduino_zero_native - run: echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: adafruit:samd:adafruit_feather_m0 run: | echo "options=':usbstack=arduino,debug=off'" >> $GITHUB_OUTPUT echo "index-url=--additional-urls https://adafruit.github.io/arduino-board-index/package_adafruit_index.json" >> $GITHUB_OUTPUT - echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: adafruit:nrf52:feather52832 run: | sudo apt-get update @@ -75,7 +70,6 @@ jobs: echo "/home/runner/.local/bin" >> $GITHUB_PATH echo "options=':softdevice=s132v6,debug=l0'" >> $GITHUB_OUTPUT echo "index-url=--additional-urls https://adafruit.github.io/arduino-board-index/package_adafruit_index.json" >> $GITHUB_OUTPUT - echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: esp32:esp32:esp32 run: | python -m pip install pyserial @@ -102,10 +96,8 @@ jobs: echo "index-url=--additional-urls http://dan.drown.org/stm32duino/package_STM32duino_index.json" >> $GITHUB_OUTPUT - id: MegaCoreX:megaavr:4809 run: | - echo "skip-pattern=(STM32WL|LoRaWAN)" >> $GITHUB_OUTPUT echo "index-url=--additional-urls https://mcudude.github.io/MegaCoreX/package_MCUdude_MegaCoreX_index.json" >> $GITHUB_OUTPUT - id: arduino:mbed_rp2040:pico - run: echo "skip-pattern=(STM32WL|LoRaWAN_End_Device_Persistent)" >> $GITHUB_OUTPUT - id: rp2040:rp2040:rpipico run: echo "index-url=--additional-urls https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json" >> $GITHUB_OUTPUT - id: CubeCell:CubeCell:CubeCell-Board @@ -158,7 +150,7 @@ jobs: - name: Checkout repository if: ${{ env.run-build == 'true' }} - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Build examples if: ${{ env.run-build == 'true' }} @@ -186,7 +178,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install dependencies run: | @@ -214,7 +206,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: submodules: recursive @@ -235,7 +227,7 @@ jobs: runs-on: [self-hosted, ARM64] steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install dependencies run: | @@ -270,7 +262,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Install dependencies run: | diff --git a/.gitignore b/.gitignore index 226f5c8e..b65463c9 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ extras/SX126x_Spectrum_Scan/out/* # cmake build/ + +# Compote build output +dist diff --git a/README.md b/README.md index e726a7ad..d33794b3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# RadioLib ![Build Status](https://github.com/jgromes/RadioLib/workflows/CI/badge.svg) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/jgromes/library/RadioLib.svg)](https://registry.platformio.org/libraries/jgromes/RadioLib) +# RadioLib ![Build Status](https://github.com/jgromes/RadioLib/workflows/CI/badge.svg) [![PlatformIO Registry](https://badges.registry.platformio.org/packages/jgromes/library/RadioLib.svg)](https://registry.platformio.org/libraries/jgromes/RadioLib) [![Component Registry](https://components.espressif.com/components/jgromes/radiolib/badge.svg)](https://components.espressif.com/components/jgromes/radiolib) ### _One radio library to rule them all!_ diff --git a/examples/SX126x/SX126x_PingPong/SX126x_PingPong.ino b/examples/SX126x/SX126x_PingPong/SX126x_PingPong.ino index fcdff8a2..99a962b2 100644 --- a/examples/SX126x/SX126x_PingPong/SX126x_PingPong.ino +++ b/examples/SX126x/SX126x_PingPong/SX126x_PingPong.ino @@ -42,6 +42,9 @@ volatile bool operationDone = false; // is transmitted or received by the module // IMPORTANT: this function MUST be 'void' type // and MUST NOT have any arguments! +#if defined(ESP8266) || defined(ESP32) + ICACHE_RAM_ATTR +#endif void setFlag(void) { // we sent or received a packet, set the flag operationDone = true; diff --git a/examples/SX127x/SX127x_PingPong/SX127x_PingPong.ino b/examples/SX127x/SX127x_PingPong/SX127x_PingPong.ino index 9e006639..df025bc9 100644 --- a/examples/SX127x/SX127x_PingPong/SX127x_PingPong.ino +++ b/examples/SX127x/SX127x_PingPong/SX127x_PingPong.ino @@ -39,6 +39,9 @@ volatile bool operationDone = false; // is transmitted or received by the module // IMPORTANT: this function MUST be 'void' type // and MUST NOT have any arguments! +#if defined(ESP8266) || defined(ESP32) + ICACHE_RAM_ATTR +#endif void setFlag(void) { // we sent or received packet, set the flag operationDone = true; diff --git a/idf_component.yml b/idf_component.yml new file mode 100644 index 00000000..09f82ad9 --- /dev/null +++ b/idf_component.yml @@ -0,0 +1,11 @@ +version: "6.4.2" +description: "Universal wireless communication library. User-friendly library for sub-GHz radio modules (SX1278, RF69, CC1101, SX1268, and many others), as well as ham radio digital modes (RTTY, SSTV, AX.25 etc.) and other protocols (Pagers, LoRaWAN)." +tags: "radio, communication, morse, cc1101, aprs, sx1276, sx1278, sx1272, rtty, ax25, afsk, nrf24, rfm96, sx1231, rfm96, rfm98, sstv, sx1278, sx1272, sx1276, sx1280, sx1281, sx1282, sx1261, sx1262, sx1268, si4432, rfm22, llcc68, pager, pocsag, lorawan" +url: "https://github.com/jgromes/RadioLib" +repository: "https://github.com/jgromes/RadioLib.git" +license: "MIT" +dependencies: + # Required IDF version + idf: ">=4.1" +maintainers: + "Jan Gromeš " diff --git a/keywords.txt b/keywords.txt index 6ee6222c..48efe632 100644 --- a/keywords.txt +++ b/keywords.txt @@ -293,6 +293,10 @@ setModem KEYWORD2 # LoRaWAN wipe KEYWORD2 +getBufferNonces KEYWORD2 +setBufferNonces KEYWORD2 +getBufferSession KEYWORD2 +setBufferSession KEYWORD2 restore KEYWORD2 beginOTAA KEYWORD2 beginABP KEYWORD2 @@ -426,9 +430,10 @@ RADIOLIB_ERR_INVALID_CHANNEL LITERAL1 RADIOLIB_ERR_INVALID_CID LITERAL1 RADIOLIB_ERR_UPLINK_UNAVAILABLE LITERAL1 RADIOLIB_ERR_COMMAND_QUEUE_FULL 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 -RADIOLIB_ERR_DATA_RATE_INVALID LITERAL1 \ No newline at end of file +RADIOLIB_ERR_DATA_RATE_INVALID LITERAL1 +RADIOLIB_ERR_DWELL_TIME_EXCEEDED LITERAL1 +RADIOLIB_ERR_CHECKSUM_MISMATCH LITERAL1 \ No newline at end of file diff --git a/src/ArduinoHal.cpp b/src/ArduinoHal.cpp index 59b5fb5e..9c5fd571 100644 --- a/src/ArduinoHal.cpp +++ b/src/ArduinoHal.cpp @@ -2,10 +2,6 @@ #if defined(RADIOLIB_BUILD_ARDUINO) -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) -#include -#endif - ArduinoHal::ArduinoHal(): RadioLibHal(INPUT, OUTPUT, LOW, HIGH, RISING, FALLING), spi(&RADIOLIB_DEFAULT_SPI), initInterface(true) {} ArduinoHal::ArduinoHal(SPIClass& spi, SPISettings spiSettings): RadioLibHal(INPUT, OUTPUT, LOW, HIGH, RISING, FALLING), spi(&spi), spiSettings(spiSettings) {} @@ -118,49 +114,6 @@ void inline ArduinoHal::spiEnd() { spi->end(); } -void ArduinoHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len) { - #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - #if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040) - EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE); - #elif defined(ARDUINO_ARCH_APOLLO3) - EEPROM.init(); - #endif - for(size_t i = 0; i < len; i++) { - buff[i] = EEPROM.read(addr + i); - } - #if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040) - EEPROM.end(); - #endif - #else - (void)addr; - (void)buff; - (void)len; - #endif -} - -void ArduinoHal::writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len) { - #if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - #if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040) - EEPROM.begin(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE); - #elif defined(ARDUINO_ARCH_APOLLO3) - EEPROM.init(); - #endif - for(size_t i = 0; i < len; i++) { - if(EEPROM.read(addr + i) != buff[i]) { // only write if value is new - EEPROM.write(addr + i, buff[i]); - } - } - #if defined(RADIOLIB_ESP32) || defined(ARDUINO_ARCH_RP2040) - EEPROM.commit(); - EEPROM.end(); - #endif - #else - (void)addr; - (void)buff; - (void)len; - #endif -} - void inline ArduinoHal::tone(uint32_t pin, unsigned int frequency, unsigned long duration) { #if !defined(RADIOLIB_TONE_UNSUPPORTED) if(pin == RADIOLIB_NC) { diff --git a/src/ArduinoHal.h b/src/ArduinoHal.h index bcda95ae..00074c1d 100644 --- a/src/ArduinoHal.h +++ b/src/ArduinoHal.h @@ -51,9 +51,6 @@ class ArduinoHal : public RadioLibHal { void spiEndTransaction() override; void spiEnd() override; - void readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len) override; - void writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len) override; - // implementations of virtual RadioLibHal methods void init() override; void term() override; diff --git a/src/BuildOpt.h b/src/BuildOpt.h index 4609a1da..5653a1ef 100644 --- a/src/BuildOpt.h +++ b/src/BuildOpt.h @@ -7,14 +7,18 @@ * Debug output enable. * Warning: Debug output will slow down the whole system significantly. * Also, it will result in larger compiled binary. - * Levels: debug - only main info - * verbose - full transcript of all SPI communication + * Levels: basic - only main info + * protocol - mainly LoRaWAN stuff, but other protocols as well + * SPI - full transcript of all SPI communication */ -#if !defined(RADIOLIB_DEBUG) - #define RADIOLIB_DEBUG (0) +#if !defined(RADIOLIB_DEBUG_BASIC) + #define RADIOLIB_DEBUG_BASIC (0) #endif -#if !defined(RADIOLIB_VERBOSE) - #define RADIOLIB_VERBOSE (0) +#if !defined(RADIOLIB_DEBUG_PROTOCOL) + #define RADIOLIB_DEBUG_PROTOCOL (0) +#endif +#if !defined(RADIOLIB_DEBUG_SPI) + #define RADIOLIB_DEBUG_SPI (0) #endif // set which output port should be used for debug output @@ -100,21 +104,6 @@ #define RADIOLIB_STATIC_ARRAY_SIZE (256) #endif -// the base address for persistent storage -// some protocols (e.g. LoRaWAN) require a method -// to store some data persistently -// on Arduino, this will use EEPROM, on non-Arduino platform, -// it will use anything provided by the hardware abstraction layer -// RadioLib will place these starting at this address -#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE) - #define RADIOLIB_HAL_PERSISTENT_STORAGE_BASE (0) -#endif - -// the amount of space allocated to the persistent storage -#if !defined(RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE) - #define RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE (0x01C0) -#endif - /* * Uncomment on boards whose clock runs too slow or too fast * Set the value according to the following scheme: @@ -234,7 +223,6 @@ #elif defined(SAMD_SERIES) // Adafruit SAMD boards (M0 and M4) #define RADIOLIB_PLATFORM "Adafruit SAMD" - #define RADIOLIB_EEPROM_UNSUPPORTED #elif defined(ARDUINO_ARCH_SAMD) // Arduino SAMD (Zero, MKR, etc.) @@ -242,18 +230,15 @@ #define RADIOLIB_ARDUINOHAL_PIN_MODE_CAST (PinMode) #define RADIOLIB_ARDUINOHAL_PIN_STATUS_CAST (PinStatus) #define RADIOLIB_ARDUINOHAL_INTERRUPT_MODE_CAST (PinStatus) - #define RADIOLIB_EEPROM_UNSUPPORTED #elif defined(__SAM3X8E__) // Arduino Due #define RADIOLIB_PLATFORM "Arduino Due" #define RADIOLIB_TONE_UNSUPPORTED - #define RADIOLIB_EEPROM_UNSUPPORTED #elif (defined(NRF52832_XXAA) || defined(NRF52840_XXAA)) && !defined(ARDUINO_ARDUINO_NANO33BLE) // Adafruit nRF52 boards #define RADIOLIB_PLATFORM "Adafruit nRF52" - #define RADIOLIB_EEPROM_UNSUPPORTED #elif defined(ARDUINO_ARC32_TOOLS) // Intel Curie @@ -276,7 +261,6 @@ #define RADIOLIB_ARDUINOHAL_PIN_MODE_CAST (PinMode) #define RADIOLIB_ARDUINOHAL_PIN_STATUS_CAST (PinStatus) #define RADIOLIB_ARDUINOHAL_INTERRUPT_MODE_CAST (PinStatus) - #define RADIOLIB_EEPROM_UNSUPPORTED // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds #define RADIOLIB_TONE_UNSUPPORTED @@ -288,7 +272,6 @@ #define RADIOLIB_ARDUINOHAL_PIN_MODE_CAST (PinMode) #define RADIOLIB_ARDUINOHAL_PIN_STATUS_CAST (PinStatus) #define RADIOLIB_ARDUINOHAL_INTERRUPT_MODE_CAST (PinStatus) - #define RADIOLIB_EEPROM_UNSUPPORTED // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds #define RADIOLIB_TONE_UNSUPPORTED @@ -310,7 +293,6 @@ #define RADIOLIB_ARDUINOHAL_PIN_MODE_CAST (PinMode) #define RADIOLIB_ARDUINOHAL_PIN_STATUS_CAST (PinStatus) #define RADIOLIB_ARDUINOHAL_INTERRUPT_MODE_CAST (PinStatus) - #define RADIOLIB_EEPROM_UNSUPPORTED // Arduino mbed OS boards have a really bad tone implementation which will crash after a couple seconds #define RADIOLIB_TONE_UNSUPPORTED @@ -469,23 +451,35 @@ #define RADIOLIB_EXCLUDE_STM32WLX (1) #endif +// set the global debug mode flag +#if RADIOLIB_DEBUG_BASIC || RADIOLIB_DEBUG_PROTOCOL || RADIOLIB_DEBUG_SPI + #define RADIOLIB_DEBUG (1) +#else + #define RADIOLIB_DEBUG (0) +#endif + #if RADIOLIB_DEBUG #if defined(RADIOLIB_BUILD_ARDUINO) #define RADIOLIB_DEBUG_PRINT(...) Module::serialPrintf(__VA_ARGS__) #define RADIOLIB_DEBUG_PRINTLN(M, ...) Module::serialPrintf(M "\n", ##__VA_ARGS__) + #define RADIOLIB_DEBUG_PRINT_LVL(LEVEL, M, ...) Module::serialPrintf(LEVEL "" M, ##__VA_ARGS__) + #define RADIOLIB_DEBUG_PRINTLN_LVL(LEVEL, M, ...) Module::serialPrintf(LEVEL "" M "\n", ##__VA_ARGS__) // some platforms do not support printf("%f"), so it has to be done this way - #define RADIOLIB_DEBUG_PRINT_FLOAT(VAL, DECIMALS) RADIOLIB_DEBUG_PORT.print(VAL, DECIMALS) + #define RADIOLIB_DEBUG_PRINT_FLOAT(LEVEL, VAL, DECIMALS) RADIOLIB_DEBUG_PRINT(LEVEL); RADIOLIB_DEBUG_PORT.print(VAL, DECIMALS) #else #if !defined(RADIOLIB_DEBUG_PRINT) #define RADIOLIB_DEBUG_PRINT(...) fprintf(RADIOLIB_DEBUG_PORT, __VA_ARGS__) + #define RADIOLIB_DEBUG_PRINT_LVL(LEVEL, M, ...) fprintf(RADIOLIB_DEBUG_PORT, LEVEL "" M, ##__VA_ARGS__) #endif #if !defined(RADIOLIB_DEBUG_PRINTLN) #define RADIOLIB_DEBUG_PRINTLN(M, ...) fprintf(RADIOLIB_DEBUG_PORT, M "\n", ##__VA_ARGS__) + #define RADIOLIB_DEBUG_PRINTLN_LVL(LEVEL, M, ...) fprintf(RADIOLIB_DEBUG_PORT, LEVEL "" M "\n", ##__VA_ARGS__) #endif - #define RADIOLIB_DEBUG_PRINT_FLOAT(VAL, DECIMALS) RADIOLIB_DEBUG_PRINT("%.3f", VAL) + #define RADIOLIB_DEBUG_PRINT_FLOAT(LEVEL, VAL, DECIMALS) RADIOLIB_DEBUG_PRINT(LEVEL "%.3f", VAL) #endif - #define RADIOLIB_DEBUG_HEXDUMP(...) Module::hexdump(__VA_ARGS__) + + #define RADIOLIB_DEBUG_HEXDUMP(LEVEL, ...) Module::hexdump(LEVEL, __VA_ARGS__) #else #define RADIOLIB_DEBUG_PRINT(...) {} #define RADIOLIB_DEBUG_PRINTLN(...) {} @@ -493,14 +487,49 @@ #define RADIOLIB_DEBUG_HEXDUMP(...) {} #endif -#if RADIOLIB_VERBOSE - #define RADIOLIB_VERBOSE_PRINT(...) RADIOLIB_DEBUG_PRINT(__VA_ARGS__) - #define RADIOLIB_VERBOSE_PRINTLN(...) RADIOLIB_DEBUG_PRINTLN(__VA_ARGS__) +#if RADIOLIB_DEBUG_BASIC + #define RADIOLIB_DEBUG_BASIC_PRINT(...) RADIOLIB_DEBUG_PRINT_LVL("RLB_DBG: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_BASIC_PRINT_NOTAG(...) RADIOLIB_DEBUG_PRINT_LVL("", __VA_ARGS__) + #define RADIOLIB_DEBUG_BASIC_PRINTLN(...) RADIOLIB_DEBUG_PRINTLN_LVL("RLB_DBG: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_BASIC_PRINT_FLOAT(...) RADIOLIB_DEBUG_PRINT_FLOAT("RLB_DBG: ", __VA_ARGS__); + #define RADIOLIB_DEBUG_BASIC_HEXDUMP(...) RADIOLIB_DEBUG_HEXDUMP("RLB_DBG: ", __VA_ARGS__); #else - #define RADIOLIB_VERBOSE_PRINT(...) {} - #define RADIOLIB_VERBOSE_PRINTLN(...) {} + #define RADIOLIB_DEBUG_BASIC_PRINT(...) {} + #define RADIOLIB_DEBUG_BASIC_PRINT_NOTAG(...) {} + #define RADIOLIB_DEBUG_BASIC_PRINTLN(...) {} + #define RADIOLIB_DEBUG_BASIC_PRINT_FLOAT(...) {} + #define RADIOLIB_DEBUG_BASIC_HEXDUMP(...) {} #endif +#if RADIOLIB_DEBUG_PROTOCOL + #define RADIOLIB_DEBUG_PROTOCOL_PRINT(...) RADIOLIB_DEBUG_PRINT_LVL("RLB_PRO: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_PROTOCOL_PRINTLN(...) RADIOLIB_DEBUG_PRINTLN_LVL("RLB_PRO: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_PROTOCOL_PRINT_FLOAT(...) RADIOLIB_DEBUG_PRINT_FLOAT("RLB_PRO: ", __VA_ARGS__); + #define RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(...) RADIOLIB_DEBUG_HEXDUMP("RLB_PRO: ", __VA_ARGS__); +#else + #define RADIOLIB_DEBUG_PROTOCOL_PRINT(...) {} + #define RADIOLIB_DEBUG_PROTOCOL_PRINTLN(...) {} + #define RADIOLIB_DEBUG_PROTOCOL_PRINT_FLOAT(...) {} + #define RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(...) {} +#endif + +#if RADIOLIB_DEBUG_SPI + #define RADIOLIB_DEBUG_SPI_PRINT(...) RADIOLIB_DEBUG_PRINT_LVL("RLB_SPI: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_SPI_PRINT_NOTAG(...) RADIOLIB_DEBUG_PRINT_LVL("", __VA_ARGS__) + #define RADIOLIB_DEBUG_SPI_PRINTLN(...) RADIOLIB_DEBUG_PRINTLN_LVL("RLB_SPI: ", __VA_ARGS__) + #define RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(...) RADIOLIB_DEBUG_PRINTLN_LVL("", __VA_ARGS__) + #define RADIOLIB_DEBUG_SPI_PRINT_FLOAT(...) RADIOLIB_DEBUG_PRINT_FLOAT("RLB_SPI: ", __VA_ARGS__); + #define RADIOLIB_DEBUG_SPI_HEXDUMP(...) RADIOLIB_DEBUG_HEXDUMP("RLB_SPI: ", __VA_ARGS__); +#else + #define RADIOLIB_DEBUG_SPI_PRINT(...) {} + #define RADIOLIB_DEBUG_SPI_PRINT_NOTAG(...) {} + #define RADIOLIB_DEBUG_SPI_PRINTLN(...) {} + #define RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(...) {} + #define RADIOLIB_DEBUG_SPI_PRINT_FLOAT(...) {} + #define RADIOLIB_DEBUG_SPI_HEXDUMP(...) {} +#endif + + /*! \brief A simple assert macro, will return on error. */ @@ -534,4 +563,4 @@ #define RADIOLIB_VERSION (((RADIOLIB_VERSION_MAJOR) << 24) | ((RADIOLIB_VERSION_MINOR) << 16) | ((RADIOLIB_VERSION_PATCH) << 8) | (RADIOLIB_VERSION_EXTRA)) -#endif +#endif \ No newline at end of file diff --git a/src/BuildOptUser.h b/src/BuildOptUser.h index fc1a5657..d81104d7 100644 --- a/src/BuildOptUser.h +++ b/src/BuildOptUser.h @@ -5,7 +5,8 @@ // most commonly, RADIOLIB_EXCLUDE_* macros // or enabling debug output -//#define RADIOLIB_DEBUG (1) -//#define RADIOLIB_VERBOSE (1) +//#define RADIOLIB_DEBUG_BASIC (1) // basic debugging (e.g. reporting GPIO timeouts or module not being found) +//#define RADIOLIB_DEBUG_PROTOCOL (1) // protocol information (e.g. LoRaWAN internal information) +//#define RADIOLIB_DEBUG_SPI (1) // verbose transcription of all SPI communication - produces large debug logs! #endif diff --git a/src/Hal.cpp b/src/Hal.cpp index 1b4e818e..1a42aa15 100644 --- a/src/Hal.cpp +++ b/src/Hal.cpp @@ -33,50 +33,3 @@ void RadioLibHal::yield() { uint32_t RadioLibHal::pinToInterrupt(uint32_t pin) { return(pin); } - -void RadioLibHal::readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len) { - // these are only needed for some protocols, so it's not needed to have them by default - (void)addr; - (void)buff; - (void)len; -} - -void RadioLibHal::writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len) { - // these are only needed for some protocols, so it's not needed to have them by default - (void)addr; - (void)buff; - (void)len; -} - -void RadioLibHal::wipePersistentStorage() { - uint8_t dummy = 0; - for(size_t i = 0; i < RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE; i++) { - this->writePersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + i, &dummy, sizeof(uint8_t)); - } -} - -uint32_t RadioLibHal::getPersistentAddr(uint32_t id) { - return(RadioLibPersistentParamTable[id]); -} - -template -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] + offset, ptr, sizeof(T)); -} - -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 -T RadioLibHal::getPersistentParameter(uint32_t id) { - T val = 0; - uint8_t *ptr = (uint8_t*)&val; - this->readPersistentStorage(RADIOLIB_HAL_PERSISTENT_STORAGE_BASE + RadioLibPersistentParamTable[id], ptr, sizeof(T)); - return(val); -} - -template uint8_t RadioLibHal::getPersistentParameter(uint32_t id); -template uint16_t RadioLibHal::getPersistentParameter(uint32_t id); -template uint32_t RadioLibHal::getPersistentParameter(uint32_t id); diff --git a/src/Hal.h b/src/Hal.h index 4a0c72c3..03bf174c 100644 --- a/src/Hal.h +++ b/src/Hal.h @@ -6,88 +6,6 @@ #include "BuildOpt.h" -#define RADIOLIB_EEPROM_TABLE_VERSION (0x0002) - -// list of persistent parameters -enum RADIOLIB_EEPROM_PARAMS { - RADIOLIB_EEPROM_TABLE_VERSION_ID, // table layout version - RADIOLIB_EEPROM_LORAWAN_CLASS_ID, // class A, B or C - RADIOLIB_EEPROM_LORAWAN_MODE_ID, // none, OTAA or ABP - RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID, // checksum of keys used for device activation - RADIOLIB_EEPROM_LORAWAN_VERSION_ID, // LoRaWAN version - RADIOLIB_EEPROM_LORAWAN_LAST_TIME_ID, // last heard time through DeviceTimeReq or Beacon - RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID, - RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID, - RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID, - RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID, - RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID, - RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID, - RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID, - RADIOLIB_EEPROM_LORAWAN_HOME_NET_ID, - RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID, - RADIOLIB_EEPROM_LORAWAN_N_FCNT_DOWN_ID, - RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_UP_ID, - RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_DOWN_ID, - RADIOLIB_EEPROM_LORAWAN_ADR_FCNT_ID, - RADIOLIB_EEPROM_LORAWAN_RJ_COUNT0_ID, - RADIOLIB_EEPROM_LORAWAN_RJ_COUNT1_ID, - RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID, - RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID, - RADIOLIB_EEPROM_LORAWAN_DUTY_CYCLE_ID, - RADIOLIB_EEPROM_LORAWAN_RX_PARAM_SETUP_ID, - RADIOLIB_EEPROM_LORAWAN_RX_TIMING_SETUP_ID, - RADIOLIB_EEPROM_LORAWAN_TX_PARAM_SETUP_ID, - RADIOLIB_EEPROM_LORAWAN_ADR_PARAM_SETUP_ID, - RADIOLIB_EEPROM_LORAWAN_REJOIN_PARAM_SETUP_ID, - RADIOLIB_EEPROM_LORAWAN_BEACON_FREQ_ID, - RADIOLIB_EEPROM_LORAWAN_PING_SLOT_CHANNEL_ID, - RADIOLIB_EEPROM_LORAWAN_PERIODICITY_ID, - RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID, - RADIOLIB_EEPROM_LORAWAN_MAC_QUEUE_UL_ID, - RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID, - RADIOLIB_EEPROM_LORAWAN_DL_CHANNELS_ID -}; - -static const uint32_t RadioLibPersistentParamTable[] = { - 0x00, // RADIOLIB_EEPROM_TABLE_VERSION_ID - 0x02, // RADIOLIB_EEPROM_LORAWAN_CLASS_ID - 0x03, // RADIOLIB_EEPROM_LORAWAN_MODE_ID - 0x05, // RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID - 0x07, // RADIOLIB_EEPROM_LORAWAN_VERSION_ID - 0x08, // RADIOLIB_EEPROM_LORAWAN_LAST_TIME_ID - 0x0C, // RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID - 0x10, // RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID - 0x20, // RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID - 0x30, // RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID - 0x40, // RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID - 0x50, // RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID - 0x54, // RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID - 0x58, // RADIOLIB_EEPROM_LORAWAN_HOME_NET_ID - 0x5C, // RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID - 0x60, // RADIOLIB_EEPROM_LORAWAN_N_FCNT_DOWN_ID - 0x64, // RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_UP_ID - 0x68, // RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_DOWN_ID - 0x6C, // RADIOLIB_EEPROM_LORAWAN_ADR_FCNT_ID - 0x70, // RADIOLIB_EEPROM_LORAWAN_RJ_COUNT0_ID - 0x72, // RADIOLIB_EEPROM_LORAWAN_RJ_COUNT1_ID - 0x74, // RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID - 0xA0, // RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID - 0xA4, // RADIOLIB_EEPROM_LORAWAN_DUTY_CYCLE_ID - 0xA5, // RADIOLIB_EEPROM_LORAWAN_RX_PARAM_SETUP_ID - 0xA9, // RADIOLIB_EEPROM_LORAWAN_RX_TIMING_SETUP_ID - 0xAA, // RADIOLIB_EEPROM_LORAWAN_TX_PARAM_SETUP_ID - 0xAB, // RADIOLIB_EEPROM_LORAWAN_ADR_PARAM_SETUP_ID - 0xAC, // RADIOLIB_EEPROM_LORAWAN_REJOIN_PARAM_SETUP_ID - 0xAD, // RADIOLIB_EEPROM_LORAWAN_BEACON_FREQ_ID - 0xB0, // RADIOLIB_EEPROM_LORAWAN_PING_SLOT_CHANNEL_ID - 0xB4, // RADIOLIB_EEPROM_LORAWAN_PERIODICITY_ID - 0xB5, // RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID - 0xB6, // RADIOLIB_EEPROM_LORAWAN_MAC_QUEUE_UL_ID - 0x0100, // RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID - 0x0180, // RADIOLIB_EEPROM_LORAWAN_DL_CHANNELS_ID - 0x01C0, // end -}; - /*! \class RadioLibHal \brief Hardware abstraction library base interface. @@ -289,56 +207,6 @@ class RadioLibHal { \returns The interrupt number of a given pin. */ virtual uint32_t pinToInterrupt(uint32_t pin); - - /*! - \brief Method to read from persistent storage (e.g. EEPROM). - \param addr Address to start reading at. - \param buff Buffer to read into. - \param len Number of bytes to read. - */ - virtual void readPersistentStorage(uint32_t addr, uint8_t* buff, size_t len); - - /*! - \brief Method to write to persistent storage (e.g. EEPROM). - \param addr Address to start writing to. - \param buff Buffer to write. - \param len Number of bytes to write. - */ - virtual void writePersistentStorage(uint32_t addr, uint8_t* buff, size_t len); - - /*! - \brief Method to wipe the persistent storage by writing to 0. - Will write at most RADIOLIB_HAL_PERSISTENT_STORAGE_SIZE bytes. - */ - void wipePersistentStorage(); - - /*! - \brief Method to convert from persistent parameter ID to its physical address. - \param id Parameter ID. - \returns Parameter physical address. - */ - uint32_t getPersistentAddr(uint32_t id); - - /*! - \brief Method to set arbitrary parameter to persistent storage. - This method DOES NOT perform any endianness conversion, so the value - 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 - void setPersistentParameter(uint32_t id, T val, uint32_t offset = 0); - - /*! - \brief Method to get arbitrary parameter from persistent storage. - This method DOES NOT perform any endianness conversion, so the value - will be retrieved in the system endian! - \param id Parameter ID to load from. - \returns The loaded value. - */ - template - T getPersistentParameter(uint32_t id); }; #endif diff --git a/src/Module.cpp b/src/Module.cpp index b6f8583d..c46e26e9 100644 --- a/src/Module.cpp +++ b/src/Module.cpp @@ -44,10 +44,10 @@ void Module::init() { this->hal->init(); this->hal->pinMode(csPin, this->hal->GpioModeOutput); this->hal->digitalWrite(csPin, this->hal->GpioLevelHigh); - RADIOLIB_DEBUG_PRINTLN("\nRadioLib Debug Info"); - RADIOLIB_DEBUG_PRINTLN("Version: %d.%d.%d.%d", RADIOLIB_VERSION_MAJOR, RADIOLIB_VERSION_MINOR, RADIOLIB_VERSION_PATCH, RADIOLIB_VERSION_EXTRA); - RADIOLIB_DEBUG_PRINTLN("Platform: " RADIOLIB_PLATFORM); - RADIOLIB_DEBUG_PRINTLN("Compiled: " __DATE__ " " __TIME__ "\n"); + RADIOLIB_DEBUG_BASIC_PRINTLN("RadioLib Debug Info"); + RADIOLIB_DEBUG_BASIC_PRINTLN("Version: %d.%d.%d.%d", RADIOLIB_VERSION_MAJOR, RADIOLIB_VERSION_MINOR, RADIOLIB_VERSION_PATCH, RADIOLIB_VERSION_EXTRA); + RADIOLIB_DEBUG_BASIC_PRINTLN("Platform: " RADIOLIB_PLATFORM); + RADIOLIB_DEBUG_BASIC_PRINTLN("Compiled: " __DATE__ " " __TIME__ "\n"); } void Module::term() { @@ -89,14 +89,14 @@ int16_t Module::SPIsetRegValue(uint16_t reg, uint8_t value, uint8_t msb, uint8_t } // check failed, print debug info - RADIOLIB_DEBUG_PRINTLN(); - RADIOLIB_DEBUG_PRINTLN("address:\t0x%X", reg); - RADIOLIB_DEBUG_PRINTLN("bits:\t\t%d %d", msb, lsb); - RADIOLIB_DEBUG_PRINTLN("value:\t\t0x%X", value); - RADIOLIB_DEBUG_PRINTLN("current:\t0x%X", currentValue); - RADIOLIB_DEBUG_PRINTLN("mask:\t\t0x%X", mask); - RADIOLIB_DEBUG_PRINTLN("new:\t\t0x%X", newValue); - RADIOLIB_DEBUG_PRINTLN("read:\t\t0x%X", readValue); + RADIOLIB_DEBUG_SPI_PRINTLN(); + RADIOLIB_DEBUG_SPI_PRINTLN("address:\t0x%X", reg); + RADIOLIB_DEBUG_SPI_PRINTLN("bits:\t\t%d %d", msb, lsb); + RADIOLIB_DEBUG_SPI_PRINTLN("value:\t\t0x%X", value); + RADIOLIB_DEBUG_SPI_PRINTLN("current:\t0x%X", currentValue); + RADIOLIB_DEBUG_SPI_PRINTLN("mask:\t\t0x%X", mask); + RADIOLIB_DEBUG_SPI_PRINTLN("new:\t\t0x%X", newValue); + RADIOLIB_DEBUG_SPI_PRINTLN("read:\t\t0x%X", readValue); return(RADIOLIB_ERR_SPI_WRITE_FAILED); #else @@ -182,19 +182,19 @@ void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* d } // print debug information - #if RADIOLIB_VERBOSE + #if RADIOLIB_DEBUG_SPI uint8_t* debugBuffPtr = NULL; if(cmd == SPIwriteCommand) { - RADIOLIB_VERBOSE_PRINT("W\t%X\t", reg); + RADIOLIB_DEBUG_SPI_PRINT("W\t%X\t", reg); debugBuffPtr = &buffOut[this->SPIaddrWidth/8]; } else if(cmd == SPIreadCommand) { - RADIOLIB_VERBOSE_PRINT("R\t%X\t", reg); + RADIOLIB_DEBUG_SPI_PRINT("R\t%X\t", reg); debugBuffPtr = &buffIn[this->SPIaddrWidth/8]; } for(size_t n = 0; n < numBytes; n++) { - RADIOLIB_VERBOSE_PRINT("%X\t", debugBuffPtr[n]); + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", debugBuffPtr[n]); } - RADIOLIB_VERBOSE_PRINTLN(); + RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(); #endif #if !RADIOLIB_STATIC_ONLY @@ -291,7 +291,7 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint while(this->hal->digitalRead(this->gpioPin)) { this->hal->yield(); if(this->hal->millis() - start >= timeout) { - RADIOLIB_DEBUG_PRINTLN("GPIO pre-transfer timeout, is it connected?"); + RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO pre-transfer timeout, is it connected?"); #if !RADIOLIB_STATIC_ONLY delete[] buffOut; delete[] buffIn; @@ -318,7 +318,7 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint while(this->hal->digitalRead(this->gpioPin)) { this->hal->yield(); if(this->hal->millis() - start >= timeout) { - RADIOLIB_DEBUG_PRINTLN("GPIO post-transfer timeout, is it connected?"); + RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO post-transfer timeout, is it connected?"); #if !RADIOLIB_STATIC_ONLY delete[] buffOut; delete[] buffIn; @@ -342,31 +342,34 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint } // print debug information - #if RADIOLIB_VERBOSE + #if RADIOLIB_DEBUG_SPI // print command byte(s) - RADIOLIB_VERBOSE_PRINT("CMD"); + RADIOLIB_DEBUG_SPI_PRINT("CMD"); if(write) { - RADIOLIB_VERBOSE_PRINT("W\t"); + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("W\t"); } else { - RADIOLIB_VERBOSE_PRINT("R\t"); + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("R\t"); } size_t n = 0; for(; n < cmdLen; n++) { - RADIOLIB_VERBOSE_PRINT("%X\t", cmd[n]); + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", cmd[n]); } - RADIOLIB_VERBOSE_PRINTLN(); + RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(); // print data bytes - RADIOLIB_VERBOSE_PRINT("SI\t"); + RADIOLIB_DEBUG_SPI_PRINT("SI\t"); + for(n = 0; n < cmdLen; n++) { + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("\t"); + } for(; n < buffLen; n++) { - RADIOLIB_VERBOSE_PRINT("%X\t", buffOut[n]); + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", buffOut[n]); } - RADIOLIB_VERBOSE_PRINTLN(); - RADIOLIB_VERBOSE_PRINT("SO\t"); - for(n = cmdLen; n < buffLen; n++) { - RADIOLIB_VERBOSE_PRINT("%X\t", buffIn[n]); + RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(); + RADIOLIB_DEBUG_SPI_PRINT("SO\t"); + for(n = 0; n < buffLen; n++) { + RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", buffIn[n]); } - RADIOLIB_VERBOSE_PRINTLN(); + RADIOLIB_DEBUG_SPI_PRINTLN_NOTAG(); #endif #if !RADIOLIB_STATIC_ONLY @@ -404,7 +407,7 @@ uint32_t Module::reflect(uint32_t in, uint8_t bits) { } #if RADIOLIB_DEBUG -void Module::hexdump(uint8_t* data, size_t len, uint32_t offset, uint8_t width, bool be) { +void Module::hexdump(const char* level, uint8_t* data, size_t len, uint32_t offset, uint8_t width, bool be) { size_t rem_len = len; for(size_t i = 0; i < len; i+=16) { char str[80]; @@ -443,20 +446,23 @@ void Module::hexdump(uint8_t* data, size_t len, uint32_t offset, uint8_t width, for(size_t j = line_len; j < 16; j++) { sprintf(&str[58 + j], " "); } + if(level) { + RADIOLIB_DEBUG_PRINT(level); + } RADIOLIB_DEBUG_PRINT(str); RADIOLIB_DEBUG_PRINTLN(); rem_len -= 16; } } -void Module::regdump(uint16_t start, size_t len) { +void Module::regdump(const char* level, uint16_t start, size_t len) { #if RADIOLIB_STATIC_ONLY uint8_t buff[RADIOLIB_STATIC_ARRAY_SIZE]; #else uint8_t* buff = new uint8_t[len]; #endif SPIreadRegisterBurst(start, len, buff); - hexdump(buff, len, start); + hexdump(level, buff, len, start); #if !RADIOLIB_STATIC_ONLY delete[] buff; #endif diff --git a/src/Module.h b/src/Module.h index c67aaf6a..9b5b78db 100644 --- a/src/Module.h +++ b/src/Module.h @@ -471,19 +471,21 @@ class Module { #if RADIOLIB_DEBUG /*! \brief Function to dump data as hex into the debug port. + \param level RadioLib debug level, set to NULL to not print. \param data Data to dump. \param len Number of bytes to dump. \param width Word width (1 for uint8_t, 2 for uint16_t, 4 for uint32_t). \param be Print multi-byte data as big endian. Defaults to false. */ - static void hexdump(uint8_t* data, size_t len, uint32_t offset = 0, uint8_t width = 1, bool be = false); + static void hexdump(const char* level, uint8_t* data, size_t len, uint32_t offset = 0, uint8_t width = 1, bool be = false); /*! \brief Function to dump device registers as hex into the debug port. + \param level RadioLib debug level, set to NULL to not print. \param start First address to dump. \param len Number of bytes to dump. */ - void regdump(uint16_t start, size_t len); + void regdump(const char* level, uint16_t start, size_t len); #endif #if RADIOLIB_DEBUG and defined(RADIOLIB_BUILD_ARDUINO) diff --git a/src/TypeDef.h b/src/TypeDef.h index 42334988..818d99d5 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -528,35 +528,35 @@ */ #define RADIOLIB_ERR_COMMAND_QUEUE_FULL (-1109) -/*! - \brief Unable to pop existing MAC command because the queue is empty. -*/ -#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) +#define RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND (-1110) /*! \brief Unable to join network because JoinNonce is not higher than saved value. */ -#define RADIOLIB_ERR_JOIN_NONCE_INVALID (-1112) +#define RADIOLIB_ERR_JOIN_NONCE_INVALID (-1111) /*! \brief Received downlink Network frame counter is invalid (lower than last heard value). */ -#define RADIOLIB_ERR_N_FCNT_DOWN_INVALID (-1113) +#define RADIOLIB_ERR_N_FCNT_DOWN_INVALID (-1112) /*! \brief Received downlink Application frame counter is invalid (lower than last heard value). */ -#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1114) +#define RADIOLIB_ERR_A_FCNT_DOWN_INVALID (-1113) /*! \brief Uplink payload length at this datarate exceeds the active dwell time limitations. */ -#define RADIOLIB_ERR_DWELL_TIME_EXCEEDED (-1115) +#define RADIOLIB_ERR_DWELL_TIME_EXCEEDED (-1114) + +/*! + \brief The buffer integrity check did not match the supplied checksum value. +*/ +#define RADIOLIB_ERR_CHECKSUM_MISMATCH (-1115) /*! \} diff --git a/src/modules/CC1101/CC1101.cpp b/src/modules/CC1101/CC1101.cpp index fbfa212b..447e0420 100644 --- a/src/modules/CC1101/CC1101.cpp +++ b/src/modules/CC1101/CC1101.cpp @@ -21,18 +21,18 @@ int16_t CC1101::begin(float freq, float br, float freqDev, float rxBw, int8_t pw if((version == RADIOLIB_CC1101_VERSION_CURRENT) || (version == RADIOLIB_CC1101_VERSION_LEGACY) || (version == RADIOLIB_CC1101_VERSION_CLONE)) { flagFound = true; } else { - RADIOLIB_DEBUG_PRINTLN("CC1101 not found! (%d of 10 tries) RADIOLIB_CC1101_REG_VERSION == 0x%04X, expected 0x0004/0x0014", i + 1, version); + RADIOLIB_DEBUG_BASIC_PRINTLN("CC1101 not found! (%d of 10 tries) RADIOLIB_CC1101_REG_VERSION == 0x%04X, expected 0x0004/0x0014", i + 1, version); this->mod->hal->delay(10); i++; } } if(!flagFound) { - RADIOLIB_DEBUG_PRINTLN("No CC1101 found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No CC1101 found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } else { - RADIOLIB_DEBUG_PRINTLN("M\tCC1101"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tCC1101"); } // configure settings not accessible by API @@ -916,7 +916,6 @@ void CC1101::setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], uint8_t CC1101::randomByte() { // set mode to Rx SPIsendCommand(RADIOLIB_CC1101_CMD_RX); - RADIOLIB_DEBUG_PRINTLN("CC1101::randomByte"); // wait a bit for the RSSI reading to stabilise this->mod->hal->delay(10); @@ -1113,7 +1112,7 @@ void CC1101::SPIsendCommand(uint8_t cmd) { // stop transfer this->mod->hal->spiEndTransaction(); this->mod->hal->digitalWrite(this->mod->getCs(), this->mod->hal->GpioLevelHigh); - RADIOLIB_VERBOSE_PRINTLN("CMD\tW\t%02X\t%02X", cmd, status); + RADIOLIB_DEBUG_SPI_PRINTLN("CMD\tW\t%02X\t%02X", cmd, status); (void)status; } diff --git a/src/modules/CC1101/CC1101.h b/src/modules/CC1101/CC1101.h index e3354ae1..9d2254a4 100644 --- a/src/modules/CC1101/CC1101.h +++ b/src/modules/CC1101/CC1101.h @@ -599,14 +599,14 @@ class CC1101: public PhysicalLayer { int16_t standby(uint8_t mode) override; /*! - \brief Starts direct mode transmission. + \brief Starts synchronous direct mode transmission. \param frf Raw RF frequency value. Defaults to 0, required for quick frequency shifts in RTTY. \returns \ref status_codes */ int16_t transmitDirect(uint32_t frf = 0) override; /*! - \brief Starts direct mode reception. + \brief Starts synchronous direct mode reception. \returns \ref status_codes */ int16_t receiveDirect() override; diff --git a/src/modules/RF69/RF69.cpp b/src/modules/RF69/RF69.cpp index 54e6cc74..a934898e 100644 --- a/src/modules/RF69/RF69.cpp +++ b/src/modules/RF69/RF69.cpp @@ -23,18 +23,18 @@ int16_t RF69::begin(float freq, float br, float freqDev, float rxBw, int8_t pwr, if(version == RADIOLIB_RF69_CHIP_VERSION) { flagFound = true; } else { - RADIOLIB_DEBUG_PRINTLN("RF69 not found! (%d of 10 tries) RADIOLIB_RF69_REG_VERSION == 0x%04X, expected 0x0024", i + 1, version); + RADIOLIB_DEBUG_BASIC_PRINTLN("RF69 not found! (%d of 10 tries) RADIOLIB_RF69_REG_VERSION == 0x%04X, expected 0x0024", i + 1, version); this->mod->hal->delay(10); i++; } } if(!flagFound) { - RADIOLIB_DEBUG_PRINTLN("No RF69 found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No RF69 found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } else { - RADIOLIB_DEBUG_PRINTLN("M\tRF69"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tRF69"); } // configure settings not accessible by API diff --git a/src/modules/SX123x/SX1231.cpp b/src/modules/SX123x/SX1231.cpp index 44ff904d..11424eed 100644 --- a/src/modules/SX123x/SX1231.cpp +++ b/src/modules/SX123x/SX1231.cpp @@ -21,23 +21,23 @@ int16_t SX1231::begin(float freq, float br, float freqDev, float rxBw, int8_t po flagFound = true; this->chipRevision = version; } else { - RADIOLIB_DEBUG_PRINTLN("SX1231 not found! (%d of 10 tries) RF69_REG_VERSION == 0x%04X, expected 0x0021 / 0x0022 / 0x0023", i + 1, version); + RADIOLIB_DEBUG_BASIC_PRINTLN("SX1231 not found! (%d of 10 tries) RF69_REG_VERSION == 0x%04X, expected 0x0021 / 0x0022 / 0x0023", i + 1, version); mod->hal->delay(10); i++; } } if(!flagFound) { - RADIOLIB_DEBUG_PRINTLN("No SX1231 found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX1231 found!"); mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX1231"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX1231"); // configure settings not accessible by API int16_t state = config(); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PRINTLN("M\tRF69"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tRF69"); // configure publicly accessible settings state = setFrequency(freq); diff --git a/src/modules/SX123x/SX1233.cpp b/src/modules/SX123x/SX1233.cpp index ab8808f9..16c4bca1 100644 --- a/src/modules/SX123x/SX1233.cpp +++ b/src/modules/SX123x/SX1233.cpp @@ -22,23 +22,23 @@ int16_t SX1233::begin(float freq, float br, float freqDev, float rxBw, int8_t po flagFound = true; this->chipRevision = version; } else { - RADIOLIB_DEBUG_PRINTLN("SX1231 not found! (%d of 10 tries) RF69_REG_VERSION == 0x%04X, expected 0x0021 / 0x0022 / 0x0023", i + 1, version); + RADIOLIB_DEBUG_BASIC_PRINTLN("SX1231 not found! (%d of 10 tries) RF69_REG_VERSION == 0x%04X, expected 0x0021 / 0x0022 / 0x0023", i + 1, version); mod->hal->delay(10); i++; } } if(!flagFound) { - RADIOLIB_DEBUG_PRINTLN("No SX1233 found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX1233 found!"); mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX1233"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX1233"); // configure settings not accessible by API int16_t state = config(); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PRINTLN("M\tRF69"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tRF69"); // configure publicly accessible settings state = setFrequency(freq); diff --git a/src/modules/SX126x/SX126x.cpp b/src/modules/SX126x/SX126x.cpp index 8ba17ac0..161a5245 100644 --- a/src/modules/SX126x/SX126x.cpp +++ b/src/modules/SX126x/SX126x.cpp @@ -6,6 +6,7 @@ SX126x::SX126x(Module* mod) : PhysicalLayer(RADIOLIB_SX126X_FREQUENCY_STEP_SIZE, RADIOLIB_SX126X_MAX_PACKET_LENGTH) { this->mod = mod; this->XTAL = false; + this->standbyXOSC = false; } int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, float tcxoVoltage, bool useRegulatorLDO) { @@ -22,11 +23,11 @@ int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, flo // try to find the SX126x chip if(!SX126x::findChip(this->chipType)) { - RADIOLIB_DEBUG_PRINTLN("No SX126x found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX126x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX126x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX126x"); // BW in kHz and SF are required in order to calculate LDRO for setModulationParams // set the defaults, this will get overwritten later anyway @@ -107,11 +108,11 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleL // try to find the SX126x chip if(!SX126x::findChip(this->chipType)) { - RADIOLIB_DEBUG_PRINTLN("No SX126x found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX126x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX126x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX126x"); // initialize configuration variables (will be overwritten during public settings configuration) this->bitRate = 21333; // 48.0 kbps @@ -246,7 +247,7 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { return(RADIOLIB_ERR_UNKNOWN); } - RADIOLIB_DEBUG_PRINTLN("Timeout in %lu us", timeout); + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); // start transmission state = startTransmit(data, len, addr); @@ -295,7 +296,7 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { return(RADIOLIB_ERR_UNKNOWN); } - RADIOLIB_DEBUG_PRINTLN("Timeout in %lu us", timeout); + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); // start reception uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); @@ -463,7 +464,7 @@ int16_t SX126x::sleep(bool retainConfig) { } int16_t SX126x::standby() { - return(SX126x::standby(RADIOLIB_SX126X_STANDBY_RC)); + return(SX126x::standby(this->standbyXOSC ? RADIOLIB_SX126X_STANDBY_XOSC : RADIOLIB_SX126X_STANDBY_RC)); } int16_t SX126x::standby(uint8_t mode, bool wakeup) { @@ -643,7 +644,7 @@ int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_ uint32_t symbolLength = ((uint32_t)(10 * 1000) << this->spreadingFactor) / (10 * this->bandwidthKhz); uint32_t sleepPeriod = symbolLength * sleepSymbols; - RADIOLIB_DEBUG_PRINTLN("Auto sleep period: %lu", sleepPeriod); + RADIOLIB_DEBUG_BASIC_PRINTLN("Auto sleep period: %lu", sleepPeriod); // when the unit detects a preamble, it starts a timer that will timeout if it doesn't receive a header in time. // the duration is sleepPeriod + 2 * wakePeriod. @@ -654,7 +655,7 @@ int16_t SX126x::startReceiveDutyCycleAuto(uint16_t senderPreambleLength, uint16_ uint32_t wakePeriod = RADIOLIB_MAX( (symbolLength * (senderPreambleLength + 1) - (sleepPeriod - 1000)) / 2, // (A) symbolLength * (minSymbols + 1)); //(B) - RADIOLIB_DEBUG_PRINTLN("Auto wake period: %lu", wakePeriod); + RADIOLIB_DEBUG_BASIC_PRINTLN("Auto wake period: %lu", wakePeriod); // If our sleep period is shorter than our transition time, just use the standard startReceive if(sleepPeriod < this->tcxoDelay + 1016) { @@ -1580,10 +1581,10 @@ int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) RADIOLIB_ASSERT(state); // check the version - #if RADIOLIB_DEBUG + #if RADIOLIB_DEBUG_BASIC char ver_pre[16]; this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_pre); - RADIOLIB_DEBUG_PRINTLN("Pre-update version string: %s", ver_pre); + RADIOLIB_DEBUG_BASIC_PRINTLN("Pre-update version string: %s", ver_pre); #endif // enable patch update @@ -1612,10 +1613,10 @@ int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_PRAM_UPDATE, NULL, 0); // check the version again - #if RADIOLIB_DEBUG + #if RADIOLIB_DEBUG_BASIC char ver_post[16]; this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_post); - RADIOLIB_DEBUG_PRINTLN("Post-update version string: %s", ver_post); + RADIOLIB_DEBUG_BASIC_PRINTLN("Post-update version string: %s", ver_post); #endif return(state); @@ -1857,12 +1858,12 @@ int16_t SX126x::calibrateImage(float freqMin, float freqMax) { int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data, 2); // if something failed, show the device errors - #if RADIOLIB_DEBUG + #if RADIOLIB_DEBUG_BASIC if(state != RADIOLIB_ERR_NONE) { // unless mode is forced to standby, device errors will be 0 standby(); uint16_t errors = getDeviceErrors(); - RADIOLIB_DEBUG_PRINTLN("Calibration failed, device errors: 0x%X", errors); + RADIOLIB_DEBUG_BASIC_PRINTLN("Calibration failed, device errors: 0x%X", errors); } #endif return(state); @@ -2086,7 +2087,7 @@ int16_t SX126x::config(uint8_t modem) { RADIOLIB_ASSERT(state); // set Rx/Tx fallback mode to STDBY_RC - data[0] = RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC; + data[0] = this->standbyXOSC ? RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_XOSC : RADIOLIB_SX126X_RX_TX_FALLBACK_MODE_STDBY_RC; state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RX_TX_FALLBACK_MODE, data, 1); RADIOLIB_ASSERT(state); @@ -2121,12 +2122,12 @@ int16_t SX126x::config(uint8_t modem) { state = this->mod->SPIcheckStream(); // if something failed, show the device errors - #if RADIOLIB_DEBUG + #if RADIOLIB_DEBUG_BASIC if(state != RADIOLIB_ERR_NONE) { // unless mode is forced to standby, device errors will be 0 standby(); uint16_t errors = getDeviceErrors(); - RADIOLIB_DEBUG_PRINTLN("Calibration failed, device errors: 0x%X", errors); + RADIOLIB_DEBUG_BASIC_PRINTLN("Calibration failed, device errors: 0x%X", errors); } #endif @@ -2159,15 +2160,15 @@ bool SX126x::findChip(const char* verStr) { // check version register if(strncmp(verStr, version, 6) == 0) { - RADIOLIB_DEBUG_PRINTLN("Found SX126x: RADIOLIB_SX126X_REG_VERSION_STRING:"); - RADIOLIB_DEBUG_HEXDUMP((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING); - RADIOLIB_DEBUG_PRINTLN(); + RADIOLIB_DEBUG_BASIC_PRINTLN("Found SX126x: RADIOLIB_SX126X_REG_VERSION_STRING:"); + RADIOLIB_DEBUG_BASIC_HEXDUMP((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING); + RADIOLIB_DEBUG_BASIC_PRINTLN(); flagFound = true; } else { - #if RADIOLIB_DEBUG - RADIOLIB_DEBUG_PRINTLN("SX126x not found! (%d of 10 tries) RADIOLIB_SX126X_REG_VERSION_STRING:", i + 1); - RADIOLIB_DEBUG_HEXDUMP((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING); - RADIOLIB_DEBUG_PRINTLN("Expected string: %s", verStr); + #if RADIOLIB_DEBUG_BASIC + RADIOLIB_DEBUG_BASIC_PRINTLN("SX126x not found! (%d of 10 tries) RADIOLIB_SX126X_REG_VERSION_STRING:", i + 1); + RADIOLIB_DEBUG_BASIC_HEXDUMP((uint8_t*)version, 16, RADIOLIB_SX126X_REG_VERSION_STRING); + RADIOLIB_DEBUG_BASIC_PRINTLN("Expected string: %s", verStr); #endif this->mod->hal->delay(10); i++; diff --git a/src/modules/SX126x/SX126x.h b/src/modules/SX126x/SX126x.h index f02d5f7d..848433b8 100644 --- a/src/modules/SX126x/SX126x.h +++ b/src/modules/SX126x/SX126x.h @@ -447,6 +447,11 @@ class SX126x: public PhysicalLayer { */ bool XTAL; + /*! + \brief Whether to use XOSC (true) or RC (false) oscillator in standby mode. Defaults to false. + */ + bool standbyXOSC; + // basic methods /*! diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index bdf4bae5..7606f001 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -14,11 +14,11 @@ int16_t SX127x::begin(uint8_t* chipVersions, uint8_t numVersions, uint8_t syncWo // try to find the SX127x chip if(!SX127x::findChip(chipVersions, numVersions)) { - RADIOLIB_DEBUG_PRINTLN("No SX127x found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX127x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX127x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX127x"); // set mode to standby int16_t state = standby(); @@ -65,11 +65,11 @@ int16_t SX127x::beginFSK(uint8_t* chipVersions, uint8_t numVersions, float freqD // try to find the SX127x chip if(!SX127x::findChip(chipVersions, numVersions)) { - RADIOLIB_DEBUG_PRINTLN("No SX127x found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No SX127x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tSX127x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX127x"); // set mode to standby int16_t state = standby(); @@ -1554,7 +1554,7 @@ bool SX127x::findChip(uint8_t* vers, uint8_t num) { } if(!flagFound) { - RADIOLIB_DEBUG_PRINTLN("SX127x not found! (%d of 10 tries) RADIOLIB_SX127X_REG_VERSION == 0x%04X", i + 1, version); + RADIOLIB_DEBUG_BASIC_PRINTLN("SX127x not found! (%d of 10 tries) RADIOLIB_SX127X_REG_VERSION == 0x%04X", i + 1, version); this->mod->hal->delay(10); i++; } diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp index 07d8661b..55b18dd9 100644 --- a/src/modules/SX128x/SX128x.cpp +++ b/src/modules/SX128x/SX128x.cpp @@ -17,7 +17,7 @@ int16_t SX128x::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; this->mod->SPIstreamType = true; this->mod->SPIparseStatusCb = SPIparseStatus; - RADIOLIB_DEBUG_PRINTLN("M\tSX128x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize LoRa modulation variables this->bandwidthKhz = bw; @@ -78,7 +78,7 @@ int16_t SX128x::beginGFSK(float freq, uint16_t br, float freqDev, int8_t pwr, ui this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; this->mod->SPIstreamType = true; this->mod->SPIparseStatusCb = SPIparseStatus; - RADIOLIB_DEBUG_PRINTLN("M\tSX128x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize GFSK modulation variables this->bitRateKbps = br; @@ -147,7 +147,7 @@ int16_t SX128x::beginBLE(float freq, uint16_t br, float freqDev, int8_t pwr, uin this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; this->mod->SPIstreamType = true; this->mod->SPIparseStatusCb = SPIparseStatus; - RADIOLIB_DEBUG_PRINTLN("M\tSX128x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize BLE modulation variables this->bitRateKbps = br; @@ -202,7 +202,7 @@ int16_t SX128x::beginFLRC(float freq, uint16_t br, uint8_t cr, int8_t pwr, uint1 this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; this->mod->SPIstreamType = true; this->mod->SPIparseStatusCb = SPIparseStatus; - RADIOLIB_DEBUG_PRINTLN("M\tSX128x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize FLRC modulation variables this->bitRateKbps = br; @@ -308,7 +308,7 @@ int16_t SX128x::transmit(uint8_t* data, size_t len, uint8_t addr) { // calculate timeout (500% of expected time-on-air) uint32_t timeout = getTimeOnAir(len) * 5; - RADIOLIB_DEBUG_PRINTLN("Timeout in %lu us", timeout); + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); // start transmission state = startTransmit(data, len, addr); @@ -341,7 +341,7 @@ int16_t SX128x::receive(uint8_t* data, size_t len) { // calculate timeout (1000% of expected time-on-air) uint32_t timeout = getTimeOnAir(len) * 10; - RADIOLIB_DEBUG_PRINTLN("Timeout in %lu us", timeout); + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); // start reception uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); diff --git a/src/modules/Si443x/Si4430.cpp b/src/modules/Si443x/Si4430.cpp index 45b55356..7f9ed5fb 100644 --- a/src/modules/Si443x/Si4430.cpp +++ b/src/modules/Si443x/Si4430.cpp @@ -9,7 +9,7 @@ int16_t Si4430::begin(float freq, float br, float freqDev, float rxBw, int8_t po // execute common part int16_t state = Si443x::begin(br, freqDev, rxBw, preambleLen); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PRINTLN("M\tSi4430"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSi4430"); // configure publicly accessible settings state = setFrequency(freq); diff --git a/src/modules/Si443x/Si4431.cpp b/src/modules/Si443x/Si4431.cpp index 799cec76..c603e6f1 100644 --- a/src/modules/Si443x/Si4431.cpp +++ b/src/modules/Si443x/Si4431.cpp @@ -9,7 +9,7 @@ int16_t Si4431::begin(float freq, float br, float freqDev, float rxBw, int8_t po // execute common part int16_t state = Si443x::begin(br, freqDev, rxBw, preambleLen); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PRINTLN("M\tSi4431"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSi4431"); // configure publicly accessible settings state = setFrequency(freq); diff --git a/src/modules/Si443x/Si4432.cpp b/src/modules/Si443x/Si4432.cpp index 40a97226..56690862 100644 --- a/src/modules/Si443x/Si4432.cpp +++ b/src/modules/Si443x/Si4432.cpp @@ -9,7 +9,7 @@ int16_t Si4432::begin(float freq, float br, float freqDev, float rxBw, int8_t po // execute common part int16_t state = Si443x::begin(br, freqDev, rxBw, preambleLen); RADIOLIB_ASSERT(state); - RADIOLIB_DEBUG_PRINTLN("M\tSi4432"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSi4432"); // configure publicly accessible settings state = setFrequency(freq); diff --git a/src/modules/Si443x/Si443x.cpp b/src/modules/Si443x/Si443x.cpp index 2097ee58..541f3eb7 100644 --- a/src/modules/Si443x/Si443x.cpp +++ b/src/modules/Si443x/Si443x.cpp @@ -15,11 +15,11 @@ int16_t Si443x::begin(float br, float freqDev, float rxBw, uint8_t preambleLen) // try to find the Si443x chip if(!Si443x::findChip()) { - RADIOLIB_DEBUG_PRINTLN("No Si443x found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No Si443x found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } else { - RADIOLIB_DEBUG_PRINTLN("M\tSi443x"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSi443x"); } // reset the device @@ -700,7 +700,7 @@ bool Si443x::findChip() { if(version == RADIOLIB_SI443X_DEVICE_VERSION) { flagFound = true; } else { - RADIOLIB_DEBUG_PRINTLN("Si443x not found! (%d of 10 tries) RADIOLIB_SI443X_REG_DEVICE_VERSION == 0x%02X, expected 0x0%X", i + 1, version, RADIOLIB_SI443X_DEVICE_VERSION); + RADIOLIB_DEBUG_BASIC_PRINTLN("Si443x not found! (%d of 10 tries) RADIOLIB_SI443X_REG_DEVICE_VERSION == 0x%02X, expected 0x0%X", i + 1, version, RADIOLIB_SI443X_DEVICE_VERSION); this->mod->hal->delay(10); i++; } @@ -769,9 +769,9 @@ int16_t Si443x::updateClockRecovery() { uint16_t rxOsr_fixed = (uint16_t)rxOsr; // print that whole mess - RADIOLIB_DEBUG_PRINTLN("%X\n%X\n%X", bypass, decRate, manch); - RADIOLIB_DEBUG_PRINT_FLOAT(rxOsr, 2); - RADIOLIB_DEBUG_PRINTLN("\t%d\t%X\n%lu\t%lX\n%d\t%X", rxOsr_fixed, rxOsr_fixed, ncoOff, ncoOff, crGain, crGain); + RADIOLIB_DEBUG_BASIC_PRINTLN("%X\n%X\n%X", bypass, decRate, manch); + RADIOLIB_DEBUG_BASIC_PRINT_FLOAT(rxOsr, 2); + RADIOLIB_DEBUG_BASIC_PRINTLN("\t%d\t%X\n%lu\t%lX\n%d\t%X", rxOsr_fixed, rxOsr_fixed, ncoOff, ncoOff, crGain, crGain); // update oversampling ratio int16_t state = this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_CLOCK_REC_OFFSET_2, (uint8_t)((rxOsr_fixed & 0x0700) >> 3), 7, 5); diff --git a/src/modules/nRF24/nRF24.cpp b/src/modules/nRF24/nRF24.cpp index 3f64f27f..a5429884 100644 --- a/src/modules/nRF24/nRF24.cpp +++ b/src/modules/nRF24/nRF24.cpp @@ -23,11 +23,11 @@ int16_t nRF24::begin(int16_t freq, int16_t dr, int8_t pwr, uint8_t addrWidth) { // check SPI connection int16_t val = this->mod->SPIgetRegValue(RADIOLIB_NRF24_REG_SETUP_AW); if(!((val >= 0) && (val <= 3))) { - RADIOLIB_DEBUG_PRINTLN("No nRF24 found!"); + RADIOLIB_DEBUG_BASIC_PRINTLN("No nRF24 found!"); this->mod->term(); return(RADIOLIB_ERR_CHIP_NOT_FOUND); } - RADIOLIB_DEBUG_PRINTLN("M\tnRF24"); + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tnRF24"); // configure settings inaccessible by public API int16_t state = config(); diff --git a/src/protocols/LoRaWAN/LoRaWAN.cpp b/src/protocols/LoRaWAN/LoRaWAN.cpp index cbbe15a1..0e7692c3 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -6,10 +6,6 @@ #if !RADIOLIB_EXCLUDE_LORAWAN -#if defined(RADIOLIB_EEPROM_UNSUPPORTED) - #warning "Persistent storage not supported!" -#endif - // flag to indicate whether there was some action during Rx mode (timeout or downlink) static volatile bool downlinkAction = false; @@ -48,172 +44,210 @@ void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA this->enableCSMA = enableCSMA; } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) void LoRaWANNode::wipe() { - Module* mod = this->phyLayer->getMod(); - mod->hal->wipePersistentStorage(); + memset(this->bufferNonces, 0, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); + memset(this->bufferSession, 0, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); } -int16_t LoRaWANNode::restore() { - // if already joined, ignore - if(this->activeMode != RADIOLIB_LORAWAN_MODE_NONE) { - return(this->activeMode); +uint8_t* LoRaWANNode::getBufferNonces() { + return(this->bufferNonces); +} + +int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) { + if(this->isJoined()) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Did not update buffer: session already active"); + return(RADIOLIB_ERR_NONE); } - Module* mod = this->phyLayer->getMod(); + int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); + RADIOLIB_ASSERT(state); - uint8_t nvm_table_version = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_TABLE_VERSION_ID); - // if (RADIOLIB_EEPROM_LORAWAN_TABLE_VERSION > nvm_table_version) { - // // set default values for variables that are new or something - // } - (void)nvm_table_version; + // copy the whole buffer over + memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); - // check the mode value - uint16_t lwMode = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID); - if(lwMode == RADIOLIB_LORAWAN_MODE_NONE) { - #if RADIOLIB_DEBUG - RADIOLIB_DEBUG_PRINTLN("mode value not set (no saved session)"); - RADIOLIB_DEBUG_PRINTLN("first 16 bytes of NVM:"); - uint8_t nvmBuff[16]; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(0), nvmBuff, 16); - RADIOLIB_DEBUG_HEXDUMP(nvmBuff, 16); - #endif - // the mode value is not set, user will have to do perform the join procedure + // revert to inactive as long as no session is restored + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)false; + + return(state); +} + +uint8_t* LoRaWANNode::getBufferSession() { + // update buffer contents + this->saveSession(); + + return(this->bufferSession); +} + +int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) { + if(this->isJoined()) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Did not update buffer: session already active"); + return(RADIOLIB_ERR_NONE); + } + + int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); + RADIOLIB_ASSERT(state); + + // the Nonces buffer holds a checksum signature - compare this to the signature that is in the session buffer + uint16_t signatureNonces = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_SIGNATURE]); + uint16_t signatureInSession = LoRaWANNode::ntoh(&persistentBuffer[RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE]); + if(signatureNonces != signatureInSession) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The supplied session buffer does not match the Nonces buffer"); + return(RADIOLIB_ERR_CHECKSUM_MISMATCH); + } + + // copy the whole buffer over + memcpy(this->bufferSession, persistentBuffer, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); + + // as both the Nonces and session are restored, revert to active session + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; + + return(state); +} + +int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) { + // check if there are actually values in the buffer + size_t i = 0; + for(; i < size; i++) { + if(buffer[i]) { + break; + } + } + if(i == size) { return(RADIOLIB_ERR_NETWORK_NOT_JOINED); } - if(!this->isValidSession()) { + // check integrity of the whole buffer (compare checksum to included checksum) + uint16_t checkSum = LoRaWANNode::checkSum16(buffer, size - 2); + uint16_t signature = LoRaWANNode::ntoh(&buffer[size - 2]); + if(signature != checkSum) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Calculated checksum: %04X, expected: %04X", checkSum, signature); + return(RADIOLIB_ERR_CHECKSUM_MISMATCH); + } + return(RADIOLIB_ERR_NONE); +} + +int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan) { + // if already joined, ignore + if(this->activeMode != RADIOLIB_LORAWAN_MODE_NONE) { + return(RADIOLIB_ERR_NONE); + } + + bool isSameKeys = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CHECKSUM]) == checkSum; + bool isSameMode = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE]) == lwMode; + bool isSameClass = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CLASS]) == lwClass; + bool isSamePlan = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_PLAN]) == freqPlan; + + // check if Nonces buffer matches the current configuration + if(!isSameKeys || !isSameMode || !isSameClass || !isSamePlan) { + // if configuration did not match, discard whatever is currently in the buffers and start fresh + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Configuration mismatch (checksum: %d, mode: %d, class: %d, plan: %d)", isSameKeys, isSameMode, isSameClass, isSamePlan); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Nonces buffer:"); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Clearing buffer and starting fresh"); + this->wipe(); + return(RADIOLIB_ERR_NETWORK_NOT_JOINED); + } + + if(lwMode == RADIOLIB_LORAWAN_MODE_OTAA) { + // Nonces buffer is OK, so we can at least restore Nonces + this->devNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_DEV_NONCE]); + this->joinNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_JOIN_NONCE], 3); + } + + // uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LORAWAN_NONCES_VERSION]; + // if (RADIOLIB_LORAWAN_NONCES_VERSION_VAL > nvm_table_version) { + // // set default values for variables that are new or something + // } + + if(this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] == 0) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("No active session in progress; please join the network"); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); return(RADIOLIB_ERR_NETWORK_NOT_JOINED); } // pull all authentication keys from persistent storage - this->devAddr = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID); - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + this->devAddr = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DEV_ADDR]); + memcpy(this->appSKey, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_APP_SKEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->nwkSEncKey, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->fNwkSIntKey, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->sNwkSIntKey, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY], RADIOLIB_AES128_BLOCK_SIZE); - // get session parameters - this->rev = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_VERSION_ID); - RADIOLIB_DEBUG_PRINTLN("LoRaWAN session: v1.%d", this->rev); - this->devNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID); - this->joinNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID); - this->aFcntDown = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID); - this->nFcntDown = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_N_FCNT_DOWN_ID); - this->confFcntUp = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_UP_ID); - this->confFcntDown = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_DOWN_ID); - this->adrFcnt = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_ADR_FCNT_ID); + // restore session parameters + this->rev = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_VERSION]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LoRaWAN session: v1.%d", this->rev); + this->homeNetId = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_HOMENET_ID]); + this->aFcntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN]); + this->nFcntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN]); + this->confFcntUp = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP]); + this->confFcntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN]); + this->adrFcnt = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_FCNT]); + this->fcntUp = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_FCNT_UP]); - // fcntUp is stored in highly efficient wear-leveling system, so parse it - this->restoreFcntUp(); + int16_t state = RADIOLIB_ERR_UNKNOWN; - // get the defined channels - int16_t state = this->restoreChannels(); - RADIOLIB_ASSERT(state); + // for dynamic bands, first restore the defined channels before restoring ADR + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // restore the defined channels + state = this->restoreChannels(); + RADIOLIB_ASSERT(state); + } - // get MAC state + // restore the complete MAC state LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, + .len = MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn, .repeat = 0, }; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP], cmd.len); + (void)execMacCommand(&cmd); - // only apply the single ADR command on dynamic bands; fixed bands is done through channel restore - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + cmd.cid = RADIOLIB_LORAWAN_MAC_LINK_ADR; + cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], cmd.len); + (void)execMacCommand(&cmd); + + // for fixed bands, first restore ADR, then the defined channels + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_FIXED) { + state = this->restoreChannels(); + RADIOLIB_ASSERT(state); } cmd.cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DUTY_CYCLE].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DUTY_CYCLE_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE], cmd.len); + (void)execMacCommand(&cmd); cmd.cid = RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_RX_PARAM_SETUP_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP], cmd.len); + (void)execMacCommand(&cmd); cmd.cid = RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_RX_TIMING_SETUP_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); - - cmd.cid = RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_TX_PARAM_SETUP_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP], cmd.len); + (void)execMacCommand(&cmd); cmd.cid = RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_ADR_PARAM_SETUP_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP], cmd.len); + (void)execMacCommand(&cmd); cmd.cid = RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP; cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP].lenDn; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_REJOIN_PARAM_SETUP_ID), cmd.payload, cmd.len); - execMacCommand(&cmd, false); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP], cmd.len); + (void)execMacCommand(&cmd); - uint8_t queueBuff[sizeof(LoRaWANMacCommandQueue_t)] = { 0 }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_MAC_QUEUE_UL_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t)); - memcpy(&this->commandsUp, queueBuff, sizeof(LoRaWANMacCommandQueue_t)); - RADIOLIB_DEBUG_PRINTLN("Number of MAC commands: %d", this->commandsUp.numCommands); + // copy uplink MAC command queue back in place + memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t)); state = this->setPhyProperties(); RADIOLIB_ASSERT(state); // full session is restored, so set joined flag to whichever mode is restored - this->activeMode = lwMode; + this->activeMode = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE]); - return(this->activeMode); -} - -void LoRaWANNode::restoreFcntUp() { - Module* mod = this->phyLayer->getMod(); - - uint8_t fcntBuffStart = mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID); - uint8_t fcntBuffEnd = mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID + 1); - uint8_t buffSize = fcntBuffEnd - fcntBuffStart; - #if RADIOLIB_STATIC_ONLY - uint8_t fcntBuff[RADIOLIB_STATIC_ARRAY_SIZE]; - #else - uint8_t* fcntBuff = new uint8_t[buffSize]; - #endif - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID), fcntBuff, buffSize); - - // copy the two most significant bytes from the first two bytes - uint32_t bits_30_22 = (uint32_t)fcntBuff[0]; - uint32_t bits_22_14 = (uint32_t)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; - } - } - uint32_t bits_14_7 = (uint32_t)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 < buffSize; idx++) { - if(fcntBuff[idx] >> 7 != state) { - break; - } - } - uint32_t bits_7_0 = (uint32_t)fcntBuff[idx-1] & 0x7F; - #if !RADIOLIB_STATIC_ONLY - delete[] fcntBuff; - #endif - - this->fcntUp = (bits_30_22 << 22) | (bits_22_14 << 14) | (bits_14_7 << 7) | bits_7_0; + return(state); } int16_t LoRaWANNode::restoreChannels() { @@ -224,44 +258,34 @@ int16_t LoRaWANNode::restoreChannels() { this->setupChannelsFix(this->subBand); } - Module* mod = this->phyLayer->getMod(); uint8_t bufferZeroes[5] = { 0 }; if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - uint8_t numBytesUp = RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn; - uint8_t bufferUp[RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID), bufferUp, numBytesUp); - + uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS]; + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, .payload = { 0 }, .len = 0, .repeat = 0 }; for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn; - memcpy(cmd.payload, &(bufferUp[i * cmd.len]), cmd.len); + memcpy(cmd.payload, startChannelsUp + (i * cmd.len), cmd.len); if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes cmd.repeat = 1; - (void)execMacCommand(&cmd, false); + (void)execMacCommand(&cmd); } } - uint8_t numBytesDn = RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn; - uint8_t bufferDn[RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS * RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DL_CHANNELS_ID), bufferDn, numBytesDn); - + uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_DL_CHANNELS]; + cmd.cid = RADIOLIB_LORAWAN_MAC_DL_CHANNEL; - for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn; - memcpy(cmd.payload, &bufferDn[i * cmd.len], cmd.len); + memcpy(cmd.payload, startChannelsDown + (i * cmd.len), cmd.len); if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { // only execute if it is not all zeroes - (void)execMacCommand(&cmd, false); + (void)execMacCommand(&cmd); } } } else { // RADIOLIB_LORAWAN_BAND_FIXED - uint8_t numADRCommands = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID); - RADIOLIB_DEBUG_PRINTLN("Restoring %d stored channel masks", numADRCommands); - uint8_t numBytes = numADRCommands * MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; - uint8_t buffer[RADIOLIB_LORAWAN_MAX_NUM_ADR_COMMANDS * RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID), buffer, numBytes); - + uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS]; + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, .payload = { 0 }, @@ -269,92 +293,87 @@ int16_t LoRaWANNode::restoreChannels() { .repeat = 0, }; - for(int i = 0; i < numADRCommands; i++) { + // there are at most 8 channel masks present + for(int i = 0; i < 8; i++) { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; - memcpy(cmd.payload, &buffer[i * cmd.len], cmd.len); + memcpy(cmd.payload, startMACpayload + (i * cmd.len), cmd.len); // there COULD, according to spec, be an all zeroes ADR command - meh - if(memcmp(cmd.payload, bufferZeroes, cmd.len) != 0) { - cmd.repeat = (i+1); - execMacCommand(&cmd, false); + if(memcmp(cmd.payload, bufferZeroes, cmd.len) == 0) { + break; } + cmd.repeat = (i+1); + (void)execMacCommand(&cmd); } } return(RADIOLIB_ERR_NONE); } -void LoRaWANNode::clearSession() { - Module* mod = this->phyLayer->getMod(); - uint8_t zeroes[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID), zeroes, sizeof(uint32_t)); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID), zeroes, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID), zeroes, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID), zeroes, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID), zeroes, RADIOLIB_AES128_BLOCK_SIZE); - this->activeMode = RADIOLIB_LORAWAN_MODE_NONE; -} - -bool LoRaWANNode::isValidSession() { - uint8_t mask = 0; - Module* mod = this->phyLayer->getMod(); - uint8_t dummyBuf[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID), dummyBuf, sizeof(uint32_t)); - for(size_t i = 0; i < sizeof(uint32_t); i++) { - mask |= dummyBuf[i]; - } - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID), dummyBuf, RADIOLIB_AES128_BLOCK_SIZE); - for(size_t i = 0; i < RADIOLIB_AES128_BLOCK_SIZE; i++) { - mask |= dummyBuf[i]; - } - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID), dummyBuf, RADIOLIB_AES128_BLOCK_SIZE); - for(size_t i = 0; i < RADIOLIB_AES128_BLOCK_SIZE; i++) { - mask |= dummyBuf[i]; - } - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID), dummyBuf, RADIOLIB_AES128_BLOCK_SIZE); - for(size_t i = 0; i < RADIOLIB_AES128_BLOCK_SIZE; i++) { - mask |= dummyBuf[i]; - } - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID), dummyBuf, RADIOLIB_AES128_BLOCK_SIZE); - for(size_t i = 0; i < RADIOLIB_AES128_BLOCK_SIZE; i++) { - mask |= dummyBuf[i]; - } - - return(mask > 0); -} - -#endif // RADIOLIB_EEPROM_UNSUPPORTED - -void LoRaWANNode::beginCommon(uint8_t joinDr) { +void LoRaWANNode::beginCommon(uint8_t initialDr) { // in case a new session is started while there is an ongoing session // clear the MAC queues completely memset(&(this->commandsUp), 0, sizeof(LoRaWANMacCommandQueue_t)); memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); + uint8_t drUp = 0; + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // if join datarate is user-specified and valid, select that value + if(initialDr != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + if(initialDr >= this->band->txFreqs[0].drMin && initialDr <= this->band->txFreqs[0].drMax) { + drUp = initialDr; + } else { + // if there is no channel that allowed the user-specified datarate, revert to default datarate + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Datarate %d is not valid - using default", initialDr); + initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; + } + } + + // if there is no (channel that allowed the) user-specified datarate, use a default datarate + // we use the floor of the average datarate of the first default channel + if(initialDr == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + drUp = (this->band->txFreqs[0].drMin + this->band->txFreqs[0].drMax) / 2; + } + + } else { + // if the user specified a certain datarate, check if any of the configured channels allows it + if(initialDr != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + uint8_t i = 0; + for(; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + if(initialDr >= this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin + && initialDr <= this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax) { + break; + } + } + } + // if there is no channel that allowed the user-specified datarate, revert to default datarate + if(i == RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Datarate %d is not valid - using default", initialDr); + initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; + } + } + + // if there is no (channel that allowed the) user-specified datarate, use a default datarate + // we use the join-request datarate for one of the available channels + if(initialDr == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + // randomly select one of 8 or 9 channels and find corresponding datarate + uint8_t numChannels = this->band->numTxSpans == 1 ? 8 : 9; + uint8_t rand = this->phyLayer->random(numChannels) + 1; // range 1-8 or 1-9 + if(rand <= 8) { + drUp = this->band->txSpans[0].joinRequestDataRate; // if one of the first 8 channels, select datarate of span 0 + } else { + drUp = this->band->txSpans[1].joinRequestDataRate; // if ninth channel, select datarate of span 1 + } + } + + } + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, .payload = { 0 }, .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, .repeat = 0, }; - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - uint8_t drUp = 0; - // if join datarate is user-specified and valid, select that value; otherwise use - if(joinDr != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { - if(joinDr >= this->band->txFreqs[0].drMin && joinDr <= this->band->txFreqs[0].drMax) { - drUp = joinDr; - } else { - RADIOLIB_DEBUG_PRINTLN("Datarate %d is not valid (min: %d, max %d) - using default", - joinDr, this->band->txFreqs[0].drMin, this->band->txFreqs[0].drMax); - joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; - } - } - if(joinDr == RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { - drUp = (this->band->txFreqs[0].drMin + this->band->txFreqs[0].drMax) / 2; - } - cmd.payload[0] = (drUp << 4); - } else { - uint8_t drJr = this->band->txSpans[0].joinRequestDataRate; - cmd.payload[0] = (drJr << 4); - } + cmd.payload[0] = (drUp << 4); // set uplink datarate cmd.payload[0] |= 0; // default to max Tx Power cmd.payload[3] = (1 << 7); // set the RFU bit, which means that the channel mask gets ignored (void)execMacCommand(&cmd); @@ -433,54 +452,36 @@ void LoRaWANNode::beginCommon(uint8_t joinDr) { (void)execMacCommand(&cmd); } -int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDr, bool force) { +int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force, uint8_t joinDr) { // if not forced and already joined, don't do anything if(!force && this->isJoined()) { - return(this->activeMode); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginOTAA(): Did not rejoin: session already active"); + return(RADIOLIB_ERR_NONE); } - - // check if we actually need to send the join request - Module* mod = this->phyLayer->getMod(); -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) + int16_t state = RADIOLIB_ERR_UNKNOWN; + + // generate activation key checksum uint16_t checkSum = 0; checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast(&joinEUI), 8); checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast(&devEUI), 8); checkSum ^= LoRaWANNode::checkSum16(nwkKey, 16); checkSum ^= LoRaWANNode::checkSum16(appKey, 16); - bool isValidCheckSum = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID) == checkSum; - bool isValidMode = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID) == RADIOLIB_LORAWAN_MODE_OTAA; - - if(isValidCheckSum && isValidMode) { - // if not forced and a valid session is stored, restore it - if(!force && this->isValidSession()) { - return(this->restore()); - } - // either forced or no active session (a join was issued previously but didn't result in an active session) - this->clearSession(); - // the credentials are still the same, so restore the DevNonce and JoinNonce - this->devNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID); - this->joinNonce = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID); - } else { - // either invalid key checksum or mode, so wipe either way - #if RADIOLIB_DEBUG - RADIOLIB_DEBUG_PRINTLN("Didn't restore session (checksum: %d, mode: %d)", isValidCheckSum, isValidMode); - RADIOLIB_DEBUG_PRINTLN("First 16 bytes of NVM:"); - uint8_t nvmBuff[16]; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(0), nvmBuff, 16); - RADIOLIB_DEBUG_HEXDUMP(nvmBuff, 16); - RADIOLIB_DEBUG_PRINTLN("Wiping EEPROM and starting a clean session"); - #endif - - this->wipe(); + // if The Force is used, disable the active session; + // as a result, restore() will only restore Nonces if they are available, not the session + if(force) { + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)false; } -#else - (void)force; -#endif - int16_t state = RADIOLIB_ERR_NONE; + state = this->restore(checkSum, RADIOLIB_LORAWAN_MODE_OTAA, RADIOLIB_LORAWAN_CLASS_A, this->band->bandNum); + + if(!force) { + return(state); + } + Module* mod = this->phyLayer->getMod(); + // setup join-request uplink/downlink frequencies and datarates if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { state = this->setupChannelsDyn(true); @@ -489,7 +490,12 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } RADIOLIB_ASSERT(state); - // setup all MAC properties to default values + // on fixed bands, the join-datarate is specified per specification + // therefore, we ignore the value that was specified by the user + if(this->band->bandType == RADIOLIB_LORAWAN_BAND_FIXED) { + joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; + } + // setup all MAC properties to default values this->beginCommon(joinDr); // set the physical layer configuration @@ -509,9 +515,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // increment devNonce as we are sending another join-request this->devNonce += 1; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_NONCE_ID, this->devNonce); -#endif + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_DEV_NONCE], this->devNonce); // build the join-request message uint8_t joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN]; @@ -529,7 +533,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); this->rxDelayStart = mod->hal->millis(); - RADIOLIB_DEBUG_PRINTLN("Join-request sent <-- Rx Delay start"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Join-request sent <-- Rx Delay start"); RADIOLIB_ASSERT(state); // configure Rx delay for join-accept message - these are re-configured once a valid join-request is received @@ -546,7 +550,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // check received length size_t lenRx = this->phyLayer->getPacketLength(true); if((lenRx != RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) && (lenRx != RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN - RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN)) { - RADIOLIB_DEBUG_PRINTLN("joinAccept reply length mismatch, expected %luB got %luB", RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN, lenRx); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("joinAccept reply length mismatch, expected %luB got %luB", RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN, lenRx); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } @@ -560,7 +564,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // check reply message type if((joinAcceptMsgEnc[0] & RADIOLIB_LORAWAN_MHDR_MTYPE_MASK) != RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT) { - RADIOLIB_DEBUG_PRINTLN("joinAccept reply message type invalid, expected 0x%02x got 0x%02x", RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT, joinAcceptMsgEnc[0]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("joinAccept reply message type invalid, expected 0x%02x got 0x%02x", RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT, joinAcceptMsgEnc[0]); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } @@ -572,13 +576,13 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(&joinAcceptMsgEnc[1], RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN - 1, &joinAcceptMsg[1]); - RADIOLIB_DEBUG_PRINTLN("joinAcceptMsg:"); - RADIOLIB_DEBUG_HEXDUMP(joinAcceptMsg, lenRx); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("joinAcceptMsg:"); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(joinAcceptMsg, lenRx); // get current JoinNonce from downlink and previous JoinNonce from persistent storage uint32_t joinNonceNew = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], 3); - RADIOLIB_DEBUG_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", this->joinNonce, joinNonceNew); + RADIOLIB_DEBUG_PROTOCOL_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); @@ -591,7 +595,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // check LoRaWAN revision (the MIC verification depends on this) uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS]; this->rev = (dlSettings & RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1) >> 7; - RADIOLIB_DEBUG_PRINTLN("LoRaWAN revision: 1.%d", this->rev); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LoRaWAN revision: 1.%d", this->rev); // verify MIC if(this->rev == 1) { @@ -710,28 +714,32 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; this->adrFcnt = 0; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) // save the activation keys checksum, device address & keys as well as JoinAccept values; these are only ever set when joining - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_TABLE_VERSION_ID, RADIOLIB_EEPROM_TABLE_VERSION); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID, checkSum); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID, RADIOLIB_LORAWAN_MODE_OTAA); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_JOIN_NONCE_ID, this->joinNonce); -#endif + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_VERSION], RADIOLIB_LORAWAN_NONCES_VERSION_VAL); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE], RADIOLIB_LORAWAN_MODE_OTAA); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CLASS], RADIOLIB_LORAWAN_CLASS_A); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_PLAN], this->band->bandNum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CHECKSUM], checkSum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_JOIN_NONCE], this->joinNonce, 3); + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; this->activeMode = RADIOLIB_LORAWAN_MODE_OTAA; + // generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer + uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LORAWAN_NONCES_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_SIGNATURE], signature); + 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) { +int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, bool force, uint8_t initialDr) { // if not forced and already joined, don't do anything if(!force && this->isJoined()) { - return(this->activeMode); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active"); + return(RADIOLIB_ERR_NONE); } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - // only needed for persistent storage - Module* mod = this->phyLayer->getMod(); + int16_t state = RADIOLIB_ERR_UNKNOWN; // check if we actually need to restart from a clean session uint16_t checkSum = 0; @@ -741,32 +749,17 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); } if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); } - bool isValidCheckSum = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID) == checkSum; - bool isValidMode = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID) == RADIOLIB_LORAWAN_MODE_ABP; - - if(isValidCheckSum && isValidMode) { - // if not forced and a valid session is stored, restore it - if(!force && this->isValidSession()) { - return(this->restore()); - } - // either forced or no active session (a join was issued previously but didn't result in an active session) - this->clearSession(); - } else { - // either invalid key checksum or mode, so wipe either way - #if RADIOLIB_DEBUG - RADIOLIB_DEBUG_PRINTLN("Didn't restore session (checksum: %d, mode: %d)", isValidCheckSum, isValidMode); - RADIOLIB_DEBUG_PRINTLN("First 16 bytes of NVM:"); - uint8_t nvmBuff[16]; - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(0), nvmBuff, 16); - RADIOLIB_DEBUG_HEXDUMP(nvmBuff, 16); - RADIOLIB_DEBUG_PRINTLN("Wiping EEPROM and starting a clean session"); - #endif - - this->wipe(); + // if The Force is used, disable the active session; + // as a result, restore() will not restore the session (and there are no Nonces in ABP mode) + if(force) { + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)false; + } + + state = this->restore(checkSum, RADIOLIB_LORAWAN_MODE_ABP, RADIOLIB_LORAWAN_CLASS_A, this->band->bandNum); + + if(!force) { + return(state); } -#else - (void)force; -#endif this->devAddr = addr; memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE); @@ -781,8 +774,6 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, memcpy(this->sNwkSIntKey, sNwkSIntKey, RADIOLIB_AES128_KEY_SIZE); } - int16_t state = RADIOLIB_ERR_NONE; - // setup the uplink/downlink channels and initial datarate if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { this->setupChannelsDyn(); @@ -791,7 +782,7 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, } // setup all MAC properties to default values - this->beginCommon(); + this->beginCommon(initialDr); // set the physical layer configuration state = this->setPhyProperties(); @@ -805,15 +796,20 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; this->adrFcnt = 0; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - // save the activation keys checksum, device address & keys - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_TABLE_VERSION_ID, RADIOLIB_EEPROM_TABLE_VERSION); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CHECKSUM_ID, checkSum); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_MODE_ID, RADIOLIB_LORAWAN_MODE_ABP); -#endif + // save the activation keys checksum, mode, class, frequency plan + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_VERSION], RADIOLIB_LORAWAN_NONCES_VERSION_VAL); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE], RADIOLIB_LORAWAN_MODE_ABP); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CLASS], RADIOLIB_LORAWAN_CLASS_A); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_PLAN], this->band->bandNum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_CHECKSUM], checkSum); + this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; this->activeMode = RADIOLIB_LORAWAN_MODE_ABP; + // generate the signature of the Nonces buffer, and store it in the last two bytes of the Nonces buffer + uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LORAWAN_NONCES_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_SIGNATURE], signature); + return(RADIOLIB_ERR_NONE); } @@ -821,110 +817,40 @@ bool LoRaWANNode::isJoined() { return(this->activeMode != RADIOLIB_LORAWAN_MODE_NONE); } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) int16_t LoRaWANNode::saveSession() { - Module* mod = this->phyLayer->getMod(); - // store DevAddr and all keys - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DEV_ADDR_ID, this->devAddr); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_APP_S_KEY_ID), this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FNWK_SINT_KEY_ID), this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_SNWK_SINT_KEY_ID), this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_NWK_SENC_KEY_ID), this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DEV_ADDR], this->devAddr); + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY], this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY], this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY], this->sNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); + + // copy the signature of the Nonces buffer over to the Session buffer + uint16_t noncesSignature = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_SIGNATURE]); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE], noncesSignature); // store network parameters - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_HOME_NET_ID, this->homeNetId); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_VERSION_ID, this->rev); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_HOMENET_ID], this->homeNetId); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_VERSION], this->rev); // store all frame counters - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_A_FCNT_DOWN_ID, this->aFcntDown); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_N_FCNT_DOWN_ID, this->nFcntDown); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_UP_ID, this->confFcntUp); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_CONF_FCNT_DOWN_ID, this->confFcntDown); - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_ADR_FCNT_ID, this->adrFcnt); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN], this->aFcntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN], this->nFcntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP], this->confFcntUp); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN], this->confFcntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_FCNT], this->adrFcnt); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_FCNT_UP], this->fcntUp); - // fcntUp is saved using highly efficient wear-leveling as this is by far going to be written most often - this->saveFcntUp(); + // save the current uplink MAC command queue + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); - // 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_EEPROM_LORAWAN_MAC_QUEUE_UL_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_EEPROM_LORAWAN_MAC_QUEUE_UL_ID), queueBuff, sizeof(LoRaWANMacCommandQueue_t)); - } + // generate the signature of the Session buffer, and store it in the last two bytes of the Session buffer + uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LORAWAN_SESSION_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_SIGNATURE], signature); return(RADIOLIB_ERR_NONE); } -void LoRaWANNode::saveFcntUp() { - Module* mod = this->phyLayer->getMod(); - - uint8_t fcntBuffStart = mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID); - uint8_t fcntBuffEnd = mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID + 1); - uint8_t buffSize = fcntBuffEnd - fcntBuffStart; - #if RADIOLIB_STATIC_ONLY - uint8_t fcntBuff[RADIOLIB_STATIC_ARRAY_SIZE]; - #else - uint8_t* fcntBuff = new uint8_t[buffSize]; - #endif - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID), fcntBuff, buffSize); - - // 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(RADIOLIB_EEPROM_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(RADIOLIB_EEPROM_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; - } - } - // check if the last written byte is equal to current, only rewrite if different - uint8_t bits_14_7 = (this->fcntUp >> 7) & 0x7F; - if((fcntBuff[idx - 1] & 0x7F) != bits_14_7) { - // find next index to write - idx = idx < 5 ? idx : 2; - - // flip the first bit of this byte to indicate that we just wrote here - bits_14_7 |= (~(fcntBuff[idx] >> 7)) << 7; - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_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 < buffSize; idx++) { - if(fcntBuff[idx] >> 7 != state) { - break; - } - } - idx = idx < buffSize ? 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(RADIOLIB_EEPROM_LORAWAN_FCNT_UP_ID, bits_7_0, idx); - -} -#endif // RADIOLIB_EEPROM_UNSUPPORTED - #if defined(RADIOLIB_BUILD_ARDUINO) int16_t LoRaWANNode::uplink(String& str, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) { return(this->uplink(str.c_str(), port, isConfirmed, event)); @@ -968,7 +894,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf this->isMACPayload = false; } - int16_t state = RADIOLIB_ERR_NONE; + int16_t state = RADIOLIB_ERR_UNKNOWN; // check if there are some MAC commands to piggyback (only when piggybacking onto a application-frame) uint8_t foptsLen = 0; @@ -1004,7 +930,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // if the TxPower field has some offset, remove it and switch to maximum power if(this->txPowerCur > 0) { // set the maximum power supported by both the module and the band - state = this->setTxPower(this->txPowerMax, true); + state = this->setTxPower(this->txPowerMax); if(state == RADIOLIB_ERR_NONE) { this->txPowerCur = 0; adrStage = 0; // successfully did some ADR stuff @@ -1018,7 +944,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf case(2): { // try to decrease the datarate if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] > 0) { - if(this->setDatarate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] - 1, true) == RADIOLIB_ERR_NONE) { + if(this->setDatarate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] - 1) == RADIOLIB_ERR_NONE) { adrStage = 0; // successfully did some ADR stuff } } @@ -1106,8 +1032,8 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf memcpy(foptsPtr, &cmd, 1 + cmd.len); foptsPtr += cmd.len + 1; } - RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); - RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsLen); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(foptsBuff, foptsLen); // pop the commands from back to front for (; i >= 0; i--) { @@ -1154,10 +1080,9 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf block1[RADIOLIB_LORAWAN_MIC_DATA_RATE_POS] = this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]; block1[RADIOLIB_LORAWAN_MIC_CH_INDEX_POS] = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].idx; - RADIOLIB_DEBUG_PRINTLN("FcntUp: %d", this->fcntUp); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink (FcntUp = %d) decoded:", this->fcntUp); - RADIOLIB_DEBUG_PRINTLN("uplinkMsg pre-MIC:"); - RADIOLIB_DEBUG_HEXDUMP(uplinkMsg, uplinkMsgLen); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(uplinkMsg, uplinkMsgLen); // calculate authentication codes memcpy(uplinkMsg, block1, RADIOLIB_AES128_BLOCK_SIZE); @@ -1183,7 +1108,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); - RADIOLIB_DEBUG_PRINTLN("Uplink sent <-- Rx Delay start"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink sent <-- Rx Delay start"); // calculate Time on Air of this uplink in milliseconds this->lastToA = this->phyLayer->getTimeOnAir(uplinkMsgLen - RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS) / 1000; @@ -1267,11 +1192,11 @@ int16_t LoRaWANNode::downlinkCommon() { // open Rx window by starting receive with specified timeout state = this->phyLayer->startReceive(timeoutMod, irqFlags, irqMask, 0); - RADIOLIB_DEBUG_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, timeoutHost); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, timeoutHost); // wait for the timeout to complete (and a small additional delay) mod->hal->delay(timeoutHost / 1000 + scanGuard / 2); - RADIOLIB_DEBUG_PRINTLN("Closing Rx%d window", i+1); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Closing Rx%d window", i+1); // check if the IRQ bit for Rx Timeout is set if(!this->phyLayer->isRxTimeout()) { @@ -1280,7 +1205,7 @@ int16_t LoRaWANNode::downlinkCommon() { } else if(i == 0) { // nothing in the first window, configure for the second this->phyLayer->standby(); - RADIOLIB_DEBUG_PRINTLN("PHY: Frequency %cL = %6.3f MHz", 'D', this->rx2.freq); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", 'D', this->rx2.freq); state = this->phyLayer->setFrequency(this->rx2.freq); RADIOLIB_ASSERT(state); @@ -1322,7 +1247,7 @@ int16_t LoRaWANNode::downlinkCommon() { #if defined(RADIOLIB_BUILD_ARDUINO) int16_t LoRaWANNode::downlink(String& str, LoRaWANEvent_t* event) { - int16_t state = RADIOLIB_ERR_NONE; + int16_t state = RADIOLIB_ERR_UNKNOWN; // build a temporary buffer // LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL @@ -1344,7 +1269,7 @@ int16_t LoRaWANNode::downlink(String& str, LoRaWANEvent_t* event) { #endif int16_t LoRaWANNode::downlink(LoRaWANEvent_t* event) { - int16_t state = RADIOLIB_ERR_NONE; + int16_t state = RADIOLIB_ERR_UNKNOWN; // build a temporary buffer // LoRaWAN downlinks can have 250 bytes at most with 1 extra byte for NULL @@ -1364,12 +1289,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) // get the packet length size_t downlinkMsgLen = this->phyLayer->getPacketLength(); - RADIOLIB_DEBUG_PRINTLN("Downlink message length: %d", downlinkMsgLen); // check the minimum required frame length // an extra byte is subtracted because downlink frames may not have a port if(downlinkMsgLen < RADIOLIB_LORAWAN_FRAME_LEN(0, 0) - 1 - RADIOLIB_AES128_BLOCK_SIZE) { - RADIOLIB_DEBUG_PRINTLN("Downlink message too short (%lu bytes)", downlinkMsgLen); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Downlink message too short (%lu bytes)", downlinkMsgLen); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } @@ -1414,15 +1338,10 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp); } - 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); - RADIOLIB_DEBUG_PRINTLN("FOpts: %02X", downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS]); - // in LoRaWAN v1.1, a frame can be a network frame if there is no Application payload // i.e., no payload at all (empty frame or FOpts only), or MAC only payload (FPort = 0) // TODO "NFCntDown is used for MAC communication on port 0 and when the FPort field is missing" @@ -1439,7 +1358,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) isAppDownlink = false; } } - RADIOLIB_DEBUG_PRINTLN("FOptsLen: %d", foptsLen); + + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Downlink (%sFcntDown = %d) encoded:", isAppDownlink ? "A" : "N", fcnt16); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen); // check the FcntDown value (Network or Application) uint32_t fcntDownPrev = 0; @@ -1449,8 +1370,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) fcntDownPrev = this->nFcntDown; } - RADIOLIB_DEBUG_PRINTLN("fcnt: %d, fcntPrev: %d, isAppDownlink: %d", fcnt16, fcntDownPrev, (int)isAppDownlink); - // if this is not the first downlink... // assume a 16-bit to 32-bit rollover if difference between counters 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 @@ -1496,7 +1415,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) // check the address uint32_t addr = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS]); if(addr != this->devAddr) { - RADIOLIB_DEBUG_PRINTLN("Device address mismatch, expected 0x%08X, got 0x%08X", this->devAddr, addr); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Device address mismatch, expected 0x%08X, got 0x%08X", this->devAddr, addr); #if !RADIOLIB_STATIC_ONLY delete[] downlinkMsg; #endif @@ -1522,9 +1441,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) processAES(&downlinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(0)], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true); } - RADIOLIB_DEBUG_PRINTLN("fopts:"); - RADIOLIB_DEBUG_HEXDUMP(fopts, foptsLen); - bool hasADR = false; uint8_t numADR = 0; uint8_t lastCID = 0; @@ -1538,7 +1454,7 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) if(cid == RADIOLIB_LORAWAN_MAC_LINK_ADR) { // if there was an earlier ADR command but it was not the last, ignore it if(hasADR && lastCID != RADIOLIB_LORAWAN_MAC_LINK_ADR) { - RADIOLIB_DEBUG_PRINTLN("Encountered non-consecutive block of ADR commands - skipping"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Encountered non-consecutive block of ADR commands - skipping"); remLen -= (macLen + 1); foptsPtr += (macLen + 1); lastCID = cid; @@ -1557,8 +1473,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) .repeat = (cid == RADIOLIB_LORAWAN_MAC_LINK_ADR ? numADR : (uint8_t)0), }; memcpy(cmd.payload, foptsPtr + 1, macLen); - RADIOLIB_DEBUG_PRINTLN("[%02X]: %02X %02X %02X %02X %02X (%d)", - cmd.cid, cmd.payload[0], cmd.payload[1], cmd.payload[2], cmd.payload[3], cmd.payload[4], cmd.len); // process the MAC command bool sendUp = execMacCommand(&cmd); @@ -1570,18 +1484,12 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) remLen -= (macLen + 1); foptsPtr += (macLen + 1); lastCID = cid; - RADIOLIB_DEBUG_PRINTLN("Processed: %d, remaining: %d", (macLen + 1), remLen); } #if !RADIOLIB_STATIC_ONLY delete[] fopts; #endif - RADIOLIB_DEBUG_PRINTLN("MAC response:"); - for (int i = 0; i < this->commandsUp.numCommands; i++) { - RADIOLIB_DEBUG_HEXDUMP(&(this->commandsUp.commands[i].cid), sizeof(LoRaWANMacCommand_t)); - } - // if FOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink if(this->commandsUp.len > 15) { size_t foptsBufSize = this->commandsUp.len; @@ -1598,8 +1506,8 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) memcpy(foptsPtr, &cmd, 1 + cmd.len); foptsPtr += cmd.len + 1; } - RADIOLIB_DEBUG_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); - RADIOLIB_DEBUG_HEXDUMP(foptsBuff, foptsBufSize); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(foptsBuff, foptsBufSize); // pop the commands from back to front for (; i >= 0; i--) { @@ -1614,9 +1522,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) // temporarily lift dutyCycle restrictions to allow immediate MAC response bool prevDC = this->dutyCycleEnabled; this->dutyCycleEnabled = false; - RADIOLIB_DEBUG_PRINTLN("Sending MAC-only uplink .. "); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Sending MAC-only uplink .. "); state = this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND); - RADIOLIB_DEBUG_PRINTLN(" .. state: %d", state); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN(" .. state: %d", state); this->dutyCycleEnabled = prevDC; #if !RADIOLIB_STATIC_ONLY @@ -1629,9 +1537,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) uint8_t* strDown = new uint8_t[this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]]; #endif size_t lenDown = 0; - RADIOLIB_DEBUG_PRINTLN("Receiving after MAC-only uplink .. "); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Receiving after MAC-only uplink .. "); state = this->downlink(strDown, &lenDown); - RADIOLIB_DEBUG_PRINTLN(" .. state: %d", state); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN(" .. state: %d", state); #if !RADIOLIB_STATIC_ONLY delete[] strDown; #endif @@ -1767,7 +1675,7 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) { // calculate the expected value and compare uint32_t micCalculated = generateMIC(msg, len - sizeof(uint32_t), key); if(micCalculated != micReceived) { - RADIOLIB_DEBUG_PRINTLN("MIC mismatch, expected %08x, got %08x", micCalculated, micReceived); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("MIC mismatch, expected %08x, got %08x", micCalculated, micReceived); return(false); } @@ -1809,7 +1717,7 @@ int16_t LoRaWANNode::setPhyProperties() { } int16_t LoRaWANNode::setupChannelsDyn(bool joinRequest) { - RADIOLIB_DEBUG_PRINTLN("Setting up dynamic channels"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Setting up dynamic channels"); size_t num = 0; // copy the default defined channels into the first slots (where Tx = Rx) @@ -1833,19 +1741,21 @@ int16_t LoRaWANNode::setupChannelsDyn(bool joinRequest) { } for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - RADIOLIB_DEBUG_PRINTLN("UL: %d %d %6.3f (%d - %d) | DL: %d %d %6.3f (%d - %d)", - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("UL: %3d %d %7.3f (%d - %d) | DL: %3d %d %7.3f (%d - %d)", + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax - ); + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax + ); + } } return(RADIOLIB_ERR_NONE); @@ -1854,15 +1764,11 @@ int16_t LoRaWANNode::setupChannelsDyn(bool joinRequest) { // setup a subband and its corresponding join-request datarate // WARNING: subBand starts at 1 (corresponds to all populair schemes) int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { - RADIOLIB_DEBUG_PRINTLN("Setting up fixed channels (subband %d)", subBand); - // randomly select one of 8 or 9 channels and find corresponding datarate - uint8_t numChannels = this->band->numTxSpans == 1 ? 8 : 9; - uint8_t rand = this->phyLayer->random(numChannels) + 1; // range 1-8 or 1-9 - uint8_t drJR = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; - if(rand <= 8) { - drJR = this->band->txSpans[0].joinRequestDataRate; // if one of the first 8 channels, select datarate of span 0 - } else { - drJR = this->band->txSpans[1].joinRequestDataRate; // if ninth channel, select datarate of span 1 + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Setting up fixed channels (subband %d)", subBand); + + // clear all existing channels + for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LORAWAN_CHANNEL_NONE; } // if no subband is selected by user, cycle through banks of 8 using devNonce value @@ -1870,55 +1776,33 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { uint8_t numBanks8 = this->band->txSpans[0].numChannels / 8; subBand = this->devNonce % numBanks8; } + + uint8_t chMaskCntl = 0; + uint16_t chMask = 0; - // chMask is set for 16 channels at once, so widen the Cntl value - uint8_t chMaskCntl = (subBand - 1) / 2; // compensate the 1 offset - - uint8_t numADR = 1; - - LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, - .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, - .repeat = 0, - }; - // if there are two channel spans, first set the channel from second span if(this->band->numTxSpans == 2) { - cmd.payload[0] = (drJR << 4); // set join-request datarate - cmd.payload[0] |= 0; // set Tx power to maximum - // enable channel that belongs to this subband - cmd.payload[1] = (1 << (subBand - 1)); // set channel mask - cmd.payload[2] = 0; - cmd.payload[3] = (7 << 4); // set the chMaskCntl value to all channels off - cmd.payload[3] |= 0; // keep NbTrans the same - cmd.repeat = numADR++; - (void)execMacCommand(&cmd, false); + chMaskCntl = 7; + chMask = (1 << (subBand - 1)); // set channel mask + this->applyChannelMaskFix(chMaskCntl, chMask); } - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; - cmd.payload[0] = (drJR << 4); // set join-request datarate - cmd.payload[0] |= 0; // set Tx power to maximum + // chMask is set for 16 channels at once, so widen the Cntl value + chMaskCntl = (subBand - 1) / 2; // compensate the 1 offset + // now select the correct bank of 8 channels - // 0x00 0xFF channel mask for subband = 2, 4.. (even) - // 0xFF 0x00 channel mask for subband = 1, 3.. (odd) - if(subBand % 2 == 0) { - cmd.payload[1] = 0x00; - cmd.payload[2] = 0xFF; + if(subBand % 2 == 0) { // even subbands + chMask = 0xFF00; } else { - cmd.payload[1] = 0xFF; - cmd.payload[2] = 0x00; + chMask = 0x00FF; // odd subbands } - cmd.payload[3] = (chMaskCntl << 4); // set the chMaskCntl value - cmd.payload[3] |= 0; // keep NbTrans the same - cmd.repeat = numADR++; - (void)execMacCommand(&cmd, false); + this->applyChannelMaskFix(chMaskCntl, chMask); return(RADIOLIB_ERR_NONE); } int16_t LoRaWANNode::processCFList(uint8_t* cfList) { - RADIOLIB_DEBUG_PRINTLN("Processing CFList"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Processing CFList"); if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { // retrieve number of existing (default) channels @@ -1956,18 +1840,17 @@ int16_t LoRaWANNode::processCFList(uint8_t* cfList) { .len = 0, .repeat = 0, }; - cmd.payload[0] = 0xFF; // same datarate and payload // in case of mask-type bands, copy those frequencies that are masked true into the available TX channels - size_t numChMasks = 3 + this->band->numTxSpans; // 4 masks for bands with 2 spans, 5 spans for bands with 1 span + size_t numChMasks = 3 + this->band->numTxSpans; // 4 masks for bands with 2 spans, 5 spans for bands with 1 span for(size_t chMaskCntl = 0; chMaskCntl < numChMasks; chMaskCntl++) { cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; - cmd.payload[3] = chMaskCntl << 4; // NbTrans = 0 -> keep the same + cmd.payload[0] = 0xFF; // same datarate and payload + memcpy(&cmd.payload[1], &cfList[chMaskCntl*2], 2); // copy mask + cmd.payload[3] = chMaskCntl << 4; // set chMaskCntl, set NbTrans = 0 -> keep the same cmd.repeat = (chMaskCntl + 1); - memcpy(&cmd.payload[1], &cfList[chMaskCntl*2], 2); (void)execMacCommand(&cmd); } - // delete the ADR response } return(RADIOLIB_ERR_NONE); @@ -1987,7 +1870,7 @@ int16_t LoRaWANNode::selectChannels() { } } if(numChannels == 0) { - RADIOLIB_DEBUG_PRINTLN("There are no channels defined - are you in ABP mode with no defined subband?"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("There are no channels defined - are you in ABP mode with no defined subband?"); return(RADIOLIB_ERR_INVALID_CHANNEL); } // select a random ID & channel from the list of enabled and possible channels @@ -2016,7 +1899,7 @@ int16_t LoRaWANNode::selectChannels() { return(RADIOLIB_ERR_NONE); } -int16_t LoRaWANNode::setDatarate(uint8_t drUp, bool saveToEeprom) { +int16_t LoRaWANNode::setDatarate(uint8_t drUp) { // scan through all enabled channels and check if the requested datarate is available bool isValidDR = false; for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { @@ -2029,7 +1912,7 @@ int16_t LoRaWANNode::setDatarate(uint8_t drUp, bool saveToEeprom) { } } if(!isValidDR) { - RADIOLIB_DEBUG_PRINTLN("No defined channel allows datarate %d", drUp); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("No defined channel allows datarate %d", drUp); return(RADIOLIB_ERR_INVALID_DATA_RATE); } @@ -2043,7 +1926,7 @@ int16_t LoRaWANNode::setDatarate(uint8_t drUp, bool saveToEeprom) { cmd.payload[0] |= 0x0F; // keep Tx Power the same cmd.payload[3] = (1 << 7); // set the RFU bit, which means that the channel mask gets ignored cmd.payload[3] |= 0; // keep NbTrans the same - (void)execMacCommand(&cmd, saveToEeprom); + (void)execMacCommand(&cmd); // check if ACK is set for Tx Power if((cmd.payload[0] >> 1) != 1) { @@ -2116,7 +1999,7 @@ uint8_t LoRaWANNode::maxPayloadDwellTime() { return(payLen - 13); // fixed 13-byte header } -int16_t LoRaWANNode::setTxPower(int8_t txPower, bool saveToEeprom) { +int16_t LoRaWANNode::setTxPower(int8_t txPower) { // only allow values within the band's (or MAC state) maximum if(txPower > this->txPowerMax) { return(RADIOLIB_ERR_INVALID_OUTPUT_POWER); @@ -2136,7 +2019,7 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower, bool saveToEeprom) { cmd.payload[0] |= numSteps; // set the Tx Power cmd.payload[3] = (1 << 7); // set the RFU bit, which means that the channel mask gets ignored cmd.payload[3] |= 0; // keep NbTrans the same - (void)execMacCommand(&cmd, saveToEeprom); + (void)execMacCommand(&cmd); // check if ACK is set for Tx Power if((cmd.payload[0] >> 2) != 1) { @@ -2171,7 +2054,7 @@ int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) { dataRate->lora.spreadingFactor = ((dataRateBand & 0x70) >> 4) + 6; dataRate->lora.codingRate = (dataRateBand & 0x03) + 5; - RADIOLIB_DEBUG_PRINTLN("PHY: SF = %d, BW = %6.3f kHz, CR = 4/%d", + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: SF = %d, BW = %6.3f kHz, CR = 4/%d", dataRate->lora.spreadingFactor, dataRate->lora.bandwidth, dataRate->lora.codingRate); } @@ -2180,8 +2063,8 @@ int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) { int16_t LoRaWANNode::configureChannel(uint8_t dir) { // set the frequency - RADIOLIB_DEBUG_PRINTLN(""); - RADIOLIB_DEBUG_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN(""); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq); int state = this->phyLayer->setFrequency(this->currentChannels[dir].freq); RADIOLIB_ASSERT(state); @@ -2239,10 +2122,6 @@ int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQ } int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue, uint8_t* payload) { - 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) { // if a pointer to a payload is supplied, copy the command's payload over @@ -2264,14 +2143,9 @@ int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* que return(RADIOLIB_ERR_COMMAND_QUEUE_ITEM_NOT_FOUND); } -bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { - RADIOLIB_DEBUG_PRINTLN("exe MAC CID = %02x, len = %d", cmd->cid, cmd->len); - - Module* mod = this->phyLayer->getMod(); -#if defined(RADIOLIB_EEPROM_UNSUPPORTED) - (void)saveToEeprom; - (void)mod; -#endif +bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("[MAC] 0x%02X", cmd->cid); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(cmd->payload, cmd->len); if(cmd->cid >= RADIOLIB_LORAWAN_MAC_PROPRIETARY) { // TODO call user-provided callback for proprietary MAC commands? @@ -2282,7 +2156,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { case(RADIOLIB_LORAWAN_MAC_RESET): { // get the server version uint8_t srvVersion = cmd->payload[0]; - RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ResetConf: server version 1.%d", srvVersion); if(srvVersion == this->rev) { // valid server version, stop sending the ResetInd MAC command deleteMacCommand(RADIOLIB_LORAWAN_MAC_RESET, &this->commandsUp); @@ -2291,6 +2165,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { } break; case(RADIOLIB_LORAWAN_MAC_LINK_CHECK): { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkCheckAns: [user]"); // delete any existing response (does nothing if there is none) deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_CHECK, &this->commandsDown); @@ -2306,10 +2181,12 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // but we don't bother and try to set each individual command uint8_t drUp = (cmd->payload[0] & 0xF0) >> 4; uint8_t txPower = cmd->payload[0] & 0x0F; + bool isInternalTxDr = cmd->payload[3] >> 7; + uint16_t chMask = LoRaWANNode::ntoh(&cmd->payload[1]); uint8_t chMaskCntl = (cmd->payload[3] & 0x70) >> 4; uint8_t nbTrans = cmd->payload[3] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("ADR REQ: dataRate = %d, txPower = %d, chMask = 0x%04x, chMaskCntl = %02x, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkADRReq: dataRate = %d, txPower = %d, chMask = 0x%04x, chMaskCntl = %d, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans); // apply the configuration uint8_t drAck = 0; @@ -2332,7 +2209,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; drAck = 1; } else { - RADIOLIB_DEBUG_PRINTLN("ADR failed to configure dataRate %d, code %d!", drUp, state); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure dataRate %d, code %d!", drUp, state); } } @@ -2347,6 +2224,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { } else { int8_t pwr = this->txPowerMax - 2*txPower; + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: TX = %d dBm", pwr); state = RADIOLIB_ERR_INVALID_OUTPUT_POWER; while(state == RADIOLIB_ERR_INVALID_OUTPUT_POWER) { // go from the highest power and lower it until we hit one supported by the module @@ -2362,23 +2240,26 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { uint8_t chMaskAck = 1; // only apply channel mask when the RFU bit is not set - // (which is set on the internal MAC command when creating new session) - if((cmd->payload[3] >> 7) == 0) { + // (which is only set in internal MAC commands for changing Tx/Dr) + if(!isInternalTxDr) { if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { chMaskAck = (uint8_t)this->applyChannelMaskDyn(chMaskCntl, chMask); } else { // RADIOLIB_LORAWAN_BAND_FIXED - bool clearChannels = false; if(cmd->repeat == 1) { // if this is the first ADR command in the queue, clear all saved channels // so we can apply the new channel mask - clearChannels = true; - RADIOLIB_DEBUG_PRINTLN("ADR mask: clearing channels"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR mask: clearing channels"); + for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LORAWAN_CHANNEL_NONE; + } + // clear all previous channel masks + memset(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS], 0, 16*8); } else { // if this is not the first ADR command, clear the ADR response that was in the queue (void)deleteMacCommand(RADIOLIB_LORAWAN_MAC_LINK_ADR, &this->commandsUp); } - chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask, clearChannels); + chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask); } } @@ -2389,66 +2270,58 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->nbTrans = nbTrans; } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - uint8_t payLen = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { // if RFU bit is set, this is just a change in Datarate or TxPower, so read ADR command and overwrite first byte - if((cmd->payload[3] >> 7) == 1) { - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID) + 1, &(cmd->payload[1]), 3); + if(isInternalTxDr) { + memcpy(&(cmd->payload[1]), &this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR] + 1, 3); } + // if there was no channel mask (all zeroes), we should never apply that channel mask, so set RFU bit again if(cmd->payload[1] == 0 && cmd->payload[2] == 0) { cmd->payload[3] |= (1 << 7); } // save to the single ADR MAC location - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_LINK_ADR_ID), &(cmd->payload[0]), payLen); + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], &(cmd->payload[0]), cmd->len); } else { // RADIOLIB_LORAWAN_BAND_FIXED - // if RFU bit is set, this is just a change in Datarate or TxPower - // so read bytes 1..3 from last stored ADR command into the current MAC payload and re-store it - if((cmd->payload[3] >> 7) == 1) { - // read how many ADR masks are already stored - uint8_t numMacADR = mod->hal->getPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID); - if(numMacADR > 0) { - mod->hal->readPersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (numMacADR - 1) * payLen + 1, &(cmd->payload[1]), 3); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (numMacADR - 1) * payLen, &(cmd->payload[0]), payLen); - } + + // save Tx/Dr to the Link ADR position in the session buffer + uint8_t bufTxDr[RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; + bufTxDr[0] = cmd->payload[0]; + bufTxDr[3] = 1 << 7; + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], bufTxDr, cmd->len); - } else { - // save to the uplink channel location, to the cmd->repeat-th slot of 4 bytes - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + (cmd->repeat - 1) * payLen, &(cmd->payload[0]), payLen); - // saved an ADR mask, so re-store counter - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_NUM_ADR_MASKS_ID, cmd->repeat); + // if RFU bit is set, this is just a change in Datarate or TxPower, in which case we don't save the channel masks + // if the RFU bit is not set, we must save this channel mask + if(!isInternalTxDr) { + // save the channel mask to the uplink channels position in session buffer, with Tx and DR set to 'same' + cmd->payload[0] = 0xFF; + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS] + (cmd->repeat - 1) * cmd->len, cmd->payload, cmd->len); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Saving mask to ULChannels[%d]:", (cmd->repeat - 1) * cmd->len); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS] + (cmd->repeat - 1) * cmd->len, cmd->len); } + } - } -#endif // send the reply cmd->len = 1; cmd->payload[0] = (pwrAck << 2) | (drAck << 1) | (chMaskAck << 0); cmd->repeat = 0; // discard any repeat value that may have been set - RADIOLIB_DEBUG_PRINTLN("ADR ANS: status = 0x%02x", cmd->payload[0]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkADRAns: status = 0x%02x", cmd->payload[0]); return(true); } break; case(RADIOLIB_LORAWAN_MAC_DUTY_CYCLE): { uint8_t maxDutyCycle = cmd->payload[0] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("Max duty cycle: 1/2^%d", maxDutyCycle); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DutyCycleReq: max duty cycle = 1/2^%d", maxDutyCycle); if(maxDutyCycle == 0) { this->dutyCycle = this->band->dutyCycle; } else { this->dutyCycle = (uint32_t)60 * (uint32_t)60 * (uint32_t)1000 / (uint32_t)(1UL << maxDutyCycle); } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_DUTY_CYCLE_ID, cmd->payload[0]); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE], cmd->payload, cmd->len); cmd->len = 0; return(true); @@ -2462,7 +2335,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { uint8_t rx2Ack = 1; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); this->rx2.freq = (float)freqRaw/10000.0; - RADIOLIB_DEBUG_PRINTLN("Rx param REQ: rx1DrOffset = %d, rx2DataRate = %d, freq = %f", this->rx1DrOffset, this->rx2.drMax, this->rx2.freq); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RXParamSetupReq: rx1DrOffset = %d, rx2DataRate = %d, freq = %f", this->rx1DrOffset, this->rx2.drMax, this->rx2.freq); // apply the configuration uint8_t chanAck = 0; @@ -2471,29 +2344,24 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq); } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - uint8_t payLen = MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn; - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_RX_PARAM_SETUP_ID), &(cmd->payload[0]), payLen); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP], cmd->payload, cmd->len); // TODO this should be sent repeatedly until the next downlink cmd->len = 1; cmd->payload[0] = (rx1OffsAck << 2) | (rx2Ack << 1) | (chanAck << 0); - RADIOLIB_DEBUG_PRINTLN("Rx param ANS: status = 0x%02x", cmd->payload[0]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RXParamSetupAns: status = 0x%02x", cmd->payload[0]); return(true); } break; case(RADIOLIB_LORAWAN_MAC_DEV_STATUS): { // set the uplink reply + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DevStatusReq"); cmd->len = 2; cmd->payload[1] = this->battLevel; int8_t snr = this->phyLayer->getSNR(); cmd->payload[0] = snr & 0x3F; - RADIOLIB_DEBUG_PRINTLN("DevStatus ANS: status = 0x%02x%02x", cmd->payload[0], cmd->payload[1]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DevStatusAns: status = 0x%02x%02x", cmd->payload[0], cmd->payload[1]); return(true); } break; @@ -2504,7 +2372,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { float freq = (float)freqRaw/10000.0; uint8_t maxDr = (cmd->payload[4] & 0xF0) >> 4; uint8_t minDr = cmd->payload[4] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("New channel: index = %d, freq = %f MHz, maxDr = %d, minDr = %d", chIndex, freq, maxDr, minDr); + uint8_t newChAck = 0; uint8_t freqAck = 0; @@ -2524,7 +2392,8 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq); } - RADIOLIB_DEBUG_PRINTLN("UL: %d %d %6.3f (%d - %d) | DL: %d %d %6.3f (%d - %d)", + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("NewChannelReq:"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("UL: %3d %d %7.3f (%d - %d) | DL: %3d %d %7.3f (%d - %d)", this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].idx, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].enabled, this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].freq, @@ -2538,20 +2407,12 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].drMax ); -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - // save to uplink channels location, to the chIndex-th slot of 5 bytes - uint8_t payLen = MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn; - RADIOLIB_DEBUG_PRINTLN("Saving channel:"); - RADIOLIB_DEBUG_HEXDUMP(&(cmd->payload[0]), payLen); - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_UL_CHANNELS_ID) + chIndex * payLen, &(cmd->payload[0]), payLen); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); // send the reply cmd->len = 1; cmd->payload[0] = (newChAck << 1) | (freqAck << 0); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("NewChannelAns: status = 0x%02x", cmd->payload[0]); return(true); } break; @@ -2561,7 +2422,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { uint8_t chIndex = cmd->payload[0]; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); float freq = (float)freqRaw/10000.0; - RADIOLIB_DEBUG_PRINTLN("DL channel: index = %d, freq = %f MHz", chIndex, freq); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DlChannelReq: index = %d, freq = %f MHz", chIndex, freq); uint8_t freqDlAck = 0; uint8_t freqUlAck = 0; @@ -2582,18 +2443,12 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { } } -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - // save to downlink channels location, to the chIndex-th slot of 4 bytes - uint8_t payLen = MacTable[RADIOLIB_LORAWAN_MAC_DL_CHANNEL].lenDn; - mod->hal->writePersistentStorage(mod->hal->getPersistentAddr(RADIOLIB_EEPROM_LORAWAN_DL_CHANNELS_ID) + chIndex * payLen, &(cmd->payload[0]), payLen); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); // TODO send this repeatedly until a downlink is received cmd->len = 1; cmd->payload[0] = (freqUlAck << 1) | (freqDlAck << 0); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DlChannelAns: status = 0x%02x", cmd->payload[0]); return(true); } break; @@ -2601,7 +2456,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { case(RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP): { // get the configuration uint8_t delay = cmd->payload[0] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("RX timing: delay = %d sec", delay); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RXTimingSetupReq: delay = %d sec", delay); // apply the configuration if(delay == 0) { @@ -2610,12 +2465,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->rxDelays[0] = delay * 1000; this->rxDelays[1] = this->rxDelays[0] + 1000; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_RX_TIMING_SETUP_ID, cmd->payload[0]); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP], cmd->payload, cmd->len); // send the reply cmd->len = 0; @@ -2632,7 +2482,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // who the f came up with this ... const uint8_t eirpEncoding[] = { 8, 10, 12, 13, 14, 16, 18, 20, 21, 24, 26, 27, 29, 30, 33, 36 }; this->txPowerMax = eirpEncoding[maxEirpRaw]; - RADIOLIB_DEBUG_PRINTLN("TX timing: dlDwell = %d, ulDwell = %d, maxEirp = %d dBm", dlDwell, ulDwell, this->txPowerMax); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("TxParamSetupReq: dlDwell = %d, ulDwell = %d, maxEirp = %d dBm", dlDwell, ulDwell, eirpEncoding[maxEirpRaw]); this->dwellTimeEnabledUp = ulDwell ? true : false; this->dwellTimeUp = ulDwell ? RADIOLIB_LORAWAN_DWELL_TIME : 0; @@ -2640,12 +2490,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { this->dwellTimeEnabledDn = dlDwell ? true : false; this->dwellTimeDn = dlDwell ? RADIOLIB_LORAWAN_DWELL_TIME : 0; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_TX_PARAM_SETUP_ID, cmd->payload[0]); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; return(true); @@ -2654,7 +2499,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { case(RADIOLIB_LORAWAN_MAC_REKEY): { // get the server version uint8_t srvVersion = cmd->payload[0]; - RADIOLIB_DEBUG_PRINTLN("Server version: 1.%d", srvVersion); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RekeyConf: server version = 1.%d", srvVersion); if((srvVersion > 0) && (srvVersion <= this->rev)) { // valid server version, stop sending the ReKey MAC command deleteMacCommand(RADIOLIB_LORAWAN_MAC_REKEY, &this->commandsUp); @@ -2665,20 +2510,16 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { case(RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP): { this->adrLimitExp = (cmd->payload[0] & 0xF0) >> 4; this->adrDelayExp = cmd->payload[0] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("ADR param setup: limitExp = %d, delayExp = %d", this->adrLimitExp, this->adrDelayExp); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADRParamSetupReq: limitExp = %d, delayExp = %d", this->adrLimitExp, this->adrDelayExp); -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_ADR_PARAM_SETUP_ID, cmd->payload[0]); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; return(true); } break; case(RADIOLIB_LORAWAN_MAC_DEVICE_TIME): { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DeviceTimeAns: [user]"); // delete any existing response (does nothing if there is none) deleteMacCommand(RADIOLIB_LORAWAN_MAC_DEVICE_TIME, &this->commandsDown); @@ -2689,12 +2530,12 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { case(RADIOLIB_LORAWAN_MAC_FORCE_REJOIN): { // TODO implement this - uint16_t rejoinReq = LoRaWANNode::ntoh(&cmd->payload[0]); + uint16_t rejoinReq = LoRaWANNode::ntoh(cmd->payload); uint8_t period = (rejoinReq & 0x3800) >> 11; uint8_t maxRetries = (rejoinReq & 0x0700) >> 8; uint8_t rejoinType = (rejoinReq & 0x0070) >> 4; uint8_t dr = rejoinReq & 0x000F; - RADIOLIB_DEBUG_PRINTLN("Force rejoin: period = %d, maxRetries = %d, rejoinType = %d, dr = %d", period, maxRetries, rejoinType, dr); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ForceRejoinReq: period = %d, maxRetries = %d, rejoinType = %d, dr = %d", period, maxRetries, rejoinType, dr); (void)period; (void)maxRetries; (void)rejoinType; @@ -2706,17 +2547,13 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom) { // TODO implement this uint8_t maxTime = (cmd->payload[0] & 0xF0) >> 4; uint8_t maxCount = cmd->payload[0] & 0x0F; - RADIOLIB_DEBUG_PRINTLN("Rejoin setup: maxTime = %d, maxCount = %d", maxTime, maxCount); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RejoinParamSetupReq: maxTime = %d, maxCount = %d", maxTime, maxCount); -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - if(saveToEeprom) { - mod->hal->setPersistentParameter(RADIOLIB_EEPROM_LORAWAN_REJOIN_PARAM_SETUP_ID, cmd->payload[0]); - - } -#endif + memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; cmd->payload[0] = (1 << 1) | 1; + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RejoinParamSetupAns: status = 0x%02x", cmd->payload[0]); (void)maxTime; (void)maxCount; @@ -2751,31 +2588,29 @@ bool LoRaWANNode::applyChannelMaskDyn(uint8_t chMaskCntl, uint16_t chMask) { } for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - RADIOLIB_DEBUG_PRINTLN("UL: %d %d %6.3f (%d - %d) | DL: %d %d %6.3f (%d - %d)", - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("UL: %3d %d %7.3f (%d - %d) | DL: %3d %d %7.3f (%d - %d)", + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax - ); + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax + ); + } } return(true); } -bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask, bool clear) { - RADIOLIB_DEBUG_PRINTLN("mask[%d] = 0x%04x", chMaskCntl, chMask); - if(clear) { - for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LORAWAN_CHANNEL_NONE; - } - } +bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("mask[%d] = 0x%04x", chMaskCntl, chMask); + // find out how many channels have already been configured uint8_t idx = 0; for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { @@ -2831,7 +2666,7 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask, bool uint16_t mask = 1 << i; if(mask & chMask) { // enable bank of 8 channels from first span - for(uint8_t j = 0; j < 8; i++) { + for(uint8_t j = 0; j < 8; j++) { uint8_t chNum = i * 8 + j; chnl.enabled = true; chnl.idx = chNum; @@ -2854,9 +2689,8 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask, bool } if(this->band->numTxSpans == 2 && chMaskCntl == 6) { // all channels on (but we revert to selected subband) - if(this->subBand >= 0) { - this->setupChannelsFix(this->subBand); - } + this->setupChannelsFix(this->subBand); + // a '1' enables a single channel from second span LoRaWANChannel_t chnl; for(uint8_t i = 0; i < 8; i++) { @@ -2900,19 +2734,21 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask, bool } for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - RADIOLIB_DEBUG_PRINTLN("UL: %d %d %6.3f (%d - %d) | DL: %d %d %6.3f (%d - %d)", - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, + if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("UL: %3d %d %7.3f (%d - %d) | DL: %3d %d %7.3f (%d - %d)", + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax - ); + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].drMax + ); + } } return(true); @@ -2974,7 +2810,7 @@ void LoRaWANNode::performCSMA() { bool channelFreeDuringDIFS = true; for (uint8_t i = 0; i < this->difsSlots; i++) { if (performCAD()) { - RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING DIFS"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Occupied channel during DIFS"); channelFreeDuringDIFS = false; // Channel is occupied during DIFS, hop to another. this->selectChannels(); @@ -2986,7 +2822,7 @@ void LoRaWANNode::performCSMA() { // Continue decrementing BO with per each CAD reporting free channel. while (BO > 0) { if (performCAD()) { - RADIOLIB_DEBUG_PRINTLN("OCCUPIED CHANNEL DURING BO"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Occupied channel during BO"); // Channel is busy during CAD, hop to another and return to DIFS state again. this->selectChannels(); break; // Exit loop. Go back to DIFS state. @@ -3045,16 +2881,14 @@ 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) { - if(keyLen > RADIOLIB_AES128_KEY_SIZE / 2) { - keyLen = RADIOLIB_AES128_KEY_SIZE / 2; - } - uint16_t buf16[RADIOLIB_AES128_KEY_SIZE / 2] = { 0 }; - uint8_t bufLen = keyLen / 2; - memcpy(buf16, key, keyLen); +uint16_t LoRaWANNode::checkSum16(uint8_t *key, uint16_t keyLen) { uint16_t checkSum = 0; - for(int i = 0; i < bufLen; i++) { - checkSum ^= buf16[i]; + for(uint16_t i = 0; i < keyLen; i += 2) { + checkSum ^= ((uint16_t)key[i] << 8) | key[i + 1]; + } + if(keyLen % 2 == 1) { + uint16_t val = ((uint16_t)key[keyLen - 1] << 8); + checkSum ^= val; } return(checkSum); } diff --git a/src/protocols/LoRaWAN/LoRaWAN.h b/src/protocols/LoRaWAN/LoRaWAN.h index 44941a4d..0aab1fa7 100644 --- a/src/protocols/LoRaWAN/LoRaWAN.h +++ b/src/protocols/LoRaWAN/LoRaWAN.h @@ -6,14 +6,14 @@ #include "../../utils/Cryptography.h" // activation mode -#define RADIOLIB_LORAWAN_MODE_OTAA (0x01AA) +#define RADIOLIB_LORAWAN_MODE_OTAA (0x07AA) #define RADIOLIB_LORAWAN_MODE_ABP (0x0AB9) #define RADIOLIB_LORAWAN_MODE_NONE (0x0000) // operation mode -#define RADIOLIB_LORAWAN_CLASS_A (0x00) -#define RADIOLIB_LORAWAN_CLASS_B (0x01) -#define RADIOLIB_LORAWAN_CLASS_C (0x02) +#define RADIOLIB_LORAWAN_CLASS_A (0x0A) +#define RADIOLIB_LORAWAN_CLASS_B (0x0B) +#define RADIOLIB_LORAWAN_CLASS_C (0x0C) // preamble format #define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34) @@ -226,6 +226,56 @@ const LoRaWANMacSpec_t MacTable[RADIOLIB_LORAWAN_NUM_MAC_COMMANDS + 1] = { { RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true } }; +#define RADIOLIB_LORAWAN_NONCES_VERSION_VAL (0x0001) + +enum LoRaWANSchemeBase_t { + RADIOLIB_LORAWAN_NONCES_VERSION = 0x00, // 2 bytes + RADIOLIB_LORAWAN_NONCES_MODE = 0x02, // 2 bytes + RADIOLIB_LORAWAN_NONCES_CLASS = 0x04, // 1 byte + RADIOLIB_LORAWAN_NONCES_PLAN = 0x05, // 1 byte + RADIOLIB_LORAWAN_NONCES_CHECKSUM = 0x06, // 2 bytes + RADIOLIB_LORAWAN_NONCES_DEV_NONCE = 0x08, // 2 bytes + RADIOLIB_LORAWAN_NONCES_JOIN_NONCE = 0x0A, // 3 bytes + RADIOLIB_LORAWAN_NONCES_ACTIVE = 0x0D, // 1 byte + RADIOLIB_LORAWAN_NONCES_SIGNATURE = 0x0E, // 2 bytes + RADIOLIB_LORAWAN_NONCES_BUF_SIZE = 0x10 // = 16 bytes +}; + +enum LoRaWANSchemeSession_t { + RADIOLIB_LORAWAN_SESSION_NWK_SENC_KEY = 0x00, // 16 bytes + RADIOLIB_LORAWAN_SESSION_APP_SKEY = 0x10, // 16 bytes + RADIOLIB_LORAWAN_SESSION_FNWK_SINT_KEY = 0x20, // 16 bytes + RADIOLIB_LORAWAN_SESSION_SNWK_SINT_KEY = 0x30, // 16 bytes + RADIOLIB_LORAWAN_SESSION_DEV_ADDR = 0x40, // 4 bytes + RADIOLIB_LORAWAN_SESSION_NONCES_SIGNATURE = 0x44, // 2 bytes + RADIOLIB_LORAWAN_SESSION_A_FCNT_DOWN = 0x46, // 4 bytes + RADIOLIB_LORAWAN_SESSION_CONF_FCNT_UP = 0x4A, // 4 bytes + RADIOLIB_LORAWAN_SESSION_CONF_FCNT_DOWN = 0x4E, // 4 bytes + RADIOLIB_LORAWAN_SESSION_RJ_COUNT0 = 0x52, // 2 bytes + RADIOLIB_LORAWAN_SESSION_RJ_COUNT1 = 0x54, // 2 bytes + RADIOLIB_LORAWAN_SESSION_HOMENET_ID = 0x56, // 4 bytes + RADIOLIB_LORAWAN_SESSION_VERSION = 0x5A, // 1 byte + RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE = 0x5B, // 1 byte + RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP = 0x5C, // 4 bytes + RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP = 0x60, // 1 byte + RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP = 0x61, // 1 byte + RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP = 0x62, // 1 byte + RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP = 0x63, // 1 byte + RADIOLIB_LORAWAN_SESSION_BEACON_FREQ = 0x64, // 3 bytes + RADIOLIB_LORAWAN_SESSION_PING_SLOT_CHANNEL = 0x67, // 4 bytes + RADIOLIB_LORAWAN_SESSION_PERIODICITY = 0x6B, // 1 byte + RADIOLIB_LORAWAN_SESSION_LAST_TIME = 0x6C, // 4 bytes + RADIOLIB_LORAWAN_SESSION_UL_CHANNELS = 0x70, // 16*8 bytes + RADIOLIB_LORAWAN_SESSION_DL_CHANNELS = 0xF0, // 16*4 bytes + RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL = 0x0130, // 9*8+2 bytes + RADIOLIB_LORAWAN_SESSION_N_FCNT_DOWN = 0x017A, // 4 bytes + RADIOLIB_LORAWAN_SESSION_ADR_FCNT = 0x017E, // 4 bytes + RADIOLIB_LORAWAN_SESSION_LINK_ADR = 0x0182, // 4 bytes + RADIOLIB_LORAWAN_SESSION_FCNT_UP = 0x0186, // 4 bytes + RADIOLIB_LORAWAN_SESSION_SIGNATURE = 0x018A, // 2 bytes + RADIOLIB_LORAWAN_SESSION_BUF_SIZE = 0x018C // 396 bytes +}; + /*! \struct LoRaWANChannelSpan_t \brief Structure to save information about LoRaWAN channels. @@ -284,6 +334,9 @@ struct LoRaWANChannelSpan_t { \brief Structure to save information about LoRaWAN band */ struct LoRaWANBand_t { + /*! \brief Identier for this band */ + uint8_t bandNum; + /*! \brief Whether the channels are fixed per specification, or dynamically allocated through the network (plus defaults) */ uint8_t bandType; @@ -415,7 +468,6 @@ class LoRaWANNode { */ LoRaWANNode(PhysicalLayer* phy, const LoRaWANBand_t* band, uint8_t subBand = 0); -#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. @@ -423,12 +475,36 @@ class LoRaWANNode { void wipe(); /*! - \brief Restore session by loading information from persistent storage. - \returns \ref status_codes in case of error, - else LoRaWAN session mode (0 = no active session, 0xAA / 170 = OTAA, 0xAB / 171 = ABP) + \brief Returns the pointer to the internal buffer that holds the LW base parameters + \returns Pointer to uint8_t array of size RADIOLIB_LORAWAN_NONCES_BUF_SIZE */ - int16_t restore(); -#endif + uint8_t* getBufferNonces(); + + /*! + \brief Fill the internal buffer that holds the LW base parameters with a supplied buffer + \param persistentBuffer Buffer that should match the internal format (previously extracted using getBufferNonces) + \returns \ref status_codes + */ + int16_t setBufferNonces(uint8_t* persistentBuffer); + + /*! + \brief Returns the pointer to the internal buffer that holds the LW session parameters + \returns Pointer to uint8_t array of size RADIOLIB_LORAWAN_SESSION_BUF_SIZE + */ + uint8_t* getBufferSession(); + + /*! + \brief Fill the internal buffer that holds the LW session parameters with a supplied buffer + \param persistentBuffer Buffer that should match the internal format (previously extracted using getBufferSession) + \returns \ref status_codes + */ + int16_t setBufferSession(uint8_t* persistentBuffer); + + /*! + \brief Restore session by loading information from persistent storage. + \returns \ref status_codes + */ + int16_t restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass, uint8_t freqPlan); /*! \brief Join network by performing over-the-air activation. By this procedure, @@ -437,12 +513,11 @@ class LoRaWANNode { \param devEUI 8-byte device identifier. \param nwkKey Pointer to the network AES-128 key. \param appKey Pointer to the application AES-128 key. - \param joinDr (OTAA:) The datarate at which to send the join-request; (ABP:) ignored \param force Set to true to force joining even if previously joined. - + \param joinDr The datarate at which to send the join-request and any subsequent uplinks (unless ADR is enabled) \returns \ref status_codes */ - int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, uint8_t joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED, bool force = false); + int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force = false, uint8_t joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED); /*! \brief Join network by performing activation by personalization. @@ -450,19 +525,19 @@ class LoRaWANNode { \param addr Device address. \param nwkSKey Pointer to the network session AES-128 key (LoRaWAN 1.0) or MAC command network session key (LoRaWAN 1.1). \param appSKey Pointer to the application session AES-128 key. - \param fNwkSIntKey Pointer to the network session F 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. + \param fNwkSIntKey Pointer to the Forwarding network session (LoRaWAN 1.1), unused for LoRaWAN 1.0. + \param sNwkSIntKey Pointer to the Serving network session (LoRaWAN 1.1), unused for LoRaWAN 1.0. \param force Set to true to force a new session, even if one exists. + \param initialDr The datarate at which to send the first uplink and any subsequent uplinks (unless ADR is enabled) \returns \ref status_codes */ - int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL, bool force = false); + int16_t beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, uint8_t* fNwkSIntKey = NULL, uint8_t* sNwkSIntKey = NULL, bool force = false, uint8_t initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED); /*! \brief Whether there is an ongoing session active */ bool isJoined(); /*! - \brief Save the current state of the session. - All variables are compared to what is saved and only the differences are rewritten. + \brief Save the current state of the session to the session buffer. \returns \ref status_codes */ int16_t saveSession(); @@ -637,10 +712,9 @@ class LoRaWANNode { /*! \brief Set uplink datarate. This should not be used when ADR is enabled. \param dr Datarate to use for uplinks. - \param saveToEeprom Whether to save this setting to EEPROM or not (default false). \returns \ref status_codes */ - int16_t setDatarate(uint8_t drUp, bool saveToEeprom = false); + int16_t setDatarate(uint8_t drUp); /*! \brief Toggle ADR to on or off. @@ -686,10 +760,9 @@ class LoRaWANNode { /*! \brief Configure TX power of the radio module. \param txPower Output power during TX mode to be set in dBm. - \param saveToEeprom Whether to save this setting to EEPROM or not (default false). \returns \ref status_codes */ - int16_t setTxPower(int8_t txPower, bool saveToEeprom = false); + int16_t setTxPower(int8_t txPower); /*! \brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance. @@ -732,7 +805,15 @@ class LoRaWANNode { PhysicalLayer* phyLayer = NULL; const LoRaWANBand_t* band = NULL; - void beginCommon(uint8_t joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED); + static int16_t checkBufferCommon(uint8_t *buffer, uint16_t size); + + void beginCommon(uint8_t initialDr); + + // a buffer that holds all LW base parameters that should persist at all times! + uint8_t bufferNonces[RADIOLIB_LORAWAN_NONCES_BUF_SIZE] = { 0 }; + + // a buffer that holds all LW session parameters that preferably persist, but can be afforded to get lost + uint8_t bufferSession[RADIOLIB_LORAWAN_SESSION_BUF_SIZE] = { 0 }; LoRaWANMacCommandQueue_t commandsUp = { .numCommands = 0, @@ -776,7 +857,7 @@ class LoRaWANNode { bool FSK = false; // flag that shows whether the device is joined and there is an ongoing session (none, ABP or OTAA) - uint16_t activeMode = 0; + uint16_t activeMode = RADIOLIB_LORAWAN_MODE_NONE; // ADR is enabled by default bool adrEnabled = true; @@ -835,26 +916,6 @@ class LoRaWANNode { // save the selected sub-band in case this must be restored in ADR control uint8_t subBand = 0; -#if !defined(RADIOLIB_EEPROM_UNSUPPORTED) - /*! - \brief Save the current uplink frame counter. - Note that the usable frame counter width is 'only' 30 bits for highly efficient wear-levelling. - */ - void saveFcntUp(); - - /*! - \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. - */ - void restoreFcntUp(); - - // set all keys to zero - void clearSession(); - - // test if saved keys are non-zero - bool isValidSession(); -#endif - // wait for, open and listen during Rx1 and Rx2 windows; only performs listening int16_t downlinkCommon(); @@ -901,13 +962,13 @@ class LoRaWANNode { int16_t deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* queue, uint8_t* payload = NULL); // execute mac command, return the number of processed bytes for sequential processing - bool execMacCommand(LoRaWANMacCommand_t* cmd, bool saveToEeprom = true); + bool execMacCommand(LoRaWANMacCommand_t* cmd); // apply a channel mask to a set of readily defined channels (dynamic bands only) bool applyChannelMaskDyn(uint8_t chMaskCntl, uint16_t chMask); // define or delete channels from a fixed set of channels (fixed bands only) - bool applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask, bool clear); + bool applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask); // get the payload length for a specific MAC command uint8_t getMacPayloadLength(uint8_t cid); @@ -922,7 +983,7 @@ class LoRaWANNode { 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); + static uint16_t checkSum16(uint8_t *key, uint16_t keyLen); // network-to-host conversion method - takes data from network packet and converts it to the host endians template diff --git a/src/protocols/LoRaWAN/LoRaWANBands.cpp b/src/protocols/LoRaWAN/LoRaWANBands.cpp index e8b244bd..ad0b1b9e 100644 --- a/src/protocols/LoRaWAN/LoRaWANBands.cpp +++ b/src/protocols/LoRaWAN/LoRaWANBands.cpp @@ -2,7 +2,21 @@ #if !RADIOLIB_EXCLUDE_LORAWAN +enum LoRaWANBandNum_t { + BandNone, + BandEU868, + BandUS915, + BandCN780, + BandEU433, + BandAU915, + BandCN500, + BandAS923, + BandKR920, + BandIN865 +}; + const LoRaWANBand_t EU868 = { + .bandNum = BandEU868, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, @@ -48,6 +62,7 @@ const LoRaWANBand_t EU868 = { }; const LoRaWANBand_t US915 = { + .bandNum = BandUS915, .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 19, 61, 133, 250, 250, 0, 0, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, @@ -114,6 +129,7 @@ const LoRaWANBand_t US915 = { }; const LoRaWANBand_t CN780 = { + .bandNum = BandCN780, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 250, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, @@ -159,6 +175,7 @@ const LoRaWANBand_t CN780 = { }; const LoRaWANBand_t EU433 = { + .bandNum = BandEU433, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, @@ -204,6 +221,7 @@ const LoRaWANBand_t EU433 = { }; const LoRaWANBand_t AU915 = { + .bandNum = BandAU915, .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, @@ -270,6 +288,7 @@ const LoRaWANBand_t AU915 = { }; const LoRaWANBand_t CN500 = { + .bandNum = BandCN500, .bandType = RADIOLIB_LORAWAN_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 19, @@ -329,6 +348,7 @@ const LoRaWANBand_t CN500 = { }; const LoRaWANBand_t AS923 = { + .bandNum = BandAS923, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, @@ -374,6 +394,7 @@ const LoRaWANBand_t AS923 = { }; const LoRaWANBand_t KR920 = { + .bandNum = BandKR920, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 14, @@ -419,6 +440,7 @@ const LoRaWANBand_t KR920 = { }; const LoRaWANBand_t IN865 = { + .bandNum = BandIN865, .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 30, diff --git a/src/protocols/Morse/Morse.cpp b/src/protocols/Morse/Morse.cpp index 8ec0c469..90cc2a68 100644 --- a/src/protocols/Morse/Morse.cpp +++ b/src/protocols/Morse/Morse.cpp @@ -85,7 +85,7 @@ int MorseClient::read(uint8_t* symbol, uint8_t* len, float low, float high) { if((pauseLen >= low*(float)letterSpace) && (pauseLen <= high*(float)letterSpace)) { return(RADIOLIB_MORSE_CHAR_COMPLETE); } else if(pauseLen > wordSpace) { - RADIOLIB_DEBUG_PRINTLN("\n"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("\n"); return(RADIOLIB_MORSE_WORD_COMPLETE); } @@ -96,15 +96,15 @@ int MorseClient::read(uint8_t* symbol, uint8_t* len, float low, float high) { uint32_t signalLen = mod->hal->millis() - signalStart; if((signalLen >= low*(float)dotLength) && (signalLen <= high*(float)dotLength)) { - RADIOLIB_DEBUG_PRINT("."); + RADIOLIB_DEBUG_PROTOCOL_PRINT("."); (*symbol) |= (RADIOLIB_MORSE_DOT << (*len)); (*len)++; } else if((signalLen >= low*(float)dashLength) && (signalLen <= high*(float)dashLength)) { - RADIOLIB_DEBUG_PRINT("-"); + RADIOLIB_DEBUG_PROTOCOL_PRINT("-"); (*symbol) |= (RADIOLIB_MORSE_DASH << (*len)); (*len)++; } else { - RADIOLIB_DEBUG_PRINTLN("", signalLen); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("", signalLen); } } @@ -122,7 +122,7 @@ size_t MorseClient::write(uint8_t b) { // inter-word pause (space) if(b == ' ') { - RADIOLIB_DEBUG_PRINTLN("space"); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("space"); standby(); mod->waitForMicroseconds(mod->hal->micros(), wordSpace*1000); return(1); @@ -141,11 +141,11 @@ size_t MorseClient::write(uint8_t b) { // send dot or dash if (code & RADIOLIB_MORSE_DASH) { - RADIOLIB_DEBUG_PRINT("-"); + RADIOLIB_DEBUG_PROTOCOL_PRINT("-"); transmitDirect(baseFreq, baseFreqHz); mod->waitForMicroseconds(mod->hal->micros(), dashLength*1000); } else { - RADIOLIB_DEBUG_PRINT("."); + RADIOLIB_DEBUG_PROTOCOL_PRINT("."); transmitDirect(baseFreq, baseFreqHz); mod->waitForMicroseconds(mod->hal->micros(), dotLength*1000); } @@ -161,7 +161,7 @@ size_t MorseClient::write(uint8_t b) { // letter space standby(); mod->waitForMicroseconds(mod->hal->micros(), letterSpace*1000 - dotLength*1000); - RADIOLIB_DEBUG_PRINTLN(); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN(); return(1); } diff --git a/src/protocols/Pager/Pager.cpp b/src/protocols/Pager/Pager.cpp index 799fd626..1db47da6 100644 --- a/src/protocols/Pager/Pager.cpp +++ b/src/protocols/Pager/Pager.cpp @@ -28,6 +28,9 @@ PagerClient::PagerClient(PhysicalLayer* phy) { #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE readBitInstance = phyLayer; #endif + filterNumAddresses = 0; + filterAddresses = NULL; + filterMasks = NULL; } int16_t PagerClient::begin(float base, uint16_t speed, bool invert, uint16_t shift) { @@ -245,7 +248,24 @@ int16_t PagerClient::startReceive(uint32_t pin, uint32_t addr, uint32_t mask) { readBitPin = pin; filterAddr = addr; filterMask = mask; + filterAddresses = NULL; + filterMasks = NULL; + filterNumAddresses = 0; + return(startReceiveCommon()); +} +int16_t PagerClient::startReceive(uint32_t pin, uint32_t *addrs, uint32_t *masks, size_t numAddresses) { + // save the variables + readBitPin = pin; + filterAddr = 0; + filterMask = 0; + filterAddresses = addrs; + filterMasks = masks; + filterNumAddresses = numAddresses; + return(startReceiveCommon()); +} + +int16_t PagerClient::startReceiveCommon() { // set the carrier frequency int16_t state = phyLayer->setFrequency(baseFreq); RADIOLIB_ASSERT(state); @@ -260,7 +280,7 @@ int16_t PagerClient::startReceive(uint32_t pin, uint32_t addr, uint32_t mask) { // now set up the direct mode reception Module* mod = phyLayer->getMod(); - mod->hal->pinMode(pin, mod->hal->GpioModeInput); + mod->hal->pinMode(readBitPin, mod->hal->GpioModeInput); // set direct sync word to the frame sync word // the logic here is inverted, because modules like SX1278 @@ -356,8 +376,7 @@ int16_t PagerClient::readData(uint8_t* data, size_t* len, uint32_t* addr) { // should be an address code word, extract the address uint32_t addr_found = ((cw & RADIOLIB_PAGER_ADDRESS_BITS_MASK) >> (RADIOLIB_PAGER_ADDRESS_POS - 3)) | (framePos/2); - if((addr_found & filterMask) == (filterAddr & filterMask)) { - // we have a match! + if (addressMatched(addr_found)) { match = true; if(addr) { *addr = addr_found; @@ -460,6 +479,26 @@ int16_t PagerClient::readData(uint8_t* data, size_t* len, uint32_t* addr) { } #endif +bool PagerClient::addressMatched(uint32_t addr) { + // check whether to match single or multiple addresses/masks + if(filterNumAddresses == 0) { + return((addr & filterMask) == (filterAddr & filterMask)); + } + + // multiple addresses, check there are some to match + if((filterAddresses == NULL) || (filterMasks == NULL)) { + return(false); + } + + for(size_t i = 0; i < filterNumAddresses; i++) { + if((filterAddresses[i] & filterMasks[i]) == (addr & filterMasks[i])) { + return(true); + } + } + + return(false); +} + void PagerClient::write(uint32_t* data, size_t len) { // write code words from buffer for(size_t i = 0; i < len; i++) { @@ -515,7 +554,7 @@ uint32_t PagerClient::read() { codeWord = ~codeWord; } - RADIOLIB_VERBOSE_PRINTLN("R\t%lX", codeWord); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("R\t%lX", codeWord); // TODO BCH error correction here return(codeWord); } diff --git a/src/protocols/Pager/Pager.h b/src/protocols/Pager/Pager.h index 11b9c4dd..f5cdb819 100644 --- a/src/protocols/Pager/Pager.h +++ b/src/protocols/Pager/Pager.h @@ -130,6 +130,16 @@ class PagerClient { */ int16_t startReceive(uint32_t pin, uint32_t addr, uint32_t mask = 0xFFFFF); + /*! + \brief Start reception of POCSAG packets for multiple addresses and masks. + \param pin Pin to receive digital data on (e.g., DIO2 for SX127x). + \param addrs Array of addresses to receive. + \param masks Array of address masks to use for filtering. Masks will be applied to corresponding addresses in addr array. + \param numAddress Number of addresses/masks to match. + \returns \ref status_codes + */ + int16_t startReceive(uint32_t pin, uint32_t *addrs, uint32_t *masks, size_t numAddress); + /*! \brief Get the number of POCSAG batches available in buffer. Limited by the size of direct mode buffer! \returns Number of available batches. @@ -175,10 +185,15 @@ class PagerClient { uint16_t bitDuration; uint32_t filterAddr; uint32_t filterMask; + uint32_t *filterAddresses; + uint32_t *filterMasks; + size_t filterNumAddresses; bool inv = false; void write(uint32_t* data, size_t len); void write(uint32_t codeWord); + int16_t startReceiveCommon(); + bool addressMatched(uint32_t addr); #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE uint32_t read(); diff --git a/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/src/protocols/PhysicalLayer/PhysicalLayer.cpp index f5ca56ca..a1b51730 100644 --- a/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -413,7 +413,7 @@ void PhysicalLayer::updateDirectBuffer(uint8_t bit) { this->syncBuffer <<= 1; this->syncBuffer |= bit; - RADIOLIB_VERBOSE_PRINTLN("S\t%lu", this->syncBuffer); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("S\t%lu", this->syncBuffer); if((this->syncBuffer & this->directSyncWordMask) == this->directSyncWord) { this->gotSync = true; @@ -434,7 +434,7 @@ void PhysicalLayer::updateDirectBuffer(uint8_t bit) { // check complete byte if(this->bufferBitPos == 8) { this->buffer[this->bufferWritePos] = Module::reflect(this->buffer[this->bufferWritePos], 8); - RADIOLIB_VERBOSE_PRINTLN("R\t%X", this->buffer[this->bufferWritePos]); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("R\t%X", this->buffer[this->bufferWritePos]); this->bufferWritePos++; this->bufferBitPos = 0;