diff --git a/examples/Stream/Stream_Receive/Stream_Receive.ino b/examples/Stream/Stream_Receive/Stream_Receive.ino new file mode 100644 index 00000000..73904bb0 --- /dev/null +++ b/examples/Stream/Stream_Receive/Stream_Receive.ino @@ -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 + +// 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; + } + +} diff --git a/examples/Stream/Stream_Transmit/Stream_Transmit.ino b/examples/Stream/Stream_Transmit/Stream_Transmit.ino new file mode 100644 index 00000000..8be539f7 --- /dev/null +++ b/examples/Stream/Stream_Transmit/Stream_Transmit.ino @@ -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 + +// 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; + } +} diff --git a/keywords.txt b/keywords.txt index 236c8039..30d7eb7e 100644 --- a/keywords.txt +++ b/keywords.txt @@ -148,7 +148,10 @@ getFHSSChannel KEYWORD2 clearFHSSInt KEYWORD2 randomByte KEYWORD2 getPacketLength KEYWORD2 - +setFifoEmptyAction KEYWORD2 +clearFifoEmptyAction KEYWORD2 +fifoAdd KEYWORD2 +fifoGet KEYWORD2 # RF69-specific setAESKey KEYWORD2 diff --git a/src/modules/SX127x/SX127x.cpp b/src/modules/SX127x/SX127x.cpp index 5c038033..22013554 100644 --- a/src/modules/SX127x/SX127x.cpp +++ b/src/modules/SX127x/SX127x.cpp @@ -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); diff --git a/src/modules/SX127x/SX127x.h b/src/modules/SX127x/SX127x.h index 15ba7154..bad363d3 100644 --- a/src/modules/SX127x/SX127x.h +++ b/src/modules/SX127x/SX127x.h @@ -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.