From 64eb36ba852be43f56bb555c1d7d5a0ac52e0873 Mon Sep 17 00:00:00 2001 From: Felix Rusu Date: Mon, 18 May 2020 13:54:21 -0400 Subject: [PATCH] add listenModeSleep() + example --- Examples/ListenModeSleep/ListenModeSleep.ino | 142 +++++++++++++++++++ RFM69.cpp | 68 +++++++++ RFM69.h | 7 + 3 files changed, 217 insertions(+) create mode 100644 Examples/ListenModeSleep/ListenModeSleep.ino diff --git a/Examples/ListenModeSleep/ListenModeSleep.ino b/Examples/ListenModeSleep/ListenModeSleep.ino new file mode 100644 index 0000000..849cb6b --- /dev/null +++ b/Examples/ListenModeSleep/ListenModeSleep.ino @@ -0,0 +1,142 @@ +// ********************************************************************************** +// Sample RFM69 sender/node sketch with radio listen mode sleep +// Saves additional 2-3uA over WDT sleep +// ********************************************************************************** +// Copyright Felix Rusu 2020, http://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 +// ********************************************************************************** +#include +#include +#include +#include //easy string manipulator: http://arduiniana.org/libraries/pstring/ +//********************************************************************************************* +//************ IMPORTANT SETTINGS - YOU MUST CHANGE/CONFIGURE TO FIT YOUR HARDWARE ************ +//********************************************************************************************* +#define NODEID 123 //must be unique for each node on same network (range up to 254, 255 is used for broadcast) +#define NETWORKID 100 //the same on all nodes that talk to each other (range up to 255) +#define GATEWAYID 1 +#define FREQUENCY RF69_915MHZ //match the RFM69 version! Others: RF69_433MHZ, RF69_868MHZ +//#define FREQUENCY_EXACT 916000000 +#define IS_RFM69HW //uncomment only for RFM69HW! Leave out if you have RFM69W! +#define ENCRYPTKEY "sampleEncryptKey" //exactly the same 16 characters/bytes on all nodes! +#define ENABLE_ATC //comment out this line to disable AUTO TRANSMISSION CONTROL +#define ATC_RSSI -90 +// ********************************************************************************** +//to avoid the buggy listen mode high resolution timer TRANSMITPERIOD should always be > 262ms +#define TRANSMITPERIOD 3000 //sleep time in ms +//#define WDTSLEEP //uncomment to sleep with WDT instead (compare sleep currents!) +// ********************************************************************************** +#define SERIAL_EN //comment this out when deploying to an installed SM to save a few KB of sketch size +#define SERIAL_BAUD 115200 +#ifdef SERIAL_EN + #define DEBUG(input) Serial.print(input) + #define DEBUGln(input) Serial.println(input) + #define DEBUGFlush() Serial.flush() +#else + #define DEBUG(input) + #define DEBUGln(input) + #define DEBUGFlush() +#endif +// ********************************************************************************** +#ifdef ENABLE_ATC + RFM69_ATC radio; +#else + RFM69 radio; +#endif + +char buff[61]; //61 max payload for radio +PString Pbuff(buff, sizeof(buff)); +// ********************************************************************************** + +void setup() { + pinMode(LED_BUILTIN, OUTPUT); +#ifdef SERIAL_EN + Serial.begin(SERIAL_BAUD); +#endif + + if (!radio.initialize(FREQUENCY,NODEID,NETWORKID)) + DEBUG("radio.init() FAIL"); + else + DEBUG("radio.init() SUCCESS"); + +#ifdef IS_RFM69HW + radio.setHighPower(); //uncomment only for RFM69HW! +#endif + +#ifdef FREQUENCY_EXACT + radio.setFrequency(FREQUENCY_EXACT); //set frequency to some custom frequency +#endif + +#ifdef ENCRYPTKEY + radio.encrypt(ENCRYPTKEY); +#endif + +#ifdef ENABLE_ATC + radio.enableAutoPower(ATC_RSSI); +#endif + + Pbuff = F("Transmitting at "); + Pbuff.print(FREQUENCY==RF69_433MHZ ? 433 : FREQUENCY==RF69_868MHZ ? 868 : 915); + Pbuff.print(F("Mhz...")); + DEBUGln(buff); + +#ifdef ENABLE_ATC + DEBUGln("RFM69_ATC Enabled (Auto Transmission Control)\n"); +#endif +} + +uint32_t packetCounter=0; +void loop() { + Pbuff = F("PACKET #"); + Pbuff.print(packetCounter++); + DEBUGln(buff); + digitalWrite(LED_BUILTIN, HIGH); + radio.sendWithRetry(GATEWAYID, buff, Pbuff.length()); + digitalWrite(LED_BUILTIN, LOW); + DEBUGFlush(); + +#ifdef WDTSLEEP + radio.sleep(); + LowPower.longPowerDown(TRANSMITPERIOD); +#else + if (TRANSMITPERIOD%262 && TRANSMITPERIOD > 262*2) + { + DEBUG("Sleeping "); DEBUGln(TRANSMITPERIOD-TRANSMITPERIOD%262-262); DEBUGFlush(); + radio.listenModeSleep(TRANSMITPERIOD-TRANSMITPERIOD%262-262); + DEBUG("Sleeping "); DEBUGln(TRANSMITPERIOD%262 + 262); DEBUGFlush(); + radio.listenModeSleep(TRANSMITPERIOD%262 + 262); + } + else { + DEBUG("Sleeping "); DEBUGln(TRANSMITPERIOD); DEBUGFlush(); + radio.listenModeSleep(TRANSMITPERIOD); + } + + //wakeup (must reinit) + radio.RFM69::initialize(FREQUENCY,NODEID,NETWORKID); //call base init! + #ifdef ENCRYPTKEY + radio.encrypt(ENCRYPTKEY); + #endif + #ifdef FREQUENCY_EXACT + radio.setFrequency(FREQUENCY_EXACT); + #endif +#endif +} diff --git a/RFM69.cpp b/RFM69.cpp index c0bcc7b..bd3b3a2 100644 --- a/RFM69.cpp +++ b/RFM69.cpp @@ -26,6 +26,7 @@ #include "RFM69.h" #include "RFM69registers.h" #include +#include //http://github.com/LowPowerLab/LowPower uint8_t RFM69::DATA[RF69_MAX_DATA_LEN+1]; uint8_t RFM69::_mode; // current transceiver state @@ -864,6 +865,73 @@ void RFM69::rcCalibration() while ((readReg(REG_OSC1) & RF_OSC1_RCCAL_DONE) == 0x00); } +// ListenMode sleep/timer +void RFM69::listenModeSleep(uint16_t millisInterval) +{ + setMode( RF69_MODE_STANDBY ); + while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady + + detachInterrupt( _interruptNum ); + //attachInterrupt( _interruptNum, delayIrq, RISING); + writeReg( REG_DIOMAPPING1, RF_DIOMAPPING1_DIO0_11 ); + writeReg( REG_BITRATEMSB, RF_BITRATEMSB_200000); + writeReg( REG_BITRATELSB, RF_BITRATELSB_200000); + writeReg( REG_FDEVMSB, RF_FDEVMSB_100000 ); + writeReg( REG_FDEVLSB, RF_FDEVLSB_100000 ); + writeReg( REG_RXBW, RF_RXBW_DCCFREQ_000 | RF_RXBW_MANT_16 | RF_RXBW_EXP_0 ); + + uint8_t idleResol; + uint32_t divisor; + uint32_t microInterval = millisInterval * 1000L; + + if( microInterval > 255 * 4100L ) { + idleResol = RF_LISTEN1_RESOL_IDLE_262000; + divisor = 262000; + } + else if( microInterval > 255 * 64L ) { + idleResol = RF_LISTEN1_RESOL_IDLE_4100; + divisor = 4100; + } + else { + idleResol = RF_LISTEN1_RESOL_IDLE_64; + divisor = 64; + } + + writeReg( REG_LISTEN1, RF_LISTEN1_RESOL_RX_64 | idleResol | RF_LISTEN1_CRITERIA_RSSI | RF_LISTEN1_END_10 ); + writeReg( REG_LISTEN2, (microInterval + (divisor >> 1 ) ) / divisor ); + writeReg( REG_LISTEN3, 4 ); + writeReg( REG_RSSITHRESH, 255 ); + writeReg( REG_RXTIMEOUT2, 1 ); + writeReg( REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_STANDBY ); + writeReg( REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_STANDBY | RF_OPMODE_LISTEN_ON ); + + attachInterrupt( _interruptNum, delayIrq, RISING); + + LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF ); + LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF ); + LowPower.powerDown( SLEEP_FOREVER, ADC_OFF, BOD_OFF ); + + endListenModeSleep(); +} + +//============================================================================= +// endListenModeSleep() - called by listenModeSleep() +//============================================================================= +void RFM69::endListenModeSleep() +{ + detachInterrupt( _interruptNum ); + writeReg( REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_LISTENABORT | RF_OPMODE_STANDBY ); + writeReg( REG_OPMODE, RF_OPMODE_SEQUENCER_ON | RF_OPMODE_STANDBY ); + writeReg( REG_RXTIMEOUT2, 0 ); + setMode( RF69_MODE_STANDBY ); + while ((readReg(REG_IRQFLAGS1) & RF_IRQFLAGS1_MODEREADY) == 0x00); // wait for ModeReady +} + +//============================================================================= +// delayIRQ() - called by listenModeSleep() +//============================================================================= +void RFM69::delayIrq() { return; } + //============================================================================= // ListenMode specific functions //============================================================================= diff --git a/RFM69.h b/RFM69.h index 7105111..885f25a 100644 --- a/RFM69.h +++ b/RFM69.h @@ -228,6 +228,9 @@ class RFM69 { void readAllRegs(); void readAllRegsCompact(); + // ListenMode sleep/timer + void listenModeSleep(uint16_t millisInterval); + protected: static void isr0(); void interruptHandler(); @@ -235,6 +238,10 @@ class RFM69 { static volatile bool _haveData; virtual void sendFrame(uint16_t toAddress, const void* buffer, uint8_t size, bool requestACK=false, bool sendACK=false); + // for ListenMode sleep/timer + static void delayIrq(); + void endListenModeSleep(); + uint8_t _slaveSelectPin; uint8_t _interruptPin; uint8_t _interruptNum;