Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ jobs:
target: esp32p4
- path: 'components/vl53l/example'
target: esp32s3
- path: 'components/vl53l/vl53l5cx_example'
target: esp32s3
- path: 'components/vl53l/vl53l5cx_webapp_example'
target: esp32s3
command: 'IDF_COMPONENT_MANAGER=0 idf.py build'
- path: 'components/wdi/ble_example'
target: esp32s3
- path: 'components/wdi/ble_central_example'
Expand Down
2 changes: 1 addition & 1 deletion components/dispatcher/web/test/apps_registry_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const assert = require("assert");
const { spawnSync } = require("child_process");

const root = path.resolve(__dirname, "..", "..", "..", "..");
const CATEGORIES = ["device management", "motor control", "bus tools", "input devices", "utilities"];
const CATEGORIES = ["device management", "motor control", "bus tools", "input devices", "sensors", "utilities"];
const TRANSPORTS = ["webusb", "webserial", "webhid"];

function meta(src, name) {
Expand Down
3 changes: 2 additions & 1 deletion components/dispatcher/web/test/resolve_module_id_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const consoles = [
"components/telemetry/web/telemetry.html",
"components/system/web/system_console.html",
"components/desktop/web/desktop.html",
"components/vl53l/web/vl53l5cx_console.html",
];
const hub = "components/dispatcher/web/dispatcher_hub.html";

Expand Down Expand Up @@ -855,7 +856,7 @@ function navTests() {
if (navBlock === null) navBlock = b;
else assert.strictEqual(b, navBlock, rel + ": app navigation block differs from " + consoles[0]);
assert.ok(!/installAppHandBack|hubConnectQuery/.test(b), "the navigation block must hold only what every page uses");
// the hand-back block: identical in the 8 consoles, absent elsewhere (no unused function in the plain pages)
// the hand-back block: identical in every console, absent elsewhere (no unused function in the plain pages)
if (consoles.includes(rel)) {
const h = extractHandBackBlock(src, rel);
if (handBackBlock === null) handBackBlock = h;
Expand Down
15 changes: 12 additions & 3 deletions components/usb_device/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
include($ENV{IDF_PATH}/tools/cmake/version.cmake)

# MSC, used only by the source: esp_partition (find the FAT partition),
# wear_levelling (wl_mount), fatfs (esp_vfs_fat_info / ff_diskio_get_drive).
set(usb_device_priv_requires esp_partition fatfs wear_levelling)
# ESP32-P4 full-speed PHY selection (usb_wrap_ll_phy_select); the USB HALs
# live in their own component from IDF 6.
if(IDF_VERSION_MAJOR GREATER_EQUAL 6)
list(APPEND usb_device_priv_requires esp_hal_usb)
endif()

idf_component_register(
INCLUDE_DIRS "include"
SRC_DIRS "src"
# vfs: route_console_to_cdc() registers a VFS device (esp_vfs_register /
# esp_vfs_t) to redirect stdout to the CDC interface.
# sdmmc: the public header names sdmmc_card_t (MscMedium::sd_card).
REQUIRES base_component esp_tinyusb sdmmc vfs
# MSC, used only by the source: esp_partition (find the FAT partition),
# wear_levelling (wl_mount), fatfs (esp_vfs_fat_info / ff_diskio_get_drive).
PRIV_REQUIRES esp_partition fatfs wear_levelling
PRIV_REQUIRES ${usb_device_priv_requires}
)

# X-Input registers a custom TinyUSB application class driver by overriding the
Expand Down
7 changes: 6 additions & 1 deletion components/usb_device/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,12 @@ for back-compatibility.
- **Port selection** (`Config::port`): on targets with two USB-OTG
controllers (ESP32-P4) pick the full-speed or the high-speed port; boards do
not always route the high-speed port to a device-capable connector (the
M5Stack Tab5's USB-C carries the full-speed port).
M5Stack Tab5's and the Espressif / Waveshare dev kits' USB-C carry the
full-speed port). On the P4 the full-speed port also needs the right PHY
(`Config::fs_phy`): the chip has two full-speed pad pairs, IDF leaves
USB-OTG 1.1 on GPIO 26 / 27, and the USB-C (shared with the USB-Serial-JTAG
console) is GPIO 24 / 25 — the default here, so a full-speed device shows up
on the connector.
- **Idiomatic espp**: no exceptions; `initialize()` reports failures via
`std::error_code`.
- **Safe marshaling**: the TinyUSB RX callbacks (TinyUSB task context) are drained
Expand Down
16 changes: 13 additions & 3 deletions components/usb_device/include/usb_device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -352,10 +352,20 @@ class UsbDevice : public BaseComponent {
* targets that have one). -1 = TinyUSB's default for the target: the
* high-speed port on the ESP32-P4, the full-speed port elsewhere. Boards
* do not always route the high-speed port to a device-capable connector
* (the M5Stack Tab5 wires it to its USB-A host jack; its USB-C carries the
* full-speed port, shared with the USB-Serial-JTAG console), so this lets
* the application pick the connector. */
* (the M5Stack Tab5 wires it to its USB-A host jack), so this lets the
* application pick the controller. Note that on the P4 the full-speed
* controller comes up on its own PHY pads (GPIO 26 / 27), not on the
* USB-C of the Tab5 / dev kits, which is the USB-Serial-JTAG pad pair:
* `fs_phy` (below) moves it there. */
int port{-1};
/** ESP32-P4 only, for the full-speed port (`port` 0): which of the two
* internal full-speed PHYs (pad pairs) the USB-OTG 1.1 controller drives.
* 0 = GPIO 24 / 25, the pads the USB-Serial-JTAG console normally uses
* (the USB-C connector of the Espressif / Waveshare dev kits; the
* console moves to the other pair while TinyUSB owns the port), 1 =
* GPIO 26 / 27 (IDF's power-on mapping), -1 = leave the mapping alone.
* Ignored on other targets and for the high-speed port. */
int fs_phy{0};

std::optional<CdcFunction> cdc{}; /**< Enable a CDC-ACM function. */
std::optional<VendorFunction> vendor{}; /**< Enable a vendor-specific / WebUSB function. */
Expand Down
21 changes: 21 additions & 0 deletions components/usb_device/src/usb_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@

#include "tinyusb.h"
#include "tinyusb_default_config.h"
#if CONFIG_IDF_TARGET_ESP32P4
#include "hal/usb_wrap_ll.h" // usb_wrap_ll_phy_select: which FS PHY pads USB-OTG 1.1 drives
#include "soc/usb_wrap_struct.h"
#endif
#include "tusb.h"
// Only pull in the CDC-ACM helper when the CDC class is actually compiled in
// (CONFIG_TINYUSB_CDC_COUNT > 0 -> CFG_TUD_CDC). This keeps XInput-only / vendor-
Expand Down Expand Up @@ -1505,6 +1509,23 @@ bool UsbDevice::initialize(std::error_code &ec) {
}
tusb_cfg.port = static_cast<tinyusb_port_t>(config_.port);
}
#if CONFIG_IDF_TARGET_ESP32P4
// The P4 has two internal full-speed PHYs; at power-on the USB-Serial-JTAG
// owns PHY 0 (GPIO 24 / 25, the dev kits' USB-C) and USB-OTG 1.1 is wired to
// PHY 1 (GPIO 26 / 27), and IDF never swaps them. A full-speed device meant
// for the USB-C therefore has to take PHY 0 (the console moves to PHY 1).
if (tusb_cfg.port == TINYUSB_PORT_FULL_SPEED_0 && config_.fs_phy >= 0) {
if (config_.fs_phy > 1) {
logger_.error("Invalid fs_phy {} (0 = GPIO 24/25, 1 = GPIO 26/27, -1 = leave)",
config_.fs_phy);
ec = std::make_error_code(std::errc::invalid_argument);
return false;
}
usb_wrap_ll_phy_select(&USB_WRAP, static_cast<unsigned int>(config_.fs_phy));
logger_.info("USB-OTG 1.1 on full-speed PHY {} (GPIO {})", config_.fs_phy,
config_.fs_phy == 0 ? "24/25" : "26/27");
}
#endif
tusb_cfg.descriptor.device = &impl_->device_desc;
tusb_cfg.descriptor.string = impl_->strings.data();
tusb_cfg.descriptor.string_count = static_cast<int>(impl_->strings.size());
Expand Down
8 changes: 7 additions & 1 deletion components/vl53l/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
# vl53l.hpp / vl53l5cx.hpp need base_peripheral. The optional
# vl53l5cx_service.hpp (the VL53L5CX console protocol as a dispatcher service)
# adds base_component / dispatcher / stream_frame; vl53l5cx_protocol.hpp is
# standard-library only.
idf_component_register(
INCLUDE_DIRS "include"
REQUIRES "base_peripheral"
# src: the VL53L5CX driver and its firmware images (~84 KB of .rodata each)
SRC_DIRS "src"
REQUIRES "base_peripheral" "base_component" "dispatcher" "stream_frame"
)
93 changes: 75 additions & 18 deletions components/vl53l/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,83 @@

[![Badge](https://components.espressif.com/components/espp/vl53l/badge.svg)](https://components.espressif.com/components/espp/vl53l)

The
[VL53L4CD](https://www.st.com/en/imaging-and-photonics-solutions/vl53l4cd.html)
(and [VL53L4CX](https://www.st.com/en/imaging-and-photonics-solutions/vl53l4cx.html))
is a Time-of-Flight (ToF) distance sensor. It is a sensor that can measure
distance from a target object without touching it. The sensor uses a laser to
send a pulse of light and measures the time it takes for the light to reflect
back to the sensor. The sensor can measure distance up to 1.3 meters (VL53L4CD)
or up to 6 meters (VL53L4CX) with a resolution of 1mm.
Drivers for ST's VL53L Time-of-Flight (ToF) sensors that talk over I2C with
16-bit register addresses:

> **Note:** This component is designed for the **VL53L4CD** and **VL53L4CX**
> sensors, which use a 16-bit register addressing scheme. It is **not**
> compatible with the older VL53L0X or VL53L1X sensors, which use a different
> register map and communication protocol.
| Class | Sensors | What it measures |
| --- | --- | --- |
| `espp::Vl53l` (`vl53l.hpp`) | VL53L4CD, VL53L4CX | one distance, up to 1.3 m (CD) or 6 m (CX), 1 mm resolution |
| `espp::Vl53l5cx` (`vl53l5cx.hpp`) | VL53L5CX, VL53L7CX | a 4x4 or 8x8 grid of distances up to 4 m over a 63° (L5CX) or 90° (L7CX) diagonal field of view, at up to 60 Hz (4x4) or 15 Hz (8x8) |

The sensor can struggle with outdoor use, as sunlight can interfere with the
sensor's ability to measure distance. The sensor is also not able to measure
distance through glass or other transparent materials.
The sensors use a laser to send a pulse of light and measure the time it takes
for the light to reflect back. They can struggle outdoors, as sunlight
interferes with the measurement, and cannot measure through glass or other
transparent materials.

## Example
> **Note:** Neither driver is compatible with the older VL53L0X or VL53L1X
> sensors, which use a different (8-bit) register map and communication
> protocol.

The [example](./example) shows the use of the `Vl53l` component to communicate
with a VL53L4CD time-of-flight distance sensor.
## VL53L4CD / VL53L4CX (`espp::Vl53l`)

Single-zone ranging: timing budget, inter-measurement period, continuous or
autonomous mode, data-ready polling and interrupt clearing.

## VL53L5CX (`espp::Vl53l5cx`)

Multizone ranging, for the VL53L5CX and the VL53L7CX (its 90° sibling: the
same silicon, ids and command interface, a different firmware image -
`Config::variant` picks it, since the parts cannot be told apart over I2C; the
wrong image fails at the first firmware command after boot). The sensor has
no firmware in ROM: `initialize()` (run by
the constructor by default) reboots it and uploads the ~84 KB VL53L5CX firmware
over I2C, which takes about two seconds at 400 kHz (the sensor supports 1 MHz
Fast Mode Plus). The driver then offers:

- resolution (4x4 / 8x8), ranging frequency, integration time, sharpener,
target order (closest / strongest), ranging mode (continuous / autonomous),
power mode, I2C address change;
- `start_ranging()` / `stop_ranging()`, `is_data_ready()` polling and
`get_ranging_data()`, which fills a `RangingData` with, per zone, the
distance, sigma, signal and ambient rates, SPAD count, reflectance estimate,
number of targets and a target status (5 and 9 mean a valid range);
- the motion indicator (`enable_motion_indicator()`).

Zones are reported in row-major order (`zone = row * width + col`); see ST's
UM2884 for the physical orientation of zone 0. One target per zone is
supported (the ULD default). The crosstalk calibration and detection threshold
plugins of ST's driver are not ported yet; the default crosstalk shape is
loaded.

The I2C device's transaction timeout must cover the driver's longest transfers
(512-byte firmware chunks, ~1.4 KB result frames): a few tens of milliseconds
at 400 kHz, more at 100 kHz.

### Web console (`espp::Vl53l5cxService`)

`vl53l5cx_service.hpp` wraps a `Vl53l5cx` as an espp dispatcher module (id 10,
protocol `espp.vl53l5cx`, wire format in `vl53l5cx_protocol.hpp`): a host can
configure every setting, start / stop ranging and receive each ranging frame.
The hosted [VL53L5CX console](https://esp-cpp.github.io/espp/apps/vl53l5cx_console.html)
(`web/vl53l5cx_console.html`) shows the frames as a live 3D view over WebUSB /
Web Serial; see the [vl53l5cx_webapp_example](./vl53l5cx_webapp_example).

![VL53L5CX console: live 3D surface view of an 8x8 frame](https://github.com/user-attachments/assets/dd696291-8066-4764-86a0-1ca3d2425e8f)

### License note

The VL53L5CX firmware image and default configuration (`src/vl53l5cx_firmware.cpp`)
and the sensor sequences in `src/vl53l5cx.cpp` come from STMicroelectronics'
VL53L5CX Ultra Lite Driver (ULD 1.1.2, as redistributed by
[stm32duino/VL53L5CX](https://github.com/stm32duino/VL53L5CX)) and are
redistributed under their BSD-3-Clause license (see the file headers). The
rest of the component is MIT like the rest of espp.

## Examples

- [example](./example): `Vl53l` with a VL53L4CD, printing the distance.
- [vl53l5cx_example](./vl53l5cx_example): `Vl53l5cx` printing the 8x8 grid of
distances at 15 Hz.
- [vl53l5cx_webapp_example](./vl53l5cx_webapp_example): `Vl53l5cx` behind
`Vl53l5cxService` over USB (WebUSB / Web Serial) for the hosted VL53L5CX
console's live 3D view and settings, next to the standard USB services.
10 changes: 9 additions & 1 deletion components/vl53l/idf_component.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,31 @@
## IDF Component Manager Manifest File
license: "MIT"
description: "VL53L Time of Flight (ToF) Distance Sensor component for ESP-IDF"
description: "VL53L Time of Flight (ToF) sensor components for ESP-IDF: VL53L4CD / VL53L4CX distance sensors and the VL53L5CX / VL53L7CX multizone (4x4 / 8x8) sensors, with a WebUSB console service"
url: "https://github.com/esp-cpp/espp/tree/main/components/vl53l"
repository: "git://github.com/esp-cpp/espp.git"
maintainers:
- William Emfinger <waemfinger@gmail.com>
documentation: "https://esp-cpp.github.io/espp/distance_sensors/vl53l.html"
examples:
- path: example
- path: vl53l5cx_example
- path: vl53l5cx_webapp_example
tags:
- cpp
- Component
- Sensing
- VL53L
- VL53L5CX
- VL53L7CX
- ToF
- Time-of-Flight
- Distance
- Multizone
- Laser
dependencies:
idf:
version: '>=5.0'
espp/base_peripheral: '>=1.0'
espp/base_component: '>=1.0'
espp/dispatcher: '>=1.0'
espp/stream_frame: '>=1.0'
Loading
Loading