Martin's corner on the web

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 🙂

 

4 thoughts on “Low power serial keypad

  1. Sergegsx

    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 !

    1. Martin Post author

      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