Initial implementation of deep sleep

This commit is contained in:
Mike C 2015-08-04 22:59:31 -04:00
parent b0f29a2ae7
commit 7a69f68eb4
6 changed files with 227 additions and 5 deletions

View file

@ -10,6 +10,7 @@ The following external libraries are included and may not be licensed under the
* Adafruit NeoPixel Library
* SparkFun Lipo Fuel Gauge Library / Example code
* "Sleepy" class from https://github.com/jcw/jeelib/
Non-Code License
=

View file

@ -1,6 +1,5 @@
* Implement flashing lights if battery level is too low (use fuelgauge.isAlerting() and setAlertThreshold())
* Need to implement "always on toggle"
* Need to implement PIR distance sensor to verify door of safe closed
* Need low power mode implementation(s)
* Need to add support for Adafruit Trinket Pro
* Add note about adjusting values for distance, use sleep, alert threshold (if using fuel gauge)

View file

@ -117,12 +117,44 @@ boolean MAX1704::isSleeping(){
}
void MAX1704::sleep(){
byte msb = 0;
byte lsb = 0;
// Get the current config so we don't wipe any previous settings
readFrom(MAX1704_CONFIG,msb,lsb);
// Set SLEEP config bit to 1 to enable
lsb |= (1<<7);
// Update config
Wire.beginTransmission(MAX1704_ADDR);
Wire.write(MAX1704_CONFIG);
Wire.write(msb);
Wire.write(lsb);
Wire.endTransmission();
// This delay is here to ensure it's fully asleep before moving on
delay(150);
}
void MAX1704::awake(){
byte msb = 0;
byte lsb = 0;
// Get the current config so we don't wipe any previous settings
readFrom(MAX1704_CONFIG,msb,lsb);
// Set SLEEP config bit to 0 to disable
lsb &= ~(1<<7);
// Update config
Wire.beginTransmission(MAX1704_ADDR);
Wire.write(MAX1704_CONFIG);
Wire.write(msb);
Wire.write(lsb);
Wire.endTransmission();
// This delay is here to ensure it's fully awake before moving on
delay(150);
}
void MAX1704::performCommand(byte address, int value){

111
src/Sleepy.cpp Normal file
View file

@ -0,0 +1,111 @@
/// @file
/// Sleepy library definitions.
// 2009-02-13 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
#include "Sleepy.h"
#include <avr/sleep.h>
#include <util/atomic.h>
// #define DEBUG_DHT 1 // add code to send info over the serial port of non-zero
// ATtiny84 has BODS and BODSE for ATtiny84, revision B, and newer, even though
// the iotnx4.h header doesn't list it, so we *can* disable brown-out detection!
// See the ATtiny24/44/84 datasheet reference, section 7.2.1, page 34.
#if (defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny44__)) && \
!defined(BODSE) && !defined(BODS)
#define BODSE 2
#define BODS 7
#endif
// flag bits sent to the receiver
#define MODE_CHANGE 0x80 // a pin mode was changed
#define DIG_CHANGE 0x40 // a digital output was changed
#define PWM_CHANGE 0x30 // an analog (pwm) value was changed on port 2..3
#define ANA_MASK 0x0F // an analog read was requested on port 1..4
/// @fn static void Sleepy::powerDown ();
/// Take the ATmega into the deepest possible power down state. Getting out of
/// this state requires setting up the watchdog beforehand, or making sure that
/// suitable interrupts will occur once powered down.
/// Disables the Brown Out Detector (BOD), the A/D converter (ADC), and other
/// peripheral functions such as TWI, SPI, and UART before sleeping, and
/// restores their previous state when back up.
// ISR(WDT_vect) { Sleepy::watchdogEvent(); }
static volatile byte watchdogCounter;
void Sleepy::watchdogInterrupts (char mode) {
// correct for the fact that WDP3 is *not* in bit position 3!
if (mode & bit(3))
mode ^= bit(3) | bit(WDP3);
// pre-calculate the WDTCSR value, can't do it inside the timed sequence
// we only generate interrupts, no reset
byte wdtcsr = mode >= 0 ? bit(WDIE) | mode : 0;
MCUSR &= ~(1<<WDRF);
ATOMIC_BLOCK(ATOMIC_FORCEON) {
#ifndef WDTCSR
#define WDTCSR WDTCR
#endif
WDTCSR |= (1<<WDCE) | (1<<WDE); // timed sequence
WDTCSR = wdtcsr;
}
}
/// @see http://www.nongnu.org/avr-libc/user-manual/group__avr__sleep.html
void Sleepy::powerDown () {
byte adcsraSave = ADCSRA;
ADCSRA &= ~ bit(ADEN); // disable the ADC
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
ATOMIC_BLOCK(ATOMIC_FORCEON) {
sleep_enable();
// sleep_bod_disable(); // can't use this - not in my avr-libc version!
#ifdef BODSE
MCUCR = MCUCR | bit(BODSE) | bit(BODS); // timed sequence
MCUCR = (MCUCR & ~ bit(BODSE)) | bit(BODS);
#endif
}
sleep_cpu();
sleep_disable();
// re-enable what we disabled
ADCSRA = adcsraSave;
}
byte Sleepy::loseSomeTime (word msecs) {
byte ok = 1;
word msleft = msecs;
// only slow down for periods longer than the watchdog granularity
while (msleft >= 16) {
char wdp = 0; // wdp 0..9 corresponds to roughly 16..8192 ms
// calc wdp as log2(msleft/16), i.e. loop & inc while next value is ok
for (word m = msleft; m >= 32; m >>= 1)
if (++wdp >= 9)
break;
watchdogCounter = 0;
watchdogInterrupts(wdp);
powerDown();
watchdogInterrupts(-1); // off
// when interrupted, our best guess is that half the time has passed
word halfms = 8 << wdp;
msleft -= halfms;
if (watchdogCounter == 0) {
ok = 0; // lost some time, but got interrupted
break;
}
msleft -= halfms;
}
// adjust the milli ticks, since we will have missed several
#if defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny85__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny45__)
extern volatile unsigned long millis_timer_millis;
millis_timer_millis += msecs - msleft;
#else
extern volatile unsigned long timer0_millis;
timer0_millis += msecs - msleft;
#endif
return ok; // true if we lost approx the time planned
}
void Sleepy::watchdogEvent() {
++watchdogCounter;
}

61
src/Sleepy.h Normal file
View file

@ -0,0 +1,61 @@
// 2009-02-13 <jc@wippler.nl> http://opensource.org/licenses/mit-license.php
#ifndef Sleepy_h
#define Sleepy_h
/// @file
/// Ports library definitions.
#if ARDUINO >= 100
#include <Arduino.h> // Arduino 1.0
#else
#include <WProgram.h> // Arduino 0022
#endif
#include <stdint.h>
#include <avr/pgmspace.h>
//#include <util/delay.h>
// tweak this to switch ATtiny84 etc to new Arduino 1.0+ conventions
// see http://arduino.cc/forum/index.php/topic,51984.msg371307.html#msg371307
// and http://forum.jeelabs.net/node/1567
#if ARDUINO >= 100
#define WRITE_RESULT size_t
#else
#define WRITE_RESULT void
#endif
/// Low-power utility code using the Watchdog Timer (WDT). Requires a WDT
/// interrupt handler, e.g. EMPTY_INTERRUPT(WDT_vect);
class Sleepy {
public:
/// start the watchdog timer (or disable it if mode < 0)
/// @param mode Enable watchdog trigger after "16 << mode" milliseconds
/// (mode 0..9), or disable it (mode < 0).
/// @note If you use this function, you MUST included a definition of a WDT
/// interrupt handler in your code. The simplest is to include this line:
///
/// ISR(WDT_vect) { Sleepy::watchdogEvent(); }
///
/// This will get called when the watchdog fires.
static void watchdogInterrupts (char mode);
/// enter low-power mode, wake up with watchdog, INT0/1, or pin-change
static void powerDown ();
/// Spend some time in low-power mode, the timing is only approximate.
/// @param msecs Number of milliseconds to sleep, in range 0..65535.
/// @returns 1 if all went normally, or 0 if some other interrupt occurred
/// @note If you use this function, you MUST included a definition of a WDT
/// interrupt handler in your code. The simplest is to include this line:
///
/// ISR(WDT_vect) { Sleepy::watchdogEvent(); }
///
/// This will get called when the watchdog fires.
static byte loseSomeTime (word msecs);
/// This must be called from your watchdog interrupt code.
static void watchdogEvent();
};
#endif

View file

@ -2,6 +2,7 @@
#include "Arduino.h"
#include "Wire.h"
#include "MAX1704.h"
#include "Sleepy.h"
#include "Adafruit_NeoPixel.h"
#ifdef __AVR__
#include <avr/power.h>
@ -9,6 +10,8 @@
// Various defines / pins / etc
#define DEBUG true
#define USE_SLEEP false
#define SLEEP_INTERVAL 1000 // miliseconds
#define BUTTON_CAP_TOGGLE 9
#define POT_BRIGHT_PIN A0
#define POT_COLOR_PIN A1
@ -30,6 +33,7 @@ bool max_found = false;
// Various function definitions
void hsvToRgb(int h, double s, double v); // See end of file for implementation
ISR(WDT_vect) { Sleepy::watchdogEvent(); } // Setup the watchdog -- For sleepy
void setup() {
Serial.begin(9600);
@ -37,7 +41,7 @@ void setup() {
// Setup serial and wait for console
if (DEBUG) {
while(!Serial);
//while(!Serial);
Serial.println("Starting up...");
}
@ -124,6 +128,20 @@ void loop() {
Serial.println("----------");
delay(250);
}
// Sleep mode enabled
if (USE_SLEEP && !always_on) { // Don't enable sleep if always on toggle is set
if (DEBUG) {
Serial.println("SLEEPING!");
}
fuelGauge.sleep();
neopix.setBrightness(0);
neopix.show();
delay(250); // Be sure that everything is settled before going to sleep
Sleepy::loseSomeTime(SLEEP_INTERVAL);
fuelGauge.awake();
neopix.setBrightness(brightness);
}
}
// Convert degrees on a circle to color values