VL53L0X with new I2C driver ESP-IDF v5.4 problems

didierdrogba
Posts: 1
Joined: Wed Apr 23, 2025 11:34 pm

VL53L0X with new I2C driver ESP-IDF v5.4 problems

Postby didierdrogba » Wed Apr 23, 2025 11:59 pm

Hello,
I'm working on an ESP32-S3 (Freenove cam wroom board) using ESP-IDF v5.4.1 and trying to interface the VL53L0X time-of-flight sensor using the new I2C driver (i2c_master.h). The camera on I2C1 is working well, but the VL53L0X connected to I2C0 is failing with NACK errors. I am using official ST API for the vl53l0x and have implemented it in my project.

Relevant setup:
  • VL53L0X ToF sensor
    I2C port 0 used
    SDA = GPIO 21
    SCL = GPIO 47
    XSHUT = GPIO 3
    Pull-up resistors = 10kΩ (external, both SDA and SCL)
    I2C clock = 100kHz
    Builtin camera uses GPIO 4 and 5 on port 1 and works fine
    Making it for FreeRTOS
Problem Summary
  • I2C scanner finds the VL53L0X at address 0x29
    But I keep getting:
    i2c_master_receive(1240): I2C transaction failed
    I2C transaction unexpected nack detected
    VL53L0X_WaitDeviceBooted() or DataInit() often fail
Questions
  • Do I need to modify ST’s VL53L0X API for the new i2c_master_transmit() model? (I already wrote the wrapper for official vl53l0x_platform, code below)
    Is my XSHUT toggle timing correct?
    Should I manage i2c_master_dev_handle_t globally and reuse it?
    Why does the scanner find the device but ranging still fails?
Thanks in advance for your time, below is the detailed info on my setup.

Code:
i2c init:

Code: Select all

#include "i2c_init.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "driver/gpio.h"

i2c_master_bus_handle_t vl53l0x_i2c_bus;

void init_i2c_master_vl53l0x(void) {
    const i2c_master_bus_config_t i2c_config = {
        .clk_source = I2C_CLK_SRC_DEFAULT,
        .i2c_port = I2C_NUM_0,
        .sda_io_num = VL53L0X_SDA_GPIO,
        .scl_io_num = VL53L0X_SCL_GPIO,
        .glitch_ignore_cnt = 7,
        .flags.enable_internal_pullup = true,
    };

    ESP_ERROR_CHECK(i2c_new_master_bus(&i2c_config, &vl53l0x_i2c_bus));
    ESP_LOGI("I2C_INIT", "VL53L0X I2C master bus initialized on port 0 (SDA=%d, SCL=%d)", VL53L0X_SDA_GPIO, VL53L0X_SCL_GPIO);
}

void vl53l0x_hard_reset(void) {
    gpio_config_t io_conf = {
        .pin_bit_mask = (1ULL << VL53L0X_XSHUT_GPIO),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = true,
        .pull_down_en = false,
        .intr_type = GPIO_INTR_DISABLE,
    };

    gpio_config(&io_conf);

    gpio_set_level(VL53L0X_XSHUT_GPIO, 0); // Hold reset
    vTaskDelay(pdMS_TO_TICKS(10));
    gpio_set_level(VL53L0X_XSHUT_GPIO, 1); // Power up
    vTaskDelay(pdMS_TO_TICKS(10));        // Wait for boot
    ESP_LOGI("I2C_INIT", "VL53L0X XSHUT toggled HIGH");
}
vl53l0x task for the freertos:

Code: Select all

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "vl53l0x_api.h"
#include "vl53l0x_platform.h"
#include "vl53l0x_platform_log.h"
#include "driver/gpio.h"
#include "tcp_server.h"  // for `global_sock`
#include "lwip/sockets.h"
#include "i2c_init.h"  // for `init_i2c_master_vl53l0x()`

#define XSHUT_GPIO GPIO_NUM_3
#define TAG "VL53L0X_TASK"
#define VL53L0X_I2C_ADDR 0x29

VL53L0X_Dev_t sensor_dev;
VL53L0X_DEV dev = &sensor_dev;

