Press and Hold IR Remote Control Button

Hi fellow makers,

I’m working on a project that consists of inputting a signal via an IR remote control (Mini Remote Control : ID 389 : $4.95 : Adafruit Industries, Unique & fun DIY electronics and kits), decoding signal with an IR receiver (IR (Infrared) Receiver Sensor [TSOP38238] : ID 157 : $1.95 : Adafruit Industries, Unique & fun DIY electronics and kits), and controlling a set of relays using SainSmart 8 channel relay board (http://bit.ly/210rB3u). My client’s requirements state that I need to be able to calibrate the remote controller to be able to press and hold buttons and not just press and release. I am able to press and hold using common push buttons but I am unable to do so using an IR remote. However, I’m using IRlib Arduino library and I noticed that this is possible with one of the examples of the library called “IRrecvDump”. This sketch listens in for any IR signal and outputs the IR protocol used among other relevant data (I’ve attached a picture of it). When I press and hold any button I get a first decoded reading followed by other FFFFFFF (0 bits) readings. What is interesting is that as soon as I release the button the serial monitor stops reading. I suspect this has something to do with the .DumpResults() method but I’m not proficient enough with C++ and adapting or creating Arduino libraries. I’ve attached my code for your benefit. Any help is greatly appreciated. Thanks.

#include <IRLib.h>

#define RELAY_ON 0
#define RELAY_OFF 1
/*-----( Declare objects )-----*/
/*-----( Declare Variables )-----*/
#define Relay_1  1      
#define Relay_2  2    //GREEN
#define Relay_3  3    //RED
#define Relay_4  4    //YELLOW
#define Relay_5  5
#define Relay_6  6
#define Relay_7  7
#define Relay_8  8

#define SETUP   0xfd20df
#define BACK    0xfd708f
#define ENTER   0xfd906f

#define UP      0xfda05f
#define RIGHT   0xfd50af
#define DOWN    0xfdb04f
#define LEFT    0xfd10ef

#define ONE     0xfd08f7
#define TWO     0xfd8877
#define THREE   0xfd48b7
#define FOUR    0xfd28d7
#define FIVE    0xfda857
#define SIX     0xfd6897
#define SEVEN   0xfd18e7
#define EIGHT   0xfd9867
#define NINE    0xfd58a7

#define HOLD    9
#define SNAP    10
#define TIME    12

IRrecv My_Receiver(11);//receiver on pin 11
IRdecode My_Decoder;//Decoder object 
 
void setup() {  
  Serial.begin(9600);
  My_Receiver.enableIRIn(); // Start the receiver

  //  digitalWrite(Relay_1, RELAY_OFF);
  digitalWrite(Relay_2, RELAY_OFF);
  digitalWrite(Relay_3, RELAY_OFF);
  digitalWrite(Relay_4, RELAY_OFF);  
//  pinMode(Relay_1, OUTPUT);   
  pinMode(Relay_2, OUTPUT);  
  pinMode(Relay_3, OUTPUT);  
  pinMode(Relay_4, OUTPUT);
  pinMode(HOLD, INPUT);  
  pinMode(SNAP, INPUT);  
  pinMode(TIME, INPUT);
//  digitalWrite(HOLD, LOW);
//  digitalWrite(SNAP, LOW);
//  digitalWrite(TIME, LOW);        
  delay(4000); //Check that all relays are inactive at Reset
}
 
void loop() {
//  Serial.println(digitalRead(11));
  if (My_Receiver.GetResults(&My_Decoder)) {
    My_Decoder.decode();
    if (My_Decoder.decode_type== NEC) {
      switch(My_Decoder.value) {
        case UP:  //  Setup relays for Press and Hold  
          Serial.println("CLOSE MODE");
          mode = HOLD;          
          Serial.println(mode);         
          delay(1000);
          break;
        case RIGHT:  //  Setup relays for Press and Release 
          Serial.println("SNAP MODE");
          mode = SNAP;
          Serial.println(mode);        
          delay(1000);
          break;
        case LEFT:  //  Setup relays for Timed Release 
          Serial.println("TIMED MODE");
          mode = TIME;
          Serial.println(mode);       
          delay(1000);
          break;
        case THREE:  //  Relay number 3
          digitalWrite(Relay_3, RELAY_ON);
          Serial.print("THREE");
          delay(1000);
          digitalWrite(Relay_3, RELAY_OFF);
          break;   
        case FOUR:  //  Relay number 4
          Serial.print("FOUR");
          if(mode == HOLD) {
            digitalWrite(Relay_4, RELAY_ON);
            Serial.print(digitalRead(11));
            delay(2000);
          }
          break;                         
      }
    My_Receiver.resume(); //Restart the receiver
    }   
  }     
}

Hi,

Nobody in Arduino forums can help me with this?

On most IR remotes, holding a button down will result in the key code being sent followed by a stream of "repeat" codes (FFFFFFF), so it looks like your decoder is working correctly.

Thanks for responding Martin-X. For that particular sketch it does work (IRrecvDump). That's what I'm trying to show. What I don't understand is how to achieve IR press and hold functionality inside of one of the case statements of my code. Any ideas? Thanks.

Record in a variable each new (non-repeat) code received, then on receiving the repeat code just perform the action for the stored code.

Pseudo-code below, apologies if it's really a load of rubbish, but it should give you an idea...

currentButton = irDecode();         // test for button code

if (validCode)                         // code received
{
  if (currentButton != prevButton)     // new button or repeat code
  {
    if (currentButton == repeatCode)
    {
      actionCode = prevButton;          // perform action based on stored button
    }
    else
    {
      prevButton = currentButton;      // store the new code
      actionCode = currentButton;      // act on the new code
    }
  }

  switch (actionCode)
  {
   ....
  }
}

Hi,

After weeks of trying to figure this out I’m still stuck with this problem. I simply cannot find any way to make this work using IRlib. Essentially, what I’m trying to achieve is a simple Volume Control using IR. The only difference is that instead of volume I want to control a relay like you would with a simple push button. When you press and hold the button from the IR remote the relay turns on and stays on. Only when you release the button will the relay turn off.

I’m struggling immensely with this because first, after pressing and holding the button the first time, the byte changes to 0xFFFFFF which is interpreted as REPEAT. As you can see from my code, I’ve been trying to condition it as to leaving the relay on while on REPEAT mode also. The problem I’m having with this is that it takes two cycles to determine that its repeating so after the first cycle the relay turns off according to my conditions. Can’t figure a way to bypass this.

Second, after quickly pressing and releasing the button the relay should turn on and off fast accordingly. If you just press a button and release, the My_Decoder.value method stores the decoded byte until a new one is pressed. I’m trying to avoid leaving the relay on after releasing the button before the MCU detects REPEAT mode. Only when you leave it pressed it will change to 0xFFFFFF or REPEAT. I tried solving this with the last else if statement of my loop and it works, but its triggering the previous problem of turning off the relay in the second cycle and turning it on again after it detects REPEAT mode.

Third, I can’t seem to find a way to turn off the relay after I release the button. I thought that by using an else statement after the first if with a simple digitalWrite OFF I could achieve this. I’m beginning to think that it’s impossible using this library or maybe its due to my poor programming skills. I really hope its the latter. Please guys, help is deeply appreciated. I simply can’t accept that it doesn’t work. There must be a way. Thanks. :frowning: :frowning: :confused:

#include <IRLib.h>

#define RELAY_ON 0
#define RELAY_OFF 1
/*-----( Declare objects )-----*/
/*-----( Declare Variables )-----*/
#define Relay_1  1      
#define Relay_2  2    //GREEN
#define Relay_3  3    //RED
#define Relay_4  4    //YELLOW
#define Relay_5  5
#define Relay_6  6
#define Relay_7  7
#define Relay_8  8

#define SETUP   0xfd20df
#define BACK    0xfd708f
#define ENTER   0xfd906f

#define UP      0xfda05f
#define RIGHT   0xfd50af
#define DOWN    0xfdb04f
#define LEFT    0xfd10ef

#define ONE     0xfd08f7
#define TWO     0xfd8877
#define THREE   0xfd48b7
#define FOUR    0xfd28d7
#define FIVE    0xfda857
#define SIX     0xfd6897
#define SEVEN   0xfd18e7
#define EIGHT   0xfd9867
#define NINE    0xfd58a7


bool GotNew;
unsigned long codeValue, preValue;   // The data bits if type is not raw 

IRrecv My_Receiver(11);//receiver on pin 11
IRdecode My_Decoder;//Decoder object 
 
void setup() {  
  Serial.begin(9600);
  My_Receiver.enableIRIn(); // Start the receiver
  GotNew = false;
//  GotRepeat = false;

  //  digitalWrite(Relay_1, RELAY_OFF);
  digitalWrite(Relay_2, RELAY_OFF);
  digitalWrite(Relay_3, RELAY_OFF);
  digitalWrite(Relay_4, RELAY_OFF);  
//  pinMode(Relay_1, OUTPUT);   
  pinMode(Relay_2, OUTPUT);  
  pinMode(Relay_3, OUTPUT);  
  pinMode(Relay_4, OUTPUT);  
  delay(4000); //Check that all relays are inactive at Reset
}
 
void loop() {
   

//  Serial.println(My_Receiver.GetResults(&My_Decoder)); 

  if (My_Receiver.GetResults(&My_Decoder)) {
    My_Decoder.decode();
//    GotOne=true;
    storeCode();
    delay(1000);

    if ((My_Decoder.value == FOUR && GotNew == true) || My_Decoder.value == REPEAT) {
      digitalWrite(Relay_4, RELAY_ON); 
      delay(500);
    }

    else {
      digitalWrite(Relay_4, RELAY_OFF); 
      delay(500);  
    }
    My_Receiver.resume(); 
  }

  else if (!My_Receiver.GetResults(&My_Decoder) && My_Decoder.value != REPEAT) {
        Serial.println("OFF");

    digitalWrite(Relay_4, RELAY_OFF); 
      delay(500); 
  }

  else if(My_Decoder.value == codeValue) {
    digitalWrite(Relay_4, RELAY_OFF); 
    Serial.println("not a chance");
    delay(500);  
  }
}

void storeCode(void) {
  
  if (My_Decoder.value == REPEAT) {
    // Don't record a NEC repeat value as that's useless.
    Serial.println(F("repeat; ignoring."));
    GotNew = false;
//    GotRepeat = true;
    codeValue = preValue;
   }
   else {
     GotNew = true;
     codeValue = My_Decoder.value;
     
    }
   Serial.print(F(" Value:0x"));
   Serial.println(My_Decoder.value, HEX);
}

Looks like the cleanest way to turn the relay off is by a time-out, as it's impossible to determine if the sender has stopped sending or is simply between individual repeat codes.

Keep a track of the last time a button code or repeat was received, and use a variable to remember the current relay state.

While trying to get things working, I'd suggest just using one relay and one IR code.

Have a look at this...

int state = 0;
unsigned long lastButtonTime = millis();

const timeoutDelay = 500;  // milliseconds - I'm guessing here, this needs to be longer than the IR repeat interval

void loop()
{
  IRcode = getIRCode();

  if ( IRcode == myButtonCode )
  {
    relay(on);
    state = 1;
    lastButtonTime = millis();
  }

  if ( IRcode == repeat && state == 1 )
  {
    lastButtonTime = millis();  // update the time-out
  }

  if ( (millis() - lastButtonTime > timeoutDelay) && state == 1 )
  {
    relay(off);
    state = 0;
  }
}

Hey Martin-X,

I really appreciate your help. I tried the following code. I can get the relay to turn on fine but I still don’t seem to be able to turn it off while letting go of the button on the IR remote. I stays on forever. Here is the code. Thanks again.

#include <IRLib.h>

#define RELAY_ON 0
#define RELAY_OFF 1

#define Relay_4  4    //YELLOW

#define FOUR    0xfd28d7

IRrecv My_Receiver(11);//receiver on pin 11
IRdecode My_Decoder;//Decoder object 

int state = 0;
unsigned long lastButtonTime = millis();
//byte IRcode;

const int timeoutDelay = 500;  // milliseconds - I'm guessing here, this needs to be longer than the IR repeat interval

void setup() {  
  Serial.begin(9600);
  My_Receiver.enableIRIn(); // Start the receiver 
  digitalWrite(Relay_4, RELAY_OFF);   
  pinMode(Relay_4, OUTPUT);
  delay(4000); //Check that all relays are inactive at Reset
}

void loop()
{
    Serial.println(My_Decoder.value, HEX);
    if (My_Receiver.GetResults(&My_Decoder)) {
    My_Decoder.decode();
    if (My_Decoder.decode_type== NEC) {
      Serial.println(My_Decoder.value, HEX);  
      if (My_Decoder.value == FOUR)
      {
        digitalWrite(Relay_4, RELAY_ON);
        state = 1;
        lastButtonTime = millis();
        Serial.print("on");
      }
    
      else if (My_Decoder.value == REPEAT && state == 1)
      {
        lastButtonTime = millis();  // update the time-out
      }
    
      else if ((millis() - lastButtonTime > timeoutDelay) && state == 1)
      {
        digitalWrite(Relay_4, RELAY_OFF);
        Serial.print("off");
        state = 0;
      }
    }
      My_Receiver.resume(); //Restart the receiver
  }   
}

The section of code that turns the relay off was embedded in the first if statement, so I’ve moved it to the last section of the loop(). I can’t compile to check I’ve not fluffed up because I don’t have your IR library, so give this a try…

#include <IRLib.h>

#define RELAY_ON 0
#define RELAY_OFF 1

#define Relay_4  4    //YELLOW

#define FOUR    0xfd28d7

IRrecv My_Receiver(11);//receiver on pin 11
IRdecode My_Decoder;//Decoder object

int state = 0;
unsigned long lastButtonTime = millis();
//byte IRcode;

const int timeoutDelay = 500;  // milliseconds - I'm guessing here, this needs to be longer than the IR repeat interval

void setup() {
  Serial.begin(9600);
  My_Receiver.enableIRIn(); // Start the receiver
  digitalWrite(Relay_4, RELAY_OFF);
  pinMode(Relay_4, OUTPUT);
  delay(4000); //Check that all relays are inactive at Reset
}

void loop()
{
  Serial.println(My_Decoder.value, HEX);
  if (My_Receiver.GetResults(&My_Decoder)) {
    My_Decoder.decode();
    if (My_Decoder.decode_type == NEC) {
      Serial.println(My_Decoder.value, HEX);
      if (My_Decoder.value == FOUR)
      {
        digitalWrite(Relay_4, RELAY_ON);
        state = 1;
        lastButtonTime = millis();
        Serial.print("on");
      }

      else if (My_Decoder.value == REPEAT && state == 1)
      {
        lastButtonTime = millis();  // update the time-out
      }

    }
    My_Receiver.resume(); //Restart the receiver
  }

// Turn off relay...

  if ((millis() - lastButtonTime > timeoutDelay) && state == 1)
  {
    digitalWrite(Relay_4, RELAY_OFF);
    Serial.print("off");
    state = 0;
  }
}

WOW!! Holy ^%%^!! That works mate! Thanks a million! :) :) :) :) :) You are absolutely awesome. Not only did I struggled with this code for weeks but I learned from it and from you. Can't stress enough how much I appreciate your help. Take care Martin-X.

