Ignoring subsequent pulses from IR remote while key is held down

I’m using my TV remote to control and mute audio in an external amplifier for the TV. For testing, I have an LED on Pin 11 indicating the volume and mute state. My sketch needs to toggle the mute LED output for each pressing of the mute button, no matter how long I hold down the button, at least for 500 milliseconds. The remote sends the mute code ten times a second. I want the code to accept the first signal and ignore the rest of them for 500 milliseconds.

No matter how much or where I set the delay, the toggle is haphazard. How can I get the code to ignore subsequent signals from the IR remote resulting from holding down the button?

Please don’t say “just don’t hold down the button.” That works, but I’ll never get my family members trained to do that.

Below is the relevant code. Scroll down to “MUTE.” The three delays you see work no better than one.

#include <IRremote.h>
int IRpin = 11; // pin for the IR sensor
int LED = 9; // LED pin
float volume = 0;
IRrecv irrecv(IRpin);
decode_results results;
boolean LEDon = true; // initializing LEDon as true
void setup()
{
** Serial.begin(9600);**
** irrecv.enableIRIn(); // Start the receiver**
** pinMode(LED, OUTPUT);**
}
void loop()

**{ results.value=0; **

** if (irrecv.decode(&results)) **
{
** irrecv.resume(); // Receive the next value**
** }**
//VOLUME UP

** if (results.value == 3772833823) {results.value = 0; //remote button VOLUME UP}**
** { analogWrite(LED, volume);**
** if (volume <=250) volume = volume + 6.4;**
** delay(10); **
** }}**
//VOLUME DOWN
** if (results.value == 3772829743) {results.value = 0; //button VOLUME DOWN}**
** { analogWrite(LED, volume);**
** if (volume >=6.4) volume = volume - 6.4; **
** delay(10); **
** }**
** } **
//MUTE
** if (results.value == 3772837903) {results.value = 0; //remote button MUTE}**
{ delay(150);
** analogWrite(LED, LEDon?LOW:volume); //Turn the LED on or off, depending on if ledOn is true or false**
** delay(150);**
** LEDon = LEDon?false:true; //Toggle between false and true**
** delay(150);**
}
} }

You may have more success using millis() instead of delay() like in this example code (untested).
It will not, however, be compatible with any delays you have elsewhere in your code.

unsigned long inMuteMs = 0 ;
. . .
. . .
void loop() {
  . . .
  . . .
  //MUTE

  if (results.value == 3772837903) {
    //remote button MUTE
    if ( millis() - inMuteMs > 1000 && inMuteMs != 0 ) {
      // abandon old mute attempt
      inMuteMs = 0 ;
    }
    else if ( millis() - inMuteMs > 500 && inMuteMs != 0 ) {
      // valid mute
      results.value = 0;
      analogWrite(LED, LEDon ? LOW : volume); //Turn the LED on or off, depending on if ledOn is true or false
      LEDon = LEDon ? false : true; //Toggle between false and true
      inMuteMs = 0 ;
    }
    else if ( inMuteMs == 0) {
      // start of new mute attempt
      inMuteMs =  millis() ;
    }
  }
  . . .
  . . .
}

Instead of delaying, why not look for a repeated code and just ignore it?

6v6gt:
You may have more success using millis() instead of delay() like in this example code (untested).
It will not, however, be compatible with any delays you have elsewhere in your code.

unsigned long inMuteMs = 0 ;

. . .
. . .
void loop() {
  . . .
  . . .
  //MUTE

if (results.value == 3772837903) {
    //remote button MUTE
    if ( millis() - inMuteMs > 1000 && inMuteMs != 0 ) {
      // abandon old mute attempt
      inMuteMs = 0 ;
    }
    else if ( millis() - inMuteMs > 500 && inMuteMs != 0 ) {
      // valid mute
      results.value = 0;
      analogWrite(LED, LEDon ? LOW : volume); //Turn the LED on or off, depending on if ledOn is true or false
      LEDon = LEDon ? false : true; //Toggle between false and true
      inMuteMs = 0 ;
    }
    else if ( inMuteMs == 0) {
      // start of new mute attempt
      inMuteMs =  millis() ;
    }
  }
  . . .
  . . .
}

Thanks, this may be the direction I need to take. I studied millis() earlier today but the Arduino page I looked at implied it was only for determining elapsed time. I'll see if I can dig deeper.

