converting button code into IRrecv code...

I'm trying to adapt some code I found to allow for a button to do two different things (long press -> one action, short press -> different action). Eventually this will get used to emulate a keyboard so that the IR signal for one button can trigger two different key strokes. Below is the code I'm working with and I can't seem to get it to start the long press clock. I will admit that I'm trying to debug it with the serial monitor only without the LEDs rigged up on the board. Can someone point me towards where the issue is here? My remote sends the SAME code when the button is held down.

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 3

#define IR_CODE_ENTER 0x815710EF //Select

boolean LED1State = false;
boolean LED2State = false;

long buttonTimer = 0;
long longPressTime = 500;

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  // removed for IR adaptation - pinMode(button, INPUT);

}

void loop() {
if(irrecv.decode(&results)) {
  //Serial.println(results.value, HEX); //used to verify IR sensor is working correctly and receiving codes
  if ((&results) == IR_CODE_ENTER) {

    if (buttonActive == false) {

      buttonActive = true;
      buttonTimer = millis();
      Serial.println(buttonTimer);
      
    }

    if ((millis() - buttonTimer > longPressTime) && (longPressActive == false)) {

      longPressActive = true;
      LED1State = !LED1State;
      digitalWrite(LED1, LED1State);
      Serial.println("Led 1 switched");

    }

  } else {

    if (buttonActive == true) {

      if (longPressActive == true) {

        longPressActive = false;

      } else {

        LED2State = !LED2State;
        digitalWrite(LED2, LED2State);
        Serial.println("Led 2 switched");
        

      }

      buttonActive = false;

    }

  }

}
irrecv.resume();
}

I am COMPLETELY new to programming and have only had luck slightly modifying code that is posted publicly that mimics what I want to do. Thanks for your help.

NOTE: Here is the working code for the keyboard emulator - I would like to insert the working long press option into this at some point (removing the switch cases for the ones that will be used for long press).

/*
   Sketch allows you to control a Pro Micro (Arduino leonardo compatable) with an IR remote.
   The sketch reads IR codes from the remote and outputs them as keyboard presses.
  
   Change the #define section below to match your remote controls IR codes.
   Change the Keyboard.print and Keyboard.press statements to match your player controls.
  
   based on code by Rick Osgood
   edited by Alex Waugh for Raspberry Pi Car computer running OpenAuto by Bluewavestudios.io 
*/

#include <IRremote.h>
#include <Keyboard.h>

int RECV_PIN = 21;  // IR Receiver connected to pin 21

// Change these to fit your IR remote codes
#define IR_CODE_HAT_UP 0x815700FF //Navigate up
#define IR_CODE_HAT_DOWN 0x8157C03F //Navigate down
#define IR_CODE_HAT_RT 0x815740BF //Navigate right
#define IR_CODE_HAT_LT 0x8157807F //Navigate left
#define IR_CODE_ZOOM_DOWN 0x8157D02F //scroll left
#define IR_CODE_ZOOM_UP 0x815750AF //Scroll right
#define IR_CODE_ENTER 0x815710EF //Select
#define IR_CODE_RET 0x8157906F //Back
#define IR_CODE_MENU 0x815730CF //Home
#define IR_CODE_VOICE 0x8157708F //Voice command in Android Auto
#define IR_CODE_POS 0x8157B04F //Bring OpenAuto Pro to front


IRrecv irrecv(RECV_PIN);
decode_results results;

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

void loop() {
  Keyboard.begin();  //begin keyboard
  if (irrecv.decode(&results)) {
    //Serial.println(results.value, HEX);
    switch(results.value) {
     case IR_CODE_HAT_UP:
       Keyboard.write(KEY_UP_ARROW);
       break;
     case IR_CODE_HAT_DOWN:
       Keyboard.write(KEY_DOWN_ARROW);
       break;
     case IR_CODE_HAT_RT:
       Keyboard.write(KEY_RIGHT_ARROW);
       break;
     case IR_CODE_HAT_LT:
       Keyboard.write(KEY_LEFT_ARROW);
       break;
     case IR_CODE_ZOOM_DOWN:
       Keyboard.write('1');
       break;
     case IR_CODE_ZOOM_UP:
       Keyboard.write('2');
       break;
     case IR_CODE_ENTER:
       Keyboard.write(KEY_RETURN);
       break;
     case IR_CODE_RET:
       Keyboard.write(KEY_ESC);
       break;
     case IR_CODE_MENU:
       Keyboard.write('H');
       break;
     case IR_CODE_VOICE:
       Keyboard.write('M');
       break;
     case IR_CODE_POS:
       Keyboard.write(KEY_F12);
       break;
     }
    delay(100); //Debouncing the IR receive signal
    irrecv.resume(); // Receive the next value
  }
  Keyboard.end();  //stops keybaord
}

