Single Button IR Remote

Hi Guys & Girls,

I am making, well trying to make a single button remote control that has a multi-function button.

The remote is single button because it’s for a relative with special needs.

I want for him to be able to have a bit of independance, hes only young and he gets a lot of joy from his childrens TV programs, especially the music in them.

I have the parts, but can’t seem to find for the life of me how to implement what I want in code.

I know I need to IR remote hex codes.

I will give an example as to best explain exactly what I want.

Say for example I press the single button and I want for it to change between channel 201, 202, 204 etc. So one press would be 201, second press 202 and so on.

#include <IRremote.h>
#include <IRremoteInt.h>

// switch pin
const int switchPin = 7;

// variable for the button state
int buttonState = 0;

// Create IR Send Object
IRsend irsend;

void setup() 
{
  // Set Switch pin as Input
  pinMode(switchPin, INPUT);
}

void loop() 
{
  // Set button state depending upon switch position
  buttonState = digitalRead(switchPin);

  // If button is pressed send code command
  if (buttonState == HIGH)
   irsend.send***(0x******, **); // TV code
  }

  // delay before repeating
  delay(200);
  }

This is the code I have so far, but obviously this does one thing. Could someone please help me out. Would be very much appreciate not only by me but my little Nephew to.

Thanks

Kura

So the first thing to do is to write down EXACTLY what you want the button to do. If all it has to do is change channel up as you describe then that's probably easy because most TVs have a specific command you could send to do exactly that. So then you work out what IR codes the TV is expecting.

But think it through carefully. I can't imagine changing to next highest channel is all you need. What happens if he gets to Ch205 and then decides the program on Ch204 was better but hitting the button just goes up to 206 and on? Sounds really frustrating to me.

I'm not confident you can do anything useful with a single button, but you know your nephew and have thought about this more than I have.

Steve

You want to detect when the switch BECOMES pressed, not when the switch IS pressed. The IS an example for that.

As for what to send, you'll need to use the IR sketch for receiving, to learn what the normal remote sends for channel up and down. There is not a specific code for channel n.

Maybe this will get you started:

/* Connect pushbutton between GND and pin4
 * press button and if released in less than 1 second, 
 * counter will be incremented, 
 * if held and released in less than 2 seconds, 
 * counter will be decremented,
 * if held longer than 3 seconds, nothing happens.
 */

uint32_t tStart, interval;
int counter;
const byte btnPin = 4;
bool btnState = true,
     lastBtnState = true,
     done = true;
     
void setup()
{
  Serial.begin(9600);
  pinMode(btnPin,INPUT_PULLUP); 
}

void loop()
{
  btnState = digitalRead(btnPin);
  if(btnState == false && lastBtnState == true)
  {
    tStart = millis();
    done = false;
    lastBtnState = btnState;
    delay(50);
  }
  if(btnState == true && done == false)
  {
    interval = millis() - tStart;
    delay(20);
    if(interval < 1000)
      counter++;
    else if(interval < 3000)
      counter--;          
    done = true;
    lastBtnState = btnState;
    Serial.println(counter);
  }
}

The numbers given were an example. The channels could be 1, 5, 206 and 3000 for all I know so I want to preprogram them, however waiting for a reply on what channels the little chap watches.

I don’t want to add if it’s held for this long do this otherwise do something else. I don’t know how long he could hold the button down.

Simply want to be able to have a bit of code that says something like

Button Pressed send code
Button Pressed send code

There was a command I saw and I csn’t find it for essentially doing this and I can’t remember where I saw it.

I also appreciate I need to get the IR codes and I am waiting for the revision number from the original TV remote for this as they are detailed online.

Thanks for the advice so far.

EDIT: The command was uniquePress() which is mentioned in the thread below.

However I am not sure how to implement it in my code.