Meanwhile I tried your code. The good news is it ran first try. Buy it's not quite there. When I hold the Mute button down for an extended period of time, it flashes my Pin 9 LED every 500 mS. That would be acceptable except that it does not respond to a short tap of the Mute button Longer presses yield unpredictable results. So I find myself having to use short and long presses to toggle the LED on and off.

I'll dig into your code to see if I can understand how it works. I'm new to this and dyslexia doesn't help, especially when trying to keep track of toggle states.

aarg:
Instead of delaying, why not look for a repeated code and just ignore it?

I like this idea. I added a second "if" statement containing the same mute test and tried to add a delay to so the first test would have to wait until I stopped pressing the button. But that still just toggled the LED.

If anyone knows of a way to ignore code besides delay, I'm all ears. THANKS!

Does this IR remote use the NEC protocol?

If it does I wrote a library that ingnores the repeat code.
The library uses interrupts rather than a timer.

see here:

.

@Tom_Austin... No, you just always remember the previous code. If the current code is the same as the last one, you do nothing. No delays or wait intervals required.

Here’s my revised code. It turns off the LED on Pin 9 when the Mute Button is push, but it doesn’t turn it back on when the button is pushed again. Why?

Relevant code is in bold.

#include <IRremote.h>

int IRpin = 11; // pin for the IR sensor
int LED = 9; // LED pin
float volume = 0; //initialize volume variable to zero
IRrecv irrecv(IRpin);
decode_results results;
int mute = 0;

boolean LEDon = true; // initializing LEDon as true

void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // Start the receiver
pinMode(LED, OUTPUT);
}

void loop()

{results.value=0;

if (irrecv.decode(&results))
{
irrecv.resume(); // Receive the next value
}

//VOLUME UP
if (results.value == 3772833823) {results.value = 0; //this code is remote button VOLUME UP}
analogWrite(LED, volume);
if (volume <=250) volume = volume + 6.4;
delay(10); // keeps the transistion smooth
}

//VOLUME DOWN
if (results.value == 3772829743) {results.value = 0; //this code is remote button VOLUME DOWN}
analogWrite(LED, volume);
if (volume >=6.4) volume = volume - 6.4;
delay(10); // keeps the transistion smooth
}

** //MUTE**
** if (results.value == 3772837903) //remote button MUTE**
** {**

** mute = analogRead(LED);**
** delay(10);**
** if (mute = 0) {analogWrite(LED, volume);**
** delay(50);**
;
** }**

** else**
** {**
** analogWrite(LED, 0);**
** delay(50);**
** mute = 0;**

} } }

You had some good advice in reply #1. You should run with it.

aarg:
You had some good advice in reply #1. You should run with it.

And in post #2. It does look like a classic debounce problem and testing for number of consecutive "mute" button pulses before considering the button press to be valid, would be the easiest in this case.
A successful "mute button presses", any "non-mute" button press, or a gap in time of X mS, would cancel any count of "mute" button presses. A "mute" button press would be ignored if it occurred within X mS of a successful "mute" button press.

My code sample could be changed to attempt to address the phenomenon that the OP reported. The following should be moved so it is executed in the loop all the time and not just when the "mute" button was pressed, and the timeout maybe reduced. But it wont work together with the other delay() statements.

    if ( millis() - inMuteMs > 1000 && inMuteMs != 0 ) {
      // abandon old mute attempt
      inMuteMs = 0 ;
    }

6v6gt:
My code sample could be changed to attempt to address the phenomenon that the OP reported. The following should be moved so it is executed in the loop all the time and not just when the "mute" button was pressed, and the timeout maybe reduced. But it wont work together with the other delay() statements.

    if ( millis() - inMuteMs > 1000 && inMuteMs != 0 ) {

// abandon old mute attempt
     inMuteMs = 0 ;
   }

Thanks. I did study and experiment with your code, spending hours trying to get it to work like I want. No matter what I tried, I kept getting the same behaviors so I went looking for other ways. But I had not tried your suggestion to move this section out of the mute button "if," as such (and I removed the delays in other parts of the code):