You need to remember what was the last IR code received. Then the first thing you do when you get a new IR code is compare it to that. If it matches, then do nothing - the timer will keep timing. If it doesn't match then you've got a new button and you should start the timer again.

I think I understand... set a boolean for last code and then start the timer when last code equals new code (in other words... when is the same code being sent again and star the timer then)?

I found a TON of syntax errors after I started to dig into things a bit more. I wanted to just see if I could get the serial port to react at the right time so I cut out all of the other bits of the code and just wrote this:

#include <IRremote.h>

int RECV_PIN = 8;  // IR Receiver connected to pin 3

#define IR_CODE_ENTER 0x815710EF //Select

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {
  // put your setup code here, to run once:
 Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver
  
}


  // put your main code here, to run repeatedly:
void loop() {
if(irrecv.decode(&results)) {
 
  if (results.value == IR_CODE_ENTER) {
     Serial.println("select");

        
    }
  irrecv.resume();
}
}

That worked to purt "select" reliably when I pressed the button and told me I at least had the IR working correctly.

I will see about setting a "last code" boolean and see if I can get that to work.

Okay... I've been crashing through this and here's what I have:

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 3

#define IR_CODE_ENTER 0x815710EF //Select

boolean LED1State = false;
boolean LED2State = false;

long lastCode;
long buttonTimer = 0;
long longPressTime = 5;

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver
  // removed for serial debugging - pinMode(LED1, OUTPUT);
  // removed for serial debugging - pinMode(LED2, OUTPUT);
  // removed for IR adaptation - pinMode(button, INPUT);

}

void loop() {

if(irrecv.decode(&results)) {
 
  if (results.value == IR_CODE_ENTER) {
     // Serial.println("select");
        lastCode = results.value; {
        
        if (buttonActive == false) { 
             buttonActive = true;
                          
              {
                 
               while (results.value == lastCode) {
                buttonTimer = buttonTimer +1;
                Serial.print(" ");
                Serial.println(buttonTimer);
                }
      
        }
        }
    if ((buttonTimer >= longPressTime) && (longPressActive == false)) {

      longPressActive = true;
      Serial.println("long press");

    }
    }

  } else {

    if (buttonActive == true) {

      if (longPressActive == true) {

        longPressActive = false;

      } else {

        Serial.println("short press");
        

      }

      buttonActive = false;

    }

  }

irrecv.resume();
}
buttonTimer = 0;
}

The problem with above is that it keeps counting up the codes... a never ending loop of upward counting.

So what I think I have determined is that I need to count these signals as they come through rather than count the milliseconds. The trouble now, is that I need a way for the loop to know that no new signal is come in. ie: some sort of delay resolution if it doesn't see a matched signal then move on. If matched signal, keep counting.

Any ideas?

The problem with above is that it keeps counting up the codes... a never ending loop of upward counting.

This portion of code

            while (results.value == lastCode)
            {
              buttonTimer = buttonTimer + 1;
              Serial.print(" ");
              Serial.println(buttonTimer);
            }

Blocks the free running of the loop() function which is bad practice even if it worked but it will never end because the results.value will never change within it. So the first thing to do is to read results.value within the while loop.

There may be other problems but start with that

When you hold a button down on a remote, the remote usually does NOT send the same code that it sends when you first press the button.

Instead, it sends a "repeat the last action" value.

That will change how you detect a long press.

@UkHeliBob thanks for the input. I've found that out, but was doing some hail Marry stabs at something to see if I could understand what was going on and how things worked. I've established that what I need to do is compare the results.value to a static timer function that is longer than the repeat send of the remote. This will allow me to find out if the button has been released. I have stumbled upon some code that illustrates that, so I plan to see if I can get that figured out and I'll post back up here.

@PaulS yes, typically IR remotes send the FFFF code or something like it, however, this one resends the same code infinitum. Thus, I don't have to set an if case for the repeat code.

