diff --git a/lib/RadioLib/.github/workflows/cppcheck.yml b/lib/RadioLib/.github/workflows/cppcheck.yml index 684e11f..8f3ec3d 100644 --- a/lib/RadioLib/.github/workflows/cppcheck.yml +++ b/lib/RadioLib/.github/workflows/cppcheck.yml @@ -24,4 +24,4 @@ jobs: - name: Run cppcheck run: - cppcheck src --enable=all --force + cppcheck src --enable=all --force --inline-suppr --quiet --suppress=ConfigurationNotChecked --suppress=unusedFunction diff --git a/lib/RadioLib/.github/workflows/main.yml b/lib/RadioLib/.github/workflows/main.yml index 5eaf568..928541a 100644 --- a/lib/RadioLib/.github/workflows/main.yml +++ b/lib/RadioLib/.github/workflows/main.yml @@ -104,6 +104,10 @@ jobs: run: echo "index-url=--additional-urls https://resource.heltec.cn/download/package_CubeCell_index.json" >> $GITHUB_OUTPUT - id: MegaCore:avr:1281 run: echo "index-url=--additional-urls https://mcudude.github.io/MegaCore/package_MCUdude_MegaCore_index.json" >> $GITHUB_OUTPUT + - id: teensy:avr:teensy41 + run: | + echo "skip-pattern=(STM32WL|LoRaWAN)" >> $GITHUB_OUTPUT + echo "index-url=--additional-urls https://www.pjrc.com/teensy/package_teensy_index.json" >> $GITHUB_OUTPUT - id: arduino:renesas_uno:minima run: | echo "skip-pattern=(STM32WL|LoRaWAN)" >> $GITHUB_OUTPUT @@ -166,7 +170,7 @@ jobs: else # apply special flags for LoRaWAN if [[ ${example} =~ "LoRaWAN" ]]; then - flags="-DRADIOLIB_LORAWAN_DEV_ADDR=0 -DRADIOLIB_LORAWAN_NWKS_KEY=0 -DRADIOLIB_LORAWAN_SNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_NWKSENC_KEY=0 -DRADIOLIB_LORAWAN_APPS_KEY=0 -DRADIOLIB_LORAWAN_APP_KEY=0 -DRADIOLIB_LORAWAN_NWK_KEY=0 -DRADIOLIB_LORAWAN_DEV_EUI=0 -DARDUINO_TTGO_LORA32_V1" + flags="-DRADIOLIB_LORAWAN_DEV_ADDR=0 -DRADIOLIB_LORAWAN_FNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_SNWKSINT_KEY=0 -DRADIOLIB_LORAWAN_NWKSENC_KEY=0 -DRADIOLIB_LORAWAN_APPS_KEY=0 -DRADIOLIB_LORAWAN_APP_KEY=0 -DRADIOLIB_LORAWAN_NWK_KEY=0 -DRADIOLIB_LORAWAN_DEV_EUI=0 -DARDUINO_TTGO_LORA32_V1" fi # build sketch @@ -222,13 +226,15 @@ jobs: - name: Install dependencies run: | - sudo apt-get install -y gcc-arm-none-eabi + sudo apt-get install -y gcc-arm-none-eabi gcc-riscv64-unknown-elf cargo install elf2tab - name: Build the example run: | cd $PWD/examples/NonArduino/Tock - ./build.sh + git clone https://github.com/tock/libtock-c.git + cd libtock-c; git checkout 44bf89c545953d8859faf101d4b4a4b6a151fe6c; cd ../ + LIBTOCK_C_DIRECTORY="$(pwd)/libtock-c" ./build.sh rpi-build: runs-on: [self-hosted, ARM64] diff --git a/lib/RadioLib/.gitmodules b/lib/RadioLib/.gitmodules deleted file mode 100644 index 7eb8694..0000000 --- a/lib/RadioLib/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "examples/NonArduino/Tock/libtock-c"] - path = examples/NonArduino/Tock/libtock-c - url = https://github.com/tock/libtock-c.git diff --git a/lib/RadioLib/CMakeLists.txt b/lib/RadioLib/CMakeLists.txt index 680864d..2291050 100644 --- a/lib/RadioLib/CMakeLists.txt +++ b/lib/RadioLib/CMakeLists.txt @@ -32,6 +32,12 @@ target_include_directories(RadioLib PUBLIC $ $) +# use c++20 standard +set_property(TARGET RadioLib PROPERTY CXX_STANDARD 20) + +# enable most warnings +target_compile_options(RadioLib PRIVATE -Wall -Wextra) + include(GNUInstallDirs) install(TARGETS RadioLib diff --git a/lib/RadioLib/README.md b/lib/RadioLib/README.md index d33794b..cc9f908 100644 --- a/lib/RadioLib/README.md +++ b/lib/RadioLib/README.md @@ -16,6 +16,7 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github ### Supported modules: * __CC1101__ FSK radio module * __LLCC68__ LoRa module +* __LR11x0__ series LoRa/GFSK modules (LR1110, LR1120, LR1121) * __nRF24L01__ 2.4 GHz module * __RF69__ FSK/OOK radio module * __RFM2x__ series FSK modules (RFM22, RM23) @@ -29,21 +30,21 @@ RadioLib was originally created as a driver for [__RadioShield__](https://github ### Supported protocols and digital modes: * [__AX.25__](https://www.sigidwiki.com/wiki/PACKET) using 2-FSK or AFSK for modules: -SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x +SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x, Si443x, LR11x0 and SX128x * [__RTTY__](https://www.sigidwiki.com/wiki/RTTY) using 2-FSK or AFSK for modules: -SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x +SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x, LR11x0 and SX128x * [__Morse Code__](https://www.sigidwiki.com/wiki/Morse_Code_(CW)) using 2-FSK or AFSK for modules: -SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x +SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x, LR11x0 and SX128x * [__SSTV__](https://www.sigidwiki.com/wiki/SSTV) using 2-FSK or AFSK for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, RFM2x and Si443x * [__Hellschreiber__](https://www.sigidwiki.com/wiki/Hellschreiber) using 2-FSK or AFSK for modules: -SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x +SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x, LR11x0 and SX128x * [__APRS__](https://www.sigidwiki.com/wiki/APRS) using AFSK for modules: SX127x, RFM9x, SX126x, RF69, SX1231, CC1101, nRF24L01, RFM2x, Si443x and SX128x * [__POCSAG__](https://www.sigidwiki.com/wiki/POCSAG) using 2-FSK for modules: SX127x, RFM9x, RF69, SX1231, CC1101, nRF24L01, RFM2x and Si443x * [__LoRaWAN__](https://lora-alliance.org/) using LoRa for modules: -SX127x, RFM9x, SX126x and SX128x +SX127x, RFM9x, SX126x, LR11x0 and SX128x * NOTE: LoRaWAN support is currently in beta, feedback via [Issues](https://github.com/jgromes/RadioLib/issues) and [Discussions](https://github.com/jgromes/RadioLib/discussions) is appreciated! ### Supported Arduino platforms: diff --git a/lib/RadioLib/examples/APRS/APRS_Position_LoRa/APRS_Position_LoRa.ino b/lib/RadioLib/examples/APRS/APRS_Position_LoRa/APRS_Position_LoRa.ino index 6b3053a..31a2f82 100644 --- a/lib/RadioLib/examples/APRS/APRS_Position_LoRa/APRS_Position_LoRa.ino +++ b/lib/RadioLib/examples/APRS/APRS_Position_LoRa/APRS_Position_LoRa.ino @@ -8,6 +8,7 @@ - SX127x/RFM9x - SX126x/LLCC68 - SX128x + - LR11x0 For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration diff --git a/lib/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino b/lib/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino index 809d717..8a55be7 100644 --- a/lib/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino +++ b/lib/RadioLib/examples/AX25/AX25_Frames/AX25_Frames.ino @@ -12,6 +12,7 @@ - SX126x - nRF24 - Si443x/RFM2x + - LR11x0 Using raw AX.25 frames requires some knowledge of the protocol, refer to diff --git a/lib/RadioLib/examples/AX25/AX25_Transmit/AX25_Transmit.ino b/lib/RadioLib/examples/AX25/AX25_Transmit/AX25_Transmit.ino index 98236d1..bbefc67 100644 --- a/lib/RadioLib/examples/AX25/AX25_Transmit/AX25_Transmit.ino +++ b/lib/RadioLib/examples/AX25/AX25_Transmit/AX25_Transmit.ino @@ -12,6 +12,7 @@ - SX126x - nRF24 - Si443x/RFM2x + - LR11x0 For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration diff --git a/lib/RadioLib/examples/Hellschreiber/Hellschreiber_Transmit/Hellschreiber_Transmit.ino b/lib/RadioLib/examples/Hellschreiber/Hellschreiber_Transmit/Hellschreiber_Transmit.ino index 8374da5..258962b 100644 --- a/lib/RadioLib/examples/Hellschreiber/Hellschreiber_Transmit/Hellschreiber_Transmit.ino +++ b/lib/RadioLib/examples/Hellschreiber/Hellschreiber_Transmit/Hellschreiber_Transmit.ino @@ -13,6 +13,7 @@ - nRF24 - Si443x/RFM2x - SX128x + - LR11x0 For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Blocking/LR11x0_Channel_Activity_Detection_Blocking.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Blocking/LR11x0_Channel_Activity_Detection_Blocking.ino new file mode 100644 index 0000000..171c87d --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Blocking/LR11x0_Channel_Activity_Detection_Blocking.ino @@ -0,0 +1,75 @@ +/* + RadioLib LR11x0 Blocking Channel Activity Detection Example + + This example uses LR1110 to scan the current LoRa + channel and detect ongoing LoRa transmissions. + Unlike SX127x CAD, LR11x0 can detect any part + of LoRa transmission, not just the preamble. + + Other modules from LR11x0 family can also be used. + + Using blocking CAD is not recommended, as it will lead + to significant amount of timeouts, inefficient use of processor + time and can some miss packets! + Instead, interrupt CAD is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[LR1110] Scanning channel for LoRa transmission ... ")); + + // start scanning current channel + int state = radio.scanChannel(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa preamble was detected + Serial.println(F("detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // no preamble was detected, channel is free + Serial.println(F("channel is free!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait 100 ms before new scan + delay(100); +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Interrupt/LR11x0_Channel_Activity_Detection_Interrupt.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Interrupt/LR11x0_Channel_Activity_Detection_Interrupt.ino new file mode 100644 index 0000000..90f0c8a --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Channel_Activity_Detection_Interrupt/LR11x0_Channel_Activity_Detection_Interrupt.ino @@ -0,0 +1,110 @@ +/* + RadioLib LR11x0 Channel Activity Detection Example + + This example uses LR1110 to scan the current LoRa + channel and detect ongoing LoRa transmissions. + Unlike SX127x CAD, LR11x0 can detect any part + of LoRa transmission, not just the preamble. + + Other modules from LR11x0 family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when LoRa packet or timeout is detected + radio.setIrqAction(setFlag); + + // start scanning the channel + Serial.print(F("[LR1110] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a packet was detected or CAD timed out +volatile bool scanFlag = false; + +// this function is called when a complete packet +// is 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) { + // something happened, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // check CAD result + int state = radio.getChannelScanResult(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa packet was detected + Serial.println(F("[LR1110] Packet detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // channel is free + Serial.println(F("[LR1110] Channel is free!")); + + } else { + // some other error occurred + Serial.print(F("[LR1110] Failed, code ")); + Serial.println(state); + + } + + // start scanning the channel again + Serial.print(F("[LR1110] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_GFSK_Modem/LR11x0_GFSK_Modem.ino b/lib/RadioLib/examples/LR11x0/LR11x0_GFSK_Modem/LR11x0_GFSK_Modem.ino new file mode 100644 index 0000000..45dc050 --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_GFSK_Modem/LR11x0_GFSK_Modem.ino @@ -0,0 +1,153 @@ +/* + RadioLib LR11x0 GFSK Modem Example + + This example shows how to use GFSK modem in LR11x0 chips. + + NOTE: The sketch below is just a guide on how to use + GFSK modem, so this code should not be run directly! + Instead, modify the other examples to use GFSK + modem and use the appropriate configuration + methods. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---gfsk-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.beginGFSK(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, you can switch between any of the modems + // + // radio.begin() start LoRa modem (and disable GFSK) + // radio.beginGFSK() start GFSK modem (and disable LoRa) + + // the following settings can also + // be modified at run-time + state = radio.setFrequency(433.5); + state = radio.setBitRate(100.0); + state = radio.setFrequencyDeviation(10.0); + state = radio.setRxBandwidth(250.0); + state = radio.setOutputPower(10.0); + state = radio.setDataShaping(RADIOLIB_SHAPING_1_0); + uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + state = radio.setSyncWord(syncWord, 8); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Unable to set configuration, code ")); + Serial.println(state); + while (true); + } + + // GFSK modem on LR11x0 can handle the sync word setting in bits, not just + // whole bytes. The value used is left-justified. + // This makes same result as radio.setSyncWord(syncWord, 8): + state = radio.setSyncBits(syncWord, 64); + // This will use 0x012 as sync word (12 bits only): + state = radio.setSyncBits(syncWord, 12); + + // GFSK modem allows advanced CRC configuration + // Default is CCIT CRC16 (2 bytes, initial 0x1D0F, polynomial 0x1021, inverted) + // Set CRC to IBM CRC (2 bytes, initial 0xFFFF, polynomial 0x8005, non-inverted) + state = radio.setCRC(2, 0xFFFF, 0x8005, false); + // set CRC length to 0 to disable CRC + + #warning "This sketch is just an API guide! Read the note at line 6." +} + +void loop() { + // GFSK modem can use the same transmit/receive methods + // as the LoRa modem, even their interrupt-driven versions + + // transmit GFSK packet + int state = radio.transmit("Hello World!"); + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = radio.transmit(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Packet transmitted successfully!")); + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + Serial.println(F("[LR1110] Packet too long!")); + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while transmitting!")); + } else { + Serial.println(F("[LR1110] Failed to transmit packet, code ")); + Serial.println(state); + } + + // receive GFSK packet + String str; + state = radio.receive(str); + /* + byte byteArr[8]; + int state = radio.receive(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Received packet!")); + Serial.print(F("[LR1110] Data:\t")); + Serial.println(str); + } else if (state == RADIOLIB_ERR_RX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while waiting for packet!")); + } else { + Serial.print(F("[LR1110] Failed to receive packet, code ")); + Serial.println(state); + } + + // GFSK modem has built-in address filtering system + // it can be enabled by setting node address, broadcast + // address, or both + // + // to transmit packet to a particular address, + // use the following methods: + // + // radio.transmit("Hello World!", address); + // radio.startTransmit("Hello World!", address); + + // set node address to 0x02 + state = radio.setNodeAddress(0x02); + // set broadcast address to 0xFF + state = radio.setBroadcastAddress(0xFF); + if (state != RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Unable to set address filter, code ")); + Serial.println(state); + } + + // address filtering can also be disabled + // NOTE: calling this method will also erase previously set + // node and broadcast address + /* + state = radio.disableAddressFiltering(); + if (state != RADIOLIB_ERR_NONE) { + Serial.println(F("Unable to remove address filter, code ")); + } + */ +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino b/lib/RadioLib/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino new file mode 100644 index 0000000..a46d8a8 --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_LR_FHSS_Modem/LR11x0_LR_FHSS_Modem.ino @@ -0,0 +1,110 @@ +/* + RadioLib LR11x0 LR-FHSS Modem Example + + This example shows how to use LR-FHSS modem in LR11x0 chips. + + NOTE: The sketch below is just a guide on how to use + LR-FHSS modem, so this code should not be run directly! + Instead, modify the other examples to use LR-FHSS + modem and use the appropriate configuration + methods. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lr-fhss-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.beginLRFHSS(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, you can switch between any of the modems + // + // radio.begin() start LoRa modem (and disable LR-FHSS) + // radio.beginLRFHSS() start LR-FHSS modem (and disable LoRa) + + // the following settings can also + // be modified at run-time + state = radio.setFrequency(433.5); + state = radio.setLrFhssConfig(RADIOLIB_LR11X0_LR_FHSS_BW_1523_4, // bandwidth + RADIOLIB_LR11X0_LR_FHSS_CR_1_2, // coding rate + 3, // header count + 0x13A); // hopping sequence seed + state = radio.setOutputPower(10.0); + state = radio.setSyncWord(0x12345678); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Unable to set configuration, code ")); + Serial.println(state); + while (true); + } + + #warning "This sketch is just an API guide! Read the note at line 6." +} + +void loop() { + // LR-FHSS modem can use the same transmit/receive methods + // as the LoRa modem, even their interrupt-driven versions + + // transmit LR-FHSS packet + int state = radio.transmit("Hello World!"); + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = radio.transmit(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Packet transmitted successfully!")); + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + Serial.println(F("[LR1110] Packet too long!")); + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while transmitting!")); + } else { + Serial.println(F("[LR1110] Failed to transmit packet, code ")); + Serial.println(state); + } + + // receive LR-FHSS packet + String str; + state = radio.receive(str); + /* + byte byteArr[8]; + int state = radio.receive(byteArr, 8); + */ + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("[LR1110] Received packet!")); + Serial.print(F("[LR1110] Data:\t")); + Serial.println(str); + } else if (state == RADIOLIB_ERR_RX_TIMEOUT) { + Serial.println(F("[LR1110] Timed out while waiting for packet!")); + } else { + Serial.print(F("[LR1110] Failed to receive packet, code ")); + Serial.println(state); + } + +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino new file mode 100644 index 0000000..8d0efc9 --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino @@ -0,0 +1,104 @@ +/* + RadioLib LR11x0 Blocking Receive Example + + This example listens for LoRa transmissions using LR11x0 Lora modules. + To successfully receive data, the following settings have to be the same + on both transmitter and receiver: + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + - preamble length + + Other modules from LR11x0 family can also be used. + + Using blocking receive is not recommended, as it will lead + to significant amount of timeouts, inefficient use of processor + time and can some miss packets! + Instead, interrupt receive is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[LR1110] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + String str; + int state = radio.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = radio.receive(byteArr, 8); + */ + + if (state == RADIOLIB_ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print(F("[LR1110] Data:\t\t")); + Serial.println(str); + + // print the RSSI (Received Signal Strength Indicator) + // of the last received packet + Serial.print(F("[LR1110] RSSI:\t\t")); + Serial.print(radio.getRSSI()); + Serial.println(F(" dBm")); + + // print the SNR (Signal-to-Noise Ratio) + // of the last received packet + Serial.print(F("[LR1110] SNR:\t\t")); + Serial.print(radio.getSNR()); + Serial.println(F(" dB")); + + } else if (state == RADIOLIB_ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } else if (state == RADIOLIB_ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino new file mode 100644 index 0000000..f20f2aa --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino @@ -0,0 +1,138 @@ +/* + RadioLib LR11x0 Receive with Interrupts Example + + This example listens for LoRa transmissions and tries to + receive them. Once a packet is received, an interrupt is + triggered. To successfully receive data, the following + settings have to be the same on both transmitter + and receiver: + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + + Other modules from LR11x0 family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when new packet is received + radio.setPacketReceivedAction(setFlag); + + // start listening for LoRa packets + Serial.print(F("[LR1110] Starting to listen ... ")); + state = radio.startReceive(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, 'listen' mode can be disabled by calling + // any of the following methods: + // + // radio.standby() + // radio.sleep() + // radio.transmit(); + // radio.receive(); + // radio.scanChannel(); +} + +// flag to indicate that a packet was received +volatile bool receivedFlag = false; + +// this function is called when a complete packet +// is 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 got a packet, set the flag + receivedFlag = true; +} + +void loop() { + // check if the flag is set + if(receivedFlag) { + // reset flag + receivedFlag = false; + + // you can read received data as an Arduino String + String str; + int state = radio.readData(str); + + // you can also read received data as byte array + /* + byte byteArr[8]; + int numBytes = radio.getPacketLength(); + int state = radio.readData(byteArr, numBytes); + */ + + if (state == RADIOLIB_ERR_NONE) { + // packet was successfully received + Serial.println(F("[LR1110] Received packet!")); + + // print data of the packet + Serial.print(F("[LR1110] Data:\t\t")); + Serial.println(str); + + // print RSSI (Received Signal Strength Indicator) + Serial.print(F("[LR1110] RSSI:\t\t")); + Serial.print(radio.getRSSI()); + Serial.println(F(" dBm")); + + // print SNR (Signal-to-Noise Ratio) + Serial.print(F("[LR1110] SNR:\t\t")); + Serial.print(radio.getSNR()); + Serial.println(F(" dB")); + + } else if (state == RADIOLIB_ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + } +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino new file mode 100644 index 0000000..741102a --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino @@ -0,0 +1,106 @@ +/* + RadioLib LR11x0 Blocking Transmit Example + + This example transmits packets using LR1110 LoRa radio module. + Each packet contains up to 256 bytes of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + Other modules from LR11x0 family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + delay(1000); + while (true); + } + + // some modules have an external RF switch + + // controlled via two pins (RX enable, TX enable) + // to enable automatic control of the switch, + // call the following method + // RX enable: 4 + // TX enable: 5 + /* + radio.setRfSwitchPins(4, 5); + */ +} + +// counter to keep track of transmitted packets +int count = 0; + +void loop() { + Serial.print(F("[LR1110] Transmitting packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + // NOTE: transmit() is a blocking method! + // See example LR11x0_Transmit_Interrupt for details + // on non-blocking transmission method. + String str = "Hello World! #" + String(count++); + int state = radio.transmit(str); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; + int state = radio.transmit(byteArr, 8); + */ + + if (state == RADIOLIB_ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + // print measured data rate + Serial.print(F("[LR1110] Datarate:\t")); + Serial.print(radio.getDataRate()); + Serial.println(F(" bps")); + + } else if (state == RADIOLIB_ERR_PACKET_TOO_LONG) { + // the supplied packet was longer than 256 bytes + Serial.println(F("too long!")); + + } else if (state == RADIOLIB_ERR_TX_TIMEOUT) { + // timeout occured while transmitting packet + Serial.println(F("timeout!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before transmitting again + delay(1000); +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino b/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino new file mode 100644 index 0000000..e47c68f --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino @@ -0,0 +1,131 @@ +/* + RadioLib LR11x0 Transmit with Interrupts Example + + This example transmits LoRa packets with one second delays + between them. Each packet contains up to 256 bytes + of data, in the form of: + - Arduino String + - null-terminated char array (C-string) + - arbitrary binary data (byte array) + + Other modules from LR11x0 family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// IRQ pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +// save transmission state between loops +int transmissionState = RADIOLIB_ERR_NONE; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when packet transmission is finished + radio.setPacketSentAction(setFlag); + + // start transmitting the first packet + Serial.print(F("[LR1110] Sending first packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = radio.startTransmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + state = radio.startTransmit(byteArr, 8); + */ +} + +// flag to indicate that a packet was sent +volatile bool transmittedFlag = false; + +// this function is called when a complete packet +// is transmitted 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 a packet, set the flag + transmittedFlag = true; +} + +// counter to keep track of transmitted packets +int count = 0; + +void loop() { + // check if the previous transmission finished + if(transmittedFlag) { + // reset flag + transmittedFlag = false; + + if (transmissionState == RADIOLIB_ERR_NONE) { + // packet was successfully sent + Serial.println(F("transmission finished!")); + + // NOTE: when using interrupt-driven transmit method, + // it is not possible to automatically measure + // transmission data rate using getDataRate() + + } else { + Serial.print(F("failed, code ")); + Serial.println(transmissionState); + + } + + // clean up after transmission is finished + // this will ensure transmitter is disabled, + // RF switch is powered down etc. + radio.finishTransmit(); + + // wait a second before transmitting again + delay(1000); + + // send another one + Serial.print(F("[LR1110] Sending another packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + String str = "Hello World! #" + String(count++); + transmissionState = radio.startTransmit(str); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + transmissionState = radio.startTransmit(byteArr, 8); + */ + } +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino b/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino new file mode 100644 index 0000000..b1643a3 --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Blocking/LR11x0_WiFi_Scan_Blocking.ino @@ -0,0 +1,112 @@ +/* + RadioLib LR11x0 WiFi scan Blocking Example + + This example performs a passive scan of WiFi networks. + The scan shows basic information about the networks, + such as the frequency, country code and SSID. + + Other modules from LR11x0 family can also be used. + + Using blocking scan is not recommended, as depending + on the scan settings, the program may be blocked + for several seconds! Instead, interrupt scan is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---wifi-scan + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[LR1110] Running WiFi scan ... ")); + + // scan all WiFi signals with default scan configuration + uint8_t count = 0; + int state = radio.wifiScan('*', &count); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + + // print the table header + Serial.print(F("[LR1110] Reading ")); + Serial.print(count); + Serial.println(F(" scan results:")); + Serial.println(F(" # | WiFi type\t| Frequency\t| MAC Address\t | Country\t| RSSI [dBm]\t| SSID")); + + // read all results one by one + // this result type contains the most information, including the SSID + LR11x0WifiResultExtended_t result; + for(int i = 0; i < count; i++) { + if(i < 10) { Serial.print(" "); } Serial.print(i); Serial.print(" | "); + state = radio.getWifiScanResult(&result, i); + if(state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed to read result, code ")); + Serial.println(state); + continue; + } + + // print the basic information + Serial.print(F("802.11")); Serial.print(result.type); Serial.print("\t| "); + Serial.print(result.channelFreq); Serial.print(" MHz\t| "); + + // print MAC address + for(int j = 0; j < 6; j++) { + if(result.mac[j] < 0x10) { Serial.print("0"); } + Serial.print(result.mac[j], HEX); + if(j < 5) { Serial.print(":"); } + } + Serial.print(" | "); + + // print the two-letter country code + String country = result.countryCode; + Serial.print(country); + Serial.print(" \t| "); + + // print the RSSI + Serial.print(result.rssi); + Serial.print("\t| "); + + // print the network SSID + Serial.println((char*)result.ssid); + + } + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait for a second before scanning again + delay(1000); +} diff --git a/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino b/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino new file mode 100644 index 0000000..bf999ba --- /dev/null +++ b/lib/RadioLib/examples/LR11x0/LR11x0_WiFi_Scan_Interrupt/LR11x0_WiFi_Scan_Interrupt.ino @@ -0,0 +1,150 @@ +/* + RadioLib LR11x0 WiFi scan Interrupt Example + + This example performs a passive scan of WiFi networks. + The scan shows basic information about the networks, + such as the frequency, country code and SSID. + + Other modules from LR11x0 family can also be used. + + Using blocking scan is not recommended, as depending + on the scan settings, the program may be blocked + for several seconds! Instead, interrupt scan is recommended. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#lr11x0---wifi-scan + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// LR1110 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +LR1110 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//LR1110 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize LR1110 with default settings + Serial.print(F("[LR1110] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when WiFi scan is complete + radio.setIrqAction(setFlag); + + // scan all WiFi signals with default scan configuration + Serial.print(F("[LR1110] Starting passive WiFi scan ... ")); + state = radio.startWifiScan('*'); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a scan was completed +volatile bool scanFlag = false; + +// this function is called when a scan is completed +// 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) { + // scan is complete, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // get the number of scan results + uint8_t count = 0; + Serial.print(F("[LR1110] Reading WiFi scan results ... ")); + int state = radio.getWifiScanResultsCount(&count); + if(state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + + // print the table header + Serial.print(F("[LR1110] Reading ")); + Serial.print(count); + Serial.println(F(" scan results:")); + Serial.println(F(" # | WiFi type\t| Frequency\t| MAC Address\t | Country\t| RSSI [dBm]\t| SSID")); + + // read all results one by one + // this result type contains the most information, including the SSID + LR11x0WifiResultExtended_t result; + for(int i = 0; i < count; i++) { + if(i < 10) { Serial.print(" "); } Serial.print(i); Serial.print(" | "); + state = radio.getWifiScanResult(&result, i); + if(state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed to read result, code ")); + Serial.println(state); + continue; + } + + // print the basic information + Serial.print(F("802.11")); Serial.print(result.type); Serial.print("\t| "); + Serial.print(result.channelFreq); Serial.print(" MHz\t| "); + + // print MAC address + for(int j = 0; j < 6; j++) { + if(result.mac[j] < 0x10) { Serial.print("0"); } + Serial.print(result.mac[j], HEX); + if(j < 5) { Serial.print(":"); } + } + Serial.print(" | "); + + // print the two-letter country code + String country = result.countryCode; + Serial.print(country); + Serial.print(" \t| "); + + // print the RSSI + Serial.print(result.rssi); + Serial.print("\t| "); + + // print the network SSID + Serial.println((char*)result.ssid); + } + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + } + + // start scanning again + Serial.print(F("[LR1110] Starting passive WiFi scan ... ")); + state = radio.startWifiScan('*'); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino index 99c6522..637efa8 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/LoRaWAN_ABP.ino @@ -33,34 +33,34 @@ void setup() { Serial.begin(115200); - while (!Serial); + while(!Serial); delay(5000); // Give time to switch to the serial monitor Serial.println(F("\nSetup ... ")); - Serial.println(F("Initalise the radio")); + Serial.println(F("Initialise the radio")); int state = radio.begin(); - debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + debug(state != RADIOLIB_ERR_NONE, F("Initialise radio failed"), state, true); - Serial.println(F("Initalise LoRaWAN Network credentials")); - state = node.beginABP(devAddr, NwkSEncKey, AppSKey, NwkSKey, SNwkSIntKey, true); + Serial.println(F("Initialise LoRaWAN Network credentials")); + state = node.beginABP(devAddr, fNwkSIntKey, sNwkSIntKey, nwkSEncKey, appSKey, true); debug(state < RADIOLIB_ERR_NONE, F("Session setup failed"), state, true); Serial.println(F("Ready!\n")); } - void loop() { Serial.println(F("Sending uplink")); - // Read some inputs - uint8_t Digital1 = digitalRead(2); - uint16_t Analog1 = analogRead(3); + // This is the place to gather the sensor inputs + // Instead of reading any real sensor, we just generate some random numbers as example + uint8_t value1 = radio.random(100); + uint16_t value2 = radio.random(2000); // Build payload byte array uint8_t uplinkPayload[3]; - uplinkPayload[0] = Digital1; - uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions - uplinkPayload[2] = lowByte(Analog1); + uplinkPayload[0] = value1; + uplinkPayload[1] = highByte(value2); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(value2); // Perform an uplink int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/configABP.h b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/configABP.h index a536ff1..f505800 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/configABP.h +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_ABP/configABP.h @@ -12,8 +12,8 @@ const uint32_t uplinkIntervalSeconds = 5UL * 60UL; // minutes x seconds #define RADIOLIB_LORAWAN_DEV_ADDR 0x------ #endif -#ifndef RADIOLIB_LORAWAN_NWKS_KEY // Replace with your NwkS Key -#define RADIOLIB_LORAWAN_NWKS_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- +#ifndef RADIOLIB_LORAWAN_FNWKSINT_KEY // Replace with your FNwkSInt Key +#define RADIOLIB_LORAWAN_FNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- #endif #ifndef RADIOLIB_LORAWAN_SNWKSINT_KEY // Replace with your SNwkSInt Key #define RADIOLIB_LORAWAN_SNWKSINT_KEY 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x--, 0x-- @@ -49,11 +49,12 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 // LilyGo #elif defined(ARDUINO_TTGO_LORA32_V1) - #pragma message ("TTGO LoRa32 v1 - no Display") + #pragma message ("Using TTGO LoRa32 v1 - no Display") SX1276 radio = new Module(18, 26, 14, 33); #elif defined(ARDUINO_TTGO_LORA32_V2) - #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + #pragma message ("Using TTGO LoRa32 v2 + Display") + SX1276 radio = new Module(18, 26, 12, RADIOLIB_NC); #elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") @@ -63,24 +64,41 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using TTGO T-Beam") SX1276 radio = new Module(18, 26, 23, 33); -// Heltec +// HelTec: https://github.com/espressif/arduino-esp32/blob/master/variants/heltec_*/pins_arduino.h #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) - #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + #pragma message ("Using Heltec WiFi LoRa32") + SX1276 radio = new Module(18, 26, 14, 33); -#elif defined(ARDUINO_heltec_wifi_kit_32_V2) - #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) + #pragma message ("Using Heltec WiFi LoRa32 v2") SX1276 radio = new Module(18, 26, 14, 35); -#elif defined(ARDUINO_heltec_wifi_kit_32_V3) - #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK) + #pragma message ("Using Heltec Wireless Stick") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_V3) + #pragma message ("Using Heltec Wireless Stick v3") SX1262 radio = new Module(8, 14, 12, 13); +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE) + #pragma message ("Using Heltec Wireless Stick Lite") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE_V3) + #pragma message ("Using Heltec Wireless Stick Lite v3") + SX1262 radio = new Module(34, 14, 12, 13); + #elif defined(ARDUINO_CUBECELL_BOARD) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using CubeCell") SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); #elif defined(ARDUINO_CUBECELL_BOARD_V2) @@ -100,10 +118,10 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 // copy over the keys in to the something that will not compile if incorrectly formatted uint32_t devAddr = RADIOLIB_LORAWAN_DEV_ADDR; -uint8_t NwkSKey[] = { RADIOLIB_LORAWAN_NWKS_KEY }; -uint8_t SNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; // Previously sNwkSIntKey -uint8_t NwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; // Previously fNwkSIntKey -uint8_t AppSKey[] = { RADIOLIB_LORAWAN_APPS_KEY }; +uint8_t fNwkSIntKey[] = { RADIOLIB_LORAWAN_FNWKSINT_KEY }; +uint8_t sNwkSIntKey[] = { RADIOLIB_LORAWAN_SNWKSINT_KEY }; +uint8_t nwkSEncKey[] = { RADIOLIB_LORAWAN_NWKSENC_KEY }; +uint8_t appSKey[] = { RADIOLIB_LORAWAN_APPS_KEY }; // create the LoRaWAN node LoRaWANNode node(&radio, &Region, subBand); diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino index ad49a26..f45830a 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/LoRaWAN_Reference.ino @@ -33,18 +33,17 @@ // include the library #include - void setup() { Serial.begin(115200); - while (!Serial); // Wait for serial to be initalised + while(!Serial); // Wait for serial to be initialised delay(5000); // Give time to switch to the serial monitor Serial.println(F("\nSetup")); int16_t state = 0; // return value for calls to RadioLib - Serial.println(F("Initalise the radio")); + Serial.println(F("Initialise the radio")); state = radio.begin(); - debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + debug(state != RADIOLIB_ERR_NONE, F("Initialise radio failed"), state, true); // Override the default join rate // uint8_t joinDR = 3; @@ -60,13 +59,9 @@ void setup() { // Disable the ADR algorithm (on by default which is preferable) node.setADR(false); - // Set a fixed datarate & make it persistent (not normal) + // Set a fixed datarate node.setDatarate(4); - // Enable CSMA which tries to minimize packet loss by searching - // for a free channel before actually sending an uplink - node.setCSMA(6, 2, true); - // Manages uplink intervals to the TTN Fair Use Policy node.setDutyCycle(true, 1250); @@ -74,8 +69,7 @@ void setup() { node.setDwellTime(true, 400); Serial.println(F("Ready!\n")); -} // setup - +} void loop() { int state = RADIOLIB_ERR_NONE; @@ -89,19 +83,19 @@ void loop() { uint8_t battLevel = 146; node.setDeviceStatus(battLevel); - - // Read some inputs - uint8_t Digital1 = digitalRead(2); - uint16_t Analog1 = analogRead(3); + // This is the place to gather the sensor inputs + // Instead of reading any real sensor, we just generate some random numbers as example + uint8_t value1 = radio.random(100); + uint16_t value2 = radio.random(2000); // Build payload byte array uint8_t uplinkPayload[3]; - uplinkPayload[0] = Digital1; - uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions - uplinkPayload[2] = lowByte(Analog1); + uplinkPayload[0] = value1; + uplinkPayload[1] = highByte(value2); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(value2); uint8_t downlinkPayload[10]; // Make sure this fits your plans! - size_t downlinkSize; // To hold the actual payload size rec'd + size_t downlinkSize; // To hold the actual payload size received // you can also retrieve additional information about an uplink or // downlink by passing a reference to LoRaWANEvent_t structure @@ -111,13 +105,14 @@ void loop() { uint8_t Port = 10; // Retrieve the last uplink frame counter - uint32_t fcntUp = node.getFcntUp(); + uint32_t fcntUp = node.getFCntUp(); + // Send a confirmed uplink every 64th frame // and also request the LinkCheck and DeviceTime MAC commands if(fcntUp % 64 == 0) { Serial.println(F("[LoRaWAN] Requesting LinkCheck and DeviceTime")); - node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_LINK_CHECK); - node.sendMacCommandReq(RADIOLIB_LORAWAN_MAC_DEVICE_TIME); + node.sendMacCommandReq(RADIOLIB_LW_MAC_LINK_CHECK); + node.sendMacCommandReq(RADIOLIB_LW_MAC_DEVICE_TIME); state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), Port, downlinkPayload, &downlinkSize, true, &uplinkDetails, &downlinkDetails); } else { state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload), Port, downlinkPayload, &downlinkSize); @@ -127,7 +122,7 @@ void loop() { // Check if downlink was received if(state != RADIOLIB_LORAWAN_NO_DOWNLINK) { // Did we get a downlink with data for us - if (downlinkSize > 0) { + if(downlinkSize > 0) { Serial.println(F("Downlink data: ")); arrayDump(downlinkPayload, downlinkSize); } else { @@ -164,9 +159,9 @@ void loop() { Serial.print(downlinkDetails.power); Serial.println(F(" dBm")); Serial.print(F("[LoRaWAN] Frame count:\t")); - Serial.println(downlinkDetails.fcnt); + Serial.println(downlinkDetails.fCnt); Serial.print(F("[LoRaWAN] Port:\t\t")); - Serial.println(downlinkDetails.port); + Serial.println(downlinkDetails.fPort); uint8_t margin = 0; uint8_t gwCnt = 0; @@ -198,5 +193,4 @@ void loop() { Serial.println(F("s")); delay(delayMs); - -} // loop +} diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/config.h b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/config.h index cb681da..19eaf57 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/config.h +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Reference/config.h @@ -44,11 +44,12 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 // LilyGo #elif defined(ARDUINO_TTGO_LORA32_V1) - #pragma message ("TTGO LoRa32 v1 - no Display") + #pragma message ("Using TTGO LoRa32 v1 - no Display") SX1276 radio = new Module(18, 26, 14, 33); #elif defined(ARDUINO_TTGO_LORA32_V2) - #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + #pragma message ("Using TTGO LoRa32 v2 + Display") + SX1276 radio = new Module(18, 26, 12, RADIOLIB_NC); #elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") @@ -58,24 +59,41 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using TTGO T-Beam") SX1276 radio = new Module(18, 26, 23, 33); -// Heltec +// HelTec: https://github.com/espressif/arduino-esp32/blob/master/variants/heltec_*/pins_arduino.h #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) - #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + #pragma message ("Using Heltec WiFi LoRa32") + SX1276 radio = new Module(18, 26, 14, 33); -#elif defined(ARDUINO_heltec_wifi_kit_32_V2) - #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) + #pragma message ("Using Heltec WiFi LoRa32 v2") SX1276 radio = new Module(18, 26, 14, 35); -#elif defined(ARDUINO_heltec_wifi_kit_32_V3) - #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK) + #pragma message ("Using Heltec Wireless Stick") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_V3) + #pragma message ("Using Heltec Wireless Stick v3") SX1262 radio = new Module(8, 14, 12, 13); +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE) + #pragma message ("Using Heltec Wireless Stick Lite") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE_V3) + #pragma message ("Using Heltec Wireless Stick Lite v3") + SX1262 radio = new Module(34, 14, 12, 13); + #elif defined(ARDUINO_CUBECELL_BOARD) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using CubeCell") SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); #elif defined(ARDUINO_CUBECELL_BOARD_V2) diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino index 0914c1c..c9b32eb 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/LoRaWAN_Starter.ino @@ -26,13 +26,13 @@ void setup() { Serial.begin(115200); - while (!Serial); + while(!Serial); delay(5000); // Give time to switch to the serial monitor Serial.println(F("\nSetup ... ")); - Serial.println(F("Initalise the radio")); + Serial.println(F("Initialise the radio")); int state = radio.begin(); - debug(state != RADIOLIB_ERR_NONE, F("Initalise radio failed"), state, true); + debug(state != RADIOLIB_ERR_NONE, F("Initialise radio failed"), state, true); Serial.println(F("Join ('login') to the LoRaWAN Network")); state = node.beginOTAA(joinEUI, devEUI, nwkKey, appKey, true); @@ -41,19 +41,19 @@ void setup() { Serial.println(F("Ready!\n")); } - void loop() { Serial.println(F("Sending uplink")); - // Read some inputs - uint8_t Digital1 = digitalRead(2); - uint16_t Analog1 = analogRead(3); + // This is the place to gather the sensor inputs + // Instead of reading any real sensor, we just generate some random numbers as example + uint8_t value1 = radio.random(100); + uint16_t value2 = radio.random(2000); // Build payload byte array uint8_t uplinkPayload[3]; - uplinkPayload[0] = Digital1; - uplinkPayload[1] = highByte(Analog1); // See notes for high/lowByte functions - uplinkPayload[2] = lowByte(Analog1); + uplinkPayload[0] = value1; + uplinkPayload[1] = highByte(value2); // See notes for high/lowByte functions + uplinkPayload[2] = lowByte(value2); // Perform an uplink int state = node.sendReceive(uplinkPayload, sizeof(uplinkPayload)); diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/config.h b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/config.h index cb681da..19eaf57 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/config.h +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/config.h @@ -44,11 +44,12 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 // LilyGo #elif defined(ARDUINO_TTGO_LORA32_V1) - #pragma message ("TTGO LoRa32 v1 - no Display") + #pragma message ("Using TTGO LoRa32 v1 - no Display") SX1276 radio = new Module(18, 26, 14, 33); #elif defined(ARDUINO_TTGO_LORA32_V2) - #pragma error ("ARDUINO_TTGO_LORA32_V2 awaiting pin map") + #pragma message ("Using TTGO LoRa32 v2 + Display") + SX1276 radio = new Module(18, 26, 12, RADIOLIB_NC); #elif defined(ARDUINO_TTGO_LoRa32_v21new) // T3_V1.6.1 #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") @@ -58,24 +59,41 @@ const uint8_t subBand = 0; // For US915, change this to 2, otherwise leave on 0 #pragma error ("ARDUINO_TBEAM_USE_RADIO_SX1262 awaiting pin map") #elif defined(ARDUINO_TBEAM_USE_RADIO_SX1276) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using TTGO T-Beam") SX1276 radio = new Module(18, 26, 23, 33); -// Heltec +// HelTec: https://github.com/espressif/arduino-esp32/blob/master/variants/heltec_*/pins_arduino.h #elif defined(ARDUINO_HELTEC_WIFI_LORA_32) - #pragma error ("ARDUINO_HELTEC_WIFI_LORA_32 awaiting pin map") + #pragma message ("Using Heltec WiFi LoRa32") + SX1276 radio = new Module(18, 26, 14, 33); -#elif defined(ARDUINO_heltec_wifi_kit_32_V2) - #pragma message ("ARDUINO_heltec_wifi_kit_32_V2 awaiting pin map") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V2) + #pragma message ("Using Heltec WiFi LoRa32 v2") SX1276 radio = new Module(18, 26, 14, 35); -#elif defined(ARDUINO_heltec_wifi_kit_32_V3) - #pragma message ("Using Heltec WiFi LoRa32 v3 - Display + USB-C") +#elif defined(ARDUINO_HELTEC_WIFI_LORA_32_V3) + #pragma message ("Using Heltec WiFi LoRa32 v3") + SX1262 radio = new Module(8, 14, 12, 13); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK) + #pragma message ("Using Heltec Wireless Stick") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_V3) + #pragma message ("Using Heltec Wireless Stick v3") SX1262 radio = new Module(8, 14, 12, 13); +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE) + #pragma message ("Using Heltec Wireless Stick Lite") + SX1276 radio = new Module(18, 26, 14, 35); + +#elif defined (ARDUINO_HELTEC_WIRELESS_STICK_LITE_V3) + #pragma message ("Using Heltec Wireless Stick Lite v3") + SX1262 radio = new Module(34, 14, 12, 13); + #elif defined(ARDUINO_CUBECELL_BOARD) - #pragma message ("Using TTGO LoRa32 v2.1 marked T3_V1.6.1 + Display") + #pragma message ("Using CubeCell") SX1262 radio = new Module(RADIOLIB_BUILTIN_MODULE); #elif defined(ARDUINO_CUBECELL_BOARD_V2) diff --git a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/notes.md b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/notes.md index b566759..2821bef 100644 --- a/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/notes.md +++ b/lib/RadioLib/examples/LoRaWAN/LoRaWAN_Starter/notes.md @@ -152,6 +152,10 @@ Prebuilt modules are easy - we can detect the board and setup the pinmap for you * HELTEC_WIFI_LORA_32 * HELTEC_WIFI_LORA_32_V2 * HELTEC_WIFI_LORA_32_V3 +* HELTEC_WIRELESS_STICK +* HELTEC_WIRELESS_STICK_V3 +* HELTEC_WIRELESS_STICK_LITE +* HELTEC_WIRELESS_STICK_LITE_V3 If you have a TTGO T-Beam, you must choose the correct radio from the Board Revision sub-menu found under the main Tools menu. diff --git a/lib/RadioLib/examples/LoRaWAN/README.md b/lib/RadioLib/examples/LoRaWAN/README.md index a269810..8a44860 100644 --- a/lib/RadioLib/examples/LoRaWAN/README.md +++ b/lib/RadioLib/examples/LoRaWAN/README.md @@ -1,18 +1,20 @@ # LoRaWAN examples RadioLib LoRaWAN v1.1 examples. -* [LoRaWAN_Starter](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Starter): this is the recommended entry point for new users. Please read the `notes` that come with this example to learn more about LoRaWAN and how to use it in RadioLib! -* [LoRaWAN_Reference](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Reference): this sketch showcases most of the available API for LoRaWAN in RadioLib. Be frightened by the possibilities! It is recommended you have read all the `notes` for the Starter sketch first, as well as the [Learn section on The Things Network](https://www.thethingsnetwork.org/docs/lorawan/)! +* [LoRaWAN_Starter](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Starter): this is the recommended entry point for new users. Please read the [`notes`](https://github.com/jgromes/RadioLib/blob/master/examples/LoRaWAN/LoRaWAN_Starter/notes.md) that come with this example to learn more about LoRaWAN and how to use it in RadioLib! +* [LoRaWAN_Reference](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_Reference): this sketch showcases most of the available API for LoRaWAN in RadioLib. Be frightened by the possibilities! It is recommended you have read all the [`notes`](https://github.com/jgromes/RadioLib/blob/master/examples/LoRaWAN/LoRaWAN_Starter/notes.md) for the Starter sketch first, as well as the [Learn section on The Things Network](https://www.thethingsnetwork.org/docs/lorawan/)! * [LoRaWAN_ABP](https://github.com/jgromes/RadioLib/tree/master/examples/LoRaWAN/LoRaWAN_ABP): if you wish to use ABP instead of OTAA (but why?), this example shows how you can do this using RadioLib. --- -All three of these examples do not fully comply with LoRaWAN v1.1: for that, persistent storage is necessary. As the implementation of persistent storage differs between different platforms, these are not given here, but in a separate repository, see below: +> [!WARNING] +> These examples do not fully comply with LoRaWAN v1.1: for that, persistent storage is necessary. As the implementation of persistent storage differs between different platforms, these are not given here, but in a separate repository, see below: ## RadioLib persistence In [this repository](https://github.com/radiolib-org/radiolib-persistence), examples are provided that do comply with the required persistence of certain parameters for LoRaWAN v1.1. Examples are (or will become) available for some of the most popular platforms. **These examples assume you have successfully used the Starter sketch and understood (most of) the accompanying notes!** Currently, examples are available for the following platforms: * [LoRaWAN for ESP32](https://github.com/radiolib-org/radiolib-persistence/tree/main/examples/LoRaWAN_ESP32) +* [LoRaWAN for ESP8266](https://github.com/radiolib-org/radiolib-persistence/tree/main/examples/LoRaWAN_ESP8266) -_This list is last updated for RadioLib 6.5.0_ +_This list is last updated at 30/03/2024._ diff --git a/lib/RadioLib/examples/Morse/Morse_Transmit_SSB/Morse_Transmit_SSB.ino b/lib/RadioLib/examples/Morse/Morse_Transmit_SSB/Morse_Transmit_SSB.ino index 61abde3..4b35e77 100644 --- a/lib/RadioLib/examples/Morse/Morse_Transmit_SSB/Morse_Transmit_SSB.ino +++ b/lib/RadioLib/examples/Morse/Morse_Transmit_SSB/Morse_Transmit_SSB.ino @@ -14,6 +14,7 @@ - nRF24 - Si443x/RFM2x - SX128x + - LR11x0 For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration diff --git a/lib/RadioLib/examples/NonArduino/Raspberry/CMakeLists.txt b/lib/RadioLib/examples/NonArduino/Raspberry/CMakeLists.txt index 79feff1..c9d4787 100644 --- a/lib/RadioLib/examples/NonArduino/Raspberry/CMakeLists.txt +++ b/lib/RadioLib/examples/NonArduino/Raspberry/CMakeLists.txt @@ -18,4 +18,5 @@ add_executable(${PROJECT_NAME} main.cpp) target_link_libraries(${PROJECT_NAME} RadioLib pigpio) # you can also specify RadioLib compile-time flags here -#target_compile_definitions(${PROJECT_NAME} PUBLIC RADIOLIB_DEBUG RADIOLIB_VERBOSE) +#target_compile_definitions(RadioLib PUBLIC RADIOLIB_DEBUG_BASIC RADIOLIB_DEBUG_SPI) +#target_compile_definitions(RadioLib PUBLIC RADIOLIB_DEBUG_PORT=stdout) diff --git a/lib/RadioLib/examples/NonArduino/Raspberry/PiHal.h b/lib/RadioLib/examples/NonArduino/Raspberry/PiHal.h index 8394ad2..5a1b288 100644 --- a/lib/RadioLib/examples/NonArduino/Raspberry/PiHal.h +++ b/lib/RadioLib/examples/NonArduino/Raspberry/PiHal.h @@ -7,6 +7,14 @@ // include the library for Raspberry GPIO pins #include "pigpio.h" +// these should really be swapped, but for some reason, +// it seems like the change directions are inverted in gpioSetAlert functions +#define PI_RISING (FALLING_EDGE) +#define PI_FALLING (RISING_EDGE) + +// forward declaration of alert handler that will be used to emulate interrupts +static void pigpioAlertHandler(int event, int level, uint32_t tick, void *userdata); + // create a new Raspberry Pi hardware abstraction layer // using the pigpio library // the HAL must inherit from the base RadioLibHal class @@ -15,7 +23,7 @@ class PiHal : public RadioLibHal { public: // default constructor - initializes the base HAL and any needed private members PiHal(uint8_t spiChannel, uint32_t spiSpeed = 2000000) - : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, RISING_EDGE, FALLING_EDGE), + : RadioLibHal(PI_INPUT, PI_OUTPUT, PI_LOW, PI_HIGH, PI_RISING, PI_FALLING), _spiChannel(spiChannel), _spiSpeed(spiSpeed) { } @@ -71,38 +79,50 @@ class PiHal : public RadioLibHal { } void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override { - if(interruptNum == RADIOLIB_NC) { + if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) { return; } - gpioSetISRFunc(interruptNum, mode, 0, (gpioISRFunc_t)interruptCb); + // enable emulated interrupt + interruptEnabled[interruptNum] = true; + interruptModes[interruptNum] = mode; + interruptCallbacks[interruptNum] = interruptCb; + + // set pigpio alert callback + gpioSetAlertFuncEx(interruptNum, pigpioAlertHandler, (void*)this); } void detachInterrupt(uint32_t interruptNum) override { - if(interruptNum == RADIOLIB_NC) { + if((interruptNum == RADIOLIB_NC) || (interruptNum > PI_MAX_USER_GPIO)) { return; } - gpioSetISRFunc(interruptNum, 0, 0, NULL); + // clear emulated interrupt + interruptEnabled[interruptNum] = false; + interruptModes[interruptNum] = 0; + interruptCallbacks[interruptNum] = NULL; + + // disable pigpio alert callback + gpioSetAlertFuncEx(interruptNum, NULL, NULL); } - void delay(unsigned long ms) override { + void delay(RadioLibTime_t ms) override { gpioDelay(ms * 1000); } - void delayMicroseconds(unsigned long us) override { + void delayMicroseconds(RadioLibTime_t us) override { gpioDelay(us); } - unsigned long millis() override { + RadioLibTime_t millis() override { return(gpioTick() / 1000); } - unsigned long micros() override { + RadioLibTime_t micros() override { return(gpioTick()); } - long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override { + long pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout) override { if(pin == RADIOLIB_NC) { return(0); } @@ -120,7 +140,7 @@ class PiHal : public RadioLibHal { return(this->micros() - start); } - void spiBegin() { + void spiBegin() { if(_spiHandle < 0) { _spiHandle = spiOpen(_spiChannel, _spiSpeed, 0); } @@ -141,6 +161,12 @@ class PiHal : public RadioLibHal { } } + // interrupt emulation + bool interruptEnabled[PI_MAX_USER_GPIO + 1]; + uint32_t interruptModes[PI_MAX_USER_GPIO + 1]; + typedef void (*RadioLibISR)(void); + RadioLibISR interruptCallbacks[PI_MAX_USER_GPIO + 1]; + private: // the HAL can contain any additional private members const unsigned int _spiSpeed; @@ -148,4 +174,21 @@ class PiHal : public RadioLibHal { int _spiHandle = -1; }; +// this handler emulates interrupts +static void pigpioAlertHandler(int event, int level, uint32_t tick, void *userdata) { + if((event > PI_MAX_USER_GPIO) || (!userdata)) { + return; + } + + // PiHal isntance is passed via the user data + PiHal* hal = (PiHal*)userdata; + + // check the interrupt is enabled, the level matches and a callback exists + if((hal->interruptEnabled[event]) && + (hal->interruptModes[event] == level) && + (hal->interruptCallbacks[event])) { + hal->interruptCallbacks[event](); + } +} + #endif diff --git a/lib/RadioLib/examples/NonArduino/Raspberry/build.sh b/lib/RadioLib/examples/NonArduino/Raspberry/build.sh index 5d55b66..55caae9 100644 --- a/lib/RadioLib/examples/NonArduino/Raspberry/build.sh +++ b/lib/RadioLib/examples/NonArduino/Raspberry/build.sh @@ -6,3 +6,4 @@ cd build cmake -G "CodeBlocks - Unix Makefiles" .. make cd .. +size build/rpi-sx1261 diff --git a/lib/RadioLib/examples/NonArduino/Tock/CMakeLists.txt b/lib/RadioLib/examples/NonArduino/Tock/CMakeLists.txt index c7aaf86..d298f57 100644 --- a/lib/RadioLib/examples/NonArduino/Tock/CMakeLists.txt +++ b/lib/RadioLib/examples/NonArduino/Tock/CMakeLists.txt @@ -27,9 +27,13 @@ cmake_minimum_required(VERSION 3.18) # create the project project(tock-sx1261) -set(LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/userland_generic.ld) +set(LINKER_SCRIPT $ENV{LIBTOCK_C_DIRECTORY}/userland_generic.ld) -include("tock.cmake") +if (RISCV_BUILD) + include("tock-riscv.cmake") +else() + include("tock-arm.cmake") +endif() # when using debuggers such as gdb, the following line can be used #set(CMAKE_BUILD_TYPE Debug) @@ -43,18 +47,62 @@ add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/../../../../RadioLib" "${CMAKE_CUR add_executable(${PROJECT_NAME} main.cpp) # link with RadioLib and libtock-c -target_link_libraries(${PROJECT_NAME} PUBLIC - RadioLib - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libtock/build/cortex-m4/libtock.a - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libc++/cortex-m/libgcc.a - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/libc++/cortex-m/libstdc++.a - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/newlib/cortex-m/v7-m/libc.a - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c/newlib/cortex-m/v7-m/libm.a -) +# The build system for libtock-c is a bit odd and the version of libraries +# built changes based on compiler version. +if (RISCV_BUILD) + if(EXISTS "$ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0") + target_link_libraries(${PROJECT_NAME} PUBLIC + RadioLib + $ENV{LIBTOCK_C_DIRECTORY}/libtock/build/rv32imc/libtock.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0/riscv/lib/gcc/riscv64-unknown-elf/13.2.0/rv32i/ilp32/libgcc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libstdc++.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.3.0.20230120/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.3.0.20230120/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libm.a + ) + + target_include_directories(RadioLib AFTER PUBLIC + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.3.0.20230120/riscv/riscv64-unknown-elf/include/ + ) + else() + target_link_libraries(${PROJECT_NAME} PUBLIC + RadioLib + $ENV{LIBTOCK_C_DIRECTORY}/libtock/build/rv32imc/libtock.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-10.5.0/riscv/lib/gcc/riscv64-unknown-elf/10.5.0/rv32i/ilp32/libgcc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-10.5.0/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libstdc++.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.2.0.20211231/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.2.0.20211231/riscv/riscv64-unknown-elf/lib/rv32i/ilp32/libm.a + ) + + target_include_directories(RadioLib AFTER PUBLIC + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.2.0.20211231/riscv/riscv64-unknown-elf/include/ + ) + endif() +else() + if(EXISTS "$ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0") + target_link_libraries(${PROJECT_NAME} PUBLIC + RadioLib + $ENV{LIBTOCK_C_DIRECTORY}/libtock/build/cortex-m4/libtock.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0/arm/lib/gcc/arm-none-eabi/13.2.0/libgcc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-13.2.0/arm/arm-none-eabi/lib/libstdc++.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.3.0.20230120/arm/arm-none-eabi/lib/libc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.3.0.20230120/arm/arm-none-eabi/lib/libm.a + ) + else() + target_link_libraries(${PROJECT_NAME} PUBLIC + RadioLib + $ENV{LIBTOCK_C_DIRECTORY}/libtock/build/cortex-m4/libtock.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-10.5.0/arm/lib/gcc/arm-none-eabi/10.5.0/libgcc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-libc++-10.5.0/arm/arm-none-eabi/lib/libstdc++.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.2.0.20211231/arm/arm-none-eabi/lib/libc.a + $ENV{LIBTOCK_C_DIRECTORY}/lib/libtock-newlib-4.2.0.20211231/arm/arm-none-eabi/lib/libm.a + ) + endif() +endif() target_include_directories(${PROJECT_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/libtock-c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/ + $ENV{LIBTOCK_C_DIRECTORY} ) # you can also specify RadioLib compile-time flags here diff --git a/lib/RadioLib/examples/NonArduino/Tock/README.md b/lib/RadioLib/examples/NonArduino/Tock/README.md index cd13b6d..05b2e7c 100644 --- a/lib/RadioLib/examples/NonArduino/Tock/README.md +++ b/lib/RadioLib/examples/NonArduino/Tock/README.md @@ -13,16 +13,22 @@ This has been tested on the but will work on any LoRa compatible Tock board (currently only the expLoRaBLE board). +libtock-c by default is bulit for RISC-V and ARM. RadioLib is also built +for both architectures by default. You can skip the RISC-V RadioLib build +by setting the `SKIP_RISCV` varaible. + The RadioLib example can be built with: ```shell $ git clone https://github.com/jgromes/RadioLib.git $ cd RadioLib/examples/NonArduino/Tock/ -$ ./build.sh +$ git clone https://github.com/tock/libtock-c.git +$ cd libtock-c; git checkout 44bf89c545953d8859faf101d4b4a4b6a151fe6c; cd ../ +$ LIBTOCK_C_DIRECTORY="$(pwd)/libtock-c" ./build.sh ``` Then in the Tock repo you can flash the kernel and app with: ```shell -$ make flash; APP=RadioLib/examples/NonArduino/Tock/build/tock-sx1261.tbf make flash-app +$ make flash; APP=RadioLib/examples/NonArduino/Tock/build-arm/tock-sx1261.tbf make flash-app ``` diff --git a/lib/RadioLib/examples/NonArduino/Tock/build.sh b/lib/RadioLib/examples/NonArduino/Tock/build.sh index a527bc9..cb91a4a 100644 --- a/lib/RadioLib/examples/NonArduino/Tock/build.sh +++ b/lib/RadioLib/examples/NonArduino/Tock/build.sh @@ -1,20 +1,30 @@ #!/bin/bash set -e -rm -rf ./build +rm -rf ./build-* -cd libtock-c/libtock +cd libtock-c/examples/cxx_hello make -j4 -cd ../../ +cd ../../../ -mkdir -p build -cd build +mkdir -p build-arm +cd build-arm cmake -G "CodeBlocks - Unix Makefiles" .. make -j4 cd .. +if ! env | grep SKIP_RISCV; then + mkdir -p build-riscv + cd build-riscv + + cmake -G "CodeBlocks - Unix Makefiles" -DRISCV_BUILD=1 .. + make -j4 + + cd .. +fi + elf2tab -n radio-lib --stack 4096 --app-heap 2048 --kernel-heap 2048 \ --kernel-major 2 --kernel-minor 1 \ - -v ./build/tock-sx1261 + -v ./build-arm/tock-sx1261 diff --git a/lib/RadioLib/examples/NonArduino/Tock/tock.cmake b/lib/RadioLib/examples/NonArduino/Tock/tock-arm.cmake similarity index 78% rename from lib/RadioLib/examples/NonArduino/Tock/tock.cmake rename to lib/RadioLib/examples/NonArduino/Tock/tock-arm.cmake index fb3682f..f557e2f 100644 --- a/lib/RadioLib/examples/NonArduino/Tock/tock.cmake +++ b/lib/RadioLib/examples/NonArduino/Tock/tock-arm.cmake @@ -25,8 +25,6 @@ # This is copied from https://github.com/Lora-net/LoRaMac-node/pull/1390 # and has been relicensed by the original author -include("toolchain-arm-none-eabi.cmake") - if(NOT DEFINED LINKER_SCRIPT) message(FATAL_ERROR "No linker script defined") endif(NOT DEFINED LINKER_SCRIPT) @@ -40,6 +38,22 @@ set(STACK_SIZE 4096) set(APP_HEAP_SIZE 2048) set(KERNEL_HEAP_SIZE 2048) +set(TOOLCHAIN arm-none-eabi) + +find_program(TOOLCHAIN_PREFIX ${TOOLCHAIN}-gcc NO_CACHE) +get_filename_component(TOOLCHAIN_PREFIX ${TOOLCHAIN_PREFIX} DIRECTORY) + +set(TOOLCHAIN_BIN_DIR ${TOOLCHAIN_PREFIX}/../bin) +set(TOOLCHAIN_INC_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/include) +set(TOOLCHAIN_LIB_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/lib) + +#--------------------------------------------------------------------------------------- +# Set compilers +#--------------------------------------------------------------------------------------- +set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc CACHE INTERNAL "C Compiler") +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-g++ CACHE INTERNAL "C++ Compiler") +set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc CACHE INTERNAL "ASM Compiler") + # Object build options set(OBJECT_GEN_FLAGS "-mthumb -g2 -fno-builtin -mcpu=cortex-m4 -Wall -Wextra -pedantic -Wno-unused-parameter -ffunction-sections -fdata-sections -fomit-frame-pointer -mabi=aapcs -fno-unroll-loops -ffast-math -ftree-vectorize -frecord-gcc-switches -gdwarf-2 -Os -fdata-sections -ffunction-sections -fstack-usage -Wl,--emit-relocs -fPIC -mthumb -mfloat-abi=soft -msingle-pic-base -mpic-register=r9 -mno-pic-data-is-text-relative -D__TOCK__ -DSVCALL_AS_NORMAL_FUNCTION -DSOFTDEVICE_s130") diff --git a/lib/RadioLib/examples/NonArduino/Tock/tock-riscv.cmake b/lib/RadioLib/examples/NonArduino/Tock/tock-riscv.cmake new file mode 100644 index 0000000..3d84294 --- /dev/null +++ b/lib/RadioLib/examples/NonArduino/Tock/tock-riscv.cmake @@ -0,0 +1,76 @@ +# Tock target specific CMake file +# +# Licensed under the MIT License +# +# Copyright (c) 2023 Alistair Francis +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all +# copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +# SOFTWARE. +# +# This is copied from https://github.com/Lora-net/LoRaMac-node/pull/1390 +# and has been relicensed by the original author + +if(NOT DEFINED LINKER_SCRIPT) +message(FATAL_ERROR "No linker script defined") +endif(NOT DEFINED LINKER_SCRIPT) +message("Linker script: ${LINKER_SCRIPT}") + +#--------------------------------------------------------------------------------------- +# Set compiler/linker flags +#--------------------------------------------------------------------------------------- + +set(STACK_SIZE 4096) +set(APP_HEAP_SIZE 2048) +set(KERNEL_HEAP_SIZE 2048) + +find_program(TOOLCHAIN + NAMES + riscv64-none-elf-gcc + riscv32-none-elf-gcc + riscv64-elf-gcc + riscv32-unknown-elf-gcc + riscv64-unknown-elf-gcc + riscv64-unknown-elf-clang + riscv32-unknown-elf-clang + NO_CACHE) + +get_filename_component(TOOLCHAIN_PREFIX ${TOOLCHAIN} DIRECTORY) + +get_filename_component(TOOLCHAIN ${TOOLCHAIN} NAME) +string(REPLACE "-gcc" "" TOOLCHAIN ${TOOLCHAIN}) + +set(TOOLCHAIN_BIN_DIR ${TOOLCHAIN_PREFIX}/../bin) +set(TOOLCHAIN_INC_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/include) +set(TOOLCHAIN_LIB_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/lib) + +#--------------------------------------------------------------------------------------- +# Set compilers +#--------------------------------------------------------------------------------------- +set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc CACHE INTERNAL "C Compiler") +set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-g++ CACHE INTERNAL "C++ Compiler") +set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc CACHE INTERNAL "ASM Compiler") + +# Object build options +set(OBJECT_GEN_FLAGS "-march=rv32i -mabi=ilp32 -mcmodel=medlow -g2 -fno-builtin -Wall -Wextra -pedantic -Wno-unused-parameter -ffunction-sections -fdata-sections -fomit-frame-pointer -fno-unroll-loops -ffast-math -ftree-vectorize -frecord-gcc-switches -gdwarf-2 -Os -fdata-sections -ffunction-sections -fstack-usage -Wl,--emit-relocs -D__TOCK__ -DSVCALL_AS_NORMAL_FUNCTION -DSOFTDEVICE_s130") + +set(CMAKE_C_FLAGS "${OBJECT_GEN_FLAGS} -std=gnu99 " CACHE INTERNAL "C Compiler options") +set(CMAKE_CXX_FLAGS "${OBJECT_GEN_FLAGS} -std=c++20 " CACHE INTERNAL "C++ Compiler options") +set(CMAKE_ASM_FLAGS "${OBJECT_GEN_FLAGS} -x assembler-with-cpp " CACHE INTERNAL "ASM Compiler options") + +# Linker flags +set(CMAKE_EXE_LINKER_FLAGS "-Wl,--gc-sections -march=rv32i -mabi=ilp32 -mcmodel=medlow -T${LINKER_SCRIPT} -Wl,-Map=${CMAKE_PROJECT_NAME}.map -Xlinker --defsym=STACK_SIZE=${STACK_SIZE} -Xlinker --defsym=APP_HEAP_SIZE=${APP_HEAP_SIZE} -Xlinker --defsym=KERNEL_HEAP_SIZE=${KERNEL_HEAP_SIZE} -nostdlib -Wl,--start-group" CACHE INTERNAL "Linker options") diff --git a/lib/RadioLib/examples/NonArduino/Tock/toolchain-arm-none-eabi.cmake b/lib/RadioLib/examples/NonArduino/Tock/toolchain-arm-none-eabi.cmake deleted file mode 100644 index 87a23f6..0000000 --- a/lib/RadioLib/examples/NonArduino/Tock/toolchain-arm-none-eabi.cmake +++ /dev/null @@ -1,90 +0,0 @@ -# Arm specific CMake file -# -# This is copied from: -# https://github.com/Lora-net/LoRaMac-node/blob/2bf36bde72f68257eb96b5c00900619546bedca8/cmake/toolchain-arm-none-eabi.cmake -# -# The below file is licensed as Revised BSD License -# See https://github.com/Lora-net/LoRaMac-node/blob/master/LICENSE for details - -## -## ______ _ -## / _____) _ | | -## ( (____ _____ ____ _| |_ _____ ____| |__ -## \____ \| ___ | (_ _) ___ |/ ___) _ \ -## _____) ) ____| | | || |_| ____( (___| | | | -## (______/|_____)_|_|_| \__)_____)\____)_| |_| -## (C)2013-2017 Semtech -## ___ _____ _ ___ _ _____ ___ ___ ___ ___ -## / __|_ _/_\ / __| |/ / __/ _ \| _ \/ __| __| -## \__ \ | |/ _ \ (__| ' <| _| (_) | / (__| _| -## |___/ |_/_/ \_\___|_|\_\_| \___/|_|_\\___|___| -## embedded.connectivity.solutions.============== -## -## License: Revised BSD License, see LICENSE.TXT file included in the project -## Authors: Johannes Bruder ( STACKFORCE ), Miguel Luis ( Semtech ) -## -## -## CMake arm-none-eabi toolchain file -## - -# Append current directory to CMAKE_MODULE_PATH for making device specific cmake modules visible -list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_LIST_DIR}) - -# Target definition -set(CMAKE_SYSTEM_NAME Generic) -set(CMAKE_SYSTEM_PROCESSOR ARM) - -#--------------------------------------------------------------------------------------- -# Set toolchain paths -#--------------------------------------------------------------------------------------- -set(TOOLCHAIN arm-none-eabi) - -find_program(TOOLCHAIN_PREFIX ${TOOLCHAIN}-gcc NO_CACHE) -get_filename_component(TOOLCHAIN_PREFIX ${TOOLCHAIN_PREFIX} DIRECTORY) - -set(TOOLCHAIN_BIN_DIR ${TOOLCHAIN_PREFIX}/../bin) -set(TOOLCHAIN_INC_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/include) -set(TOOLCHAIN_LIB_DIR ${TOOLCHAIN_PREFIX}/../${TOOLCHAIN}/lib) - -# Set system depended extensions -if(WIN32) - set(TOOLCHAIN_EXT ".exe" ) -else() - set(TOOLCHAIN_EXT "" ) -endif() - -# Perform compiler test with static library -set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) - -#--------------------------------------------------------------------------------------- -# Preset some general GCC Options -#--------------------------------------------------------------------------------------- - -# Options for DEBUG build -# -Og enables optimizations that do not interfere with debugging -# -g produce debugging information in the operating system's native format -set(CMAKE_C_FLAGS_DEBUG "-Og -g -DDEBUG" CACHE INTERNAL "C Compiler options for debug build type") -set(CMAKE_CXX_FLAGS_DEBUG "-Og -g -DDEBUG" CACHE INTERNAL "C++ Compiler options for debug build type") -set(CMAKE_ASM_FLAGS_DEBUG "-g" CACHE INTERNAL "ASM Compiler options for debug build type") -set(CMAKE_EXE_LINKER_FLAGS_DEBUG "" CACHE INTERNAL "Linker options for debug build type") - -# Options for RELEASE build -# -Os Optimize for size. -Os enables all -O2 optimizations -set(CMAKE_C_FLAGS_RELEASE "-Os" CACHE INTERNAL "C Compiler options for release build type") -set(CMAKE_CXX_FLAGS_RELEASE "-Os" CACHE INTERNAL "C++ Compiler options for release build type") -set(CMAKE_ASM_FLAGS_RELEASE "" CACHE INTERNAL "ASM Compiler options for release build type") -set(CMAKE_EXE_LINKER_FLAGS_RELEASE "" CACHE INTERNAL "Linker options for release build type") - -#--------------------------------------------------------------------------------------- -# Set compilers -#--------------------------------------------------------------------------------------- -set(CMAKE_C_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc${TOOLCHAIN_EXT} CACHE INTERNAL "C Compiler") -set(CMAKE_CXX_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-g++${TOOLCHAIN_EXT} CACHE INTERNAL "C++ Compiler") -set(CMAKE_ASM_COMPILER ${TOOLCHAIN_BIN_DIR}/${TOOLCHAIN}-gcc${TOOLCHAIN_EXT} CACHE INTERNAL "ASM Compiler") - -set(CMAKE_FIND_ROOT_PATH ${TOOLCHAIN_PREFIX}/${${TOOLCHAIN}} ${CMAKE_PREFIX_PATH}) -set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) -set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) -set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) - diff --git a/lib/RadioLib/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino b/lib/RadioLib/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino index e02ad3c..5d3b138 100644 --- a/lib/RadioLib/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino +++ b/lib/RadioLib/examples/RTTY/RTTY_Transmit/RTTY_Transmit.ino @@ -13,6 +13,7 @@ - nRF24 - Si443x/RFM2x - SX128x + - LR11x0 For default module settings, see the wiki page https://github.com/jgromes/RadioLib/wiki/Default-configuration diff --git a/lib/RadioLib/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino b/lib/RadioLib/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino new file mode 100644 index 0000000..8daf635 --- /dev/null +++ b/lib/RadioLib/examples/SX128x/SX128x_Channel_Activity_Detection_Interrupt/SX128x_Channel_Activity_Detection_Interrupt.ino @@ -0,0 +1,108 @@ +/* + RadioLib SX128x Channel Activity Detection Example + + This example uses SX1280 to scan the current LoRa + channel and detect ongoing LoRa transmissions. + + Other modules from SX128x family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx128x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 radio = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 radio = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + int state = radio.begin(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // set the function that will be called + // when LoRa packet or timeout is detected + radio.setDio1Action(setFlag); + + // start scanning the channel + Serial.print(F("[SX1280] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a packet was detected or CAD timed out +volatile bool scanFlag = false; + +// this function is called when a complete packet +// is 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) { + // something happened, set the flag + scanFlag = true; +} + +void loop() { + // check if the flag is set + if(scanFlag) { + // reset flag + scanFlag = false; + + // check CAD result + int state = radio.getChannelScanResult(); + + if (state == RADIOLIB_LORA_DETECTED) { + // LoRa packet was detected + Serial.println(F("[SX1280] Packet detected!")); + + } else if (state == RADIOLIB_CHANNEL_FREE) { + // channel is free + Serial.println(F("[SX1280] Channel is free!")); + + } else { + // some other error occurred + Serial.print(F("[SX1280] Failed, code ")); + Serial.println(state); + + } + + // start scanning the channel again + Serial.print(F("[SX1280] Starting scan for LoRa preamble ... ")); + state = radio.startChannelScan(); + if (state == RADIOLIB_ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } +} diff --git a/lib/RadioLib/extras/SSTV_Image_Converter/ImageConverter.py b/lib/RadioLib/extras/SSTV_Image_Converter/ImageConverter.py new file mode 100644 index 0000000..8444465 --- /dev/null +++ b/lib/RadioLib/extras/SSTV_Image_Converter/ImageConverter.py @@ -0,0 +1,50 @@ +#!/usr/bin/python3 +# -*- encoding: utf-8 -*- + +import argparse +import numpy as np +from PIL import Image +from argparse import RawTextHelpFormatter + +def main(): + parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' + RadioLib image to array conversion tool. + + Input is a PNG image to be transmitted via RadioLib SSTV. + The image must have correct size for the chose SSTV mode! + + Output is a file (by default named "img.h") which can be included and transmitted. + The resulting array will be very large (typically 320 kB), + make sure your platform has sufficient Flash/RAM space. + ''') + parser.add_argument('input', + type=str, + help='Input PNG file') + parser.add_argument('output', + type=str, + nargs='?', + default='img', + help='Output header file') + args = parser.parse_args() + outfile = f'{args.output}.h' + print(f'Converting "{args.input}" to "{outfile}"') + + # open the image as numpy array + img = Image.open(args.input) + arr = np.array(img) + + # open the output file + with open(outfile, 'w') as f: + print(f'const uint32_t img[{arr.shape[0]}][{arr.shape[1]}] = {{', file=f) + for row in arr: + print(' { ', end='', file=f) + for pix in row: + rgb = pix[0] << 16 | pix[1] << 8 | pix[2] + print(hex(rgb), end=', ', file=f) + print(' },', file=f) + print('};', file=f) + + print('Done!') + +if __name__ == "__main__": + main() diff --git a/lib/RadioLib/extras/SSTV_Image_Converter/radiolib.png b/lib/RadioLib/extras/SSTV_Image_Converter/radiolib.png new file mode 100644 index 0000000..6a20aa8 Binary files /dev/null and b/lib/RadioLib/extras/SSTV_Image_Converter/radiolib.png differ diff --git a/lib/RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py b/lib/RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py new file mode 100644 index 0000000..435a27a --- /dev/null +++ b/lib/RadioLib/extras/SX126x_Spectrum_Scan/SpectrumScan.py @@ -0,0 +1,176 @@ +#!/usr/bin/python3 +# -*- encoding: utf-8 -*- + +import argparse +import serial +import sys +import numpy as np +import matplotlib as mpl +import matplotlib.pyplot as plt + +from datetime import datetime +from argparse import RawTextHelpFormatter + +# number of samples in each scanline +SCAN_WIDTH = 33 + +# scanline Serial start/end markers +SCAN_MARK_START = 'SCAN ' +SCAN_MARK_FREQ = 'FREQ ' +SCAN_MARK_END = ' END' + +# output path +OUT_PATH = 'out' + +# default settings +DEFAULT_BAUDRATE = 115200 +DEFAULT_COLOR_MAP = 'viridis' +DEFAULT_SCAN_LEN = 200 +DEFAULT_RSSI_OFFSET = -11 + +# Print iterations progress +# from https://stackoverflow.com/questions/3173320/text-progress-bar-in-terminal-with-block-characters +def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 50, fill = 'â–ˆ', printEnd = "\r"): + """ + Call in a loop to create terminal progress bar + @params: + iteration - Required : current iteration (Int) + total - Required : total iterations (Int) + prefix - Optional : prefix string (Str) + suffix - Optional : suffix string (Str) + decimals - Optional : positive number of decimals in percent complete (Int) + length - Optional : character length of bar (Int) + fill - Optional : bar fill character (Str) + printEnd - Optional : end character (e.g. "\r", "\r\n") (Str) + """ + percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) + filledLength = int(length * iteration // total) + bar = fill * filledLength + '-' * (length - filledLength) + print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd) + if iteration == total: + print() + + +def main(): + parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' + RadioLib SX126x_Spectrum_Scan plotter script. Displays output from SX126x_Spectrum_Scan example + as grayscale and + + Depends on pyserial and matplotlib, install by: + 'python3 -m pip install pyserial matplotlib' + + Step-by-step guide on how to use the script: + 1. Upload the SX126x_Spectrum_Scan example to your Arduino board with SX1262 connected. + 2. Run the script with appropriate arguments. + 3. Once the scan is complete, output files will be saved to out/ + ''') + parser.add_argument('port', + type=str, + help='COM port to connect to the device') + parser.add_argument('--speed', + default=DEFAULT_BAUDRATE, + type=int, + help=f'COM port baudrate (defaults to {DEFAULT_BAUDRATE})') + parser.add_argument('--map', + default=DEFAULT_COLOR_MAP, + type=str, + help=f'Matplotlib color map to use for the output (defaults to "{DEFAULT_COLOR_MAP}")') + parser.add_argument('--len', + default=DEFAULT_SCAN_LEN, + type=int, + help=f'Number of scanlines to record (defaults to {DEFAULT_SCAN_LEN})') + parser.add_argument('--offset', + default=DEFAULT_RSSI_OFFSET, + type=int, + help=f'Default RSSI offset in dBm (defaults to {DEFAULT_RSSI_OFFSET})') + parser.add_argument('--freq', + default=-1, + type=float, + help=f'Default starting frequency in MHz') + args = parser.parse_args() + + freq_mode = False + scan_len = args.len + if (args.freq != -1): + freq_mode = True + scan_len = 1000 + + # create the color map and the result array + arr = np.zeros((SCAN_WIDTH, scan_len)) + + # scanline counter + row = 0 + + # list of frequencies in frequency mode + freq_list = [] + + # open the COM port + with serial.Serial(args.port, args.speed, timeout=None) as com: + while(True): + # update the progress bar + if not freq_mode: + printProgressBar(row, scan_len) + + # read a single line + try: + line = com.readline().decode('utf-8') + except: + continue + + if SCAN_MARK_FREQ in line: + new_freq = float(line.split(' ')[1]) + if (len(freq_list) > 1) and (new_freq < freq_list[-1]): + break + + freq_list.append(new_freq) + print('{:.3f}'.format(new_freq), end = '\r') + continue + + # check the markers + if (SCAN_MARK_START in line) and (SCAN_MARK_END in line): + # get the values + scanline = line[len(SCAN_MARK_START):-len(SCAN_MARK_END)].split(',') + for col in range(SCAN_WIDTH): + arr[col][row] = int(scanline[col]) + + # increment the row counter + row = row + 1 + + # check if we're done + if (not freq_mode) and (row >= scan_len): + break + + # scale to the number of scans (sum of any given scanline) + num_samples = arr.sum(axis=0)[0] + arr *= (num_samples/arr.max()) + + if freq_mode: + scan_len = len(freq_list) + + # create the figure + fig, ax = plt.subplots() + + # display the result as heatmap + extent = [0, scan_len, -4*(SCAN_WIDTH + 1), args.offset] + if freq_mode: + extent[0] = freq_list[0] + extent[1] = freq_list[-1] + im = ax.imshow(arr[:,:scan_len], cmap=args.map, extent=extent) + fig.colorbar(im) + + # set some properites and show + timestamp = datetime.now().strftime('%y-%m-%d %H-%M-%S') + title = f'RadioLib SX126x Spectral Scan {timestamp}' + if freq_mode: + plt.xlabel("Frequency [Hz]") + else: + plt.xlabel("Time [sample]") + plt.ylabel("RSSI [dBm]") + ax.set_aspect('auto') + fig.suptitle(title) + fig.canvas.manager.set_window_title(title) + plt.savefig(f'{OUT_PATH}/{title.replace(" ", "_")}.png', dpi=300) + plt.show() + +if __name__ == "__main__": + main() diff --git a/lib/RadioLib/extras/decoder/DebugDecoder.py b/lib/RadioLib/extras/decoder/DebugDecoder.py new file mode 100644 index 0000000..c5c9b37 --- /dev/null +++ b/lib/RadioLib/extras/decoder/DebugDecoder.py @@ -0,0 +1,111 @@ +import re, sys, argparse +from pathlib import Path +from argparse import RawTextHelpFormatter + + +''' +TODO list: + 1. Parse macro values (the names of bits in all registers in header file) + 2. Failed SPI write handling + 3. SX126x/SX128x handling +''' + + +def get_macro_name(value, macros): + for macro in macros: + if macro[1] == value: + return macro[0] + return 'UNKNOWN_VALUE' + + +def get_macro_value(value): + return ' 0x{0:02X}\n'.format(int(value, 16)) + + +parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter, description=''' + RadioLib debug output decoder script. Turns RadioLib Serial dumps into readable text. + + Step-by-step guid on how to use the decoder: + 1. Uncomment lines 312 (#define RADIOLIB_DEBUG) and 313 (#define RADIOLIB_VERBOSE) in RadioLib/src/BuildOpt.h + 2. Recompile and upload the failing Arduino sketch + 3. Open Arduino IDE Serial Monitor and enable timestamps + 4. Copy the Serial output and save it into a .txt file + 5. Run this script + + Output will be saved in the file specified by --out and printed to the terminal +''') +parser.add_argument('file', metavar='file', type=str, help='Text file of the debug output') +parser.add_argument('--out', metavar='out', default='./out.txt', type=str, help='Where to save the decoded file (defaults to ./out.txt)') +args = parser.parse_args() + +# open the log file +log = open(args.file, 'r').readlines() + +# find modules that are in use +used_modules = [] +pattern_module = re.compile('(([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?.[0-9]{3} -> )?M\t') +for entry in log: + m = pattern_module.search(entry) + if m != None: + used_modules.append(entry[m.end():].rstrip()) + +# get paths to all relevant header files +header_files = [] +for path in Path('../../src').rglob('*.h'): + for module in used_modules: + if module in path.name: + header_files.append(path) + +# extract names of address macros from the header files +macro_addresses = [] +pattern_define = re.compile('#define \w* +\w*(\n| +\/\/){1}') +for path in header_files: + file = open(path, 'r').readlines() + for line in file: + m = pattern_define.search(line) + if m != None: + s = re.split(' +', m.group().rstrip()) + if (s.__len__() > 1) and ('_REG' in s[1]): + macro_addresses.append([s[1], int(s[2], 0)]) + +''' +# extract names of value macros for each adddress macro +macro_values = [] +for path in header_files: + file = open(path, 'r').readlines() + for line in file: + for module in used_modules: + pattern_addr_macro = re.compile('\/\/ SI443X_REG_\w+'.format(module.capitalize())) +''' + +# parse every line in the log file +out = [] +pattern_debug = re.compile('(([01]?[0-9]|2[0-3]):[0-5][0-9](:[0-5][0-9])?.[0-9]{3} -> )?[RWM]\t.+') +for entry in log: + m = pattern_debug.search(entry) + if m != None: + s = re.split('( |\t)+', entry.rstrip()) + cmd_len = int((s.__len__() - 7)/2) + new_entry = s[0] + s[1] + s[2] + s[3] + if s[4] == 'W': + macro_address = int(s[6], 16) + new_entry += 'write {0:>2} 0x{1:02X} {2}\n'.format(cmd_len, macro_address, get_macro_name(macro_address, macro_addresses)) + for i in range(cmd_len): + new_entry += get_macro_value(s[8 + 2*i]); + elif s[4] == 'R': + macro_address = int(s[6], 16) + new_entry += 'read {0:>2} 0x{1:02X} {2}\n'.format(cmd_len, macro_address, get_macro_name(macro_address, macro_addresses)) + for i in range(cmd_len): + new_entry += get_macro_value(s[8 + 2*i]); + elif s[4] == 'M': + new_entry += 'module {}\n'.format(s[6]) + out.append(new_entry) + else: + out.append(entry) + +# write the output file +out_file = open(args.out, 'w') +for line in out: + print(line, end='') + out_file.write(line) +out_file.close() diff --git a/lib/RadioLib/extras/template/ModuleTemplate.cpp b/lib/RadioLib/extras/template/ModuleTemplate.cpp new file mode 100644 index 0000000..1efb1fc --- /dev/null +++ b/lib/RadioLib/extras/template/ModuleTemplate.cpp @@ -0,0 +1,22 @@ +#include ".h" +#if !defined(RADIOLIB_EXCLUDE_) + +::(Module* mod) { + /* + Constructor implementation MUST assign the provided "mod" pointer to the private "_mod" pointer. + */ + _mod = mod; +} + +int16_t ::begin() { + /* + "begin" method implementation MUST call the "init" method with appropriate settings. + */ + _mod->init(); + + /* + "begin" method SHOULD implement some sort of mechanism to verify the connection between Arduino and the module. + + For example, reading a version register + */ +} diff --git a/lib/RadioLib/extras/template/ModuleTemplate.h b/lib/RadioLib/extras/template/ModuleTemplate.h new file mode 100644 index 0000000..ee12194 --- /dev/null +++ b/lib/RadioLib/extras/template/ModuleTemplate.h @@ -0,0 +1,99 @@ +/* + RadioLib Module Template header file + + Before opening pull request, please make sure that: + 1. All files MUST be compiled without errors using default Arduino IDE settings. + 2. All files SHOULD be compiled without warnings with compiler warnings set to "All". + 3. Example sketches MUST be working correctly and MUST be stable enough to run for prolonged periods of time. + 4. Writing style SHOULD be consistent. + 5. Comments SHOULD be in place for the most important chunks of code and SHOULD be free of typos. + 6. To indent, 2 spaces MUST be used. + + If at any point you are unsure about the required style, please refer to the rest of the modules. +*/ + +#if !defined(_RADIOLIB__H) && !defined(RADIOLIB_EXCLUDE_) +#if !defined(_RADIOLIB__H) +#define _RADIOLIB__H + +/* + Header file for each module MUST include Module.h and TypeDef.h in the src folder. + The header file MAY include additional header files. +*/ +#include "../../Module.h" +#include "../../TypeDef.h" + +/* + Only use the following include if the module implements methods for OSI physical layer control. + This concerns only modules similar to SX127x/RF69/CC1101 etc. + + In this case, your class MUST implement all virtual methods of PhysicalLayer class. +*/ +//#include "../../protocols/PhysicalLayer/PhysicalLayer.h" + +/* + Register map + Definition of SPI register map SHOULD be placed here. The register map SHOULD have two parts: + + 1 - Address map: only defines register names and addresses. Register names MUST match names in + official documentation (datasheets etc.). + 2 - Variable map: defines variables inside register. This functions as a bit range map for a specific register. + Bit range (MSB and LSB) as well as short description for each variable MUST be provided in a comment. + + See RF69 and SX127x header files for examples of register maps. +*/ +// register map | spaces up to this point +#define RADIOLIB__REG_ 0x00 + +// _REG_ MSB LSB DESCRIPTION +#define RADIOLIB__ 0b00000000 // 7 0 + + +/* + Module class definition + + The module class MAY inherit from the following classes: + + 1 - PhysicalLayer: In case the module implements methods for OSI physical layer control (e.g. SX127x). + 2 - Common class: In case the module further specifies some more generic class (e.g. SX127x/SX1278) +*/ +class { + public: + /* + Constructor MUST have only one parameter "Module* mod". + The class MAY implement additional overloaded constructors. + */ + // constructor + (Module* mod); + + /* + The class MUST implement at least one basic method called "begin". + The "begin" method MUST initialize the module and return the status as int16_t type. + */ + // basic methods + int16_t begin(); + + /* + The class MAY implement additional methods. + All implemented methods SHOULD return the status as int16_t type. + */ + +#if !defined(RADIOLIB_GODMODE) + private: +#endif + /* + The class MUST contain private member "Module* _mod" + */ + Module* _mod; + + /* + The class MAY contain additional private variables and/or methods. + Private member variables MUST have a name prefixed with "_" (underscore, ASCII 0x5F) + + Usually, these are variables for saving module configuration, or methods that do not have to be exposed to the end user. + */ +}; + +#endif + +#endif diff --git a/lib/RadioLib/idf_component.yml b/lib/RadioLib/idf_component.yml index 572ca4d..2a2ae15 100644 --- a/lib/RadioLib/idf_component.yml +++ b/lib/RadioLib/idf_component.yml @@ -1,6 +1,6 @@ version: "6.5.0" 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" +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, lr1110, lr1120, lr1121" url: "https://github.com/jgromes/RadioLib" repository: "https://github.com/jgromes/RadioLib.git" license: "MIT" diff --git a/lib/RadioLib/keywords.txt b/lib/RadioLib/keywords.txt index cb12945..fac7aa1 100644 --- a/lib/RadioLib/keywords.txt +++ b/lib/RadioLib/keywords.txt @@ -15,6 +15,9 @@ ArduinoHal KEYWORD1 # modules CC1101 KEYWORD1 LLCC68 KEYWORD1 +LR1110 KEYWORD1 +LR1120 KEYWORD1 +LR1121 KEYWORD1 nRF24 KEYWORD1 RF69 KEYWORD1 RFM22 KEYWORD1 @@ -88,6 +91,11 @@ AS923 KEYWORD1 KR920 KEYWORD1 IN865 KEYWORD1 +# LR11x0 scan results +LR11x0WifiResult_t KEYWORD1 +LR11x0WifiResultFull_t KEYWORD1 +LR11x0WifiResultExtended_t KEYWORD1 + ####################################### # Methods and Functions (KEYWORD2) ####################################### @@ -124,6 +132,7 @@ setCodingRate KEYWORD2 setFrequency KEYWORD2 setSyncWord KEYWORD2 setOutputPower KEYWORD2 +checkOutputPower KEYWORD2 setCurrentLimit KEYWORD2 setPreambleLength KEYWORD2 setGain KEYWORD2 @@ -134,6 +143,7 @@ getSNR KEYWORD2 getDataRate KEYWORD2 setBitRate KEYWORD2 setRxBandwidth KEYWORD2 +autoSetRxBandwidth KEYWORD2 setAFCBandwidth KEYWORD2 setAFC KEYWORD2 setAFCAGCTrigger KEYWORD2 @@ -224,6 +234,7 @@ spectralScanStart KEYWORD2 spectralScanAbort KEYWORD2 spectralScanGetStatus KEYWORD2 spectralScanGetResult KEYWORD2 +setPaRampTime KEYWORD2 # nRF24 setIrqAction KEYWORD2 @@ -234,6 +245,16 @@ disablePipe KEYWORD2 getStatus KEYWORD2 setAutoAck KEYWORD2 +# LR11x0 +beginLRFHSS KEYWORD2 +setLrFhssConfig KEYWORD2 +startWifiScan KEYWORD2 +getWifiScanResultsCount KEYWORD2 +getWifiScanResult KEYWORD2 +wifiScan KEYWORD2 +setWiFiScanAction KEYWORD2 +clearWiFiScanAction KEYWORD2 + # RTTY idle KEYWORD2 byteArr KEYWORD2 @@ -307,10 +328,10 @@ uplink KEYWORD2 downlink KEYWORD2 sendReceive KEYWORD2 setDeviceStatus KEYWORD2 -getFcntUp KEYWORD2 -getNFcntDown KEYWORD2 -getAFcntDown KEYWORD2 -resetFcntDown KEYWORD2 +getFCntUp KEYWORD2 +getNFCntDown KEYWORD2 +getAFCntDown KEYWORD2 +resetFCntDown KEYWORD2 setDatarate KEYWORD2 setADR KEYWORD2 setDutyCycle KEYWORD2 @@ -319,10 +340,10 @@ timeUntilUplink KEYWORD2 setDwellTime KEYWORD2 maxPayloadDwellTime KEYWORD2 setTxPower KEYWORD2 -setCSMA KEYWORD2 getMacLinkCheckAns KEYWORD2 getMacDeviceTimeAns KEYWORD2 getDevAddr KEYWORD2 +getLastToA KEYWORD2 ####################################### # Constants (LITERAL1) diff --git a/lib/RadioLib/library.json b/lib/RadioLib/library.json index 0758385..3f91e3e 100644 --- a/lib/RadioLib/library.json +++ b/lib/RadioLib/library.json @@ -2,7 +2,7 @@ "name": "RadioLib", "version": "6.5.0", "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).", - "keywords": "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", + "keywords": "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, lr1110, lr1120, lr1121", "homepage": "https://github.com/jgromes/RadioLib", "repository": { diff --git a/lib/RadioLib/library.properties b/lib/RadioLib/library.properties index 0b435df..d7abb49 100644 --- a/lib/RadioLib/library.properties +++ b/lib/RadioLib/library.properties @@ -3,7 +3,7 @@ version=6.5.0 author=Jan Gromes maintainer=Jan Gromes sentence=Universal wireless communication library -paragraph=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). +paragraph=User-friendly library for sub-GHz radio modules (SX1278, RF69, CC1101, SX1268, LR1110 and many others), as well as ham radio digital modes (RTTY, SSTV, AX.25 etc.) and other protocols (Pagers, LoRaWAN). category=Communication url=https://github.com/jgromes/RadioLib architectures=* diff --git a/lib/RadioLib/src/ArduinoHal.cpp b/lib/RadioLib/src/ArduinoHal.cpp index 9c5fd57..fce0fb1 100644 --- a/lib/RadioLib/src/ArduinoHal.cpp +++ b/lib/RadioLib/src/ArduinoHal.cpp @@ -53,7 +53,7 @@ void inline ArduinoHal::detachInterrupt(uint32_t interruptNum) { ::detachInterrupt(interruptNum); } -void inline ArduinoHal::delay(unsigned long ms) { +void inline ArduinoHal::delay(RadioLibTime_t ms) { #if !defined(RADIOLIB_CLOCK_DRIFT_MS) ::delay(ms); #else @@ -61,7 +61,7 @@ void inline ArduinoHal::delay(unsigned long ms) { #endif } -void inline ArduinoHal::delayMicroseconds(unsigned long us) { +void inline ArduinoHal::delayMicroseconds(RadioLibTime_t us) { #if !defined(RADIOLIB_CLOCK_DRIFT_MS) ::delayMicroseconds(us); #else @@ -69,7 +69,7 @@ void inline ArduinoHal::delayMicroseconds(unsigned long us) { #endif } -unsigned long inline ArduinoHal::millis() { +RadioLibTime_t inline ArduinoHal::millis() { #if !defined(RADIOLIB_CLOCK_DRIFT_MS) return(::millis()); #else @@ -77,7 +77,7 @@ unsigned long inline ArduinoHal::millis() { #endif } -unsigned long inline ArduinoHal::micros() { +RadioLibTime_t inline ArduinoHal::micros() { #if !defined(RADIOLIB_CLOCK_DRIFT_MS) return(::micros()); #else @@ -85,7 +85,7 @@ unsigned long inline ArduinoHal::micros() { #endif } -long inline ArduinoHal::pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) { +long inline ArduinoHal::pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout) { if(pin == RADIOLIB_NC) { return 0; } @@ -114,7 +114,7 @@ void inline ArduinoHal::spiEnd() { spi->end(); } -void inline ArduinoHal::tone(uint32_t pin, unsigned int frequency, unsigned long duration) { +void inline ArduinoHal::tone(uint32_t pin, unsigned int frequency, RadioLibTime_t duration) { #if !defined(RADIOLIB_TONE_UNSUPPORTED) if(pin == RADIOLIB_NC) { return; diff --git a/lib/RadioLib/src/ArduinoHal.h b/lib/RadioLib/src/ArduinoHal.h index 00074c1..959b9d2 100644 --- a/lib/RadioLib/src/ArduinoHal.h +++ b/lib/RadioLib/src/ArduinoHal.h @@ -32,7 +32,7 @@ class ArduinoHal : public RadioLibHal { \param spi SPI interface to be used, can also use software SPI implementations. \param spiSettings SPI interface settings. */ - ArduinoHal(SPIClass& spi, SPISettings spiSettings = RADIOLIB_DEFAULT_SPI_SETTINGS); + explicit ArduinoHal(SPIClass& spi, SPISettings spiSettings = RADIOLIB_DEFAULT_SPI_SETTINGS); // implementations of pure virtual RadioLibHal methods void pinMode(uint32_t pin, uint32_t mode) override; @@ -40,11 +40,11 @@ class ArduinoHal : public RadioLibHal { uint32_t digitalRead(uint32_t pin) override; void attachInterrupt(uint32_t interruptNum, void (*interruptCb)(void), uint32_t mode) override; void detachInterrupt(uint32_t interruptNum) override; - void delay(unsigned long ms) override; - void delayMicroseconds(unsigned long us) override; - unsigned long millis() override; - unsigned long micros() override; - long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) override; + void delay(RadioLibTime_t ms) override; + void delayMicroseconds(RadioLibTime_t us) override; + RadioLibTime_t millis() override; + RadioLibTime_t micros() override; + long pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout) override; void spiBegin() override; void spiBeginTransaction() override; void spiTransfer(uint8_t* out, size_t len, uint8_t* in) override; @@ -54,13 +54,13 @@ class ArduinoHal : public RadioLibHal { // implementations of virtual RadioLibHal methods void init() override; void term() override; - void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0) override; + void tone(uint32_t pin, unsigned int frequency, RadioLibTime_t duration = 0) override; void noTone(uint32_t pin) override; void yield() override; uint32_t pinToInterrupt(uint32_t pin) override; #if !RADIOLIB_GODMODE - private: + protected: #endif SPIClass* spi = NULL; SPISettings spiSettings = RADIOLIB_DEFAULT_SPI_SETTINGS; diff --git a/lib/RadioLib/src/BuildOpt.h b/lib/RadioLib/src/BuildOpt.h index 9b25d3f..9d860ab 100644 --- a/lib/RadioLib/src/BuildOpt.h +++ b/lib/RadioLib/src/BuildOpt.h @@ -1,6 +1,8 @@ #if !defined(_RADIOLIB_BUILD_OPTIONS_H) #define _RADIOLIB_BUILD_OPTIONS_H +#include "TypeDef.h" + /* RadioLib build configuration options */ /* @@ -350,12 +352,12 @@ // ... and for the grand finale, we have millis() and micros() DEFINED AS MACROS! #if defined(millis) #undef millis - inline unsigned long millis() { return((unsigned long)(STCV / 1000)); }; + inline RadioLibTime_t millis() { return((RadioLibTime_t)(STCV / 1000)); }; #endif #if defined(micros) #undef micros - inline unsigned long micros() { return((unsigned long)(STCV)); }; + inline RadioLibTime_t micros() { return((RadioLibTime_t)(STCV)); }; #endif #elif defined(TEENSYDUINO) @@ -529,6 +531,17 @@ #define RADIOLIB_DEBUG_SPI_HEXDUMP(...) {} #endif +// debug info strings +#define RADIOLIB_VALUE_TO_STRING(x) #x +#define RADIOLIB_VALUE(x) RADIOLIB_VALUE_TO_STRING(x) + +#define RADIOLIB_INFO "\nRadioLib Info\nVersion: \"" \ + RADIOLIB_VALUE(RADIOLIB_VERSION_MAJOR) "." \ + RADIOLIB_VALUE(RADIOLIB_VERSION_MINOR) "." \ + RADIOLIB_VALUE(RADIOLIB_VERSION_PATCH) "." \ + RADIOLIB_VALUE(RADIOLIB_VERSION_EXTRA) "\"\n" \ + "Platform: " RADIOLIB_VALUE(RADIOLIB_PLATFORM) "\n" \ + "Compiled: " RADIOLIB_VALUE(__DATE__) " " RADIOLIB_VALUE(__TIME__) /*! \brief A simple assert macro, will return on error. @@ -563,4 +576,4 @@ #define RADIOLIB_VERSION (((RADIOLIB_VERSION_MAJOR) << 24) | ((RADIOLIB_VERSION_MINOR) << 16) | ((RADIOLIB_VERSION_PATCH) << 8) | (RADIOLIB_VERSION_EXTRA)) -#endif \ No newline at end of file +#endif diff --git a/lib/RadioLib/src/Hal.cpp b/lib/RadioLib/src/Hal.cpp index 1a42aa1..acc43bf 100644 --- a/lib/RadioLib/src/Hal.cpp +++ b/lib/RadioLib/src/Hal.cpp @@ -16,7 +16,7 @@ void RadioLibHal::term() { } -void RadioLibHal::tone(uint32_t pin, unsigned int frequency, unsigned long duration) { +void RadioLibHal::tone(uint32_t pin, unsigned int frequency, RadioLibTime_t duration) { (void)pin; (void)frequency; (void)duration; diff --git a/lib/RadioLib/src/Hal.h b/lib/RadioLib/src/Hal.h index 03bf174..291b347 100644 --- a/lib/RadioLib/src/Hal.h +++ b/lib/RadioLib/src/Hal.h @@ -104,28 +104,28 @@ class RadioLibHal { Must be implemented by the platform-specific hardware abstraction! \param ms Number of milliseconds to wait. */ - virtual void delay(unsigned long ms) = 0; + virtual void delay(RadioLibTime_t ms) = 0; /*! \brief Blocking microsecond wait function. Must be implemented by the platform-specific hardware abstraction! \param us Number of microseconds to wait. */ - virtual void delayMicroseconds(unsigned long us) = 0; + virtual void delayMicroseconds(RadioLibTime_t us) = 0; /*! \brief Get number of milliseconds since start. Must be implemented by the platform-specific hardware abstraction! \returns Number of milliseconds since start. */ - virtual unsigned long millis() = 0; + virtual RadioLibTime_t millis() = 0; /*! \brief Get number of microseconds since start. Must be implemented by the platform-specific hardware abstraction! \returns Number of microseconds since start. */ - virtual unsigned long micros() = 0; + virtual RadioLibTime_t micros() = 0; /*! \brief Measure the length of incoming digital pulse in microseconds. @@ -135,7 +135,7 @@ class RadioLibHal { \param timeout Timeout in microseconds. \returns Pulse length in microseconds, or 0 if the pulse did not start before timeout. */ - virtual long pulseIn(uint32_t pin, uint32_t state, unsigned long timeout) = 0; + virtual long pulseIn(uint32_t pin, uint32_t state, RadioLibTime_t timeout) = 0; /*! \brief SPI initialization method. @@ -188,7 +188,7 @@ class RadioLibHal { \param frequency Frequency of the square wave. \param duration Duration of the tone in ms. When set to 0, the tone will be infinite. */ - virtual void tone(uint32_t pin, unsigned int frequency, unsigned long duration = 0); + virtual void tone(uint32_t pin, unsigned int frequency, RadioLibTime_t duration = 0); /*! \brief Method to stop producing a tone. diff --git a/lib/RadioLib/src/Module.cpp b/lib/RadioLib/src/Module.cpp index c46e26e..8a95d52 100644 --- a/lib/RadioLib/src/Module.cpp +++ b/lib/RadioLib/src/Module.cpp @@ -31,8 +31,7 @@ Module::Module(const Module& mod) { } Module& Module::operator=(const Module& mod) { - this->SPIreadCommand = mod.SPIreadCommand; - this->SPIwriteCommand = mod.SPIwriteCommand; + memcpy((void*)&mod.spiConfig, &this->spiConfig, sizeof(SPIConfig_t)); this->csPin = mod.csPin; this->irqPin = mod.irqPin; this->rstPin = mod.rstPin; @@ -40,14 +39,12 @@ Module& Module::operator=(const Module& mod) { return(*this); } +static volatile const char info[] = RADIOLIB_INFO; void Module::init() { this->hal->init(); this->hal->pinMode(csPin, this->hal->GpioModeOutput); this->hal->digitalWrite(csPin, this->hal->GpioLevelHigh); - 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"); + RADIOLIB_DEBUG_BASIC_PRINTLN(RADIOLIB_INFO); } void Module::term() { @@ -55,7 +52,7 @@ void Module::term() { this->hal->term(); } -int16_t Module::SPIgetRegValue(uint16_t reg, uint8_t msb, uint8_t lsb) { +int16_t Module::SPIgetRegValue(uint32_t reg, uint8_t msb, uint8_t lsb) { if((msb > 7) || (lsb > 7) || (lsb > msb)) { return(RADIOLIB_ERR_INVALID_BIT_RANGE); } @@ -65,7 +62,7 @@ int16_t Module::SPIgetRegValue(uint16_t reg, uint8_t msb, uint8_t lsb) { return(maskedValue); } -int16_t Module::SPIsetRegValue(uint16_t reg, uint8_t value, uint8_t msb, uint8_t lsb, uint8_t checkInterval, uint8_t checkMask) { +int16_t Module::SPIsetRegValue(uint32_t reg, uint8_t value, uint8_t msb, uint8_t lsb, uint8_t checkInterval, uint8_t checkMask) { if((msb > 7) || (lsb > 7) || (lsb > msb)) { return(RADIOLIB_ERR_INVALID_BIT_RANGE); } @@ -78,14 +75,19 @@ int16_t Module::SPIsetRegValue(uint16_t reg, uint8_t value, uint8_t msb, uint8_t #if RADIOLIB_SPI_PARANOID // check register value each millisecond until check interval is reached // some registers need a bit of time to process the change (e.g. SX127X_REG_OP_MODE) - uint32_t start = this->hal->micros(); + RadioLibTime_t start = this->hal->micros(); + #if RADIOLIB_DEBUG_SPI uint8_t readValue = 0x00; + #endif while(this->hal->micros() - start < (checkInterval * 1000)) { - readValue = SPIreadRegister(reg); - if((readValue & checkMask) == (newValue & checkMask)) { + uint8_t val = SPIreadRegister(reg); + if((val & checkMask) == (newValue & checkMask)) { // check passed, we can stop the loop return(RADIOLIB_ERR_NONE); } + #if RADIOLIB_DEBUG_SPI + readValue = val; + #endif } // check failed, print debug info @@ -104,47 +106,75 @@ int16_t Module::SPIsetRegValue(uint16_t reg, uint8_t value, uint8_t msb, uint8_t #endif } -void Module::SPIreadRegisterBurst(uint16_t reg, size_t numBytes, uint8_t* inBytes) { - if(!SPIstreamType) { - SPItransfer(SPIreadCommand, reg, NULL, inBytes, numBytes); +void Module::SPIreadRegisterBurst(uint32_t reg, size_t numBytes, uint8_t* inBytes) { + if(!this->spiConfig.stream) { + SPItransfer(this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ], reg, NULL, inBytes, numBytes); } else { - uint8_t cmd[] = { SPIreadCommand, (uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF) }; - SPItransferStream(cmd, 3, false, NULL, inBytes, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT); + uint8_t cmd[6]; + uint8_t* cmdPtr = cmd; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] >> 8*i) & 0xFF; + } + for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) { + *(cmdPtr++) = (reg >> 8*i) & 0xFF; + } + SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, false, NULL, inBytes, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT); } } -uint8_t Module::SPIreadRegister(uint16_t reg) { +uint8_t Module::SPIreadRegister(uint32_t reg) { uint8_t resp = 0; - if(!SPIstreamType) { - SPItransfer(SPIreadCommand, reg, NULL, &resp, 1); + if(!spiConfig.stream) { + SPItransfer(this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ], reg, NULL, &resp, 1); } else { - uint8_t cmd[] = { SPIreadCommand, (uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF) }; - SPItransferStream(cmd, 3, false, NULL, &resp, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT); + uint8_t cmd[6]; + uint8_t* cmdPtr = cmd; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] >> 8*i) & 0xFF; + } + for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) { + *(cmdPtr++) = (reg >> 8*i) & 0xFF; + } + SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, false, NULL, &resp, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT); } return(resp); } -void Module::SPIwriteRegisterBurst(uint16_t reg, uint8_t* data, size_t numBytes) { - if(!SPIstreamType) { - SPItransfer(SPIwriteCommand, reg, data, NULL, numBytes); +void Module::SPIwriteRegisterBurst(uint32_t reg, uint8_t* data, size_t numBytes) { + if(!spiConfig.stream) { + SPItransfer(spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE], reg, data, NULL, numBytes); } else { - uint8_t cmd[] = { SPIwriteCommand, (uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF) }; - SPItransferStream(cmd, 3, true, data, NULL, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT); + uint8_t cmd[6]; + uint8_t* cmdPtr = cmd; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] >> 8*i) & 0xFF; + } + for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) { + *(cmdPtr++) = (reg >> 8*i) & 0xFF; + } + SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, true, data, NULL, numBytes, true, RADIOLIB_MODULE_SPI_TIMEOUT); } } -void Module::SPIwriteRegister(uint16_t reg, uint8_t data) { - if(!SPIstreamType) { - SPItransfer(SPIwriteCommand, reg, &data, NULL, 1); +void Module::SPIwriteRegister(uint32_t reg, uint8_t data) { + if(!spiConfig.stream) { + SPItransfer(spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE], reg, &data, NULL, 1); } else { - uint8_t cmd[] = { SPIwriteCommand, (uint8_t)((reg >> 8) & 0xFF), (uint8_t)(reg & 0xFF) }; - SPItransferStream(cmd, 3, true, &data, NULL, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT); + uint8_t cmd[6]; + uint8_t* cmdPtr = cmd; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] >> 8*i) & 0xFF; + } + for(int8_t i = (int8_t)((this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8) - 1); i >= 0; i--) { + *(cmdPtr++) = (reg >> 8*i) & 0xFF; + } + SPItransferStream(cmd, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8, true, &data, NULL, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT); } } -void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes) { +void Module::SPItransfer(uint16_t cmd, uint32_t reg, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes) { // prepare the buffers - size_t buffLen = this->SPIaddrWidth/8 + numBytes; + size_t buffLen = this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 + this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8 + numBytes; #if RADIOLIB_STATIC_ONLY uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE]; uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE]; @@ -155,7 +185,8 @@ void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* d uint8_t* buffOutPtr = buffOut; // copy the command - if(this->SPIaddrWidth <= 8) { + // TODO properly handle variable commands and addresses + if(this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] <= 8) { *(buffOutPtr++) = reg | cmd; } else { *(buffOutPtr++) = (reg >> 8) | cmd; @@ -163,10 +194,10 @@ void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* d } // copy the data - if(cmd == SPIwriteCommand) { + if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE]) { memcpy(buffOutPtr, dataOut, numBytes); } else { - memset(buffOutPtr, this->SPInopCommand, numBytes); + memset(buffOutPtr, this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP], numBytes); } // do the transfer @@ -177,19 +208,19 @@ void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* d this->hal->spiEndTransaction(); // copy the data - if(cmd == SPIreadCommand) { - memcpy(dataIn, &buffIn[this->SPIaddrWidth/8], numBytes); + if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ]) { + memcpy(dataIn, &buffIn[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8], numBytes); } // print debug information #if RADIOLIB_DEBUG_SPI uint8_t* debugBuffPtr = NULL; - if(cmd == SPIwriteCommand) { + if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE]) { RADIOLIB_DEBUG_SPI_PRINT("W\t%X\t", reg); - debugBuffPtr = &buffOut[this->SPIaddrWidth/8]; - } else if(cmd == SPIreadCommand) { + debugBuffPtr = &buffOut[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8]; + } else if(cmd == spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ]) { RADIOLIB_DEBUG_SPI_PRINT("R\t%X\t", reg); - debugBuffPtr = &buffIn[this->SPIaddrWidth/8]; + debugBuffPtr = &buffIn[this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR]/8]; } for(size_t n = 0; n < numBytes; n++) { RADIOLIB_DEBUG_SPI_PRINT_NOTAG("%X\t", debugBuffPtr[n]); @@ -203,8 +234,13 @@ void Module::SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* d #endif } -int16_t Module::SPIreadStream(uint8_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { - return(this->SPIreadStream(&cmd, 1, data, numBytes, waitForGpio, verify)); +int16_t Module::SPIreadStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { + uint8_t cmdBuf[2]; + uint8_t* cmdPtr = cmdBuf; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (cmd >> 8*i) & 0xFF; + } + return(this->SPIreadStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, data, numBytes, waitForGpio, verify)); } int16_t Module::SPIreadStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { @@ -212,16 +248,27 @@ int16_t Module::SPIreadStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size_ int16_t state = this->SPItransferStream(cmd, cmdLen, false, NULL, data, numBytes, waitForGpio, RADIOLIB_MODULE_SPI_TIMEOUT); RADIOLIB_ASSERT(state); + #if !RADIOLIB_SPI_PARANOID + (void)verify; + return(RADIOLIB_ERR_NONE); + #else + // check the status - if(verify) { - state = this->SPIcheckStream(); + if(verify && (this->spiConfig.checkStatusCb != nullptr)) { + state = this->spiConfig.checkStatusCb(this); } return(state); + #endif } -int16_t Module::SPIwriteStream(uint8_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { - return(this->SPIwriteStream(&cmd, 1, data, numBytes, waitForGpio, verify)); +int16_t Module::SPIwriteStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { + uint8_t cmdBuf[2]; + uint8_t* cmdPtr = cmdBuf; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = (cmd >> 8*i) & 0xFF; + } + return(this->SPIwriteStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, data, numBytes, waitForGpio, verify)); } int16_t Module::SPIwriteStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size_t numBytes, bool waitForGpio, bool verify) { @@ -229,12 +276,18 @@ int16_t Module::SPIwriteStream(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, size int16_t state = this->SPItransferStream(cmd, cmdLen, true, data, NULL, numBytes, waitForGpio, RADIOLIB_MODULE_SPI_TIMEOUT); RADIOLIB_ASSERT(state); + #if !RADIOLIB_SPI_PARANOID + (void)verify; + return(RADIOLIB_ERR_NONE); + #else + // check the status - if(verify) { - state = this->SPIcheckStream(); + if(verify && (this->spiConfig.checkStatusCb != nullptr)) { + state = this->spiConfig.checkStatusCb(this); } return(state); + #endif } int16_t Module::SPIcheckStream() { @@ -243,31 +296,33 @@ int16_t Module::SPIcheckStream() { #if RADIOLIB_SPI_PARANOID // get the status uint8_t spiStatus = 0; - uint8_t cmd = this->SPIstatusCommand; - state = this->SPItransferStream(&cmd, 1, false, NULL, &spiStatus, 0, true, RADIOLIB_MODULE_SPI_TIMEOUT); + uint8_t cmdBuf[2]; + uint8_t* cmdPtr = cmdBuf; + for(int8_t i = (int8_t)this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8 - 1; i >= 0; i--) { + *(cmdPtr++) = ( this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] >> 8*i) & 0xFF; + } + state = this->SPItransferStream(cmdBuf, this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD]/8, false, NULL, &spiStatus, 1, true, RADIOLIB_MODULE_SPI_TIMEOUT); RADIOLIB_ASSERT(state); // translate to RadioLib status code - if(this->SPIparseStatusCb != nullptr) { - this->SPIstreamError = this->SPIparseStatusCb(spiStatus); + if(this->spiConfig.parseStatusCb != nullptr) { + this->spiConfig.err = this->spiConfig.parseStatusCb(spiStatus); } #endif return(state); } -int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes, bool waitForGpio, uint32_t timeout) { - // prepare the buffers +int16_t Module::SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes, bool waitForGpio, RadioLibTime_t timeout) { + // prepare the output buffer size_t buffLen = cmdLen + numBytes; if(!write) { - buffLen++; + buffLen += (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8); } #if RADIOLIB_STATIC_ONLY uint8_t buffOut[RADIOLIB_STATIC_ARRAY_SIZE]; - uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE]; #else uint8_t* buffOut = new uint8_t[buffLen]; - uint8_t* buffIn = new uint8_t[buffLen]; #endif uint8_t* buffOutPtr = buffOut; @@ -280,27 +335,33 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint if(write) { memcpy(buffOutPtr, dataOut, numBytes); } else { - memset(buffOutPtr, this->SPInopCommand, numBytes + 1); + memset(buffOutPtr, this->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP], numBytes + (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8)); } // ensure GPIO is low if(this->gpioPin == RADIOLIB_NC) { - this->hal->delay(1); + this->hal->delay(50); } else { - uint32_t start = this->hal->millis(); + RadioLibTime_t start = this->hal->millis(); while(this->hal->digitalRead(this->gpioPin)) { this->hal->yield(); if(this->hal->millis() - start >= timeout) { RADIOLIB_DEBUG_BASIC_PRINTLN("GPIO pre-transfer timeout, is it connected?"); #if !RADIOLIB_STATIC_ONLY delete[] buffOut; - delete[] buffIn; #endif return(RADIOLIB_ERR_SPI_CMD_TIMEOUT); } } } + // prepare the input buffer + #if RADIOLIB_STATIC_ONLY + uint8_t buffIn[RADIOLIB_STATIC_ARRAY_SIZE]; + #else + uint8_t* buffIn = new uint8_t[buffLen]; + #endif + // do the transfer this->hal->spiBeginTransaction(); this->hal->digitalWrite(this->csPin, this->hal->GpioLevelLow); @@ -314,7 +375,7 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint this->hal->delay(1); } else { this->hal->delayMicroseconds(1); - uint32_t start = this->hal->millis(); + RadioLibTime_t start = this->hal->millis(); while(this->hal->digitalRead(this->gpioPin)) { this->hal->yield(); if(this->hal->millis() - start >= timeout) { @@ -331,14 +392,14 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint // parse status int16_t state = RADIOLIB_ERR_NONE; - if((this->SPIparseStatusCb != nullptr) && (numBytes > 0)) { - state = this->SPIparseStatusCb(buffIn[cmdLen]); + if((this->spiConfig.parseStatusCb != nullptr) && (numBytes > 0)) { + state = this->spiConfig.parseStatusCb(buffIn[this->spiConfig.statusPos]); } // copy the data if(!write) { - // skip the first byte for read-type commands (status-only) - memcpy(dataIn, &buffIn[cmdLen + 1], numBytes); + // skip the status bytes if present + memcpy(dataIn, &buffIn[cmdLen + (this->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] / 8)], numBytes); } // print debug information @@ -380,7 +441,7 @@ int16_t Module::SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint return(state); } -void Module::waitForMicroseconds(uint32_t start, uint32_t len) { +void Module::waitForMicroseconds(RadioLibTime_t start, RadioLibTime_t len) { #if RADIOLIB_INTERRUPT_TIMING (void)start; if((this->TimerSetupCb != nullptr) && (len != this->prevTimingLen)) { @@ -410,8 +471,8 @@ uint32_t Module::reflect(uint32_t in, uint8_t bits) { 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]; - sprintf(str, "%07" PRIx32 " ", i+offset); + char str[120]; + sprintf(str, "%07" PRIx32 " ", (uint32_t)i+offset); size_t line_len = 16; if(rem_len < line_len) { line_len = rem_len; @@ -436,15 +497,21 @@ void Module::hexdump(const char* level, uint8_t* data, size_t len, uint32_t offs } str[56] = '|'; str[57] = ' '; + + // at this point we need to start escaping "%" characters + char* strPtr = &str[58]; for(size_t j = 0; j < line_len; j++) { char c = data[i+j]; if((c < ' ') || (c > '~')) { c = '.'; + } else if(c == '%') { + *strPtr++ = '%'; } - sprintf(&str[58 + j], "%c", c); + sprintf(strPtr++, "%c", c); + } for(size_t j = line_len; j < 16; j++) { - sprintf(&str[58 + j], " "); + sprintf(strPtr++, " "); } if(level) { RADIOLIB_DEBUG_PRINT(level); @@ -487,7 +554,7 @@ size_t Module::serialPrintf(const char* format, ...) { vsnprintf(buffer, len + 1, format, arg); va_end(arg); } - len = RADIOLIB_DEBUG_PORT.write((const uint8_t*)buffer, len); + len = RADIOLIB_DEBUG_PORT.write(reinterpret_cast(buffer), len); if (buffer != temp) { delete[] buffer; } diff --git a/lib/RadioLib/src/Module.h b/lib/RadioLib/src/Module.h index 9b5b78d..90de297 100644 --- a/lib/RadioLib/src/Module.h +++ b/lib/RadioLib/src/Module.h @@ -13,16 +13,53 @@ #endif /*! -* Value to use as the last element in a mode table to indicate the -* end of the table. -* -* See setRfSwitchTable() for details. + \def END_OF_MODE_TABLE Value to use as the last element in a mode table to indicate the + end of the table. See \ref setRfSwitchTable for details. */ #define END_OF_MODE_TABLE { Module::MODE_END_OF_TABLE, {} } // default timeout for SPI transfers #define RADIOLIB_MODULE_SPI_TIMEOUT (1000) +/*! + \defgroup module_spi_command_pos Position of commands in Module::spiConfig command array. + \{ +*/ + +/*! \def RADIOLIB_MODULE_SPI_COMMAND_READ Position of the read command. */ +#define RADIOLIB_MODULE_SPI_COMMAND_READ (0) + +/*! \def RADIOLIB_MODULE_SPI_COMMAND_WRITE Position of the write command. */ +#define RADIOLIB_MODULE_SPI_COMMAND_WRITE (1) + +/*! \def RADIOLIB_MODULE_SPI_COMMAND_NOP Position of the no-operation command. */ +#define RADIOLIB_MODULE_SPI_COMMAND_NOP (2) + +/*! \def RADIOLIB_MODULE_SPI_COMMAND_STATUS Position of the status command. */ +#define RADIOLIB_MODULE_SPI_COMMAND_STATUS (3) + +/*! + \} +*/ + +/*! + \defgroup module_spi_width_pos Position of bit field widths in Module::spiConfig width array. + \{ +*/ + +/*! \def RADIOLIB_MODULE_SPI_WIDTH_ADDR Position of the address width. */ +#define RADIOLIB_MODULE_SPI_WIDTH_ADDR (0) + +/*! \def RADIOLIB_MODULE_SPI_WIDTH_CMD Position of the command width. */ +#define RADIOLIB_MODULE_SPI_WIDTH_CMD (1) + +/*! \def RADIOLIB_MODULE_SPI_WIDTH_STATUS Position of the status width. */ +#define RADIOLIB_MODULE_SPI_WIDTH_STATUS (2) + +/*! + \} +*/ + /*! \class Module \brief Implements all common low-level methods to control the wireless module. @@ -31,43 +68,49 @@ class Module { public: /*! - * \brief The maximum number of pins supported by the RF switch - * code. - * - * Note: It is not recommended to use this constant in your sketch - * when defining a rfswitch pins array, to prevent issues when this - * value is ever increased and such an array gets extra zero - * elements (that will be interpreted as pin 0). - */ + \brief The maximum number of pins supported by the RF switch code. + Note: It is not recommended to use this constant in your sketch + when defining a rfswitch pins array, to prevent issues when this + value is ever increased and such an array gets extra zero + elements (that will be interpreted as pin 0). + */ static const size_t RFSWITCH_MAX_PINS = 3; /*! - * Description of RF switch pin states for a single mode. - * - * See setRfSwitchTable() for details. - */ + \struct RfSwitchMode_t + \brief Description of RF switch pin states for a single mode. + See \ref setRfSwitchTable for details. + */ struct RfSwitchMode_t { + /*! \brief RF switching mode, one of \ref OpMode_t or a custom radio-defined value. */ uint8_t mode; + + /*! \brief Output pin values */ uint32_t values[RFSWITCH_MAX_PINS]; }; /*! - * Constants to use in a mode table set be setRfSwitchTable. These - * constants work for most radios, but some radios define their own - * constants to be used instead. - * - * See setRfSwitchTable() for details. - */ + \enum OpMode_t + \brief Constants to use in a mode table set be setRfSwitchTable. These + constants work for most radios, but some radios define their own + constants to be used instead. + + See \ref setRfSwitchTable for details. + */ enum OpMode_t { - /*! End of table marker, use \ref END_OF_MODE_TABLE constant - * instead. Value is zero to ensure zero-initialized mode ends the - * table */ + /*! + \brief End of table marker, use \ref END_OF_MODE_TABLE constant instead. + Value is zero to ensure zero-initialized mode ends the table. + */ MODE_END_OF_TABLE = 0, - /*! Idle mode */ + + /*! \brief Idle mode */ MODE_IDLE, - /*! Receive mode */ + + /*! \brief Receive mode */ MODE_RX, - /*! Transmission mode */ + + /*! \brief Transmission mode */ MODE_TX, }; @@ -111,62 +154,64 @@ class Module { /*! \brief Overload for assignment operator. - \param frame rvalue Module. + \param mod rvalue Module. */ Module& operator=(const Module& mod); // public member variables - /*! - \brief Hardware abstraction layer to be used. - */ + /*! \brief Hardware abstraction layer to be used. */ RadioLibHal* hal = NULL; - /*! - \brief Basic SPI read command. Defaults to 0x00. - */ - uint8_t SPIreadCommand = 0b00000000; - - /*! - \brief Basic SPI write command. Defaults to 0x80. - */ - uint8_t SPIwriteCommand = 0b10000000; - - /*! - \brief Basic SPI no-operation command. Defaults to 0x00. - */ - uint8_t SPInopCommand = 0x00; - - /*! - \brief Basic SPI status read command. Defaults to 0x00. - */ - uint8_t SPIstatusCommand = 0x00; - - /*! - \brief SPI address width. Defaults to 8, currently only supports 8 and 16-bit addresses. - */ - uint8_t SPIaddrWidth = 8; - - /*! - \brief Whether the SPI interface is stream-type (e.g. SX126x) or register-type (e.g. SX127x). - Defaults to register-type SPI interfaces. - */ - bool SPIstreamType = false; - - /*! - \brief The last recorded SPI stream error. - */ - int16_t SPIstreamError = RADIOLIB_ERR_UNKNOWN; - - /*! - \brief SPI status parsing callback typedef. - */ + /*! \brief Callback for parsing SPI status. */ typedef int16_t (*SPIparseStatusCb_t)(uint8_t in); + /*! \brief Callback for validation SPI status. */ + typedef int16_t (*SPIcheckStatusCb_t)(Module* mod); + + enum BitWidth_t { + BITS_0 = 0, + BITS_8 = 8, + BITS_16 = 16, + BITS_32 = 32, + }; + /*! - \brief Callback to function that will parse the module-specific status codes to RadioLib status codes. - Typically used for modules with SPI stream-type interface (e.g. SX126x/SX128x). + \struct SPIConfig_t + \brief SPI configuration structure. */ - SPIparseStatusCb_t SPIparseStatusCb = nullptr; + struct SPIConfig_t { + /*! \brief Whether the SPI module is stream-type (SX126x/8x) or registrer access type (SX127x, CC1101 etc). */ + bool stream; + + /*! \brief Last recorded SPI error - only updated for modules that return status during SPI transfers. */ + int16_t err; + + /*! \brief SPI commands */ + uint16_t cmds[4]; + + /*! \brief Bit widths of SPI addresses, commands and status bytes */ + BitWidth_t widths[3]; + + /*! \brief Byte position of status command in SPI stream */ + uint8_t statusPos; + + /*! \brief Callback for parsing SPI status. */ + SPIparseStatusCb_t parseStatusCb; + + /*! \brief Callback for validation SPI status. */ + SPIcheckStatusCb_t checkStatusCb; + }; + + /*! \brief SPI configuration structure. The default configuration corresponds to register-access modules, such as SX127x. */ + SPIConfig_t spiConfig = { + .stream = false, + .err = RADIOLIB_ERR_UNKNOWN, + .cmds = { 0x00, 0x80, 0x00, 0x00 }, + .widths = { Module::BITS_8, Module::BITS_0, Module::BITS_8 }, + .statusPos = 0, + .parseStatusCb = nullptr, + .checkStatusCb = nullptr, + }; #if RADIOLIB_INTERRUPT_TIMING @@ -208,7 +253,7 @@ class Module { \param lsb Least significant bit of the register variable. Bits below this one will be masked out. \returns Masked register value or status code. */ - int16_t SPIgetRegValue(uint16_t reg, uint8_t msb = 7, uint8_t lsb = 0); + int16_t SPIgetRegValue(uint32_t reg, uint8_t msb = 7, uint8_t lsb = 0); /*! \brief Overwrite-safe SPI write method with verification. This method is the preferred SPI write mechanism. @@ -220,7 +265,7 @@ class Module { \param checkMask Mask of bits to check, only bits set to 1 will be verified. \returns \ref status_codes */ - int16_t SPIsetRegValue(uint16_t reg, uint8_t value, uint8_t msb = 7, uint8_t lsb = 0, uint8_t checkInterval = 2, uint8_t checkMask = 0xFF); + int16_t SPIsetRegValue(uint32_t reg, uint8_t value, uint8_t msb = 7, uint8_t lsb = 0, uint8_t checkInterval = 2, uint8_t checkMask = 0xFF); /*! \brief SPI burst read method. @@ -228,14 +273,14 @@ class Module { \param numBytes Number of bytes that will be read. \param inBytes Pointer to array that will hold the read data. */ - void SPIreadRegisterBurst(uint16_t reg, size_t numBytes, uint8_t* inBytes); + void SPIreadRegisterBurst(uint32_t reg, size_t numBytes, uint8_t* inBytes); /*! \brief SPI basic read method. Use of this method is reserved for special cases, SPIgetRegValue should be used instead. \param reg Address of SPI register to read. \returns Value that was read from register. */ - uint8_t SPIreadRegister(uint16_t reg); + uint8_t SPIreadRegister(uint32_t reg); /*! \brief SPI burst write method. @@ -243,14 +288,14 @@ class Module { \param data Pointer to array that holds the data that will be written. \param numBytes Number of bytes that will be written. */ - void SPIwriteRegisterBurst(uint16_t reg, uint8_t* data, size_t numBytes); + void SPIwriteRegisterBurst(uint32_t reg, uint8_t* data, size_t numBytes); /*! \brief SPI basic write method. Use of this method is reserved for special cases, SPIsetRegValue should be used instead. \param reg Address of SPI register to write. \param data Value that will be written to the register. */ - void SPIwriteRegister(uint16_t reg, uint8_t data); + void SPIwriteRegister(uint32_t reg, uint8_t data); /*! \brief SPI single transfer method. @@ -260,7 +305,7 @@ class Module { \param dataIn Data that was transferred from slave to master. \param numBytes Number of bytes to transfer. */ - void SPItransfer(uint8_t cmd, uint16_t reg, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes); + void SPItransfer(uint16_t cmd, uint32_t reg, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes); /*! \brief Method to check the result of last SPI stream transfer. @@ -277,7 +322,7 @@ class Module { \param verify Whether to verify the result of the transaction after it is finished. \returns \ref status_codes */ - int16_t SPIreadStream(uint8_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio = true, bool verify = true); + int16_t SPIreadStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio = true, bool verify = true); /*! \brief Method to perform a read transaction with SPI stream. @@ -300,7 +345,7 @@ class Module { \param verify Whether to verify the result of the transaction after it is finished. \returns \ref status_codes */ - int16_t SPIwriteStream(uint8_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio = true, bool verify = true); + int16_t SPIwriteStream(uint16_t cmd, uint8_t* data, size_t numBytes, bool waitForGpio = true, bool verify = true); /*! \brief Method to perform a write transaction with SPI stream. @@ -326,7 +371,7 @@ class Module { \param timeout GPIO wait period timeout in milliseconds. \returns \ref status_codes */ - int16_t SPItransferStream(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes, bool waitForGpio, uint32_t timeout); + int16_t SPItransferStream(const uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, size_t numBytes, bool waitForGpio, RadioLibTime_t timeout); // pin number access methods @@ -438,7 +483,7 @@ class Module { /*! \brief Find a mode in the RfSwitchTable. - \param The mode to find. + \param mode The mode to find. \returns A pointer to the RfSwitchMode_t struct in the table that matches the passed mode. Returns nullptr if no rfswitch pins are configured, or the passed mode is not listed in the table. @@ -458,7 +503,7 @@ class Module { \param start Waiting start timestamp, in microseconds. \param len Waiting duration, in microseconds; */ - void waitForMicroseconds(uint32_t start, uint32_t len); + void waitForMicroseconds(RadioLibTime_t start, RadioLibTime_t len); /*! \brief Function to reflect bits within a byte. diff --git a/lib/RadioLib/src/RadioLib.h b/lib/RadioLib/src/RadioLib.h index 0e3d7f6..34b1d90 100644 --- a/lib/RadioLib/src/RadioLib.h +++ b/lib/RadioLib/src/RadioLib.h @@ -53,16 +53,7 @@ // print debug info #if RADIOLIB_DEBUG - #define RADIOLIB_VALUE_TO_STRING(x) #x - #define RADIOLIB_VALUE(x) RADIOLIB_VALUE_TO_STRING(x) - #pragma message("\nRadioLib Debug Info\nVersion: \"" \ - RADIOLIB_VALUE(RADIOLIB_VERSION_MAJOR) "." \ - RADIOLIB_VALUE(RADIOLIB_VERSION_MINOR) "." \ - RADIOLIB_VALUE(RADIOLIB_VERSION_PATCH) "." \ - RADIOLIB_VALUE(RADIOLIB_VERSION_EXTRA) "\"\n" \ - "Platform: " RADIOLIB_VALUE(RADIOLIB_PLATFORM) "\n" \ - "Compiled: " RADIOLIB_VALUE(__DATE__) " " RADIOLIB_VALUE(__TIME__) \ - ) + #pragma message(RADIOLIB_INFO) #endif // check unknown/unsupported platform @@ -77,6 +68,9 @@ #include "modules/CC1101/CC1101.h" #include "modules/LLCC68/LLCC68.h" +#include "modules/LR11x0/LR1110.h" +#include "modules/LR11x0/LR1120.h" +#include "modules/LR11x0/LR1121.h" #include "modules/nRF24/nRF24.h" #include "modules/RF69/RF69.h" #include "modules/RFM2x/RFM22.h" diff --git a/lib/RadioLib/src/TypeDef.h b/lib/RadioLib/src/TypeDef.h index 6cb6724..245eb72 100644 --- a/lib/RadioLib/src/TypeDef.h +++ b/lib/RadioLib/src/TypeDef.h @@ -563,6 +563,28 @@ */ #define RADIOLIB_LORAWAN_NO_DOWNLINK (-1116) +// LR11x0-specific status codes + +/*! + \brief The selected 802.11 WiFi type is invalid. +*/ +#define RADIOLIB_ERR_INVALID_WIFI_TYPE (-1200) + +/*! + \} +*/ + +/*! + \defgroup typedefs Type aliases used by RadioLib. + + \{ +*/ + +/*! + \brief Type used for durations in RadioLib +*/ +typedef unsigned long RadioLibTime_t; + /*! \} */ diff --git a/lib/RadioLib/src/modules/CC1101/CC1101.cpp b/lib/RadioLib/src/modules/CC1101/CC1101.cpp index 16bd25e..09e07b2 100644 --- a/lib/RadioLib/src/modules/CC1101/CC1101.cpp +++ b/lib/RadioLib/src/modules/CC1101/CC1101.cpp @@ -8,8 +8,8 @@ CC1101::CC1101(Module* module) : PhysicalLayer(RADIOLIB_CC1101_FREQUENCY_STEP_SI int16_t CC1101::begin(float freq, float br, float freqDev, float rxBw, int8_t pwr, uint8_t preambleLength) { // set module properties - this->mod->SPIreadCommand = RADIOLIB_CC1101_CMD_READ; - this->mod->SPIwriteCommand = RADIOLIB_CC1101_CMD_WRITE; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_CC1101_CMD_READ; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_CC1101_CMD_WRITE; this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); @@ -100,29 +100,29 @@ void CC1101::reset() { int16_t CC1101::transmit(uint8_t* data, size_t len, uint8_t addr) { // calculate timeout (5ms + 500 % of expected time-on-air) - uint32_t timeout = 5000000 + (uint32_t)((((float)(len * 8)) / (this->bitRate * 1000.0)) * 5000000.0); + RadioLibTime_t timeout = 5 + (RadioLibTime_t)((((float)(len * 8)) / this->bitRate) * 5); // start transmission int16_t state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for transmission start or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getGpio())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } } // wait for transmission end or timeout - start = this->mod->hal->micros(); + start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getGpio())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } @@ -133,18 +133,18 @@ int16_t CC1101::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t CC1101::receive(uint8_t* data, size_t len) { // calculate timeout (500 ms + 400 full max-length packets at current bit rate) - uint32_t timeout = 500000 + (1.0/(this->bitRate*1000.0))*(RADIOLIB_CC1101_MAX_PACKET_LENGTH*400.0); + RadioLibTime_t timeout = 500 + (1.0/(this->bitRate))*(RADIOLIB_CC1101_MAX_PACKET_LENGTH*400.0); // start reception int16_t state = startReceive(); RADIOLIB_ASSERT(state); // wait for packet start or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { standby(); SPIsendCommand(RADIOLIB_CC1101_CMD_FLUSH_RX); return(RADIOLIB_ERR_RX_TIMEOUT); @@ -152,11 +152,11 @@ int16_t CC1101::receive(uint8_t* data, size_t len) { } // wait for packet end or timeout - start = this->mod->hal->micros(); + start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { standby(); SPIsendCommand(RADIOLIB_CC1101_CMD_FLUSH_RX); return(RADIOLIB_ERR_RX_TIMEOUT); @@ -172,7 +172,7 @@ int16_t CC1101::standby() { SPIsendCommand(RADIOLIB_CC1101_CMD_IDLE); // wait until idle is reached - uint32_t start = this->mod->hal->millis(); + RadioLibTime_t start = this->mod->hal->millis(); while(SPIgetRegValue(RADIOLIB_CC1101_REG_MARCSTATE, 4, 0) != RADIOLIB_CC1101_MARC_STATE_IDLE) { mod->hal->yield(); if(this->mod->hal->millis() - start > 100) { @@ -361,7 +361,7 @@ int16_t CC1101::startReceive() { return(state); } -int16_t CC1101::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t CC1101::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -489,6 +489,24 @@ int16_t CC1101::setRxBandwidth(float rxBw) { return(RADIOLIB_ERR_INVALID_RX_BANDWIDTH); } +int16_t CC1101::autoSetRxBandwidth() { + // Uncertainty ~ +/- 40ppm for a cheap CC1101 + // Uncertainty * 2 for both transmitter and receiver + float uncertainty = ((this->frequency) * 40 * 2); + uncertainty = (uncertainty/1000); //Since bitrate is in kBit + float minbw = ((this->bitRate) + uncertainty); + + int possibles[16] = {58, 68, 81, 102, 116, 135, 162, 203, 232, 270, 325, 406, 464, 541, 650, 812}; + + for (int i = 0; i < 16; i++) { + if (possibles[i] > minbw) { + int16_t state = setRxBandwidth(possibles[i]); + return(state); + } + } + return(RADIOLIB_ERR_UNKNOWN); + } + int16_t CC1101::setFrequencyDeviation(float freqDev) { // set frequency deviation to lowest available setting (required for digimodes) float newFreqDev = freqDev; @@ -542,6 +560,62 @@ int16_t CC1101::getFrequencyDeviation(float *freqDev) { } int16_t CC1101::setOutputPower(int8_t pwr) { + // check if power value is configurable + uint8_t powerRaw = 0; + int16_t state = checkOutputPower(pwr, NULL, &powerRaw); + RADIOLIB_ASSERT(state); + + // store the value + this->power = pwr; + + if(this->modulation == RADIOLIB_CC1101_MOD_FORMAT_ASK_OOK){ + // Amplitude modulation: + // PA_TABLE[0] is the power to be used when transmitting a 0 (no power) + // PA_TABLE[1] is the power to be used when transmitting a 1 (full power) + + uint8_t paValues[2] = {0x00, powerRaw}; + SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_PATABLE, paValues, 2); + return(RADIOLIB_ERR_NONE); + + } else { + // Freq modulation: + // PA_TABLE[0] is the power to be used when transmitting. + return(SPIsetRegValue(RADIOLIB_CC1101_REG_PATABLE, powerRaw)); + } +} + +int16_t CC1101::checkOutputPower(int8_t power, int8_t* clipped) { + return(checkOutputPower(power, clipped, NULL)); +} + +int16_t CC1101::checkOutputPower(int8_t power, int8_t* clipped, uint8_t* raw) { + constexpr int8_t allowedPwrs[8] = { -30, -20, -15, -10, 0, 5, 7, 10 }; + + if(clipped) { + if(power <= -30) { + *clipped = -30; + } else if(power >= 10) { + *clipped = 10; + } else { + for(int i = 0; i < 8; i++) { + if(allowedPwrs[i] > power) { + break; + } + *clipped = allowedPwrs[i]; + } + } + } + + // if just a check occurs (and not requesting the raw power value), return now + if(!raw) { + for(int i = 0; i < 8; i++) { + if(allowedPwrs[i] == power) { + return(RADIOLIB_ERR_NONE); + } + } + return(RADIOLIB_ERR_INVALID_OUTPUT_POWER); + } + // round to the known frequency settings uint8_t f; if(this->frequency < 374.0) { @@ -568,53 +642,35 @@ int16_t CC1101::setOutputPower(int8_t pwr) { {0xCB, 0xC8, 0xCB, 0xC7}, {0xC2, 0xC0, 0xC2, 0xC0}}; - uint8_t powerRaw; - switch(pwr) { - case -30: - powerRaw = paTable[0][f]; + switch(power) { + case allowedPwrs[0]: // -30 + *raw = paTable[0][f]; break; - case -20: - powerRaw = paTable[1][f]; + case allowedPwrs[1]: // -20 + *raw = paTable[1][f]; break; - case -15: - powerRaw = paTable[2][f]; + case allowedPwrs[2]: // -15 + *raw = paTable[2][f]; break; - case -10: - powerRaw = paTable[3][f]; + case allowedPwrs[3]: // -10 + *raw = paTable[3][f]; break; - case 0: - powerRaw = paTable[4][f]; + case allowedPwrs[4]: // 0 + *raw = paTable[4][f]; break; - case 5: - powerRaw = paTable[5][f]; + case allowedPwrs[5]: // 5 + *raw = paTable[5][f]; break; - case 7: - powerRaw = paTable[6][f]; + case allowedPwrs[6]: // 7 + *raw = paTable[6][f]; break; - case 10: - powerRaw = paTable[7][f]; + case allowedPwrs[7]: // 10 + *raw = paTable[7][f]; break; default: return(RADIOLIB_ERR_INVALID_OUTPUT_POWER); } - - // store the value - this->power = pwr; - - if(this->modulation == RADIOLIB_CC1101_MOD_FORMAT_ASK_OOK){ - // Amplitude modulation: - // PA_TABLE[0] is the power to be used when transmitting a 0 (no power) - // PA_TABLE[1] is the power to be used when transmitting a 1 (full power) - - uint8_t paValues[2] = {0x00, powerRaw}; - SPIwriteRegisterBurst(RADIOLIB_CC1101_REG_PATABLE, paValues, 2); - return(RADIOLIB_ERR_NONE); - - } else { - // Freq modulation: - // PA_TABLE[0] is the power to be used when transmitting. - return(SPIsetRegValue(RADIOLIB_CC1101_REG_PATABLE, powerRaw)); - } + return(RADIOLIB_ERR_NONE); } int16_t CC1101::setSyncWord(uint8_t* syncWord, uint8_t len, uint8_t maxErrBits, bool requireCarrierSense) { @@ -740,7 +796,7 @@ int16_t CC1101::setOOK(bool enableOOK) { float CC1101::getRSSI() { float rssi; - if (this->directModeEnabled) { + if(!this->directModeEnabled) { if(this->rawRSSI >= 128) { rssi = (((float)this->rawRSSI - 256.0)/2.0) - 74.0; } else { @@ -748,12 +804,9 @@ float CC1101::getRSSI() { } } else { uint8_t rawRssi = SPIreadRegister(RADIOLIB_CC1101_REG_RSSI); - if (rawRssi >= 128) - { + if(rawRssi >= 128) { rssi = ((rawRssi - 256) / 2) - 74; - } - else - { + } else { rssi = (rawRssi / 2) - 74; } } diff --git a/lib/RadioLib/src/modules/CC1101/CC1101.h b/lib/RadioLib/src/modules/CC1101/CC1101.h index bb97911..e61c5fc 100644 --- a/lib/RadioLib/src/modules/CC1101/CC1101.h +++ b/lib/RadioLib/src/modules/CC1101/CC1101.h @@ -537,8 +537,9 @@ class CC1101: public PhysicalLayer { /*! \brief Default constructor. - \param mod Instance of Module that will be used to communicate with the radio. + \param module Instance of Module that will be used to communicate with the radio. */ + // cppcheck-suppress noExplicitConstructor CC1101(Module* module); // basic methods @@ -660,23 +661,23 @@ class CC1101: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Interrupt-driven binary transmit method. @@ -698,7 +699,7 @@ class CC1101: public PhysicalLayer { \brief Interrupt-driven receive method. GDO0 will be activated when full packet is received. \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. @@ -708,7 +709,7 @@ class CC1101: public PhysicalLayer { \param len Ignored. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; /*! \brief Reads data received after calling startReceive method. When the packet length is not known in advance, @@ -728,14 +729,14 @@ class CC1101: public PhysicalLayer { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets bit rate. Allowed values range from 0.025 to 600.0 kbps. \param br Bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Sets receiver bandwidth. Allowed values are 58, 68, 81, 102, 116, 135, 162, @@ -745,6 +746,14 @@ class CC1101: public PhysicalLayer { */ int16_t setRxBandwidth(float rxBw); + /*! + \brief calculates and sets Rx bandwidth based on the freq, baud and freq uncertainty. + Reimplement of atlas0fd00m's (RfCat) CalculatePktChanBw function. + Modified for worse ppm with the CC1101, and adjusted for the supportted CC1101 bw. + \returns \ref status_codes + */ + int16_t autoSetRxBandwidth(); + /*! \brief Sets frequency deviation. Allowed values range from 1.587 to 380.8 kHz. \param freqDev Frequency deviation to be set in kHz. @@ -764,7 +773,25 @@ class CC1101: public PhysicalLayer { \param pwr Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t pwr); + int16_t setOutputPower(int8_t pwr) override; + + /*! + \brief Check if output power is configurable. + This method is needed for compatibility with PhysicalLayer::checkOutputPower. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \param raw Raw internal value. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped, uint8_t* raw); /*! \brief Sets 16-bit sync word as a two byte value. @@ -789,6 +816,7 @@ class CC1101: public PhysicalLayer { /*! \brief Sets preamble length. \param preambleLength Preamble length to be set (in bits), allowed values: 16, 24, 32, 48, 64, 96, 128 and 192. + \param qualityThreshold Preamble quality threshold (PQT) to set. \returns \ref status_codes */ int16_t setPreambleLength(uint8_t preambleLength, uint8_t qualityThreshold); @@ -820,58 +848,58 @@ class CC1101: public PhysicalLayer { In asynchronous direct mode, returns the current RSSI level. \returns RSSI in dBm. */ - float getRSSI(); + float getRSSI() override; /*! \brief Gets LQI (Link Quality Indicator) of the last received packet. \returns Last packet LQI (lower is better). */ - uint8_t getLQI() const; + uint8_t getLQI() const; - /*! + /*! \brief Query modem for the packet length of received payload. \param update Update received packet length. Will return cached value when set to false. \returns Length of last received packet in bytes. */ size_t getPacketLength(bool update = true) override; - /*! + /*! \brief Set modem in fixed packet length mode. \param len Packet length. \returns \ref status_codes */ int16_t fixedPacketLengthMode(uint8_t len = RADIOLIB_CC1101_MAX_PACKET_LENGTH); - /*! + /*! \brief Set modem in variable packet length mode. - \param len Maximum packet length. + \param maxLen Maximum packet length. \returns \ref status_codes */ int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_CC1101_MAX_PACKET_LENGTH); - /*! + /*! \brief Enable sync word filtering and generation. - \param numBits Sync word length in bits. + \param maxErrBits Maximum number of allowed error bits in sync word. \param requireCarrierSense Require carrier sense above threshold in addition to sync word. \returns \ref status_codes */ int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0, bool requireCarrierSense = false); - /*! + /*! \brief Disable preamble and sync word filtering and generation. \param requireCarrierSense Require carrier sense above threshold. \returns \ref status_codes */ int16_t disableSyncWordFiltering(bool requireCarrierSense = false); - /*! + /*! \brief Enable CRC filtering and generation. \param enable Set or unset CRC generation and filtering. \returns \ref status_codes */ int16_t setCrcFiltering(bool enable = true); - /*! + /*! \brief Set modem in "sniff" mode: no packet filtering (e.g., no preamble, sync word, address, CRC). \param enable Set or unset promiscuous mode. \param requireCarrierSense Set carriersense required above threshold, defaults to false. @@ -879,7 +907,7 @@ class CC1101: public PhysicalLayer { */ int16_t setPromiscuousMode(bool enable = true, bool requireCarrierSense = false); - /*! + /*! \brief Get whether the modem is in promiscuous mode: no packet filtering (e.g., no preamble, sync word, address, CRC). \returns Whether the modem is in promiscuous mode. @@ -909,16 +937,16 @@ class CC1101: public PhysicalLayer { void setRfSwitchTable(const uint32_t (&pins)[Module::RFSWITCH_MAX_PINS], const Module::RfSwitchMode_t table[]); /*! - \brief Get one truly random byte from RSSI noise. - \returns TRNG byte. - */ - uint8_t randomByte(); + \brief Get one truly random byte from RSSI noise. + \returns TRNG byte. + */ + uint8_t randomByte() override; /*! - \brief Read version SPI register. Should return CC1101_VERSION_LEGACY (0x04) or - CC1101_VERSION_CURRENT (0x14) if CC1101 is connected and working. - \returns Version register contents or \ref status_codes - */ + \brief Read version SPI register. Should return CC1101_VERSION_LEGACY (0x04) or + CC1101_VERSION_CURRENT (0x14) if CC1101 is connected and working. + \returns Version register contents or \ref status_codes + */ int16_t getChipVersion(); #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE @@ -926,13 +954,13 @@ class CC1101: public PhysicalLayer { \brief Set interrupt service routine function to call when data bit is receveid in direct mode. \param func Pointer to interrupt service routine. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Function to read and process data bit in direct reception mode. \param pin Pin on which to read. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif /*! @@ -941,12 +969,12 @@ class CC1101: public PhysicalLayer { \param value The value that indicates which function to place on that pin. See chip datasheet for details. \returns \ref status_codes */ - int16_t setDIOMapping(uint32_t pin, uint32_t value); + int16_t setDIOMapping(uint32_t pin, uint32_t value) override; #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; // SPI read overrides to set bit for burst write and status registers access int16_t SPIgetRegValue(uint8_t reg, uint8_t msb = 7, uint8_t lsb = 0); diff --git a/lib/RadioLib/src/modules/LLCC68/LLCC68.h b/lib/RadioLib/src/modules/LLCC68/LLCC68.h index 3a73706..08c52af 100644 --- a/lib/RadioLib/src/modules/LLCC68/LLCC68.h +++ b/lib/RadioLib/src/modules/LLCC68/LLCC68.h @@ -21,7 +21,7 @@ class LLCC68: public SX1262 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - LLCC68(Module* mod); + LLCC68(Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Initialization method for LoRa modem. diff --git a/lib/RadioLib/src/modules/LR11x0/LR1110.cpp b/lib/RadioLib/src/modules/LR11x0/LR1110.cpp new file mode 100644 index 0000000..39f0052 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1110.cpp @@ -0,0 +1,55 @@ +#include "LR1110.h" +#if !RADIOLIB_EXCLUDE_LR11X0 + +LR1110::LR1110(Module* mod) : LR11x0(mod) { + chipType = RADIOLIB_LR11X0_HW_LR1110; +} + +int16_t LR1110::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::begin(bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1110::beginGFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::beginGFSK(br, freqDev, rxBw, power, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1110::beginLRFHSS(float freq, uint8_t bw, uint8_t cr, int8_t power, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::beginLRFHSS(bw, cr, power, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1110::setFrequency(float freq) { + return(this->setFrequency(freq, true)); +} + +int16_t LR1110::setFrequency(float freq, bool calibrate, float band) { + RADIOLIB_CHECK_RANGE(freq, 150.0, 960.0, RADIOLIB_ERR_INVALID_FREQUENCY); + + // calibrate image rejection + if(calibrate) { + int16_t state = LR11x0::calibImage(freq - band, freq + band); + RADIOLIB_ASSERT(state); + } + + // set frequency + return(LR11x0::setRfFrequency((uint32_t)(freq*1000000.0f))); +} + +#endif \ No newline at end of file diff --git a/lib/RadioLib/src/modules/LR11x0/LR1110.h b/lib/RadioLib/src/modules/LR11x0/LR1110.h new file mode 100644 index 0000000..60040ed --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1110.h @@ -0,0 +1,98 @@ +#if !defined(_RADIOLIB_LR1110_H) +#define _RADIOLIB_LR1110_H + +#include "../../TypeDef.h" + +#if !RADIOLIB_EXCLUDE_LR11X0 + +#include "../../Module.h" +#include "LR11x0.h" + +/*! + \class LR1110 + \brief Derived class for %LR1110 modules. +*/ +class LR1110: public LR11x0 { + public: + /*! + \brief Default constructor. + \param mod Instance of Module that will be used to communicate with the radio. + */ + LR1110(Module* mod); // cppcheck-suppress noExplicitConstructor + + // basic methods + + /*! + \brief Initialization method for LoRa modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param bw LoRa bandwidth in kHz. Defaults to 125.0 kHz. + \param sf LoRa spreading factor. Defaults to 9. + \param cr LoRa coding rate denominator. Defaults to 7 (coding rate 4/7). + \param syncWord 1-byte LoRa sync word. Defaults to RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE (0x12). + \param power Output power in dBm. Defaults to 10 dBm. + \param preambleLength LoRa preamble length in symbols. Defaults to 8 symbols. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, int8_t power = 10, uint16_t preambleLength = 8, float tcxoVoltage = 1.6); + + /*! + \brief Initialization method for FSK modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param br FSK bit rate in kbps. Defaults to 4.8 kbps. + \param freqDev Frequency deviation from carrier frequency in kHz. Defaults to 5.0 kHz. + \param rxBw Receiver bandwidth in kHz. Defaults to 156.2 kHz. + \param power Output power in dBm. Defaults to 10 dBm. + \param preambleLength FSK preamble length in bits. Defaults to 16 bits. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t beginGFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6); + + /*! + \brief Initialization method for LR-FHSS modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. Defaults to 722.66 kHz. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. Defaults to 2/3 coding rate. + \param power Output power in dBm. Defaults to 10 dBm. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t beginLRFHSS(float freq = 434.0, uint8_t bw = RADIOLIB_LR11X0_LR_FHSS_BW_722_66, uint8_t cr = RADIOLIB_LR11X0_LR_FHSS_CR_2_3, int8_t power = 10, float tcxoVoltage = 1.6); + + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz. + Will also perform calibrations. + \param freq Carrier frequency to be set in MHz. + \returns \ref status_codes + */ + int16_t setFrequency(float freq) override; + + /*! + \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz. + \param freq Carrier frequency to be set in MHz. + \param calibrate Run image calibration. + \param band Half bandwidth for image calibration. For example, + if carrier is 434 MHz and band is set to 4 MHz, then the image will be calibrate + for band 430 - 438 MHz. Unused if calibrate is set to false, defaults to 4 MHz + \returns \ref status_codes + */ + int16_t setFrequency(float freq, bool calibrate, float band = 4); + +#if !RADIOLIB_GODMODE + private: +#endif + +}; + +#endif + +#endif diff --git a/lib/RadioLib/src/modules/LR11x0/LR1120.cpp b/lib/RadioLib/src/modules/LR11x0/LR1120.cpp new file mode 100644 index 0000000..b3c0c88 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1120.cpp @@ -0,0 +1,59 @@ +#include "LR1120.h" +#if !RADIOLIB_EXCLUDE_LR11X0 + +LR1120::LR1120(Module* mod) : LR11x0(mod) { + chipType = RADIOLIB_LR11X0_HW_LR1120; +} + +int16_t LR1120::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::begin(bw, sf, cr, syncWord, power, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1120::beginGFSK(float freq, float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::beginGFSK(br, freqDev, rxBw, power, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1120::beginLRFHSS(float freq, uint8_t bw, uint8_t cr, int8_t power, float tcxoVoltage) { + // execute common part + int16_t state = LR11x0::beginLRFHSS(bw, cr, power, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + return(state); +} + +int16_t LR1120::setFrequency(float freq) { + return(this->setFrequency(freq, true)); +} + +int16_t LR1120::setFrequency(float freq, bool calibrate, float band) { + if(!(((freq >= 150.0) && (freq <= 960.0)) || + ((freq >= 1900.0) && (freq <= 2200.0)) || + ((freq >= 2400.0) && (freq <= 2500.0)))) { + return(RADIOLIB_ERR_INVALID_FREQUENCY); + } + + // calibrate image rejection + if(calibrate) { + int16_t state = LR11x0::calibImage(freq - band, freq + band); + RADIOLIB_ASSERT(state); + } + + // set frequency + return(LR11x0::setRfFrequency((uint32_t)(freq*1000000.0f))); +} + +#endif \ No newline at end of file diff --git a/lib/RadioLib/src/modules/LR11x0/LR1120.h b/lib/RadioLib/src/modules/LR11x0/LR1120.h new file mode 100644 index 0000000..62045b0 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1120.h @@ -0,0 +1,99 @@ +#if !defined(_RADIOLIB_LR1120_H) +#define _RADIOLIB_LR1120_H + +#include "../../TypeDef.h" + +#if !RADIOLIB_EXCLUDE_LR11X0 + +#include "../../Module.h" +#include "LR11x0.h" + +/*! + \class LR1120 + \brief Derived class for %LR1120 modules. +*/ +class LR1120: public LR11x0 { + public: + /*! + \brief Default constructor. + \param mod Instance of Module that will be used to communicate with the radio. + */ + LR1120(Module* mod); // cppcheck-suppress noExplicitConstructor + + // basic methods + + /*! + \brief Initialization method for LoRa modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param bw LoRa bandwidth in kHz. Defaults to 125.0 kHz. + \param sf LoRa spreading factor. Defaults to 9. + \param cr LoRa coding rate denominator. Defaults to 7 (coding rate 4/7). + \param syncWord 1-byte LoRa sync word. Defaults to RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE (0x12). + \param power Output power in dBm. Defaults to 10 dBm. + \param preambleLength LoRa preamble length in symbols. Defaults to 8 symbols. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t begin(float freq = 434.0, float bw = 125.0, uint8_t sf = 9, uint8_t cr = 7, uint8_t syncWord = RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE, int8_t power = 10, uint16_t preambleLength = 8, float tcxoVoltage = 1.6); + + /*! + \brief Initialization method for FSK modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param br FSK bit rate in kbps. Defaults to 4.8 kbps. + \param freqDev Frequency deviation from carrier frequency in kHz. Defaults to 5.0 kHz. + \param rxBw Receiver bandwidth in kHz. Defaults to 156.2 kHz. + \param power Output power in dBm. Defaults to 10 dBm. + \param preambleLength FSK preamble length in bits. Defaults to 16 bits. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t beginGFSK(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 156.2, int8_t power = 10, uint16_t preambleLength = 16, float tcxoVoltage = 1.6); + + /*! + \brief Initialization method for LR-FHSS modem. + \param freq Carrier frequency in MHz. Defaults to 434.0 MHz. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. Defaults to 722.66 kHz. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. Defaults to 2/3 coding rate. + \param power Output power in dBm. Defaults to 10 dBm. + \param tcxoVoltage TCXO reference voltage to be set. Defaults to 1.6 V. + If you are seeing -706/-707 error codes, it likely means you are using non-0 value for module with XTAL. + To use XTAL, either set this value to 0, or set LR11x0::XTAL to true. + \returns \ref status_codes + */ + int16_t beginLRFHSS(float freq = 434.0, uint8_t bw = RADIOLIB_LR11X0_LR_FHSS_BW_722_66, uint8_t cr = RADIOLIB_LR11X0_LR_FHSS_CR_2_3, int8_t power = 10, float tcxoVoltage = 1.6); + + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz, + 1900 - 2200 MHz and 2400 - 2500 MHz. Will also perform calibrations. + \param freq Carrier frequency to be set in MHz. + \returns \ref status_codes + */ + int16_t setFrequency(float freq) override; + + /*! + \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz, + 1900 - 2200 MHz and 2400 - 2500 MHz. Will also perform calibrations. + \param freq Carrier frequency to be set in MHz. + \param calibrate Run image calibration. + \param band Half bandwidth for image calibration. For example, + if carrier is 434 MHz and band is set to 4 MHz, then the image will be calibrate + for band 430 - 438 MHz. Unused if calibrate is set to false, defaults to 4 MHz + \returns \ref status_codes + */ + int16_t setFrequency(float freq, bool calibrate, float band = 4); + +#if !RADIOLIB_GODMODE + private: +#endif + +}; + +#endif + +#endif diff --git a/lib/RadioLib/src/modules/LR11x0/LR1121.cpp b/lib/RadioLib/src/modules/LR11x0/LR1121.cpp new file mode 100644 index 0000000..14e7292 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1121.cpp @@ -0,0 +1,8 @@ +#include "LR1121.h" +#if !RADIOLIB_EXCLUDE_LR11X0 + +LR1121::LR1121(Module* mod) : LR1120(mod) { + chipType = RADIOLIB_LR11X0_HW_LR1121; +} + +#endif \ No newline at end of file diff --git a/lib/RadioLib/src/modules/LR11x0/LR1121.h b/lib/RadioLib/src/modules/LR11x0/LR1121.h new file mode 100644 index 0000000..2dc0242 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR1121.h @@ -0,0 +1,35 @@ +#if !defined(_RADIOLIB_LR1121_H) +#define _RADIOLIB_LR1121_H + +#include "../../TypeDef.h" + +#if !RADIOLIB_EXCLUDE_LR11X0 + +#include "../../Module.h" +#include "LR11x0.h" +#include "LR1120.h" + +/*! + \class LR1121 + \brief Derived class for %LR1121 modules. +*/ +class LR1121: public LR1120 { + public: + /*! + \brief Default constructor. + \param mod Instance of Module that will be used to communicate with the radio. + */ + LR1121(Module* mod); // cppcheck-suppress noExplicitConstructor + + // TODO this is where overrides to disable GNSS+WiFi scanning methods on LR1121 + // will be put once those are implemented + +#if !RADIOLIB_GODMODE + private: +#endif + +}; + +#endif + +#endif diff --git a/lib/RadioLib/src/modules/LR11x0/LR11x0.cpp b/lib/RadioLib/src/modules/LR11x0/LR11x0.cpp new file mode 100644 index 0000000..9c4c230 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR11x0.cpp @@ -0,0 +1,3112 @@ +#include "LR11x0.h" + +#include "../../utils/CRC.h" +#include "../../utils/Cryptography.h" + +#include +#include + +#if !RADIOLIB_EXCLUDE_LR11X0 + +LR11x0::LR11x0(Module* mod) : PhysicalLayer(RADIOLIB_LR11X0_FREQUENCY_STEP_SIZE, RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { + this->mod = mod; + this->XTAL = false; +} + +int16_t LR11x0::begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LORA); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setBandwidth(bw); + RADIOLIB_ASSERT(state); + + state = setSpreadingFactor(sf); + RADIOLIB_ASSERT(state); + + state = setCodingRate(cr); + RADIOLIB_ASSERT(state); + + state = setSyncWord(syncWord); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + state = setPreambleLength(preambleLength); + RADIOLIB_ASSERT(state); + + // set publicly accessible settings that are not a part of begin method + state = setCRC(2); + RADIOLIB_ASSERT(state); + + state = invertIQ(false); + RADIOLIB_ASSERT(state); + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::beginGFSK(float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, float tcxoVoltage) { + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_GFSK); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setBitRate(br); + RADIOLIB_ASSERT(state); + + state = setFrequencyDeviation(freqDev); + RADIOLIB_ASSERT(state); + + state = setRxBandwidth(rxBw); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + state = setPreambleLength(preambleLength); + RADIOLIB_ASSERT(state); + + // set publicly accessible settings that are not a part of begin method + uint8_t sync[] = { 0x12, 0xAD }; + state = setSyncWord(sync, 2); + RADIOLIB_ASSERT(state); + + state = setDataShaping(RADIOLIB_SHAPING_NONE); + RADIOLIB_ASSERT(state); + + state = setEncoding(RADIOLIB_ENCODING_NRZ); + RADIOLIB_ASSERT(state); + + state = variablePacketLengthMode(RADIOLIB_LR11X0_MAX_PACKET_LENGTH); + RADIOLIB_ASSERT(state); + + state = setCRC(2); + RADIOLIB_ASSERT(state); + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::beginLRFHSS(uint8_t bw, uint8_t cr, int8_t power, float tcxoVoltage) { + // set module properties and perform initial setup + int16_t state = this->modSetup(tcxoVoltage, RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setLrFhssConfig(bw, cr); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + state = setSyncWord(0x12AD101B); + RADIOLIB_ASSERT(state); + + // set fixed configuration + return(setModulationParamsLrFhss(RADIOLIB_LR11X0_LR_FHSS_BIT_RATE_RAW, RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0)); +} + +int16_t LR11x0::reset() { + // run the reset sequence + this->mod->hal->pinMode(this->mod->getRst(), this->mod->hal->GpioModeOutput); + this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelLow); + this->mod->hal->delay(10); + this->mod->hal->digitalWrite(this->mod->getRst(), this->mod->hal->GpioLevelHigh); + + // the typical transition duration should be 273 ms + this->mod->hal->delay(300); + + // wait for BUSY to go low + RadioLibTime_t start = this->mod->hal->millis(); + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + if(this->mod->hal->millis() - start >= 3000) { + RADIOLIB_DEBUG_BASIC_PRINTLN("BUSY pin timeout after reset!"); + return(RADIOLIB_ERR_SPI_CMD_TIMEOUT); + } + } + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::transmit(uint8_t* data, size_t len, uint8_t addr) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // check packet length + if(len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // get currently active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + RadioLibTime_t timeout = getTimeOnAir(len); + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // calculate timeout (150% of expected time-on-air) + timeout = (timeout * 3) / 2; + + } else if((modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) || (modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { + // calculate timeout (500% of expected time-on-air) + timeout = timeout * 5; + + } else { + return(RADIOLIB_ERR_UNKNOWN); + } + + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); + + // start transmission + state = startTransmit(data, len, addr); + RADIOLIB_ASSERT(state); + + // wait for packet transmission or timeout + RadioLibTime_t start = this->mod->hal->micros(); + while(!this->mod->hal->digitalRead(this->mod->getIrq())) { + this->mod->hal->yield(); + if(this->mod->hal->micros() - start > timeout) { + finishTransmit(); + return(RADIOLIB_ERR_TX_TIMEOUT); + } + } + RadioLibTime_t elapsed = this->mod->hal->micros() - start; + + // update data rate + this->dataRateMeasured = (len*8.0)/((float)elapsed/1000000.0); + + return(finishTransmit()); +} + +int16_t LR11x0::receive(uint8_t* data, size_t len) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + RadioLibTime_t timeout = 0; + + // get currently active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // calculate timeout (100 LoRa symbols, the default for SX127x series) + float symbolLength = (float)(uint32_t(1) << this->spreadingFactor) / (float)this->bandwidthKhz; + timeout = (RadioLibTime_t)(symbolLength * 100.0); + + } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + // calculate timeout (500 % of expected time-one-air) + size_t maxLen = len; + if(len == 0) { + maxLen = 0xFF; + } + float brBps = ((float)(RADIOLIB_LR11X0_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)this->bitRate; + timeout = (RadioLibTime_t)(((maxLen * 8.0) / brBps) * 1000.0 * 5.0); + + } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + size_t maxLen = len; + if(len == 0) { + maxLen = 0xFF; + } + timeout = (RadioLibTime_t)(((maxLen * 8.0) / (RADIOLIB_LR11X0_LR_FHSS_BIT_RATE)) * 1000.0 * 5.0); + + } else { + return(RADIOLIB_ERR_UNKNOWN); + + } + + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); + + // start reception + uint32_t timeoutValue = (uint32_t)(((float)timeout * 1000.0) / 30.52); + state = startReceive(timeoutValue); + RADIOLIB_ASSERT(state); + + // wait for packet reception or timeout + bool softTimeout = false; + RadioLibTime_t start = this->mod->hal->millis(); + while(!this->mod->hal->digitalRead(this->mod->getIrq())) { + this->mod->hal->yield(); + // safety check, the timeout should be done by the radio + if(this->mod->hal->millis() - start > timeout) { + softTimeout = true; + break; + } + } + + // if it was a timeout, this will return an error code + // TODO taken from SX126x, does this really work? + state = standby(); + if((state != RADIOLIB_ERR_NONE) && (state != RADIOLIB_ERR_SPI_CMD_TIMEOUT)) { + return(state); + } + + // check whether this was a timeout or not + if((getIrqStatus() & RADIOLIB_LR11X0_IRQ_TIMEOUT) || softTimeout) { + standby(); + clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + return(RADIOLIB_ERR_RX_TIMEOUT); + } + + // read the received data + return(readData(data, len)); +} + +int16_t LR11x0::transmitDirect(uint32_t frf) { + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_TX); + + // user requested to start transmitting immediately (required for RTTY) + int16_t state = RADIOLIB_ERR_NONE; + if(frf != 0) { + state = setRfFrequency(frf); + } + RADIOLIB_ASSERT(state); + + // start transmitting + return(setTxCw()); +} + +int16_t LR11x0::receiveDirect() { + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_RX); + + // LR11x0 is unable to output received data directly + return(RADIOLIB_ERR_UNKNOWN); +} + +int16_t LR11x0::scanChannel() { + return(this->scanChannel(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT)); +} + +int16_t LR11x0::scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { + // set mode to CAD + int state = startChannelScan(symbolNum, detPeak, detMin); + RADIOLIB_ASSERT(state); + + // wait for channel activity detected or timeout + while(!this->mod->hal->digitalRead(this->mod->getIrq())) { + this->mod->hal->yield(); + } + + // check CAD result + return(getChannelScanResult()); +} + +int16_t LR11x0::standby() { + return(LR11x0::standby(RADIOLIB_LR11X0_STANDBY_RC)); +} + +int16_t LR11x0::standby(uint8_t mode, bool wakeup) { + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_IDLE); + + if(wakeup) { + // pull NSS low for a while to wake up + this->mod->hal->digitalWrite(this->mod->getCs(), this->mod->hal->GpioLevelLow); + this->mod->hal->delay(1); + this->mod->hal->digitalWrite(this->mod->getCs(), this->mod->hal->GpioLevelHigh); + } + + uint8_t buff[] = { mode }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_STANDBY, true, buff, 1)); +} + +int16_t LR11x0::sleep(bool retainConfig, uint32_t sleepTime) { + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_IDLE); + + uint8_t buff[] = { + (uint8_t)retainConfig, + (uint8_t)((sleepTime >> 24) & 0xFF), (uint8_t)((sleepTime >> 16) & 0xFF), + (uint8_t)((sleepTime >> 16) & 0xFF), (uint8_t)(sleepTime & 0xFF), + }; + if(sleepTime) { + buff[0] |= RADIOLIB_LR11X0_SLEEP_WAKEUP_ENABLED; + } + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_SLEEP, true, buff, sizeof(buff)); + + // wait for the module to safely enter sleep mode + this->mod->hal->delay(1); + + return(state); +} + +void LR11x0::setIrqAction(void (*func)(void)) { + this->mod->hal->attachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq()), func, this->mod->hal->GpioInterruptRising); +} + +void LR11x0::clearIrqAction() { + this->mod->hal->detachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq())); +} + +void LR11x0::setPacketReceivedAction(void (*func)(void)) { + this->setIrqAction(func); +} + +void LR11x0::clearPacketReceivedAction() { + this->clearIrqAction(); +} + +void LR11x0::setPacketSentAction(void (*func)(void)) { + this->setIrqAction(func); +} + +void LR11x0::clearPacketSentAction() { + this->clearIrqAction(); +} + +int16_t LR11x0::startTransmit(uint8_t* data, size_t len, uint8_t addr) { + // suppress unused variable warning + (void)addr; + + // check packet length + if(len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // maximum packet length is decreased by 1 when address filtering is active + if((this->addrComp != RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED) && (len > RADIOLIB_LR11X0_MAX_PACKET_LENGTH - 1)) { + return(RADIOLIB_ERR_PACKET_TOO_LONG); + } + + // set packet Length + int16_t state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, len, this->crcTypeLoRa, this->invertIQEnabled); + + } else if(modem == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, len, this->crcTypeGFSK, this->whitening); + + } else if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(RADIOLIB_ERR_UNKNOWN); + + } + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_TX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT, 0); + RADIOLIB_ASSERT(state); + + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + // in LR-FHSS mode, the packet is built by the device + // TODO add configurable grid step and device offset + state = lrFhssBuildFrame(this->lrFhssHdrCount, this->lrFhssCr, RADIOLIB_LR11X0_LR_FHSS_GRID_STEP_FCC, true, this->lrFhssBw, this->lrFhssHopSeq, 0, data, len); + RADIOLIB_ASSERT(state); + + } else { + // write packet to buffer + state = writeBuffer8(data, len); + RADIOLIB_ASSERT(state); + + } + + // clear interrupt flags + state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_TX); + + // start transmission + state = setTx(RADIOLIB_LR11X0_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + // wait for BUSY to go low (= PA ramp up done) + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + } + + return(state); +} + +int16_t LR11x0::finishTransmit() { + // clear interrupt flags + clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + + // set mode to standby to disable transmitter/RF switch + return(standby()); +} + +int16_t LR11x0::startReceive() { + return(this->startReceive(RADIOLIB_LR11X0_RX_TIMEOUT_INF, RADIOLIB_LR11X0_IRQ_RX_DONE, 0, 0)); +} + +int16_t LR11x0::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { + (void)irqMask; + (void)len; + + // check active modem + int16_t state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set DIO mapping + uint32_t irq = irqFlags; + if(timeout != RADIOLIB_LR11X0_RX_TIMEOUT_INF) { + irq |= RADIOLIB_LR11X0_IRQ_TIMEOUT; + } + + state = setDioIrqParams(irq, RADIOLIB_LR11X0_IRQ_NONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + // set implicit mode and expected len if applicable + if((this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) && (modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA)) { + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, this->invertIQEnabled); + RADIOLIB_ASSERT(state); + } + + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_RX); + + // set mode to receive + state = setRx(timeout); + + return(state); +} + +uint32_t LR11x0::getIrqStatus() { + // there is no dedicated "get IRQ" command, the IRQ bits are sent after the status bytes + uint8_t buff[6] = { 0 }; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0; + mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true, RADIOLIB_MODULE_SPI_TIMEOUT); + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; + uint32_t irq = ((uint32_t)(buff[2]) << 24) | ((uint32_t)(buff[3]) << 16) | ((uint32_t)(buff[4]) << 8) | (uint32_t)buff[5]; + return(irq); +} + +int16_t LR11x0::readData(uint8_t* data, size_t len) { + // check active modem + int16_t state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if((modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) && + (modem != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS)) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check integrity CRC + uint32_t irq = getIrqStatus(); + int16_t crcState = RADIOLIB_ERR_NONE; + if((irq & RADIOLIB_LR11X0_IRQ_CRC_ERR) || (irq & RADIOLIB_LR11X0_IRQ_HEADER_ERR)) { + crcState = RADIOLIB_ERR_CRC_MISMATCH; + } + + // get packet length + // the offset is needed since LR11x0 seems to move the buffer base by 4 bytes on every packet + uint8_t offset = 0; + size_t length = getPacketLength(true, &offset); + if((len != 0) && (len < length)) { + // user requested less data than we got, only return what was requested + length = len; + } + + // read packet data + state = readBuffer8(data, length, offset); + RADIOLIB_ASSERT(state); + + // clear the Rx buffer + state = clearRxBuffer(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + + // check if CRC failed - this is done after reading data to give user the option to keep them + RADIOLIB_ASSERT(crcState); + + return(state); +} + +int16_t LR11x0::startChannelScan() { + return(this->startChannelScan(RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT, RADIOLIB_LR11X0_CAD_PARAM_DEFAULT)); +} + +int16_t LR11x0::startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { + // check active modem + int16_t state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set mode to standby + state = standby(); + RADIOLIB_ASSERT(state); + + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_RX); + + // set DIO pin mapping + state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_CAD_DETECTED | RADIOLIB_LR11X0_IRQ_CAD_DONE, RADIOLIB_LR11X0_IRQ_NONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + // set mode to CAD + return(startCad(symbolNum, detPeak, detMin)); +} + +int16_t LR11x0::getChannelScanResult() { + // check active modem + int16_t state = RADIOLIB_ERR_NONE; + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + RADIOLIB_ASSERT(state); + if(modem != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check CAD result + uint32_t cadResult = getIrqStatus(); + if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DETECTED) { + // detected some LoRa activity + return(RADIOLIB_LORA_DETECTED); + } else if(cadResult & RADIOLIB_LR11X0_IRQ_CAD_DONE) { + // channel is free + return(RADIOLIB_CHANNEL_FREE); + } + + return(RADIOLIB_ERR_UNKNOWN); +} + +int16_t LR11x0::setOutputPower(int8_t power) { + return(this->setOutputPower(power, false)); +} + +int16_t LR11x0::setOutputPower(int8_t power, bool forceHighPower) { + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL, forceHighPower); + RADIOLIB_ASSERT(state); + + // determine whether to use HP or LP PA and check range accordingly + bool useHp = forceHighPower || (power > 14); + + // TODO how and when to configure OCP? + + // update PA config - always use VBAT for high-power PA + state = setPaConfig((uint8_t)useHp, (uint8_t)useHp, 0x04, 0x07); + RADIOLIB_ASSERT(state); + + // set output power + state = setTxParams(power, RADIOLIB_LR11X0_PA_RAMP_48U); + return(state); +} + +int16_t LR11x0::checkOutputPower(int8_t power, int8_t* clipped) { + return(checkOutputPower(power, clipped, false)); +} + +int16_t LR11x0::checkOutputPower(int8_t power, int8_t* clipped, bool forceHighPower) { + if(forceHighPower || (power > 14)) { + if(clipped) { + *clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power)); + } + RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + + } else { + if(clipped) { + *clipped = RADIOLIB_MAX(-17, RADIOLIB_MIN(14, power)); + } + RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + + } + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::setBandwidth(float bw) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // ensure byte conversion doesn't overflow + RADIOLIB_CHECK_RANGE(bw, 0.0, 510.0, RADIOLIB_ERR_INVALID_BANDWIDTH); + + // check allowed bandwidth values + uint8_t bw_div2 = bw / 2 + 0.01; + switch (bw_div2) { + case 31: // 62.5: + this->bandwidth = RADIOLIB_LR11X0_LORA_BW_62_5; + break; + case 62: // 125.0: + this->bandwidth = RADIOLIB_LR11X0_LORA_BW_125_0; + break; + case 125: // 250.0 + this->bandwidth = RADIOLIB_LR11X0_LORA_BW_250_0; + break; + case 250: // 500.0 + this->bandwidth = RADIOLIB_LR11X0_LORA_BW_500_0; + break; + default: + return(RADIOLIB_ERR_INVALID_BANDWIDTH); + } + + // update modulation parameters + this->bandwidthKhz = bw; + return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize)); +} + +int16_t LR11x0::setSpreadingFactor(uint8_t sf, bool legacy) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + RADIOLIB_CHECK_RANGE(sf, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR); + + // TODO enable SF6 legacy mode + if(legacy && (sf == 6)) { + //this->mod->SPIsetRegValue(RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT, RADIOLIB_LR11X0_SF6_SX127X, 18, 18); + } + + // update modulation parameters + this->spreadingFactor = sf; + return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize)); +} + +int16_t LR11x0::setCodingRate(uint8_t cr, bool longInterleave) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + RADIOLIB_CHECK_RANGE(cr, 5, 8, RADIOLIB_ERR_INVALID_CODING_RATE); + + if(longInterleave) { + switch(cr) { + case 5: + case 6: + this->codingRate = cr; + break; + case 8: + this->codingRate = cr - 1; + break; + default: + return(RADIOLIB_ERR_INVALID_CODING_RATE); + } + + } else { + this->codingRate = cr - 4; + + } + + // update modulation parameters + return(setModulationParamsLoRa(this->spreadingFactor, this->bandwidth, this->codingRate, this->ldrOptimize)); +} + +int16_t LR11x0::setSyncWord(uint32_t syncWord) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(setLoRaSyncWord(syncWord & 0xFF)); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(lrFhssSetSyncWord(syncWord)); + + } + + return(RADIOLIB_ERR_WRONG_MODEM); +} + +int16_t LR11x0::setBitRate(float br) { + RADIOLIB_CHECK_RANGE(br, 0.6, 300.0, RADIOLIB_ERR_INVALID_BIT_RATE); + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set bit rate value + // TODO implement fractional bit rate configuration + this->bitRate = br * 1000.0; + return(setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev)); +} + +int16_t LR11x0::setFrequencyDeviation(float freqDev) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set frequency deviation to lowest available setting (required for digimodes) + float newFreqDev = freqDev; + if(freqDev < 0.0) { + newFreqDev = 0.6; + } + + RADIOLIB_CHECK_RANGE(newFreqDev, 0.6, 200.0, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION); + this->frequencyDev = newFreqDev * 1000.0; + return(setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev)); +} + +int16_t LR11x0::setRxBandwidth(float rxBw) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check modulation parameters + /*if(2 * this->frequencyDev + this->bitRate > rxBw * 1000.0) { + return(RADIOLIB_ERR_INVALID_MODULATION_PARAMETERS); + }*/ + + // check allowed receiver bandwidth values + if(fabs(rxBw - 4.8) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_4_8; + } else if(fabs(rxBw - 5.8) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_5_8; + } else if(fabs(rxBw - 7.3) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_7_3; + } else if(fabs(rxBw - 9.7) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_9_7; + } else if(fabs(rxBw - 11.7) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_11_7; + } else if(fabs(rxBw - 14.6) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_14_6; + } else if(fabs(rxBw - 19.5) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_19_5; + } else if(fabs(rxBw - 23.4) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_23_4; + } else if(fabs(rxBw - 29.3) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_29_3; + } else if(fabs(rxBw - 39.0) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_39_0; + } else if(fabs(rxBw - 46.9) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_46_9; + } else if(fabs(rxBw - 58.6) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_58_6; + } else if(fabs(rxBw - 78.2) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_78_2; + } else if(fabs(rxBw - 93.8) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_93_8; + } else if(fabs(rxBw - 117.3) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_117_3; + } else if(fabs(rxBw - 156.2) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_156_2; + } else if(fabs(rxBw - 187.2) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_187_2; + } else if(fabs(rxBw - 234.3) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_234_3; + } else if(fabs(rxBw - 312.0) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_312_0; + } else if(fabs(rxBw - 373.6) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_373_6; + } else if(fabs(rxBw - 467.0) <= 0.001) { + this->rxBandwidth = RADIOLIB_LR11X0_GFSK_RX_BW_467_0; + } else { + return(RADIOLIB_ERR_INVALID_RX_BANDWIDTH); + } + + // update modulation parameters + return(setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev)); +} + +int16_t LR11x0::setSyncWord(uint8_t* syncWord, size_t len) { + if((!syncWord) || (!len) || (len > RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN)) { + return(RADIOLIB_ERR_INVALID_SYNC_WORD); + } + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // with length set to 1 and LoRa modem active, assume it is the LoRa sync word + if(len > 1) { + return(RADIOLIB_ERR_INVALID_SYNC_WORD); + } + return(setSyncWord(syncWord[0])); + + } else if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + + } + + // update sync word length + this->syncWordLength = len*8; + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening); + RADIOLIB_ASSERT(state); + + // sync word is passed most-significant byte first + uint8_t fullSyncWord[RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN] = { 0 }; + memcpy(fullSyncWord, syncWord, len); + return(setGfskSyncWord(fullSyncWord)); +} + +int16_t LR11x0::setSyncBits(uint8_t *syncWord, uint8_t bitsLen) { + if((!syncWord) || (!bitsLen) || (bitsLen > 8*RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN)) { + return(RADIOLIB_ERR_INVALID_SYNC_WORD); + } + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + uint8_t bytesLen = bitsLen / 8; + if ((bitsLen % 8) != 0) { + bytesLen++; + } + + return(setSyncWord(syncWord, bytesLen)); +} + +int16_t LR11x0::setNodeAddress(uint8_t nodeAddr) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // enable address filtering (node only) + this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE; + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening); + RADIOLIB_ASSERT(state); + + // set node address + this->node = nodeAddr; + return(setPacketAdrs(this->node, 0)); +} + +int16_t LR11x0::setBroadcastAddress(uint8_t broadAddr) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // enable address filtering (node and broadcast) + this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE_BROADCAST; + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening); + RADIOLIB_ASSERT(state); + + // set node and broadcast address + return(setPacketAdrs(this->node, broadAddr)); +} + +int16_t LR11x0::disableAddressFiltering() { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // disable address filterin + this->addrComp = RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED; + return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening)); +} + +int16_t LR11x0::setDataShaping(uint8_t sh) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set data shaping + switch(sh) { + case RADIOLIB_SHAPING_NONE: + this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_NONE; + break; + case RADIOLIB_SHAPING_0_3: + this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_3; + break; + case RADIOLIB_SHAPING_0_5: + this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_5; + break; + case RADIOLIB_SHAPING_0_7: + this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_7; + break; + case RADIOLIB_SHAPING_1_0: + this->pulseShape = RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_1_0; + break; + default: + return(RADIOLIB_ERR_INVALID_DATA_SHAPING); + } + + // update modulation parameters + return(setModulationParamsGFSK(this->bitRate, this->pulseShape, this->rxBandwidth, this->frequencyDev)); +} + +int16_t LR11x0::setEncoding(uint8_t encoding) { + return(setWhitening(encoding)); +} + +int16_t LR11x0::fixedPacketLengthMode(uint8_t len) { + return(setPacketMode(RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED, len)); +} + +int16_t LR11x0::variablePacketLengthMode(uint8_t maxLen) { + return(setPacketMode(RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_VARIABLE, maxLen)); +} + +int16_t LR11x0::setWhitening(bool enabled, uint16_t initial) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + if(!enabled) { + // disable whitening + this->whitening = RADIOLIB_LR11X0_GFSK_WHITENING_DISABLED; + + } else { + // enable whitening + this->whitening = RADIOLIB_LR11X0_GFSK_WHITENING_ENABLED; + + // write initial whitening value + state = setGfskWhitParams(initial); + RADIOLIB_ASSERT(state); + } + + return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening)); +} + +int16_t LR11x0::setDataRate(DataRate_t dr) { + // select interpretation based on active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + + if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + // set the bit rate + state = this->setBitRate(dr.fsk.bitRate); + RADIOLIB_ASSERT(state); + + // set the frequency deviation + state = this->setFrequencyDeviation(dr.fsk.freqDev); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // set the spreading factor + state = this->setSpreadingFactor(dr.lora.spreadingFactor); + RADIOLIB_ASSERT(state); + + // set the bandwidth + state = this->setBandwidth(dr.lora.bandwidth); + RADIOLIB_ASSERT(state); + + // set the coding rate + state = this->setCodingRate(dr.lora.codingRate); + } + + return(state); +} + +int16_t LR11x0::checkDataRate(DataRate_t dr) { + // select interpretation based on active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + + if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + RADIOLIB_CHECK_RANGE(dr.fsk.bitRate, 0.6, 300.0, RADIOLIB_ERR_INVALID_BIT_RATE); + RADIOLIB_CHECK_RANGE(dr.fsk.freqDev, 0.6, 200.0, RADIOLIB_ERR_INVALID_FREQUENCY_DEVIATION); + return(RADIOLIB_ERR_NONE); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + RADIOLIB_CHECK_RANGE(dr.lora.spreadingFactor, 5, 12, RADIOLIB_ERR_INVALID_SPREADING_FACTOR); + RADIOLIB_CHECK_RANGE(dr.lora.bandwidth, 0.0, 510.0, RADIOLIB_ERR_INVALID_BANDWIDTH); + RADIOLIB_CHECK_RANGE(dr.lora.codingRate, 5, 8, RADIOLIB_ERR_INVALID_CODING_RATE); + return(RADIOLIB_ERR_NONE); + + } + + return(RADIOLIB_ERR_UNKNOWN); +} + +int16_t LR11x0::setPreambleLength(size_t preambleLength) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + this->preambleLengthLoRa = preambleLength; + return(setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled)); + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + this->preambleLengthGFSK = preambleLength; + this->preambleDetLength = RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_16_BITS; + return(setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening)); + } + + return(RADIOLIB_ERR_WRONG_MODEM); +} + +int16_t LR11x0::setTCXO(float voltage, uint32_t delay) { + // check if TCXO is enabled at all + if(this->XTAL) { + return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE); + } + + // set mode to standby + standby(); + + // check RADIOLIB_LR11X0_ERROR_STAT_HF_XOSC_START_ERR flag and clear it + uint16_t errors = 0; + int16_t state = getErrors(&errors); + RADIOLIB_ASSERT(state); + if(errors & RADIOLIB_LR11X0_ERROR_STAT_HF_XOSC_START_ERR) { + clearErrors(); + } + + // check 0 V disable + if(fabs(voltage - 0.0) <= 0.001) { + setTcxoMode(0, 0); + return(reset()); + } + + // check allowed voltage values + uint8_t tune = 0; + if(fabs(voltage - 1.6) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_1_6; + } else if(fabs(voltage - 1.7) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_1_7; + } else if(fabs(voltage - 1.8) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_1_8; + } else if(fabs(voltage - 2.2) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_2_2; + } else if(fabs(voltage - 2.4) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_2_4; + } else if(fabs(voltage - 2.7) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_2_7; + } else if(fabs(voltage - 3.0) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_3_0; + } else if(fabs(voltage - 3.3) <= 0.001) { + tune = RADIOLIB_LR11X0_TCXO_VOLTAGE_3_3; + } else { + return(RADIOLIB_ERR_INVALID_TCXO_VOLTAGE); + } + + // calculate delay value + uint32_t delayValue = (uint32_t)((float)delay / 30.52f); + if(delayValue == 0) { + delayValue = 1; + } + + // enable TCXO control + return(setTcxoMode(tune, delayValue)); +} + +int16_t LR11x0::setCRC(uint8_t len, uint32_t initial, uint32_t polynomial, bool inverted) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // LoRa CRC doesn't allow to set CRC polynomial, initial value, or inversion + this->crcTypeLoRa = len > 0 ? RADIOLIB_LR11X0_LORA_CRC_ENABLED : RADIOLIB_LR11X0_LORA_CRC_DISABLED; + state = setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + // update packet parameters + switch(len) { + case 0: + this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_DISABLED; + break; + case 1: + if(inverted) { + this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_1_BYTE_INV; + } else { + this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_1_BYTE; + } + break; + case 2: + if(inverted) { + this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_2_BYTE_INV; + } else { + this->crcTypeGFSK = RADIOLIB_LR11X0_GFSK_CRC_2_BYTE; + } + break; + default: + return(RADIOLIB_ERR_INVALID_CRC_CONFIGURATION); + } + + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, this->packetType, RADIOLIB_LR11X0_MAX_PACKET_LENGTH, this->crcTypeGFSK, this->whitening); + RADIOLIB_ASSERT(state); + + state = setGfskCrcParams(initial, polynomial); + + } + + return(state); +} + +int16_t LR11x0::invertIQ(bool enable) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + this->invertIQEnabled = enable; + return(setPacketParamsLoRa(this->preambleLengthLoRa, this->headerType, this->implicitLen, this->crcTypeLoRa, (uint8_t)this->invertIQEnabled)); +} + +float LR11x0::getRSSI() { + float val = 0; + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + (void)getPacketType(&type); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + (void)getPacketStatusLoRa(&val, NULL, NULL); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + (void)getPacketStatusGFSK(NULL, &val, NULL, NULL); + + } + + return(val); +} + +float LR11x0::getSNR() { + float val = 0; + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + (void)getPacketType(&type); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + (void)getPacketStatusLoRa(NULL, &val, NULL); + } + + return(val); +} + +float LR11x0::getFrequencyError() { + // TODO implement this + return(0); +} + +size_t LR11x0::getPacketLength(bool update) { + return(this->getPacketLength(update, NULL)); +} + +size_t LR11x0::getPacketLength(bool update, uint8_t* offset) { + (void)update; + + // in implicit mode, return the cached value + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + (void)getPacketType(&type); + if((type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) && (this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT)) { + return(this->implicitLen); + } + + uint8_t len = 0; + (void)getRxBufferStatus(&len, offset); + return((size_t)len); +} + +RadioLibTime_t LR11x0::getTimeOnAir(size_t len) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + (void)getPacketType(&type); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // calculate number of symbols + float N_symbol = 0; + if(this->codingRate <= RADIOLIB_LR11X0_LORA_CR_4_8_SHORT) { + // legacy coding rate - nice and simple + + // get SF coefficients + float coeff1 = 0; + int16_t coeff2 = 0; + int16_t coeff3 = 0; + if(this->spreadingFactor < 7) { + // SF5, SF6 + coeff1 = 6.25; + coeff2 = 4*this->spreadingFactor; + coeff3 = 4*this->spreadingFactor; + } else if(this->spreadingFactor < 11) { + // SF7. SF8, SF9, SF10 + coeff1 = 4.25; + coeff2 = 4*this->spreadingFactor + 8; + coeff3 = 4*this->spreadingFactor; + } else { + // SF11, SF12 + coeff1 = 4.25; + coeff2 = 4*this->spreadingFactor + 8; + coeff3 = 4*(this->spreadingFactor - 2); + } + + // get CRC length + int16_t N_bitCRC = 16; + if(this->crcTypeLoRa == RADIOLIB_LR11X0_LORA_CRC_DISABLED) { + N_bitCRC = 0; + } + + // get header length + int16_t N_symbolHeader = 20; + if(this->headerType == RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT) { + N_symbolHeader = 0; + } + + // calculate number of LoRa preamble symbols + uint32_t N_symbolPreamble = (this->preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((this->preambleLengthLoRa & 0xF0) >> 4)); + + // calculate the number of symbols + N_symbol = (float)N_symbolPreamble + coeff1 + 8.0 + ceil(RADIOLIB_MAX((int16_t)(8 * len + N_bitCRC - coeff2 + N_symbolHeader), (int16_t)0) / (float)coeff3) * (float)(this->codingRate + 4); + + } else { + // long interleaving - abandon hope all ye who enter here + /// \todo implement this mess - SX1280 datasheet v3.0 section 7.4.4.2 + + } + + // get time-on-air in us + return(((uint32_t(1) << this->spreadingFactor) / this->bandwidthKhz) * N_symbol * 1000.0); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(((uint32_t)len * 8 * 1000000UL) / this->bitRate); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(((uint32_t)len * 8 * 1000000UL) / RADIOLIB_LR11X0_LR_FHSS_BIT_RATE); + } + + return(0); +} + +RadioLibTime_t LR11x0::calculateRxTimeout(RadioLibTime_t timeoutUs) { + // the timeout value is given in units of 30.52 microseconds + // the calling function should provide some extra width, as this number of units is truncated to integer + RadioLibTime_t timeout = timeoutUs / 30.52; + return(timeout); +} + +int16_t LR11x0::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { + irqFlags = RADIOLIB_LR11X0_IRQ_RX_DONE | RADIOLIB_LR11X0_IRQ_TIMEOUT; // flags that can appear in the IRQ register + irqMask = irqFlags; // on LR11x0, these are the same + return(RADIOLIB_ERR_NONE); +} + +bool LR11x0::isRxTimeout() { + uint32_t irq = getIrqStatus(); + bool rxTimedOut = irq & RADIOLIB_LR11X0_IRQ_TIMEOUT; + return(rxTimedOut); +} + +uint8_t LR11x0::randomByte() { + uint32_t num = 0; + (void)getRandomNumber(&num); + return((uint8_t)num); +} + +int16_t LR11x0::implicitHeader(size_t len) { + return(this->setHeaderType(RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT, len)); +} + +int16_t LR11x0::explicitHeader() { + return(this->setHeaderType(RADIOLIB_LR11X0_LORA_HEADER_EXPLICIT)); +} + +float LR11x0::getDataRate() const { + return(this->dataRateMeasured); +} + +int16_t LR11x0::setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount, uint16_t hopSeed) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check and cache all parameters + RADIOLIB_CHECK_RANGE((int8_t)cr, (int8_t)RADIOLIB_LR11X0_LR_FHSS_CR_5_6, (int8_t)RADIOLIB_LR11X0_LR_FHSS_CR_1_3, RADIOLIB_ERR_INVALID_CODING_RATE); + this->lrFhssCr = cr; + RADIOLIB_CHECK_RANGE((int8_t)bw, (int8_t)RADIOLIB_LR11X0_LR_FHSS_BW_39_06, (int8_t)RADIOLIB_LR11X0_LR_FHSS_BW_1574_2, RADIOLIB_ERR_INVALID_BANDWIDTH); + this->lrFhssBw = bw; + RADIOLIB_CHECK_RANGE(hdrCount, 1, 4, RADIOLIB_ERR_INVALID_BIT_RANGE); + this->lrFhssHdrCount = hdrCount; + RADIOLIB_CHECK_RANGE((int16_t)hopSeed, (int16_t)0x000, (int16_t)0x1FF, RADIOLIB_ERR_INVALID_DATA_SHAPING); + this->lrFhssHopSeq = hopSeed; + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::startWifiScan(char wifiType, uint8_t mode, uint16_t chanMask, uint8_t numScans, uint16_t timeout) { + uint8_t type; + switch(wifiType) { + case('b'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_B; + break; + case('g'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_G; + break; + case('n'): + type = RADIOLIB_LR11X0_WIFI_SCAN_802_11_N; + break; + case('*'): + type = RADIOLIB_LR11X0_WIFI_SCAN_ALL; + break; + default: + return(RADIOLIB_ERR_INVALID_WIFI_TYPE); + } + + // go to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // reset cumulative timings + state = wifiResetCumulTimings(); + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(RADIOLIB_LR11X0_IRQ_WIFI_DONE, 0); + RADIOLIB_ASSERT(state); + + // start scan with the maximum number of results and abort on timeout + this->wifiScanMode = mode; + state = wifiScan(type, chanMask, this->wifiScanMode, RADIOLIB_LR11X0_WIFI_MAX_NUM_RESULTS, numScans, timeout, RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_ENABLED); + return(state); +} + +void LR11x0::setWiFiScanAction(void (*func)(void)) { + this->setIrqAction(func); +} + +void LR11x0::clearWiFiScanAction() { + this->clearIrqAction(); +} + +int16_t LR11x0::getWifiScanResultsCount(uint8_t* count) { + // clear IRQ first, as this is likely to be called right after scan has finished + int16_t state = clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + RADIOLIB_ASSERT(state); + + uint8_t buff[1] = { 0 }; + state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_GET_NB_RESULTS, false, buff, sizeof(buff)); + + // pass the replies + if(count) { *count = buff[0]; } + + return(state); +} + +int16_t LR11x0::getWifiScanResult(LR11x0WifiResult_t* result, uint8_t index, bool brief) { + if(!result) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + + // read a single result + uint8_t format = brief ? RADIOLIB_LR11X0_WIFI_RESULT_TYPE_BASIC : RADIOLIB_LR11X0_WIFI_RESULT_TYPE_COMPLETE; + uint8_t raw[RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN] = { 0 }; + int16_t state = wifiReadResults(index, 1, format, raw); + RADIOLIB_ASSERT(state); + + // parse the information + switch(raw[0] & 0x03) { + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_B): + result->type = 'b'; + break; + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_G): + result->type = 'g'; + break; + case(RADIOLIB_LR11X0_WIFI_SCAN_802_11_N): + result->type = 'n'; + break; + } + result->dataRateId = (raw[0] & 0xFC) >> 2; + result->channelFreq = 2407 + (raw[1] & 0x0F)*5; + result->origin = (raw[1] & 0x30) >> 4; + result->ap = (raw[1] & 0x40) != 0; + result->rssi = (float)raw[2] / -2.0f;; + memcpy(result->mac, &raw[3], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + + if(!brief) { + if(this->wifiScanMode == RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON) { + LR11x0WifiResultExtended_t* resultExtended = reinterpret_cast(result); + resultExtended->rate = raw[3]; + resultExtended->service = (((uint16_t)raw[4] << 8) | ((uint16_t)raw[5])); + resultExtended->length = (((uint16_t)raw[6] << 8) | ((uint16_t)raw[7])); + resultExtended->frameType = raw[9] & 0x03; + resultExtended->frameSubType = (raw[9] & 0x3C) >> 2; + resultExtended->toDistributionSystem = (raw[9] & 0x40) != 0; + resultExtended->fromDistributionSystem = (raw[9] & 0x80) != 0; + memcpy(resultExtended->mac0, &raw[10], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + memcpy(resultExtended->mac, &raw[16], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + memcpy(resultExtended->mac2, &raw[22], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + resultExtended->timestamp = (((uint64_t)raw[28] << 56) | ((uint64_t)raw[29] << 48)) | + (((uint64_t)raw[30] << 40) | ((uint64_t)raw[31] << 32)) | + (((uint64_t)raw[32] << 24) | ((uint64_t)raw[33] << 16)) | + (((uint64_t)raw[34] << 8) | (uint64_t)raw[35]); + resultExtended->periodBeacon = (((uint16_t)raw[36] << 8) | ((uint16_t)raw[37])) * 1024UL; + resultExtended->seqCtrl = (((uint16_t)raw[38] << 8) | ((uint16_t)raw[39])); + memcpy(resultExtended->ssid, &raw[40], RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN); + resultExtended->currentChannel = raw[72]; + memcpy(resultExtended->countryCode, &raw[73], 2); + resultExtended->countryCode[2] = '\0'; + resultExtended->ioReg = raw[75]; + resultExtended->fcsCheckOk = (raw[76] != 0); + resultExtended->phiOffset = (((uint16_t)raw[77] << 8) | ((uint16_t)raw[78])); + return(RADIOLIB_ERR_NONE); + } + + LR11x0WifiResultFull_t* resultFull = reinterpret_cast(result); + resultFull->frameType = raw[3] & 0x03; + resultFull->frameSubType = (raw[3] & 0x3C) >> 2; + resultFull->toDistributionSystem = (raw[3] & 0x40) != 0; + resultFull->fromDistributionSystem = (raw[3] & 0x80) != 0; + memcpy(resultFull->mac, &raw[4], RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN); + resultFull->phiOffset = (((uint16_t)raw[10] << 8) | ((uint16_t)raw[11])); + resultFull->timestamp = (((uint64_t)raw[12] << 56) | ((uint64_t)raw[13] << 48)) | + (((uint64_t)raw[14] << 40) | ((uint64_t)raw[15] << 32)) | + (((uint64_t)raw[16] << 24) | ((uint64_t)raw[17] << 16)) | + (((uint64_t)raw[18] << 8) | (uint64_t)raw[19]); + resultFull->periodBeacon = (((uint16_t)raw[20] << 8) | ((uint16_t)raw[21])) * 1024UL; + } + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::wifiScan(uint8_t wifiType, uint8_t* count, uint8_t mode, uint16_t chanMask, uint8_t numScans, uint16_t timeout) { + if(!count) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + + // start scan + RADIOLIB_DEBUG_BASIC_PRINTLN("WiFi scan start"); + int16_t state = startWifiScan(wifiType, mode, chanMask, numScans, timeout); + RADIOLIB_ASSERT(state); + + // wait for scan finished or timeout + RadioLibTime_t softTimeout = 30UL * 1000UL; + RadioLibTime_t start = this->mod->hal->millis(); + while(!this->mod->hal->digitalRead(this->mod->getIrq())) { + this->mod->hal->yield(); + if(this->mod->hal->millis() - start > softTimeout) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout waiting for IRQ"); + this->standby(); + return(RADIOLIB_ERR_RX_TIMEOUT); + } + } + RADIOLIB_DEBUG_BASIC_PRINTLN("WiFi scan done in %lu ms", (long unsigned int)(this->mod->hal->millis() - start)); + + // read number of results + return(getWifiScanResultsCount(count)); +} + +int16_t LR11x0::modSetup(float tcxoVoltage, uint8_t modem) { + this->mod->init(); + this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); + this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_32; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; + this->mod->spiConfig.statusPos = 0; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_LR11X0_CMD_READ_REG_MEM; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_LR11X0_CMD_WRITE_REG_MEM; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_LR11X0_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_LR11X0_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; + this->mod->spiConfig.checkStatusCb = SPIcheckStatus; + + // try to find the LR11x0 chip - this will also reset the module at least once + if(!LR11x0::findChip(this->chipType)) { + RADIOLIB_DEBUG_BASIC_PRINTLN("No LR11x0 found!"); + this->mod->term(); + return(RADIOLIB_ERR_CHIP_NOT_FOUND); + } + RADIOLIB_DEBUG_BASIC_PRINTLN("M\tLR11x0"); + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set TCXO control, if requested + if(!this->XTAL && tcxoVoltage > 0.0) { + state = setTCXO(tcxoVoltage); + RADIOLIB_ASSERT(state); + } + + // configure settings not accessible by API + return(config(modem)); +} + +int16_t LR11x0::SPIparseStatus(uint8_t in) { + if((in & 0b00001110) == RADIOLIB_LR11X0_STAT_1_CMD_PERR) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } else if((in & 0b00001110) == RADIOLIB_LR11X0_STAT_1_CMD_FAIL) { + return(RADIOLIB_ERR_SPI_CMD_FAILED); + } else if((in == 0x00) || (in == 0xFF)) { + return(RADIOLIB_ERR_CHIP_NOT_FOUND); + } + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::SPIcheckStatus(Module* mod) { + // the status check command doesn't return status in the same place as other read commands, + // but only as the first byte (as with any other command), hence LR11x0::SPIcommand can't be used + // it also seems to ignore the actual command, and just sending in bunch of NOPs will work + uint8_t buff[6] = { 0 }; + mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_0; + int16_t state = mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true, RADIOLIB_MODULE_SPI_TIMEOUT); + mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_STATUS] = Module::BITS_8; + RADIOLIB_ASSERT(state); + return(LR11x0::SPIparseStatus(buff[0])); +} + +int16_t LR11x0::SPIcommand(uint16_t cmd, bool write, uint8_t* data, size_t len, uint8_t* out, size_t outLen) { + int16_t state = RADIOLIB_ERR_UNKNOWN; + if(!write) { + // the SPI interface of LR11x0 requires two separate transactions for reading + // send the 16-bit command + state = this->mod->SPIwriteStream(cmd, out, outLen, true, false); + RADIOLIB_ASSERT(state); + + // read the result without command + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_0; + state = this->mod->SPIreadStream(RADIOLIB_LR11X0_CMD_NOP, data, len, true, false); + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_16; + + } else { + // write is just a single transaction + state = this->mod->SPIwriteStream(cmd, data, len, true, true); + + } + + return(state); +} + +bool LR11x0::findChip(uint8_t ver) { + uint8_t i = 0; + bool flagFound = false; + while((i < 10) && !flagFound) { + // reset the module + reset(); + + // read the version + uint8_t device = 0xFF; + if((this->getVersion(NULL, &device, NULL, NULL) == RADIOLIB_ERR_NONE) && (device == ver)) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Found LR11x0: RADIOLIB_LR11X0_CMD_GET_VERSION = 0x%02x", device); + flagFound = true; + } else { + RADIOLIB_DEBUG_BASIC_PRINTLN("LR11x0 not found! (%d of 10 tries) RADIOLIB_LR11X0_CMD_GET_VERSION = 0x%02x", i + 1, device); + RADIOLIB_DEBUG_BASIC_PRINTLN("Expected: 0x%02x", ver); + this->mod->hal->delay(10); + i++; + } + } + + return(flagFound); +} + +int16_t LR11x0::config(uint8_t modem) { + int16_t state = RADIOLIB_ERR_UNKNOWN; + + // set Rx/Tx fallback mode to STDBY_RC + state = this->setRxTxFallbackMode(RADIOLIB_LR11X0_FALLBACK_MODE_STBY_RC); + RADIOLIB_ASSERT(state); + + // clear IRQ + state = this->clearIrq(RADIOLIB_LR11X0_IRQ_ALL); + state |= this->setDioIrqParams(RADIOLIB_LR11X0_IRQ_NONE, RADIOLIB_LR11X0_IRQ_NONE); + RADIOLIB_ASSERT(state); + + // calibrate all blocks + (void)this->calibrate(RADIOLIB_LR11X0_CALIBRATE_ALL); + + // wait for calibration completion + this->mod->hal->delay(5); + while(this->mod->hal->digitalRead(this->mod->getGpio())) { + this->mod->hal->yield(); + } + + // if something failed, show the device errors + #if RADIOLIB_DEBUG_BASIC + if(state != RADIOLIB_ERR_NONE) { + // unless mode is forced to standby, device errors will be 0 + standby(); + uint16_t errors = 0; + getErrors(&errors); + RADIOLIB_DEBUG_BASIC_PRINTLN("Calibration failed, device errors: 0x%X", errors); + } + #endif + + // set modem + state = this->setPacketType(modem); + return(state); +} + +int16_t LR11x0::setPacketMode(uint8_t mode, uint8_t len) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set requested packet mode + state = setPacketParamsGFSK(this->preambleLengthGFSK, this->preambleDetLength, this->syncWordLength, this->addrComp, mode, len, this->crcTypeGFSK, this->whitening); + RADIOLIB_ASSERT(state); + + // update cached value + this->packetType = mode; + return(state); +} + +int16_t LR11x0::startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // select CAD parameters + // TODO the magic numbers are based on Semtech examples, this is probably suboptimal + uint8_t num = symbolNum; + if(num == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) { + num = 2; + } + + const uint8_t detPeakValues[8] = { 48, 48, 50, 55, 55, 59, 61, 65 }; + uint8_t peak = detPeak; + if(peak == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) { + peak = detPeakValues[this->spreadingFactor - 5]; + } + + uint8_t min = detMin; + if(min == RADIOLIB_LR11X0_CAD_PARAM_DEFAULT) { + min = 10; + } + + // set CAD parameters + // TODO add configurable exit mode and timeout + state = setCadParams(num, peak, min, RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC, 0); + RADIOLIB_ASSERT(state); + + // start CAD + return(setCad()); +} + +int16_t LR11x0::setHeaderType(uint8_t hdrType, size_t len) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + int16_t state = getPacketType(&type); + RADIOLIB_ASSERT(state); + if(type != RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set requested packet mode + state = setPacketParamsLoRa(this->preambleLengthLoRa, hdrType, len, this->crcTypeLoRa, this->invertIQEnabled); + RADIOLIB_ASSERT(state); + + // update cached value + this->headerType = hdrType; + this->implicitLen = len; + + return(state); +} + +Module* LR11x0::getMod() { + return(this->mod); +} + +int16_t LR11x0::writeRegMem32(uint32_t addr, uint32_t* data, size_t len) { + // check maximum size + if(len > (RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t))) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + return(this->writeCommon(RADIOLIB_LR11X0_CMD_WRITE_REG_MEM, addr, data, len)); +} + +int16_t LR11x0::readRegMem32(uint32_t addr, uint32_t* data, size_t len) { + // check maximum size + if(len >= (RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t))) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // the request contains the address and length + uint8_t reqBuff[5] = { + (uint8_t)((addr >> 24) & 0xFF), (uint8_t)((addr >> 16) & 0xFF), + (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF), + (uint8_t)len, + }; + + // build buffers - later we need to ensure endians are correct, + // so there is probably no way to do this without copying buffers and iterating + #if RADIOLIB_STATIC_ONLY + uint8_t rplBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* rplBuff = new uint8_t[len*sizeof(uint32_t)]; + #endif + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_READ_REG_MEM, false, rplBuff, len*sizeof(uint32_t), reqBuff, sizeof(reqBuff)); + + // convert endians + if(data && (state == RADIOLIB_ERR_NONE)) { + for(size_t i = 0; i < len; i++) { + data[i] = ((uint32_t)rplBuff[2 + i*sizeof(uint32_t)] << 24) | ((uint32_t)rplBuff[3 + i*sizeof(uint32_t)] << 16) | ((uint32_t)rplBuff[4 + i*sizeof(uint32_t)] << 8) | (uint32_t)rplBuff[5 + i*sizeof(uint32_t)]; + } + } + + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + + return(state); +} + +int16_t LR11x0::writeBuffer8(uint8_t* data, size_t len) { + // check maximum size + if(len > RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WRITE_BUFFER, true, data, len)); +} + +int16_t LR11x0::readBuffer8(uint8_t* data, size_t len, size_t offset) { + // check maximum size + if(len > RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // build buffers + size_t reqLen = 2*sizeof(uint8_t) + len; + #if RADIOLIB_STATIC_ONLY + uint8_t reqBuff[sizeof(uint32_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* reqBuff = new uint8_t[reqLen]; + #endif + + // set the offset and length + reqBuff[0] = (uint8_t)offset; + reqBuff[1] = (uint8_t)len; + + // send the request + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_READ_BUFFER, false, data, len, reqBuff, reqLen); + #if !RADIOLIB_STATIC_ONLY + delete[] reqBuff; + #endif + return(state); +} + +int16_t LR11x0::clearRxBuffer(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CLEAR_RX_BUFFER, true, NULL, 0)); +} + +int16_t LR11x0::writeRegMemMask32(uint32_t addr, uint32_t mask, uint32_t data) { + uint8_t buff[12] = { + (uint8_t)((addr >> 24) & 0xFF), (uint8_t)((addr >> 16) & 0xFF), (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF), + (uint8_t)((mask >> 24) & 0xFF), (uint8_t)((mask >> 16) & 0xFF), (uint8_t)((mask >> 8) & 0xFF), (uint8_t)(mask & 0xFF), + (uint8_t)((data >> 24) & 0xFF), (uint8_t)((data >> 16) & 0xFF), (uint8_t)((data >> 8) & 0xFF), (uint8_t)(data & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WRITE_REG_MEM_MASK, true, buff, sizeof(buff))); +} + +int16_t LR11x0::getStatus(uint8_t* stat1, uint8_t* stat2, uint32_t* irq) { + uint8_t buff[6] = { 0 }; + + // the status check command doesn't return status in the same place as other read commands + // but only as the first byte (as with any other command), hence LR11x0::SPIcommand can't be used + // it also seems to ignore the actual command, and just sending in bunch of NOPs will work + int16_t state = this->mod->SPItransferStream(NULL, 0, false, NULL, buff, sizeof(buff), true, RADIOLIB_MODULE_SPI_TIMEOUT); + + // pass the replies + if(stat1) { *stat1 = buff[0]; } + if(stat2) { *stat2 = buff[1]; } + if(irq) { *irq = ((uint32_t)(buff[2]) << 24) | ((uint32_t)(buff[3]) << 16) | ((uint32_t)(buff[4]) << 8) | (uint32_t)buff[5]; } + + return(state); +} + +int16_t LR11x0::getVersion(uint8_t* hw, uint8_t* device, uint8_t* major, uint8_t* minor) { + uint8_t buff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_VERSION, false, buff, sizeof(buff)); + + // pass the replies + if(hw) { *hw = buff[0]; } + if(device) { *device = buff[1]; } + if(major) { *major = buff[2]; } + if(minor) { *minor = buff[3]; } + + return(state); +} + +int16_t LR11x0::getErrors(uint16_t* err) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_ERRORS, false, buff, sizeof(buff)); + + // pass the replies + if(err) { *err = ((uint16_t)(buff[0]) << 8) | (uint16_t)buff[1]; } + + return(state); +} + +int16_t LR11x0::clearErrors(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CLEAR_ERRORS, true, NULL, 0)); +} + +int16_t LR11x0::calibrate(uint8_t params) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CALIBRATE, true, ¶ms, 1)); +} + +int16_t LR11x0::setRegMode(uint8_t mode) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_REG_MODE, true, &mode, 1)); +} + +int16_t LR11x0::calibImage(float freq1, float freq2) { + uint8_t buff[2] = { + (uint8_t)floor((freq1 - 1.0f) / 4.0f), + (uint8_t)ceil((freq2 + 1.0f) / 4.0f) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CALIB_IMAGE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setDioAsRfSwitch(uint8_t en, uint8_t stbyCfg, uint8_t rxCfg, uint8_t txCfg, uint8_t txHpCfg, uint8_t txHfCfg, uint8_t gnssCfg, uint8_t wifiCfg) { + uint8_t buff[8] = { en, stbyCfg, rxCfg, txCfg, txHpCfg, txHfCfg, gnssCfg, wifiCfg }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_DIO_AS_RF_SWITCH, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setDioIrqParams(uint32_t irq1, uint32_t irq2) { + uint8_t buff[8] = { + (uint8_t)((irq1 >> 24) & 0xFF), (uint8_t)((irq1 >> 16) & 0xFF), (uint8_t)((irq1 >> 8) & 0xFF), (uint8_t)(irq1 & 0xFF), + (uint8_t)((irq2 >> 24) & 0xFF), (uint8_t)((irq2 >> 16) & 0xFF), (uint8_t)((irq2 >> 8) & 0xFF), (uint8_t)(irq2 & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_DIO_IRQ_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::clearIrq(uint32_t irq) { + uint8_t buff[4] = { + (uint8_t)((irq >> 24) & 0xFF), (uint8_t)((irq >> 16) & 0xFF), (uint8_t)((irq >> 8) & 0xFF), (uint8_t)(irq & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CLEAR_IRQ, true, buff, sizeof(buff))); +} + +int16_t LR11x0::configLfClock(uint8_t setup) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_REG_MODE, true, &setup, 1)); +} + +int16_t LR11x0::setTcxoMode(uint8_t tune, uint32_t delay) { + uint8_t buff[4] = { + tune, (uint8_t)((delay >> 16) & 0xFF), (uint8_t)((delay >> 8) & 0xFF), (uint8_t)(delay & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_TCXO_MODE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::reboot(bool stay) { + uint8_t buff[1] = { (uint8_t)stay }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_REBOOT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::getVbat(float* vbat) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_VBAT, false, buff, sizeof(buff)); + + // pass the replies + if(vbat) { *vbat = (((float)buff[0]/51.0f) - 1.0f)*1.35f; } + + return(state); +} + +int16_t LR11x0::getTemp(float* temp) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_TEMP, false, buff, sizeof(buff)); + + // pass the replies + if(temp) { + uint16_t raw = ((uint16_t)(buff[0]) << 8) | (uint16_t)buff[1]; + *temp = 25.0f - (1000.0f/1.7f)*(((float)raw/2047.0f)*1350.0f - 0.7295f); + } + + return(state); +} + +int16_t LR11x0::setFs(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_FS, true, NULL, 0)); +} + +int16_t LR11x0::getRandomNumber(uint32_t* rnd) { + uint8_t buff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_RANDOM_NUMBER, false, buff, sizeof(buff)); + + // pass the replies + if(rnd) { *rnd = ((uint32_t)(buff[0]) << 24) | ((uint32_t)(buff[1]) << 16) | ((uint32_t)(buff[2]) << 8) | (uint32_t)buff[3]; } + + return(state); +} + +int16_t LR11x0::eraseInfoPage(void) { + // only page 1 can be erased + uint8_t buff[1] = { RADIOLIB_LR11X0_INFO_PAGE }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_ERASE_INFO_PAGE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::writeInfoPage(uint16_t addr, const uint32_t* data, size_t len) { + // check maximum size + if(len > (RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t))) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // build buffers - later we need to ensure endians are correct, + // so there is probably no way to do this without copying buffers and iterating + size_t buffLen = sizeof(uint8_t) + sizeof(uint16_t) + len*sizeof(uint32_t); + #if RADIOLIB_STATIC_ONLY + uint8_t dataBuff[sizeof(uint8_t) + sizeof(uint16_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* dataBuff = new uint8_t[buffLen]; + #endif + + // set the address + dataBuff[0] = RADIOLIB_LR11X0_INFO_PAGE; + dataBuff[1] = (uint8_t)((addr >> 8) & 0xFF); + dataBuff[2] = (uint8_t)(addr & 0xFF); + + // convert endians + for(size_t i = 0; i < len; i++) { + dataBuff[3 + i] = (uint8_t)((data[i] >> 24) & 0xFF); + dataBuff[4 + i] = (uint8_t)((data[i] >> 16) & 0xFF); + dataBuff[5 + i] = (uint8_t)((data[i] >> 8) & 0xFF); + dataBuff[6 + i] = (uint8_t)(data[i] & 0xFF); + } + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WRITE_INFO_PAGE, true, dataBuff, buffLen); + #if !RADIOLIB_STATIC_ONLY + delete[] dataBuff; + #endif + return(state); +} + +int16_t LR11x0::readInfoPage(uint16_t addr, uint32_t* data, size_t len) { + // check maximum size + if(len > (RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t))) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // the request contains the address and length + uint8_t reqBuff[4] = { + RADIOLIB_LR11X0_INFO_PAGE, + (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF), + (uint8_t)len, + }; + + // build buffers - later we need to ensure endians are correct, + // so there is probably no way to do this without copying buffers and iterating + #if RADIOLIB_STATIC_ONLY + uint8_t rplBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* rplBuff = new uint8_t[len*sizeof(uint32_t)]; + #endif + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_READ_INFO_PAGE, false, rplBuff, len*sizeof(uint32_t), reqBuff, sizeof(reqBuff)); + + // convert endians + if(data && (state == RADIOLIB_ERR_NONE)) { + for(size_t i = 0; i < len; i++) { + data[i] = ((uint32_t)rplBuff[2 + i*sizeof(uint32_t)] << 24) | ((uint32_t)rplBuff[3 + i*sizeof(uint32_t)] << 16) | ((uint32_t)rplBuff[4 + i*sizeof(uint32_t)] << 8) | (uint32_t)rplBuff[5 + i*sizeof(uint32_t)]; + } + } + + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + + return(state); +} + +int16_t LR11x0::getChipEui(uint8_t* eui) { + if(!eui) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_CHIP_EUI, false, eui, RADIOLIB_LR11X0_EUI_LEN)); +} + +int16_t LR11x0::getSemtechJoinEui(uint8_t* eui) { + if(!eui) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_SEMTECH_JOIN_EUI, false, eui, RADIOLIB_LR11X0_EUI_LEN)); +} + +int16_t LR11x0::deriveRootKeysAndGetPin(uint8_t* pin) { + if(!pin) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_DERIVE_ROOT_KEYS_AND_GET_PIN, false, pin, RADIOLIB_LR11X0_PIN_LEN)); +} + +int16_t LR11x0::enableSpiCrc(bool en) { + // TODO implement this + (void)en; + // LR11X0 CRC is gen 0xA6 (0x65 but reflected), init 0xFF, input and result reflected + /*RadioLibCRCInstance.size = 8; + RadioLibCRCInstance.poly = 0xA6; + RadioLibCRCInstance.init = 0xFF; + RadioLibCRCInstance.out = 0x00; + RadioLibCRCInstance.refIn = true; + RadioLibCRCInstance.refOut = true;*/ + return(RADIOLIB_ERR_UNSUPPORTED); +} + +int16_t LR11x0::driveDiosInSleepMode(bool en) { + uint8_t buff[1] = { (uint8_t)en }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_DRIVE_DIOS_IN_SLEEP_MODE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::resetStats(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_RESET_STATS, true, NULL, 0)); +} + +int16_t LR11x0::getStats(uint16_t* nbPktReceived, uint16_t* nbPktCrcError, uint16_t* data1, uint16_t* data2) { + uint8_t buff[8] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_STATS, false, buff, sizeof(buff)); + + // pass the replies + if(nbPktReceived) { *nbPktReceived = ((uint16_t)(buff[0]) << 8) | (uint16_t)buff[1]; } + if(nbPktCrcError) { *nbPktCrcError = ((uint16_t)(buff[2]) << 8) | (uint16_t)buff[3]; } + if(data1) { *data1 = ((uint16_t)(buff[4]) << 8) | (uint16_t)buff[5]; } + if(data2) { *data2 = ((uint16_t)(buff[6]) << 8) | (uint16_t)buff[7]; } + + return(state); +} + +int16_t LR11x0::getPacketType(uint8_t* type) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_PACKET_TYPE, false, buff, sizeof(buff)); + + // pass the replies + if(type) { *type = buff[0]; } + + return(state); +} + +int16_t LR11x0::getRxBufferStatus(uint8_t* len, uint8_t* startOffset) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_RX_BUFFER_STATUS, false, buff, sizeof(buff)); + + // pass the replies + if(len) { *len = buff[0]; } + if(startOffset) { *startOffset = buff[1]; } + + return(state); +} + +int16_t LR11x0::getPacketStatusLoRa(float* rssiPkt, float* snrPkt, float* signalRssiPkt) { + uint8_t buff[3] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_PACKET_STATUS, false, buff, sizeof(buff)); + + // pass the replies + if(rssiPkt) { *rssiPkt = (float)buff[0] / -2.0f; } + if(snrPkt) { *snrPkt = (float)buff[1] / 4.0f; } + if(signalRssiPkt) { *signalRssiPkt = buff[2]; } + + return(state); +} + +int16_t LR11x0::getPacketStatusGFSK(float* rssiSync, float* rssiAvg, uint8_t* rxLen, uint8_t* stat) { + uint8_t buff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_PACKET_STATUS, false, buff, sizeof(buff)); + + // pass the replies + if(rssiSync) { *rssiSync = (float)buff[0] / -2.0f; } + if(rssiAvg) { *rssiAvg = (float)buff[1] / -2.0f; } + if(rxLen) { *rxLen = buff[2]; } + if(stat) { *stat = buff[3]; } + + return(state); +} + +int16_t LR11x0::getRssiInst(float* rssi) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_RSSI_INST, false, buff, sizeof(buff)); + + // pass the replies + if(rssi) { *rssi = (float)buff[0] / -2.0f; } + + return(state); +} + +int16_t LR11x0::setGfskSyncWord(uint8_t* sync) { + if(!sync) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_GFSK_SYNC_WORD, true, sync, RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN)); +} + +int16_t LR11x0::setLoRaPublicNetwork(bool pub) { + uint8_t buff[1] = { (uint8_t)pub }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_LORA_PUBLIC_NETWORK, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRx(uint32_t timeout) { + uint8_t buff[3] = { + (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RX, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setTx(uint32_t timeout) { + uint8_t buff[3] = { + (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_TX, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRfFrequency(uint32_t rfFreq) { + uint8_t buff[4] = { + (uint8_t)((rfFreq >> 24) & 0xFF), (uint8_t)((rfFreq >> 16) & 0xFF), + (uint8_t)((rfFreq >> 8) & 0xFF), (uint8_t)(rfFreq & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RF_FREQUENCY, true, buff, sizeof(buff))); +} + +int16_t LR11x0::autoTxRx(uint32_t delay, uint8_t intMode, uint32_t timeout) { + uint8_t buff[7] = { + (uint8_t)((delay >> 16) & 0xFF), (uint8_t)((delay >> 8) & 0xFF), (uint8_t)(delay & 0xFF), intMode, + (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_AUTO_TX_RX, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setCadParams(uint8_t symNum, uint8_t detPeak, uint8_t detMin, uint8_t cadExitMode, uint32_t timeout) { + uint8_t buff[7] = { + symNum, detPeak, detMin, cadExitMode, + (uint8_t)((timeout >> 16) & 0xFF), (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_CAD_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setPacketType(uint8_t type) { + uint8_t buff[1] = { type }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PACKET_TYPE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setModulationParamsLoRa(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro) { + uint8_t buff[4] = { sf, bw, cr, ldro }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_MODULATION_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setModulationParamsGFSK(uint32_t br, uint8_t sh, uint8_t rxBw, uint32_t freqDev) { + uint8_t buff[10] = { + (uint8_t)((br >> 24) & 0xFF), (uint8_t)((br >> 16) & 0xFF), + (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF), sh, rxBw, + (uint8_t)((freqDev >> 24) & 0xFF), (uint8_t)((freqDev >> 16) & 0xFF), + (uint8_t)((freqDev >> 8) & 0xFF), (uint8_t)(freqDev & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_MODULATION_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setModulationParamsLrFhss(uint32_t br, uint8_t sh) { + uint8_t buff[5] = { + (uint8_t)((br >> 24) & 0xFF), (uint8_t)((br >> 16) & 0xFF), + (uint8_t)((br >> 8) & 0xFF), (uint8_t)(br & 0xFF), sh + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_MODULATION_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setModulationParamsSigfox(uint32_t br, uint8_t sh) { + // same as for LR-FHSS + return(this->setModulationParamsLrFhss(br, sh)); +} + +int16_t LR11x0::setPacketParamsLoRa(uint16_t preambleLen, uint8_t hdrType, uint8_t payloadLen, uint8_t crcType, uint8_t invertIQ) { + uint8_t buff[6] = { + (uint8_t)((preambleLen >> 8) & 0xFF), (uint8_t)(preambleLen & 0xFF), + hdrType, payloadLen, crcType, invertIQ + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PACKET_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setPacketParamsGFSK(uint16_t preambleLen, uint8_t preambleDetectorLen, uint8_t syncWordLen, uint8_t addrCmp, uint8_t packType, uint8_t payloadLen, uint8_t crcType, uint8_t whiten) { + uint8_t buff[9] = { + (uint8_t)((preambleLen >> 8) & 0xFF), (uint8_t)(preambleLen & 0xFF), + preambleDetectorLen, syncWordLen, addrCmp, packType, payloadLen, crcType, whiten + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PACKET_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setPacketParamsSigfox(uint8_t payloadLen, uint16_t rampUpDelay, uint16_t rampDownDelay, uint16_t bitNum) { + uint8_t buff[7] = { + payloadLen, (uint8_t)((rampUpDelay >> 8) & 0xFF), (uint8_t)(rampUpDelay & 0xFF), + (uint8_t)((rampDownDelay >> 8) & 0xFF), (uint8_t)(rampDownDelay & 0xFF), + (uint8_t)((bitNum >> 8) & 0xFF), (uint8_t)(bitNum & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PACKET_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setTxParams(int8_t pwr, uint8_t ramp) { + uint8_t buff[2] = { (uint8_t)pwr, ramp }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_TX_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setPacketAdrs(uint8_t node, uint8_t broadcast) { + uint8_t buff[2] = { node, broadcast }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PACKET_ADRS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRxTxFallbackMode(uint8_t mode) { + uint8_t buff[1] = { mode }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RX_TX_FALLBACK_MODE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRxDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, uint8_t mode) { + uint8_t buff[7] = { + (uint8_t)((rxPeriod >> 16) & 0xFF), (uint8_t)((rxPeriod >> 8) & 0xFF), (uint8_t)(rxPeriod & 0xFF), + (uint8_t)((sleepPeriod >> 16) & 0xFF), (uint8_t)((sleepPeriod >> 8) & 0xFF), (uint8_t)(sleepPeriod & 0xFF), + mode + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RX_DUTY_CYCLE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setPaConfig(uint8_t paSel, uint8_t regPaSupply, uint8_t paDutyCycle, uint8_t paHpSel) { + uint8_t buff[4] = { paSel, regPaSupply, paDutyCycle, paHpSel }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_PA_CONFIG, true, buff, sizeof(buff))); +} + +int16_t LR11x0::stopTimeoutOnPreamble(bool stop) { + uint8_t buff[1] = { (uint8_t)stop }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_STOP_TIMEOUT_ON_PREAMBLE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setCad(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_CAD, true, NULL, 0)); +} + +int16_t LR11x0::setTxCw(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_TX_CW, true, NULL, 0)); +} + +int16_t LR11x0::setTxInfinitePreamble(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_TX_INFINITE_PREAMBLE, true, NULL, 0)); +} + +int16_t LR11x0::setLoRaSynchTimeout(uint8_t symbolNum) { + uint8_t buff[1] = { symbolNum }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_LORA_SYNCH_TIMEOUT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRangingAddr(uint32_t addr, uint8_t checkLen) { + uint8_t buff[5] = { + (uint8_t)((addr >> 24) & 0xFF), (uint8_t)((addr >> 16) & 0xFF), + (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF), checkLen + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RANGING_ADDR, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRangingReqAddr(uint32_t addr) { + uint8_t buff[4] = { + (uint8_t)((addr >> 24) & 0xFF), (uint8_t)((addr >> 16) & 0xFF), + (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RANGING_REQ_ADDR, true, buff, sizeof(buff))); +} + +int16_t LR11x0::getRangingResult(uint8_t type, float* res) { + uint8_t reqBuff[1] = { type }; + uint8_t rplBuff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_NOP, false, rplBuff, sizeof(rplBuff), reqBuff, sizeof(reqBuff)); + RADIOLIB_ASSERT(state); + + if(res) { + if(type == RADIOLIB_LR11X0_RANGING_RESULT_DISTANCE) { + uint32_t raw = ((uint32_t)(rplBuff[0]) << 24) | ((uint32_t)(rplBuff[1]) << 16) | ((uint32_t)(rplBuff[2]) << 8) | (uint32_t)rplBuff[3]; + *res = ((float)(raw*3e8))/((float)(4096*this->bandwidthKhz*1000)); + } else { + *res = (float)rplBuff[3]/2.0f; + } + } + + return(state); +} + +int16_t LR11x0::setRangingTxRxDelay(uint32_t delay) { + uint8_t buff[4] = { + (uint8_t)((delay >> 24) & 0xFF), (uint8_t)((delay >> 16) & 0xFF), + (uint8_t)((delay >> 8) & 0xFF), (uint8_t)(delay & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RANGING_TX_RX_DELAY, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setGfskCrcParams(uint32_t init, uint32_t poly) { + uint8_t buff[8] = { + (uint8_t)((init >> 24) & 0xFF), (uint8_t)((init >> 16) & 0xFF), + (uint8_t)((init >> 8) & 0xFF), (uint8_t)(init & 0xFF), + (uint8_t)((poly >> 24) & 0xFF), (uint8_t)((poly >> 16) & 0xFF), + (uint8_t)((poly >> 8) & 0xFF), (uint8_t)(poly & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_GFSK_CRC_PARAMS, true, buff, sizeof(buff))); + +} + +int16_t LR11x0::setGfskWhitParams(uint16_t seed) { + uint8_t buff[2] = { + (uint8_t)((seed >> 8) & 0xFF), (uint8_t)(seed & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_GFSK_WHIT_PARAMS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRxBoosted(bool en) { + uint8_t buff[1] = { (uint8_t)en }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RX_BOOSTED, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setRangingParameter(uint8_t symbolNum) { + // the first byte is reserved + uint8_t buff[2] = { 0x00, symbolNum }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_RANGING_PARAMETER, true, buff, sizeof(buff))); +} + +int16_t LR11x0::setLoRaSyncWord(uint8_t sync) { + uint8_t buff[1] = { sync }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_SET_LORA_SYNC_WORD, true, buff, sizeof(buff))); +} + +int16_t LR11x0::lrFhssBuildFrame(uint8_t hdrCount, uint8_t cr, uint8_t grid, bool hop, uint8_t bw, uint16_t hopSeq, int8_t devOffset, uint8_t* payload, size_t len) { + // check maximum size + const uint8_t maxLen[4][4] = { + { 189, 178, 167, 155, }, + { 151, 142, 133, 123, }, + { 112, 105, 99, 92, }, + { 74, 69, 65, 60, }, + }; + if((cr > RADIOLIB_LR11X0_LR_FHSS_CR_1_3) || ((hdrCount - 1) > (int)sizeof(maxLen[0])) || (len > maxLen[cr][hdrCount - 1])) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // build buffers + size_t buffLen = 9 + len; + #if RADIOLIB_STATIC_ONLY + uint8_t dataBuff[9 + 190]; + #else + uint8_t* dataBuff = new uint8_t[buffLen]; + #endif + + // set properties of the packet + dataBuff[0] = hdrCount; + dataBuff[1] = cr; + dataBuff[2] = RADIOLIB_LR11X0_LR_FHSS_MOD_TYPE_GMSK; + dataBuff[3] = grid; + dataBuff[4] = (uint8_t)hop; + dataBuff[5] = bw; + dataBuff[6] = (uint8_t)((hopSeq >> 8) & 0x01); + dataBuff[7] = (uint8_t)(hopSeq & 0xFF); + dataBuff[8] = devOffset; + memcpy(&dataBuff[9], payload, len); + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_LR_FHSS_BUILD_FRAME, true, dataBuff, buffLen); + #if !RADIOLIB_STATIC_ONLY + delete[] dataBuff; + #endif + return(state); +} + +int16_t LR11x0::lrFhssSetSyncWord(uint32_t sync) { + uint8_t buff[4] = { + (uint8_t)((sync >> 24) & 0xFF), (uint8_t)((sync >> 16) & 0xFF), + (uint8_t)((sync >> 8) & 0xFF), (uint8_t)(sync & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_LR_FHSS_SET_SYNC_WORD, true, buff, sizeof(buff))); +} + +int16_t LR11x0::configBleBeacon(uint8_t chan, uint8_t* payload, size_t len) { + return(this->bleBeaconCommon(RADIOLIB_LR11X0_CMD_CONFIG_BLE_BEACON, chan, payload, len)); +} + +int16_t LR11x0::getLoRaRxHeaderInfos(uint8_t* info) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GET_LORA_RX_HEADER_INFOS, false, buff, sizeof(buff)); + + // pass the replies + if(info) { *info = buff[0]; } + + return(state); +} + +int16_t LR11x0::bleBeaconSend(uint8_t chan, uint8_t* payload, size_t len) { + return(this->bleBeaconCommon(RADIOLIB_LR11X0_CMD_BLE_BEACON_SEND, chan, payload, len)); +} + +int16_t LR11x0::bleBeaconCommon(uint16_t cmd, uint8_t chan, uint8_t* payload, size_t len) { + // check maximum size + // TODO what is the actual maximum? + if(len > RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // build buffers + #if RADIOLIB_STATIC_ONLY + uint8_t dataBuff[sizeof(uint8_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* dataBuff = new uint8_t[sizeof(uint8_t) + len]; + #endif + + // set the channel + dataBuff[0] = chan; + memcpy(&dataBuff[1], payload, len); + + int16_t state = this->SPIcommand(cmd, true, dataBuff, sizeof(uint8_t) + len); + #if !RADIOLIB_STATIC_ONLY + delete[] dataBuff; + #endif + return(state); +} + +int16_t LR11x0::wifiScan(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t nbMaxRes, uint8_t nbScanPerChan, uint16_t timeout, uint8_t abortOnTimeout) { + uint8_t buff[9] = { + type, (uint8_t)((mask >> 8) & 0xFF), (uint8_t)(mask & 0xFF), + acqMode, nbMaxRes, nbScanPerChan, + (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + abortOnTimeout + }; + + // call the SPI write stream directly to skip waiting for BUSY - it will be set to high once the scan starts + return(this->mod->SPIwriteStream(RADIOLIB_LR11X0_CMD_WIFI_SCAN, buff, sizeof(buff), false, false)); +} + +int16_t LR11x0::wifiScanTimeLimit(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t nbMaxRes, uint16_t timePerChan, uint16_t timeout) { + uint8_t buff[9] = { + type, (uint8_t)((mask >> 8) & 0xFF), (uint8_t)(mask & 0xFF), + acqMode, nbMaxRes, + (uint8_t)((timePerChan >> 8) & 0xFF), (uint8_t)(timePerChan & 0xFF), + (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_SCAN_TIME_LIMIT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::wifiCountryCode(uint16_t mask, uint8_t nbMaxRes, uint8_t nbScanPerChan, uint16_t timeout, uint8_t abortOnTimeout) { + uint8_t buff[7] = { + (uint8_t)((mask >> 8) & 0xFF), (uint8_t)(mask & 0xFF), + nbMaxRes, nbScanPerChan, + (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF), + abortOnTimeout + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::wifiCountryCodeTimeLimit(uint16_t mask, uint8_t nbMaxRes, uint16_t timePerChan, uint16_t timeout) { + uint8_t buff[7] = { + (uint8_t)((mask >> 8) & 0xFF), (uint8_t)(mask & 0xFF), + nbMaxRes, + (uint8_t)((timePerChan >> 8) & 0xFF), (uint8_t)(timePerChan & 0xFF), + (uint8_t)((timeout >> 8) & 0xFF), (uint8_t)(timeout & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE_TIME_LIMIT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::wifiReadResults(uint8_t index, uint8_t nbResults, uint8_t format, uint8_t* results) { + uint8_t buff[3] = { index, nbResults, format }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS, false, results, RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN, buff, sizeof(buff))); +} + +int16_t LR11x0::wifiResetCumulTimings(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_RESET_CUMUL_TIMINGS, true, NULL, 0)); +} + +int16_t LR11x0::wifiReadCumulTimings(uint32_t* detection, uint32_t* capture, uint32_t* demodulation) { + uint8_t buff[16] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_CUMUL_TIMINGS, false, buff, sizeof(buff)); + + // pass the replies + if(detection) { *detection = ((uint32_t)(buff[4]) << 24) | ((uint32_t)(buff[5]) << 16) | ((uint32_t)(buff[6]) << 8) | (uint32_t)buff[7]; } + if(capture) { *capture = ((uint32_t)(buff[8]) << 24) | ((uint32_t)(buff[9]) << 16) | ((uint32_t)(buff[10]) << 8) | (uint32_t)buff[11]; } + if(demodulation) { *demodulation = ((uint32_t)(buff[12]) << 24) | ((uint32_t)(buff[13]) << 16) | ((uint32_t)(buff[14]) << 8) | (uint32_t)buff[15]; } + + return(state); +} + +int16_t LR11x0::wifiGetNbCountryCodeResults(uint8_t* nbResults) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_GET_NB_COUNTRY_CODE_RESULTS, false, buff, sizeof(buff)); + + // pass the replies + if(nbResults) { *nbResults = buff[0]; } + + return(state); +} + +int16_t LR11x0::wifiReadCountryCodeResults(uint8_t index, uint8_t nbResults, uint8_t* results) { + uint8_t reqBuff[2] = { index, nbResults }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_COUNTRY_CODE_RESULTS, false, results, nbResults, reqBuff, sizeof(reqBuff))); +} + +int16_t LR11x0::wifiCfgTimestampAPphone(uint32_t timestamp) { + uint8_t buff[4] = { + (uint8_t)((timestamp >> 24) & 0xFF), (uint8_t)((timestamp >> 16) & 0xFF), + (uint8_t)((timestamp >> 8) & 0xFF), (uint8_t)(timestamp & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE_TIME_LIMIT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::wifiReadVersion(uint8_t* major, uint8_t* minor) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_VERSION, false, buff, sizeof(buff)); + + // pass the replies + if(major) { *major = buff[0]; } + if(minor) { *minor = buff[1]; } + + return(state); +} + +int16_t LR11x0::gnssSetConstellationToUse(uint8_t mask) { + uint8_t buff[1] = { mask }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_SET_CONSTELLATION_TO_USE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssReadConstellationToUse(uint8_t* mask) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_CONSTELLATION_TO_USE, false, buff, sizeof(buff)); + + // pass the replies + if(mask) { *mask = buff[0]; } + + return(state); +} + +int16_t LR11x0::gnssSetAlmanacUpdate(uint8_t mask) { + uint8_t buff[1] = { mask }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_SET_ALMANAC_UPDATE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssReadAlmanacUpdate(uint8_t* mask) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_ALMANAC_UPDATE, false, buff, sizeof(buff)); + + // pass the replies + if(mask) { *mask = buff[0]; } + + return(state); +} + +int16_t LR11x0::gnssReadVersion(uint8_t* fw, uint8_t* almanac) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_VERSION, false, buff, sizeof(buff)); + + // pass the replies + if(fw) { *fw = buff[0]; } + if(almanac) { *almanac = buff[1]; } + + return(state); +} + +int16_t LR11x0::gnssReadSupportedConstellations(uint8_t* mask) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_SUPPORTED_CONSTELLATIONS, false, buff, sizeof(buff)); + + // pass the replies + if(mask) { *mask = buff[0]; } + + return(state); +} + +int16_t LR11x0::gnssSetMode(uint8_t mode) { + uint8_t buff[1] = { mode }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_SET_MODE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssAutonomous(uint32_t gpsTime, uint8_t resMask, uint8_t nbSvMask) { + uint8_t buff[7] = { + (uint8_t)((gpsTime >> 24) & 0xFF), (uint8_t)((gpsTime >> 16) & 0xFF), + (uint8_t)((gpsTime >> 8) & 0xFF), (uint8_t)(gpsTime & 0xFF), + RADIOLIB_LR11X0_GNSS_AUTO_EFFORT_MODE, resMask, nbSvMask + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_AUTONOMOUS, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssAssisted(uint32_t gpsTime, uint8_t effort, uint8_t resMask, uint8_t nbSvMask) { + uint8_t buff[7] = { + (uint8_t)((gpsTime >> 24) & 0xFF), (uint8_t)((gpsTime >> 16) & 0xFF), + (uint8_t)((gpsTime >> 8) & 0xFF), (uint8_t)(gpsTime & 0xFF), + effort, resMask, nbSvMask + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_ASSISTED, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssSetAssistancePosition(float lat, float lon) { + uint16_t latRaw = (lat*2048.0f)/90.0f + 0.5f; + uint16_t lonRaw = (lon*2048.0f)/180.0f + 0.5f; + uint8_t buff[4] = { + (uint8_t)((latRaw >> 8) & 0xFF), (uint8_t)(latRaw & 0xFF), + (uint8_t)((lonRaw >> 8) & 0xFF), (uint8_t)(lonRaw & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_SET_ASSISTANCE_POSITION, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssReadAssistancePosition(float* lat, float* lon) { + uint8_t buff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_ASSISTANCE_POSITION, false, buff, sizeof(buff)); + + // pass the replies + if(lat) { + uint16_t latRaw = ((uint16_t)(buff[0]) << 8) | (uint16_t)(buff[1]); + *lat = ((float)latRaw*90.0f)/2048.0f; + } + if(lon) { + uint16_t lonRaw = ((uint16_t)(buff[2]) << 8) | (uint16_t)(buff[3]); + *lon = ((float)lonRaw*180.0f)/2048.0f; + } + + return(state); +} + +int16_t LR11x0::gnssPushSolverMsg(uint8_t* payload, size_t len) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_PUSH_SOLVER_MSG, true, payload, len)); +} + +int16_t LR11x0::gnssPushDmMsg(uint8_t* payload, size_t len) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_PUSH_DM_MSG, true, payload, len)); +} + +int16_t LR11x0::gnssGetContextStatus(uint8_t* fwVersion, uint32_t* almanacCrc, uint8_t* errCode, uint8_t* almUpdMask, uint8_t* freqSpace) { + // send the command + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_CONTEXT_STATUS, true, NULL, 0); + RADIOLIB_ASSERT(state); + + // read the result - this requires some magic bytes first, that's why LR11x0::SPIcommand cannot be used + uint8_t cmd_buff[3] = { 0x00, 0x02, 0x18 }; + uint8_t buff[9] = { 0 }; + state = this->mod->SPItransferStream(cmd_buff, sizeof(cmd_buff), false, NULL, buff, sizeof(buff), true, RADIOLIB_MODULE_SPI_TIMEOUT); + + // pass the replies + if(fwVersion) { *fwVersion = buff[0]; } + if(almanacCrc) { *almanacCrc = ((uint32_t)(buff[1]) << 24) | ((uint32_t)(buff[2]) << 16) | ((uint32_t)(buff[3]) << 8) | (uint32_t)buff[4]; } + if(errCode) { *errCode = (buff[5] & 0xF0) >> 4; } + if(almUpdMask) { *almUpdMask = (buff[5] & 0x0E) >> 1; } + if(freqSpace) { *freqSpace = ((buff[5] & 0x01) << 1) | ((buff[6] & 0x80) >> 7); } + + return(state); +} + +int16_t LR11x0::gnssGetNbSvDetected(uint8_t* nbSv) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_NB_SV_DETECTED, false, buff, sizeof(buff)); + + // pass the replies + if(nbSv) { *nbSv = buff[0]; } + + return(state); +} + +int16_t LR11x0::gnssGetSvDetected(uint8_t* svId, uint8_t* snr, uint16_t* doppler, size_t nbSv) { + // TODO this is arbitrary - is there an actual maximum? + if(nbSv > RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t)) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + // build buffers + size_t buffLen = nbSv*sizeof(uint32_t); + #if RADIOLIB_STATIC_ONLY + uint8_t dataBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* dataBuff = new uint8_t[buffLen]; + #endif + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_SV_DETECTED, false, dataBuff, buffLen); + if(state == RADIOLIB_ERR_NONE) { + for(size_t i = 0; i < nbSv; i++) { + if(svId) { svId[i] = dataBuff[4*i]; } + if(snr) { snr[i] = dataBuff[4*i + 1]; } + if(doppler) { doppler[i] = ((uint16_t)(dataBuff[4*i + 2]) << 8) | (uint16_t)dataBuff[4*i + 3]; } + } + } + + #if !RADIOLIB_STATIC_ONLY + delete[] dataBuff; + #endif + return(state); +} + +int16_t LR11x0::gnssGetConsumption(uint32_t* cpu, uint32_t* radio) { + uint8_t buff[8] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_CONSUMPTION, false, buff, sizeof(buff)); + + // pass the replies + if(cpu) { *cpu = ((uint32_t)(buff[0]) << 24) | ((uint32_t)(buff[1]) << 16) | ((uint32_t)(buff[2]) << 8) | (uint32_t)buff[3]; } + if(radio) { *radio = ((uint32_t)(buff[4]) << 24) | ((uint32_t)(buff[5]) << 16) | ((uint32_t)(buff[6]) << 8) | (uint32_t)buff[7]; } + + return(state); +} + +int16_t LR11x0::gnssGetResultSize(uint16_t* size) { + uint8_t buff[2] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_RESULT_SIZE, false, buff, sizeof(buff)); + + // pass the replies + if(size) { *size = ((uint16_t)(buff[0]) << 8) | (uint16_t)buff[1]; } + + return(state); +} + +int16_t LR11x0::gnssReadResults(uint8_t* result, uint16_t size) { + if(!result) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_READ_RESULTS, false, result, size)); +} + +int16_t LR11x0::gnssAlmanacFullUpdateHeader(uint16_t date, uint32_t globalCrc) { + uint8_t buff[RADIOLIB_LR11X0_GNSS_ALMANAC_BLOCK_SIZE] = { + RADIOLIB_LR11X0_GNSS_ALMANAC_HEADER_ID, + (uint8_t)((date >> 8) & 0xFF), (uint8_t)(date & 0xFF), + (uint8_t)((globalCrc >> 24) & 0xFF), (uint8_t)((globalCrc >> 16) & 0xFF), + (uint8_t)((globalCrc >> 8) & 0xFF), (uint8_t)(globalCrc & 0xFF), + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_ALMANAC_FULL_UPDATE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssAlmanacFullUpdateSV(uint8_t svn, uint8_t* svnAlmanac) { + uint8_t buff[RADIOLIB_LR11X0_GNSS_ALMANAC_BLOCK_SIZE] = { svn }; + memcpy(&buff[1], svnAlmanac, RADIOLIB_LR11X0_GNSS_ALMANAC_BLOCK_SIZE - 1); + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_ALMANAC_FULL_UPDATE, true, buff, sizeof(buff))); +} + +int16_t LR11x0::gnssGetSvVisible(uint32_t time, float lat, float lon, uint8_t constellation, uint8_t* nbSv) { + uint16_t latRaw = (lat*2048.0f)/90.0f + 0.5f; + uint16_t lonRaw = (lon*2048.0f)/180.0f + 0.5f; + uint8_t reqBuff[9] = { + (uint8_t)((time >> 24) & 0xFF), (uint8_t)((time >> 16) & 0xFF), + (uint8_t)((time >> 8) & 0xFF), (uint8_t)(time & 0xFF), + (uint8_t)((latRaw >> 8) & 0xFF), (uint8_t)(latRaw & 0xFF), + (uint8_t)((lonRaw >> 8) & 0xFF), (uint8_t)(lonRaw & 0xFF), + constellation, + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_GNSS_GET_SV_VISIBLE, false, nbSv, 1, reqBuff, sizeof(reqBuff))); +} + +int16_t LR11x0::cryptoSetKey(uint8_t keyId, uint8_t* key) { + if(!key) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + uint8_t buff[1 + RADIOLIB_AES128_KEY_SIZE] = { 0 }; + buff[0] = keyId; + memcpy(&buff[1], key, RADIOLIB_AES128_KEY_SIZE); + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_SET_KEY, false, buff, sizeof(buff))); +} + +int16_t LR11x0::cryptoDeriveKey(uint8_t srcKeyId, uint8_t dstKeyId, uint8_t* key) { + if(!key) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + uint8_t buff[2 + RADIOLIB_AES128_KEY_SIZE] = { 0 }; + buff[0] = srcKeyId; + buff[1] = dstKeyId; + memcpy(&buff[2], key, RADIOLIB_AES128_KEY_SIZE); + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_DERIVE_KEY, false, buff, sizeof(buff))); +} + +int16_t LR11x0::cryptoProcessJoinAccept(uint8_t decKeyId, uint8_t verKeyId, uint8_t lwVer, uint8_t* header, uint8_t* dataIn, size_t len, uint8_t* dataOut) { + // calculate buffer sizes + size_t headerLen = 1; + if(lwVer) { + headerLen += 11; // LoRaWAN 1.1 header is 11 bytes longer than 1.0 + } + size_t reqLen = 3*sizeof(uint8_t) + headerLen + len; + size_t rplLen = sizeof(uint8_t) + len; + + // build buffers + #if RADIOLIB_STATIC_ONLY + uint8_t reqBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + uint8_t rplBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* reqBuff = new uint8_t[reqLen]; + uint8_t* rplBuff = new uint8_t[rplLen]; + #endif + + // set the request fields + reqBuff[0] = decKeyId; + reqBuff[1] = verKeyId; + reqBuff[2] = lwVer; + memcpy(&reqBuff[3], header, headerLen); + memcpy(&reqBuff[3 + headerLen], dataIn, len); + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_PROCESS_JOIN_ACCEPT, false, rplBuff, rplLen, reqBuff, reqLen); + #if !RADIOLIB_STATIC_ONLY + delete[] reqBuff; + #endif + if(state != RADIOLIB_ERR_NONE) { + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + return(state); + } + + // check the crypto engine state + if(rplBuff[0] != RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Crypto Engine error: %02x", rplBuff[0]); + return(RADIOLIB_ERR_SPI_CMD_FAILED); + } + + // pass the data + memcpy(dataOut, &rplBuff[1], len); + return(state); +} + +int16_t LR11x0::cryptoComputeAesCmac(uint8_t keyId, uint8_t* data, size_t len, uint32_t* mic) { + size_t reqLen = sizeof(uint8_t) + len; + #if RADIOLIB_STATIC_ONLY + uint8_t reqBuff[sizeof(uint8_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* reqBuff = new uint8_t[reqLen]; + #endif + uint8_t rplBuff[5] = { 0 }; + + reqBuff[0] = keyId; + memcpy(&reqBuff[1], data, len); + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_COMPUTE_AES_CMAC, false, rplBuff, sizeof(rplBuff), reqBuff, reqLen); + #if !RADIOLIB_STATIC_ONLY + delete[] reqBuff; + #endif + + // check the crypto engine state + if(rplBuff[0] != RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Crypto Engine error: %02x", rplBuff[0]); + return(RADIOLIB_ERR_SPI_CMD_FAILED); + } + + if(mic) { *mic = ((uint32_t)(rplBuff[1]) << 24) | ((uint32_t)(rplBuff[2]) << 16) | ((uint32_t)(rplBuff[3]) << 8) | (uint32_t)rplBuff[4]; } + return(state); +} + +int16_t LR11x0::cryptoVerifyAesCmac(uint8_t keyId, uint32_t micExp, uint8_t* data, size_t len, bool* result) { + size_t reqLen = sizeof(uint8_t) + sizeof(uint32_t) + len; + #if RADIOLIB_STATIC_ONLY + uint8_t reqBuff[sizeof(uint8_t) + sizeof(uint32_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* reqBuff = new uint8_t[reqLen]; + #endif + uint8_t rplBuff[1] = { 0 }; + + reqBuff[0] = keyId; + reqBuff[1] = (uint8_t)((micExp >> 24) & 0xFF); + reqBuff[2] = (uint8_t)((micExp >> 16) & 0xFF); + reqBuff[3] = (uint8_t)((micExp >> 8) & 0xFF); + reqBuff[4] = (uint8_t)(micExp & 0xFF); + memcpy(&reqBuff[5], data, len); + + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_VERIFY_AES_CMAC, false, rplBuff, sizeof(rplBuff), reqBuff, reqLen); + #if !RADIOLIB_STATIC_ONLY + delete[] reqBuff; + #endif + + // check the crypto engine state + if(rplBuff[0] != RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Crypto Engine error: %02x", rplBuff[0]); + return(RADIOLIB_ERR_SPI_CMD_FAILED); + } + + if(result) { *result = (rplBuff[0] == RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS); } + return(state); +} + +int16_t LR11x0::cryptoAesEncrypt01(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut) { + return(this->cryptoCommon(RADIOLIB_LR11X0_CMD_CRYPTO_AES_ENCRYPT_01, keyId, dataIn, len, dataOut)); +} + +int16_t LR11x0::cryptoAesEncrypt(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut) { + return(this->cryptoCommon(RADIOLIB_LR11X0_CMD_CRYPTO_AES_ENCRYPT, keyId, dataIn, len, dataOut)); +} + +int16_t LR11x0::cryptoAesDecrypt(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut) { + return(this->cryptoCommon(RADIOLIB_LR11X0_CMD_CRYPTO_AES_DECRYPT, keyId, dataIn, len, dataOut)); +} + +int16_t LR11x0::cryptoStoreToFlash(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_STORE_TO_FLASH, true, NULL, 0)); +} + +int16_t LR11x0::cryptoRestoreFromFlash(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_RESTORE_FROM_FLASH, true, NULL, 0)); +} + +int16_t LR11x0::cryptoSetParam(uint8_t id, uint32_t value) { + uint8_t buff[5] = { + id, + (uint8_t)((value >> 24) & 0xFF), (uint8_t)((value >> 16) & 0xFF), + (uint8_t)((value >> 8) & 0xFF), (uint8_t)(value & 0xFF) + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_SET_PARAM, true, buff, sizeof(buff))); +} + +int16_t LR11x0::cryptoGetParam(uint8_t id, uint32_t* value) { + uint8_t reqBuff[1] = { id }; + uint8_t rplBuff[4] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_GET_PARAM, false, rplBuff, sizeof(rplBuff), reqBuff, sizeof(reqBuff)); + RADIOLIB_ASSERT(state); + if(value) { *value = ((uint32_t)(rplBuff[0]) << 24) | ((uint32_t)(rplBuff[1]) << 16) | ((uint32_t)(rplBuff[2]) << 8) | (uint32_t)rplBuff[3]; } + return(state); +} + +int16_t LR11x0::cryptoCheckEncryptedFirmwareImage(uint32_t offset, uint32_t* data, size_t len) { + // check maximum size + if(len > (RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN/sizeof(uint32_t))) { + return(RADIOLIB_ERR_SPI_CMD_INVALID); + } + + return(this->writeCommon(RADIOLIB_LR11X0_CMD_CRYPTO_CHECK_ENCRYPTED_FIRMWARE_IMAGE, offset, data, len)); +} + +int16_t LR11x0::cryptoCheckEncryptedFirmwareImageResult(bool* result) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_CRYPTO_CHECK_ENCRYPTED_FIRMWARE_IMAGE_RESULT, false, buff, sizeof(buff)); + + // pass the replies + if(result) { *result = (bool)buff[0]; } + + return(state); +} + +int16_t LR11x0::bootEraseFlash(void) { + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_BOOT_ERASE_FLASH, true, NULL, 0)); +} + +int16_t LR11x0::bootWriteFlashEncrypted(uint32_t offset, uint32_t* data, size_t len) { + RADIOLIB_CHECK_RANGE(len, 1, 32, RADIOLIB_ERR_SPI_CMD_INVALID); + return(this->writeCommon(RADIOLIB_LR11X0_CMD_BOOT_WRITE_FLASH_ENCRYPTED, offset, data, len)); +} + +int16_t LR11x0::bootReboot(bool stay) { + uint8_t buff[1] = { (uint8_t)stay }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_BOOT_REBOOT, true, buff, sizeof(buff))); +} + +int16_t LR11x0::bootGetPin(uint8_t* pin) { + if(!pin) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_BOOT_GET_PIN, false, pin, RADIOLIB_LR11X0_PIN_LEN)); +} + +int16_t LR11x0::bootGetChipEui(uint8_t* eui) { + if(!eui) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_BOOT_GET_CHIP_EUI, false, eui, RADIOLIB_LR11X0_EUI_LEN)); +} + +int16_t LR11x0::bootGetJoinEui(uint8_t* eui) { + if(!eui) { + return(RADIOLIB_ERR_MEMORY_ALLOCATION_FAILED); + } + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_BOOT_GET_JOIN_EUI, false, eui, RADIOLIB_LR11X0_EUI_LEN)); +} + +int16_t LR11x0::writeCommon(uint16_t cmd, uint32_t addrOffset, const uint32_t* data, size_t len) { + // build buffers - later we need to ensure endians are correct, + // so there is probably no way to do this without copying buffers and iterating + size_t buffLen = sizeof(uint32_t) + len*sizeof(uint32_t); + #if RADIOLIB_STATIC_ONLY + uint8_t dataBuff[sizeof(uint32_t) + RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* dataBuff = new uint8_t[buffLen]; + #endif + + // set the address or offset + dataBuff[0] = (uint8_t)((addrOffset >> 24) & 0xFF); + dataBuff[1] = (uint8_t)((addrOffset >> 16) & 0xFF); + dataBuff[2] = (uint8_t)((addrOffset >> 8) & 0xFF); + dataBuff[3] = (uint8_t)(addrOffset & 0xFF); + + // convert endians + for(size_t i = 0; i < len; i++) { + dataBuff[4 + i] = (uint8_t)((data[i] >> 24) & 0xFF); + dataBuff[5 + i] = (uint8_t)((data[i] >> 16) & 0xFF); + dataBuff[6 + i] = (uint8_t)((data[i] >> 8) & 0xFF); + dataBuff[7 + i] = (uint8_t)(data[i] & 0xFF); + } + + int16_t state = this->SPIcommand(cmd, true, dataBuff, buffLen); + #if !RADIOLIB_STATIC_ONLY + delete[] dataBuff; + #endif + return(state); +} + +int16_t LR11x0::cryptoCommon(uint16_t cmd, uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut) { + // build buffers + #if RADIOLIB_STATIC_ONLY + uint8_t reqBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + uint8_t rplBuff[RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN]; + #else + uint8_t* reqBuff = new uint8_t[sizeof(uint8_t) + len]; + uint8_t* rplBuff = new uint8_t[sizeof(uint8_t) + len]; + #endif + + // set the request fields + reqBuff[0] = keyId; + memcpy(&reqBuff[1], dataIn, len); + + int16_t state = this->SPIcommand(cmd, false, rplBuff, sizeof(uint8_t) + len, reqBuff, sizeof(uint8_t) + len); + #if !RADIOLIB_STATIC_ONLY + delete[] reqBuff; + #endif + if(state != RADIOLIB_ERR_NONE) { + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + return(state); + } + + // check the crypto engine state + if(rplBuff[0] != RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS) { + RADIOLIB_DEBUG_BASIC_PRINTLN("Crypto Engine error: %02x", rplBuff[0]); + return(RADIOLIB_ERR_SPI_CMD_FAILED); + } + + // pass the data + memcpy(dataOut, &rplBuff[1], len); + return(state); +} + +#endif diff --git a/lib/RadioLib/src/modules/LR11x0/LR11x0.h b/lib/RadioLib/src/modules/LR11x0/LR11x0.h new file mode 100644 index 0000000..bef2e76 --- /dev/null +++ b/lib/RadioLib/src/modules/LR11x0/LR11x0.h @@ -0,0 +1,1458 @@ +#if !defined(_RADIOLIB_LR11X0_H) +#define _RADIOLIB_LR11X0_H + +#include "../../TypeDef.h" + +#if !RADIOLIB_EXCLUDE_LR11X0 + +#include "../../Module.h" + +#include "../../protocols/PhysicalLayer/PhysicalLayer.h" + +// LR11X0 physical layer properties +#define RADIOLIB_LR11X0_FREQUENCY_STEP_SIZE 1.0 +#define RADIOLIB_LR11X0_MAX_PACKET_LENGTH 255 +#define RADIOLIB_LR11X0_CRYSTAL_FREQ 32.0 +#define RADIOLIB_LR11X0_DIV_EXPONENT 25 + +// LR11X0 SPI commands +#define RADIOLIB_LR11X0_CMD_NOP (0x0000) +#define RADIOLIB_LR11X0_CMD_WRITE_REG_MEM (0x0105) +#define RADIOLIB_LR11X0_CMD_READ_REG_MEM (0x0106) +#define RADIOLIB_LR11X0_CMD_WRITE_BUFFER (0x0109) +#define RADIOLIB_LR11X0_CMD_READ_BUFFER (0x010A) +#define RADIOLIB_LR11X0_CMD_CLEAR_RX_BUFFER (0x010B) +#define RADIOLIB_LR11X0_CMD_WRITE_REG_MEM_MASK (0x010C) +#define RADIOLIB_LR11X0_CMD_GET_STATUS (0x0100) +#define RADIOLIB_LR11X0_CMD_GET_VERSION (0x0101) +#define RADIOLIB_LR11X0_CMD_GET_ERRORS (0x010D) +#define RADIOLIB_LR11X0_CMD_CLEAR_ERRORS (0x010E) +#define RADIOLIB_LR11X0_CMD_CALIBRATE (0x010F) +#define RADIOLIB_LR11X0_CMD_SET_REG_MODE (0x0110) +#define RADIOLIB_LR11X0_CMD_CALIB_IMAGE (0x0111) +#define RADIOLIB_LR11X0_CMD_SET_DIO_AS_RF_SWITCH (0x0112) +#define RADIOLIB_LR11X0_CMD_SET_DIO_IRQ_PARAMS (0x0113) +#define RADIOLIB_LR11X0_CMD_CLEAR_IRQ (0x0114) +#define RADIOLIB_LR11X0_CMD_CONFIG_LF_LOCK (0x0116) +#define RADIOLIB_LR11X0_CMD_SET_TCXO_MODE (0x0117) +#define RADIOLIB_LR11X0_CMD_REBOOT (0x0118) +#define RADIOLIB_LR11X0_CMD_GET_VBAT (0x0119) +#define RADIOLIB_LR11X0_CMD_GET_TEMP (0x011A) +#define RADIOLIB_LR11X0_CMD_SET_SLEEP (0x011B) +#define RADIOLIB_LR11X0_CMD_SET_STANDBY (0x011C) +#define RADIOLIB_LR11X0_CMD_SET_FS (0x011D) +#define RADIOLIB_LR11X0_CMD_GET_RANDOM_NUMBER (0x0120) +#define RADIOLIB_LR11X0_CMD_ERASE_INFO_PAGE (0x0121) +#define RADIOLIB_LR11X0_CMD_WRITE_INFO_PAGE (0x0122) +#define RADIOLIB_LR11X0_CMD_READ_INFO_PAGE (0x0123) +#define RADIOLIB_LR11X0_CMD_GET_CHIP_EUI (0x0125) +#define RADIOLIB_LR11X0_CMD_GET_SEMTECH_JOIN_EUI (0x0126) +#define RADIOLIB_LR11X0_CMD_DERIVE_ROOT_KEYS_AND_GET_PIN (0x0127) +#define RADIOLIB_LR11X0_CMD_ENABLE_SPI_CRC (0x0128) +#define RADIOLIB_LR11X0_CMD_DRIVE_DIOS_IN_SLEEP_MODE (0x012A) +#define RADIOLIB_LR11X0_CMD_RESET_STATS (0x0200) +#define RADIOLIB_LR11X0_CMD_GET_STATS (0x0201) +#define RADIOLIB_LR11X0_CMD_GET_PACKET_TYPE (0x0202) +#define RADIOLIB_LR11X0_CMD_GET_RX_BUFFER_STATUS (0x0203) +#define RADIOLIB_LR11X0_CMD_GET_PACKET_STATUS (0x0204) +#define RADIOLIB_LR11X0_CMD_GET_RSSI_INST (0x0205) +#define RADIOLIB_LR11X0_CMD_SET_GFSK_SYNC_WORD (0x0206) +#define RADIOLIB_LR11X0_CMD_SET_LORA_PUBLIC_NETWORK (0x0208) +#define RADIOLIB_LR11X0_CMD_SET_RX (0x0209) +#define RADIOLIB_LR11X0_CMD_SET_TX (0x020A) +#define RADIOLIB_LR11X0_CMD_SET_RF_FREQUENCY (0x020B) +#define RADIOLIB_LR11X0_CMD_AUTO_TX_RX (0x020C) +#define RADIOLIB_LR11X0_CMD_SET_CAD_PARAMS (0x020D) +#define RADIOLIB_LR11X0_CMD_SET_PACKET_TYPE (0x020E) +#define RADIOLIB_LR11X0_CMD_SET_MODULATION_PARAMS (0x020F) +#define RADIOLIB_LR11X0_CMD_SET_PACKET_PARAMS (0x0210) +#define RADIOLIB_LR11X0_CMD_SET_TX_PARAMS (0x0211) +#define RADIOLIB_LR11X0_CMD_SET_PACKET_ADRS (0x0212) +#define RADIOLIB_LR11X0_CMD_SET_RX_TX_FALLBACK_MODE (0x0213) +#define RADIOLIB_LR11X0_CMD_SET_RX_DUTY_CYCLE (0x0214) +#define RADIOLIB_LR11X0_CMD_SET_PA_CONFIG (0x0215) +#define RADIOLIB_LR11X0_CMD_STOP_TIMEOUT_ON_PREAMBLE (0x0217) +#define RADIOLIB_LR11X0_CMD_SET_CAD (0x0218) +#define RADIOLIB_LR11X0_CMD_SET_TX_CW (0x0219) +#define RADIOLIB_LR11X0_CMD_SET_TX_INFINITE_PREAMBLE (0x021A) +#define RADIOLIB_LR11X0_CMD_SET_LORA_SYNCH_TIMEOUT (0x021B) +#define RADIOLIB_LR11X0_CMD_SET_RANGING_ADDR (0x021C) +#define RADIOLIB_LR11X0_CMD_SET_RANGING_REQ_ADDR (0x021D) +#define RADIOLIB_LR11X0_CMD_GET_RANGING_RESULT (0x021E) +#define RADIOLIB_LR11X0_CMD_SET_RANGING_TX_RX_DELAY (0x021F) +#define RADIOLIB_LR11X0_CMD_SET_GFSK_CRC_PARAMS (0x0224) +#define RADIOLIB_LR11X0_CMD_SET_GFSK_WHIT_PARAMS (0x0225) +#define RADIOLIB_LR11X0_CMD_SET_RX_BOOSTED (0x0227) +#define RADIOLIB_LR11X0_CMD_SET_RANGING_PARAMETER (0x0228) +#define RADIOLIB_LR11X0_CMD_SET_LORA_SYNC_WORD (0x022B) +#define RADIOLIB_LR11X0_CMD_LR_FHSS_BUILD_FRAME (0x022C) +#define RADIOLIB_LR11X0_CMD_LR_FHSS_SET_SYNC_WORD (0x022D) +#define RADIOLIB_LR11X0_CMD_CONFIG_BLE_BEACON (0x022E) +#define RADIOLIB_LR11X0_CMD_GET_LORA_RX_HEADER_INFOS (0x0230) +#define RADIOLIB_LR11X0_CMD_BLE_BEACON_SEND (0x0231) +#define RADIOLIB_LR11X0_CMD_WIFI_SCAN (0x0300) +#define RADIOLIB_LR11X0_CMD_WIFI_SCAN_TIME_LIMIT (0x0301) +#define RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE (0x0302) +#define RADIOLIB_LR11X0_CMD_WIFI_COUNTRY_CODE_TIME_LIMIT (0x0303) +#define RADIOLIB_LR11X0_CMD_WIFI_GET_NB_RESULTS (0x0305) +#define RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS (0x0306) +#define RADIOLIB_LR11X0_CMD_WIFI_RESET_CUMUL_TIMINGS (0x0307) +#define RADIOLIB_LR11X0_CMD_WIFI_READ_CUMUL_TIMINGS (0x0308) +#define RADIOLIB_LR11X0_CMD_WIFI_GET_NB_COUNTRY_CODE_RESULTS (0x0309) +#define RADIOLIB_LR11X0_CMD_WIFI_READ_COUNTRY_CODE_RESULTS (0x030A) +#define RADIOLIB_LR11X0_CMD_WIFI_CFG_TIMESTAMP_AP_PHONE (0x030B) +#define RADIOLIB_LR11X0_CMD_WIFI_READ_VERSION (0x0320) +#define RADIOLIB_LR11X0_CMD_GNSS_SET_CONSTELLATION_TO_USE (0x0400) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_CONSTELLATION_TO_USE (0x0401) +#define RADIOLIB_LR11X0_CMD_GNSS_SET_ALMANAC_UPDATE (0x0402) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_ALMANAC_UPDATE (0x0403) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_VERSION (0x0406) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_SUPPORTED_CONSTELLATIONS (0x0407) +#define RADIOLIB_LR11X0_CMD_GNSS_SET_MODE (0x0408) +#define RADIOLIB_LR11X0_CMD_GNSS_AUTONOMOUS (0x0409) +#define RADIOLIB_LR11X0_CMD_GNSS_ASSISTED (0x040A) +#define RADIOLIB_LR11X0_CMD_GNSS_SET_ASSISTANCE_POSITION (0x0410) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_ASSISTANCE_POSITION (0x0411) +#define RADIOLIB_LR11X0_CMD_GNSS_PUSH_SOLVER_MSG (0x0414) +#define RADIOLIB_LR11X0_CMD_GNSS_PUSH_DM_MSG (0x0415) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_CONTEXT_STATUS (0x0416) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_NB_SV_DETECTED (0x0417) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_SV_DETECTED (0x0418) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_CONSUMPTION (0x0419) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_RESULT_SIZE (0x040C) +#define RADIOLIB_LR11X0_CMD_GNSS_READ_RESULTS (0x040D) +#define RADIOLIB_LR11X0_CMD_GNSS_ALMANAC_FULL_UPDATE (0x040E) +#define RADIOLIB_LR11X0_CMD_GNSS_GET_SV_VISIBLE (0x041F) +#define RADIOLIB_LR11X0_CMD_CRYPTO_SET_KEY (0x0502) +#define RADIOLIB_LR11X0_CMD_CRYPTO_DERIVE_KEY (0x0503) +#define RADIOLIB_LR11X0_CMD_CRYPTO_PROCESS_JOIN_ACCEPT (0x0504) +#define RADIOLIB_LR11X0_CMD_CRYPTO_COMPUTE_AES_CMAC (0x0505) +#define RADIOLIB_LR11X0_CMD_CRYPTO_VERIFY_AES_CMAC (0x0506) +#define RADIOLIB_LR11X0_CMD_CRYPTO_AES_ENCRYPT_01 (0x0507) +#define RADIOLIB_LR11X0_CMD_CRYPTO_AES_ENCRYPT (0x0508) +#define RADIOLIB_LR11X0_CMD_CRYPTO_AES_DECRYPT (0x0509) +#define RADIOLIB_LR11X0_CMD_CRYPTO_STORE_TO_FLASH (0x050A) +#define RADIOLIB_LR11X0_CMD_CRYPTO_RESTORE_FROM_FLASH (0x050B) +#define RADIOLIB_LR11X0_CMD_CRYPTO_SET_PARAM (0x050D) +#define RADIOLIB_LR11X0_CMD_CRYPTO_GET_PARAM (0x050E) +#define RADIOLIB_LR11X0_CMD_CRYPTO_CHECK_ENCRYPTED_FIRMWARE_IMAGE (0x050F) +#define RADIOLIB_LR11X0_CMD_CRYPTO_CHECK_ENCRYPTED_FIRMWARE_IMAGE_RESULT (0x0510) +#define RADIOLIB_LR11X0_CMD_BOOT_ERASE_FLASH (0x8000) +#define RADIOLIB_LR11X0_CMD_BOOT_WRITE_FLASH_ENCRYPTED (0x8003) +#define RADIOLIB_LR11X0_CMD_BOOT_REBOOT (0x8005) +#define RADIOLIB_LR11X0_CMD_BOOT_GET_PIN (0x800B) +#define RADIOLIB_LR11X0_CMD_BOOT_GET_CHIP_EUI (0x800C) +#define RADIOLIB_LR11X0_CMD_BOOT_GET_JOIN_EUI (0x800D) + +// LR11X0 register map +#define RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT (0x00F20414) +#define RADIOLIB_LR11X0_REG_LORA_HIGH_POWER_FIX (0x00F30054) + +// LR11X0 SPI command variables + +// RADIOLIB_LR11X0_CMD_GET_STATUS MSB LSB DESCRIPTION +#define RADIOLIB_LR11X0_STAT_1_CMD_FAIL (0x00UL << 1) // 3 1 command status: last command could not be executed +#define RADIOLIB_LR11X0_STAT_1_CMD_PERR (0x01UL << 1) // 3 1 processing error +#define RADIOLIB_LR11X0_STAT_1_CMD_OK (0x02UL << 1) // 3 1 successfully processed +#define RADIOLIB_LR11X0_STAT_1_CMD_DAT (0x03UL << 1) // 3 1 successfully processed, data is being transmitted +#define RADIOLIB_LR11X0_STAT_1_IRQ_INACTIVE (0x00UL << 0) // 0 0 interrupt status: inactive +#define RADIOLIB_LR11X0_STAT_1_IRQ_ACTIVE (0x01UL << 0) // 0 0 at least 1 interrupt active +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_CLEARED (0x00UL << 4) // 7 4 reset status: cleared +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_ANALOG (0x01UL << 4) // 7 4 analog (power on, brown-out) +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_EXTERNAL (0x02UL << 4) // 7 4 NRESET pin +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_SYSTEM (0x03UL << 4) // 7 4 system +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_WATCHDOG (0x04UL << 4) // 7 4 watchdog +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_WAKEUP (0x05UL << 4) // 7 4 NSS toggling wake-up +#define RADIOLIB_LR11X0_STAT_2_CMD_RST_RTC (0x06UL << 4) // 7 4 realtime clock +#define RADIOLIB_LR11X0_STAT_2_MODE_SLEEP (0x00UL << 1) // 3 1 chip mode: sleep +#define RADIOLIB_LR11X0_STAT_2_MODE_STBY_RC (0x01UL << 1) // 3 1 standby with RC oscillator +#define RADIOLIB_LR11X0_STAT_2_MODE_STBY_OSC (0x02UL << 1) // 3 1 standby with external oscillator +#define RADIOLIB_LR11X0_STAT_2_MODE_FS (0x03UL << 1) // 3 1 frequency synthesis +#define RADIOLIB_LR11X0_STAT_2_MODE_RX (0x04UL << 1) // 3 1 receive +#define RADIOLIB_LR11X0_STAT_2_MODE_TX (0x05UL << 1) // 3 1 transmit +#define RADIOLIB_LR11X0_STAT_2_MODE_WIFI_GNSS (0x06UL << 1) // 3 1 WiFi or GNSS geolocation +#define RADIOLIB_LR11X0_STAT_2_BOOT (0x00UL << 0) // 0 0 code executed from: bootloader +#define RADIOLIB_LR11X0_STAT_2_FLASH (0x01UL << 0) // 0 0 flash + +// RADIOLIB_LR11X0_CMD_WRITE_REG_MEM +#define RADIOLIB_LR11X0_SPI_MAX_READ_WRITE_LEN (256) // 7 0 maximum length of read/write SPI payload in bytes + +// RADIOLIB_LR11X0_CMD_GET_VERSION +#define RADIOLIB_LR11X0_HW_LR1110 (0x01UL << 0) // 7 0 HW version: LR1110 +#define RADIOLIB_LR11X0_HW_LR1120 (0x02UL << 0) // 7 0 LR1120 +#define RADIOLIB_LR11X0_HW_LR1121 (0x03UL << 0) // 7 0 LR1121 +#define RADIOLIB_LR11X0_HW_BOOT (0xDFUL << 0) // 7 0 bootloader mode + +// RADIOLIB_LR11X0_CMD_GET_ERRORS +#define RADIOLIB_LR11X0_ERROR_STAT_LF_RC_CALIB_ERR (0x01UL << 0) // 15 0 error: low frequency RC not calibrated +#define RADIOLIB_LR11X0_ERROR_STAT_HF_RC_CALIB_ERR (0x01UL << 1) // 15 0 high frequency RC not calibrated +#define RADIOLIB_LR11X0_ERROR_STAT_ADC_CALIB_ERR (0x01UL << 2) // 15 0 ADC not calibrated +#define RADIOLIB_LR11X0_ERROR_STAT_PLL_CALIB_ERR (0x01UL << 3) // 15 0 PLL not calibrated +#define RADIOLIB_LR11X0_ERROR_STAT_IMG_CALIB_ERR (0x01UL << 4) // 15 0 image rejection not calibrated +#define RADIOLIB_LR11X0_ERROR_STAT_HF_XOSC_START_ERR (0x01UL << 5) // 15 0 high frequency oscillator failed to start +#define RADIOLIB_LR11X0_ERROR_STAT_LF_XOSC_START_ERR (0x01UL << 6) // 15 0 low frequency oscillator failed to start +#define RADIOLIB_LR11X0_ERROR_STAT_PLL_LOCK_ERR (0x01UL << 7) // 15 0 PLL failed to lock +#define RADIOLIB_LR11X0_ERROR_STAT_RX_ADC_OFFSET_ERR (0x01UL << 8) // 15 0 ADC offset not calibrated + +// RADIOLIB_LR11X0_CMD_CALIBRATE +#define RADIOLIB_LR11X0_CALIBRATE_PLL_TX (0x01UL << 5) // 5 5 calibrate: Tx PLL +#define RADIOLIB_LR11X0_CALIBRATE_IMG (0x01UL << 4) // 4 4 image rejection +#define RADIOLIB_LR11X0_CALIBRATE_ADC (0x01UL << 3) // 3 3 A/D converter +#define RADIOLIB_LR11X0_CALIBRATE_PLL (0x01UL << 2) // 2 2 PLL +#define RADIOLIB_LR11X0_CALIBRATE_HF_RC (0x01UL << 1) // 1 1 high frequency RC +#define RADIOLIB_LR11X0_CALIBRATE_LF_RC (0x01UL << 0) // 0 0 low frequency RC +#define RADIOLIB_LR11X0_CALIBRATE_ALL (0x3FUL << 0) // 5 0 everything + +// RADIOLIB_LR11X0_CMD_SET_REG_MODE +#define RADIOLIB_LR11X0_REG_MODE_LDO (0x00UL << 0) // 0 0 regulator mode: LDO in all modes +#define RADIOLIB_LR11X0_REG_MODE_DC_DC (0x01UL << 0) // 0 0 DC-DC and LDO + +// RADIOLIB_LR11X0_CMD_SET_DIO_AS_RF_SWITCH +#define RADIOLIB_LR11X0_RFSW_DIO5_ENABLED (0x01UL << 0) // 4 0 RF switch: DIO5 enabled +#define RADIOLIB_LR11X0_RFSW_DIO5_DISABLED (0x00UL << 0) // 4 0 DIO5 disabled (default) +#define RADIOLIB_LR11X0_RFSW_DIO6_ENABLED (0x01UL << 1) // 4 0 RF switch: DIO6 enabled +#define RADIOLIB_LR11X0_RFSW_DIO6_DISABLED (0x00UL << 1) // 4 0 DIO6 disabled (default) +#define RADIOLIB_LR11X0_RFSW_DIO7_ENABLED (0x01UL << 2) // 4 0 RF switch: DIO7 enabled +#define RADIOLIB_LR11X0_RFSW_DIO7_DISABLED (0x00UL << 2) // 4 0 DIO7 disabled (default) +#define RADIOLIB_LR11X0_RFSW_DIO8_ENABLED (0x01UL << 3) // 4 0 RF switch: DIO8 enabled +#define RADIOLIB_LR11X0_RFSW_DIO8_DISABLED (0x00UL << 3) // 4 0 DIO8 disabled (default) +#define RADIOLIB_LR11X0_RFSW_DIO10_ENABLED (0x01UL << 4) // 4 0 RF switch: DIO10 enabled +#define RADIOLIB_LR11X0_RFSW_DIO10_DISABLED (0x00UL << 4) // 4 0 DIO10 disabled (default) + +// RADIOLIB_LR11X0_CMD_SET_DIO_IRQ_PARAMS +#define RADIOLIB_LR11X0_IRQ_TX_DONE (0x01UL << 2) // 31 0 interrupt: packet transmitted +#define RADIOLIB_LR11X0_IRQ_RX_DONE (0x01UL << 3) // 31 0 packet received +#define RADIOLIB_LR11X0_IRQ_PREAMBLE_DETECTED (0x01UL << 4) // 31 0 preamble detected +#define RADIOLIB_LR11X0_IRQ_SYNC_WORD_HEADER_VALID (0x01UL << 5) // 31 0 sync word or LoRa header valid +#define RADIOLIB_LR11X0_IRQ_HEADER_ERR (0x01UL << 6) // 31 0 LoRa header CRC error +#define RADIOLIB_LR11X0_IRQ_CRC_ERR (0x01UL << 7) // 31 0 packet CRC error +#define RADIOLIB_LR11X0_IRQ_CAD_DONE (0x01UL << 8) // 31 0 CAD completed +#define RADIOLIB_LR11X0_IRQ_CAD_DETECTED (0x01UL << 9) // 31 0 CAD detected +#define RADIOLIB_LR11X0_IRQ_TIMEOUT (0x01UL << 10) // 31 0 Rx or Tx timeout +#define RADIOLIB_LR11X0_IRQ_LR_FHSS_HOP (0x01UL << 11) // 31 0 FHSS hop +#define RADIOLIB_LR11X0_IRQ_GNSS_DONE (0x01UL << 19) // 31 0 GNSS scan finished +#define RADIOLIB_LR11X0_IRQ_WIFI_DONE (0x01UL << 20) // 31 0 WiFi scan finished +#define RADIOLIB_LR11X0_IRQ_LBD (0x01UL << 21) // 31 0 low battery detected +#define RADIOLIB_LR11X0_IRQ_CMD_ERROR (0x01UL << 22) // 31 0 command error +#define RADIOLIB_LR11X0_IRQ_ERROR (0x01UL << 23) // 31 0 some other error than CMD_ERR +#define RADIOLIB_LR11X0_IRQ_FSK_LEN_ERROR (0x01UL << 24) // 31 0 FSK packet received with length error +#define RADIOLIB_LR11X0_IRQ_FSK_ADDR_ERROR (0x01UL << 25) // 31 0 FSK packet received with address error +#define RADIOLIB_LR11X0_IRQ_LORA_RX_TIMESTAMP (0x01UL << 27) // 31 0 last LoRa symbol was received (timestamp source) +#define RADIOLIB_LR11X0_IRQ_ALL (0x0BF80FFCUL) // 31 0 all interrupts +#define RADIOLIB_LR11X0_IRQ_NONE (0x00UL << 0) // 31 0 no interrupts + +// RADIOLIB_LR11X0_CMD_CONFIG_LF_LOCK +#define RADIOLIB_LR11X0_LF_CLK_RC (0x00UL << 0) // 1 0 32.768 kHz source: RC oscillator +#define RADIOLIB_LR11X0_LF_CLK_XOSC (0x01UL << 0) // 1 0 crystal oscillator +#define RADIOLIB_LR11X0_LF_CLK_EXT (0x02UL << 0) // 1 0 external signal on DIO11 +#define RADIOLIB_LR11X0_LF_BUSY_RELEASE_DISABLED (0x00UL << 2) // 2 2 +#define RADIOLIB_LR11X0_LF_BUSY_RELEASE_ENABLED (0x01UL << 2) // 2 2 + +// RADIOLIB_LR11X0_CMD_SET_TCXO_MODE +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_1_6 (0x00UL << 0) // 2 0 TCXO supply voltage: 1.6V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_1_7 (0x01UL << 0) // 2 0 1.7V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_1_8 (0x02UL << 0) // 2 0 1.8V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_2_2 (0x03UL << 0) // 2 0 2.2V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_2_4 (0x04UL << 0) // 2 0 2.4V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_2_7 (0x05UL << 0) // 2 0 2.7V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_3_0 (0x06UL << 0) // 2 0 3.0V +#define RADIOLIB_LR11X0_TCXO_VOLTAGE_3_3 (0x07UL << 0) // 2 0 3.3V + +// RADIOLIB_LR11X0_CMD_SET_SLEEP +#define RADIOLIB_LR11X0_SLEEP_RETENTION_DISABLED (0x00UL << 0) // 0 0 configuration retention in sleep mode: disabled +#define RADIOLIB_LR11X0_SLEEP_RETENTION_ENABLED (0x01UL << 0) // 0 0 enabled +#define RADIOLIB_LR11X0_SLEEP_WAKEUP_DISABLED (0x00UL << 0) // 1 1 automated wakeup: disabled +#define RADIOLIB_LR11X0_SLEEP_WAKEUP_ENABLED (0x01UL << 0) // 1 1 enabled + +// RADIOLIB_LR11X0_CMD_SET_STANDBY +#define RADIOLIB_LR11X0_STANDBY_RC (0x00UL << 0) // 7 0 standby mode: RC oscillator +#define RADIOLIB_LR11X0_STANDBY_XOSC (0x00UL << 0) // 7 0 XTAL/TCXO oscillator + +// RADIOLIB_LR11X0_CMD_ERASE_INFO_PAGE +#define RADIOLIB_LR11X0_INFO_PAGE (1) + +// RADIOLIB_LR11X0_CMD_GET_CHIP_EUI +#define RADIOLIB_LR11X0_EUI_LEN (8) + +// RADIOLIB_LR11X0_CMD_DERIVE_ROOT_KEYS_AND_GET_PIN +#define RADIOLIB_LR11X0_PIN_LEN (4) + +// RADIOLIB_LR11X0_CMD_GET_PACKET_STATUS +#define RADIOLIB_LR11X0_RX_STATUS_ADDR_ERR (0x01UL << 5) // 7 0 Rx status: address filtering error +#define RADIOLIB_LR11X0_RX_STATUS_CRC_ERR (0x01UL << 4) // 7 0 CRC error +#define RADIOLIB_LR11X0_RX_STATUS_LEN_ERR (0x01UL << 3) // 7 0 length filtering error +#define RADIOLIB_LR11X0_RX_STATUS_ABORTED (0x01UL << 2) // 7 0 packet reception aborted +#define RADIOLIB_LR11X0_RX_STATUS_PACKET_RECEIVED (0x01UL << 1) // 7 0 packet received +#define RADIOLIB_LR11X0_RX_STATUS_PACKET_SENT (0x01UL << 0) // 7 0 packet sent + +// RADIOLIB_LR11X0_CMD_SET_GFSK_SYNC_WORD +#define RADIOLIB_LR11X0_GFSK_SYNC_WORD_LEN (8) + +// RADIOLIB_LR11X0_CMD_SET_LORA_PUBLIC_NETWORK +#define RADIOLIB_LR11X0_LORA_PRIVATE_NETWORK (0x00UL << 0) // 7 0 LoRa sync word: private network +#define RADIOLIB_LR11X0_LORA_PUBLIC_NETWORK (0x01UL << 0) // 7 0 public network + +// RADIOLIB_LR11X0_CMD_SET_RX +#define RADIOLIB_LR11X0_RX_TIMEOUT_NONE (0x000000UL) // 23 0 Rx timeout duration: no timeout (Rx single mode) +#define RADIOLIB_LR11X0_RX_TIMEOUT_INF (0xFFFFFFUL) // 23 0 infinite (Rx continuous mode) + +// RADIOLIB_LR11X0_CMD_SET_TX +#define RADIOLIB_LR11X0_TX_TIMEOUT_NONE (0x000000UL) // 23 0 disable Tx timeout + +// RADIOLIB_LR11X0_CMD_AUTO_TX_RX +#define RADIOLIB_LR11X0_AUTO_TX_RX_DISABLED (0xFFFFFFUL) // 23 0 disable auto Tx/Rx mode +#define RADIOLIB_LR11X0_AUTO_TX_RX_SKIP_INT (0x000000UL) // 23 0 skip intermediary mode +#define RADIOLIB_LR11X0_AUTO_INTERMEDIARY_MODE_SLEEP (0x00UL << 0) // 1 0 intermediary mode: sleep +#define RADIOLIB_LR11X0_AUTO_INTERMEDIARY_MODE_STBY_RC (0x01UL << 0) // 1 0 standby with RC +#define RADIOLIB_LR11X0_AUTO_INTERMEDIARY_MODE_STBY_XOSC (0x02UL << 0) // 1 0 standby with XOSC +#define RADIOLIB_LR11X0_AUTO_INTERMEDIARY_MODE_FS (0x03UL << 0) // 1 0 frequency synthesis +#define RADIOLIB_LR11X0_AUTO_TX_RX_TIMEOUT_DISABLED (0x000000UL) // 23 0 disable timeout of the second mode + +// RADIOLIB_LR11X0_CMD_SET_CAD_PARAMS +#define RADIOLIB_LR11X0_CAD_EXIT_MODE_STBY_RC (0x00UL << 0) // 7 0 mode to set after CAD: standby with RC +#define RADIOLIB_LR11X0_CAD_EXIT_MODE_RX (0x01UL << 0) // 7 0 receive if activity detected +#define RADIOLIB_LR11X0_CAD_EXIT_MODE_LBT (0x10UL << 0) // 7 0 transmit if no activity detected +#define RADIOLIB_LR11X0_CAD_PARAM_DEFAULT (0xFFUL << 0) // 7 0 used by the CAD methods to specify default parameter value + +// RADIOLIB_LR11X0_CMD_SET_PACKET_TYPE +#define RADIOLIB_LR11X0_PACKET_TYPE_NONE (0x00UL << 0) // 2 0 packet type: none +#define RADIOLIB_LR11X0_PACKET_TYPE_GFSK (0x01UL << 0) // 2 0 (G)FSK +#define RADIOLIB_LR11X0_PACKET_TYPE_LORA (0x02UL << 0) // 2 0 LoRa +#define RADIOLIB_LR11X0_PACKET_TYPE_SIGFOX (0x03UL << 0) // 2 0 Sigfox +#define RADIOLIB_LR11X0_PACKET_TYPE_LR_FHSS (0x04UL << 0) // 2 0 GMSK/LR-FHSS +#define RADIOLIB_LR11X0_PACKET_TYPE_RANGING (0x05UL << 0) // 2 0 ranging +#define RADIOLIB_LR11X0_PACKET_TYPE_BLE (0x06UL << 0) // 2 0 BLE beacon + +// RADIOLIB_LR11X0_CMD_SET_MODULATION_PARAMS +#define RADIOLIB_LR11X0_LORA_BW_62_5 (0x03UL << 0) // 7 0 LoRa bandwidth: 62.5 kHz +#define RADIOLIB_LR11X0_LORA_BW_125_0 (0x04UL << 0) // 7 0 125.0 kHz +#define RADIOLIB_LR11X0_LORA_BW_250_0 (0x05UL << 0) // 7 0 250.0 kHz +#define RADIOLIB_LR11X0_LORA_BW_500_0 (0x06UL << 0) // 7 0 500.0 kHz +#define RADIOLIB_LR11X0_LORA_CR_4_5_SHORT (0x01UL << 0) // 7 0 coding rate: 4/5 with short interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_6_SHORT (0x02UL << 0) // 7 0 4/6 with short interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_7_SHORT (0x03UL << 0) // 7 0 4/7 with short interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_8_SHORT (0x04UL << 0) // 7 0 4/8 with short interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_5_LONG (0x05UL << 0) // 7 0 4/5 with long interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_6_LONG (0x06UL << 0) // 7 0 4/6 with long interleaver +#define RADIOLIB_LR11X0_LORA_CR_4_8_LONG (0x07UL << 0) // 7 0 4/8 with long interleaver +#define RADIOLIB_LR11X0_LORA_LDRO_DISABLED (0x00UL << 0) // 7 0 low data rate optimize: disabled +#define RADIOLIB_LR11X0_LORA_LDRO_ENABLED (0x01UL << 0) // 7 0 enabled +#define RADIOLIB_LR11X0_GFSK_BIT_RATE_DIV_DISABLED (0x00UL << 31) // 31 0 divide bit rate value by 256: disabled +#define RADIOLIB_LR11X0_GFSK_BIT_RATE_DIV_ENABLED (0x01UL << 31) // 31 0 enabled +#define RADIOLIB_LR11X0_GFSK_SHAPING_NONE (0x00UL << 0) // 7 0 shaping filter: none +#define RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_3 (0x08UL << 0) // 7 0 Gaussian, BT = 0.3 +#define RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_5 (0x09UL << 0) // 7 0 Gaussian, BT = 0.5 +#define RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_0_7 (0x0AUL << 0) // 7 0 Gaussian, BT = 0.7 +#define RADIOLIB_LR11X0_GFSK_SHAPING_GAUSSIAN_BT_1_0 (0x0BUL << 0) // 7 0 Gaussian, BT = 1.0 +#define RADIOLIB_LR11X0_GFSK_SHAPING_RAISED_COSINE_BT_0_7 (0x16UL << 0) // 7 0 raised cosine, BT = 0.7 +#define RADIOLIB_LR11X0_GFSK_RX_BW_4_8 (0x1FUL << 0) // 7 0 GFSK Rx bandwidth: 4.8 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_5_8 (0x17UL << 0) // 7 0 5.8 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_7_3 (0x0FUL << 0) // 7 0 7.3 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_9_7 (0x1EUL << 0) // 7 0 9.7 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_11_7 (0x16UL << 0) // 7 0 11.7 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_14_6 (0x0EUL << 0) // 7 0 14.6 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_19_5 (0x1DUL << 0) // 7 0 19.5 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_23_4 (0x15UL << 0) // 7 0 23.4 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_29_3 (0x0DUL << 0) // 7 0 29.3 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_39_0 (0x1CUL << 0) // 7 0 39.0 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_46_9 (0x14UL << 0) // 7 0 46.9 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_58_6 (0x0CUL << 0) // 7 0 58.6 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_78_2 (0x1BUL << 0) // 7 0 78.2 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_93_8 (0x13UL << 0) // 7 0 93.8 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_117_3 (0x0BUL << 0) // 7 0 117.3 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_156_2 (0x1AUL << 0) // 7 0 156.2 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_187_2 (0x12UL << 0) // 7 0 187.2 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_234_3 (0x0AUL << 0) // 7 0 234.3 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_312_0 (0x19UL << 0) // 7 0 312.0 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_373_6 (0x11UL << 0) // 7 0 373.6 kHz +#define RADIOLIB_LR11X0_GFSK_RX_BW_467_0 (0x09UL << 0) // 7 0 467.0 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BIT_RATE (488.28215) // 31 0 LR FHSS bit rate: 488.28215 bps +#define RADIOLIB_LR11X0_LR_FHSS_BIT_RATE_RAW (0x8001E848UL) // 31 0 488.28215 bps in raw +#define RADIOLIB_LR11X0_LR_FHSS_SHAPING_GAUSSIAN_BT_1_0 (0x0BUL << 0) // 7 0 shaping filter: Gaussian, BT = 1.0 +#define RADIOLIB_LR11X0_SIGFOX_SHAPING_GAUSSIAN_BT_0_7 (0x16UL << 0) // 7 0 shaping filter: Gaussian, BT = 0.7 + +// RADIOLIB_LR11X0_CMD_SET_PACKET_PARAMS +#define RADIOLIB_LR11X0_LORA_HEADER_EXPLICIT (0x00UL << 0) // 7 0 LoRa header mode: explicit +#define RADIOLIB_LR11X0_LORA_HEADER_IMPLICIT (0x01UL << 0) // 7 0 implicit +#define RADIOLIB_LR11X0_LORA_PAYLOAD_LEN_ANY (0x00UL << 0) // 7 0 accept any payload length +#define RADIOLIB_LR11X0_LORA_CRC_ENABLED (0x01UL << 0) // 7 0 CRC: enabled +#define RADIOLIB_LR11X0_LORA_CRC_DISABLED (0x00UL << 0) // 7 0 disabled +#define RADIOLIB_LR11X0_LORA_IQ_STANDARD (0x00UL << 0) // 7 0 IQ setup: standard +#define RADIOLIB_LR11X0_LORA_IQ_INVERTED (0x01UL << 0) // 7 0 inverted +#define RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_DISABLED (0x00UL << 0) // 7 0 preamble detector: disabled +#define RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_8_BITS (0x04UL << 0) // 7 0 8 bits +#define RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_16_BITS (0x05UL << 0) // 7 0 16 bits +#define RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_24_BITS (0x06UL << 0) // 7 0 24 bits +#define RADIOLIB_LR11X0_GFSK_PREAMBLE_DETECT_32_BITS (0x07UL << 0) // 7 0 32 bits +#define RADIOLIB_LR11X0_GFSK_ADDR_FILTER_DISABLED (0x00UL << 0) // 7 0 address filtering: disabled +#define RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE (0x01UL << 0) // 7 0 node address +#define RADIOLIB_LR11X0_GFSK_ADDR_FILTER_NODE_BROADCAST (0x02UL << 0) // 7 0 node and broadcast address +#define RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_FIXED (0x00UL << 0) // 7 0 packet length: fixed +#define RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_VARIABLE (0x01UL << 0) // 7 0 variable +#define RADIOLIB_LR11X0_GFSK_PACKET_LENGTH_VARIABLE_SX128X (0x02UL << 0) // 7 0 variable, SX128x 9-bit length encoding +#define RADIOLIB_LR11X0_GFSK_PAYLOAD_LEN_ANY (0x00UL << 0) // 7 0 accept any payload length +#define RADIOLIB_LR11X0_GFSK_CRC_DISABLED (0x01UL << 0) // 7 0 CRC: disabled +#define RADIOLIB_LR11X0_GFSK_CRC_1_BYTE (0x00UL << 0) // 7 0 1-byte +#define RADIOLIB_LR11X0_GFSK_CRC_2_BYTE (0x02UL << 0) // 7 0 2-byte +#define RADIOLIB_LR11X0_GFSK_CRC_1_BYTE_INV (0x04UL << 0) // 7 0 1-byte, inverted +#define RADIOLIB_LR11X0_GFSK_CRC_2_BYTE_INV (0x06UL << 0) // 7 0 2-byte, inverted +#define RADIOLIB_LR11X0_GFSK_WHITENING_DISABLED (0x00UL << 0) // 7 0 whitening: disabled +#define RADIOLIB_LR11X0_GFSK_WHITENING_ENABLED (0x01UL << 0) // 7 0 enabled + +// RADIOLIB_LR11X0_CMD_SET_TX_PARAMS +#define RADIOLIB_LR11X0_PA_RAMP_48U (0x02UL << 0) // 7 0 PA ramp time: 48 us + +// RADIOLIB_LR11X0_CMD_SET_RX_TX_FALLBACK_MODE +#define RADIOLIB_LR11X0_FALLBACK_MODE_STBY_RC (0x01UL << 0) // 1 0 fallback mode after Rx/Tx: standby with RC +#define RADIOLIB_LR11X0_FALLBACK_MODE_STBY_XOSC (0x02UL << 0) // 1 0 standby with XOSC +#define RADIOLIB_LR11X0_FALLBACK_MODE_FS (0x03UL << 0) // 1 0 frequency synthesis + +// RADIOLIB_LR11X0_CMD_SET_RX_DUTY_CYCLE +#define RADIOLIB_LR11X0_RX_DUTY_CYCLE_MODE_RX (0x00UL << 0) // 0 0 mode in Rx windows: Rx (default) +#define RADIOLIB_LR11X0_RX_DUTY_CYCLE_MODE_CAD (0x01UL << 0) // 0 0 CAD +#define RADIOLIB_LR11X0_TIMING_STEP (1.0f/32768.0f) // 23 0 timing step fo delays + +// RADIOLIB_LR11X0_CMD_SET_PA_CONFIG +#define RADIOLIB_LR11X0_PA_SEL_LP (0x00UL << 0) // 7 0 PA select: low power PA +#define RADIOLIB_LR11X0_PA_SEL_HP (0x01UL << 0) // 7 0 high power PA +#define RADIOLIB_LR11X0_PA_SEL_HF (0x02UL << 0) // 7 0 high frequency PA +#define RADIOLIB_LR11X0_PA_SUPPLY_INTERNAL (0x00UL << 0) // 7 0 PA power source: internal +#define RADIOLIB_LR11X0_PA_SUPPLY_VBAT (0x01UL << 0) // 7 0 VBAT (required for >= 14 dBm) + +// RADIOLIB_LR11X0_CMD_STOP_TIMEOUT_ON_PREAMBLE +#define RADIOLIB_LR11X0_STOP_ON_SYNC_HEADER (0x00UL << 0) // 0 0 stop timeout on: sync word or header (default) +#define RADIOLIB_LR11X0_STOP_ON_PREAMBLE (0x01UL << 0) // 0 0 preamble + +// RADIOLIB_LR11X0_CMD_GET_RANGING_RESULT +#define RADIOLIB_LR11X0_RANGING_RESULT_DISTANCE (0) // 7 0 ranging result type: distance +#define RADIOLIB_LR11X0_RANGING_RESULT_RSSI (1) // 7 0 RSSI + +// RADIOLIB_LR11X0_CMD_SET_RX_BOOSTED +#define RADIOLIB_LR11X0_RX_BOOSTED_ENABLED (0x01UL << 0) // 0 0 Rx boosted mode: enabled +#define RADIOLIB_LR11X0_RX_BOOSTED_DISABLED (0x00UL << 0) // 0 0 disabled + +// RADIOLIB_LR11X0_CMD_SET_LORA_SYNC_WORD +#define RADIOLIB_LR11X0_LORA_SYNC_WORD_PRIVATE (0x12) +#define RADIOLIB_LR11X0_LORA_SYNC_WORD_PUBLIC (0x34) + +// RADIOLIB_LR11X0_CMD_LR_FHSS_BUILD_FRAME +#define RADIOLIB_LR11X0_LR_FHSS_CR_5_6 (0x00UL << 0) // 7 0 LR FHSS coding rate: 5/6 +#define RADIOLIB_LR11X0_LR_FHSS_CR_2_3 (0x01UL << 0) // 7 0 2/3 +#define RADIOLIB_LR11X0_LR_FHSS_CR_1_2 (0x02UL << 0) // 7 0 1/2 +#define RADIOLIB_LR11X0_LR_FHSS_CR_1_3 (0x03UL << 0) // 7 0 1/3 +#define RADIOLIB_LR11X0_LR_FHSS_MOD_TYPE_GMSK (0x00UL << 0) // 7 0 LR FHSS modulation: GMSK +#define RADIOLIB_LR11X0_LR_FHSS_GRID_STEP_FCC (0x00UL << 0) // 7 0 LR FHSS step size: 25.390625 kHz (FCC) +#define RADIOLIB_LR11X0_LR_FHSS_GRID_STEP_NON_FCC (0x01UL << 0) // 7 0 3.90625 kHz (non-FCC) +#define RADIOLIB_LR11X0_LR_FHSS_HOPPING_DISABLED (0x00UL << 0) // 7 0 LR FHSS hopping: disabled +#define RADIOLIB_LR11X0_LR_FHSS_HOPPING_ENABLED (0x01UL << 0) // 7 0 enabled +#define RADIOLIB_LR11X0_LR_FHSS_BW_39_06 (0x00UL << 0) // 7 0 LR FHSS bandwidth: 39.06 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_85_94 (0x01UL << 0) // 7 0 85.94 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_136_72 (0x02UL << 0) // 7 0 136.72 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_183_59 (0x03UL << 0) // 7 0 183.59 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_335_94 (0x04UL << 0) // 7 0 335.94 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_386_72 (0x05UL << 0) // 7 0 386.72 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_722_66 (0x06UL << 0) // 7 0 722.66 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_773_44 (0x07UL << 0) // 7 0 773.44 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_1523_4 (0x08UL << 0) // 7 0 1523.4 kHz +#define RADIOLIB_LR11X0_LR_FHSS_BW_1574_2 (0x09UL << 0) // 7 0 1574.2 kHz + +// RADIOLIB_LR11X0_CMD_GET_LORA_RX_HEADER_INFOS +#define RADIOLIB_LR11X0_LAST_HEADER_CRC_ENABLED (0x01UL << 4) // 4 4 last header CRC: enabled +#define RADIOLIB_LR11X0_LAST_HEADER_CRC_DISABLED (0x00UL << 4) // 4 4 disabled + +// RADIOLIB_LR11X0_CMD_WIFI_SCAN +#define RADIOLIB_LR11X0_WIFI_SCAN_802_11_B (0x01UL << 0) // 7 0 Wi-Fi type to scan: 802.11b +#define RADIOLIB_LR11X0_WIFI_SCAN_802_11_G (0x02UL << 0) // 7 0 802.11g +#define RADIOLIB_LR11X0_WIFI_SCAN_802_11_N (0x03UL << 0) // 7 0 802.11n +#define RADIOLIB_LR11X0_WIFI_SCAN_ALL (0x04UL << 0) // 7 0 all (802.11b first) +#define RADIOLIB_LR11X0_WIFI_ACQ_MODE_BEACON_ONLY (0x01UL << 0) // 7 0 Wi-Fi acquisition mode: beacon only +#define RADIOLIB_LR11X0_WIFI_ACQ_MODE_BEACON_PACKET (0x02UL << 0) // 7 0 beacon and packet +#define RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_TRAFFIC (0x03UL << 0) // 7 0 full traffic +#define RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON (0x04UL << 0) // 7 0 full beacon +#define RADIOLIB_LR11X0_WIFI_ACQ_MODE_SSID_BEACON (0x05UL << 0) // 7 0 SSID beacon +#define RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_ENABLED (0x01UL << 0) // 7 0 abort scanning on preamble timeout: enabled +#define RADIOLIB_LR11X0_WIFI_ABORT_ON_TIMEOUT_DISABLED (0x00UL << 0) // 7 0 disabled +#define RADIOLIB_LR11X0_WIFI_MAX_NUM_RESULTS (32) // 7 0 maximum possible number of Wi-Fi scan results +#define RADIOLIB_LR11X0_WIFI_ALL_CHANNELS (0x3FFFUL) // 16 0 scan all channels + +// RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS +#define RADIOLIB_LR11X0_WIFI_RESULT_TYPE_COMPLETE (0x01UL << 0) // 7 0 Wi-Fi scan result type: complete +#define RADIOLIB_LR11X0_WIFI_RESULT_TYPE_BASIC (0x04UL << 0) // 7 0 basic +#define RADIOLIB_LR11X0_WIFI_RESULT_MAX_LEN (79) // 7 0 maximum possible Wi-Fi scan size +#define RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN (6) // 7 0 MAC address length in bytes +#define RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN (32) // 7 0 SSID length in bytes + +// RADIOLIB_LR11X0_CMD_GNSS_SET_CONSTELLATION_TO_USE +#define RADIOLIB_LR11X0_GNSS_CONSTELLATION_GPS (0x01UL << 0) // 7 0 GNSS constellation to use: GPS +#define RADIOLIB_LR11X0_GNSS_CONSTELLATION_BEIDOU (0x01UL << 1) // 7 0 BeiDou + +// RADIOLIB_LR11X0_CMD_GNSS_SET_MODE +#define RADIOLIB_LR11X0_GNSS_MODE_SINGLE_SCAN (0x00UL << 0) // 7 0 GNSS scanning mode: single/legacy +#define RADIOLIB_LR11X0_GNSS_MODE_SINGLE_MULTIPLE (0x03UL << 1) // 7 0 multiple/advanced + +// RADIOLIB_LR11X0_CMD_GNSS_AUTONOMOUS +#define RADIOLIB_LR11X0_GNSS_RES_PSEUDO_DOPPLER_ENABLED (0x01UL << 0) // 0 0 GNSS results in NAV message: pseudo-range (in single scan mode) or Doppler information (in multiple scan mode) +#define RADIOLIB_LR11X0_GNSS_RES_PSEUDO_DOPPLER_DISABLED (0x00UL << 0) // 0 0 not included +#define RADIOLIB_LR11X0_GNSS_RES_DOPPLER_ENABLED (0x01UL << 1) // 1 1 Doppler information +#define RADIOLIB_LR11X0_GNSS_RES_DOPPLER_DISABLED (0x00UL << 1) // 1 1 not included +#define RADIOLIB_LR11X0_GNSS_NB_SV_ALL (0x00UL << 0) // 7 0 include all detected satellites +#define RADIOLIB_LR11X0_GNSS_AUTO_EFFORT_MODE (0x00UL << 0) // 7 0 reserved, always 0 + +// RADIOLIB_LR11X0_CMD_GNSS_ASSISTED +#define RADIOLIB_LR11X0_GNSS_ASSIST_LOW_POWER (0x00UL << 0) // 7 0 effort mode: low power +#define RADIOLIB_LR11X0_GNSS_ASSIST_BEST_EFFORT (0x01UL << 0) // 7 0 best effort + +// RADIOLIB_LR11X0_CMD_GNSS_GET_CONTEXT_STATUS +#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_NONE (0x00UL << 0) // 7 4 error code: none +#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_ALMANAC_OLD (0x01UL << 0) // 7 4 almanac too old +#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_ALMANAC_CRC (0x02UL << 0) // 7 4 almanac CRC mismatch +#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_FLASH (0x03UL << 0) // 7 4 flash integrity error +#define RADIOLIB_LR11X0_GNSS_CONTEXT_ERR_ALMANAC_UPD (0x04UL << 0) // 7 4 almanac update not allowed +#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_250_HZ (0x00UL << 0) // 8 7 frequency search space: 250 Hz +#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_500_HZ (0x01UL << 0) // 8 7 500 H +#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_1000_HZ (0x02UL << 0) // 8 7 1000 Hz +#define RADIOLIB_LR11X0_GNSS_CONTEXT_FREQ_SPACE_2000_HZ (0x03UL << 0) // 8 7 2000 Hz + +// RADIOLIB_LR11X0_CMD_GNSS_GET_SV_VISIBLE +#define RADIOLIB_LR11X0_SV_CONSTELLATION_GPS (0x00UL << 0) // 7 0 GNSS constellation: GPS +#define RADIOLIB_LR11X0_SV_CONSTELLATION_BEIDOU (0x01UL << 0) // 7 0 BeiDou + +// RADIOLIB_LR11X0_CMD_GNSS_ALMANAC_FULL_UPDATE +#define RADIOLIB_LR11X0_GNSS_ALMANAC_HEADER_ID (0x80UL << 0) // 7 0 starting byte of GNSS almanac header +#define RADIOLIB_LR11X0_GNSS_ALMANAC_BLOCK_SIZE (20) + +// RADIOLIB_LR11X0_CMD_CRYPTO_SET_KEY +#define RADIOLIB_LR11X0_CRYPTO_STATUS_SUCCESS (0x00UL << 0) // 7 0 crypto engine status: success +#define RADIOLIB_LR11X0_CRYPTO_STATUS_FAIL_CMAC (0x01UL << 0) // 7 0 MIC check failed +#define RADIOLIB_LR11X0_CRYPTO_STATUS_INV_KEY_ID (0x03UL << 0) // 7 0 key/parameter source or destination ID error +#define RADIOLIB_LR11X0_CRYPTO_STATUS_BUF_SIZE (0x05UL << 0) // 7 0 data buffer size invalid +#define RADIOLIB_LR11X0_CRYPTO_STATUS_ERROR (0x06UL << 0) // 7 0 generic error + +// RADIOLIB_LR11X0_CMD_CRYPTO_PROCESS_JOIN_ACCEPT +#define RADIOLIB_LR11X0_CRYPTO_LORAWAN_VERSION_1_0 (0x00UL << 0) // 7 0 LoRaWAN version: 1.0.x +#define RADIOLIB_LR11X0_CRYPTO_LORAWAN_VERSION_1_1 (0x01UL << 0) // 7 0 1.1 + +// LR11X0 SPI register variables + +// RADIOLIB_LR11X0_REG_SF6_SX127X_COMPAT +#define RADIOLIB_LR11X0_SF6_SX126X (0x00UL << 18) // 18 18 SF6 mode: SX126x series +#define RADIOLIB_LR11X0_SF6_SX127X (0x01UL << 18) // 18 18 SX127x series + +// RADIOLIB_LR11X0_REG_LORA_HIGH_POWER_FIX +#define RADIOLIB_LR11X0_LORA_HIGH_POWER_FIX (0x00UL << 30) // 30 30 fix for errata + +/*! + \struct LR11x0WifiResult_t + \brief Structure to save result of passive WiFi scan. + This result only saves the basic information. +*/ +struct LR11x0WifiResult_t { + /*! \brief WiFi (802.11) signal type, 'b', 'n' or 'g' */ + char type; + + /*! \brief Data rate ID holding information about modulation and coding rate. See LR11x0 user manual for details. */ + uint8_t dataRateId; + + /*! \brief Channel frequency in MHz */ + uint16_t channelFreq; + + /*! \brief MAC address origin: from gateway (1), phone (2) or undetermined (3) */ + uint8_t origin; + + /*! \brief Whether this signal was sent by an access point (true) or end device (false) */ + bool ap; + + /*! \brief RSSI in dBm */ + float rssi; + + /*! \brief MAC address */ + uint8_t mac[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; +}; + +/*! + \struct LR11x0WifiResultFull_t + \brief Structure to save result of passive WiFi scan. + This result saves additional information alongside that in LR11x0WifiResult_t. +*/ +struct LR11x0WifiResultFull_t: public LR11x0WifiResult_t { + /*! \brief Frame type. See LR11x0 user manual for details. */ + uint8_t frameType; + + /*! \brief Frame sub type. See LR11x0 user manual for details. */ + uint8_t frameSubType; + + /*! \brief Frame sent from client station to distribution system. */ + bool toDistributionSystem; + + /*! \brief Frame sent from distribution system to client station. */ + bool fromDistributionSystem; + + /*! \brief See LR11x0 user manual for details. */ + uint16_t phiOffset; + + /*! \brief Number of microseconds the AP has been active. */ + uint64_t timestamp; + + /*! \brief Beacon period in microseconds. */ + uint32_t periodBeacon; +}; + +/*! + \struct LR11x0WifiResultExtended_t + \brief Structure to save result of passive WiFi scan. + This result saves additional information alongside that in LR11x0WifiResultFull_t. + Only scans performed with RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON acquisition mode + can yield this result! +*/ +struct LR11x0WifiResultExtended_t: public LR11x0WifiResultFull_t { + /*! \brief Data rate. See LR11x0 user manual for details. */ + uint8_t rate; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t service; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t length; + + /*! \brief MAC address 0 */ + uint8_t mac0[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; + + /*! \brief MAC address 2 */ + uint8_t mac2[RADIOLIB_LR11X0_WIFI_RESULT_MAC_LEN]; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint16_t seqCtrl; + + /*! \brief SSID */ + uint8_t ssid[RADIOLIB_LR11X0_WIFI_RESULT_SSID_LEN]; + + /*! \brief WiFi channel number */ + uint8_t currentChannel; + + /*! \brief Two-letter country code (null-terminated string). */ + char countryCode[3]; + + /*! \brief Refer to IEEE Std 802.11, 2016, Part 11: Wireless LAN MAC and PHY Spec. */ + uint8_t ioReg; + + /*! \brief True if frame check sequences is valid, false otherwise. */ + bool fcsCheckOk; +}; + +/*! + \class LR11x0 + \brief Base class for %LR11x0 series. All derived classes for %LR11x0 (e.g. LR1110 or LR1120) inherit from this base class. + This class should not be instantiated directly from user code, only from its derived classes. +*/ +class LR11x0: public PhysicalLayer { + public: + // introduce PhysicalLayer overloads + using PhysicalLayer::transmit; + using PhysicalLayer::receive; + using PhysicalLayer::startTransmit; + using PhysicalLayer::readData; + + /*! + \brief Default constructor. + \param mod Instance of Module that will be used to communicate with the radio. + */ + explicit LR11x0(Module* mod); + + /*! + \brief Whether the module has an XTAL (true) or TCXO (false). Defaults to false. + */ + bool XTAL; + + /*! + \brief Initialization method for LoRa modem. + \param bw LoRa bandwidth in kHz. + \param sf LoRa spreading factor. + \param cr LoRa coding rate denominator. + \param syncWord 1-byte LoRa sync word. + \param power Output power in dBm. + \param preambleLength LoRa preamble length in symbols + \param tcxoVoltage TCXO reference voltage to be set. + \returns \ref status_codes + */ + int16_t begin(float bw, uint8_t sf, uint8_t cr, uint8_t syncWord, int8_t power, uint16_t preambleLength, float tcxoVoltage); + + /*! + \brief Initialization method for FSK modem. + \param br FSK bit rate in kbps. + \param freqDev Frequency deviation from carrier frequency in kHz. + \param rxBw Receiver bandwidth in kHz. + \param power Output power in dBm. + \param preambleLength FSK preamble length in bits. + \param tcxoVoltage TCXO reference voltage to be set. + \returns \ref status_codes + */ + int16_t beginGFSK(float br, float freqDev, float rxBw, int8_t power, uint16_t preambleLength, float tcxoVoltage); + + /*! + \brief Initialization method for LR-FHSS modem. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. + \param power Output power in dBm. + \param tcxoVoltage TCXO reference voltage to be set. + \returns \ref status_codes + */ + int16_t beginLRFHSS(uint8_t bw, uint8_t cr, int8_t power, float tcxoVoltage); + + /*! + \brief Reset method. Will reset the chip to the default state using RST pin. + \returns \ref status_codes + */ + int16_t reset(); + + /*! + \brief Blocking binary transmit method. + Overloads for string-based transmissions are implemented in PhysicalLayer. + \param data Binary data to be sent. + \param len Number of bytes to send. + \param addr Address to send the data to. Will only be added if address filtering was enabled. + \returns \ref status_codes + */ + int16_t transmit(uint8_t* data, size_t len, uint8_t addr = 0) override; + + /*! + \brief Blocking binary receive method. + Overloads for string-based transmissions are implemented in PhysicalLayer. + \param data Binary data to be sent. + \param len Number of bytes to send. + \returns \ref status_codes + */ + int16_t receive(uint8_t* data, size_t len) override; + + /*! + \brief Starts 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. Only implemented for PhysicalLayer compatibility, as %SX126x series does not support direct mode reception. + Will always return RADIOLIB_ERR_UNKNOWN. + \returns \ref status_codes + */ + int16_t receiveDirect() override; + + /*! + \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. + \returns \ref status_codes + */ + int16_t scanChannel() override; + + /*! + \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. + \param symbolNum Number of symbols for CAD detection. + \param detPeak Peak value for CAD detection. + \param detMin Minimum value for CAD detection. + \returns \ref status_codes + */ + int16_t scanChannel(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + + /*! + \brief Sets the module to standby mode (overload for PhysicalLayer compatibility, uses 13 MHz RC oscillator). + \returns \ref status_codes + */ + int16_t standby() override; + + /*! + \brief Sets the module to standby mode. + \param mode Oscillator to be used in standby mode. Can be set to RADIOLIB_LR11X0_STANDBY_RC (13 MHz RC oscillator) + or RADIOLIB_LR11X0_STANDBY_XOSC (32 MHz external crystal oscillator). + \param wakeup Whether to force the module to wake up. Setting to true will immediately attempt to wake up the module. + \returns \ref status_codes + */ + int16_t standby(uint8_t mode, bool wakeup = true); + + /*! + \brief Sets the module to sleep mode. To wake the device up, call standby(). + \param retainConfig Set to true to retain configuration of the currently active modem ("warm start") + or to false to discard current configuration ("cold start"). Defaults to true. + \param sleepTime Sleep duration (enables automatic wakeup), in multiples of 30.52 us. Ignored if set to 0. + \returns \ref status_codes + */ + int16_t sleep(bool retainConfig = true, uint32_t sleepTime = 0); + + // interrupt methods + + /*! + \brief Sets interrupt service routine to call when IRQ1 activates. + \param func ISR to call. + */ + void setIrqAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when IRQ1 activates. + */ + void clearIrqAction(); + + /*! + \brief Sets interrupt service routine to call when a packet is received. + \param func ISR to call. + */ + void setPacketReceivedAction(void (*func)(void)) override; + + /*! + \brief Clears interrupt service routine to call when a packet is received. + */ + void clearPacketReceivedAction() override; + + /*! + \brief Sets interrupt service routine to call when a packet is sent. + \param func ISR to call. + */ + void setPacketSentAction(void (*func)(void)) override; + + /*! + \brief Clears interrupt service routine to call when a packet is sent. + */ + void clearPacketSentAction() override; + + /*! + \brief Interrupt-driven binary transmit method. + Overloads for string-based transmissions are implemented in PhysicalLayer. + \param data Binary data to be sent. + \param len Number of bytes to send. + \param addr Address to send the data to. Will only be added if address filtering was enabled. + \returns \ref status_codes + */ + int16_t startTransmit(uint8_t* data, size_t len, uint8_t addr = 0) override; + + /*! + \brief Clean up after transmission is done. + \returns \ref status_codes + */ + int16_t finishTransmit() override; + + /*! + \brief Interrupt-driven receive method with default parameters. + Implemented for compatibility with PhysicalLayer. + + \returns \ref status_codes + */ + int16_t startReceive() override; + + /*! + \brief Interrupt-driven receive method. IRQ1 will be activated when full packet is received. + \param timeout Raw timeout value, expressed as multiples of 1/32.768 kHz (approximately 30.52 us). + Defaults to RADIOLIB_LR11X0_RX_TIMEOUT_INF for infinite timeout (Rx continuous mode), + set to RADIOLIB_LR11X0_RX_TIMEOUT_NONE for no timeout (Rx single mode). + If timeout other than infinite is set, signal will be generated on IRQ1. + + \param irqFlags Sets the IRQ flags that will trigger IRQ1, defaults to RADIOLIB_LR11X0_IRQ_RX_DONE. + \param irqMask Only for PhysicalLayer compatibility, not used. + \param len Only for PhysicalLayer compatibility, not used. + \returns \ref status_codes + */ + int16_t startReceive(uint32_t timeout, uint32_t irqFlags = RADIOLIB_LR11X0_IRQ_RX_DONE, uint32_t irqMask = 0, size_t len = 0); + + /*! + \brief Reads the current IRQ status. + \returns IRQ status bits + */ + uint32_t getIrqStatus(); + + /*! + \brief Reads data received after calling startReceive method. When the packet length is not known in advance, + getPacketLength method must be called BEFORE calling readData! + \param data Pointer to array to save the received binary data. + \param len Number of bytes that will be read. When set to 0, the packet length will be retrieved automatically. + When more bytes than received are requested, only the number of bytes requested will be returned. + \returns \ref status_codes + */ + int16_t readData(uint8_t* data, size_t len) override; + + /*! + \brief Interrupt-driven channel activity detection method. IRQ1 will be activated + when LoRa preamble is detected, or upon timeout. Defaults to CAD parameter values recommended by AN1200.48. + \returns \ref status_codes + */ + int16_t startChannelScan() override; + + /*! + \brief Interrupt-driven channel activity detection method. IRQ1 will be activated + when LoRa preamble is detected, or upon timeout. + \param symbolNum Number of symbols for CAD detection. + \param detPeak Peak value for CAD detection. + \param detMin Minimum value for CAD detection. + \returns \ref status_codes + */ + int16_t startChannelScan(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + + /*! + \brief Read the channel scan result + \returns \ref status_codes + */ + int16_t getChannelScanResult() override; + + // configuration methods + + /*! + \brief Sets output power. Allowed values are in range from -9 to 22 dBm (high-power PA) or -17 to 14 dBm (low-power PA). + \param power Output power to be set in dBm, output PA is determined automatically preferring the low-power PA. + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power) override; + + /*! + \brief Sets output power. Allowed values are in range from -9 to 22 dBm (high-power PA) or -17 to 14 dBm (low-power PA). + \param power Output power to be set in dBm. + \param forceHighPower Force using the high-power PA. If set to false, PA will be determined automatically + based on configured output power, preferring the low-power PA. If set to true, only high-power PA will be used. + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power, bool forceHighPower); + + /*! + \brief Check if output power is configurable. + This method is needed for compatibility with PhysicalLayer::checkOutputPower. + \param power Output power in dBm, PA will be determined automatically. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \param forceHighPower Force using the high-power PA. If set to false, PA will be determined automatically + based on configured output power, preferring the low-power PA. If set to true, only high-power PA will be used. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped, bool forceHighPower); + + /*! + \brief Sets LoRa bandwidth. Allowed values are 62.5, 125.0, 250.0 and 500.0 kHz. + \param bw LoRa bandwidth to be set in kHz. + \returns \ref status_codes + */ + int16_t setBandwidth(float bw); + + /*! + \brief Sets LoRa spreading factor. Allowed values range from 5 to 12. + \param sf LoRa spreading factor to be set. + \param legacy Enable legacy mode for SF6 - this allows to communicate with SX127x at SF6. + \returns \ref status_codes + */ + int16_t setSpreadingFactor(uint8_t sf, bool legacy = false); + + /*! + \brief Sets LoRa coding rate denominator. Allowed values range from 5 to 8. + \param cr LoRa coding rate denominator to be set. + \param longInterleave Enable long interleaver when set to true. + Note that CR 4/7 is not possible with long interleaver enabled! + \returns \ref status_codes + */ + int16_t setCodingRate(uint8_t cr, bool longInterleave = false); + + /*! + \brief Sets LoRa or LR-FHSS sync word. + \param syncWord LoRa or LR-FHSS sync word to be set. For LoRa, only 8 least significant bits will be used + \returns \ref status_codes + */ + int16_t setSyncWord(uint32_t syncWord); + + /*! + \brief Sets GFSK bit rate. Allowed values range from 0.6 to 300.0 kbps. + \param br FSK bit rate to be set in kbps. + \returns \ref status_codes + */ + int16_t setBitRate(float br) override; + + /*! + \brief Sets GFSK frequency deviation. Allowed values range from 0.0 to 200.0 kHz. + \param freqDev GFSK frequency deviation to be set in kHz. + \returns \ref status_codes + */ + int16_t setFrequencyDeviation(float freqDev) override; + + /*! + \brief Sets GFSK receiver bandwidth. Allowed values are 4.8, 5.8, 7.3, 9.7, 11.7, 14.6, 19.5, + 23.4, 29.3, 39.0, 46.9, 58.6, 78.2, 93.8, 117.3, 156.2, 187.2, 234.3, 312.0, 373.6 and 467.0 kHz. + \param rxBw GFSK receiver bandwidth to be set in kHz. + \returns \ref status_codes + */ + int16_t setRxBandwidth(float rxBw); + + /*! + \brief Sets GFSK sync word in the form of array of up to 8 bytes. + \param syncWord GFSK sync word to be set. + \param len GFSK sync word length in bytes. + \returns \ref status_codes + */ + int16_t setSyncWord(uint8_t* syncWord, size_t len) override; + + /*! + \brief Sets GFSK sync word in the form of array of up to 8 bytes. + \param syncWord GFSK sync word to be set. + \param bitsLen GFSK sync word length in bits. If length is not divisible by 8, + least significant bits of syncWord will be ignored. + \returns \ref status_codes + */ + int16_t setSyncBits(uint8_t *syncWord, uint8_t bitsLen); + + /*! + \brief Sets node address. Calling this method will also enable address filtering for node address only. + \param nodeAddr Node address to be set. + \returns \ref status_codes + */ + int16_t setNodeAddress(uint8_t nodeAddr); + + /*! + \brief Sets broadcast address. Calling this method will also enable address + filtering for node and broadcast address. + \param broadAddr Node address to be set. + \returns \ref status_codes + */ + int16_t setBroadcastAddress(uint8_t broadAddr); + + /*! + \brief Disables address filtering. Calling this method will also erase previously set addresses. + \returns \ref status_codes + */ + int16_t disableAddressFiltering(); + + /*! + \brief Sets time-bandwidth product of Gaussian filter applied for shaping. + Allowed values are RADIOLIB_SHAPING_0_3, RADIOLIB_SHAPING_0_5, RADIOLIB_SHAPING_0_7 or RADIOLIB_SHAPING_1_0. + Set to RADIOLIB_SHAPING_NONE to disable data shaping. + \param sh Time-bandwidth product of Gaussian filter to be set. + \returns \ref status_codes + */ + int16_t setDataShaping(uint8_t sh) override; + + /*! + \brief Sets transmission encoding. Available in GFSK mode only. Serves only as alias for PhysicalLayer compatibility. + \param encoding Encoding to be used. Set to 0 for NRZ, and 2 for whitening. + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding) override; + + /*! + \brief Set modem in fixed packet length mode. Available in GFSK mode only. + \param len Packet length. + \returns \ref status_codes + */ + int16_t fixedPacketLengthMode(uint8_t len = RADIOLIB_LR11X0_MAX_PACKET_LENGTH); + + /*! + \brief Set modem in variable packet length mode. Available in GFSK mode only. + \param maxLen Maximum packet length. + \returns \ref status_codes + */ + int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_LR11X0_MAX_PACKET_LENGTH); + + /*! + \brief Sets GFSK whitening parameters. + \param enabled True = Whitening enabled + \param initial Initial value used for the whitening LFSR in GFSK mode. + By default set to 0x01FF for compatibility with SX127x and LoRaWAN. + \returns \ref status_codes + */ + int16_t setWhitening(bool enabled, uint16_t initial = 0x01FF); + + /*! + \brief Set data. + \param dr Data rate struct. Interpretation depends on currently active modem (GFSK or LoRa). + \returns \ref status_codes + */ + int16_t setDataRate(DataRate_t dr) override; + + /*! + \brief Check the data rate can be configured by this module. + \param dr Data rate struct. Interpretation depends on currently active modem (GFSK or LoRa). + \returns \ref status_codes + */ + int16_t checkDataRate(DataRate_t dr) override; + + /*! + \brief Sets preamble length for LoRa or GFSK modem. Allowed values range from 1 to 65535. + \param preambleLength Preamble length to be set in symbols (LoRa) or bits (GFSK). + \returns \ref status_codes + */ + int16_t setPreambleLength(size_t preambleLength) override; + + /*! + \brief Sets TCXO (Temperature Compensated Crystal Oscillator) configuration. + \param voltage TCXO reference voltage in volts. Allowed values are 1.6, 1.7, 1.8, 2.2. 2.4, 2.7, 3.0 and 3.3 V. + Set to 0 to disable TCXO. + NOTE: After setting this parameter to 0, the module will be reset (since there's no other way to disable TCXO). + \param delay TCXO timeout in us. Defaults to 5000 us. + \returns \ref status_codes + */ + int16_t setTCXO(float voltage, uint32_t delay = 5000); + + /*! + \brief Sets CRC configuration. + \param len CRC length in bytes, Allowed values are 1 or 2, set to 0 to disable CRC. + \param initial Initial CRC value. GFSK only. Defaults to 0x1D0F (CCIT CRC). + \param polynomial Polynomial for CRC calculation. GFSK only. Defaults to 0x1021 (CCIT CRC). + \param inverted Invert CRC bytes. GFSK only. Defaults to true (CCIT CRC). + \returns \ref status_codes + */ + int16_t setCRC(uint8_t len, uint32_t initial = 0x00001D0FUL, uint32_t polynomial = 0x00001021UL, bool inverted = true); + + /*! + \brief Enable/disable inversion of the I and Q signals + \param enable QI inversion enabled (true) or disabled (false); + \returns \ref status_codes + */ + int16_t invertIQ(bool enable) override; + + /*! + \brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. Only available for LoRa or GFSK modem. + \returns RSSI of the last received packet in dBm. + */ + float getRSSI() override; + + /*! + \brief Gets SNR (Signal to Noise Ratio) of the last received packet. Only available for LoRa modem. + \returns SNR of the last received packet in dB. + */ + float getSNR() override; + + /*! + \brief Gets frequency error of the latest received packet. + \returns Frequency error in Hz. + */ + float getFrequencyError(); + + /*! + \brief Query modem for the packet length of received payload. + \param update Update received packet length. Will return cached value when set to false. + \returns Length of last received packet in bytes. + */ + size_t getPacketLength(bool update = true) override; + + /*! + \brief Query modem for the packet length of received payload. + \param update Update received packet length. Will return cached value when set to false. + \returns Length of last received packet in bytes. + */ + size_t getPacketLength(bool update, uint8_t* offset); + + /*! + \brief Get expected time-on-air for a given size of payload + \param len Payload length in bytes. + \returns Expected time-on-air in microseconds. + */ + RadioLibTime_t getTimeOnAir(size_t len) override; + + /*! + \brief Calculate the timeout value for this specific module / series (in number of symbols or units of time) + \param timeoutUs Timeout in microseconds to listen for + \returns Timeout value in a unit that is specific for the used module + */ + RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; + + /*! + \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks + \param irqFlags The flags for which IRQs must be triggered + \param irqMask Mask indicating which IRQ triggers a DIO + \returns \ref status_codes + */ + int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; + + /*! + \brief Check whether the IRQ bit for RxTimeout is set + \returns Whether RxTimeout IRQ is set + */ + bool isRxTimeout() override; + + /*! + \brief Get one truly random byte from RSSI noise. + \returns TRNG byte. + */ + uint8_t randomByte() override; + + /*! + \brief Set implicit header mode for future reception/transmission. + \param len Payload length in bytes. + \returns \ref status_codes + */ + int16_t implicitHeader(size_t len); + + /*! + \brief Set explicit header mode for future reception/transmission. + \returns \ref status_codes + */ + int16_t explicitHeader(); + + /*! + \brief Gets effective data rate for the last transmitted packet. The value is calculated only for payload bytes. + \returns Effective data rate in bps. + */ + float getDataRate() const; + + /*! + \brief Sets LR-FHSS configuration. + \param bw LR-FHSS bandwidth, one of RADIOLIB_LR11X0_LR_FHSS_BW_* values. + \param cr LR-FHSS coding rate, one of RADIOLIB_LR11X0_LR_FHSS_CR_* values. + \param hdrCount Header packet count, 1 - 4. Defaults to 3. + \param hopSeed 9-bit seed number for PRNG generation of the hopping sequence. Defaults to 0x13A. + \returns \ref status_codes + */ + int16_t setLrFhssConfig(uint8_t bw, uint8_t cr, uint8_t hdrCount = 3, uint16_t hopSeed = 0x13A); + + /*! + \brief Start passive WiFi scan. BUSY pin will be de-activated when the scan is finished. + \param wifiType Type of WiFi (802.11) signals to scan, 'b', 'n', 'g' or '*' for all signals. + \param mode Scan acquisition mode, one of RADIOLIB_LR11X0_WIFI_ACQ_MODE_*. + The type of results available after the scan depends on this mode. + Defaults to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, which provides the most information. + \param chanMask Bit mask of WiFi channels to scan, defaults to all channels. + More channels leads to longer overall scan duration. + \param numScans Number of scans to perform per each enabled channel. Defaults to 16 scans. + More scans leads to longer overall scan duration. + \param timeout Timeout of each scan in milliseconds. Defaults to 100 ms + Longer timeout leads to longer overall scan duration. + \returns \ref status_codes + */ + int16_t startWifiScan(char wifiType, uint8_t mode = RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, uint16_t chanMask = RADIOLIB_LR11X0_WIFI_ALL_CHANNELS, uint8_t numScans = 16, uint16_t timeout = 100); + + /*! + \brief Sets interrupt service routine to call when a WiFi scan is completed. + \param func ISR to call. + */ + void setWiFiScanAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a WiFi scan is completed. + */ + void clearWiFiScanAction(); + + /*! + \brief Get number of WiFi scan results after the scan is finished. + \param count Pointer to a variable that will hold the number of scan results. + \returns \ref status_codes + */ + int16_t getWifiScanResultsCount(uint8_t* count); + + /*! + \brief Retrieve passive WiFi scan result. + \param result Pointer to structure to hold the result data. + \param index Result index, starting from 0. The number of scan results can be retrieved by calling getWifiScanResultsCount. + \param brief Whether to only retrieve the results in brief format. If set to false, only information in LR11x0WifiResult_t + will be retrieved. If set to true, information in LR11x0WifiResultFull_t will be retrieved. In addition, if WiFi scan mode + was set to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, all information in LR11x0WifiResultExtended_t will be retrieved. + \returns \ref status_codes + */ + int16_t getWifiScanResult(LR11x0WifiResult_t* result, uint8_t index, bool brief = false); + + /*! + \brief Blocking WiFi scan method. Performs a full passive WiFi scan. + This method may block for several seconds! + \param wifiType Type of WiFi (802.11) signals to scan, 'b', 'n', 'g' or '*' for all signals. + \param count Pointer to a variable that will hold the number of scan results. + \param mode Scan acquisition mode, one of RADIOLIB_LR11X0_WIFI_ACQ_MODE_*. + The type of results available after the scan depends on this mode. + Defaults to RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, which provides the most information. + \param chanMask Bit mask of WiFi channels to scan, defaults to all channels. + More channels leads to longer overall scan duration. + \param numScans Number of scans to perform per each enabled channel. Defaults to 16 scans. + More scans leads to longer overall scan duration. + \param timeout Timeout of each scan in milliseconds. Defaults to 100 ms + Longer timeout leads to longer overall scan duration. + \returns \ref status_codes + */ + int16_t wifiScan(uint8_t wifiType, uint8_t* count, uint8_t mode = RADIOLIB_LR11X0_WIFI_ACQ_MODE_FULL_BEACON, uint16_t chanMask = RADIOLIB_LR11X0_WIFI_ALL_CHANNELS, uint8_t numScans = 16, uint16_t timeout = 100); + +#if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL + protected: +#endif + Module* getMod() override; + + // LR11x0 SPI command implementations + int16_t writeRegMem32(uint32_t addr, uint32_t* data, size_t len); + int16_t readRegMem32(uint32_t addr, uint32_t* data, size_t len); + int16_t writeBuffer8(uint8_t* data, size_t len); + int16_t readBuffer8(uint8_t* data, size_t len, size_t offset); + int16_t clearRxBuffer(void); + int16_t writeRegMemMask32(uint32_t addr, uint32_t mask, uint32_t data); + + int16_t getStatus(uint8_t* stat1, uint8_t* stat2, uint32_t* irq); + int16_t getVersion(uint8_t* hw, uint8_t* device, uint8_t* major, uint8_t* minor); + int16_t getErrors(uint16_t* err); + int16_t clearErrors(void); + int16_t calibrate(uint8_t params); + int16_t setRegMode(uint8_t mode); + int16_t calibImage(float freq1, float freq2); + int16_t setDioAsRfSwitch(uint8_t en, uint8_t stbyCfg, uint8_t rxCfg, uint8_t txCfg, uint8_t txHpCfg, uint8_t txHfCfg, uint8_t gnssCfg, uint8_t wifiCfg); + int16_t setDioIrqParams(uint32_t irq1, uint32_t irq2); + int16_t clearIrq(uint32_t irq); + int16_t configLfClock(uint8_t setup); + int16_t setTcxoMode(uint8_t tune, uint32_t delay); + int16_t reboot(bool stay); + int16_t getVbat(float* vbat); + int16_t getTemp(float* temp); + int16_t setFs(void); + int16_t getRandomNumber(uint32_t* rnd); + int16_t eraseInfoPage(void); + int16_t writeInfoPage(uint16_t addr, const uint32_t* data, size_t len); + int16_t readInfoPage(uint16_t addr, uint32_t* data, size_t len); + int16_t getChipEui(uint8_t* eui); + int16_t getSemtechJoinEui(uint8_t* eui); + int16_t deriveRootKeysAndGetPin(uint8_t* pin); + int16_t enableSpiCrc(bool en); + int16_t driveDiosInSleepMode(bool en); + + int16_t resetStats(void); + int16_t getStats(uint16_t* nbPktReceived, uint16_t* nbPktCrcError, uint16_t* data1, uint16_t* data2); + int16_t getPacketType(uint8_t* type); + int16_t getRxBufferStatus(uint8_t* len, uint8_t* startOffset); + int16_t getPacketStatusLoRa(float* rssiPkt, float* snrPkt, float* signalRssiPkt); + int16_t getPacketStatusGFSK(float* rssiSync, float* rssiAvg, uint8_t* rxLen, uint8_t* stat); + int16_t getRssiInst(float* rssi); + int16_t setGfskSyncWord(uint8_t* sync); + int16_t setLoRaPublicNetwork(bool pub); + int16_t setRx(uint32_t timeout); + int16_t setTx(uint32_t timeout); + int16_t setRfFrequency(uint32_t rfFreq); + int16_t autoTxRx(uint32_t delay, uint8_t intMode, uint32_t timeout); + int16_t setCadParams(uint8_t symNum, uint8_t detPeak, uint8_t detMin, uint8_t cadExitMode, uint32_t timeout); + int16_t setPacketType(uint8_t type); + int16_t setModulationParamsLoRa(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro); + int16_t setModulationParamsGFSK(uint32_t br, uint8_t sh, uint8_t rxBw, uint32_t freqDev); + int16_t setModulationParamsLrFhss(uint32_t br, uint8_t sh); + int16_t setModulationParamsSigfox(uint32_t br, uint8_t sh); + int16_t setPacketParamsLoRa(uint16_t preambleLen, uint8_t hdrType, uint8_t payloadLen, uint8_t crcType, uint8_t invertIQ); + int16_t setPacketParamsGFSK(uint16_t preambleLen, uint8_t preambleDetectorLen, uint8_t syncWordLen, uint8_t addrCmp, uint8_t packType, uint8_t payloadLen, uint8_t crcType, uint8_t whiten); + int16_t setPacketParamsSigfox(uint8_t payloadLen, uint16_t rampUpDelay, uint16_t rampDownDelay, uint16_t bitNum); + int16_t setTxParams(int8_t pwr, uint8_t ramp); + int16_t setPacketAdrs(uint8_t node, uint8_t broadcast); + int16_t setRxTxFallbackMode(uint8_t mode); + int16_t setRxDutyCycle(uint32_t rxPeriod, uint32_t sleepPeriod, uint8_t mode); + int16_t setPaConfig(uint8_t paSel, uint8_t regPaSupply, uint8_t paDutyCycle, uint8_t paHpSel); + int16_t stopTimeoutOnPreamble(bool stop); + int16_t setCad(void); + int16_t setTxCw(void); + int16_t setTxInfinitePreamble(void); + int16_t setLoRaSynchTimeout(uint8_t symbolNum); + int16_t setRangingAddr(uint32_t addr, uint8_t checkLen); + int16_t setRangingReqAddr(uint32_t addr); + int16_t getRangingResult(uint8_t type, float* res); + int16_t setRangingTxRxDelay(uint32_t delay); + int16_t setGfskCrcParams(uint32_t init, uint32_t poly); + int16_t setGfskWhitParams(uint16_t seed); + int16_t setRxBoosted(bool en); + int16_t setRangingParameter(uint8_t symbolNum); + int16_t setLoRaSyncWord(uint8_t sync); + int16_t lrFhssBuildFrame(uint8_t hdrCount, uint8_t cr, uint8_t grid, bool hop, uint8_t bw, uint16_t hopSeq, int8_t devOffset, uint8_t* payload, size_t len); + int16_t lrFhssSetSyncWord(uint32_t sync); + int16_t configBleBeacon(uint8_t chan, uint8_t* payload, size_t len); + int16_t getLoRaRxHeaderInfos(uint8_t* info); + int16_t bleBeaconSend(uint8_t chan, uint8_t* payload, size_t len); + + int16_t wifiScan(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t nbMaxRes, uint8_t nbScanPerChan, uint16_t timeout, uint8_t abortOnTimeout); + int16_t wifiScanTimeLimit(uint8_t type, uint16_t mask, uint8_t acqMode, uint8_t nbMaxRes, uint16_t timePerChan, uint16_t timeout); + int16_t wifiCountryCode(uint16_t mask, uint8_t nbMaxRes, uint8_t nbScanPerChan, uint16_t timeout, uint8_t abortOnTimeout); + int16_t wifiCountryCodeTimeLimit(uint16_t mask, uint8_t nbMaxRes, uint16_t timePerChan, uint16_t timeout); + int16_t wifiGetNbResults(uint8_t* nbResults); + int16_t wifiReadResults(uint8_t index, uint8_t nbResults, uint8_t format, uint8_t* results); + int16_t wifiResetCumulTimings(void); + int16_t wifiReadCumulTimings(uint32_t* detection, uint32_t* capture, uint32_t* demodulation); + int16_t wifiGetNbCountryCodeResults(uint8_t* nbResults); + int16_t wifiReadCountryCodeResults(uint8_t index, uint8_t nbResults, uint8_t* results); + int16_t wifiCfgTimestampAPphone(uint32_t timestamp); + int16_t wifiReadVersion(uint8_t* major, uint8_t* minor); + + int16_t gnssSetConstellationToUse(uint8_t mask); + int16_t gnssReadConstellationToUse(uint8_t* mask); + int16_t gnssSetAlmanacUpdate(uint8_t mask); + int16_t gnssReadAlmanacUpdate(uint8_t* mask); + int16_t gnssReadVersion(uint8_t* fw, uint8_t* almanac); + int16_t gnssReadSupportedConstellations(uint8_t* mask); + int16_t gnssSetMode(uint8_t mode); + int16_t gnssAutonomous(uint32_t gpsTime, uint8_t resMask, uint8_t nbSvMask); + int16_t gnssAssisted(uint32_t gpsTime, uint8_t effort, uint8_t resMask, uint8_t nbSvMask); + int16_t gnssSetAssistancePosition(float lat, float lon); + int16_t gnssReadAssistancePosition(float* lat, float* lon); + int16_t gnssPushSolverMsg(uint8_t* payload, size_t len); + int16_t gnssPushDmMsg(uint8_t* payload, size_t len); + int16_t gnssGetContextStatus(uint8_t* fwVersion, uint32_t* almanacCrc, uint8_t* errCode, uint8_t* almUpdMask, uint8_t* freqSpace); + int16_t gnssGetNbSvDetected(uint8_t* nbSv); + int16_t gnssGetSvDetected(uint8_t* svId, uint8_t* snr, uint16_t* doppler, size_t nbSv); + int16_t gnssGetConsumption(uint32_t* cpu, uint32_t* radio); + int16_t gnssGetResultSize(uint16_t* size); + int16_t gnssReadResults(uint8_t* result, uint16_t size); + int16_t gnssAlmanacFullUpdateHeader(uint16_t date, uint32_t globalCrc); + int16_t gnssAlmanacFullUpdateSV(uint8_t svn, uint8_t* svnAlmanac); + int16_t gnssGetSvVisible(uint32_t time, float lat, float lon, uint8_t constellation, uint8_t* nbSv); + + int16_t cryptoSetKey(uint8_t keyId, uint8_t* key); + int16_t cryptoDeriveKey(uint8_t srcKeyId, uint8_t dstKeyId, uint8_t* key); + int16_t cryptoProcessJoinAccept(uint8_t decKeyId, uint8_t verKeyId, uint8_t lwVer, uint8_t* header, uint8_t* dataIn, size_t len, uint8_t* dataOut); + int16_t cryptoComputeAesCmac(uint8_t keyId, uint8_t* data, size_t len, uint32_t* mic); + int16_t cryptoVerifyAesCmac(uint8_t keyId, uint32_t micExp, uint8_t* data, size_t len, bool* result); + int16_t cryptoAesEncrypt01(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut); + int16_t cryptoAesEncrypt(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut); + int16_t cryptoAesDecrypt(uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut); + int16_t cryptoStoreToFlash(void); + int16_t cryptoRestoreFromFlash(void); + int16_t cryptoSetParam(uint8_t id, uint32_t value); + int16_t cryptoGetParam(uint8_t id, uint32_t* value); + int16_t cryptoCheckEncryptedFirmwareImage(uint32_t offset, uint32_t* data, size_t len); + int16_t cryptoCheckEncryptedFirmwareImageResult(bool* result); + + int16_t bootEraseFlash(void); + int16_t bootWriteFlashEncrypted(uint32_t offset, uint32_t* data, size_t len); + int16_t bootReboot(bool stay); + int16_t bootGetPin(uint8_t* pin); + int16_t bootGetChipEui(uint8_t* eui); + int16_t bootGetJoinEui(uint8_t* eui); + + int16_t SPIcommand(uint16_t cmd, bool write, uint8_t* data, size_t len, uint8_t* out = NULL, size_t outLen = 0); + +#if !RADIOLIB_GODMODE + protected: +#endif + uint8_t chipType = 0; + +#if !RADIOLIB_GODMODE + private: +#endif + Module* mod; + + // cached LoRa parameters + uint8_t bandwidth = 0, spreadingFactor = 0, codingRate = 0, ldrOptimize = 0, crcTypeLoRa = 0, headerType = 0; + uint16_t preambleLengthLoRa = 0; + float bandwidthKhz = 0; + bool ldroAuto = true; + size_t implicitLen = 0; + bool invertIQEnabled = false; + + // cached GFSK parameters + uint32_t bitRate = 0, frequencyDev = 0; + uint8_t preambleDetLength = 0, rxBandwidth = 0, pulseShape = 0, crcTypeGFSK = 0, syncWordLength = 0, addrComp = 0, whitening = 0, packetType = 0, node = 0; + uint16_t preambleLengthGFSK = 0; + + // cached LR-FHSS parameters + uint8_t lrFhssCr = 0, lrFhssBw = 0, lrFhssHdrCount = 0; + uint16_t lrFhssHopSeq = 0; + + float dataRateMeasured = 0; + + uint8_t wifiScanMode = 0; + + int16_t modSetup(float tcxoVoltage, uint8_t modem); + static int16_t SPIparseStatus(uint8_t in); + static int16_t SPIcheckStatus(Module* mod); + bool findChip(uint8_t ver); + int16_t config(uint8_t modem); + int16_t setPacketMode(uint8_t mode, uint8_t len); + int16_t startCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin); + int16_t setHeaderType(uint8_t hdrType, size_t len = 0xFF); + + // common methods to avoid some copy-paste + int16_t bleBeaconCommon(uint16_t cmd, uint8_t chan, uint8_t* payload, size_t len); + int16_t writeCommon(uint16_t cmd, uint32_t addrOffset, const uint32_t* data, size_t len); + int16_t cryptoCommon(uint16_t cmd, uint8_t keyId, uint8_t* dataIn, size_t len, uint8_t* dataOut); +}; + +#endif + +#endif diff --git a/lib/RadioLib/src/modules/RF69/RF69.cpp b/lib/RadioLib/src/modules/RF69/RF69.cpp index a934898..260962a 100644 --- a/lib/RadioLib/src/modules/RF69/RF69.cpp +++ b/lib/RadioLib/src/modules/RF69/RF69.cpp @@ -100,18 +100,18 @@ void RF69::reset() { int16_t RF69::transmit(uint8_t* data, size_t len, uint8_t addr) { // calculate timeout (5ms + 500 % of expected time-on-air) - uint32_t timeout = 5000000 + (uint32_t)((((float)(len * 8)) / (this->bitRate * 1000.0)) * 5000000.0); + RadioLibTime_t timeout = 5 + (RadioLibTime_t)((((float)(len * 8)) / this->bitRate) * 5); // start transmission int16_t state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for transmission end or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } @@ -122,18 +122,18 @@ int16_t RF69::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t RF69::receive(uint8_t* data, size_t len) { // calculate timeout (500 ms + 400 full 64-byte packets at current bit rate) - uint32_t timeout = 500000 + (1.0/(this->bitRate*1000.0))*(RADIOLIB_RF69_MAX_PACKET_LENGTH*400.0); + RadioLibTime_t timeout = 500 + (1.0/(this->bitRate))*(RADIOLIB_RF69_MAX_PACKET_LENGTH*400.0); // start reception int16_t state = startReceive(); RADIOLIB_ASSERT(state); // wait for packet reception or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { standby(); clearIRQFlags(); return(RADIOLIB_ERR_RX_TIMEOUT); @@ -259,7 +259,7 @@ int16_t RF69::startReceive() { return(state); } -int16_t RF69::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t RF69::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -566,9 +566,9 @@ int16_t RF69::setBitRate(float br) { setMode(RADIOLIB_RF69_STANDBY); // set bit rate - uint16_t bitRate = 32000 / br; - int16_t state = this->mod->SPIsetRegValue(RADIOLIB_RF69_REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8, 7, 0); - state |= this->mod->SPIsetRegValue(RADIOLIB_RF69_REG_BITRATE_LSB, bitRate & 0x00FF, 7, 0); + uint16_t bitRateRaw = 32000 / br; + int16_t state = this->mod->SPIsetRegValue(RADIOLIB_RF69_REG_BITRATE_MSB, (bitRateRaw & 0xFF00) >> 8, 7, 0); + state |= this->mod->SPIsetRegValue(RADIOLIB_RF69_REG_BITRATE_LSB, bitRateRaw & 0x00FF, 7, 0); if(state == RADIOLIB_ERR_NONE) { this->bitRate = br; } diff --git a/lib/RadioLib/src/modules/RF69/RF69.h b/lib/RadioLib/src/modules/RF69/RF69.h index 177b839..7d77ef6 100644 --- a/lib/RadioLib/src/modules/RF69/RF69.h +++ b/lib/RadioLib/src/modules/RF69/RF69.h @@ -486,9 +486,9 @@ class RF69: public PhysicalLayer { /*! \brief Default constructor. - \param mod Instance of Module that will be used to communicate with the radio. + \param module Instance of Module that will be used to communicate with the radio. */ - RF69(Module* module); + RF69(Module* module); // cppcheck-suppress noExplicitConstructor // basic methods @@ -538,7 +538,7 @@ class RF69: public PhysicalLayer { \brief Sets the module to sleep mode. \returns \ref status_codes */ - int16_t sleep(); + int16_t sleep() override; /*! \brief Sets the module to standby mode. @@ -575,7 +575,7 @@ class RF69: public PhysicalLayer { /*! \brief Sets AES key. - \param Key to be used for AES encryption. Must be exactly 16 bytes long. + \param key Key to be used for AES encryption. Must be exactly 16 bytes long. */ void setAESKey(uint8_t* key); @@ -619,23 +619,23 @@ class RF69: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Set interrupt service routine function to call when FIFO is empty. @@ -697,7 +697,7 @@ class RF69: public PhysicalLayer { \brief Interrupt-driven receive method. GDO0 will be activated when full packet is received. \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. @@ -707,7 +707,7 @@ class RF69: public PhysicalLayer { \param len Ignored. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; /*! \brief Reads data received after calling startReceive method. When the packet length is not known in advance, @@ -727,7 +727,7 @@ class RF69: public PhysicalLayer { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Gets carrier frequency. @@ -741,7 +741,7 @@ class RF69: public PhysicalLayer { \param br Bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Sets receiver bandwidth. Allowed values are 2.6, 3.1, 3.9, 5.2, 6.3, 7.8, 10.4, 12.5, 15.6, @@ -872,14 +872,14 @@ class RF69: public PhysicalLayer { /*! \brief Set modem in variable packet length mode. - \param len Maximum packet length. + \param maxLen Maximum packet length. \returns \ref status_codes */ int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_RF69_MAX_PACKET_LENGTH); /*! \brief Enable sync word filtering and generation. - \param numBits Sync word length in bits. + \param maxErrBits Maximum allowed number of error bits in sync word. \returns \ref status_codes */ int16_t enableSyncWordFiltering(uint8_t maxErrBits = 0); @@ -944,7 +944,7 @@ class RF69: public PhysicalLayer { \brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. \returns Last packet RSSI in dBm. */ - float getRSSI(); + float getRSSI() override; /*! \brief Sets the RSSI value above which the RSSI interrupt is signaled @@ -963,7 +963,7 @@ class RF69: public PhysicalLayer { \brief Get one truly random byte from RSSI noise. \returns TRNG byte. */ - uint8_t randomByte(); + uint8_t randomByte() override; /*! \brief Read version SPI register. Should return RF69_CHIP_VERSION (0x24) if SX127x is connected and working. @@ -976,13 +976,13 @@ class RF69: public PhysicalLayer { \brief Set interrupt service routine function to call when data bit is received in direct mode. \param func Pointer to interrupt service routine. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Function to read and process data bit in direct reception mode. \param pin Pin on which to read. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif /*! @@ -991,12 +991,12 @@ class RF69: public PhysicalLayer { \param value The value that indicates which function to place on that pin. See chip datasheet for details. \returns \ref status_codes */ - int16_t setDIOMapping(uint32_t pin, uint32_t value); + int16_t setDIOMapping(uint32_t pin, uint32_t value) override; #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/SX123x/SX1231.h b/lib/RadioLib/src/modules/SX123x/SX1231.h index 569a840..6dc3735 100644 --- a/lib/RadioLib/src/modules/SX123x/SX1231.h +++ b/lib/RadioLib/src/modules/SX123x/SX1231.h @@ -96,7 +96,7 @@ class SX1231: public RF69 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1231(Module* mod); + SX1231(Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Initialization method. @@ -111,7 +111,7 @@ class SX1231: public RF69 { int16_t begin(float freq = 434.0, float br = 4.8, float freqDev = 5.0, float rxBw = 125.0, int8_t power = 10, uint8_t preambleLen = 16); #if !RADIOLIB_GODMODE - private: + protected: #endif uint8_t chipRevision = 0; }; diff --git a/lib/RadioLib/src/modules/SX123x/SX1233.h b/lib/RadioLib/src/modules/SX123x/SX1233.h index 029b0cc..bf9bb14 100644 --- a/lib/RadioLib/src/modules/SX123x/SX1233.h +++ b/lib/RadioLib/src/modules/SX123x/SX1233.h @@ -26,7 +26,7 @@ class SX1233: public SX1231 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1233(Module* mod); + SX1233(Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Initialization method. @@ -48,12 +48,12 @@ class SX1233: public SX1231 { \param br Bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; #if !RADIOLIB_GODMODE private: #endif - uint8_t chipRevision = 0; + }; #endif diff --git a/lib/RadioLib/src/modules/SX126x/STM32WLx.cpp b/lib/RadioLib/src/modules/SX126x/STM32WLx.cpp index 508ea4e..69515fc 100644 --- a/lib/RadioLib/src/modules/SX126x/STM32WLx.cpp +++ b/lib/RadioLib/src/modules/SX126x/STM32WLx.cpp @@ -87,6 +87,7 @@ int16_t STM32WLx::setOutputPower(int8_t power) { return(RADIOLIB_ERR_INVALID_OUTPUT_POWER); } + RADIOLIB_ASSERT(state); // Apply workaround for HP only state = SX126x::fixPaClamping(use_hp); diff --git a/lib/RadioLib/src/modules/SX126x/STM32WLx.h b/lib/RadioLib/src/modules/SX126x/STM32WLx.h index 16c88cc..993791a 100644 --- a/lib/RadioLib/src/modules/SX126x/STM32WLx.h +++ b/lib/RadioLib/src/modules/SX126x/STM32WLx.h @@ -39,7 +39,7 @@ class STM32WLx : public SX1262 { \brief Default constructor. \param mod Instance of STM32WLx_Module that will be used to communicate with the radio. */ - STM32WLx(STM32WLx_Module* mod); + STM32WLx(STM32WLx_Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Custom operation modes for STMWLx. @@ -124,34 +124,34 @@ class STM32WLx : public SX1262 { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Sets interrupt service routine to call when a channel scan is finished. \param func ISR to call. */ - void setChannelScanAction(void (*func)(void)); + void setChannelScanAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a channel scan is finished. */ - void clearChannelScanAction(); + void clearChannelScanAction() override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/SX126x/STM32WLx_Module.cpp b/lib/RadioLib/src/modules/SX126x/STM32WLx_Module.cpp index 57d0445..0f48d9a 100644 --- a/lib/RadioLib/src/modules/SX126x/STM32WLx_Module.cpp +++ b/lib/RadioLib/src/modules/SX126x/STM32WLx_Module.cpp @@ -22,10 +22,19 @@ enum { RADIOLIB_STM32WLx_VIRTUAL_PIN_RESET, }; +/*! + \class Stm32wlxHal + \brief Hardware Abstraction Layer for STM32WL. +*/ class Stm32wlxHal : public ArduinoHal { public: Stm32wlxHal(): ArduinoHal(SubGhz.SPI, SubGhz.spi_settings) {} + /*! + \brief Pin mode override to handle STM32WL virtual pins. + \param dwPin Pin to set. + \param dwMode Mode to set. + */ void pinMode(uint32_t dwPin, uint32_t dwMode) { switch(dwPin) { case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS: @@ -40,6 +49,11 @@ class Stm32wlxHal : public ArduinoHal { } } + /*! + \brief Digital write override to handle STM32WL virtual pins. + \param dwPin Pin to set. + \param dwVal Value to set. + */ void digitalWrite(uint32_t dwPin, uint32_t dwVal) { switch (dwPin) { case RADIOLIB_STM32WLx_VIRTUAL_PIN_NSS: @@ -61,6 +75,11 @@ class Stm32wlxHal : public ArduinoHal { } } + /*! + \brief Digital read override to handle STM32WL virtual pins. + \param ulPin Pin to read. + \returns Value read on the pin. + */ uint32_t digitalRead(uint32_t ulPin) { switch (ulPin) { case RADIOLIB_STM32WLx_VIRTUAL_PIN_BUSY: diff --git a/lib/RadioLib/src/modules/SX126x/SX1261.cpp b/lib/RadioLib/src/modules/SX126x/SX1261.cpp index dd39bfe..d6a90c3 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1261.cpp +++ b/lib/RadioLib/src/modules/SX126x/SX1261.cpp @@ -6,24 +6,33 @@ SX1261::SX1261(Module* mod): SX1262(mod) { } int16_t SX1261::setOutputPower(int8_t power) { - RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL); + RADIOLIB_ASSERT(state); // get current OCP configuration uint8_t ocp = 0; - int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); RADIOLIB_ASSERT(state); // set PA config state = SX126x::setPaConfig(0x04, RADIOLIB_SX126X_PA_CONFIG_SX1261, 0x00); RADIOLIB_ASSERT(state); - // set output power - /// \todo power ramp time configuration - state = SX126x::setTxParams(power); + // set output power with default 200us ramp + state = SX126x::setTxParams(power, RADIOLIB_SX126X_PA_RAMP_200U); RADIOLIB_ASSERT(state); // restore OCP configuration return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } +int16_t SX1261::checkOutputPower(int8_t power, int8_t* clipped) { + if(clipped) { + *clipped = RADIOLIB_MAX(-17, RADIOLIB_MIN(14, power)); + } + RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + return(RADIOLIB_ERR_NONE); +} + #endif diff --git a/lib/RadioLib/src/modules/SX126x/SX1261.h b/lib/RadioLib/src/modules/SX126x/SX1261.h index 01375d5..196cc21 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1261.h +++ b/lib/RadioLib/src/modules/SX126x/SX1261.h @@ -25,14 +25,22 @@ class SX1261 : public SX1262 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1261(Module* mod); + SX1261(Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Sets output power. Allowed values are in range from -17 to 14 dBm. \param power Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t power); + int16_t setOutputPower(int8_t power) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX126x/SX1262.cpp b/lib/RadioLib/src/modules/SX126x/SX1262.cpp index 0bbce30..232ad63 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1262.cpp +++ b/lib/RadioLib/src/modules/SX126x/SX1262.cpp @@ -51,13 +51,46 @@ int16_t SX1262::setFrequency(float freq) { return(setFrequency(freq, true)); } -int16_t SX1262::setFrequency(float freq, bool calibrate, float band) { +int16_t SX1262::setFrequency(float freq, bool calibrate) { RADIOLIB_CHECK_RANGE(freq, 150.0, 960.0, RADIOLIB_ERR_INVALID_FREQUENCY); // calibrate image rejection if(calibrate) { - int16_t state = SX126x::calibrateImage(freq - band, freq + band); + uint8_t data[2] = { 0, 0 }; + + // try to match the frequency ranges + int freqBand = (int)freq; + if((freqBand >= 902) && (freqBand <= 928)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_902_MHZ_2; + } else if((freqBand >= 863) && (freqBand <= 870)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_863_MHZ_2; + } else if((freqBand >= 779) && (freqBand <= 787)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2; + } else if((freqBand >= 470) && (freqBand <= 510)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2; + } else if((freqBand >= 430) && (freqBand <= 440)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2; + } + + int16_t state; + if(data[0]) { + // matched with predefined ranges, do the calibration + state = SX126x::calibrateImage(data); + + } else { + // if nothing matched, try custom calibration - the may or may not work + RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to match predefined frequency range, trying custom"); + state = SX126x::calibrateImageRejection(freq - 4.0f, freq + 4.0f); + + } + RADIOLIB_ASSERT(state); + } // set frequency @@ -65,24 +98,33 @@ int16_t SX1262::setFrequency(float freq, bool calibrate, float band) { } int16_t SX1262::setOutputPower(int8_t power) { - RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL); + RADIOLIB_ASSERT(state); // get current OCP configuration uint8_t ocp = 0; - int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); RADIOLIB_ASSERT(state); // set PA config state = SX126x::setPaConfig(0x04, RADIOLIB_SX126X_PA_CONFIG_SX1262); RADIOLIB_ASSERT(state); - // set output power - /// \todo power ramp time configuration - state = SX126x::setTxParams(power); + // set output power with default 200us ramp + state = SX126x::setTxParams(power, RADIOLIB_SX126X_PA_RAMP_200U); RADIOLIB_ASSERT(state); // restore OCP configuration return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } +int16_t SX1262::checkOutputPower(int8_t power, int8_t* clipped) { + if(clipped) { + *clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power)); + } + RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + return(RADIOLIB_ERR_NONE); +} + #endif diff --git a/lib/RadioLib/src/modules/SX126x/SX1262.h b/lib/RadioLib/src/modules/SX126x/SX1262.h index 61f8fac..7ae6789 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1262.h +++ b/lib/RadioLib/src/modules/SX126x/SX1262.h @@ -25,7 +25,7 @@ class SX1262: public SX126x { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1262(Module* mod); + SX1262(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -69,18 +69,15 @@ class SX1262: public SX126x { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz. \param freq Carrier frequency to be set in MHz. \param calibrate Run image calibration. - \param band Half bandwidth for image calibration. For example, - if carrier is 434 MHz and band is set to 4 MHz, then the image will be calibrate - for band 430 - 438 MHz. Unused if calibrate is set to false, defaults to 4 MHz \returns \ref status_codes */ - int16_t setFrequency(float freq, bool calibrate, float band = 4); + int16_t setFrequency(float freq, bool calibrate); /*! \brief Sets output power. Allowed values are in range from -9 to 22 dBm. @@ -88,7 +85,15 @@ class SX1262: public SX126x { \param power Output power to be set in dBm. \returns \ref status_codes */ - virtual int16_t setOutputPower(int8_t power); + virtual int16_t setOutputPower(int8_t power) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX126x/SX1268.cpp b/lib/RadioLib/src/modules/SX126x/SX1268.cpp index 7f14e9f..c50baaf 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1268.cpp +++ b/lib/RadioLib/src/modules/SX126x/SX1268.cpp @@ -52,13 +52,40 @@ int16_t SX1268::setFrequency(float freq) { } /// \todo integers only (all modules - frequency, data rate, bandwidth etc.) -int16_t SX1268::setFrequency(float freq, bool calibrate, float band) { +int16_t SX1268::setFrequency(float freq, bool calibrate) { RADIOLIB_CHECK_RANGE(freq, 410.0, 810.0, RADIOLIB_ERR_INVALID_FREQUENCY); // calibrate image rejection if(calibrate) { - int16_t state = SX126x::calibrateImage(freq - band, freq + band); + uint8_t data[2] = { 0, 0 }; + + // try to match the frequency ranges + int freqBand = (int)freq; + if((freqBand >= 779) && (freqBand <= 787)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_779_MHZ_2; + } else if((freqBand >= 470) && (freqBand <= 510)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_470_MHZ_2; + } else if((freqBand >= 430) && (freqBand <= 440)) { + data[0] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_1; + data[1] = RADIOLIB_SX126X_CAL_IMG_430_MHZ_2; + } + + int16_t state; + if(data[0]) { + // matched with predefined ranges, do the calibration + state = SX126x::calibrateImage(data); + + } else { + // if nothing matched, try custom calibration - the may or may not work + RADIOLIB_DEBUG_BASIC_PRINTLN("Failed to match predefined frequency range, trying custom"); + state = SX126x::calibrateImageRejection(freq - 4.0f, freq + 4.0f); + + } + RADIOLIB_ASSERT(state); + } // set frequency @@ -66,24 +93,33 @@ int16_t SX1268::setFrequency(float freq, bool calibrate, float band) { } int16_t SX1268::setOutputPower(int8_t power) { - RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL); + RADIOLIB_ASSERT(state); // get current OCP configuration uint8_t ocp = 0; - int16_t state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); + state = readRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1); RADIOLIB_ASSERT(state); // set PA config state = SX126x::setPaConfig(0x04, RADIOLIB_SX126X_PA_CONFIG_SX1268); RADIOLIB_ASSERT(state); - // set output power - /// \todo power ramp time configuration - state = SX126x::setTxParams(power); + // set output power with default 200us ramp + state = SX126x::setTxParams(power, RADIOLIB_SX126X_PA_RAMP_200U); RADIOLIB_ASSERT(state); // restore OCP configuration return(writeRegister(RADIOLIB_SX126X_REG_OCP_CONFIGURATION, &ocp, 1)); } +int16_t SX1268::checkOutputPower(int8_t power, int8_t* clipped) { + if(clipped) { + *clipped = RADIOLIB_MAX(-9, RADIOLIB_MIN(22, power)); + } + RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + return(RADIOLIB_ERR_NONE); +} + #endif diff --git a/lib/RadioLib/src/modules/SX126x/SX1268.h b/lib/RadioLib/src/modules/SX126x/SX1268.h index f3f61dc..503c74e 100644 --- a/lib/RadioLib/src/modules/SX126x/SX1268.h +++ b/lib/RadioLib/src/modules/SX126x/SX1268.h @@ -24,7 +24,7 @@ class SX1268: public SX126x { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1268(Module* mod); + SX1268(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -68,25 +68,30 @@ class SX1268: public SX126x { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets carrier frequency. Allowed values are in range from 150.0 to 960.0 MHz. \param freq Carrier frequency to be set in MHz. \param calibrate Run image calibration. - \param band Half bandwidth for image calibration. For example, - if carrier is 434 MHz and band is set to 4 MHz, then the image will be calibrate - for band 430 - 438 MHz. Unused if calibrate is set to false, defaults to 4 MHz \returns \ref status_codes */ - int16_t setFrequency(float freq, bool calibrate, float band = 4); + int16_t setFrequency(float freq, bool calibrate); /*! \brief Sets output power. Allowed values are in range from -9 to 22 dBm. \param power Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t power); + int16_t setOutputPower(int8_t power) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX126x/SX126x.cpp b/lib/RadioLib/src/modules/SX126x/SX126x.cpp index 161a524..17393a1 100644 --- a/lib/RadioLib/src/modules/SX126x/SX126x.cpp +++ b/lib/RadioLib/src/modules/SX126x/SX126x.cpp @@ -14,12 +14,15 @@ int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, flo this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX126X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX126X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX126X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX126X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX126X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX126X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX126X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX126X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; // try to find the SX126x chip if(!SX126x::findChip(this->chipType)) { @@ -73,10 +76,11 @@ int16_t SX126x::begin(uint8_t cr, uint8_t syncWord, uint16_t preambleLength, flo RADIOLIB_ASSERT(state); if (useRegulatorLDO) { - state = setRegulatorLDO(); + state = setRegulatorLDO(); } else { - state = setRegulatorDCDC(); + state = setRegulatorDCDC(); } + RADIOLIB_ASSERT(state); // set publicly accessible settings that are not a part of begin method state = setCurrentLimit(60.0); @@ -99,12 +103,15 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleL this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX126X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX126X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX126X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX126X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX126X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX126X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX126X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX126X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; // try to find the SX126x chip if(!SX126x::findChip(this->chipType)) { @@ -201,7 +208,7 @@ int16_t SX126x::reset(bool verify) { } // set mode to standby - SX126x often refuses first few commands after reset - uint32_t start = this->mod->hal->millis(); + RadioLibTime_t start = this->mod->hal->millis(); while(true) { // try to set mode to standby int16_t state = standby(); @@ -231,41 +238,27 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { return(RADIOLIB_ERR_PACKET_TOO_LONG); } - uint32_t timeout = 0; - - // get currently active modem - uint8_t modem = getPacketType(); - if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) { - // calculate timeout (150% of expected time-on-air) - timeout = (getTimeOnAir(len) * 3) / 2; - - } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) { - // calculate timeout (500% of expected time-on-air) - timeout = getTimeOnAir(len) * 5; - - } else { - return(RADIOLIB_ERR_UNKNOWN); - } - - RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); + // calculate timeout in ms (500% of expected time-on-air) + RadioLibTime_t timeout = (getTimeOnAir(len) * 5) / 1000; + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); // start transmission state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for packet transmission or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } } - uint32_t elapsed = this->mod->hal->micros() - start; // update data rate - this->dataRateMeasured = (len*8.0)/((float)elapsed/1000000.0); + RadioLibTime_t elapsed = this->mod->hal->millis() - start; + this->dataRateMeasured = (len*8.0)/((float)elapsed/1000.0); return(finishTransmit()); } @@ -275,14 +268,15 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { int16_t state = standby(); RADIOLIB_ASSERT(state); - uint32_t timeout = 0; + RadioLibTime_t timeout = 0; // get currently active modem uint8_t modem = getPacketType(); if(modem == RADIOLIB_SX126X_PACKET_TYPE_LORA) { // calculate timeout (100 LoRa symbols, the default for SX127x series) float symbolLength = (float)(uint32_t(1) << this->spreadingFactor) / (float)this->bandwidthKhz; - timeout = (uint32_t)(symbolLength * 100.0 * 1000.0); + timeout = (RadioLibTime_t)(symbolLength * 100.0); + } else if(modem == RADIOLIB_SX126X_PACKET_TYPE_GFSK) { // calculate timeout (500 % of expected time-one-air) size_t maxLen = len; @@ -290,26 +284,27 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { maxLen = 0xFF; } float brBps = ((float)(RADIOLIB_SX126X_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)this->bitRate; - timeout = (uint32_t)(((maxLen * 8.0) / brBps) * 1000000.0 * 5.0); + timeout = (RadioLibTime_t)(((maxLen * 8.0) / brBps) * 1000.0 * 5.0); } else { return(RADIOLIB_ERR_UNKNOWN); + } - RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); // start reception - uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); + uint32_t timeoutValue = (uint32_t)(((float)timeout * 1000.0) / 15.625); state = startReceive(timeoutValue); RADIOLIB_ASSERT(state); // wait for packet reception or timeout bool softTimeout = false; - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); // safety check, the timeout should be done by the radio - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { softTimeout = true; break; } @@ -586,7 +581,7 @@ int16_t SX126x::startReceive() { return(this->startReceive(RADIOLIB_SX126X_RX_TIMEOUT_INF, RADIOLIB_SX126X_IRQ_RX_DEFAULT, RADIOLIB_SX126X_IRQ_RX_DONE, 0)); } -int16_t SX126x::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t SX126x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)len; int16_t state = startReceiveCommon(timeout, irqFlags, irqMask); RADIOLIB_ASSERT(state); @@ -644,7 +639,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_BASIC_PRINTLN("Auto sleep period: %lu", sleepPeriod); + RADIOLIB_DEBUG_BASIC_PRINTLN("Auto sleep period: %lu", (long unsigned int)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. @@ -655,7 +650,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_BASIC_PRINTLN("Auto wake period: %lu", wakePeriod); + RADIOLIB_DEBUG_BASIC_PRINTLN("Auto wake period: %lu", (long unsigned int)wakePeriod); // If our sleep period is shorter than our transition time, just use the standard startReceive if(sleepPeriod < this->tcxoDelay + 1016) { @@ -1179,15 +1174,7 @@ int16_t SX126x::setSyncBits(uint8_t *syncWord, uint8_t bitsLen) { bytesLen++; } - // write sync word - int16_t state = writeRegister(RADIOLIB_SX126X_REG_SYNC_WORD_0, syncWord, bytesLen); - RADIOLIB_ASSERT(state); - - // update packet parameters - this->syncWordLength = bitsLen; - state = setPacketParamsFSK(this->preambleLengthFSK, this->crcTypeFSK, this->syncWordLength, this->addrComp, this->whitening, this->packetType); - - return(state); + return(setSyncWord(syncWord, bytesLen)); } int16_t SX126x::setNodeAddress(uint8_t nodeAddr) { @@ -1419,7 +1406,7 @@ int16_t SX126x::variablePacketLengthMode(uint8_t maxLen) { return(setPacketMode(RADIOLIB_SX126X_GFSK_PACKET_VARIABLE, maxLen)); } -uint32_t SX126x::getTimeOnAir(size_t len) { +RadioLibTime_t SX126x::getTimeOnAir(size_t len) { // everything is in microseconds to allow integer arithmetic // some constants have .25, these are multiplied by 4, and have _x4 postfix to indicate that fact if(getPacketType() == RADIOLIB_SX126X_PACKET_TYPE_LORA) { @@ -1450,18 +1437,18 @@ uint32_t SX126x::getTimeOnAir(size_t len) { return((symbolLength_us * nSymbol_x4) / 4); } else { - return((len * 8 * this->bitRate) / (RADIOLIB_SX126X_CRYSTAL_FREQ * 32)); + return(((uint32_t)len * 8 * this->bitRate) / (RADIOLIB_SX126X_CRYSTAL_FREQ * 32)); } } -uint32_t SX126x::calculateRxTimeout(uint32_t timeoutUs) { +RadioLibTime_t SX126x::calculateRxTimeout(RadioLibTime_t timeoutUs) { // the timeout value is given in units of 15.625 microseconds // the calling function should provide some extra width, as this number of units is truncated to integer - uint32_t timeout = timeoutUs / 15.625; + RadioLibTime_t timeout = timeoutUs / 15.625; return(timeout); } -int16_t SX126x::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) { +int16_t SX126x::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT; // flags that can appear in the IRQ register irqMask = RADIOLIB_SX126X_IRQ_RX_DONE | RADIOLIB_SX126X_IRQ_TIMEOUT; // flags that will trigger DIO0 return(RADIOLIB_ERR_NONE); @@ -1583,7 +1570,7 @@ int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) // check the version #if RADIOLIB_DEBUG_BASIC char ver_pre[16]; - this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_pre); + this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast(ver_pre)); RADIOLIB_DEBUG_BASIC_PRINTLN("Pre-update version string: %s", ver_pre); #endif @@ -1615,7 +1602,7 @@ int16_t SX126x::uploadPatch(const uint32_t* patch, size_t len, bool nonvolatile) // check the version again #if RADIOLIB_DEBUG_BASIC char ver_post[16]; - this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)ver_post); + this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast(ver_post)); RADIOLIB_DEBUG_BASIC_PRINTLN("Post-update version string: %s", ver_post); #endif @@ -1742,7 +1729,7 @@ int16_t SX126x::setRx(uint32_t timeout) { int16_t SX126x::setCad(uint8_t symbolNum, uint8_t detPeak, uint8_t detMin) { // default CAD parameters are shown in Semtech AN1200.48, page 41. - uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30}; + const uint8_t detPeakValues[6] = { 22, 22, 24, 25, 26, 30}; // CAD parameters aren't available for SF-6. Just to be safe. if(this->spreadingFactor < 7) { @@ -1853,8 +1840,17 @@ int16_t SX126x::setRfFrequency(uint32_t frf) { return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_RF_FREQUENCY, data, 4)); } -int16_t SX126x::calibrateImage(float freqMin, float freqMax) { +int16_t SX126x::calibrateImageRejection(float freqMin, float freqMax) { + // calculate the calibration coefficients and calibrate image uint8_t data[] = { (uint8_t)floor((freqMin - 1.0f) / 4.0f), (uint8_t)ceil((freqMax + 1.0f) / 4.0f) }; + return(this->calibrateImage(data)); +} + +int16_t SX126x::setPaRampTime(uint8_t rampTime) { + return(this->setTxParams(this->pwr, rampTime)); +} + +int16_t SX126x::calibrateImage(uint8_t* data) { int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE, data, 2); // if something failed, show the device errors @@ -1877,7 +1873,11 @@ uint8_t SX126x::getPacketType() { int16_t SX126x::setTxParams(uint8_t pwr, uint8_t rampTime) { uint8_t data[] = { pwr, rampTime }; - return(this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_PARAMS, data, 2)); + int16_t state = this->mod->SPIwriteStream(RADIOLIB_SX126X_CMD_SET_TX_PARAMS, data, 2); + if(state == RADIOLIB_ERR_NONE) { + this->pwr = pwr; + } + return(state); } int16_t SX126x::setPacketMode(uint8_t mode, uint8_t len) { @@ -2156,18 +2156,18 @@ bool SX126x::findChip(const char* verStr) { // read the version string char version[16]; - this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, (uint8_t*)version); + this->mod->SPIreadRegisterBurst(RADIOLIB_SX126X_REG_VERSION_STRING, 16, reinterpret_cast(version)); // check version register if(strncmp(verStr, version, 6) == 0) { 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_HEXDUMP(reinterpret_cast(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING); RADIOLIB_DEBUG_BASIC_PRINTLN(); flagFound = true; } else { #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_HEXDUMP(reinterpret_cast(version), 16, RADIOLIB_SX126X_REG_VERSION_STRING); RADIOLIB_DEBUG_BASIC_PRINTLN("Expected string: %s", verStr); #endif this->mod->hal->delay(10); diff --git a/lib/RadioLib/src/modules/SX126x/SX126x.h b/lib/RadioLib/src/modules/SX126x/SX126x.h index 848433b..8cc3540 100644 --- a/lib/RadioLib/src/modules/SX126x/SX126x.h +++ b/lib/RadioLib/src/modules/SX126x/SX126x.h @@ -188,6 +188,18 @@ #define RADIOLIB_SX126X_CALIBRATE_RC64K_ON 0b00000001 // 0 0 enabled #define RADIOLIB_SX126X_CALIBRATE_ALL 0b01111111 // 6 0 calibrate all blocks +//RADIOLIB_SX126X_CMD_CALIBRATE_IMAGE +#define RADIOLIB_SX126X_CAL_IMG_430_MHZ_1 0x6B +#define RADIOLIB_SX126X_CAL_IMG_430_MHZ_2 0x6F +#define RADIOLIB_SX126X_CAL_IMG_470_MHZ_1 0x75 +#define RADIOLIB_SX126X_CAL_IMG_470_MHZ_2 0x81 +#define RADIOLIB_SX126X_CAL_IMG_779_MHZ_1 0xC1 +#define RADIOLIB_SX126X_CAL_IMG_779_MHZ_2 0xC5 +#define RADIOLIB_SX126X_CAL_IMG_863_MHZ_1 0xD7 +#define RADIOLIB_SX126X_CAL_IMG_863_MHZ_2 0xDB +#define RADIOLIB_SX126X_CAL_IMG_902_MHZ_1 0xE1 +#define RADIOLIB_SX126X_CAL_IMG_902_MHZ_2 0xE9 + //RADIOLIB_SX126X_CMD_SET_PA_CONFIG #define RADIOLIB_SX126X_PA_CONFIG_HP_MAX 0x07 #define RADIOLIB_SX126X_PA_CONFIG_PA_LUT 0x01 @@ -440,7 +452,7 @@ class SX126x: public PhysicalLayer { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX126x(Module* mod); + explicit SX126x(Module* mod); /*! \brief Whether the module has an XTAL (true) or TCXO (false). Defaults to false. @@ -574,34 +586,34 @@ class SX126x: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Sets interrupt service routine to call when a channel scan is finished. \param func ISR to call. */ - void setChannelScanAction(void (*func)(void)); + void setChannelScanAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a channel scan is finished. */ - void clearChannelScanAction(); + void clearChannelScanAction() override; /*! \brief Interrupt-driven binary transmit method. @@ -625,7 +637,7 @@ class SX126x: public PhysicalLayer { \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method. DIO1 will be activated when full packet is received. @@ -642,7 +654,7 @@ class SX126x: public PhysicalLayer { \param len Only for PhysicalLayer compatibility, not used. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint16_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE, size_t len = 0); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags = RADIOLIB_SX126X_IRQ_RX_DEFAULT, uint32_t irqMask = RADIOLIB_SX126X_IRQ_RX_DONE, size_t len = 0); /*! \brief Interrupt-driven receive method where the device mostly sleeps and periodically wakes to listen. @@ -774,7 +786,7 @@ class SX126x: public PhysicalLayer { \param br FSK bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Set data. @@ -908,7 +920,7 @@ class SX126x: public PhysicalLayer { \brief Gets SNR (Signal to Noise Ratio) of the last received packet. Only available for LoRa modem. \returns SNR of the last received packet in dB. */ - float getSNR(); + float getSNR() override; /*! \brief Gets frequency error of the latest received packet. @@ -935,7 +947,7 @@ class SX126x: public PhysicalLayer { /*! \brief Set modem in variable packet length mode. Available in FSK mode only. - \param len Maximum packet length. + \param maxLen Maximum packet length. \returns \ref status_codes */ int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_SX126X_MAX_PACKET_LENGTH); @@ -945,14 +957,14 @@ class SX126x: public PhysicalLayer { \param len Payload length in bytes. \returns Expected time-on-air in microseconds. */ - uint32_t getTimeOnAir(size_t len) override; + RadioLibTime_t getTimeOnAir(size_t len) override; /*! \brief Calculate the timeout value for this specific module / series (in number of symbols or units of time) \param timeoutUs Timeout in microseconds to listen for \returns Timeout value in a unit that is specific for the used module */ - uint32_t calculateRxTimeout(uint32_t timeoutUs); + RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; /*! \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks @@ -960,13 +972,13 @@ class SX126x: public PhysicalLayer { \param irqMask Mask indicating which IRQ triggers a DIO \returns \ref status_codes */ - int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask); + int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; /*! \brief Check whether the IRQ bit for RxTimeout is set - \returns \ref RxTimeout IRQ is set + \returns Whether RxTimeout IRQ is set */ - bool isRxTimeout(); + bool isRxTimeout() override; /*! \brief Set implicit header mode for future reception/transmission. @@ -1028,7 +1040,7 @@ class SX126x: public PhysicalLayer { \brief Get one truly random byte from RSSI noise. \returns TRNG byte. */ - uint8_t randomByte(); + uint8_t randomByte() override; /*! \brief Enable/disable inversion of the I and Q signals @@ -1042,13 +1054,13 @@ class SX126x: public PhysicalLayer { \brief Set interrupt service routine function to call when data bit is received in direct mode. \param func Pointer to interrupt service routine. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Function to read and process data bit in direct reception mode. \param pin Pin on which to read. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif /*! @@ -1102,10 +1114,25 @@ class SX126x: public PhysicalLayer { */ int16_t setPaConfig(uint8_t paDutyCycle, uint8_t deviceSel, uint8_t hpMax = RADIOLIB_SX126X_PA_CONFIG_HP_MAX, uint8_t paLut = RADIOLIB_SX126X_PA_CONFIG_PA_LUT); + /*! + \brief Perform image rejection calibration for the specified frequency band. + WARNING: Use at your own risk! Setting incorrect values may lead to decreased performance + \param freqMin Frequency band lower bound. + \param freqMax Frequency band upper bound. + \returns \ref status_codes + */ + int16_t calibrateImageRejection(float freqMin, float freqMax); + + /*! + \brief Set PA ramp-up time. Set to 200us by default. + \returns \ref status_codes + */ + int16_t setPaRampTime(uint8_t rampTime); + #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; // SX126x SPI command implementations int16_t setFs(); @@ -1119,9 +1146,9 @@ class SX126x: public PhysicalLayer { int16_t setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = RADIOLIB_SX126X_IRQ_NONE, uint16_t dio3Mask = RADIOLIB_SX126X_IRQ_NONE); virtual int16_t clearIrqStatus(uint16_t clearIrqParams = RADIOLIB_SX126X_IRQ_ALL); int16_t setRfFrequency(uint32_t frf); - int16_t calibrateImage(float freqMin, float freqMax); + int16_t calibrateImage(uint8_t* data); uint8_t getPacketType(); - int16_t setTxParams(uint8_t power, uint8_t rampTime = RADIOLIB_SX126X_PA_RAMP_200U); + int16_t setTxParams(uint8_t power, uint8_t rampTime); int16_t setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro); int16_t setModulationParamsFSK(uint32_t br, uint8_t sh, uint8_t rxBw, uint32_t freqDev); int16_t setPacketParams(uint16_t preambleLen, uint8_t crcType, uint8_t payloadLen, uint8_t hdrType, uint8_t invertIQ); @@ -1136,7 +1163,7 @@ class SX126x: public PhysicalLayer { #if !RADIOLIB_GODMODE protected: #endif - const char* chipType; + const char* chipType = NULL; uint8_t bandwidth = 0; // Allow subclasses to define different TX modes @@ -1166,6 +1193,7 @@ class SX126x: public PhysicalLayer { float dataRateMeasured = 0; uint32_t tcxoDelay = 0; + uint8_t pwr = 0; size_t implicitLen = 0; uint8_t invertIQEnabled = RADIOLIB_SX126X_LORA_IQ_STANDARD; diff --git a/lib/RadioLib/src/modules/SX127x/SX1272.cpp b/lib/RadioLib/src/modules/SX127x/SX1272.cpp index 808d55d..6e8b6c0 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1272.cpp +++ b/lib/RadioLib/src/modules/SX127x/SX1272.cpp @@ -280,15 +280,12 @@ int16_t SX1272::setOutputPower(int8_t power) { } int16_t SX1272::setOutputPower(int8_t power, bool useRfo) { - // check allowed power range - if(useRfo) { - RADIOLIB_CHECK_RANGE(power, -1, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); - } else { - RADIOLIB_CHECK_RANGE(power, 2, 20, RADIOLIB_ERR_INVALID_OUTPUT_POWER); - } + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL, useRfo); + RADIOLIB_ASSERT(state); // set mode to standby - int16_t state = SX127x::standby(); + state = SX127x::standby(); Module* mod = this->getMod(); if(useRfo) { @@ -317,6 +314,26 @@ int16_t SX1272::setOutputPower(int8_t power, bool useRfo) { return(state); } +int16_t SX1272::checkOutputPower(int8_t power, int8_t* clipped) { + return(checkOutputPower(power, clipped, false)); +} + +int16_t SX1272::checkOutputPower(int8_t power, int8_t* clipped, bool useRfo) { + // check allowed power range + if(useRfo) { + if(clipped) { + *clipped = RADIOLIB_MAX(-1, RADIOLIB_MIN(14, power)); + } + RADIOLIB_CHECK_RANGE(power, -1, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + } else { + if(clipped) { + *clipped = RADIOLIB_MAX(2, RADIOLIB_MIN(20, power)); + } + RADIOLIB_CHECK_RANGE(power, 2, 20, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + } + return(RADIOLIB_ERR_NONE); +} + int16_t SX1272::setGain(uint8_t gain) { // check allowed range if(gain > 6) { @@ -419,6 +436,10 @@ int16_t SX1272::setDataShapingOOK(uint8_t sh) { return(state); } +float SX1272::getRSSI() { + return(SX1272::getRSSI(true, false)); +} + float SX1272::getRSSI(bool packet, bool skipReceive) { return(SX127x::getRSSI(packet, skipReceive, -139)); } diff --git a/lib/RadioLib/src/modules/SX127x/SX1272.h b/lib/RadioLib/src/modules/SX127x/SX1272.h index 3ea740a..292bfd7 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1272.h +++ b/lib/RadioLib/src/modules/SX127x/SX1272.h @@ -100,7 +100,7 @@ class SX1272: public SX127x { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1272(Module* mod); + SX1272(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -111,8 +111,7 @@ class SX1272: public SX127x { \param sf %LoRa link spreading factor. Allowed values range from 6 to 12. \param cr %LoRa link coding rate denominator. Allowed values range from 5 to 8. \param syncWord %LoRa sync word. Can be used to distinguish different networks. Note that value 0x34 is reserved for LoRaWAN networks. - \param currentLimit Trim value for OCP (over current protection) in mA. Can be set to multiplies of 5 in range 45 to 120 mA and to multiples of 10 in range 120 to 240 mA. - Set to 0 to disable OCP (not recommended). + \param power Transmission output power in dBm. Allowed values range from 2 to 17 dBm. \param preambleLength Length of %LoRa transmission preamble in symbols. The actual preamble length is 4.25 symbols longer than the set number. Allowed values range from 6 to 65535. \param gain Gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain. @@ -147,7 +146,7 @@ class SX1272: public SX127x { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets %LoRa link bandwidth. Allowed values are 125, 250 and 500 kHz. Only available in %LoRa mode. @@ -207,6 +206,24 @@ class SX1272: public SX127x { */ int16_t setOutputPower(int8_t power, bool useRfo); + /*! + \brief Check if output power is configurable. + This method is needed for compatibility with PhysicalLayer::checkOutputPower. + \param power Output power in dBm, assumes PA_BOOST pin. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \param useRfo Whether to use the RFO (true) or the PA_BOOST (false) pin for the RF output. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped, bool useRfo); + /*! \brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain. Set to 0 to enable automatic gain control (recommended). @@ -232,13 +249,20 @@ class SX1272: public SX127x { */ int16_t setDataShapingOOK(uint8_t sh); + /*! + \brief Gets recorded signal strength indicator. + Overload with packet mode enabled for PhysicalLayer compatibility. + \returns RSSI value in dBm. + */ + float getRSSI() override; + /*! \brief Gets recorded signal strength indicator. \param packet Whether to read last packet RSSI, or the current value. LoRa mode only, ignored for FSK. \param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode. \returns RSSI value in dBm. */ - float getRSSI(bool packet = true, bool skipReceive = false); + float getRSSI(bool packet, bool skipReceive = false); /*! \brief Enables/disables CRC check of received packets. @@ -287,7 +311,7 @@ class SX1272: public SX127x { int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF); int16_t configFSK(); - void errataFix(bool rx); + void errataFix(bool rx) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX127x/SX1273.h b/lib/RadioLib/src/modules/SX127x/SX1273.h index 610bbcc..8e7ab1c 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1273.h +++ b/lib/RadioLib/src/modules/SX127x/SX1273.h @@ -20,7 +20,7 @@ class SX1273: public SX1272 { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1273(Module* mod); + SX1273(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods diff --git a/lib/RadioLib/src/modules/SX127x/SX1276.h b/lib/RadioLib/src/modules/SX127x/SX1276.h index c15f981..89ab9f9 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1276.h +++ b/lib/RadioLib/src/modules/SX127x/SX1276.h @@ -20,7 +20,7 @@ class SX1276: public SX1278 { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1276(Module* mod); + SX1276(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -61,7 +61,7 @@ class SX1276: public SX1278 { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX127x/SX1277.h b/lib/RadioLib/src/modules/SX127x/SX1277.h index 1d334cd..c7efd95 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1277.h +++ b/lib/RadioLib/src/modules/SX127x/SX1277.h @@ -20,7 +20,7 @@ class SX1277: public SX1278 { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1277(Module* mod); + SX1277(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -61,7 +61,7 @@ class SX1277: public SX1278 { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets %LoRa link spreading factor. Allowed values range from 6 to 9. Only available in %LoRa mode. diff --git a/lib/RadioLib/src/modules/SX127x/SX1278.cpp b/lib/RadioLib/src/modules/SX127x/SX1278.cpp index cefb352..78251d2 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1278.cpp +++ b/lib/RadioLib/src/modules/SX127x/SX1278.cpp @@ -294,19 +294,12 @@ int16_t SX1278::setOutputPower(int8_t power) { } int16_t SX1278::setOutputPower(int8_t power, bool useRfo) { - // check allowed power range - if(useRfo) { - // RFO output - RADIOLIB_CHECK_RANGE(power, -3, 15, RADIOLIB_ERR_INVALID_OUTPUT_POWER); - } else { - // PA_BOOST output, check high-power operation - if(power != 20) { - RADIOLIB_CHECK_RANGE(power, 2, 17, RADIOLIB_ERR_INVALID_OUTPUT_POWER); - } - } + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL, useRfo); + RADIOLIB_ASSERT(state); // set mode to standby - int16_t state = SX127x::standby(); + state = SX127x::standby(); Module* mod = this->getMod(); if(useRfo) { @@ -342,6 +335,34 @@ int16_t SX1278::setOutputPower(int8_t power, bool useRfo) { return(state); } +int16_t SX1278::checkOutputPower(int8_t power, int8_t* clipped) { + return(checkOutputPower(power, clipped, false)); +} + +int16_t SX1278::checkOutputPower(int8_t power, int8_t* clipped, bool useRfo) { + // check allowed power range + if(useRfo) { + // RFO output + if(clipped) { + *clipped = RADIOLIB_MAX(-3, RADIOLIB_MIN(15, power)); + } + RADIOLIB_CHECK_RANGE(power, -3, 15, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + } else { + // PA_BOOST output, check high-power operation + if(clipped) { + if(power != 20) { + *clipped = RADIOLIB_MAX(2, RADIOLIB_MIN(17, power)); + } else { + *clipped = 20; + } + } + if(power != 20) { + RADIOLIB_CHECK_RANGE(power, 2, 17, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + } + } + return(RADIOLIB_ERR_NONE); +} + int16_t SX1278::setGain(uint8_t gain) { // check allowed range if(gain > 6) { @@ -448,6 +469,10 @@ int16_t SX1278::setDataShapingOOK(uint8_t sh) { return(state); } +float SX1278::getRSSI() { + return(SX1278::getRSSI(true, false)); +} + float SX1278::getRSSI(bool packet, bool skipReceive) { int16_t offset = -157; if(frequency < 868.0) { diff --git a/lib/RadioLib/src/modules/SX127x/SX1278.h b/lib/RadioLib/src/modules/SX127x/SX1278.h index 979edb7..3c2b008 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1278.h +++ b/lib/RadioLib/src/modules/SX127x/SX1278.h @@ -111,7 +111,7 @@ class SX1278: public SX127x { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1278(Module* mod); + SX1278(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -157,7 +157,7 @@ class SX1278: public SX127x { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets %LoRa link bandwidth. Allowed values are 10.4, 15.6, 20.8, 31.25, 41.7, 62.5, 125, 250 and 500 kHz. Only available in %LoRa mode. @@ -218,6 +218,24 @@ class SX1278: public SX127x { */ int16_t setOutputPower(int8_t power, bool useRfo); + /*! + \brief Check if output power is configurable. + This method is needed for compatibility with PhysicalLayer::checkOutputPower. + \param power Output power in dBm, assumes PA_BOOST pin. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \param useRfo Whether to use the RFO (true) or the PA_BOOST (false) pin for the RF output. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped, bool useRfo); + /*! \brief Sets gain of receiver LNA (low-noise amplifier). Can be set to any integer in range 1 to 6 where 1 is the highest gain. Set to 0 to enable automatic gain control (recommended). @@ -243,13 +261,20 @@ class SX1278: public SX127x { */ int16_t setDataShapingOOK(uint8_t sh); + /*! + \brief Gets recorded signal strength indicator. + Overload with packet mode enabled for PhysicalLayer compatibility. + \returns RSSI value in dBm. + */ + float getRSSI() override; + /*! \brief Gets recorded signal strength indicator. \param packet Whether to read last packet RSSI, or the current value. LoRa mode only, ignored for FSK. \param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode. \returns RSSI value in dBm. */ - float getRSSI(bool packet = true, bool skipReceive = false); + float getRSSI(bool packet, bool skipReceive = false); /*! \brief Enables/disables CRC check of received packets. @@ -298,7 +323,7 @@ class SX1278: public SX127x { int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF); int16_t configFSK(); - void errataFix(bool rx); + void errataFix(bool rx) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX127x/SX1279.h b/lib/RadioLib/src/modules/SX127x/SX1279.h index e802e9c..1dceb42 100644 --- a/lib/RadioLib/src/modules/SX127x/SX1279.h +++ b/lib/RadioLib/src/modules/SX127x/SX1279.h @@ -20,7 +20,7 @@ class SX1279: public SX1278 { \brief Default constructor. Called from Arduino sketch when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX1279(Module* mod); + SX1279(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -61,7 +61,7 @@ class SX1279: public SX1278 { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX127x/SX127x.cpp b/lib/RadioLib/src/modules/SX127x/SX127x.cpp index 7606f00..1309625 100644 --- a/lib/RadioLib/src/modules/SX127x/SX127x.cpp +++ b/lib/RadioLib/src/modules/SX127x/SX127x.cpp @@ -147,15 +147,16 @@ int16_t SX127x::transmit(uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ASSERT(state); int16_t modem = getActiveModem(); - uint32_t start = 0; - uint32_t timeout = 0; + RadioLibTime_t start = 0; + RadioLibTime_t timeout = 0; + RadioLibTime_t toa = getTimeOnAir(len); if(modem == RADIOLIB_SX127X_LORA) { - // calculate timeout (150 % of expected time-on-air) - timeout = getTimeOnAir(len) * 1.5; + // calculate timeout in ms (150 % of expected time-on-air) + timeout = (toa * 1.5) / 1000; } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // calculate timeout (5ms + 500 % of expected time-on-air) - timeout = 5000 + getTimeOnAir(len) * 5; + // calculate timeout in ms (5ms + 500 % of expected time-on-air) + timeout = 5 + (toa * 5) / 1000; } else { return(RADIOLIB_ERR_UNKNOWN); @@ -163,22 +164,23 @@ int16_t SX127x::transmit(uint8_t* data, size_t len, uint8_t addr) { } // start transmission + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for packet transmission or timeout - start = this->mod->hal->micros(); + start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } } // update data rate - uint32_t elapsed = this->mod->hal->micros() - start; - this->dataRate = (len*8.0)/((float)elapsed/1000000.0); + RadioLibTime_t elapsed = this->mod->hal->millis() - start; + this->dataRate = (len*8.0)/((float)elapsed/1000.0); return(finishTransmit()); } @@ -195,20 +197,20 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { RADIOLIB_ASSERT(state); // if no DIO1 is provided, use software timeout (100 LoRa symbols, same as hardware timeout) - uint32_t timeout = 0; + RadioLibTime_t timeout = 0; if(this->mod->getGpio() == RADIOLIB_NC) { float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth; - timeout = (uint32_t)(symbolLength * 100.0 * 1000.0); + timeout = (RadioLibTime_t)(symbolLength * 100.0); } // wait for packet reception or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); if(this->mod->getGpio() == RADIOLIB_NC) { // no GPIO pin provided, use software timeout - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { clearIRQFlags(); return(RADIOLIB_ERR_RX_TIMEOUT); } @@ -223,18 +225,18 @@ int16_t SX127x::receive(uint8_t* data, size_t len) { } } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // calculate timeout (500 % of expected time-on-air) - uint32_t timeout = getTimeOnAir(len) * 5; + // calculate timeout in ms (500 % of expected time-on-air) + RadioLibTime_t timeout = (getTimeOnAir(len) * 5) / 1000; // set mode to receive state = startReceive(len, RADIOLIB_SX127X_RX); RADIOLIB_ASSERT(state); // wait for packet reception or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { clearIRQFlags(); return(RADIOLIB_ERR_RX_TIMEOUT); } @@ -417,7 +419,7 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { return(setMode(mode)); } -int16_t SX127x::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t SX127x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)irqFlags; (void)irqMask; uint8_t mode = RADIOLIB_SX127X_RXCONTINUOUS; @@ -903,13 +905,13 @@ int16_t SX127x::setBitRateCommon(float br, uint8_t fracRegAddr) { RADIOLIB_ASSERT(state); // set bit rate - uint16_t bitRate = (RADIOLIB_SX127X_CRYSTAL_FREQ * 1000.0) / br; - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_BITRATE_MSB, (bitRate & 0xFF00) >> 8, 7, 0); - state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_BITRATE_LSB, bitRate & 0x00FF, 7, 0); + uint16_t bitRateRaw = (RADIOLIB_SX127X_CRYSTAL_FREQ * 1000.0) / br; + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_BITRATE_MSB, (bitRateRaw & 0xFF00) >> 8, 7, 0); + state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_BITRATE_LSB, bitRateRaw & 0x00FF, 7, 0); // set fractional part of bit rate if(!ookEnabled) { - float bitRateRem = ((RADIOLIB_SX127X_CRYSTAL_FREQ * 1000.0) / (float)br) - (float)bitRate; + float bitRateRem = ((RADIOLIB_SX127X_CRYSTAL_FREQ * 1000.0) / (float)br) - (float)bitRateRaw; uint8_t bitRateFrac = bitRateRem * 16; state |= this->mod->SPIsetRegValue(fracRegAddr, bitRateFrac, 7, 0); } @@ -1242,7 +1244,7 @@ float SX127x::getNumSymbols(size_t len) { return(n_pre + n_pay + 4.25f); } -uint32_t SX127x::getTimeOnAir(size_t len) { +RadioLibTime_t SX127x::getTimeOnAir(size_t len) { // check active modem uint8_t modem = getActiveModem(); if (modem == RADIOLIB_SX127X_LORA) { @@ -1252,43 +1254,42 @@ uint32_t SX127x::getTimeOnAir(size_t len) { // get number of symbols float n_sym = getNumSymbols(len); - // Get time-on-air in us + // get time-on-air in us return ceil((double)symbolLength * (double)n_sym) * 1000; } else if(modem == RADIOLIB_SX127X_FSK_OOK) { - // Get number of bits preamble + // get number of bits preamble float n_pre = (float) ((this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_MSB_FSK) << 8) | this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PREAMBLE_LSB_FSK)) * 8; - //Get the number of bits of the sync word + // get the number of bits of the sync word float n_syncWord = (float) (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_SYNC_CONFIG, 2, 0) + 1) * 8; - //Get CRC bits + // get CRC bits float crc = (this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PACKET_CONFIG_1, 4, 4) == RADIOLIB_SX127X_CRC_ON) * 16; if (this->packetLengthConfig == RADIOLIB_SX127X_PACKET_FIXED) { - //If Packet size fixed -> len = fixed packet length + // if packet size fixed -> len = fixed packet length len = this->mod->SPIgetRegValue(RADIOLIB_SX127X_REG_PAYLOAD_LENGTH_FSK); } else { - //if packet variable -> Add 1 extra byte for payload length + // if packet variable -> Add 1 extra byte for payload length len += 1; } - // Calculate time-on-air in us {[(length in bytes) * (8 bits / 1 byte)] / [(Bit Rate in kbps) * (1000 bps / 1 kbps)]} * (1000000 us in 1 sec) - return (uint32_t) (((crc + n_syncWord + n_pre + (float) (len * 8)) / (this->bitRate * 1000.0)) * 1000000.0); - } else { - return(RADIOLIB_ERR_UNKNOWN); + // calculate time-on-air in us {[(length in bytes) * (8 bits / 1 byte)] / [(Bit Rate in kbps) * (1000 bps / 1 kbps)]} * (1000000 us in 1 sec) + return((uint32_t) (((crc + n_syncWord + n_pre + (float) (len * 8)) / (this->bitRate * 1000.0)) * 1000000.0)); } - + + return(RADIOLIB_ERR_UNKNOWN); } -uint32_t SX127x::calculateRxTimeout(uint32_t timeoutUs) { +RadioLibTime_t SX127x::calculateRxTimeout(RadioLibTime_t timeoutUs) { // the timeout is given as the number of symbols // the calling function should provide some extra width, as this number of symbols is truncated to integer // the order of operators is swapped here to decrease the effects of this truncation error float symbolLength = (float) (uint32_t(1) << this->spreadingFactor) / (float) this->bandwidth; - uint32_t numSymbols = (timeoutUs / symbolLength) / 1000; + RadioLibTime_t numSymbols = (timeoutUs / symbolLength) / 1000; return(numSymbols); } -int16_t SX127x::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) { +int16_t SX127x::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { // IRQ flags/masks are inverted to what seems logical for SX127x (0 being activated, 1 being deactivated) irqFlags = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DEFAULT; irqMask = RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_DONE & RADIOLIB_SX127X_MASK_IRQ_FLAG_RX_TIMEOUT; @@ -1334,8 +1335,14 @@ int16_t SX127x::setRSSIConfig(uint8_t smoothingSamples, int8_t offset) { RADIOLIB_CHECK_RANGE(offset, -16, 15, RADIOLIB_ERR_INVALID_RSSI_OFFSET); + // calculate the two's complement + uint8_t offsetRaw = RADIOLIB_ABS(offset); + offsetRaw ^= 0x1F; + offsetRaw += 1; + offsetRaw &= 0x1F; + // set new register values - state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_RSSI_CONFIG, offset << 3, 7, 3); + state = this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_RSSI_CONFIG, offsetRaw << 3, 7, 3); state |= this->mod->SPIsetRegValue(RADIOLIB_SX127X_REG_RSSI_CONFIG, smoothingSamples, 2, 0); return(state); } @@ -1537,7 +1544,7 @@ int16_t SX127x::setPacketMode(uint8_t mode, uint8_t len) { return(state); } -bool SX127x::findChip(uint8_t* vers, uint8_t num) { +bool SX127x::findChip(const uint8_t* vers, uint8_t num) { uint8_t i = 0; bool flagFound = false; while((i < 10) && !flagFound) { @@ -1546,8 +1553,8 @@ bool SX127x::findChip(uint8_t* vers, uint8_t num) { // check version register int16_t version = getChipVersion(); - for(uint8_t i = 0; i < num; i++) { - if(version == vers[i]) { + for(uint8_t j = 0; j < num; j++) { + if(version == vers[j]) { flagFound = true; break; } diff --git a/lib/RadioLib/src/modules/SX127x/SX127x.h b/lib/RadioLib/src/modules/SX127x/SX127x.h index fd3d4a9..a9711d8 100644 --- a/lib/RadioLib/src/modules/SX127x/SX127x.h +++ b/lib/RadioLib/src/modules/SX127x/SX127x.h @@ -594,13 +594,13 @@ class SX127x: public PhysicalLayer { \brief Default constructor. Called internally when creating new LoRa instance. \param mod Instance of Module that will be used to communicate with the %LoRa chip. */ - SX127x(Module* mod); + explicit SX127x(Module* mod); // basic methods /*! \brief Initialization method. Will be called with appropriate parameters when calling initialization method from derived class. - \param chipVersion Array of possible values in SPI version register. Used to verify the connection and hardware version. + \param chipVersions Array of possible values in SPI version register. Used to verify the connection and hardware version. \param numVersions Number of possible chip versions. \param syncWord %LoRa sync word. \param preambleLength Length of %LoRa transmission preamble in symbols. @@ -615,7 +615,7 @@ class SX127x: public PhysicalLayer { /*! \brief Initialization method for FSK modem. Will be called with appropriate parameters when calling FSK initialization method from derived class. - \param chipVersion Array of possible values in SPI version register. Used to verify the connection and hardware version. + \param chipVersions Array of possible values in SPI version register. Used to verify the connection and hardware version. \param numVersions Number of possible chip versions. \param freqDev Frequency deviation of the FSK transmission in kHz. \param rxBw Receiver bandwidth in kHz. @@ -655,7 +655,7 @@ class SX127x: public PhysicalLayer { %Module will wake up automatically when methods like transmit or receive are called. \returns \ref status_codes */ - int16_t sleep(); + int16_t sleep() override; /*! \brief Sets the %LoRa module to standby. @@ -721,34 +721,34 @@ class SX127x: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Sets interrupt service routine to call when a channel scan is finished. \param func ISR to call. */ - void setChannelScanAction(void (*func)(void)); + void setChannelScanAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a channel scan is finished. */ - void clearChannelScanAction(); + void clearChannelScanAction() override; /*! \brief Set interrupt service routine function to call when FIFO is empty. @@ -810,7 +810,7 @@ class SX127x: public PhysicalLayer { Implemented for compatibility with PhysicalLayer. \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method. DIO0 will be activated when full valid packet is received. @@ -832,7 +832,7 @@ class SX127x: public PhysicalLayer { \param len Expected length of packet to be received. Required for LoRa spreading factor 6. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; /*! \brief Reads data that was received after calling startReceive method. When the packet length is not known in advance, @@ -904,7 +904,7 @@ class SX127x: public PhysicalLayer { \brief Gets signal-to-noise ratio of the latest received packet. Only available in LoRa mode. \returns Last packet signal-to-noise ratio (SNR). */ - float getSNR(); + float getSNR() override; /*! \brief Get data rate of the latest transmitted packet. @@ -928,7 +928,7 @@ class SX127x: public PhysicalLayer { /*! \brief Sets FSK automatic frequency correction bandwidth. Allowed values range from 2.6 to 250 kHz. Only available in FSK mode. - \param rxBw Receiver AFC bandwidth to be set (in kHz). + \param afcBw Receiver AFC bandwidth to be set (in kHz). \returns \ref status_codes */ int16_t setAFCBandwidth(float afcBw); @@ -1038,7 +1038,7 @@ class SX127x: public PhysicalLayer { /*! \brief Set modem in variable packet length mode. Available in FSK mode only. - \param len Maximum packet length. + \param maxLen Maximum packet length. \returns \ref status_codes */ int16_t variablePacketLengthMode(uint8_t maxLen = RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK); @@ -1055,14 +1055,14 @@ class SX127x: public PhysicalLayer { \param len Payload length in bytes. \returns Expected time-on-air in microseconds. */ - uint32_t getTimeOnAir(size_t len) override; + RadioLibTime_t getTimeOnAir(size_t len) override; /*! \brief Calculate the timeout value for this specific module / series (in number of symbols or units of time) \param timeoutUs Timeout in microseconds to listen for \returns Timeout value in a unit that is specific for the used module */ - uint32_t calculateRxTimeout(uint32_t timeoutUs); + RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs) override; /*! \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks @@ -1070,13 +1070,13 @@ class SX127x: public PhysicalLayer { \param irqMask Mask indicating which IRQ triggers a DIO \returns \ref status_codes */ - int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask); + int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) override; /*! \brief Check whether the IRQ bit for RxTimeout is set - \returns \ref RxTimeout IRQ is set + \returns Whether RxTimeout IRQ is set */ - bool isRxTimeout(); + bool isRxTimeout() override; /*! \brief Enable CRC filtering and generation. @@ -1133,7 +1133,7 @@ class SX127x: public PhysicalLayer { \brief Get one truly random byte from RSSI noise. \returns TRNG byte. */ - uint8_t randomByte(); + uint8_t randomByte() override; /*! \brief Read version SPI register. Should return SX1278_CHIP_VERSION (0x12) or SX1272_CHIP_VERSION (0x22) if SX127x is connected and working. @@ -1153,13 +1153,13 @@ class SX127x: public PhysicalLayer { \brief Set interrupt service routine function to call when data bit is received in direct mode. \param func Pointer to interrupt service routine. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Function to read and process data bit in direct reception mode. \param pin Pin on which to read. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif /*! @@ -1192,7 +1192,7 @@ class SX127x: public PhysicalLayer { \param value The value that indicates which function to place on that pin. See chip datasheet for details. \returns \ref status_codes */ - int16_t setDIOMapping(uint32_t pin, uint32_t value); + int16_t setDIOMapping(uint32_t pin, uint32_t value) override; /*! \brief Configure DIO mapping to use RSSI or Preamble Detect for pins that support it. @@ -1201,14 +1201,6 @@ class SX127x: public PhysicalLayer { */ int16_t setDIOPreambleDetect(bool usePreambleDetect); - /*! - \brief Gets recorded signal strength indicator. - \param packet Whether to read last packet RSSI, or the current value. LoRa mode only, ignored for FSK. - \param skipReceive Set to true to skip putting radio in receive mode for the RSSI measurement in FSK/OOK mode. - \returns RSSI value in dBm. - */ - float getRSSI(bool packet, bool skipReceive, int16_t offset); - /*! \brief Sets the RSSI value above which the RSSI interrupt is signaled \param dbm A dBm value between -127.5 and 0 inclusive @@ -1229,7 +1221,7 @@ class SX127x: public PhysicalLayer { #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; #if !RADIOLIB_GODMODE protected: @@ -1246,6 +1238,7 @@ class SX127x: public PhysicalLayer { int16_t getActiveModem(); int16_t setFrequencyRaw(float newFreq); int16_t setBitRateCommon(float br, uint8_t fracRegAddr); + float getRSSI(bool packet, bool skipReceive, int16_t offset); #if !RADIOLIB_GODMODE private: @@ -1261,7 +1254,7 @@ class SX127x: public PhysicalLayer { int16_t config(); int16_t directMode(); int16_t setPacketMode(uint8_t mode, uint8_t len); - bool findChip(uint8_t* vers, uint8_t num); + bool findChip(const uint8_t* vers, uint8_t num); int16_t setMode(uint8_t mode); int16_t setActiveModem(uint8_t modem); void clearIRQFlags(); diff --git a/lib/RadioLib/src/modules/SX128x/SX1280.cpp b/lib/RadioLib/src/modules/SX128x/SX1280.cpp index b5b30dd..7692c0a 100644 --- a/lib/RadioLib/src/modules/SX128x/SX1280.cpp +++ b/lib/RadioLib/src/modules/SX128x/SX1280.cpp @@ -13,7 +13,7 @@ int16_t SX1280::range(bool master, uint32_t addr, uint16_t calTable[3][6]) { // wait until ranging is finished Module* mod = this->getMod(); - uint32_t start = mod->hal->millis(); + RadioLibTime_t start = mod->hal->millis(); while(!mod->hal->digitalRead(mod->getIrq())) { mod->hal->yield(); if(mod->hal->millis() - start > 10000) { diff --git a/lib/RadioLib/src/modules/SX128x/SX1280.h b/lib/RadioLib/src/modules/SX128x/SX1280.h index c798f04..e06faec 100644 --- a/lib/RadioLib/src/modules/SX128x/SX1280.h +++ b/lib/RadioLib/src/modules/SX128x/SX1280.h @@ -19,7 +19,7 @@ class SX1280: public SX1281 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1280(Module* mod); + SX1280(Module* mod); // cppcheck-suppress noExplicitConstructor /*! \brief Blocking ranging method. diff --git a/lib/RadioLib/src/modules/SX128x/SX1281.h b/lib/RadioLib/src/modules/SX128x/SX1281.h index 3a41b60..3d697da 100644 --- a/lib/RadioLib/src/modules/SX128x/SX1281.h +++ b/lib/RadioLib/src/modules/SX128x/SX1281.h @@ -18,7 +18,7 @@ class SX1281: public SX128x { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1281(Module* mod); + SX1281(Module* mod); // cppcheck-suppress noExplicitConstructor #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX128x/SX1282.h b/lib/RadioLib/src/modules/SX128x/SX1282.h index 091bf13..dc51ba9 100644 --- a/lib/RadioLib/src/modules/SX128x/SX1282.h +++ b/lib/RadioLib/src/modules/SX128x/SX1282.h @@ -19,7 +19,7 @@ class SX1282: public SX1280 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX1282(Module* mod); + SX1282(Module* mod); // cppcheck-suppress noExplicitConstructor #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/modules/SX128x/SX128x.cpp b/lib/RadioLib/src/modules/SX128x/SX128x.cpp index 55b18dd..58ba3b2 100644 --- a/lib/RadioLib/src/modules/SX128x/SX128x.cpp +++ b/lib/RadioLib/src/modules/SX128x/SX128x.cpp @@ -11,12 +11,15 @@ int16_t SX128x::begin(float freq, float bw, uint8_t sf, uint8_t cr, uint8_t sync this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX128X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX128X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX128X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX128X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX128X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX128X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX128X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize LoRa modulation variables @@ -72,12 +75,15 @@ int16_t SX128x::beginGFSK(float freq, uint16_t br, float freqDev, int8_t pwr, ui this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX128X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX128X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX128X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX128X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX128X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX128X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX128X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize GFSK modulation variables @@ -141,12 +147,15 @@ int16_t SX128x::beginBLE(float freq, uint16_t br, float freqDev, int8_t pwr, uin this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX128X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX128X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX128X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX128X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX128X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX128X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX128X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize BLE modulation variables @@ -196,12 +205,15 @@ int16_t SX128x::beginFLRC(float freq, uint16_t br, uint8_t cr, int8_t pwr, uint1 this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); this->mod->hal->pinMode(this->mod->getGpio(), this->mod->hal->GpioModeInput); - this->mod->SPIreadCommand = RADIOLIB_SX128X_CMD_READ_REGISTER; - this->mod->SPIwriteCommand = RADIOLIB_SX128X_CMD_WRITE_REGISTER; - this->mod->SPInopCommand = RADIOLIB_SX128X_CMD_NOP; - this->mod->SPIstatusCommand = RADIOLIB_SX128X_CMD_GET_STATUS; - this->mod->SPIstreamType = true; - this->mod->SPIparseStatusCb = SPIparseStatus; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_ADDR] = Module::BITS_16; + this->mod->spiConfig.widths[RADIOLIB_MODULE_SPI_WIDTH_CMD] = Module::BITS_8; + this->mod->spiConfig.statusPos = 1; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_SX128X_CMD_READ_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_SX128X_CMD_WRITE_REGISTER; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_NOP] = RADIOLIB_SX128X_CMD_NOP; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_STATUS] = RADIOLIB_SX128X_CMD_GET_STATUS; + this->mod->spiConfig.stream = true; + this->mod->spiConfig.parseStatusCb = SPIparseStatus; RADIOLIB_DEBUG_BASIC_PRINTLN("M\tSX128x"); // initialize FLRC modulation variables @@ -269,7 +281,7 @@ int16_t SX128x::reset(bool verify) { } // set mode to standby - uint32_t start = this->mod->hal->millis(); + RadioLibTime_t start = this->mod->hal->millis(); while(true) { // try to set mode to standby int16_t state = standby(); @@ -305,20 +317,19 @@ int16_t SX128x::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t state = standby(); RADIOLIB_ASSERT(state); - // calculate timeout (500% of expected time-on-air) - uint32_t timeout = getTimeOnAir(len) * 5; - - RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); + // calculate timeout in ms (500% of expected time-on-air) + RadioLibTime_t timeout = (getTimeOnAir(len) * 5) / 1000; + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); // start transmission state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for packet transmission or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } @@ -339,9 +350,8 @@ int16_t SX128x::receive(uint8_t* data, size_t len) { RADIOLIB_ASSERT(state); // calculate timeout (1000% of expected time-on-air) - uint32_t timeout = getTimeOnAir(len) * 10; - - RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu us", timeout); + RadioLibTime_t timeout = getTimeOnAir(len) * 10; + RADIOLIB_DEBUG_BASIC_PRINTLN("Timeout in %lu ms", timeout); // start reception uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); @@ -350,11 +360,11 @@ int16_t SX128x::receive(uint8_t* data, size_t len) { // wait for packet reception or timeout bool softTimeout = false; - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(!this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); // safety check, the timeout should be done by the radio - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { softTimeout = true; break; } @@ -401,28 +411,8 @@ int16_t SX128x::receiveDirect() { } int16_t SX128x::scanChannel() { - // check active modem - if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { - return(RADIOLIB_ERR_WRONG_MODEM); - } - - // set mode to standby - int16_t state = standby(); - RADIOLIB_ASSERT(state); - - // set DIO pin mapping - state = setDioIrqParams(RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE, RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE); - RADIOLIB_ASSERT(state); - - // clear interrupt flags - state = clearIrqStatus(); - RADIOLIB_ASSERT(state); - - // set RF switch (if present) - this->mod->setRfSwitchState(Module::MODE_RX); - // set mode to CAD - state = setCad(); + int16_t state = startChannelScan(); RADIOLIB_ASSERT(state); // wait for channel activity detected or timeout @@ -431,18 +421,7 @@ int16_t SX128x::scanChannel() { } // check CAD result - uint16_t cadResult = getIrqStatus(); - if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DETECTED) { - // detected some LoRa activity - clearIrqStatus(); - return(RADIOLIB_LORA_DETECTED); - } else if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DONE) { - // channel is free - clearIrqStatus(); - return(RADIOLIB_CHANNEL_FREE); - } - - return(RADIOLIB_ERR_UNKNOWN); + return(getChannelScanResult()); } int16_t SX128x::sleep(bool retainConfig) { @@ -578,7 +557,7 @@ int16_t SX128x::startReceive() { return(this->startReceive(RADIOLIB_SX128X_RX_TIMEOUT_INF, RADIOLIB_SX128X_IRQ_RX_DEFAULT, RADIOLIB_SX128X_IRQ_RX_DONE, 0)); } -int16_t SX128x::startReceive(uint16_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t SX128x::startReceive(uint16_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)len; // check active modem @@ -654,6 +633,52 @@ int16_t SX128x::readData(uint8_t* data, size_t len) { return(state); } +int16_t SX128x::startChannelScan() { + // check active modem + if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set DIO pin mapping + state = setDioIrqParams(RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE, RADIOLIB_SX128X_IRQ_CAD_DETECTED | RADIOLIB_SX128X_IRQ_CAD_DONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set RF switch (if present) + this->mod->setRfSwitchState(Module::MODE_RX); + + // set mode to CAD + return(setCad()); +} + +int16_t SX128x::getChannelScanResult() { + // check active modem + if(getPacketType() != RADIOLIB_SX128X_PACKET_TYPE_LORA) { + return(RADIOLIB_ERR_WRONG_MODEM); + } + + // check CAD result + uint16_t cadResult = getIrqStatus(); + int16_t state = RADIOLIB_ERR_UNKNOWN; + if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DETECTED) { + // detected some LoRa activity + state = RADIOLIB_LORA_DETECTED; + } else if(cadResult & RADIOLIB_SX128X_IRQ_CAD_DONE) { + // channel is free + state = RADIOLIB_CHANNEL_FREE; + } + + clearIrqStatus(); + return(state); +} + int16_t SX128x::setFrequency(float freq) { RADIOLIB_CHECK_RANGE(freq, 2400.0, 2500.0, RADIOLIB_ERR_INVALID_FREQUENCY); @@ -755,11 +780,22 @@ int16_t SX128x::setCodingRate(uint8_t cr, bool longInterleaving) { } int16_t SX128x::setOutputPower(int8_t pwr) { - RADIOLIB_CHECK_RANGE(pwr, -18, 13, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + // check if power value is configurable + int16_t state = checkOutputPower(power, NULL); + RADIOLIB_ASSERT(state); + this->power = pwr + 18; return(setTxParams(this->power)); } +int16_t SX128x::checkOutputPower(int8_t power, int8_t* clipped) { + if(clipped) { + *clipped = RADIOLIB_MAX(-18, RADIOLIB_MIN(13, power)); + } + RADIOLIB_CHECK_RANGE(power, -18, 13, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + return(RADIOLIB_ERR_NONE); +} + int16_t SX128x::setPreambleLength(uint32_t preambleLength) { uint8_t modem = getPacketType(); if((modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) || (modem == RADIOLIB_SX128X_PACKET_TYPE_RANGING)) { @@ -888,13 +924,13 @@ int16_t SX128x::setFrequencyDeviation(float freqDev) { } // update modulation parameters - uint8_t modIndex = (uint8_t)((8.0 * (newFreqDev / (float)this->bitRateKbps)) - 1.0); - if(modIndex > RADIOLIB_SX128X_BLE_GFSK_MOD_IND_4_00) { + uint8_t modInd = (uint8_t)((8.0 * (newFreqDev / (float)this->bitRateKbps)) - 1.0); + if(modInd > RADIOLIB_SX128X_BLE_GFSK_MOD_IND_4_00) { return(RADIOLIB_ERR_INVALID_MODULATION_PARAMETERS); } // update modulation parameters - this->modIndex = modIndex; + this->modIndex = modInd; return(setModulationParams(this->bitRate, this->modIndex, this->shaping)); } @@ -928,7 +964,7 @@ int16_t SX128x::setDataShaping(uint8_t sh) { } } -int16_t SX128x::setSyncWord(uint8_t* syncWord, uint8_t len) { +int16_t SX128x::setSyncWord(const uint8_t* syncWord, uint8_t len) { // check active modem uint8_t modem = getPacketType(); if(!((modem == RADIOLIB_SX128X_PACKET_TYPE_GFSK) || (modem == RADIOLIB_SX128X_PACKET_TYPE_FLRC))) { @@ -1225,7 +1261,7 @@ size_t SX128x::getPacketLength(bool update) { return((size_t)rxBufStatus[0]); } -uint32_t SX128x::getTimeOnAir(size_t len) { +RadioLibTime_t SX128x::getTimeOnAir(size_t len) { // check active modem uint8_t modem = getPacketType(); if(modem == RADIOLIB_SX128X_PACKET_TYPE_LORA) { diff --git a/lib/RadioLib/src/modules/SX128x/SX128x.h b/lib/RadioLib/src/modules/SX128x/SX128x.h index 1348f3a..782b95c 100644 --- a/lib/RadioLib/src/modules/SX128x/SX128x.h +++ b/lib/RadioLib/src/modules/SX128x/SX128x.h @@ -359,7 +359,7 @@ class SX128x: public PhysicalLayer { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - SX128x(Module* mod); + explicit SX128x(Module* mod); // basic methods @@ -455,7 +455,7 @@ class SX128x: public PhysicalLayer { \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. \returns \ref status_codes */ - int16_t scanChannel(); + int16_t scanChannel() override; /*! \brief Sets the module to sleep mode. To wake the device up, call standby(). @@ -497,23 +497,23 @@ class SX128x: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Interrupt-driven binary transmit method. @@ -537,7 +537,7 @@ class SX128x: public PhysicalLayer { \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method. DIO1 will be activated when full packet is received. @@ -551,7 +551,7 @@ class SX128x: public PhysicalLayer { \param len Only for PhysicalLayer compatibility, not used. \returns \ref status_codes */ - int16_t startReceive(uint16_t timeout, uint16_t irqFlags = RADIOLIB_SX128X_IRQ_RX_DEFAULT, uint16_t irqMask = RADIOLIB_SX128X_IRQ_RX_DONE, size_t len = 0); + int16_t startReceive(uint16_t timeout, uint32_t irqFlags = RADIOLIB_SX128X_IRQ_RX_DEFAULT, uint32_t irqMask = RADIOLIB_SX128X_IRQ_RX_DONE, size_t len = 0); /*! \brief Reads the current IRQ status. @@ -568,6 +568,19 @@ class SX128x: public PhysicalLayer { \returns \ref status_codes */ int16_t readData(uint8_t* data, size_t len) override; + + /*! + \brief Interrupt-driven channel activity detection method. DIO1 will be activated + when LoRa preamble is detected, or upon timeout. Defaults to CAD parameter values recommended by AN1200.48. + \returns \ref status_codes + */ + int16_t startChannelScan() override; + + /*! + \brief Read the channel scan result + \returns \ref status_codes + */ + int16_t getChannelScanResult() override; // configuration methods @@ -576,7 +589,7 @@ class SX128x: public PhysicalLayer { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets LoRa bandwidth. Allowed values are 203.125, 406.25, 812.5 and 1625.0 kHz. @@ -606,7 +619,15 @@ class SX128x: public PhysicalLayer { \param pwr Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t pwr); + int16_t setOutputPower(int8_t pwr) override; + + /*! + \brief Check if output power is configurable. + \param power Output power in dBm. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + int16_t checkOutputPower(int8_t power, int8_t* clipped) override; /*! \brief Sets preamble length for currently active modem. Allowed values range from 1 to 65535. @@ -621,7 +642,7 @@ class SX128x: public PhysicalLayer { \param br FSK/FLRC bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Sets FSK frequency deviation. Allowed values range from 0.0 to 3200.0 kHz. @@ -645,7 +666,7 @@ class SX128x: public PhysicalLayer { \param len Sync word length in bytes. \returns \ref status_codes */ - int16_t setSyncWord(uint8_t* syncWord, uint8_t len); + int16_t setSyncWord(const uint8_t* syncWord, uint8_t len); /*! \brief Sets LoRa sync word. @@ -696,13 +717,13 @@ class SX128x: public PhysicalLayer { \brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. \returns RSSI of the last received packet in dBm. */ - float getRSSI(); + float getRSSI() override; /*! \brief Gets SNR (Signal to Noise Ratio) of the last received packet. Only available for LoRa or ranging modem. \returns SNR of the last received packet in dB. */ - float getSNR(); + float getSNR() override; /*! \brief Gets frequency error of the latest received packet. @@ -722,7 +743,7 @@ class SX128x: public PhysicalLayer { \param len Payload length in bytes. \returns Expected time-on-air in microseconds. */ - uint32_t getTimeOnAir(size_t len); + RadioLibTime_t getTimeOnAir(size_t len) override; /*! \brief Set implicit header mode for future reception/transmission. @@ -754,33 +775,33 @@ class SX128x: public PhysicalLayer { \brief Dummy random method, to ensure PhysicalLayer compatibility. \returns Always returns 0. */ - uint8_t randomByte(); + uint8_t randomByte() override; /*! \brief Enable/disable inversion of the I and Q signals \param enable QI inversion enabled (true) or disabled (false); \returns \ref status_codes */ - int16_t invertIQ(bool enable); + int16_t invertIQ(bool enable) override; #if !RADIOLIB_EXCLUDE_DIRECT_RECEIVE /*! \brief Dummy method, to ensure PhysicalLayer compatibility. \param func Ignored. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Dummy method, to ensure PhysicalLayer compatibility. \param pin Ignored. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; // cached LoRa parameters float bandwidthKhz = 0; diff --git a/lib/RadioLib/src/modules/Si443x/Si4430.h b/lib/RadioLib/src/modules/Si443x/Si4430.h index 6acac97..2d212e0 100644 --- a/lib/RadioLib/src/modules/Si443x/Si4430.h +++ b/lib/RadioLib/src/modules/Si443x/Si4430.h @@ -21,7 +21,7 @@ class Si4430: public Si4432 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio chip. */ - Si4430(Module* mod); + Si4430(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -44,14 +44,14 @@ class Si4430: public Si4432 { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets output power. Allowed values range from -8 to 13 dBm in 3 dBm steps. \param power Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t power); + int16_t setOutputPower(int8_t power) override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/Si443x/Si4431.h b/lib/RadioLib/src/modules/Si443x/Si4431.h index 8d3d9dd..a8939ea 100644 --- a/lib/RadioLib/src/modules/Si443x/Si4431.h +++ b/lib/RadioLib/src/modules/Si443x/Si4431.h @@ -21,7 +21,7 @@ class Si4431: public Si4432 { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio chip. */ - Si4431(Module* mod); + Si4431(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -44,7 +44,7 @@ class Si4431: public Si4432 { \param power Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t power); + int16_t setOutputPower(int8_t power) override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/Si443x/Si4432.h b/lib/RadioLib/src/modules/Si443x/Si4432.h index b0a8cb8..f3ac066 100644 --- a/lib/RadioLib/src/modules/Si443x/Si4432.h +++ b/lib/RadioLib/src/modules/Si443x/Si4432.h @@ -21,7 +21,7 @@ class Si4432: public Si443x { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio chip. */ - Si4432(Module* mod); + Si4432(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -44,14 +44,14 @@ class Si4432: public Si443x { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets output power. Allowed values range from -1 to 20 dBm in 3 dBm steps. \param power Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t power); + int16_t setOutputPower(int8_t power) override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/Si443x/Si443x.cpp b/lib/RadioLib/src/modules/Si443x/Si443x.cpp index 541f3eb..7dfd8eb 100644 --- a/lib/RadioLib/src/modules/Si443x/Si443x.cpp +++ b/lib/RadioLib/src/modules/Si443x/Si443x.cpp @@ -73,17 +73,17 @@ void Si443x::reset() { int16_t Si443x::transmit(uint8_t* data, size_t len, uint8_t addr) { // calculate timeout (5ms + 500 % of expected time-on-air) - uint32_t timeout = 5000000 + (uint32_t)((((float)(len * 8)) / (this->bitRate * 1000.0)) * 5000000.0); + RadioLibTime_t timeout = 5 + (uint32_t)((((float)(len * 8)) / this->bitRate) * 5); // start transmission int16_t state = startTransmit(data, len, addr); RADIOLIB_ASSERT(state); // wait for transmission end or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } @@ -94,16 +94,16 @@ int16_t Si443x::transmit(uint8_t* data, size_t len, uint8_t addr) { int16_t Si443x::receive(uint8_t* data, size_t len) { // calculate timeout (500 ms + 400 full 64-byte packets at current bit rate) - uint32_t timeout = 500000 + (1.0/(this->bitRate*1000.0))*(RADIOLIB_SI443X_MAX_PACKET_LENGTH*400.0); + RadioLibTime_t timeout = 500 + (1.0/(this->bitRate))*(RADIOLIB_SI443X_MAX_PACKET_LENGTH*400.0); // start reception int16_t state = startReceive(); RADIOLIB_ASSERT(state); // wait for packet reception or timeout - uint32_t start = this->mod->hal->micros(); + RadioLibTime_t start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getIrq())) { - if(this->mod->hal->micros() - start > timeout) { + if(this->mod->hal->millis() - start > timeout) { standby(); clearIRQFlags(); return(RADIOLIB_ERR_RX_TIMEOUT); @@ -300,7 +300,7 @@ int16_t Si443x::startReceive() { return(state); } -int16_t Si443x::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t Si443x::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -558,11 +558,11 @@ int16_t Si443x::setEncoding(uint8_t encoding) { /// \todo - add inverted Manchester? switch(encoding) { case RADIOLIB_ENCODING_NRZ: - return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_OFF, 2, 0)); + return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_OFF, 2, 0)); case RADIOLIB_ENCODING_MANCHESTER: - return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_ON | RADIOLIB_SI443X_WHITENING_OFF, 2, 0)); + return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_ON | RADIOLIB_SI443X_WHITENING_OFF, 2, 0)); case RADIOLIB_ENCODING_WHITENING: - return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_ON, 2, 0)); + return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_ON, 2, 0)); default: return(RADIOLIB_ERR_INVALID_ENCODING); } @@ -577,12 +577,8 @@ int16_t Si443x::setDataShaping(uint8_t sh) { switch(sh) { case RADIOLIB_SHAPING_NONE: return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_OFF, 2, 0)); - case RADIOLIB_SHAPING_0_3: - return(RADIOLIB_ERR_INVALID_ENCODING); case RADIOLIB_SHAPING_0_5: return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_2, RADIOLIB_SI443X_MODULATION_GFSK, 1, 0)); - case RADIOLIB_SHAPING_1_0: - return(this->mod->SPIsetRegValue(RADIOLIB_SI443X_REG_MODULATION_MODE_CONTROL_1, RADIOLIB_SI443X_MANCHESTER_INVERTED_OFF | RADIOLIB_SI443X_MANCHESTER_OFF | RADIOLIB_SI443X_WHITENING_ON, 2, 0)); default: return(RADIOLIB_ERR_INVALID_ENCODING); } @@ -771,7 +767,7 @@ int16_t Si443x::updateClockRecovery() { // print that whole mess 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); + RADIOLIB_DEBUG_BASIC_PRINTLN("\t%d\t%X\n%lu\t%lX\n%d\t%X", rxOsr_fixed, rxOsr_fixed, (long unsigned int)ncoOff, (long unsigned int)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/lib/RadioLib/src/modules/Si443x/Si443x.h b/lib/RadioLib/src/modules/Si443x/Si443x.h index 9d63980..24f8bb0 100644 --- a/lib/RadioLib/src/modules/Si443x/Si443x.h +++ b/lib/RadioLib/src/modules/Si443x/Si443x.h @@ -564,7 +564,7 @@ class Si443x: public PhysicalLayer { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - Si443x(Module* mod); + explicit Si443x(Module* mod); // basic methods @@ -607,7 +607,7 @@ class Si443x: public PhysicalLayer { %Module will wake up automatically when methods like transmit or receive are called. \returns \ref status_codes */ - int16_t sleep(); + int16_t sleep() override; /*! \brief Sets the module to standby (with XTAL on). @@ -658,23 +658,23 @@ class Si443x: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Interrupt-driven binary transmit method. Will start transmitting arbitrary binary data up to 64 bytes long. @@ -695,7 +695,7 @@ class Si443x: public PhysicalLayer { \brief Interrupt-driven receive method. IRQ will be activated when full valid packet is received. \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. @@ -705,7 +705,7 @@ class Si443x: public PhysicalLayer { \param len Ignored. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; /*! \brief Reads data that was received after calling startReceive method. When the packet length is not known in advance, @@ -724,7 +724,7 @@ class Si443x: public PhysicalLayer { \param br Bit rate to be set (in kbps). \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Sets FSK frequency deviation from carrier frequency. Allowed values range from 0.625 to 320.0 kHz. @@ -745,7 +745,7 @@ class Si443x: public PhysicalLayer { \param syncWord Pointer to the array of sync word bytes. \param len Sync word length in bytes. */ - int16_t setSyncWord(uint8_t* syncWord, size_t len); + int16_t setSyncWord(uint8_t* syncWord, size_t len) override; /*! \brief Sets preamble length. @@ -787,7 +787,7 @@ class Si443x: public PhysicalLayer { \brief Get one truly random byte from RSSI noise. \returns TRNG byte. */ - uint8_t randomByte(); + uint8_t randomByte() override; /*! \brief Read version SPI register. Should return RADIOLIB_SI443X_DEVICE_VERSION (0x06) if Si443x is connected and working. @@ -800,13 +800,13 @@ class Si443x: public PhysicalLayer { \brief Set interrupt service routine function to call when data bit is received in direct mode. \param func Pointer to interrupt service routine. */ - void setDirectAction(void (*func)(void)); + void setDirectAction(void (*func)(void)) override; /*! \brief Function to read and process data bit in direct reception mode. \param pin Pin on which to read. */ - void readBit(uint32_t pin); + void readBit(uint32_t pin) override; #endif /*! @@ -826,7 +826,7 @@ class Si443x: public PhysicalLayer { #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; #if !RADIOLIB_GODMODE protected: diff --git a/lib/RadioLib/src/modules/nRF24/nRF24.cpp b/lib/RadioLib/src/modules/nRF24/nRF24.cpp index a542988..09d8bf0 100644 --- a/lib/RadioLib/src/modules/nRF24/nRF24.cpp +++ b/lib/RadioLib/src/modules/nRF24/nRF24.cpp @@ -8,8 +8,8 @@ nRF24::nRF24(Module* mod) : PhysicalLayer(RADIOLIB_NRF24_FREQUENCY_STEP_SIZE, RA int16_t nRF24::begin(int16_t freq, int16_t dr, int8_t pwr, uint8_t addrWidth) { // set module properties - this->mod->SPIreadCommand = RADIOLIB_NRF24_CMD_READ; - this->mod->SPIwriteCommand = RADIOLIB_NRF24_CMD_WRITE; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_READ] = RADIOLIB_NRF24_CMD_READ; + this->mod->spiConfig.cmds[RADIOLIB_MODULE_SPI_COMMAND_WRITE] = RADIOLIB_NRF24_CMD_WRITE; this->mod->init(); this->mod->hal->pinMode(this->mod->getIrq(), this->mod->hal->GpioModeInput); @@ -88,7 +88,7 @@ int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t addr) { RADIOLIB_ASSERT(state); // wait until transmission is finished - uint32_t start = this->mod->hal->micros(); + uint32_t start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); @@ -98,8 +98,8 @@ int16_t nRF24::transmit(uint8_t* data, size_t len, uint8_t addr) { return(RADIOLIB_ERR_ACK_NOT_RECEIVED); } - // check timeout: 15 retries * 4ms (max Tx time as per datasheet) - if(this->mod->hal->micros() - start >= 60000) { + // check timeout: 15 retries * 4ms (max Tx time as per datasheet) + 10 ms + if(this->mod->hal->millis() - start >= ((15 * 4) + 10)) { finishTransmit(); return(RADIOLIB_ERR_TX_TIMEOUT); } @@ -114,12 +114,12 @@ int16_t nRF24::receive(uint8_t* data, size_t len) { RADIOLIB_ASSERT(state); // wait for Rx_DataReady or timeout - uint32_t start = this->mod->hal->micros(); + uint32_t start = this->mod->hal->millis(); while(this->mod->hal->digitalRead(this->mod->getIrq())) { this->mod->hal->yield(); - // check timeout: 15 retries * 4ms (max Tx time as per datasheet) - if(this->mod->hal->micros() - start >= 60000) { + // check timeout: 15 retries * 4ms (max Tx time as per datasheet) + 10 ms + if(this->mod->hal->millis() - start >= ((15 * 4) + 10)) { standby(); clearIRQ(); return(RADIOLIB_ERR_RX_TIMEOUT); @@ -249,7 +249,7 @@ int16_t nRF24::startReceive() { return(state); } -int16_t nRF24::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t nRF24::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -298,14 +298,14 @@ int16_t nRF24::setBitRate(float br) { RADIOLIB_ASSERT(state); // set data rate - uint16_t dataRate = (uint16_t)br; - if(dataRate == 250) { + uint16_t bitRate = (uint16_t)br; + if(bitRate == 250) { state = this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_250_KBPS, 5, 5); state |= this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_250_KBPS, 3, 3); - } else if(dataRate == 1000) { + } else if(bitRate == 1000) { state = this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_1_MBPS, 5, 5); state |= this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_1_MBPS, 3, 3); - } else if(dataRate == 2000) { + } else if(bitRate == 2000) { state = this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_2_MBPS, 5, 5); state |= this->mod->SPIsetRegValue(RADIOLIB_NRF24_REG_RF_SETUP, RADIOLIB_NRF24_DR_2_MBPS, 3, 3); } else { @@ -313,7 +313,7 @@ int16_t nRF24::setBitRate(float br) { } if(state == RADIOLIB_ERR_NONE) { - this->dataRate = dataRate; + this->dataRate = bitRate; } diff --git a/lib/RadioLib/src/modules/nRF24/nRF24.h b/lib/RadioLib/src/modules/nRF24/nRF24.h index 54ed9c2..14d2620 100644 --- a/lib/RadioLib/src/modules/nRF24/nRF24.h +++ b/lib/RadioLib/src/modules/nRF24/nRF24.h @@ -193,7 +193,7 @@ class nRF24: public PhysicalLayer { \brief Default constructor. \param mod Instance of Module that will be used to communicate with the radio. */ - nRF24(Module* mod); + nRF24(Module* mod); // cppcheck-suppress noExplicitConstructor // basic methods @@ -215,7 +215,7 @@ class nRF24: public PhysicalLayer { \brief Sets the module to sleep mode. \returns \ref status_codes */ - int16_t sleep(); + int16_t sleep() override; /*! \brief Sets the module to standby mode. @@ -279,23 +279,23 @@ class nRF24: public PhysicalLayer { \brief Sets interrupt service routine to call when a packet is received. \param func ISR to call. */ - void setPacketReceivedAction(void (*func)(void)); + void setPacketReceivedAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is received. */ - void clearPacketReceivedAction(); + void clearPacketReceivedAction() override; /*! \brief Sets interrupt service routine to call when a packet is sent. \param func ISR to call. */ - void setPacketSentAction(void (*func)(void)); + void setPacketSentAction(void (*func)(void)) override; /*! \brief Clears interrupt service routine to call when a packet is sent. */ - void clearPacketSentAction(); + void clearPacketSentAction() override; /*! \brief Interrupt-driven binary transmit method. IRQ will be activated when full packet is transmitted. @@ -317,7 +317,7 @@ class nRF24: public PhysicalLayer { \brief Interrupt-driven receive method. IRQ will be activated when full packet is received. \returns \ref status_codes */ - int16_t startReceive(); + int16_t startReceive() override; /*! \brief Interrupt-driven receive method, implemented for compatibility with PhysicalLayer. @@ -327,7 +327,7 @@ class nRF24: public PhysicalLayer { \param len Ignored. \returns \ref status_codes */ - int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) override; /*! \brief Reads data received after calling startReceive method. When the packet length is not known in advance, @@ -345,21 +345,21 @@ class nRF24: public PhysicalLayer { \param freq Carrier frequency to be set in MHz. \returns \ref status_codes */ - int16_t setFrequency(float freq); + int16_t setFrequency(float freq) override; /*! \brief Sets bit rate. Allowed values are 2000, 1000 or 250 kbps. \param br Bit rate to be set in kbps. \returns \ref status_codes */ - int16_t setBitRate(float br); + int16_t setBitRate(float br) override; /*! \brief Sets output power. Allowed values are -18, -12, -6 or 0 dBm. \param pwr Output power to be set in dBm. \returns \ref status_codes */ - int16_t setOutputPower(int8_t pwr); + int16_t setOutputPower(int8_t pwr) override; /*! \brief Sets address width of transmit and receive pipes in bytes. Allowed values are 3, 4 or 5 bytes. @@ -460,7 +460,7 @@ class nRF24: public PhysicalLayer { /*! \brief Dummy encoding configuration method, to ensure PhysicalLayer compatibility. - \param sh Ignored. + \param encoding Ignored. \returns \ref status_codes */ int16_t setEncoding(uint8_t encoding) override; @@ -468,7 +468,7 @@ class nRF24: public PhysicalLayer { #if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL protected: #endif - Module* getMod(); + Module* getMod() override; void SPIreadRxPayload(uint8_t* data, uint8_t numBytes); void SPIwriteTxPayload(uint8_t* data, uint8_t numBytes); diff --git a/lib/RadioLib/src/protocols/AFSK/AFSK.h b/lib/RadioLib/src/protocols/AFSK/AFSK.h index ea48630..e38b5b7 100644 --- a/lib/RadioLib/src/protocols/AFSK/AFSK.h +++ b/lib/RadioLib/src/protocols/AFSK/AFSK.h @@ -26,7 +26,7 @@ class AFSKClient { \brief Copy contructor. \param aud Pointer to the AFSKClient instance to copy. */ - AFSKClient(AFSKClient* aud); + explicit AFSKClient(AFSKClient* aud); /*! \brief Initialization method. @@ -44,7 +44,7 @@ class AFSKClient { /*! \brief Stops transmitting audio tone. - \param freq Keep transmitter on - this may limit noise when switching transmitter on or off. + \param keepOn Keep transmitter on - this may limit noise when switching transmitter on or off. \returns \ref status_codes */ int16_t noTone(bool keepOn = false); diff --git a/lib/RadioLib/src/protocols/APRS/APRS.cpp b/lib/RadioLib/src/protocols/APRS/APRS.cpp index 55b034d..5c54b21 100644 --- a/lib/RadioLib/src/protocols/APRS/APRS.cpp +++ b/lib/RadioLib/src/protocols/APRS/APRS.cpp @@ -49,22 +49,28 @@ int16_t APRSClient::sendPosition(char* destCallsign, uint8_t destSSID, char* lat #endif // build the info field - if((msg == NULL) && (time == NULL)) { - // no message, no timestamp - sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_NO_MSG "%s%c%s%c", lat, table, lon, symbol); - } else if((msg != NULL) && (time == NULL)) { - // message, no timestamp - sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_MSG "%s%c%s%c%s", lat, table, lon, symbol, msg); - } else if((msg == NULL) && (time != NULL)) { - // timestamp, no message - sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_NO_MSG "%s%s%c%s%c", time, lat, table, lon, symbol); + if(msg != NULL) { + if(time != NULL) { + // timestamp and message + sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "%s%s%c%s%c%s", time, lat, table, lon, symbol, msg); + } else { + // message, no timestamp + sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_MSG "%s%c%s%c%s", lat, table, lon, symbol, msg); + } + } else { - // timestamp and message - sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_MSG "%s%s%c%s%c%s", time, lat, table, lon, symbol, msg); + if(time != NULL) { + // timestamp, no message + sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_TIME_NO_MSG "%s%s%c%s%c", time, lat, table, lon, symbol); + } else { + // no message, no timestamp + sprintf(info, RADIOLIB_APRS_DATA_TYPE_POSITION_NO_TIME_NO_MSG "%s%c%s%c", lat, table, lon, symbol); + } + } - info[len] = '\0'; // send the frame + info[len] = '\0'; int16_t state = sendFrame(destCallsign, destSSID, info); #if !RADIOLIB_STATIC_ONLY delete[] info; @@ -244,9 +250,9 @@ int16_t APRSClient::sendFrame(char* destCallsign, uint8_t destSSID, char* info) char srcCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1]; axClient->getCallsign(srcCallsign); - AX25Frame frameUI(destCallsign, destSSID, srcCallsign, axClient->getSSID(), RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION | - RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME, - RADIOLIB_AX25_PID_NO_LAYER_3, (const char*)info); + AX25Frame frameUI(destCallsign, destSSID, srcCallsign, axClient->getSSID(), + RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME, + RADIOLIB_AX25_PID_NO_LAYER_3, const_cast(info)); return(axClient->sendFrame(&frameUI)); @@ -256,7 +262,7 @@ int16_t APRSClient::sendFrame(char* destCallsign, uint8_t destSSID, char* info) char* buff = new char[len]; snprintf(buff, len, RADIOLIB_APRS_LORA_HEADER "%s-%d>%s,WIDE%d-%d:%s", this->src, this->id, destCallsign, destSSID, destSSID, info); - int16_t res = this->phyLayer->transmit((uint8_t*)buff, strlen(buff)); + int16_t res = this->phyLayer->transmit(reinterpret_cast(buff), strlen(buff)); delete[] buff; return(res); } diff --git a/lib/RadioLib/src/protocols/APRS/APRS.h b/lib/RadioLib/src/protocols/APRS/APRS.h index 6655533..7b14ad8 100644 --- a/lib/RadioLib/src/protocols/APRS/APRS.h +++ b/lib/RadioLib/src/protocols/APRS/APRS.h @@ -29,17 +29,33 @@ /*! \defgroup mic_e_message_types Mic-E message types. - \{ */ + +/*! \brief Mic-E "Off duty" message. */ #define RADIOLIB_APRS_MIC_E_TYPE_OFF_DUTY 0b00000111 + +/*! \brief Mic-E "En route" message. */ #define RADIOLIB_APRS_MIC_E_TYPE_EN_ROUTE 0b00000110 + +/*! \brief Mic-E "In service" message. */ #define RADIOLIB_APRS_MIC_E_TYPE_IN_SERVICE 0b00000101 + +/*! \brief Mic-E "Returning" message. */ #define RADIOLIB_APRS_MIC_E_TYPE_RETURNING 0b00000100 + +/*! \brief Mic-E "Commited" message. */ #define RADIOLIB_APRS_MIC_E_TYPE_COMMITTED 0b00000011 + +/*! \brief Mic-E special message. */ #define RADIOLIB_APRS_MIC_E_TYPE_SPECIAL 0b00000010 + +/*! \brief Mic-E priority message. */ #define RADIOLIB_APRS_MIC_E_TYPE_PRIORITY 0b00000001 + +/*! \brief Mic-E emergency message. */ #define RADIOLIB_APRS_MIC_E_TYPE_EMERGENCY 0b00000000 + /*! \} */ @@ -97,9 +113,9 @@ class APRSClient { \param destCallsign Destination station callsign. \param destSSID Destination station SSID. \param lat Latitude as a null-terminated string. - \param long Longitude as a null-terminated string. + \param lon Longitude as a null-terminated string. \param msg Message to be transmitted. Defaults to NULL (no message). - \param msg Position timestamp. Defaults to NULL (no timestamp). + \param time Position timestamp. Defaults to NULL (no timestamp). \returns \ref status_codes */ int16_t sendPosition(char* destCallsign, uint8_t destSSID, char* lat, char* lon, char* msg = NULL, char* time = NULL); diff --git a/lib/RadioLib/src/protocols/AX25/AX25.cpp b/lib/RadioLib/src/protocols/AX25/AX25.cpp index 7fe3f49..7e7d475 100644 --- a/lib/RadioLib/src/protocols/AX25/AX25.cpp +++ b/lib/RadioLib/src/protocols/AX25/AX25.cpp @@ -8,7 +8,7 @@ AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* src } AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* srcCallsign, uint8_t srcSSID, uint8_t control, uint8_t protocolID, const char* info) - : AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, protocolID, (uint8_t*)info, strlen(info)) { + : AX25Frame(destCallsign, destSSID, srcCallsign, srcSSID, control, protocolID, reinterpret_cast(const_cast(info)), strlen(info)) { } @@ -50,8 +50,41 @@ AX25Frame::AX25Frame(const char* destCallsign, uint8_t destSSID, const char* src } } -AX25Frame::AX25Frame(const AX25Frame& frame) { - *this = frame; +AX25Frame::AX25Frame(const AX25Frame& frame) + : destSSID(frame.destSSID), + srcSSID(frame.srcSSID), + numRepeaters(frame.numRepeaters), + control(frame.control), + protocolID(frame.protocolID), + infoLen(frame.infoLen), + rcvSeqNumber(frame.rcvSeqNumber), + sendSeqNumber(frame.sendSeqNumber) { + strncpy(this->destCallsign, frame.destCallsign, RADIOLIB_AX25_MAX_CALLSIGN_LEN); + strncpy(this->srcCallsign, frame.srcCallsign, RADIOLIB_AX25_MAX_CALLSIGN_LEN); + + if(frame.infoLen) { + #if !RADIOLIB_STATIC_ONLY + this->info = new uint8_t[frame.infoLen]; + #endif + memcpy(this->info, frame.info, frame.infoLen); + } + + if(frame.numRepeaters) { + #if !RADIOLIB_STATIC_ONLY + this->repeaterCallsigns = new char*[frame.numRepeaters]; + for(uint8_t i = 0; i < frame.numRepeaters; i++) { + this->repeaterCallsigns[i] = new char[strlen(frame.repeaterCallsigns[i]) + 1]; + } + this->repeaterSSIDs = new uint8_t[frame.numRepeaters]; + #endif + + this->numRepeaters = frame.numRepeaters; + for(uint8_t i = 0; i < frame.numRepeaters; i++) { + memcpy(this->repeaterCallsigns[i], frame.repeaterCallsigns[i], strlen(frame.repeaterCallsigns[i])); + this->repeaterCallsigns[i][strlen(frame.repeaterCallsigns[i])] = '\0'; + } + memcpy(this->repeaterSSIDs, frame.repeaterSSIDs, frame.numRepeaters); + } } AX25Frame::~AX25Frame() { @@ -154,17 +187,46 @@ void AX25Frame::setSendSequence(uint8_t seqNumber) { AX25Client::AX25Client(PhysicalLayer* phy) { phyLayer = phy; #if !RADIOLIB_EXCLUDE_AFSK + audio = nullptr; bellModem = nullptr; #endif } #if !RADIOLIB_EXCLUDE_AFSK -AX25Client::AX25Client(AFSKClient* audio) { - phyLayer = audio->phyLayer; - bellModem = new BellClient(audio); +AX25Client::AX25Client(AFSKClient* aud) + : audio(aud) { + phyLayer = this->audio->phyLayer; + bellModem = new BellClient(this->audio); bellModem->setModem(Bell202); } +AX25Client::AX25Client(const AX25Client& ax25) + : phyLayer(ax25.phyLayer), + sourceSSID(ax25.sourceSSID), + preambleLen(ax25.preambleLen) { + strncpy(sourceCallsign, ax25.sourceCallsign, RADIOLIB_AX25_MAX_CALLSIGN_LEN); + #if !RADIOLIB_EXCLUDE_AFSK + if(ax25.bellModem) { + this->audio = ax25.audio; + this->bellModem = new BellClient(ax25.audio); + } + #endif +} + +AX25Client& AX25Client::operator=(const AX25Client& ax25) { + this->phyLayer = ax25.phyLayer; + this->sourceSSID = ax25.sourceSSID; + this->preambleLen = ax25.preambleLen; + strncpy(sourceCallsign, ax25.sourceCallsign, RADIOLIB_AX25_MAX_CALLSIGN_LEN); + #if !RADIOLIB_EXCLUDE_AFSK + if(ax25.bellModem) { + this->audio = ax25.audio; + this->bellModem = new BellClient(ax25.audio); + } + #endif + return(*this); +} + int16_t AX25Client::setCorrection(int16_t mark, int16_t space, float length) { BellModem_t modem; modem.freqMark = Bell202.freqMark + mark; @@ -194,12 +256,7 @@ int16_t AX25Client::begin(const char* srcCallsign, uint8_t srcSSID, uint8_t preL preambleLen = preLen; // configure for direct mode - #if !RADIOLIB_EXCLUDE_AFSK - if(bellModem != nullptr) { - return(phyLayer->startDirect()); - } - #endif - return(RADIOLIB_ERR_NONE); + return(phyLayer->startDirect()); } #if defined(RADIOLIB_BUILD_ARDUINO) @@ -210,10 +267,12 @@ int16_t AX25Client::transmit(String& str, const char* destCallsign, uint8_t dest int16_t AX25Client::transmit(const char* str, const char* destCallsign, uint8_t destSSID) { // create control field - uint8_t controlField = RADIOLIB_AX25_CONTROL_U_UNNUMBERED_INFORMATION | RADIOLIB_AX25_CONTROL_POLL_FINAL_DISABLED | RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME; + uint8_t controlField = RADIOLIB_AX25_CONTROL_UNNUMBERED_FRAME; // build the frame - AX25Frame frame(destCallsign, destSSID, sourceCallsign, sourceSSID, controlField, RADIOLIB_AX25_PID_NO_LAYER_3, (uint8_t*)str, strlen(str)); + AX25Frame frame(destCallsign, destSSID, sourceCallsign, sourceSSID, controlField, + RADIOLIB_AX25_PID_NO_LAYER_3, + reinterpret_cast(const_cast(str)), strlen(str)); // send Unnumbered Information frame return(sendFrame(&frame)); diff --git a/lib/RadioLib/src/protocols/AX25/AX25.h b/lib/RadioLib/src/protocols/AX25/AX25.h index d1688d9..3f25832 100644 --- a/lib/RadioLib/src/protocols/AX25/AX25.h +++ b/lib/RadioLib/src/protocols/AX25/AX25.h @@ -246,9 +246,21 @@ class AX25Client { #if !RADIOLIB_EXCLUDE_AFSK /*! \brief Constructor for AFSK mode. - \param audio Pointer to the AFSK instance providing audio. + \param aud Pointer to the AFSK instance providing audio. */ - explicit AX25Client(AFSKClient* audio); + explicit AX25Client(AFSKClient* aud); + + /*! + \brief Copy constructor. + \param ax25 AX25Client instance to copy. + */ + AX25Client(const AX25Client& ax25); + + /*! + \brief Overload for assignment operator. + \param ax25 rvalue AX25Client. + */ + AX25Client& operator=(const AX25Client& ax25); /*! \brief Set AFSK tone correction offset. On some platforms, this is required to get the audio produced @@ -310,10 +322,11 @@ class AX25Client { PhysicalLayer* phyLayer; #if !RADIOLIB_EXCLUDE_AFSK + AFSKClient* audio; BellClient* bellModem; #endif - char sourceCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1] = {0, 0, 0, 0, 0, 0, 0}; + char sourceCallsign[RADIOLIB_AX25_MAX_CALLSIGN_LEN + 1] = { 0 }; uint8_t sourceSSID = 0; uint16_t preambleLen = 0; diff --git a/lib/RadioLib/src/protocols/BellModem/BellModem.cpp b/lib/RadioLib/src/protocols/BellModem/BellModem.cpp index 8d08943..30da937 100644 --- a/lib/RadioLib/src/protocols/BellModem/BellModem.cpp +++ b/lib/RadioLib/src/protocols/BellModem/BellModem.cpp @@ -1,7 +1,7 @@ #include "BellModem.h" #if !RADIOLIB_EXCLUDE_BELL -const struct BellModem_t Bell101 { +const BellModem_t Bell101 = { .freqMark = 1270, .freqSpace = 1070, .baudRate = 110, @@ -9,7 +9,7 @@ const struct BellModem_t Bell101 { .freqSpaceReply = 2025, }; -const struct BellModem_t Bell103 { +const BellModem_t Bell103 = { .freqMark = 1270, .freqSpace = 1070, .baudRate = 300, @@ -17,7 +17,7 @@ const struct BellModem_t Bell103 { .freqSpaceReply = 2025, }; -const struct BellModem_t Bell202 { +const BellModem_t Bell202 = { .freqMark = 1200, .freqSpace = 2200, .baudRate = 1200, @@ -58,7 +58,7 @@ size_t BellClient::write(uint8_t b) { uint16_t toneSpace = this->modemType.freqSpace; if(this->reply) { toneMark = this->modemType.freqMarkReply; - toneMark = this->modemType.freqSpaceReply; + toneSpace = this->modemType.freqSpaceReply; } // get the Module pointer to access HAL @@ -70,7 +70,7 @@ size_t BellClient::write(uint8_t b) { // iterate over the bits and set correct frequencies for(uint16_t mask = 0x80; mask >= 0x01; mask >>= 1) { - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); if(b & mask) { this->tone(toneMark, false); } else { diff --git a/lib/RadioLib/src/protocols/BellModem/BellModem.h b/lib/RadioLib/src/protocols/BellModem/BellModem.h index b98479e..886e206 100644 --- a/lib/RadioLib/src/protocols/BellModem/BellModem.h +++ b/lib/RadioLib/src/protocols/BellModem/BellModem.h @@ -75,7 +75,7 @@ class BellClient: public AFSKClient, public RadioLibPrint { \brief Audio-client constructor. Can be used when AFSKClient instance already exists. \param aud Audio client to use. */ - BellClient(AFSKClient* aud); + explicit BellClient(AFSKClient* aud); /*! \brief Initialization method. @@ -93,7 +93,7 @@ class BellClient: public AFSKClient, public RadioLibPrint { /*! \brief Set correction coefficient for tone length. - \param correction Timing correction factor, used to adjust the length of tones. + \param corr Timing correction factor, used to adjust the length of tones. Less than 1.0 leads to shorter tones, defaults to 1.0 (no correction). \returns \ref status_codes */ @@ -104,7 +104,7 @@ class BellClient: public AFSKClient, public RadioLibPrint { \param b Byte to write. \returns 1 if the byte was written, 0 otherwise. */ - size_t write(uint8_t b); + size_t write(uint8_t b) override; /*! \brief Set the modem to idle (ready to transmit). @@ -119,7 +119,7 @@ class BellClient: public AFSKClient, public RadioLibPrint { #if !RADIOLIB_GODMODE private: #endif - BellModem_t modemType; + BellModem_t modemType = Bell101; float correction = 1.0; uint16_t toneLen = 0; bool autoStart = true; diff --git a/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.cpp b/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.cpp index 0748b49..d8c3a5f 100644 --- a/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.cpp +++ b/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.cpp @@ -14,6 +14,27 @@ ExternalRadio::ExternalRadio(RadioLibHal *hal, uint32_t pin) : PhysicalLayer(1, this->prevFrf = 0; } +ExternalRadio::ExternalRadio(const ExternalRadio& ext) : PhysicalLayer(1, 0) { + this->prevFrf = ext.prevFrf; + if(ext.mod) { + this->mod = new Module(ext.mod->hal, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, ext.mod->getGpio()); + } +} + +ExternalRadio& ExternalRadio::operator=(const ExternalRadio& ext) { + this->prevFrf = ext.prevFrf; + if(ext.mod) { + this->mod = new Module(ext.mod->hal, RADIOLIB_NC, RADIOLIB_NC, RADIOLIB_NC, ext.mod->getGpio()); + } + return(*this); +} + +ExternalRadio::~ExternalRadio() { + if(this->mod) { + delete this->mod; + } +} + Module* ExternalRadio::getMod() { return(mod); } diff --git a/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.h b/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.h index 25908fd..823674b 100644 --- a/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.h +++ b/lib/RadioLib/src/protocols/ExternalRadio/ExternalRadio.h @@ -9,6 +9,10 @@ #include "../PhysicalLayer/PhysicalLayer.h" +/*! + \class ExternalRadio + \brief Class to interface with external radio hardware. +*/ class ExternalRadio: public PhysicalLayer { public: #if defined(RADIOLIB_BUILD_ARDUINO) @@ -16,7 +20,7 @@ class ExternalRadio: public PhysicalLayer { \brief Default constructor. \param pin Output pin when using direct transmission, defaults to unused pin. */ - ExternalRadio(uint32_t pin = RADIOLIB_NC); + ExternalRadio(uint32_t pin = RADIOLIB_NC); // cppcheck-suppress noExplicitConstructor #endif /*! @@ -24,13 +28,30 @@ class ExternalRadio: public PhysicalLayer { \param hal Pointer to the hardware abstraction layer to use. \param pin Output pin when using direct transmission, defaults to unused pin. */ - ExternalRadio(RadioLibHal *hal, uint32_t pin = RADIOLIB_NC); + ExternalRadio(RadioLibHal *hal, uint32_t pin = RADIOLIB_NC); // cppcheck-suppress noExplicitConstructor + + /*! + \brief Copy constructor. + \param ext ExternalRadio instance to copy. + */ + ExternalRadio(const ExternalRadio& ext); + + /*! + \brief Overload for assignment operator. + \param ext rvalue ExternalRadio. + */ + ExternalRadio& operator=(const ExternalRadio& ext); + + /*! + \brief Default destructor. + */ + ~ExternalRadio(); /*! \brief Method to retrieve pointer to the underlying Module instance. \returns Pointer to the Module instance. */ - Module* getMod(); + Module* getMod() override; /*! \brief Dummy implementation overriding PhysicalLayer. @@ -59,7 +80,7 @@ class ExternalRadio: public PhysicalLayer { the output pin will be set to logic high. Otherwise it will be set to logic low. \returns \ref status_codes */ - int16_t transmitDirect(uint32_t frf = 0); + int16_t transmitDirect(uint32_t frf = 0) override; private: Module* mod; diff --git a/lib/RadioLib/src/protocols/FSK4/FSK4.cpp b/lib/RadioLib/src/protocols/FSK4/FSK4.cpp index f589f45..7e2d737 100644 --- a/lib/RadioLib/src/protocols/FSK4/FSK4.cpp +++ b/lib/RadioLib/src/protocols/FSK4/FSK4.cpp @@ -22,7 +22,7 @@ int16_t FSK4Client::begin(float base, uint32_t shift, uint16_t rate) { shiftFreqHz = shift; // calculate duration of 1 bit - bitDuration = (uint32_t)1000000/rate; + bitDuration = (RadioLibTime_t)1000000/rate; // calculate carrier shift shiftFreq = getRawShift(shift); @@ -81,7 +81,7 @@ size_t FSK4Client::write(uint8_t b) { void FSK4Client::tone(uint8_t i) { Module* mod = phyLayer->getMod(); - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); transmitDirect(baseFreq + tones[i], baseFreqHz + tonesHz[i]); mod->waitForMicroseconds(start, bitDuration); } @@ -112,12 +112,12 @@ int32_t FSK4Client::getRawShift(int32_t shift) { int32_t step = round(phyLayer->getFreqStep()); // check minimum shift value - if(abs(shift) < step / 2) { + if(RADIOLIB_ABS(shift) < step / 2) { return(0); } // round shift to multiples of frequency step size - if(abs(shift) % step < (step / 2)) { + if(RADIOLIB_ABS(shift) % step < (step / 2)) { return(shift / step); } if(shift < 0) { diff --git a/lib/RadioLib/src/protocols/FSK4/FSK4.h b/lib/RadioLib/src/protocols/FSK4/FSK4.h index a1e91e3..31df410 100644 --- a/lib/RadioLib/src/protocols/FSK4/FSK4.h +++ b/lib/RadioLib/src/protocols/FSK4/FSK4.h @@ -84,9 +84,9 @@ class FSK4Client { uint32_t baseFreq = 0, baseFreqHz = 0; uint32_t shiftFreq = 0, shiftFreqHz = 0; - uint32_t bitDuration = 0; - uint32_t tones[4]; - uint32_t tonesHz[4]; + RadioLibTime_t bitDuration = 0; + uint32_t tones[4] = { 0 }; + uint32_t tonesHz[4] = { 0 }; void tone(uint8_t i); diff --git a/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.cpp b/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.cpp index f23bc40..b79e7a4 100644 --- a/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.cpp +++ b/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.cpp @@ -30,13 +30,13 @@ int16_t HellClient::begin(float base, float rate) { return(phyLayer->startDirect()); } -size_t HellClient::printGlyph(uint8_t* buff) { +size_t HellClient::printGlyph(const uint8_t* buff) { // print the character Module* mod = phyLayer->getMod(); bool transmitting = false; for(uint8_t mask = 0x40; mask >= 0x01; mask >>= 1) { for(int8_t i = RADIOLIB_HELL_FONT_HEIGHT - 1; i >= 0; i--) { - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); if((buff[i] & mask) && (!transmitting)) { transmitting = true; transmitDirect(baseFreq, baseFreqHz); diff --git a/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.h b/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.h index 3ee4217..0c26697 100644 --- a/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.h +++ b/lib/RadioLib/src/protocols/Hellschreiber/Hellschreiber.h @@ -117,7 +117,7 @@ class HellClient: public RadioLibPrint { \param buff Buffer of pixels to send, in a 7x7 pixel array. \returns Always returns the number of printed glyphs (1). */ - size_t printGlyph(uint8_t* buff); + size_t printGlyph(const uint8_t* buff); /*! \brief Invert text color. @@ -130,7 +130,7 @@ class HellClient: public RadioLibPrint { \param b Byte to write. \returns 1 if the byte was written, 0 otherwise. */ - size_t write(uint8_t b); + size_t write(uint8_t b) override; #if !RADIOLIB_GODMODE private: diff --git a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.cpp b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.cpp index a29aaf1..0636ddd 100644 --- a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.cpp +++ b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.cpp @@ -45,8 +45,8 @@ void LoRaWANNode::setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA } void LoRaWANNode::wipe() { - memset(this->bufferNonces, 0, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); - memset(this->bufferSession, 0, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); + memset(this->bufferNonces, 0, RADIOLIB_LW_NONCES_BUF_SIZE); + memset(this->bufferSession, 0, RADIOLIB_LW_SESSION_BUF_SIZE); } uint8_t* LoRaWANNode::getBufferNonces() { @@ -59,14 +59,14 @@ int16_t LoRaWANNode::setBufferNonces(uint8_t* persistentBuffer) { return(RADIOLIB_ERR_NONE); } - int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); + int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LW_NONCES_BUF_SIZE); RADIOLIB_ASSERT(state); // copy the whole buffer over - memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LORAWAN_NONCES_BUF_SIZE); + memcpy(this->bufferNonces, persistentBuffer, RADIOLIB_LW_NONCES_BUF_SIZE); // revert to inactive as long as no session is restored - this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)false; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; return(state); } @@ -84,22 +84,22 @@ int16_t LoRaWANNode::setBufferSession(uint8_t* persistentBuffer) { return(RADIOLIB_ERR_NONE); } - int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LORAWAN_SESSION_BUF_SIZE); + int16_t state = LoRaWANNode::checkBufferCommon(persistentBuffer, RADIOLIB_LW_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]); + uint16_t signatureNonces = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE]); + uint16_t signatureInSession = LoRaWANNode::ntoh(&persistentBuffer[RADIOLIB_LW_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); + memcpy(this->bufferSession, persistentBuffer, RADIOLIB_LW_SESSION_BUF_SIZE); // as both the Nonces and session are restored, revert to active session - this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; return(state); } @@ -128,65 +128,65 @@ int16_t LoRaWANNode::checkBufferCommon(uint8_t *buffer, uint16_t size) { 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) { + if(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]) { 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; + bool isSameKeys = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM]) == checkSum; + bool isSameMode = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE]) == lwMode; + bool isSameClass = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS]) == lwClass; + bool isSamePlan = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_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_HEXDUMP(this->bufferNonces, RADIOLIB_LW_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) { + if(lwMode == RADIOLIB_LW_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); + this->devNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE]); + this->joinNonce = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], 3); } - // uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LORAWAN_NONCES_VERSION]; - // if (RADIOLIB_LORAWAN_NONCES_VERSION_VAL > nvm_table_version) { + // uint8_t nvm_table_version = this->bufferNonces[RADIOLIB_LW_NONCES_VERSION]; + // if (RADIOLIB_LW_NONCES_VERSION_VAL > nvm_table_version) { // // set default values for variables that are new or something // } - if(this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] == 0) { + if(this->bufferNonces[RADIOLIB_LW_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); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE); return(RADIOLIB_ERR_NETWORK_NOT_JOINED); } // pull all authentication keys from persistent storage - 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); + this->devAddr = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR]); + memcpy(this->appSKey, &this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->nwkSEncKey, &this->bufferSession[RADIOLIB_LW_SESSION_NWK_SENC_KEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->fNwkSIntKey, &this->bufferSession[RADIOLIB_LW_SESSION_FNWK_SINT_KEY], RADIOLIB_AES128_BLOCK_SIZE); + memcpy(this->sNwkSIntKey, &this->bufferSession[RADIOLIB_LW_SESSION_SNWK_SINT_KEY], RADIOLIB_AES128_BLOCK_SIZE); // restore session parameters - this->rev = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_VERSION]); + this->rev = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_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]); + this->homeNetId = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID]); + this->aFCntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN]); + this->nFCntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN]); + this->confFCntUp = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP]); + this->confFCntDown = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN]); + this->adrFCnt = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT]); + this->fCntUp = LoRaWANNode::ntoh(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP]); int16_t state = RADIOLIB_ERR_UNKNOWN; // for dynamic bands, first restore the defined channels before restoring ADR - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { // restore the defined channels state = this->restoreChannels(); RADIOLIB_ASSERT(state); @@ -194,77 +194,71 @@ int16_t LoRaWANNode::restore(uint16_t checkSum, uint16_t lwMode, uint8_t lwClass // restore the complete MAC state LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, + .cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn, + .len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn, .repeat = 0, }; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP], cmd.len); + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_SESSION_TX_PARAM_SETUP], cmd.len); (void)execMacCommand(&cmd); - 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); + cmd.cid = RADIOLIB_LW_MAC_LINK_ADR; + cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_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) { + if(this->band->bandType == RADIOLIB_LW_BAND_FIXED) { state = this->restoreChannels(); RADIOLIB_ASSERT(state); } - cmd.cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DUTY_CYCLE].lenDn; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE], cmd.len); + cmd.cid = RADIOLIB_LW_MAC_DUTY_CYCLE; + cmd.len = MacTable[RADIOLIB_LW_MAC_DUTY_CYCLE].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_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; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP], cmd.len); + cmd.cid = RADIOLIB_LW_MAC_RX_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_RX_PARAM_SETUP].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_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; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP], cmd.len); + cmd.cid = RADIOLIB_LW_MAC_RX_TIMING_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_RX_TIMING_SETUP].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_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; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP], cmd.len); + cmd.cid = RADIOLIB_LW_MAC_ADR_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_ADR_PARAM_SETUP].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_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; - memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP], cmd.len); + cmd.cid = RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP].lenDn; + memcpy(cmd.payload, &this->bufferSession[RADIOLIB_LW_SESSION_REJOIN_PARAM_SETUP], cmd.len); (void)execMacCommand(&cmd); // 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 = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_MODE]); + memcpy(&this->commandsUp, &this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], sizeof(LoRaWANMacCommandQueue_t)); return(state); } int16_t LoRaWANNode::restoreChannels() { // first do the default channels, in case these are not covered by restored channels - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { this->setupChannelsDyn(false); - } else { // RADIOLIB_LORAWAN_BAND_FIXED + } else { // RADIOLIB_LW_BAND_FIXED this->setupChannelsFix(this->subBand); } uint8_t bufferZeroes[5] = { 0 }; - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { - uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS]; + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { + uint8_t *startChannelsUp = &this->bufferSession[RADIOLIB_LW_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; + LoRaWANMacCommand_t cmd = { .cid = RADIOLIB_LW_MAC_NEW_CHANNEL, .payload = { 0 }, .len = 0, .repeat = 0 }; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn; 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; @@ -272,22 +266,22 @@ int16_t LoRaWANNode::restoreChannels() { } } - uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_DL_CHANNELS]; + uint8_t *startChannelsDown = &this->bufferSession[RADIOLIB_LW_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; + cmd.cid = RADIOLIB_LW_MAC_DL_CHANNEL; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + cmd.len = MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn; 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); } } - } else { // RADIOLIB_LORAWAN_BAND_FIXED - uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS]; + } else { // RADIOLIB_LW_BAND_FIXED + uint8_t *startMACpayload = &this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS]; LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LW_MAC_LINK_ADR, .payload = { 0 }, .len = 0, .repeat = 0, @@ -295,7 +289,7 @@ int16_t LoRaWANNode::restoreChannels() { // there are at most 8 channel masks present for(int i = 0; i < 8; i++) { - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; + cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn; 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) { @@ -315,46 +309,46 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { memset(&(this->commandsDown), 0, sizeof(LoRaWANMacCommandQueue_t)); uint8_t drUp = 0; - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { // if join datarate is user-specified and valid, select that value - if(initialDr != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + if(initialDr != RADIOLIB_LW_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; + initialDr = RADIOLIB_LW_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) { + if(initialDr == RADIOLIB_LW_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) { + if(initialDr != RADIOLIB_LW_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) { + for(; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled) { + if(initialDr >= this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMin + && initialDr <= this->availableChannels[RADIOLIB_LW_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) { + if(i == RADIOLIB_LW_NUM_AVAILABLE_CHANNELS) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Datarate %d is not valid - using default", initialDr); - initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED; + initialDr = RADIOLIB_LW_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) { + if(initialDr == RADIOLIB_LW_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 @@ -368,9 +362,9 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { } LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LW_MAC_LINK_ADR, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, + .len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn, .repeat = 0, }; cmd.payload[0] = (drUp << 4); // set uplink datarate @@ -378,8 +372,8 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { cmd.payload[3] = (1 << 7); // set the RFU bit, which means that the channel mask gets ignored (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_DUTY_CYCLE; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_DUTY_CYCLE].lenDn; + cmd.cid = RADIOLIB_LW_MAC_DUTY_CYCLE; + cmd.len = MacTable[RADIOLIB_LW_MAC_DUTY_CYCLE].lenDn; uint8_t maxDCyclePower; switch(this->band->dutyCycle) { case(0): @@ -398,21 +392,21 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { cmd.payload[0] = maxDCyclePower; (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn; - cmd.payload[0] = (RADIOLIB_LORAWAN_RX1_DR_OFFSET << 4); + cmd.cid = RADIOLIB_LW_MAC_RX_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_RX_PARAM_SETUP].lenDn; + cmd.payload[0] = (RADIOLIB_LW_RX1_DR_OFFSET << 4); cmd.payload[0] |= this->rx2.drMax; // may be set by user, otherwise band's default upon initialization uint32_t rx2Freq = uint32_t(this->rx2.freq * 10000); LoRaWANNode::hton(&cmd.payload[1], rx2Freq, 3); (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP].lenDn; - cmd.payload[0] = (RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS / 1000); + cmd.cid = RADIOLIB_LW_MAC_RX_TIMING_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_RX_TIMING_SETUP].lenDn; + cmd.payload[0] = (RADIOLIB_LW_RECEIVE_DELAY_1_MS / 1000); (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP].lenDn; + cmd.cid = RADIOLIB_LW_MAC_TX_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn; cmd.payload[0] = (this->band->dwellTimeDn > 0 ? 1 : 0) << 5; cmd.payload[0] |= (this->band->dwellTimeUp > 0 ? 1 : 0) << 4; uint8_t maxEIRPRaw; @@ -439,16 +433,16 @@ void LoRaWANNode::beginCommon(uint8_t initialDr) { cmd.payload[0] |= maxEIRPRaw; (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP].lenDn; - cmd.payload[0] = (RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP << 4); - cmd.payload[0] |= RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP; + cmd.cid = RADIOLIB_LW_MAC_ADR_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_ADR_PARAM_SETUP].lenDn; + cmd.payload[0] = (RADIOLIB_LW_ADR_ACK_LIMIT_EXP << 4); + cmd.payload[0] |= RADIOLIB_LW_ADR_ACK_DELAY_EXP; (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP].lenDn; - cmd.payload[0] = (RADIOLIB_LORAWAN_REJOIN_MAX_TIME_N << 4); - cmd.payload[0] |= RADIOLIB_LORAWAN_REJOIN_MAX_COUNT_N; + cmd.cid = RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP].lenDn; + cmd.payload[0] = (RADIOLIB_LW_REJOIN_MAX_TIME_N << 4); + cmd.payload[0] |= RADIOLIB_LW_REJOIN_MAX_COUNT_N; (void)execMacCommand(&cmd); } @@ -471,10 +465,10 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // 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; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; } - state = this->restore(checkSum, RADIOLIB_LORAWAN_MODE_OTAA, RADIOLIB_LORAWAN_CLASS_A, this->band->bandNum); + state = this->restore(checkSum, RADIOLIB_LW_MODE_OTAA, RADIOLIB_LW_CLASS_A, this->band->bandNum); if(!force) { return(state); @@ -483,7 +477,7 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe Module* mod = this->phyLayer->getMod(); // setup join-request uplink/downlink frequencies and datarates - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { state = this->setupChannelsDyn(true); } else { state = this->setupChannelsFix(this->subBand); @@ -492,22 +486,18 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe // 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; + if(this->band->bandType == RADIOLIB_LW_BAND_FIXED) { + joinDr = RADIOLIB_LW_DATA_RATE_UNUSED; } // setup all MAC properties to default values this->beginCommon(joinDr); - // set the physical layer configuration - state = this->setPhyProperties(); - RADIOLIB_ASSERT(state); - // select a random pair of Tx/Rx channels state = this->selectChannels(); RADIOLIB_ASSERT(state); - // configure for uplink with default configuration - state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK); + // set the physical layer configuration for uplink + state = this->setPhyProperties(RADIOLIB_LW_CHANNEL_DIR_UPLINK); RADIOLIB_ASSERT(state); // copy devNonce currently in use @@ -515,42 +505,42 @@ 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; - LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LORAWAN_NONCES_DEV_NONCE], this->devNonce); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_DEV_NONCE], this->devNonce); // build the join-request message - uint8_t joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN]; + uint8_t joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN]; // set the packet fields - joinRequestMsg[0] = RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_REQUEST | RADIOLIB_LORAWAN_MHDR_MAJOR_R1; - LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_JOIN_EUI_POS], joinEUI); - LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_EUI_POS], devEUI); - LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_NONCE_POS], devNonceUsed); + joinRequestMsg[0] = RADIOLIB_LW_MHDR_MTYPE_JOIN_REQUEST | RADIOLIB_LW_MHDR_MAJOR_R1; + LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_JOIN_EUI_POS], joinEUI); + LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_DEV_EUI_POS], devEUI); + LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_DEV_NONCE_POS], devNonceUsed); // add the authentication code - uint32_t mic = this->generateMIC(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN - sizeof(uint32_t), nwkKey); - LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LORAWAN_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic); + uint32_t mic = this->generateMIC(joinRequestMsg, RADIOLIB_LW_JOIN_REQUEST_LEN - sizeof(uint32_t), nwkKey); + LoRaWANNode::hton(&joinRequestMsg[RADIOLIB_LW_JOIN_REQUEST_LEN - sizeof(uint32_t)], mic); // send it - state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LORAWAN_JOIN_REQUEST_LEN); + state = this->phyLayer->transmit(joinRequestMsg, RADIOLIB_LW_JOIN_REQUEST_LEN); this->rxDelayStart = mod->hal->millis(); 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 - this->rxDelays[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_1_MS; - this->rxDelays[1] = RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_2_MS; + this->rxDelays[0] = RADIOLIB_LW_JOIN_ACCEPT_DELAY_1_MS; + this->rxDelays[1] = RADIOLIB_LW_JOIN_ACCEPT_DELAY_2_MS; // handle Rx1 and Rx2 windows - returns RADIOLIB_ERR_NONE if a downlink is received state = downlinkCommon(); RADIOLIB_ASSERT(state); // build the buffer for the reply data - uint8_t joinAcceptMsgEnc[RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN]; + uint8_t joinAcceptMsgEnc[RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN]; // 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_PROTOCOL_PRINTLN("joinAccept reply length mismatch, expected %luB got %luB", RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN, lenRx); + if((lenRx != RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN) && (lenRx != RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN - RADIOLIB_LW_JOIN_ACCEPT_CFLIST_LEN)) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("joinAccept reply length mismatch, expected %dB got %luB", RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN, lenRx); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } @@ -563,52 +553,52 @@ 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_PROTOCOL_PRINTLN("joinAccept reply message type invalid, expected 0x%02x got 0x%02x", RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT, joinAcceptMsgEnc[0]); + if((joinAcceptMsgEnc[0] & RADIOLIB_LW_MHDR_MTYPE_MASK) != RADIOLIB_LW_MHDR_MTYPE_JOIN_ACCEPT) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("joinAccept reply message type invalid, expected 0x%02x got 0x%02x", RADIOLIB_LW_MHDR_MTYPE_JOIN_ACCEPT, joinAcceptMsgEnc[0]); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } // decrypt the join accept message // this is done by encrypting again in ECB mode // the first byte is the MAC header which is not encrypted - uint8_t joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN]; + uint8_t joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN]; joinAcceptMsg[0] = joinAcceptMsgEnc[0]; RadioLibAES128Instance.init(nwkKey); - RadioLibAES128Instance.encryptECB(&joinAcceptMsgEnc[1], RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN - 1, &joinAcceptMsg[1]); + RadioLibAES128Instance.encryptECB(&joinAcceptMsgEnc[1], RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN - 1, &joinAcceptMsg[1]); 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); + uint32_t joinNonceNew = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_JOIN_NONCE_POS], 3); RADIOLIB_DEBUG_PROTOCOL_PRINTLN("JoinNoncePrev: %d, JoinNonce: %d", this->joinNonce, joinNonceNew); - // JoinNonce received must be greater than the last JoinNonce heard, else error + // JoinNonce received must be greater than the last JoinNonce heard, else error if((this->joinNonce > 0) && (joinNonceNew <= this->joinNonce)) { return(RADIOLIB_ERR_JOIN_NONCE_INVALID); } this->joinNonce = joinNonceNew; - this->homeNetId = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], 3); - this->devAddr = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS]); + this->homeNetId = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_HOME_NET_ID_POS], 3); + this->devAddr = LoRaWANNode::ntoh(&joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_DEV_ADDR_POS]); // 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; + uint8_t dlSettings = joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_DL_SETTINGS_POS]; + this->rev = (dlSettings & RADIOLIB_LW_JOIN_ACCEPT_R_1_1) >> 7; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LoRaWAN revision: 1.%d", this->rev); // verify MIC if(this->rev == 1) { // 1.1 version, first we need to derive the join accept integrity key uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY; + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_JS_INT_KEY; LoRaWANNode::hton(&keyDerivationBuff[1], devEUI); RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->jSIntKey); // prepare the buffer for MIC calculation uint8_t micBuff[3*RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - micBuff[0] = RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE; + micBuff[0] = RADIOLIB_LW_JOIN_REQUEST_TYPE; LoRaWANNode::hton(&micBuff[1], joinEUI); LoRaWANNode::hton(&micBuff[9], devNonceUsed); memcpy(&micBuff[11], joinAcceptMsg, lenRx); @@ -626,9 +616,9 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, + .cid = RADIOLIB_LW_MAC_RX_PARAM_SETUP, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP].lenDn, + .len = MacTable[RADIOLIB_LW_MAC_RX_PARAM_SETUP].lenDn, .repeat = 0, }; cmd.payload[0] = dlSettings & 0x7F; @@ -636,68 +626,68 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe LoRaWANNode::hton(&cmd.payload[1], rx2Freq, 3); (void)execMacCommand(&cmd); - cmd.cid = RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP; - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP].lenDn; - cmd.payload[0] = joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS]; + cmd.cid = RADIOLIB_LW_MAC_RX_TIMING_SETUP; + cmd.len = MacTable[RADIOLIB_LW_MAC_RX_TIMING_SETUP].lenDn; + cmd.payload[0] = joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_RX_DELAY_POS]; (void)execMacCommand(&cmd); // in case of dynamic band, setup the default channels first - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { this->setupChannelsDyn(false); } // process CFlist if present - if(lenRx == RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN) { - uint8_t cfList[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN] = { 0 }; - memcpy(&cfList[0], &joinAcceptMsg[RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS], RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN); + if(lenRx == RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN) { + uint8_t cfList[RADIOLIB_LW_JOIN_ACCEPT_CFLIST_LEN] = { 0 }; + memcpy(&cfList[0], &joinAcceptMsg[RADIOLIB_LW_JOIN_ACCEPT_CFLIST_POS], RADIOLIB_LW_JOIN_ACCEPT_CFLIST_LEN); this->processCFList(cfList); } // if no CFList was received, default or subband are already setup so don't need to do anything else // prepare buffer for key derivation uint8_t keyDerivationBuff[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3); + LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LW_JOIN_ACCEPT_JOIN_NONCE_POS], joinNonce, 3); // check protocol version (1.0 vs 1.1) if(this->rev == 1) { // 1.1 version, derive the keys - LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI); - LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS], devNonceUsed); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY; + LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LW_JOIN_ACCEPT_JOIN_EUI_POS], joinEUI); + LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LW_JOIN_ACCEPT_DEV_NONCE_POS], devNonceUsed); + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_APP_S_KEY; RadioLibAES128Instance.init(appKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->appSKey); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_F_NWK_S_INT_KEY; + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_F_NWK_S_INT_KEY; RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->fNwkSIntKey); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_S_NWK_S_INT_KEY; + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_S_NWK_S_INT_KEY; RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->sNwkSIntKey); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_NWK_S_ENC_KEY; + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_NWK_S_ENC_KEY; RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->nwkSEncKey); // enqueue the RekeyInd MAC command to be sent in the next uplink LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_REKEY, + .cid = RADIOLIB_LW_MAC_REKEY, .payload = { this->rev }, .len = sizeof(uint8_t), - .repeat = 0x01 << RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP, + .repeat = 0x01 << RADIOLIB_LW_ADR_ACK_LIMIT_EXP, }; state = pushMacCommand(&cmd, &this->commandsUp); RADIOLIB_ASSERT(state); } else { // 1.0 version, just derive the keys - LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS], this->homeNetId, 3); - LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS], devNonceUsed); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY; + LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LW_JOIN_ACCEPT_HOME_NET_ID_POS], this->homeNetId, 3); + LoRaWANNode::hton(&keyDerivationBuff[RADIOLIB_LW_JOIN_ACCEPT_DEV_ADDR_POS], devNonceUsed); + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_APP_S_KEY; RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->appSKey); - keyDerivationBuff[0] = RADIOLIB_LORAWAN_JOIN_ACCEPT_F_NWK_S_INT_KEY; + keyDerivationBuff[0] = RADIOLIB_LW_JOIN_ACCEPT_F_NWK_S_INT_KEY; RadioLibAES128Instance.init(nwkKey); RadioLibAES128Instance.encryptECB(keyDerivationBuff, RADIOLIB_AES128_BLOCK_SIZE, this->fNwkSIntKey); @@ -707,32 +697,31 @@ int16_t LoRaWANNode::beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKe } // reset all frame counters - this->fcntUp = 0; - this->aFcntDown = 0; - this->nFcntDown = 0; - this->confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE; - this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; - this->adrFcnt = 0; + this->fCntUp = 0; + this->aFCntDown = 0; + this->nFCntDown = 0; + this->confFCntUp = RADIOLIB_LW_FCNT_NONE; + this->confFCntDown = RADIOLIB_LW_FCNT_NONE; + this->adrFCnt = 0; // save the activation keys checksum, device address & keys as well as JoinAccept values; these are only ever set when joining - 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); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_VERSION], RADIOLIB_LW_NONCES_VERSION_VAL); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE], RADIOLIB_LW_MODE_OTAA); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS], RADIOLIB_LW_CLASS_A); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN], this->band->bandNum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM], checkSum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_JOIN_NONCE], this->joinNonce, 3); - this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; - this->activeMode = RADIOLIB_LORAWAN_MODE_OTAA; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; // 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); + uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_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, uint8_t initialDr) { +int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force, uint8_t initialDr) { // if not forced and already joined, don't do anything if(!force && this->isJoined()) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("beginABP(): Did not rejoin: session already active"); @@ -744,7 +733,7 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, // check if we actually need to restart from a clean session uint16_t checkSum = 0; checkSum ^= LoRaWANNode::checkSum16(reinterpret_cast(&addr), 4); - checkSum ^= LoRaWANNode::checkSum16(nwkSKey, 16); + checkSum ^= LoRaWANNode::checkSum16(nwkSEncKey, 16); checkSum ^= LoRaWANNode::checkSum16(appSKey, 16); if(fNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(fNwkSIntKey, 16); } if(sNwkSIntKey) { checkSum ^= LoRaWANNode::checkSum16(sNwkSIntKey, 16); } @@ -752,10 +741,10 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, // 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; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)false; } - state = this->restore(checkSum, RADIOLIB_LORAWAN_MODE_ABP, RADIOLIB_LORAWAN_CLASS_A, this->band->bandNum); + state = this->restore(checkSum, RADIOLIB_LW_MODE_ABP, RADIOLIB_LW_CLASS_A, this->band->bandNum); if(!force) { return(state); @@ -763,19 +752,19 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, this->devAddr = addr; memcpy(this->appSKey, appSKey, RADIOLIB_AES128_KEY_SIZE); - memcpy(this->nwkSEncKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE); + memcpy(this->nwkSEncKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE); if(fNwkSIntKey) { this->rev = 1; memcpy(this->fNwkSIntKey, fNwkSIntKey, RADIOLIB_AES128_KEY_SIZE); } else { - memcpy(this->fNwkSIntKey, nwkSKey, RADIOLIB_AES128_KEY_SIZE); + memcpy(this->fNwkSIntKey, nwkSEncKey, RADIOLIB_AES128_KEY_SIZE); } if(sNwkSIntKey) { memcpy(this->sNwkSIntKey, sNwkSIntKey, RADIOLIB_AES128_KEY_SIZE); } // setup the uplink/downlink channels and initial datarate - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { this->setupChannelsDyn(); } else { this->setupChannelsFix(this->subBand); @@ -784,84 +773,79 @@ int16_t LoRaWANNode::beginABP(uint32_t addr, uint8_t* nwkSKey, uint8_t* appSKey, // setup all MAC properties to default values this->beginCommon(initialDr); - // set the physical layer configuration - state = this->setPhyProperties(); - RADIOLIB_ASSERT(state); - // reset all frame counters - this->fcntUp = 0; - this->aFcntDown = 0; - this->nFcntDown = 0; - this->confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE; - this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; - this->adrFcnt = 0; + this->fCntUp = 0; + this->aFCntDown = 0; + this->nFCntDown = 0; + this->confFCntUp = RADIOLIB_LW_FCNT_NONE; + this->confFCntDown = RADIOLIB_LW_FCNT_NONE; + this->adrFCnt = 0; // 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); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_VERSION], RADIOLIB_LW_NONCES_VERSION_VAL); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_MODE], RADIOLIB_LW_MODE_ABP); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_CLASS], RADIOLIB_LW_CLASS_A); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_PLAN], this->band->bandNum); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_CHECKSUM], checkSum); - this->bufferNonces[RADIOLIB_LORAWAN_NONCES_ACTIVE] = (uint8_t)true; - this->activeMode = RADIOLIB_LORAWAN_MODE_ABP; + this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE] = (uint8_t)true; // 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); + uint16_t signature = LoRaWANNode::checkSum16(this->bufferNonces, RADIOLIB_LW_NONCES_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE], signature); return(RADIOLIB_ERR_NONE); } bool LoRaWANNode::isJoined() { - return(this->activeMode != RADIOLIB_LORAWAN_MODE_NONE); + return(this->bufferNonces[RADIOLIB_LW_NONCES_ACTIVE]); } int16_t LoRaWANNode::saveSession() { // store DevAddr and all keys - 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); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_DEV_ADDR], this->devAddr); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_APP_SKEY], this->appSKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_NWK_SENC_KEY], this->nwkSEncKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_FNWK_SINT_KEY], this->fNwkSIntKey, RADIOLIB_AES128_BLOCK_SIZE); + memcpy(&this->bufferSession[RADIOLIB_LW_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); + uint16_t noncesSignature = LoRaWANNode::ntoh(&this->bufferNonces[RADIOLIB_LW_NONCES_SIGNATURE]); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_NONCES_SIGNATURE], noncesSignature); // store network parameters - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_HOMENET_ID], this->homeNetId); - LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_VERSION], this->rev); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_HOMENET_ID], this->homeNetId); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_VERSION], this->rev); // store all frame counters - 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); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_A_FCNT_DOWN], this->aFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_N_FCNT_DOWN], this->nFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_UP], this->confFCntUp); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_CONF_FCNT_DOWN], this->confFCntDown); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_FCNT], this->adrFCnt); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_FCNT_UP], this->fCntUp); // save the current uplink MAC command queue - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_MAC_QUEUE_UL], &this->commandsUp, sizeof(LoRaWANMacCommandQueue_t)); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_MAC_QUEUE_UL], &this->commandsUp, 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); + uint16_t signature = LoRaWANNode::checkSum16(this->bufferSession, RADIOLIB_LW_SESSION_BUF_SIZE - 2); + LoRaWANNode::hton(&this->bufferSession[RADIOLIB_LW_SESSION_SIGNATURE], signature); return(RADIOLIB_ERR_NONE); } #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)); +int16_t LoRaWANNode::uplink(String& str, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) { + return(this->uplink(str.c_str(), fPort, isConfirmed, event)); } #endif -int16_t LoRaWANNode::uplink(const char* str, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) { - return(this->uplink((uint8_t*)str, strlen(str), port, isConfirmed, event)); +int16_t LoRaWANNode::uplink(const char* str, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) { + return(this->uplink((uint8_t*)str, strlen(str), fPort, isConfirmed, event)); } -int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed, LoRaWANEvent_t* event) { +int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* event) { // if not joined, don't do anything if(!this->isJoined()) { return(RADIOLIB_ERR_NETWORK_NOT_JOINED); @@ -877,16 +861,16 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf } // if adhering to dutyCycle and the time since last uplink + interval has not elapsed, return an error - if(this->dutyCycleEnabled && this->rxDelayStart + dutyCycleInterval(this->dutyCycle, this->lastToA) > mod->hal->millis()) { + if(this->dutyCycleEnabled && this->rxDelayStart + (RadioLibTime_t)dutyCycleInterval(this->dutyCycle, this->lastToA) > mod->hal->millis()) { return(RADIOLIB_ERR_UPLINK_UNAVAILABLE); } - // check destination port - if(port > 0xDF) { + // check destination fPort + if(fPort > 0xDF) { return(RADIOLIB_ERR_INVALID_PORT); } - // port 0 is only allowed for MAC-only payloads - if(port == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { + // fPort 0 is only allowed for MAC-only payloads + if(fPort == RADIOLIB_LW_FPORT_MAC_COMMAND) { if (!this->isMACPayload) { return(RADIOLIB_ERR_INVALID_PORT); } @@ -897,19 +881,17 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf 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; - size_t foptsBufSize = 0; - if(this->commandsUp.numCommands > 0 && port != RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { + uint8_t fOptsLen = 0; + if(this->commandsUp.numCommands > 0 && fPort != RADIOLIB_LW_FPORT_MAC_COMMAND) { // there are, assume the maximum possible FOpts len for buffer allocation - foptsLen = this->commandsUp.len; - foptsBufSize = 15; + fOptsLen = this->commandsUp.len; } // check maximum payload len as defined in phy - if(len > this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]]) { + if(len > this->band->payloadLenMax[this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK]]) { return(RADIOLIB_ERR_PACKET_TOO_LONG); // if testing with TS009 specification verification protocol, don't throw error but clip the message - // len = this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]]; + // len = this->band->payloadLenMax[this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK]]; } bool adrAckReq = false; @@ -917,22 +899,22 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // check if we need to do ADR stuff uint32_t adrLimit = 0x01 << this->adrLimitExp; uint32_t adrDelay = 0x01 << this->adrDelayExp; - if((this->fcntUp - this->adrFcnt) >= adrLimit) { + if((this->fCntUp - this->adrFCnt) >= adrLimit) { adrAckReq = true; } // if we hit the Limit + Delay, try one of three, in order: // set TxPower to max, set DR to min, enable all default channels - if ((this->fcntUp - this->adrFcnt) == (adrLimit + adrDelay)) { + if ((this->fCntUp - this->adrFCnt) == (adrLimit + adrDelay)) { uint8_t adrStage = 1; while(adrStage != 0) { switch(adrStage) { case(1): { // if the TxPower field has some offset, remove it and switch to maximum power - if(this->txPowerCur > 0) { + if(this->txPowerSteps > 0) { // set the maximum power supported by both the module and the band state = this->setTxPower(this->txPowerMax); if(state == RADIOLIB_ERR_NONE) { - this->txPowerCur = 0; + this->txPowerSteps = 0; adrStage = 0; // successfully did some ADR stuff } } @@ -943,8 +925,8 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf break; 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) == RADIOLIB_ERR_NONE) { + if(this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] > 0) { + if(this->setDatarate(this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] - 1) == RADIOLIB_ERR_NONE) { adrStage = 0; // successfully did some ADR stuff } } @@ -954,7 +936,7 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf } break; case(3): { - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { this->setupChannelsDyn(false); // revert to default frequencies } else { // go back to default selected subband @@ -968,23 +950,23 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf } // we tried something to improve the range, so increase the ADR frame counter by 'ADR delay' - this->adrFcnt += adrDelay; + this->adrFCnt += adrDelay; } } - // configure for uplink + // set the physical layer configuration for uplink this->selectChannels(); - state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK); + state = this->setPhyProperties(RADIOLIB_LW_CHANNEL_DIR_UPLINK); RADIOLIB_ASSERT(state); // if dwell time is imposed, calculated expected time on air and cancel if exceeds - if(this->dwellTimeEnabledUp && this->phyLayer->getTimeOnAir(RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen) - 16)/1000 > this->dwellTimeUp) { + if(this->dwellTimeEnabledUp && this->phyLayer->getTimeOnAir(RADIOLIB_LW_FRAME_LEN(len, fOptsLen) - 16)/1000 > this->dwellTimeUp) { return(RADIOLIB_ERR_DWELL_TIME_EXCEEDED); } // build the uplink message // the first 16 bytes are reserved for MIC calculation blocks - size_t uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsBufSize); + size_t uplinkMsgLen = RADIOLIB_LW_FRAME_LEN(len, fOptsLen); #if RADIOLIB_STATIC_ONLY uint8_t uplinkMsg[RADIOLIB_STATIC_ARRAY_SIZE]; #else @@ -993,47 +975,47 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf // set the packet fields if(isConfirmed) { - uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_UP; - this->confFcntUp = this->fcntUp; + uplinkMsg[RADIOLIB_LW_FHDR_LEN_START_OFFS] = RADIOLIB_LW_MHDR_MTYPE_CONF_DATA_UP; + this->confFCntUp = this->fCntUp; } else { - uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] = RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_UP; + uplinkMsg[RADIOLIB_LW_FHDR_LEN_START_OFFS] = RADIOLIB_LW_MHDR_MTYPE_UNCONF_DATA_UP; } - uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] |= RADIOLIB_LORAWAN_MHDR_MAJOR_R1; - LoRaWANNode::hton(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS], this->devAddr); + uplinkMsg[RADIOLIB_LW_FHDR_LEN_START_OFFS] |= RADIOLIB_LW_MHDR_MAJOR_R1; + LoRaWANNode::hton(&uplinkMsg[RADIOLIB_LW_FHDR_DEV_ADDR_POS], this->devAddr); - // length of fopts will be added later - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] = 0x00; + // length of fOpts will be added later + uplinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] = 0x00; if(this->adrEnabled) { - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ENABLED; + uplinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] |= RADIOLIB_LW_FCTRL_ADR_ENABLED; if(adrAckReq) { - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ADR_ACK_REQ; + uplinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] |= RADIOLIB_LW_FCTRL_ADR_ACK_REQ; } } - // if the saved confirm-fcnt is set, set the ACK bit + // if the saved confirm-fCnt is set, set the ACK bit bool isConfirmingDown = false; - if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) { + if(this->confFCntDown != RADIOLIB_LW_FCNT_NONE) { isConfirmingDown = true; - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= RADIOLIB_LORAWAN_FCTRL_ACK; + uplinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] |= RADIOLIB_LW_FCTRL_ACK; } - LoRaWANNode::hton(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS], (uint16_t)this->fcntUp); + LoRaWANNode::hton(&uplinkMsg[RADIOLIB_LW_FHDR_FCNT_POS], (uint16_t)this->fCntUp); // check if we have some MAC commands to append - if(foptsLen > 0) { + if(fOptsLen > 0) { // assume maximum possible buffer size - uint8_t foptsBuff[15]; - uint8_t* foptsPtr = foptsBuff; + uint8_t fOptsBuff[RADIOLIB_LW_FHDR_FOPTS_MAX_LEN]; + uint8_t* fOptsPtr = fOptsBuff; - // append all MAC replies into fopts buffer + // append all MAC replies into fOpts buffer int16_t i = 0; for (; i < this->commandsUp.numCommands; i++) { LoRaWANMacCommand_t cmd = this->commandsUp.commands[i]; - memcpy(foptsPtr, &cmd, 1 + cmd.len); - foptsPtr += cmd.len + 1; + memcpy(fOptsPtr, &cmd, 1 + cmd.len); + fOptsPtr += cmd.len + 1; } RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); - RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(foptsBuff, foptsLen); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(fOptsBuff, fOptsLen); // pop the commands from back to front for (; i >= 0; i--) { @@ -1044,43 +1026,43 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf } } - uplinkMsgLen = RADIOLIB_LORAWAN_FRAME_LEN(len, foptsLen); - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] |= foptsLen; + uplinkMsgLen = RADIOLIB_LW_FRAME_LEN(len, fOptsLen); + uplinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] |= fOptsLen; // encrypt it - processAES(foptsBuff, foptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], this->fcntUp, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x01, true); + processAES(fOptsBuff, fOptsLen, this->nwkSEncKey, &uplinkMsg[RADIOLIB_LW_FHDR_FOPTS_POS], this->fCntUp, RADIOLIB_LW_CHANNEL_DIR_UPLINK, 0x01, true); } - // set the port - uplinkMsg[RADIOLIB_LORAWAN_FHDR_FPORT_POS(foptsLen)] = port; + // set the fPort + uplinkMsg[RADIOLIB_LW_FHDR_FPORT_POS(fOptsLen)] = fPort; - // select encryption key based on the target port + // select encryption key based on the target fPort uint8_t* encKey = this->appSKey; - if(port == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { + if(fPort == RADIOLIB_LW_FPORT_MAC_COMMAND) { encKey = this->nwkSEncKey; } // encrypt the frame payload - processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], this->fcntUp, RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK, 0x00, true); + processAES(data, len, encKey, &uplinkMsg[RADIOLIB_LW_FRAME_PAYLOAD_POS(fOptsLen)], this->fCntUp, RADIOLIB_LW_CHANNEL_DIR_UPLINK, 0x00, true); // create blocks for MIC calculation uint8_t block0[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - block0[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_MIC_BLOCK_MAGIC; - block0[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK; - LoRaWANNode::hton(&block0[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr); - LoRaWANNode::hton(&block0[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], this->fcntUp); - block0[RADIOLIB_LORAWAN_MIC_BLOCK_LEN_POS] = uplinkMsgLen - RADIOLIB_AES128_BLOCK_SIZE - sizeof(uint32_t); + block0[RADIOLIB_LW_BLOCK_MAGIC_POS] = RADIOLIB_LW_MIC_BLOCK_MAGIC; + block0[RADIOLIB_LW_BLOCK_DIR_POS] = RADIOLIB_LW_CHANNEL_DIR_UPLINK; + LoRaWANNode::hton(&block0[RADIOLIB_LW_BLOCK_DEV_ADDR_POS], this->devAddr); + LoRaWANNode::hton(&block0[RADIOLIB_LW_BLOCK_FCNT_POS], this->fCntUp); + block0[RADIOLIB_LW_MIC_BLOCK_LEN_POS] = uplinkMsgLen - RADIOLIB_AES128_BLOCK_SIZE - sizeof(uint32_t); uint8_t block1[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; memcpy(block1, block0, RADIOLIB_AES128_BLOCK_SIZE); - if(this->confFcntDown != RADIOLIB_LORAWAN_FCNT_NONE) { - LoRaWANNode::hton(&block1[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntDown); + if(this->confFCntDown != RADIOLIB_LW_FCNT_NONE) { + LoRaWANNode::hton(&block1[RADIOLIB_LW_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFCntDown); } - 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; + block1[RADIOLIB_LW_MIC_DATA_RATE_POS] = this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK]; + block1[RADIOLIB_LW_MIC_CH_INDEX_POS] = this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK].idx; - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink (FcntUp = %d) decoded:", this->fcntUp); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink (FCntUp = %d) decoded:", this->fCntUp); RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(uplinkMsg, uplinkMsgLen); @@ -1104,14 +1086,14 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf } // send it (without the MIC calculation blocks) - state = this->phyLayer->transmit(&uplinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS], uplinkMsgLen - RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS); + state = this->phyLayer->transmit(&uplinkMsg[RADIOLIB_LW_FHDR_LEN_START_OFFS], uplinkMsgLen - RADIOLIB_LW_FHDR_LEN_START_OFFS); // set the timestamp so that we can measure when to start receiving this->rxDelayStart = mod->hal->millis(); 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; + this->lastToA = this->phyLayer->getTimeOnAir(uplinkMsgLen - RADIOLIB_LW_FHDR_LEN_START_OFFS) / 1000; #if !RADIOLIB_STATIC_ONLY delete[] uplinkMsg; @@ -1119,55 +1101,53 @@ int16_t LoRaWANNode::uplink(uint8_t* data, size_t len, uint8_t port, bool isConf RADIOLIB_ASSERT(state); // the downlink confirmation was acknowledged, so clear the counter value - this->confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; + this->confFCntDown = RADIOLIB_LW_FCNT_NONE; // pass the extra info if requested if(event) { - event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK; + event->dir = RADIOLIB_LW_CHANNEL_DIR_UPLINK; event->confirmed = isConfirmed; event->confirming = isConfirmingDown; - event->datarate = this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK]; + event->datarate = this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK]; event->freq = currentChannels[event->dir].freq; - event->power = this->txPowerMax - this->txPowerCur * 2; - event->fcnt = this->fcntUp; - event->port = port; + event->power = this->txPowerMax - this->txPowerSteps * 2; + event->fCnt = this->fCntUp; + event->fPort = fPort; } // increase frame counter by one for the next uplink - this->fcntUp += 1; + this->fCntUp += 1; return(RADIOLIB_ERR_NONE); } int16_t LoRaWANNode::downlinkCommon() { Module* mod = this->phyLayer->getMod(); - const uint32_t scanGuard = 10; + + // according to the spec, the Rx window must be at least enough time to effectively detect a preamble + // but we pad it a bit on both sides (start and end) to make sure it is wide enough + const RadioLibTime_t scanGuard = 10; // Rx window padding in milliseconds // check if there are any upcoming Rx windows // if the Rx1 window has already started, you're too late, because most downlinks happen in Rx1 - if(mod->hal->millis() - this->rxDelayStart > (this->rxDelays[0] - scanGuard)) { + RadioLibTime_t now = mod->hal->millis(); // fix the current timestamp to prevent negative delays + if(now > this->rxDelayStart + this->rxDelays[0] - scanGuard) { // if between start of Rx1 and end of Rx2, wait until Rx2 closes - if(mod->hal->millis() - this->rxDelayStart < this->rxDelays[1]) { - mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - mod->hal->millis()); + if(now < this->rxDelayStart + this->rxDelays[1]) { + mod->hal->delay(this->rxDelays[1] + this->rxDelayStart - now); } // update the end timestamp in case user got stuck between uplink and downlink this->rxDelayEnd = mod->hal->millis(); return(RADIOLIB_ERR_NO_RX_WINDOW); } - // configure for downlink - int16_t state = this->configureChannel(RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK); + // set the physical layer configuration for downlink + int16_t state = this->setPhyProperties(RADIOLIB_LW_CHANNEL_DIR_DOWNLINK); RADIOLIB_ASSERT(state); - // downlink messages are sent with inverted IQ - if(!this->FSK) { - state = this->phyLayer->invertIQ(true); - RADIOLIB_ASSERT(state); - } - // create the masks that are required for receiving downlinks - uint16_t irqFlags = 0x0000; - uint16_t irqMask = 0x0000; + uint32_t irqFlags = 0; + uint32_t irqMask = 0; this->phyLayer->irqRxDoneRxTimeout(irqFlags, irqMask); this->phyLayer->setPacketReceivedAction(LoRaWANNodeOnDownlinkAction); @@ -1177,14 +1157,16 @@ int16_t LoRaWANNode::downlinkCommon() { downlinkAction = false; // calculate the Rx timeout - // according to the spec, this must be at least enough time to effectively detect a preamble - // but pad it a bit on both sides (start and end) to make sure it is wide enough - uint32_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000; - uint32_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost); + RadioLibTime_t timeoutHost = this->phyLayer->getTimeOnAir(0) + 2*scanGuard*1000; + RadioLibTime_t timeoutMod = this->phyLayer->calculateRxTimeout(timeoutHost); // wait for the start of the Rx window + RadioLibTime_t waitLen = this->rxDelayStart + this->rxDelays[i] - mod->hal->millis(); + // make sure that no underflow occured; if so, clip the delay (although this will likely miss any downlink) + if(waitLen > this->rxDelays[i]) { + waitLen = this->rxDelays[i]; + } // the waiting duration is shortened a bit to cover any possible timing errors - uint32_t waitLen = this->rxDelays[i] - (mod->hal->millis() - this->rxDelayStart); if(waitLen > scanGuard) { waitLen -= scanGuard; } @@ -1192,7 +1174,8 @@ int16_t LoRaWANNode::downlinkCommon() { // open Rx window by starting receive with specified timeout state = this->phyLayer->startReceive(timeoutMod, irqFlags, irqMask, 0); - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, timeoutHost); + RADIOLIB_ASSERT(state); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Opening Rx%d window (%d us timeout)... <-- Rx Delay end ", i+1, (int)timeoutHost); // wait for the timeout to complete (and a small additional delay) mod->hal->delay(timeoutHost / 1000 + scanGuard / 2); @@ -1210,7 +1193,8 @@ int16_t LoRaWANNode::downlinkCommon() { RADIOLIB_ASSERT(state); DataRate_t dataRate; - findDataRate(this->rx2.drMax, &dataRate); + state = findDataRate(this->rx2.drMax, &dataRate); + RADIOLIB_ASSERT(state); state = this->phyLayer->setDataRate(dataRate); RADIOLIB_ASSERT(state); } @@ -1230,8 +1214,16 @@ int16_t LoRaWANNode::downlinkCommon() { } // wait for the DIO to fire indicating a downlink is received + now = mod->hal->millis(); + bool downlinkComplete = true; while(!downlinkAction) { mod->hal->yield(); + // this should never happen, but if it does this would be an infinite loop + if(mod->hal->millis() - now > 3000UL) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Downlink missing!"); + downlinkComplete = false; + break; + } } // we have a message, clear actions, go to standby and reset the IQ inversion @@ -1242,7 +1234,11 @@ int16_t LoRaWANNode::downlinkCommon() { RADIOLIB_ASSERT(state); } - return(RADIOLIB_ERR_NONE); + if(!downlinkComplete) { + state = RADIOLIB_LORAWAN_NO_DOWNLINK; + } + + return(state); } #if defined(RADIOLIB_BUILD_ARDUINO) @@ -1291,8 +1287,8 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) size_t downlinkMsgLen = this->phyLayer->getPacketLength(); // 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) { + // an extra byte is subtracted because downlink frames may not have a fPort + if(downlinkMsgLen < RADIOLIB_LW_FRAME_LEN(0, 0) - 1 - RADIOLIB_AES128_BLOCK_SIZE) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Downlink message too short (%lu bytes)", downlinkMsgLen); return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } @@ -1305,13 +1301,6 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) uint8_t downlinkMsg[RADIOLIB_STATIC_ARRAY_SIZE]; #endif - // set the MIC calculation block - memset(downlinkMsg, 0x00, RADIOLIB_AES128_BLOCK_SIZE); - downlinkMsg[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_MIC_BLOCK_MAGIC; - LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr); - downlinkMsg[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK; - downlinkMsg[RADIOLIB_LORAWAN_MIC_BLOCK_LEN_POS] = downlinkMsgLen - sizeof(uint32_t); - // read the data state = this->phyLayer->readData(&downlinkMsg[RADIOLIB_AES128_BLOCK_SIZE], downlinkMsgLen); // downlink frames are sent without CRC, which will raise error on SX127x @@ -1327,93 +1316,8 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) return(state); } - // get the frame counter and set it to the MIC calculation block - uint16_t fcnt16 = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCNT_POS]); - LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt16); - - // if this downlink is confirming an uplink, its MIC was generated with the least-significant 16 bits of that fcntUp - bool isConfirmingUp = false; - if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_FCTRL_POS] & RADIOLIB_LORAWAN_FCTRL_ACK) && (this->rev == 1)) { - isConfirmingUp = true; - LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFcntUp); - } - - // 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); - - // 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" - // so what about empty frames for ACK? Per TS008, these should be Application downlinks - bool isAppDownlink = true; - if(payLen <= 0) { - if(this->rev == 1) { - isAppDownlink = false; - } - } - else if(downlinkMsg[RADIOLIB_LORAWAN_FHDR_FPORT_POS(foptsLen)] == RADIOLIB_LORAWAN_FPORT_MAC_COMMAND) { - foptsLen = payLen - 1; - if(this->rev == 1) { - isAppDownlink = false; - } - } - - 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; - if (isAppDownlink) { - fcntDownPrev = this->aFcntDown; - } else { - fcntDownPrev = this->nFcntDown; - } - - // 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 - uint32_t fcnt32 = fcnt16; - if(fcntDownPrev > 0) { - if((fcnt16 <= fcntDownPrev) && ((0xFFFF - (uint16_t)fcntDownPrev + fcnt16) > RADIOLIB_LORAWAN_MAX_FCNT_GAP)) { - #if !RADIOLIB_STATIC_ONLY - delete[] downlinkMsg; - #endif - if (isAppDownlink) { - return(RADIOLIB_ERR_A_FCNT_DOWN_INVALID); - } else { - return(RADIOLIB_ERR_N_FCNT_DOWN_INVALID); - } - } else if (fcnt16 <= fcntDownPrev) { - uint16_t msb = (fcntDownPrev >> 16) + 1; // assume a rollover - fcnt32 |= ((uint32_t)msb << 16); // add back the MSB part - } - } - - // check the MIC - if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) { - #if !RADIOLIB_STATIC_ONLY - delete[] downlinkMsg; - #endif - return(RADIOLIB_ERR_CRC_MISMATCH); - } - - // save current fcnt to respective frame counter - if (isAppDownlink) { - this->aFcntDown = fcnt32; - } else { - this->nFcntDown = fcnt32; - } - - // if this is a confirmed frame, save the downlink number (only app frames can be confirmed) - bool isConfirmedDown = false; - if((downlinkMsg[RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS] & 0xFE) == RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_DOWN) { - this->confFcntDown = this->aFcntDown; - isConfirmedDown = true; - } - // check the address - uint32_t addr = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS]); + uint32_t addr = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LW_FHDR_DEV_ADDR_POS]); if(addr != this->devAddr) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Device address mismatch, expected 0x%08X, got 0x%08X", this->devAddr, addr); #if !RADIOLIB_STATIC_ONLY @@ -1422,23 +1326,121 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) return(RADIOLIB_ERR_DOWNLINK_MALFORMED); } + // calculate length of FOpts and payload + uint8_t fOptsLen = downlinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] & RADIOLIB_LW_FHDR_FOPTS_LEN_MASK; + + // check if the ACK bit is set, indicating this frame acknowledges the previous uplink + bool isConfirmingUp = false; + if((downlinkMsg[RADIOLIB_LW_FHDR_FCTRL_POS] & RADIOLIB_LW_FCTRL_ACK)) { + isConfirmingUp = true; + } + + // total - MHDR(1) - DevAddr(4) - FCtrl(1) - FCnt(2) - FOpts - MIC(4) + // potentially also an FPort, but we'll find out soon enough + uint8_t payLen = downlinkMsgLen - 1 - 4 - 1 - 2 - fOptsLen - 4; + + // get the frame counter + uint16_t fCnt16 = LoRaWANNode::ntoh(&downlinkMsg[RADIOLIB_LW_FHDR_FCNT_POS]); + + // set the MIC calculation blocks + memset(downlinkMsg, 0x00, RADIOLIB_AES128_BLOCK_SIZE); + downlinkMsg[RADIOLIB_LW_BLOCK_MAGIC_POS] = RADIOLIB_LW_MIC_BLOCK_MAGIC; + // if this downlink is confirming an uplink, the MIC was generated with the least-significant 16 bits of that fCntUp + if(isConfirmingUp && (this->rev == 1)) { + LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LW_BLOCK_CONF_FCNT_POS], (uint16_t)this->confFCntUp); + } + downlinkMsg[RADIOLIB_LW_BLOCK_DIR_POS] = RADIOLIB_LW_CHANNEL_DIR_DOWNLINK; + LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LW_BLOCK_DEV_ADDR_POS], this->devAddr); + LoRaWANNode::hton(&downlinkMsg[RADIOLIB_LW_BLOCK_FCNT_POS], fCnt16); + downlinkMsg[RADIOLIB_LW_MIC_BLOCK_LEN_POS] = downlinkMsgLen - sizeof(uint32_t); + + // check the MIC + if(!verifyMIC(downlinkMsg, RADIOLIB_AES128_BLOCK_SIZE + downlinkMsgLen, this->sNwkSIntKey)) { + #if !RADIOLIB_STATIC_ONLY + delete[] downlinkMsg; + #endif + return(RADIOLIB_ERR_CRC_MISMATCH); + } + + // in LoRaWAN v1.1, a frame is a Network frame if there is no Application payload + // i.e.: either no payload at all (empty frame or FOpts only), or MAC only payload (FPort = 0) + uint8_t fPort = RADIOLIB_LW_FPORT_MAC_COMMAND; + bool isAppDownlink = false; + if(this->rev == 0) { + isAppDownlink = true; + } + if(payLen > 0) { + payLen -= 1; // subtract one as fPort is set + fPort = downlinkMsg[RADIOLIB_LW_FHDR_FPORT_POS(fOptsLen)]; + if(fPort > RADIOLIB_LW_FPORT_MAC_COMMAND) { + isAppDownlink = true; + } else { + fOptsLen = payLen; + } + } + + 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; + if (isAppDownlink) { + fCntDownPrev = this->aFCntDown; + } else { + fCntDownPrev = this->nFCntDown; + } + + // 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 + uint32_t fCnt32 = fCnt16; + if(fCntDownPrev > 0) { + if((fCnt16 <= fCntDownPrev) && ((0xFFFF - (uint16_t)fCntDownPrev + fCnt16) > RADIOLIB_LW_MAX_FCNT_GAP)) { + #if !RADIOLIB_STATIC_ONLY + delete[] downlinkMsg; + #endif + if (isAppDownlink) { + return(RADIOLIB_ERR_A_FCNT_DOWN_INVALID); + } else { + return(RADIOLIB_ERR_N_FCNT_DOWN_INVALID); + } + } else if (fCnt16 <= fCntDownPrev) { + uint16_t msb = (fCntDownPrev >> 16) + 1; // assume a rollover + fCnt32 |= ((uint32_t)msb << 16); // add back the MSB part + } + } + + // save current fCnt to respective frame counter + if (isAppDownlink) { + this->aFCntDown = fCnt32; + } else { + this->nFCntDown = fCnt32; + } + + // if this is a confirmed frame, save the downlink number (only app frames can be confirmed) + bool isConfirmedDown = false; + if((downlinkMsg[RADIOLIB_LW_FHDR_LEN_START_OFFS] & 0xFE) == RADIOLIB_LW_MHDR_MTYPE_CONF_DATA_DOWN) { + this->confFCntDown = this->aFCntDown; + isConfirmedDown = true; + } + // process FOpts (if there are any) - if(foptsLen > 0) { + if(fOptsLen > 0) { // there are some Fopts, decrypt them #if !RADIOLIB_STATIC_ONLY - uint8_t* fopts = new uint8_t[RADIOLIB_MAX(RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK, (int)foptsLen)]; + uint8_t* fOpts = new uint8_t[RADIOLIB_MAX(RADIOLIB_LW_FHDR_FOPTS_LEN_MASK, (int)fOptsLen)]; #else - uint8_t fopts[RADIOLIB_STATIC_ARRAY_SIZE]; + uint8_t fOpts[RADIOLIB_STATIC_ARRAY_SIZE]; #endif - // TODO it COULD be the case that the assumed FCnt rollover is incorrect, if possible figure out a way to catch this and retry with just fcnt16 + // TODO it COULD be the case that the assumed FCnt rollover is incorrect, if possible figure out a way to catch this and retry with just fCnt16 // if there are <= 15 bytes of FOpts, they are in the FHDR, otherwise they are in the payload // in case of the latter, process AES is if it were a normal payload but using the NwkSEncKey - if(foptsLen <= RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK) { + if(fOptsLen <= RADIOLIB_LW_FHDR_FOPTS_LEN_MASK) { uint8_t ctrId = 0x01 + isAppDownlink; // see LoRaWAN v1.1 errata - processAES(&downlinkMsg[RADIOLIB_LORAWAN_FHDR_FOPTS_POS], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, ctrId, true); + processAES(&downlinkMsg[RADIOLIB_LW_FHDR_FOPTS_POS], (size_t)fOptsLen, this->nwkSEncKey, fOpts, fCnt32, RADIOLIB_LW_CHANNEL_DIR_DOWNLINK, ctrId, true); } else { - processAES(&downlinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(0)], (size_t)foptsLen, this->nwkSEncKey, fopts, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true); + processAES(&downlinkMsg[RADIOLIB_LW_FRAME_PAYLOAD_POS(0)], (size_t)fOptsLen, this->nwkSEncKey, fOpts, fCnt32, RADIOLIB_LW_CHANNEL_DIR_DOWNLINK, 0x00, true); } bool hasADR = false; @@ -1446,17 +1448,17 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) uint8_t lastCID = 0; // process the MAC command(s) - int8_t remLen = foptsLen; - uint8_t* foptsPtr = fopts; + int8_t remLen = fOptsLen; + uint8_t* fOptsPtr = fOpts; while(remLen > 0) { - uint8_t cid = *foptsPtr; + uint8_t cid = *fOptsPtr; uint8_t macLen = getMacPayloadLength(cid); - if(cid == RADIOLIB_LORAWAN_MAC_LINK_ADR) { + if(cid == RADIOLIB_LW_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) { + if(hasADR && lastCID != RADIOLIB_LW_MAC_LINK_ADR) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Encountered non-consecutive block of ADR commands - skipping"); remLen -= (macLen + 1); - foptsPtr += (macLen + 1); + fOptsPtr += (macLen + 1); lastCID = cid; continue; } @@ -1470,9 +1472,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) .cid = cid, .payload = { 0 }, .len = macLen, - .repeat = (cid == RADIOLIB_LORAWAN_MAC_LINK_ADR ? numADR : (uint8_t)0), + .repeat = (cid == RADIOLIB_LW_MAC_LINK_ADR ? numADR : (uint8_t)0), }; - memcpy(cmd.payload, foptsPtr + 1, macLen); + memcpy(cmd.payload, fOptsPtr + 1, macLen); // process the MAC command bool sendUp = execMacCommand(&cmd); @@ -1482,32 +1484,32 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) // processing succeeded, move in the buffer to the next command remLen -= (macLen + 1); - foptsPtr += (macLen + 1); + fOptsPtr += (macLen + 1); lastCID = cid; } #if !RADIOLIB_STATIC_ONLY - delete[] fopts; + delete[] fOpts; #endif - // 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; + // if fOptsLen for the next uplink is larger than can be piggybacked onto an uplink, send separate uplink + if(this->commandsUp.len > RADIOLIB_LW_FHDR_FOPTS_MAX_LEN) { + size_t fOptsBufSize = this->commandsUp.len; #if RADIOLIB_STATIC_ONLY - uint8_t foptsBuff[RADIOLIB_STATIC_ARRAY_SIZE]; + uint8_t fOptsBuff[RADIOLIB_STATIC_ARRAY_SIZE]; #else - uint8_t* foptsBuff = new uint8_t[foptsBufSize]; + uint8_t* fOptsBuff = new uint8_t[fOptsBufSize]; #endif - uint8_t* foptsPtr = foptsBuff; - // append all MAC replies into fopts buffer + uint8_t* fOptsPtr = fOptsBuff; + // append all MAC replies into fOpts buffer int16_t i = 0; for (; i < this->commandsUp.numCommands; i++) { LoRaWANMacCommand_t cmd = this->commandsUp.commands[i]; - memcpy(foptsPtr, &cmd, 1 + cmd.len); - foptsPtr += cmd.len + 1; + memcpy(fOptsPtr, &cmd, 1 + cmd.len); + fOptsPtr += cmd.len + 1; } RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Uplink MAC payload (%d commands):", this->commandsUp.numCommands); - RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(foptsBuff, foptsBufSize); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(fOptsBuff, fOptsBufSize); // pop the commands from back to front for (; i >= 0; i--) { @@ -1523,18 +1525,18 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) bool prevDC = this->dutyCycleEnabled; this->dutyCycleEnabled = false; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Sending MAC-only uplink .. "); - state = this->uplink(foptsBuff, foptsBufSize, RADIOLIB_LORAWAN_FPORT_MAC_COMMAND); + state = this->uplink(fOptsBuff, fOptsBufSize, RADIOLIB_LW_FPORT_MAC_COMMAND); RADIOLIB_DEBUG_PROTOCOL_PRINTLN(" .. state: %d", state); this->dutyCycleEnabled = prevDC; #if !RADIOLIB_STATIC_ONLY - delete[] foptsBuff; + delete[] fOptsBuff; #endif #if RADIOLIB_STATIC_ONLY uint8_t strDown[RADIOLIB_STATIC_ARRAY_SIZE]; #else - uint8_t* strDown = new uint8_t[this->band->payloadLenMax[this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]]]; + uint8_t* strDown = new uint8_t[this->band->payloadLenMax[this->dataRates[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK]]]; #endif size_t lenDown = 0; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Receiving after MAC-only uplink .. "); @@ -1548,23 +1550,23 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) } - // a downlink was received, so reset the ADR counter to the last uplink's fcnt - this->adrFcnt = this->fcntUp - 1; + // a downlink was received, so reset the ADR counter to the last uplink's fCnt + this->adrFCnt = this->getFCntUp(); // pass the extra info if requested if(event) { - event->dir = RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK; + event->dir = RADIOLIB_LW_CHANNEL_DIR_DOWNLINK; event->confirmed = isConfirmedDown; event->confirming = isConfirmingUp; - event->datarate = this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK]; + event->datarate = this->dataRates[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK]; event->freq = currentChannels[event->dir].freq; - event->power = this->txPowerMax - this->txPowerCur * 2; - event->fcnt = isAppDownlink ? this->aFcntDown : this->nFcntDown; - event->port = isAppDownlink ? downlinkMsg[RADIOLIB_LORAWAN_FHDR_FPORT_POS(foptsLen)] : RADIOLIB_LORAWAN_FPORT_MAC_COMMAND; + event->power = this->txPowerMax - this->txPowerSteps * 2; + event->fCnt = isAppDownlink ? this->aFCntDown : this->nFCntDown; + event->fPort = fPort; } - // process Application payload (if there is any) - if(payLen <= 0 || foptsLen > RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN) { + // if MAC-only payload, return now + if(fPort == RADIOLIB_LW_FPORT_MAC_COMMAND) { // no payload *len = 0; #if !RADIOLIB_STATIC_ONLY @@ -1574,10 +1576,11 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) return(RADIOLIB_ERR_NONE); } - *len = payLen - 1; + // process Application payload + *len = payLen; - // TODO it COULD be the case that the assumed rollover is incorrect, then figure out a way to catch this and retry with just fcnt16 - processAES(&downlinkMsg[RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(foptsLen)], payLen - 1, this->appSKey, data, fcnt32, RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK, 0x00, true); + // TODO it COULD be the case that the assumed rollover is incorrect, then figure out a way to catch this and retry with just fCnt16 + processAES(&downlinkMsg[RADIOLIB_LW_FRAME_PAYLOAD_POS(fOptsLen)], payLen, this->appSKey, data, fCnt32, RADIOLIB_LW_CHANNEL_DIR_DOWNLINK, 0x00, true); #if !RADIOLIB_STATIC_ONLY delete[] downlinkMsg; @@ -1587,9 +1590,9 @@ int16_t LoRaWANNode::downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event) } #if defined(RADIOLIB_BUILD_ARDUINO) -int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { +int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t fPort, String& strDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { // send the uplink - int16_t state = this->uplink(strUp, port, isConfirmed, eventUp); + int16_t state = this->uplink(strUp, fPort, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink @@ -1598,9 +1601,9 @@ int16_t LoRaWANNode::sendReceive(String& strUp, uint8_t port, String& strDown, b } #endif -int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { +int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { // send the uplink - int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed, eventUp); + int16_t state = this->uplink(dataUp, lenUp, fPort, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink @@ -1608,9 +1611,9 @@ int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, bo return(state); } -int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { +int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { // send the uplink - int16_t state = this->uplink(strUp, port, isConfirmed, eventUp); + int16_t state = this->uplink(strUp, fPort, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink @@ -1618,9 +1621,9 @@ int16_t LoRaWANNode::sendReceive(const char* strUp, uint8_t port, uint8_t* dataD return(state); } -int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { +int16_t LoRaWANNode::sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed, LoRaWANEvent_t* eventUp, LoRaWANEvent_t* eventDown) { // send the uplink - int16_t state = this->uplink(dataUp, lenUp, port, isConfirmed, eventUp); + int16_t state = this->uplink(dataUp, lenUp, fPort, isConfirmed, eventUp); RADIOLIB_ASSERT(state); // wait for the downlink @@ -1632,25 +1635,25 @@ void LoRaWANNode::setDeviceStatus(uint8_t battLevel) { this->battLevel = battLevel; } -// return Fcnt of last uplink; also return 0 if no uplink occured yet -uint32_t LoRaWANNode::getFcntUp() { - if(this->fcntUp == 0) { +// return fCnt of last uplink; also return 0 if no uplink occured yet +uint32_t LoRaWANNode::getFCntUp() { + if(this->fCntUp == 0) { return(0); } - return(this->fcntUp - 1); + return(this->fCntUp - 1); } -uint32_t LoRaWANNode::getNFcntDown() { - return(this->nFcntDown); +uint32_t LoRaWANNode::getNFCntDown() { + return(this->nFCntDown); } -uint32_t LoRaWANNode::getAFcntDown() { - return(this->aFcntDown); +uint32_t LoRaWANNode::getAFCntDown() { + return(this->aFCntDown); } -void LoRaWANNode::resetFcntDown() { - this->nFcntDown = 0; - this->aFcntDown = 0; +void LoRaWANNode::resetFCntDown() { + this->nFCntDown = 0; + this->aFCntDown = 0; } uint32_t LoRaWANNode::generateMIC(uint8_t* msg, size_t len, uint8_t* key) { @@ -1682,29 +1685,64 @@ bool LoRaWANNode::verifyMIC(uint8_t* msg, size_t len, uint8_t* key) { return(true); } -int16_t LoRaWANNode::setPhyProperties() { +int16_t LoRaWANNode::setPhyProperties(uint8_t dir) { // set the physical layer configuration - int8_t pwr = this->txPowerMax - this->txPowerCur * 2; - int16_t 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 - state = this->phyLayer->setOutputPower(pwr--); - } + RADIOLIB_DEBUG_PROTOCOL_PRINTLN(""); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: Frequency %cL = %6.3f MHz", dir ? 'D' : 'U', this->currentChannels[dir].freq); + int16_t state = this->phyLayer->setFrequency(this->currentChannels[dir].freq); RADIOLIB_ASSERT(state); + // if this channel is an FSK channel, toggle the FSK switch + if(this->band->dataRates[this->dataRates[dir]] == RADIOLIB_LW_DATA_RATE_FSK_50_K) { + this->FSK = true; + } else { + this->FSK = false; + } + + int8_t pwr = this->txPowerMax - this->txPowerSteps * 2; + + // at this point, assume that Tx power value is already checked, so ignore the return value + (void)this->phyLayer->checkOutputPower(pwr, &pwr); + state = this->phyLayer->setOutputPower(pwr); + RADIOLIB_ASSERT(state); + + DataRate_t dr; + state = findDataRate(this->dataRates[dir], &dr); + RADIOLIB_ASSERT(state); + state = this->phyLayer->setDataRate(dr); + RADIOLIB_ASSERT(state); + + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("PHY: SF = %d, TX = %d dBm, BW = %6.3f kHz, CR = 4/%d", + dr.lora.spreadingFactor, pwr, dr.lora.bandwidth, dr.lora.codingRate); + + if(this->FSK) { + state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0); + RADIOLIB_ASSERT(state); + state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING); + } + + // downlink messages are sent with inverted IQ + if(dir == RADIOLIB_LW_CHANNEL_DIR_DOWNLINK) { + if(!this->FSK) { + state = this->phyLayer->invertIQ(true); + RADIOLIB_ASSERT(state); + } + } + + // this only needs to be done once-ish uint8_t syncWord[3] = { 0 }; uint8_t syncWordLen = 0; size_t preLen = 0; if(this->FSK) { - preLen = 8*RADIOLIB_LORAWAN_GFSK_PREAMBLE_LEN; - syncWord[0] = (uint8_t)(RADIOLIB_LORAWAN_GFSK_SYNC_WORD >> 16); - syncWord[1] = (uint8_t)(RADIOLIB_LORAWAN_GFSK_SYNC_WORD >> 8); - syncWord[2] = (uint8_t)RADIOLIB_LORAWAN_GFSK_SYNC_WORD; + preLen = 8*RADIOLIB_LW_GFSK_PREAMBLE_LEN; + syncWord[0] = (uint8_t)(RADIOLIB_LW_GFSK_SYNC_WORD >> 16); + syncWord[1] = (uint8_t)(RADIOLIB_LW_GFSK_SYNC_WORD >> 8); + syncWord[2] = (uint8_t)RADIOLIB_LW_GFSK_SYNC_WORD; syncWordLen = 3; } else { - preLen = RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN; - syncWord[0] = RADIOLIB_LORAWAN_LORA_SYNC_WORD; + preLen = RADIOLIB_LW_LORA_PREAMBLE_LEN; + syncWord[0] = RADIOLIB_LW_LORA_SYNC_WORD; syncWordLen = 1; } @@ -1722,38 +1760,38 @@ int16_t LoRaWANNode::setupChannelsDyn(bool joinRequest) { size_t num = 0; // copy the default defined channels into the first slots (where Tx = Rx) for(; num < 3 && this->band->txFreqs[num].enabled; num++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = this->band->txFreqs[num]; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][num] = this->band->txFreqs[num]; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][num] = this->band->txFreqs[num]; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][num] = this->band->txFreqs[num]; } // if we're about to send a join-request, copy the join-request channels to the next slots if(joinRequest) { size_t numJR = 0; for(; numJR < 3 && this->band->txJoinReq[num].enabled; numJR++, num++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = this->band->txFreqs[num]; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][num] = this->band->txFreqs[num]; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][num] = this->band->txFreqs[num]; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][num] = this->band->txFreqs[num]; } } // clear all remaining channels - for(; num < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; num++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][num] = RADIOLIB_LORAWAN_CHANNEL_NONE; + for(; num < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; num++) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][num] = RADIOLIB_LW_CHANNEL_NONE; } - for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + for (int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMax ); } } @@ -1767,8 +1805,8 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { 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; + for(size_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LW_CHANNEL_NONE; } // if no subband is selected by user, cycle through banks of 8 using devNonce value @@ -1804,18 +1842,18 @@ int16_t LoRaWANNode::setupChannelsFix(uint8_t subBand) { int16_t LoRaWANNode::processCFList(uint8_t* cfList) { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("Processing CFList"); - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { // retrieve number of existing (default) channels size_t num = 0; - for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(!this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(!this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled) { break; } num++; } LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, + .cid = RADIOLIB_LW_MAC_NEW_CHANNEL, .payload = { 0 }, .len = 0, .repeat = 0, @@ -1823,19 +1861,19 @@ int16_t LoRaWANNode::processCFList(uint8_t* cfList) { // datarate range for all new channels is equal to the default channels cmd.payload[4] = (this->band->txFreqs[0].drMax << 4) | this->band->txFreqs[0].drMin; for(uint8_t i = 0; i < 5; i++, num++) { - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_NEW_CHANNEL].lenDn; + cmd.len = MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn; cmd.payload[0] = num; memcpy(&cmd.payload[1], &cfList[i*3], 3); (void)execMacCommand(&cmd); } - } else { // RADIOLIB_LORAWAN_BAND_FIXED + } else { // RADIOLIB_LW_BAND_FIXED // complete channel mask received, so clear all existing channels - for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LORAWAN_CHANNEL_NONE; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LW_CHANNEL_NONE; } LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LW_MAC_LINK_ADR, .payload = { 0 }, .len = 0, .repeat = 0, @@ -1844,7 +1882,7 @@ int16_t LoRaWANNode::processCFList(uint8_t* cfList) { // 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 for(size_t chMaskCntl = 0; chMaskCntl < numChMasks; chMaskCntl++) { - cmd.len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn; + cmd.len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn; 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 @@ -1859,11 +1897,11 @@ int16_t LoRaWANNode::processCFList(uint8_t* cfList) { int16_t LoRaWANNode::selectChannels() { // figure out which channel IDs are enabled (chMask may have disabled some) and are valid for the current datarate uint8_t numChannels = 0; - uint8_t channelsEnabled[RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS]; - for(uint8_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { - if(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] >= this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMin - && this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] <= this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].drMax) { + uint8_t channelsEnabled[RADIOLIB_LW_NUM_AVAILABLE_CHANNELS]; + for(uint8_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled) { + if(this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] >= this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMin + && this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] <= this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMax) { channelsEnabled[numChannels] = i; numChannels++; } @@ -1875,26 +1913,26 @@ int16_t LoRaWANNode::selectChannels() { } // select a random ID & channel from the list of enabled and possible channels uint8_t channelID = channelsEnabled[this->phyLayer->random(numChannels)]; - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][channelID]; + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK] = this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][channelID]; - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { // for dynamic bands, the downlink channel is the one matched to the uplink channel - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][channelID]; + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK] = this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][channelID]; - } else { // RADIOLIB_LORAWAN_BAND_FIXED + } else { // RADIOLIB_LW_BAND_FIXED // for fixed bands, the downlink channel is the uplink channel ID `modulo` number of downlink channels LoRaWANChannel_t channelDn; channelDn.enabled = true; - channelDn.idx = this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK].idx % this->band->rx1Span.numChannels; + channelDn.idx = this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK].idx % this->band->rx1Span.numChannels; channelDn.freq = this->band->rx1Span.freqStart + channelDn.idx*this->band->rx1Span.freqStep; channelDn.drMin = this->band->rx1Span.drMin; channelDn.drMax = this->band->rx1Span.drMax; - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = channelDn; + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK] = channelDn; } - uint8_t drDown = getDownlinkDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], this->rx1DrOffset, this->band->rx1DataRateBase, - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax); - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; + uint8_t drDown = getDownlinkDataRate(this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK], this->rx1DrOffset, this->band->rx1DataRateBase, + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].drMin, this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].drMax); + this->dataRates[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK] = drDown; return(RADIOLIB_ERR_NONE); } @@ -1902,8 +1940,8 @@ int16_t LoRaWANNode::selectChannels() { 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++) { - LoRaWANChannel_t *chnl = &(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i]); + for(size_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + LoRaWANChannel_t *chnl = &(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i]); if(chnl->enabled) { if(drUp >= chnl->drMin && drUp <= chnl->drMax) { isValidDR = true; @@ -1917,9 +1955,9 @@ int16_t LoRaWANNode::setDatarate(uint8_t drUp) { } LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LW_MAC_LINK_ADR, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, + .len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn, .repeat = 0, }; cmd.payload[0] = (drUp << 4); @@ -1940,7 +1978,7 @@ void LoRaWANNode::setADR(bool enable) { this->adrEnabled = enable; } -void LoRaWANNode::setDutyCycle(bool enable, uint32_t msPerHour) { +void LoRaWANNode::setDutyCycle(bool enable, RadioLibTime_t msPerHour) { this->dutyCycleEnabled = enable; if(msPerHour <= 0) { this->dutyCycle = this->band->dutyCycle; @@ -1951,26 +1989,26 @@ void LoRaWANNode::setDutyCycle(bool enable, uint32_t msPerHour) { // given an airtime in milliseconds, calculate the minimum uplink interval // to adhere to a given dutyCycle -uint32_t LoRaWANNode::dutyCycleInterval(uint32_t msPerHour, uint32_t airtime) { +RadioLibTime_t LoRaWANNode::dutyCycleInterval(RadioLibTime_t msPerHour, RadioLibTime_t airtime) { if(msPerHour == 0 || airtime == 0) { return(0); } - uint32_t oneHourInMs = (uint32_t)60 * (uint32_t)60 * (uint32_t)1000; + RadioLibTime_t oneHourInMs = (RadioLibTime_t)60 * (RadioLibTime_t)60 * (RadioLibTime_t)1000; float numPackets = msPerHour / airtime; - uint32_t delayMs = oneHourInMs / numPackets + 1; // + 1 to prevent rounding problems + RadioLibTime_t delayMs = oneHourInMs / numPackets + 1; // + 1 to prevent rounding problems return(delayMs); } -uint32_t LoRaWANNode::timeUntilUplink() { +RadioLibTime_t LoRaWANNode::timeUntilUplink() { Module* mod = this->phyLayer->getMod(); - uint32_t nextUplink = this->rxDelayStart + dutyCycleInterval(this->dutyCycle, this->lastToA); + RadioLibTime_t nextUplink = this->rxDelayStart + dutyCycleInterval(this->dutyCycle, this->lastToA); if(mod->hal->millis() > nextUplink){ return(0); } return(nextUplink - mod->hal->millis() + 1); } -void LoRaWANNode::setDwellTime(bool enable, uint32_t msPerUplink) { +void LoRaWANNode::setDwellTime(bool enable, RadioLibTime_t msPerUplink) { this->dwellTimeEnabledUp = enable; if(msPerUplink <= 0) { this->dwellTimeUp = this->band->dwellTimeUp; @@ -1982,7 +2020,8 @@ void LoRaWANNode::setDwellTime(bool enable, uint32_t msPerUplink) { uint8_t LoRaWANNode::maxPayloadDwellTime() { // configure current datarate DataRate_t dr; - findDataRate(this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK], &dr); + // TODO this may fail horribly? + (void)findDataRate(this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK], &dr); (void)this->phyLayer->setDataRate(dr); uint8_t minPayLen = 0; uint8_t maxPayLen = 255; @@ -2007,12 +2046,12 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower) { // Tx Power is set in steps of two // the selected value is rounded down to nearest multiple of two away from txPowerMax // e.g. on EU868, max is 16; if 13 is selected then we set to 12 - uint8_t numSteps = (this->txPowerMax - txPower + 1) / (-RADIOLIB_LORAWAN_POWER_STEP_SIZE_DBM); + uint8_t numSteps = (this->txPowerMax - txPower + 1) / (-RADIOLIB_LW_POWER_STEP_SIZE_DBM); LoRaWANMacCommand_t cmd = { - .cid = RADIOLIB_LORAWAN_MAC_LINK_ADR, + .cid = RADIOLIB_LW_MAC_LINK_ADR, .payload = { 0 }, - .len = MacTable[RADIOLIB_LORAWAN_MAC_LINK_ADR].lenDn, + .len = MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn, .repeat = 0, }; cmd.payload[0] = 0xF0; // keep datarate the same @@ -2030,22 +2069,24 @@ int16_t LoRaWANNode::setTxPower(int8_t txPower) { } int16_t LoRaWANNode::findDataRate(uint8_t dr, DataRate_t* dataRate) { + int16_t state = RADIOLIB_ERR_UNKNOWN; + uint8_t dataRateBand = this->band->dataRates[dr]; - if(dataRateBand & RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) { + if(dataRateBand & RADIOLIB_LW_DATA_RATE_FSK_50_K) { dataRate->fsk.bitRate = 50; dataRate->fsk.freqDev = 25; } else { uint8_t bw = dataRateBand & 0x0C; switch(bw) { - case(RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ): + case(RADIOLIB_LW_DATA_RATE_BW_125_KHZ): dataRate->lora.bandwidth = 125.0; break; - case(RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ): + case(RADIOLIB_LW_DATA_RATE_BW_250_KHZ): dataRate->lora.bandwidth = 250.0; break; - case(RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ): + case(RADIOLIB_LW_DATA_RATE_BW_500_KHZ): dataRate->lora.bandwidth = 500.0; break; default: @@ -2054,50 +2095,37 @@ 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_PROTOCOL_PRINTLN("PHY: SF = %d, BW = %6.3f kHz, CR = 4/%d", - dataRate->lora.spreadingFactor, dataRate->lora.bandwidth, dataRate->lora.codingRate); } - return(RADIOLIB_ERR_NONE); -} - -int16_t LoRaWANNode::configureChannel(uint8_t dir) { - // set the frequency - 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); - - // if this channel is an FSK channel, toggle the FSK switch - if(this->band->dataRates[this->dataRates[dir]] == RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K) { - this->FSK = true; - } else { - this->FSK = false; - } - - DataRate_t dr; - findDataRate(this->dataRates[dir], &dr); - state = this->phyLayer->setDataRate(dr); - RADIOLIB_ASSERT(state); - - if(this->FSK) { - state = this->phyLayer->setDataShaping(RADIOLIB_SHAPING_1_0); - RADIOLIB_ASSERT(state); - state = this->phyLayer->setEncoding(RADIOLIB_ENCODING_WHITENING); - } + state = this->phyLayer->checkDataRate(*dataRate); return(state); } -bool LoRaWANNode::sendMacCommandReq(uint8_t cid) { +int16_t LoRaWANNode::sendMacCommandReq(uint8_t cid) { bool valid = false; - for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_MAC_COMMANDS; i++) { + for(size_t i = 0; i < RADIOLIB_LW_NUM_MAC_COMMANDS; i++) { if(MacTable[i].cid == cid) { valid = MacTable[i].user; } } - if(!valid) - return(false); + if(!valid) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("You are not allowed to request this MAC command"); + return(RADIOLIB_ERR_INVALID_CID); + } + + // if there are already 15 MAC bytes in the uplink queue, we can't add a new one + if(this->commandsUp.len + 1 > RADIOLIB_LW_FHDR_FOPTS_MAX_LEN) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The maximum number of FOpts payload was reached"); + return(RADIOLIB_ERR_COMMAND_QUEUE_FULL); + } + if(this->commandsUp.numCommands > RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE) { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("The RadioLib internal MAC command queue was full"); + return(RADIOLIB_ERR_COMMAND_QUEUE_FULL); + } + + // delete any prior requests for this MAC command, in case this is requested more than once + (void)deleteMacCommand(cid, &this->commandsUp); LoRaWANMacCommand_t cmd = { .cid = cid, @@ -2110,7 +2138,7 @@ bool LoRaWANNode::sendMacCommandReq(uint8_t cid) { } int16_t LoRaWANNode::pushMacCommand(LoRaWANMacCommand_t* cmd, LoRaWANMacCommandQueue_t* queue) { - if(queue->numCommands >= RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE) { + if(queue->numCommands >= RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE) { return(RADIOLIB_ERR_COMMAND_QUEUE_FULL); } @@ -2130,11 +2158,11 @@ int16_t LoRaWANNode::deleteMacCommand(uint8_t cid, LoRaWANMacCommandQueue_t* que } queue->len -= (1 + queue->commands[index].len); // 1 byte for command ID, len for payload // move all subsequent commands one forward in the queue - if(index < RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1) { - memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - index - 1) * sizeof(LoRaWANMacCommand_t)); + if(index < RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE - 1) { + memmove(&queue->commands[index], &queue->commands[index + 1], (RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE - index - 1) * sizeof(LoRaWANMacCommand_t)); } // set the latest element to all 0 - memset(&queue->commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t)); + memset(&queue->commands[RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE - 1], 0x00, sizeof(LoRaWANMacCommand_t)); queue->numCommands--; return(RADIOLIB_ERR_NONE); } @@ -2147,133 +2175,134 @@ 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) { + if(cmd->cid >= RADIOLIB_LW_MAC_PROPRIETARY) { // TODO call user-provided callback for proprietary MAC commands? return(false); } switch(cmd->cid) { - case(RADIOLIB_LORAWAN_MAC_RESET): { + case(RADIOLIB_LW_MAC_RESET): { // get the server version uint8_t srvVersion = cmd->payload[0]; 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); + deleteMacCommand(RADIOLIB_LW_MAC_RESET, &this->commandsUp); } return(false); } break; - case(RADIOLIB_LORAWAN_MAC_LINK_CHECK): { + case(RADIOLIB_LW_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); + deleteMacCommand(RADIOLIB_LW_MAC_LINK_CHECK, &this->commandsDown); // insert response into MAC downlink queue pushMacCommand(cmd, &this->commandsDown); return(false); } break; - case(RADIOLIB_LORAWAN_MAC_LINK_ADR): { + case(RADIOLIB_LW_MAC_LINK_ADR): { int16_t state = RADIOLIB_ERR_UNKNOWN; // get the ADR configuration - // per spec, all these configuration should only be set if all ACKs are set, otherwise retain previous state - // 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; + uint8_t txSteps = 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_PROTOCOL_PRINTLN("LinkADRReq: dataRate = %d, txPower = %d, chMask = 0x%04x, chMaskCntl = %d, nbTrans = %d", drUp, txPower, chMask, chMaskCntl, nbTrans); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("LinkADRReq: dataRate = %d, txSteps = %d, chMask = 0x%04x, chMaskCntl = %d, nbTrans = %d", drUp, txSteps, chMask, chMaskCntl, nbTrans); - // apply the configuration + // try to apply the datarate configuration uint8_t drAck = 0; if(drUp == 0x0F) { // keep the same drAck = 1; - // replace the 'placeholder' with the current actual value for saving - cmd->payload[0] = (cmd->payload[0] & 0x0F) | (this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] << 4); - - } else if (this->band->dataRates[drUp] != RADIOLIB_LORAWAN_DATA_RATE_UNUSED) { + } else if (this->band->dataRates[drUp] != RADIOLIB_LW_DATA_RATE_UNUSED) { // check if the module supports this data rate DataRate_t dr; - findDataRate(drUp, &dr); - state = this->phyLayer->checkDataRate(dr); + state = findDataRate(drUp, &dr); if(state == RADIOLIB_ERR_NONE) { uint8_t drDown = getDownlinkDataRate(drUp, this->rx1DrOffset, this->band->rx1DataRateBase, - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMin, - this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].drMax); - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK] = drUp; - this->dataRates[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK] = drDown; + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].drMin, + this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].drMax); + this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] = drUp; + this->dataRates[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK] = drDown; drAck = 1; } else { RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure dataRate %d, code %d!", drUp, state); + drUp = 0x0F; // set value to 'keep the same' } } // try to apply the power configuration uint8_t pwrAck = 0; - if(txPower == 0x0F) { + if(txSteps == 0x0F) { pwrAck = 1; - // replace the 'placeholder' with the current actual value for saving - cmd->payload[0] = (cmd->payload[0] & 0xF0) | this->txPowerCur; - } 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 - state = this->phyLayer->setOutputPower(pwr--); - } - // only acknowledge if the requested datarate was succesfully configured - if(state == RADIOLIB_ERR_NONE) { + int8_t power = this->txPowerMax - 2*txSteps; + int8_t powerActual = 0; + state = this->phyLayer->checkOutputPower(power, &powerActual); + // only acknowledge if the radio is able to operate at or below the requested power level + if(state == RADIOLIB_ERR_NONE || (state == RADIOLIB_ERR_INVALID_OUTPUT_POWER && powerActual < power)) { pwrAck = 1; - this->txPowerCur = txPower; + this->txPowerSteps = txSteps; + } else { + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADR failed to configure Tx power %d, code %d!", power, state); + txSteps = 0x0F; // set value to 'keep the same' } } - uint8_t chMaskAck = 1; // only apply channel mask when the RFU bit is not set // (which is only set in internal MAC commands for changing Tx/Dr) if(!isInternalTxDr) { - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + if(this->band->bandType == RADIOLIB_LW_BAND_DYNAMIC) { chMaskAck = (uint8_t)this->applyChannelMaskDyn(chMaskCntl, chMask); - } else { // RADIOLIB_LORAWAN_BAND_FIXED + } else { // RADIOLIB_LW_BAND_FIXED 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 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; + for(size_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i] = RADIOLIB_LW_CHANNEL_NONE; } // clear all previous channel masks - memset(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS], 0, 16*8); + memset(&this->bufferSession[RADIOLIB_LW_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); + (void)deleteMacCommand(RADIOLIB_LW_MAC_LINK_ADR, &this->commandsUp); } chMaskAck = (uint8_t)this->applyChannelMaskFix(chMaskCntl, chMask); } } - if(nbTrans == 0) { // keep the same - cmd->payload[3] = (cmd->payload[3] & 0xF0) | this->nbTrans; // set current number of retransmissions for saving - } else { + if(nbTrans) { // if there is a value for NbTrans, set this value this->nbTrans = nbTrans; } - if(this->band->bandType == RADIOLIB_LORAWAN_BAND_DYNAMIC) { + // replace 'placeholder' or failed values with the current values for saving + // per spec, all these configuration should only be set if all ACKs are set, otherwise retain previous state + // but we don't bother and try to set each individual command + if(drUp == 0x0F || !drAck) { + cmd->payload[0] = (cmd->payload[0] & 0x0F) | (this->dataRates[RADIOLIB_LW_CHANNEL_DIR_UPLINK] << 4); + } + if(txSteps == 0x0F || !pwrAck) { + cmd->payload[0] = (cmd->payload[0] & 0xF0) | this->txPowerSteps; + } + if(nbTrans == 0) { + cmd->payload[3] = (cmd->payload[3] & 0xF0) | this->nbTrans; + } + + if(this->band->bandType == RADIOLIB_LW_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(isInternalTxDr) { - memcpy(&(cmd->payload[1]), &this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR] + 1, 3); + memcpy(&(cmd->payload[1]), &this->bufferSession[RADIOLIB_LW_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 @@ -2282,24 +2311,24 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { } // save to the single ADR MAC location - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_LINK_ADR], &(cmd->payload[0]), cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_LINK_ADR], &(cmd->payload[0]), cmd->len); - } else { // RADIOLIB_LORAWAN_BAND_FIXED + } else { // RADIOLIB_LW_BAND_FIXED // save Tx/Dr to the Link ADR position in the session buffer - uint8_t bufTxDr[RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; + uint8_t bufTxDr[RADIOLIB_LW_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); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_LINK_ADR], bufTxDr, cmd->len); // 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); + memcpy(&this->bufferSession[RADIOLIB_LW_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); + RADIOLIB_DEBUG_PROTOCOL_HEXDUMP(&this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS] + (cmd->repeat - 1) * cmd->len, cmd->len); } } @@ -2312,22 +2341,22 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_DUTY_CYCLE): { + case(RADIOLIB_LW_MAC_DUTY_CYCLE): { uint8_t maxDutyCycle = cmd->payload[0] & 0x0F; 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); + this->dutyCycle = (RadioLibTime_t)60 * (RadioLibTime_t)60 * (RadioLibTime_t)1000 / (RadioLibTime_t)(1UL << maxDutyCycle); } - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DUTY_CYCLE], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_DUTY_CYCLE], cmd->payload, cmd->len); cmd->len = 0; return(true); } break; - case(RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP): { + case(RADIOLIB_LW_MAC_RX_PARAM_SETUP): { // get the configuration this->rx1DrOffset = (cmd->payload[0] & 0x70) >> 4; uint8_t rx1OffsAck = 1; @@ -2341,10 +2370,10 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { uint8_t chanAck = 0; if(this->phyLayer->setFrequency(this->rx2.freq) == RADIOLIB_ERR_NONE) { chanAck = 1; - this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq); + this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].freq); } - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_PARAM_SETUP], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_RX_PARAM_SETUP], cmd->payload, cmd->len); // TODO this should be sent repeatedly until the next downlink cmd->len = 1; @@ -2353,7 +2382,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_DEV_STATUS): { + case(RADIOLIB_LW_MAC_DEV_STATUS): { // set the uplink reply RADIOLIB_DEBUG_PROTOCOL_PRINTLN("DevStatusReq"); cmd->len = 2; @@ -2365,7 +2394,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_NEW_CHANNEL): { + case(RADIOLIB_LW_MAC_NEW_CHANNEL): { // get the configuration uint8_t chIndex = cmd->payload[0]; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); @@ -2376,38 +2405,38 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { uint8_t newChAck = 0; uint8_t freqAck = 0; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].enabled = true; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].idx = chIndex; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].freq = freq; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].drMin = minDr; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].drMax = maxDr; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].enabled = true; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].idx = chIndex; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].freq = freq; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].drMin = minDr; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].drMax = maxDr; // downlink channel is identical to uplink channel - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex] = this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex]; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex] = this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex]; newChAck = 1; // check if the frequency is possible - if(this->phyLayer->setFrequency(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].freq) == RADIOLIB_ERR_NONE) { + if(this->phyLayer->setFrequency(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].freq) == RADIOLIB_ERR_NONE) { freqAck = 1; - this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq); + this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].freq); } 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, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][chIndex].drMax, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].drMin, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][chIndex].drMax, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].idx, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].enabled, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].freq, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].drMin, - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][chIndex].drMax + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex].drMin, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][chIndex].drMax ); - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_UL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_UL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); // send the reply cmd->len = 1; @@ -2417,7 +2446,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_DL_CHANNEL): { + case(RADIOLIB_LW_MAC_DL_CHANNEL): { // get the configuration uint8_t chIndex = cmd->payload[0]; uint32_t freqRaw = LoRaWANNode::ntoh(&cmd->payload[1], 3); @@ -2429,21 +2458,21 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { // check if the frequency is possible if(this->phyLayer->setFrequency(freq) == RADIOLIB_ERR_NONE) { freqDlAck = 1; - this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK].freq); + this->phyLayer->setFrequency(this->currentChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK].freq); } // update the downlink frequency - for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].idx == chIndex) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK][i].freq = freq; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].idx == chIndex) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].freq = freq; // check if the corresponding uplink frequency is actually set - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq > 0) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].freq > 0) { freqUlAck = 1; } } } - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_DL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_DL_CHANNELS] + chIndex * cmd->len, cmd->payload, cmd->len); // TODO send this repeatedly until a downlink is received cmd->len = 1; @@ -2453,7 +2482,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP): { + case(RADIOLIB_LW_MAC_RX_TIMING_SETUP): { // get the configuration uint8_t delay = cmd->payload[0] & 0x0F; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RXTimingSetupReq: delay = %d sec", delay); @@ -2465,7 +2494,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { this->rxDelays[0] = delay * 1000; this->rxDelays[1] = this->rxDelays[0] + 1000; - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_RX_TIMING_SETUP], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_RX_TIMING_SETUP], cmd->payload, cmd->len); // send the reply cmd->len = 0; @@ -2474,7 +2503,7 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(true); } break; - case(RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP): { + case(RADIOLIB_LW_MAC_TX_PARAM_SETUP): { uint8_t dlDwell = (cmd->payload[0] & 0x20) >> 5; uint8_t ulDwell = (cmd->payload[0] & 0x10) >> 4; uint8_t maxEirpRaw = cmd->payload[0] & 0x0F; @@ -2485,50 +2514,50 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { 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; + this->dwellTimeUp = ulDwell ? RADIOLIB_LW_DWELL_TIME : 0; this->dwellTimeEnabledDn = dlDwell ? true : false; - this->dwellTimeDn = dlDwell ? RADIOLIB_LORAWAN_DWELL_TIME : 0; + this->dwellTimeDn = dlDwell ? RADIOLIB_LW_DWELL_TIME : 0; - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_TX_PARAM_SETUP], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_TX_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; return(true); } break; - case(RADIOLIB_LORAWAN_MAC_REKEY): { + case(RADIOLIB_LW_MAC_REKEY): { // get the server version uint8_t srvVersion = cmd->payload[0]; 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); + deleteMacCommand(RADIOLIB_LW_MAC_REKEY, &this->commandsUp); } return(false); } break; - case(RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP): { + case(RADIOLIB_LW_MAC_ADR_PARAM_SETUP): { this->adrLimitExp = (cmd->payload[0] & 0xF0) >> 4; this->adrDelayExp = cmd->payload[0] & 0x0F; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("ADRParamSetupReq: limitExp = %d, delayExp = %d", this->adrLimitExp, this->adrDelayExp); - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_ADR_PARAM_SETUP], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_ADR_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; return(true); } break; - case(RADIOLIB_LORAWAN_MAC_DEVICE_TIME): { + case(RADIOLIB_LW_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); + deleteMacCommand(RADIOLIB_LW_MAC_DEVICE_TIME, &this->commandsDown); // insert response into MAC downlink queue pushMacCommand(cmd, &this->commandsDown); return(false); } break; - case(RADIOLIB_LORAWAN_MAC_FORCE_REJOIN): { + case(RADIOLIB_LW_MAC_FORCE_REJOIN): { // TODO implement this uint16_t rejoinReq = LoRaWANNode::ntoh(cmd->payload); uint8_t period = (rejoinReq & 0x3800) >> 11; @@ -2543,13 +2572,13 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { return(false); } break; - case(RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP): { + case(RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP): { // TODO implement this uint8_t maxTime = (cmd->payload[0] & 0xF0) >> 4; uint8_t maxCount = cmd->payload[0] & 0x0F; RADIOLIB_DEBUG_PROTOCOL_PRINTLN("RejoinParamSetupReq: maxTime = %d, maxCount = %d", maxTime, maxCount); - memcpy(&this->bufferSession[RADIOLIB_LORAWAN_SESSION_REJOIN_PARAM_SETUP], cmd->payload, cmd->len); + memcpy(&this->bufferSession[RADIOLIB_LW_SESSION_REJOIN_PARAM_SETUP], cmd->payload, cmd->len); cmd->len = 0; cmd->payload[0] = (1 << 1) | 1; @@ -2565,42 +2594,42 @@ bool LoRaWANNode::execMacCommand(LoRaWANMacCommand_t* cmd) { } bool LoRaWANNode::applyChannelMaskDyn(uint8_t chMaskCntl, uint16_t chMask) { - for(size_t i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { + for(size_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { if(chMaskCntl == 0) { // apply the mask by looking at each channel bit if(chMask & (1UL << i)) { // if it should be enabled but is not currently defined, stop immediately - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx == RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].idx == RADIOLIB_LW_CHANNEL_INDEX_NONE) { return(false); } - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled = true; } else { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = false; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled = false; } } else if(chMaskCntl == 6) { // enable all defined channels - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled = true; + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].idx != RADIOLIB_LW_CHANNEL_INDEX_NONE) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled = true; } } } - for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + for (int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMax ); } } @@ -2613,8 +2642,8 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t 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++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].freq > 0) { + for(size_t i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].freq > 0) { idx++; } } @@ -2632,7 +2661,7 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[0].freqStart + chNum*this->band->txSpans[0].freqStep; chnl.drMin = this->band->txSpans[0].drMin; chnl.drMax = this->band->txSpans[0].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } } @@ -2654,7 +2683,7 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[1].freqStart + i*this->band->txSpans[1].freqStep; chnl.drMin = this->band->txSpans[1].drMin; chnl.drMax = this->band->txSpans[1].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } } @@ -2673,7 +2702,7 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[0].freqStart + chNum*this->band->txSpans[0].freqStep; chnl.drMin = this->band->txSpans[0].drMin; chnl.drMax = this->band->txSpans[0].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } // enable single channel from second span uint8_t chNum = 64 + i; @@ -2682,7 +2711,7 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[1].freqStart + i*this->band->txSpans[1].freqStep; chnl.drMin = this->band->txSpans[1].drMin; chnl.drMax = this->band->txSpans[1].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } } @@ -2703,16 +2732,16 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[1].freqStart + i*this->band->txSpans[1].freqStep; chnl.drMin = this->band->txSpans[1].drMin; chnl.drMax = this->band->txSpans[1].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } } } if(this->band->numTxSpans == 2 && chMaskCntl == 7) { // all channels off (clear all channels) - LoRaWANChannel_t chnl = RADIOLIB_LORAWAN_CHANNEL_NONE; - for(int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i] = chnl; + LoRaWANChannel_t chnl = RADIOLIB_LW_CHANNEL_NONE; + for(int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i] = chnl; // downlink channels are not defined so don't need to reset } idx = 0; @@ -2727,26 +2756,26 @@ bool LoRaWANNode::applyChannelMaskFix(uint8_t chMaskCntl, uint16_t chMask) { chnl.freq = this->band->txSpans[1].freqStart + i*this->band->txSpans[1].freqStep; chnl.drMin = this->band->txSpans[1].drMin; chnl.drMax = this->band->txSpans[1].drMax; - this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][idx++] = chnl; + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][idx++] = chnl; } } } - for (int i = 0; i < RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS; i++) { - if(this->availableChannels[RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK][i].enabled) { + for (int i = 0; i < RADIOLIB_LW_NUM_AVAILABLE_CHANNELS; i++) { + if(this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_UPLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_UPLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_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_LW_CHANNEL_DIR_DOWNLINK][i].idx, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].enabled, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].freq, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMin, + this->availableChannels[RADIOLIB_LW_CHANNEL_DIR_DOWNLINK][i].drMax ); } } @@ -2765,8 +2794,8 @@ uint8_t LoRaWANNode::getMacPayloadLength(uint8_t cid) { } int16_t LoRaWANNode::getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt) { - uint8_t payload[5]; - int16_t state = deleteMacCommand(RADIOLIB_LORAWAN_LINK_CHECK_REQ, &this->commandsDown, payload); + uint8_t payload[RADIOLIB_LW_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; + int16_t state = deleteMacCommand(RADIOLIB_LW_LINK_CHECK_REQ, &this->commandsDown, payload); RADIOLIB_ASSERT(state); if(margin) { *margin = payload[0]; } @@ -2776,14 +2805,14 @@ int16_t LoRaWANNode::getMacLinkCheckAns(uint8_t* margin, uint8_t* gwCnt) { } int16_t LoRaWANNode::getMacDeviceTimeAns(uint32_t* gpsEpoch, uint8_t* fraction, bool returnUnix) { - uint8_t payload[5]; - int16_t state = deleteMacCommand(RADIOLIB_LORAWAN_MAC_DEVICE_TIME, &this->commandsDown, payload); + uint8_t payload[RADIOLIB_LW_MAX_MAC_COMMAND_LEN_DOWN] = { 0 }; + int16_t state = deleteMacCommand(RADIOLIB_LW_MAC_DEVICE_TIME, &this->commandsDown, payload); RADIOLIB_ASSERT(state); if(gpsEpoch) { *gpsEpoch = LoRaWANNode::ntoh(&payload[0]); if(returnUnix) { - uint32_t unixOffset = 315964800 - 18; // 18 leap seconds since GPS epoch (Jan. 6th 1980) + uint32_t unixOffset = 315964800UL - 18UL; // 18 leap seconds since GPS epoch (Jan. 6th 1980) *gpsEpoch += unixOffset; } } @@ -2796,6 +2825,10 @@ uint64_t LoRaWANNode::getDevAddr() { return(this->devAddr); } +RadioLibTime_t LoRaWANNode::getLastToA() { + return(this->lastToA); +} + // The following function enables LMAC, a CSMA scheme for LoRa as specified // in the LoRa Alliance Technical Recommendation #13. // A user may enable CSMA to provide frames an additional layer of protection from interference. @@ -2840,7 +2873,7 @@ bool LoRaWANNode::performCAD() { return false; // Channel is free } -void LoRaWANNode::processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fcnt, uint8_t dir, uint8_t ctrId, bool counter) { +void LoRaWANNode::processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fCnt, uint8_t dir, uint8_t ctrId, bool counter) { // figure out how many encryption blocks are there size_t numBlocks = len/RADIOLIB_AES128_BLOCK_SIZE; if(len % RADIOLIB_AES128_BLOCK_SIZE) { @@ -2850,11 +2883,11 @@ void LoRaWANNode::processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out // generate the encryption blocks uint8_t encBuffer[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; uint8_t encBlock[RADIOLIB_AES128_BLOCK_SIZE] = { 0 }; - encBlock[RADIOLIB_LORAWAN_BLOCK_MAGIC_POS] = RADIOLIB_LORAWAN_ENC_BLOCK_MAGIC; - encBlock[RADIOLIB_LORAWAN_ENC_BLOCK_COUNTER_ID_POS] = ctrId; - encBlock[RADIOLIB_LORAWAN_BLOCK_DIR_POS] = dir; - LoRaWANNode::hton(&encBlock[RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS], this->devAddr); - LoRaWANNode::hton(&encBlock[RADIOLIB_LORAWAN_BLOCK_FCNT_POS], fcnt); + encBlock[RADIOLIB_LW_BLOCK_MAGIC_POS] = RADIOLIB_LW_ENC_BLOCK_MAGIC; + encBlock[RADIOLIB_LW_ENC_BLOCK_COUNTER_ID_POS] = ctrId; + encBlock[RADIOLIB_LW_BLOCK_DIR_POS] = dir; + LoRaWANNode::hton(&encBlock[RADIOLIB_LW_BLOCK_DEV_ADDR_POS], this->devAddr); + LoRaWANNode::hton(&encBlock[RADIOLIB_LW_BLOCK_FCNT_POS], fCnt); // now encrypt the input // on downlink frames, this has a decryption effect because server actually "decrypts" the plaintext @@ -2862,7 +2895,7 @@ void LoRaWANNode::processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out for(size_t i = 0; i < numBlocks; i++) { if(counter) { - encBlock[RADIOLIB_LORAWAN_ENC_BLOCK_COUNTER_POS] = i + 1; + encBlock[RADIOLIB_LW_ENC_BLOCK_COUNTER_POS] = i + 1; } // encrypt the buffer diff --git a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.h b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.h index 875ac7c..f16398e 100644 --- a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.h +++ b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWAN.h @@ -1,283 +1,326 @@ -#if !defined(_RADIOLIB_LORAWAN_H) && !RADIOLIB_EXCLUDE_LORAWAN -#define _RADIOLIB_LORAWAN_H +#if !defined(_RADIOLIB_LW_H) && !RADIOLIB_EXCLUDE_LORAWAN +#define _RADIOLIB_LW_H #include "../../TypeDef.h" #include "../PhysicalLayer/PhysicalLayer.h" #include "../../utils/Cryptography.h" // activation mode -#define RADIOLIB_LORAWAN_MODE_OTAA (0x07AA) -#define RADIOLIB_LORAWAN_MODE_ABP (0x0AB9) -#define RADIOLIB_LORAWAN_MODE_NONE (0x0000) +#define RADIOLIB_LW_MODE_OTAA (0x07AA) +#define RADIOLIB_LW_MODE_ABP (0x0AB9) +#define RADIOLIB_LW_MODE_NONE (0x0000) // operation mode -#define RADIOLIB_LORAWAN_CLASS_A (0x0A) -#define RADIOLIB_LORAWAN_CLASS_B (0x0B) -#define RADIOLIB_LORAWAN_CLASS_C (0x0C) +#define RADIOLIB_LW_CLASS_A (0x0A) +#define RADIOLIB_LW_CLASS_B (0x0B) +#define RADIOLIB_LW_CLASS_C (0x0C) // preamble format -#define RADIOLIB_LORAWAN_LORA_SYNC_WORD (0x34) -#define RADIOLIB_LORAWAN_LORA_PREAMBLE_LEN (8) -#define RADIOLIB_LORAWAN_GFSK_SYNC_WORD (0xC194C1) -#define RADIOLIB_LORAWAN_GFSK_PREAMBLE_LEN (5) +#define RADIOLIB_LW_LORA_SYNC_WORD (0x34) +#define RADIOLIB_LW_LORA_PREAMBLE_LEN (8) +#define RADIOLIB_LW_GFSK_SYNC_WORD (0xC194C1) +#define RADIOLIB_LW_GFSK_PREAMBLE_LEN (5) // MAC header field encoding MSB LSB DESCRIPTION -#define RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_REQUEST (0x00 << 5) // 7 5 message type: join request -#define RADIOLIB_LORAWAN_MHDR_MTYPE_JOIN_ACCEPT (0x01 << 5) // 7 5 join accept -#define RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_UP (0x02 << 5) // 7 5 unconfirmed data up -#define RADIOLIB_LORAWAN_MHDR_MTYPE_UNCONF_DATA_DOWN (0x03 << 5) // 7 5 unconfirmed data down -#define RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_UP (0x04 << 5) // 7 5 confirmed data up -#define RADIOLIB_LORAWAN_MHDR_MTYPE_CONF_DATA_DOWN (0x05 << 5) // 7 5 confirmed data down -#define RADIOLIB_LORAWAN_MHDR_MTYPE_PROPRIETARY (0x07 << 5) // 7 5 proprietary -#define RADIOLIB_LORAWAN_MHDR_MTYPE_MASK (0x07 << 5) // 7 5 bitmask of all possible options -#define RADIOLIB_LORAWAN_MHDR_MAJOR_R1 (0x00 << 0) // 1 0 major version: LoRaWAN R1 +#define RADIOLIB_LW_MHDR_MTYPE_JOIN_REQUEST (0x00 << 5) // 7 5 message type: join request +#define RADIOLIB_LW_MHDR_MTYPE_JOIN_ACCEPT (0x01 << 5) // 7 5 join accept +#define RADIOLIB_LW_MHDR_MTYPE_UNCONF_DATA_UP (0x02 << 5) // 7 5 unconfirmed data up +#define RADIOLIB_LW_MHDR_MTYPE_UNCONF_DATA_DOWN (0x03 << 5) // 7 5 unconfirmed data down +#define RADIOLIB_LW_MHDR_MTYPE_CONF_DATA_UP (0x04 << 5) // 7 5 confirmed data up +#define RADIOLIB_LW_MHDR_MTYPE_CONF_DATA_DOWN (0x05 << 5) // 7 5 confirmed data down +#define RADIOLIB_LW_MHDR_MTYPE_PROPRIETARY (0x07 << 5) // 7 5 proprietary +#define RADIOLIB_LW_MHDR_MTYPE_MASK (0x07 << 5) // 7 5 bitmask of all possible options +#define RADIOLIB_LW_MHDR_MAJOR_R1 (0x00 << 0) // 1 0 major version: LoRaWAN R1 // frame control field encoding -#define RADIOLIB_LORAWAN_FCTRL_ADR_ENABLED (0x01 << 7) // 7 7 adaptive data rate: enabled -#define RADIOLIB_LORAWAN_FCTRL_ADR_DISABLED (0x00 << 7) // 7 7 disabled -#define RADIOLIB_LORAWAN_FCTRL_ADR_ACK_REQ (0x01 << 6) // 6 6 adaptive data rate ACK request -#define RADIOLIB_LORAWAN_FCTRL_ACK (0x01 << 5) // 5 5 confirmed message acknowledge -#define RADIOLIB_LORAWAN_FCTRL_FRAME_PENDING (0x01 << 4) // 4 4 downlink frame is pending +#define RADIOLIB_LW_FCTRL_ADR_ENABLED (0x01 << 7) // 7 7 adaptive data rate: enabled +#define RADIOLIB_LW_FCTRL_ADR_DISABLED (0x00 << 7) // 7 7 disabled +#define RADIOLIB_LW_FCTRL_ADR_ACK_REQ (0x01 << 6) // 6 6 adaptive data rate ACK request +#define RADIOLIB_LW_FCTRL_ACK (0x01 << 5) // 5 5 confirmed message acknowledge +#define RADIOLIB_LW_FCTRL_FRAME_PENDING (0x01 << 4) // 4 4 downlink frame is pending -// port field -#define RADIOLIB_LORAWAN_FPORT_MAC_COMMAND (0x00 << 0) // 7 0 payload contains MAC commands only -#define RADIOLIB_LORAWAN_FPORT_RESERVED (0xE0 << 0) // 7 0 reserved port values +// fPort field +#define RADIOLIB_LW_FPORT_MAC_COMMAND (0x00 << 0) // 7 0 payload contains MAC commands only +#define RADIOLIB_LW_FPORT_RESERVED (0xE0 << 0) // 7 0 reserved fPort values // MAC commands - only those sent from end-device to gateway -#define RADIOLIB_LORAWAN_LINK_CHECK_REQ (0x02 << 0) // 7 0 MAC command: request to check connectivity to network -#define RADIOLIB_LORAWAN_LINK_ADR_ANS (0x03 << 0) // 7 0 answer to ADR change -#define RADIOLIB_LORAWAN_DUTY_CYCLE_ANS (0x04 << 0) // 7 0 answer to duty cycle change -#define RADIOLIB_LORAWAN_RX_PARAM_SETUP_ANS (0x05 << 0) // 7 0 answer to reception slot setup request -#define RADIOLIB_LORAWAN_DEV_STATUS_ANS (0x06 << 0) // 7 0 device status information -#define RADIOLIB_LORAWAN_NEW_CHANNEL_ANS (0x07 << 0) // 7 0 acknowledges change of a radio channel -#define RADIOLIB_LORAWAN_RX_TIMING_SETUP_ANS (0x08 << 0) // 7 0 acknowledges change of a reception slots timing +#define RADIOLIB_LW_LINK_CHECK_REQ (0x02 << 0) // 7 0 MAC command: request to check connectivity to network +#define RADIOLIB_LW_LINK_ADR_ANS (0x03 << 0) // 7 0 answer to ADR change +#define RADIOLIB_LW_DUTY_CYCLE_ANS (0x04 << 0) // 7 0 answer to duty cycle change +#define RADIOLIB_LW_RX_PARAM_SETUP_ANS (0x05 << 0) // 7 0 answer to reception slot setup request +#define RADIOLIB_LW_DEV_STATUS_ANS (0x06 << 0) // 7 0 device status information +#define RADIOLIB_LW_NEW_CHANNEL_ANS (0x07 << 0) // 7 0 acknowledges change of a radio channel +#define RADIOLIB_LW_RX_TIMING_SETUP_ANS (0x08 << 0) // 7 0 acknowledges change of a reception slots timing -#define RADIOLIB_LORAWAN_NOPTS_LEN (8) +#define RADIOLIB_LW_NOPTS_LEN (8) // data rate encoding -#define RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K (0x01 << 7) // 7 7 FSK @ 50 kbps -#define RADIOLIB_LORAWAN_DATA_RATE_SF_12 (0x06 << 4) // 6 4 LoRa spreading factor: SF12 -#define RADIOLIB_LORAWAN_DATA_RATE_SF_11 (0x05 << 4) // 6 4 SF11 -#define RADIOLIB_LORAWAN_DATA_RATE_SF_10 (0x04 << 4) // 6 4 SF10 -#define RADIOLIB_LORAWAN_DATA_RATE_SF_9 (0x03 << 4) // 6 4 SF9 -#define RADIOLIB_LORAWAN_DATA_RATE_SF_8 (0x02 << 4) // 6 4 SF8 -#define RADIOLIB_LORAWAN_DATA_RATE_SF_7 (0x01 << 4) // 6 4 SF7 -#define RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ (0x00 << 2) // 3 2 LoRa bandwidth: 500 kHz -#define RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ (0x01 << 2) // 3 2 250 kHz -#define RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ (0x02 << 2) // 3 2 125 kHz -#define RADIOLIB_LORAWAN_DATA_RATE_BW_RESERVED (0x03 << 2) // 3 2 reserved value -#define RADIOLIB_LORAWAN_DATA_RATE_CR_4_5 (0x00 << 0) // 1 0 LoRa coding rate: 4/5 -#define RADIOLIB_LORAWAN_DATA_RATE_CR_4_6 (0x01 << 0) // 1 0 4/6 -#define RADIOLIB_LORAWAN_DATA_RATE_CR_4_7 (0x02 << 0) // 1 0 4/7 -#define RADIOLIB_LORAWAN_DATA_RATE_CR_4_8 (0x03 << 0) // 1 0 4/8 -#define RADIOLIB_LORAWAN_DATA_RATE_UNUSED (0xFF << 0) // 7 0 unused data rate +#define RADIOLIB_LW_DATA_RATE_FSK_50_K (0x01 << 7) // 7 7 FSK @ 50 kbps +#define RADIOLIB_LW_DATA_RATE_SF_12 (0x06 << 4) // 6 4 LoRa spreading factor: SF12 +#define RADIOLIB_LW_DATA_RATE_SF_11 (0x05 << 4) // 6 4 SF11 +#define RADIOLIB_LW_DATA_RATE_SF_10 (0x04 << 4) // 6 4 SF10 +#define RADIOLIB_LW_DATA_RATE_SF_9 (0x03 << 4) // 6 4 SF9 +#define RADIOLIB_LW_DATA_RATE_SF_8 (0x02 << 4) // 6 4 SF8 +#define RADIOLIB_LW_DATA_RATE_SF_7 (0x01 << 4) // 6 4 SF7 +#define RADIOLIB_LW_DATA_RATE_BW_500_KHZ (0x00 << 2) // 3 2 LoRa bandwidth: 500 kHz +#define RADIOLIB_LW_DATA_RATE_BW_250_KHZ (0x01 << 2) // 3 2 250 kHz +#define RADIOLIB_LW_DATA_RATE_BW_125_KHZ (0x02 << 2) // 3 2 125 kHz +#define RADIOLIB_LW_DATA_RATE_BW_RESERVED (0x03 << 2) // 3 2 reserved value +#define RADIOLIB_LW_DATA_RATE_CR_4_5 (0x00 << 0) // 1 0 LoRa coding rate: 4/5 +#define RADIOLIB_LW_DATA_RATE_CR_4_6 (0x01 << 0) // 1 0 4/6 +#define RADIOLIB_LW_DATA_RATE_CR_4_7 (0x02 << 0) // 1 0 4/7 +#define RADIOLIB_LW_DATA_RATE_CR_4_8 (0x03 << 0) // 1 0 4/8 +#define RADIOLIB_LW_DATA_RATE_UNUSED (0xFF << 0) // 7 0 unused data rate -#define RADIOLIB_LORAWAN_CHANNEL_DIR_UPLINK (0x00 << 0) -#define RADIOLIB_LORAWAN_CHANNEL_DIR_DOWNLINK (0x01 << 0) -#define RADIOLIB_LORAWAN_CHANNEL_DIR_BOTH (0x02 << 0) -#define RADIOLIB_LORAWAN_CHANNEL_DIR_NONE (0x03 << 0) -#define RADIOLIB_LORAWAN_BAND_DYNAMIC (0) -#define RADIOLIB_LORAWAN_BAND_FIXED (1) -#define RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES (15) -#define RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE (0xFF >> 0) +#define RADIOLIB_LW_CHANNEL_DIR_UPLINK (0x00 << 0) +#define RADIOLIB_LW_CHANNEL_DIR_DOWNLINK (0x01 << 0) +#define RADIOLIB_LW_CHANNEL_DIR_BOTH (0x02 << 0) +#define RADIOLIB_LW_CHANNEL_DIR_NONE (0x03 << 0) +#define RADIOLIB_LW_BAND_DYNAMIC (0) +#define RADIOLIB_LW_BAND_FIXED (1) +#define RADIOLIB_LW_CHANNEL_NUM_DATARATES (15) +#define RADIOLIB_LW_CHANNEL_INDEX_NONE (0xFF >> 0) // recommended default settings -#define RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS (1000) -#define RADIOLIB_LORAWAN_RECEIVE_DELAY_2_MS ((RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS) + 1000) -#define RADIOLIB_LORAWAN_RX1_DR_OFFSET (0) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_1_MS (5000) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DELAY_2_MS (6000) -#define RADIOLIB_LORAWAN_MAX_FCNT_GAP (16384) -#define RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP (0x06) -#define RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP (0x05) -#define RADIOLIB_LORAWAN_RETRANSMIT_TIMEOUT_MIN_MS (1000) -#define RADIOLIB_LORAWAN_RETRANSMIT_TIMEOUT_MAX_MS (3000) -#define RADIOLIB_LORAWAN_POWER_STEP_SIZE_DBM (-2) -#define RADIOLIB_LORAWAN_REJOIN_MAX_COUNT_N (10) // send rejoin request 16384 uplinks -#define RADIOLIB_LORAWAN_REJOIN_MAX_TIME_N (15) // once every year, not actually implemented +#define RADIOLIB_LW_RECEIVE_DELAY_1_MS (1000) +#define RADIOLIB_LW_RECEIVE_DELAY_2_MS ((RADIOLIB_LW_RECEIVE_DELAY_1_MS) + 1000) +#define RADIOLIB_LW_RX1_DR_OFFSET (0) +#define RADIOLIB_LW_JOIN_ACCEPT_DELAY_1_MS (5000) +#define RADIOLIB_LW_JOIN_ACCEPT_DELAY_2_MS (6000) +#define RADIOLIB_LW_MAX_FCNT_GAP (16384) +#define RADIOLIB_LW_ADR_ACK_LIMIT_EXP (0x06) +#define RADIOLIB_LW_ADR_ACK_DELAY_EXP (0x05) +#define RADIOLIB_LW_RETRANSMIT_TIMEOUT_MIN_MS (1000) +#define RADIOLIB_LW_RETRANSMIT_TIMEOUT_MAX_MS (3000) +#define RADIOLIB_LW_POWER_STEP_SIZE_DBM (-2) +#define RADIOLIB_LW_REJOIN_MAX_COUNT_N (10) // send rejoin request 16384 uplinks +#define RADIOLIB_LW_REJOIN_MAX_TIME_N (15) // once every year, not actually implemented // join request message layout -#define RADIOLIB_LORAWAN_JOIN_REQUEST_LEN (23) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_JOIN_EUI_POS (1) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_EUI_POS (9) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_DEV_NONCE_POS (17) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE (0xFF) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE_0 (0x00) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE_1 (0x01) -#define RADIOLIB_LORAWAN_JOIN_REQUEST_TYPE_2 (0x02) +#define RADIOLIB_LW_JOIN_REQUEST_LEN (23) +#define RADIOLIB_LW_JOIN_REQUEST_JOIN_EUI_POS (1) +#define RADIOLIB_LW_JOIN_REQUEST_DEV_EUI_POS (9) +#define RADIOLIB_LW_JOIN_REQUEST_DEV_NONCE_POS (17) +#define RADIOLIB_LW_JOIN_REQUEST_TYPE (0xFF) +#define RADIOLIB_LW_JOIN_REQUEST_TYPE_0 (0x00) +#define RADIOLIB_LW_JOIN_REQUEST_TYPE_1 (0x01) +#define RADIOLIB_LW_JOIN_REQUEST_TYPE_2 (0x02) // join accept message layout -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_MAX_LEN (33) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_NONCE_POS (1) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_HOME_NET_ID_POS (4) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_ADDR_POS (7) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_JOIN_EUI_POS (4) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DL_SETTINGS_POS (11) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_RX_DELAY_POS (12) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_DEV_NONCE_POS (12) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS (13) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN (16) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_TYPE_POS (RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_POS + RADIOLIB_LORAWAN_JOIN_ACCEPT_CFLIST_LEN - 1) +#define RADIOLIB_LW_JOIN_ACCEPT_MAX_LEN (33) +#define RADIOLIB_LW_JOIN_ACCEPT_JOIN_NONCE_POS (1) +#define RADIOLIB_LW_JOIN_ACCEPT_HOME_NET_ID_POS (4) +#define RADIOLIB_LW_JOIN_ACCEPT_DEV_ADDR_POS (7) +#define RADIOLIB_LW_JOIN_ACCEPT_JOIN_EUI_POS (4) +#define RADIOLIB_LW_JOIN_ACCEPT_DL_SETTINGS_POS (11) +#define RADIOLIB_LW_JOIN_ACCEPT_RX_DELAY_POS (12) +#define RADIOLIB_LW_JOIN_ACCEPT_DEV_NONCE_POS (12) +#define RADIOLIB_LW_JOIN_ACCEPT_CFLIST_POS (13) +#define RADIOLIB_LW_JOIN_ACCEPT_CFLIST_LEN (16) +#define RADIOLIB_LW_JOIN_ACCEPT_CFLIST_TYPE_POS (RADIOLIB_LW_JOIN_ACCEPT_CFLIST_POS + RADIOLIB_LW_JOIN_ACCEPT_CFLIST_LEN - 1) // join accept message variables -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_0 (0x00 << 7) // 7 7 LoRaWAN revision: 1.0 -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_R_1_1 (0x01 << 7) // 7 7 1.1 -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_F_NWK_S_INT_KEY (0x01) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_APP_S_KEY (0x02) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_S_NWK_S_INT_KEY (0x03) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_NWK_S_ENC_KEY (0x04) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_ENC_KEY (0x05) -#define RADIOLIB_LORAWAN_JOIN_ACCEPT_JS_INT_KEY (0x06) +#define RADIOLIB_LW_JOIN_ACCEPT_R_1_0 (0x00 << 7) // 7 7 LoRaWAN revision: 1.0 +#define RADIOLIB_LW_JOIN_ACCEPT_R_1_1 (0x01 << 7) // 7 7 1.1 +#define RADIOLIB_LW_JOIN_ACCEPT_F_NWK_S_INT_KEY (0x01) +#define RADIOLIB_LW_JOIN_ACCEPT_APP_S_KEY (0x02) +#define RADIOLIB_LW_JOIN_ACCEPT_S_NWK_S_INT_KEY (0x03) +#define RADIOLIB_LW_JOIN_ACCEPT_NWK_S_ENC_KEY (0x04) +#define RADIOLIB_LW_JOIN_ACCEPT_JS_ENC_KEY (0x05) +#define RADIOLIB_LW_JOIN_ACCEPT_JS_INT_KEY (0x06) // frame header layout -#define RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS (16) -#define RADIOLIB_LORAWAN_FHDR_DEV_ADDR_POS (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 1) -#define RADIOLIB_LORAWAN_FHDR_FCTRL_POS (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 5) -#define RADIOLIB_LORAWAN_FHDR_FCNT_POS (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 6) -#define RADIOLIB_LORAWAN_FHDR_FOPTS_POS (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 8) -#define RADIOLIB_LORAWAN_FHDR_FOPTS_LEN_MASK (0x0F) -#define RADIOLIB_LORAWAN_FHDR_FOPTS_MAX_LEN (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 16) -#define RADIOLIB_LORAWAN_FHDR_FPORT_POS(FOPTS) (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 8 + (FOPTS)) -#define RADIOLIB_LORAWAN_FRAME_PAYLOAD_POS(FOPTS) (RADIOLIB_LORAWAN_FHDR_LEN_START_OFFS + 9 + (FOPTS)) -#define RADIOLIB_LORAWAN_FRAME_LEN(PAYLOAD, FOPTS) (16 + 13 + (PAYLOAD) + (FOPTS)) +#define RADIOLIB_LW_FHDR_LEN_START_OFFS (16) +#define RADIOLIB_LW_FHDR_DEV_ADDR_POS (RADIOLIB_LW_FHDR_LEN_START_OFFS + 1) +#define RADIOLIB_LW_FHDR_FCTRL_POS (RADIOLIB_LW_FHDR_LEN_START_OFFS + 5) +#define RADIOLIB_LW_FHDR_FCNT_POS (RADIOLIB_LW_FHDR_LEN_START_OFFS + 6) +#define RADIOLIB_LW_FHDR_FOPTS_POS (RADIOLIB_LW_FHDR_LEN_START_OFFS + 8) +#define RADIOLIB_LW_FHDR_FOPTS_LEN_MASK (0x0F) +#define RADIOLIB_LW_FHDR_FOPTS_MAX_LEN (15) +#define RADIOLIB_LW_FHDR_FPORT_POS(FOPTS) (RADIOLIB_LW_FHDR_LEN_START_OFFS + 8 + (FOPTS)) +#define RADIOLIB_LW_FRAME_PAYLOAD_POS(FOPTS) (RADIOLIB_LW_FHDR_LEN_START_OFFS + 9 + (FOPTS)) +#define RADIOLIB_LW_FRAME_LEN(PAYLOAD, FOPTS) (16 + 13 + (PAYLOAD) + (FOPTS)) // payload encryption/MIC blocks common layout -#define RADIOLIB_LORAWAN_BLOCK_MAGIC_POS (0) -#define RADIOLIB_LORAWAN_BLOCK_CONF_FCNT_POS (1) -#define RADIOLIB_LORAWAN_BLOCK_DIR_POS (5) -#define RADIOLIB_LORAWAN_BLOCK_DEV_ADDR_POS (6) -#define RADIOLIB_LORAWAN_BLOCK_FCNT_POS (10) +#define RADIOLIB_LW_BLOCK_MAGIC_POS (0) +#define RADIOLIB_LW_BLOCK_CONF_FCNT_POS (1) +#define RADIOLIB_LW_BLOCK_DIR_POS (5) +#define RADIOLIB_LW_BLOCK_DEV_ADDR_POS (6) +#define RADIOLIB_LW_BLOCK_FCNT_POS (10) // payload encryption block layout -#define RADIOLIB_LORAWAN_ENC_BLOCK_MAGIC (0x01) -#define RADIOLIB_LORAWAN_ENC_BLOCK_COUNTER_ID_POS (4) -#define RADIOLIB_LORAWAN_ENC_BLOCK_COUNTER_POS (15) +#define RADIOLIB_LW_ENC_BLOCK_MAGIC (0x01) +#define RADIOLIB_LW_ENC_BLOCK_COUNTER_ID_POS (4) +#define RADIOLIB_LW_ENC_BLOCK_COUNTER_POS (15) // payload MIC blocks layout -#define RADIOLIB_LORAWAN_MIC_BLOCK_MAGIC (0x49) -#define RADIOLIB_LORAWAN_MIC_BLOCK_LEN_POS (15) -#define RADIOLIB_LORAWAN_MIC_DATA_RATE_POS (3) -#define RADIOLIB_LORAWAN_MIC_CH_INDEX_POS (4) - -// MAC commands -#define RADIOLIB_LORAWAN_NUM_MAC_COMMANDS (16) - -#define RADIOLIB_LORAWAN_MAC_RESET (0x01) -#define RADIOLIB_LORAWAN_MAC_LINK_CHECK (0x02) -#define RADIOLIB_LORAWAN_MAC_LINK_ADR (0x03) -#define RADIOLIB_LORAWAN_MAC_DUTY_CYCLE (0x04) -#define RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP (0x05) -#define RADIOLIB_LORAWAN_MAC_DEV_STATUS (0x06) -#define RADIOLIB_LORAWAN_MAC_NEW_CHANNEL (0x07) -#define RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP (0x08) -#define RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP (0x09) -#define RADIOLIB_LORAWAN_MAC_DL_CHANNEL (0x0A) -#define RADIOLIB_LORAWAN_MAC_REKEY (0x0B) -#define RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP (0x0C) -#define RADIOLIB_LORAWAN_MAC_DEVICE_TIME (0x0D) -#define RADIOLIB_LORAWAN_MAC_FORCE_REJOIN (0x0E) -#define RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP (0x0F) -#define RADIOLIB_LORAWAN_MAC_PROPRIETARY (0x80) +#define RADIOLIB_LW_MIC_BLOCK_MAGIC (0x49) +#define RADIOLIB_LW_MIC_BLOCK_LEN_POS (15) +#define RADIOLIB_LW_MIC_DATA_RATE_POS (3) +#define RADIOLIB_LW_MIC_CH_INDEX_POS (4) // maximum allowed dwell time on bands that implement dwell time limitations -#define RADIOLIB_LORAWAN_DWELL_TIME (400) - -// unused LoRaWAN version -#define RADIOLIB_LORAWAN_VERSION_NONE (0xFF) +#define RADIOLIB_LW_DWELL_TIME (400) // unused frame counter value -#define RADIOLIB_LORAWAN_FCNT_NONE (0xFFFFFFFF) +#define RADIOLIB_LW_FCNT_NONE (0xFFFFFFFF) + +// MAC commands +#define RADIOLIB_LW_NUM_MAC_COMMANDS (16) + +#define RADIOLIB_LW_MAC_RESET (0x01) +#define RADIOLIB_LW_MAC_LINK_CHECK (0x02) +#define RADIOLIB_LW_MAC_LINK_ADR (0x03) +#define RADIOLIB_LW_MAC_DUTY_CYCLE (0x04) +#define RADIOLIB_LW_MAC_RX_PARAM_SETUP (0x05) +#define RADIOLIB_LW_MAC_DEV_STATUS (0x06) +#define RADIOLIB_LW_MAC_NEW_CHANNEL (0x07) +#define RADIOLIB_LW_MAC_RX_TIMING_SETUP (0x08) +#define RADIOLIB_LW_MAC_TX_PARAM_SETUP (0x09) +#define RADIOLIB_LW_MAC_DL_CHANNEL (0x0A) +#define RADIOLIB_LW_MAC_REKEY (0x0B) +#define RADIOLIB_LW_MAC_ADR_PARAM_SETUP (0x0C) +#define RADIOLIB_LW_MAC_DEVICE_TIME (0x0D) +#define RADIOLIB_LW_MAC_FORCE_REJOIN (0x0E) +#define RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP (0x0F) +#define RADIOLIB_LW_MAC_PROPRIETARY (0x80) // the length of internal MAC command queue - hopefully this is enough for most use cases -#define RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE (9) +#define RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE (9) // the maximum number of simultaneously available channels -#define RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS (16) +#define RADIOLIB_LW_NUM_AVAILABLE_CHANNELS (16) // maximum MAC command sizes -#define RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_DOWN (5) -#define RADIOLIB_LORAWAN_MAX_MAC_COMMAND_LEN_UP (2) -#define RADIOLIB_LORAWAN_MAX_NUM_ADR_COMMANDS (8) +#define RADIOLIB_LW_MAX_MAC_COMMAND_LEN_DOWN (5) +#define RADIOLIB_LW_MAX_MAC_COMMAND_LEN_UP (2) +#define RADIOLIB_LW_MAX_NUM_ADR_COMMANDS (8) +/*! + \struct LoRaWANMacSpec_t + \brief MAC command specification structure. +*/ struct LoRaWANMacSpec_t { + /*! \brief Command ID */ const uint8_t cid; + + /*! \brief Uplink message length */ const uint8_t lenDn; + + /*! \brief Downlink message length */ const uint8_t lenUp; - const bool user; // whether this MAC command can be issued by a user or not + + /*! \brief Whether this MAC command can be issued by the user or not */ + const bool user; }; -const LoRaWANMacSpec_t MacTable[RADIOLIB_LORAWAN_NUM_MAC_COMMANDS + 1] = { +constexpr LoRaWANMacSpec_t MacTable[RADIOLIB_LW_NUM_MAC_COMMANDS + 1] = { { 0x00, 0, 0, false }, // not an actual MAC command, exists for index offsetting - { RADIOLIB_LORAWAN_MAC_RESET, 1, 1, false }, - { RADIOLIB_LORAWAN_MAC_LINK_CHECK, 2, 0, true }, - { RADIOLIB_LORAWAN_MAC_LINK_ADR, 4, 1, false }, - { RADIOLIB_LORAWAN_MAC_DUTY_CYCLE, 1, 0, false }, - { RADIOLIB_LORAWAN_MAC_RX_PARAM_SETUP, 4, 1, false }, - { RADIOLIB_LORAWAN_MAC_DEV_STATUS, 0, 2, false }, - { RADIOLIB_LORAWAN_MAC_NEW_CHANNEL, 5, 1, false }, - { RADIOLIB_LORAWAN_MAC_RX_TIMING_SETUP, 1, 0, false }, - { RADIOLIB_LORAWAN_MAC_TX_PARAM_SETUP, 1, 0, false }, - { RADIOLIB_LORAWAN_MAC_DL_CHANNEL, 4, 1, false }, - { RADIOLIB_LORAWAN_MAC_REKEY, 1, 1, false }, - { RADIOLIB_LORAWAN_MAC_ADR_PARAM_SETUP, 1, 0, false }, - { RADIOLIB_LORAWAN_MAC_DEVICE_TIME, 5, 0, true }, - { RADIOLIB_LORAWAN_MAC_FORCE_REJOIN, 2, 0, false }, - { RADIOLIB_LORAWAN_MAC_REJOIN_PARAM_SETUP, 1, 1, false }, - { RADIOLIB_LORAWAN_MAC_PROPRIETARY, 5, 0, true } -}; - -#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 + { RADIOLIB_LW_MAC_RESET, 1, 1, false }, + { RADIOLIB_LW_MAC_LINK_CHECK, 2, 0, true }, + { RADIOLIB_LW_MAC_LINK_ADR, 4, 1, false }, + { RADIOLIB_LW_MAC_DUTY_CYCLE, 1, 0, false }, + { RADIOLIB_LW_MAC_RX_PARAM_SETUP, 4, 1, false }, + { RADIOLIB_LW_MAC_DEV_STATUS, 0, 2, false }, + { RADIOLIB_LW_MAC_NEW_CHANNEL, 5, 1, false }, + { RADIOLIB_LW_MAC_RX_TIMING_SETUP, 1, 0, false }, + { RADIOLIB_LW_MAC_TX_PARAM_SETUP, 1, 0, false }, + { RADIOLIB_LW_MAC_DL_CHANNEL, 4, 1, false }, + { RADIOLIB_LW_MAC_REKEY, 1, 1, false }, + { RADIOLIB_LW_MAC_ADR_PARAM_SETUP, 1, 0, false }, + { RADIOLIB_LW_MAC_DEVICE_TIME, 5, 0, true }, + { RADIOLIB_LW_MAC_FORCE_REJOIN, 2, 0, false }, + { RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP, 1, 1, false }, + { RADIOLIB_LW_MAC_PROPRIETARY, 5, 0, true } }; /*! - \struct LoRaWANChannelSpan_t + \struct LoRaWANMacCommand_t + \brief Structure to save information about MAC command +*/ +struct LoRaWANMacCommand_t { + /*! \brief The command ID */ + uint8_t cid; + + /*! \brief Payload buffer (5 bytes is the longest possible) */ + uint8_t payload[5]; + + /*! \brief Length of the payload */ + uint8_t len; + + /*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */ + uint8_t repeat; +}; + +/*! + \struct LoRaWANMacCommandQueue_t + \brief Structure to hold information about a queue of MAC commands +*/ +struct LoRaWANMacCommandQueue_t { + /*! \brief Number of commands in the queue */ + uint8_t numCommands; + + /*! \brief Total length of the queue */ + uint8_t len; + + /*! \brief MAC command buffer */ + LoRaWANMacCommand_t commands[RADIOLIB_LW_MAC_COMMAND_QUEUE_SIZE]; +}; + +#define RADIOLIB_LW_NONCES_VERSION_VAL (0x0001) + +enum LoRaWANSchemeBase_t { + RADIOLIB_LW_NONCES_START = 0x00, + RADIOLIB_LW_NONCES_VERSION = RADIOLIB_LW_NONCES_START, // 2 bytes + RADIOLIB_LW_NONCES_MODE = RADIOLIB_LW_NONCES_VERSION + sizeof(uint16_t), // 2 bytes + RADIOLIB_LW_NONCES_CLASS = RADIOLIB_LW_NONCES_MODE + sizeof(uint16_t), // 1 byte + RADIOLIB_LW_NONCES_PLAN = RADIOLIB_LW_NONCES_CLASS + sizeof(uint8_t), // 1 byte + RADIOLIB_LW_NONCES_CHECKSUM = RADIOLIB_LW_NONCES_PLAN + sizeof(uint8_t), // 2 bytes + RADIOLIB_LW_NONCES_DEV_NONCE = RADIOLIB_LW_NONCES_CHECKSUM + sizeof(uint16_t), // 2 bytes + RADIOLIB_LW_NONCES_JOIN_NONCE = RADIOLIB_LW_NONCES_DEV_NONCE + sizeof(uint16_t), // 3 bytes + RADIOLIB_LW_NONCES_ACTIVE = RADIOLIB_LW_NONCES_JOIN_NONCE + 3, // 1 byte + RADIOLIB_LW_NONCES_SIGNATURE = RADIOLIB_LW_NONCES_ACTIVE + sizeof(uint8_t), // 2 bytes + RADIOLIB_LW_NONCES_BUF_SIZE = RADIOLIB_LW_NONCES_SIGNATURE + sizeof(uint16_t) // Nonces buffer size +}; + +enum LoRaWANSchemeSession_t { + RADIOLIB_LW_SESSION_START = 0x00, + RADIOLIB_LW_SESSION_NWK_SENC_KEY = RADIOLIB_LW_SESSION_START, // 16 bytes + RADIOLIB_LW_SESSION_APP_SKEY = RADIOLIB_LW_SESSION_NWK_SENC_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes + RADIOLIB_LW_SESSION_FNWK_SINT_KEY = RADIOLIB_LW_SESSION_APP_SKEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes + RADIOLIB_LW_SESSION_SNWK_SINT_KEY = RADIOLIB_LW_SESSION_FNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 16 bytes + RADIOLIB_LW_SESSION_DEV_ADDR = RADIOLIB_LW_SESSION_SNWK_SINT_KEY + RADIOLIB_AES128_BLOCK_SIZE, // 4 bytes + RADIOLIB_LW_SESSION_NONCES_SIGNATURE = RADIOLIB_LW_SESSION_DEV_ADDR + sizeof(uint32_t), // 2 bytes + RADIOLIB_LW_SESSION_A_FCNT_DOWN = RADIOLIB_LW_SESSION_NONCES_SIGNATURE + sizeof(uint16_t), // 4 bytes + RADIOLIB_LW_SESSION_CONF_FCNT_UP = RADIOLIB_LW_SESSION_A_FCNT_DOWN + sizeof(uint32_t), // 4 bytes + RADIOLIB_LW_SESSION_CONF_FCNT_DOWN = RADIOLIB_LW_SESSION_CONF_FCNT_UP + sizeof(uint32_t), // 4 bytes + RADIOLIB_LW_SESSION_RJ_COUNT0 = RADIOLIB_LW_SESSION_CONF_FCNT_DOWN + sizeof(uint32_t), // 2 bytes + RADIOLIB_LW_SESSION_RJ_COUNT1 = RADIOLIB_LW_SESSION_RJ_COUNT0 + sizeof(uint16_t), // 2 bytes + RADIOLIB_LW_SESSION_HOMENET_ID = RADIOLIB_LW_SESSION_RJ_COUNT1 + sizeof(uint16_t), // 4 bytes + RADIOLIB_LW_SESSION_VERSION = RADIOLIB_LW_SESSION_HOMENET_ID + sizeof(uint32_t), // 1 byte + RADIOLIB_LW_SESSION_DUTY_CYCLE = RADIOLIB_LW_SESSION_VERSION + sizeof(uint8_t), // 1 byte + RADIOLIB_LW_SESSION_RX_PARAM_SETUP = RADIOLIB_LW_SESSION_DUTY_CYCLE + MacTable[RADIOLIB_LW_MAC_DUTY_CYCLE].lenDn, // 4 bytes + RADIOLIB_LW_SESSION_RX_TIMING_SETUP = RADIOLIB_LW_SESSION_RX_PARAM_SETUP + MacTable[RADIOLIB_LW_MAC_RX_PARAM_SETUP].lenDn, // 1 byte + RADIOLIB_LW_SESSION_TX_PARAM_SETUP = RADIOLIB_LW_SESSION_RX_TIMING_SETUP + MacTable[RADIOLIB_LW_MAC_RX_TIMING_SETUP].lenDn, // 1 byte + RADIOLIB_LW_SESSION_ADR_PARAM_SETUP = RADIOLIB_LW_SESSION_TX_PARAM_SETUP + MacTable[RADIOLIB_LW_MAC_TX_PARAM_SETUP].lenDn, // 1 byte + RADIOLIB_LW_SESSION_REJOIN_PARAM_SETUP = RADIOLIB_LW_SESSION_ADR_PARAM_SETUP + MacTable[RADIOLIB_LW_MAC_ADR_PARAM_SETUP].lenDn, // 1 byte + RADIOLIB_LW_SESSION_BEACON_FREQ = RADIOLIB_LW_SESSION_REJOIN_PARAM_SETUP + MacTable[RADIOLIB_LW_MAC_REJOIN_PARAM_SETUP].lenDn, // 3 bytes + RADIOLIB_LW_SESSION_PING_SLOT_CHANNEL = RADIOLIB_LW_SESSION_BEACON_FREQ + 3, // 4 bytes + RADIOLIB_LW_SESSION_PERIODICITY = RADIOLIB_LW_SESSION_PING_SLOT_CHANNEL + 4, // 1 byte + RADIOLIB_LW_SESSION_LAST_TIME = RADIOLIB_LW_SESSION_PERIODICITY + 1, // 4 bytes + RADIOLIB_LW_SESSION_UL_CHANNELS = RADIOLIB_LW_SESSION_LAST_TIME + 4, // 16*5 bytes + RADIOLIB_LW_SESSION_DL_CHANNELS = RADIOLIB_LW_SESSION_UL_CHANNELS + 16*MacTable[RADIOLIB_LW_MAC_NEW_CHANNEL].lenDn, // 16*4 bytes + RADIOLIB_LW_SESSION_MAC_QUEUE_UL = RADIOLIB_LW_SESSION_DL_CHANNELS + 16*MacTable[RADIOLIB_LW_MAC_DL_CHANNEL].lenDn, // 9*8+2 bytes + RADIOLIB_LW_SESSION_N_FCNT_DOWN = RADIOLIB_LW_SESSION_MAC_QUEUE_UL + sizeof(LoRaWANMacCommandQueue_t), // 4 bytes + RADIOLIB_LW_SESSION_ADR_FCNT = RADIOLIB_LW_SESSION_N_FCNT_DOWN + sizeof(uint32_t), // 4 bytes + RADIOLIB_LW_SESSION_LINK_ADR = RADIOLIB_LW_SESSION_ADR_FCNT + sizeof(uint32_t), // 4 bytes + RADIOLIB_LW_SESSION_FCNT_UP = RADIOLIB_LW_SESSION_LINK_ADR + MacTable[RADIOLIB_LW_MAC_LINK_ADR].lenDn, // 4 bytes + RADIOLIB_LW_SESSION_SIGNATURE = RADIOLIB_LW_SESSION_FCNT_UP + sizeof(uint32_t), // 2 bytes + RADIOLIB_LW_SESSION_BUF_SIZE = RADIOLIB_LW_SESSION_SIGNATURE + sizeof(uint16_t) // Session buffer size +}; + +/*! + \struct LoRaWANChannel_t \brief Structure to save information about LoRaWAN channels. To save space, adjacent channels are saved in "spans". */ @@ -299,7 +342,7 @@ struct LoRaWANChannel_t { }; // alias for unused channel -#define RADIOLIB_LORAWAN_CHANNEL_NONE { .enabled = false, .idx = RADIOLIB_LORAWAN_CHANNEL_INDEX_NONE, .freq = 0, .drMin = 0, .drMax = 0 } +#define RADIOLIB_LW_CHANNEL_NONE { .enabled = false, .idx = RADIOLIB_LW_CHANNEL_INDEX_NONE, .freq = 0, .drMin = 0, .drMax = 0 } /*! \struct LoRaWANChannelSpan_t @@ -327,7 +370,7 @@ struct LoRaWANChannelSpan_t { }; // alias for unused channel span -#define RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE { .numChannels = 0, .freqStart = 0, .freqStep = 0, .drMin = 0, .drMax = 0, .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED } +#define RADIOLIB_LW_CHANNEL_SPAN_NONE { .numChannels = 0, .freqStart = 0, .freqStep = 0, .drMin = 0, .drMax = 0, .joinRequestDataRate = RADIOLIB_LW_DATA_RATE_UNUSED } /*! \struct LoRaWANBand_t @@ -341,7 +384,7 @@ struct LoRaWANBand_t { uint8_t bandType; /*! \brief Array of allowed maximum payload lengths for each data rate */ - uint8_t payloadLenMax[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES]; + uint8_t payloadLenMax[RADIOLIB_LW_CHANNEL_NUM_DATARATES]; /*! \brief Maximum allowed output power in this band in dBm */ int8_t powerMax; @@ -350,11 +393,13 @@ struct LoRaWANBand_t { int8_t powerNumSteps; /*! \brief Number of milliseconds per hour of allowed Time-on-Air */ - uint32_t dutyCycle; + RadioLibTime_t dutyCycle; - /*! \brief Maximum dwell time per message in milliseconds */ - uint32_t dwellTimeUp; - uint32_t dwellTimeDn; + /*! \brief Maximum dwell time per uplink message in milliseconds */ + RadioLibTime_t dwellTimeUp; + + /*! \brief Maximum dwell time per downlink message in milliseconds */ + RadioLibTime_t dwellTimeDn; /*! \brief A set of default uplink (TX) channels for frequency-type bands */ LoRaWANChannel_t txFreqs[3]; @@ -378,7 +423,7 @@ struct LoRaWANBand_t { LoRaWANChannel_t rx2; /*! \brief The corresponding datarates, bandwidths and coding rates for DR index */ - uint8_t dataRates[RADIOLIB_LORAWAN_CHANNEL_NUM_DATARATES]; + uint8_t dataRates[RADIOLIB_LW_CHANNEL_NUM_DATARATES]; }; // supported bands @@ -410,41 +455,17 @@ enum LoRaWANBandNum_t { }; // provide easy access to the number of currently supported bands -#define RADIOLIB_LORAWAN_NUM_SUPPORTED_BANDS (BandLast - BandEU868) +#define RADIOLIB_LW_NUM_SUPPORTED_BANDS (BandLast - BandEU868) // array of currently supported bands extern const LoRaWANBand_t* LoRaWANBands[]; -/*! - \struct LoRaWANMacCommand_t - \brief Structure to save information about MAC command -*/ -struct LoRaWANMacCommand_t { - /*! \brief The command ID */ - uint8_t cid; - - /*! \brief Payload buffer (5 bytes is the longest possible) */ - uint8_t payload[5]; - - /*! \brief Length of the payload */ - uint8_t len; - - /*! \brief Repetition counter (the command will be uplinked repeat + 1 times) */ - uint8_t repeat; -}; - -struct LoRaWANMacCommandQueue_t { - uint8_t numCommands; - uint8_t len; - LoRaWANMacCommand_t commands[RADIOLIB_LORAWAN_MAC_COMMAND_QUEUE_SIZE]; -}; - /*! \struct LoRaWANEvent_t \brief Structure to save extra information about uplink/downlink event. */ struct LoRaWANEvent_t { - /*! \brief Event direction, one of RADIOLIB_LORAWAN_CHANNEL_DIR_* */ + /*! \brief Event direction, one of RADIOLIB_LW_CHANNEL_DIR_* */ uint8_t dir; /*! \brief Whether the event is confirmed or not (e.g., confirmed uplink sent by user application) */ @@ -464,10 +485,10 @@ struct LoRaWANEvent_t { int16_t power; /*! \brief The appropriate frame counter - for different events, different frame counters will be reported! */ - uint32_t fcnt; + uint32_t fCnt; /*! \brief Port number */ - uint8_t port; + uint8_t fPort; }; /*! @@ -477,10 +498,10 @@ struct LoRaWANEvent_t { class LoRaWANNode { public: - // Offset between TX and RX1 (such that RX1 has equal or lower DR) + /*! \brief Offset between TX and RX1 (such that RX1 has equal or lower DR) */ uint8_t rx1DrOffset = 0; - // RX2 channel properties - may be changed by MAC command + /*! \brief RX2 channel properties - may be changed by MAC command */ LoRaWANChannel_t rx2; /*! @@ -499,7 +520,7 @@ class LoRaWANNode { /*! \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 + \returns Pointer to uint8_t array of size RADIOLIB_LW_NONCES_BUF_SIZE */ uint8_t* getBufferNonces(); @@ -512,7 +533,7 @@ class LoRaWANNode { /*! \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 + \returns Pointer to uint8_t array of size RADIOLIB_LW_SESSION_BUF_SIZE */ uint8_t* getBufferSession(); @@ -540,21 +561,22 @@ class LoRaWANNode { \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, bool force = false, uint8_t joinDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED); + int16_t beginOTAA(uint64_t joinEUI, uint64_t devEUI, uint8_t* nwkKey, uint8_t* appKey, bool force = false, uint8_t joinDr = RADIOLIB_LW_DATA_RATE_UNUSED); /*! \brief Join network by performing activation by personalization. In this procedure, all necessary configuration must be provided by the user. \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 fNwkSIntKey Pointer to the Forwarding network session (LoRaWAN 1.1), NULL for LoRaWAN 1.0. + \param sNwkSIntKey Pointer to the Serving network session (LoRaWAN 1.1), NULL for LoRaWAN 1.0. + \param nwkSEncKey Pointer to the MAC command network session key [NwkSEncKey] (LoRaWAN 1.1) + or network session AES-128 key [NwkSKey] (LoRaWAN 1.0). \param appSKey Pointer to the application session AES-128 key. - \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, uint8_t initialDr = RADIOLIB_LORAWAN_DATA_RATE_UNUSED); + int16_t beginABP(uint32_t addr, uint8_t* fNwkSIntKey, uint8_t* sNwkSIntKey, uint8_t* nwkSEncKey, uint8_t* appSKey, bool force = false, uint8_t initialDr = RADIOLIB_LW_DATA_RATE_UNUSED); /*! \brief Whether there is an ongoing session active */ bool isJoined(); @@ -570,52 +592,52 @@ class LoRaWANNode { Only LinkCheck and DeviceTime are available to the user. Other commands are ignored; duplicate MAC commands are discarded. \param cid ID of the MAC command - \returns Whether or not the MAC command was added to the queue. + \returns \ref status_codes */ - bool sendMacCommandReq(uint8_t cid); + int16_t sendMacCommandReq(uint8_t cid); #if defined(RADIOLIB_BUILD_ARDUINO) /*! \brief Send a message to the server. \param str Address of Arduino String that will be transmitted. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t uplink(String& str, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); + int16_t uplink(String& str, uint8_t fPort, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); #endif /*! \brief Send a message to the server. \param str C-string that will be transmitted. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t uplink(const char* str, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); + int16_t uplink(const char* str, uint8_t fPort, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); /*! \brief Send a message to the server. \param data Data to send. \param len Length of the data. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t uplink(uint8_t* data, size_t len, uint8_t port, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); + int16_t uplink(uint8_t* data, size_t len, uint8_t fPort, bool isConfirmed = false, LoRaWANEvent_t* event = NULL); #if defined(RADIOLIB_BUILD_ARDUINO) /*! \brief Wait for downlink from the server in either RX1 or RX2 window. \param str Address of Arduino String to save the received data. \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ int16_t downlink(String& str, LoRaWANEvent_t* event = NULL); @@ -626,7 +648,7 @@ class LoRaWANNode { \param data Buffer to save received data into. \param len Pointer to variable that will be used to save the number of received bytes. \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ int16_t downlink(uint8_t* data, size_t* len, LoRaWANEvent_t* event = NULL); @@ -634,7 +656,7 @@ class LoRaWANNode { /*! \brief Wait for downlink, simplified to allow for simpler sendReceive \param event Pointer to a structure to store extra information about the event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ int16_t downlink(LoRaWANEvent_t* event = NULL); @@ -643,62 +665,62 @@ class LoRaWANNode { /*! \brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window. \param strUp Address of Arduino String that will be transmitted. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param strDown Address of Arduino String to save the received data. \param isConfirmed Whether to send a confirmed uplink or not. \param eventUp Pointer to a structure to store extra information about the uplink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \param eventDown Pointer to a structure to store extra information about the downlink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t sendReceive(String& strUp, uint8_t port, String& strDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); + int16_t sendReceive(String& strUp, uint8_t fPort, String& strDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); #endif /*! \brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window. \param strUp C-string that will be transmitted. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param dataDown Buffer to save received data into. \param lenDown Pointer to variable that will be used to save the number of received bytes. \param isConfirmed Whether to send a confirmed uplink or not. \param eventUp Pointer to a structure to store extra information about the uplink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \param eventDown Pointer to a structure to store extra information about the downlink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t sendReceive(const char* strUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); + int16_t sendReceive(const char* strUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); /*! \brief Send a message to the server and wait for a downlink during Rx1 and/or Rx2 window. \param dataUp Data to send. \param lenUp Length of the data. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param dataDown Buffer to save received data into. \param lenDown Pointer to variable that will be used to save the number of received bytes. \param isConfirmed Whether to send a confirmed uplink or not. \param eventUp Pointer to a structure to store extra information about the uplink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \param eventDown Pointer to a structure to store extra information about the downlink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); + int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort, uint8_t* dataDown, size_t* lenDown, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); /*! \brief Send a message to the server and wait for a downlink but don't bother the user with downlink contents \param dataUp Data to send. \param lenUp Length of the data. - \param port Port number to send the message to. + \param fPort Port number to send the message to. \param isConfirmed Whether to send a confirmed uplink or not. \param eventUp Pointer to a structure to store extra information about the uplink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \param eventDown Pointer to a structure to store extra information about the downlink event - (port, frame counter, etc.). If set to NULL, no extra information will be passed to the user. + (fPort, frame counter, etc.). If set to NULL, no extra information will be passed to the user. \returns \ref status_codes */ - int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t port = 1, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); + int16_t sendReceive(uint8_t* dataUp, size_t lenUp, uint8_t fPort = 1, bool isConfirmed = false, LoRaWANEvent_t* eventUp = NULL, LoRaWANEvent_t* eventDown = NULL); /*! \brief Set device status. @@ -711,30 +733,30 @@ class LoRaWANNode { \brief Returns the last uplink's frame counter; also 0 if no uplink occured yet. */ - uint32_t getFcntUp(); + uint32_t getFCntUp(); /*! \brief Returns the last network downlink's frame counter; also 0 if no network downlink occured yet. */ - uint32_t getNFcntDown(); + uint32_t getNFCntDown(); /*! \brief Returns the last application downlink's frame counter; also 0 if no application downlink occured yet. */ - uint32_t getAFcntDown(); + uint32_t getAFCntDown(); /*! \brief Reset the downlink frame counters (application and network) This is unsafe and can possibly allow replay attacks using downlinks. It mainly exists as part of the TS009 Specification Verification protocol. */ - void resetFcntDown(); + void resetFCntDown(); /*! \brief Set uplink datarate. This should not be used when ADR is enabled. - \param dr Datarate to use for uplinks. + \param drUp Datarate to use for uplinks. \returns \ref status_codes */ int16_t setDatarate(uint8_t drUp); @@ -751,7 +773,7 @@ class LoRaWANNode { \param msPerHour The maximum allowed Time-on-Air per hour in milliseconds (default 0 = maximum allowed for configured band). */ - void setDutyCycle(bool enable = true, uint32_t msPerHour = 0); + void setDutyCycle(bool enable = true, RadioLibTime_t msPerHour = 0); /*! \brief Calculate the minimum interval to adhere to a certain dutyCycle. @@ -760,18 +782,18 @@ class LoRaWANNode { \param airtime The airtime of the uplink. \returns Required interval (delay) in milliseconds between consecutive uplinks. */ - uint32_t dutyCycleInterval(uint32_t msPerHour, uint32_t airtime); + RadioLibTime_t dutyCycleInterval(RadioLibTime_t msPerHour, RadioLibTime_t airtime); /*! \brief Returns time in milliseconds until next uplink is available under dutyCycle limits */ - uint32_t timeUntilUplink(); + RadioLibTime_t timeUntilUplink(); /*! \brief Toggle adherence to dwellTime limits to on or off. \param enable Whether to adhere to dwellTime limits or not (default true). - \param msPerHour The maximum allowed Time-on-Air per uplink in milliseconds + \param msPerUplink The maximum allowed Time-on-Air per uplink in milliseconds (default 0 = maximum allowed for configured band). */ - void setDwellTime(bool enable, uint32_t msPerUplink = 0); + void setDwellTime(bool enable, RadioLibTime_t msPerUplink = 0); /*! \brief Returns the maximum payload given the currently present dwell time limits. @@ -787,14 +809,6 @@ class LoRaWANNode { */ int16_t setTxPower(int8_t txPower); - /*! - \brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance. - \param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO. - \param difsSlots Num of CADs to estimate a clear CH. - \param enableCSMA enable/disable CSMA for LoRaWAN. - */ - void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false); - /*! \brief Returns the quality of connectivity after requesting a LinkCheck MAC command. Returns 'true' if a network response was successfully parsed. @@ -822,6 +836,12 @@ class LoRaWANNode { */ uint64_t getDevAddr(); + /*! + \brief Get the Time-on-air of the last uplink message + \returns (RadioLibTime_t) time-on-air (ToA) of last uplink message + */ + RadioLibTime_t getLastToA(); + #if !RADIOLIB_GODMODE private: #endif @@ -833,10 +853,10 @@ class LoRaWANNode { 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 }; + uint8_t bufferNonces[RADIOLIB_LW_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 }; + uint8_t bufferSession[RADIOLIB_LW_SESSION_BUF_SIZE] = { 0 }; LoRaWANMacCommandQueue_t commandsUp = { .numCommands = 0, @@ -864,24 +884,21 @@ class LoRaWANNode { // session-specific parameters uint32_t homeNetId = 0; - uint8_t adrLimitExp = RADIOLIB_LORAWAN_ADR_ACK_LIMIT_EXP; - uint8_t adrDelayExp = RADIOLIB_LORAWAN_ADR_ACK_DELAY_EXP; + uint8_t adrLimitExp = RADIOLIB_LW_ADR_ACK_LIMIT_EXP; + uint8_t adrDelayExp = RADIOLIB_LW_ADR_ACK_DELAY_EXP; uint8_t nbTrans = 1; // Number of allowed frame retransmissions - uint8_t txPowerCur = 0; + uint8_t txPowerSteps = 0; uint8_t txPowerMax = 0; - uint32_t fcntUp = 0; - uint32_t aFcntDown = 0; - uint32_t nFcntDown = 0; - uint32_t confFcntUp = RADIOLIB_LORAWAN_FCNT_NONE; - uint32_t confFcntDown = RADIOLIB_LORAWAN_FCNT_NONE; - uint32_t adrFcnt = 0; + uint32_t fCntUp = 0; + uint32_t aFCntDown = 0; + uint32_t nFCntDown = 0; + uint32_t confFCntUp = RADIOLIB_LW_FCNT_NONE; + uint32_t confFCntDown = RADIOLIB_LW_FCNT_NONE; + uint32_t adrFCnt = 0; // whether the current configured channel is in FSK mode bool FSK = false; - // flag that shows whether the device is joined and there is an ongoing session (none, ABP or OTAA) - uint16_t activeMode = RADIOLIB_LORAWAN_MODE_NONE; - // ADR is enabled by default bool adrEnabled = true; @@ -907,28 +924,28 @@ class LoRaWANNode { uint8_t difsSlots; // available channel frequencies from list passed during OTA activation - LoRaWANChannel_t availableChannels[2][RADIOLIB_LORAWAN_NUM_AVAILABLE_CHANNELS]; + LoRaWANChannel_t availableChannels[2][RADIOLIB_LW_NUM_AVAILABLE_CHANNELS]; // currently configured channels for TX and RX1 - LoRaWANChannel_t currentChannels[2] = { RADIOLIB_LORAWAN_CHANNEL_NONE, RADIOLIB_LORAWAN_CHANNEL_NONE }; + LoRaWANChannel_t currentChannels[2] = { RADIOLIB_LW_CHANNEL_NONE, RADIOLIB_LW_CHANNEL_NONE }; // currently configured datarates for TX and RX1 - uint8_t dataRates[2] = { RADIOLIB_LORAWAN_DATA_RATE_UNUSED, RADIOLIB_LORAWAN_DATA_RATE_UNUSED }; + uint8_t dataRates[2] = { RADIOLIB_LW_DATA_RATE_UNUSED, RADIOLIB_LW_DATA_RATE_UNUSED }; // LoRaWAN revision (1.0 vs 1.1) uint8_t rev = 0; // Time on Air of last uplink - uint32_t lastToA = 0; + RadioLibTime_t lastToA = 0; // timestamp to measure the RX1/2 delay (from uplink end) - uint32_t rxDelayStart = 0; + RadioLibTime_t rxDelayStart = 0; // timestamp when the Rx1/2 windows were closed (timeout or uplink received) - uint32_t rxDelayEnd = 0; + RadioLibTime_t rxDelayEnd = 0; // delays between the uplink and RX1/2 windows - uint32_t rxDelays[2] = { RADIOLIB_LORAWAN_RECEIVE_DELAY_1_MS, RADIOLIB_LORAWAN_RECEIVE_DELAY_2_MS }; + RadioLibTime_t rxDelays[2] = { RADIOLIB_LW_RECEIVE_DELAY_1_MS, RADIOLIB_LW_RECEIVE_DELAY_2_MS }; // device status - battery level uint8_t battLevel = 0xFF; @@ -951,7 +968,7 @@ class LoRaWANNode { // configure the common physical layer properties (preamble, sync word etc.) // channels must be configured separately by setupChannelsDyn()! - int16_t setPhyProperties(); + int16_t setPhyProperties(uint8_t dir); // setup uplink/downlink channel data rates and frequencies // for dynamic channels, there is a small set of predefined channels @@ -971,9 +988,6 @@ class LoRaWANNode { // find the first usable data rate for the given band int16_t findDataRate(uint8_t dr, DataRate_t* dataRate); - // configure channel based on cached data rate ID and frequency - int16_t configureChannel(uint8_t dir); - // restore all available channels from persistent storage int16_t restoreChannels(); @@ -996,6 +1010,14 @@ class LoRaWANNode { // get the payload length for a specific MAC command uint8_t getMacPayloadLength(uint8_t cid); + /*! + \brief Configures CSMA for LoRaWAN as per TR-13, LoRa Alliance. + \param backoffMax Num of BO slots to be decremented after DIFS phase. 0 to disable BO. + \param difsSlots Num of CADs to estimate a clear CH. + \param enableCSMA enable/disable CSMA for LoRaWAN. + */ + void setCSMA(uint8_t backoffMax, uint8_t difsSlots, bool enableCSMA = false); + // Performs CSMA as per LoRa Alliance Technical Recommendation 13 (TR-013). void performCSMA(); @@ -1003,7 +1025,7 @@ class LoRaWANNode { bool performCAD(); // function to encrypt and decrypt payloads - void processAES(uint8_t* in, size_t len, uint8_t* key, uint8_t* out, uint32_t fcnt, uint8_t dir, uint8_t ctrId, bool counter); + 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, uint16_t keyLen); diff --git a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWANBands.cpp b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWANBands.cpp index eaeb084..2c927f3 100644 --- a/lib/RadioLib/src/protocols/LoRaWAN/LoRaWANBands.cpp +++ b/lib/RadioLib/src/protocols/LoRaWAN/LoRaWANBands.cpp @@ -3,7 +3,7 @@ #if !RADIOLIB_EXCLUDE_LORAWAN // array of pointers to currently supported LoRaWAN bands -const LoRaWANBand_t* LoRaWANBands[RADIOLIB_LORAWAN_NUM_SUPPORTED_BANDS] = { +const LoRaWANBand_t* LoRaWANBands[RADIOLIB_LW_NUM_SUPPORTED_BANDS] = { &EU868, &US915, &CN780, @@ -17,7 +17,7 @@ const LoRaWANBand_t* LoRaWANBands[RADIOLIB_LORAWAN_NUM_SUPPORTED_BANDS] = { const LoRaWANBand_t EU868 = { .bandNum = BandEU868, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, .powerNumSteps = 7, @@ -30,55 +30,55 @@ const LoRaWANBand_t EU868 = { { .enabled = true, .idx = 2, .freq = 868.500, .drMin = 0, .drMax = 5}, }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 869.525, .drMin = 0, .drMax = 0 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_250_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_FSK_50_K, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t US915 = { .bandNum = BandUS915, - .bandType = RADIOLIB_LORAWAN_BAND_FIXED, + .bandType = RADIOLIB_LW_BAND_FIXED, .payloadLenMax = { 19, 61, 133, 250, 250, 0, 0, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, .powerNumSteps = 10, .dutyCycle = 0, - .dwellTimeUp = RADIOLIB_LORAWAN_DWELL_TIME, + .dwellTimeUp = RADIOLIB_LW_DWELL_TIME, .dwellTimeDn = 0, .txFreqs = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 2, .txSpans = { @@ -105,32 +105,32 @@ const LoRaWANBand_t US915 = { .freqStep = 0.600, .drMin = 8, .drMax = 13, - .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED + .joinRequestDataRate = RADIOLIB_LW_DATA_RATE_UNUSED }, .rx1DataRateBase = 10, .rx2 = { .enabled = true, .idx = 0, .freq = 923.300, .drMin = 8, .drMax = 8 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t CN780 = { .bandNum = BandCN780, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 250, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, .powerNumSteps = 5, @@ -149,34 +149,34 @@ const LoRaWANBand_t CN780 = { }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 786.000, .drMin = 0, .drMax = 0 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_250_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_FSK_50_K, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t EU433 = { .bandNum = BandEU433, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 12, .powerNumSteps = 5, @@ -189,40 +189,40 @@ const LoRaWANBand_t EU433 = { { .enabled = true, .idx = 2, .freq = 433.575, .drMin = 0, .drMax = 5}, }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 434.665, .drMin = 0, .drMax = 0 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_250_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_FSK_50_K, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t AU915 = { .bandNum = BandAU915, - .bandType = RADIOLIB_LORAWAN_BAND_FIXED, + .bandType = RADIOLIB_LW_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 0, 41, 117, 230, 230, 230, 230, 0 }, .powerMax = 30, .powerNumSteps = 10, @@ -230,14 +230,14 @@ const LoRaWANBand_t AU915 = { .dwellTimeUp = 0, .dwellTimeDn = 0, .txFreqs = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 2, .txSpans = { @@ -264,32 +264,32 @@ const LoRaWANBand_t AU915 = { .freqStep = 0.600, .drMin = 8, .drMax = 13, - .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED + .joinRequestDataRate = RADIOLIB_LW_DATA_RATE_UNUSED }, .rx1DataRateBase = 8, .rx2 = { .enabled = true, .idx = 0, .freq = 923.300, .drMin = 8, .drMax = 8 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_500_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_500_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t CN500 = { .bandNum = BandCN500, - .bandType = RADIOLIB_LORAWAN_BAND_FIXED, + .bandType = RADIOLIB_LW_BAND_FIXED, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 19, .powerNumSteps = 7, @@ -297,14 +297,14 @@ const LoRaWANBand_t CN500 = { .dwellTimeUp = 0, .dwellTimeDn = 0, .txFreqs = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 1, .txSpans = { @@ -316,7 +316,7 @@ const LoRaWANBand_t CN500 = { .drMax = 5, .joinRequestDataRate = 0 }, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE }, .rx1Span = { .numChannels = 48, @@ -324,78 +324,78 @@ const LoRaWANBand_t CN500 = { .freqStep = 0.200, .drMin = 0, .drMax = 5, - .joinRequestDataRate = RADIOLIB_LORAWAN_DATA_RATE_UNUSED + .joinRequestDataRate = RADIOLIB_LW_DATA_RATE_UNUSED }, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 505.300, .drMin = 0, .drMax = 0 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_5, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_5, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t AS923 = { .bandNum = BandAS923, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 16, .powerNumSteps = 7, .dutyCycle = 36000, - .dwellTimeUp = RADIOLIB_LORAWAN_DWELL_TIME, - .dwellTimeDn = RADIOLIB_LORAWAN_DWELL_TIME, + .dwellTimeUp = RADIOLIB_LW_DWELL_TIME, + .dwellTimeDn = RADIOLIB_LW_DWELL_TIME, .txFreqs = { { .enabled = true, .idx = 0, .freq = 923.200, .drMin = 0, .drMax = 5}, { .enabled = true, .idx = 1, .freq = 923.400, .drMin = 0, .drMax = 5}, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 923.200, .drMin = 2, .drMax = 2 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_250_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_250_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_FSK_50_K, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t KR920 = { .bandNum = BandKR920, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 14, .powerNumSteps = 7, @@ -408,40 +408,40 @@ const LoRaWANBand_t KR920 = { { .enabled = true, .idx = 2, .freq = 922.500, .drMin = 0, .drMax = 5} }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 921.900, .drMin = 0, .drMax = 0 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; const LoRaWANBand_t IN865 = { .bandNum = BandIN865, - .bandType = RADIOLIB_LORAWAN_BAND_DYNAMIC, + .bandType = RADIOLIB_LW_BAND_DYNAMIC, .payloadLenMax = { 59, 59, 59, 123, 230, 230, 230, 230, 0, 0, 0, 0, 0, 0, 0 }, .powerMax = 30, .powerNumSteps = 10, @@ -454,34 +454,34 @@ const LoRaWANBand_t IN865 = { { .enabled = true, .idx = 2, .freq = 865.9850, .drMin = 0, .drMax = 5} }, .txJoinReq = { - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE, - RADIOLIB_LORAWAN_CHANNEL_NONE + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE, + RADIOLIB_LW_CHANNEL_NONE }, .numTxSpans = 0, .txSpans = { - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, - RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE + RADIOLIB_LW_CHANNEL_SPAN_NONE, + RADIOLIB_LW_CHANNEL_SPAN_NONE }, - .rx1Span = RADIOLIB_LORAWAN_CHANNEL_SPAN_NONE, + .rx1Span = RADIOLIB_LW_CHANNEL_SPAN_NONE, .rx1DataRateBase = 0, .rx2 = { .enabled = true, .idx = 0, .freq = 866.550, .drMin = 2, .drMax = 2 }, .dataRates = { - RADIOLIB_LORAWAN_DATA_RATE_SF_12 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_11 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_10 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_9 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_8 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_SF_7 | RADIOLIB_LORAWAN_DATA_RATE_BW_125_KHZ | RADIOLIB_LORAWAN_DATA_RATE_CR_4_7, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_FSK_50_K, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED, - RADIOLIB_LORAWAN_DATA_RATE_UNUSED + RADIOLIB_LW_DATA_RATE_SF_12 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_11 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_10 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_9 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_8 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_SF_7 | RADIOLIB_LW_DATA_RATE_BW_125_KHZ | RADIOLIB_LW_DATA_RATE_CR_4_7, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_FSK_50_K, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED, + RADIOLIB_LW_DATA_RATE_UNUSED } }; diff --git a/lib/RadioLib/src/protocols/Morse/Morse.cpp b/lib/RadioLib/src/protocols/Morse/Morse.cpp index 90cc2a6..b41dae7 100644 --- a/lib/RadioLib/src/protocols/Morse/Morse.cpp +++ b/lib/RadioLib/src/protocols/Morse/Morse.cpp @@ -104,7 +104,7 @@ int MorseClient::read(uint8_t* symbol, uint8_t* len, float low, float high) { (*symbol) |= (RADIOLIB_MORSE_DASH << (*len)); (*len)++; } else { - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("", signalLen); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("", (long unsigned int)signalLen); } } diff --git a/lib/RadioLib/src/protocols/Morse/Morse.h b/lib/RadioLib/src/protocols/Morse/Morse.h index 3edb0be..78d6a91 100644 --- a/lib/RadioLib/src/protocols/Morse/Morse.h +++ b/lib/RadioLib/src/protocols/Morse/Morse.h @@ -148,7 +148,7 @@ class MorseClient: public RadioLibPrint { \param b Byte to write. \returns 1 if the byte was written, 0 otherwise. */ - size_t write(uint8_t b); + size_t write(uint8_t b) override; #if !RADIOLIB_GODMODE private: @@ -167,9 +167,9 @@ class MorseClient: public RadioLibPrint { // variables to keep decoding state uint32_t signalCounter = 0; - uint32_t signalStart = 0; + RadioLibTime_t signalStart = 0; uint32_t pauseCounter = 0; - uint32_t pauseStart = 0; + RadioLibTime_t pauseStart = 0; size_t printNumber(unsigned long, uint8_t); size_t printFloat(double, uint8_t); diff --git a/lib/RadioLib/src/protocols/Pager/Pager.cpp b/lib/RadioLib/src/protocols/Pager/Pager.cpp index 1db47da..e9aa14a 100644 --- a/lib/RadioLib/src/protocols/Pager/Pager.cpp +++ b/lib/RadioLib/src/protocols/Pager/Pager.cpp @@ -28,15 +28,12 @@ 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) { // calculate duration of 1 bit in us dataRate = (float)speed/1000.0f; - bitDuration = (uint32_t)1000000/speed; + bitDuration = (RadioLibTime_t)1000000/speed; // calculate 24-bit frequency baseFreq = base; @@ -206,7 +203,7 @@ int16_t PagerClient::transmit(uint8_t* data, size_t len, uint32_t addr, uint8_t // in BCD mode, pad the rest of the code word with spaces (0xC) if(encoding == RADIOLIB_PAGER_BCD) { uint8_t numSteps = (symbolPos - RADIOLIB_PAGER_FUNC_BITS_POS + symbolLength)/symbolLength; - for(uint8_t i = 0; i < numSteps; i++) { + for(uint8_t j = 0; j < numSteps; j++) { symbol = encodeBCD(' '); symbol = Module::reflect(symbol, 8); symbol >>= (8 - symbolLength); @@ -397,17 +394,15 @@ int16_t PagerClient::readData(uint8_t* data, size_t* len, uint32_t* addr) { } // we have the address, start pulling out the message - bool complete = false; size_t decodedBytes = 0; uint32_t prevCw = 0; bool overflow = false; int8_t ovfBits = 0; - while(!complete && phyLayer->available()) { + while(phyLayer->available()) { uint32_t cw = read(); // check if it's the idle code word if(cw == RADIOLIB_PAGER_IDLE_CODE_WORD) { - complete = true; break; } @@ -511,7 +506,7 @@ void PagerClient::write(uint32_t codeWord) { Module* mod = phyLayer->getMod(); for(int8_t i = 31; i >= 0; i--) { uint32_t mask = (uint32_t)0x01 << i; - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); // figure out the shift direction - start by assuming the bit is 0 int16_t change = shiftFreq; @@ -532,7 +527,7 @@ void PagerClient::write(uint32_t codeWord) { // this is pretty silly, while(mod->hal->micros() ... ) would be enough // but for some reason, MegaCore throws a linker error on it // "relocation truncated to fit: R_AVR_7_PCREL against `no symbol'" - uint32_t now = mod->hal->micros(); + RadioLibTime_t now = mod->hal->micros(); while(now - start < bitDuration) { now = mod->hal->micros(); } @@ -554,7 +549,7 @@ uint32_t PagerClient::read() { codeWord = ~codeWord; } - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("R\t%lX", codeWord); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("R\t%lX", (long unsigned int)codeWord); // TODO BCH error correction here return(codeWord); } diff --git a/lib/RadioLib/src/protocols/Pager/Pager.h b/lib/RadioLib/src/protocols/Pager/Pager.h index f5cdb81..6963a68 100644 --- a/lib/RadioLib/src/protocols/Pager/Pager.h +++ b/lib/RadioLib/src/protocols/Pager/Pager.h @@ -177,17 +177,17 @@ class PagerClient { #endif PhysicalLayer* phyLayer; - float baseFreq; - float dataRate; - uint32_t baseFreqRaw; - uint16_t shiftFreq; - uint16_t shiftFreqHz; - uint16_t bitDuration; - uint32_t filterAddr; - uint32_t filterMask; - uint32_t *filterAddresses; - uint32_t *filterMasks; - size_t filterNumAddresses; + float baseFreq = 0; + float dataRate = 0; + uint32_t baseFreqRaw = 0; + uint16_t shiftFreq = 0; + uint16_t shiftFreqHz = 0; + RadioLibTime_t bitDuration = 0; + uint32_t filterAddr = 0; + uint32_t filterMask = 0; + uint32_t *filterAddresses = nullptr; + uint32_t *filterMasks = nullptr; + size_t filterNumAddresses = 0; bool inv = false; void write(uint32_t* data, size_t len); diff --git a/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.cpp b/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.cpp index a1b5173..f351cb4 100644 --- a/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.cpp +++ b/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.cpp @@ -132,7 +132,7 @@ int16_t PhysicalLayer::startReceive() { return(RADIOLIB_ERR_UNSUPPORTED); } -int16_t PhysicalLayer::startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len) { +int16_t PhysicalLayer::startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len) { (void)timeout; (void)irqFlags; (void)irqMask; @@ -256,6 +256,12 @@ int16_t PhysicalLayer::setOutputPower(int8_t power) { return(RADIOLIB_ERR_UNSUPPORTED); } +int16_t PhysicalLayer::checkOutputPower(int8_t power, int8_t* clipped) { + (void)power; + (void)clipped; + return(RADIOLIB_ERR_UNSUPPORTED); +} + int16_t PhysicalLayer::setSyncWord(uint8_t* sync, size_t len) { (void)sync; (void)len; @@ -294,17 +300,17 @@ float PhysicalLayer::getSNR() { return(RADIOLIB_ERR_UNSUPPORTED); } -uint32_t PhysicalLayer::getTimeOnAir(size_t len) { +RadioLibTime_t PhysicalLayer::getTimeOnAir(size_t len) { (void)len; return(0); } -uint32_t PhysicalLayer::calculateRxTimeout(uint32_t timeoutUs) { +RadioLibTime_t PhysicalLayer::calculateRxTimeout(RadioLibTime_t timeoutUs) { (void)timeoutUs; return(0); } -int16_t PhysicalLayer::irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask) { +int16_t PhysicalLayer::irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask) { (void)irqFlags; (void)irqMask; return(RADIOLIB_ERR_UNSUPPORTED); @@ -413,7 +419,7 @@ void PhysicalLayer::updateDirectBuffer(uint8_t bit) { this->syncBuffer <<= 1; this->syncBuffer |= bit; - RADIOLIB_DEBUG_PROTOCOL_PRINTLN("S\t%lu", this->syncBuffer); + RADIOLIB_DEBUG_PROTOCOL_PRINTLN("S\t%lu", (long unsigned int)this->syncBuffer); if((this->syncBuffer & this->directSyncWordMask) == this->directSyncWord) { this->gotSync = true; diff --git a/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.h b/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.h index 173c293..9b3dc24 100644 --- a/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.h +++ b/lib/RadioLib/src/protocols/PhysicalLayer/PhysicalLayer.h @@ -4,22 +4,42 @@ #include "../../TypeDef.h" #include "../../Module.h" -// data rate structure interpretation in case LoRa is used +/*! + \struct LoRaRate_t + \brief Data rate structure interpretation in case LoRa is used +*/ struct LoRaRate_t { + /*! \brief LoRa spreading factor */ uint8_t spreadingFactor; + + /*! \brief LoRa bandwidth in kHz */ float bandwidth; + + /*! \brief LoRa coding rate */ uint8_t codingRate; }; -// data rate structure interpretation in case FSK is used +/*! + \struct FSKRate_t + \brief Data rate structure interpretation in case FSK is used +*/ struct FSKRate_t { + /*! \brief FSK bit rate in kbps */ float bitRate; + + /*! \brief FS frequency deviation in kHz*/ float freqDev; }; -// common data rate +/*! + \union DataRate_t + \brief Common data rate structure +*/ union DataRate_t { + /*! \brief Interpretation for LoRa modems */ LoRaRate_t lora; + + /*! \brief Interpretation for FSK modems */ FSKRate_t fsk; }; @@ -124,7 +144,7 @@ class PhysicalLayer { \param len Packet length, needed for some modules under special circumstances (e.g. LoRa implicit header mode). \returns \ref status_codes */ - virtual int16_t startReceive(uint32_t timeout, uint16_t irqFlags, uint16_t irqMask, size_t len); + virtual int16_t startReceive(uint32_t timeout, uint32_t irqFlags, uint32_t irqMask, size_t len); /*! \brief Binary receive method. Must be implemented in module class. @@ -237,7 +257,7 @@ class PhysicalLayer { /*! \brief Sets FSK data encoding. Only available in FSK mode. Must be implemented in module class. - \param enc Encoding to be used. See \ref config_encoding for possible values. + \param encoding Encoding to be used. See \ref config_encoding for possible values. \returns \ref status_codes */ virtual int16_t setEncoding(uint8_t encoding); @@ -256,6 +276,14 @@ class PhysicalLayer { */ virtual int16_t setOutputPower(int8_t power); + /*! + \brief Check if output power is configurable. Must be implemented in module class if the module supports it. + \param power Output power in dBm. The allowed range depends on the module used. + \param clipped Clipped output power value to what is possible within the module's range. + \returns \ref status_codes + */ + virtual int16_t checkOutputPower(int8_t power, int8_t* clipped); + /*! \brief Set sync word. Must be implemented in module class if the module supports it. \param sync Pointer to the sync word. @@ -315,14 +343,14 @@ class PhysicalLayer { \param len Payload length in bytes. \returns Expected time-on-air in microseconds. */ - virtual uint32_t getTimeOnAir(size_t len); + virtual RadioLibTime_t getTimeOnAir(size_t len); /*! \brief Calculate the timeout value for this specific module / series (in number of symbols or units of time) \param timeoutUs Timeout in microseconds to listen for \returns Timeout value in a unit that is specific for the used module */ - virtual uint32_t calculateRxTimeout(uint32_t timeoutUs); + virtual RadioLibTime_t calculateRxTimeout(RadioLibTime_t timeoutUs); /*! \brief Create the flags that make up RxDone and RxTimeout used for receiving downlinks @@ -330,11 +358,11 @@ class PhysicalLayer { \param irqMask Mask indicating which IRQ triggers a DIO \returns \ref status_codes */ - virtual int16_t irqRxDoneRxTimeout(uint16_t &irqFlags, uint16_t &irqMask); + virtual int16_t irqRxDoneRxTimeout(uint32_t &irqFlags, uint32_t &irqMask); /*! \brief Check whether the IRQ bit for RxTimeout is set - \returns \ref RxTimeout IRQ is set + \returns Whether RxTimeout IRQ is set */ virtual bool isRxTimeout(); diff --git a/lib/RadioLib/src/protocols/Print/ITA2String.cpp b/lib/RadioLib/src/protocols/Print/ITA2String.cpp index 4f2d99a..6c67faa 100644 --- a/lib/RadioLib/src/protocols/Print/ITA2String.cpp +++ b/lib/RadioLib/src/protocols/Print/ITA2String.cpp @@ -20,6 +20,25 @@ ITA2String::ITA2String(const char* str) { ita2Len = 0; } +ITA2String::ITA2String(const ITA2String& ita2) { + this->asciiLen = ita2.asciiLen; + this->ita2Len = ita2.ita2Len; + #if !RADIOLIB_STATIC_ONLY + this->strAscii = new char[asciiLen + 1]; + #endif + strcpy(this->strAscii, ita2.strAscii); +} + +ITA2String& ITA2String::operator=(const ITA2String& ita2) { + this->asciiLen = ita2.asciiLen; + this->ita2Len = ita2.ita2Len; + #if !RADIOLIB_STATIC_ONLY + this->strAscii = new char[asciiLen + 1]; + #endif + strcpy(this->strAscii, ita2.strAscii); + return(*this); +} + ITA2String::~ITA2String() { #if !RADIOLIB_STATIC_ONLY delete[] strAscii; @@ -46,6 +65,9 @@ uint8_t* ITA2String::byteArr() { uint8_t* temp = new uint8_t[asciiLen*2 + 1]; #endif + // ensure the minimum possible array size is always initialized + temp[0] = 0; + size_t arrayLen = 0; bool flagFigure = false; for(size_t i = 0; i < asciiLen; i++) { diff --git a/lib/RadioLib/src/protocols/Print/ITA2String.h b/lib/RadioLib/src/protocols/Print/ITA2String.h index 579bb1c..70e7b4b 100644 --- a/lib/RadioLib/src/protocols/Print/ITA2String.h +++ b/lib/RadioLib/src/protocols/Print/ITA2String.h @@ -27,13 +27,25 @@ class ITA2String { \brief Default single-character constructor. \param c ASCII-encoded character to encode as ITA2. */ - ITA2String(char c); + explicit ITA2String(char c); /*! \brief Default string constructor. \param str ASCII-encoded string to encode as ITA2. */ - ITA2String(const char* str); + explicit ITA2String(const char* str); + + /*! + \brief Copy constructor. + \param ita2 ITA2String instance to copy. + */ + ITA2String(const ITA2String& ita2); + + /*! + \brief Overload for assignment operator. + \param ita2 rvalue ITA2String. + */ + ITA2String& operator=(const ITA2String& ita2); /*! \brief Default destructor. diff --git a/lib/RadioLib/src/protocols/Print/Print.cpp b/lib/RadioLib/src/protocols/Print/Print.cpp index 94b7b33..52e767b 100644 --- a/lib/RadioLib/src/protocols/Print/Print.cpp +++ b/lib/RadioLib/src/protocols/Print/Print.cpp @@ -152,7 +152,7 @@ size_t RadioLibPrint::print(double n, int digits) { return(RadioLibPrint::printFloat(n, digits)); } -size_t RadioLibPrint::println(const char* str) { +size_t RadioLibPrint::println(const char str[]) { size_t n = RadioLibPrint::print(str); n += RadioLibPrint::println(); return(n); diff --git a/lib/RadioLib/src/protocols/Print/Print.h b/lib/RadioLib/src/protocols/Print/Print.h index 48bd7e7..3645416 100644 --- a/lib/RadioLib/src/protocols/Print/Print.h +++ b/lib/RadioLib/src/protocols/Print/Print.h @@ -10,17 +10,20 @@ #define RADIOLIB_ASCII_EXTENDED 1 #define RADIOLIB_ITA2 2 -// based on Arduino Print class +/*! + \class RadioLibPrint + \brief Printing class, based on Arduino Print class with additional encodings. +*/ class RadioLibPrint { public: virtual size_t write(uint8_t) = 0; size_t write(const char *str) { if (str == NULL) return 0; - return write((const uint8_t *)str, strlen(str)); + return write(reinterpret_cast(str), strlen(str)); } virtual size_t write(const uint8_t *buffer, size_t size); size_t write(const char *buffer, size_t size) { - return write((const uint8_t *)buffer, size); + return write(reinterpret_cast(buffer), size); } size_t print(ITA2String& ita2); @@ -57,7 +60,7 @@ class RadioLibPrint { protected: #endif uint8_t encoding = RADIOLIB_ASCII_EXTENDED; - const char* lineFeed; + const char* lineFeed = "\r\n"; size_t printNumber(unsigned long, uint8_t); size_t printFloat(double, uint8_t); diff --git a/lib/RadioLib/src/protocols/RTTY/RTTY.cpp b/lib/RadioLib/src/protocols/RTTY/RTTY.cpp index c823fb2..17da7d2 100644 --- a/lib/RadioLib/src/protocols/RTTY/RTTY.cpp +++ b/lib/RadioLib/src/protocols/RTTY/RTTY.cpp @@ -28,7 +28,7 @@ int16_t RTTYClient::begin(float base, uint32_t shift, uint16_t rate, uint8_t enc shiftFreqHz = shift; // calculate duration of 1 bit - bitDuration = (uint32_t)1000000/rate; + bitDuration = (RadioLibTime_t)1000000/rate; // calculate module carrier frequency resolution uint32_t step = round(phyLayer->getFreqStep()); @@ -91,14 +91,14 @@ size_t RTTYClient::write(uint8_t b) { void RTTYClient::mark() { Module* mod = phyLayer->getMod(); - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); transmitDirect(baseFreq + shiftFreq, baseFreqHz + shiftFreqHz); mod->waitForMicroseconds(start, bitDuration); } void RTTYClient::space() { Module* mod = phyLayer->getMod(); - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); transmitDirect(baseFreq, baseFreqHz); mod->waitForMicroseconds(start, bitDuration); } diff --git a/lib/RadioLib/src/protocols/RTTY/RTTY.h b/lib/RadioLib/src/protocols/RTTY/RTTY.h index 4da4ca6..1807b36 100644 --- a/lib/RadioLib/src/protocols/RTTY/RTTY.h +++ b/lib/RadioLib/src/protocols/RTTY/RTTY.h @@ -59,7 +59,7 @@ class RTTYClient: public RadioLibPrint { \param b Byte to write. \returns 1 if the byte was written, 0 otherwise. */ - size_t write(uint8_t b); + size_t write(uint8_t b) override; #if !RADIOLIB_GODMODE private: @@ -71,7 +71,7 @@ class RTTYClient: public RadioLibPrint { uint32_t baseFreq = 0, baseFreqHz = 0; uint32_t shiftFreq = 0, shiftFreqHz = 0; - uint32_t bitDuration = 0; + RadioLibTime_t bitDuration = 0; uint8_t stopBitsNum = 0; void mark(); diff --git a/lib/RadioLib/src/protocols/SSTV/SSTV.cpp b/lib/RadioLib/src/protocols/SSTV/SSTV.cpp index b06df29..9d31e59 100644 --- a/lib/RadioLib/src/protocols/SSTV/SSTV.cpp +++ b/lib/RadioLib/src/protocols/SSTV/SSTV.cpp @@ -246,7 +246,7 @@ void SSTVClient::sendHeader() { this->tone(RADIOLIB_SSTV_TONE_BREAK, RADIOLIB_SSTV_HEADER_BIT_LENGTH); } -void SSTVClient::sendLine(uint32_t* imgLine) { +void SSTVClient::sendLine(const uint32_t* imgLine) { // check first line flag in Scottie modes if(firstLine && ((txMode.visCode == RADIOLIB_SSTV_SCOTTIE_1) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_2) || (txMode.visCode == RADIOLIB_SSTV_SCOTTIE_DX))) { firstLine = false; @@ -289,9 +289,9 @@ uint16_t SSTVClient::getPictureHeight() const { return(txMode.height); } -void SSTVClient::tone(float freq, uint32_t len) { +void SSTVClient::tone(float freq, RadioLibTime_t len) { Module* mod = phyLayer->getMod(); - uint32_t start = mod->hal->micros(); + RadioLibTime_t start = mod->hal->micros(); #if !RADIOLIB_EXCLUDE_AFSK if(audioClient != nullptr) { audioClient->tone(freq, false); diff --git a/lib/RadioLib/src/protocols/SSTV/SSTV.h b/lib/RadioLib/src/protocols/SSTV/SSTV.h index 986dd0c..7c0feac 100644 --- a/lib/RadioLib/src/protocols/SSTV/SSTV.h +++ b/lib/RadioLib/src/protocols/SSTV/SSTV.h @@ -54,7 +54,7 @@ struct tone_t { /*! \brief Length of tone in us, set to 0 for picture scan tones. */ - uint32_t len; + RadioLibTime_t len; /*! \brief Frequency of tone in Hz, set to 0 for picture scan tones. @@ -174,7 +174,7 @@ class SSTVClient { \param imgLine Image line to send, in 24-bit RGB. It is up to the user to ensure that imgLine has enough pixels to send it in the current SSTV mode. */ - void sendLine(uint32_t* imgLine); + void sendLine(const uint32_t* imgLine); /*! \brief Get picture height of the currently configured SSTV mode. @@ -194,7 +194,7 @@ class SSTVClient { SSTVMode_t txMode = Scottie1; bool firstLine = true; - void tone(float freq, uint32_t len = 0); + void tone(float freq, RadioLibTime_t len = 0); }; #endif diff --git a/lib/RadioLib/src/utils/CRC.cpp b/lib/RadioLib/src/utils/CRC.cpp index 4422942..ac7fb74 100644 --- a/lib/RadioLib/src/utils/CRC.cpp +++ b/lib/RadioLib/src/utils/CRC.cpp @@ -4,7 +4,7 @@ RadioLibCRC::RadioLibCRC() { } -uint32_t RadioLibCRC::checksum(uint8_t* buff, size_t len) { +uint32_t RadioLibCRC::checksum(const uint8_t* buff, size_t len) { uint32_t crc = this->init; size_t pos = 0; for(size_t i = 0; i < 8*len; i++) { diff --git a/lib/RadioLib/src/utils/CRC.h b/lib/RadioLib/src/utils/CRC.h index 124fa49..bec3d71 100644 --- a/lib/RadioLib/src/utils/CRC.h +++ b/lib/RadioLib/src/utils/CRC.h @@ -21,32 +21,32 @@ class RadioLibCRC { /*! \brief CRC size in bits. */ - uint8_t size; + uint8_t size = 8; /*! \brief CRC polynomial. */ - uint32_t poly; + uint32_t poly = 0; /*! \brief Initial value. */ - uint32_t init; + uint32_t init = 0; /*! \brief Final XOR value. */ - uint32_t out; + uint32_t out = 0; /*! \brief Whether to reflect input bytes. */ - bool refIn; + bool refIn = false; /*! \brief Whether to reflect the result. */ - bool refOut; + bool refOut = false; /*! \brief Default constructor. @@ -59,7 +59,7 @@ class RadioLibCRC { \param len Size of the buffer in bytes. \returns The resulting checksum. */ - uint32_t checksum(uint8_t* buff, size_t len); + uint32_t checksum(const uint8_t* buff, size_t len); }; // the global singleton diff --git a/lib/RadioLib/src/utils/Cryptography.cpp b/lib/RadioLib/src/utils/Cryptography.cpp index 16d1c46..9411bd1 100644 --- a/lib/RadioLib/src/utils/Cryptography.cpp +++ b/lib/RadioLib/src/utils/Cryptography.cpp @@ -82,7 +82,7 @@ void RadioLibAES128::generateCMAC(uint8_t* in, size_t len, uint8_t* cmac) { delete[] buff; } -bool RadioLibAES128::verifyCMAC(uint8_t* in, size_t len, uint8_t* cmac) { +bool RadioLibAES128::verifyCMAC(uint8_t* in, size_t len, const uint8_t* cmac) { uint8_t cmacReal[RADIOLIB_AES128_BLOCK_SIZE]; this->generateCMAC(in, len, cmacReal); for(size_t i = 0; i < RADIOLIB_AES128_BLOCK_SIZE; i++) { @@ -93,7 +93,7 @@ bool RadioLibAES128::verifyCMAC(uint8_t* in, size_t len, uint8_t* cmac) { return(true); } -void RadioLibAES128::keyExpansion(uint8_t* roundKey, uint8_t* key) { +void RadioLibAES128::keyExpansion(uint8_t* roundKey, const uint8_t* key) { uint8_t tmp[4]; // the first round key is the key itself @@ -167,7 +167,7 @@ void RadioLibAES128::rotWord(uint8_t* word) { } } -void RadioLibAES128::addRoundKey(uint8_t round, state_t* state, uint8_t* roundKey) { +void RadioLibAES128::addRoundKey(uint8_t round, state_t* state, const uint8_t* roundKey) { for(size_t row = 0; row < 4; row++) { for(size_t col = 0; col < 4; col++) { (*state)[row][col] ^= roundKey[(round * RADIOLIB_AES128_N_B * 4) + (row * RADIOLIB_AES128_N_B) + col]; @@ -175,13 +175,13 @@ void RadioLibAES128::addRoundKey(uint8_t round, state_t* state, uint8_t* roundKe } } -void RadioLibAES128::blockXor(uint8_t* dst, uint8_t* a, uint8_t* b) { +void RadioLibAES128::blockXor(uint8_t* dst, const uint8_t* a, const uint8_t* b) { for(uint8_t j = 0; j < RADIOLIB_AES128_BLOCK_SIZE; j++) { dst[j] = a[j] ^ b[j]; } } -void RadioLibAES128::blockLeftshift(uint8_t* dst, uint8_t* src) { +void RadioLibAES128::blockLeftshift(uint8_t* dst, const uint8_t* src) { uint8_t ovf = 0x00; for(int8_t i = RADIOLIB_AES128_BLOCK_SIZE - 1; i >= 0; i--) { dst[i] = src[i] << 1; diff --git a/lib/RadioLib/src/utils/Cryptography.h b/lib/RadioLib/src/utils/Cryptography.h index 661996c..dd6644e 100644 --- a/lib/RadioLib/src/utils/Cryptography.h +++ b/lib/RadioLib/src/utils/Cryptography.h @@ -142,23 +142,23 @@ class RadioLibAES128 { \param cmac CMAC to verify. \returns True if valid, false otherwise. */ - bool verifyCMAC(uint8_t* in, size_t len, uint8_t* cmac); + bool verifyCMAC(uint8_t* in, size_t len, const uint8_t* cmac); private: - uint8_t* keyPtr; - uint8_t roundKey[RADIOLIB_AES128_KEY_EXP_SIZE]; + uint8_t* keyPtr = nullptr; + uint8_t roundKey[RADIOLIB_AES128_KEY_EXP_SIZE] = { 0 }; - void keyExpansion(uint8_t* roundKey, uint8_t* key); + void keyExpansion(uint8_t* roundKey, const uint8_t* key); void cipher(state_t* state, uint8_t* roundKey); void decipher(state_t* state, uint8_t* roundKey); void subWord(uint8_t* word); void rotWord(uint8_t* word); - void addRoundKey(uint8_t round, state_t* state, uint8_t* roundKey); + void addRoundKey(uint8_t round, state_t* state, const uint8_t* roundKey); - void blockXor(uint8_t* dst, uint8_t* a, uint8_t* b); - void blockLeftshift(uint8_t* dst, uint8_t* src); + void blockXor(uint8_t* dst, const uint8_t* a, const uint8_t* b); + void blockLeftshift(uint8_t* dst, const uint8_t* src); void generateSubkeys(uint8_t* key1, uint8_t* key2); void subBytes(state_t* state, const uint8_t* box); diff --git a/lib/RadioLib/src/utils/FEC.cpp b/lib/RadioLib/src/utils/FEC.cpp index 39d7f5d..515014d 100644 --- a/lib/RadioLib/src/utils/FEC.cpp +++ b/lib/RadioLib/src/utils/FEC.cpp @@ -5,6 +5,14 @@ RadioLibBCH::RadioLibBCH() { } +RadioLibBCH::~RadioLibBCH() { + #if !RADIOLIB_STATIC_ONLY + delete[] this->alphaTo; + delete[] this->indexOf; + delete[] this->generator; + #endif +} + /* BCH Encoder based on https://www.codeproject.com/articles/13189/pocsag-encoder @@ -116,12 +124,15 @@ void RadioLibBCH::begin(uint8_t n, uint8_t k, uint32_t poly) { // Search for roots 1, 2, ..., m-1 in cycle sets int32_t rdncy = 0; #if RADIOLIB_STATIC_ONLY - int32_t min[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1]; + int32_t min[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1] = { 0 }; #else int32_t* min = new int32_t[this->n - this->k + 1]; #endif kaux = 0; + // ensure the first element is always initializer + min[0] = 0; + for(ii = 1; ii <= jj; ii++) { min[kaux] = 0; for(jj = 0; jj < size[ii]; jj++) { @@ -140,12 +151,15 @@ void RadioLibBCH::begin(uint8_t n, uint8_t k, uint32_t poly) { int32_t noterms = kaux; #if RADIOLIB_STATIC_ONLY - int32_t zeros[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1]; + int32_t zeros[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1] = { 0 }; #else int32_t* zeros = new int32_t[this->n - this->k + 1]; #endif kaux = 1; + // ensure the first element is always initializer + zeros[1] = 0; + for(ii = 0; ii < noterms; ii++) { for(jj = 0; jj < size[min[ii]]; jj++) { zeros[kaux] = cycle[min[ii]][jj]; @@ -186,9 +200,10 @@ void RadioLibBCH::begin(uint8_t n, uint8_t k, uint32_t poly) { uint32_t RadioLibBCH::encode(uint32_t dataword) { // we only use the "k" most significant bits #if RADIOLIB_STATIC_ONLY - int32_t data[RADIOLIB_BCH_MAX_K]; + int32_t data[RADIOLIB_BCH_MAX_K] = { 0 }; #else int32_t* data = new int32_t[this->k]; + memset(data, 0, this->k*sizeof(int32_t)); #endif int32_t j1 = 0; for(int32_t i = this->n; i > (this->n - this->k); i--) { @@ -201,11 +216,11 @@ uint32_t RadioLibBCH::encode(uint32_t dataword) { // reset the M(x)+r array elements #if RADIOLIB_STATIC_ONLY - int32_t Mr[RADIOLIB_BCH_MAX_N]; + int32_t Mr[RADIOLIB_BCH_MAX_N] = { 0 }; #else int32_t* Mr = new int32_t[this->n]; + memset(Mr, 0x00, this->n*sizeof(int32_t)); #endif - memset(Mr, 0x00, this->n*sizeof(int32_t)); // copy the contents of data into Mr and add the zeros memcpy(Mr, data, this->k*sizeof(int32_t)); @@ -228,9 +243,10 @@ uint32_t RadioLibBCH::encode(uint32_t dataword) { } #if RADIOLIB_STATIC_ONLY - int32_t bb[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1]; + int32_t bb[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1] = { 0 }; #else int32_t* bb = new int32_t[this->n - this->k + 1]; + memset(bb, 0, (this->n - this->k + 1)*sizeof(int32_t)); #endif j = 0; for(int32_t i = start; i < end; ++i) { diff --git a/lib/RadioLib/src/utils/FEC.h b/lib/RadioLib/src/utils/FEC.h index 0716fe1..30ba17e 100644 --- a/lib/RadioLib/src/utils/FEC.h +++ b/lib/RadioLib/src/utils/FEC.h @@ -30,6 +30,11 @@ class RadioLibBCH { */ RadioLibBCH(); + /*! + \brief Default detructor. + */ + ~RadioLibBCH(); + /*! \brief Initialization method. \param n Code word length in bits, up to 32. @@ -47,19 +52,19 @@ class RadioLibBCH { uint32_t encode(uint32_t dataword); private: - uint8_t n; - uint8_t k; - uint32_t poly; - uint8_t m; + uint8_t n = 0; + uint8_t k = 0; + uint32_t poly = 0; + uint8_t m = 0; #if RADIOLIB_STATIC_ONLY - int32_t alphaTo[RADIOLIB_BCH_MAX_N + 1]; - int32_t indexOf[RADIOLIB_BCH_MAX_N + 1]; - int32_t generator[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1]; + int32_t alphaTo[RADIOLIB_BCH_MAX_N + 1] = { 0 }; + int32_t indexOf[RADIOLIB_BCH_MAX_N + 1] = { 0 }; + int32_t generator[RADIOLIB_BCH_MAX_N - RADIOLIB_BCH_MAX_K + 1] = { 0 }; #else - int32_t* alphaTo; - int32_t* indexOf; - int32_t* generator; + int32_t* alphaTo = nullptr; + int32_t* indexOf = nullptr; + int32_t* generator = nullptr; #endif };