Arduino Laser-based Timing System

22,534

80

34

Introduction: Arduino Laser-based Timing System

As part of my teaching, I needed a system to accurately measure how quickly a model vehicle travelled 10 meters. Initially, I thought I would buy a cheap ready-made system from eBay or Aliexpress, these systems are commonly known as light gates, photo gates or similar. It turned out that pre-built light gate timing systems are actually quite expensive though, so I decided to build my own.

The operation of a light gate timing system is pretty simple. Each light gate consists of a laser module at one side, this projects a laser spot onto a light-dependent resistor module (LDR) at the other side. By measuring the output of the LDR, the system can detect when the laser beam has been broken. Using two of these gates the system starts the timer when the first beam is broken and stops the timer when it senses the second beam has been broken. The resulting recorded time is displayed on the LCD screen.

Building a system such as this with students is a great introduction to coding, it is also a really useful classroom resource once it is finished. This type of system is great for STEM activities and can be used to measure how fast things like rubber band cars, mousetrap cars or pinewood derby cars travel a set distance.

Disclaimer: The solution presented here is far from optimal. I'm aware some things could be a lot better or more efficient. This project was initially put together on a very tight deadline and worked absolutely fine for the purpose intended. I have plans to release both a version 2 and version 3 of this system with improvements, please see the last step of the instructable. Implementation of the circuit and code is at your own risk.

Supplies:

  • Arduino R3 (or compatible board)
  • Adafruit feather wing protoboard - A small section of any type of protoboard is also fine
  • LCD keypad shield - Ensure this is made to fit the version of the arduino you have
  • 2 x Light Dependent Resistor (LDR) module - Searching ebay for "arduino LDR" should show plenty of options
  • 2 x Laser module - Searching ebay for "arduino laser" should show plenty of options. Make sure the power of the laser is not greater than 5mW.
  • 4 x Small tripod
  • 4x 1/4 inch nut - To fit a standard tripod thread
  • Clear acrylic for Arduino case
  • M3 nuts and bolts
  • Plastic PCD standoffs - Kits of these can be had for quite cheap on Ebay.
  • 4 x 3D printed enclosures
  • Ribbon cable

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

Step 1: Program Adrunio

Upload the code below to the Arduino. If you are unfamiliar with how to do this, check out this great instructable.

The basic logic of the code is as follows:

  1. Turn on laser modules and check than each LDR can "see" the laser beam.
  2. Wait until LDR 1 detects a break in the laser beam, immediately start the timer.
  3. Wait until LDR 2 detects a break in the laser beam, immediately stop the timer.
  4. Show the resulting time on the LCD screen in milliseconds.

The code is only designed to time a single run, once the time from the screen has been noted down the reset button on the shield is used to restart the program.

LINK TO ARDUINO CODE

