Skip to content
SensorsDistance

VL53L0X Time-of-Flight Distance Sensor

A tiny laser time-of-flight ranger that measures distance in millimetres over I²C, accurate to a few percent and far narrower than ultrasonic.

Resolution
1 mm output
Field of view
About 25° cone

What is the VL53L0X?

The VL53L0X is a tiny distance sensor that works like a very fast stopwatch for light. It fires a short pulse of invisible infrared laser light, waits for it to bounce back off whatever is in front of it, and measures how long the trip took. Light travels about 30 cm in a nanosecond, so the sensor has to resolve differences of a few picoseconds to tell millimetres apart. ST Microelectronics packs that whole machine, a laser, a light detector and the timing electronics, into a module smaller than a grain of rice.

This technique is called time of flight (ToF). It is the same idea as the ultrasonic HC-SR04, except the pulse is light and not sound. Light is much faster and much more focused than ultrasound, so the VL53L0X gives you a narrow beam, millimetre output, quick updates and a clean answer from small targets that a wide ultrasonic cone would miss.

You will find it on a small breakout board with four wires that matter: power, ground, SDA and SCL. It talks I²C, works with Arduino and ESP32 libraries from Adafruit and Pololu, and uses just two pins that you can share with an OLED, an IMU and other I²C devices. It is a favourite for line-following and obstacle-avoiding robots, drone landing-height hold, smart bins, touchless switches, parking guides and liquid level checks.

Warning

The VL53L0X is a single-point sensor with a narrow beam. It does not measure an area, and it cannot see transparent glass, mirrors or very dark, shiny surfaces reliably. Strong sunlight reduces its range a lot, so test it in the conditions where you will really use it.

Specifications

Sensor
ST VL53L0X time-of-flight ranging module with a 940 nm VCSEL laser and SPAD detector array
Measuring range
About 30 mm to 1.2 m in default mode, up to about 2 m in long-range mode under good conditions
Resolution
1 mm output
Accuracy
Typically ±3 % in the default, long-range and high-accuracy modes (±5 % in high-speed mode); depends on target and light
Field of view
About 25° cone
Timing budget
20 ms (high speed), 33 ms (default) up to 200 ms (high accuracy)
Chip supply voltage
2.6 V – 3.5 V (breakouts add a regulator and usually accept 3.3–5 V on VIN; check yours)
Current draw
Roughly 19 mA average while ranging; a few µA in hardware standby
Interface
I²C up to 400 kHz, with a GPIO1/INT output and an XSHUT reset input
I²C address
0x29 by default, changeable in software after boot
Laser safety
Class 1 (eye-safe under normal use, per ST)
Size (bare module)
4.4 × 2.4 × 1.0 mm; breakouts are roughly 10–25 mm wide

Pinout

PinNameDescription
1VINPower for the board. Most breakouts accept 3.3–5 V and regulate it down
2GNDGround
3SCLI²C clock
4SDAI²C data
5GPIO1 (INT)Optional interrupt output that signals new data or a threshold event
6XSHUTActive-LOW shutdown. Used to reset the sensor and to give each sensor its own address

Swipe sideways to see every column →

Pin order varies between breakouts (Adafruit, Pololu and the cheap GY-530 all differ), so always read the silkscreen. For a single sensor you only need VIN, GND, SCL and SDA. GPIO1 and XSHUT can stay unconnected. If you run several VL53L0X sensors on one bus, each one needs its own XSHUT wire to a spare GPIO so you can boot them one at a time and give each a new I²C address. The sensor chip itself is a 2.8 V device, so cheap boards without level shifting need care on a 5 V Uno: check that your breakout pulls SDA and SCL up to a safe voltage, or use a logic level shifter.

Variants

SensorTypical rangeField of viewInterfaceNotesPrice
VL53L0X30 mm–1.2 m (2 m long range)~25°I²CCheap, tiny, 1 mm output~$3–6
VL53L1Xup to 4 mSelectable 15–27°I²CLonger range, same style of library~$6–10
VL6180X0–100 mmNarrowI²CShort range plus ambient light sensor~$4–8
HC-SR04 (ultrasonic)20 mm–4 m~15° cone but wide in practiceTrigger/echoCheap, large beam, affected by soft targets~$1–3
GP2Y0A21 (IR analog)100–800 mmNarrowAnalogReading depends on colour and non-linear~$3–5

Swipe sideways to see every column →

The VL53L0X is the best default for short-range, millimetre-resolution projects such as robot obstacle avoidance, gesture and presence detection and liquid level sensing. Step up to the VL53L1X when you need more than about 2 m, and drop to an HC-SR04 if you simply need a cheap, wide-beam sensor and can live with a coarser reading.