I REALLY need help. I have been working all day trying to figure out how to stop moving a servo when I let go of the button on the IR remote and I just can't understand your code.

This is the forum I created for it. I use a different IR library and therefore cannot figure it out.

https://forum.arduino.cc/index.php?topic=405268.0

I thought this will be handy

Hi,

I am trying to do something similar, but by controlling a small continuous servo motor. I have the code working to move the servo a set distance (actually it moves for a set amount of time). I can also continuously input from the remote with the same “FFFFF” output.

My plan would be to set the time very low and hold the button to make it move, rather than it moving a longer distance when being pressed once.
Thanks for the help!

Here is my current code:

/* Example program for from IRLib – an Arduino library for infrared encoding and decoding
 * Version 1.3   January 2014
 * Copyright 2014 by Chris Young http://cyborg5.com
 * "IRservo" Control a servo using an IR remote
 */
#include <IRLib.h>
#include <Servo.h> 
/* Note: Servo library uses TIMER1. The default timer for IRLib on Arduino Uno
 * is TIMER2 so there is no conflict. However a default timer on Arduino Leonardo
 * is TIMER1 so you will have to modify the timer used to use TIMER3 or TIMER4
 * as specified in IRLibTimer.h. Also you will need to modify the input being used.
 */
// You will have to set these values depending on the protocol
// and remote codes that you are using. 
#define MY_PROTOCOL NEC
#define RIGHT_ARROW   0x10EF807F //Move several clockwise
#define LEFT_ARROW    0x10EF10EF //Move servo counterclockwise
#define SELECT_BUTTON 0x10EF20DF //
#define UP_ARROW      0x10EFA05F //
#define DOWN_ARROW    0x10EF00FF //
#define BUTTON_A 0x10EFF807   //
#define BUTTON_B 0x10EF7887  // 
#define BUTTON_C 0x10EF58A7

