RFM69_LowPowerLab/RFM69.h

322 lines
14 KiB
C
Raw Normal View History

2013-07-14 04:49:26 +01:00
// **********************************************************************************
// Driver definition for HopeRF RFM69W/RFM69HW/RFM69CW/RFM69HCW, Semtech SX1231/1231H
2013-07-14 04:49:26 +01:00
// **********************************************************************************
2018-04-09 19:19:14 +01:00
// Copyright LowPowerLab LLC 2018, https://www.LowPowerLab.com/contact
// **********************************************************************************
// License
// **********************************************************************************
// This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU General
// Public License as published by the Free Software
// Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will
// be useful, but WITHOUT ANY WARRANTY; without even the
// implied warranty of MERCHANTABILITY or FITNESS FOR A
// PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// Licence can be viewed at
// http://www.gnu.org/licenses/gpl-3.0.txt
//
// Please maintain this license information along with authorship
// and copyright notices in any redistribution of this code
2013-07-14 04:49:26 +01:00
// **********************************************************************************
2013-06-20 22:06:39 +01:00
#ifndef RFM69_h
#define RFM69_h
#include <Arduino.h> // assumes Arduino IDE v1.0 or greater
2018-04-05 21:00:36 +01:00
#include <SPI.h>
//////////////////////////////////////////////////////////////////////
//Platform and digitalPinToInterrupt definitions credit to RadioHead//
//////////////////////////////////////////////////////////////////////
// Select platform automatically, if possible
#ifndef RF69_PLATFORM
#if (MPIDE>=150 && defined(ARDUINO))
// Using ChipKIT Core on Arduino IDE
#define RF69_PLATFORM RF69_PLATFORM_CHIPKIT_CORE
#elif defined(MPIDE)
// Uno32 under old MPIDE, which has been discontinued:
#define RF69_PLATFORM RF69_PLATFORM_UNO32
2020-06-23 16:02:01 +01:00
#elif defined(NRF51)
2018-04-05 21:00:36 +01:00
#define RF69_PLATFORM RF69_PLATFORM_NRF51
2020-06-23 16:02:01 +01:00
#elif defined(NRF52)
2018-04-05 21:00:36 +01:00
#define RF69_PLATFORM RF69_PLATFORM_NRF52
#elif defined(ESP8266)
#define RF69_PLATFORM RF69_PLATFORM_ESP8266
#elif defined(ESP32)
#define RF69_PLATFORM RF69_PLATFORM_ESP32
#elif defined(ARDUINO)
#define RF69_PLATFORM RF69_PLATFORM_ARDUINO
2020-06-23 16:02:01 +01:00
#elif defined(AVR_ATtinyxy7) || defined(AVR_ATtinyxy6) // MegaTinyCore AVR Series-1
#define RF69_PLATFORM RF69_PLATFORM_ARDUINO
2018-04-05 21:00:36 +01:00
#elif defined(__MSP430G2452__) || defined(__MSP430G2553__)
#define RF69_PLATFORM RF69_PLATFORM_MSP430
#elif defined(MCU_STM32F103RE)
#define RF69_PLATFORM RF69_PLATFORM_STM32
#elif defined(STM32F2XX)
#define RF69_PLATFORM RF69_PLATFORM_STM32F2
#elif defined(USE_STDPERIPH_DRIVER)
#define RF69_PLATFORM RF69_PLATFORM_STM32STD
#elif defined(RASPBERRY_PI)
#define RF69_PLATFORM RF69_PLATFORM_RASPI
2020-06-23 16:02:01 +01:00
#elif defined(__unix__) // Linux
2018-04-05 21:00:36 +01:00
#define RF69_PLATFORM RF69_PLATFORM_UNIX
2020-06-23 16:02:01 +01:00
#elif defined(__APPLE__) // OSX
2018-04-05 21:00:36 +01:00
#define RF69_PLATFORM RF69_PLATFORM_UNIX
#else
#error Platform not defined!
#endif
#endif
2019-07-25 15:41:14 +01:00
#if defined(ESP8266) || defined(ESP32)
#define ISR_PREFIX ICACHE_RAM_ATTR
#else
#define ISR_PREFIX
#endif
2018-04-05 21:00:36 +01:00
// digitalPinToInterrupt is not available prior to Arduino 1.5.6 and 1.0.6
// See http://arduino.cc/en/Reference/attachInterrupt
#ifndef NOT_AN_INTERRUPT
#define NOT_AN_INTERRUPT -1
#endif
#ifndef digitalPinToInterrupt
#if (RF69_PLATFORM == RF69_PLATFORM_ARDUINO) && !defined(__arm__)
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
// Arduino Mega, Mega ADK, Mega Pro
// 2->0, 3->1, 21->2, 20->3, 19->4, 18->5
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : ((p) >= 18 && (p) <= 21 ? 23 - (p) : NOT_AN_INTERRUPT)))
#elif defined(__AVR_ATmega1284__) || defined(__AVR_ATmega1284P__)
2018-04-09 19:19:14 +01:00
// Arduino 1284 and 1284P - See Maniacbug and Optiboot
2018-04-05 21:00:36 +01:00
// 10->0, 11->1, 2->2
#define digitalPinToInterrupt(p) ((p) == 10 ? 0 : ((p) == 11 ? 1 : ((p) == 2 ? 2 : NOT_AN_INTERRUPT)))
#elif defined(__AVR_ATmega32U4__)
// Leonardo, Yun, Micro, Pro Micro, Flora, Esplora
// 3->0, 2->1, 0->2, 1->3, 7->4
#define digitalPinToInterrupt(p) ((p) == 0 ? 2 : ((p) == 1 ? 3 : ((p) == 2 ? 1 : ((p) == 3 ? 0 : ((p) == 7 ? 4 : NOT_AN_INTERRUPT)))))
#else
// All other arduino except Due:
// Serial Arduino, Extreme, NG, BT, Uno, Diecimila, Duemilanove, Nano, Menta, Pro, Mini 04, Fio, LilyPad, Ethernet etc
// 2->0, 3->1
#define digitalPinToInterrupt(p) ((p) == 2 ? 0 : ((p) == 3 ? 1 : NOT_AN_INTERRUPT))
#endif
#elif (RF69_PLATFORM == RF69_PLATFORM_UNO32) || (RF69_PLATFORM == RF69_PLATFORM_CHIPKIT_CORE)
// Hmmm, this is correct for Uno32, but what about other boards on ChipKIT Core?
#define digitalPinToInterrupt(p) ((p) == 38 ? 0 : ((p) == 2 ? 1 : ((p) == 7 ? 2 : ((p) == 8 ? 3 : ((p) == 735 ? 4 : NOT_AN_INTERRUPT)))))
#else
// Everything else (including Due and Teensy) interrupt number the same as the interrupt pin number
#define digitalPinToInterrupt(p) (p)
#endif
#elif defined(__SAMD21__) || defined (__SAMD51__) //Arduino.h in most/all cores wrongly "#define digitalPinToInterrupt(P) (P)" after calling variant.h
#define digitalPinToInterrupt(P) (g_APinDescription[P].ulExtInt)
2018-04-05 21:00:36 +01:00
#endif
// On some platforms, attachInterrupt() takes a pin number, not an interrupt number
2020-06-09 14:51:25 +01:00
#if (defined(TEENSYDUINO))||((RF69_PLATFORM == RF69_PLATFORM_ARDUINO) && defined (__arm__) && (defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_SAM_DUE)))
2018-04-05 21:00:36 +01:00
#define RF69_ATTACHINTERRUPT_TAKES_PIN_NUMBER
#endif
////////////////////////////////////////////////////
2013-06-20 22:06:39 +01:00
2021-01-26 19:24:22 +00:00
#define RF69_SPI_CS SS // SS is the SPI slave select pin, for instance D10 on ATmega328
// INT0 on AVRs should be connected to RFM69's DIO0 (ex on ATmega328 it's D2, on ATmega644/1284 it's D2)
#if defined(__AVR_ATmega168__) || defined(__AVR_ATmega328P__) || defined(__AVR_ATmega88) || defined(__AVR_ATmega8__) || defined(__AVR_ATmega88__)
#define RF69_IRQ_PIN 2
#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)
#define RF69_IRQ_PIN 2
#elif defined(__AVR_ATmega32U4__)
2018-04-05 21:00:36 +01:00
#define RF69_IRQ_PIN 7
#elif defined(__STM32F1__) || defined(STM32F1)
2018-01-26 00:35:46 +00:00
#define RF69_IRQ_PIN PA3
#define RF69_ATTACHINTERRUPT_TAKES_PIN_NUMBER
2018-05-25 22:33:26 +01:00
#elif defined(MOTEINO_M0)
#define RF69_IRQ_PIN 9
2020-03-31 16:53:58 +01:00
#elif defined(__SAMD51__)
#define RF69_IRQ_PIN 18
2018-04-05 21:00:36 +01:00
#elif defined(ARDUINO_SAMD_ZERO) //includes Feather SAMD
#define RF69_IRQ_PIN 3
2020-01-21 17:58:55 +00:00
#elif defined(ESP8266)
#define RF69_IRQ_PIN 4
2020-03-31 16:51:11 +01:00
#elif defined(ESP32)
#define RF69_IRQ_PIN 2
2018-04-05 21:00:36 +01:00
#else
2015-05-19 19:13:06 +01:00
#define RF69_IRQ_PIN 2
#endif
2018-04-05 21:00:36 +01:00
#define RF69_MAX_DATA_LEN 61 // to take advantage of the built in AES/CRC we want to limit the frame size to the internal FIFO size (66 bytes - 3 bytes overhead - 2 bytes crc)
#define CSMA_LIMIT -90 // upper RX signal sensitivity threshold in dBm for carrier sense access
#define RF69_MODE_SLEEP 0 // XTAL OFF
#define RF69_MODE_STANDBY 1 // XTAL ON
#define RF69_MODE_SYNTH 2 // PLL ON
#define RF69_MODE_RX 3 // RX MODE
#define RF69_MODE_TX 4 // TX MODE
2013-06-20 22:06:39 +01:00
// available frequency bands
#define RF69_315MHZ 31 // non trivial values to avoid misconfiguration
#define RF69_433MHZ 43
#define RF69_868MHZ 86
#define RF69_915MHZ 91
2013-06-20 22:06:39 +01:00
#define null 0
#define COURSE_TEMP_COEF -90 // puts the temperature reading in the ballpark, user can fine tune the returned value
2019-06-07 00:22:12 +01:00
#define RF69_BROADCAST_ADDR 0
#define RF69_CSMA_LIMIT_MS 1000
#define RF69_TX_LIMIT_MS 1000
#define RF69_FSTEP 61.03515625 // == FXOSC / 2^19 = 32MHz / 2^19 (p13 in datasheet)
2013-06-20 22:06:39 +01:00
// TWS: define CTLbyte bits
#define RFM69_CTL_SENDACK 0x80
#define RFM69_CTL_REQACK 0x40
2019-06-05 16:30:45 +01:00
#define RFM69_ACK_TIMEOUT 30 // 30ms roundtrip req for 61byte packets
//Native hardware ListenMode is experimental
//It was determined to be buggy and unreliable, see https://lowpowerlab.com/forum/low-power-techniques/ultra-low-power-listening-mode-for-battery-nodes/msg20261/#msg20261
//uncomment to try ListenMode, adds ~1K to compiled size
//FYI - 10bit addressing is not supported in ListenMode
//#define RF69_LISTENMODE_ENABLE
2018-04-05 21:20:12 +01:00
#if defined(RF69_LISTENMODE_ENABLE)
// By default, receive for 256uS in listen mode and idle for ~1s
#define DEFAULT_LISTEN_RX_US 256
#define DEFAULT_LISTEN_IDLE_US 1000000
#endif
2013-06-20 22:06:39 +01:00
class RFM69 {
public:
2019-06-05 16:38:14 +01:00
static uint8_t DATA[RF69_MAX_DATA_LEN+1]; // RX/TX payload buffer, including end of string NULL char
2019-01-16 00:29:39 +00:00
static uint8_t DATALEN;
static uint16_t SENDERID;
static uint16_t TARGETID; // should match _address
2019-01-16 00:29:39 +00:00
static uint8_t PAYLOADLEN;
static uint8_t ACK_REQUESTED;
static uint8_t ACK_RECEIVED; // should be polled immediately after sending a packet with ACK request
static int16_t RSSI; // most accurate RSSI during reception (closest to the reception). RSSI of last packet.
static uint8_t _mode; // should be protected?
2021-05-03 22:24:50 +01:00
RFM69(uint8_t slaveSelectPin, uint8_t interruptPin, bool isRFM69HW, uint8_t interruptNum __attribute__((unused))) //interruptNum is now deprecated
2018-04-05 21:00:36 +01:00
: RFM69(slaveSelectPin, interruptPin, isRFM69HW){};
2020-06-02 12:27:30 +01:00
RFM69(uint8_t slaveSelectPin=RF69_SPI_CS, uint8_t interruptPin=RF69_IRQ_PIN, bool isRFM69HW=false, SPIClass *spi=nullptr);
2013-06-20 22:06:39 +01:00
bool initialize(uint8_t freqBand, uint16_t ID, uint8_t networkID=1);
void setAddress(uint16_t addr);
void setNetwork(uint8_t networkID);
2020-08-29 12:02:17 +01:00
virtual bool canSend();
virtual void send(uint16_t toAddress, const void* buffer, uint8_t bufferSize, bool requestACK=false);
2019-06-05 16:30:45 +01:00
virtual bool sendWithRetry(uint16_t toAddress, const void* buffer, uint8_t bufferSize, uint8_t retries=2, uint8_t retryWaitTime=RFM69_ACK_TIMEOUT);
virtual bool receiveDone();
bool ACKReceived(uint16_t fromNodeID);
bool ACKRequested();
virtual void sendACK(const void* buffer = "", uint8_t bufferSize=0);
uint32_t getFrequency();
void setFrequency(uint32_t freqHz);
2013-06-20 22:06:39 +01:00
void encrypt(const char* key);
void setCS(uint8_t newSPISlaveSelect);
bool setIrq(uint8_t newIRQPin);
2018-08-24 00:29:26 +01:00
int16_t readRSSI(bool forceTrigger=false); // *current* signal strength indicator; e.g. < -90dBm says the frequency channel is free + ready to transmit
2020-01-21 17:52:45 +00:00
void spyMode(bool onOff=true);
//void promiscuous(bool onOff=true); //replaced with spyMode()
virtual void setHighPower(bool onOFF=true); // has to be called after initialize() for RFM69HW
virtual void setPowerLevel(uint8_t level); // reduce/increase transmit power level
2020-06-02 14:54:44 +01:00
uint8_t getPowerLevel(); // get powerLevel
2013-06-20 22:06:39 +01:00
void sleep();
uint8_t readTemperature(uint8_t calFactor=0); // get CMOS temperature (8bit)
void rcCalibration(); // calibrate the internal RC oscillator for use in wide temperature variations - see datasheet section [4.3.5. RC Timer Accuracy]
2013-06-20 22:06:39 +01:00
// allow hacking registers by making these public
uint8_t readReg(uint8_t addr);
void writeReg(uint8_t addr, uint8_t val);
2013-06-20 22:06:39 +01:00
void readAllRegs();
2017-07-24 18:41:41 +01:00
void readAllRegsCompact();
2020-05-18 18:54:21 +01:00
// ListenMode sleep/timer
void listenModeSleep(uint16_t millisInterval);
void endListenModeSleep();
2020-05-18 18:54:21 +01:00
2013-06-20 22:06:39 +01:00
protected:
static void isr0();
void interruptHandler();
2021-05-03 22:24:50 +01:00
virtual void interruptHook(uint8_t CTLbyte __attribute__((unused))) {};
static volatile bool _haveData;
virtual void sendFrame(uint16_t toAddress, const void* buffer, uint8_t size, bool requestACK=false, bool sendACK=false);
2013-06-20 22:06:39 +01:00
2020-05-18 18:54:21 +01:00
// for ListenMode sleep/timer
static void delayIrq();
uint8_t _slaveSelectPin;
uint8_t _interruptPin;
2021-05-03 23:11:00 +01:00
uint8_t _interruptNum;
uint16_t _address;
2020-01-21 17:52:45 +00:00
bool _spyMode;
uint8_t _powerLevel;
2013-06-20 22:06:39 +01:00
bool _isRFM69HW;
2020-06-02 12:27:30 +01:00
SPIClass *_spi;
2016-02-29 19:36:24 +00:00
#if defined (SPCR) && defined (SPSR)
uint8_t _SPCR;
uint8_t _SPSR;
2016-02-29 19:36:24 +00:00
#endif
2018-12-14 00:39:20 +00:00
#ifdef SPI_HAS_TRANSACTION
SPISettings _settings;
#endif
2013-06-20 22:06:39 +01:00
virtual void receiveBegin();
virtual void setMode(uint8_t mode);
virtual void setHighPowerRegs(bool onOff);
virtual void select();
virtual void unselect();
2013-06-20 22:06:39 +01:00
2018-04-05 21:20:12 +01:00
#if defined(RF69_LISTENMODE_ENABLE)
2019-08-02 21:38:00 +01:00
static RFM69* selfPointer;
2018-04-05 21:20:12 +01:00
//=============================================================================
// ListenMode specific declarations
//=============================================================================
public:
// When we receive a packet in listen mode, this is the time left in the sender's burst.
// You need to wait at least this long before trying to reply.
static volatile uint16_t RF69_LISTEN_BURST_REMAINING_MS;
void listenModeStart(void);
void listenModeEnd(void);
void listenModeHighSpeed(bool highSpeed) { _isHighSpeed = highSpeed; }
// rx and idle duration in microseconds
bool listenModeSetDurations(uint32_t& rxDuration, uint32_t& idleDuration);
// The values passed to listenModeSetDurations() may be slightly different to accomodate
// what is allowed by the radio. This function returns the actual values used.
void listenModeGetDurations(uint32_t& rxDuration, uint32_t& idleDuration);
// This repeatedly sends the message to the target node for the duration
// of an entire listen cycle. The amount of time remaining in the burst
// is transmitted to the receiver, and it is expected that the receiver
// wait for the burst to end before attempting a reply.
// See RF69_LISTEN_BURST_REMAINING_MS above.
2019-05-01 16:35:34 +01:00
void listenModeSendBurst(uint8_t targetNode, const void* buffer, uint8_t size);
2018-04-05 21:20:12 +01:00
protected:
void listenModeInterruptHandler(void);
void listenModeApplyHighSpeedSettings();
void listenModeReset(); //resets variables used on the receiving end
bool reinitRadio(void);
static void listenModeIrq();
bool _isHighSpeed;
bool _haveEncryptKey;
char _encryptKey[16];
// Save these so we can reinitialize the radio after sending a burst
// or exiting listen mode.
uint8_t _freqBand;
uint8_t _networkID;
uint8_t _rxListenCoef;
uint8_t _rxListenResolution;
uint8_t _idleListenCoef;
uint8_t _idleListenResolution;
uint32_t _listenCycleDurationUs;
2016-03-29 21:17:53 +01:00
#endif
2018-04-05 21:20:12 +01:00
};
2020-06-02 07:12:21 +01:00
#endif