SuperDuplex: An InfraRed Bootloader for Arduino…

We’ve implemented an optical infrared Arduino bootloader based on the common 38kHz infrared remote modualtion.  Using the Asuro as inspiration, our bootloader goes a step further in that it works seamlessly from within the Arduino IDE, utilizing the STK500V2 protocol without modification. 

As described in detail below, due to hardware specifics and a low carrier frequency, the maximum data rate is 4200 baud and in practice about 4k baud reliably without error.  While not amazingly fast, this results in about 1 min 30 seconds to transfer a moderately large Arduino sketch of 20kb.  With the sketch loaded, the IR hardware also functions as a bidirectional half duplex serial link.  As itemized in the following list, the project as a whole is the sum of it’s parts:

  • Echo Cancellation Simplex
  • Continuous Demodulation
  • Demodulator Phase Delay
  • USB Transceiver (Software)
  • USB Transceiver (Hardware)
  • Arduino IR Transceiver
  • Arduino Bootloader
  • Board Specifications

Echo Cancellation Simplex
If you’ve ever talked to someone on speakerphone and heard your own echo, you can understand the confusion this creates.  This is the issue when adapting the normally full-duplex USB converter to infrared communication, while transmitting the reflected infrared bitstream is also being heard and it stuffs up the receive buffer like a bad head cold.  To make the bootloader work seamlessly with the Arduino IDE over modulated infrared, full duplex serial operation is blocked, this is achieved on both sides with software and/or hardware.

Continuous Demodulation
Almost all infrared receivers found in home appliances have an automatic gain control (AGC) circuit to account for variations in signal strength as well as ambient interference from fluorescent lights.  If your lucky, the datasheet for the part will state the maximum duty cycle for data transmission before the AGC normalizes the signal and kills the output.  From testing it appeared to be less than 5%, which is much too low to send appreciable amounts of data within a reasonable time frame.

Ideally we want the AGC to allow a bit over 50% or no AGC at all.  Infrared demodulators without AGC are not common but thankfully, they do exist.  Besides being specifically purposed for high volume data transmission, they also find use in sensing and light barrier systems.  A couple of these devices are the TSOP58038 and the TSOP4038 from Vishay.

Demodulator Phase Delay
To reject ambient noise, the infrared data is modulated with a 38kHz carrier frequency, a digital one is represented as a tone and a digital zero as no tone.  When the demodulator sees the correct carrier frequency,  it outputs a digital one.  Inside the demodulator, the detection process is implemented in analog as a band pass filter and requires about 7 carrier waves to pass before it reports on the output.  It is this requirement that introduces as phase response delay of about 200 microseconds and which can be seen in the following signal capture of the transmission “Hello World”:

The red signal is the 38kHz modulated send and yellow is the demodulated receive signal.  The received signal is phase shifted right a full bit width (200us), resolution is at 1ms per division.

USB Transceiver (Software)
For simplicity the computer side transceiver was first implemented in software with a spare Arduino and an IR shield.  A 120 ohm resistor is used to hold the Arduino’s reset pin high, preventing the transceiver from resetting.  It’s implemented using pinChange interrupt as a bit bang pass though, with the condition that when transmitting, the receiver is ignored.  As TTL serial signals are active low and the IR LED is active high, the transmission component is inverted.The Arduino, the IR shield and the logical truth table to make sense of it all.

USB Transceiver (Hardware)
Once the transceiver was proven in software, the design was moved into the hardware space.  This presented a couple of challenges in that the aforementioned phase delay made a logical OR difficult.  The solution was to implement a similar or longer delay on the TX line OR input.  As can be seen in the following schematic, this is implemented with a 100nF capacitor and 4k7 resistor, click for pops: 

The phase delay arrangement turns on quickly and turns off slowly,  adding about 2 milliseconds to block the receiver while transmitting using a bit of transistor logic.  As the signals are active low and the TX line is inverted, the gate function required for RXD is the logical OR between inverted TX and the demodulator output (refer to truth table for clarification).

Arduino IR Transceiver
On the Arduino side, all that is required for additional hardware is a demodulator and an IR LED, this makes for a very cheap Arduino sans FT232R converter.  The demodulator is connected directly to the Arduino UART receiver (Pin 0).  The IR LED connection takes inspiration from the Asuro bootloader, the LEDs anode is fed with a 38kHz PWM from Pin3 while the cathode is connected to Arduino Pin1, the UART Transmitter.  As TTL serial is active low, the LED lights with the 38kHz carrier when commanded by the UART.This schematic has been setup on a bread board Arduino, the basics which have been detailed in the previous post here:

