diff --git a/examples/SX128x/SX128x_Channel_Activity_Detection/SX128x_Channel_Activity_Detection.ino b/examples/SX128x/SX128x_Channel_Activity_Detection/SX128x_Channel_Activity_Detection.ino new file mode 100644 index 00000000..6ec30377 --- /dev/null +++ b/examples/SX128x/SX128x_Channel_Activity_Detection/SX128x_Channel_Activity_Detection.ino @@ -0,0 +1,72 @@ +/* + RadioLib SX128x Channel Activity Detection Example + + This example uses SX1280 to scan the current LoRa + channel and detect ongoing LoRa transmissions. + + Other modules from SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[SX1280] Scanning channel for LoRa transmission ... ")); + + // start scanning current channel + int state = lora.scanChannel(); + + if (state == LORA_DETECTED) { + // LoRa preamble was detected + Serial.println(F("detected!")); + + } else if (state == CHANNEL_FREE) { + // no preamble was detected, channel is free + Serial.println(F("channel is free!")); + + } else { + // some other error occurred + Serial.print(F("failed, code ")); + Serial.println(state); + + } + + // wait 100 ms before new scan + delay(100); +} diff --git a/examples/SX128x/SX128x_GFSK_Modem/SX128x_GFSK_Modem.ino b/examples/SX128x/SX128x_GFSK_Modem/SX128x_GFSK_Modem.ino new file mode 100644 index 00000000..9b93c481 --- /dev/null +++ b/examples/SX128x/SX128x_GFSK_Modem/SX128x_GFSK_Modem.ino @@ -0,0 +1,116 @@ +/* + RadioLib SX128x GFSK Modem Example + + This example shows how to use GFSK modem in SX128x chips. + + NOTE: The sketch below is just a guide on how to use + GFSK modem, so this code should not be run directly! + Instead, modify the other examples to use GFSK + modem and use the appropriate configuration + methods. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 gfsk = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bit rate: 800 kbps + // frequency deviation: 400.0 kHz + // output power: 10 dBm + // preamble length: 16 bits + // coding rate: 7 + // data shaping: Gaussian, BT = 0.5 + // sync word: 0x2D 0x01 + // CRC: enabled, CRC16 (CCIT) + int state = gfsk.beginGFSK(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // if needed, you can switch between LoRa and FSK modes + // + // gfsk.begin() start LoRa mode (and disable GFSK) + // lora.beginGFSK() start GFSK mode (and disable LoRa) + + // the following settings can also + // be modified at run-time + state = gfsk.setFrequency(2410.5); + state = gfsk.setBitRate(200); + state = gfsk.setFrequencyDeviation(100.0); + state = gfsk.setRxBandwidth(250.0); + state = gfsk.setOutputPower(5); + state = gfsk.setDataShaping(1.0); + uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67, 0x89}; + state = gfsk.setSyncWord(syncWord, 5); + if (state != ERR_NONE) { + Serial.print(F("Unable to set configuration, code ")); + Serial.println(state); + while (true); + } + + #warning "This sketch is just an API guide! Read the note at line 6." +} + +void loop() { + // GFSK modem can use the same transmit/receive methods + // as the LoRa modem, even their interrupt-driven versions + + // transmit GFSK packet + int state = gfsk.transmit("Hello World!"); + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = gfsk.transmit(byteArr, 8); + */ + if (state == ERR_NONE) { + Serial.println(F("[SX1280] Packet transmitted successfully!")); + } else if (state == ERR_PACKET_TOO_LONG) { + Serial.println(F("[SX1280] Packet too long!")); + } else if (state == ERR_TX_TIMEOUT) { + Serial.println(F("[SX1280] Timed out while transmitting!")); + } else { + Serial.println(F("[SX1280] Failed to transmit packet, code ")); + Serial.println(state); + } + + // receive GFSK packet + String str; + state = gfsk.receive(str); + /* + byte byteArr[8]; + int state = gfsk.receive(byteArr, 8); + */ + if (state == ERR_NONE) { + Serial.println(F("[SX1280] Received packet!")); + Serial.print(F("[SX1280] Data:\t")); + Serial.println(str); + } else if (state == ERR_RX_TIMEOUT) { + Serial.println(F("[SX1280] Timed out while waiting for packet!")); + } else { + Serial.print(F("[SX1280] Failed to receive packet, code ")); + Serial.println(state); + } +} diff --git a/examples/SX128x/SX128x_Receive/SX128x_Receive.ino b/examples/SX128x/SX128x_Receive/SX128x_Receive.ino new file mode 100644 index 00000000..0427cf41 --- /dev/null +++ b/examples/SX128x/SX128x_Receive/SX128x_Receive.ino @@ -0,0 +1,106 @@ +/* + RadioLib SX128x Receive Example + + This example listens for LoRa transmissions using SX126x 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 SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[SX1280] Waiting for incoming transmission ... ")); + + // you can receive data as an Arduino String + // NOTE: receive() is a blocking method! + // See example ReceiveInterrupt for details + // on non-blocking reception method. + String str; + int state = lora.receive(str); + + // you can also receive data as byte array + /* + byte byteArr[8]; + int state = lora.receive(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("success!")); + + // print the data of the packet + Serial.print(F("[SX1280] Data:\t\t")); + Serial.println(str); + + // print the RSSI (Received Signal Strength Indicator) + // of the last received packet + Serial.print(F("[SX1280] RSSI:\t\t")); + Serial.print(lora.getRSSI()); + Serial.println(F(" dBm")); + + // print the SNR (Signal-to-Noise Ratio) + // of the last received packet + Serial.print(F("[SX1280] SNR:\t\t")); + Serial.print(lora.getSNR()); + Serial.println(F(" dB")); + + } else if (state == ERR_RX_TIMEOUT) { + // timeout occurred while waiting for a packet + Serial.println(F("timeout!")); + + } else if (state == 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/SX128x/SX128x_Receive_Interrupt/SX128x_Receive_Interrupt.ino b/examples/SX128x/SX128x_Receive_Interrupt/SX128x_Receive_Interrupt.ino new file mode 100644 index 00000000..2d6f1664 --- /dev/null +++ b/examples/SX128x/SX128x_Receive_Interrupt/SX128x_Receive_Interrupt.ino @@ -0,0 +1,159 @@ +/* + RadioLib SX128x 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 SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == 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 + lora.setDio1Action(setFlag); + + // start listening for LoRa packets + Serial.print(F("[SX1280] Starting to listen ... ")); + state = lora.startReceive(); + if (state == 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: + // + // lora.standby() + // lora.sleep() + // lora.transmit(); + // lora.receive(); + // lora.readData(); + // lora.scanChannel(); +} + +// flag to indicate that a packet was received +volatile bool receivedFlag = false; + +// disable interrupt when it's not needed +volatile bool enableInterrupt = true; + +// 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! +void setFlag(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we got a packet, set the flag + receivedFlag = true; +} + +void loop() { + // check if the flag is set + if(receivedFlag) { + // disable the interrupt service routine while + // processing the data + enableInterrupt = false; + + // reset flag + receivedFlag = false; + + // you can read received data as an Arduino String + String str; + int state = lora.readData(str); + + // you can also read received data as byte array + /* + byte byteArr[8]; + int state = lora.readData(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("[SX1280] Received packet!")); + + // print data of the packet + Serial.print(F("[SX1280] Data:\t\t")); + Serial.println(str); + + // print RSSI (Received Signal Strength Indicator) + Serial.print(F("[SX1280] RSSI:\t\t")); + Serial.print(lora.getRSSI()); + Serial.println(F(" dBm")); + + // print SNR (Signal-to-Noise Ratio) + Serial.print(F("[SX1280] SNR:\t\t")); + Serial.print(lora.getSNR()); + Serial.println(F(" dB")); + + } else if (state == 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); + + } + + // put module back to listen mode + lora.startReceive(); + + // we're ready to receive more packets, + // enable interrupt service routine + enableInterrupt = true; + } + +} diff --git a/examples/SX128x/SX128x_Settings/SX128x_Settings.ino b/examples/SX128x/SX128x_Settings/SX128x_Settings.ino new file mode 100644 index 00000000..49d218d6 --- /dev/null +++ b/examples/SX128x/SX128x_Settings/SX128x_Settings.ino @@ -0,0 +1,134 @@ +/* + RadioLib SX128x Settings Example + + This example shows how to change all the properties of LoRa transmission. + RadioLib currently supports the following settings: + - pins (SPI slave select, DIO1, DIO2, BUSY pin) + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - output power during transmission + - CRC + - preamble length + + Other modules from SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 loraSX1280 = new Module(10, 2, 3, 9); + +// SX1280 has the following connections: +// NSS pin: 8 +// DIO1 pin: 4 +// NRST pin: 5 +// BUSY pin: 6 +SX1281 loraSX1281 = new Module(8, 4, 5, 6); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1282 loraSX1282 = RadioShield.ModuleB; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = loraSX1280.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // initialize the second LoRa instance with + // non-default settings + // this LoRa link will have high data rate, + // but lower range + Serial.print(F("[SX1281] Initializing ... ")); + // carrier frequency: 2450.0 MHz + // bandwidth: 1625.0 kHz + // spreading factor: 7 + // coding rate: 5 + // output power: 2 dBm + // preamble length: 20 symbols + // CRC: enabled + state = loraSX1281.begin(2450.0, 1625.0, 7, 5, 2, 20); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } + + // you can also change the settings at runtime + // and check if the configuration was changed successfully + + // set carrier frequency to 2410.5 MHz + if (loraSX1280.setFrequency(2410.5) == ERR_INVALID_FREQUENCY) { + Serial.println(F("Selected frequency is invalid for this module!")); + while (true); + } + + // set bandwidth to 203.125 kHz + if (loraSX1280.setBandwidth(203.125) == ERR_INVALID_BANDWIDTH) { + Serial.println(F("Selected bandwidth is invalid for this module!")); + while (true); + } + + // set spreading factor to 10 + if (loraSX1280.setSpreadingFactor(10) == ERR_INVALID_SPREADING_FACTOR) { + Serial.println(F("Selected spreading factor is invalid for this module!")); + while (true); + } + + // set coding rate to 6 + if (loraSX1280.setCodingRate(6) == ERR_INVALID_CODING_RATE) { + Serial.println(F("Selected coding rate is invalid for this module!")); + while (true); + } + + // set output power to -2 dBm + if (loraSX1280.setOutputPower(-2) == ERR_INVALID_OUTPUT_POWER) { + Serial.println(F("Selected output power is invalid for this module!")); + while (true); + } + + // set LoRa preamble length to 16 symbols (accepted range is 2 - 65535) + if (loraSX1280.setPreambleLength(16) == ERR_INVALID_PREAMBLE_LENGTH) { + Serial.println(F("Selected preamble length is invalid for this module!")); + while (true); + } + + // disable CRC + if (loraSX1280.setCRC(false) == ERR_INVALID_CRC_CONFIGURATION) { + Serial.println(F("Selected CRC is invalid for this module!")); + while (true); + } + + Serial.println(F("All settings succesfully changed!")); +} + +void loop() { + // nothing here +} diff --git a/examples/SX128x/SX128x_Transmit/SX128x_Transmit.ino b/examples/SX128x/SX128x_Transmit/SX128x_Transmit.ino new file mode 100644 index 00000000..e8e777dc --- /dev/null +++ b/examples/SX128x/SX128x_Transmit/SX128x_Transmit.ino @@ -0,0 +1,85 @@ +/* + RadioLib SX128x Transmit Example + + This example transmits packets using SX1280 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 SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == ERR_NONE) { + Serial.println(F("success!")); + } else { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + Serial.print(F("[SX1280] Transmitting packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + // NOTE: transmit() is a blocking method! + // See example SX128x_Transmit_Interrupt for details + // on non-blocking transmission method. + int state = lora.transmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, 0x78, 0xAB, 0xCD, 0xEF}; + int state = lora.transmit(byteArr, 8); + */ + + if (state == ERR_NONE) { + // the packet was successfully transmitted + Serial.println(F("success!")); + + } else if (state == ERR_PACKET_TOO_LONG) { + // the supplied packet was longer than 256 bytes + Serial.println(F("too long!")); + + } 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/SX128x/SX128x_Transmit_Interrupt/SX128x_Transmit_Interrupt.ino b/examples/SX128x/SX128x_Transmit_Interrupt/SX128x_Transmit_Interrupt.ino new file mode 100644 index 00000000..284244e8 --- /dev/null +++ b/examples/SX128x/SX128x_Transmit_Interrupt/SX128x_Transmit_Interrupt.ino @@ -0,0 +1,135 @@ +/* + RadioLib SX128x 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 SX128x family can also be used. + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ +*/ + +// include the library +#include + +// SX1280 has the following connections: +// NSS pin: 10 +// DIO1 pin: 2 +// NRST pin: 3 +// BUSY pin: 9 +SX1280 lora = new Module(10, 2, 3, 9); + +// or using RadioShield +// https://github.com/jgromes/RadioShield +//SX1280 lora = RadioShield.ModuleA; + +// save transmission state between loops +int transmissionState = ERR_NONE; + +void setup() { + Serial.begin(9600); + + // initialize SX1280 with default settings + Serial.print(F("[SX1280] Initializing ... ")); + // carrier frequency: 2400.0 MHz + // bandwidth: 812.5 kHz + // spreading factor: 9 + // coding rate: 7 + // output power: 10 dBm + // preamble length: 12 symbols + // CRC: enabled + int state = lora.begin(); + if (state == 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 + lora.setDio1Action(setFlag); + + // start transmitting the first packet + Serial.print(F("[SX1280] Sending first packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = lora.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 = lora.startTransmit(byteArr, 8); + */ +} + +// flag to indicate that a packet was sent +volatile bool transmittedFlag = false; + +// disable interrupt when it's not needed +volatile bool enableInterrupt = true; + +// 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! +void setFlag(void) { + // check if the interrupt is enabled + if(!enableInterrupt) { + return; + } + + // we sent a packet, set the flag + transmittedFlag = true; +} + +void loop() { + // check if the previous transmission finished + if(transmittedFlag) { + // disable the interrupt service routine while + // processing the data + enableInterrupt = false; + + // reset flag + transmittedFlag = false; + + if (transmissionState == ERR_NONE) { + // packet was successfully sent + Serial.println(F("transmission finished!")); + + } else { + Serial.print(F("failed, code ")); + Serial.println(transmissionState); + + } + + // wait a second before transmitting again + delay(1000); + + // send another one + Serial.print(F("[SX1280] Sending another packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + transmissionState = lora.startTransmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + int state = lora.startTransmit(byteArr, 8); + */ + + // we're ready to send more packets, + // enable interrupt service routine + enableInterrupt = true; + } +} diff --git a/keywords.txt b/keywords.txt index 57b8b822..d42b2c73 100644 --- a/keywords.txt +++ b/keywords.txt @@ -37,6 +37,9 @@ SX1276 KEYWORD1 SX1277 KEYWORD1 SX1278 KEYWORD1 SX1279 KEYWORD1 +SX1280 KEYWORD1 +SX1281 KEYWORD1 +SX1282 KEYWORD1 XBee KEYWORD1 XBeeSerial KEYWORD1 @@ -209,6 +212,9 @@ sendHeader KEYWORD2 sendLine KEYWORD2 getPictureHeight KEYWORD2 +# SX128x +beginGFSK KEYWORD2 + ####################################### # Constants (LITERAL1) ####################################### diff --git a/src/RadioLib.h b/src/RadioLib.h index ae4a6909..d4911d51 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -14,6 +14,7 @@ - Si443x FSK module - SX126x LoRa/FSK module - SX127x LoRa/FSK module + - SX128x LoRa/GFSK/BLE/FLRC module - SX1231 FSK module - XBee module (S2B) - PhysicalLayer protocols @@ -77,6 +78,9 @@ #include "modules/SX127x/SX1277.h" #include "modules/SX127x/SX1278.h" #include "modules/SX127x/SX1279.h" +#include "modules/SX128x/SX1280.h" +#include "modules/SX128x/SX1281.h" +#include "modules/SX128x/SX1282.h" #include "modules/XBee/XBee.h" // physical layer protocols @@ -110,7 +114,6 @@ \brief Library control object when using RadioShield. Contains two pre-configured "modules", which correspond to the slots on shield. */ - class Radio { public: diff --git a/src/modules/SX128x/SX1280.cpp b/src/modules/SX128x/SX1280.cpp new file mode 100644 index 00000000..afbc96df --- /dev/null +++ b/src/modules/SX128x/SX1280.cpp @@ -0,0 +1,5 @@ +#include "SX1280.h" + +SX1280::SX1280(Module* mod) : SX1281(mod) { + +} diff --git a/src/modules/SX128x/SX1280.h b/src/modules/SX128x/SX1280.h new file mode 100644 index 00000000..08aa1ec1 --- /dev/null +++ b/src/modules/SX128x/SX1280.h @@ -0,0 +1,31 @@ +#ifndef _RADIOLIB_SX1280_H +#define _RADIOLIB_SX1280_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "SX128x.h" +#include "SX1281.h" + +// TODO implement ranging + +/*! + \class SX1280 + + \brief Derived class for %SX1280 modules. +*/ +class SX1280: public SX1281 { + public: + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ + SX1280(Module* mod); + +#ifndef RADIOLIB_GODMODE + private: +#endif + +}; + +#endif diff --git a/src/modules/SX128x/SX1281.cpp b/src/modules/SX128x/SX1281.cpp new file mode 100644 index 00000000..4aa09b0d --- /dev/null +++ b/src/modules/SX128x/SX1281.cpp @@ -0,0 +1,5 @@ +#include "SX1281.h" + +SX1281::SX1281(Module* mod) : SX128x(mod) { + +} diff --git a/src/modules/SX128x/SX1281.h b/src/modules/SX128x/SX1281.h new file mode 100644 index 00000000..88037a09 --- /dev/null +++ b/src/modules/SX128x/SX1281.h @@ -0,0 +1,28 @@ +#ifndef _RADIOLIB_SX1281_H +#define _RADIOLIB_SX1281_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "SX128x.h" + +/*! + \class SX1281 + + \brief Derived class for %SX1281 modules. +*/ +class SX1281: public SX128x { + public: + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ + SX1281(Module* mod); + +#ifndef RADIOLIB_GODMODE + private: +#endif + +}; + +#endif diff --git a/src/modules/SX128x/SX1282.cpp b/src/modules/SX128x/SX1282.cpp new file mode 100644 index 00000000..847a36d1 --- /dev/null +++ b/src/modules/SX128x/SX1282.cpp @@ -0,0 +1,5 @@ +#include "SX1282.h" + +SX1282::SX1282(Module* mod) : SX1280(mod) { + +} diff --git a/src/modules/SX128x/SX1282.h b/src/modules/SX128x/SX1282.h new file mode 100644 index 00000000..09f92075 --- /dev/null +++ b/src/modules/SX128x/SX1282.h @@ -0,0 +1,31 @@ +#ifndef _RADIOLIB_SX1282_H +#define _RADIOLIB_SX1282_H + +#include "../../TypeDef.h" +#include "../../Module.h" +#include "SX128x.h" +#include "SX1280.h" + +// TODO implement advanced ranging + +/*! + \class SX1282 + + \brief Derived class for %SX1282 modules. +*/ +class SX1282: public SX1280 { + public: + /*! + \brief Default constructor. + + \param mod Instance of Module that will be used to communicate with the radio. + */ + SX1282(Module* mod); + +#ifndef RADIOLIB_GODMODE + private: +#endif + +}; + +#endif diff --git a/src/modules/SX128x/SX128x.cpp b/src/modules/SX128x/SX128x.cpp new file mode 100644 index 00000000..a11b1788 --- /dev/null +++ b/src/modules/SX128x/SX128x.cpp @@ -0,0 +1,1208 @@ +#include "SX128x.h" + +SX128x::SX128x(Module* mod) : PhysicalLayer(SX128X_FREQUENCY_STEP_SIZE, SX128X_MAX_PACKET_LENGTH) { + _mod = mod; +} + +int16_t SX128x::begin(float freq, float bw, uint8_t sf, uint8_t cr, int8_t power, uint16_t preambleLength) { + // set module properties + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getGpio(), INPUT); + + // initialize LoRa modulation variables + _bwKhz = bw; + _sf = SX128X_LORA_SF_9; + _cr = SX128X_LORA_CR_4_7; + + // initialize LoRa packet variables + _preambleLengthLoRa = preambleLength; + _headerType = SX128X_LORA_HEADER_EXPLICIT; + _payloadLen = 0xFF; + _crcLoRa = SX128X_LORA_CRC_ON; + + // reset the module and verify startup + int16_t state = reset(); + RADIOLIB_ASSERT(state); + + // set mode to standby + state = standby(); + RADIOLIB_ASSERT(state); + + // configure settings not accessible by API + state = config(SX128X_PACKET_TYPE_LORA); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setBandwidth(bw); + RADIOLIB_ASSERT(state); + + state = setSpreadingFactor(sf); + RADIOLIB_ASSERT(state); + + state = setCodingRate(cr); + RADIOLIB_ASSERT(state); + + state = setPreambleLength(preambleLength); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t SX128x::beginGFSK(float freq, uint16_t br, float freqDev, int8_t power, uint16_t preambleLength, float dataShaping) { + // set module properties + _mod->init(RADIOLIB_USE_SPI); + Module::pinMode(_mod->getIrq(), INPUT); + Module::pinMode(_mod->getGpio(), INPUT); + + // initialize GFSK modulation variables + _brKbps = br; + _br = SX128X_BLE_GFSK_BR_0_800_BW_2_4; + _modIndexReal = 1.0; + _modIndex = SX128X_BLE_GFSK_MOD_IND_1_00; + _shaping = SX128X_BLE_GFSK_BT_0_5; + + // initialize GFSK packet variables + _preambleLengthGFSK = preambleLength; + _syncWordLen = 2; + _syncWordMatch = SX128X_GFSK_FLRC_SYNC_WORD_1; + _crcGFSK = SX128X_GFSK_FLRC_CRC_2_BYTE; + _whitening = SX128X_GFSK_BLE_WHITENING_ON; + + // reset the module and verify startup + int16_t state = reset(); + RADIOLIB_ASSERT(state); + + // set mode to standby + state = standby(); + RADIOLIB_ASSERT(state); + + // configure settings not accessible by API + state = config(SX128X_PACKET_TYPE_GFSK); + RADIOLIB_ASSERT(state); + + // configure publicly accessible settings + state = setFrequency(freq); + RADIOLIB_ASSERT(state); + + state = setBitRate(br); + RADIOLIB_ASSERT(state); + + state = setFrequencyDeviation(freqDev); + RADIOLIB_ASSERT(state); + + state = setOutputPower(power); + RADIOLIB_ASSERT(state); + + state = setPreambleLength(preambleLength); + RADIOLIB_ASSERT(state); + + state = setDataShaping(dataShaping); + RADIOLIB_ASSERT(state); + + // set publicly accessible settings that are not a part of begin method + uint8_t sync[] = { 0x2D, 0x01 }; + state = setSyncWord(sync, 2); + RADIOLIB_ASSERT(state); + + return(state); +} + +int16_t SX128x::reset(bool verify) { + // run the reset sequence - same as SX126x, as SX128x docs don't seem to mention this + Module::pinMode(_mod->getRst(), OUTPUT); + Module::digitalWrite(_mod->getRst(), LOW); + delay(1); + Module::digitalWrite(_mod->getRst(), HIGH); + + // return immediately when verification is disabled + if(!verify) { + return(ERR_NONE); + } + + // set mode to standby + uint32_t start = millis(); + while(true) { + // try to set mode to standby + int16_t state = standby(); + if(state == ERR_NONE) { + // standby command successful + return(ERR_NONE); + } + + // standby command failed, check timeout and try again + if(millis() - start >= 3000) { + // timed out, possibly incorrect wiring + return(state); + } + + // wait a bit to not spam the module + delay(10); + } +} + +int16_t SX128x::transmit(uint8_t* data, size_t len, uint8_t addr) { + // check packet length + if(len > SX128X_MAX_PACKET_LENGTH) { + return(ERR_PACKET_TOO_LONG); + } + + // check active modem + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_RANGING) { + return(ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // calculate timeout (500% of expected time-on-air) + uint32_t timeout = getTimeOnAir(len) * 5; + + RADIOLIB_DEBUG_PRINT(F("Timeout in ")); + RADIOLIB_DEBUG_PRINT(timeout); + RADIOLIB_DEBUG_PRINTLN(F(" us")); + + // start transmission + state = startTransmit(data, len, addr); + RADIOLIB_ASSERT(state); + + // wait for packet transmission or timeout + uint32_t start = micros(); + while(!digitalRead(_mod->getIrq())) { + yield(); + if(micros() - start > timeout) { + clearIrqStatus(); + standby(); + return(ERR_TX_TIMEOUT); + } + } + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set mode to standby to disable transmitter + state = standby(); + + return(state); +} + +int16_t SX128x::receive(uint8_t* data, size_t len) { + // check active modem + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_RANGING) { + return(ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // calculate timeout (1000% of expected time-on-air) + uint32_t timeout = getTimeOnAir(len) * 10; + + RADIOLIB_DEBUG_PRINT(F("Timeout in ")); + RADIOLIB_DEBUG_PRINT(timeout); + RADIOLIB_DEBUG_PRINTLN(F(" us")); + + // start reception + uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); + state = startReceive(timeoutValue); + RADIOLIB_ASSERT(state); + + // wait for packet reception or timeout + uint32_t start = micros(); + while(!digitalRead(_mod->getIrq())) { + yield(); + if(micros() - start > timeout) { + clearIrqStatus(); + standby(); + return(ERR_RX_TIMEOUT); + } + } + + // read the received data + return(readData(data, len)); +} + +int16_t SX128x::transmitDirect(uint32_t frf) { + // user requested to start transmitting immediately (required for RTTY) + int16_t state = ERR_NONE; + if(frf != 0) { + state = setRfFrequency(frf); + } + RADIOLIB_ASSERT(state); + + // start transmitting + return(SPIwriteCommand(SX128X_CMD_SET_TX_CONTINUOUS_WAVE, NULL, 0)); +} + +int16_t SX128x::receiveDirect() { + // SX128x is unable to output received data directly + return(ERR_UNKNOWN); +} + +int16_t SX128x::scanChannel() { + // check active modem + if(getPacketType() != SX128X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // set DIO pin mapping + state = setDioIrqParams(SX128X_IRQ_CAD_DETECTED | SX128X_IRQ_CAD_DONE, SX128X_IRQ_CAD_DETECTED | SX128X_IRQ_CAD_DONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set mode to CAD + state = setCad(); + RADIOLIB_ASSERT(state); + + // wait for channel activity detected or timeout + while(!digitalRead(_mod->getIrq())) { + yield(); + } + + // check CAD result + uint16_t cadResult = getIrqStatus(); + if(cadResult & SX128X_IRQ_CAD_DETECTED) { + // detected some LoRa activity + clearIrqStatus(); + return(LORA_DETECTED); + } else if(cadResult & SX128X_IRQ_CAD_DONE) { + // channel is free + clearIrqStatus(); + return(CHANNEL_FREE); + } + + return(ERR_UNKNOWN); +} + +int16_t SX128x::sleep(bool retainConfig) { + uint8_t sleepConfig = SX128X_SLEEP_DATA_BUFFER_RETAIN | SX128X_SLEEP_DATA_RAM_RETAIN; + if(!retainConfig) { + sleepConfig = SX128X_SLEEP_DATA_BUFFER_FLUSH | SX128X_SLEEP_DATA_RAM_FLUSH; + } + int16_t state = SPIwriteCommand(SX128X_CMD_SET_SLEEP, &sleepConfig, 1, false); + + // wait for SX128x to safely enter sleep mode + delay(1); + + return(state); +} + +int16_t SX128x::standby() { + return(SX128x::standby(SX128X_STANDBY_RC)); +} + +int16_t SX128x::standby(uint8_t mode) { + uint8_t data[] = { mode }; + return(SPIwriteCommand(SX128X_CMD_SET_STANDBY, data, 1)); +} + +void SX128x::setDio1Action(void (*func)(void)) { + attachInterrupt(digitalPinToInterrupt(_mod->getIrq()), func, RISING); +} + +void SX128x::clearDio1Action() { + detachInterrupt(digitalPinToInterrupt(_mod->getIrq())); +} + +int16_t SX128x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { + // suppress unused variable warning + (void)addr; + + // check packet length + if(len > SX128X_MAX_PACKET_LENGTH) { + return(ERR_PACKET_TOO_LONG); + } + + // set packet Length + int16_t state = ERR_NONE; + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_LORA) { + state = setPacketParamsLoRa(_preambleLengthLoRa, _headerType, len, _crcLoRa); + } else if(modem == SX128X_PACKET_TYPE_GFSK) { + state = setPacketParamsGFSK(_preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening, len); + } else { + return(ERR_WRONG_MODEM); + } + RADIOLIB_ASSERT(state); + + // update output power + state = setTxParams(_pwr); + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // write packet to buffer + state = writeBuffer(data, len); + RADIOLIB_ASSERT(state); + + // set DIO mapping + state = setDioIrqParams(SX128X_IRQ_TX_DONE | SX128X_IRQ_RX_TX_TIMEOUT, SX128X_IRQ_TX_DONE); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // start transmission + state = setTx(SX128X_TX_TIMEOUT_NONE); + RADIOLIB_ASSERT(state); + + // wait for BUSY to go low (= PA ramp up done) + while(digitalRead(_mod->getGpio())) { + yield(); + } + + return(state); +} + +int16_t SX128x::startReceive(uint16_t timeout) { + // check active modem + if(getPacketType() == SX128X_PACKET_TYPE_RANGING) { + return(ERR_WRONG_MODEM); + } + + // set DIO mapping + int16_t state = setDioIrqParams(SX128X_IRQ_RX_DONE | SX128X_IRQ_RX_TX_TIMEOUT | SX128X_IRQ_CRC_ERROR | SX128X_IRQ_HEADER_ERROR, SX128X_IRQ_RX_DONE); + RADIOLIB_ASSERT(state); + + // set buffer pointers + state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + RADIOLIB_ASSERT(state); + + // set implicit mode and expected len if applicable + if((_headerType == SX128X_LORA_HEADER_IMPLICIT) && (getPacketType() == SX128X_PACKET_TYPE_LORA)) { + state = setPacketParamsLoRa(_preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa); + RADIOLIB_ASSERT(state); + } + + // set mode to receive + state = setRx(timeout); + + return(state); +} + +int16_t SX128x::readData(uint8_t* data, size_t len) { + // set mode to standby + int16_t state = standby(); + RADIOLIB_ASSERT(state); + + // check integrity CRC + uint16_t irq = getIrqStatus(); + int16_t crcState = ERR_NONE; + if((irq & SX128X_IRQ_CRC_ERROR) || (irq & SX128X_IRQ_HEADER_ERROR)) { + crcState = ERR_CRC_MISMATCH; + } + + // get packet length + size_t length = len; + if(len == SX128X_MAX_PACKET_LENGTH) { + length = getPacketLength(); + } + + // read packet data + state = readBuffer(data, length); + RADIOLIB_ASSERT(state); + + // clear interrupt flags + state = clearIrqStatus(); + + // 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 SX128x::setFrequency(float freq) { + RADIOLIB_CHECK_RANGE(freq, 2400.0, 2500.0, ERR_INVALID_FREQUENCY); + + // calculate raw value + uint32_t frf = (freq * (uint32_t(1) << SX128X_DIV_EXPONENT)) / SX128X_CRYSTAL_FREQ; + return(setRfFrequency(frf)); +} + +int16_t SX128x::setBandwidth(float bw) { + // check active modem + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_LORA) { + // check range for LoRa + RADIOLIB_CHECK_RANGE(bw, 203.125, 1625.0, ERR_INVALID_BANDWIDTH); + } else if(modem == SX128X_PACKET_TYPE_RANGING) { + // check range for ranging + RADIOLIB_CHECK_RANGE(bw, 406.25, 1625.0, ERR_INVALID_BANDWIDTH); + } else { + return(ERR_WRONG_MODEM); + } + + if(abs(bw - 203.125) <= 0.001) { + _bw = SX128X_LORA_BW_203_125; + } else if(abs(bw - 406.25) <= 0.001) { + _bw = SX128X_LORA_BW_406_25; + } else if(abs(bw - 812.5) <= 0.001) { + _bw = SX128X_LORA_BW_812_50; + } else if(abs(bw - 1625.0) <= 0.001) { + _bw = SX128X_LORA_BW_1625_00; + } else { + return(ERR_INVALID_BANDWIDTH); + } + + // update modulation parameters + _bwKhz = bw; + return(setModulationParams(_sf, _bw, _cr)); +} + +int16_t SX128x::setSpreadingFactor(uint8_t sf) { + // check active modem + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_LORA) { + // check range for LoRa + RADIOLIB_CHECK_RANGE(sf, 5, 12, ERR_INVALID_SPREADING_FACTOR); + } else if(modem == SX128X_PACKET_TYPE_RANGING) { + // check range for ranging + RADIOLIB_CHECK_RANGE(sf, 5, 10, ERR_INVALID_SPREADING_FACTOR); + } else { + return(ERR_WRONG_MODEM); + } + + // update modulation parameters + _sf = sf << 4; + int16_t state = setModulationParams(_sf, _bw, _cr); + RADIOLIB_ASSERT(state); + + // update mystery register in LoRa mode - SX1280 datasheet v3.0 section 13.4.1 + if(modem == SX128X_PACKET_TYPE_LORA) { + uint8_t data = 0; + if((_sf == SX128X_LORA_SF_5) || (_sf == SX128X_LORA_SF_6)) { + data = 0x1E; + } else if((_sf == SX128X_LORA_SF_7) || (_sf == SX128X_LORA_SF_8)) { + data = 0x37; + } else { + data = 0x32; + } + state = SX128x::writeRegister(SX128X_REG_LORA_SF_CONFIG, &data, 1); + } + + return(state); +} + +int16_t SX128x::setCodingRate(uint8_t cr, bool longInterleaving) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING))) { + return(ERR_WRONG_MODEM); + } + + RADIOLIB_CHECK_RANGE(cr, 5, 8, ERR_INVALID_CODING_RATE); + + // update modulation parameters + if(longInterleaving && (modem == SX128X_PACKET_TYPE_LORA)) { + _cr = cr; + } else { + _cr = cr - 4; + } + return(setModulationParams(_sf, _bw, _cr)); +} + +int16_t SX128x::setOutputPower(int8_t power) { + RADIOLIB_CHECK_RANGE(power, -18, 13, ERR_INVALID_OUTPUT_POWER); + _pwr = power + 18; + return(setTxParams(_pwr)); +} + +int16_t SX128x::setPreambleLength(uint32_t preambleLength) { + uint8_t modem = getPacketType(); + if((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING)) { + // LoRa or ranging + RADIOLIB_CHECK_RANGE(preambleLength, 2, 491520, ERR_INVALID_PREAMBLE_LENGTH); + + // check preamble length is even - no point even trying odd numbers + if(preambleLength % 2 != 0) { + return(ERR_INVALID_PREAMBLE_LENGTH); + } + + // calculate exponent and mantissa values (use the next longer preamble if there's no exact match) + uint8_t e = 1; + uint8_t m = 1; + uint32_t len = 0; + for(; e <= 15; e++) { + for(; m <= 15; m++) { + len = m * (uint32_t(1) << e); + if(len >= preambleLength) { + break; + } + } + if(len >= preambleLength) { + break; + } + } + + // update packet parameters + _preambleLengthLoRa = (e << 4) | m; + return(setPacketParamsLoRa(_preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa)); + + } else if((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_FLRC)) { + // GFSK or FLRC + RADIOLIB_CHECK_RANGE(preambleLength, 4, 32, ERR_INVALID_PREAMBLE_LENGTH); + + // check preamble length is multiple of 4 + if(preambleLength % 4 != 0) { + return(ERR_INVALID_PREAMBLE_LENGTH); + } + + // update packet parameters + _preambleLengthGFSK = ((preambleLength / 4) - 1) << 4; + return(setPacketParamsGFSK(_preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening)); + } + + return(ERR_WRONG_MODEM); +} + +int16_t SX128x::setBitRate(uint16_t br) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_BLE))) { + return(ERR_WRONG_MODEM); + } + + if(br == 125) { + _br = SX128X_BLE_GFSK_BR_0_125_BW_0_3; + } else if(br == 250) { + _br = SX128X_BLE_GFSK_BR_0_250_BW_0_6; + } else if(br == 400) { + _br = SX128X_BLE_GFSK_BR_0_400_BW_1_2; + } else if(br == 500) { + _br = SX128X_BLE_GFSK_BR_0_500_BW_1_2; + } else if(br == 800) { + _br = SX128X_BLE_GFSK_BR_0_800_BW_2_4; + } else if(br == 1000) { + _br = SX128X_BLE_GFSK_BR_1_000_BW_2_4; + } else if(br == 1600) { + _br = SX128X_BLE_GFSK_BR_1_600_BW_2_4; + } else if(br == 2000) { + _br = SX128X_BLE_GFSK_BR_2_000_BW_2_4; + } else { + return(ERR_INVALID_BIT_RANGE); + } + + // update modulation parameters + _brKbps = br; + return(setModulationParams(_br, _modIndex, _shaping)); +} + +int16_t SX128x::setFrequencyDeviation(float freqDev) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_BLE))) { + return(ERR_WRONG_MODEM); + } + + RADIOLIB_CHECK_RANGE(freqDev, 0.0, 3200.0, ERR_INVALID_FREQUENCY_DEVIATION); + + // override for the lowest possible frequency deviation - required for some PhysicalLayer protocols + if(freqDev == 0.0) { + _modIndex = SX128X_BLE_GFSK_MOD_IND_0_35; + _br = SX128X_BLE_GFSK_BR_0_125_BW_0_3; + return(setModulationParams(_br, _modIndex, _shaping)); + } + + // update modulation parameters + uint8_t modIndex = (uint8_t)((8.0 * (freqDev / (float)_brKbps)) - 1.0); + if(modIndex > SX128X_BLE_GFSK_MOD_IND_4_00) { + return(ERR_INVALID_MODULATION_PARAMETERS); + } + + // update modulation parameters + _modIndex = modIndex; + return(setModulationParams(_br, _modIndex, _shaping)); +} + +int16_t SX128x::setDataShaping(float dataShaping) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_BLE))) { + return(ERR_WRONG_MODEM); + } + + // check allowed values + dataShaping *= 10.0; + if(abs(dataShaping - 0.0) <= 0.001) { + _shaping = SX128X_BLE_GFSK_BT_OFF; + } else if(abs(dataShaping - 5.0) <= 0.001) { + _shaping = SX128X_BLE_GFSK_BT_0_5; + } else if(abs(dataShaping - 10.0) <= 0.001) { + _shaping = SX128X_BLE_GFSK_BT_1_0; + } else { + return(ERR_INVALID_DATA_SHAPING); + } + + // update modulation parameters + return(setModulationParams(_br, _modIndex, _shaping)); +} + +int16_t SX128x::setSyncWord(uint8_t* syncWord, uint8_t len) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_FLRC))) { + return(ERR_WRONG_MODEM); + } + + if(len > 5) { + return(ERR_INVALID_SYNC_WORD); + } + + // reverse sync word byte order + uint8_t syncWordBuff[] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; + for(uint8_t i = 0; i < len; i++) { + syncWordBuff[4 - i] = syncWord[i]; + } + + // update sync word + int16_t state = SX128x::writeRegister(SX128X_REG_SYNC_WORD_1_BYTE_4, syncWordBuff, 5); + RADIOLIB_ASSERT(state); + + // update packet parameters + _syncWordLen = len; + if(_syncWordLen == 0) { + _syncWordMatch = SX128X_GFSK_FLRC_SYNC_WORD_OFF; + } else { + // TODO add support for multiple sync words + _syncWordMatch = SX128X_GFSK_FLRC_SYNC_WORD_1; + } + return(setPacketParamsGFSK(_preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening)); +} + +int16_t SX128x::setCRC(uint8_t len, uint32_t initial, uint16_t polynomial) { + // check active modem + uint8_t modem = getPacketType(); + + int16_t state = ERR_NONE; + if((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_FLRC)) { + // update packet parameters + if(modem == SX128X_PACKET_TYPE_GFSK) { + if(len > 2) { + return(ERR_INVALID_CRC_CONFIGURATION); + } + } else { + if(len > 3) { + return(ERR_INVALID_CRC_CONFIGURATION); + } + } + _crcGFSK = len << 4; + state = setPacketParamsGFSK(_preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening); + RADIOLIB_ASSERT(state); + + // set initial CRC value + uint8_t data[] = { (uint8_t)((initial >> 8) & 0xFF), (uint8_t)(initial & 0xFF) }; + state = writeRegister(SX128X_REG_CRC_INITIAL_MSB, data, 2); + RADIOLIB_ASSERT(state); + + // set CRC polynomial + data[0] = (uint8_t)((polynomial >> 8) & 0xFF); + data[1] = (uint8_t)(polynomial & 0xFF); + state = writeRegister(SX128X_REG_CRC_POLYNOMIAL_MSB, data, 2); + return(state); + + } else if(modem == SX128X_PACKET_TYPE_BLE) { + // update packet parameters + if(len == 0) { + _crcBLE = SX128X_BLE_CRC_OFF; + } else if(len == 3) { + _crcBLE = SX128X_BLE_CRC_3_BYTE; + } else { + return(ERR_INVALID_CRC_CONFIGURATION); + } + state = setPacketParamsBLE(_connectionState, _crcBLE, _bleTestPayload, _whitening); + RADIOLIB_ASSERT(state); + + // set initial CRC value + uint8_t data[] = { (uint8_t)((initial >> 16) & 0xFF), (uint8_t)((initial >> 8) & 0xFF), (uint8_t)(initial & 0xFF) }; + state = writeRegister(SX128X_REG_BLE_CRC_INITIAL_MSB, data, 3); + return(state); + + } else if((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING)) { + // update packet parameters + if(len == 0) { + _crcLoRa = SX128X_LORA_CRC_OFF; + } else if(len == 2) { + _crcLoRa = SX128X_LORA_CRC_ON; + } else { + return(ERR_INVALID_CRC_CONFIGURATION); + } + state = setPacketParamsLoRa(_preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa); + return(state); + } + + return(ERR_UNKNOWN); +} + +int16_t SX128x::setWhitening(bool enabled) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_GFSK) || (modem == SX128X_PACKET_TYPE_BLE))) { + return(ERR_WRONG_MODEM); + } + + // update packet parameters + if(enabled) { + _whitening = SX128X_GFSK_BLE_WHITENING_ON; + } else { + _whitening = SX128X_GFSK_BLE_WHITENING_OFF; + } + + if(modem == SX128X_PACKET_TYPE_GFSK) { + return(setPacketParamsGFSK(_preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening)); + } + return(setPacketParamsBLE(_connectionState, _crcBLE, _bleTestPayload, _whitening)); +} + +float SX128x::getRSSI() { + // get packet status + uint8_t packetStatus[5]; + SPIreadCommand(SX128X_CMD_GET_PACKET_STATUS, packetStatus, 5); + + // check active modem + uint8_t modem = getPacketType(); + if((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING)) { + // LoRa or ranging + uint8_t rssiSync = packetStatus[0]; + float rssiMeasured = -1.0 * rssiSync/2.0; + float snr = getSNR(); + if(snr <= 0.0) { + return(rssiMeasured - snr); + } else { + return(rssiMeasured); + } + } else { + // GFSK, BLE or FLRC + uint8_t rssiSync = packetStatus[1]; + return(-1.0 * rssiSync/2.0); + } +} + +float SX128x::getSNR() { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING))) { + return(0.0); + } + + // get packet status + uint8_t packetStatus[5]; + SPIreadCommand(SX128X_CMD_GET_PACKET_STATUS, packetStatus, 5); + + // calculate real SNR + uint8_t snr = packetStatus[1]; + if(snr < 128) { + return(snr/4.0); + } else { + return((snr - 256)/4.0); + } +} + +size_t SX128x::getPacketLength(bool update) { + (void)update; + uint8_t rxBufStatus[2]; + SPIreadCommand(SX128X_CMD_GET_RX_BUFFER_STATUS, rxBufStatus, 2); + return((size_t)rxBufStatus[0]); +} + +uint32_t SX128x::getTimeOnAir(size_t len) { + // check active modem + uint8_t modem = getPacketType(); + if(modem == SX128X_PACKET_TYPE_LORA) { + // calculate number of symbols + float N_symbol = 0; + uint8_t sf = _sf >> 4; + if(_cr <= SX128X_LORA_CR_4_8) { + // legacy coding rate - nice and simple + + // get SF coefficients + float coeff1 = 0; + uint8_t coeff2 = 0; + uint8_t coeff3 = 0; + if(sf < 7) { + // SF5, SF6 + coeff1 = 6.25; + coeff2 = 4*sf; + coeff3 = 4*sf; + } else if(sf < 11) { + // SF7. SF8, SF9, SF10 + coeff1 = 4.25; + coeff2 = 4*sf + 8; + coeff3 = 4*sf; + } else { + // SF11, SF12 + coeff1 = 4.25; + coeff2 = 4*sf + 8; + coeff3 = 4*(sf - 2); + } + + // get CRC length + uint8_t N_bitCRC = 16; + if(_crcLoRa == SX128X_LORA_CRC_OFF) { + N_bitCRC = 0; + } + + // get header length + uint8_t N_symbolHeader = 20; + if(_headerType == SX128X_LORA_HEADER_IMPLICIT) { + N_symbolHeader = 0; + } + + // calculate number of LoRa preamble symbols + uint32_t N_symbolPreamble = (_preambleLengthLoRa & 0x0F) * (uint32_t(1) << ((_preambleLengthLoRa & 0xF0) >> 4)); + + // calculate the number of symbols + N_symbol = (float)N_symbolPreamble + coeff1 + 8.0 + ceil(max(8 * len + N_bitCRC - coeff2 + N_symbolHeader, 0) / (float)coeff3) * (float)(_cr + 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) << sf) / _bwKhz) * N_symbol * 1000.0); + + } else { + return((len * 8) / _brKbps); + } + +} + +int16_t SX128x::implicitHeader(size_t len) { + return(setHeaderType(SX128X_LORA_HEADER_IMPLICIT, len)); +} + +int16_t SX128x::explicitHeader() { + return(setHeaderType(SX128X_LORA_HEADER_EXPLICIT)); +} + +int16_t SX128x::setEncoding(uint8_t encoding) { + return(setWhitening(encoding)); +} + +uint8_t SX128x::getStatus() { + uint8_t data = 0; + SPIreadCommand(SX128X_CMD_GET_STATUS, &data, 1); + return(data); +} + +int16_t SX128x::writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) { + uint8_t cmd[] = { SX128X_CMD_WRITE_REGISTER, (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF) }; + return(SPIwriteCommand(cmd, 3, data, numBytes)); +} + +int16_t SX128x::readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes) { + uint8_t cmd[] = { SX128X_CMD_READ_REGISTER, (uint8_t)((addr >> 8) & 0xFF), (uint8_t)(addr & 0xFF) }; + return(SX128x::SPItransfer(cmd, 3, false, NULL, data, numBytes, true)); +} + +int16_t SX128x::writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset) { + uint8_t cmd[] = { SX128X_CMD_WRITE_BUFFER, offset }; + return(SPIwriteCommand(cmd, 2, data, numBytes)); +} + +int16_t SX128x::readBuffer(uint8_t* data, uint8_t numBytes) { + uint8_t cmd[] = { SX128X_CMD_READ_BUFFER, SX128X_CMD_NOP }; + return(SPIreadCommand(cmd, 2, data, numBytes)); +} + +int16_t SX128x::setTx(uint16_t periodBaseCount, uint8_t periodBase) { + uint8_t data[] = { periodBase, (uint8_t)((periodBaseCount >> 8) & 0xFF), (uint8_t)(periodBaseCount & 0xFF) }; + return(SPIwriteCommand(SX128X_CMD_SET_TX, data, 3)); +} + +int16_t SX128x::setRx(uint16_t periodBaseCount, uint8_t periodBase) { + uint8_t data[] = { periodBase, (uint8_t)((periodBaseCount >> 8) & 0xFF), (uint8_t)(periodBaseCount & 0xFF) }; + return(SPIwriteCommand(SX128X_CMD_SET_RX, data, 3)); +} + +int16_t SX128x::setCad() { + return(SPIwriteCommand(SX128X_CMD_SET_CAD, NULL, 0)); +} + +uint8_t SX128x::getPacketType() { + uint8_t data = 0xFF; + SPIreadCommand(SX128X_CMD_GET_PACKET_TYPE, &data, 1); + return(data); +} + +int16_t SX128x::setRfFrequency(uint32_t frf) { + uint8_t data[] = { (uint8_t)((frf >> 16) & 0xFF), (uint8_t)((frf >> 8) & 0xFF), (uint8_t)(frf & 0xFF) }; + return(SPIwriteCommand(SX128X_CMD_SET_RF_FREQUENCY, data, 3)); +} + +int16_t SX128x::setTxParams(uint8_t power, uint8_t rampTime) { + uint8_t data[] = { power, rampTime }; + return(SPIwriteCommand(SX128X_CMD_SET_TX_PARAMS, data, 2)); +} + +int16_t SX128x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) { + uint8_t data[] = { txBaseAddress, rxBaseAddress }; + return(SPIwriteCommand(SX128X_CMD_SET_BUFFER_BASE_ADDRESS, data, 2)); +} + +int16_t SX128x::setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3) { + uint8_t data[] = { modParam1, modParam2, modParam3 }; + return(SPIwriteCommand(SX128X_CMD_SET_MODULATION_PARAMS, data, 3)); +} + +int16_t SX128x::setPacketParamsGFSK(uint8_t preambleLen, uint8_t syncWordLen, uint8_t syncWordMatch, uint8_t crcLen, uint8_t whitening, uint8_t payloadLen, uint8_t headerType) { + uint8_t data[] = { preambleLen, syncWordLen, syncWordMatch, headerType, payloadLen, crcLen, whitening }; + return(SPIwriteCommand(SX128X_CMD_SET_PACKET_PARAMS, data, 7)); +} + +int16_t SX128x::setPacketParamsBLE(uint8_t connState, uint8_t crcLen, uint8_t bleTestPayload, uint8_t whitening) { + uint8_t data[] = { connState, crcLen, bleTestPayload, whitening, 0x00, 0x00, 0x00 }; + return(SPIwriteCommand(SX128X_CMD_SET_PACKET_PARAMS, data, 7)); +} + +int16_t SX128x::setPacketParamsLoRa(uint8_t preambleLen, uint8_t headerType, uint8_t payloadLen, uint8_t crc, uint8_t invertIQ) { + uint8_t data[] = { preambleLen, headerType, payloadLen, crc, invertIQ, 0x00, 0x00 }; + return(SPIwriteCommand(SX128X_CMD_SET_PACKET_PARAMS, data, 7)); +} + +int16_t SX128x::setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask) { + uint8_t data[] = { (uint8_t)((irqMask >> 8) & 0xFF), (uint8_t)(irqMask & 0xFF), + (uint8_t)((dio1Mask >> 8) & 0xFF), (uint8_t)(dio1Mask & 0xFF), + (uint8_t)((dio2Mask >> 8) & 0xFF), (uint8_t)(dio2Mask & 0xFF), + (uint8_t)((dio3Mask >> 8) & 0xFF), (uint8_t)(dio3Mask & 0xFF) }; + return(SPIwriteCommand(SX128X_CMD_SET_DIO_IRQ_PARAMS, data, 8)); +} + +uint16_t SX128x::getIrqStatus() { + uint8_t data[] = { 0x00, 0x00 }; + SPIreadCommand(SX128X_CMD_GET_IRQ_STATUS, data, 2); + return(((uint16_t)(data[0]) << 8) | data[1]); +} + +int16_t SX128x::clearIrqStatus(uint16_t clearIrqParams) { + uint8_t data[] = { (uint8_t)((clearIrqParams >> 8) & 0xFF), (uint8_t)(clearIrqParams & 0xFF) }; + return(SPIwriteCommand(SX128X_CMD_CLEAR_IRQ_STATUS, data, 2)); +} + +int16_t SX128x::setHeaderType(uint8_t headerType, size_t len) { + // check active modem + uint8_t modem = getPacketType(); + if(!((modem == SX128X_PACKET_TYPE_LORA) || (modem == SX128X_PACKET_TYPE_RANGING))) { + return(ERR_WRONG_MODEM); + } + + // update packet parameters + _headerType = headerType; + _payloadLen = len; + return(setPacketParamsLoRa(_preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa)); +} + +int16_t SX128x::config(uint8_t modem) { + // reset buffer base address + int16_t state = setBufferBaseAddress(); + RADIOLIB_ASSERT(state); + + // set modem + uint8_t data[1]; + data[0] = modem; + state = SPIwriteCommand(SX128X_CMD_SET_PACKET_TYPE, data, 1); + RADIOLIB_ASSERT(state); + + // set CAD parameters + data[0] = SX128X_CAD_ON_8_SYMB; + state = SPIwriteCommand(SX128X_CMD_SET_CAD_PARAMS, data, 1); + RADIOLIB_ASSERT(state); + + return(ERR_NONE); +} + +int16_t SX128x::SPIwriteCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX128x::SPItransfer(cmd, cmdLen, true, data, NULL, numBytes, waitForBusy)); +} + +int16_t SX128x::SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX128x::SPItransfer(&cmd, 1, true, data, NULL, numBytes, waitForBusy)); +} + +int16_t SX128x::SPIreadCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX128x::SPItransfer(cmd, cmdLen, false, NULL, data, numBytes, waitForBusy)); +} + +int16_t SX128x::SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy) { + return(SX128x::SPItransfer(&cmd, 1, false, NULL, data, numBytes, waitForBusy)); +} + +int16_t SX128x::SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy, uint32_t timeout) { + // get pointer to used SPI interface and the settings + SPIClass* spi = _mod->getSpi(); + SPISettings spiSettings = _mod->getSpiSettings(); + + #ifdef RADIOLIB_VERBOSE + uint8_t debugBuff[256]; + #endif + + // ensure BUSY is low (state machine ready) + uint32_t start = millis(); + while(digitalRead(_mod->getGpio())) { + yield(); + if(millis() - start >= timeout) { + digitalWrite(_mod->getCs(), HIGH); + return(ERR_SPI_CMD_TIMEOUT); + } + } + + // pull NSS low + digitalWrite(_mod->getCs(), LOW); + + // start transfer + spi->beginTransaction(spiSettings); + + // send command byte(s) + for(uint8_t n = 0; n < cmdLen; n++) { + spi->transfer(cmd[n]); + } + + // variable to save error during SPI transfer + uint8_t status = 0; + + // send/receive all bytes + if(write) { + for(uint8_t n = 0; n < numBytes; n++) { + // send byte + uint8_t in = spi->transfer(dataOut[n]); + #ifdef RADIOLIB_VERBOSE + debugBuff[n] = in; + #endif + + // check status + if(((in & 0b00011100) == SX128X_STATUS_CMD_TIMEOUT) || + ((in & 0b00011100) == SX128X_STATUS_CMD_ERROR) || + ((in & 0b00011100) == SX128X_STATUS_CMD_FAILED)) { + status = in & 0b00011100; + break; + } else if(in == 0x00 || in == 0xFF) { + status = SX128X_STATUS_SPI_FAILED; + break; + } + } + + } else { + // skip the first byte for read-type commands (status-only) + uint8_t in = spi->transfer(SX128X_CMD_NOP); + #ifdef RADIOLIB_VERBOSE + debugBuff[0] = in; + #endif + + // check status + if(((in & 0b00011100) == SX128X_STATUS_CMD_TIMEOUT) || + ((in & 0b00011100) == SX128X_STATUS_CMD_ERROR) || + ((in & 0b00011100) == SX128X_STATUS_CMD_FAILED)) { + status = in & 0b00011100; + } else if(in == 0x00 || in == 0xFF) { + status = SX128X_STATUS_SPI_FAILED; + } else { + for(uint8_t n = 0; n < numBytes; n++) { + dataIn[n] = spi->transfer(SX128X_CMD_NOP); + } + } + } + + // stop transfer + spi->endTransaction(); + digitalWrite(_mod->getCs(), HIGH); + + // wait for BUSY to go high and then low + if(waitForBusy) { + delayMicroseconds(1); + start = millis(); + while(digitalRead(_mod->getGpio())) { + yield(); + if(millis() - start >= timeout) { + status = SX128X_STATUS_CMD_TIMEOUT; + break; + } + } + } + + // print debug output + #ifdef RADIOLIB_VERBOSE + // print command byte(s) + RADIOLIB_VERBOSE_PRINT("CMD\t"); + for(uint8_t n = 0; n < cmdLen; n++) { + RADIOLIB_VERBOSE_PRINT(cmd[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + + // print data bytes + RADIOLIB_VERBOSE_PRINT("DAT"); + if(write) { + RADIOLIB_VERBOSE_PRINT("W\t"); + for(uint8_t n = 0; n < numBytes; n++) { + RADIOLIB_VERBOSE_PRINT(dataOut[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(debugBuff[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + } else { + RADIOLIB_VERBOSE_PRINT("R\t"); + // skip the first byte for read-type commands (status-only) + RADIOLIB_VERBOSE_PRINT(SX128X_CMD_NOP, HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(debugBuff[0], HEX); + RADIOLIB_VERBOSE_PRINT('\t') + + for(uint8_t n = 0; n < numBytes; n++) { + RADIOLIB_VERBOSE_PRINT(SX128X_CMD_NOP, HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + RADIOLIB_VERBOSE_PRINT(dataIn[n], HEX); + RADIOLIB_VERBOSE_PRINT('\t'); + } + RADIOLIB_VERBOSE_PRINTLN(); + } + RADIOLIB_VERBOSE_PRINTLN(); + #else + // some faster platforms require a short delay here + // not sure why, but it seems that long enough SPI transaction + // (e.g. setPacketParams for GFSK) will fail without it + #if defined(ARDUINO_ARCH_STM32) + delay(1); + #endif + #endif + + // parse status + switch(status) { + case SX128X_STATUS_CMD_TIMEOUT: + return(ERR_SPI_CMD_TIMEOUT); + case SX128X_STATUS_CMD_ERROR: + return(ERR_SPI_CMD_INVALID); + case SX128X_STATUS_CMD_FAILED: + return(ERR_SPI_CMD_FAILED); + case SX128X_STATUS_SPI_FAILED: + return(ERR_CHIP_NOT_FOUND); + default: + return(ERR_NONE); + } +} diff --git a/src/modules/SX128x/SX128x.h b/src/modules/SX128x/SX128x.h new file mode 100644 index 00000000..2e74f7e0 --- /dev/null +++ b/src/modules/SX128x/SX128x.h @@ -0,0 +1,748 @@ +#ifndef _RADIOLIB_SX128X_H +#define _RADIOLIB_SX128X_H + +#include "../../TypeDef.h" +#include "../../Module.h" + +#include "../../protocols/PhysicalLayer/PhysicalLayer.h" + +// SX128X physical layer properties +#define SX128X_FREQUENCY_STEP_SIZE 198.3642578 +#define SX128X_MAX_PACKET_LENGTH 255 +#define SX128X_CRYSTAL_FREQ 52.0 +#define SX128X_DIV_EXPONENT 18 + +// SX128X SPI commands +#define SX128X_CMD_NOP 0x00 +#define SX128X_CMD_GET_STATUS 0xC0 +#define SX128X_CMD_WRITE_REGISTER 0x18 +#define SX128X_CMD_READ_REGISTER 0x19 +#define SX128X_CMD_WRITE_BUFFER 0x1A +#define SX128X_CMD_READ_BUFFER 0x1B +#define SX128X_CMD_SET_SLEEP 0x84 +#define SX128X_CMD_SET_STANDBY 0x80 +#define SX128X_CMD_SET_FS 0xC1 +#define SX128X_CMD_SET_TX 0x83 +#define SX128X_CMD_SET_RX 0x82 +#define SX128X_CMD_SET_RX_DUTY_CYCLE 0x94 +#define SX128X_CMD_SET_CAD 0xC5 +#define SX128X_CMD_SET_TX_CONTINUOUS_WAVE 0xD1 +#define SX128X_CMD_SET_TX_CONTINUOUS_PREAMBLE 0xD2 +#define SX128X_CMD_SET_PACKET_TYPE 0x8A +#define SX128X_CMD_GET_PACKET_TYPE 0x03 +#define SX128X_CMD_SET_RF_FREQUENCY 0x86 +#define SX128X_CMD_SET_TX_PARAMS 0x8E +#define SX128X_CMD_SET_CAD_PARAMS 0x88 +#define SX128X_CMD_SET_BUFFER_BASE_ADDRESS 0x8F +#define SX128X_CMD_SET_MODULATION_PARAMS 0x8B +#define SX128X_CMD_SET_PACKET_PARAMS 0x8C +#define SX128X_CMD_GET_RX_BUFFER_STATUS 0x17 +#define SX128X_CMD_GET_PACKET_STATUS 0x1D +#define SX128X_CMD_GET_RSSI_INST 0x1F +#define SX128X_CMD_SET_DIO_IRQ_PARAMS 0x8D +#define SX128X_CMD_GET_IRQ_STATUS 0x15 +#define SX128X_CMD_CLEAR_IRQ_STATUS 0x97 +#define SX128X_CMD_SET_REGULATOR_MODE 0x96 +#define SX128X_CMD_SET_SAVE_CONTEXT 0xD5 +#define SX128X_CMD_SET_AUTO_TX 0x98 +#define SX128X_CMD_SET_AUTO_FS 0x9E +#define SX128X_CMD_SET_PERF_COUNTER_MODE 0x9C +#define SX128X_CMD_SET_LONG_PREAMBLE 0x9B +#define SX128X_CMD_SET_UART_SPEED 0x9D +#define SX128X_CMD_SET_RANGING_ROLE 0xA3 +#define SX128X_CMD_SET_ADVANCED_RANGING 0x9A + +// SX128X register map +#define SX128X_REG_SYNC_WORD_1_BYTE_4 0x09CE +#define SX128X_REG_SYNC_WORD_1_BYTE_3 0x09CF +#define SX128X_REG_SYNC_WORD_1_BYTE_2 0x09D0 +#define SX128X_REG_SYNC_WORD_1_BYTE_1 0x09D1 +#define SX128X_REG_SYNC_WORD_1_BYTE_0 0x09D2 +#define SX128X_REG_SYNC_WORD_2_BYTE_4 0x09D3 +#define SX128X_REG_SYNC_WORD_2_BYTE_3 0x09D4 +#define SX128X_REG_SYNC_WORD_2_BYTE_2 0x09D5 +#define SX128X_REG_SYNC_WORD_2_BYTE_1 0x09D6 +#define SX128X_REG_SYNC_WORD_2_BYTE_0 0x09D7 +#define SX128X_REG_SYNC_WORD_3_BYTE_4 0x09D8 +#define SX128X_REG_SYNC_WORD_3_BYTE_3 0x09D9 +#define SX128X_REG_SYNC_WORD_3_BYTE_2 0x09DA +#define SX128X_REG_SYNC_WORD_3_BYTE_1 0x09DB +#define SX128X_REG_SYNC_WORD_3_BYTE_0 0x09DC +#define SX128X_REG_CRC_INITIAL_MSB 0x09C8 +#define SX128X_REG_CRC_INITIAL_LSB 0x09C9 +#define SX128X_REG_CRC_POLYNOMIAL_MSB 0x09C6 +#define SX128X_REG_CRC_POLYNOMIAL_LSB 0x09C7 +#define SX128X_REG_ACCESS_ADDRESS_BYTE_3 (SX128X_REG_SYNC_WORD_1_BYTE_3) +#define SX128X_REG_ACCESS_ADDRESS_BYTE_2 (SX128X_REG_SYNC_WORD_1_BYTE_2) +#define SX128X_REG_ACCESS_ADDRESS_BYTE_1 (SX128X_REG_SYNC_WORD_1_BYTE_1) +#define SX128X_REG_ACCESS_ADDRESS_BYTE_0 (SX128X_REG_SYNC_WORD_1_BYTE_0) +#define SX128X_REG_BLE_CRC_INITIAL_MSB 0x09C7 +#define SX128X_REG_BLE_CRC_INITIAL_MID (SX128X_REG_CRC_INITIAL_MSB) +#define SX128X_REG_BLE_CRC_INITIAL_LSB (SX128X_REG_CRC_INITIAL_LSB) +#define SX128X_REG_SLAVE_RANGING_ADDRESS_BYTE_3 0x0916 +#define SX128X_REG_SLAVE_RANGING_ADDRESS_BYTE_2 0x0917 +#define SX128X_REG_SLAVE_RANGING_ADDRESS_BYTE_1 0x0918 +#define SX128X_REG_SLAVE_RANGING_ADDRESS_BYTE_0 0x0919 +#define SX128X_REG_SLAVE_RANGING_ADDRESS_WIDTH 0x0931 +#define SX128X_REG_MASTER_RANGING_ADDRESS_BYTE_3 0x0912 +#define SX128X_REG_MASTER_RANGING_ADDRESS_BYTE_2 0x0913 +#define SX128X_REG_MASTER_RANGING_ADDRESS_BYTE_1 0x0914 +#define SX128X_REG_MASTER_RANGING_ADDRESS_BYTE_0 0x0915 +#define SX128X_REG_RANGING_CALIBRATION_MSB 0x092C +#define SX128X_REG_RANGING_CALIBRATION_LSB 0x092D +#define SX128X_REG_RANGING_RESULT_MSB 0x0961 +#define SX128X_REG_RANGING_RESULT_MID 0x0962 +#define SX128X_REG_RANGING_RESULT_LSB 0x0963 +#define SX128X_REG_MANUAL_GAIN_CONTROL_ENABLE_1 0x089F +#define SX128X_REG_MANUAL_GAIN_CONTROL_ENABLE_2 0x0895 +#define SX128X_REG_MANUAL_GAIN_SETTING 0x089E +#define SX128X_REG_GAIN_MODE 0x0891 +#define SX128X_REG_LORA_FIXED_PAYLOAD_LENGTH 0x0901 +#define SX128X_REG_LORA_SF_CONFIG 0x0925 +#define SX128X_REG_FEI_MSB 0x0954 +#define SX128X_REG_FEI_MID 0x0955 +#define SX128X_REG_FEI_LSB 0x0956 +#define SX128X_REG_RANGING_FILTER_WINDOW_SIZE 0x091E +#define SX128X_REG_RANGING_FILTER_RSSI_OFFSET 0x0953 +#define SX128X_REG_RANGING_FILTER_RESET 0x0923 +#define SX128X_REG_RANGING_LORA_CLOCK_ENABLE 0x097F +#define SX128X_REG_RANGING_TYPE 0x0924 +#define SX128X_REG_RANGING_ADDRESS_SWITCH 0x0927 +#define SX128X_REG_RANGING_ADDRESS_MSB 0x095F +#define SX128X_REG_RANGING_ADDRESS_LSB 0x0960 + + +// SX128X SPI command variables +//SX128X_CMD_GET_STATUS MSB LSB DESCRIPTION +#define SX128X_STATUS_MODE_STDBY_RC 0b01000000 // 7 5 current chip mode: STDBY_RC +#define SX128X_STATUS_MODE_STDBY_XOSC 0b01100000 // 7 5 STDBY_XOSC +#define SX128X_STATUS_MODE_FS 0b10000000 // 7 5 FS +#define SX128X_STATUS_MODE_RX 0b10100000 // 7 5 Rx +#define SX128X_STATUS_MODE_TX 0b11000000 // 7 5 Tx +#define SX128X_STATUS_CMD_PROCESSED 0b00000100 // 4 2 command status: processing OK +#define SX128X_STATUS_DATA_AVAILABLE 0b00001000 // 4 2 data available +#define SX128X_STATUS_CMD_TIMEOUT 0b00001100 // 4 2 timeout +#define SX128X_STATUS_CMD_ERROR 0b00010000 // 4 2 processing error +#define SX128X_STATUS_CMD_FAILED 0b00010100 // 4 2 failed to execute +#define SX128X_STATUS_TX_DONE 0b00011000 // 4 2 transmission finished +#define SX128X_STATUS_BUSY 0b00000001 // 0 0 chip busy +#define SX128X_STATUS_SPI_FAILED 0b11111111 // 7 0 SPI transaction failed + +//SX128X_CMD_SET_SLEEP +#define SX128X_SLEEP_DATA_BUFFER_FLUSH 0b00000000 // 1 1 data buffer behavior in sleep mode: flush +#define SX128X_SLEEP_DATA_BUFFER_RETAIN 0b00000010 // 1 1 retain +#define SX128X_SLEEP_DATA_RAM_FLUSH 0b00000000 // 0 0 data RAM (configuration) behavior in sleep mode: flush +#define SX128X_SLEEP_DATA_RAM_RETAIN 0b00000001 // 0 0 retain + +//SX128X_CMD_SET_STANDBY +#define SX128X_STANDBY_RC 0x00 // 7 0 standby mode: 13 MHz RC oscillator +#define SX128X_STANDBY_XOSC 0x01 // 7 0 52 MHz crystal oscillator + +//SX128X_CMD_SET_TX + SX128X_CMD_SET_RX + SX128X_CMD_SET_RX_DUTY_CYCLE +#define SX128X_PERIOD_BASE_15_625_US 0x00 // 7 0 time period step: 15.625 us +#define SX128X_PERIOD_BASE_62_5_US 0x01 // 7 0 62.5 us +#define SX128X_PERIOD_BASE_1_MS 0x02 // 7 0 1 ms +#define SX128X_PERIOD_BASE_4_MS 0x03 // 7 0 4 ms + +//SX128X_CMD_SET_TX +#define SX128X_TX_TIMEOUT_NONE 0x0000 // 15 0 Tx timeout duration: no timeout (Tx single mode) + +//SX128X_CMD_SET_RX +#define SX128X_RX_TIMEOUT_NONE 0x0000 // 15 0 Rx timeout duration: no timeout (Rx single mode) +#define SX128X_RX_TIMEOUT_INF 0xFFFF // 15 0 infinite (Rx continuous mode) + +//SX128X_CMD_SET_PACKET_TYPE +#define SX128X_PACKET_TYPE_GFSK 0x00 // 7 0 packet type: (G)FSK +#define SX128X_PACKET_TYPE_LORA 0x01 // 7 0 LoRa +#define SX128X_PACKET_TYPE_RANGING 0x02 // 7 0 ranging engine +#define SX128X_PACKET_TYPE_FLRC 0x03 // 7 0 FLRC +#define SX128X_PACKET_TYPE_BLE 0x04 // 7 0 BLE + +//SX128X_CMD_SET_TX_PARAMS +#define SX128X_PA_RAMP_02_US 0x00 // 7 0 PA ramp time: 2 us +#define SX128X_PA_RAMP_04_US 0x20 // 7 0 4 us +#define SX128X_PA_RAMP_06_US 0x40 // 7 0 6 us +#define SX128X_PA_RAMP_08_US 0x60 // 7 0 8 us +#define SX128X_PA_RAMP_10_US 0x80 // 7 0 10 us +#define SX128X_PA_RAMP_12_US 0xA0 // 7 0 12 us +#define SX128X_PA_RAMP_16_US 0xC0 // 7 0 16 us +#define SX128X_PA_RAMP_20_US 0xE0 // 7 0 20 us + +//SX128X_CMD_SET_CAD_PARAMS +#define SX128X_CAD_ON_1_SYMB 0x00 // 7 0 number of symbols used for CAD: 1 +#define SX128X_CAD_ON_2_SYMB 0x20 // 7 0 2 +#define SX128X_CAD_ON_4_SYMB 0x40 // 7 0 4 +#define SX128X_CAD_ON_8_SYMB 0x60 // 7 0 8 +#define SX128X_CAD_ON_16_SYMB 0x80 // 7 0 16 + +//SX128X_CMD_SET_MODULATION_PARAMS +#define SX128X_BLE_GFSK_BR_2_000_BW_2_4 0x04 // 7 0 GFSK/BLE bit rate and bandwidth setting: 2.0 Mbps 2.4 MHz +#define SX128X_BLE_GFSK_BR_1_600_BW_2_4 0x28 // 7 0 1.6 Mbps 2.4 MHz +#define SX128X_BLE_GFSK_BR_1_000_BW_2_4 0x4C // 7 0 1.0 Mbps 2.4 MHz +#define SX128X_BLE_GFSK_BR_1_000_BW_1_2 0x45 // 7 0 1.0 Mbps 1.2 MHz +#define SX128X_BLE_GFSK_BR_0_800_BW_2_4 0x70 // 7 0 0.8 Mbps 2.4 MHz +#define SX128X_BLE_GFSK_BR_0_800_BW_1_2 0x69 // 7 0 0.8 Mbps 1.2 MHz +#define SX128X_BLE_GFSK_BR_0_500_BW_1_2 0x8D // 7 0 0.5 Mbps 1.2 MHz +#define SX128X_BLE_GFSK_BR_0_500_BW_0_6 0x86 // 7 0 0.5 Mbps 0.6 MHz +#define SX128X_BLE_GFSK_BR_0_400_BW_1_2 0xB1 // 7 0 0.4 Mbps 1.2 MHz +#define SX128X_BLE_GFSK_BR_0_400_BW_0_6 0xAA // 7 0 0.4 Mbps 0.6 MHz +#define SX128X_BLE_GFSK_BR_0_250_BW_0_6 0xCE // 7 0 0.25 Mbps 0.6 MHz +#define SX128X_BLE_GFSK_BR_0_250_BW_0_3 0xC7 // 7 0 0.25 Mbps 0.3 MHz +#define SX128X_BLE_GFSK_BR_0_125_BW_0_3 0xEF // 7 0 0.125 Mbps 0.3 MHz +#define SX128X_BLE_GFSK_MOD_IND_0_35 0x00 // 7 0 GFSK/BLE modulation index: 0.35 +#define SX128X_BLE_GFSK_MOD_IND_0_50 0x01 // 7 0 0.50 +#define SX128X_BLE_GFSK_MOD_IND_0_75 0x02 // 7 0 0.75 +#define SX128X_BLE_GFSK_MOD_IND_1_00 0x03 // 7 0 1.00 +#define SX128X_BLE_GFSK_MOD_IND_1_25 0x04 // 7 0 1.25 +#define SX128X_BLE_GFSK_MOD_IND_1_50 0x05 // 7 0 1.50 +#define SX128X_BLE_GFSK_MOD_IND_1_75 0x06 // 7 0 1.75 +#define SX128X_BLE_GFSK_MOD_IND_2_00 0x07 // 7 0 2.00 +#define SX128X_BLE_GFSK_MOD_IND_2_25 0x08 // 7 0 2.25 +#define SX128X_BLE_GFSK_MOD_IND_2_50 0x09 // 7 0 2.50 +#define SX128X_BLE_GFSK_MOD_IND_2_75 0x0A // 7 0 2.75 +#define SX128X_BLE_GFSK_MOD_IND_3_00 0x0B // 7 0 3.00 +#define SX128X_BLE_GFSK_MOD_IND_3_25 0x0C // 7 0 3.25 +#define SX128X_BLE_GFSK_MOD_IND_3_50 0x0D // 7 0 3.50 +#define SX128X_BLE_GFSK_MOD_IND_3_75 0x0E // 7 0 3.75 +#define SX128X_BLE_GFSK_MOD_IND_4_00 0x0F // 7 0 4.00 +#define SX128X_BLE_GFSK_BT_OFF 0x00 // 7 0 GFSK Gaussian filter BT product: filter disabled +#define SX128X_BLE_GFSK_BT_1_0 0x10 // 7 0 1.0 +#define SX128X_BLE_GFSK_BT_0_5 0x20 // 7 0 0.5 +#define SX128X_FLRC_BR_1_300_BW_1_2 0x45 // 7 0 FLRC bit rate and bandwidth setting: 1.3 Mbps 1.2 MHz +#define SX128X_FLRC_BR_1_000_BW_1_2 0x69 // 7 0 1.04 Mbps 1.2 MHz +#define SX128X_FLRC_BR_0_650_BW_0_6 0x86 // 7 0 0.65 Mbps 0.6 MHz +#define SX128X_FLRC_BR_0_520_BW_0_6 0xAA // 7 0 0.52 Mbps 0.6 MHz +#define SX128X_FLRC_BR_0_325_BW_0_3 0xC7 // 7 0 0.325 Mbps 0.3 MHz +#define SX128X_FLRC_BR_0_260_BW_0_3 0xEB // 7 0 0.260 Mbps 0.3 MHz +#define SX128X_FLRC_CR_1_2 0x00 // 7 0 FLRC coding rate: 1/2 +#define SX128X_FLRC_CR_3_4 0x02 // 7 0 3/4 +#define SX128X_FLRC_CR_1_0 0x04 // 7 0 1/1 +#define SX128X_FLRC_BT_OFF 0x00 // 7 0 FLRC Gaussian filter BT product: filter disabled +#define SX128X_FLRC_BT_1_0 0x10 // 7 0 1.0 +#define SX128X_FLRC_BT_0_5 0x20 // 7 0 0.5 +#define SX128X_LORA_SF_5 0x50 // 7 0 LoRa spreading factor: 5 +#define SX128X_LORA_SF_6 0x60 // 7 0 6 +#define SX128X_LORA_SF_7 0x70 // 7 0 7 +#define SX128X_LORA_SF_8 0x80 // 7 0 8 +#define SX128X_LORA_SF_9 0x90 // 7 0 9 +#define SX128X_LORA_SF_10 0xA0 // 7 0 10 +#define SX128X_LORA_SF_11 0xB0 // 7 0 11 +#define SX128X_LORA_SF_12 0xC0 // 7 0 12 +#define SX128X_LORA_BW_1625_00 0x0A // 7 0 LoRa bandwidth: 1625.0 kHz +#define SX128X_LORA_BW_812_50 0x18 // 7 0 812.5 kHz +#define SX128X_LORA_BW_406_25 0x26 // 7 0 406.25 kHz +#define SX128X_LORA_BW_203_125 0x34 // 7 0 203.125 kHz +#define SX128X_LORA_CR_4_5 0x01 // 7 0 LoRa coding rate: 4/5 +#define SX128X_LORA_CR_4_6 0x02 // 7 0 4/6 +#define SX128X_LORA_CR_4_7 0x03 // 7 0 4/7 +#define SX128X_LORA_CR_4_8 0x04 // 7 0 4/8 +#define SX128X_LORA_CR_4_5_LI 0x05 // 7 0 4/5, long interleaving +#define SX128X_LORA_CR_4_6_LI 0x06 // 7 0 4/6, long interleaving +#define SX128X_LORA_CR_4_7_LI 0x07 // 7 0 4/7, long interleaving + +//SX128X_CMD_SET_PACKET_PARAMS +#define SX128X_GFSK_FLRC_SYNC_WORD_OFF 0x00 // 7 0 GFSK/FLRC sync word used: none +#define SX128X_GFSK_FLRC_SYNC_WORD_1 0x10 // 7 0 sync word 1 +#define SX128X_GFSK_FLRC_SYNC_WORD_2 0x20 // 7 0 sync word 2 +#define SX128X_GFSK_FLRC_SYNC_WORD_1_2 0x30 // 7 0 sync words 1 and 2 +#define SX128X_GFSK_FLRC_SYNC_WORD_3 0x40 // 7 0 sync word 3 +#define SX128X_GFSK_FLRC_SYNC_WORD_1_3 0x50 // 7 0 sync words 1 and 3 +#define SX128X_GFSK_FLRC_SYNC_WORD_2_3 0x60 // 7 0 sync words 2 and 3 +#define SX128X_GFSK_FLRC_SYNC_WORD_1_2_3 0x70 // 7 0 sync words 1, 2 and 3 +#define SX128X_GFSK_FLRC_PACKET_FIXED 0x00 // 7 0 GFSK/FLRC packet length mode: fixed +#define SX128X_GFSK_FLRC_PACKET_VARIABLE 0x20 // 7 0 variable +#define SX128X_GFSK_FLRC_CRC_OFF 0x00 // 7 0 GFSK/FLRC packet CRC: none +#define SX128X_GFSK_FLRC_CRC_1_BYTE 0x10 // 7 0 1 byte +#define SX128X_GFSK_FLRC_CRC_2_BYTE 0x20 // 7 0 2 bytes +#define SX128X_GFSK_FLRC_CRC_3_BYTE 0x30 // 7 0 3 bytes (FLRC only) +#define SX128X_GFSK_BLE_WHITENING_ON 0x00 // 7 0 GFSK/BLE whitening: enabled +#define SX128X_GFSK_BLE_WHITENING_OFF 0x08 // 7 0 disabled +#define SX128X_BLE_PAYLOAD_LENGTH_MAX_31 0x00 // 7 0 BLE maximum payload length: 31 bytes +#define SX128X_BLE_PAYLOAD_LENGTH_MAX_37 0x20 // 7 0 37 bytes +#define SX128X_BLE_PAYLOAD_LENGTH_TEST 0x40 // 7 0 63 bytes (test mode) +#define SX128X_BLE_PAYLOAD_LENGTH_MAX_255 0x80 // 7 0 255 bytes (Bluetooth 4.2 and above) +#define SX128X_BLE_CRC_OFF 0x00 // 7 0 BLE packet CRC: none +#define SX128X_BLE_CRC_3_BYTE 0x10 // 7 0 3 byte +#define SX128X_BLE_PRBS_9 0x00 // 7 0 BLE test payload contents: PRNG sequence using x^9 + x^5 + x +#define SX128X_BLE_EYELONG 0x04 // 7 0 repeated 0xF0 +#define SX128X_BLE_EYESHORT 0x08 // 7 0 repeated 0xAA +#define SX128X_BLE_PRBS_15 0x0C // 7 0 PRNG sequence using x^15 + x^14 + x^13 + x^12 + x^2 + x + 1 +#define SX128X_BLE_ALL_1 0x10 // 7 0 repeated 0xFF +#define SX128X_BLE_ALL_0 0x14 // 7 0 repeated 0x00 +#define SX128X_BLE_EYELONG_INV 0x18 // 7 0 repeated 0x0F +#define SX128X_BLE_EYESHORT_INV 0x1C // 7 0 repeated 0x55 +#define SX128X_FLRC_SYNC_WORD_OFF 0x00 // 7 0 FLRC sync word: disabled +#define SX128X_FLRC_SYNC_WORD_ON 0x04 // 7 0 enabled +#define SX128X_LORA_HEADER_EXPLICIT 0x00 // 7 0 LoRa header mode: explicit +#define SX128X_LORA_HEADER_IMPLICIT 0x80 // 7 0 implicit +#define SX128X_LORA_CRC_OFF 0x00 // 7 0 LoRa packet CRC: disabled +#define SX128X_LORA_CRC_ON 0x20 // 7 0 enabled +#define SX128X_LORA_IQ_STANDARD 0x40 // 7 0 LoRa IQ: standard +#define SX128X_LORA_IQ_INVERTED 0x00 // 7 0 inverted + +//SX128X_CMD_GET_PACKET_STATUS +#define SX128X_PACKET_STATUS_SYNC_ERROR 0b01000000 // 6 6 packet status errors byte: sync word error +#define SX128X_PACKET_STATUS_LENGTH_ERROR 0b00100000 // 5 5 packet length error +#define SX128X_PACKET_STATUS_CRC_ERROR 0b00010000 // 4 4 CRC error +#define SX128X_PACKET_STATUS_ABORT_ERROR 0b00001000 // 3 3 packet reception aborted +#define SX128X_PACKET_STATUS_HEADER_RECEIVED 0b00000100 // 2 2 header received +#define SX128X_PACKET_STATUS_PACKET_RECEIVED 0b00000010 // 1 1 packet received +#define SX128X_PACKET_STATUS_PACKET_CTRL_BUSY 0b00000001 // 0 0 packet controller is busy +#define SX128X_PACKET_STATUS_RX_PID 0b11000000 // 7 6 packet status status byte: PID field of the received packet +#define SX128X_PACKET_STATUS_NO_ACK 0b00100000 // 5 5 NO_ACK field of the received packet +#define SX128X_PACKET_STATUS_RX_PID_ERROR 0b00010000 // 4 4 PID field error +#define SX128X_PACKET_STATUS_PACKET_SENT 0b00000001 // 0 0 packet sent +#define SX128X_PACKET_STATUS_SYNC_DET_ERROR 0b00000000 // 2 0 packet status sync byte: sync word detection error +#define SX128X_PACKET_STATUS_SYNC_DET_1 0b00000001 // 2 0 detected sync word 1 +#define SX128X_PACKET_STATUS_SYNC_DET_2 0b00000010 // 2 0 detected sync word 2 +#define SX128X_PACKET_STATUS_SYNC_DET_3 0b00000100 // 2 0 detected sync word 3 + +//SX128X_CMD_SET_DIO_IRQ_PARAMS +#define SX128X_IRQ_PREAMBLE_DETECTED 0x8000 // 15 15 interrupt source: preamble detected +#define SX128X_IRQ_ADVANCED_RANGING_DONE 0x8000 // 15 15 advanced ranging done +#define SX128X_IRQ_RX_TX_TIMEOUT 0x4000 // 14 14 Rx or Tx timeout +#define SX128X_IRQ_CAD_DETECTED 0x2000 // 13 13 channel activity detected +#define SX128X_IRQ_CAD_DONE 0x1000 // 12 12 CAD finished +#define SX128X_IRQ_RANGING_SLAVE_REQ_VALID 0x0800 // 11 11 ranging request valid (slave) +#define SX128X_IRQ_RANGING_MASTER_TIMEOUT 0x0400 // 10 10 ranging timeout (master) +#define SX128X_IRQ_RANGING_MASTER_RES_VALID 0x0200 // 9 9 ranging result valid (master) +#define SX128X_IRQ_RANGING_SLAVE_REQ_DISCARD 0x0100 // 8 8 ranging result valid (master) +#define SX128X_IRQ_RANGING_SLAVE_RESP_DONE 0x0080 // 7 7 ranging response complete (slave) +#define SX128X_IRQ_CRC_ERROR 0x0040 // 6 6 CRC error +#define SX128X_IRQ_HEADER_ERROR 0x0020 // 5 5 header error +#define SX128X_IRQ_HEADER_VALID 0x0010 // 4 4 header valid +#define SX128X_IRQ_SYNC_WORD_ERROR 0x0008 // 3 3 sync word error +#define SX128X_IRQ_SYNC_WORD_VALID 0x0004 // 2 2 sync word valid +#define SX128X_IRQ_RX_DONE 0x0002 // 1 1 Rx done +#define SX128X_IRQ_TX_DONE 0x0001 // 0 0 Tx done +#define SX128X_IRQ_NONE 0x0000 // 15 0 none +#define SX128X_IRQ_ALL 0xFFFF // 15 0 all + +/*! + \class SX128x + + \brief Base class for %SX128x series. All derived classes for %SX128x (e.g. SX1280 or SX1281) inherit from this base class. + This class should not be instantiated directly from Arduino sketch, only from its derived classes. +*/ +class SX128x: 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. + */ + SX128x(Module* mod); + + // basic methods + + /*! + \brief Initialization method for LoRa modem. + + \param freq Carrier frequency in MHz. Defaults to 2400.0 MHz. + + \param bw LoRa bandwidth in kHz. Defaults to 812.5 kHz. + + \param sf LoRa spreading factor. Defaults to 9. + + \param cr LoRa coding rate denominator. Defaults to 7 (coding rate 4/7). + + \param power Output power in dBm. Defaults to 10 dBm. + + \param preambleLength LoRa preamble length in symbols. Defaults to 12 symbols. + + \returns \ref status_codes + */ + int16_t begin(float freq = 2400.0, float bw = 812.5, uint8_t sf = 9, uint8_t cr = 7, int8_t power = 10, uint16_t preambleLength = 12); + + /*! + \brief Initialization method for GFSK modem. + + \param freq Carrier frequency in MHz. Defaults to 2400.0 MHz. + + \param br FSK bit rate in kbps. Defaults to 800 kbps. + + \param freqDev Frequency deviation from carrier frequency in kHz. Defaults to 400.0 kHz. + + \param power Output power in dBm. Defaults to 10 dBm. + + \parma preambleLength FSK preamble length in bits. Defaults to 16 bits. + + \param dataShaping Time-bandwidth product of the Gaussian filter to be used for shaping. Defaults to 0.5. + + \returns \ref status_codes + */ + int16_t beginGFSK(float freq = 2400.0, uint16_t br = 800, float freqDev = 400.0, int8_t power = 10, uint16_t preambleLength = 16, float dataShaping = 0.5); + + /*! + \brief Reset method. Will reset the chip to the default state using RST pin. + + \param verify Whether correct module startup should be verified. When set to true, RadioLib will attempt to verify the module has started correctly + by repeatedly issuing setStandby command. Enabled by default. + + \returns \ref status_codes + */ + int16_t reset(bool verify = true); + + /*! + \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); + + /*! + \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); + + /*! + \brief Starts direct mode transmission. + + \param frf Raw RF frequency value. Defaults to 0, required for quick frequency shifts in RTTY. + + \returns \ref status_codes + */ + int16_t transmitDirect(uint32_t frf = 0); + + /*! + \brief Starts direct mode reception. Only implemented for PhysicalLayer compatibility, as %SX128x series does not support direct mode reception. + Will always return ERR_UNKNOWN. + + \returns \ref status_codes + */ + int16_t receiveDirect(); + + /*! + \brief Performs scan for LoRa transmission in the current channel. Detects both preamble and payload. + + \returns \ref status_codes + */ + int16_t scanChannel(); + + /*! + \brief Sets the module to sleep mode. + + \param retainConfig Set to true to retain configuration and data buffer or to false to discard current configuration and data buffer. Defaults to true. + + \returns \ref status_codes + */ + int16_t sleep(bool retainConfig = true); + + /*! + \brief Sets the module to standby mode (overload for PhysicalLayer compatibility, uses 13 MHz RC oscillator). + + \returns \ref status_codes + */ + int16_t standby(); + + /*! + \brief Sets the module to standby mode. + + \param mode Oscillator to be used in standby mode. Can be set to SX128X_STANDBY_RC (13 MHz RC oscillator) or SX128X_STANDBY_XOSC (52 MHz external crystal oscillator). + + \returns \ref status_codes + */ + int16_t standby(uint8_t mode); + + // 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 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); + + /*! + \brief Interrupt-driven receive method. DIO1 will be activated when full packet is received. + + \param timeout Raw timeout value, expressed as multiples of 15.625 us. Defaults to SX128X_RX_TIMEOUT_INF for infinite timeout (Rx continuous mode), set to SX128X_RX_TIMEOUT_NONE for no timeout (Rx single mode). + + \returns \ref status_codes + */ + int16_t startReceive(uint16_t timeout = SX128X_RX_TIMEOUT_INF); + + /*! + \brief Reads data received after calling startReceive method. + + \param data Pointer to array to save the received binary data. + + \param len Number of bytes that will be received. Must be known in advance for binary transmissions. + + \returns \ref status_codes + */ + int16_t readData(uint8_t* data, size_t len); + + // configuration methods + + /*! + \brief Sets carrier frequency. Allowed values are in range from 2400.0 to 2500.0 MHz. + + \param freq Carrier frequency to be set in MHz. + + \returns \ref status_codes + */ + int16_t setFrequency(float freq); + + /*! + \brief Sets LoRa bandwidth. Allowed values are 203.125, 406.25, 812.5 and 1625.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. + + \returns \ref status_codes + */ + int16_t setSpreadingFactor(uint8_t sf); + + /*! + \brief Sets LoRa coding rate denominator. Allowed values range from 5 to 8. + + \param cr LoRa coding rate denominator to be set. + + \param longInterleaving Whether to enable long interleaving mode. Not available for coding rate 4/7, defaults to false. + + \returns \ref status_codes + */ + int16_t setCodingRate(uint8_t cr, bool longInterleaving = false); + + /*! + \brief Sets output power. Allowed values are in range from -18 to 13 dBm. + + \param power Output power to be set in dBm. + + \returns \ref status_codes + */ + int16_t setOutputPower(int8_t power); + + /*! + \brief Sets preamble length for currently active modem. Allowed values range from 1 to 65535. + + \param preambleLength Preamble length to be set in symbols (LoRa) or bits (FSK/BLE/FLRC). + + \returns \ref status_codes + */ + int16_t setPreambleLength(uint32_t preambleLength); + + /*! + \brief Sets FSK bit rate. Allowed values are 125, 250, 400, 500, 800, 1000, 1600 and 2000 kbps. + + \param br FSK bit rate to be set in kbps. + + \returns \ref status_codes + */ + int16_t setBitRate(uint16_t br); + + /*! + \brief Sets FSK frequency deviation. Allowed values range from 0.0 to 3200.0 kHz. + + \param freqDev FSK frequency deviation to be set in kHz. + + \returns \ref status_codes + */ + int16_t setFrequencyDeviation(float freqDev); + + /*! + \brief Sets time-bandwidth product of Gaussian filter applied for shaping. Allowed values are 0.5 and 1.0. Set to 0 to disable shaping. + + \param sh Time-bandwidth product of Gaussian filter to be set. + + \returns \ref status_codes + */ + int16_t setDataShaping(float dataShaping); + + /*! + \brief Sets sync word in the form of array of up to 8 bytes. + + \param syncWord Sync word to be set. + + \param len Sync word length in bytes. + + \returns \ref status_codes + */ + int16_t setSyncWord(uint8_t* syncWord, uint8_t len); + + /*! + \brief Sets CRC configuration. + + \param len CRC length in bytes, Allowed values are 1, 2 or 3, set to 0 to disable CRC. + + \param initial Initial CRC value. Defaults to 0x1D0F (CCIT CRC), not available for LoRa modem. + + \param polynomial Polynomial for CRC calculation. Defaults to 0x1021 (CCIT CRC), not available for LoRa or BLE modem. + + \returns \ref status_codes + */ + int16_t setCRC(uint8_t len, uint32_t initial = 0x1D0F, uint16_t polynomial = 0x1021); + + /*! + \brief Sets whitening parameters, not available for LoRa or FLRC modem. + + \param enabled Set to true to enable whitening. + + \returns \ref status_codes + */ + int16_t setWhitening(bool enabled); + + /*! + \brief Gets RSSI (Recorded Signal Strength Indicator) of the last received packet. + + \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 or ranging modem. + + \returns SNR of the last received packet in dB. + */ + float getSNR(); + + /*! + \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); + + /*! + \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); + + /*! + \brief Set implicit header mode for future reception/transmission. + + \returns \ref status_codes + */ + int16_t implicitHeader(size_t len); + + /*! + \brief Set explicit header mode for future reception/transmission. + + \param len Payload length in bytes. + + \returns \ref status_codes + */ + int16_t explicitHeader(); + + /*! + \brief Sets transmission encoding. Serves only as alias for PhysicalLayer compatibility. + + \param encoding Encoding to be used. Set to 0 for NRZ, and 2 for whitening. + + \returns \ref status_codes + */ + int16_t setEncoding(uint8_t encoding); + +#ifndef RADIOLIB_GODMODE + protected: +#endif + // SX128x SPI command implementations + uint8_t getStatus(); + int16_t writeRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); + int16_t readRegister(uint16_t addr, uint8_t* data, uint8_t numBytes); + int16_t writeBuffer(uint8_t* data, uint8_t numBytes, uint8_t offset = 0x00); + int16_t readBuffer(uint8_t* data, uint8_t numBytes); + int16_t setTx(uint16_t periodBaseCount = SX128X_TX_TIMEOUT_NONE, uint8_t periodBase = SX128X_PERIOD_BASE_15_625_US); + int16_t setRx(uint16_t periodBaseCount, uint8_t periodBase = SX128X_PERIOD_BASE_15_625_US); + int16_t setCad(); + uint8_t getPacketType(); + int16_t setRfFrequency(uint32_t frf); + int16_t setTxParams(uint8_t power, uint8_t rampTime = SX128X_PA_RAMP_10_US); + int16_t setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); + int16_t setModulationParams(uint8_t modParam1, uint8_t modParam2, uint8_t modParam3); + int16_t setPacketParamsGFSK(uint8_t preambleLen, uint8_t syncWordLen, uint8_t syncWordMatch, uint8_t crcLen, uint8_t whitening, uint8_t payloadLen = 0xFF, uint8_t headerType = SX128X_GFSK_FLRC_PACKET_VARIABLE); + int16_t setPacketParamsBLE(uint8_t connState, uint8_t crcLen, uint8_t bleTestPayload, uint8_t whitening); + int16_t setPacketParamsLoRa(uint8_t preambleLen, uint8_t headerType, uint8_t payloadLen, uint8_t crc, uint8_t invertIQ = SX128X_LORA_IQ_STANDARD); + int16_t setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = SX128X_IRQ_NONE, uint16_t dio3Mask = SX128X_IRQ_NONE); + uint16_t getIrqStatus(); + int16_t clearIrqStatus(uint16_t clearIrqParams = SX128X_IRQ_ALL); + + int16_t setHeaderType(uint8_t headerType, size_t len = 0xFF); + +#ifndef RADIOLIB_GODMODE + private: +#endif + Module* _mod; + + // common parameters + uint8_t _pwr; + + // cached LoRa parameters + float _bwKhz; + uint8_t _bw, _sf, _cr; + uint8_t _preambleLengthLoRa, _headerType, _payloadLen, _crcLoRa; + + // cached GFSK parameters + float _modIndexReal; + uint16_t _brKbps; + uint8_t _br, _modIndex, _shaping; + uint8_t _preambleLengthGFSK, _syncWordLen, _syncWordMatch, _crcGFSK, _whitening; + + // cached BLE parameters + uint8_t _connectionState, _crcBLE, _bleTestPayload; + + int16_t config(uint8_t modem); + + // common low-level SPI interface + int16_t SPIwriteCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPIwriteCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPIreadCommand(uint8_t cmd, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPIreadCommand(uint8_t* cmd, uint8_t cmdLen, uint8_t* data, uint8_t numBytes, bool waitForBusy = true); + int16_t SPItransfer(uint8_t* cmd, uint8_t cmdLen, bool write, uint8_t* dataOut, uint8_t* dataIn, uint8_t numBytes, bool waitForBusy, uint32_t timeout = 5000); +}; + +#endif