Problem with IR receiver code

I've set up my Diecimila to use some IR receiver code (RC5 protocol) and the code works perfectly: I'm able to interpret the signals from my Philips remote without a hitch. The problem is that I'm trying to use the code to set the volume on my speakers but I'm having trouble generating a PWM signal.

Basically, I want to have an integer variable named volume where I can store the current volume level. Pressing "Volume Up" adds 1 to the current level and "Volume Down" subtracts 1.

However, for some reason the value of volume is never stored and just resets to zero each time it reads new data from the remote. Therefore, I can't use the variable to set a PWM value. For example, when I press "Volume Up" continuously I get this:

1
1
1
1

instead of this:

1
2
3
4

How can I make this work?

I've pasted the code I'm working with below for reference:

// RC5 remote control receiver decoder.
// Tested with Philips or Philips compatible TV remotes.
// Developed by Alessandro Lambardi, 25/12/2007
// Released under Creative Commons license 2.5.
// Non-commercial use, attribution, share alike.
//
// Completely interrupt driven, no 'wait until' loops.
// When a valid code is received it is made available at
// variable data_word (main loop).
// External Arduino clock 16Mhz, hardware prescaler = 1
// Designed for AVR ATtiny24, adapted for Arduino.
//
// Program memory resources used (ATtiny24):
// 670 Program bytes circa out of 2048 (1/3 circa)
// 4 data bytes out of 128
//
// Internal hardware resources used:
// 8Bit Timer 0:      reset by software as required
// PORTA B, bit 0      can be relocated together with pin
//                  change interrupt assignments

#define F_CPU 16000000UL      // CPU clock in Hertz.
#define TMR0_PRESCALER 256UL // Provides a 62.5KHz clock
#define IR_BIT 1778UL      // bit duration (us) in use for IR remote (RC5 std)
#define      IR_IN  8      //IR receiver is on digital pin 8 (PORT B, bit0)
#define S_LEFT 9 // Left speaker volume
#define S_RIGHT 11 // Right speaker volume

#define TMR0_T (F_CPU/TMR0_PRESCALER*IR_BIT/1000000UL)
#define TMR0_Tmin (TMR0_T - TMR0_T/4UL)
#define TMR0_Tmax (TMR0_T + TMR0_T/4UL)
#if TMR0_Tmax > 255
  #error "TMR0_Tmax too big, change TMR0 prescaler value ", (TMR0_Tmax)
#endif

// Variables that are set inside interrupt routines and watched outside
// must be volatile
volatile uint8_t      tmr0_OC1A_int;  // flag, signals TMR0 timeout (bad !)
volatile uint8_t      no_bits;        // RC5 bits counter (0..14)
volatile uint16_t      ir_data_word;   // if <> holds a valid RC5 bits string (good !)

void start_timer0(uint8_t cnt) {
  OCR0A = cnt;
  TCNT0 = 0;
  tmr0_OC1A_int = 0;
  TIMSK0 |= _BV(OCIE0A);      // enable interrupt on OC0A match
  TCCR0B |= _BV(CS02);      // start timer0 with prescaler = 256
}

// Interrupt service routines
ISR(PCINT0_vect) {                  //  signal handler for pin change interrupt
  if(no_bits == 0) {            // hunt for first start bit (must be == 1)
    if(!digitalRead(IR_IN)){
      start_timer0(TMR0_Tmax);
      no_bits++;
      ir_data_word = 1;
    }
  } else {
    if(!tmr0_OC1A_int) {            // not too much time,
      if(TCNT0 > TMR0_Tmin) {      // not too little.
        // if so wait next (mid bit) interrupt edge
        start_timer0(TMR0_Tmax);
        no_bits++;
        ir_data_word <<= 1;
        if(!digitalRead(IR_IN)){
          ir_data_word |= 1;
        } else {
          ir_data_word &= ~1;
        }
      }
    }
  }
}

ISR(TIM0_COMPA_vect) {            // timer0 OC1A match interrupt handler
  TCCR0B &= ~(_BV(CS02) | _BV(CS01) | _BV(CS00));      // stop timer
  tmr0_OC1A_int = 1;      // signal timeout
  no_bits = 0;            // start over with hunt for valid stream
  ir_data_word = 0;
}

int volume = 0; // Initialize volume at zero
int oldvolume = 0; // Variable for restoring volume after mute
boolean mute = false; // Flag to indicate mute status
uint8_t data_word;    // 0..63 holds a valid RC5 key code.