IRrecv My_Receiver(11);//Receive on pin 11
IRdecode My_Decoder; 
Servo My_Servo;  // create servo object to control a servo 
int pos=90;         // variable to store the servo position 
// int Speed=0;       // Number of degrees to move each time a left/right button is pressed
 
void setup() 
{ 
  Serial.begin(9600); Serial.println("On.");
  My_Receiver.No_Output();//Turn off any unused IR LED output circuit
  My_Servo.attach(9);  // attaches the servo on pin 9 to the servo object 
  My_Servo.write(pos); // Set initial position
  My_Receiver.enableIRIn(); // Start the receiver
} 
 
void loop() 
{ 
    if (My_Receiver.GetResults(&My_Decoder)) {
       My_Decoder.decode();
       if(My_Decoder.decode_type==MY_PROTOCOL) {
          switch(My_Decoder.value) {
            case LEFT_ARROW:    arrowtalk_left(); break;
            case RIGHT_ARROW:   arrowtalk_right(); break;
          }
//        My_Servo.write(pos); // tell servo to go to position in variable 'pos' 
       }
     My_Receiver.resume();
    }
}

void arrowtalk_left()
{
  Serial.println("Left.");
  pos = 60;
  My_Servo.write(pos);
  delay(100);
  pos = 90;
  My_Servo.write(pos);
}

