The VL53L1X measures distance with an invisible laser and a time-of-flight detector: up to about 4 m, independent of colour, in a package smaller than a fingernail. It suits people counters, parking guides, tank levels and robot obstacle sensing.
On the ESP32 DevKit V1 it uses the default I2C pins D21/D22 at 3.3 V, plus an optional XSHUT wire on D4 that the sketch uses to hard-reset the sensor at start-up. The sketch runs it in long mode with a 50 ms timing budget and prints the distance, range status and signal strength.
Wiring
| ESP32 DevKit V1 | VL53L1X time-of-flight distance sensor (up to 4 m) | Wire | Notes |
|---|---|---|---|
| 3V3 | VIN | red | Breakouts with VIN have their own 2.8 V regulator. The sensor draws about 20 mA while ranging. |
| GND | GND | black | |
| D22 | SCL | yellow | |
| D21 | SDA | blue | |
| D4 | XSHUT | orange | Driven LOW to switch the sensor off, otherwise released (input). Never driven HIGH. |
2.6–5.5 V on boards with a regulator (VIN) part, 3.3 V board. Breakouts with a VIN pin have a 2.8 V regulator and level shifting, so ESP32, Pico and Uno all work. Bare-chip boards without a regulator are 2.6–3.5 V only — check for the regulator before giving it 5 V.
Board quirks
- XSHUT is pulled up to the sensor’s own 2.8 V supply on most breakouts and is often not level-shifted. The sketch only ever pulls it LOW or lets it float (INPUT), so 3.3 V is never forced into it.
- A short XSHUT pulse at start-up gives the sensor a clean reset after an ESP32 crash or upload — without it the VL53L1X can stay stuck mid-measurement and
init()fails until you unplug it. - With several sensors, XSHUT is how you give each one its own address: hold all but one LOW, call
setAddress(), release the next, and so on. One GPIO per sensor. - Long mode reaches about 4 m indoors on a white wall but is easily blinded by sunlight; Short mode stops at about 1.3 m and copes much better outdoors. A longer timing budget (e.g. 100 ms) steadies long-range readings.
- The range status matters: only “range valid” readings are trustworthy. “signal fail” or “wrap target fail” mean the target is too far, too dark or outside the beam.
- Compared with the VL53L0X (2 m), the L1X reaches twice as far and can narrow its field of view (ROI), but costs more.
Code
// VL53L1X time-of-flight sensor on an ESP32 DevKit V1 — long mode, up to about 4 m indoors.
// VIN → 3V3, GND → GND, SDA → D21, SCL → D22, XSHUT → D4 (driven LOW or released, never driven HIGH).
#include <Wire.h>
#include <VL53L1X.h>
constexpr uint8_t XSHUT_PIN = 4;
VL53L1X sensor;
void setup() {
Serial.begin(115200);
// Hard-reset the sensor: XSHUT LOW = off. Then release it (INPUT): the board's pull-up turns it on.
pinMode(XSHUT_PIN, OUTPUT);
digitalWrite(XSHUT_PIN, LOW);
delay(10);
pinMode(XSHUT_PIN, INPUT);
delay(10);
Wire.begin(21, 22);
Wire.setClock(400000);
sensor.setTimeout(500);
if (!sensor.init()) {
Serial.println("No VL53L1X at 0x29 - check wiring, and that XSHUT is not held LOW");
while (true) delay(1000);
}
sensor.setDistanceMode(VL53L1X::Long); // Short ≈ 1.3 m (copes better with sunlight), Long ≈ 4 m indoors
sensor.setMeasurementTimingBudget(50000); // 50 ms per reading; longer = steadier at long range
sensor.startContinuous(50);
Serial.println("VL53L1X ready, long mode, 50 ms");
}
void loop() {
uint16_t mm = sensor.read();
if (sensor.timeoutOccurred()) {
Serial.println("Timeout");
return;
}
Serial.printf("%u mm status: %s signal %.1f MCPS\n", mm, VL53L1X::rangeStatusToString(sensor.ranging_data.range_status),
sensor.ranging_data.peak_signal_count_rate_MCPS);
delay(200);
}// VL53L1X time-of-flight sensor on an ESP32 DevKit V1 — long mode, up to about 4 m indoors.
// VIN → 3V3, GND → GND, SDA → D21, SCL → D22, XSHUT → D4 (driven LOW or released, never driven HIGH).
#include <Wire.h>
#include <VL53L1X.h>
constexpr uint8_t XSHUT_PIN = 4;
VL53L1X sensor;
void setup() {
Serial.begin(115200);
// Hard-reset the sensor: XSHUT LOW = off. Then release it (INPUT): the board's pull-up turns it on.
pinMode(XSHUT_PIN, OUTPUT);
digitalWrite(XSHUT_PIN, LOW);
delay(10);
pinMode(XSHUT_PIN, INPUT);
delay(10);
Wire.begin(21, 22);
Wire.setClock(400000);
sensor.setTimeout(500);
if (!sensor.init()) {
Serial.println("No VL53L1X at 0x29 - check wiring, and that XSHUT is not held LOW");
while (true) delay(1000);
}
sensor.setDistanceMode(VL53L1X::Long); // Short ≈ 1.3 m (copes better with sunlight), Long ≈ 4 m indoors
sensor.setMeasurementTimingBudget(50000); // 50 ms per reading; longer = steadier at long range
sensor.startContinuous(50);
Serial.println("VL53L1X ready, long mode, 50 ms");
}
void loop() {
uint16_t mm = sensor.read();
if (sensor.timeoutOccurred()) {
Serial.println("Timeout");
return;
}
Serial.printf("%u mm status: %s signal %.1f MCPS\n", mm, VL53L1X::rangeStatusToString(sensor.ranging_data.range_status),
sensor.ranging_data.peak_signal_count_rate_MCPS);
delay(200);
}Library: VL53L1X (Pololu) — compiled with VL53L1X 1.3.1.
Expected serial output
VL53L1X ready, long mode, 50 ms
1342 mm status: range valid signal 4.8 MCPS
1339 mm status: range valid signal 4.9 MCPS
3872 mm status: signal fail signal 0.3 MCPSVL53L1X ready, long mode, 50 ms 1342 mm status: range valid signal 4.8 MCPS 1339 mm status: range valid signal 4.9 MCPS 3872 mm status: signal fail signal 0.3 MCPS
Common errors
“No VL53L1X at 0x29”
Check SDA/SCL, and that XSHUT isn’t wired to GND or held LOW. Run the I2C scanner — a VL53L0X also answers at 0x29 but needs a different library.
Readings stuck at 0 or a fixed value
The protective film is still on the sensor window, or a cover glass reflects the laser. Remove the film; cover glass needs crosstalk calibration.
Works indoors, fails outside
Sunlight swamps the 940 nm laser. Use Short mode and shade the sensor.