(FYI: The code is hosted on create.arduino.cc and I would love to have embedded the code here, but the Instructables editor doesn't allow the embedded iframe to display or work correctly. If anyone at Instructables is reading this, please implement this as a feature in the future, thanks)

Step 2: 3D Print Enclosures

The laser and LDR modules need to be held in place to ensure that no beam breaks occur as a result of the modules moving. 3D print the enclosures below and bolt the modules in place, the laser module will need to be held in place with a zip tie as it has no thru bolt hole.

Be sure to trap a 1/4 inch nut inside each of the cases, this will be used later to allow these cases to connect to the tripods. The two halves of the enclosure are held together with M3 nuts and bolts.

Step 3: Laser Cut Arduino Case

Laser-cut the files below from the 4mm thick clear acrylic. Line up the arduino R3 and protoboard with the holes on the acrylic pieces and bolt into place. Bolt the top piece of the case to the bottom using the PCD standoffs as spacers.

Step 4: Wire the Circuit

The LCD shield used in this project is explained in detail in this great instructable. The LCD screen and input buttons use some of the arduino's I/O pins however, for this reason all I/O for the laser modules and LDR's use pins 1,2,12 and 13 only.


Very little wiring is needed, but ensure the circuit is connected as shown in the diagram. I added some JST type connectors to the laser and LDR module wires to allow me to easily disassemble and store the entire setup.

Yes, arduino pins 1 and 2 are directly powering the laser modules with no in-line resistor. As the laser modules selected are designed specifically for use with arduino this shouldn't be an issue though. The laser modules draw a maximum power of 5mW, this means that at the 5V supply voltage of the pin, the module should be drawing around 1mA, this is well below the ~40mA limit for current supply on arduino I/O pins.

Step 5: Assemble and Tune

Finally, you are ready to assemble everything.

  1. Mount the LDR and Laser module cases on the small tripods.
  2. Position the Laser modules to shine directly at the LDR sensor

At this stage, you will need to fine-tune things a little. The LDR modules output a digital signal, a high signal (5V) indicating that no laser beam is detected, a low sign (0V) indicating that it can see the laser beam. The light intensity threshold at which the module switches from a 5V to a 0V output signal (and visa versa) is controlled by a potentiometer on the LDR board. You will need to adjust the potentionmeter so that the module switches between a 0V and 5V output when you expect it to.

Either gradually adjust the potentiometer until the system functions as expected, or use a multimeter to measure the LDR module output and tune as required.

Step 6: Operation and Further Work

You should now be ready to use the system! The images show the stages of operation.

  1. Press the select button to initialise the system.
  2. Align the lasers so that they shine directly on the LDR sensor.
  3. The system is now armed. Set you model car going.
  4. The system will start timing once the first laser beam is broken.
  5. The system will stop once the second laser beam is broken.
  6. The time in milliseconds is then displayed on the screen.
  7. Press the reset button to time another run.

I will probably create a version 2.0 of this system as there are some obvious improvements that could be made:

  1. There is no need to power the laser modules from the Arduino, they could be battery powered and simply switched on when needed. When I designed the system, wiring the laser modules to the Arduino for power seemed like the simplest solution, in practice, this results in long cable runs that get in the way.
  2. Condenser lenses are really needed on the LDR housings. Lining the laser dot up exactly with the centre of the (very small) LDR sensor is very tricky and can sometimes take several minutes, using a condenser lens would give the user a much bigger target to aim at with the laser dot.

I'm also now even thinking about a version 3.0 that is fully wireless and just connects to my laptop using Bluetooth, this is a much bigger project for another day, however.

STEM Contest

Runner Up in the
STEM Contest

Be the First to Share

    Recommendations

    • Trash to Treasure Contest

      Trash to Treasure Contest
    • Raspberry Pi Contest 2020

      Raspberry Pi Contest 2020
    • Wearables Contest

      Wearables Contest

    34 Discussions

    0
    Chip Geek
    Chip Geek

    2 days ago

    An idea: You could get by with only 1 laser and 1 detector. Just add 2 mirrors like the below drawing. "/" and "\" are mirrors, "-" and "|" show laser beam path. Car travels from top to bottom of my (very crude) picture.

    /----------- <== Laser at start line pointing to first mirror
    |
    |
    |
    \----------- ==> photo detector on Adruino at finish line

    The first time the detector sees the beam is blocked is the start, the second time is at the finish. This also greatly simplifies the wiring since only the laser needs power (could be battery powered - no wires needed at all).

    For very accurate timing, read the data sheet for the Atmel ATmega328 chip used. Look for "Input Capture" for Timer1, PB0, pin 14, Arduino D8. You can get sub-microsecond timing (not only millisecond for your code). By adding 2 more mirrors, you could also get the velocity at the finish line.

    Contact me if you'd like some help on this. I could write the code for you if you want.

    1
    bpark1000
    bpark1000

    Tip 7 days ago on Step 6

    LDR's are notoriously slow and prone to aging. Better would be photodiodes (small pieces of solar cell will work fine, along with trans-impedance amplifier). Even better is to modulate the beam with a high-speed signal (simple 50% duty square wave is fine) and demodulate that on the other end. This is easy when the same computer is controlling both ends (turn Tx beam on, wait to center of "on" pulse, read Rx output, turn Tx beam off, wait to center of "off" pulse, read Rx output). Subtract the latter reading from the former to get the intensity of the laser beam. Room light is rejected as it will make same reading for both which is cancelled out.
    Use visible laser to make alignment easier (you will need modulatable laser). Put Rx end in a tube to reject room light.

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    Hi. Thanks very much for this. I will certainly consider this approach on future projects. I think the ultimate aim with this is to make all sensors and Tx sources wireless using nrf24l01 modules, would synchronising the modulated signals at Tx and Rx still be possible in this case? Or would a different approach be a better bet?

    0
    bpark1000
    bpark1000

    Reply 4 days ago

    So you are going to send the received optical signal over an RF link? That will impose uncertain delay. Both ends will need wires anyway for power! If you object to wires crossing field, use optical retro-reflector (bike reflector) and put optical Rx & Tx on the same end!
    Yes you can synchronize, but additional algorithm is needed to synchronize. Since beam will be "on" most of the time, you will have long time to sync. You can use "Costas loop" (done in software) to do this. Normally this would be difficult, but since you are running at low frequency, the "lock in" need be only over +/-1% of the carrier frequency. (Use crystal oscillators on both ends). You can also use "incoherent" method (multiply received signal by locally-generated carrier and carrier shifted by 1/4th period. Each of these is low-pass filtered (~100Hz). Result is the sum of the 2 filtered results. This will give slower response then the coherent method (which can respond within 1 cycle of the carrier).

    0
    jpswensen1
    jpswensen1

    8 days ago

    Any idea on the latency and or response time of this setup? That is, how long after and object passes does the arduino detect the object? milliseconds? microsecond?

    I was just wondering if the car is going too fast whether this setup would miss detecting it.

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    Hi. Honestly I have not tested the latency or response time. All objects I measured using the system were moving at <2m/s over a 10m course, there were also large measurable differences in times between different cars. As mentioend in some other comments, there is better (faster/more accurate) Tx and Rx hardware, I'll probably look to make a version 2 of the system rather than test the accuracy of this one.

    1
    Reyes23
    Reyes23

    15 days ago

    Nice project! What is the total cost of it?

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    Hi. I'll try and update the instructable with component costs in the next few days.

    2
    TheDoblerones
    TheDoblerones

    18 days ago

    Nice Project!
    If you have the time and will to improve your Arduino Code I would highly suggest you reading about external interrupts. They make it a lot easier to detect a signal change.

    0
    Joe Palmer
    Joe Palmer

    Reply 15 days ago

    Great suggestion, thanks. If you have a link to a good resource on interrupts please let me know.

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    Danke!

    0
    akhtarkh
    akhtarkh

    Question 8 days ago on Step 1

    what software/application have you used to make the circuit diagram? It dose not look like fritzing.

    0
    Joe Palmer
    Joe Palmer

    Answer 4 days ago

    The circuit diagram was made using tinkercad circuits. It is more of a schematic rather than a proper circuit diagram as the exact components were not avilable in the editor.

    0
    AlejandroS101
    AlejandroS101

    8 days ago

    Nice Project!
    As a teacher you can show variants of the sensors for the laser light.
    An example may be the use of common red LEDs attached to an integrated comparator to show that all semiconductors are sensitive to light.

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    I think there is a real opportunity here to further develop a project such as this for STEM. Students could be set the fairly striaghtforward task of measuring the time a model car takes to travel a set distance, then introduced to various ways to do this. There are also many "levels" to which the students can accomplish the task allowing for lots of differentiation in marking. Ironically the solution I present here is probably the most basic level, more able students could look to replace the Tx and Rx hardware with faster or more reliable components, improve the code for robustness or investigate wireless options.

    0
    PeterD304
    PeterD304

    8 days ago

    Photodiodes like the BPW34 are much faster than LDR's. Perhaps you should try using those instead.

    0
    Joe Palmer
    Joe Palmer

    Reply 4 days ago

    Thanks, a few people have mentioned this. As a result I will use improved Tx and Rx hardware for the version 2.

    0
    starphire
    starphire

    Reply 8 days ago

    I was going to note this as well, but I think it's most helpful to point to a resource that explains the complexities of LDR response times (summary: they are variably slow depending on the photocell, light levels, and how it's being measured). But even at their best, the sluggish "fall time" in a use like this probably exceeds a 1ms count of your Arduino. Here's a good writeup on their properties: http://www.ladyada.net/media/sensors/gde_photocell... .
    I agree that a silicon photosensor is much more suitable, as even the slowest phototransistor will take less than 1ms to respond to a break in the beam. But in the spirit of keeping things simple for beginners, a nice breakout board that's sensitive to visible wavelengths, has a simple analog output, and is inexpensive would be the ticket. That's surprisingly hard to find, as most such photosensors are for IR only and have permanent filters to block out visible light. But here's one that would fit the bill: https://www.adafruit.com/product/1384

    The small size of silicon photosensors makes alignment more finicky, but this can be overcome with a small convex lens mounted at the focal distance in front of your sensor. Another trick is to put the laser diode and the sensor next to each other facing the same direction, and use a mirror to reflect the beam back to the sensor. Now you can have it all aligned in one block, making alignment in setup fast and foolproof if you use a retroreflector (bicycle reflector!) as the mirror.

    2
    jm-l
    jm-l

    Tip 8 days ago

    great work , thanks for sharing.

    a few constructive notes:

    ———————————————————————

    - I noticed the use of pin #1 for one of the laser. whilst it's not a problem in your setup, this pin is used for Serial connection and will prevent anyone extending your code to use Serial.print() for debugging purpose. I would suggest to move the lasers to pin 2 and 3 (give them names as well, that makes code more readable and easy to change) this way if you want to benefit from interrupts, that would be ready to go :-)

    ———————————————————————

    - you use also pin13 for one of your LDR. Pin 13 on your UNO is connected to the onboard LED. as you are not short of pins, you could keep that one free in case you need to blink a led or something.

    ———————————————————————

    - the two "delay(100);" when waiting for the laser are not necessary

    ———————————————————————

    - digitalRead() returns HIGH or LOW. Yes it's indeed 0 and 1 behind the scene, but for readability it's better to stick to HIGH and LOW in the code

    ———————————————————————

    - for anything dealing with time and millis(), you need to use unsigned long, not an int. Your int on your Arduino Uno is limited to 32767 so after 33 seconds your code will start behaving very weirdly. so you need to declare

    unsigned long start_time = 0;
    unsigned long stop_time = 0;
    unsigned long total_time = 0;

    ———————————————————————

    - at the end of the code instead of your infinite loop (which usually is coded as while(true);) you could wait for the user to press select to start a cycle again :) and don't forget to turn the lasers off

    ———————————————————————

    - Note that you don't need really intermediary variables for your while(). instead of doing

    while (light_sense_1 == 0) {
    light_sense_1 = digitalRead(12); // LDR1 indefinitely until laser break is detected on LDR 1
    }

    you can just do
    while (digitalRead(12) == LOW); // poll LDR1 indefinitely until laser break is detected on LDR1

    (or better if you had defined pin 12 with a name, the code will be self documenting;

    const byte LDR1_Pin = 12;
    ...
    while (digitalRead(LDR1_Pin) == LOW) ;

    ———————————————————————

    so the code could become something like this:


    /*
    Created by
    Joe Palmer 16/04/2019
    Function: This program will power two laser dot projection units.
    Laser dots are aimed at LDR unit to create light gates.
    Total travel time between the two light gates is measured and displayed on LCD screen
    */
    // Using LiquidCrystal library
    #include

    // select the pins used on the LCD panel
    LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
    // define some values used by the panel and buttons
    #define btnRIGHT 0
    #define btnUP 1
    #define btnDOWN 2
    #define btnLEFT 3
    #define btnSELECT 4
    #define btnNONE 5
    unsigned long start_time = 0;
    unsigned long stop_time = 0;
    const byte laser1Pin = 1; // would be better on 2 as 1 is used by the Serial port which is convenient for debug
    const byte laser2Pin = 2; // would be better on 3
    const byte ldr1Pin = 12;
    const byte ldr2Pin = 13; // would be better on 10 to keep the built in LED
    const byte keyboardPin = A0;
    // read the buttons
    byte read_LCD_buttons()
    {
    int adc_key_in = analogRead(keyboardPin); // read the value from the sensor
    // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
    // we add approx 50 to those values and check to see if we are close
    if (adc_key_in > 1500) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
    if (adc_key_in < 50) return btnRIGHT;
    if (adc_key_in < 195) return btnUP;
    if (adc_key_in < 380) return btnDOWN;
    if (adc_key_in < 500) return btnLEFT;
    if (adc_key_in < 700) return btnSELECT;
    return btnNONE; // when all others fail, return this...
    }

    void setup()
    {
    lcd.begin(16, 2); // start the library
    lcd.clear(); // clear and position cursor at 0,0
    lcd.print(F("Initialising")); // print a welcome message
    pinMode(laser1Pin, OUTPUT); //Laser1
    pinMode(laser2Pin, OUTPUT); //Laser2
    pinMode(ldr1Pin, INPUT); //LDR1
    pinMode(ldr2Pin, INPUT); //LDR2
    }

    void loop()
    {
    digitalWrite(laser1Pin, LOW); // Turn laser 1 OFF
    digitalWrite(laser2Pin, LOW); // Turn laser 2 OFF
    lcd.setCursor(0, 1); // move cursor to second line "1"
    lcd.print(F("select to start")); // prompt user to press select button
    while (read_LCD_buttons() != btnSELECT) ; // read the buttons indefinetely until user presses select
    digitalWrite(laser1Pin, HIGH); //Turn on laser 1
    digitalWrite(laser2Pin, HIGH); // Turn on laser 2
    lcd.clear(); //clear LCD
    lcd.print(F("waiting laser"));
    while (digitalRead(ldr1Pin) == HIGH || digitalRead(ldr2Pin) == HIGH) ; // read both LDRs indefinitely until laser is detected on both.
    // no light is 4.8V so HIGH, light detected is 0V so LOW
    lcd.clear(); //clear LCD
    lcd.print("armed"); //
    while (digitalRead(ldr1Pin) == LOW) ; // poll LDR1 indefinitely until laser break is detected on LDR 1
    start_time = millis();
    lcd.clear(); //clear LCD
    lcd.print(F("timing"));
    while (digitalRead(ldr2Pin) == LOW) ; // poll LDR2 indefinitely until laser break is detected on LDR 2
    stop_time = millis();
    lcd.clear(); //clear LCD
    lcd.print(F("T=")); //
    lcd.print(stop_time - start_time); // will fit in the 14 characters left on first line
    // here the loop will loop and print the "select to start" message on second line to restart a measure and make sure lasers are off
    }


    ———————————————————————
    additional note:

    With the function createChar() it is possible to create and display custom characters on the LCD. So if you want to print a nice "∆t = " message at the end with a greek delta, you could define a custom character for ∆ in a 5 x 8 dot character pattern.

    this code would do that for example:

    #include
    LiquidCrystal lcd(8, 9, 4, 5, 6, 7);

    const byte deltaCharacter[8] = // a 1 is a dot turned on in LCD, 0 turned off
    {
    0b00000,
    0b00100,
    0b00100,
    0b01010,
    0b01010,
    0b10001,
    0b11111,
    0b00000
    };

    void setup()
    {
    lcd.begin(16, 2); // start the library
    lcd.createChar(0, delta); // create the character ∆

    // print '∆t='
    lcd.clear();
    lcd.write(0); // display the custom character
    lcd.print("t=");
    }

    void loop() {}

    ———————————————————————
    hope this helps