Using IR Remotes in Place of Mechanical Switches

Using IR Remotes in Place of Mechanical Switches

  • Physical switches and pushbuttons are commonly used to control devices and
    influence program flow. Each physical switch requires its own GPIO input;
    for example, five switches consume five Arduino input pins.
    In addition, mechanical switches are prone to contact bounce and are best
    handled by detecting "state changes" rather than continuously reading their
    current state.

  • An IR remote provides a flexible alternative to hard-wired switches.
    For example, a 44-button IR remote requires only a single Arduino input pin.
    To provide user feedback, a piezo speaker may be added to emit a short beep
    whenever a command is received.

  • The following discussion explains how we can use an IR remote in place of physical switches in our projects.




  • Below is a schematic of the circuit we will use in this project.

  • Before we get into the required software, we will make a keypad overlay.

  • We will use an NEC IR remote for our keypad.
    3 versions are referred to herein.

  • We use a drawing program to create a 1 to 1 inch keypad overlay.

You can print this PDF at actual size to make the 44 button overlay discussed.

44 Button Overlay.pdf (76.4 KB)




  • The following sketch is written as a reference tutorial.
    This tutorial demonstrates how to use an inexpensive IR remote control
    as a flexible replacement for multiple physical buttons or switches
    using an Arduino UNO and the IRremote library.
