diff --git a/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino b/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino new file mode 100644 index 00000000..b1f2611b --- /dev/null +++ b/examples/SX126x/SX126x_Channel_Activity_Detection/SX126x_Channel_Activity_Detection.ino @@ -0,0 +1,61 @@ +/* + RadioLib SX126x Channel Activity Detection Example + + This example scans the current LoRa channel and detects + valid LoRa preambles. Preamble is the first part of + LoRa transmission, so this can be used to check + if the LoRa channel is free, or if you should start + receiving a message. + + Other modules from SX126x family can also be used. +*/ + +// include the library +#include + +// SX1262 module is in slot A on the shield +SX1262 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1262 with default settings + Serial.print(F("[SX1262] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bandwidth: 125.0 kHz + // spreading factor: 9 + // coding rate: 7 + // sync word: 0x1424 (private network) + // output power: 14 dBm + // current limit: 60 mA + // preamble length: 8 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("[SX1262] 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!")); + + } + + // wait 100 ms before new scan + delay(100); +} diff --git a/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino b/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino new file mode 100644 index 00000000..65373380 --- /dev/null +++ b/examples/SX126x/SX126x_FSK_Modem/SX126x_FSK_Modem.ino @@ -0,0 +1,143 @@ +/* + RadioLib SX126x FSK Modem Example + + This example shows how to use FSK modem in SX126x chips. + + NOTE: The sketch below is just a guide on how to use + FSK modem, so this code should not be run directly! + Instead, modify the other examples to use FSK + modem and use the appropriate configuration + methods. +*/ + +// include the library +#include + +// SX1262 module is in slot A on the shield +SX1262 fsk = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1262 FSK modem with default settings + Serial.print(F("[SX1262] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bit rate: 48.0 kbps + // frequency deviation: 50.0 kHz + // Rx bandwidth: 117.3 kHz + // output power: 14 dBm + // current limit: 60.0 mA + // preamble length: 16 bits + // data shaping: Gaussian, BT = 0.5 + // sync word: 0x2D 0x01 + // CRC: enabled, CRC16 (CCIT) + int state = fsk.beginFSK(); + 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 + // + // lora.begin() start LoRa mode (and disable FSK) + // lora.beginFSK() start FSK mode (and disable LoRa) + + // the following settings can also + // be modified at run-time + state = fsk.setFrequency(433.5); + state = fsk.setBitRate(100.0); + state = fsk.setFrequencyDeviation(10.0); + state = fsk.setRxBandwidth(250.0); + state = fsk.setOutputPower(10.0); + state = fsk.setCurrentLimit(100.0); + state = fsk.setDataShaping(1.0); + uint8_t syncWord[] = {0x01, 0x23, 0x45, 0x67, + 0x89, 0xAB, 0xCD, 0xEF}; + state = fsk.setSyncWord(syncWord, 8); + if (state != ERR_NONE) { + Serial.print(F("Unable to set configuration, code ")); + Serial.println(state); + while (true); + } + + // FSK modem allows advanced CRC configuration + // Default is CCIT CRC16 (2 bytes, initial 0x1D0F, polynomial 0x1021, inverted) + // Set CRC to IBM CRC (2 bytes, initial 0xFFFF, polynomial 0x8005, non-inverted) + state = fsk.setCRC(2, 0xFFFF, 0x8005, false); + // set CRC length to 0 to disable CRC + + #warning "This sketch is just an API guide! Read the note at line 6." +} + +void loop() { + // FSK modem can use the same transmit/receive methods + // as the LoRa modem, even their interrupt-driven versions + + // transmit FSK packet + int state = fsk.transmit("Hello World!"); + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, + 0x78, 0xAB, 0xCD, 0xEF}; + int state = lora.transmit(byteArr, 8); + */ + if (state == ERR_NONE) { + Serial.println(F("[SX1262] Packet transmitted successfully!")); + } else if (state == ERR_PACKET_TOO_LONG) { + Serial.println(F("[SX1262] Packet too long!")); + } else if (state == ERR_TX_TIMEOUT) { + Serial.println(F("[SX1262] Timed out while transmitting!")); + } else { + Serial.println(F("[SX1262] Failed to transmit packet, code ")); + Serial.println(state); + } + + // receive FSK packet + String str; + state = fsk.receive(str); + /* + byte byteArr[8]; + int state = lora.receive(byteArr, 8); + */ + if (state == ERR_NONE) { + Serial.println(F("[SX1262] Received packet!")); + Serial.print(F("[SX1262] Data:\t")); + Serial.println(str); + } else if (state == ERR_RX_TIMEOUT) { + Serial.println(F("[SX1262] Timed out while waiting for packet!")); + } else { + Serial.println(F("[SX1262] Failed to receive packet, code ")); + Serial.println(state); + } + + // FSK modem has built-in address filtering system + // it can be enabled by setting node address, broadcast + // address, or both + // + // to transmit packet to a particular address, + // use the following methods: + // + // fsk.transmit("Hello World!", address); + // fsk.startTransmit("Hello World!", address); + + // set node address to 0x02 + state = fsk.setNodeAddress(0x02); + // set broadcast address to 0xFF + state = fsk.setBroadcastAddress(0xFF); + if (state != ERR_NONE) { + Serial.println(F("[SX1262] Unable to set address filter, code ")); + Serial.println(state); + } + + // address filtering can also be disabled + // NOTE: calling this method will also erase previously set + // node and broadcast address + /* + state = fsk.disableAddressFiltering(); + if (state != ERR_NONE) { + Serial.println(F("Unable to remove address filter, code ")); + } + */ +} diff --git a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino index a165db65..36169ef0 100644 --- a/examples/SX126x/SX126x_Receive/SX126x_Receive.ino +++ b/examples/SX126x/SX126x_Receive/SX126x_Receive.ino @@ -33,6 +33,7 @@ void setup() { // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { Serial.println(F("success!")); diff --git a/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino new file mode 100644 index 00000000..20adb353 --- /dev/null +++ b/examples/SX126x/SX126x_Receive_Interrupt/SX126x_Receive_Interrupt.ino @@ -0,0 +1,141 @@ +/* + RadioLib SX126x Receive with Inerrupts 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 SX126x/RFM9x family can also be used. +*/ + +// include the library +#include + +// SX1262 module is in slot A on the shield +SX1262 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1262 with default settings + Serial.print(F("[SX1262] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bandwidth: 125.0 kHz + // spreading factor: 9 + // coding rate: 7 + // sync word: 0x1424 (private network) + // output power: 14 dBm + // current limit: 60 mA + // preamble length: 8 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("[SX1262] 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.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.receive(byteArr, 8); + */ + + if (state == ERR_NONE) { + // packet was successfully received + Serial.println(F("[SX1262] Received packet!")); + + // print data of the packet + Serial.print(F("[SX1262] Data:\t\t")); + Serial.println(str); + + // print RSSI (Received Signal Strength Indicator) + Serial.print(F("[SX1262] RSSI:\t\t")); + Serial.print(lora.getRSSI()); + Serial.println(" dBm"); + + // print SNR (Signal-to-Noise Ratio) + Serial.print(F("[SX1262] SNR:\t\t")); + Serial.print(lora.getSNR()); + Serial.println(F(" dBm")); + + } else if (state == ERR_CRC_MISMATCH) { + // packet was received, but is malformed + Serial.println(F("CRC error!")); + + } + + // we're ready to receive more packets, + // enable interrupt service routine + enableInterrupt = true; + } + +} diff --git a/examples/SX126x/SX126x_Settings/SX126x_Settings.ino b/examples/SX126x/SX126x_Settings/SX126x_Settings.ino new file mode 100644 index 00000000..c93b488f --- /dev/null +++ b/examples/SX126x/SX126x_Settings/SX126x_Settings.ino @@ -0,0 +1,137 @@ +/* + RadioLib SX126x Settings Example + + This example shows how to change all the properties of LoRa transmission. + RadioLib currently supports the following settings: + - pins (SPI slave select, digital IO 0, digital IO 1) + - carrier frequency + - bandwidth + - spreading factor + - coding rate + - sync word + - output power during transmission + - CRC + - preamble length + + Other modules from SX126x family can also be used. +*/ + +// include the library +#include + +// SX1268 module is in slot A on the shield +SX1268 loraSX1268 = RadioShield.ModuleA; + +// SX1262 module is in slot B on the shield +SX1262 loraSX1262 = RadioShield.ModuleB; + +void setup() { + Serial.begin(9600); + + // initialize SX1268 with default settings + Serial.print(F("[SX1268] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bandwidth: 125.0 kHz + // spreading factor: 9 + // coding rate: 7 + // sync word: 0x1424 (private network) + // output power: 14 dBm + // current limit: 60 mA + // preamble length: 8 symbols + // CRC: enabled + int state = loraSX1268.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("[SX1262] Initializing ... ")); + // carrier frequency: 915.0 MHz + // bandwidth: 500.0 kHz + // spreading factor: 6 + // coding rate: 5 + // sync word: 0x3444 (public network) + // output power: 2 dBm + // current limit: 50 mA + // preamble length: 20 symbols + // CRC: enabled + state = loraSX1262.begin(915.0, 500.0, 6, 5, 0x3444, 50, 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 433.5 MHz + if (loraSX1268.setFrequency(433.5) == ERR_INVALID_FREQUENCY) { + Serial.println(F("Selected frequency is invalid for this module!")); + while (true); + } + + // set bandwidth to 250 kHz + if (loraSX1268.setBandwidth(250.0) == ERR_INVALID_BANDWIDTH) { + Serial.println(F("Selected bandwidth is invalid for this module!")); + while (true); + } + + // set spreading factor to 10 + if (loraSX1268.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 (loraSX1268.setCodingRate(6) == ERR_INVALID_CODING_RATE) { + Serial.println(F("Selected coding rate is invalid for this module!")); + while (true); + } + + // set LoRa sync word to 0x1234 + if (loraSX1268.setSyncWord(0x1234) != ERR_NONE) { + Serial.println(F("Unable to set sync word!")); + while (true); + } + + // set output power to 10 dBm (accepted range is -17 - 22 dBm) + if (loraSX1268.setOutputPower(10) == ERR_INVALID_OUTPUT_POWER) { + Serial.println(F("Selected output power is invalid for this module!")); + while (true); + } + + // set over current protection limit to 80 mA (accepted range is 45 - 240 mA) + // NOTE: set value to 0 to disable overcurrent protection + if (loraSX1268.setCurrentLimit(80) == ERR_INVALID_CURRENT_LIMIT) { + Serial.println(F("Selected current limit is invalid for this module!")); + while (true); + } + + // set LoRa preamble length to 15 symbols (accepted range is 0 - 65535) + if (loraSX1268.setPreambleLength(15) == ERR_INVALID_PREAMBLE_LENGTH) { + Serial.println(F("Selected preamble length is invalid for this module!")); + while (true); + } + + // disable CRC + if (loraSX1268.setCRC(false) == ERR_INVALID_CRC) { + 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/SX126x/SX126x_Transmit/SX126x_Transmit.ino b/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino index 7c452520..cb899eb8 100644 --- a/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino +++ b/examples/SX126x/SX126x_Transmit/SX126x_Transmit.ino @@ -29,6 +29,7 @@ void setup() { // output power: 14 dBm // current limit: 60 mA // preamble length: 8 symbols + // CRC: enabled int state = lora.begin(); if (state == ERR_NONE) { Serial.println(F("success!")); diff --git a/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino new file mode 100644 index 00000000..6a97fd9b --- /dev/null +++ b/examples/SX126x/SX126x_Transmit_Interrupt/SX126x_Transmit_Interrupt.ino @@ -0,0 +1,107 @@ +/* + RadioLib SX126x Transmit with Inerrupts 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 SX126x family can also be used. +*/ + +// include the library +#include + +// SX1262 module is in slot A on the shield +SX1262 lora = RadioShield.ModuleA; + +void setup() { + Serial.begin(9600); + + // initialize SX1262 with default settings + Serial.print(F("[SX1262] Initializing ... ")); + // carrier frequency: 434.0 MHz + // bandwidth: 125.0 kHz + // spreading factor: 9 + // coding rate: 7 + // sync word: 0x1424 (private network) + // output power: 14 dBm + // current limit: 60 mA + // preamble length: 8 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("[SX1262] Sending first packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + state = lora.startTransmit("Hello World!"); + + // you can also transmit byte array up to 256 bytes long + /* + byte byteArr[] = {0x01, 0x23, 0x45, 0x56, + 0x78, 0xAB, 0xCD, 0xEF}; + state = lora.transmit(byteArr, 8); + */ + + if (state != ERR_NONE) { + Serial.print(F("failed, code ")); + Serial.println(state); + } +} + +// flag to indicate that a packet was received +volatile bool transmittedFlag = false; + +void setFlag(void) { + // packet transmission is finished, set the flag + transmittedFlag = true; +} + +void loop() { + // check if the previous transmission finished + if(transmittedFlag) { + Serial.println(F("packet transmission finished!")); + + // wait one second before next transmission + delay(1000); + + // send another packet + Serial.print(F("[SX1262] Sending another packet ... ")); + + // you can transmit C-string or Arduino string up to + // 256 characters long + int state = lora.startTransmit("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.startTransmit(byteArr, 8); + */ + + // NOTE: when using interrupt-driven transmit method, + // it is not possible to automatically measure + // transmission data rate using getDataRate() + + if (state != ERR_NONE) { + Serial.print(F("failed, code ")); + Serial.println(state); + } + } + +} diff --git a/keywords.txt b/keywords.txt index 53bfc893..d1284da3 100644 --- a/keywords.txt +++ b/keywords.txt @@ -197,3 +197,6 @@ ERR_INVALID_RTTY_SHIFT LITERAL1 ERR_UNSUPPORTED_ENCODING LITERAL1 ERR_INVALID_NUM_BROAD_ADDRS LITERAL1 + +ERR_INVALID_CRC LITERAL1 +LORA_DETECTED LITERAL1 diff --git a/src/RadioLib.h b/src/RadioLib.h index 0de12de2..ea5e3151 100644 --- a/src/RadioLib.h +++ b/src/RadioLib.h @@ -13,7 +13,9 @@ #include "modules/RFM96.h" #include "modules/RFM97.h" #include "modules/SX1231.h" +#include "modules/SX1261.h" #include "modules/SX1262.h" +#include "modules/SX1268.h" #include "modules/SX1272.h" #include "modules/SX1273.h" #include "modules/SX1276.h" @@ -42,13 +44,13 @@ class Radio { public: Radio(); - + Module* ModuleA; Module* ModuleB; - + private: - - + + }; extern Radio RadioShield; diff --git a/src/TypeDef.h b/src/TypeDef.h index 30a077c1..7cc0a70b 100644 --- a/src/TypeDef.h +++ b/src/TypeDef.h @@ -108,4 +108,8 @@ // CC1101-specific status codes #define ERR_INVALID_NUM_BROAD_ADDRS -601 +// SX126x-specific status codes +#define ERR_INVALID_CRC -701 +#define LORA_DETECTED -702 + #endif diff --git a/src/modules/SX1261.h b/src/modules/SX1261.h new file mode 100644 index 00000000..cb8a5758 --- /dev/null +++ b/src/modules/SX1261.h @@ -0,0 +1,16 @@ +#ifndef _RADIOLIB_SX1261_H +#define _RADIOLIB_SX1261_H + +#include "TypeDef.h" +#include "Module.h" +#include "SX126x.h" +#include "SX1262.h" + +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_SX1261 0x01 +#define SX126X_PA_CONFIG_SX1262 0x00 + +// TODO: implement SX1261 class +using SX1261 = SX1262; + +#endif diff --git a/src/modules/SX1268.h b/src/modules/SX1268.h new file mode 100644 index 00000000..8f00ecd5 --- /dev/null +++ b/src/modules/SX1268.h @@ -0,0 +1,16 @@ +#ifndef _RADIOLIB_SX1268_H +#define _RADIOLIB_SX1268_H + +#include "TypeDef.h" +#include "Module.h" +#include "SX126x.h" +#include "SX1262.h" + +//SX126X_CMD_SET_PA_CONFIG +#define SX126X_PA_CONFIG_SX1261 0x01 +#define SX126X_PA_CONFIG_SX1262 0x00 + +// TODO: implement SX1268 class +using SX1268 = SX1262; + +#endif diff --git a/src/modules/SX126x.cpp b/src/modules/SX126x.cpp index 13e5f6d9..0f529568 100644 --- a/src/modules/SX126x.cpp +++ b/src/modules/SX126x.cpp @@ -65,12 +65,13 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleL pinMode(_mod->getRx(), INPUT); // initialize configuration variables (will be overwritten during public settings configuration) - _br = 21333; - _freqDev = 52429; + _br = 21333; // 48.0 kbps + _freqDev = 52428; // 50.0 kHz _rxBw = SX126X_GFSK_RX_BW_117_3; _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_5; - _crcTypeFSK = SX126X_GFSK_CRC_1_BYTE; + _crcTypeFSK = SX126X_GFSK_CRC_2_BYTE_INV; // CCIT CRC configuration _preambleLengthFSK = preambleLength; + _addrComp = SX126X_GFSK_ADDRESS_FILT_OFF; // get status and errors getStatus(); @@ -110,8 +111,7 @@ int16_t SX126x::beginFSK(float br, float freqDev, float rxBw, uint16_t preambleL // set default sync word 0x2D01 - not a beginFSK attribute uint8_t sync[] = {0x2D, 0x01}; - _syncWordLength = 2; - state = setSyncWord(sync, _syncWordLength); + state = setSyncWord(sync, 2); if(state != ERR_NONE) { return(state); } @@ -148,37 +148,22 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { float nSymbol = _preambleLength + sfCoeff1 + 8 + ceil(max(8.0 * len + (_crcType * 16.0) - 4.0 * _sf + sfCoeff2 + 20.0, 0.0) / sfDivisor) * (_cr + 4); timeout = (uint32_t)(symbolLength * nSymbol * 1500.0); - // set packet length - setPacketParams(_preambleLength, _crcType, len); - } else if(modem == SX126X_PACKET_TYPE_GFSK) { + // calculate timeout (500% of expected time-on-air) + float brBps = ((float)(SX126X_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)_br; + timeout = (uint32_t)(((len * 8.0) / brBps) * 1000000.0 * 5.0); + } else { return(ERR_UNKNOWN); } - DEBUG_PRINT(F("Timeout in ")) + DEBUG_PRINT(F("Timeout in ")); DEBUG_PRINT(timeout); - DEBUG_PRINTLN(F(" us")) - - // set DIO mapping - setDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_TX_DONE); - - // set buffer pointers - setBufferBaseAddress(); - - // write packet to buffer - writeBuffer(data, len); - - // clear interrupt flags - clearIrqStatus(); + DEBUG_PRINTLN(F(" us")); // start transmission - uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); - setTx(timeoutValue); - - // wait for BUSY to go low (= PA ramp up done) - while(digitalRead(_mod->getRx())); + startTransmit(data, len, addr); // wait for packet transmission or timeout uint32_t start = micros(); @@ -196,6 +181,9 @@ int16_t SX126x::transmit(uint8_t* data, size_t len, uint8_t addr) { // clear interrupt flags clearIrqStatus(); + // set mode to standby to disable transmitter + standby(); + return(ERR_NONE); } @@ -213,27 +201,25 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { timeout = (uint32_t)(symbolLength * 100.0 * 1000.0); } else if(modem == SX126X_PACKET_TYPE_GFSK) { + // calculate timeout (500 % of expected time-one-air) + size_t maxLen = len; + if(len == 0) { + maxLen = 0xFF; + } + float brBps = ((float)(SX126X_CRYSTAL_FREQ) * 1000000.0 * 32.0) / (float)_br; + timeout = (uint32_t)(((maxLen * 8.0) / brBps) * 1000000.0 * 5.0); } else { return(ERR_UNKNOWN); } - DEBUG_PRINT(F("Timeout in ")) + DEBUG_PRINT(F("Timeout in ")); DEBUG_PRINT(timeout); - DEBUG_PRINTLN(F(" us")) - - // set DIO mapping - setDioIrqParams(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_RX_DONE); - - // set buffer pointers - setBufferBaseAddress(); - - // clear interrupt flags - clearIrqStatus(); + DEBUG_PRINTLN(F(" us")); // start reception uint32_t timeoutValue = (uint32_t)((float)timeout / 15.625); - setRx(timeoutValue); + startReceive(timeoutValue); // wait for packet reception or timeout uint32_t start = micros(); @@ -244,6 +230,132 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { } } + // read the received data + return(readData(data, len)); +} + +int16_t SX126x::transmitDirect(uint32_t frf) { + // user requested to start transmitting immediately (required for RTTY) + if(frf != 0) { + setRfFrequency(frf); + } + + // start transmitting + uint8_t data[] = {SX126X_CMD_NOP}; + SPIwriteCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, data, 1); + return(ERR_NONE); +} + +int16_t SX126x::receiveDirect() { + // SX126x is unable to ouput received data directly + return(ERR_UNKNOWN); +} + +int16_t SX126x::scanChannel() { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // set mode to standby + standby(); + + // set DIO pin mapping + setDioIrqParams(SX126X_IRQ_CAD_DETECTED | SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DONE, SX126X_IRQ_CAD_DETECTED); + + // clear interrupt flags + clearIrqStatus(); + + // set mode to CAD + setCad(); + + // wait for channel activity detected or timeout + while(!digitalRead(_mod->getInt0())) { + if(digitalRead(_mod->getInt1())) { + clearIrqStatus(); + return(LORA_DETECTED); + } + } + + // clear interrupt flags + clearIrqStatus(); + + return(CHANNEL_FREE); +} + +int16_t SX126x::sleep() { + uint8_t data[] = {SX126X_SLEEP_START_COLD | SX126X_SLEEP_RTC_OFF}; + SPIwriteCommand(SX126X_CMD_SET_SLEEP, data, 1); + + // wait for SX126x to safely enter sleep mode + delayMicroseconds(500); + + return(ERR_NONE); +} + +int16_t SX126x::standby(uint8_t mode) { + uint8_t data[] = {mode}; + SPIwriteCommand(SX126X_CMD_SET_STANDBY, data, 1); + return(ERR_NONE); +} + +void SX126x::setDio1Action(void (*func)(void)) { + attachInterrupt(digitalPinToInterrupt(_mod->getInt0()), func, RISING); +} + +void SX126x::setDio2Action(void (*func)(void)) { + attachInterrupt(digitalPinToInterrupt(_mod->getInt1()), func, RISING); +} + +int16_t SX126x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { + // set packet Length + uint8_t modem = getPacketType(); + if(modem == SX126X_PACKET_TYPE_LORA) { + setPacketParams(_preambleLength, _crcType, len); + } else if(modem == SX126X_PACKET_TYPE_LORA) { + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp, len); + } else { + return(ERR_UNKNOWN); + } + + // set DIO mapping + setDioIrqParams(SX126X_IRQ_TX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_TX_DONE); + + // set buffer pointers + setBufferBaseAddress(); + + // write packet to buffer + writeBuffer(data, len); + + // clear interrupt flags + clearIrqStatus(); + + // start transmission + setTx(0x000000); + + // wait for BUSY to go low (= PA ramp up done) + while(digitalRead(_mod->getRx())); + + return(ERR_NONE); +} + +int16_t SX126x::startReceive(uint32_t timeout) { + // set DIO mapping + setDioIrqParams(SX126X_IRQ_RX_DONE | SX126X_IRQ_TIMEOUT, SX126X_IRQ_RX_DONE); + + // set buffer pointers + setBufferBaseAddress(); + + // clear interrupt flags + clearIrqStatus(); + + // set mode to receive + setRx(timeout); + + return(ERR_NONE); +} + +int16_t SX126x::readData(uint8_t* data, size_t len) { // check integrity CRC uint16_t irq = getIrqStatus(); if((irq & SX126X_IRQ_CRC_ERR) || (irq & SX126X_IRQ_HEADER_ERR)) { @@ -274,39 +386,6 @@ int16_t SX126x::receive(uint8_t* data, size_t len) { return(ERR_NONE); } -int16_t SX126x::transmitDirect(uint32_t frf) { - // user requested to start transmitting immediately (required for RTTY) - if(frf != 0) { - setRfFrequency(frf); - } - - // start transmitting - uint8_t data[] = {SX126X_CMD_NOP}; - SPIwriteCommand(SX126X_CMD_SET_TX_CONTINUOUS_WAVE, data, 1); - return(ERR_NONE); -} - -int16_t SX126x::receiveDirect() { - // SX126x is unable to ouput received data directly - return(ERR_UNKNOWN); -} - -int16_t SX126x::sleep() { - uint8_t data[] = {SX126X_SLEEP_START_COLD | SX126X_SLEEP_RTC_OFF}; - SPIwriteCommand(SX126X_CMD_SET_SLEEP, data, 1); - - // wait for SX126x to safely enter sleep mode - delayMicroseconds(500); - - return(ERR_NONE); -} - -int16_t SX126x::standby(uint8_t mode) { - uint8_t data[] = {mode}; - SPIwriteCommand(SX126X_CMD_SET_STANDBY, data, 1); - return(ERR_NONE); -} - int16_t SX126x::setBandwidth(float bw) { // check active modem if(getPacketType() != SX126X_PACKET_TYPE_LORA) { @@ -407,7 +486,7 @@ int16_t SX126x::setPreambleLength(uint16_t preambleLength) { return(ERR_NONE); } else if(modem == SX126X_PACKET_TYPE_GFSK) { _preambleLengthFSK = preambleLength; - setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength); + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); return(ERR_NONE); } @@ -426,7 +505,8 @@ int16_t SX126x::setFrequencyDeviation(float freqDev) { } // calculate raw frequency deviation value - _freqDev = (uint32_t)((freqDev * 1000.0) / ((SX126X_CRYSTAL_FREQ * 1000000.0) / (float)((uint32_t)(1) << 25))); + //_freqDev = (uint32_t)((freqDev * 1000.0) / ((SX126X_CRYSTAL_FREQ * 1000000.0) / (float)((uint32_t)(1) << 25))); + _freqDev = (uint32_t)(((freqDev * 1000.0) * (float)((uint32_t)(1) << 25)) / (SX126X_CRYSTAL_FREQ * 1000000.0)); // update modulation parameters setModulationParamsFSK(_br, _pulseShape, _rxBw, _freqDev); @@ -520,15 +600,16 @@ int16_t SX126x::setDataShaping(float sh) { } // check allowed values + sh *= 10.0; if(abs(sh - 0.0) <= 0.001) { _pulseShape = SX126X_GFSK_FILTER_NONE; - } else if(abs(sh - 0.3) <= 0.001) { + } else if(abs(sh - 3.0) <= 0.001) { _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_3; - } else if(abs(sh - 0.5) <= 0.001) { + } else if(abs(sh - 5.0) <= 0.001) { _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_5; - } else if(abs(sh - 0.7) <= 0.001) { + } else if(abs(sh - 7.0) <= 0.001) { _pulseShape = SX126X_GFSK_FILTER_GAUSS_0_7; - } else if(abs(sh - 1.0) <= 0.001) { + } else if(abs(sh - 10.0) <= 0.001) { _pulseShape = SX126X_GFSK_FILTER_GAUSS_1; } else { return(ERR_INVALID_DATA_SHAPING); @@ -555,8 +636,112 @@ int16_t SX126x::setSyncWord(uint8_t* syncWord, uint8_t len) { writeRegister(SX126X_REG_SYNC_WORD_0, syncWord, len); // update packet parameters - _syncWordLength = len; - setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength); + _syncWordLength = len * 8; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); + + return(ERR_NONE); +} + +int16_t SX126x::setNodeAddress(uint8_t nodeAddr) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // enable address filtering (node only) + _addrComp = SX126X_GFSK_ADDRESS_FILT_NODE; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); + + // set node address + writeRegister(SX126X_REG_NODE_ADDRESS, &nodeAddr, 1); + + return(ERR_NONE); +} + +int16_t SX126x::setBroadcastAddress(uint8_t broadAddr) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // enable address filtering (node and broadcast) + _addrComp = SX126X_GFSK_ADDRESS_FILT_NODE_BROADCAST; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); + + // set broadcast address + writeRegister(SX126X_REG_BROADCAST_ADDRESS, &broadAddr, 1); + + return(ERR_NONE); +} + +int16_t SX126x::disableAddressFiltering() { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // disable address filtering + _addrComp = SX126X_GFSK_ADDRESS_FILT_OFF; + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); + + return(ERR_NONE); +} + +int16_t SX126x::setCRC(bool enableCRC) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_LORA) { + return(ERR_WRONG_MODEM); + } + + // update packet parameters + if(enableCRC) { + _crcType = SX126X_LORA_CRC_ON; + } else { + _crcType = SX126X_LORA_CRC_OFF; + } + setPacketParams(_preambleLength, _crcType); + + return(ERR_NONE); +} + +int16_t SX126x::setCRC(uint8_t len, uint16_t initial, uint16_t polynomial, bool inverted) { + // check active modem + if(getPacketType() != SX126X_PACKET_TYPE_GFSK) { + return(ERR_WRONG_MODEM); + } + + // update packet parameters + switch(len) { + case 0: + _crcTypeFSK = SX126X_GFSK_CRC_OFF; + break; + case 1: + if(inverted) { + _crcTypeFSK = SX126X_GFSK_CRC_1_BYTE_INV; + } else { + _crcTypeFSK = SX126X_GFSK_CRC_1_BYTE; + } + break; + case 2: + if(inverted) { + _crcTypeFSK = SX126X_GFSK_CRC_2_BYTE_INV; + } else { + _crcTypeFSK = SX126X_GFSK_CRC_2_BYTE; + } + break; + default: + return(ERR_INVALID_CRC); + } + setPacketParamsFSK(_preambleLengthFSK, _crcTypeFSK, _syncWordLength, _addrComp); + + // write initial CRC value + uint8_t data[2] = {(uint8_t)((initial >> 8) & 0xFF), (uint8_t)(initial & 0xFF)}; + writeRegister(SX126X_REG_CRC_INITIAL_MSB, data, 2); + + // write CRC polynomial value + data[0] = (uint8_t)((polynomial >> 8) & 0xFF); + data[1] = (uint8_t)(polynomial & 0xFF); + writeRegister(SX126X_REG_CRC_POLYNOMIAL_MSB, data, 2); return(ERR_NONE); } @@ -566,11 +751,6 @@ float SX126x::getDataRate() { } float SX126x::getRSSI() { - // check active modem - if(getPacketType() != SX126X_PACKET_TYPE_LORA) { - return(ERR_WRONG_MODEM); - } - // get last packet RSSI from packet status uint32_t packetStatus = getPacketStatus(); uint8_t rssiPkt = packetStatus & 0xFF; @@ -630,7 +810,7 @@ void SX126x::readBuffer(uint8_t* data, uint8_t numBytes) { uint8_t* dat = new uint8_t[1 + numBytes]; dat[0] = SX126X_CMD_NOP; memcpy(dat + 1, data, numBytes); - SPIreadCommand(SX126X_CMD_READ_BUFFER, dat, numBytes); + SPIreadCommand(SX126X_CMD_READ_BUFFER, dat, 1 + numBytes); memcpy(data, dat + 1, numBytes); delete[] dat; } @@ -699,11 +879,11 @@ void SX126x::setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t p SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 6); } -void SX126x::setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t payloadLength, uint8_t packetType, uint8_t addrComp, uint8_t preambleDetectorLength, uint8_t whitening) { +void SX126x::setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t addrComp, uint8_t payloadLength, uint8_t packetType, uint8_t preambleDetectorLength, uint8_t whitening) { uint8_t data[9] = {(uint8_t)((preambleLength >> 8) & 0xFF), (uint8_t)(preambleLength & 0xFF), preambleDetectorLength, syncWordLength, addrComp, packetType, payloadLength, crcType, whitening}; - SPIwriteCommand(SX126X_CMD_SET_MODULATION_PARAMS, data, 9); + SPIwriteCommand(SX126X_CMD_SET_PACKET_PARAMS, data, 9); } void SX126x::setBufferBaseAddress(uint8_t txBaseAddress, uint8_t rxBaseAddress) { @@ -768,7 +948,7 @@ int16_t SX126x::config() { // set DIO2 as IRQ uint8_t* data = new uint8_t[1]; data[0] = SX126X_DIO2_AS_IRQ; - SPIwriteCommand(SX126X_DIO2_AS_RF_SWITCH, data, 1); + SPIwriteCommand(SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, data, 1); // set regulator mode data[0] = SX126X_REGULATOR_DC_DC; @@ -810,7 +990,7 @@ int16_t SX126x::configFSK() { // set DIO2 as IRQ uint8_t* data = new uint8_t[1]; data[0] = SX126X_DIO2_AS_IRQ; - SPIwriteCommand(SX126X_DIO2_AS_RF_SWITCH, data, 1); + SPIwriteCommand(SX126X_CMD_SET_DIO2_AS_RF_SWITCH_CTRL, data, 1); // set regulator mode data[0] = SX126X_REGULATOR_DC_DC; diff --git a/src/modules/SX126x.h b/src/modules/SX126x.h index 38a98dc9..39344996 100644 --- a/src/modules/SX126x.h +++ b/src/modules/SX126x.h @@ -318,6 +318,8 @@ class SX126x: public PhysicalLayer { // introduce PhysicalLayer overloads using PhysicalLayer::transmit; using PhysicalLayer::receive; + using PhysicalLayer::startTransmit; + using PhysicalLayer::readData; // constructor SX126x(Module* mod); @@ -329,9 +331,17 @@ class SX126x: public PhysicalLayer { int16_t receive(uint8_t* data, size_t len); int16_t transmitDirect(uint32_t frf = 0); int16_t receiveDirect(); + int16_t scanChannel(); int16_t sleep(); int16_t standby(uint8_t mode = SX126X_STANDBY_RC); + // interrupt methods + void setDio1Action(void (*func)(void)); + void setDio2Action(void (*func)(void)); + int16_t startTransmit(uint8_t* data, size_t len, uint8_t addr = 0); + int16_t startReceive(uint32_t timeout = 0xFFFFFF); + int16_t readData(uint8_t* data, size_t len); + // configuration methods int16_t setBandwidth(float bw); int16_t setSpreadingFactor(uint8_t sf); @@ -344,6 +354,11 @@ class SX126x: public PhysicalLayer { int16_t setRxBandwidth(float rxBw); int16_t setDataShaping(float sh); int16_t setSyncWord(uint8_t* syncWord, uint8_t len); + int16_t setNodeAddress(uint8_t nodeAddr); + int16_t setBroadcastAddress(uint8_t broadAddr); + int16_t disableAddressFiltering(); + int16_t setCRC(bool enableCRC); + int16_t setCRC(uint8_t len, uint16_t initial = 0x1D0F, uint16_t polynomial = 0x1021, bool inverted = true); float getDataRate(); float getRSSI(); float getSNR(); @@ -359,14 +374,14 @@ class SX126x: public PhysicalLayer { void readBuffer(uint8_t* data, uint8_t numBytes); void setDioIrqParams(uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask = SX126X_IRQ_NONE, uint16_t dio3Mask = SX126X_IRQ_NONE); uint16_t getIrqStatus(); - void clearIrqStatus(uint16_t clearIrqParams = 0xFFFF); + void clearIrqStatus(uint16_t clearIrqParams = SX126X_IRQ_ALL); void setRfFrequency(uint32_t frf); uint8_t getPacketType(); void setTxParams(uint8_t power, uint8_t rampTime = SX126X_PA_RAMP_200U); void setModulationParams(uint8_t sf, uint8_t bw, uint8_t cr, uint8_t ldro = 0xFF); void setModulationParamsFSK(uint32_t br, uint8_t pulseShape, uint8_t rxBw, uint32_t freqDev); void setPacketParams(uint16_t preambleLength, uint8_t crcType, uint8_t payloadLength = 0xFF, uint8_t headerType = SX126X_LORA_HEADER_EXPLICIT, uint8_t invertIQ = SX126X_LORA_IQ_STANDARD); - void setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t payloadLength = 0xFF, uint8_t packetType = SX126X_GFSK_PACKET_VARIABLE, uint8_t addrComp = SX126X_GFSK_ADDRESS_FILT_OFF, uint8_t preambleDetectorLength = SX126X_GFSK_PREAMBLE_DETECT_8, uint8_t whitening = SX126X_GFSK_WHITENING_ON); + void setPacketParamsFSK(uint16_t preambleLength, uint8_t crcType, uint8_t syncWordLength, uint8_t addrComp, uint8_t payloadLength = 0xFF, uint8_t packetType = SX126X_GFSK_PACKET_VARIABLE, uint8_t preambleDetectorLength = SX126X_GFSK_PREAMBLE_DETECT_16, uint8_t whitening = SX126X_GFSK_WHITENING_ON); void setBufferBaseAddress(uint8_t txBaseAddress = 0x00, uint8_t rxBaseAddress = 0x00); uint8_t getStatus(); uint32_t getPacketStatus(); @@ -383,7 +398,7 @@ class SX126x: public PhysicalLayer { float _bwKhz; uint32_t _br, _freqDev; - uint8_t _rxBw, _pulseShape, _crcTypeFSK, _syncWordLength; + uint8_t _rxBw, _pulseShape, _crcTypeFSK, _syncWordLength, _addrComp; uint16_t _preambleLengthFSK; float _dataRate;