void setup() {

  Serial.begin(9600);      // opens serial port, sets data rate to 9600 bps

  // IR remote control receiver is on IR_IN digital port
  pinMode(IR_IN, INPUT);

  // pin change interrupt enable on IR_IN port
  PCMSK0 |= _BV(PCINT0);
  PCICR |= _BV(PCIE0);

// Timer 0 : Fast PWM mode. Stopped, for now.
  TCCR0A = _BV(WGM00) | _BV(WGM01);
  TCCR0B = _BV(WGM02);
  no_bits = 0;
  sei();                  // enable interrupts
}

void loop() {
  
  if((no_bits == 14) && ((ir_data_word & 0x37C0) == 0x3400)){ // Changed from 0x3000 to 0x3400 to account for different device (aux)
    no_bits = 0;                                    // prepare for next capture
    data_word = ir_data_word & 0x3F;      // extract data word

    //Serial.print(data_word, HEX);          // output code to console
    //Serial.print("\n");                    // output code to console

    switch(data_word) {
      case 0x10:      // Volume UP
      // If mute isn't set and we haven't reached max, add to volume
      if (mute == false) {
        if (volume < 255) {
          volume = volume + 1;
          Serial.print(volume);
          Serial.print("\n\r");
        }
      }
      break;
      case 0x11:      // Volume DOWN
        // If mute isn't set and we haven't reached min, subtract from volume
        if (mute == false) {
          if (volume > 0) {
            volume = volume - 1;
            Serial.print(volume);
            Serial.print("\n\r");
          }
        }
      break;
      case 0x0D:      // MUTE
        // If mute isn't set, save current volume and set mute
        if (mute == false) {
          mute = true;
          oldvolume = volume;
          volume = 0;
          Serial.print("MUTE");
          Serial.print("\n\r");
        } else {
          // Else, restore previous volume and  unset mute
          mute = false;
          volume = oldvolume;
          oldvolume = 0;
          Serial.print("UNMUTE");
          Serial.print("\n\r");
        }
      break;
    }
  }
  analogWrite(S_LEFT, volume);
  analogWrite(S_RIGHT, volume);
}

It looks good to me (assuming the RC5 part of the code is working properly).

Can you put a little debug print statement in each switch clause to see when it enters that clause like "Serial.println("up");" in the volume up section just before you print the volume value. And you're sure each button press enters exactly one clause?

I put debug text into each of the switch cases and the code can go into each of them just fine. I'm also certain that it is triggering only one case at a time. The key recognition from the remote control signals is working perfectly.

My trouble is with adding and subtracting from the volume variable. It resets to zero every time the code starts looking for a new key. This happens with the other variables that I set as well. So for example, even though I'm trying to set a mute flag to distinguish between mute ON and mute OFF, the code always outputs mute ON because it doesn't save the value of the flag between each cycle.

I suspect that in the top part of the code (before the loop()), the author of the code did something that is messing up my variables... This code is originally from http://www.5volt.eu/archives/14

Any ideas that could help me out?

Try this. Take the definitions:-

int volume = 0; // Initialize volume at zero
int oldvolume = 0; // Variable for restoring volume after mute
boolean mute = false; // Flag to indicate mute status

and declare them just after the #DEFINEs

volatile int volume = 0; // Initialize volume at zero
volatile int oldvolume = 0; // Variable for restoring volume after mute
volatile boolean mute = false; // Flag to indicate mute status

Good suggestion, but I tried it and I'm getting the same behavior: the keys are registered fine, but value of volume and mute just gets reset back to 0 and false once it starts looking for a new keypress...

OK so now what you do is start hacking bits of the code out until you find it. Start off by not enabling the interrupts and timers. Yes you have to comment out stuff to make it compile and you might have to temporarily add some push buttons to jog the code but strip it down to where that variable is not being reset. Then as you enable more of the code you can see when it stops working.

I've been poking around this code some more and I discovered something interesting: if the volume is initialized to something besides 0, the IR receiver code doesn't work at all.

So for example, if I take the working code and simply change volume = 0 to volume = 64, the code stops working and it can't determine the keys being pressed.

It may be that the timer that is controlling the PWM for the analogWrite functions is breaking this somehow. So basically, even if I could increment the value of volume, I can't get this code and the analog output to work at the same time!!