//
//================================================^================================================
//                               IR_Remote_44_Tutorial_TinyIRReceiver
//================================================^================================================
//
//  https://forum.arduino.cc/t/using-ir-remotes-in-place-of-mechanical-switches/1425851
//
//  LarryD
//
//  Version    YY/MM/DD    Comments
//  =======    ========    ========================================================================
//  1.00       25/09/14    Running code
//  1.01       25/11/14    Added SHORT and LONG press detection.
//  1.02       26/01/18    Added the suggestion in Post #2 @J-M-L from the above URL.
//  1.03       26/01/22    Fixed some minor bugs.
//
//  R E F E R E N C E   T U T O R I A L :
//  This tutorial demonstrates how to use an inexpensive IR remote control
//  as a flexible replacement for multiple physical buttons or switches
//  using an Arduino UNO and the IRremote library.
//
//  Instead of consuming one GPIO pin per switch/button, an IR remote allows
//  dozens of “virtual buttons” to be handled through a single input pin.
//  This sketch shows a clean, scalable way to:
//     - receive IR commands using interrupts
//     - distinguish between short and long button presses
//     - map raw IR codes to readable button symbols
//     - cleanly separate hardware handling from application logic
//
//  This sketch is intended both as a learning example and as a
//  drop-in foundation for your own projects.
//
//  T A R G E T   A U D I E N C E :
//     - Arduino users who are comfortable with:
//       * digitalRead(), digitalWrite(), and analogWrite(), Arrays
//       * using the Serial Monitor
//       * how millis() can be used to make a non-blocking TIMER
//     - No prior interrupt experience is required.
//       (Interrupts are used internally and explained conceptually)
//     - Tested on Arduino UNO (ATmega328P).
//
//  Q U I C K   S T A R T :
//     1. Install "Arduino-IRremote" library (Version 4.5 or later).
//     2. Wire all components as per accompanying schematic.
//        IRremoteTutorial_WorkSheet.jpg
//     3. Select your NEC remote by uncommenting ONE define:
//        #define Remote17
//     4. Upload sketch and open Serial Monitor at 115200 baud.
//
//  E x a m p l e :
//     When a remote button is pressed:
//     1. The IR receiver detects the signal.
//     2. An interrupt is triggered immediately.
//     3. The Interrupt Service Routine (ISR) decodes the command
//        and stores it in a shared data structure.
//     4. A flag is set to indicate that a new command is available.
//     5. The main loop() checks for this flag.
//     6. The button press is classified as either SHORT or LONG.
//     7. The command is processed and the appropriate action occurs
//        (LEDs change, PWM adjusts, variables update, program flow is influenced).
//
//  Conceptual diagram:
//     IR Remote
//         ↓
//     TSOP4838
//         ↓  (interrupt)
//     TinyIRReceiver ISR
//         ↓
//     Flags + Command
//         ↓
//     loop()
//         ↓
//     serviceButtonPress()
//         ↓
//     processButtonPress()
//
//  N E X T   S T E P S :
//     - Add more remote buttons and map them to new features.
//     - Replace LEDs with relays, motors, or displays.
//     - Integrate this sketch into an existing project.
//     - Add menus, modes, or configuration screens.
//
//  P R E A M B L E :
//  Physical switches and pushbuttons are commonly used to control devices and
//  influence program flow. Each physical switch requires its own GPIO input;
//  for example, five switches consume five Arduino input pins.
//  In addition, mechanical switches are prone to contact bounce and are best
//  handled by detecting "state changes" rather than continuously reading their
//  current state.
//
//  An IR remote provides a flexible alternative to hard-wired switches.
//  For example, a 44-button IR remote requires only a single Arduino input pin.
//  To provide user feedback, a piezo speaker may be added to emit a short beep
//  whenever a command is received.
//
//  Each remote IR button press sends a HEX command to the Arduino.
//  We translate that HEX code into a human-readable "button"
//  so the rest of the sketch never deals with raw IR values.
//
//  All remote button presses are decoded by a single IR handler, greatly
//  simplifying the sketch logic. A simple switch/case construct can then be
//  used to control program behavior.
//
//  Hardware construction is also simplified: only one opening is required in
//  the enclosure for the IR receiver. This reduces internal wiring, allows for
//  a smaller enclosure, improves execution efficiency, and makes it easy to
//  add new “virtual switches.” The IR remote also enables remote operation
//  (for example, from 10 feet away).
//
//  With minor modifications, this sketch can be used as drop-in code for existing projects.
//
//  O P E R A T I O N :
//  "Using the Arduino UNO"
//  A TSOP4838 IR receiver is connected to GPIO pin 2 (IR_RECEIVE_PIN).
//  IR receive commands are demodulated by this module.
//  Any controller GPIO pin can be connected to the IR module output.
//  The module has a built-in pull-up resistor.
//  A Piezo speaker is connected to GPIO pin 7.
//  A short 3400Hz burst is generated when a new IR command is received.
//  A heartbeat LED, connected to GPIO pin 13, toggles every 500 ms.
//  It is also used by the IR library for IR feedback.
//
//  Button mappings:
//     - Button '1'  Increases PWM duty cycle on LED1 (PWM pin 9);
//                   supports short and long presses.
//     - Button '2'  Decreases PWM duty cycle on LED1 (PWM pin 9);
//                   supports short and long presses.
//     - Button '3'  Toggles LED2 on GPIO pin 8;
//                   supports short presses.
//     - Up Arrow    Menu navigation; short and long press supported.
//     - Down Arrow  Menu navigation; short and long press supported.
//     - Left/Right  Handled similarly to the Up and Down arrows.
//
//  N O T E S :
//  The TinyIRReceiver.hpp is used in this example. It is part of the "IRremote library".
//  Install the latest version, as of this writing, version 4.5 is available.
//  https://github.com/Arduino-IRremote/Arduino-IRremote/tree/master
//  To install this library, use "Include Libraries/Manage Libraries" under the "Sketch" tab (IDE 1.8.18).
//  TinyIRReceiver.hpp is being used as it has a small footprint.
//  This library is designed for NEC remotes. It is based on interrupts not timers, fully compatible with
//  tone() and analogWrite()
//
//  Selected library functions and flags:
//     - initPCIInterruptForTinyReceiver()
//       Initializes and enables interrupt generation on IR input changes.
//     - disablePCIInterruptForTinyReceiver()
//       Disables interrupt generation on IR input changes.
//     - enablePCIInterruptForTinyReceiver()
//       Re-enables interrupt generation on IR input changes.
//     - TinyIRReceiverData.justWritten
//       TRUE indicates a newly decoded IR command.
//       Set this flag to FALSE immediately after processing.
//     - TinyIRReceiverData.Command
//       Holds the received IR command.
//     - TinyIRReceiverData.Flags
//       Contains status flags for the received command.
//       IRDATA_FLAGS_IS_REPEAT indicates a repeat frame.
//
//  R E F E R E N C E :
//  Tiny NEC receiver and sender:
//  https://github.com/Arduino-IRremote/Arduino-IRremote?tab=readme-ov-file#tiny-nec-receiver-and-sender
//
//  TSOP4838 IR receiver:
//  https://www.vishay.com/docs/82459/tsop48.pdf
//