You've described only two button presses. I'm pretty sure he will press a button more than twice. Do you plan to have a list of channels which loops round e.g. it always starts up on Ch3 say, then each button press goes to say 5,19, 32, 101 or whatever the list contains. What happens at the end of the list? The next press goes back to 3 and starts again?

A lot of what you want to do depends on what TV you have. E.g. many TVs/satellite setups these days can have a "favourites" list of channels and there's a command that goes to next in that list and loops at the end which makes it really easy.

Without that you have a lot more coding to do if you have to maintain the list and, on each button press, send the individual digits for the channel number plus possibly an "Enter" equivalent.

But it's all possible.

Steve

slipstick:
You've described only two button presses. I'm pretty sure he will press a button more than twice. Do you plan to have a list of channels which loops round e.g. it always starts up on Ch3 say, then each button press goes to say 5,19, 32, 101 or whatever the list contains. What happens at the end of the list? The next press goes back to 3 and starts again?

Hey Steve,

Yes exactly that, cyclical. I assumed the simplist route would be to have a preset list in the code and call from that list in the code?

I assumed the simplist route would be to have a preset list in the code and call from that list in the code?

List the EXACT sequence of actions required, with the regular remote, to change from channel 7 to channel 195. If you can't do that, you haven't a hope in hell of writing a program to do that.

PaulS:
List the EXACT sequence of actions required, with the regular remote, to change from channel 7 to channel 195. If you can't do that, you haven't a hope in hell of writing a program to do that.

Why would I need to do that? I give you I don't know how exactly to write the code for it which is why I am asking here. But I need 6 channels programmed.

I don't know whether I can enter them as button presses so for example 0x0C06, 0x0C00. 0x0C01 for channel 601 or whether I can cheat the skybox and enter 0x0C601.

The second part I need to know is how to write the button press.

For example can I write something that's basically like this

pressButton 0 send IR code 0x0C06, 0x0C00. 0x0C01
pressButton 1 send IR Code 0x0C06, 0x0C01, 0x0C02

I'm assuming you have got your Arduino to successfully change the TV channelband that you have written down the codes to output through IR LED,

Loop
Use a static byte variable initially set to 0,
Test for button press,
If pressed add one to the byte variable, if its seven reset to 0,
Use switch/case to check current byte number and output IR signal,

Done.

So code would be roughly like this,

