2026-01-31 1:48 AM - last edited on 2026-01-31 2:14 AM by Andrew Neil
I've interfaced the VL53L4CD with ESP32-S3, but I'm unable to get accurate ranging values below 70mm.
I'm using the ESP32-S3 with Arduino IDE.
How can I resolve this issue ?
#include <Wire.h>
#include "vl53l4cd_class.h"
#define SDA_PIN 8
#define SCL_PIN 9
VL53L4CD sensor(&Wire, -1); // -1 = XSHUT not used
void setup() {
Serial.begin(115200);
delay(1000);
Wire.begin(SDA_PIN, SCL_PIN);
Wire.setClock(400000);
Serial.println("VL53L4CD Init...");
if (sensor.begin() != 0) {
Serial.println("VL53L4CD not detected");
while (1);
}
Serial.println("VL53L4CD detected");
sensor.VL53L4CD_SetRangeTiming(50, 0); // 50ms timing
sensor.VL53L4CD_StartRanging();
}
void loop() {
uint8_t dataReady = 0;
VL53L4CD_Result_t results;
sensor.VL53L4CD_CheckForDataReady(&dataReady);
if (dataReady) {
sensor.VL53L4CD_GetResult(&results);
sensor.VL53L4CD_ClearInterrupt();
Serial.print("Distance: ");
Serial.print(results.distance_mm);
Serial.println(" mm");
}
delay(20);
}Edited to apply source code formatting - please see How to insert source code for future reference.
Solved! Go to Solution.
2026-02-11 2:33 AM
Hi sir, I have already resolved the issue. Please use the test code below, you can measure below 70mm using VLl53L4CD ToF SENSOR
Actually ,this example code from ST Electronics
/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cd_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#define DEV_I2C Wire
#define SerialPort Serial
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif
#define LedPin LED_BUILTIN
/* Please uncomment the line below if you want also to use the satellites */
//#define SATELLITES_MOUNTED
#define XSHUT_TOP 3
#ifdef SATELLITES_MOUNTED
#define XSHUT_LEFT 6
#define XSHUT_RIGHT 4
#endif
// Components.
VL53L4CD sensor_vl53l4cd_top(&DEV_I2C, XSHUT_TOP);
#ifdef SATELLITES_MOUNTED
VL53L4CD sensor_vl53l4cd_left(&DEV_I2C, XSHUT_LEFT);
VL53L4CD sensor_vl53l4cd_right(&DEV_I2C, XSHUT_RIGHT);
#endif
/* Setup ---------------------------------------------------------------------*/
void setup()
{
// Led.
pinMode(LedPin, OUTPUT);
// Initialize serial for output.
SerialPort.begin(115200);
SerialPort.println("Starting...");
// Initialize I2C bus.
DEV_I2C.begin();
// Configure VL53L4CD top component.
sensor_vl53l4cd_top.begin();
// Switch off VL53L4CD top component.
sensor_vl53l4cd_top.VL53L4CD_Off();
#ifdef SATELLITES_MOUNTED
// Configure (if present) VL53L4CD left component.
sensor_vl53l4cd_left.begin();
//Switch off (if present) VL53L4CD left component.
sensor_vl53l4cd_left.VL53L4CD_Off();
// Configure (if present) VL53L4CD right component.
sensor_vl53l4cd_right.begin();
// Switch off (if present) VL53L4CD right component.
sensor_vl53l4cd_right.VL53L4CD_Off();
#endif
//Initialize all the sensors
sensor_vl53l4cd_top.InitSensor(0x10);
#ifdef SATELLITES_MOUNTED
sensor_vl53l4cd_left.InitSensor(0x12);
sensor_vl53l4cd_right.InitSensor(0x14);
#endif
// Start Measurements
sensor_vl53l4cd_top.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_top.VL53L4CD_StartRanging();
#ifdef SATELLITES_MOUNTED
sensor_vl53l4cd_left.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_left.VL53L4CD_StartRanging();
sensor_vl53l4cd_right.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_right.VL53L4CD_StartRanging();
#endif
}
void loop()
{
VL53L4CD_Result_t results;
uint8_t NewDataReady = 0;
char report[128];
uint8_t status;
do {
status = sensor_vl53l4cd_top.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_top.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_top.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Top: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
#ifdef SATELLITES_MOUNTED
NewDataReady = 0;
do {
status = sensor_vl53l4cd_left.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_left.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_left.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Left: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
NewDataReady = 0;
do {
status = sensor_vl53l4cd_right.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_right.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_right.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Right: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
#endif
}
2026-02-01 6:15 PM
Hi
From your code, seems you didn't initialize the device correctly,
Tey to add sensor.VL53L4CD_SensorInit();
Before
sensor.VL53L4CD_SetRangeTiming(50, 0); // 50ms timing
sensor.VL53L4CD_StartRanging()
Br
Zhiyuan.Han
2026-02-09 1:20 AM
Hi,
I'm encountering the same problem of @Nithinkr (measure limited to 70mm ?!!) and i'm working on arduino IDE with https://github.com/stm32duino/VL53L4CD Library.
Please find attach my init code and library default registers value (Except the API is there a documentation about registers ? :(
Is there a way to solve this issue ?
Thank you for your help.
this->sensorOpt.begin();
this->sensorOpt.InitSensor();
uint8_t st = this->sensorOpt.VL53L4CD_SetRangeTiming(50, 0);
if (st) { snprintf(report, sizeof(report), "SetRangeTiming failed (%u)\r\n", st); this->pDebugManagerRef->print(report); return; }
this->pDebugManagerRef->print("SetRangeTiming 50ms of budget and 0ms of inter\r\n");
this->sensorOpt.VL53L4CD_StartRanging();
this->pDebugManagerRef->print("VL53L5CX OK\r\n")
static const uint8_t VL53L4CD_DEFAULT_CONFIGURATION[] = {
0x12, /* 0x2d : set bit 2 and 5 to 1 for fast plus mode (1MHz I2C),
else don't touch */
0x00, /* 0x2e : bit 0 if I2C pulled up at 1.8V, else set bit 0 to 1
(pull up at AVDD) */
0x00, /* 0x2f : bit 0 if GPIO pulled up at 1.8V, else set bit 0 to 1
(pull up at AVDD) */
0x11, /* 0x30 : set bit 4 to 0 for active high interrupt and 1 for active low
(bits 3:0 must be 0x1), use SetInterruptPolarity() */
0x02, /* 0x31 : bit 1 = interrupt depending on the polarity,
use CheckForDataReady() */
0x00, /* 0x32 : not user-modifiable */
0x02, /* 0x33 : not user-modifiable */
0x08, /* 0x34 : not user-modifiable */
0x00, /* 0x35 : not user-modifiable */
0x08, /* 0x36 : not user-modifiable */
0x10, /* 0x37 : not user-modifiable */
0x01, /* 0x38 : not user-modifiable */
0x01, /* 0x39 : not user-modifiable */
0x00, /* 0x3a : not user-modifiable */
0x00, /* 0x3b : not user-modifiable */
0x00, /* 0x3c : not user-modifiable */
0x00, /* 0x3d : not user-modifiable */
0xff, /* 0x3e : not user-modifiable */
0x00, /* 0x3f : not user-modifiable */
0x0F, /* 0x40 : not user-modifiable */
0x00, /* 0x41 : not user-modifiable */
0x00, /* 0x42 : not user-modifiable */
0x00, /* 0x43 : not user-modifiable */
0x00, /* 0x44 : not user-modifiable */
0x00, /* 0x45 : not user-modifiable */
0x20, /* 0x46 : interrupt configuration 0->level low detection, 1-> level high,
2-> Out of window, 3->In window, 0x20-> New sample ready , TBC */
0x0b, /* 0x47 : not user-modifiable */
0x00, /* 0x48 : not user-modifiable */
0x00, /* 0x49 : not user-modifiable */
0x02, /* 0x4a : not user-modifiable */
0x14, /* 0x4b : not user-modifiable */
0x21, /* 0x4c : not user-modifiable */
0x00, /* 0x4d : not user-modifiable */
0x00, /* 0x4e : not user-modifiable */
0x05, /* 0x4f : not user-modifiable */
0x00, /* 0x50 : not user-modifiable */
0x00, /* 0x51 : not user-modifiable */
0x00, /* 0x52 : not user-modifiable */
0x00, /* 0x53 : not user-modifiable */
0xc8, /* 0x54 : not user-modifiable */
0x00, /* 0x55 : not user-modifiable */
0x00, /* 0x56 : not user-modifiable */
0x38, /* 0x57 : not user-modifiable */
0xff, /* 0x58 : not user-modifiable */
0x01, /* 0x59 : not user-modifiable */
0x00, /* 0x5a : not user-modifiable */
0x08, /* 0x5b : not user-modifiable */
0x00, /* 0x5c : not user-modifiable */
0x00, /* 0x5d : not user-modifiable */
0x01, /* 0x5e : not user-modifiable */
0xcc, /* 0x5f : not user-modifiable */
0x07, /* 0x60 : not user-modifiable */
0x01, /* 0x61 : not user-modifiable */
0xf1, /* 0x62 : not user-modifiable */
0x05, /* 0x63 : not user-modifiable */
0x00, /* 0x64 : Sigma threshold MSB (mm in 14.2 format for MSB+LSB),
use SetSigmaThreshold(), default value 90 mm */
0xa0, /* 0x65 : Sigma threshold LSB */
0x00, /* 0x66 : Min count Rate MSB (MCPS in 9.7 format for MSB+LSB),
use SetSignalThreshold() */
0x80, /* 0x67 : Min count Rate LSB */
0x08, /* 0x68 : not user-modifiable */
0x38, /* 0x69 : not user-modifiable */
0x00, /* 0x6a : not user-modifiable */
0x00, /* 0x6b : not user-modifiable */
0x00, /* 0x6c : Intermeasurement period MSB, 32 bits register,
use SetIntermeasurementInMs() */
0x00, /* 0x6d : Intermeasurement period */
0x0f, /* 0x6e : Intermeasurement period */
0x89, /* 0x6f : Intermeasurement period LSB */
0x00, /* 0x70 : not user-modifiable */
0x00, /* 0x71 : not user-modifiable */
0x00, /* 0x72 : distance threshold high MSB (in mm, MSB+LSB),
use SetD:tanceThreshold() */
0x00, /* 0x73 : distance threshold high LSB */
0x00, /* 0x74 : distance threshold low MSB ( in mm, MSB+LSB),
use SetD:tanceThreshold() */
0x00, /* 0x75 : distance threshold low LSB */
0x00, /* 0x76 : not user-modifiable */
0x01, /* 0x77 : not user-modifiable */
0x07, /* 0x78 : not user-modifiable */
0x05, /* 0x79 : not user-modifiable */
0x06, /* 0x7a : not user-modifiable */
0x06, /* 0x7b : not user-modifiable */
0x00, /* 0x7c : not user-modifiable */
0x00, /* 0x7d : not user-modifiable */
0x02, /* 0x7e : not user-modifiable */
0xc7, /* 0x7f : not user-modifiable */
0xff, /* 0x80 : not user-modifiable */
0x9B, /* 0x81 : not user-modifiable */
0x00, /* 0x82 : not user-modifiable */
0x00, /* 0x83 : not user-modifiable */
0x00, /* 0x84 : not user-modifiable */
0x01, /* 0x85 : not user-modifiable */
0x00, /* 0x86 : clear interrupt, use ClearInterrupt() */
0x00 /* 0x87 : start ranging, use StartRanging() or StopRanging(),
If you want an automatic start after VL53L4CD_init() call,
put 0x40 in location 0x87 */
};
2026-02-10 2:52 PM
When you are streaming, can you read register 0x3F. The driver writes this to a value of 0x00, but if this is reading a value of 8D, this would give a distance of 70mm when the target is below 70mm.
If this is the case, before issuing a start command, write to register 0x3F a value of 0x00. This should solve this issue.
2026-02-11 2:33 AM
Hi sir, I have already resolved the issue. Please use the test code below, you can measure below 70mm using VLl53L4CD ToF SENSOR
Actually ,this example code from ST Electronics
/* Includes ------------------------------------------------------------------*/
#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cd_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>
#define DEV_I2C Wire
#define SerialPort Serial
#ifndef LED_BUILTIN
#define LED_BUILTIN 13
#endif
#define LedPin LED_BUILTIN
/* Please uncomment the line below if you want also to use the satellites */
//#define SATELLITES_MOUNTED
#define XSHUT_TOP 3
#ifdef SATELLITES_MOUNTED
#define XSHUT_LEFT 6
#define XSHUT_RIGHT 4
#endif
// Components.
VL53L4CD sensor_vl53l4cd_top(&DEV_I2C, XSHUT_TOP);
#ifdef SATELLITES_MOUNTED
VL53L4CD sensor_vl53l4cd_left(&DEV_I2C, XSHUT_LEFT);
VL53L4CD sensor_vl53l4cd_right(&DEV_I2C, XSHUT_RIGHT);
#endif
/* Setup ---------------------------------------------------------------------*/
void setup()
{
// Led.
pinMode(LedPin, OUTPUT);
// Initialize serial for output.
SerialPort.begin(115200);
SerialPort.println("Starting...");
// Initialize I2C bus.
DEV_I2C.begin();
// Configure VL53L4CD top component.
sensor_vl53l4cd_top.begin();
// Switch off VL53L4CD top component.
sensor_vl53l4cd_top.VL53L4CD_Off();
#ifdef SATELLITES_MOUNTED
// Configure (if present) VL53L4CD left component.
sensor_vl53l4cd_left.begin();
//Switch off (if present) VL53L4CD left component.
sensor_vl53l4cd_left.VL53L4CD_Off();
// Configure (if present) VL53L4CD right component.
sensor_vl53l4cd_right.begin();
// Switch off (if present) VL53L4CD right component.
sensor_vl53l4cd_right.VL53L4CD_Off();
#endif
//Initialize all the sensors
sensor_vl53l4cd_top.InitSensor(0x10);
#ifdef SATELLITES_MOUNTED
sensor_vl53l4cd_left.InitSensor(0x12);
sensor_vl53l4cd_right.InitSensor(0x14);
#endif
// Start Measurements
sensor_vl53l4cd_top.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_top.VL53L4CD_StartRanging();
#ifdef SATELLITES_MOUNTED
sensor_vl53l4cd_left.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_left.VL53L4CD_StartRanging();
sensor_vl53l4cd_right.VL53L4CD_SetRangeTiming(200, 0);
sensor_vl53l4cd_right.VL53L4CD_StartRanging();
#endif
}
void loop()
{
VL53L4CD_Result_t results;
uint8_t NewDataReady = 0;
char report[128];
uint8_t status;
do {
status = sensor_vl53l4cd_top.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_top.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_top.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Top: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
#ifdef SATELLITES_MOUNTED
NewDataReady = 0;
do {
status = sensor_vl53l4cd_left.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_left.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_left.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Left: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
NewDataReady = 0;
do {
status = sensor_vl53l4cd_right.VL53L4CD_CheckForDataReady(&NewDataReady);
} while (!NewDataReady);
//Led on
digitalWrite(LedPin, HIGH);
if ((!status) && (NewDataReady != 0)) {
// (Mandatory) Clear HW interrupt to restart measurements
sensor_vl53l4cd_right.VL53L4CD_ClearInterrupt();
// Read measured distance. RangeStatus = 0 means valid data
sensor_vl53l4cd_right.VL53L4CD_GetResult(&results);
snprintf(report, sizeof(report), "VL53L4CD Right: Status = %3u, Distance = %5u mm, Signal = %6u kcps/spad\r\n",
results.range_status,
results.distance_mm,
results.signal_per_spad_kcps);
SerialPort.print(report);
}
digitalWrite(LedPin, LOW);
#endif
}
2026-02-13 8:04 AM
Hi @Nithinkr , @Darin WINTERTON ,
Many thanks for your help, by taking the code of @Nithinkr , i found my problem -> I was looping the measurements with xshut (VL53L4CD_Off() method), a delay and without calling InitSensor each time (just doing VL53L4CD_On() and VL53L4CD_StartRanging()).
Too bad that xShut behaviour is not clearly described in datasheet about data persistence etc... but it's solved :)