Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add FHSS support #441

Merged
merged 4 commits into from
Jan 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 110 additions & 0 deletions examples/SX127x/SX127x_Receive_FHSS/SX127x_Receive_FHSS.ino
Original file line number Diff line number Diff line change
@@ -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 <RadioLib.h> //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;
}
113 changes: 113 additions & 0 deletions examples/SX127x/SX127x_Transmit_FHSS/SX127x_Transmit_FHSS.ino
Original file line number Diff line number Diff line change
@@ -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 <RadioLib.h> //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;
}
4 changes: 4 additions & 0 deletions keywords.txt
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ setDirectAction KEYWORD2
readBit KEYWORD2
enableBitSync KEYWORD2
disableBitSync KEYWORD2
setFHSSHoppingPeriod KEYWORD2
getFHSSHoppingPeriod KEYWORD2
getFHSSChannel KEYWORD2
clearFHSSInt KEYWORD2

# RF69-specific
setAESKey KEYWORD2
Expand Down
43 changes: 39 additions & 4 deletions src/modules/SX127x/SX127x.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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
28 changes: 28 additions & 0 deletions src/modules/SX127x/SX127x.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down