** //MUTE**
** if ( millis() - inMuteMs > 1000 && inMuteMs != 0 ) {**
** // abandon old mute attempt**
** inMuteMs = 0 ;**

** }**
** if (results.value == 3772837903) { //remote button MUTE**
** if ( millis() - inMuteMs > 500 && inMuteMs != 0 ) {**
** // valid mute**
** results.value = 0;**
** analogWrite(LED, LEDon ? LOW : volume); //Turn the LED on or off, depending on if ledOn is true or false**
** LEDon = LEDon ? false : true; //Toggle between false and true**
** inMuteMs = 0 ;**
** }**
** else if ( inMuteMs == 0) {**
** // start of new mute attempt**
** inMuteMs = millis() ;**
** }**
** }**
}

It works somewhat, but is erratic. The code above toggles the LED only when the mute button is pressed for a certain duration that's hard to replicate. I can shorten the 500 ms to 250 or 110 to get a quicker response. But that again only works by holding the key for a specific duration that's hard to replicate.

Is my understanding correct that this code validates a key press by letting some time go by such that multiple sendings of the remote code are received within a desired window of time? If so, to validate, why is it necessary to require receiving more than one mute code?

I'm still trying to understand how millis works. I appreciate your help and I will get it eventually. Meanwhile, how can millis be used to ignore other signals after the first signal from the remote has been received?

“I’m still trying to understand how millis works”

Here are a few examples:

//Blink without Delay skeleton 
//4 examples demonstrated
//

//LED wiring options
//=============================================
//Depending which way your LEDs are wired, uncomment the next line.
//#define PlusEqualsON 

#ifdef PlusEqualsON
//wired so +5V turns LED ON
#define ledON  HIGH
#define ledOFF LOW
//=========================
#else
//wired so +5V turns LED OFF
#define ledON  LOW
#define ledOFF HIGH
//=========================
#endif

//switch wiring options
//=============================================
//Depending which way your switches are wired, uncomment the next line.
#define PushEqualsLOW 

#ifdef PushEqualsLOW 
//pushing the switch makes pin LOW
#define Pushed   LOW
#define Released HIGH
//=========================
#else
//pushing the switch makes pin HIGH
#define Pushed   HIGH
#define Released LOW
//=========================
#endif

//=============================================
unsigned long currentMillis;
unsigned long pin13Millis;
unsigned long pin12Millis;
unsigned long pin11Millis;
unsigned long SwitchMillis;

//if these are not changed in the sketch, they can be const
unsigned long debounceMillis = 100UL;     //100ms
unsigned long ledOnTime      = 5*1000UL;  //5 seconds

byte laststartSwitchState    = HIGH;
byte buttonState             = HIGH;
byte counter                 = 0;

//the following are enable/disable flags
//some of these might not be used in this sketch
boolean flag13 = true;
boolean flag12 = true;
boolean flag11 = true;
boolean flag10 = true;

const byte startSwitch = 2; //pushed = LOW
const byte testSwitch  = 3; //pushed = LOW

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

void setup()
{
  Serial.begin(9600);
  
  digitalWrite(13,ledOFF);
  pinMode(13, OUTPUT); 

  digitalWrite(12,ledOFF);
  pinMode(12, OUTPUT);

  digitalWrite(11,ledOFF);
  pinMode(11, OUTPUT);
  
  digitalWrite(10,ledOFF);
  pinMode(10, OUTPUT);

  pinMode(startSwitch, INPUT_PULLUP); //pushed = LOW
  pinMode(testSwitch,  INPUT_PULLUP); //pushed = LOW

} //  >>>>>>>>>>>>>> E N D  O F  s e t u p ( ) <<<<<<<<<<<<<<<<<

