► Games

► Sound & Music

► Watches & Clocks


► Power Supplies

► Tools

► Tutorials

By processor

► ATtiny10

► ATtiny85

► ATtiny84

► ATtiny841

► ATtiny2313

► ATtiny861

► ATmega328

► ATmega1284

About me

  • About me



RSS feed

IR Remote Wand

8th May 2018

The IR Remote Wand is a universal remote control that you can program with up to five codes to control a variety of different products:


IR Remote Wand universal remote control based on an ATtiny85.

It supports some of the most popular IR remote control protocols: Philips RC-5, NEC, Samsung, and Sony. It's based on an ATtiny85, and the circuit goes to sleep when you're not using it, to avoid the need for an on-off switch and to prolong the battery life. You can use my earlier IR Remote Control Detective [Updated] to discover the codes for the functions you want to support.


I need to use three different remote controls to operate my television, DVD player, and audio system. However, I typically only use one or two buttons on each remote control for the functions I need regularly. I therefore decided to design a smaller, more convenient, remote control that I could program with the essential functions from all the other remote controls.

Because I wanted relatively few functions I based the remote control on an ATtiny85; using one I/O line to drive the infrared LED left four I/O lines available for buttons, and the reset signal can be used as a fifth button. If you need more than five buttons you could make one button cycle between different options, or use one button to change modes.

The circuit

Here's the circuit of the remote control:


Circuit of the IR Remote Wand universal remote control.

I decided to use surface-mount components, to keep the remote wand as compact as possible and give it a flat base. Initially I planned on making it longer, and powering it from two 1.5V AAA batteries in series; however I found some rechargeable 3.7V AAA-sized Lithium batteries on Amazon [1] and decided to use one of these instead to reduce the length of the PCB. The circuit fits on a 10mm x 100mm PCB, which I ordered from Ragworm in the UK. The battery is retained by two clips [2] that are soldered to the PCB.

For the IR LED I used an SMD 940nm infrared LED [3], although any IR LED should work. A green LED in parallel with the IR LED gives a visual indication that the IR Remote Wand is working.

I used an SOIC ATtiny85, and the other components are 0805 size, so they should be relatively easy to solder by hand with a fine-tipped soldering iron. The program is under 1500 bytes, so an ATtiny25 or ATtiny45 could be used instead of the ATtiny85.

The program

Generating the carrier

All the remote control protocols are based on a similar carrier frequency of between 36kHz and 40kHz; the exact frequency doesn't seem to be critical, so I chose a frequency of 38.5kHz; this is generated on OC1A (PB1) using Timer/Counter1. The mark/space ratio is set to about 25%, which reduces the current consumption of the IR LED:

const int top = 25;                       // 1000000/26 = 38.5kHz
const int match = 19;                     // approx. 25% mark/space ratio