void arrowtalk_right()
{
  Serial.println("Right.");
  pos = 120;
  My_Servo.write(pos);
  delay(150);
  pos = 90;
  My_Servo.write(pos);
}

I want set IR remote as when I press one button for 3 times then it should get select it configure it to that button, need help please help. thanks in advance.

vp

Please see my response to a similar issue here:

Original thread:

my post (if you don’t want to leave the page):

kaakrc:
Hi guys! My answer may help you.

xxx Quick summary:

It’s a timing problem. You NEED to give the code/arduino time to read the ir sensor. If you do, it will/should read the correct value every time.

I’m new to actually posting onto the forums so I hope the code copies correctly…

I was using the WS2812b led strip like user Josh780. I was having a similar issue of the IR sensor reading a different value EVERY single time I pushed the button. But then when I would use a default example code it would work perfectly and always read the correct value (e.g. serial print exactly what values the IR sensor reads). I thought it would store read values in some sort of buffer but it doesn’t look that way.

I am working on a project with addressable LEDs (WS2812) that will pulse or move to the music (I know, super unoriginal) and I wanted a way to “switch” led modes and I didn’t like just having a single pushbutton. Ironically that had similar timing issues that cause problems with the leds.

xxx My solution:

Unless you can live with a 500 ms delay in your loop (most of us CAN’T) then the next logical step is to take a HUGE pause, but only when you need to. So whenever the IR sensor reads something, we pause and wait for a REAL reading. The first reading doesn’t matter, it’s garbage anyway, or you wouldn’t be reading this post.

