[Stream] Added Stream mode support for SX127x (#201) (#403)

This commit is contained in:
jgromes 2022-07-10 17:43:51 +02:00
parent 8ae092ac81
commit 9836e57616
5 changed files with 441 additions and 16 deletions

View file

@ -0,0 +1,142 @@
/*
RadioLib Stream Receive Example
This example shows how to receive data in "Stream" mode.
In this mode, arbitrary length of data may be sent, up to
"infinite" continuous transmission between two devices.
Caveats:
- CRC of the payload is not supported
- the length of the payload must be known in advance
Modules that can be used for Stream are:
- SX127x/RFM9x (FSK mode only)
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/
*/
// include the library
#include <RadioLib.h>
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 radio = new Module(10, 2, 9, 3);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 radio = RadioShield.ModuleA;
void setup() {
Serial.begin(9600);
// initialize SX1278 with default settings
Serial.print(F("[SX1278] Initializing ... "));
int state = radio.beginFSK();
// when using one of the non-LoRa modules for Stream transmit
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
// int state = radio.begin();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// set the function that will be called
// when receive buffer is full
radio.setFifoFullAction(fifoGet);
// fixed packet length mode is required
radio.fixedPacketLengthMode(0);
// start listening for packets
Serial.print(F("[SX1278] Starting to listen ... "));
state = radio.startReceive();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// if needed, 'listen' mode can be disabled by calling
// any of the following methods:
//
// radio.standby()
// radio.sleep()
// radio.transmit();
// radio.receive();
// radio.readData();
// radio.scanChannel();
}
// flag to indicate that a packet was received
volatile bool receivedFlag = false;
// disable interrupt when it's not needed
volatile bool enableInterrupt = true;
// how many bytes are there in total
const int totalLength = 512;
// counter to keep track of how many bytes have been received so far
volatile int receivedLength = 0;
// buffer to save the received data into
volatile uint8_t rxBuffer[totalLength + 1];
// this function is called when the radio receive buffer
// is full and ready to be read
// IMPORTANT: this function MUST be 'void' type
// and MUST NOT have any arguments!
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void fifoGet(void) {
// check if the interrupt is enabled
if(!enableInterrupt) {
return;
}
// set the flag when we receive the full packet
receivedFlag = radio.fifoGet(rxBuffer, totalLength, &receivedLength);
}
void loop() {
// check if the flag is set
if(receivedFlag) {
// disable the interrupt service routine while
// processing the data
enableInterrupt = false;
// packet was successfully received
Serial.println(F("[SX1278] Received packet!"));
// print data of the packet
Serial.print(F("[SX1278] Data:\t\t"));
Serial.println((char*)rxBuffer);
// reset flag
receivedFlag = false;
receivedLength = 0;
// put module back to listen mode
radio.startReceive();
// we're ready to receive more packets,
// enable interrupt service routine
enableInterrupt = true;
}
}

View file

@ -0,0 +1,154 @@
/*
RadioLib Stream Transmit Example
This example shows how to transmit data in "Stream" mode.
In this mode, arbitrary length of data may be sent, up to
"infinite" continuous transmission between two devices.
Caveats:
- CRC of the payload is not supported
- the length of the payload must be known in advance
Modules that can be used for Stream are:
- SX127x/RFM9x (FSK mode only)
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/
*/
// include the library
#include <RadioLib.h>
// SX1278 has the following connections:
// NSS pin: 10
// DIO0 pin: 2
// RESET pin: 9
// DIO1 pin: 3
SX1278 radio = new Module(10, 2, 9, 3);
// or using RadioShield
// https://github.com/jgromes/RadioShield
//SX1278 radio = RadioShield.ModuleA;
// save transmission state between loops
int transmissionState = RADIOLIB_ERR_NONE;
// this packet is much longer than would normally fit
// into SX1278's internal buffer
String longPacket = "Lorem ipsum dolor sit amet, consectetur adipiscing elit.\
Maecenas at urna ut nunc imperdiet laoreet. Aliquam erat volutpat.\
Etiam mattis mauris vitae posuere tincidunt. In sit amet bibendum nisl,\
a ultrices lorem. Duis hendrerit ultricies condimentum. Phasellus eget nisi\
eget massa aliquam bibendum. Pellentesque ante neque, aliquam non diam non,\
fringilla facilisis ipsum. Morbi in molestie orci. Vestibulum luctus\
venenatis arcu sit amet pellentesque. Nulla posuere sit amet turpis\
id pharetra. Curabitur nec.";
void setup() {
Serial.begin(9600);
// initialize SX1278 with default settings
Serial.print(F("[SX1278] Initializing ... "));
int state = radio.beginFSK();
// when using one of the non-LoRa modules for Stream transmit
// (RF69, CC1101, Si4432 etc.), use the basic begin() method
// int state = radio.begin();
if (state == RADIOLIB_ERR_NONE) {
Serial.println(F("success!"));
} else {
Serial.print(F("failed, code "));
Serial.println(state);
while (true);
}
// set the function that will be called
// when transmit buffer is empty
radio.setFifoEmptyAction(fifoAdd);
// fixed packet length mode is required
radio.fixedPacketLengthMode(0);
// start transmitting the long packet
Serial.print(F("[SX1278] Sending a very long packet ... "));
transmissionState = radio.startTransmit(longPacket);
}
// flag to indicate that a packet was sent
volatile bool transmittedFlag = false;
// disable interrupt when it's not needed
volatile bool enableInterrupt = true;
// how many bytes are there in total
volatile int totalLength = longPacket.length();
// counter to keep track of how many bytes still need to be sent
volatile int remLength = totalLength;
// this function is called when the radio transmit buffer
// is empty and ready to be refilled
// IMPORTANT: this function MUST be 'void' type
// and MUST NOT have any arguments!
#if defined(ESP8266) || defined(ESP32)
ICACHE_RAM_ATTR
#endif
void fifoAdd(void) {
// check if the interrupt is enabled
if(!enableInterrupt) {
return;
}
// add more bytes to the transmit buffer
uint8_t* txBuffPtr = (uint8_t*)longPacket.c_str();
transmittedFlag = radio.fifoAdd(txBuffPtr, totalLength, &remLength);
}
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;
// reset the counter
remLength = totalLength;
if (transmissionState == RADIOLIB_ERR_NONE) {
// packet was successfully sent
Serial.println(F("transmission finished!"));
// NOTE: when using interrupt-driven transmit method,
// it is not possible to automatically measure
// transmission data rate using getDataRate()
} else {
Serial.print(F("failed, code "));
Serial.println(transmissionState);
}
// NOTE: in FSK mode, SX127x will not automatically
// turn transmitter off after sending a packet
// set mode to standby to ensure we don't jam others
radio.standby();
// wait a second before transmitting again
delay(1000);
// send another one
Serial.print(F("[SX1278] Sending another long packet ... "));
transmissionState = radio.startTransmit(longPacket);
// we're ready to send more packets,
// enable interrupt service routine
enableInterrupt = true;
}
}

