Category Archives: Alpine Timing Project

IR beam barrier receiver: Code with interrupt handler for Arduino

This post contains code and information on basic circuitry used to detect a break in an IT barrier solution using Ardiuno and Interrupts.

Parts:

The resistor and capacitor is according to the Datasheet optional to improve on electrical overstress. The connection is described in the datasheet.

The TSOP4038 can handle an continuous 38 KHz IR signal, while other low cost IR receivers seem to get “overloaded” with too long IR signals received. Also, I like the packaging: compact and “mounting friendly”, with only a small pinhole needed for the IR receiver “eye”. The TSOP4038 can be gotten in Norway from the large distributors in small quantities for less than USD 0,7.

The TSOP4038 signal is connected to Arduino Pin 2. The reason for the usage of this pin, is that it is one of two ports on the regular Arduino that can support interrupt triggering. The code could of course be made without the use of interrupts, for example by reading the pin that the IR reiceiver is connected to on a regular basis. This is probably OK for applications such as alarms and surveillance, where delays of some tenths of seconds to not matter. In my race timing application, I must have high precision on when exactly the beam was broken. To achieve this I must use Interrupts.

In my current sample code I trigger an LED to turn off whenever the IR beam has been broken for more the minimum time. In all IR beam barrier solutions, you want the duty cycle of the IR senders to be less than 50% in order to be able to boost up the IR signals. See my other post on creating the IR sender part. My code must therefore be able to handle several “down” IR packets before it can confirm that the beam was actually broken when the first packet disappeared. So, how precise is the break detection? Assuming a 25% duty cycle, where there will be IR in one out of 4 packets, the maximum time before a break is detected is 0,0001056 seconds. Since this is a tenth of one thousand of a second, and we in Alpine racing only are concerned with a hundred of a second precision, we are in good shape since we are about 100x the needed precision.

#define PIN_DETECT 2 // This is where the IR detector "out" signal is connected to
#define PIN_STATUS 13 // Used to indicate a break in the barrier, could be a buzzer or something else

volatile boolean irbeamup = false; // This value is changed by the Interrupt code 
                                   // and is used both inside and outside the Interrupt code = volatile 
long lastuptime = 0;               // Indicates how many milliseconds since we last had a beam detected

void irchanged()
// Interrupt Service Routine for detecting change in the IR reading
// This is connected to Pin 2 on the Arduino
// If there is IR, the value is low, if there is no IR, the value is high
// We want to execute and exit the code as quick as possible, so we only read the value to set the
// boolean irbeamup flag

{
   if (digitalRead(PIN_DETECT) == LOW) irbeamup = true;
   else irbeamup = false;
}

void setup()
{
    pinMode(PIN_DETECT, INPUT);             // This is where we read the IR detector
    pinMode(PIN_STATUS, OUTPUT);            // To show some status
    attachInterrupt(0, irchanged, CHANGE);  // Here the magic is happening: On a CHANGE on Port 0 (Pin 2), 
                                            // the irchanged function will interrupt whatever is going on
}

void loop() {
   if (irbeamup == true) {                     // We have a beam 
        lastuptime = millis();                 // So set the current time to lastuptime
        digitalWrite(PIN_STATUS, HIGH);        // Light the LED, we have a beam
   }
   else {                                      // A break in the beam was detected
                                               // However, if less than 100 mS since last IR was detected,
                                               // I do not yet indicate a break
        if (millis() - lastuptime > 100) {
                  digitalWrite(PIN_STATUS, LOW);
                  // Other actions for the break. We know we last had a beam at lastuptime.
        }
   }
}