void SetupPCM () {
  TCCR1 = 1<<PWM1A | 3<<COM1A0 | 1<<CS10; // Inverted PWM output on OC1A divide by 1
  OCR1C = top;                            // 38.5kHz
  OCR1A = top;                            // Keep output low  

The carrier can be turned on or off by writing to the compare match register OCR1A. Setting it to top gives a continuously low output; setting it to match generates the carrier tone.

Generating the codes

The routine Send generates the IR code. The IRtype parameter specifies the IR protocol, as follows:

'N' - NEC (32 bit)
'M' - Samsung (32 bit)
12 - Sony 12 bit
15 - Sony 15 bit
20 - Sony 20 bit
'R' - Philips RC-5 (14 bit)

Here's the routine:

void Send (char IRtype, unsigned int Address, unsigned int Command) {
  // NEC or Samsung codes
  if ((IRtype == 'N') || (IRtype == 'M')) {
    unsigned long code = ((unsigned long) Command<<16 | Address);
    TCNT1 = 0;                            // Start counting from 0
    // Send Start pulse
    if (IRtype == 'N') Pulse(342, 171); else Pulse(190, 190);
    // Send 32 bits
    for (int Bit=0; Bit<32; Bit++)
      if (code & ((unsigned long) 1<<Bit)) Pulse(21, 64); else Pulse(21, 21);
    Pulse(21, 0);
  // Sony 12, 15, or 20 bit codes
  } else if (IRtype == 12 || IRtype == 15 || IRtype == 20) {
    unsigned long code = ((unsigned long) Address<<7 | Command);
    TCNT1 = 0;                            // Start counting from 0
    // Send Start pulse
    Pulse(96, 24);
    // Send 12, 15, or 20 bits
    for (int Bit=0; Bit<IRtype; Bit++)
      if (code & ((unsigned long) 1<<Bit)) Pulse(48, 24); else Pulse(24, 24);
  // Philips RC-5 code
  } else if (IRtype == 'R') {
    static int toggle = toggle ^ 1;
    int nextbit, extended = Command>>6 ^ 1;
    unsigned int code = 0x2000 | extended<<12 |
      toggle<<11 | Address<<6 | (Command & 0x3F);
    TCNT1 = 0;                            // Start counting from 0
    for (int b=0; b<14; b++) {
      nextbit = code>>(13-b) & 1;
      for (uint8_t i=0; i<2; i++) {
        if (nextbit) OCR1A = top; else OCR1A = match;
        // Wait for 32 Timer/Counter1 overflows
        for (int c=0; c<32; c++) {
          while ((TIFR & 1<<TOV1) == 0);
          TIFR = 1<<TOV1;                 // Clear overflow flag
        nextbit = !nextbit;
    OCR1A = top;                          // Leave output off

The code for generating Philips RC-5 codes is based on my earlier IR Remote Control Tool, the code for generating the NEC codes is based on IR Remote Control Tool (NEC), and the code for generating the Sony codes is based on my Sony NEX/Alpha Remote Control. The Samsung is identical to the NEC protocol apart from the length of the start pulse and gap.

Detecting the pushbuttons

Four of the pushbuttons are configured to generate pin-change interrupts; the interrupt service routine then simply checks which button is pressed and sends the IR code assigned to that button:

ISR (PCINT0_vect) {
  int in = PINB;
  if ((in & 1<<S1) == 0) Send('M', 0x0707, 0xFD02);
  else if ((in & 1<<S2) == 0) Send('M', 0x0707, 0xFB04);
  else if ((in & 1<<S4) == 0) Send('R', 0x0013, 0x0011);
  else if ((in & 1<<S5) == 0) Send('R', 0x0013, 0x0010);

The fifth pushbutton, S3, is connected to Reset; the code assigned to this button is sent in Setup(). Note that this takes effect when you release the button, whereas the other four buttons take effect when you push the button, so S3 is a good choice for a mode change button.

Obviously you'll need to change the parameters to the Send() calls with the appropriate ones for the codes you want to send.

Power saving

An on/off switch is definitely out of the question for a remote control, so the program is designed to minimise power consumption. The processor spends all of its time asleep, and only wakes up on a pin-change interrupt or reset signal from one of the pushbuttons. The power consumption in sleep mode is about 0.25µA, which will give several years of battery life.

Compiling the program

I compiled the program using Spence Konde's ATTiny Core [4]. Choose the ATtiny25/45/85 option under the ATtinyCore heading on the Board menu. Then choose Timer 1 Clock: CPUB.O.D. Disabled, ATtiny85, 1 MHz (internal) from the subsequent menus. If necessary choose Burn Bootloader to set the fuses appropriately. Then upload the program using ISP (in-system programming); I used Sparkfun's Tiny AVR Programmer Board; see ATtiny-Based Beginner's Kit.

Here's the whole IR Remote Wand program: IR Remote Wand Program.

Alternatively, get it on GitHub here together with the Eagle files for the PCB: IR Remote Wand on GitHub.

Or order a board from OSH Park here: IR Remote Wand Board.


13th May 2018: Corrected a mistake with the code to generate the NEC and Samsung codes.

  1. ^ Lixada 4PCS AAA 10440 600mAh 3.7V Rechargeable Lithium Battery on Amazon.
  2. ^ 55 - Battery Clip, AAA on Farnell.
  3. ^ VSMB2948SL - Infrared Emitter 50° SMD 100mA 1.6V 15ns on Farnell.
  4. ^ ATTinyCore on GitHub.

blog comments powered by Disqus