Skip to main content

InduinoX: IR Emitter

In my last post, I promised an arduino board based remote control. This is the post you have been waiting for. Now the InduinoX comes with an IR emitter too (don't you just love this board?)! The IR emitter is connected to analog pin 0. So you can start transmitting the remote signal you previously discovered.

Before we go too far, make sure you have read the primer on IR remotes. My TV remote sends out the signal in the sony protocol as observed from my gnuplots and oscilloscope. You know now that according to Sony protocol, a 1 is represented by 1200ms of high and 600ms of low, while a 0 is represented by 600ms of high and 600ms of low. Also the signal is to be prefixed with a header, which is 2400ms high signal followed by a 600ms of low. So lets write some code to send the header, 0 and 1 first.

#define HEADER_ON    2400
#define HEADER_OFF   600
#define BIT_1_ON     1200
#define BIT_1_OFF    600
#define BIT_0_ON     600
#define BIT_0_OFF    600

void sendHeader() {
  sendHigh(HEADER_ON);
  sendLow(HEADER_OFF);
}

void sendBit1() {
  sendHigh(BIT_1_ON);
  sendLow(BIT_1_OFF);
}

void sendBit0() {
  sendHigh(BIT_0_ON);
  sendLow(BIT_0_OFF);
}


So far, so good. Now you have to remember that the signal should be modulated with a 40 kHz signal. Which means a high should really be quick successions of high and low at 40 kHz. The time period of 40 kHz signal is 25 microseconds (1/40,000th of a second). So have to wiggle the IR LED low to high and from high to low every 12.5 ms. Which is why our sendHigh() and sendLow() look like this (note that on the InduinoX, the IR emitter is connected to analog pin 0):

#define IR_PIN A0

int cmdTime = 0;

void sendHigh(int time) {
  cmdTime += time;
  while (time > 0) {
    digitalWrite(IR_PIN, HIGH);  // sets the pin high
    delayMicroseconds(10);       // sleep for 13 us (3us delay for pin high)
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(9);        // sleep for 12 us (3 us delay for pin low)
    time -= 25;
  }
}

void sendLow(int time) {
  cmdTime += time;
  while (time > 0) {
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(10);       // sleep for 13 us (3us delay for pin low)
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(9);        // sleep for 12 us (3 us delay for pin low)
    time -= 25;
  }
}


Finally it is time to send the signal.

#define BITS         12

void sendCmdOnce(int cmd, int last) {
  cmdTime = 0;
  cli();  // Disable interrupts so our timings can be accurate
  sendHeader();
  for (int i = BITS - 1; i >= 0; i--) {
    if ((cmd & (1 << i)) == 0) {
      sendBit0();
    } else {
      sendBit1();
    }
  }
  sei();  // Enable interrupts
}


We are not done yet! Remember that sony protocol requires you to send the same signal at least 3 times every 45ms. Here is the code for that:

void sendCmd(int cmd) {
  for (int i = 0; i < 3; i++) {
    sendCmdOnce(cmd, i);
    delay(45 - cmdTime / 1000);
  }
}


Simple :).Now lets put all the code in proper order along with the setup() and loop() functions.

#define BITS         12
#define HEADER_ON    2400
#define HEADER_OFF   600
#define BIT_1_ON     1200
#define BIT_1_OFF    600
#define BIT_0_ON     600
#define BIT_0_OFF    600

#define IR_PIN A0

int cmdTime = 0;

void setup() {
  pinMode(IR_PIN, OUTPUT);
}

void loop() {
  sendCmd(0x490);
  delay(1000);
}

void sendCmd(int cmd) {
  for (int i = 0; i < 3; i++) {
    sendCmdOnce(cmd, i);
    delay(45 - cmdTime / 1000);
  }
}

void sendCmdOnce(int cmd, int last) {
  cmdTime = 0;
  cli();  // Disable interrupts so our timings can be accurate
  sendHeader();
  for (int i = BITS - 1; i >= 0; i--) {
    if ((cmd & (1 << i)) == 0) {
      sendBit0();
    } else {
      sendBit1();
    }
  }
  sei();  // Enable interrupts
}

void sendHeader() {
  sendHigh(HEADER_ON);
  sendLow(HEADER_OFF);
}

void sendBit1() {
  sendHigh(BIT_1_ON);
  sendLow(BIT_1_OFF);
}

void sendBit0() {
  sendHigh(BIT_0_ON);
  sendLow(BIT_0_OFF);
}

void sendHigh(int time) {
  cmdTime += time;
  while (time > 0) {
    digitalWrite(IR_PIN, HIGH);  // sets the pin high
    delayMicroseconds(10);       // sleep for 13 us (3us delay for pin high)
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(9);        // sleep for 12 us (3 us delay for pin low)
    time -= 25;
  }
}

void sendLow(int time) {
  cmdTime += time;
  while (time > 0) {
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(10);       // sleep for 13 us (3us delay for pin low)
    digitalWrite(IR_PIN, LOW);   // sets the pin low
    delayMicroseconds(9);        // sleep for 12 us (3 us delay for pin low)
    time -= 25;
  }
}


Please forgive my bad programming practices (global variables, confusing naming styles for my defines etc), I did it over a lazy Saturday ;). The setup() prepares the IR emitter to output. The loop() keeps sending the same volume up signal (0x490) over and over every second. So if you point the IR emitter to your TV you will see the volume go up every second. I was very excited when all this just worked! Just to make sure, I looked at the IR emitter PIN voltage on my oscilloscope and everything looks like it is in order.

Singal repeating every 45ms

Up close of 0x490 signal (inverted)

Comments

Popular posts from this blog

Attesting General Power of Attorney in SF

Recently I had to go through the motions of getting a General Power of Attorney (GPA) document attested in San Francisco. I am an Indian by birth. My parents were trying to buy a house back in India for me. Since I did not want to travel to India they needed a GPA so that they can act on my behalf to sign all the documents required to buy the house. The problem however is that they needed it urgently because the seller lives in UK and wants to get all the things done quickly so he can go back. My parents send me a GPA document that they obtained from a lawyer. This is a document that will give the power to my parents to buy the said property in the document on my behalf. The lawyer said that I will have to get the document attested at an Indian Consulate in USA. The closest one for me is in SF and I can drive there in about an hour from where I live. So I though it will be like a day's work to get all the things done. I looked up at their  website  for the procedure to att

XBMC / Boxee remote control android app

I have been writing a few android apps over weekends at home and during 20% time at Google. However I never actually released any of them in the android market mainly because they were quick and dirty apps that fit my needs but perhaps would not be appealing to the general public. One such app that I quickly wrote over a couple of weekends is a XBMC remote. The media center that I use at home is XBMC and I have always wanted to have more control and faster access to my media. Using my remote to navigate through the menus is not as fast. Especially when I wanted to queue a lot of music it is very slow. So I wrote this nice little app called "XBMC remote" for my android phone to control XBMC from anywhere :). Give it a try. Search for "xbmc" in android market and install it if you use XBMC as your media center. When you first launch the app you will start with this screen. You will have to setup your web server address, username and password (if required) by

gtkdocize not found

If you are ever configuring an app and see the message "gtkdocize not found" in Gentoo, then you need to emerge gtk-doc. I had some hard time figuring this out so I am writing it in my blog for the next time. When I saw that error message I did an "emerge -s gtkdocize". Usually it is that simple in Gentoo. But not this time. The emerge command returned no results at all. Then I searched for gtkdoc and still no luck. After searching in Google, I still did not have a solution. After thinking for a while I decided to try to search for gtk-doc. Bingo! That worked! Interestingly, this is my first post from my Virtual machine :-)