View file

@ -148,7 +148,10 @@ getFHSSChannel KEYWORD2
clearFHSSInt KEYWORD2
randomByte KEYWORD2
getPacketLength KEYWORD2
setFifoEmptyAction KEYWORD2
clearFifoEmptyAction KEYWORD2
fifoAdd KEYWORD2
fifoGet KEYWORD2
# RF69-specific
setAESKey KEYWORD2

View file

@ -429,6 +429,87 @@ void SX127x::clearDio1Action() {
_mod->detachInterrupt(RADIOLIB_DIGITAL_PIN_TO_INTERRUPT(_mod->getGpio()));
}
void SX127x::setFifoEmptyAction(void (*func)(void)) {
// set DIO1 to the FIFO empty event (the register setting is done in startTransmit)
setDio1Action(func);
}
void SX127x::clearFifoEmptyAction() {
clearDio1Action();
}
void SX127x::setFifoFullAction(void (*func)(void)) {
// set the interrupt
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_FIFO_THRESH, 5, 0);
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_PACK_FIFO_LEVEL, 5, 4);
// set DIO1 to the FIFO full event
setDio1Action(func);
}
void SX127x::clearFifoFullAction() {
clearDio1Action();
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, 0x00, 5, 4);
}
bool SX127x::fifoAdd(uint8_t* data, int totalLen, volatile int* remLen) {
// subtract first (this may be the first time we get to modify the remaining length)
*remLen -= RADIOLIB_SX127X_FIFO_THRESH - 1;
// check if there is still something left to send
if(*remLen <= 0) {
// we're done
return(true);
}
// calculate the number of bytes we can copy
int len = *remLen;
if(len > RADIOLIB_SX127X_FIFO_THRESH - 1) {
len = RADIOLIB_SX127X_FIFO_THRESH - 1;
}
// clear interrupt flags
clearIRQFlags();
// copy the bytes to the FIFO
_mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, &data[totalLen - *remLen], len);
// this is a hack, but it seems Rx FIFO level is getting triggered 1 byte before it should
// we just add a padding byte that we can drop without consequence
_mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, '/');
// we're not done yet
return(false);
}
bool SX127x::fifoGet(volatile uint8_t* data, int totalLen, volatile int* rcvLen) {
// get pointer to the correct position in data buffer
uint8_t* dataPtr = &data[*rcvLen];
// check how much data are we still expecting
uint8_t len = RADIOLIB_SX127X_FIFO_THRESH - 1;
if(totalLen - *rcvLen < len) {
// we're nearly at the end
len = totalLen - *rcvLen;
}
// get the data
_mod->SPIreadRegisterBurst(RADIOLIB_SX127X_REG_FIFO, len, dataPtr);
(*rcvLen) += (len);
// dump the padding byte
_mod->SPIreadRegister(RADIOLIB_SX127X_REG_FIFO);
// clear flags
clearIRQFlags();
// check if we're done
if(*rcvLen >= totalLen) {
return(true);
}
return(false);
}
int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) {
// set mode to standby
int16_t state = setMode(RADIOLIB_SX127X_STANDBY);
@ -461,17 +542,16 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) {
state |= _mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_ADDR_PTR, RADIOLIB_SX127X_FIFO_TX_BASE_ADDR_MAX);
} else if(modem == RADIOLIB_SX127X_FSK_OOK) {
// check packet length
if(len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK) {
return(RADIOLIB_ERR_PACKET_TOO_LONG);
}
// set DIO mapping
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6);
// clear interrupt flags
clearIRQFlags();
// set DIO mapping
if(len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK) {
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO1_PACK_FIFO_EMPTY, 5, 4);
} else {
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_DIO_MAPPING_1, RADIOLIB_SX127X_DIO0_PACK_PACKET_SENT, 7, 6);
}
// set packet length
if (_packetLengthConfig == RADIOLIB_SX127X_PACKET_VARIABLE) {
_mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, len);
@ -485,7 +565,18 @@ int16_t SX127x::startTransmit(uint8_t* data, size_t len, uint8_t addr) {
}
// write packet to FIFO
_mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, data, len);
size_t packetLen = len;
if((modem == RADIOLIB_SX127X_FSK_OOK) && (len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK)) {
packetLen = RADIOLIB_SX127X_FIFO_THRESH - 1;
_mod->SPIsetRegValue(RADIOLIB_SX127X_REG_FIFO_THRESH, RADIOLIB_SX127X_TX_START_FIFO_NOT_EMPTY, 7, 7);
}
_mod->SPIwriteRegisterBurst(RADIOLIB_SX127X_REG_FIFO, data, packetLen);
// this is a hack, but it seems than in Stream mode, Rx FIFO level is getting triggered 1 byte before it should
// just add a padding byte that can be dropped without consequence
if((modem == RADIOLIB_SX127X_FSK_OOK) && (len > RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK)) {
_mod->SPIwriteRegister(RADIOLIB_SX127X_REG_FIFO, '/');
}
// set RF switch (if present)
_mod->setRfSwitchState(LOW, HIGH);

