A Parking Sensor on the VL53L0X Laser Range Finder: LCD and Buzzer
The VL53L0X measures distance by the time of flight of a laser pulse: up to 1.2–2 m with millimetre resolution. We build a parking sensor: centimetres and a bar on an LCD 1602, and a buzzer that beeps faster as the obstacle gets closer.
About the project
The VL53L0X is a tiny laser range finder (ToF, time-of-flight) from ST. It sends an invisible 940 nm infrared pulse and counts how many picoseconds the light takes to come back. Unlike the ultrasonic HC-SR04, it has a narrow beam, copes with soft surfaces and talks I2C. We build a parking sensor for the garage.
What you need
- ESP32 DevKit
- VL53L0X module (GY-530 or Pololu)
- LCD 1602 with an I2C backpack
- A buzzer (passive or active) and a 100 Ω resistor
- The VL53L0X (Pololu) and LiquidCrystal_I2C libraries
Wiring
The VL53L0X and the LCD share one I2C bus, at addresses 0x29 and 0x27:
| Module | VIN/VCC | GND | SDA | SCL |
|---|---|---|---|---|
| VL53L0X | 3V3 | GND | GPIO21 | GPIO22 |
| LCD 1602 | 3V3 | GND | GPIO21 | GPIO22 |
Buzzer: GPIO25 → 100 Ω → buzzer "+", "−" → GND. The resistor limits the pin current; without it the simulator warns about exceeding 20 mA.
The GPIO1 (measurement-ready interrupt) and XSHUT (shutdown) pins can stay unconnected. XSHUT matters with several sensors: they all share address 0x29, so you enable them one by one and reassign the addresses with setAddress().
How it works
startContinuous()makes the sensor measure non-stop, ~33 ms per reading (the default timing budget).readRangeContinuousMillimeters()returns the latest value in mm. 8190/8191 means nothing is within range.setTimeout(500)+timeoutOccurred()keep the sketch from hanging if the sensor drops off the bus.- Range: ~1.2 m in the standard mode, up to 2 m in "long range" (
setSignalRateLimit(0.1)plus longer VCSEL pulses), at the cost of accuracy.
Code
// Parking sensor: VL53L0X laser rangefinder + LCD 1602 (both on I2C) + buzzer
#include <Wire.h>
#include <VL53L0X.h>
#include <LiquidCrystal_I2C.h>
VL53L0X sensor;
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int BUZZER = 25;
unsigned long lastBeep = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
lcd.init();
lcd.backlight();
sensor.setTimeout(500);
if (!sensor.init()) {
lcd.print("VL53L0X error");
while (true) delay(1000);
}
sensor.startContinuous();
pinMode(BUZZER, OUTPUT);
}
void loop() {
uint16_t mm = sensor.readRangeContinuousMillimeters();
bool valid = !sensor.timeoutOccurred() && mm < 8000; // 8190 = nothing in range
lcd.setCursor(0, 0);
if (valid) {
lcd.print("Distance: ");
lcd.print(mm / 10);
lcd.print(" cm ");
} else {
lcd.print("Too far ");
}
lcd.setCursor(0, 1);
int bars = valid ? constrain(map(mm, 50, 1000, 16, 0), 0, 16) : 0;
for (int i = 0; i < 16; i++) lcd.write(i < bars ? 0xFF : ' '); // 0xFF = a filled cell
// the closer, the faster the beeps; a continuous tone under 10 cm
if (!valid || mm > 1000) {
noTone(BUZZER);
} else if (mm < 100) {
tone(BUZZER, 2000);
} else {
unsigned long interval = map(mm, 100, 1000, 80, 800);
if (millis() - lastBeep > interval) {
tone(BUZZER, 2000, 40);
lastBeep = millis();
}
}
}
Good to know
- Colour and material. Matte black plastic reflects poorly, and the range drops to 50–70 cm. Glass and mirrors can give false distances.
- Bright sunlight blinds the sensor, so the range is noticeably shorter outdoors.
- A cover glass in front of the sensor, without special calibration, causes crosstalk and a "permanent 3 cm". An open window is better.
- The VL53L1X is the big brother: up to 4 m and a programmable field of view, with an almost identical library.