feat(vl53l1x): Add radar screen example with OLED.#345
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new VL53L1X example that visualizes measured distance as a “radar/parking sensor” style bar on an SSD1327 128×128 OLED over SPI, tuned for a round bezel layout (issue #326).
Changes:
- Added
radar_screen.pyexample wiring up VL53L1X (I2C) + SSD1327 (SPI). - Implemented distance→bar-width and distance→brightness mapping plus a static outline frame.
- Ensured the display is cleared on exit via
try...finally.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
lib/vl53l1x/examples/radar_screen.py
Outdated
| # Clamp distance to maximum range to avoid negative ratios | ||
| clamped_dist = max(0, min(distance, MAX_DISTANCE_MM)) | ||
|
|
||
| # Calculate ratio (1.0 = extremely close, 0.0 = MAX_DISTANCE_MM or further) | ||
| ratio = 1.0 - (clamped_dist / MAX_DISTANCE_MM) | ||
|
|
||
| # Map ratio to a maximum width of 80 pixels (to fit the round screen) and brightness (15 levels) | ||
| bar_width = int(80 * ratio) | ||
| brightness = int(15 * ratio) | ||
|
|
There was a problem hiding this comment.
This loop does per-frame floating point math (ratio = 1.0 - (clamped_dist / MAX_DISTANCE_MM)), which can be relatively expensive on many MicroPython targets. Since the mapping is linear, consider using integer arithmetic (e.g., compute a proximity = MAX_DISTANCE_MM - clamped_dist and scale with //) to reduce CPU overhead and make behavior fully deterministic.
| # Clamp distance to maximum range to avoid negative ratios | |
| clamped_dist = max(0, min(distance, MAX_DISTANCE_MM)) | |
| # Calculate ratio (1.0 = extremely close, 0.0 = MAX_DISTANCE_MM or further) | |
| ratio = 1.0 - (clamped_dist / MAX_DISTANCE_MM) | |
| # Map ratio to a maximum width of 80 pixels (to fit the round screen) and brightness (15 levels) | |
| bar_width = int(80 * ratio) | |
| brightness = int(15 * ratio) | |
| # Clamp distance to maximum range and compute integer proximity | |
| clamped_dist = max(0, min(distance, MAX_DISTANCE_MM)) | |
| proximity = MAX_DISTANCE_MM - clamped_dist | |
| # Map proximity linearly to a maximum width of 80 pixels and brightness (15 levels) using integer math | |
| bar_width = (80 * proximity) // MAX_DISTANCE_MM | |
| brightness = (15 * proximity) // MAX_DISTANCE_MM |
There was a problem hiding this comment.
This is a fantastic optimization, thank you! I completely overlooked the performance cost of floating-point operations on microcontrollers. Switching to purely integer arithmetic using // is a much smarter approach to keep the loop fast and efficient. I've applied both this and the inclusive boundary fix locally. Updating the PR now.
8fe1b80 to
c53e048
Compare
nedseb
left a comment
There was a problem hiding this comment.
Salut Kaan,
docstring, try/finally, commentaires propres, c'est nickel.
✅ Points positifs
- Docstring en début de fichier — bien retenu de la review précédente 👍
- try/finally pour nettoyer l'écran à la sortie — parfait
- Arithmétique entière pour le mapping distance→barre — bon réflexe (ou bon suivi du commentaire Copilot, dans les deux cas c'est bien)
- Outline box autour de la barre — bon choix UX, ça donne un repère visuel pour le max
- Constante
MAX_DISTANCE_MMnommée et en haut — propre - L'initialisation SPI suit exactement le pattern des autres exemples SSD1327
📝 À corriger
1. Commit message incomplet
Le deuxième commit fix(vl53l1x): Optimize radar math, fix boundaries ne termine pas par un point. La convention du projet est :
fix(vl53l1x): Optimize radar math and fix boundaries.
Le commitlint CI n'a pas bloqué ici parce qu'il vérifie le header-max-length et le type-enum mais pas le point final (subject-full-stop est désactivé dans commitlint.config.js). Cependant, la convention documentée dans CONTRIBUTING.md demande un point final. C'est bon de prendre l'habitude.
2. Commentaire un peu long (ligne 37)
# Map proximity linearly to a maximum width of 80 pixels and brightness (15 levels) using integer mathÇa dépasse la longueur de ligne recommandée (99 chars). Ruff ne le détecte pas (E501 est ignoré) mais pour la lisibilité, tu peux le couper :
# Map proximity to bar width (max 80px) and brightness (max 15)3. Magic numbers pour le layout
Les positions 23, 69, 82, 17, 24, 70, 80, 15, 28, 35, 36, 50 sont des constantes de layout éparpillées. Pour un exemple c'est acceptable, mais si tu veux aller plus loin, tu pourrais les regrouper en haut :
BAR_X = 24
BAR_Y = 70
BAR_MAX_WIDTH = 80
BAR_HEIGHT = 15Ce n'est pas bloquant — c'est un exemple, pas du code de production. Mais c'est un bon réflexe à développer.
4. Suggestion : afficher aussi la barre quand distance > MAX
Actuellement, si l'objet est hors portée (> 1000 mm), la barre est vide (width=0, brightness=0) mais l'outline est toujours dessinée. C'est cohérent, mais afficher "Out of range" au lieu de "1234 mm" quand distance > MAX_DISTANCE_MM rendrait l'UX plus claire. Optionnel.
c53e048 to
7991fb6
Compare
7991fb6 to
9a4e34e
Compare
# [0.12.0](v0.11.1...v0.12.0) (2026-04-01) ### Features * **vl53l1x:** Add radar screen example with OLED. ([#345](#345)) ([e6871c3](e6871c3))
|
🎉 This PR is included in version 0.12.0 🎉 The release is available on:
Your semantic-release bot 📦🚀 |
Summary
Add a
radar_screen.pyexample for the VL53L1X sensor. It displays a real-time distance bar on the SSD1327 OLED screen using the SPI bus. The closer the object, the longer and brighter the bar. The layout has been specifically adapted for a round display bezel, including a static outline frame to indicate the maximum range.Closes #326
Changes
lib/vl53l1x/examples/radar_screen.py.framebuf.rect) around the filled bar (framebuf.fill_rect) to clearly show the scale.try...finallyblock to ensure the OLED screen is properly cleared when the script is interrupted.Checklist
ruff checkpassespython -m pytest tests/ -k mock -vpasses (no mock test broken)<scope>: <Description.>format