Arduino Bootloader
The existing bootloader was modified for half duplex by turning off the receiver while transmitting, 38kHz PWM was setup and some delay was added to handle the phase offset of the demodulator.  Prior to making changes, some effort was required to successfully compile the bootloader as documented here:

When uploading the IDE checks for the existence of the bootloader, so the hex, source and makefile need to be in the folder here: arduino-1.0hardwarearduinobootloadersIRbbArduino.  The source, hex and makefile can be downloaded here: IRbbArduino.

Board Specifications
The final bit of the puzzle is to let the Arduino IDE know who we are talking to.  We add a few lines of text to the end of the boards.txt file in arduino-1.0hardwarearduino.  As seen below, it defines who, what and how we are talking.  The IDE must be restarted for these changes to apply:

############################################################## Arduino328 16MHz w/IR Bootloader@2400

Of course no post would be complete without a video of the bootloader in action.  The autoreset feature has yet to be implemented but will include a line in the sketch to signal a reset vector on command.  For now it’s reset manually or with power on, there is about a 2 second window in which it must be reset after the IDE indicates uploading.

As per requested in comments below, here is the code for the PC side software transceiver which runs on an Arduino.
Don’t forget to disable reset with a 120 ohm pullup, otherwise the Arduino will bootload instead of passing the data along to IR.

 n0m1 IR bootloader PC side, software based bitbang pass through
 by krazatchu & socialhardware - 2012/05/07

 IR Demodulator on pin 2
 IR LED on pin 3 with NPN inversion

 PC TXD on pin 1
 PC RXD on pin 0

 Reset pullup/disable using 120 ohm resistor
 Optional Red LED indictor on A1/A4

#include <PinChangeInt.h>
boolean SerialRX = false;

