Precision Distance Measurement with VL53L4CX ToF Sensor

Precision Distance Measurement with VL53L4CX ToF Sensor

Please read Liability Disclaimer and License Agreement CAREFULLY

In today's fast-paced world of technology, precise and efficient distance measurement is a fundamental requirement for countless applications, from robotics and automation to security systems and environmental monitoring.

Arduino, the open-source IDE, has revolutionized the way enthusiasts and professionals approach such challenges.

This article delves into the world of distance measurement with a spotlight on the SATEL-VL53L4CX sensor, a high-precision time-of-flight (ToF) sensor that is gaining popularity for its accuracy and versatility. We'll explore how to harness the power of Arduino and the VL53L4CX sensor to measure distances with unparalleled precision, opening up a world of possibilities for your projects. Whether you're a seasoned maker or just beginning your Arduino journey, this guide will walk you through the code and techniques needed to unlock the full potential of this powerful sensor.

SATEL-VL53L4CX

SATEL-VL53L4CX features:

• Accurate absolute ranging distance, independent of the target size and reflectance
• Distance measurement from 0mm up to 6m
• Short distance linearity down up to 10mm
• Histogram based technology
• Multi object detection capability
• Targets beyond 80cm range are immune to crosstalk from cover glass and smudge
• Divisible board that can be used as a mini-PCB breakout board, easy to integrate into the customer's device
• Two breakout boards available in the package

For the actual built I have used this sensor cover to protect the sensor from environment as I will use it inside a well to measure the water level - check this article for detailes.

/* https://github.com/stm32duino/VL53L4CX */
#include <Arduino.h>
#include <Wire.h>
#include <vl53l4cx_class.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include <stdlib.h>

#define DEBUG

#define DEV_I2C Wire
#define SerialPort Serial

#ifdef DEBUG
  #define DEBUG_PRINTLN(x)  SerialPort.println (x)
  #define DEBUG_PRINT(x)  SerialPort.print (x)
#else
  #define DEBUG_PRINTLN(x)
  #define DEBUG_PRINT(x)
#endif

#define LED_PIN PC13
#define INT_PIN PB8
#define XSHUT PB9

const uint8_t msgLen = 4; // < Start marker for serial
const uint8_t sMarker = 60; // < Start marker for serial
const uint8_t eMarker = 62; // > End marker for serial

VL53L4CX myDevice(&DEV_I2C, XSHUT);

volatile uint8_t interruptDetect = 0;
//uint32_t myTime;
uint32_t sumDistance;
uint16_t avgDistance, maxDistance;
uint8_t countMeasures;
VL53L4CX_Error sensor_status;
VL53L4CX_CalibrationData_t calibrationData;

void interruptCallback() {
  interruptDetect = 1;
}

void setup() {
  
  // Led.
  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, HIGH);
  pinMode(INT_PIN, INPUT_PULLUP);
  attachInterrupt(INT_PIN, interruptCallback, FALLING);
  // Initialize serial for output.
  SerialPort.begin(9600);
  //myTime = millis();
  //DEBUG_PRINTLN("Starting...");
  // Initialize I2C bus.
  initVL53L4CX();
  // initVL53L4CX() take about 100ms
  //myTime = millis() - myTime;
  //DEBUG_PRINT("Setup Done In...");
  //DEBUG_PRINTLN(myTime);

  //myTime = millis();
  digitalWrite(LED_PIN, LOW);
}

void loop()
{
  measureDistance();
  // If we have 20 samples make the average and report it
  if(countMeasures == 19){
    avgDistance = sumDistance/20;
    uint8_t msg[msgLen] = {sMarker, 0, 0, eMarker};
    msg[1] = (avgDistance >> 8) & 0xFF;
    msg[2] = avgDistance & 0xFF;
    Serial.write(msg, msgLen);
    countMeasures = 0;
    maxDistance =0;
    sumDistance = 0;
    //DEBUG_PRINT("Distance = ");
    //DEBUG_PRINTLN(avgDistance);
  }
}

void measureDistance(){
  VL53L4CX_MultiRangingData_t MultiRangingData;
  VL53L4CX_MultiRangingData_t *pMultiRangingData = &MultiRangingData;

  uint8_t NewDataReady = 0;
  int no_of_object_found = 0;
  if (interruptDetect) {
    int status;
    interruptDetect = 0;
    status = myDevice.VL53L4CX_GetMeasurementDataReady(&NewDataReady);
    if ((!status) && (NewDataReady != 0)) {
      status = myDevice.VL53L4CX_GetMultiRangingData(pMultiRangingData);
      if(!status){
        no_of_object_found = pMultiRangingData->NumberOfObjectsFound;
        for (uint8_t j = 0; j < no_of_object_found; j++){
          if(pMultiRangingData->RangeData[j].RangeMilliMeter > maxDistance){
            maxDistance = pMultiRangingData->RangeData[j].RangeMilliMeter;
          }
        }
        sumDistance += maxDistance;
        countMeasures++;
        status = myDevice.VL53L4CX_ClearInterruptAndStartMeasurement();
      }
    }
  }
}

