Arduino Pro Micro - Wake up with keypad key

Hi all!

Unfortunately my knowledge and experience with this topic is minimal - I hope to ask my question unerstandable:

I use an Arduino Pro Micro with a keypad with the pins 14, 2, 3, 5 (vertical lines) and 10, 7, 8, 9 (horizontal lines).
I want to wake up the Arduino from sleep mode (SLEEP_MODE_PWR_DOWN) with one of this keys via interrupt.

As far I understand the ProMicro has INTERRUPT1 on PIN2. So if PIN2 gets a LOW the Arduino wakes up.
That's why I set PIN10 as OUTPUT and LOW. If I press on the keypad the key, that connects PIN2 with PIN10 the interrupt should see LOW and should wake up.

...
pinMode(10, OUTPUT);
digitalWrite(10, LOW);
attachInterrupt(1, wakeupnow, LOW);
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_mode();
// SLEEP
sleep_disable();
detachInterrupt(1);
...

What did I miss? Thanks for any hint or solution.

Regards
deetee

Does it wake up if you connect pin 2 directly to GND (p.e. by a wire)? How is pin 2 configured (that's why you should post complete code!)?

As far I understand the ProMicro has INTERRUPT1 on PIN2.

The other Arduinos have interrupt 0 on pin 2. Are you sure that the Pro Micro is different?

By the way, the pins used in the KeyPad library are all output pins, except when getKey() is called, so none of them will wake the Arduino.

The other Arduinos have interrupt 0 on pin 2. Are you sure that the Pro Micro is different?

Yes, the Micro has interrupt 1 on pin 2.

By the way, the pins used in the KeyPad library are all output pins, except when getKey() is called, so none of them will wake the Arduino.

How do you know without having seen the actual code? If they are all outputs the risk of a short circuit is quite high. Usually the libraries set them as inputs while they are not used.

Hi!

Thanks so far for helping me.

Yes - the arduino wakes up, if I connect PIN2 to GND directly.

And no - I do not use the keypad library, but some similar code:
The rows are set to INPUT_PULLUP.
In a loop set the col to OUTPUT and LOW., assign key if row reads low and (re)set col to HIGH.

Regards
deetee

Yes - the arduino wakes up, if I connect PIN2 to GND directly.

Post a complete wiring diagram and your complete code!

I hope some raw text will do:

Keyboard (4x4):

18  2  3  5
 |  |  |  |
 X--A--X--X--10
 |  |  |  |
 X--X--X--X-- 7
 |  |  |  |
 X--X--X--X-- 8
 |  |  |  |
 X--X--X--X-- 9
// PINS
#define KEYBOARDCOL1 5
#define KEYBOARDCOL2 3
#define KEYBOARDCOL3 2
#define KEYBOARDCOL4 18
#define KEYBOARDROW1 10
#define KEYBOARDROW2 7
#define KEYBOARDROW3 8
#define KEYBOARDROW4 9

...

static byte getkey() {
  byte pinscol[4] = {KEYBOARDCOL1, KEYBOARDCOL2, KEYBOARDCOL3, KEYBOARDCOL4}; // Pins
  byte pinsrow[4] = {KEYBOARDROW1, KEYBOARDROW2, KEYBOARDROW3, KEYBOARDROW4};
  char k[4][4] = {
    {KEY1, KEY2, KEY3, KEY4},
    {KEY5, KEY6, KEY7, KEY8},
    {KEY9, KEY10, KEY11, KEY12},
    {KEY13, KEY14, KEY15, KEY16},
  };
  byte kee = NULL;
  for (byte r = 0; r < 4; r++) pinMode(pinsrow[r], INPUT_PULLUP); // Set rows to Vcc
  for (byte c = 0; c < 4; c++) {
    pinMode(pinscol[c], OUTPUT); // Set cols as OUTPUT and to GND
    digitalWrite(pinscol[c], LOW);
    for (byte r = 0; r < 4; r++) if (!digitalRead(pinsrow[r])) kee = (k[r][c]); //Assign key
    digitalWrite(pinscol[c], HIGH); // Reset cols to Vcc and INPUT
  }
  return (kee);

...

static void wakeupnow() {} // Dummy wakeup code

static void sleepnow(void) { // Power down
  screenoff(); // Display off only if screensaver didn't
  pinMode(10, OUTPUT);
  digitalWrite(10, LOW);
  attachInterrupt(1, wakeupnow, LOW);
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  sleep_mode();
  // SLEEP ... till key is pressed //
  sleep_disable();
  detachInterrupt(1);
  screenon(); // Display on
  delayshort(200);
}

I was hoping that setting PIN10 to LOW will enable to wake up by pressing key "A" (PIN2, Interrupt 1).

Regards
deetee

I was hoping that setting PIN10 to LOW will enable to wake up by pressing key "A" (PIN2, Interrupt 1).

The question is if the output state of the digital IOs will be kept while in power down sleep. I cannot find any hint on that in the datasheet. Try to write a HIGH to some pin and measure that signal while asleep. What do you measure with a multimeter?

Hi Pylon!

Thanks for your hints and guiding me.

After some measurements I saw that a pin (ie PIN10) which is set (as OUTPUT) to LOW or HIGH remains LOW or HIGH when the arduino falls asleep.

But if PIN10 is set to LOW a connection with the interrupt pin (PIN2) doesn't wake up the arduino (while a connection between PIN2 and GND wakes up the arduino immediately).

It seems that GND activates an interrupt but LOW doesn't ... really strange.

So far I don't have a clue what to do/try next.

Regards
deetee

Nick Gammon wrote this sketch to wake an Arduino from sleep on key press:

// Wake from deep sleep with a keypress demonstration

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

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

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

char keys[] = {
  "123A"
  "456B"
  "789C"
  "*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

It's almost seven years old and probably written for an UNO so you may have to make some adjustments.

The question is if the output state of the digital IOs will be kept while in power down sleep

In the ATmega328 the output pins maintain their state during power down sleep mode, and I doubt it is different in the ATmega32u4.

This statement from the ATmega32u4 data sheet strongly suggests that is the case:

When entering a sleep mode, all port pins should be configured to use minimum power. The
most important is then to ensure that no pins drive resistive loads.

Thanks JohnWasser - your posts are always a source of inspiration.

But adapting a code regarding a (for me difficult) topic is like driving in a fog.

So I tried this - but it doesn't work:

  • Setting cols to OUTPUT/LOW and rows to INPUT/HIGH
  • Set and enable sleep
  • Enable pin change interrupt (only PCIE0 was available)
  • Sleep CPU
  byte pinscol[4] = {KEYBOARDCOL1, KEYBOARDCOL2, KEYBOARDCOL3, KEYBOARDCOL4}; // Pins
  byte pinsrow[4] = {KEYBOARDROW1, KEYBOARDROW2, KEYBOARDROW3, KEYBOARDROW4};
  delay(1000);
  screenoff(); // Display off

  for (byte i = 0; i < 4; i++) { // Set keypad pins
    pinMode(pinscol[i], OUTPUT);
    digitalWrite(pinscol[i], LOW);
    pinMode(pinsrow[i], INPUT);
    digitalWrite(pinsrow[i], HIGH);
  }
  delayshort(50);

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);
  sleep_enable();
  PCIFR |= _BV(PCIF0); // Clear interrupt
  PCICR |= _BV(PCIE0); // Enable pin change interrupt
  sleep_cpu();
  // SLEEP ... till a key is pressed //
  sleep_disable();
  PCICR = 0; // Cancel pin change interrupt
  screenon(); // Display on
  delayshort(200);
  power_all_enable();

Which (essential thing) did I miss?

Regards
deetee

So I tried this - but it doesn't work:

  • Setting cols to OUTPUT/LOW and rows to INPUT/HIGH
  • Set and enable sleep
  • Enable pin change interrupt (only PCIE0 was available)
  • Sleep CPU

Unfortunately the hardware is different. On the UNO (ATmega328p) all digital IOs are available for pin change interrupts. On the Micro (ATmega32U4) only a few pins have the option to work as a pin change interrupt. But the ATmega32U4 wakes up on INT0 and INT1 (pin 2 and 3) by a not only a level (LOW) interrupt but also change interrupts. Try setting the interrupt to CHANGE or FALLING and check if that awakes your Micro.

Hi pylon!

Thanks for always having a further idea.

So I tried every combination of HIGH and LOW of:

digitalWrite(10, LOW);

with every possibility of HIGH, LOW, FALLING, RISING, CHANGE of:

attachInterrupt(1, wakeupnow, LOW);

... but without success.

My last solution will be to (hardware-)separate one key from the keypad and connect it to an own interruptable pin.
An additional advantage could be to implement a "real" shift key to access some fast menu (of my scientific calculator).

Regards
deetee