How time of flight works

Inside the module there is a 940 nm infrared laser (a VCSEL, vertical-cavity surface-emitting laser) and a receiver made of SPADs, single-photon avalanche diodes, which are so sensitive they can register individual photons. The chip fires a pulse, starts counting, and stops the instant the reflected photons arrive. Distance is then speed of light multiplied by travel time and divided by two, because the light goes out and back.

ST's trick is that the sensor does not simply time one pulse. It fires many pulses and builds a histogram of the arrival times, then picks out the peak. That peak is the real target. Ambient light and stray reflections appear as a flat background, which the chip subtracts. This is why the sensor works indoors in normal light and why bright sunlight, which floods the receiver with infrared photons, makes the job harder.

The sensor also contains a small reference path inside the package, so the chip can measure the very first part of the pulse and correct for its own timing drift and temperature. You do not see any of this: you call a function, and it returns a distance in millimetres.

One important consequence is that target reflectivity still matters. Time of flight is far less sensitive to surface colour than the old analog infrared sensors, but a black, matte target reflects far fewer photons than a white one, so the signal is weaker and the maximum range is shorter. A grey card at 1 m might read perfectly, while a black object at the same distance may return nothing.

Ranging modes and timing budget

The VL53L0X lets you trade speed against range and accuracy. The two knobs are the ranging profile and the timing budget, the time the chip spends on one measurement. A longer budget collects more photons, which gives a more accurate reading and a longer range, but fewer readings per second.

ST describes four common profiles. The approximate values below come from ST's indoor test conditions with a white target (88 % reflectance) and a grey target (17 %). Your results depend on the target, the angle and the ambient light, so treat them as a guide and test.

ProfileTiming budgetApprox. range, white targetApprox. range, grey targetTypical accuracy
Default33 ms~1.2 m~0.7 m±3 %
High accuracy200 ms~1.2 m~0.7 m±1–3 % (best consistency)
Long range33 ms~2 m~1.2 m±3 %, worse in bright light
High speed20 ms~1.2 m~0.6 m±5 %

The Adafruit library exposes these through configSensor(), and you can set the budget with setMeasurementTimingBudgetMicroSeconds(). The Pololu library exposes the underlying settings: a signal-rate limit, the laser pulse periods and the timing budget.

Pick the profile that fits the job. A robot dodging walls wants high speed or the default profile. A tank level sensor that reads every few seconds can use high accuracy. A room-presence detector watching a doorway may use long range, though it will be more sensitive to sunlight.

Wiring: two data wires and you are done

The VL53L0X is easy to connect because it uses I²C. For a single sensor you connect four wires:

  • VIN to 5 V on an Arduino Uno, or 3.3 V on an ESP32
  • GND to GND
  • SDA to the board's I²C data pin: A4 on the Uno, GPIO21 on the ESP32
  • SCL to the board's I²C clock pin: A5 on the Uno, GPIO22 on the ESP32

GPIO1 (INT) and XSHUT can stay unconnected. They are only needed if you want a hardware interrupt or want to run several sensors on one bus.

The bare VL53L0X chip runs from 2.6 V to 3.5 V, so every breakout includes a small regulator. Adafruit's and Pololu's boards also include level shifting, which is why they run safely from 3.3 V or 5 V. Cheap GY-530-style boards are less consistent: some pull the I²C lines up to the supply, which on a 5 V Uno puts 5 V on a 2.8 V chip. If your sensor is a no-name board, read the schematic or use a logic level shifter, or at least power it from 3.3 V.

If the sensor is not found, run an I²C scanner. You should see address 0x29. If nothing appears, check power, SDA and SCL first, and note that a bent header pin is more common than a faulty sensor.

  1. 1Open the Arduino IDE and go to Tools → Manage Libraries.
  2. 2Search for "Adafruit VL53L0X" and install it, accepting the prompts to install Adafruit BusIO and Adafruit Unified Sensor.
  3. 3Wire VIN, GND, SDA and SCL as shown for your board.
  4. 4Open File → Examples → Adafruit VL53L0X → vl53l0x, or paste the sketch below.
  5. 5Select your board and port, upload the sketch and open the Serial Monitor at 115200 baud.
  6. 6Point the sensor at a flat wall and move it back and forth. The distance in millimetres should change smoothly.

Reading the sensor, line by line

Adafruit_VL53L0X lox = Adafruit_VL53L0X(); creates the sensor object. In setup(), lox.begin() starts I²C, boots the chip and returns false if it cannot find the sensor at address 0x29. The sketch stops there with a message, which is much better than silently printing nonsense.

