I wrote a sketch to be able to use my old Denon RC-207 IR remote for Arduino projects. It uses a TSOP1738 photo module hooked up to a Duemilanove (running 0012) exactly in the way the TSOP datasheet prescribes.
The code is pasted below. For the build process, including figuring out what kind of code the remote produces, you can go here.
/*
* Name: Denon_IR_decoder
* Purpose: Decode Denon RC-207 remote controller.
* Date: 9 Jan 2009
*
* copyleft 2009, Martijn van den Burg
*
*/
#include <avr/interrupt.h>
#define TIMER_RESET TCNT1 = 0
#define ADDRESS_LENGTH 5 // number of bits that make up the address
#define CODE_LENGTH 16 // number of bits that makes up one IR code
#define CMD_LENGTH 10 // number of bits that make up the data
#define HI_LO_CUTOFF 250 // distinguish between long and short pulse (clock ticks)
#define MIN_LOW 50 // for spurious interrupt detection
#define DEBUG 1 // controls Serial output
#define IRPin 2 // IR receiver module
#define ledPin 13 // for status or debugging
byte myaddress[] = {0, 0, 0, 1, 0}; // device address
byte code_bits[CODE_LENGTH * 2]; // bits read from the IR module
unsigned int ircode = 264; // int value representing an IR remote button
volatile unsigned int interrupt_received = 0; // modified inside ISR => volatile
volatile unsigned int overflow = 0; // flag for timer overflow
void setup() {
if (DEBUG) {
Serial.begin(115200);
Serial.println("setup()");
}
// Use 16 bit Counter1
TCCR1A = 0x00; // COM1A1 =0, COM1A0=0 => Disconnect Pin OC1 from Timer/Counter 1 -- PWM11=0,PWM10=0 => PWM Operation disabled
// ICNC1=0 => Capture Noise Canceler disabled/enabled
// ICES1=0 => Input Capture Edge Select
// CS12=0 CS11=1 CS10=1 => Set prescaler to clock/64
TCCR1B = 0x03; // 16MHz clock with 1/64 prescaler means TCNT1 increments every 4us
// overflow in 2^16 * 4 / 1000 = 262 ms
TIMSK1 = 0x01; // enable timer1 overflow interrupt
pinMode(IRPin, INPUT);
pinMode(ledPin, OUTPUT);
// execute a function when interrupt 0 (INT0) goes LOW
attachInterrupt(0, isr_readIRPin, LOW);
digitalWrite(ledPin, HIGH);
delay(250);
digitalWrite(ledPin, LOW);
}
/* Set a flag when a timer overflow interrupt occurs. */
ISR(TIMER1_OVF_vect) {
overflow = 1;
}
/* Interrupt Service Handler.
* Go and decode the IR burst when an interrupt is received.
*/
void isr_readIRPin() {
int c= getCode(code_bits);
if (c> 0) { // a valid code
ircode = c;
if (DEBUG) {
Serial.println(ircode);
}
}
else { // not a valid code
}
} // isr_readIRPin
void loop() {
// do cool stuff here
} // loop
/* Read and decode input from IRPin.
* The Denon code consists of 16 bits:
* - ADDRESS_LENGTH address bits
* - CMD_LENGTH command bits
* - 1 stop bit
* This code is transmitted twice in one burst, for failure recognition. In between
* is a pauze of approx. 45 msec.
* The second time the code is transmitted, the command bits are inverted.
* Returns 1 upon success, 0 upon failure.
*/
int getCode(byte bits[]) {
TIMER_RESET;
int bitcounter = 0;
overflow = 0;
/* Read the IR pulses.
* Each change from low to high v.v. is counted by change_counter.
*/
for (int change_counter = 0; change_counter < (( 4 * CODE_LENGTH) - 2); change_counter++) {
if (overflow) { // counter overflow - should never happen
return 0;
}
/* LOWs are all equal length, so they don't contain information.
* They can be used to detect spurious interrupts though.
* The value of MIN_LOW is empirically determined.
*/
if (digitalRead(IRPin) == LOW) {
while (digitalRead(IRPin) == LOW) { }
if ( (TCNT1 <= MIN_LOW)) { // we got a spurious IR pulse
return 0;
}
}
else { // IRPin is HIGH
TIMER_RESET;
while ( digitalRead(IRPin) == HIGH) { }
bits[bitcounter++] = (TCNT1 > HI_LO_CUTOFF ? 1 : 0);
}
} // for
// last one is a stop bit; it's always 1
bits[bitcounter++] = 1;
/* check if the address is OK; fallthrough if it is */
if (checkAddress(bits) == 0) {
return 0;
}
/* check data integrity */
if (verifyData(bits) == 0) {
return 0;
}
/* convert command bits to a decimal value */
unsigned int code = calculateCommand(bits);
/* return the calculated command code to the caller. */
return code;
} // getCode
/* Check if the address bits in the code that we read are the same as
* those for our device.
* Returns 1 for success, 0 for failure.
*/
int checkAddress(byte codebits[]) {
for (int x = 0; x < sizeof(myaddress); x++) { // 'myaddress' is array of bytes
if (codebits[x] != myaddress[x]) {
return 0;
}
}
// fall through: we're OK
return 1;
} // checkAddress
/* Verify the command bits: the ones in the second part of the burst
* are the inverse of the ones in the first part.
* So we have to compare bytes 5-14 with bytes 21-30.
* Returns 1 for success, 0 for failure.
*/
int verifyData(byte codebits[]) {
for (int firstbyte = ADDRESS_LENGTH; firstbyte < (CODE_LENGTH - 1); firstbyte++) {
int secondbyte = firstbyte + CODE_LENGTH;
if (codebits[firstbyte] == codebits[secondbyte]) { // i.e. not each other's inverse
return 0;
}
}
return 1;
} // verifyData
/* Convert the CMD_LENGTH command bits to a decimal number.
* Returns an integer.
*/
int calculateCommand(byte codebits[]) {
unsigned int command = 0;
unsigned int bitshift = 0;
/* go from LSB to MSB */
for (int n = (CODE_LENGTH - 2); n >= ADDRESS_LENGTH; n--) {
command += codebits[n] << bitshift;
bitshift++;
}
return command;
} // calculateCommand