Go Down

Topic: Low power consumption (0.1 uA) but wake up on keypad press (Read 7 times) previous topic - next topic

Nick Gammon

I've been wondering how things like remote controls, gate combination locks, calculators, etc. work. They seem to use low amounts of power, but wake up when any key is pressed.

The sketch below reproduces that behaviour. With a 16-key keypad connected (4 columns, 4 rows) it uses only 100 nA of power when asleep. However by using pin change interrupts, any keypress will wake it. It then uses the Keypad library to find which key that was, and flash LED 13 x times (where x is the key).

This low power consumption was measured on a "bare bones" board, no voltage regulator, USB interface, or anything like that.

Code: [Select]

// Wake from deep sleep with a keypress demonstration

// Author: Nick Gammon
// Date: 18th November 2012

#include <Keypad2.h>
#include <avr/sleep.h>

const byte ROWS = 4;
const byte COLS = 4;

char keys[ROWS][COLS] =
  {
    {'1', '2', '3', 'A'},
    {'4', '5', '6', 'B'},
    {'7', '8', '9', 'C'},
    {'*', '0', '#', 'D'},
  };

byte rowPins[ROWS] = {6, 7, 8, 9}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {2, 3, 4, 5}; //connect to the column pinouts of the keypad
 
// number of items in an array
#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))

const byte ledPin = 13;

  // Create the Keypad
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

/*

Pin change interrupts.

Pin                  Mask / Flag / Enable

D0   PCINT16 (PCMSK2 / PCIF2 / PCIE2)
D1   PCINT17 (PCMSK2 / PCIF2 / PCIE2)
D2   PCINT18 (PCMSK2 / PCIF2 / PCIE2)
D3   PCINT19 (PCMSK2 / PCIF2 / PCIE2)
D4   PCINT20 (PCMSK2 / PCIF2 / PCIE2)
D5   PCINT21 (PCMSK2 / PCIF2 / PCIE2)
D6   PCINT22 (PCMSK2 / PCIF2 / PCIE2)
D7   PCINT23 (PCMSK2 / PCIF2 / PCIE2)
D8   PCINT0 (PCMSK0 / PCIF0 / PCIE0)
D9   PCINT1 (PCMSK0 / PCIF0 / PCIE0)
D10   PCINT2 (PCMSK0 / PCIF0 / PCIE0)
D11   PCINT3 (PCMSK0 / PCIF0 / PCIE0)
D12   PCINT4 (PCMSK0 / PCIF0 / PCIE0)
D13   PCINT5 (PCMSK0 / PCIF0 / PCIE0)
A0   PCINT8 (PCMSK1 / PCIF1 / PCIE1)
A1   PCINT9 (PCMSK1 / PCIF1 / PCIE1)
A2   PCINT10 (PCMSK1 / PCIF1 / PCIE1)
A3   PCINT11 (PCMSK1 / PCIF1 / PCIE1)
A4   PCINT12 (PCMSK1 / PCIF1 / PCIE1)
A5   PCINT13 (PCMSK1 / PCIF1 / PCIE1)

*/

// we don't want to do anything except wake
EMPTY_INTERRUPT (PCINT0_vect)
EMPTY_INTERRUPT (PCINT1_vect)
EMPTY_INTERRUPT (PCINT2_vect)

void setup ()
  {
  pinMode (ledPin, OUTPUT);
 
  // pin change interrupt masks (see above list)
  PCMSK2 |= _BV (PCINT22);   // pin 6
  PCMSK2 |= _BV (PCINT23);   // pin 7
  PCMSK0 |= _BV (PCINT0);    // pin 8
  PCMSK0 |= _BV (PCINT1);    // pin 9

  }  // end of setup

// set pins as keypad library expects them
// or call: kpd.initializePins ();
//    however in the library I have that is a private method

void reconfigurePins ()
  {
  byte i;
 
  // go back to all pins as per the keypad library
 
  for (i = 0; i < NUMITEMS (colPins); i++)
    {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], HIGH);
    }  // end of for each column

  for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    pinMode (rowPins [i], INPUT);
    digitalWrite (rowPins [i], HIGH);
    }   // end of for each row

  }  // end of reconfigurePins

