VL53L4CX reports "ready" incorrectly; does it support one-shot?
Background:
- I'm building a battery-powered device with an extremely tight energy budget (14mAh battery).
- I take a ranging once every 10 seconds (configurable, can be down to 1s).
- We need good accuracy indoors in close distances (up to 2m).
- Outdoors and at larger distances we prefer to have more distance and less precision.
- For initial testing I set the ranging profile to long and the timing budget to 33ms.
My initial choice has been VL53L4CX, based on the datasheet information and my utter lack of experience. However:
- The device doesn't seem to handle GetMeasurementDataReady correctly - it immediately returns true after I execute StartMeasurement. Test code and code output below.
- I'm really struggling to lower the power consumption in one-shot mode. It seems that the minimal time needed to acquire a measurement is about 60ms between StartMeasurement and StopMeasurement - really stretching my power budget.
- I have since learned that VL53L4CX sends the raw data to the host for processing - this is not ideal for us, since we really want to conserve power, and this is a lot of data to send over I2C and then process on a slow MCU.
- I have also learned that there is a "lite" version of the driver - something I might want to take advantage of, in the spirit of keeping things simple and not eating through the tiny battery.
Based on those, two questions:
- Is there a way to have good, fast, low-power one-shot measurements on the VL53L4CX? Or is that device just not built for that kind of operation and should be used for continuous high-performance measurements only?
- If not, what would be the best replacement for our use case?
Sample code:
_Noreturn void APP_Main(void) {
printf("\nTOF TEST\n");
// Power DC/DC for initialization.
Power_DCDC_Enable(true);
VL53LX_Error ret;
int32_t ret32;
uint8_t ready;
// bind I2C bus to TOF driver
VL53L4CX_IO_t tof_i2c_io = {
.Address = VL53L4CX_DEVICE_ADDRESS,
.Init = CUSTOM_VL53L4CX_I2C_INIT,
.DeInit = CUSTOM_VL53L4CX_I2C_DEINIT,
.WriteReg = CUSTOM_VL53L4CX_I2C_WRITEREG,
.ReadReg = CUSTOM_VL53L4CX_I2C_READREG,
.GetTick = BSP_GetTick,
};
ret32 = VL53L4CX_RegisterBusIO(&tof, &tof_i2c_io);
if (ret32 != 0) {
printf("TOF: can't bind bus\n");
}
// Wait for the device to finish booting. This is technically optional.
// Ideally we should replace this function with something that sleeps.
ret = VL53LX_WaitDeviceBooted(&tof);
if (ret != 0) {
printf("TOF: WaitDeviceBooted failed: %d\n", ret);
}
// Perform mandatory initialization.
ret = VL53LX_DataInit(&tof);
if (ret != 0) {
printf("TOF: DataInit failed: %d\n", ret);
}
// Configure the distance mode.
ret = VL53LX_SetDistanceMode(&tof, VL53L4CX_PROFILE_LONG);
if (ret != 0) {
printf("TOF: SetDistanceMode failed: %d\n", ret);
}
// Configure the timing budget.
ret = VL53LX_SetMeasurementTimingBudgetMicroSeconds(&tof, 33000);
if (ret != 0) {
printf("TOF: SetMeasurementTimingBudgetMicroSeconds failed: %d\n", ret);
}
// Check the ready flag - should be zero
ret = VL53LX_GetMeasurementDataReady(&tof, &ready);
if (ret != 0) {
printf("TOF: GetMeasurementDataReady failed: %d\n", ret);
} else {
printf("TOF: GetMeasurementDataReady = %d\n", ready);
}
// Set up interval timer.
Power_Init(3);
printf("Entering main loop.\n");
while (true) {
// Wait for the next measurement to be requested.
while (!Power_Interval_Passed()) {
Power_Stop();
}
printf("----------------------------------------\n");
ret = VL53LX_GetMeasurementDataReady(&tof, &ready);
if (ret != 0) {
printf("TOF: GetMeasurementDataReady failed: %d\n", ret);
} else {
printf("TOF: GetMeasurementDataReady = %d\n", ready);
}
printf("TOF: StartMeasurement\n");
ret = VL53LX_StartMeasurement(&tof);
if (ret != 0) {
printf("TOF: StartMeasurement failed: %d\n", ret);
}
ret = VL53LX_GetMeasurementDataReady(&tof, &ready);
if (ret != 0) {
printf("TOF: GetMeasurementDataReady failed: %d\n", ret);
} else {
printf("TOF: GetMeasurementDataReady = %d\n", ready);
}
// This doesn't work and exits immediately.
printf("TOF: WaitMeasurementDataReady\n");
ret = VL53LX_WaitMeasurementDataReady(&tof);
if (ret != 0) {
printf("TOF: WaitMeasurementDataReady failed: %d\n", ret);
};
#if 0
// Add a delay to let the sensor gather enough data.
Power_Stop_Until_Ms(60);
#endif
printf("TOF: StopMeasurement\n");
ret = VL53LX_StopMeasurement(&tof);
if (ret != 0) {
printf("TOF: StopMeasurement failed: %d\n", ret);
}
VL53LX_MultiRangingData_t measurement;
printf("TOF: GetMultiRangingData\n");
ret = VL53LX_GetMultiRangingData(&tof, &measurement);
if (ret != 0) {
printf("TOF: GetMultiRangingData failed: %d\n", ret);
}
printf("TOF: targets found: %d distance: %d\n", measurement.NumberOfObjectsFound, measurement.RangeData[0].RangeMilliMeter);
printf("TOF: Done.\n");
}
}Result:
TOF TEST
TOF: GetMeasurementDataReady = 0
Entering main loop.
----------------------------------------
TOF: GetMeasurementDataReady = 0
TOF: StartMeasurement
TOF: GetMeasurementDataReady = 1
TOF: WaitMeasurementDataReady
TOF: StopMeasurement
TOF: GetMultiRangingData
TOF: targets found: 0 distance: 8191
TOF: Done.Note how the device immediately reports itself as ready, even though it's not possible to perform a ranging yet.
However, uncommenting the 60ms delay leads to a successful measurement:
TOF TEST
TOF: GetMeasurementDataReady = 0
Entering main loop.
----------------------------------------
TOF: GetMeasurementDataReady = 0
TOF: StartMeasurement
TOF: GetMeasurementDataReady = 1
TOF: WaitMeasurementDataReady
TOF: StopMeasurement
TOF: GetMultiRangingData
TOF: targets found: 2 distance: 101
TOF: Done.
