Community project

Laser Beam Timer with Sound Signal

Photo of Laser Beam Timer with Sound Signal
Generated with AI

Felix Spelletjes

Published November 15, 2026

This project builds a laser-based timer that detects when a beam is broken and plays a sound signal to mark the event. Using an Arduino Uno, a laser module creates the start beam while a photoresistor detects when it's interrupted. The DFPlayer Mini audio module plays a customizable sound through an 8Ω speaker to announce the finish time.

The guide provides a complete wiring diagram, parts list, and step-by-step assembly instructions for mounting the laser safely and positioning the light detector. Firmware handles beam calibration, threshold tuning via serial commands, and audio playback, with all configuration options explained so makers can adapt the timer for races, experiments, or other timed events.

Wiring diagram

Wiring diagram for Laser Beam Timer with Sound Signal
Loading diagram…

Gather all the parts

QtyComponent
1

Keyestudio KY-008 laser module (S, NC, minus)

5V 650nm module with built-in current resistor; S is power, middle pin unused.

1

Photo Cell (CdS Photoresistor)

CdS light-dependent resistor. Resistance decreases as light increases. Adafruit product 161 currently specifies approximately 1 kΩ in light and 10 kΩ in dark. Use either symmetric lead in a voltage divider and read the divider with an analog input.

1

Resistor

10 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Push Button

Momentary push button switch

1

DFPlayer Mini MP3 Audio Module

Small UART-controlled MP3 playback module with a microSD card slot and onboard audio DAC/amplifier interface. The MCU controls playback over serial while speaker/DAC pins connect to the audio output path.

1

Resistor

1 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

Resistor

2 kΩ

Through-hole resistor (current-limiting in series with an LED)

1

8Ω Speaker

8 Ω, 3 W

Generic small 8Ω 0.5-3W loudspeaker (~28mm typical). Pair with an I2S amp (MAX98357A) or class-D amp (TPA3116D2) for usable volume; do not drive directly from a GPIO pin. Audio output for music/voice playback.

1

Electrolytic Capacitor

470 µF, 10 V

Radial aluminium electrolytic capacitor bought by value and voltage rating, such as 10 µF, 100 µF or 1000 µF 10 V. It is polarised: the + lead goes to the higher voltage and the - lead, marked by the stripe on the sleeve, to the lower voltage or ground. It is mostly used for bulk filtering across a power rail, with a voltage rating comfortably above the rail.

Assemble it in 5 steps

1. Build the light detector

With USB unplugged, connect one LDR leg to 5V. Connect its other leg to A0 and to the 10 kΩ resistor. Connect the other resistor leg to GND. Put the LDR inside a short dark tube to reduce room light.

2. Fit the start button

Connect the button between D2 and GND. The board uses its internal pull-up resistor. With a four-legged button, use legs on opposite switched sides.

3. Wire the sound module

Connect DFPlayer VCC to 5V and GND to GND. TX goes to D10. D11 goes through 1 kΩ to RX, with 2 kΩ from RX to GND. This reduces the 5V signal to about 3.3V. Connect the speaker only between SPK1 and SPK2. Fit the 470 µF capacitor across VCC and GND, with its stripe facing GND. Keep volume modest for USB power.

  • Never connect either speaker lead to GND. Reversed capacitor polarity can damage it.

4. Prepare the starting sound

Format a microSD card up to 32 GB as FAT32. Make an mp3 folder and copy your starting sound as 0001.mp3. Insert the card into the DFPlayer with power unplugged. The card fits its built-in slot; no separate reader module is wired.

5. Mount the finish beam safely

Use the Keyestudio-style KY-008 with S as the power pin and minus as ground. Connect S directly to 5V, minus to GND, and leave the middle NC pin unused. Aim at the LDR across a short indoor lane. Fix both firmly, below eye level, with an opaque beam stop behind the LDR. The laser stays on whenever USB is connected.

  • Never look into the beam or aim at people or reflective surfaces. A 5 mW laser can injure eyes. Do not use where children or spectators can enter the beam. Other KY-008 sellers can reverse pin labels; use the specified variant.

Review all connections

1. Connections between "laser" and "Arduino"

FunctionlaserArduino
powerS5V
groundGNDGND

2. Connections between "ldr" and "Arduino"

FunctionldrArduino
powerTerminal 15V

3. Connections between "light_resistor" and "Arduino"

Functionlight_resistorArduino
digitalP1GPIO 14
digitalP1 → Photo Cell (CdS Photoresistor) Terminal 2EXT
groundP2GND

4. Connections between "start_button" and "Arduino"

Functionstart_buttonArduino
digitalSIGNALGPIO 2
groundGNDGND

5. Connections between "audio" and "Arduino"

