Keypad library, internal pullups, battery power

I have a simple wireless transmitter that I have set up as a remote with VirtualWire & Keypad.

I am trying to simplify my design, which is: 5V 16MHz ProMini, 3 AA battery pack, Velleman 4x4 matrix keypad, sparkfun 434 MHZ transmitter, 22K pullup resistors on the 4 Row pins.

I am measuring 9.22 mA current draw in idle mode, jumping to 14mA or so with a key press.

I have been looking to simplifly & reduce current draw. Reading here, it was suggested to have all unused pins be inputs with internal pullups enabled (declare pinModes as inputs, then digitalWrite High). I did that for the 9 unused pins and saw the current draw drop to 8.95 mA.

I tried doing the same for the keypad pins, figured the library would reset them as needed - only that didn’t quite seem to work, as idle current draw jumped to 20mA.

Is there a way to turn on internal pullups for the keypad pins that the library calls out so I can 1: get rid of the external pullups, and 2: reduce current draw some more when there is nothing going on?

I am also looking into the Sleep modes to turn off the Analog/Digital Comparator and Analog VRef which are not needed.

I don’t have a 3.3VPromini, want to get one of those and check the range/current draw with 3.7V Li Ion battery.

// transmitter_mine.pde//
// Simple example of how to use VirtualWire to transmit messages
// Implements a simplex (one-way) transmitter with Sparkfun module
// RF Link Transmitter - 434MHz, WRL-08946

// See VirtualWire.h for detailed API docs
// Author: Mike McCauley (mikem@open.com.au)
// Copyright (C) 2008 Mike McCauley
// $Id: transmitter.pde,v 1.3 2009/03/30 00:07:24 mikem Exp $

// uses default pin 12 for transmit data

// modified to use input from Keypad to send character to the receiver,
// with 4x4 matrix

// define unused pins so can turn on internal pullups
int dpin0=0;
int dpin1=1;
int dpin2=2;
int dpin11=11;
int dpin14=14;
int dpin15=15;
int dpin16=16;
int dpin17=17;
int dpin18=18;
int dpin19=19;

// adding these caused higher current draw, commented out for now
/*
 int dpin3=3;
 int dpin4=4;
 int dpin5=5;
 int dpin6=6;
 int dpin7=7;
 int dpin8=8;
 int dpin9=9;
 int dpin10=10;
 int dpin12=12;
 int dpin13=13;
*/

#include <VirtualWire.h>
#include <Keypad.h>

const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns

// Define the Keymap
char keys[ROWS][COLS] = 
{
  { '1','2','3','A' },  // row 1
  { '4','5','6','B' },  // row 2
  { '7','8','9','C' },  // row 3
  { '*','0','#','D' },  // row 4
};
// Connect keypad ROW1, ROW2, ROW3 and ROW4 to these Arduino pins.
byte rowPins[ROWS] = { 3, 4, 5, 6 };

// Connect keypad COL1, COL2, COL3, COL4 to these Arduino pins.
byte colPins[COLS] = { 10, 9, 8, 7 }; 

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

#define ledPin 13
char msg[2];

void setup()
{

// define all the unused pins as inputs with internal pullups for lower power state  
  pinMode(dpin0, INPUT);
  digitalWrite(dpin0, HIGH);
  pinMode(dpin1, INPUT);
  digitalWrite(dpin1, HIGH);
  pinMode(dpin2, INPUT);
  digitalWrite(dpin2, HIGH);
  pinMode(dpin11, INPUT);
  digitalWrite(dpin11, HIGH);
  pinMode(dpin14, INPUT);
  digitalWrite(dpin14, HIGH);
  pinMode(dpin15, INPUT);
  digitalWrite(dpin15, HIGH);
  pinMode(dpin16, INPUT);
  digitalWrite(dpin16, HIGH);
  pinMode(dpin17, INPUT);
  digitalWrite(dpin17, HIGH);
  pinMode(dpin18, INPUT);
  digitalWrite(dpin18, HIGH);
  pinMode(dpin19, INPUT);
  digitalWrite(dpin19, HIGH);

// tried adding the following, seemed to conflict with keypad library as current draw doubled
/*
  pinMode(dpin3, INPUT);
  digitalWrite(dpin3, HIGH);
  pinMode(dpin4, INPUT);
  digitalWrite(dpin4, HIGH);
  pinMode(dpin5, INPUT);
  digitalWrite(dpin5, HIGH);
  pinMode(dpin6, INPUT);
  digitalWrite(dpin6, HIGH);
  pinMode(dpin7, INPUT);
  digitalWrite(dpin7, HIGH);
  pinMode(dpin8, INPUT);
  digitalWrite(dpin8, HIGH);
  pinMode(dpin9, INPUT);
  digitalWrite(dpin9, HIGH);
  pinMode(dpin10, INPUT);
  digitalWrite(dpin10, HIGH);
  pinMode(dpin12, INPUT);
  digitalWrite(dpin12, HIGH);
  pinMode(dpin13, INPUT);
  digitalWrite(dpin13, HIGH);
*/
  
  digitalWrite(ledPin, LOW);
//  Serial.begin(9600)             // for debugging only 
//  Serial.println("TX setup");   // for debugging only

  // Initialise the IO and ISR
  vw_setup(2000);       // Bits per sec
}