void initVL53L4CX() {
  DEV_I2C.begin();
  // Initialize Step 1 and 2
  sensor_status = myDevice.InitSensor(0x12);
  //DEBUG_PRINTLN("Step 1+2");
  //DEBUG_PRINTLN(sensor_status);

  sensor_status = myDevice.VL53L4CX_GetCalibrationData(&calibrationData);
  //DEBUG_PRINTLN("Step Get Calibration Data");
  //DEBUG_PRINTLN(sensor_status);
  // The values below are generated by VL53L4CX_Calibrate.ino for 1m or 100cm with protection cap on
  calibrationData.customer.global_config__spad_enables_ref_0  = 254;
  calibrationData.customer.global_config__spad_enables_ref_1  = 255;
  calibrationData.customer.global_config__spad_enables_ref_2  = 247;
  calibrationData.customer.global_config__spad_enables_ref_3  = 253;
  calibrationData.customer.global_config__spad_enables_ref_4  = 223;
  calibrationData.customer.global_config__spad_enables_ref_5  = 11;
  calibrationData.customer.global_config__ref_en_start_select  = 0;
  calibrationData.customer.ref_spad_man__num_requested_ref_spads  = 7;
  calibrationData.customer.ref_spad_man__ref_location  = 2;
  calibrationData.customer.algo__crosstalk_compensation_plane_offset_kcps  = 420;
  calibrationData.customer.algo__crosstalk_compensation_x_plane_gradient_kcps  = 0;
  calibrationData.customer.algo__crosstalk_compensation_y_plane_gradient_kcps  = 0;
  calibrationData.customer.ref_spad_char__total_rate_target_mcps  = 2560;
  calibrationData.customer.algo__part_to_part_range_offset_mm  = 0;
  calibrationData.customer.mm_config__inner_offset_mm  = 0;
  calibrationData.customer.mm_config__outer_offset_mm  = 0;
  calibrationData.add_off_cal_data.result__mm_inner_actual_effective_spads  = 7904;
  calibrationData.add_off_cal_data.result__mm_outer_actual_effective_spads  = 45944;
  calibrationData.add_off_cal_data.result__mm_inner_peak_signal_count_rtn_mcps  = 128;
  calibrationData.add_off_cal_data.result__mm_outer_peak_signal_count_rtn_mcps  = 384;
  calibrationData.optical_centre.x_centre  = 140;
  calibrationData.optical_centre.y_centre  = 111;
  calibrationData.xtalkhisto.xtalk_shape.zone_id  = 0;
  calibrationData.xtalkhisto.xtalk_shape.time_stamp  = 0;
  calibrationData.xtalkhisto.xtalk_shape.VL53L4CX_p_019  = 0;
  calibrationData.xtalkhisto.xtalk_shape.VL53L4CX_p_020  = 12;
  calibrationData.xtalkhisto.xtalk_shape.VL53L4CX_p_021  = 12;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[0]  = 35;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[1]  = 434;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[2]  = 451;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[3]  = 104;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[4]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[5]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[6]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[7]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[8]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[9]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[10]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.bin_data[11]  = 0;
  calibrationData.xtalkhisto.xtalk_shape.phasecal_result__reference_phase  = 10369;
  calibrationData.xtalkhisto.xtalk_shape.phasecal_result__vcsel_start  = 6;
  calibrationData.xtalkhisto.xtalk_shape.cal_config__vcsel_start  = 9;
  calibrationData.xtalkhisto.xtalk_shape.vcsel_width  = 40;
  calibrationData.xtalkhisto.xtalk_shape.VL53L4CX_p_015  = 48209;
  calibrationData.xtalkhisto.xtalk_shape.zero_distance_phase  = 4225;
  calibrationData.xtalkhisto.xtalk_hist_removed.cfg_device_state  = 3;
  calibrationData.xtalkhisto.xtalk_hist_removed.rd_device_state  = 3;
  calibrationData.xtalkhisto.xtalk_hist_removed.zone_id  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.time_stamp  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_019  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_020  = 12;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_021  = 12;
  calibrationData.xtalkhisto.xtalk_hist_removed.number_of_ambient_bins  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[0]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[1]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[2]  = 2;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[3]  = 3;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[4]  = 4;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_seq[5]  = 5;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[0]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[1]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[2]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[3]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[4]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_rep[5]  = 1;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[0]  = 43;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[1]  = 504;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[2]  = 523;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[3]  = 123;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[4]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[5]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[6]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[7]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[8]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[9]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[10]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[11]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[12]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[13]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[14]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[15]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[16]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[17]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[18]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[19]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[20]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[21]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[22]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.bin_data[23]  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.result__interrupt_status  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.result__range_status  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.result__report_status  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.result__stream_count  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.result__dss_actual_effective_spads  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.phasecal_result__reference_phase  = 10369;
  calibrationData.xtalkhisto.xtalk_hist_removed.phasecal_result__vcsel_start  = 6;
  calibrationData.xtalkhisto.xtalk_hist_removed.cal_config__vcsel_start  = 9;
  calibrationData.xtalkhisto.xtalk_hist_removed.vcsel_width  = 40;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_005  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_015  = 48209;
  calibrationData.xtalkhisto.xtalk_hist_removed.total_periods_elapsed  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.peak_duration_us  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.woi_duration_us  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.min_bin_value  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.max_bin_value  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.zero_distance_phase  = 4225;
  calibrationData.xtalkhisto.xtalk_hist_removed.number_of_ambient_samples  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.ambient_events_sum  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.VL53L4CX_p_028  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.roi_config__user_roi_centre_spad  = 0;
  calibrationData.xtalkhisto.xtalk_hist_removed.roi_config__user_roi_requested_global_xy_size  = 0;
  calibrationData.gain_cal.standard_ranging_gain_factor  = 2011;
  calibrationData.gain_cal.histogram_ranging_gain_factor  = 2020;
  calibrationData.cal_peak_rate_map.cal_distance_mm  = 0;
  calibrationData.cal_peak_rate_map.cal_reflectance_pc  = 0;
  calibrationData.cal_peak_rate_map.max_samples  = 25;
  calibrationData.cal_peak_rate_map.width  = 5;
  calibrationData.cal_peak_rate_map.height  = 5;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[0]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[1]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[2]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[3]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[4]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[5]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[6]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[7]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[8]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[9]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[10]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[11]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[12]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[13]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[14]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[15]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[16]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[17]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[18]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[19]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[20]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[21]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[22]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[23]  = 0;
  calibrationData.cal_peak_rate_map.peak_rate_mcps[24]  = 0;
  calibrationData.per_vcsel_cal_data.short_a_offset_mm  = 0;
  calibrationData.per_vcsel_cal_data.short_b_offset_mm  = 0;
  calibrationData.per_vcsel_cal_data.medium_a_offset_mm  = -913;
  calibrationData.per_vcsel_cal_data.medium_b_offset_mm  = -914;
  calibrationData.per_vcsel_cal_data.long_a_offset_mm  = -908;
  calibrationData.per_vcsel_cal_data.long_b_offset_mm  = -910;

  sensor_status = myDevice.VL53L4CX_SetCalibrationData(&calibrationData);
  //DEBUG_PRINTLN("Step Set Calibration Data");
  //DEBUG_PRINTLN(sensor_status);

  //should be called after VL53L4CX_DataInit()
  sensor_status = myDevice.VL53L4CX_SetDistanceMode(VL53L4CX_DISTANCEMODE_LONG);
  //DEBUG_PRINTLN("Step Distance Mode");
  //DEBUG_PRINTLN(sensor_status);

  sensor_status = myDevice.VL53L4CX_StopMeasurement();
  //DEBUG_PRINTLN("Step Stop Measurement");
  //DEBUG_PRINTLN(sensor_status);

  // Enable/Disable dynamic Xtalk compensation feature
  sensor_status = myDevice.VL53L4CX_SmudgeCorrectionEnable(VL53L4CX_SMUDGE_CORRECTION_CONTINUOUS);
  //DEBUG_PRINTLN("Step Smudge Correction");
  //DEBUG_PRINTLN(sensor_status);
  // The maximum accepted value for the computed timing budget is 199.238ms
  sensor_status = myDevice.VL53L4CX_SetMeasurementTimingBudgetMicroSeconds(199238);
  //DEBUG_PRINTLN("Step Timing Budget");
  //DEBUG_PRINTLN(sensor_status);

  // Use it to check the time set
  //uint32_t tim;
  //sensor_status = myDevice.VL53L4CX_GetMeasurementTimingBudgetMicroSeconds(&tim);
  //DEBUG_PRINTLN(tim);
  //DEBUG_PRINTLN("Get Time");
  //DEBUG_PRINTLN(sensor_status);

  // The whole Init and calibration process takes about 4.5s
  myDevice.VL53L4CX_StartMeasurement();
}

Comments powered by CComment