FunctionaudioArduino
powerVCC5V
groundGNDGND
uartTXGPIO 10

6. Connections between "rx_top" and "Arduino"

Functionrx_topArduino
digitalP1GPIO 11
digitalP2 → DFPlayer Mini MP3 Audio Module RXEXT

7. Connections between "rx_bottom" and "Arduino"

Functionrx_bottomArduino
digitalP1 → DFPlayer Mini MP3 Audio Module RXEXT
groundP2GND

8. Connections between "speaker" and "Arduino"

FunctionspeakerArduino
analogPOS → DFPlayer Mini MP3 Audio Module SPK1EXT
analogNEG → DFPlayer Mini MP3 Audio Module SPK2EXT

9. Connections between "audio_cap" and "Arduino"

Functionaudio_capArduino
power+ → DFPlayer Mini MP3 Audio Module VCCEXT
ground-GND

Deploy the firmware

#include <Arduino.h>
#include <SoftwareSerial.h>
#include <DFRobotDFPlayerMini.h>
SoftwareSerial audioSerial(10,11);
DFRobotDFPlayerMini player;
const uint8_t BUTTON=2, LIGHT=A0;
bool running=false, calibrated=false, audioOK=false;
int darkValue=0, brightValue=1023, threshold=512, margin=20;
uint32_t started=0, lastStatus=0, changed=0;
bool previous=HIGH, stable=HIGH;
char command[48]; uint8_t used=0;
void handle(const char* c){
 if(!strcmp(c,"DARK") || !strcmp(c,"BRIGHT")){
  if(running)return;
  long sum=0; for(int i=0;i<50;i++){sum+=analogRead(LIGHT);delay(2);}
  if(!strcmp(c,"DARK")){darkValue=sum/50;Serial.println(F("CAL,DARK"));}
  else {brightValue=sum/50;calibrated=brightValue-darkValue>=80;threshold=(brightValue+darkValue)/2;Serial.println(calibrated?F("CAL,OK"):F("ERROR,Insufficient light contrast"));}
 }
 if(!strncmp(c,"SET,",4)){
  if(running){Serial.println(F("ERROR,Cancel the run before calibrating"));return;}
  int t,m; char extra;
  if(sscanf(c+4,"%d,%d%c",&t,&m,&extra)==2 && t>=1 && t<=1022 && m>=0 && m<=100 && t+m<=1023){
   threshold=t;margin=m;calibrated=true;
   Serial.print(F("CAL,MANUAL,"));Serial.print(threshold);Serial.print(',');Serial.println(margin);
  }else Serial.println(F("ERROR,Invalid threshold or margin"));
 }
 if(!strcmp(c,"RESET")){running=false;Serial.println(F("RESET"));}
}
void setup(){
 pinMode(BUTTON,INPUT_PULLUP);Serial.begin(115200);audioSerial.begin(9600);
 audioOK=player.begin(audioSerial,true,true);
 if(audioOK)player.volume(15);
 Serial.println(F("READY"));
 if(!audioOK)Serial.println(F("ERROR,Check DFPlayer and SD card; restart board"));
}
void loop(){
 while(Serial.available()){char c=Serial.read();if(c=='\n'){command[used]=0;handle(command);used=0;}else if(c!='\r' && used<sizeof(command)-1)command[used++]=c;}
 uint32_t now=millis();bool b=digitalRead(BUTTON);
 if(b!=previous){previous=b;changed=now;}
 if(now-changed>=25 && b!=stable){stable=b;if(!b && !running){
  if(!calibrated)Serial.println(F("ERROR,Calibrate first"));
  else if(!audioOK)Serial.println(F("ERROR,Audio unavailable"));
  else if(analogRead(LIGHT)<threshold+margin)Serial.println(F("ERROR,Beam blocked or misaligned"));
  else {started=micros();running=true;Serial.println(F("START"));player.playMp3Folder(1);}
 }}
 if(running && micros()-started>=3600000000UL){running=false;Serial.println(F("ERROR,Run exceeded one hour; cancelled"));}
 if(running && analogRead(LIGHT)<threshold){uint32_t elapsed=micros()-started;running=false;Serial.print(F("STOP_US,"));Serial.println(elapsed);}
 if(millis()-lastStatus>=100){lastStatus=millis();Serial.print(F("LIGHT,"));Serial.print(analogRead(LIGHT));Serial.print(',');Serial.print(threshold);Serial.print(',');Serial.println(margin);if(running){Serial.print(F("TIME_US,"));Serial.println(micros()-started);}}
}

Remix this project

Make it yours in one click

Open a full copy of this project in your own Schematik workspace, diagram, code, parts, and assembly steps included. Swap the sensor, add features, or redesign the whole thing with AI. The author's original stays untouched.

Open in Schematik