Low power serial keypad

I bought one of these keypads off ebay few days ago, and decided to try it out.

IMG_2114

These work pretty straight-forward, you have 8 pins – 4 for rows and 4 for columns. When you press a button, it creates contact between the respective row and column. Still, you need 8 pins to drive it, so I decided to use an Attiny84 to do the reading and output the result to a serial output @ 9600 baud. This way I can use the keypad with only one pin (RX) and of course VCC and GND. Further more, for low power applications, I wanted the ATtiny84 to sleep and only wake upon key press. This will make this solution viable for battery operated nodes, I use pin change interrupts to trap keys. The ATtiny84 provides a feedback pin that goes HIGH for 100ms when a button is pressed, this can be used for visual confirmation via a LED, or a piezo buzzer or to wake up a sleeping host system so it can read the key.IMG_2113

Power consumption when waiting for a key press cannot be measured with multimer as it is below any scale, the datasheets states the ATtiny84 consumes 0.1 µA in Power-Down Mode, so you can run it for ages.

My code is simple:

#include < avr / sleep.h >
#include <Keypad.h> // http://playground.arduino.cc/code/Keypad
#include <PinChangeInterrupt.h> // http://code.google.com/p/arduino-tiny/downloads/list

/*
Edit PinChangeInterrupt.h to allow 4 PinChange handlers:

#if defined( __AVR_ATtinyX4__ )
#define NUMBER_PIN_CHANGE_INTERRUPT_HANDLERS (4)
#define NUMBER_PIN_CHANGE_INTERRUPT_PORTS 2
#endif

*/

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif

/*
                     +-\/-+
               VCC  1|    |14  GND
TX        (D0) PB0  2|    |13  AREF (D10)
          (D1) PB1  3|    |12  PA1 (D9)
             RESET  4|    |11  PA2 (D8)
INT0  PWM (D2) PB2  5|    |10  PA3 (D7)
      PWM (D3) PA7  6|    |9   PA4 (D6)
      PWM (D4) PA6  7|    |8   PA5 (D5) PWM
                     +----+
*/

const byte ROWS = 4; //four rows
const byte COLS = 4; //four columns
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {5, 4, 3, 2}; //connect to the column pinouts of the keypad

Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

void wakeUp() {}

void setup(){
    Serial.begin(9600);
    pinMode(10,OUTPUT);
    digitalWrite(10,LOW);
    PRR = bit(PRTIM1); // only keep timer 0 going
    ADCSRA &= ~ bit(ADEN); bitSet(PRR, PRADC);   // Disable the ADC to save power
}

void loop(){

  pinMode(9,INPUT);
  digitalWrite(9,HIGH);  //Internal pull-up
  attachPcInterrupt(9,wakeUp,FALLING); // attach a PinChange Interrupt on the falling edge

  pinMode(8,INPUT);
  digitalWrite(8,HIGH);  //Internal pull-up
  attachPcInterrupt(8,wakeUp,FALLING); // attach a PinChange Interrupt on the falling edge

  pinMode(7,INPUT);
  digitalWrite(7,HIGH);  //Internal pull-up
  attachPcInterrupt(7,wakeUp,FALLING); // attach a PinChange Interrupt on the falling edge

  pinMode(6,INPUT);
  digitalWrite(6,HIGH);  //Internal pull-up
  attachPcInterrupt(6,wakeUp,FALLING); // attach a PinChange Interrupt on the falling edge

  pinMode(5,OUTPUT);
  digitalWrite(5,LOW); 
  pinMode(4,OUTPUT);
  digitalWrite(4,LOW); 
  pinMode(3,OUTPUT);
  digitalWrite(3,LOW); 
  pinMode(2,OUTPUT);
  digitalWrite(2,LOW); 

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);         // Set sleep mode
  sleep_mode();                                // Sleep now
  sleep_disable();
  detachPcInterrupt(9);
  detachPcInterrupt(8);
  detachPcInterrupt(7);
  detachPcInterrupt(6);

  pinMode(5,INPUT);
  pinMode(4,INPUT);
  pinMode(3,INPUT);
  pinMode(2,INPUT);

  pinMode(9,INPUT);
  pinMode(8,INPUT);
  pinMode(7,INPUT);
  pinMode(6,INPUT);

  unsigned long ctime=millis();

  while(millis()-ctime<1000) {
  char key = keypad.getKey();

  if (key){
    digitalWrite(10,HIGH);  
    delay(100);    //Wait for the host to wake/become ready    
    Serial.print(key);
    Serial.flush();
    digitalWrite(10,LOW);  
    ctime=millis();
  }

  }

}

See it in action, this is a test with a FTDI cable and PuTTY terminal program capturing the output.

So, it is really easy to use on Arduino, Raspeberry PI’s UART and so forth.

IMG_2140 IMG_2138 IMG_2137

I can see how this will end up as a product in the shop 🙂

 

Page views: 5058

4 thoughts on “Low power serial keypad

  1. Very nice idea on setting a Attiny84 in between the keypad and the Arduino.
    I think I will apply it to a keypad I have around.
    I see there is no XTAL on the pcb, could you explain how you are doing this? I guess using the internal clock? I would like to know if you could explain the code lines for this.
    Thanks and great work !

    • Yes, it is running off the internal oscillator. That by itself also contributes to cutting down the power consumption, plus simplifies the whole setup. It is enabled by setting the fuses

      low_fuse=0x62
      high_fuse=0xD7
      extended_fuse=0xFF

      using an ISP programmer

  2. Pingback: Logging manualy entered values to emonCMS using keypad | Martin's corner on the web

  3. Pingback: Keypad as USB keyboard | Martin's corner on the web

Comments are closed.