I'm working on making a universal remote control for ir devices around my house. So far I have an arduino device (atmega328p @ 8MHz) and sketch that allow me to monitor an ir receiver and record the on and off times for the signal being received. It then replays the same signal with an ir led. The circuit is an ir led connected to pin 11 and an ir receiver hooked up to pin 2. Here is the script I have now.
#include <avr/io.h>
const unsigned long F_IR = 38000; // IR frequency, in hz
const unsigned int PRESCALE = 1; // match this with the values in TCCR2B CS22, CS21, CS20
const byte Timer2APin = 11; // OC2A PIN = PORT B PIN 3 = ATMEGA328 PIN 17 = ARDUINO PIN 11
volatile unsigned int IRPulseNum = 0; // make a counter for IR pulse length array
const byte codeLength = 68; // one each for hi and low part of each bit
// enough to record a start bit, 32 code bits,and a stop bit
// still need to find out if this is long enough for all ir codes...
volatile unsigned int code[codeLength]; // make array to hold IR pulse lengths
volatile unsigned long pulseStart; // this will be used to measure pulses from receiver
void setup(){
pinMode(2, INPUT); // set interrupt pin as input
digitalWrite(2, HIGH); // turn on the pullup. is this needed?
pinMode(Timer2APin, INPUT); // SETS DATA DIRECTION REGISTER
// OUTPUT WILL NOT SHOW UP WHILE SET TO INPUT
// OUTPUT WILL SHOW UP WHILE SET TO OUTPUT
// SET VALUE OF TCCR2A
// (0 << COM2A1) | (1 << COM2A0) TOGGLE OC2A PIN ON COMPARE MATCH
// (0 << COM2B1) | (0 << COM2B0) OC2B PIN DISCONNECTED
// (1 << WGM21) | (0 << WGM20) CTC MODE - WGM22 IN TCCR2B MUST ALSO BE 0
TCCR2A = ((0 << COM2A1) | (1 << COM2A0) | (0 << COM2B1) | (0 << COM2B0) | (1 << WGM21) | (0 << WGM20));
// SET VALUE OF TCCR2B
// (0 << FOC2A) | (0 << FOC2B) FORCE OUT COMPARE A AND B OFF
// (0 << WGM22) CTC MODE - WGM21 AND WGM20 IN TCCR2A MUST ALSO BE 1 AND 0 RESPECTIVELY
// (0 << CS22) | (0 << CS21) | (1 << CS20) CLOCK SELECT - NO PRESCALAR
TCCR2B = ((0 << FOC2A) | (0 << FOC2B) | (0 << WGM22) | (0 << CS22) | (0 << CS21) | (1 << CS20));
TCNT2 = 0; // RESET COUNTER2 VALUE
OCR2A = F_CPU / F_IR / 2 / PRESCALE - 1; // SET TOP COUNTER2 VALUE (FOR CTC)
// have to set this value AFTER the counter starts or it won't work!
// F_CPU is already defined in the board.txt file
// just a rearranged formula from datasheet for ctc
// f_clk_io
// f_OC2A = ---------------------------
// 2 * prescale * (1 + OCR2A)
attachInterrupt(0, IRDetectLow, FALLING); // put interrupt on pin 2 to detect ir code start
}
void loop(){
if (IRPulseNum == codeLength){ // this only happens when the code is done recording
Serial.begin(9600); // turn on serial
delay(1000); // wait a little to give serial time to start up and not miss anything
// print out the array of carrier on and off times that was recorded
Serial.println("Code:");
for (byte i = 0; i < codeLength; i++)
{
if (i % 2 == 0){
Serial.print("Code[");
Serial.print(i);
Serial.print("]: Signal on for ");
}
else{
Serial.print("Code[");
Serial.print(i);
Serial.print("]: No signal for ");
}
Serial.print(code[i]);
Serial.println(" microseconds");
}
Serial.end(); // don't need this anymore
IRPulseNum++; // this will trigger the next if statement
}
if (IRPulseNum > codeLength){ // only happens once code is recorded and sent back to computer
delay(15000); // wait a while
for (byte i = 0; i < codeLength; i++){ // loop though the recorded array turning the ir signal
// on and off for the recorded times
if(i % 2 == 0){ // even array indexes (carrier on)
pinMode(Timer2APin, OUTPUT); // turn ir signal on
}
else{ // odd array indexes (carrier off)
pinMode(Timer2APin, INPUT); // turn ir signal off
}
delayMicroseconds(code[i]); // wait for the amount of time from the array
}
pinMode(Timer2APin, INPUT); // turn ir signal off once whole code is sent
}
}
void IRDetectLow(){ // this runs when carrier start is detected
if (IRPulseNum == codeLength - 1){ // if last spot in array
code[IRPulseNum]= micros() - pulseStart; // calculate how long carrier was off
IRPulseNum++; // increment array counter. used to signal recording is over
detachInterrupt(0); // turn off interrupt because recording is done
}
else{ // not the last spot in array
if (IRPulseNum > 0){ // if it's not the first pulse
code[IRPulseNum]= micros() - pulseStart; // calculate how long carrier was off
IRPulseNum++; // increment array counter
}
attachInterrupt(0, IRDetectHigh, RISING); // switch the interrupt to watch for carrier end
pulseStart = micros(); // record the time when carrier start was detected
}
}
void IRDetectHigh(){ // this runs when carrier end is detected
if (IRPulseNum == codeLength - 1){ // if last spot in array
code[IRPulseNum]= micros() - pulseStart; // calculate how long carrier was on
IRPulseNum++; // increment array counter. used to signal recording is over
detachInterrupt(0); // turn off interrupt because recording is done
}
else{ // not the last spot in array
code[IRPulseNum]= micros() - pulseStart; // calculate how long carrier was on
IRPulseNum++; // increment array counter
attachInterrupt(0, IRDetectLow, FALLING); // switch the interrupt to watch for carrier start
pulseStart = micros(); // record the time when carrier end was detected
}
}
Eventually, I want to be able to save the code and assign it to a button that will play it back. The way it's saved in the array now takes up quite a bit of space (68 unsigned longs * 4 bytes ea = 272 bytes for one code). I realize there are other ways of saving these codes, but this is the most universal way I can think of. I think it should be basically protocol independent as long as you record a long enough signal. Now I'm trying to figure out the best way to save this data. I want it to keep the settings when it turns off, and I would like to save at least a couple dozen codes. Is saving the data into program memory a good option here? If the program took up no space I'd have room for about 117 codes. I'm not sure how big the rest of the program will be when it's doing everything I want it to. What options do I have here? It looks like eeprom chips are pretty cheap, but I've never used one before. Are they a good option here? Are they simple to use? Is there some other option I'm overlooking? Suggestions, comments, or criticisms welcome.