diff --git a/examples/SX127x/SX127x_Receive_FHSS/SX127x_Receive_FHSS.ino b/examples/SX127x/SX127x_Receive_FHSS/SX127x_Receive_FHSS.ino new file mode 100644 index 00000000..7ee31b83 --- /dev/null +++ b/examples/SX127x/SX127x_Receive_FHSS/SX127x_Receive_FHSS.ino @@ -0,0 +1,110 @@ +/* + RadioLib SX127x Transmit with Frequency Hopping Example + + This example transmits packets using SX1278 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 SX127x/RFM9x family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ + + The SX1276 / 7 / 8 / 9 supports FHSS or Frequency Hopping Spread Spectrum. + Once a hopping period is set and a transmission is started the radio + will begin triggering interrupts every hop period where the radio frequency + is changed to the next channel. +*/ + +#include //Click here to get the library: http://librarymanager/All#RadioLib + +// SX1276 has the following connections: +const int pin_cs = 10; +const int pin_dio0 = 2; +const int pin_dio1 = 9; +const int pin_rst = 3; +SX1276 radio = new Module(pin_cs, pin_dio0, pin_rst, pin_dio1); + +volatile bool rxComplete = false; +volatile bool fhssChange = false; + +// the channel frequencies can be generated randomly or hard coded +float channels[] = {908.0, 906.0, 907.0, 905.0, 903.0, 910.0, 909.0}; +int numberOfChannels = sizeof(channels) / sizeof(float); + +int hopsCompleted = 0; + +void setup() { + Serial.begin(9600); + + // begin radio on home channel + Serial.print(F("[SX127x] Initializing ... ")); + int state = radio.begin(channels[0]); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed with code: ")); + Serial.println(state); + } + else + Serial.println(F("Success!")); + + // set hop period to enable FHSS + state = radio.setFHSSHoppingPeriod(9); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Error setting hopping period: ")); + Serial.println(state); + } + radio.setDio0Action(dio0ISR); // called when transmission is finished + radio.setDio1Action(dio1ISR); // called after a transmission has started, so we can move to next freq + + // start listening for LoRa packets + Serial.print(F("[SX1278] Starting to listen ... ")); + state = radio.startReceive(); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("failed, code ")); + Serial.println(state); + while (true); + } +} + +void loop() { + if (rxComplete == true) { + uint8_t incomingBuffer[255]; + radio.readData(incomingBuffer, 255); + uint8_t receivedBytes = radio.getPacketLength(); + Serial.write(incomingBuffer, receivedBytes); + Serial.println(); + + Serial.print(F("Hops completed: ")); + Serial.println(hopsCompleted); + hopsCompleted = 0; + + radio.startReceive(); + + rxComplete = false; + } + + if (fhssChange == true) { + radio.setFrequency(channels[radio.getFHSSChannel() % numberOfChannels]); + + hopsCompleted++; + radio.clearFHSSInt(); + fhssChange = false; + } +} + +// ISR when DIO0 goes low +// called when transmission is complete or when RX is received +void dio0ISR(void) { + rxComplete = true; +} + +// ISR when DIO1 goes low +// called when FhssChangeChannel interrupt occurs (at the beginning of each transmission) +void dio1ISR(void) { + fhssChange = true; +} \ No newline at end of file diff --git a/examples/SX127x/SX127x_Transmit_FHSS/SX127x_Transmit_FHSS.ino b/examples/SX127x/SX127x_Transmit_FHSS/SX127x_Transmit_FHSS.ino new file mode 100644 index 00000000..f1ecbaf8 --- /dev/null +++ b/examples/SX127x/SX127x_Transmit_FHSS/SX127x_Transmit_FHSS.ino @@ -0,0 +1,113 @@ +/* + RadioLib SX127x Transmit with Frequency Hopping Example + + This example transmits packets using SX1278 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 SX127x/RFM9x family can also be used. + + For default module settings, see the wiki page + https://github.com/jgromes/RadioLib/wiki/Default-configuration#sx127xrfm9x---lora-modem + + For full API reference, see the GitHub Pages + https://jgromes.github.io/RadioLib/ + + The SX1276 / 7 / 8 / 9 supports FHSS or Frequency Hopping Spread Spectrum. + Once a hopping period is set and a transmission is started the radio + will begin triggering interrupts every hop period where the radio frequency + is changed to the next channel. +*/ + +#include //Click here to get the library: http://librarymanager/All#RadioLib + +// SX1276 has the following connections: +const int pin_cs = 10; +const int pin_dio0 = 2; +const int pin_dio1 = 9; +const int pin_rst = 3; +SX1276 radio = new Module(pin_cs, pin_dio0, pin_rst, pin_dio1); + +volatile bool xmitComplete = false; +volatile bool fhssChange = false; + +// the channel frequencies can be generated randomly or hard coded +float channels[] = {908.0, 906.0, 907.0, 905.0, 903.0, 910.0, 909.0}; +int numberOfChannels = sizeof(channels) / sizeof(float); + +int hopsCompleted = 0; +int counter = 0; + +void setup() { + Serial.begin(9600); + + // begin radio on home channel + Serial.print(F("[SX127x] Initializing ... ")); + int state = radio.begin(channels[0]); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Failed with code: ")); + Serial.println(state); + } + else + Serial.println(F("Success!")); + + // set hop period to enable FHSS + state = radio.setFHSSHoppingPeriod(9); + if(state != RADIOLIB_ERR_NONE) { + Serial.print(F("Error setting hopping period: ")); + Serial.println(state); + } + + radio.setDio0Action(dio0ISR); // called when transmission is finished + radio.setDio1Action(dio1ISR); // called after a transmission has started, so we can move to next freq + + Serial.print(F("Transmitting packet...")); + + String longOutput = "Let's create a really long packet to trigger lots of hop interrupts. A packet can be up to 256 bytes long. This packet is 222 bytes so using sf = 9, bw = 125, timeOnAir is 1488ms. 1488ms / (9*4.10ms) = 40 hops. Counter: "; + + state = radio.startTransmit(longOutput + counter); + if (state != RADIOLIB_ERR_NONE) { + Serial.print(F("Error transmitting with code: ")); + Serial.println(state); + } +} + +void loop() { + if (xmitComplete == true) { + xmitComplete = false; + Serial.println(F("Transmit complete")); + Serial.print(F("Radio after xmit is on channel: ")); + Serial.println(radio.getFHSSChannel()); + // the FHSS channel is automatically reset to 0 upon end of transmission + + radio.setFrequency(channels[radio.getFHSSChannel() % numberOfChannels]); // Return to home channel before next transaction + + Serial.print(F("Hops completed: ")); + Serial.println(hopsCompleted); + hopsCompleted = 0; + + radio.startReceive(); + } + + if (fhssChange == true) { + radio.setFrequency(channels[radio.getFHSSChannel() % numberOfChannels]); + + hopsCompleted++; + fhssChange = false; + radio.clearFHSSInt(); + } +} + +// ISR when DIO0 goes low +// called when transmission is complete or when RX is received +void dio0ISR(void) { + xmitComplete = true; +} + +// ISR when DIO1 goes low +// called when FhssChangeChannel interrupt occurs (at regular HoppingPeriods) +void dio1ISR(void) { + fhssChange = true; +} diff --git a/keywords.txt b/keywords.txt index 9167c2a6..31db163d 100644 --- a/keywords.txt +++ b/keywords.txt @@ -142,6 +142,10 @@ setDirectAction KEYWORD2 readBit KEYWORD2 enableBitSync KEYWORD2 disableBitSync KEYWORD2 +setFHSSHoppingPeriod KEYWORD2 +getFHSSHoppingPeriod KEYWORD2 +getFHSSChannel KEYWORD2 +clearFHSSInt KEYWORD2 # RF69-specific setAESKey KEYWORD2 diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 24323447..1db50c20 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -375,7 +375,12 @@ int16_t SX127x::startReceive(uint8_t len, uint8_t mode) { int16_t modem = getActiveModem(); if(modem == RADIOLIB_SX127X_LORA) { // set DIO pin mapping - state = _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_RX_DONE | RADIOLIB_SX127X_DIO1_RX_TIMEOUT, 7, 4); + if(_mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { + state = _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_RX_DONE | RADIOLIB_SX127X_DIO1_FHSS_CHANGE_CHANNEL, 7, 4); + } + else { + state = _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_RX_DONE | RADIOLIB_SX127X_DIO1_RX_TIMEOUT, 7, 4); + } // set expected packet length for SF6 if(_sf == 6) { @@ -448,7 +453,12 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) { } // set DIO mapping - _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_TX_DONE, 7, 6); + if(_mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) > RADIOLIB_SX127X_HOP_PERIOD_OFF) { + _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_TX_DONE | RADIOLIB_SX127X_DIO1_FHSS_CHANGE_CHANNEL, 7, 4); + } + else { + _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_TX_DONE, 7, 6); + } // apply fixes to errata RADIOLIB_ERRATA_SX127X(false); @@ -987,8 +997,12 @@ int16_t SX127x::setOOK(bool enableOOK) { } int16_t SX127x::setFrequencyRaw(float newFreq) { - // set mode to standby - int16_t state = setMode(RADIOLIB_SX127X_STANDBY); + int16_t state = RADIOLIB_ERR_NONE; + + // set mode to standby if not FHSS + if(_mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD) == RADIOLIB_SX127X_HOP_PERIOD_OFF) { + state = setMode(RADIOLIB_SX127X_STANDBY); + } // calculate register values uint32_t FRF = (newFreq * (uint32_t(1) << RADIOLIB_SX127X_DIV_EXPONENT)) / RADIOLIB_SX127X_CRYSTAL_FREQ; @@ -1353,4 +1367,25 @@ void SX127x::readBit(RADIOLIB_PIN_TYPE pin) { updateDirectBuffer((uint8_t)digitalRead(pin)); } +int16_t SX127x::setFHSSHoppingPeriod(uint8_t freqHoppingPeriod) { + return(_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD, freqHoppingPeriod)); +} + +uint8_t SX127x::getFHSSHoppingPeriod(void) { + return(_mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_PERIOD)); +} + +uint8_t SX127x::getFHSSChannel(void) { + return(_mod->SPIgetRegValue(RADIOLIB_SX127X_REG_HOP_CHANNEL, 5, 0)); +} + +void SX127x::clearFHSSInt(void) { + int16_t modem = getActiveModem(); + if(modem == RADIOLIB_SX127X_LORA) { + _mod->SPIwriteRegister(RADIOLIB_SX127X_REG_IRQ_FLAGS, getIRQFlags() | RADIOLIB_SX127X_CLEAR_IRQ_FLAG_FHSS_CHANGE_CHANNEL); + } else if(modem == RADIOLIB_SX127X_FSK_OOK) { + return; //These are not the interrupts you are looking for + } +} + #endif diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 66ab01c8..b0671bac 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -1073,6 +1073,34 @@ class SX127x: public PhysicalLayer { */ void readBit(RADIOLIB_PIN_TYPE pin); + /*! + \brief Sets the hopping period and enables FHSS + + \param freqHoppingPeriod Integer multiple of symbol periods between hops + + \returns \ref status_codes + */ + int16_t setFHSSHoppingPeriod(uint8_t freqHoppingPeriod); + + /*! + \brief Gets FHSS hopping period + + \returns 8 bit period + */ + uint8_t getFHSSHoppingPeriod(void); + + /*! + \brief Gets the FHSS channel in use + + \returns 6 bit channel number + */ + uint8_t getFHSSChannel(void); + + /*! + \brief Clear the FHSS interrupt + */ + void clearFHSSInt(void); + #if !defined(RADIOLIB_GODMODE) && !defined(RADIOLIB_LOW_LEVEL) protected: #endif