VL53L0X with new I2C driver ESP-IDF v5.4 problems
Posted: 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:
Code:
i2c init:
vl53l0x task for the freertos:
part of the api/vl53l0x_platform.c:
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
- 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
- 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?
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");
}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));
}
}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);
}