Serial comm using IR

Here's a method to allow two Arduinos to communicate serially over an IR link, using a few dollars worth of parts at the transmit and receive ends. Currenlty this solution is restricted to one-way communication, with transmit hardware at one Arduino and receive hardware at the other. From the software point of view, the arrangement operates transparently as normal Serial communication, with data transmitted and received over TXD and RXD (pins 0 and 1).


The transmit side uses a high-power IR LED from Vishay (TSAL6100 which allows up to 200ma peak forward current. The circuit above drives the LED at about 70ma, set by the 50-ohm resistor. The IR LED is pulsed by NORing TXD with a pulse clock from pin 11 (OC2A), which provides the 38KHz carrier used by the receiver.

The receiver side uses a 38KHz IR receiver (Vishay TSOP1138 which drives the RXD pin. This device is specced at up to 4000 bits/sec, so a baud rate of 2400 is supported.

The only software required is on the transmit side, to set up the 38KHz square wave on pin 11. If an external oscillator were to be used, this would be an all-hardware solution, with the IR link acting totally transparently as a normal serial link.

Here's the code for the transmit side, which includes the pulse clock setup. The main loop in this case merely writes out "Hello, world!" every 5 seconds:

// defines for setting and clearing register bits
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#define SYSCLOCK 16000000  // main system clock (Hz)
#define PULSECLOCK 38000  // Hz
#define IROUT 11

uint8_t timer2top(unsigned long freq) ;

void setup() {
  Serial.begin(2400) ;

  cbi(TCCR2A,COM2A1) ; // connect OC2A (COM2A0 = 1)
  sbi(TCCR2A,COM2A0) ;
  cbi(TCCR2B,WGM22) ;  // CTC mode for TIMER2
  sbi(TCCR2A,WGM21) ;
  cbi(TCCR2A,WGM20) ;
  TCNT2 = 0 ;
  cbi(ASSR,AS2) ;  // use system clock for timer 2
  OCR2A = 255 ;   // set TOP to 255 for now
  cbi(TCCR2B,CS22) ;  // TIMER2 prescale = 1
  cbi(TCCR2B,CS21) ;
  sbi(TCCR2B,CS20) ;
  cbi(TCCR2B,FOC2A) ;  // clear forced output compare bits
  cbi(TCCR2B,FOC2B) ;

  pinMode(IROUT, OUTPUT) ;  // set OC2A to OUPUT  
  OCR2A = timer2top(PULSECLOCK) ; 
  sei() ;

// main loop
void loop() {
  Serial.println("Hello, world!") ;  
  delay(5000) ;

// return TIMER2 TOP value per given desired frequency (Hz)
uint8_t timer2top(unsigned long freq) {
  return((byte)((unsigned long)SYSCLOCK/2/freq) - 1) ;

On the receive side, no special software is needed--just use the usual Serial.available() and calls. This code receives characters and prints them:

void setup() {
  Serial.begin(2400) ;

void loop() {
  int n, i, ch ;
  n = Serial.available() ;
  if (n > 0) {
        i = n ;
    while (i--) {
      ch = ;
      Serial.print((char)ch) ;
  delay(1000) ;

I've tested this in a few different circumstances. I can get reliable communication at a distance of 50 feet in the shade, somewhat less in full sunlight outdoors.