Tuesday, October 25, 2011

InduinoX and wireless relays: Part I

It has been a while since I received my wireless relay and I finally got some time this weekend to put them to good use.

The connections were really simple. I connected a 5V DC power supply (check polarity, the center pin should be positive) to the wireless transmitter and a 12V DC power supply to the relay board (which also has the receiver).

To control the relays using InduinoX, I connected the wireless transmitter board ground to the InduinoX board's ground. Connected the relay 0 pin on the board to digital pin 7 on InduinoX.


Finally connecte the light bulb to mains neutral, the live from mains to the common pin on the relay and "normally closed" (NC) pin on the relay to the light bulb.


Generally you would connect the "Normally open" (NO) pin to the light bulb instead of NC, but I will explain in a minute why I had to connect NC. Now to the coding part. The specification for the relay tells us that when the relay pin is open (high impedance), the relay is active. And when the relay pin is connected to the ground, the relay is inactive.

On the InduinoX board, when a digital pin is set to input mode, the input impedance is very high causing it to act like an open switch. So setting the pin 7 to input mode will cause the relay to be active. Now the InduinoX board always starts with all the digital pins set to input mode which means the relay will start off being active. Which is why I connected the NC pin to the light bulb otherwise with NO connected to the light bulb, the light will be on when the InduinoX starts up.

To turn off the relay I have to connect the relay pin to ground. The way it is done with InduinoX is by setting the pin to output mode and then send low signal. Here is the code for all this logic.

#define TRUE  1
#define FALSE 0

int LIGHT_PIN = 7;
int SLEEP_TIME = 1000;

int isLightOn = FALSE;

void setup() {
  turnOffDevice(LIGHT_PIN);
  digitalWrite(LIGHT_PIN, LOW);
}

void turnOffDevice(int pin) {
  pinMode(pin, INPUT);
}

void turnOnDevice(int pin) {
  pinMode(pin, OUTPUT);
}

void loop()
{
  delay(SLEEP_TIME);
  if (isLightOn) {
    turnOffDevice(LIGHT_PIN);
    isLightOn = FALSE;
  } else {
    turnOnDevice(LIGHT_PIN);
    isLightOn = TRUE;
  }
}

A more exciting project with the light bulb and LDR in my next post.

When light bulb is off

When light bulb is on

Tuesday, October 18, 2011

Scopes and electronics

An oscilloscope is very important for any electronic enthusiast. Recently I have been trying to send IR remote codes from an arduino board to my TV. I was having some trouble with timings and my TV would not respond properly. Without the oscilloscope this would not have been easy to debug.

I have been looking for some not very expensive, simple to use oscilloscope that can be connected to my computer, so I can save data. I came upon PicoScope. They have a wide variety of products, but the one that caught my eye is the PicoScope 2205 which came in my budget and good enough for my projects. However it is not available in India. Lucky for me my sister and brother-in-law are in UK! I asked them for the PicoScope 2205 and 2 probes (x1 and x10). Thanks to them I now have an oscilloscope.

Contents of PicoScope 2205 and Probes

So when I had to find the IR signal coming from my TV remote, I connected the probe to the IR receiver data out pin on the arduino board and started recording the signal using the PicoLog software. It is really simple to use PicoLog. I did not even read any manual or help files. Launched the app, started a new recording and setup the recording method to "Fast block", and "Stop" action at the end of one run.


Then the recording parameters have to be set a bit. Basically I went for 5 us time base (the lowest I could go to get a full command from my remote) and maximum samples possible. That makes for a 81 ms recording time on the PicoScope 2205 buffer. Good enough, because I know that the signal cannot last for more than 45 ms. I set the range on Channel A to 5V DC.


Finally I had to set the trigger on Channel A to "Falling" direction. That is because I found that for some reason the data out pin on the IR receiver is high when there is no signal and goes low when it detects a IR signal. Set the threshold to 2000 mV, but higher would also work. Just wanted to be on the safe side.


That's it for the configuration part. Next I connected the probe on the data pin of IR receiver and pressed the volume up button on my remote. The trigger happened, data got recorded by PicoLog and this is what I got.


I see a lot of other uses for my oscilloscope in future. I suggest you get one too if you are serious about electronic projects.

Thursday, October 13, 2011

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)

Tuesday, October 11, 2011

InduinoX: IR receiver

From my previous post, you probably understood how IR remotes work in general. Now lets take a look at how we can read the signals coming from various remotes that you might have.

Fortunately for us the InduinoX board comes with an IR receiver. And the good news is that some one already wrote the code to read IR signal from arduino! Just copy the code from http://www.arduino.cc/playground/Code/InfraredReceivers, replace the value of IRpin from 2 to A1, because on InduinoX, the IR receiver is connected to analog pin 1. Compile and upload the code to InduinoX.

The code basically sets up the serial port to 115200 baud rate, then sets up timers (read this excellent tutorial on PWMs on arduino), finally the code waits on the IR receive pin for the signal to change (the signal changes when a button on a remote is pressed pointing to the IR receiver). Then the code keeps track of the time it takes for the signal to change from high to low and low to high. At the end of the signal transitions the code prints the timing information. The output looks like

0 0
2476 0
2476 1
3000 1
3000 0
3664 0
3664 1
4204 1
4204 0
5472 0
5472 1
:

Save the output to a file. The output can be used by gnuplot to display the graph of the signal. Make sure you have gnuplot installed on your machine. I used these commands

$ gnuplot
set yrange [-1:2]
plot  "/home/chandanp/temp/vol_up.txt" with lines title 'signal'

And my output looks like this


Notice how the signal repeats itself 2 times and then there is a partial signal at the end. If you happen to have an oscilloscope, you can use the probe on the IR receiver data out pin to see the output on the scope. I have a picoscope 2205 (perhaps a blog post on that later) and the output on the scope looks like this


Since the signal repeats itself every 45ms, here is a closer look at the signal.


Notice that the signal is inverted. Knowing that 0 is represented by 600ms of high and 600 ms of low and a 1 is represented by 1200ms of high and 600ms of low signal (see my previous blog post if you don't know how I arrived at these numbers), the code for volume up on my remote control is 0x490 (0100 1001 0000). In my next blog post, I will show you how I was able to change the volume on my TV by sending the signal 0x490 from InduinoX and pointing it at my TV!

Thursday, October 06, 2011

IR remotes

As part of my home automation project, I also wanted to control my home entertainment system. The controller can be a web interface or an app on android phone, or even a universal remote. Which means, I have to be able to send IR signals from the arduino board that my TV, amplifier and set-top box can understand. For this I will first have to read the signal coming from the remotes, store the various signals and replay them from the arduino board depending on my needs.

Before going too far into the details of receiving and sending IR signals, it would help to understand how a remote control actually works. There is already a lot of material on the internet on how remotes work. So I will try to keep it short and simple.

The IR transmission from remote to your device works on a really simple protocol. The transmitter which is just an LED that emits light in the infrared spectrum, pulses the light in a quick succession and the receiver which is a phototransistor, converts the light signal back to electric pulses. Now to transmit information, the transmitter should send a bunch of 0s and 1s. Normally one would expect (at least I thought), a 1 would be represented by the LED being on and 0 would be represented by the LED being off. However all the data would then have to start with a 1 because only then will the receiver be able to start "listening" to the data.


One way to solve the dilemma of data always having to start with 1, is to have some kind of start signal (the header), to let the receiver know that the data is about to start, and that it should prepare for listening to the data.


Now what happens if there are more than 1 consecutive 0s or 1s? If we know that the pulse width is certain milliseconds then we can identify the consecutive 1s or 0s. But the problem however is the timing. The receiver's and the transmitter's timers should be perfectly synchronized. Otherwise 4 consecutive 1's from the transmitter could be mistaken for only 3 consecutive 1s, if the timers drift a little bit.


So to solve the problem, the IR transmitters and receivers actually use a combination of high and low to represent either a 1 or 0. The 1 is usually represented by longer high and smaller low. While a 0 is represented by smaller high and low. For example in the following drawing the 1 is represented by 1200 milliseconds of high and 600 milliseconds of low. A 0 is represented by 600 ms of high and 600 ms of low. The header is represented by 2400 ms of high and 600 ms of low.


This is the Sony protocol, used by all Sony remotes. Other remotes may have slightly different timings for 1s and 0s, but that is generally how most remotes work. Now there is just one more twist. The IR signal is finally modulated with a high frequency signal, usually between 38kHz and 40kHz. An IR signal modulated with 40kHz looks like this.


Finally, most remotes repeat the same signal at least 2 times. The sony protocol specified that it should be repeated at least 3 times, and the signal should repeat every 45ms.


That is all we need to know to get things started with automating home entertainment systems!

Monday, October 03, 2011

InduinoX: Interfacing with the LDR

Now that I got my LCD display to work with the arduino board, I wanted to use it to show something useful. Since I needed to detect the ambient light for my home automation project, I decided to display the amount of light coming into a room using the light dependent resistor (LDR) that comes with the InduinoX board. Later I will use the LDR reading to determine whether I have to turn on the lights in the room or not.

Typical LDR (Source: http://www.induino.com/wiki/index.php?title=File:LDR.jpg)

The LDR's output is connected to analog pin 3. The voltage as read from pin 3 is inversely proportional to the light incident on it. The analog input is connected to a 10 bit analog to digital converter (ADC). Hence the values range from 0 (at 0V) to 1023 (at 5V). The analog pins can be referenced in the code using A0 (for analog input 0) to A5. For more information on analog inputs, check out http://www.arduino.cc/en/Tutorial/AnalogInputPins.

Now to get to the coding part

#include <LiquidCrystal.h>

const int LDR_PIN = 3;

LiquidCrystal lcd(13, 12, 11, 10, 9, 8);

void setup() {

  lcd.begin(16, 2);
}

void loop() {
  int ldrReading = analogRead(LDR_PIN);

  // LDR resistance is inversly proportional to the light

  ldrReading = 1023 - ldrReading;

  // Clear screen on LCD
  lcd.clear();

  // Print a message to LCD
  lcd.print("lux: ");
  lcd.print(ldrReading);

  delay(1000);
}

The code first sets up the LCD display. And in the loop(), keeps reading pin 3 every second, converts it to light intensity from 0 (completely dark) to 1023 (full light) and displays the value on the LCD display. Note that although I print the value as "Lux: X", the value is not really the luminance.

A few images of the setup under various lighting conditions

Under ambient light during day time

When the LDR is blocked with a paper

Under florescent lighting