//================================================^================================================
//Leave these "defines" at this location. i.e. before line: #include "TinyIRReceiver.hpp"
//
//#define IR_FEEDBACK_LED_PIN            13  //Defaults to 13.
//#define NO_LED_FEEDBACK_CODE               //Disables LED feedback.
#define IR_RECEIVE_PIN                   2   //Defaults to 2.
#define USE_CALLBACK_FOR_TINY_RECEIVER       //Allow the user ISR callback function to execute.

//==================================
#include <TinyIRReceiver.hpp>                //For use with NEC IR remotes.
#include <avr/pgmspace.h>

//================================================^================================================
//Four (4) NEC IR keypad configurations are defined for use.
//Select which IR keypad we are using; these arrays are stored in flash memory.
//
//===================
//
//#define Portrait44
//#define Landscape44
#define Remote17
//#define Remote21

#if !defined(Portrait44) && !defined(Landscape44) && !defined(Remote17) && !defined(Remote21)
#error "No IR remote type selected"
#endif

//Button Mapping Tables
//Each row contains:
//   [0] = raw IR command code (hex value sent by the remote)
//   [1] = a readable symbol (ASCII character assigned by the user)
//
//Example:
//   {0x5C, '1'}  → when IR code 0x5C is received, treat it like button '1'
//
//PROGMEM stores these tables in FLASH memory instead of RAM,
//which is important on small boards like the Arduino UNO.

//==================================
//Portrait keypad definition for a 44 button NEC IR remote.
#if defined(Portrait44)
const byte buttonCode[][2] PROGMEM =
{
  {0x5C, '1' }, {0x5D, '2' }, {0x41, '3' }, {0x40, '4' },
  {0x58, '5' }, {0x59, '6' }, {0x45, '7' }, {0x44, '8' },
  {0x54, '9' }, {0x55, '0' }, {0x49, '-' }, {0x48, 0x20},
  {0x50, 'A' }, {0x51, 'B' }, {0x4D, 'C' }, {0x4C, 'D' },
  {0x1C, 'E' }, {0x1D, 'F' }, {0x1E, 'G' }, {0x1F, 'H' },
  {0x18, 'I' }, {0x19, 'J' }, {0x1A, 'K' }, {0x1B, 'L' },
  {0x14, 'M' }, {0x15, 'N' }, {0x16, 'O' }, {0x17, 'P' },
  {0x10, 'Q' }, {0x11, 'R' }, {0x12, 'S' }, {0x13, 'T' },
  {0x0C, 'U' }, {0x0D, 'V' }, {0x0E, 'W' }, {0x0F, 'X' },
  {0x08, 'Y' }, {0x09, 'Z' }, {0x0A, '^' }, {0x0B, 'v' },
  {0x04, 0xFC}, {0x05, 0x0A}, {0x06, '<' }, {0x07, '>' }
};

//==================================
//Landscape keypad definition for a 44 button NEC IR remote.
#elif defined(Landscape44)
const byte buttonCode[][2] PROGMEM =
{
  {0x04, '1' }, {0x08, '2' }, {0x0C, '3' }, {0x10, '4' }, {0x14, '5' }, {0x18, '6' }, {0x1C, '7' }, {0x50, '8' }, {0x54, '9' }, {0x58, '0' }, {0x5C, '-' },
  {0x05, 'Q' }, {0x09, 'W' }, {0x0D, 'E' }, {0x11, 'R' }, {0x15, 'T' }, {0x19, 'Y' }, {0x1D, 'U' }, {0x51, 'I' }, {0x55, 'O' }, {0x59, 'P' }, {0x5D, 0x0A},
  {0x06, 0xFC}, {0x0A, 'A' }, {0x0E, 'S' }, {0x12, 'D' }, {0x16, 'F' }, {0x1A, 'G' }, {0x1E, 'H' }, {0x4D, 'J' }, {0x49, 'K' }, {0x45, 'L' }, {0x41, '^' },
  {0x07, 'Z'} , {0x0B, 'X' }, {0x0F, 'C' }, {0x13, 'V' }, {0x17, 'B' }, {0x1B, 0X20}, {0x1F, 'N' }, {0x4C, 'M' }, {0x48, '<' }, {0x44, '>' }, {0x40, 'v' }
};

