Skip to content

Week 09: Input Devices

ToF Distance Sensor

Hero image VL53L1X Time-of-Flight Sensor — Connected a VL53L1X ToF sensor to the I2C connector on my custom PCB and successfully read distance measurements in real time via the Arduino Serial Monitor.


Assignments

Group assignment:

  • Probe an input device’s analog levels and digital signals

Individual assignment:

  • Measure something: add a sensor to a microcontroller board that you have designed and read it

1. Group Assignment

We wired a simple LED brightness control system using a potentiometer and probed both the analog input level of the potentiometer and the digital signal to the LED simultaneously with an oscilloscope.

FabLab Kannai group work page

It was a good illustration of how a continuous analog signal from the potentiometer maps to a discrete PWM signal controlling the LED brightness.

2. Individual Assignment

2-1. Sensor Overview

I chose the VL53L1X — a Time-of-Flight (ToF) distance sensor by ST Microelectronics. It fires a 940 nm VCSEL (Vertical-Cavity Surface-Emitting Laser) pulse and measures the time it takes for the photons to travel to the target and back. Because the speed of light is constant, the round-trip travel time translates directly into distance with millimeter-level accuracy.

Key specs:

  • Measurement range: 40 mm – 4000 mm
  • Interface: I2C (default address 0x29)
  • Operating voltage: 2.6 V – 3.5 V

VL53L1X sensor module

The module I used exposes six pins: VIN, GND, SCL, SDA, GPIO1 (interrupt), and XSHUT (hardware standby).

2-2. Wiring

I connected the sensor to the I2C connector already present on the motor-driver board I made in Week 08. The board is based on the Seeed Studio XIAO RP2040.

VL53L1X Pin XIAO RP2040 Pin
VIN 3.3 V
GND GND
SDA P6 (GPIO 6)
SCL P7 (GPIO 7)

2-3. Programming

First attempt: MicroPython

My first instinct was to write the firmware in MicroPython, since that is what I used in Week 08. However, after searching, I found that no maintained VL53L1X library exists for MicroPython. There is a port of the older VL53L0X library, but the two sensors are not fully compatible and the register maps differ.

Solution: Arduino + Adafruit library

I switched to the Arduino and used the Adafruit VL53L1X library, following an approach taken by Hayashi-san (2025 Kannai). The library abstracts all low-level I2C register access, making the code straightforward.

#include "Adafruit_VL53L1X.h"

#define IRQ_PIN 2
#define XSHUT_PIN 3

Adafruit_VL53L1X vl53 = Adafruit_VL53L1X(XSHUT_PIN, IRQ_PIN);

void setup() {
  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.println(F("Adafruit VL53L1X sensor demo"));

  Wire.begin();
  if (!vl53.begin(0x29, &Wire)) {
    Serial.print(F("Error on init of VL sensor: "));
    Serial.println(vl53.vl_status);
    while (1) delay(10);
  }
  Serial.println(F("VL53L1X sensor OK!"));

  Serial.print(F("Sensor ID: 0x"));
  Serial.println(vl53.sensorID(), HEX);

  if (!vl53.startRanging()) {
    Serial.print(F("Couldn't start ranging: "));
    Serial.println(vl53.vl_status);
    while (1) delay(10);
  }
  Serial.println(F("Ranging started"));

  // Valid timing budgets: 15, 20, 33, 50, 100, 200 and 500 ms
  vl53.setTimingBudget(50);
  Serial.print(F("Timing budget (ms): "));
  Serial.println(vl53.getTimingBudget());
}

void loop() {
  int16_t distance;

  if (vl53.dataReady()) {
    distance = vl53.distance();
    if (distance == -1) {
      Serial.print(F("Couldn't get distance: "));
      Serial.println(vl53.vl_status);
      return;
    }
    Serial.print(F("Distance: "));
    Serial.print(distance);
    Serial.println(" mm");

    vl53.clearInterrupt();
  }
}

How the code works

  1. setup() initialises the serial port, brings up I2C with Wire.begin(), and calls vl53.begin(0x29, &Wire) to verify the sensor is present at its default I2C address.
  2. startRanging() tells the sensor to begin continuous measurement.
  3. The timing budget (set to 50 ms here) controls the trade-off between measurement speed and accuracy — a longer budget averages more photon returns and reduces noise.
  4. loop() polls dataReady(). When a new reading is available, vl53.distance() returns the distance in millimetres. -1 signals a ranging error. After reading, clearInterrupt() resets the data-ready flag so the sensor prepares for the next measurement.

2-4. Results

After uploading the sketch, the Serial Monitor immediately showed distance values updating at roughly 20 Hz. To verify the accuracy, I held the sensor board against a metal ruler and compared the reported values with the physical distance — they matched closely throughout the sensor’s range.

ToF sensor distance measurement demo

Reflections

  • MicroPython is convenient for quick prototyping, but the ecosystem of sensor libraries lags behind the Arduino ecosystem. For sensors with complex register maps like the VL53L1X, Arduino is the more practical choice.
  • The I2C connector I included in my Week 08 PCB paid off here — plugging in the sensor was straightforward with no extra wiring.

Source Code

References

AI usage

  • I used Claude Code for writing the code and brushing up my report.

Checklist

  • [x] Linked to the group assignment page
  • [x] Documented what you learned from interfacing an input device to your microcontroller and how the physical property relates to the measured results
  • [x] Documented your design and fabrication process or linked to the board you made in a previous assignment
  • [x] Explained how your code works
  • [x] Explained any problems you encountered and how you fixed them
  • [x] Included original design files and source code
  • [x] Included a hero shot of your board

Copyright 2026 Fumiko Toyoda - Creative Commons Attribution Non Commercial Source code hosted at gitlab.fabcloud.org