From 77ed4452aed3e95e739ab521b0fd85dfc17a8c47 Mon Sep 17 00:00:00 2001 From: jgromes Date: Sun, 14 Apr 2024 20:22:55 +0200 Subject: [PATCH] [LR11x0] Added basic LR11x0 support (#679) --- .../LR11x0_Receive_Blocking.ino | 104 + .../LR11x0_Receive_Interrupt.ino | 138 + .../LR11x0_Transmit_Blocking.ino | 106 + .../LR11x0_Transmit_Interrupt.ino | 131 + keywords.txt | 3 + src/RadioLib.h | 1 + src/modules/LR11x0/LR1110.cpp | 75 + src/modules/LR11x0/LR1110.h | 95 + src/modules/LR11x0/LR11x0.cpp | 2255 +++++++++++++++++ src/modules/LR11x0/LR11x0.h | 1018 ++++++++ 10 files changed, 3926 insertions(+) create mode 100644 examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino create mode 100644 examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino create mode 100644 examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino create mode 100644 examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino create mode 100644 src/modules/LR11x0/LR1110.cpp create mode 100644 src/modules/LR11x0/LR1110.h create mode 100644 src/modules/LR11x0/LR11x0.cpp create mode 100644 src/modules/LR11x0/LR11x0.h diff --git a/examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino b/examples/LR11x0/LR11x0_Receive_Blocking/LR11x0_Receive_Blocking.ino new file mode 100644 index 00000000..e29806bd --- /dev/null +++ b/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 +// 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] 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/examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino b/examples/LR11x0/LR11x0_Receive_Interrupt/LR11x0_Receive_Interrupt.ino new file mode 100644 index 00000000..0539104a --- /dev/null +++ b/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 +// 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 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/examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino b/examples/LR11x0/LR11x0_Transmit_Blocking/LR11x0_Transmit_Blocking.ino new file mode 100644 index 00000000..93dab374 --- /dev/null +++ b/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 +// 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); + 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/examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino b/examples/LR11x0/LR11x0_Transmit_Interrupt/LR11x0_Transmit_Interrupt.ino new file mode 100644 index 00000000..6de582c6 --- /dev/null +++ b/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 +// 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; + +// 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/keywords.txt b/keywords.txt index 2fee7421..0ace7c6c 100644 --- a/keywords.txt +++ b/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 diff --git a/src/RadioLib.h b/src/RadioLib.h index 94b8016c..304dae6d 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -68,6 +68,7 @@ #include "modules/CC1101/CC1101.h" #include "modules/LLCC68/LLCC68.h" +#include "modules/LR11x0/LR1110.h" #include "modules/nRF24/nRF24.h" #include "modules/RF69/RF69.h" #include "modules/RFM2x/RFM22.h" diff --git a/src/modules/LR11x0/LR1110.cpp b/src/modules/LR11x0/LR1110.cpp new file mode 100644 index 00000000..a540ce5c --- /dev/null +++ b/src/modules/LR11x0/LR1110.cpp @@ -0,0 +1,75 @@ +#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, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + 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, preambleLength, tcxoVoltage); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + 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))); +} + +int16_t LR1110::setOutputPower(int8_t power, bool forceHighPower) { + // determine whether to use HP or LP PA and check range accordingly + bool useHp = forceHighPower || (power > 14); + if(useHp) { + RADIOLIB_CHECK_RANGE(power, -9, 22, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + useHp = true; + + } else { + RADIOLIB_CHECK_RANGE(power, -17, 14, RADIOLIB_ERR_INVALID_OUTPUT_POWER); + useHp = false; + + } + + // TODO how and when to configure OCP? + + // update PA config - always use VBAT for high-power PA + int16_t state = LR11x0::setPaConfig((uint8_t)useHp, (uint8_t)useHp, 0x04, 0x07); + RADIOLIB_ASSERT(state); + + // set output power + state = LR11x0::setTxParams(power, RADIOLIB_LR11X0_PA_RAMP_48U); + return(state); +} + +#endif \ No newline at end of file diff --git a/src/modules/LR11x0/LR1110.h b/src/modules/LR11x0/LR1110.h new file mode 100644 index 00000000..636724e6 --- /dev/null +++ b/src/modules/LR11x0/LR1110.h @@ -0,0 +1,95 @@ +#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); + + // 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); + + // 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); + + /*! + \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); + + /*! + \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. + Defaults to false. + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power, bool forceHighPower = false); + +#if !RADIOLIB_GODMODE + private: +#endif + +}; + +#endif + +#endif diff --git a/src/modules/LR11x0/LR11x0.cpp b/src/modules/LR11x0/LR11x0.cpp new file mode 100644 index 00000000..a2cfb53d --- /dev/null +++ b/src/modules/LR11x0/LR11x0.cpp @@ -0,0 +1,2255 @@ +#include "LR11x0.h" + +#include "../../utils/CRC.h" +#include "../../utils/Cryptography.h" + +#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, uint16_t preambleLength, float tcxoVoltage) { + // set module properties + 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->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 + state = config(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 = setPreambleLength(preambleLength); + RADIOLIB_ASSERT(state); + + // set publicly accessible settings that are not a part of begin method + // TODO looks like CRC does not work for SX127x + state = setCRC(0); + RADIOLIB_ASSERT(state); + + state = invertIQ(false); + RADIOLIB_ASSERT(state); + + return(RADIOLIB_ERR_NONE); +} + +int16_t LR11x0::beginGFSK(float br, float freqDev, float rxBw, uint16_t preambleLength, float tcxoVoltage) { + // set module properties + 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); + + return(RADIOLIB_ERR_NONE); +} + +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 + uint32_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); + } + + uint32_t timeout = 0; + + // get currently active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + if(modem == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + // calculate timeout (150% of expected time-on-air) + timeout = (getTimeOnAir(len) * 3) / 2; + + } else if(modem == RADIOLIB_LR11X0_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); + + // start transmission + state = startTransmit(data, len, addr); + RADIOLIB_ASSERT(state); + + // wait for packet transmission or timeout + uint32_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); + } + } + uint32_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); + + uint32_t timeout = 0; + + // get currently active modem + uint8_t modem = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + state = getPacketType(&modem); + 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 = (uint32_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 = (uint32_t)(((maxLen * 8.0) / brBps) * 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; + uint32_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::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); + + // TODO this will block BUSY forever + /*if(wakeup) { + // pull NSS low to wake up + this->mod->hal->digitalWrite(this->mod->getCs(), this->mod->hal->GpioLevelLow); + }*/ + + 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::setDio1Action(void (*func)(void)) { + this->mod->hal->attachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq()), func, this->mod->hal->GpioInterruptRising); +} + +void LR11x0::clearDio1Action() { + this->mod->hal->detachInterrupt(this->mod->hal->pinToInterrupt(this->mod->getIrq())); +} + +void LR11x0::setPacketReceivedAction(void (*func)(void)) { + this->setDio1Action(func); +} + +void LR11x0::clearPacketReceivedAction() { + this->clearDio1Action(); +} + +void LR11x0::setPacketSentAction(void (*func)(void)) { + this->setDio1Action(func); +} + +void LR11x0::clearPacketSentAction() { + this->clearDio1Action(); +} + +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 { + 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); + + // 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)); +} + +int16_t LR11x0::startReceive(uint32_t timeout, uint32_t irqFlags, size_t len) { + (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)) { + 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)) { + 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::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); + + // 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(uint8_t syncWord) { + return(setLoRaSyncWord(syncWord)); +} + +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)); + } + + 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, uint16_t initial, uint16_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; + return(setPacketParamsLoRa(this->preambleLengthLoRa, this->crcTypeLoRa, this->implicitLen, this->headerType, (uint8_t)this->invertIQEnabled)); + } + + return(RADIOLIB_ERR_WRONG_MODEM); +} + +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->crcTypeLoRa, this->implicitLen, this->headerType, (uint8_t)this->invertIQEnabled)); +} + +float LR11x0::getRSSI() { + float val = 0; + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + getPacketType(&type); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + getPacketStatusLoRa(&val, NULL, NULL); + + } else if(type == RADIOLIB_LR11X0_PACKET_TYPE_GFSK) { + getPacketStatusGFSK(NULL, &val, NULL, NULL); + + } + + return(val); +} + +float LR11x0::getSNR() { + float val = 0; + + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + getPacketType(&type); + if(type == RADIOLIB_LR11X0_PACKET_TYPE_LORA) { + 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); +} + +uint32_t LR11x0::getTimeOnAir(size_t len) { + // check active modem + uint8_t type = RADIOLIB_LR11X0_PACKET_TYPE_NONE; + 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 * 1000) / this->bitRateKbps); + + } + + return(0); +} + +float LR11x0::getDataRate() const { + return(this->dataRateMeasured); +} + +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); + + // TODO set some CAD parameters - will be overwritten when calling CAD anyway + + // 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 + state = 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); +} + +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)); + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + RADIOLIB_ASSERT(state); + + // convert endians + if(data) { + 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)]; + } + } + + 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 gnssCfg, uint8_t wifiCfg) { + uint8_t buff[7] = { en, stbyCfg, rxCfg, txCfg, txHpCfg, 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, 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)); + #if !RADIOLIB_STATIC_ONLY + delete[] rplBuff; + #endif + RADIOLIB_ASSERT(state); + + // convert endians + if(data) { + 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)]; + } + } + + 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 + // 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 + // TODO do the value conversion for RSSI (fixed point?) + if(rssiSync) { *rssiSync = (float)buff[0]; } + if(rssiAvg) { *rssiAvg = (float)buff[1]; } + 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, false, 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 + }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_SCAN, true, buff, sizeof(buff))); +} + +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::wifiGetNbResults(uint8_t* nbResults) { + uint8_t buff[1] = { 0 }; + int16_t state = this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_GET_NB_RESULTS, false, buff, sizeof(buff)); + + // pass the replies + if(nbResults) { *nbResults = buff[0]; } + + return(state); +} + +int16_t LR11x0::wifiReadResults(uint8_t index, uint8_t nbResults, uint8_t format, uint8_t* results) { + uint8_t reqBuff[3] = { index, nbResults, format }; + return(this->SPIcommand(RADIOLIB_LR11X0_CMD_WIFI_READ_RESULTS, false, results, nbResults, reqBuff, sizeof(reqBuff))); +} + +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 = (lat*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 = (lat*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, 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/src/modules/LR11x0/LR11x0.h b/src/modules/LR11x0/LR11x0.h new file mode 100644 index 00000000..88c4eee7 --- /dev/null +++ b/src/modules/LR11x0/LR11x0.h @@ -0,0 +1,1018 @@ +#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 0.9536743164 +#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 + +// 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 (0x8001E848UL) // 31 0 LR FHSS bit rate: 488.28215 bps +#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 + +// 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 + +// 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 + + +/*! + \class LR11x0 + \brief +*/ +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. + */ + 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 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, 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 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, uint16_t preambleLength, 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 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 DIO1 activates. + \param func ISR to call. + */ + void setDio1Action(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when DIO1 activates. + */ + void clearDio1Action(); + + /*! + \brief Sets interrupt service routine to call when a packet is received. + \param func ISR to call. + */ + void setPacketReceivedAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a packet is received. + */ + void clearPacketReceivedAction(); + + /*! + \brief Sets interrupt service routine to call when a packet is sent. + \param func ISR to call. + */ + void setPacketSentAction(void (*func)(void)); + + /*! + \brief Clears interrupt service routine to call when a packet is sent. + */ + void clearPacketSentAction(); + + /*! + \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(); + + /*! + \brief Interrupt-driven receive method. DIO1 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 DIO1. + + \param irqFlags Sets the IRQ flags that will trigger DIO1, defaults to RADIOLIB_LR11X0_IRQ_RX_DONE. + \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, 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; + + // configuration methods + + /*! + \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 sync word. + \param syncWord LoRa sync word to be set. + \returns \ref status_codes + */ + int16_t setSyncWord(uint8_t syncWord); + + /*! + \brief Sets preamble length for LoRa or FSK modem. Allowed values range from 1 to 65535. + \param preambleLength Preamble length to be set in symbols (LoRa) or bits (FSK). + \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. FSK only. Defaults to 0x1D0F (CCIT CRC). + \param polynomial Polynomial for CRC calculation. FSK only. Defaults to 0x1021 (CCIT CRC). + \param inverted Invert CRC bytes. FSK only. Defaults to true (CCIT CRC). + \returns \ref status_codes + */ + int16_t setCRC(uint8_t len, uint16_t initial = 0x1D0F, uint16_t polynomial = 0x1021, 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(); + + /*! + \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(); + + /*! + \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. + */ + uint32_t getTimeOnAir(size_t len) override; + + /*! + \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; + +#if !RADIOLIB_GODMODE && !RADIOLIB_LOW_LEVEL + protected: +#endif + Module* getMod(); + + // 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 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, 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; + +#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 + float bitRateKbps = 0; + uint8_t bitRate = 0; + uint8_t preambleDetLength = 0, rxBandwidth = 0, pulseShape = 0, crcTypeGFSK = 0, syncWordLength = 0, addrComp = 0, whitening = 0, packetType = 0; + uint16_t preambleLengthGFSK = 0; + + float dataRateMeasured = 0; + + static int16_t SPIparseStatus(uint8_t in); + static int16_t SPIcheckStatus(Module* mod); + bool findChip(uint8_t ver); + int16_t config(uint8_t modem); + + // 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, 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