1

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):

enter image description here

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().

1

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".

Printing to Serial doesn't send data to your computer. It just adds that data to a TX buffer, and then an interrupt sends each character one byte at a time.

Your prints are happening, but aren't being sent from the queue before the MCU goes to sleep.

You should add

Serial.flush();

before actually going to sleep to ensure that all data from the TX buffer has been sent. That way your messages will actually be meaningful.

Also if you send more than 64 bytes of data through Serial in a short space of time it will block and wait for some data to be sent out of the TX buffer. The buffer is only 64 bytes in size, so if there is no room left it has to wait for the interrupt. That waiting slows your entire sketch down and can affect the way the sketch operates. A bit like quantum physics - the act of observing the operation of the sketch changes the operation of the sketch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.