void loop()
{
  char key = keypad.getKey();
  if(key)                                 // same as if(key != NO_KEY)
  {
    Serial.println(key);
    msg[0]=key;
//   msg[1]=NULL;  // only sending 1 byte, Rx works without this

    digitalWrite(ledPin, true); // Flash a light to show transmitting
//    Serial.println("sending:  "); // for debugging only
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx(); // Wait until the whole message is gone
    delay (1); // shortened flash time
    digitalWrite(ledPin, false);

  }  // end of if(key)
} // end of void loop

I am also looking into the Sleep modes to turn off the Analog/Digital Comparator and Analog VRef which are not needed.

This is very likely the next biggest culprit. Focus your attention on the analog circuitry then come back to the digitals.

Other potential power sponges...

  • Timers

  • Brown Out Detector (BOD)

  • UART

  • Internal voltage reference

Thanks! I have been playing around with that tonight. Managed to get current draw down from 8.95mA in idle mode to just 0.357mA in sleep mode.

Need to do some rewiring now to create the diode OR'd function to get an interrupt on a key press now...

Or, you can use pin change interrupts.

But I thought we only had pins 2 & 3 to use as interrupts?
My rows are on pins 3,4,5,6 right now.

Here’s something else odd - when I am in the void loop, I added some extra LED flashing to see where I am, when a key is pressed the code seems to get stuck with the LED on and becomes unresponsive.

Have I done something to mess up the virtualwire?

// transmitter_mine_updated.pde
// adding interrupt for low power modes
// working the code from this example
// http://www.arduino.cc/playground/Learning/arduinoSleepCode

// need hardware rebuilt now so can try out the wakeup

// with sleep function called -> 0.357mA! should be 175 days on 1500mAH batteries
// down from 8.95 mA in normal idle mode  -> just 7 days
// with bursts of ~14-15mA when a keypress is transmitted

// Simple example of how to use VirtualWire to transmit messages
// Implements a simplex (one-way) transmitter with Sparkfun module MO-SAWR-A,
// RF Link Transmitter - 434MHz, WRL-08946
//
// See VirtualWire.h for detailed API docs
// Author: Mike McCauley (mikem@open.com.au)
// Copyright (C) 2008 Mike McCauley
// $Id: transmitter.pde,v 1.3 2009/03/30 00:07:24 mikem Exp $

// uses default pin 12 for transmit data

// modified to use input from Keypad to send character to the receiver with 4x4 matrix

// Velleman column keypad 1 (Col1) to D11
// keypad 2 (Col2) to D10
// keypad 3 (Col3) to D9
// keypad 4 (Col4) to D8
// row keypad 5 (Row1) to D7
// keypad 6 (Row2) to D6
// keypad 7 (Row3) to D5
// keypad 8 (Row4) to D4
// Row1, 2, 3, 4 Diode OR'd to D3 to pull it low on keypress
// Interrupt on D3 "wakePin" - Input with Internal pullup
// D0, D1, D2, D14, D15, D16, D17, D18, D19 set as inputs with internal pullups enabled

// define unused pins
int dpin0 = 0;
int dpin1 = 1;
int dpin2 = 2;
int dpin14 = 14;
int dpin15 = 15;
int dpin16 = 16;
int dpin17 = 17;
int dpin18 = 18;
int dpin19 = 19;

// activity indicator
#define ledPin 13                 // use for brief flash when transmit

int wakePin = 3;                 // our wake from interrupt pin
int sleepStatus = 0;             // variable to store a request for sleep
int count = 0;                   // counter

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

const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns

