64-bit RC6 codes, Arduino, and Xbox remotes

I've extended my Arduino IRremote library to support RC6 codes up to 64 bits long. Now your Arduino can control your Xbox by acting as an IR remote control. (The previous version of my library only supported 32 bit codes, so it didn't work with the 36-bit Xbox codes.) Details of the IRremote library are here.

Note: I don't actually have an Xbox to test this code with, so please let me know if the code works for you.


This code is in my "experimental" branch since I'd like to get some testing before releasing it to the world. To use it:
  • Download the IRremote library zip file from GitHub from the Arduino-IRremote experimental branch.
  • Unzip the download
  • Move/rename the shirriff-Arduino-IRremote-nnnn directory to arduino-000nn/libraries/IRremote.

Sending an Xbox code

The following program simply turns the Xbox on, waits 10 seconds, turns it off, and repeats.
#include <IRremote.h>
IRsend irsend;
unsigned long long OnOff = 0xc800f740cLL;
int toggle = 0;

void sendOnOff() {
  if (toggle == 0) {
    irsend.sendRC6(OnOff, 36);
  } else {
    irsend.sendRC6(OnOff ^ 0x8000, 36);
  toggle = 1 - toggle;
void setup() {}

void loop() {
  delay(10000);  // Wait 10 seconds
  sendOnOff();  // Send power signal
The first important thing to note is that the On/Off code is "unsigned long long", and the associated constant ends in "LL" to indicate that it is a long long. Because a regular long is only 32 bits, a 64-bit long long must be used to hold the 64 bit code. If you try using a regular long, you'll lose the top 4 bits ("C") from the code.

The second thing to notice is the toggle bit (which is explained in more detail below). Every time you send a RC5 or RC6 code, you must flip the toggle bit. Otherwise the receiver will ignore the code. If you want to send the code multiple times for reliability, don't flip the toggle bit until you're done.

Receiving an Xbox code

The following code will print a message on the serial console if it receives an Xbox power code. It will also dump the received value.
#include <IRremote.h>

int RECV_PIN = 11;
IRrecv irrecv(RECV_PIN);
decode_results results;

void setup()
  irrecv.enableIRIn(); // Start the receiver

void loop() {
  if (irrecv.decode(&results)) {
    if ((results.value & ~0x8000LL) == 0xc800f740cLL) {
      Serial.println("Got an OnOff code");
    Serial.println(results.value >> 32, HEX);
    Serial.println(results.value, HEX);
    irrecv.resume(); // Receive the next value
Two things to note in the above code. First, the received value is anded with ~0x8000LL; this clears out the toggle bit. Second, Serial.println is called twice, first to print the top 32 bits, and second to print the bottom 32 bits.

The output when sent power codes multiple times is:

Got an OnOff code
Got an OnOff code
Got an OnOff code
Note that the received value is split between the two Serial.println calls. Also note that the output oscillates between ...F740C and ...FF40C as the sender flips the toggle bit. This is why the toggle bit must be cleared when looking for a particular value.

The IRremote/IRrecvDump example code has also been extended to display values up to 64 bits, and can be used for testing.

A quick description of RC6 codes

RC6 codes are somewhat more complicated than the usual IR code. They come in 20 or 36 bit varieties (that I know of). The code consists of a leader pulse, a start bit, and then the 20 (or 36) data bits. A 0 bit is sent as a space (off) followed by a mark (on), while a 1 bit is sent as a mark (on) followed by a space (off).

The first complication is the fourth data bit sent (called a trailer bit for some reason), is twice as long as the rest of the bits. The IRremote library handles this transparently.

The second complication is RC5 and RC6 codes use a "toggle bit" to distinguish between a button that is held down and a button that is pressed twice. While a button is held down, a code is sent. When the button is released and pressed a second time, a new code with one bit flipped is sent. On the third press, the original code is sent. When sending with the IRremote library, you must keep track of the toggle bit and flip it each time you send. When receiving with the IRremote library, you will receive one of two different codes, so you must clear out the toggle bit. (I would like the library to handle this transparently, but I haven't figured out a good way to do it.)

For details of the RC6 encoding with diagrams and timings, see SB projects.

The LIRC database

The LIRC project collects IR codes for many different remotes. If you want to use RC6 codes from LIRC, there a few things to know. A typical code is the Xbox360 file, excerpted below:
begin remote
  name  Microsoft_Xbox360
  bits           16
  pre_data_bits   21
  pre_data       0x37FF0
  toggle_bit_mask 0x8000
  rc6_mask    0x100000000

      begin codes
          OpenClose                0x8BD7
          XboxFancyButton          0x0B9B
          OnOff                    0x8BF3
To use these RC6 code with the Arduino takes a bit of work. First, the codes have been split into 21 bits of pre-data, followed by 16 bits of data. So if you want the OnOff code, you need to concatenate the bits together, to get 37 bits: 0x37ff08bf3. The second factor is the Arduino library provides the first start bit automatically, so you only need to use 36 bits. The third issue is the LIRC files inconveniently have 0 and 1 bits swapped, so you'll need to invert the code. The result is the 36-bit code 0xc800f740c that can be used with the IRremote library.

The rc6_mask specifies which bit is the double-wide "trailer" bit, which is the fourth bit sent (both in 20 and 36 bit RC6 codes). The library handles this bit automatically, so you can ignore it.

The toggle_bit_mask is important, as it indicates which position needs to be toggled every other transmission. For example, to transmit OnOff you will send 0xc800f740c the first time, but the next time you will need to transmit 0xc800ff40c.

More details of the LIRC config file format are at WinLIRC.

Xbox codes

Based on the LIRC file, the following Xbox codes should work with my library:

Key points for debugging

The following are the main things to remember when dealing with 36-bit RC6 codes:
  • Any 36-bit hex values must start with "0x" and end with "LL". If you get the error "integer constant is too large for 'long' type", you probably forgot the LL.
  • You must use "long long" or "unsigned long long" to hold 36-bit values.
  • Serial.print will not properly print 36-bit values; it will only print the lower 32 bits.
  • You must flip the toggle bit every other time when you send a RC6 code, or the receiver will ignore your code.
  • You will receive two different values for each button depending if the sender has flipped the toggle bit or not.


Carl said...

This works perfectly with an Xbox 360 Ken. Much better than sending a huge Raw code. Thanks for your work.

Do you know a way of converting a char array to long long with the arduino for use with this? It doesn't seem to support atoll(), or strtoll().

cruzinthegalaxie said...

I am testing your IRrecvDump example with a Fisher Price GeoTrax ir remote and was wondering if you had any tutorials/examples of how to record and playback the codes?

The library seams to work very well and the remote is fairly consistent in that it hops between codes, but seems to send quite a bit of unnecessary data to control Fwd/Stop/Rev actions on a train. Granted the trains are (in theory) using unique start codes per model - but not between to trains of the same model (proved this with a duplicate train.)

This is a FWD code on the GeoTrax Blue Poney Express below. The first 32 bits hops between 4 codes on repeated stop/start, then there is a filler code 7EA326B3 repeated 10-20 times.

Another night I'll set up the emitter and try sending some test codes at it to see if I can get the train to respond.


Unknown encoding: 75FCE4C7 (32 bits)

Raw (42): -13568 2650 -200 600 -150 550 -550 600 -150 850 -650 800 -300 550 -1250 2650 -200 600 -150 550 -550 600 -150 850 -650 800 -300 550 -1000 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: FFFFFFFFD28120C8 (32 bits)

Raw (14): -6678 2650 -200 600 -150 550 -550 600 -150 850 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): -6628 2650 -250 550 -200 550 -550 550 -150 850 -650 800 -300 550

Unknown encoding: FFFFFFFFD28120C8 (32 bits)

Raw (14): -6628 2650 -200 600 -150 550 -550 600 -150 850 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 10130 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 10430 2700 -200 550 -200 550 -550 600 -150 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 10430 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: FFFFFFFFD28120C8 (32 bits)

Raw (14): 10130 2650 -200 600 -150 550 -550 600 -150 850 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 10380 2650 -200 550 -200 550 -550 550 -200 850 -600 850 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 10380 2650 -200 550 -200 550 -550 600 -150 800 -650 850 -250 600

Unknown encoding: FFFFFFFFD28120C8 (32 bits)

Raw (14): 10180 2650 -250 550 -150 600 -550 550 -150 850 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 7480 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5630 2650 -250 550 -200 550 -550 550 -200 800 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5830 2650 -250 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5830 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5630 2650 -250 550 -200 550 -550 550 -200 800 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5830 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5830 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5630 2650 -250 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5780 2650 -250 550 -200 550 -550 550 -200 800 -650 800 -300 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5580 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 600

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5830 2700 -200 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5780 2650 -250 550 -200 550 -550 550 -200 800 -650 850 -250 550

Unknown encoding: 7EA326B3 (32 bits)

Raw (14): 5530 2650 -200 550 -200 550 -550 550 -200 850 -600 850 -300 550

Anonymous said...

Yep it works. Your the best!

Anonymous said...

There are multiple places where variables have been defined as "unsigned long long".
Once I changed these to "unsigned long" they compile and work correctly.

Thanks for all the great work, Ken.


Anonymous said...

I picked up the new libraries because they have additional functions.. I did not read the article fully. Now I get it........


GeneRalf said...

Hi guys,

I am using a ArduinoMega2560 and need all 12 PWM Outputs. However to control the Arduino Mega2560 it wouldbe nice to use the IRLib. It looks everything is working fine but 2 Outputs ( PWM 9 and 10) are not working anymore.
Is this because the Lib is usingthe same timer like the analogWrite ? Or are these differet timers ?

CodyW said...

When I attempt to compile your code under the Receiving an Xbox code section I get an error..

error: call of overloaded 'println(long long unsigned int, int)' is ambiguous

its because of this line:
Serial.println(results.value >> 32, HEX);

And I have not been able to figure out why its doing this, even after a lengthy search across the web for some insight. Any help would be appreciated.

Alex323qp said...

It doesn't seem to work with the latest version of Xbox 360 with kinetic :(.

Greg said...
This comment has been removed by the author.
dougBTV said...

Just wanted to post and say that this worked very well for me! I needed 36 bit RC6 codes to emulate a MCEUSB remote with an IR LED attached to an arduino rig, and I've had very good success. I wanted to do this so that I could have a linux box running LIRC (the linux ir remote control daemon) that I could then listen to instructions from an Arduino if it had line-of-sight between the two (with a USB IR receiver on the linux box)

I do want to say that I couldn't get enough juice out of the arduino proper, so I had to power the LED through an transistor, like you'd power any high powered LED. I learned the hard way. I had grabbed raw codes first, then... Experimented and saw that the usb IR receiver was a lot happier when I did this.

Also, I wrote some PHP code that you can use to convert LIRC hex codes to the Arduino IR library hex codes. You can find the code on pastebin here: http://pastebin.com/wadbXRK2

Thanks for making this awesome IR library, made life pretty easy!

Garry said...

hey Ken, great work

is it possible to use the RC6 library, (either this one or your older version) to control a Sky satellite box using these codes:


I have the arduino controlling my panasonic TV using the library Lauszus created, but I can't get the RC6 to work??

Greg said...

Yes it is possible - thanks to Ken's library I made an instruct able doing exactly that: http://www.instructables.com/id/Arduino-browser-based-remote-control-linux/

Garry said...

Thanks Greg, i'll check that out

Garry said...

Hey Greg,

I'm really new to this, but I was wondering do you have an example of what you would put into Ken's library to, for example, turn the Sky box on/off. Looking through your code, it seems


should work, at least one anyway, as i'm not worrying about the toggle bit just yet

Garry said...

Hey Greg,

Never mind, I was trying to control an older version of sky box, but the the plus codes don't work for it. the HD box is working great though, so i'm back on track.

thanks for your codes, saved me some work.

always the silly things :)

Emanuele said...

Hi, I tried the new library, but the remote control of Grundig and SKY are not supported and does not work either sendRaw, how can I do?

vsnine said...

Just a quick note, for my setup with RAW codes, bumping the serial speed up to 57600bps seemed to make it work (at all). I did not try any other speeds.

Otherwise the library works great.

Ethan said...

I'm building an arduino-based remote control for my Elco air conditioner. I found your original code, and then this version, which works great with the very long signal air-conditioners use.
I successfully reverse-engineered the code Elco uses, and created methods for decoding and sending them.
Will contribute to your codebase once I'm done with this project.

Anonymous said...

Hi Garry,

I changed the middle two hex digits in Greg's hex codes from 5C to 01 (e.g. C05C0C to C0010C for power) it worked for my old sky plus box.

Also my thanks to Ken and Greg for their generosity.

I hope this helps, Trad

Greg said...

Hi Trad
Glad my code steered you in the right direction, and that people are still tinkering with Ken's code :)
Best regards

Anonymous said...

For those working with RC6 on the Micro/Leonardo below is a patch that will fix build.

diff --git a/IRremoteInt.h b/IRremoteInt.h
index 751a888..4976881 100644
--- a/IRremoteInt.h
+++ b/IRremoteInt.h
@@ -14,7 +14,11 @@
#ifndef IRremoteint_h
#define IRremoteint_h

+#if defined(ARDUINO) && ARDUINO >= 100
+ #include "Arduino.h"
+ #include "WProgram.h"

// define which timer to use
@@ -306,6 +310,8 @@ extern volatile irparams_t irparams;
#if defined(CORE_OC4A_PIN)
#define TIMER_PWM_PIN CORE_OC4A_PIN /* Teensy */
+#elif defined(__AVR_ATmega32U4__)
+#define TIMER_PWM_PIN 13 /* Leonardo */
#error "Please add OC4A pin number here\n"

Atnoz said...

Hi Mr. Shirriff, i used your IRrecvdump example and got this data from the power button of my remote control:
Decoded RC6: 8054270C (36 bits)
Raw (62): -2614 2750 -800 500 -350 500 -400 500 -800 500 -800 1400 -800 500 -400 500 -400 500 -350 500 -400 500 -400 500 -350 550 -350 950 -800 950 -850 900 -850 500 -400 500 -350 500 -400 950 -800 500 -400 950 -350 500 -400 500 -800 500 -400 500 -400 500 -350 950 -400 500 -800 500 -400 500

How to format this serial output to feed the IRrecord from winlirc/lirc and auto/easily generate a config file for my remote?

These are my first steps in IR world and im feeling very dumb after read about all this pulses and timmings stuffs.

Thanks in advance.

Anonymous said...

Hi Ken,

I wanted to thank you for creating this library, it has been very helpful for my project! However, I had one question that I was hoping you could answer.

I am able to successfully receive an IR signal to and send an output to light a LED using an Arduino Uno using your library. However, when I attempt to use the same code with an ATTiny85, it does not work anymore. Are there any discrepancies between an Arduino Uno and ATTiny85 that affect using this library for the latter? I hope you can answer my question.

If you are unclear on anything I said, or need more info, feel free to let me know. Thank you again!