void setup() {
  // dont use serial - bitbang output by turning pwm off and on

  // onboard Red LED indicator
  pinMode(A1, OUTPUT); // LED Anode
  pinMode(A4, OUTPUT); // LED Cathode

  // Disable the Timer2 Interrupt (which is used for receiving IR)
  TIMSK2 &= ~_BV(TOIE2); //Timer2 Overflow Interrupt

  pinMode(3, OUTPUT); // IR TXT PWM
  digitalWrite(3, LOW); // IR TXT PWM

  // COM2A = 00: disconnect OC2A
  // COM2B = 00: disconnect OC2B; to send signal set to 10: OC2B non-inverted
  // WGM2 = 101: phase-correct PWM with OCRA as top
  // CS2 = 000: no prescaling
  TCCR2A = _BV(WGM20);
  TCCR2B = _BV(WGM22) | _BV(CS20);

  // The top value for the timer.  Mod frequency will be SYSCLOCK/2/OCR2A.
  OCR2A = 210; // SYSCLOCK / 2 / khz / 1000; = 16M /2 /38 /1000 = 210.526
  OCR2B = OCR2A / 3; // 33% duty cycle

  pinMode(0, INPUT); // Serial receiver 
  PCintPort::attachInterrupt(0, serialRXDoff, RISING);
  PCintPort::attachInterrupt(0, serialRXDon, FALLING);

  pinMode(1, OUTPUT); // serial out
  digitalWrite(1, HIGH); // 
  pinMode(2, INPUT); // IR RXR1 38kHz
  PCintPort::attachInterrupt(2, serialTXDoff, FALLING);
  PCintPort::attachInterrupt(2, serialTXDon, RISING);


void loop() {
 // do nothing, let interupts work

void serialRXDon (){
	// turn pwm on - PIN3  
	SerialRX = true;
	// or switch to in/out
	TCCR2A |= _BV(COM2B1);
	// blink (300); 

void serialRXDoff (){
    // turn pwm off - PIN3  
    TCCR2A &= ~(_BV(COM2B1)); 
    // digitalWrite(3, LOW);
    PORTD &= ~(1<<PORTD3);
    SerialRX = false;

void serialTXDon (){
    // turn on FTDI RXD
    if(SerialRX == false){
        // led on
		PORTC &= ~(1<<PORTC1);
        PORTD |= (1<<PORTD1); 

void serialTXDoff (){
	// turn off FTDI RXD
	if(SerialRX == false){
		// led off
		PORTC |= (1<<PORTC1);
		PORTD &= ~(1<<PORTD1);

22 Responses to “SuperDuplex: An InfraRed Bootloader for Arduino…

  • Hi,

    This sounds really interesting and I’m trying to implement it on my own. However, I’m having trouble replicating the USB Transceiver part.

    I understand that I need to pull the reset high. I also understand that a pinChange interrupt will be used to process incoming data, so perhaps I should jumper between digital pins 0 and 2 and attach an interrupt there.

    What I don’t understand is how to implement this “bit bang pass though” (or what it really means). Why can’t I make a SoftwareSerial port with TX on my LED (baud rate 2400) and simply shoot out buffered (also at 2400). When I try to do this and to on another Arduino I don’t receive anything but I do get bits from an IR remote, which tells me that my Transceiver is wrong.

    Would you be kind enough to point me in the right direction?

    In case it’s relevant, the IR stuffs I’m using are in the two following links:
    IR Receiver:
    IR LED:


  • Hi Louis,

    I had two working methods for the PC side transceiver, one in software and one in hardware.
    It sounds like you are trying to implement the software method via bit bang which is simply software serial:

    For this I used an Arduino with a pin change interrupt watching TX and passing the data along to the IR LED.
    There was another pin change interrupt watching the IR demodulator and passing the bit stream to RX.
    Where it gets interesting is implementing the blocking logic in software such that RX ignores when TX is sending, including a bit of phase shift to account for the delay of the demodulator.
    I have appended the code for the PC side transceiver just under the video above.

    As well, you will have one issue with your demodulator, it contains an automatic gain control circuit which will turn off the output after a short bit of data, as per the first page of the datasheet “will suppress some data signals”.
    You will want to use a demodulator rated for continuous operation, such as the TSOP58038 or the TSOP4038.


  • Thank you for the help! I’m trying to complete the sketch since the end appears to be missing but it’s a great exercise since I don’t have much experience this low level.

    So far, it seems that the include statement is incomplete: #include

    And the almost finished function is:

    void serialRXDoff (){
    // turn pwm off – PIN3
    TCCR2A &= ~(_BV(COM2B1));
    // digitalWrite(3, LOW);
    PORTD &= ~(1<<(COM2B1));

    I'm still trying to figure out void serialTXDoff() and serialTXDon()…

  • Thanks for spotting that, the code display plugin didn’t like the copy and paste.
    It wanted to format the code before it went in, the remainder of the code is now up.


  • Hey Michael,

    If the bootloader is properly loaded, shouldn’t I be able to upload sketches either via direct USB or using the IR pass through?

    I’m wondering because I’m having the typical “avrdude: stk500_getsync(): not in sync: resp=0xec” error, but I get it regardless of IR or USB connection.

    Note, I am still using the IR receiver that you said wouldn’t work but I don’t think it’s proven to not work until I know my bootloader is properly connected.

    Thanks again!

  • Actually, I am getting two different error messages:
    via IR: avrdude: stk500_recv(): programmer is not responding
    via USB: avrdude: stk500_getsync(): not in sync: resp=0xec

    Which suggests to me that the IR connection is lacking due to the module…

    Will check in when/if I get a new part!


  • That is correct, you will be able to use either the IR or USB @ 2400 baud.

  • The IR demodulator will not make any difference to the USB connection.
    It should also be reporting a non-zero error over IR.

    The best plan of attack is to continue on your current course, get the USB going first, then add in the IR.

  • Hi,

    I am trying to implement your project but I would like to take it a step further. Would it be possible to load a sketch into an Arduino and then once loaded press a button to upload via IR the same sketch into another arduino? this way you could carry a sketch in an Arduino and passed to another Arduino via IR without the PC.

  • The aim is to make a portable IR sketch uploader

  • Ya, that’s totally possible….

  • Hi Michael,

    I wanted to rebuild your project – and wanted to start by building the Reciever Arduino and the Software Sender. Just one quick question: Which IR Transciever do I need to use? In your discrete-PC-side-USB-IR-transceiver you used the Part TSOP58038 and on the Arduino-with-IR-bootloader Design you wrote TSOP1733TB1. Will the TSOP58038 work in both cases or are these needed for some reason?



  • Hi Michael,

    sorry for the additional trouble:
    I figured the TSOP 4838 should work as well – I hope thats right?

    As already mentioned by Louis, the sourcecode for the software implementation on your blog is damaged, could you please fix that?

    And could you give me a – short insight how to connect the arduino for the software sender?

    Thanks a lot!


  • Hi Nico,

    Both the TSOP58038 and the TSOP4038 will work for continuous demodulation of data as they lack AGC.

    Fixed the code again, thanks for spotting that.

    The hardware setup is at the top of the code:
    IR Demodulator on pin 2
    IR LED on pin 3 with NPN low side driver
    PC TXD on pin 1
    PC RXD on pin 0
    Reset pullup/disable using 120 ohm resistor
    Optional Red LED indicator on A1/A4

    The IR shield has all the parts including the reset disable switch.
    They are for sale on tindie, I can include a TSOP4038 or TSOP58038 if you like.


  • Thanks for your help, Michael.
    I got the setup nearly working.
    However, there is still the include in your code missing. Could you fix that?
    Thank you very much!


  • ( Arduino gives me the errors:
    IR_PROG:48: error: ‘PCintPort’ has not been declared
    IR_PROG:49: error: ‘PCintPort’ has not been declared
    IR_PROG:54: error: ‘PCintPort’ has not been declared
    IR_PROG:55: error: ‘PCintPort’ has not been declared – I think thats because of the missing include. I try to build the software sender on an arduino uno, the reciever is build on an naked atmega328p)

  • OK, I figured out the missing libraries: They are to download at the end of those page:
    The missing include is:
    And I used the latest Version of PinChange (2.19 beta).

    Problem is, the recieving Atmega “does work” – if I prog it directly via the TTL Converter on the Recieve/Transmit Pins with Arduino IDE switched to the Breadboard with IR/2400 BAUD Loader.

    If I try to connect the Software Transciever Arduino to the RX/TX Lines, it does not work and I get errors: avrdude: Send: 0 [30] [20]
    avrdude: Send: 0 [30] [20]
    avrdude: Send: 0 [30] [20]
    avrdude: Recv:
    avrdude: stk500_getsync(): not in sync: resp=0x00

    The same thing for trying to upload via IR.

    Funny thing is, the moment I turn on the Recieving Arduino, its IR LEd comes on. Is that correct? The Send/Transmit LED always pulses in the same “clock”. But never changes.

    The SW Transciever Arduino, which IR LEDs are Inveresed via the NPN come on as well as soon as the Upload starts, but it seems like it the other one never reciveses an signal. The Send/Transmit LED on that System always stays dark, no matter wheter the IR LEDs come on or not.

    Any ideas? 🙂

    Thanks a lot,


  • Hi Nico,

    It looks like WordPress has removed some bits from the code even using pre tags wrapping it.
    The code tag seems to fix the problem, I should have checked it over more carefully..

    The pinChangeInt library is one off the most useful libraries not included in the Arduino IDE.

    Are you driving the IR LED with an NPN transistor?
    That transistor is necessary as the IR LED takes more current (60mA) that the Arduino can supply (40mA).
    Without it the Arduino pin can be damaged.

    You can drive the IR LED direct from the Arduino but you will need to make sure the current limiting resistor keeps the current under 40mA, a safe value would be 30mA.
    This calculator is handy, the forward voltage for an IR LED is 1.2v:
    And if direct from pin, invert the signal in the software, then the LED won’t be always on.

  • Hi Michael,

    I build the cuircits to your specs. The recieving Atmega was built to those:

    The software transciever was built to the specs of
    your shield and mixed by the usb transciever hardware.

    Here is the picture of both circuits:

    The Software Transciever does, as you can see, use NPN Inversion, the Arduino Breadboard like on your circuit – not.

    The Problem remains the same:

    Problem is, the recieving Atmega “does work” – if I prog it directly via the TTL Converter on the Recieve/Transmit Pins with Arduino IDE switched to the Breadboard with IR/2400 BAUD Loader.

    If I try to connect the Software Transciever Arduino to the RX/TX Lines, it does not work and I get errors: avrdude: Send: 0 [30] [20]
    avrdude: Send: 0 [30] [20]
    avrdude: Send: 0 [30] [20]
    avrdude: Recv:
    avrdude: stk500_getsync(): not in sync: resp=0×00

    The same thing for trying to upload via IR.

    The moment I turn on the Recieving Arduino, The Send/Transmit LED always pulses in the same “clock” on Pin 13. But never changes. The IR LED comes never on. If I should change polarity of the IR LED on the Breadboard Arduino, so reverse it to your plan: that IR LED would come on the moment I apply power to it and stay on – all the time. So.. maybe there is some error already? Should I also use an NPN transistor as Invert or is your circuit correct?!

    The SW Transciever Arduino, which IR LEDs are Inveresed via the NPN come on as soon as the Upload starts, but it seems like it the other one never reciveses an signal. The Send/Transmit LED on that System always stays dark, no matter wheter the IR LEDs come on or not, and the Breadboard Arduinos IR LED never turns on.



  • I’m a little busy the next couple days, will get back to you by Tuesday – Michael

  • I’ve mannaged to get this to work !
    On the reciever side i used a atmega328p and a HS0038A2 infra red detector.
    Amazingly though, onthe sender side I used a aymega328p programed with cdc232 from using basically the same circuit as your ftdi232 cicuit ! I removed the transistor from detector and insted of a100n capacitor I had to add another 100n capacitor =200n !!! 🙂

  • I want to know about the maximum distance between ir programmer and reciever which supports lossless bootloading

Leave a Reply

Your email address will not be published. Required fields are marked *