// Define the Keymap
char keys[ROWS][COLS] = 
{
  { '1','2','3','A'     },  // row 1
  { '4','5','6','B'     },  // row 2
  { '7','8','9','C'     },  // row 3
  { '*','0','#','D'     },  // row 4
};
// Connect keypad ROW1, ROW2, ROW3 and ROW4 to these Arduino pins.
byte rowPins[ROWS] = { 7, 6, 5, 4 };

// Connect keypad COL1, COL2, COL3, COL4 to these Arduino pins.
byte colPins[COLS] = { 11, 10, 9, 8 }; 

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

// array to store data to be sent out
char msg[2];

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
}

void setup()
{
  // define all the unused pins as inputs with internal pullups for lower power state
  pinMode(dpin0, INPUT);
  digitalWrite(dpin0, HIGH);
  pinMode(dpin1, INPUT);
  digitalWrite(dpin1, HIGH);
  pinMode(dpin2, INPUT);
  digitalWrite(dpin2, HIGH);
  pinMode(dpin14, INPUT);
  digitalWrite(dpin14, HIGH);
  pinMode(dpin15, INPUT);
  digitalWrite(dpin15, HIGH);
  pinMode(dpin16, INPUT);
  digitalWrite(dpin16, HIGH);
  pinMode(dpin17, INPUT);
  digitalWrite(dpin17, HIGH);
  pinMode(dpin18, INPUT);
  digitalWrite(dpin18, HIGH);
  pinMode(dpin19, INPUT);
  digitalWrite(dpin19, HIGH);

  // define the interrut pin as input with internal pullup
  pinMode(wakePin, INPUT);
  digitalWrite(wakePin, HIGH);

  // default pin for data to be transmitted out, taken care of by VirtualWire 
  // pinMode(dpin12, OUTPUT);

  digitalWrite(ledPin, LOW);          // pin 13
  //  Serial.begin(9600);            // for debugging only with FTDI connected to USB
  //  Serial.println("TX setup");

  /* Now it is time to enable an interrupt. In the function call 
   * attachInterrupt(A, B, C)
   * A   can be either 0 or 1 for interrupts on pin 2 or 3.   
   * 
   * B   Name of a function you want to execute while in interrupt A.
   *
   * C   Trigger mode of the interrupt pin. can be:
   *             LOW        a low level trigger
   *             CHANGE     a change in level trigger
   *             RISING     a rising edge of a level trigger
   *             FALLING    a falling edge of a level trigger
   *
   * In all but the IDLE sleep modes only LOW can be used.
   */

  attachInterrupt(1, wakeUpNow, LOW); // use interrupt 1 (pin 3) and run function
  // wakeUpNow when pin 3 gets LOW
}
void sleepNow()         // here we put the arduino to sleep
{
  /* Now is the time to set the sleep mode. In the Atmega8 datasheet
   * http://www.atmel.com/dyn/resources/prod_documents/doc2486.pdf on page 35
   * there is a list of sleep modes which explains which clocks and 
   * wake up sources are available in which sleep mode.
   *
   * In the avr/sleep.h file, the call names of these sleep modes are to be found:
   *
   * The 5 different modes are:
   *     SLEEP_MODE_IDLE         -the least power savings 
   *     SLEEP_MODE_ADC
   *     SLEEP_MODE_PWR_SAVE
   *     SLEEP_MODE_STANDBY
   *     SLEEP_MODE_PWR_DOWN     -the most power savings
   *
   * For now, we want as much power savings as possible, so we 
   * choose the according 
   * sleep mode: SLEEP_MODE_PWR_DOWN
   * 
   */
   
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here

  sleep_enable();          // enables the sleep bit in the MCUCR register
  // so sleep is possible. just a safety pin 

  /* Now it is time to enable an interrupt. We do it here so an 
   * accidentally pushed interrupt button doesn't interrupt 
   * our running program. if you want to be able to run 
   * interrupt code besides the sleep function, place it in 
   * setup() for example.
   * 
   * In the function call attachInterrupt(A, B, C)
   * A   can be either 0 or 1 for interrupts on pin 2 or 3.   
   * 
   * B   Name of a function you want to execute at interrupt for A.
   *
   * C   Trigger mode of the interrupt pin. can be:
   *             LOW        a low level triggers
   *             CHANGE     a change in level triggers
   *             RISING     a rising edge of a level triggers
   *             FALLING    a falling edge of a level triggers
   *
   * In all but the IDLE sleep modes only LOW can be used.
   */

  attachInterrupt(1, wakeUpNow, LOW); // use interrupt 1 (pin 3) and run function
  // wakeUpNow when pin 3 gets LOW 

    sleep_mode();            // here the device is actually put to sleep!!
  // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

  sleep_disable();         // first thing after waking from sleep:
  // disable sleep...
  detachInterrupt(1);      // disables interrupt 1 on pin 3 so the 
  // wakeUpNow code will not be executed 
  // during normal running time.

  // Initialise the IO and ISR
  vw_setup(2000);       // Bits per sec

}

