I created a circuit of a 25 key IR remote controller for my RuneAudio device. The device is a standalone ATmega328P-PU running at 3V (2 AAA batteries) with 8 MHz internal clock. The circuit has 5 rows and 5 columns and uses Arduino keypad library. The code is mostly a copy of Nick Gammon's code (http://www.gammon.com.au/forum/?id=11497&reply=4#reply4), with slight modification for my purpose.
Following is the circuit (R1=1K, R2=10K, R3=62 Ohm):
Following is the code I am using:
#include <Keypad.h>
#include <avr/sleep.h>
#include <avr/power.h>
#include <IRremote.h>
const byte ROWS = 5;
const byte COLS = 5;
byte keynum;
char keys[ROWS][COLS] =
{
{'E', 'D', 'C', 'B', 'A'},
{'J', 'I', 'H', 'G', 'F'},
{'O', 'N', 'M', 'L', 'K'},
{'T', 'S', 'R', 'Q', 'P'},
{'Y', 'X', 'W', 'V', 'U'}
};
byte rowPins[ROWS] = {9, 10, 11, 12, 13}; // Keypad row pins
byte colPins[COLS] = {4, 5, 6, 7, 8}; // Keypad column pins
unsigned long IRCode[25] = {0xB54A38C7, 0xB54A30CF, 0xB54AE191, 0xB54ADCDD, 0xB54AD6C3, 0xB54AE5ED, 0xB54A50AF, 0xB54A9867, 0xB54AB04F, 0xB54A48B7, 0xB54AD02F, 0xB54AFCF7, 0xB54AF56C, 0xB54AEFEE, 0xB54AEAA0, 0xB54A02FD, 0xB54B176F, 0xB54B100D, 0xB54B08F6, 0xB54B031D, 0xB54A42BD, 0xB54AC23D, 0xB54A1AE5, 0xB54A7887, 0xB54A827D};
// Number of items in an array
#define NUMITEMS(arg) ((unsigned int) (sizeof (arg) / sizeof (arg [0])))
// IR setup (NEC remote)
const byte IRledPin = 3;
const int NumBits = 32;
// Define
Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
IRsend irsend;
// turn off interrupts until we are ready
ISR (PCINT0_vect)
{
PCICR = 0; // cancel pin change interrupts
} // end of ISR (PCINT0_vect)
ISR (PCINT1_vect)
{
PCICR = 0; // cancel pin change interrupts
} // end of ISR (PCINT1_vect)
ISR (PCINT2_vect)
{
PCICR = 0; // cancel pin change interrupts
} // end of ISR (PCINT2_vect)
void setup ()
{
pinMode (IRledPin, OUTPUT);
// Pin change interrupt masks (See list on Nick Gammons original code)
PCMSK0 |= bit (PCINT1); // Pin 9
PCMSK0 |= bit (PCINT2); // Pin 10
PCMSK0 |= bit (PCINT3); // Pin 11
PCMSK0 |= bit (PCINT4); // Pin 12
PCMSK0 |= bit (PCINT5); // Pin 13
Serial.begin(9600);
}
void reconfigurePins ()
{
Serial.print("1 Reconfig pins");
Serial.println("");
byte i;
for (i = 0; i < NUMITEMS (colPins); i++)
{
pinMode (colPins [i], OUTPUT);
digitalWrite (colPins [i], HIGH);
}
for (i = 0; i < NUMITEMS (rowPins); i++)
{
pinMode (rowPins [i], INPUT_PULLUP);
} // end of for each row
Serial.print("2 DONE Reconfig pins");
Serial.println("");
} // end of reconfigurePins
void goToSleep ()
{
Serial.print("3 inside SLEEP");
Serial.println("");
// Set up keypress detection
byte i;
for (i = 0; i < NUMITEMS (colPins); i++)
{
pinMode (colPins [i], OUTPUT);
digitalWrite (colPins [i], LOW);
}
for (i = 0; i < NUMITEMS (rowPins); i++)
{
pinMode (rowPins [i], INPUT_PULLUP);
} // end of for each row
// Check no pins pressed (otherwise wake on a key release)
for (i = 0; i < NUMITEMS (rowPins); i++)
{
if (digitalRead (rowPins [i]) == LOW)
{
Serial.print("ROW LOW: ");
Serial.print(rowPins [i]);
Serial.println("");
reconfigurePins ();
return;
}
}
// Overcome any debounce delays built into the keypad library
delay (10);
// 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
Serial.print("4 Going to SLEEP");
Serial.println("");
set_sleep_mode (SLEEP_MODE_PWR_DOWN);
sleep_enable();
Serial.print("5 AWAKE");
Serial.println("");
byte old_ADCSRA = ADCSRA;
// Disable ADC to save power
ADCSRA = 0;
power_all_disable (); // turn off various modules
PCIFR |= bit (PCIF0) | bit (PCIF1) | bit (PCIF2); // clear any outstanding interrupts
PCICR |= bit (PCIE0) | bit (PCIE1) | bit (PCIE2); // enable pin change interrupts
// Turn off brown-out enable in software
MCUCR = bit (BODS) | bit (BODSE);
MCUCR = bit (BODS);
sleep_cpu ();
// Cancel sleep as a precaution
sleep_disable();
power_all_enable (); // enable modules again
ADCSRA = old_ADCSRA; // re-enable ADC conversion
// Put keypad pins back how they are expected to be
reconfigurePins ();
Serial.print("6 Leaving SLEEP");
Serial.println("");
}
void loop ()
{
byte key = kpd.getKey();
if (!key)
{
Serial.print("CALLING SLEEP");
Serial.println("");
// Sleep if no key is pressed
goToSleep ();
return;
Serial.print("After RETURN");
Serial.println("");
}
// Subtract ASCII value of first key value to obtain HEX array argument
keynum = key - 'A';
Serial.print("SENDING IR.....................................................................................");
Serial.println("");
// sendNEC(unsigned long data, int nbits);
irsend.sendNEC(IRCode[keynum], NumBits);
Serial.print(IRCode[keynum]);
Serial.println("");
}
The sketch compiles and uploads just fine. It detects key presses and sends out IR signals. But it misses around half of the key presses. I am testing on a breadboard using a single key - R1C5 (S5 in the schematic).
I must admit that despite spending a lot of time on ATmega328 datasheet and Nick's blogs on power saving, I am not able to fully understand the power saving features due to my not-so-mature skills and the complexity of overall project (Raspberry Pi based rune audio + TDA7439 based tone controller + ATmega based power switch for Pi + 2channel Audio amplifier + this IR remote).
I added a few serial prints to check what is happening. The serial outs seem to be out of sync and last message before sleep is "CALLING SLEEP" whereas I expected it to be "4 Going to SLEEP". And adding serial print mysteriously reduces the number of misses.
Following is the serial output, just after power on and a single key press:
EP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
The following happens after multiple key presses:
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving
CALLING SLEEP
3 inside SLEEP
4 Going to SLEEP
5 AWAKE
1 Reconfig pins
2 DONE Reconfig pins
6 Leaving SLEEP
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig pins
SENDING IR.....................................................................................
3041540295
CALLING SLEEP
3 inside SLEEP
ROW LOW: 9
1 Reconfig pins
2 DONE Reconfig
It would be of great help if someone could show me the light here.
Ok. Replaced long serial print strings with single digits where these stand for: 1= 1 Reconfig pins 2= 2 DONE Reconfig pins 3= 3 inside SLEEP 4= ROW LOW= 5= 4 Going to SLEEP 6= 5 AWAKE 7= 6 Leaving SLEEP 8= CALLING SLEEP .....= SENDING IR
I see where it is failing. Now getting the following serial out:
6
1
2
7
8
3
49
1
2
8
3
49
1
2
8
3
49
1
2
8
3
49
So the program wakes on keypress (6), then reconfigures the pins (1,2), leaves the sleep function (7) enaters the main (8). Here byte key = kpd.getKey()
DOES NOT detect a keypress and calls sleep again (3). Sleep DOES detect a low row 9 (4-9).
After this it gets stuck in a loop of main and sleep functions till key press is over. For now the problem seems to be with kpd.getKey().