//==================================
//Keypad definition for a 17 button NEC IR remote.
#elif defined(Remote17)

const byte buttonCode[][2] PROGMEM =
{
  //           Index KeyName
  {0x46, '^' }, //0  ^
  {0x44, '<' }, //1  <
  {0x40, 0x0A}, //2  OK
  {0x43, '>' }, //3  >
  {0x15, 'v' }, //4  v
  {0x16, '1' }, //5  1
  {0x19, '2' }, //6  2
  {0x0D, '3' }, //7  3
  {0x0C, '4' }, //8  4
  {0x18, '5' }, //9  5
  {0x5E, '6' }, //10 6
  {0x08, '7' }, //11 7
  {0x1C, '8' }, //12 8
  {0x5A, '9' }, //13 9
  {0x42, '*' }, //14 *
  {0x52, '0' }, //15 0
  {0x4A, '#' }  //16 #
};

//==================================
//Keypad definition for a 21 button NEC IR remote.
#elif defined(Remote21)

const byte buttonCode[][2] PROGMEM =
{
  //           Index KeyName
  {0x45, '-' }, //0  CH-
  {0x46, '%' }, //1  CH
  {0x47, '+' }, //2  CH+
  {0x44, '<' }, //3  PRE
  {0x40, '>' }, //4  NEXT
  {0x43, '$' }, //5  Play
  {0x07, '!' }, //6  Vol+
  {0x15, '"' }, //7  Vol-
  {0x09, '=' }, //8  EQ
  {0x16, '0' }, //9  0
  {0x19, '*' }, //10 100+
  {0x0D, '#' }, //11 200+
  {0x0C, '1' }, //12 1
  {0x18, '2' }, //13 2
  {0x5E, '3' }, //14 3
  {0x08, '4' }, //15 4
  {0x1C, '5' }, //16 5
  {0x5A, '6' }, //17 6
  {0x42, '7' }, //18 7
  {0x52, '8' }, //19 8
  {0x4A, '9' }  //20 9
};

#endif


//                              G P I O s   A n d   V a r i a b l e s
//================================================^================================================
//
//Macros
//================================================
//
#define LEDon                             HIGH
#define LEDoff                            LOW

#define ENABLED                           true
#define DISABLED                          false

//Analogs
//================================================
//

//INPUTS
//================================================
//
//Defined above IR_RECEIVE_PIN            2         //Connected to the IR receiver, you can use any GPIO pin.

//OUTPUTS
//================================================
//
const byte tonePin                      = 7;        //Pin7---[220R]---[PiezoSpeaker]---GND
const byte testLED2                     = 8;        //Pin8---[220R]---A[LED]K---GND
const byte testLED1                     = 9;        //PWM_Pin9---[220R]---A[LED]K---GND
const byte heartbeatLED                 = 13;       //Toggles every 500ms. Also flashes when an IR signal is detected.

//ISR VARIABLES
//================================================
//These variables are shared between:
// - the interrupt routine (ISR)
// - the main loop()
//
//'volatile' tells the compiler that these values can change at ANY time,
//so it must always read them directly from memory.
//
volatile bool irCommandReady            = false;
volatile byte irCommand                 = 0;

enum COMMAND_TYPES {NEW_COMMAND, REPEAT};
volatile COMMAND_TYPES commandType = NEW_COMMAND;

//VARIABLES
//================================================
//
const byte arraySize                    = sizeof(buttonCode) / sizeof(buttonCode[0]);
int pwmLevelLED1                        = 0;
byte rateChange                         = 5;        //The PWM duty cycle "step change" for each button press.
byte lastCommand                        = 0;

//Timing stuff.
//========================
unsigned long heartbeatTime             = 0;
const unsigned long heartbeatInterval   = 500ul;

//Long-press definitions.
//========================
enum PRESS_TYPE {PRESS_SHORT, PRESS_LONG};

//"false", we are in the short press state. "true", we are in the long press state.
bool longPressActive                    = false;

const unsigned long longPressInterval   = 500ul;    //The time when we enter the long press state.
const unsigned long repeatInterval      = 150ul;    //Auto-repeat time. When we "again" proceed to the application code.

