My take on IR remotes (Sony) - with a twist

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... :slight_smile:

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.

Idk if this is a stupid idea or not (I'm still a noob) but if you wired a few more IR LEDs in parallel or series (I'm not sure which) onto the remote could you widen the IR signal rangea bit so you dont have to wait for a pause after tilting the remote?
Anyway great project! It would be great to see one of these on a robot, it would remind me of those robots that use the Wii Nunchuck's accelerometer hacks :slight_smile:

if you wired a few more IR LEDs in parallel or series (I'm not sure which) onto the remote could you widen the IR signal rangea bit so you dont have to wait for a pause after tilting the remote?

I thought that too, but as I live in a bit remote area, there's no electronics stores nearby. And stupid as I am, I didn't post order more of them in the first place...Well, maybe some other day then :slight_smile:

Say I liked this so much that I wanted to use this for a robot :smiley: Does anyone know how I could add a few buttons and make a Wii-mote wannabe controller with wireless transmitting capabilities? It would be awesom on a robot and I would love to try it eventually :slight_smile:

If you will use my code as a base, there's plenty of room for buttons and wireless. When it comes to hardware, some modifications are needed, though: my case is just too small for that.

Anyway, here's a transmitter and a receiver I'm considering to include to my future project:

I haven't got them yet, so I can't say how to interface them, but I think I saw someone's post about them recently.

Yea I was just looking at those same ones yesterday. I've never used RF recievers and trasmitters, so I don't really know the difference between any of them. I was thinking about having a remote with about 6 IR leds (eventually upgraded to RF) for a wide range and using an Arduino 328 pro http://www.sparkfun.com/commerce/product_info.php?products_id=9218
and a 3-axis acceleromter, a thumb joystick from sparkfun, and aroud 4 pushbuttons about the dimensions of a wii-mote, and the case I could design in CAD class.A little ambitious for my level at the moment, but I would make one eventually. ;D