void goToSleep ()
  {
  byte i;
   
  // set up to detect a keypress
  for (i = 0; i < NUMITEMS (colPins); i++)
    {
    pinMode (colPins [i], OUTPUT);
    digitalWrite (colPins [i], LOW);   // columns low
    }  // end of for each column

  for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    pinMode (rowPins [i], INPUT);
    digitalWrite (rowPins [i], HIGH);  // rows high (pull-up)
    }  // end of for each row
   
   // now check no pins pressed (otherwise we wake on a key release)
   for (i = 0; i < NUMITEMS (rowPins); i++)
    {
    if (digitalRead (rowPins [i]) == LOW)
       {
       reconfigurePins ();
       return;
       } // end of a pin pressed
    }  // end of for each row
 
  // overcome any debounce delays built into the keypad library
  delay (50);
 
  // at this point, pressing a key should connect the high in the row to the
  // to the low in the column and trigger a pin change
 
  set_sleep_mode (SLEEP_MODE_PWR_DOWN); 
  sleep_enable();

  byte old_ADCSRA = ADCSRA;
  // disable ADC to save power
  ADCSRA = 0; 

  PRR = 0xFF;  // turn off various modules
   
  PCIFR  |= _BV (PCIF0) | _BV (PCIF1) | _BV (PCIF2);   // clear any outstanding interrupts
  PCICR  |= _BV (PCIE0) | _BV (PCIE1) | _BV (PCIE2);   // enable pin change interrupts
   
  // turn off brown-out enable in software
  MCUCR = _BV (BODS) | _BV (BODSE);
  MCUCR = _BV (BODS);
  sleep_cpu (); 

  // cancel sleep as a precaution
  sleep_disable();
  PCICR = 0;  // cancel pin change interrupts
  PRR = 0;    // enable modules again
  ADCSRA = old_ADCSRA; // re-enable ADC conversion
 
  // put keypad pins back how they are expected to be
  reconfigurePins ();
   
  }  // end of goToSleep
 
void loop ()
  {
   
   byte key =  kpd.getKey();
   if (!key)
     {
     // no key pressed? go to sleep
     goToSleep ();
     return;
     }

  // confirmation we woke - flash LED number of times
  // for the appropriate pin (eg. pin 1: one time)
  for (byte i = 0; i < (key - '0'); i++)
    {
    digitalWrite (ledPin, HIGH);
    delay (500);
    digitalWrite (ledPin, LOW);
    delay (500);
    }  // end of for loop
   
  } // end of loop


A copy of this is now on my "power saving" page:

http://www.gammon.com.au/power

cyclegadget



Thanks Nick! You are always coming up good stuff!

johnwasser

I'm having trouble finding the Keypad2 library.  Tried Google but all I found were a few places that USED it, not where is was defined.

It worked with the latest Keypad library from http://www.arduino.cc/playground/code/Keypad after changing "Keypad2.h" to "Keypad.h" but only with Board set to "Arduino UNO", not the Mega 2560
Send Bitcoin tips to: 1L3CTDoTgrXNA5WyF77uWqt4gUdye9mezN
Send Litecoin tips to : LVtpaq6JgJAZwvnVq3ftVeHafWkcpmuR1e

robtillaart

Well done Nick!
The keypad is of course just one way to wake up the thing. Tripwire would also work.

But what about "complex" sensors like a Hall sensor - I imagine a RPM counter that sleeps most of the time ...possible???)
Or a PIR??


Rob Tillaart

Nederlandse sectie - http://arduino.cc/forum/index.php/board,77.0.html -
(Please do not PM for private consultancy)

Nick Gammon


I'm having trouble finding the Keypad2 library. 


I might have modified Keypad and called it Keypad2. I think it was because of the issue that the original one would have problems if you pushed multiple buttons at once.

As for the Mega, don't know about that unless it does something strange with ports.

Go Up