void loop()
{
  char key = keypad.getKey();
  
  if(key)  // same as if(key != NO_KEY)
  {
    //    Serial.println(key); // for debug only
    msg[0]=key;
    // msg[1]=NULL; // Rx seems to work without this

    digitalWrite(ledPin, true); // Flash a light to show transmitting
    //    Serial.println("sending:  "); // for debugging only
    vw_send((uint8_t *)msg, strlen(msg));
    vw_wait_tx(); // Wait until the whole message is gone
    //    delay (1); // don't need at all - get enough flash from above
    digitalWrite(ledPin, false);
    count = 0;  // reset count up to sleep if transmitted a key press

  }  // end of if(key)
  count = count+1;
    digitalWrite(ledPin, true); // LED on
    delay(50);
    digitalWrite(ledPin, false);  // LED off
    delay(50);
    digitalWrite(ledPin, true); // LED on
    delay(50);
    digitalWrite(ledPin, false);  // LED off
    delay(50);

  //  wait 2/10th of a second total  with 2 LED flashes
  
  // check if it should go to sleep because of time
  
  if (count >= 50) 
  {  // ~10 second delay to go to sleep after a key press
      count = 0;
    // flash LED 3 times quick to show going to sleep
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    sleepNow();     // sleep function called here
  } // go back to looking for next key press for a while before going to sleep
} // end of void loop

off to bed, up for work in 4 hours...

But I thought we only had pins 2 & 3 to use as interrupts?

Those are the interrupts supported by the Arduino core.

The hardware also has "pin change" interrupts on all the I/O pins. They're a little more difficult to use but there are plenty of examples floating around.

If you're more comfortable with a wired-or, use that.

Here's something else odd - when I am in the void loop, I added some extra LED flashing to see where I am, when a key is pressed the code seems to get stuck with the LED on and becomes unresponsive

On a single keystroke? Or are you pressing several keys? Or one key several times?

(Mad dash home in between fencing classes...) When I get home later, I'll do a search on the pin change interrupts - wouldn't less wiring vs more.