void loop{

static byte channel=0;

If (digitalRead(5)==HIGH){
channel=channel+1;
}
If (channel>6){
channel=0;
}
switch (channel){
case 0: output IR code; break;
case 1: output IR code; break;
case 2: output IR code; break;

And so on. You insert the IR out bit according to what the syntax is.

ChunksBruv:
So code would be roughly like this,

void loop{

static byte channel=0;

If (digitalRead(5)==HIGH){
channel=channel+1;
}
If (channel>6){
channel=0;
}
switch (channel){
case 0: output IR code; break;
case 1: output IR code; break;
case 2: output IR code; break;

And so on. You insert the IR out bit according to what the syntax is.

Thanks for that. I know the IR codes but I don't know how to implement them exactly. Do I put the IR code in as I would press the buttons on the remote? For example channel 201 would be 0x0C02, 0x0C00, 0x0C01?

So I'm asking how do I write the output IR code to change to a multi digit channel. All the things I've read online only give examples for one button actions like channel 1.

As indicated in reply #2, you want to detect when the button goes from released to pressed and react on that.

You can base your code on the state change detection example.

// sensible name for when the button is pressed
// use when using a pullup resistor; change LOW to HIGH when using a pulldown resistor
#define ISPRESSED LOW

#include <IRremote.h>
#include <IRremoteInt.h>

// switch pin
const int switchPin = 2;

// Create IR Send Object
IRsend irsend;

void setup()
{
  // serial for debugging purposes
  Serial.begin(57600);
  // Set Switch pin as Input
  pinMode(switchPin, INPUT_PULLUP);
  // just a message to show that the arduino is ready
  Serial.println("Press and release the button");
}

void loop()
{
  // variable to remember the last button state; initialised to NOT ISPRESSED
  static int lastButtonState = !ISPRESSED;
  // read the pin
  int buttonState = digitalRead(switchPin);

  // if the button changed from RELEASED to PRESSED or the other way around
  if (buttonState != lastButtonState)
  {
    // remember the change
    lastButtonState = buttonState;

    if (buttonState == ISPRESSED)
    {
      Serial.println("pressed; release the button");
    }
    else
    {
      Serial.println("released; press the button");
    }
  }
  // simple debounce delay
  delay(50);
}

Note that this code uses pin 2 (change to your needs) and the button is connected between pin and GND.

Next you can keep a counter that increments every time that the button becomes pressed. Add the below to the beginning of your code

...
...
// counter to count button presses
uint16_t counter;

void setup()
{
  ...
  ...
}

And modify loop

void loop()
{
  // variable to remember the last button state; initialised to NOT ISPRESSED
  static int lastButtonState = !ISPRESSED;
  // read the pin
  int buttonState = digitalRead(switchPin);

  // if the button changed from RELEASED to PRESSED or the other way around
  if (buttonState != lastButtonState)
  {
    // remember the change
    lastButtonState = buttonState;

    if (buttonState == ISPRESSED)
    {
      Serial.print("pressed ");
      Serial.print(counter + 1);
      Serial.println(" times; release the button");
      // increment the counter
      counter++;
    }
    else
    {
      Serial.println("released; press the button");
    }
  }
  // simple debounce delay
  delay(50);
}

Now this code counts to 65000 and a bit, not quite what you want. You can limit it to the number of channels. Add the below at the top of your code

// number of preset channels
#define NUMPRESETS 6

And modify the if block

   if (buttonState == ISPRESSED)
    {
      Serial.print("preset = ");
      Serial.println(counter + 1);
      // increment the counter
      counter++;
      // if we've reached the end of the presets, reset counter
      if(counter == NUMPRESETS)
      {
        counter = 0;
      }
    }

OK, now counter is no longer an appropriate name. Rename it to e.g. selectedPreset. We will move the actual functionality to a separate function that will be called when the button becomes pressed. The below is an example that can be used till you now what needs to be send.

/*
  send IR code(s) to receiver based on selectedPreset
*/
void sendIRCommand()
{
  switch (seletedPreset)
  {
    case 0:
      Serial.println("preset 1");
      break;
    case 1:
      Serial.println("preset 2");
      break;
    case 2:
      Serial.println("preset 3");
      break;
    case 3:
      Serial.println("preset 4");
      break;
    case 4:
      Serial.println("preset 5");
      break;
    case 5:
      Serial.println("preset 6");
      break;
  }
}

There are better approaches, but I think it’s better to wait with that till the IR code are known.

The full code

// sensible name for when the button is pressed
// use when using a pullup resistor; change LOW to HIGH when using a pulldown resistor
#define ISPRESSED LOW
// number of preser channels
#define NUMPRESETS 6

#include <IRremote.h>
#include <IRremoteInt.h>

// switch pin
const int switchPin = 2;

// Create IR Send Object
IRsend irsend;

// selected preset
uint16_t selectedPreset;

void setup()
{
  // serial for debugging purposes
  Serial.begin(57600);
  // Set Switch pin as Input
  pinMode(switchPin, INPUT_PULLUP);
  // just a message to show that the arduino is ready
  sendIRCommand();
}

void loop()
{
  // variable to remember the last button state; initialised to NOT ISPRESSED
  static int lastButtonState = !ISPRESSED;
  // read the pin
  int buttonState = digitalRead(switchPin);

  // if the button changed from RELEASED to PRESSED or the other way around
  if (buttonState != lastButtonState)
  {
    // remember the change
    lastButtonState = buttonState;

    if (buttonState == ISPRESSED)
    {
      if (buttonState == ISPRESSED)
      {
        // increment the preset
        selectedPreset++;
        // if we've reached the end of the presets, reset counter
        if (selectedPreset == NUMPRESETS)
        {
          selectedPreset = 0;
        }
        // send the IR code(s)
        sendIRCommand();
      }
    }
  }
  // simple debounce delay
  delay(50);
}

/*
  send IR code to receiver
*/
void sendIRCommand()
{
  switch (selectedPreset)
  {
    case 0:
      Serial.println("preset 1");
      break;
    case 1:
      Serial.println("preset 2");
      break;
    case 2:
      Serial.println("preset 3");
      break;
    case 3:
      Serial.println("preset 4");
      break;
    case 4:
      Serial.println("preset 5");
      break;
    case 5:
      Serial.println("preset 6");
      break;
  }
}

I hope that this helps you to see the approach.

Kurayamino:
I know the IR codes but I don't know how to implement them exactly. Do I put the IR code in as I would press the buttons on the remote? For example channel 201 would be 0x0C02, 0x0C00, 0x0C01?

So I'm asking how do I write the output IR code to change to a multi digit channel. All the things I've read online only give examples for one button actions like channel 1.

Think about it :wink: You should do what the normal remote does; that is, it sends 0x0C02 when you press 2, 0x0C00 when you press 0 and 0x0C01 when you press 1. You might want to add a short delay in between the sending as a user does.

Are all codes that you need to send three long? E.g. for 1, you actually have to enter 001 on the remote? Or are they variable length? If the latter, what's the longest?

sterretje,

Absolute legend, thank you. One question, I see where to add the IRCodes but how do I enter them to change the channel can I simply write 0x0C01, 0x0C02 ,0x0C03 to change to channel 123?

The simplest is

/*
  send IR code to receiver
*/
void sendIRCommand()
{
  switch (selectedPreset)
  {
    case 0:
      // send IR code for 2
      ...
      // some delay (500ms?); this is the 'time' that a user usually needs to move finger from one button on a normal remote to another button.
      delay(500);
      // send IR code for 0
      ...
      delay(500);
      // send IR code for 1
      ...
      delay(500);
      break;

    case 1:
    ...
  }
}

The better way however (my opinion) is to use a two-dimensional array of presets.
Add the below to the #defines (and adjust to your needs for the longest number

// maximum number of 'digits' in preset
#define MAXDIGITS 3

And the below just before the declaration of selectedPreset

// array with IR codes to be send
// below contains 2 entries for presets; others default to all zeroes
// if a single digit (e.g. 1 instead of 001) is needed, populate the last one of the 3 and set the others t 0x0000
// this assumes that there is no code that uses 0x0000; you can change it to any unused code
uint16_t presets[NUMPRESETS][MAXDIGITS] = 
{
  {0xC002, 0xC000, 0xC001}, // preset 1; 201
  {0x0000, 0x0000, 0xC001}, // preset 2;   1
};

You can add more

When sending the second preset, the sendIRCommand will ignore the value 0x0000. If 0x0000 is a code that you need, you will need to find one that is not used; remotes often have a repeat code and you do not need that one so you can change the 0x0000 to that one (e.g. when you keep VolUP pressed).

The sendIRCommand can now be simplified and send the individual codes.

/*
  send IR code to receiver
*/
void sendIRCommand()
{
  Serial.print("Sending preset ");
  Serial.println(selectedPreset + 1);
  for (int cnt = 0; cnt < MAXDIGITS; cnt++)
  {
    if (presets[selectedPreset][cnt] != 0x0000)
    {
      Serial.print(presets[selectedPreset][cnt], HEX);
      Serial.print(" ");
      delay(500);
    }
  }
  Serial.println();
}

You can replace the serial prints in the for loop by the actual sending (I have no idea how IR sending works).

sterretje, I can’t thank you enough for your help. I knew what I wanted could be done because I have a miniscule about of experience with programming and have seen functions and the like called from one place in a script to another, but I couldn’t figure out how to implement it in this scenario.

Giving you karma seems like such a small reward for your help with this, but it is the only thing I can offer. Although I can assure you that when I’ve finished my Nephew will be so happy with his little remote control. If you can imagine an incredibly cheeky smile & lots of giggling I hope that can add to my thank you for your help. If you’re interested I will report back and let you know how both me and my Nephew are getting on with the remote.

But thank you again for your time, help and knowledge.

Kura

Hey again,

I might have missed something but having uploaded the code I cannot see in the code where the IR is sent to the IR LED, I obviously tried pressing the button as well just to make sure it wasn't my mistake but nothing received from either of the IR LED's I tried. Am I missing something?

See the last line of reply #15; in sendIrRCommand, you need to add the sending to the IR led inside the if (where it currently prints the codes).

Sorry to be a pain. I see the line of code but I don’t fully understand where to place it. Say it was pin 10 where the LED was placed on the board.

Can I just check everything else with you as well?

// sensible name for when the button is pressed
// use when using a pullup resistor; change LOW to HIGH when using a pulldown resistor
#define ISPRESSED LOW
// number of preser channels
#define NUMPRESETS 6
// maximum number of 'digits' in preset
#define MAXDIGITS 3

#include <IRremote.h>
#include <IRremoteInt.h>

// switch pin
const int switchPin = 2;

// Create IR Send Object
IRsend irsend;

// selected preset
// array with IR codes to be sent
// below contains 2 entries for presets; others default to all zeroes
// if a single digit (e.g. 1 instead of 001) is needed, populate the last one of the 3 and set the others t 0x0000
// this assumes that there is no code that uses 0x0000; you can change it to any unused code
uint16_t presets[NUMPRESETS][MAXDIGITS] =
{
  {0x0C06, 0x0C00, 0x0C01}, // preset 1; 601
  {0x0C06, 0x0C01, 0x0C04}, // preset 2; 614
  {0x0C06, 0x0C01, 0x0C05}, // preset 2; 615
  {0x0C06, 0x0C01, 0x0C07}, // preset 2; 617
  {0x0C06, 0x0C02, 0x0C00}, // preset 2; 620
  {0x0C06, 0x0C02, 0x0C03}, // preset 2; 623
};

void setup()
{
  // serial for debugging purposes
  Serial.begin(57600);
  // Set Switch pin as Input
  pinMode(switchPin, INPUT_PULLUP);
  // just a message to show that the arduino is ready
  sendIRCommand();
}

void loop()
{
  // variable to remember the last button state; initialised to NOT ISPRESSED
  static int lastButtonState = !ISPRESSED;
  // read the pin
  int buttonState = digitalRead(switchPin);

  // if the button changed from RELEASED to PRESSED or the other way around
  if (buttonState != lastButtonState)
  {
    // remember the change
    lastButtonState = buttonState;

    if (buttonState == ISPRESSED)
    {
      if (buttonState == ISPRESSED)
      {
        // increment the preset
        selectedPreset++;
        // if we've reached the end of the presets, reset counter
        if (selectedPreset == NUMPRESETS)
        {
          selectedPreset = 0;
        }
        // send the IR code(s)
        sendIRCommand();
      }
    }
  }
  // simple debounce delay
  delay(50);
}
/*
  send IR code to receiver
*/
void sendIRCommand()
{
  Serial.print("Sending preset ");
  Serial.println(selectedPreset + 1);
  for (int cnt = 0; cnt < MAXDIGITS; cnt++)
  {
    if (presets[selectedPreset][cnt] != 0x0000)
    {
      Serial.print(presets[selectedPreset][cnt], HEX);
      Serial.print(" ");
      delay(500);
    }
  }
  Serial.println();
}

I hope I’m not being to annoying. I’ve never attempted anything like this before and am learning a great deal with your help.

Thanks again.