void vl53l0x_task(void *pvParameters) {
    VL53L0X_RangingMeasurementData_t measurement;
    VL53L0X_Error status;

    // Init XSHUT pin
    gpio_reset_pin(XSHUT_GPIO);
    gpio_set_direction(XSHUT_GPIO, GPIO_MODE_OUTPUT);
    gpio_set_level(XSHUT_GPIO, 1);  // Power on

    vTaskDelay(pdMS_TO_TICKS(10));  // Stabilize

    dev->I2cDevAddr = 0x29;
    dev->comms_type = 1;
    dev->comms_speed_khz = 50;

    status = VL53L0X_WaitDeviceBooted(dev);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "Device boot wait failed: %d", status);
        vTaskDelete(NULL);
    }
    ESP_LOGI("VL53L0X_TASK", "Probing sensor at address 0x%02X...", VL53L0X_I2C_ADDR);

    status = VL53L0X_DataInit(dev);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "DataInit failed: %d", status);
        vTaskDelete(NULL);
    }

    status = VL53L0X_StaticInit(dev);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "StaticInit failed: %d", status);
        vTaskDelete(NULL);
    }

    status = VL53L0X_PerformRefCalibration(dev, NULL, NULL);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "Calibration failed: %d", status);
        vTaskDelete(NULL);
    }

    status = VL53L0X_SetDeviceMode(dev, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "SetMode failed: %d", status);
        vTaskDelete(NULL);
    }

    status = VL53L0X_StartMeasurement(dev);
    if (status != VL53L0X_ERROR_NONE) {
        ESP_LOGE(TAG, "StartMeasurement failed: %d", status);
        vTaskDelete(NULL);
    }

    ESP_LOGI(TAG, "VL53L0X running...");
    ESP_LOGI("VL53L0X_TASK", "Starting VL53L0X task");

    char packet[64];

    while (1) {
        status = VL53L0X_GetRangingMeasurementData(dev, &measurement);
        if (status == VL53L0X_ERROR_NONE) {
            uint16_t distance = measurement.RangeMilliMeter;
            ESP_LOGI(TAG, "Distance: %d mm", distance);

            if (global_sock != -1) {
                snprintf(packet, sizeof(packet), "[TOF] %d\n", distance);
                send(global_sock, packet, strlen(packet), 0);
            }

            VL53L0X_ClearInterruptMask(dev, 0);
        } else {
            ESP_LOGW(TAG, "Failed to read measurement: %d", status);
        }
        vTaskDelay(pdMS_TO_TICKS(100));
    }
}
part of the api/vl53l0x_platform.c:

Code: Select all

static i2c_master_dev_handle_t vl53l0x_dev_handle = NULL;

// Initialize I2C communication (call this first)
int32_t VL53L0X_platform_init(i2c_master_bus_handle_t bus_handle, uint8_t address) {
    i2c_device_config_t dev_cfg = {
        .device_address = address,
        .scl_speed_hz = 100000,
        .dev_addr_length = I2C_ADDR_BIT_LEN_7,
    };
    
    esp_err_t ret = i2c_master_bus_add_device(bus_handle, &dev_cfg, &vl53l0x_dev_handle);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to add I2C device: %s", esp_err_to_name(ret));
        return -1;
    }
    return 0;
}

// Common transmit function
static int32_t vl53l0x_transmit(uint8_t *data, size_t size) {
    if (!vl53l0x_dev_handle) return -1;
    esp_err_t ret = i2c_master_transmit(vl53l0x_dev_handle, data, size, VL53L0X_DEFAULT_TIMEOUT_MS);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Transmit failed: %s", esp_err_to_name(ret));
        return -1;
    }
    return 0;
}

// Common receive function
static int32_t vl53l0x_receive(uint8_t *tx_data, size_t tx_len, uint8_t *rx_data, size_t rx_len) {
    if (!vl53l0x_dev_handle) return -1;
    esp_err_t ret = i2c_master_transmit_receive(vl53l0x_dev_handle, 
                                              tx_data, tx_len, 
                                              rx_data, rx_len, 
                                              VL53L0X_DEFAULT_TIMEOUT_MS);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Transmit-receive failed: %s", esp_err_to_name(ret));
        return -1;
    }
    return 0;
}

// ST API required functions
int32_t VL53L0X_write_multi(uint8_t address, uint8_t reg, uint8_t *pdata, int32_t count) {
    uint8_t buffer[count + 1];
    buffer[0] = reg;
    memcpy(&buffer[1], pdata, count);
    return vl53l0x_transmit(buffer, count + 1);
}

robertofelix.jr
Posts: 1
Joined: Tue Jul 08, 2025 12:40 am

Re: VL53L0X with new I2C driver ESP-IDF v5.4 problems

Postby robertofelix.jr » Sat Jul 12, 2025 2:41 pm

Hello. I'm trying to create a new clean API with a new i2c driver, but it's not working. Now that I've found your implementation, if you could help me. If your implementation is already ready, please do.

Who is online

Users browsing this forum: meta-externalagent and 6 guests