void loop()
{
  //save the current time
  currentMillis = millis();

  //************************************* E x a m p l e  1
  //toggle pin 13 every 200mS
  //has 200ms or more gone by?
  if (currentMillis - pin13Millis >= 200UL)
  {
    //code here runs every 200ms
    //get ready for next iteration
    pin13Millis = pin13Millis + 200UL;
    //toggle pin 13
    digitalWrite(13,!digitalRead(13));
  }

  //************************************* E x a m p l e  2
  //at power up, pin 12 LED goes ON, after 3 seconds goes OFF and stays OFF
  //could be used as a powerup reset signal
  if (flag12 == true && currentMillis - pin12Millis <= 3000UL)
  {
    //code here runs for 3 seconds after power up, then stops
    digitalWrite(12,ledON);
  }
  else
  {
    digitalWrite(12,ledOFF);
    //disable further pin 12 control
    flag12 = false;
  }

  //************************************* E x a m p l e  3
  //if testSwitch is pushed and released
  //pin 11 LED goes ON for 5 seconds, then goes OFF 
  buttonState = digitalRead(testSwitch);
  
  //are we are allowed to check the switch and is it pressed?
  if(flag11 == true && buttonState == Pushed)
  {    
    //enable timing of LED on pin 11
    flag11 = false; //false --> timing is enabled
    //turn LED ON
    digitalWrite(11,ledON);
    //record the time LED turned ON
    pin11Millis = currentMillis;
  }
    
  //are we allowed and is it time to control pin 11
  if (flag11 == false && currentMillis - pin11Millis >= ledOnTime)
  {
    //if enabled, code here runs after ledOnTime ms goes by
    digitalWrite(11,ledOFF);
    //allow switch press detection again
    flag11 = true; //true --> switch monitoring is enabled
  }

  //************************************* E x a m p l e  4
  //is it time to check the switches?
  //in particular, pushing startSwitch will turn ON/OFF (toggle) an output pin 10
  //is it time to check the switches
  if (currentMillis - SwitchMillis >= debounceMillis)
  {
    //code here runs every debounceMillis ms
    //get ready for the next iteration
    SwitchMillis += debounceMillis; 
    //go and check the switches
    checkSwitches();    
  } 

  //*********************************
  //put other non-blocking stuff here
  //*********************************

} //  >>>>>>>>>>>>>> E N D  O F  l o o p ( ) <<<<<<<<<<<<<<<<<


//======================================================================
//                      F U N C T I O N S
//======================================================================


//****************** c h e c k S w i t c h e s ( ) *********************
//switches are checked every debounceValue milli seconds 
//no minimum switch press time is validated with this code (i.e. No glitch filter)
void checkSwitches()  
{
  //re-usable for all the switches  
  boolean thisState;    

  //************************************* E x a m p l e  Push ON push OFF (toggle)   
  //check if this switch has changed state
  thisState = digitalRead(startSwitch);
  if (thisState != laststartSwitchState)
  {  
    //update the switch state
    laststartSwitchState = thisState;  

    //this switch position has changed so do some stuff

    //"HIGH condition code"
    //switch went from LOW to HIGH
    if(thisState == HIGH)        
    {
      //Do some HIGH switch stuff here
    }

    //"LOW condition code"
    //switch went from HIGH to LOW
    else                          
    {
      //Do some LOW switch stuff here  
      digitalWrite(10, !digitalRead(10));
      //print number of pushes
      counter++;
      Serial.println(counter);
    }

  } //END of startSwitch code

  //*****************************************  
  //similar code for other switches goes here 
  //*****************************************  

} //END of checkSwitches()

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

//======================================================================
//                      E N D  O F  C O D E
//======================================================================

millis() give the time in mS since the last system restart. It is used for measuring elapsed time. In your case, that sufficient time elapsed since the last successful 'mute' before another 'mute' can start.

Looking at your requirements again, together with information from post #10 it is clear that you want the first press of mute to be considered valid. The code I supplied assumed that you wanted to wait 500 mS to ensure the press was really valid. The 500 mS is the 'dead time' before another mute attempt can start. In that case, it is much simpler.

unsigned long inMuteMs = 0 ;   // inMuteMs is re-purposed as the dead period before a new mute can begin
. . .
. . .

void loop() {
 . . .
 //MUTE

 if (results.value == 3772837903  && millis() - inMuteMs > 500) { //remote button MUTE and at least 500 mS after the last attempt
      results.value = 0;
      analogWrite(LED, LEDon ? LOW : volume); //Turn the LED on or off, depending on if ledOn is true or false
      LEDon = LEDon ? false : true; //Toggle between false and true
      inMuteMs = millis() ;
 }
 . . .

}

6v6gt:

unsigned long inMuteMs = 0 ;   // inMuteMs is re-purposed as the dead period before a new mute can begin

. . .
. . .

void loop() {
. . .
//MUTE

if (results.value == 3772837903  && millis() - inMuteMs > 500) { //remote button MUTE and at least 500 mS after the last attempt
     results.value = 0;
     analogWrite(LED, LEDon ? LOW : volume); //Turn the LED on or off, depending on if ledOn is true or false
     LEDon = LEDon ? false : true; //Toggle between false and true
     inMuteMs = millis() ;
}
. . .

}

Yes, this works. Now my family won't get confused. THANKS. I can continue with the project now! And thank you ALL for helping me learn this stuff.