Having trouble with VL53L1X Time Of Flight Distance Sensor

Hey EspHomies,

I am trying to use VL53L1X Time Of Flight Distance Sensor with ESPHome on an ESP8266 D1 Mini (hopefully to turn on drawer illumination when I open a drawer but that is mostly irrelevant for now).

I have this model (data sheet: https://www.st.com/resource/en/datasheet/vl53l1x.pdf) which I bought from a reliable retailer here in the Netherlands. I bought the D1 Mini from the same source. I have everything setup and can see the sensor on address 0x29 when a scan is conducted:

[13:27:52][C][i2c.arduino:072]:   SDA Pin: GPIO4
[13:27:52][C][i2c.arduino:073]:   SCL Pin: GPIO5
[13:27:52][C][i2c.arduino:074]:   Frequency: 50000 Hz
[13:27:52][C][i2c.arduino:086]:   Recovery: bus successfully recovered
[13:27:52][I][i2c.arduino:096]: Results from i2c bus scan:
[13:27:52][I][i2c.arduino:102]: Found i2c device at address 0x29

But then later in the log it shows:

[13:27:52][E][component:082]: Component vl53l0x.sensor is marked FAILED

and sure enough, I’m not seeing any values from the sensor.

My yaml file looks like:

substitutions:
  name: esphome-web-0f11d8
  friendly_name: Desk Distance

esphome:
  name: ${name}
  friendly_name: ${friendly_name}
  min_version: 2024.6.0
  name_add_mac_suffix: false
  project:
    name: esphome.web
    version: '1.0'

esp8266:
  board: esp01_1m

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "top secret"

ota:
  platform: esphome
  password: "no peeking"

# Allow provisioning Wi-Fi via serial
improv_serial:

wifi:
  # Set up a wifi access point
  ap: {}

# In combination with the `ap` this allows the user
# to provision wifi credentials to the device via WiFi AP.
captive_portal:

dashboard_import:
  package_import_url: github://esphome/example-configs/esphome-web/esp8266.yaml@main
  import_full_config: true

# To have a "next url" for improv serial
web_server:

i2c:
  sda: GPIO4
  scl: GPIO5
  scan: True
  id: bus_a
     
sensor:
  - platform: vl53l0x
    name: "Distance A"
    # address: 0x29
    # enable_pin: GPIO02
    update_interval: 10s
    long_range: false

I have the sensor wired up to 3v and GND and SCL to D1 (GPIO5) and SDA to D2 (GPIO4). I do not have XSHUT or GPIO1 on the sensor connected to anything. I did have XSHUT connected to D4 originally but it would not appear on the I2C scan until after I disconnected that wire.

I’m not really sure where to go from here. If anyone has any suggestions to help point me in the right direction, I would really appreciate it.

Thanks!

Strange. I only wire up sda & sdl. Here’s my basic config and it works every time:

..... other stuff, then:
ota:
 - platform: esphome

i2c: # For time of flight sensor
  sda: GPIO7
  scl: GPIO8

sensor:
  - platform: vl53l0x  
    name: "TOF v5310 sensor"
    address: 0x29
    long_range: false
    timeout: 200us
    update_interval: 60s
    unit_of_measurement: "m"
    accuracy_decimals: 1

Im using an esp32 D1 mini, but it should work just fine with an 8266 D1. Try different gpios. You never know…

Also, try removing webserver: - it’s a giant memory hog.

Thanks for the reply, tried using your YAML in place of mine but I still got the same error. Tried turning off web server but that didin’t solve it either. I’m confident the pins are right because I can see the device on the i2c bus during the boot sequence.

Hmmmm, I just realised the sensor I have is a “VL53L1X” but it seems ESPHome supports the " VL53L0X". I’m not totally sure how different they are :slight_smile:

So I did some more digging… there seems to be an “unofficial” library for this sensor which you can find here:

I had to manually add the file tof_vl53l1x.h to my esphome directory and then update my yaml using the code above (I also needed to add wire.h to the libraries in the yaml which wasn’t included in the example code provided on the github!

I’m now getting actual distance readings in my log but it’s slow… updating about every 15 seconds. I need to figure out how to get it refreshing faster.

1 Like

Fixed that too… in the tof_vl53l1x.h file there is a line as such:

MyCustomSensor() : PollingComponent(15000) {} // polling every 15s

You can just adjust this to whatever you want in ms (I changed it to 500 for every half second.

MyCustomSensor() : PollingComponent(500) {} // polling every 15s

Hello, that´s great you have made it to work. I am trying to edit code, but still having problem with this lines:

sensor:
- platform: custom
  lambda: |-
    auto my_sensor = new MyCustomSensor();
    App.register_component(my_sensor);
    return {my_sensor};

  sensors:
    name: "Distance"
    accuracy_decimals: 0
    unit_of_measurement: "mm"

should these lines be re-edited some way? Would you please post your exact code? Thank you!

Do you have the I2C section? I’m on mobile at the moment and can’t seem to select the code in esphome but here’s a screenshot:

Hi… thank you for your reply!

This is my yaml:

substitutions:
  devicename: my-custom-sensor

esphome:
  name: '${devicename}'
  friendly_name: My custom sensor
  platform: ESP32
  board: esp32dev
  includes:
    - tof_vl53l1x.h
  libraries:
    - "Wire"
    - "VL53L1x"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  manual_ip:
    static_ip: xxx.xxx.xxx.xxx
    gateway: xxx.xxx.xxx.xxx
    subnet: xxx.xxx.xxx.xxx
	
  ap:
    ssid: "My-Custom-Sensor"
    password: "xxx.xxx.xxx.xxx"

captive_portal:

web_server:
  port: 80

# Enable logging
logger:
  level: DEBUG

# Enable Home Assistant API
api:
  encryption:
    key: "xxx.xxx.xxx.xxx"

ota:
  - platform: esphome
    password: "xxx.xxx.xxx.xxx"

i2c:
  sda: 21
  scl: 22
  scan: false
  frequency: 400kHz

sensor:
- platform: custom
  lambda: |-
    auto my_VL53L1X_sensor = new VL53L1XCustomSensor();
    my_VL53L1X_sensor->set_update_interval(2000); // define update interval
    App.register_component(my_VL53L1X_sensor);
    return {my_VL53L1X_sensor};
  sensors:
    name: "Distance"
    accuracy_decimals: 0
    unit_of_measurement: "mm"

this is my tof_vl53l1x.h :

#include "esphome.h"

#include <Wire.h>
#include <VL53L1X.h>


class VL53L1XCustomSensor : public PollingComponent, public Sensor {

 private:
  VL53L1X tof_sensor;

 public:
  // constructor
  VL53L1XCustomSensor() : PollingComponent(10000) {} // polling every 10s

  void setup() override {
    // This will be called by App.setup()
    Wire.begin();
    Wire.setClock(400000); // use 400 kHz I2C

    tof_sensor.setTimeout(500);
    tof_sensor.setAddress(0x29);
    if (!tof_sensor.init()) {
      ESP_LOGE("VL53L1X custom sensor", "Failed to detect and initialize sensor!");
      return;
    }

    tof_sensor.setDistanceMode(VL53L1X::Long);
    tof_sensor.setMeasurementTimingBudget(250000);	// 1000us = 1ms

    ESP_LOGI("VL53L1X custom sensor", "initialised");

    //tof_sensor.startContinuous(250);	// ms
  }

  void update() override {
    //uint16_t distance_mm = tof_sensor.read(false);
    uint16_t distance_mm = tof_sensor.readSingle();
    
    if (!tof_sensor.timeoutOccurred()) {
      publish_state(distance_mm);
    } else {
      ESP_LOGE("VL53L1X custom sensor", "Timeout during read().");
    }
  }
};```

And after lot of attempts changing some details the result is the same:

INFO ESPHome 2024.6.3
INFO Reading configuration /config/esphome/my-custom-sensor.yaml...
INFO Generating C++ source...
INFO Compiling app...
Processing my-custom-sensor (board: esp32dev; framework: arduino; platform: platformio/[email protected])
--------------------------------------------------------------------------------
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
Dependency Graph
|-- AsyncTCP-esphome @ 2.1.3
|-- Wire @ 2.0.0
|-- VL53L1X @ 1.3.1
|-- WiFi @ 2.0.0
|-- FS @ 2.0.0
|-- Update @ 2.0.0
|-- ESPAsyncWebServer-esphome @ 3.2.2
|-- DNSServer @ 2.0.0
|-- ESPmDNS @ 2.0.0
|-- noise-c @ 0.1.4
|-- ArduinoJson @ 6.18.5
Compiling .pioenvs/my-custom-sensor/src/esphome/components/i2c/i2c_bus_arduino.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/logger/logger_esp8266.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/logger/logger_host.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/logger/logger_libretiny.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/logger/logger_rp2040.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/md5/md5.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_component.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_esp32.cpp.o
In file included from src/esphome/components/i2c/i2c_bus_arduino.cpp:3:
src/esphome/components/i2c/i2c_bus_arduino.h:38:3: error: 'TwoWire' does not name a type; did you mean 'TwoWire_h'?
   TwoWire *wire_;
   ^~~~~~~
   TwoWire_h
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_esp8266.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_host.cpp.o
src/esphome/components/i2c/i2c_bus_arduino.cpp: In member function 'virtual void esphome::i2c::ArduinoI2CBus::setup()':
src/esphome/components/i2c/i2c_bus_arduino.cpp:21:5: error: 'wire_' was not declared in this scope
     wire_ = &Wire;
     ^~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:21:5: note: suggested alternative: 'writev'
     wire_ = &Wire;
     ^~~~~
     writev
src/esphome/components/i2c/i2c_bus_arduino.cpp:21:14: error: 'Wire' was not declared in this scope
     wire_ = &Wire;
              ^~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:21:14: note: suggested alternative: 'pipe'
     wire_ = &Wire;
              ^~~~
              pipe
src/esphome/components/i2c/i2c_bus_arduino.cpp:23:5: error: 'wire_' was not declared in this scope
     wire_ = new TwoWire(next_bus_num);  // NOLINT(cppcoreguidelines-owning-memory)
     ^~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:23:5: note: suggested alternative: 'writev'
     wire_ = new TwoWire(next_bus_num);  // NOLINT(cppcoreguidelines-owning-memory)
     ^~~~~
     writev
src/esphome/components/i2c/i2c_bus_arduino.cpp:23:17: error: expected type-specifier before 'TwoWire'
     wire_ = new TwoWire(next_bus_num);  // NOLINT(cppcoreguidelines-owning-memory)
                 ^~~~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp: In member function 'void esphome::i2c::ArduinoI2CBus::set_pins_and_clock_()':
src/esphome/components/i2c/i2c_bus_arduino.cpp:53:3: error: 'wire_' was not declared in this scope
   wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
   ^~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:53:3: note: suggested alternative: 'writev'
   wire_->begin(static_cast<int>(sda_pin_), static_cast<int>(scl_pin_));
   ^~~~~
   writev
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_libretiny.cpp.o
src/esphome/components/i2c/i2c_bus_arduino.cpp: In member function 'virtual esphome::i2c::ErrorCode esphome::i2c::ArduinoI2CBus::readv(uint8_t, esphome::i2c::ReadBuffer*, size_t)':
src/esphome/components/i2c/i2c_bus_arduino.cpp:125:16: error: 'wire_' was not declared in this scope
   size_t ret = wire_->requestFrom((int) address, (int) to_request, 1);
                ^~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:125:16: note: suggested alternative: 'writev'
   size_t ret = wire_->requestFrom((int) address, (int) to_request, 1);
                ^~~~~
                writev
src/esphome/components/i2c/i2c_bus_arduino.cpp: In member function 'virtual esphome::i2c::ErrorCode esphome::i2c::ArduinoI2CBus::writev(uint8_t, esphome::i2c::WriteBuffer*, size_t, bool)':
src/esphome/components/i2c/i2c_bus_arduino.cpp:179:3: error: 'wire_' was not declared in this scope
   wire_->beginTransmission(address);
   ^~~~~
src/esphome/components/i2c/i2c_bus_arduino.cpp:179:3: note: suggested alternative: 'writev'
   wire_->beginTransmission(address);
   ^~~~~
   writev
Compiling .pioenvs/my-custom-sensor/src/esphome/components/mdns/mdns_rp2040.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/network/util.cpp.o
Compiling .pioenvs/my-custom-sensor/src/esphome/components/ota/ota_backend.cpp.o
*** [.pioenvs/my-custom-sensor/src/esphome/components/i2c/i2c_bus_arduino.cpp.o] Error 1
========================= [FAILED] Took 20.34 seconds =========================

Can you please publish your ESPhome code. I have tried many times but got errors.

Just made it yesterday. The code for esphome

  name: radar
  friendly_name: Radar
  includes:
  - tof_vl53l1x.h
  libraries:
    - "Wire"
    - "VL53L1x"

esp32:
  board: esp32dev
  framework:
    type: arduino


# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "xxxxx"

ota:
  - platform: esphome
    password: "xxxx"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Catradar Fallback Hotspot"
    password: "xxxx"

captive_portal:

i2c:  # example for Wemos LOLIN D2 mini
  sda:  GPIO21 
  scl:  GPIO22
  scan: True
  #VL53L1X, 0x29
  frequency: 400kHz

sensor:
- platform: custom
  lambda: |-
    auto my_sensor = new MyCustomSensor();
    App.register_component(my_sensor);
    return {my_sensor};

  sensors:
    name: "Distance"
    accuracy_decimals: 0
    unit_of_measurement: "mm"

Create a file Wire.h with the following code


#include <Wire.h>
#include <VL53L1X.h>

VL53L1X tof_sensor;

class MyCustomSensor : public PollingComponent, public Sensor {
 public:
  // constructor
  MyCustomSensor() : PollingComponent(5000) {} // polling every 5s

  void setup() override {
    // This will be called by App.setup()
    Wire.begin();
    Wire.setClock(400000); // use 400 kHz I2C

    tof_sensor.setTimeout(500);
    tof_sensor.setAddress(0x29);
    if (!tof_sensor.init()) {
      ESP_LOGE("VL53L1X custom sensor", "Failed to detect and initialize sensor!");
      while (1);
    }

    tof_sensor.setDistanceMode(VL53L1X::Short);
    tof_sensor.setMeasurementTimingBudget(500000);

    tof_sensor.startContinuous(50);
  }

  void update() override {
    uint16_t mm = tof_sensor.read();
    
    if (!tof_sensor.timeoutOccurred()) {
      publish_state(mm);
    } else {
      ESP_LOGE("VL53L1X custom sensor", "Timeout during read().");
    }
  }
};

Download the tof_vl53l1x.h file from this link GitHub - jardous/tof_vl53l1x: ESPHome custom sensor utilizing the VL53L1X Time of Flight distance module
and add both files under /homeassistant/esphome/ folder where your yaml are and then install it to your esp.

Oh… thank you so much… I will try it and let you know :slight_smile:

Hmm can someone explain to a noob why we use both the tof_vl53l1x.h and the Wire.h they seem to contain almost the exact same information?

If I want to change the polling interval and long/short settings for example, where can I do that? And can I set any delta not spam HA with insignificant changes?

See Add the STM VL53L1X sensor (4m version of the VL53L0X) · Issue #836 · esphome/feature-requests · GitHub for a custom components. Both components mentioned in my comment there have distance_mode which might help you. For delta, you can use all existing ESPHome sensor filters.