View file

@ -12,7 +12,7 @@
// SX127x physical layer properties
#define RADIOLIB_SX127X_FREQUENCY_STEP_SIZE 61.03515625
#define RADIOLIB_SX127X_MAX_PACKET_LENGTH 255
#define RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK 64
#define RADIOLIB_SX127X_MAX_PACKET_LENGTH_FSK 63
#define RADIOLIB_SX127X_CRYSTAL_FREQ 32.0
#define RADIOLIB_SX127X_DIV_EXPONENT 19
@ -411,7 +411,7 @@
// SX127X_REG_FIFO_THRESH
#define RADIOLIB_SX127X_TX_START_FIFO_LEVEL 0b00000000 // 7 7 start packet transmission when: number of bytes in FIFO exceeds FIFO_THRESHOLD
#define RADIOLIB_SX127X_TX_START_FIFO_NOT_EMPTY 0b10000000 // 7 7 at least one byte in FIFO (default)
#define RADIOLIB_SX127X_FIFO_THRESH 0x0F // 5 0 FIFO level threshold
#define RADIOLIB_SX127X_FIFO_THRESH 0x1F // 5 0 FIFO level threshold
// SX127X_REG_SEQ_CONFIG_1
#define RADIOLIB_SX127X_SEQUENCER_START 0b10000000 // 7 7 manually start sequencer
@ -701,6 +701,41 @@ class SX127x: public PhysicalLayer {
*/
void clearDio1Action();
/*!
\brief Set interrupt service routine function to call when FIFO is empty.
\param func Pointer to interrupt service routine.
*/
void setFifoEmptyAction(void (*func)(void));
/*!
\brief Clears interrupt service routine to call when FIFO is empty.
*/
void clearFifoEmptyAction();
/*!
\brief Set interrupt service routine function to call when FIFO is full.
\param func Pointer to interrupt service routine.
*/
void setFifoFullAction(void (*func)(void));
/*!
\brief Clears interrupt service routine to call when FIFO is full.
*/
void clearFifoFullAction();
/*!
\brief Set interrupt service routine function to call when FIFO is empty.
\param func Pointer to interrupt service routine.
\returns True when a complete packet is sent, false if more data is needed.
*/
bool fifoAdd(uint8_t* data, int totalLen, volatile int* remLen);
bool fifoGet(volatile uint8_t* data, int totalLen, volatile int* rcvLen);
/*!
\brief Interrupt-driven binary transmit method. Will start transmitting arbitrary binary data up to 255 bytes long using %LoRa or up to 63 bytes using FSK modem.
@ -933,12 +968,12 @@ class SX127x: public PhysicalLayer {
/*!
\brief Size of each decrement of the RSSI threshold in the OOK demodulator.
\param value Step size: RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_0_5_DB (default), RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_1_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_1_5_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_2_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_3_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_4_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_5_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_6_0_DB
\param value Step size: RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_0_5_DB (default), RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_1_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_1_5_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_2_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_3_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_4_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_5_0_DB, RADIOLIB_SX127X_OOK_PEAK_THRESH_STEP_6_0_DB
\returns \ref status_codes
*/
int16_t setOokPeakThresholdStep(uint8_t value);
int16_t setOokPeakThresholdStep(uint8_t value);
/*!
\brief Enable Bit synchronizer.
@ -988,7 +1023,7 @@ class SX127x: public PhysicalLayer {
\returns Expected time-on-air in microseconds.
*/
uint32_t getTimeOnAir(size_t len);
/*!
\brief Enable CRC filtering and generation.