//Button press TIMER variables.
unsigned long pressStartTime            = 0;
unsigned long lastRepeatTime            = 0;


//                                           s e t u p ( )
//================================================^================================================
//
void setup()
{
  Serial.begin(115200);
  //Serial.println(F("START " __FILE__ " from " __DATE__ "\r\n Using library version " VERSION_TINYIR));

  pinMode(testLED1, OUTPUT);
  pinMode(testLED2, OUTPUT);
  pinMode(tonePin, OUTPUT);
  pinMode(heartbeatLED, OUTPUT);

  //This single function call does ALL of the following:
  //1) Configures the IR receive pin.
  //2) Enables pin-change interrupts.
  //3) Starts listening for NEC IR commands.
  //
  //When a command is detected, the library automatically calls
  //handleReceivedTinyIRData() (our ISR callback).
  //
  initPCIInterruptForTinyReceiver();

} //END of   setup()


//                                            l o o p ( )
//================================================^================================================
//
void loop()
{
  //========================================================================  T I M E R  heartbeatLED
  //NOTE: This LED is "dual purpose".
  //Is it time to toggle the heartbeat LED ?
  //TinyIRReceiver.hpp also uses this LED for IR RX FEEDBACK.
  if (millis() - heartbeatTime >= heartbeatInterval)
  {
    //Restart this TIMER.
    heartbeatTime = millis();

    //Toggle the heartbeat LED.
    digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
  }

  //======================================================================== Process new IR received commands
  //Did we receive a new IR remote command ?
  bool tempFlag;
  COMMAND_TYPES _commandType;
  byte _irCommand;

  //Atomic access
  //The ISR can change "irCommandReady" "irCommand" "commandType" at any moment.
  //To avoid reading half-updated data, we briefly disable interrupts,
  //copy the values, then immediately re-enable interrupts.
  //This keeps the program safe and predictable.
  //
  noInterrupts();
  if (irCommandReady)
  {
    tempFlag = true;
    _irCommand = irCommand;
    _commandType = commandType;
    irCommandReady = false;
  }
  else
  {
    tempFlag = false;
  }
  interrupts();

  //Did we receive a new command ?
  if (tempFlag == true)
  {
    //Process the new IR command.
    serviceButtonPress(_irCommand, _commandType);
  }


  //================================================
  //       Other non blocking code goes here
  //================================================


} //END of   loop()


//                         h a n d l e R e c e i v e d T i n y I R D a t a ( )
//================================================^================================================
//We do not call this ISR ourselves — the IR library does this automatically when an IR signal is detected.
//
void handleReceivedTinyIRData()
{
  if (irCommandReady == false)
  {
    //Save the IR code for later processing.
    irCommand = TinyIRReceiverData.Command;

    //Is this a "Repeat" or "New command" ?
    commandType = (TinyIRReceiverData.Flags & IRDATA_FLAGS_IS_REPEAT) ? REPEAT : NEW_COMMAND;

    //Flag that we have a new IR code.
    irCommandReady = true;
  }

  //IMPORTANT ISR RULES
  //- Keep ISRs VERY short
  //- Do NOT use Serial.print()
  //- Do NOT use delay()
  //- Do NOT do heavy calculations
  //
  //For this ISR only:
  //1) set a flag to tell loop() that new data is ready,
  //2) copy the received command, and
  //3) determine if this is a "new command" or a "repeat" command.

} //END of   handleReceivedTinyIRData()


