Vue d'ensemble de l'architecture — module Puara pour le Seeed XIAO ESP32-S3 Sense
Un firmware de détection de mouvement qui observe un flux caméra, calcule les différences de pixels image par image, et diffuse les résultats sous forme de messages OSC via WiFi — afin qu'un DAW, un synthétiseur, ou un environnement génératif puisse réagir à la présence, la position et la forme d'objets en mouvement dans le cadre. Conçu à l'origine pour suivre des formes sombres (sculptures cinétiques en bobines) sur un fond clair, mais fonctionne pour toute source de mouvement sombre sur fond clair.
| Carte | Seeed XIAO ESP32-S3 Sense |
| Caméra | OV2640 (intégrée) |
| Microphone | PDM (intégré, GPIO 41/42) |
| Sorties moteur | D2 = GPIO 3, D3 = GPIO 4 (PWM, 1 kHz, 10 bits) |
| Connexion | WiFi 2,4 GHz |
| Alimentation | USB-C |
Aucun câblage requis pour la caméra ou le micro ; les sorties moteur nécessitent un pilote externe pour tout ce qui dépasse un petit signal.
La caméra et le microphone alimentent le firmware, qui soit diffuse un paquet OSC vers un logiciel récepteur, soit sert une petite interface web (avec un aperçu MJPEG en direct) dans un navigateur pour calibrer la caméra avant un spectacle. Les deux chemins partagent la même connexion WiFi et le même stockage de paramètres sur l'appareil.
En interne, trois morceaux de code s'exécutant en parallèle — deux tâches FreeRTOS plus la boucle principale Arduino — partagent leur état via des structures protégées par mutex :
diffThreshold comptent comme « en mouvement ». À partir de ces
pixels, elle dérive une boîte englobante, un centroïde, une vitesse d'image en image, une dispersion
(écart-type autour du centroïde), un taux de remplissage (densité de mouvement dans la boîte
englobante), et une magnitude globale (fraction du cadre en mouvement).pixThreshold compte comme « matière de bobine sombre ». Cela donne une fraction de
couverture globale et un centroïde de la masse sombre, plus trois valeurs par zone sur une grille de
6×4 = 24 zones : couverture en pixels sombres, densité de gradient/contour (indicateur de l'enroulement
serré des bobines), et variance locale de luminosité (richesse de texture).micGain plafonné à 1,0.Le serveur web (port 80, issu du module Puara) et le serveur de capture MJPEG optionnel (port 81) fonctionnent indépendamment de cette boucle, lisant la dernière image brute et les paramètres actuels au besoin.
Tous les paramètres se trouvent sur la page web Settings, s'appliquent immédiatement sans reflashage, et persistent au redémarrage.
| Paramètre | Défaut | Notes |
|---|---|---|
oscIP | 192.168.4.2 | IP de l'ordinateur recevant l'OSC |
oscPORT | 8000 | Port UDP sur lequel le récepteur écoute |
localPORT | 8000 | Port UDP sur lequel l'appareil écoute pour l'OSC entrant |
diffThreshold | 30 | Delta de luminosité de pixel (0–255) comptant comme mouvement ; plus bas = plus sensible |
videoStream | 0 | Mettre à 1 pour activer l'aperçu MJPEG sur le port 81 ; désactiver pendant la performance |
camContrast | 0 | Contraste matériel de l'OV2640, plage -2 à 2 ; plus élevé aide les formes sombres à ressortir |
micGain | 10 | Gain linéaire sur le RMS brut du micro, plafonné à 1,0 ; plage pratique 1–100 |
pixThreshold | 100 | Luminosité absolue (0–255) en dessous de laquelle un pixel compte comme matière de bobine sombre |
Envoyé comme un seul paquet OSC vers oscIP:oscPORT ; contient toujours tous les messages
ci-dessous. Le nom de l'appareil provient de config.json (par défaut
snake_differ_0).
| Adresse | Args | Maintien | Description |
|---|---|---|---|
/<device>/motion | motion (int), magnitude (float) | mag seulement | État de mouvement actuel et fraction du cadre en mouvement |
/<device>/bbox | x_min, y_min, x_max, y_max (float×4) | oui | Boîte englobante du mouvement, normalisée 0–1 |
/<device>/centroid | cx, cy (float×2) | oui | Centre de masse des pixels en mouvement, normalisé |
/<device>/velocity | vx, vy (float×2) | non | Déplacement du centroïde par image ; zéro à l'arrêt |
/<device>/spread | sx, sy (float×2) | oui | Écart-type des pixels en mouvement autour du centroïde |
/<device>/fill | fill (float) | oui | Densité de mouvement dans la boîte englobante |
/<device>/mic | rms (float) | non | RMS du micro intégré, normalisé 0–1, indépendant du mouvement visuel |
/<device>/coverage | coverage (float) | non | Fraction de l'ensemble du cadre sous pixThreshold |
/<device>/mass | cx, cy (float×2) | non | Centroïde de tous les pixels sombres, suit la masse de bobine même à l'arrêt |
/<device>/grid/coverage | 24 flottants | non | Fraction de pixels sombres par zone (6×4, ligne par ligne, ligne 0 = haut) |
/<device>/grid/density | 24 flottants | non | Magnitude de gradient/contour par zone — étroitesse des bobines |
/<device>/grid/texture | 24 flottants | non | Variance locale par zone — richesse de texture |
| Adresse | Arg | Description |
|---|---|---|
/snake_differ/threshold | fraction (float) | Met à jour diffThreshold en direct ; la valeur est une fraction de 255 |
/snake_differ/motor1 | vitesse (int 0–1000) | Rapport cyclique PWM sur D2 / GPIO 3 |
/snake_differ/motor2 | vitesse (int 0–1000) | Rapport cyclique PWM sur D3 / GPIO 4 |
pio run --target upload
pio run --target uploadfs
Nécessite la plateforme pioarduino (ESP32 Arduino 3.x) — la plateforme PlatformIO ESP32 2.x
standard a un plantage connu à la réception UDP ; platformio.ini pointe déjà vers la bonne
plateforme.
Au premier démarrage (ou sans routeur configuré), l'appareil crée son propre point d'accès (SSID
snake_differ_0, mot de passe garnetwillis).
http://snake_differ_0.local (repli : l'IP par défaut du point d'accès,
192.168.4.1, ou celle affichée par le moniteur série). Les pages Config / Scan / Settings s'y
trouvent toutes.
Sur la page Config, saisir le SSID/mot de passe du routeur et Enregistrer ; l'appareil se connecte au prochain redémarrage et le point d'accès reste actif en secours.
videoStream = 1, ouvrir Config, vérifier le cadrage.camContrast jusqu'à ce que la forme suivie apparaisse clairement plus sombre que
le fond.pio device monitor) pour un
[diff] no motion continu.[diff] mag=... devraient apparaître immédiatement.diffThreshold jusqu'à une détection propre avec un minimum de bruit.videoStream = 0, saisir oscIP/oscPORT, Enregistrer.udpreceive 8000 → OSC-route /<device>/motion
/<device>/bbox ... → unpack par message.
Quand videoStream = 1, un flux MJPEG est servi sur
http://<ip_appareil>:81/stream (aussi intégré directement dans la page Config) via une
seule connexion HTTP multipart persistante — pas de sondage, pas d'aller-retour par image. L'aperçu est la
même image en niveaux de gris utilisée pour le calcul de différence, et camContrast affecte les
deux. Désactiver videoStream avant un spectacle — l'encodage JPEG tourne en
continu tant qu'un client est connecté.
/stream), actif uniquement si
videoStream = 1pioarduino (ESP32 Arduino 3.x) requise