Thanks for the input everyone. If anyone else has a suggestion, let me know.

  if (results.value == IR_CODE_ENTER) {

lastCode = results.value; {

Two problems:

  1. The extra {

  2. You just overwrote the lastCode. Code below this which checks if the value equals the last is now useless.

Move that down to the very end, at the closing of if(irrecv.decode(&results)) {

So I moved the last code reading out as suggested by MorganS. I also got rid of the larger else if statements to just see if I could reliably get it to register that the button was being held or not being held. The problem is that the millis just keep counting. I had thought I set it to reset the timer after reaching a "long press" case, but it does not seem to be doing that. Here's what I have at the moment:

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 8

#define IR_CODE_ENTER 0x815710EF //Select

const int timeoutDelay = 500;
long lastCode;
long buttonTimer = 0;
long longPressTime = 1000;
unsigned long lastButtonTime = millis();

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver
  // removed for serial debugging - pinMode(LED1, OUTPUT);
  // removed for serial debugging - pinMode(LED2, OUTPUT);
  // removed for IR adaptation - pinMode(button, INPUT);

}

void loop() {

if(irrecv.decode(&results)) {
  lastCode = results.value;
 
  if (results.value == IR_CODE_ENTER) {
                   
          if (buttonActive == false) { 
             buttonActive = true;
          }
          
            if ((results.value == lastCode) && (buttonActive == true)) {
                lastButtonTime = millis();
                buttonTimer = millis();
                Serial.print(" ");
                Serial.println(lastButtonTime);
                
                }
          }
      if ((millis() - lastButtonTime > timeoutDelay) && buttonActive == true )
        buttonActive = false;
        lastButtonTime = 0;
        {
        
        
    if ((buttonTimer > longPressTime) && (longPressActive == false)) {

      longPressActive = true;
      buttonTimer = 0;
      lastButtonTime = 0;
      Serial.println("long press");

    }
   
  }
 

irrecv.resume();

}
}

the "source code" for this idea came from user Martin-X from another post and looks like this:

#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;
  }
}

I feel that I'm close, but can't quite understand how to read where the loop is getting tripped up. If there are suggestions you could provide that I could add as serial prints that would show me on my own where the trip up is happening, I might be able to solve the issue.

Thanks again all for the help.

So I moved the last code reading out as suggested by MorganS.

No, you didn't:

void loop() {

if(irrecv.decode(&results)) {
  lastCode = results.value;

Put EVERY { on a line BY ITSELF.
Put EVERY } on a line BY ITSELF.
Use Tools + Auto Format.

Now that you can see where a block starts and stops, move the lastCode assignment statement to be the last statement in the block of the if(irrecv.decode(&results)) statement.

@Brattain Member I see now what you mean. I took your advice and reformated the code.

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 8

#define IR_CODE_ENTER 0x815710EF //Select

const int timeoutDelay = 175;
long lastCode;
long buttonTimer = 0;
long longPressTime = 1000;
unsigned long lastButtonTime = millis();

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver

}

void loop() {

  if (irrecv.decode(&results))
  {

    if (results.value == IR_CODE_ENTER)
    {
      if (buttonActive == false)
          buttonActive = true;
      }

      if ((results.value == lastCode) && (buttonActive == true))
      {
        lastButtonTime = millis();
        buttonTimer = millis();
        Serial.print(" ");
        Serial.println(buttonTimer);
      }

      if ((millis() - lastButtonTime > timeoutDelay) && buttonActive == true )
      {
        buttonActive = false;        
        lastButtonTime = 0;
        buttonTimer = 0;
        Serial.println("short press");
      }
        {


          if ((buttonTimer > longPressTime) && (longPressActive == false))
          {
            longPressActive = true;
            buttonTimer = 0;
            lastButtonTime = 0;
            Serial.println("long press");
          }

        }


        irrecv.resume();
        lastCode = results.value;
      }
    }

I added a serial print line for the "short press" which now is showing up in the serial monitor. Pressing some other IR button will trigger a short press, but I can only ever get one long press. The issue now is that the milli "clock variables" do not seem to be resetting and thus the milli values carry on forever. Here's what the serial monitor looks like:

short press
 2165
long press
 2569
 3352
short press
short press
 4656
 5178
 5603
 7092
 7308
 7541
short press
short press
 9290
 9925
short press
short press
 11860
 12408
 12516
 12624
 12956
 13065
 13172
 13280
 13388
short press
short press
 15922
 16517
 16624
short press
short press
 18181
short press
short press
 19853
 20111
 20377

I feel like there is an issue as to where I'm clearing the time value at. I'm still learning how these programs flow.

I think I've determined why I never get another long press. The original sample code I had used else if statements to reset the booleans if the case was no longer true. In an effort to simplify the code to help understand what was going on, I removed those else if statements. It thus is never getting the signal to reset the case for long press has happened. I'm working now on how to get those back in there.

I also found that I did not understand that millis is just a running clock. The call to set something like lastButtonTime = millis(); buttonTimer = millis(); just stores the current clock time for use later. I was expecting the numbers in the serial monitor to reset. I'm revisiting these set points and removing the calls to set the milli values to zero.

If anyone else has some suggestions to put in the serial monitor that will help me understand what the program loop is doing for debugging, I'm all ears.

I've played with this some more and still can't seem to get it to perform the way I want it to. I'd can't seem to get it to understand when it doesn't receive a signal from the IR that the button is done and it should go to the else if. I also can't seem to get it to understand that it's repeating properly.

The major change that came about recently is

if ((results.value == lastCode) && (buttonActive == true) && (millis() - lastButtonTime < timeoutDelay))
{
lastButtonTime = millis();

The idea here was that if the code was seen again, and the IR code time out has not been reached, then log the time and repeat that. This then should allow for it to get to the long press function.

Regardless, I'm really flummoxed as to how to make this work. I really could use a bit more in depth help. Thanks again for taking the time. I truly appreciate it.

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 8

#define IR_CODE_ENTER 0x815710EF //Select

const int timeoutDelay = 500;
long lastCode;
long buttonTimer;
long longPressTime = 1000;
unsigned long lastButtonTime = millis();

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver

}

void loop() {

  if (irrecv.decode(&results))
  {

    if (results.value == IR_CODE_ENTER)
    {
      if (buttonActive == false)
      {
        buttonActive = true;
        buttonTimer = millis();
        lastButtonTime = millis();
      }
      if ((results.value == lastCode) && (buttonActive == true) && (millis() - lastButtonTime < timeoutDelay))
        {
          lastButtonTime = millis();
        }
           if ((millis() - buttonTimer > longPressTime) && (longPressActive == false))
          {
            longPressActive = true;
            Serial.println("long press");
          }

        }
    else
    {
      if (buttonActive == true)
      {
        if (longPressActive == true)
        {
          longPressActive = false;

        }
        else
        {
          Serial.println("short press");
        }
        buttonActive = false;
      }
    }
    irrecv.resume();
    lastCode = results.value;
  }
}

@Brattain Member I see now what you mean. I took your advice and reformated the code.

OK, @Newbie.

I have a name, the same as you do...

When you get the code again DON'T reset the timer. Now you have just blown away the record of how long it was pushed.

The problem then is, how to detect the short press since "short" is the absence of any code at all. That sounds like another timer which gets reset on every IR code every time.

If the second timer reaches (say) 3 times the interval between repeated codes, then look at the first one. That will tell you if the key that was just released was held short or long.

PaulS:
OK, @Newbie.

I have a name, the same as you do...

HA!! I'm still learning this forum and thought that was your screen name PaulS. Sorry about that.

MorganS:
When you get the code again DON'T reset the timer. Now you have just blown away the record of how long it was pushed.

The problem then is, how to detect the short press since "short" is the absence of any code at all. That sounds like another timer which gets reset on every IR code every time.

If the second timer reaches (say) 3 times the interval between repeated codes, then look at the first one. That will tell you if the key that was just released was held short or long.

@MorganS - well, that timer reset is there to allow me to verify that the button is pressed (repeat code statement). This code presently works very well to register a long press. The current issue is that it sets the timer regardless if I hold it down or not and so I can press, release, and the press again after the long press timer and it will register a long press. The current problem that I've identified is that the "last code" statement is making it such that I have to press a new button to send a new IR code to get the "long press" statement to work again. I tried messing around with replacing the "lastCode" value with something as follows so that every time the long or short press gets set, the lastCode would be changed (there's another one of these at the short press):

 if ((millis() - buttonTimer > longPressTime) && (longPressActive == false))
          {
            longPressActive = true;
            Serial.println("long press");
            lastCode = 7542;

lastCode = results.value; lives as the last statement in the irreceive.results(@decode) bracket as suggested by others. The problem appears that my "lastCode = 7542" statement appears to be being overwritten as the code loops out of the irreceive.results bracket. I almost feel that I only need this in the long press "if" tree, but I'm unsure where to move it that will not cause a permanent last code loop.

Additionally, I've added (or rather moved):

if ((millis() - lastButtonTime > timeoutDelay))

which is supposed to detect that the button is no longer being pressed and execute the short press part of the code. I think this is what you were suggesting @MorganS with your time stamp idea. Any suggestions for modifying it?

 else
    {
      if (buttonActive == true)
      {
        if (longPressActive == true)
        {
          longPressActive = false;

        }
        else if ((millis() - lastButtonTime > timeoutDelay))
        {
          Serial.println("short press");
        }
        buttonActive = false;
        lastCode = 7542;
      }
    }

So I added some more serial print statements to allow me to better understand where the code is getting stuck. Here's the current code:

#include <IRremote.h>

int LED1 = 12;
int LED2 = 13;
// removed for IR adaptation - int button = 3;
int RECV_PIN = 8;  // IR Receiver connected to pin 8

#define IR_CODE_ENTER 0x815710EF //Select

const int timeoutDelay = 500;
long lastCode;
long buttonTimer;
long longPressTime = 750;
unsigned long lastButtonTime = millis();

boolean buttonActive = false;
boolean longPressActive = false;

IRrecv irrecv(RECV_PIN);
decode_results results;

void setup() {

  Serial.begin (9600); //Serial.begin(9600)
  irrecv.enableIRIn(); // Start the receiver

}

void loop() {

  if (irrecv.decode(&results))
  {

    if (results.value == IR_CODE_ENTER)
    {
      if (buttonActive == false)
      {
        buttonActive = true;
        Serial.println("ButtonActive = true");
        lastButtonTime = millis();
        buttonTimer = millis();
      }
      if ((results.value == lastCode) && (buttonActive == true) && (millis() - lastButtonTime < timeoutDelay))
      {
        lastButtonTime = millis();
        lastCode = results.value;
      }
      if ((millis() - buttonTimer > longPressTime) && (longPressActive == false))
      {
        longPressActive = true;
        Serial.println("long press");
        lastCode = 1234;
        buttonTimer = 0;
        lastButtonTime = 0;

      }

    }
    else
    {
      if (buttonActive == true)
      {
        if (longPressActive == true)
        {
          longPressActive = false;
          Serial.println("longPressActive = false");

        }
        else if ((millis() - lastButtonTime > timeoutDelay))
        {
          Serial.println("short press");
        }
        buttonActive = false;
        Serial.println("buttonActive = false");
        lastCode = 7542;
      }
    }
    irrecv.resume();

  }
}

With my added serial prints, I have found that the code gets hung up at:

if ((results.value == lastCode) && (buttonActive == true) && (millis() - lastButtonTime < timeoutDelay))

If I press a different button, I will get it to jump out of the loop and it will jump through here:

else
    {
      if (buttonActive == true)
      {
        if (longPressActive == true)
        {
          longPressActive = false;
          Serial.println("longPressActive = false");

@MorganS is onto something where we need it to time out and that needs to be higher in the tree, but I'm not sure how to implement that. I'll see if I can try something, but if anyone has a suggestion on what that would look like, I'd be happy to entertain suggestions. I'm headed off to the day job, but will be thinking about this problem all day and looking forward to trying again this evening. Thanks all for the help.

It seems the hang up is that I can't get the code to time out. The loop is set to wait for the receiver to receive another code that is equal to IR_CODE_ENTER. I don't know how to get it to kick out of that source and register a short press. I'm quite frustrated :expressionless:

Does anyone have a clue? I've not come up with any better code to post than my last code I posted above.

Also, I keep coming back to this line of code that was used to clear a relay that was controlled by a remote control.

if ((millis() - lastButtonTime > timeoutDelay)

  • (see below for implementation)
#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;
  }
}

they used a static time out delay value that would measure the time from the last button press and then shut 'er down if no new codes came in. The delay was just a few ticks over the repeat interval in millis. The problem I seem to have is that I can't get it to go to this bit of code and trigger all the remaining booleans that will cause the loop to work and be ready for the next IR Receive.

Let me know if you have anything to try. I'll keep thinking and researching and post anything I come up with.