The lockup is on the first key press. Was expecting to just get a flash of pin 13 LED (as that's what I had before I added the interrupt). Instead got no flash, just solid on. Think I'll disassemble and go back to the protoboard - the cover is falling off the box I have the parts installed in, makes it a handful to work with, especially with the multimeter/ammeter in place. Will wire up to have no external pullups or diodes, just internal pullups on the row pins to start. Can re-add some of the serial print via FTDI to the PC and pinpoint where its stuck at.

Thanks for checking back on me. I'm really having a blast doing this. The receiver for this is coming along great. That box has 6 panels of LEDs with 8 seven segment displays made of discrete LEDs (for a larger size) to show time & score displays. 3 are done, 2 nearly done, will be starting the control board that will hold another pro-mini next. I have had the control board all prototyped including a small 7 segment display and used that 1 display to debug all the IO and interfacing by adapting the code to write to that 1 digit at a time to simulate writing to the MAX6951 that drive the 8 larger digits. I have pages and pages of schematics, layout drawings, cable interconnects, cable drawings. Guess I need parts lists next to capture all the parts used. Robert

Okay, solved one problem - the apparent hang on keypad press. I added print statements thru the wireless sending, it was getting hung after printing "Sent":

    digitalWrite(ledPin, true); // Flash a light to show transmitting
    Serial.println("sending"); // for debugging only
    vw_send((uint8_t *)msg, strlen(msg));
    Serial.println("sent"); // for debugging only
    vw_wait_tx(); // Wait until the whole message is gone
    //    delay (1); // don't need at all - get enough flash from above
    digitalWrite(ledPin, false);

Turns out when I was pasting in the interrupt code, I managed to get this command for VirtualWire

// Initialise the IO and ISR vw_setup(2000); // Bits per sec

inserted inside of the

void sleepNow() // here we put the arduino to sleep

section, where it apparently was not getting initialized.

Now I can go chase PCInts and see if I can get interrupts on any keypress without going the Diode OR route into Interrupt Pin 2 ...

Okay,
Can I have a little more help please?
I’ve got VirtualWire+Keypad+Sleep+PCInt code all merged.
I can’t seem to wakeup after sleeping tho.
Not sure why not.
I changed PCInt example to have pins D4,5,6 call same wakeup routine as Sleep was.
4x4 keypad has Rows with external pullups connected to D3 (Int0), D4-5-6 (PCInt 20,21,22), calling the PCInts by either name has no affect.

Code posted in 2 pieces due to length:

// transmitter_mine_updated_PCINT.pde
// adding Pin Channel interrupts for low power modes
// working the code from this example
// http://www.arduino.cc/playground/Learning/arduinoSleepCode

// with sleep function called -> 0.357mA! should be 175 days on 1500mAH batteries
// down from 8.95 mA in normal idle mode  -> just 7 days
// with bursts of ~14-15mA when a keypress is transmitted

// PCInts from this example
// http://www.arduino.cc/playground/Main/PcInt

// Simple example of how to use VirtualWire to transmit messages
// Implements a simplex (one-way) transmitter with Sparkfun module MO-SAWR-A,
// RF Link Transmitter - 434MHz, WRL-08946
//
// See VirtualWire.h for detailed API docs
// Author: Mike McCauley (mikem@open.com.au)
// Copyright (C) 2008 Mike McCauley
// $Id: transmitter.pde,v 1.3 2009/03/30 00:07:24 mikem Exp $

// uses default pin 12 for transmit data

// modified to use input from Keypad to send character to the receiver with 4x4 matrix

// Velleman column keypad 1 (Col1) to D10
// keypad 2 (Col2) to D9
// keypad 3 (Col3) to D8
// keypad 4 (Col4) to D7
// row keypad 5 (Row1) to D6 - try interrupting on this via PCInt22
// keypad 6 (Row2) to D5 - try interrupting on this via PCInt21
// keypad 7 (Row3) to D4 - try interrupting on this via PCInt20
// keypad 8 (Row4) to D3 - try interrupting on this via INT1
// Option 1: Row1, 2, 3, 4 Diode OR'd to D2 (INT0) to pull it low on keypress
// Interrupt on D2 "wakePin" - Input with Internal pullup
// Option 2: Use PCInt to interrupt on input pins instead
// D0, D1, D11, D14, D15, D16, D17, D18, D19 set as inputs with internal pullups enabled

// end of notes
// ***********************************************************************

// bring in the library(s)
#include <VirtualWire.h> // Wireless transmitter/receiver library
#include <Keypad.h>      // Matrix Keypad library
#include <avr/sleep.h>   // powerdown library
#include <pins_arduino.h> // interrupts on I/O pins library

// ***********************************************************************

// define unused pins
int dpin0 = 0; // apparently redefined by Serial as Serial Monitor works
int dpin1 = 1; // apparently redefined by Serial as Serial Monitor works
int dpin11 = 11;
int dpin14 = 14;
int dpin15 = 15;
int dpin16 = 16;
int dpin17 = 17;
int dpin18 = 18;
int dpin19 = 19;

// ***********************************************************************
// define the used pins

#define ledPin 13      // activity indicator, use for brief flash when transmit

// int wakePin = 3;       // our wake from sleep interrupt pin - defined as just (3) by the keypad below?
int sleepStatus = 0;   // variable to store a request for sleep
int count = 0;         // counter for telling when to sleep

// ***********************************************************************
// create an array to store data to be sent out
char msg[1];

// set up the Keypad
const byte ROWS = 4; // Four rows
const byte COLS = 4; // Four columns

// Define the Keymap
char keys[ROWS][COLS] = 
{
  {
    '1','2','3','A'             }
  ,  // row 1
  { 
    '4','5','6','B'             }
  ,  // row 2
  { 
    '7','8','9','C'             }
  ,  // row 3
  { 
    '*','0','#','D'             }
  ,  // row 4
};
// Connect keypad ROW1, ROW2, ROW3 and ROW4 to these Arduino pins.
byte rowPins[ROWS] = { 
  6, 5, 4, 3 };  //these have 22K external pullups - can Keypad use internal pullups?
// apparently externals needed during powerdown mode as pins go to tri-state

// Connect keypad COL1, COL2, COL3, COL4 to these Arduino pins.
byte colPins[COLS] = { 
  10, 9, 8, 7 }; 

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

// ***********************************************************************
// define stuff for INT0 use

void wakeUpNow()        // here the interrupt is handled after wakeup
{
  // execute code here after wake-up before returning to the loop() function
  // timers and code using timers (serial.print and more...) will not work here.
  // we don't really need to execute any special functions here, since we
  // just want the thing to wake up
}

// ***********************************************************************
/*  This whole section added for PC INTs - tailored for just the INTs we want 
 * on PCINT20-21-22, PCI2, PCMSK2
 * an extension to the interrupt support for arduino.
 * add pin change interrupts to the external interrupts, giving a way
 * for users to have interrupts drive off of any pin.
 * Refer to avr-gcc header files, arduino source and atmega datasheet.
 */

/*
 * Theory: all IO pins on Atmega328P are covered by Pin Change Interrupts.
 * The PCINT corresponding to the pin must be enabled and masked, and
 * an ISR routine provided.  Since PCINTs are per port, not per pin, the ISR
 * must use some logic to actually implement a per-pin interrupt service.
 */

/* Pin to interrupt map:
 * D0-D7 = PCINT 16-23 = PCIR2 = PD = PCIE2 = pcmsk2 // this has the 3 I want
 * D8-D13 = PCINT 0-5 = PCIR0 = PB = PCIE0 = pcmsk0
 * A0-A5 (D14-D19) = PCINT 8-13 = PCIR1 = PC = PCIE1 = pcmsk1
 */
// ***********************************************************************
volatile uint8_t *port_to_pcmask[] = {
  &PCMSK0,  // do I need 0 & 1 if only using 2?
  &PCMSK1,  // do I need 0 & 1 if only using 2?
  &PCMSK2
};

static int PCintMode[24];  // why 24?

typedef void (*voidFuncPtr)(void);

volatile static voidFuncPtr PCintFunc[24] = { 
  NULL };// can these be 22 ...
volatile static uint8_t PCintLast[3];                // down to 20??

// ***********************************************************************
// * attach an interrupt to a specific pin using pin change interrupts.

// there's gotta be a way to clean this up for just the 3 pins I want - 4,5,6

void PCattachInterrupt(uint8_t pin, void (*userFunc)(void), int mode) 
{
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  uint8_t slot;
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // -- Fix by Baziki. In the original sources it was a little bug, which cause analog ports to work incorrectly.
  // analog stuff - not using these pins

  if (port == 1) {
    slot = port * 8 + (pin - 14);
  }
  else {
    slot = port * 8 + (pin % 8);
  }
  // --Fix end

  PCintMode[slot] = mode;
  PCintFunc[slot] = userFunc;
  // set the mask
  *pcmask |= bit;
  // enable the interrupt
  PCICR |= 0x01 << port;
}

// ***********************************************************************
// like before - how clean up for just my 3 pins? -4,5,6

void PCdetachInterrupt(uint8_t pin) {
  uint8_t bit = digitalPinToBitMask(pin);
  uint8_t port = digitalPinToPort(pin);
  volatile uint8_t *pcmask;

  // map pin to PCIR register
  if (port == NOT_A_PORT) {
    return;
  } 
  else {
    port -= 2;
    pcmask = port_to_pcmask[port];
  }

  // disable the mask.
  *pcmask &= ~bit;
  // if that's the last one, disable the interrupt.
  if (*pcmask == 0) {
    PCICR &= ~(0x01 << port);
  }
}
// ***********************************************************************
// common code for isr handler. "port" is the PCINT number.
// there isn't really a good way to back-map ports and masks to pins.

// and again - how clean up for just my 3?
static void PCint(uint8_t port) 
{
  uint8_t bit;
  uint8_t curr;
  uint8_t mask;
  uint8_t pin;

  // get the pin states for the indicated port.
  curr = *portInputRegister(port+2);
  mask = curr ^ PCintLast[port];
  PCintLast[port] = curr;
  // mask is pins that have changed. screen out non pcint pins.
  if ((mask &= *port_to_pcmask[port]) == 0) {
    return;
  }
  // mask is pcint pins that have changed.
  for (uint8_t i=0; i < 8; i++) {
    bit = 0x01 << i;
    if (bit & mask) {
      pin = port * 8 + i;
      // Trigger interrupt if mode is CHANGE, or if mode is RISING and
      // the bit is currently high, or if mode is FALLING and bit is low.
      if ((PCintMode[pin] == CHANGE
        || ((PCintMode[pin] == RISING) && (curr & bit))
        || ((PCintMode[pin] == FALLING) && !(curr & bit)))
        && (PCintFunc[pin] != NULL)) {
        PCintFunc[pin]();
      }
    }
  }
}// ***********************************************************************
//  Not using 0 & 1, only PCInt2? And what do these do?

SIGNAL(PCINT0_vect) {
  PCint(0);
}
SIGNAL(PCINT1_vect) {
  PCint(1);
} 

SIGNAL(PCINT2_vect) {
  PCint(2);
}

/* not using these anymore for sure
 volatile long ticktocks = 0;
 long i = 0;
 
 void tick(void) {
 ticktocks++;
 }
 
 void tock(void) {
 ticktocks--;
 }
 */

and the rest:

// ***********************************************************************
// set up the pins as Inputs, Outputs, etc.
void setup()
{
  // define all the unused pins as inputs with internal pullups for lower power state
  pinMode(dpin0, INPUT);       // apparently redefined by Serial as Serial Monitor works (receiving anyway)
  digitalWrite(dpin0, HIGH);   // apparently redefined by Serial as Serial Monitor works
  pinMode(dpin1, INPUT);       // apparently redefined by Serial as Serial Monitor works
  digitalWrite(dpin1, HIGH);   // apparently redefined by Serial as Serial Monitor works
  pinMode(dpin11, INPUT);
  digitalWrite(dpin11, HIGH);
  pinMode(dpin14, INPUT);
  digitalWrite(dpin14, HIGH);
  pinMode(dpin15, INPUT);
  digitalWrite(dpin15, HIGH);
  pinMode(dpin16, INPUT);
  digitalWrite(dpin16, HIGH);
  pinMode(dpin17, INPUT);
  digitalWrite(dpin17, HIGH);
  pinMode(dpin18, INPUT);
  digitalWrite(dpin18, HIGH);
  pinMode(dpin19, INPUT);
  digitalWrite(dpin19, HIGH);

  // define the interrut pin as input with internal pullup
  //  pinMode(wakePin, INPUT);
  //  digitalWrite(wakePin, HIGH);
  // redefine as just (3)? since Keypad is calling it that also?
  pinMode(3, INPUT);
  digitalWrite(3, HIGH);

  // default pin for data to be transmitted out
  // pinMode(dpin12, OUTPUT); // taken care of by VirtualWire

  digitalWrite(ledPin, LOW);  // pin 13
  Serial.begin(9600);            // need for debug monitoring only with FTDI connected to USB
  Serial.println("TX setup"); // for debug only 

  /* Now it is time to enable an interrupt. In the function call 
   * attachInterrupt(A, B, C)
   * A   can be either 0 or 1 for interrupts on pin 2 or 3.   
   * B   Name of a function you want to execute while in interrupt A.
   * C   Trigger mode of the interrupt pin. can be:
   *             LOW        a low level trigger
   *             CHANGE     a change in level trigger
   *             RISING     a rising edge of a level trigger
   *             FALLING    a falling edge of a level trigger
   *
   * In all but the IDLE sleep modes only LOW can be used.
   */

  attachInterrupt(1, wakeUpNow, LOW); // use interrupt 1 (pin 3) and run function
  // wakeUpNow when pin 3 gets LOW

    // ***********************************************************************
  // add the PCInt interrupts - changed to call out the same ISR as INT0
  // to run function wakeUpNow when pins 20, 21, 22 gets LOW

    PCattachInterrupt(4, wakeUpNow, LOW);  // D4 = 20
  PCattachInterrupt(5, wakeUpNow, LOW);  // D5 = 21
  PCattachInterrupt(6, wakeUpNow, LOW);  // D6 = 22

  // ***********************************************************************
  // Initialise the IO and ISR for VirtualWire
  vw_setup(2000);       // Bits per sec

}
// ***********************************************************************
void sleepNow()         // here we put the arduino to sleep
{
  /* Now is the time to set the sleep mode. In the Atmega328P datasheet
   * Section 9 there is a list of sleep modes which explains which clocks and 
   * wake up sources are available in which sleep mode.
   *
   * In the avr/sleep.h file, the call names of these sleep modes are to be found:
   *
   * The 5 different modes are:
   *     SLEEP_MODE_IDLE         -the least power savings 
   *     SLEEP_MODE_ADC
   *     SLEEP_MODE_PWR_SAVE
   *     SLEEP_MODE_STANDBY
   *     SLEEP_MODE_PWR_DOWN     -the most power savings
   *
   * For now, we want as much power savings as possible, so we 
   * choose the according 
   * sleep mode: SLEEP_MODE_PWR_DOWN
   */

  set_sleep_mode(SLEEP_MODE_PWR_DOWN);   // sleep mode is set here

  sleep_enable();          // enables the sleep bit in the MCUCR register
  // so sleep is possible. just a safety pin 

  // ***********************************************************************
  /* Now it is time to enable an interrupt. We do it here so an 
   * accidentally pushed interrupt button doesn't interrupt 
   * our running program. if you want to be able to run 
   * interrupt code besides the sleep function, place it in 
   * setup() for example.
   * 
   * In the function call attachInterrupt(A, B, C)
   * A   can be either 0 or 1 for interrupts on pin 2 or 3.   
   * B   Name of a function you want to execute at interrupt for A.
   * C   Trigger mode of the interrupt pin. can be:
   *             LOW        a low level triggers
   *             CHANGE     a change in level triggers
   *             RISING     a rising edge of a level triggers
   *             FALLING    a falling edge of a level triggers
   * In all but the IDLE sleep modes only LOW can be used.
   */

  attachInterrupt(1, wakeUpNow, LOW); // use interrupt 1 (pin 3) and run function
  // wakeUpNow when pin 3 gets LOW 

  // ***********************************************************************
  // add the PCInt interrupts - changed to call out the same ISR as INT0
  // to run function wakeUpNow when pins 20, 21, 22 gets LOW

  PCattachInterrupt(4, wakeUpNow, LOW);  // D4 = 20
  PCattachInterrupt(5, wakeUpNow, LOW);  // D5 = 21
  PCattachInterrupt(6, wakeUpNow, LOW);  // D6 = 22


  sleep_mode();            // here the device is actually put to sleep!!
  // THE PROGRAM CONTINUES FROM HERE AFTER WAKING UP

  sleep_disable();         // first thing after waking from sleep:
  // disable sleep...
  detachInterrupt(1);      // disables interrupt 1 on pin 3 so the 
  // wakeUpNow code will not be executed during normal running time.
  // and the PCInts also:
  PCdetachInterrupt(4);  // D4 = 20
  PCdetachInterrupt(5);  // D5 = 21
  PCdetachInterrupt(6);  // d6 = 22

  // now lets see if this mess runs!?!
}
// ***********************************************************************
// Finally, the loop for reading the keypad and sending the button pushed out
// (with a unique address to be added eventually by reading 0-F from currently unused pins)

void loop()
{
  char key = keypad.getKey();
  if(key)  // same as if(key != NO_KEY)
  {
    //    Serial.println(key); // for debug only
    msg[0]=key;
    // msg[1]=NULL; // Rx seems to work without this

    digitalWrite(ledPin, true); // Flash a light to show transmitting
    Serial.println("sending"); // for debugging only
    vw_send((uint8_t *)msg, strlen(msg));
    Serial.println("sent"); // for debugging only
    vw_wait_tx(); // Wait until the whole message is gone
    //    delay (1); // don't need at all - get enough flash while sending
    digitalWrite(ledPin, false);
    count = 0;  // reset count up to sleep if transmitted a key press

  }  // end of if(key)
  count = count+1;

  //  wait 2/10th of a second total with 2 LED flashes
  Serial.println("waiting"); // this whole section for debugging only
  digitalWrite(ledPin, true); // LED on
  delay(50);
  digitalWrite(ledPin, false);  // LED off
  delay(50);
  digitalWrite(ledPin, true); // LED on
  delay(50);
  digitalWrite(ledPin, false);  // LED off
  delay(50);

  // check if we should go to sleep because of time
  if (count >= 50) // update to 1000 after getting rid of flashes above
  {  // ~10 second delay to go to sleep after a key press
    Serial.println("going to sleep"); // for debugging only
    count = 0;
    // flash LED 3 times quick to show going to sleep - get rid of after debugged
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    digitalWrite(ledPin, true); // LED on
    delay(100);
    digitalWrite(ledPin, false);  // LED off
    delay(100);
    sleepNow();     // sleep function called here
  } // go back to looking for next key press for a while before going to sleep
} // end of void loop

Finally managed to get out of sleep mode, manually anyway. Turns out all this time I didn’t have
#include <avr/interrupt.h>
in my sketch, saw something in the while browsing one of the cheatsheets led me do a different example where I noticed that in the sketch - stuck it in, and now I can interrupt out of sleep mode.

Gonna go ask in the hardware forum how to get a keypad response while in sleep mode now …