In loop(), lox.rangingTest(&measure, false) triggers a single measurement and fills a data structure. The most important two fields are RangeMilliMeter, the distance in millimetres, and RangeStatus, which tells you whether to trust it.

A status of 0 means a valid reading. 4 means the sensor could not lock onto a target, which in practice means "out of range" or "nothing there". The library still puts a number in RangeMilliMeter in that case, and it is meaningless, so the sketch checks the status before using the value. Skipping that check is the classic beginner bug: you point the sensor at open air and it cheerfully reports 8190 mm.

RangeStatusMeaningWhat to do
0Valid rangeUse the value
1Sigma estimator failureWeak or noisy signal, treat as invalid
2Signal too lowTarget too far, too dark or too small
3Minimum range failureTarget closer than about 30 mm
4Phase failureOut of range or nothing detected
5Hardware failureCheck wiring and power
C++

Continuous ranging and filtering

rangingTest() is easy, but it blocks while it measures and starts a new measurement every call. For faster, smoother output, put the sensor in continuous mode: it measures back-to-back on its own and you simply pick up the latest value when it is ready. In the Adafruit library that is lox.startRangeContinuous(), then lox.isRangeComplete() and lox.readRange().

Even then, single readings from a laser ranger jitter by a few millimetres, and occasionally one is badly wrong because of a stray reflection or a hand passing through the beam. The cure is a small filter. A moving average smooths the noise, and a median of the last five samples removes the wild outliers. The median is the better choice for obstacle detection, because one bad reading cannot trigger an emergency stop.

The sketch below keeps the last five readings and prints the raw value next to their median. It ignores out-of-range results, the large values such as 8190 mm that the sensor returns when it sees nothing.

C++

Using more than one VL53L0X

Every VL53L0X powers up with the same I²C address, 0x29. If you connect two of them to the same bus without doing anything else, they both answer at once and neither works. The solution is the XSHUT pin.

XSHUT is an active-low reset. When it is held low, the sensor is switched off and does not respond on the bus. The standard sequence is:

  1. Wire each sensor's XSHUT to its own GPIO on the microcontroller.
  2. At boot, hold all the XSHUT pins low so every sensor is off.
  3. Release one sensor, start it and give it a new address using begin(newAddress). The address is kept in RAM until the sensor loses power.
  4. Release the next sensor, which still wakes at 0x29, and give it a different address.
  5. Repeat for each sensor. After that, all of them sit on the same two wires at different addresses.

The sketch below follows Adafruit's dual-sensor example for two sensors. The new addresses are lost when the sensor is power-cycled, so the setup code has to run on every boot.

