# VL6180X ESP-IDF Driver
Production-oriented I2C driver for the ST VL6180X Time-of-Flight proximity
sensor with integrated ambient light sensing (ALS), written for ESP-IDF
5.x/6.x using the new I2C master API (`driver/i2c_master.h`). No Arduino
dependencies.
All register semantics follow the VL6180X datasheet (DocID026171 Rev 7) and
the VL6180 datasheet (DocID024986 Rev 14). The API syntax matches the
[LIS331DLH driver](https://github.com/iot-academy/esp-lis331), so both sensors
can be used on the same I2C bus in one product.
## Features
- Native ESP-IDF I2C master driver (`vl6180x_create()`, always available);
- optional ESP-IoT-Solution `i2c_bus` transport (`vl6180x_create_i2c_bus()`),
enabled with `CONFIG_VL6180X_USE_I2C_BUS=y`;
- device identification via MODEL_ID (0xB4) at creation, plus a firmware
boot-up wait;
- single-shot and continuous ranging (distance in mm, 0..255);
- range status / error code and raw range reads;
- signal rate read (9.7 fixed point, MCPS);
- ambient light sensing in lux (VL6180X);
- range and ALS interrupt configuration and status;
- offset and crosstalk calibration;
- runtime I2C address change for multi-sensor products.
## Hardware connection
- SDA -> GPIO21, SCL -> GPIO22 (defaults in the examples).
- ESP32 internal pull-ups are disabled by the examples, so external pull-up
resistors are required on both I2C lines.
- The 7-bit I2C address is fixed at `0x29` (can be changed at runtime with
`vl6180x_set_address()`).
- SCL clock must stay within 100..400 kHz.
## Example usage
```c
#include "driver/i2c_master.h"
#include "vl6180x.h"
i2c_master_bus_config_t bus_config = {
.i2c_port = I2C_NUM_0,
.sda_io_num = 21,
.scl_io_num = 22,
.clk_source = I2C_CLK_SRC_DEFAULT,
.glitch_ignore_cnt = 7,
.flags.enable_internal_pullup = false,
};
i2c_master_bus_handle_t bus;
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus));
vl6180x_config_t config = {
.i2c_clock_hz = 100000,
.max_convergence_time_ms = 49,
.averaging_sample_period = 48,
.minimal_init = false, /* set true for clones that need a reduced init */
};
vl6180x_handle_t sensor;
ESP_ERROR_CHECK(vl6180x_create(bus, VL6180X_DEFAULT_I2C_ADDR, &config, &sensor));
uint8_t range_mm;
ESP_ERROR_CHECK(vl6180x_range_read(sensor, &range_mm));
vl6180x_range_status_t status;
ESP_ERROR_CHECK(vl6180x_range_get_status(sensor, &status));
/* status.error_code == VL6180X_RANGE_ERROR_NONE when the reading is valid. */
```
## Data interpretation
- `vl6180x_range_read()` returns the final range in **millimeters** (8-bit,
0..255). The value is only meaningful when the range status error code is
`VL6180X_RANGE_ERROR_NONE`; `VL6180X_RANGE_ERROR_MAX_CONVERGENCE` (7) means no
target was detected.
- `vl6180x_range_get_signal_rate()` returns the return signal rate as a 9.7
fixed-point value; divide by 128 to get MCPS.
- `vl6180x_als_read()` returns illuminance in **lux**:
`lux = lux_resolution * (count / gain) * (100 ms / integration_period)`.
The factory `lux_resolution` is 0.32 lux/count at gain 1 and 100 ms
integration (calibrated without glass); recalibrate after mounting cover
glass with `vl6180x_als_set_lux_resolution()`.
## Ranging modes
- Single shot: `vl6180x_range_read()`.
- Continuous: `vl6180x_range_start_continuous(handle, period_ms)` (period
clamped to 10..2540 ms), then `vl6180x_range_read_continuous()` which returns
`ESP_ERR_NOT_FINISHED` when no new sample is ready.
## Interrupts
- `vl6180x_interrupt_config()` selects the source (range or ALS) and mode
(disabled, level low/high, out of window, new sample ready).
- `vl6180x_range_set_thresholds()` sets the range threshold window.
- `vl6180x_get_interrupt_status()` reports the pending events,
`vl6180x_clear_interrupt()` clears them.
## Calibration
- `vl6180x_set_range_offset()` writes the part-to-part range offset (mm).
- `vl6180x_set_crosstalk_rate()` writes the crosstalk compensation rate (9.7
fixed point, MCPS).
## Limitations
- **ALS and interleaved mode are VL6180X features.** The corresponding
registers are not documented in the base VL6180 datasheet, so the ALS
functions are only guaranteed on a VL6180X.
- **Ranging compatibility with the base VL6180 and clones:** a genuine
VL6180X ranges with the default (full) initialization. Some VL6180-family
devices (e.g. clones) need a reduced initialization; enable it with
`vl6180x_config_t.minimal_init = true` in the creation config. One defective
unit that reports a persistent VCSEL continuity error (range error code 1)
could not be made to range even with `minimal_init`.
- The driver does not implement the upscale (extended range) feature, history
buffer reads, or the wrap-around filter.
- The driver is not thread-safe; a single handle should be used from one task
(the ESP-IDF I2C master driver serializes bus transactions, but the driver
keeps no per-sensor state lock).
- The application owns the I2C bus: the driver adds a device to it and never
deletes the bus.
## Using VL6180X together with LIS331DLH
Both drivers share the same API conventions and can be attached to the same
I2C bus in one product:
```c
i2c_master_bus_handle_t bus;
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &bus));
lis331_handle_t lis;
ESP_ERROR_CHECK(lis331_create(bus, LIS331_I2C_ADDR_SA0_GND, NULL, &lis));
vl6180x_handle_t vl;
ESP_ERROR_CHECK(vl6180x_create(bus, VL6180X_DEFAULT_I2C_ADDR, NULL, &vl));
```
The default addresses do not collide (LIS331DLH: 0x18/0x19, VL6180X: 0x29).
To use several VL6180X on one bus, power the sensors one at a time and change
each address with `vl6180x_set_address()`.
## Examples
Both examples build with `idf.py build` from their directories after exporting
an ESP-IDF environment:
- `examples/basic` - native ESP-IDF I2C master driver;
- `examples/i2c_bus` - ESP-IoT-Solution `i2c_bus` transport (requires
`CONFIG_VL6180X_USE_I2C_BUS=y`, the dependency is pulled automatically from
the component registry).
## Tests
`tests/register_encoding` contains host-side unit tests for the register
encoding and initialization sequence. Run them with `make run`.
## Dependencies
- `espressif/cmake_utilities` (always);
- `espressif/i2c_bus` (conditional on `CONFIG_VL6180X_USE_I2C_BUS`, pulled from
the component registry when enabled).
## License
This project is licensed under the MIT License - see the `LICENSE` file.
idf.py add-dependency "nestorit/vl6180x^0.0.1"