Sadly this was not properly tested on any real life simulations with actualy obstacles(other than my hand, some books and a couple of pens). Keep that in mind!
This is a simple, clean, and maintainable lap timer tested only for an ESP32 using a single IR (infrared) sensor. It is designed to be highly robust, with no display required. The entire "UI" is handled through the built-in BOOT button and the Arduino Serial Monitor.
This project is built using basic C++ with an object-oriented, multi-file structure for maximum maintainability and readability.
- 🦾State Machine Logic: The timer operates on a clean, easy-to-understand state machine.
- 🤖 Robust Sensor Calibration: A guided, multi-sample averaging process ensures the sensor threshold is set reliably, even with noisy sensors. It supports the full
0(broken) to4095(clear) ADC range. - ⚙️ Intelligent Lap Timing: A lap is not just a simple "tripwire." It is defined as the time from the lap start until the moment an obstacle clears the beam, providing a more accurate "finish line" behavior.
- 🤖 Obstacle Time: The timer also records how long the beam was broken, giving you a new data point for analysis.
- 🦾 Hardware Feedback: The built-in LED turns ON when the beam is broken and OFF when it is cleared(this worked almost all of the times).
- ⚙️ Clean Code: Logic is encapsulated into separate classes (
LapTimer,Sensor,Button,RaceTimer) to make the code easy to read and extend. - ⚙️ Race Summary: Automatically prints a formatted table of all completed laps, total time, and obstacle time.
You only need an ESP32 and a 3-pin IR sensor module.
- IR Sensor (Analog Out):
Sensor A0->ESP32 GPIO 32- (This must be a safe ADC1 pin, like 32, 33, 34, 35, 36, or 39, for it to work right)
- (Check pin layout to make sure that you connect everything right)
- BOOT Button:
GPIO 0(Built-in to most ESP32 dev boards,such as mine) - LED:
GPIO 2(The built-in blue LED that mine had)
This entire project is controlled using the Serial Monitor and the BOOT button.
- Connect your ESP32, build, and upload the code.
- Open the Serial Monitor at 115200 baud. You will be greeted with the first step.
The timer must first learn what a clear, unbroken beam looks like.
- Action: Ensure the sensor beam is CLEAR (no robot in the way).
- Action: Press and HOLD the
BOOTbutton. - The blue LED will blink rapidly as it takes multiple samples.
- Action: Release the button after 1-2 seconds.
- If the reading is valid (e.g.,
>= 1000), it will be accepted, and you'll move to the next step.
Now, the timer needs to learn what an obstacle looks like.
- Action: Place your robot or hand to BREAK the sensor beam.
- Action: Press and HOLD the
BOOTbutton. - The blue LED will blink as it samples the "broken" value.
- Action: Release the button.
- If the reading is valid (e.g.,
< 1000), the system will calculate the midpoint threshold and arm the timer.
The system is now active and "Waiting for robot..."
- Action: Let the robot cross the line.
- The timer will detect the first line cross and enter a "Pending Start" state.
- The Serial Monitor will display:
ACTION: Start line detected. Press BOOT to start race. - Action: Do a single, short press of the
BOOTbutton to confirm. - The race immediately starts, and the timer is running for Lap 1.
The race is now active. This is where this logic comes in:
- When the robot first breaks the beam:
- The built-in blue LED will turn ON.
- An "obstacle timer" starts, visible in the
heartbeatstatus.
- The system waits while the beam remains broken. The lap is not finished yet.
- When the robot clears the beam:
- The built-in blue LED turns OFF.
- The lap is now officially finished.
- The system records the Total Lap Time (from start-to-clear) and the Obstacle Time (from broken-to-clear).
- The lap summary is printed, and the next lap begins immediately.
You can stop the race at any time.
- Action: Press and HOLD the
BOOTbutton for 2 seconds. - The race session will immediately end.
- A "RACE SUMMARY" table will be printed, showing all completed laps, their total times, and their obstacle times.
- The system will enter a "Finished" state. To run another race, you must restart the ESP32.
This project is broken into classes to separate concerns.
-
src/main.cpp- The main Arduino entry point.
- It only contains a single global
LapTimerobject. - It calls
timer.begin()insetup()andtimer.update()inloop().
-
src/LapTimer.h/src/LapTimer.cpp- This is the main "application" class.
- It owns the Sensor, Button, and RaceTimer objects.
- It contains the main
Statemachine enum. - It directs all the logic (e.g., "in this state, check for a button press; in that state, check for a sensor cross").
- It manages the "is obstacle detected" sub-state during a race.
-
src/Sensor.h/src/Sensor.cpp- Handles all sensor-specific logic.
- Reads the ADC pin.
- Contains the calibration logic (averaging samples).
- Provides a simple
isBeamBroken()method.
-
src/Button.h/src/Button.cpp- A reusable button-handling class.
- Manages all debouncing logic.
- Detects
isPressed(),wasShortPressed(), andwasLongPressed().
-
src/RaceTimer.h/src/RaceTimer.cpp- Handles all time-keeping and data storage.
- Stores lap times in a
std::vector<LapData>, which now includesobstacleTimeMs. - Provides methods like
start(),stop(), andrecordLap(). - Contains the
printSummary()logic, now with the new "Obstacle (s)" column.
This project is set up for PlatformIO.
- Make sure you have Visual Studio Code with the PlatformIO IDE extension installed.
- Clone or download this repository.
- Open the project folder in VS Code (it should automatically detect it's a PlatformIO project).
- Click the "Build" and "Upload" buttons in the PlatformIO toolbar (usually at the bottom of the VS Code window).
- Open the Serial Monitor to begin.
Copyright © 2026 Je0Dev
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.