Yesterday I finally finished my almost buttonless accelerometer-based remote, after some intensive bug tracking. As it turned out, the code had to be sent three times, not twice like all my sources suggested. I live in Finland, so the reason might lie in differences between European and North American models...
The only switch is a wakeup-button. The remote detects clock-wise and counterclock-wise tilts for power toggle and TV/video mode selection, and vertical tilts (hopefully I found the right word) for volume tuning. Anyway, here's a quick sketch illustrating that:

Making a remote with an accelerometer couldn't be simpler, in addition to a working Arduino (in some iteration, I built mine) the only components you'll need are an IR led and a 2- or 3-axis accelerometer. The code, including tilt detection, IR signal sending and a sleep mode, is pretty straight-forward and takes only 2624 bytes of memory. The hardest part in my project was making a wooden case - a remote-sized wooden block that had to be hollowed from the other end without a drill press...patience needed when done with a cordless drill... ![]()
Here's some pics and my code, I hope I'll manage to capture some video on this week.



My coding isn't the tidiest but it should be readable, though.
//A remote with an accelerometer for Sony IR protocol
//The sleep code is adapted from www.arduino.cc/playground/Learning/ArduinoSleepCode, (C) D. Cuartielles
//Other parts of this code are copyrighted by me. You can use this code freely, just mention me in the
//copyright section.
//
//Copyright (C) Timo Herva aka 'mettapera', 2009
//include some sleep code funktions etc
#include <avr/sleep.h>
//device adress for TV in Sony IR protocol(in binary). Not actually used, just for reference
//int address = B10000;
//commands for some functions
byte volume_up = 0x24;//B0100100
byte volume_down = 0x64;//B1100100
byte power_toggle = 0x54;//B1010100
byte av_scroll = 0x52;//B1010010
//create an array for the 12-bit signal: 7-bit command + 5-bit address
byte array_signal[] = {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0};
//analog inputs needed by accelerometer (ADXL322)
byte pin_x = 4;
byte pin_y = 5;
//some pretty obvious pins
byte pin_wakeup = 2;
byte pin_infrared = 8;
byte pin_led = 6;
//accelerometer stuff
int x_accel = 0;
int y_accel = 0;
int x_nominal_min = 470;
int x_nominal_max = 554;
int x_min = 410;
int x_max = 614;
int y_nominal_min = 510;
int y_nominal_max = 540;
int y_min = 495;
int y_max = 555;
//variables for the carrier wave(40kHz) of the signal, desirable duty-cycle is 1/4 - 1/3
byte time_up = 5;
byte time_down = 12;
//sleep mode stuff
unsigned long timing = 0;
byte count = 0;
byte counter = 0;
byte PWM = 0;
//command decoding variables
byte value_decode1 = 0;
byte value_decode2 = 0;
int delay_value = 600;
void wakeup() {}
//sleep mode, refer to the web-address above for an in-depth explanation
void sleep() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
attachInterrupt(0, wakeup, LOW);
sleep_mode();
sleep_disable();
detachInterrupt(0);
}
int flash() {
int dummy;
dummy = timing - millis();
dummy += 6000;
dummy /= 24;
return dummy;
}
//function to decode the command needed to the array (one array and five binarynumbers consume
void command_decode(int binary_command) { //less space than five arrays)
value_decode1 = binary_command;
for (int i = 6; i > -1; i--) {
value_decode2 = value_decode1 & B1;
if (value_decode2 == 1) {
array_signal[i] = 1;
}
else {
array_signal[i] = 0;
}
value_decode1 = value_decode1 >> 1;
}
}
void carrier_make() {
digitalWrite(pin_infrared, HIGH);
delayMicroseconds(time_up);
digitalWrite(pin_infrared, LOW);
delayMicroseconds(time_down);
}
//function which send the command over an IR-led
void signal_send() {
digitalWrite(pin_led, LOW);
for (int i = 0; i < 90; i++) {//start the message with a 2.4 ms time up
carrier_make();
}
for (int a = 0; a < 12; a++) {
delayMicroseconds(delay_value);//time down is always 0.6 ms
if (array_signal[a] == 1) {
for (int i = 0; i < 45; i++) {//"1" is always 1.2 ms high
carrier_make();
}
}
else {
for (int i = 0; i < 22; i++) {//"0" is always 0.6 ms high
carrier_make();
}
}
}
delay(25);
}
//setting things ready
void setup() {
pinMode(pin_wakeup, INPUT);
pinMode(pin_infrared, OUTPUT);
pinMode(pin_led, OUTPUT);
delay(6000);
}
void loop() {
PWM = flash();
analogWrite(pin_led, PWM);
x_accel = analogRead(pin_x);//read values from the accelerometer
y_accel = analogRead(pin_y);
if ((x_accel > x_nominal_min) && (x_accel < x_nominal_max) &&
(y_accel > y_nominal_min) && (y_accel < y_nominal_max)) {
counter = 0;
if (count == 0) {
timing = millis();
count = 1;
}
}
while (y_accel < y_min) {
for (int p = 0; p < 3; p++) {
command_decode(volume_up);
signal_send();
}
delay(200);
y_accel = analogRead(pin_y);
count = 0;
}
while (y_accel > y_max) {
for (int p = 0; p < 3; p++) {
command_decode(volume_down);
signal_send();
}
delay(200);
y_accel = analogRead(pin_y);
count = 0;
}
while (x_accel < x_min && counter < 3) {
command_decode(power_toggle);
signal_send();
x_accel = analogRead(pin_x);
count = 0;
counter++;
}
while (x_accel > x_max && counter < 3) {
command_decode(av_scroll);
signal_send();
x_accel = analogRead(pin_x);
count = 0;
counter++;
}
if (timing <= (millis() - 6000) && count == 1) {
digitalWrite(pin_led, LOW);
sleep();
count = 0;
digitalWrite(pin_led, HIGH);
delay(200);
digitalWrite(pin_led, LOW);
delay(200);
}
delay(50);
}
Update:
I made a slight modification to my code to improve usability over greater distances. I changed the following code snippet
while (y_accel < y_min) {
for (int p = 0; p < 3; p++) {
command_decode(volume_up);
signal_send();
}
delay(200);
y_accel = analogRead(pin_y);
count = 0;
}
while (y_accel > y_max) {
for (int p = 0; p < 3; p++) {
command_decode(volume_down);
signal_send();
}
delay(200);
y_accel = analogRead(pin_y);
count = 0;
}
to this:
if (y_accel < y_min) {
while (y_accel < y_min) {
y_count++;
delay(200);
y_accel = analogRead(pin_y);
}
delay(500);
for (int p = 0; p < y_count; p++) {
command_decode(volume_up);
signal_send();
}
y_count = 0;
count = 0;
}
if (y_accel > y_max) {
while (y_accel > y_max) {
y_count++;
delay(200);
y_accel = analogRead(pin_y);
}
delay(500);
for (int p = 0; p < y_count; p++) {
command_decode(volume_down);
signal_send();
}
y_count = 0;
count = 0;
}
Since volume is adjusted by tilting the remote vertically, I experienced some problems when controlling a TV over a distance of 4m or so, because the IR led had to be pointed exactly to the right spot. After this change, the program sends the command after tilting the remote, giving you some time to point right to TV.