//                               s e r v i c e B u t t o n P r e s s ( )
//================================================^================================================
//Short vs Long Button Press
// - NEW_COMMAND happens when a button is first pressed.
// - REPEAT happens automatically while the button is held.
//
//We measure time using millis(), we then decide when a press
//becomes a "long press", and when auto-repeat actions need to happen.
//
void serviceButtonPress(byte _irCommand, COMMAND_TYPES _commandType)
{
  unsigned long now = millis();

  //Is this a new button command ?
  //The "repeat" command "is not" handled here.
  if (_commandType == NEW_COMMAND)
  {
    pressStartTime  = now;
    lastRepeatTime  = now;

    lastCommand = _irCommand;
    longPressActive = false;

    handleMappedButton(_irCommand, PRESS_SHORT);
  }

  //Is this a "repeat" command situation; i.e. a button is being held ?
  //The "repeat" command "is" handled here.
  else if (_commandType == REPEAT && _irCommand == lastCommand)
  {
    //During the "short press" state, have we held the button long enough
    //to get into a "long press" state ?
    if (longPressActive == false && (now - pressStartTime >= longPressInterval))
    {
      //We are now in the "long press" state.
      longPressActive = true;

      lastRepeatTime = now;
      handleMappedButton(_irCommand, PRESS_LONG);
    }

    //During a "long press" state, has the repeat interval expired ?
    else if (longPressActive == true && (now - lastRepeatTime >= repeatInterval))
    {
      //Restart this TIMER.
      lastRepeatTime = now;

      //It is time to handle a repeat action for the pressed button.
      handleMappedButton(_irCommand, PRESS_LONG);
    }
  } //END of   if (_commandType == NEW_COMMAND)

} //END of   serviceButtonPress()


//                               h a n d l e M a p p e d B u t t o n ( )
//================================================^================================================
//
void handleMappedButton(byte irCommand, PRESS_TYPE pressType)
{
  //Find the button being pressed.
  for (byte i = 0; i < arraySize; i++)
  {
    //Do we have a match ?
    if (pgm_read_byte(&buttonCode[i][0]) == irCommand)
    {
      //Convert the raw IR command into a readable symbol.
      //This allows us to use a simple switch(button)
      //instead of remembering hex codes like 0x16 or 0x5A.
      char buttonASCII = char(pgm_read_byte(&buttonCode[i][1]));

      //When we are in the "short press" state, notify the "user" of the button press.
      if (pressType == PRESS_SHORT)
      {
        Serial.println(buttonASCII);

        //tone() inside main loop can glitch PWM
        //tone() uses Timer2 on UNO.
        //analogWrite() on pin 9 uses Timer1 → OK, Breaks PWM on pins 3 & 11 (Timer2)
        tone(tonePin, 3400, 100);
      }

      //Execute the user's application code.
      processButtonPress(buttonASCII, pressType);

      //We found the button, no need to continue looking.
      break;
    }
  }

} //END of   handleMappedButton()


//                               h a n d l e B u t t o n P r e s s ( )
//================================================^================================================
//USER APPLICATION CODE; modify this function to control your own hardware.
//
void processButtonPress(char button, PRESS_TYPE pressType)
{
  switch (button)
  {
    //==================== PWM increase LED1
    case '1':
      {
        //Executed during a Short or Long press.
        pwmLevelLED1 = pwmLevelLED1 + rateChange;
        pwmLevelLED1 = constrain(pwmLevelLED1, 0, 255);

        analogWrite(testLED1, pwmLevelLED1);
      }
      break;

    //==================== PWM decrease LED1
    case '2':
      {
        //Executed during a Short or Long press.
        pwmLevelLED1 = pwmLevelLED1 - rateChange;
        pwmLevelLED1 = constrain(pwmLevelLED1, 0, 255);

        analogWrite(testLED1, pwmLevelLED1);
      }
      break;

    //==================== Toggle LED2
    case '3':
      {
        //Executed during a Short press.
        if (pressType == PRESS_SHORT)
        {
          digitalWrite(testLED2, !digitalRead(testLED2));
        }
      }
      break;

    //==================== Navigating UP
    case '^':
      {
        //Executed during a Short press.
        if (pressType == PRESS_SHORT)
        {
          Serial.println(" UP");
        }

        //Executed during a Long press.
        else if (pressType == PRESS_LONG)
        {
          Serial.println(" FAST UP");
        }
      }
      break;

    //==================== Navigating DOWN
    case 'v':
      {
        //Executed during a Short press.
        if (pressType == PRESS_SHORT)
        {
          Serial.println(" Down");
        }

        //Executed during a Long press.
        else if (pressType == PRESS_LONG)
        {
          Serial.println(" FAST Down");
        }
      }
      break;

    //==================== Navigating Right
    case '>':
      {
        //Executed during a Short press.
        if (pressType == PRESS_SHORT)
        {
          Serial.println(" RIGHT");
        }

        //Executed during a Long press.
        else if (pressType == PRESS_LONG)
        {
          Serial.println(" FAST RIGHT");
        }
      }
      break;

    //==================== Navigating Left
    case '<':
      {
        //Executed during a Short press.
        if (pressType == PRESS_SHORT)
        {
          Serial.println(" Left");
        }

        //Executed during a Long press.
        else if (pressType == PRESS_LONG)
        {
          Serial.println(" FAST Left");
        }
      }
      break;

    //====================
    default:
      {
        //This button has no code to execute.
        if (pressType == PRESS_SHORT)
        {
          Serial.println("No code for this button.\n");
        }
      }
      break;
  }

} //END of   handleButtonPress()