On boards that are not level shifted, it is safer to release XSHUT by switching its pin to INPUT (and letting the board's own pull-up raise it) than to drive it HIGH from a 5 V board. Adafruit's boards include the level shifting, so driving the pin is fine there.

C++

Getting accurate readings

A VL53L0X is easy to use but not magic. The conditions around it change the number it reports.

Mind the surface. White and light-coloured matte surfaces return the strongest signal. Dark, black, glossy or very small targets return less, which shortens the range and increases noise. Mirrors and clear glass reflect or pass the beam, so the sensor can read the wrong distance or none at all.

Avoid strong infrared light. Direct sunlight, halogen lamps and some stage lighting flood the receiver with infrared and cut the usable range dramatically. For outdoor use, shade the sensor, limit your range expectations and test at the real time of day.

Keep the window clean and uncovered. Fingerprints, dust and any cover glass cause crosstalk, where some of the emitted light reflects straight back inside the module. That shows up as a constant offset, or as readings that refuse to go above a certain distance. If you must mount the sensor behind a window, leave a tiny air gap, use an infrared-transparent window and calibrate.

Respect the minimum distance. Below about 30 mm the readings become unreliable. Mount the sensor so the closest target is at least a few centimetres away.

Aim carefully. The beam is about 25 degrees wide, so at 1 m it covers a circle roughly half a metre across. A nearby object at the edge of the cone can steal the reading from your intended target.

Calibrate with two known distances. Measure a flat white target at, say, 100 mm and 500 mm with a ruler, then compare. If the sensor is consistently 10 mm high, subtract an offset in software. If the error grows with distance, apply a gain correction as well.

Troubleshooting

Almost every VL53L0X problem comes down to I²C wiring, power, address clashes or the target.

SymptomLikely causeFix
"Failed to boot VL53L0X"Wrong wiring, no power, wrong I²C pins or a damaged headerCheck VIN, GND, SDA and SCL, and run an I²C scanner. You should see 0x29
Reads 8190 or 8191 mmOut of range, or the status was ignoredCheck RangeStatus and treat anything other than 0 as no reading
Reading stuck at a fixed small valueCover glass, a finger, or a dirty window on the sensorClean it, remove the cover, or calibrate crosstalk
Values jump by tens of millimetresWeak signal from a dark or small target, or sunlightUse a lighter target, a longer timing budget or shade the sensor
Works alone, fails with a second sensorBoth on address 0x29Use XSHUT to boot them one at a time and give each a new address
Second sensor is lost after a resetAddresses are stored in RAM and reset on power lossRun the address setup code on every boot
Distance is always off by the same amountOffset or crosstalkMeasure at known distances and subtract a fixed offset
Short range, long-range mode not helpingBright ambient infraredLower the expectation or use the default profile in a shaded spot
Random I²C hangs on a 5 V boardLevel mismatch or long wiresUse a level shifted breakout, shorten the wires and add 4.7 kΩ pull-ups

What to build with it

The VL53L0X's mix of precision, speed and tiny size opens up many projects:

  • Obstacle-avoiding robot with one or more sensors at different angles.
  • Touchless switch or gesture sensor that triggers when a hand comes within 10 cm.
  • Smart bin or tank level that measures the distance to the contents.
  • Parking helper that shows how far a car is from the wall.
  • Drone or robot altitude hold over a short height, with filtering.
  • Door and people counter using two sensors a few centimetres apart to detect direction.
  • Desk height or presence sensor that wakes a display when you sit down.

For safety-critical uses such as stopping a machine, do not rely on a single sensor reading, and do not use any ToF sensor as the only guard around moving parts.

Frequently asked questions

What is the maximum range of the VL53L0X? The sensor is specified up to about 2 m in long-range mode, with the best performance on a light target in dim conditions. In normal indoor light with the default profile, plan on about 1.2 m, and less for dark or small targets.

Does colour affect the reading? Less than with an analog IR sensor, but yes. Darker targets reflect less light, so the range drops and the reading is noisier. Very glossy or transparent surfaces can cause wrong readings.

Can it work outdoors? It can, but bright sunlight cuts the range heavily. Shade the sensor, reduce your range expectation and test it at the time of day it will really be used.

Is the laser safe for my eyes? ST classifies the VL53L0X as a Class 1 laser product, which is considered eye-safe under normal use. As always, do not stare into an optical component and do not modify it.

Can I use it with a 5 V Arduino? Yes, on a breakout board that includes a regulator and level shifting, such as Adafruit's or Pololu's. With cheap boards, check the schematic or use a logic level shifter.

Why does my sensor read 8190 mm? That value shows up when the sensor sees nothing in range. Always check the RangeStatus and ignore readings where it is not 0.

How do I use more than one sensor? Wire each sensor's XSHUT pin to a separate GPIO, boot them one at a time and assign each a new I²C address in software. The sketch above shows how.

How does it compare with an ultrasonic sensor? The VL53L0X has a much narrower beam, a finer resolution and a faster update rate, and it works on soft and angled surfaces that confuse ultrasound. The HC-SR04 reaches farther, costs less and works in sunlight, but is wider and coarser.

Sources This page is based on the ST VL53L0X datasheet and API documentation and on the Adafruit and Pololu library documentation. Range figures depend on test conditions, so validate them with your own target and lighting.

Board Integration

Library

Adafruit VL53L0X by Adafruit

Arduino IDE → Tools → Manage Libraries → search "Adafruit VL53L0X" → Install (accept the Adafruit BusIO and Adafruit Unified Sensor dependency prompts)

Wiring

Component pinArduino Uno pinNote
VIN5VAdafruit and Pololu breakouts include a regulator and level shifting. On cheap boards, check the schematic or power from 3.3 V.
GNDGND
SCLA5Uno hardware I²C clock pin.
SDAA4Uno hardware I²C data pin.
GPIO1 / INTNot connected (optional)Only needed if you want a hardware interrupt instead of polling.
XSHUTNot connected (optional)Only needed when running several VL53L0X sensors on one bus.

Swipe sideways to see every column →

C++

RangeStatus == 4 means the sensor could not find a target, not that it is broken. The library still fills in a meaningless distance, so always check the status before you trust the number. For stricter filtering, accept only status 0 and ignore the other error codes.

The sensor measures one point in a narrow cone of about 25 degrees, so it will not notice an object off to the side of where it is aimed. If you want to cover a wider area on a robot, use two or three sensors at different angles and give each one its own address through XSHUT.

The Uno's I²C pins are shared with other I²C devices, so you can add an OLED display or an IMU on the same A4 and A5 wires without a problem. Keep the wires short, and add 4.7 kΩ pull-ups only if the bus becomes unreliable.

View full Arduino Uno guide