Then we have a while loop that will wait for either:
a) a new reading or b) 1 second has passed
then we will do whatever using a switch case based on what the IR sensor reads.

I know this solution won’t work for everyone, but it feels DARN NICE to be able to press a special button and ACTUALLY have it do what I meant. The drawback is I have to press the button twice. On the bright side, it feels more like a real consumer product because the leds pause and wait 1 second for input. It just feels like it was meant to be this way.

Let me know if this helps!

my code:

if (irrecv.decode(&results)) {

quick_flash();
  irrecv.resume(); // Receive the next value
 
  t_wait = millis();
 
  while (!irrecv.decode(&results) && millis()-t_wait<1000)
  {
    //wait for new input up to 1 second
  }
 
  Serial.println(results.value, HEX); //debug

switch (results.value)
  {
    case 0xFF6897: //0
      led_mode = 0; // all off
      break;

case 0xFFA25D: //POWER BUTTON
      led_mode = 0; // all off
      break;

case 0xFF30CF: //1
      led_mode = 1;
      break;

case 0xFF18E7: //2
      led_mode = 2;
      break;

case 0xFF7A85: //3
      led_mode = 3;
      break;

case 0xFF10EF: //4
      led_mode = 4;
      break;

case 0xFFFFFFFF: //REPEAT
      break;

}
  delay(200); // so we don’t read the same button press again
  irrecv.resume(); // Receive the next value
}