//                                    E N D   o f   S K E T C H
//================================================^================================================
//

EDIT

  • Added the changes from @J-M-L Post #2
8 Likes

Thanks for sharing - remotes can be of great help when you need lots of input indeed.

First time I see the idea of a laminated overlay on existing remote buttons. How does it feel when in your hand ? Cumbersome ?

On the code side, instead of suspending the interrupts twice, have you considered having the

At the same place you capture the status and pass those later as a parameter to the handling function ?

That would add 2 cycles to the critical section each time but ensure you get the state info in an atomic way rather than with two separate reads which theoretically could be for some other click that you will end up servicing twice (not very likely I reckon though).

2 Likes

:+1:

  • It obviously doesn’t have the same feel as a remote without the overlay :slightly_frowning_face:.
    However, it still works very well :slightly_smiling_face:.
    Keep the laminated overlay as thin as possible.

  • These are my overlay rules for construction:

    • Use thin paper, 20 pound is quite acceptable.
    • Only laminate the top of the overlay i.e. not the bottom.
  • For laminating, I have good luck with this 4 inch wide tape, see below.

    • With the overlay, face side up, laying on a cutting mat, carefully place a length of 4” tape on the overlay. Avoid wrinkles and trapping air. Let the 4 inch tape extend a bit past the paper’s long edges; this extra tape can be used affix the overlay to the remote. Cut to size with a utility knife and metal straight edge.

2 Likes

Overlay Construction

  • It’s interesting how a project can keep expanding :smiling_face_with_sunglasses:
  • This likely warrants a discussion on how to create an IR keypad overlay.

Tools and Materials Required

  • Drawing software capable of generating a true 1:1 printable image
  • Utility knife (with a new blade)
  • Metal straight edge with cork backing (to prevent slipping)
  • Green cutting mat
  • 90% isopropyl alcohol (IPA)
  • Clear 4-inch packing tape
  • Removable double-sided tape (DST)
  • 20-lb paper
  • NEC 44-button IR remote (Amazon.ca ~$12, eBay ~$6)

Method

  • Design the remote keypad overlay at a true 1:1 scale.
  • Print the overlay on 20-lb paper (coloured paper is optional).
  • Install a new blade in the utility knife.
  • Clean the cutting mat with IPA.
  • Using the straight edge and utility knife, cut the overlay to its final size.
  • Verify that the overlay aligns accurately with the button locations.
  • Apply two or three short strips of DST to the cutting mat.
    • Lightly reduce the tape’s tackiness by touching it with your fingers.
      This prevents the paper from permanently adhering to the tape.
    • Test adhesion with scrap paper to confirm it is only lightly sticky.
    • The DST is used to temporarily hold the overlay during lamination.
  • Place the printed overlay onto the DST on the cutting mat.
  • Carefully apply a strip of clear packing tape over the top surface.
    • Allow the tape to extend beyond the overlay edges by about 1.5–2 cm.
    • This tape acts as a laminate.
  • Trim excess laminate using the straight edge and utility knife.
  • Remove dirt and oils from the remote's exterior with IPA.
  • Gently remove the laminated overlay from the cutting mat.
  • Carefully align the overlay on the keypad. Fold the extended tape edges around the remote to secure the overlay in place.

1 Like

I build my temporary projects using plastic trim board left from outside work. The inexpensive screws from china work great. I use a lot of 2*10 and short nylon spacers. Works great, I also print the sign on message from that module and use a removable spray adhesive on the back of the paper to hold it on the board. This way I can years in the future find the code, schematics etc associated with the project. I always use the _File and _DATE in a print statement, in the setup() so it makes life much easier.

2 Likes