The VL53L0X measures distance by timing a pulse of invisible laser light — millimetre steps up to about 1.2 m, in a beam far narrower than an ultrasonic sensor’s.
On the ESP32 DevKit V1 it is an I2C part on D21/D22 at 3.3 V, so there is nothing to level-shift (unlike the 5 V HC-SR04). The sketch uses Pololu’s VL53L0X library in continuous mode and prints the distance ten times a second.
Wiring
| ESP32 DevKit V1 | VL53L0X ToF distance sensor | Wire | Notes |
|---|---|---|---|
| 3V3 | VIN | red | Boards with a regulator accept 3.3–5 V; 3V3 also suits the rarer bare 2.8 V boards without one. |
| GND | GND | black | |
| D22 | SCL | yellow | |
| D21 | SDA | blue |
2.6–5 V (on-board regulator) part, 3.3 V board. Breakouts with a regulator and level shifting accept 3.3 V or 5 V. Bare boards without them are 2.8 V-only — check the listing.
Board quirks
- XSHUT and GPIO1 can stay unconnected for one sensor. For several sensors (all start at 0x29), wire each XSHUT to its own GPIO such as D25, D26, D27, wake them one by one and give each a new address with
setAddress(). - Peel the yellow/orange protective film off the sensor window. With it on, readings stick around 20–60 mm or time out.
- Range is about 1.2 m in normal mode indoors, less in sunlight. Black or shiny targets return less light. For up to 4 m use the VL53L1X.
- The sketch runs I2C at 400 kHz and a 50 ms timing budget, a good balance for robots and parking sensors. A shorter budget is faster but noisier.
Code
// VL53L0X time-of-flight distance (up to ~1.2 m indoors) on an ESP32 DevKit V1.
// VIN → 3V3, GND → GND, SCL → D22, SDA → D21. GPIO1 and XSHUT not needed for one sensor.
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X tof;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.setClock(400000); // the VL53L0X supports fast-mode I2C
tof.setBus(&Wire);
tof.setTimeout(500);
if (!tof.init()) {
Serial.println("No VL53L0X at 0x29 — check wiring, and peel off the protective film");
while (true) delay(1000);
}
tof.setMeasurementTimingBudget(50000); // 50 ms: steadier than the 33 ms default
tof.startContinuous();
Serial.println("VL53L0X ready");
}
void loop() {
uint16_t mm = tof.readRangeContinuousMillimeters();
if (tof.timeoutOccurred()) {
Serial.println("Timeout — sensor stopped answering");
} else if (mm >= 8190) {
Serial.println("Out of range"); // 8190/8191 = no target seen
} else {
Serial.printf("Distance: %u mm\n", mm);
}
delay(100);
}// VL53L0X time-of-flight distance (up to ~1.2 m indoors) on an ESP32 DevKit V1.
// VIN → 3V3, GND → GND, SCL → D22, SDA → D21. GPIO1 and XSHUT not needed for one sensor.
#include <Wire.h>
#include <VL53L0X.h>
VL53L0X tof;
void setup() {
Serial.begin(115200);
Wire.begin(21, 22);
Wire.setClock(400000); // the VL53L0X supports fast-mode I2C
tof.setBus(&Wire);
tof.setTimeout(500);
if (!tof.init()) {
Serial.println("No VL53L0X at 0x29 — check wiring, and peel off the protective film");
while (true) delay(1000);
}
tof.setMeasurementTimingBudget(50000); // 50 ms: steadier than the 33 ms default
tof.startContinuous();
Serial.println("VL53L0X ready");
}
void loop() {
uint16_t mm = tof.readRangeContinuousMillimeters();
if (tof.timeoutOccurred()) {
Serial.println("Timeout — sensor stopped answering");
} else if (mm >= 8190) {
Serial.println("Out of range"); // 8190/8191 = no target seen
} else {
Serial.printf("Distance: %u mm\n", mm);
}
delay(100);
}Library: VL53L0X (Pololu) — compiled with VL53L0X 1.3.1.
Expected serial output
VL53L0X ready
Distance: 312 mm
Distance: 309 mm
Out of rangeVL53L0X ready Distance: 312 mm Distance: 309 mm Out of range
Common errors
“No VL53L0X at 0x29”
SDA/SCL swapped or XSHUT pulled low. If you wired XSHUT, drive it HIGH. Run the I2C scanner.
Readings stuck at 8190 or 8191 mm
No target within range, or the film is still on. Point it at a white wall 30 cm away to test.
“Timeout” after running for a while
Loose breadboard contact or a brown-out on the sensor. Solder the header and add 10 µF across VIN–GND.