recv_match(blocking=False) ponctuel ratait les heartbeats car aucun
appel continu ne drainait le buffer pymavlink. Ajout d'une tâche
drain_pymavlink() qui appelle recv_match() toutes les 50ms pour
remplir mav.messages, et lecture du mode via mav.messages.get()
qui retourne le dernier heartbeat reçu.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MAVSDK flight_mode() retourne UNKNOWN pour les modes ArduCopter non
mappés dans son enum PX4. Lecture directe du champ custom_mode depuis
les HEARTBEAT pymavlink avec dictionnaire de mapping ArduCopter complet.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
En udpout, mavp2p (udps) ignore le client tant qu'aucun paquet
n'a été émis depuis lui. L'envoi d'un heartbeat GCS dès la
connexion force mavp2p à mémoriser l'adresse de retour et à
relayer le flux H743 vers pymavlink.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
mavp2p tourne en serveur (udps) sur 14551 et 14552.
Les anciens bind serveur (udp://:14551, udpin:0.0.0.0:14552)
causaient "Address already in use". Les deux connexions
passent maintenant en mode client vers 127.0.0.1.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
temp → temp_c, cpu → cpu_pct pour correspondre exactement
au format {ts, width, height, fps, encoder, bitrate_kbps,
temp_c, cpu_pct, throttled, uptime_s} émis par le Pi Zero.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reçoit JSON Pi Zero (width, height, fps, encoder, temp, cpu, throttled)
toutes les secondes via UDP 5602. Affiche un overlay cv2 semi-transparent
(fond noir 55%) en haut à gauche de chaque frame MJPEG :
ligne 1 : "1280x720 | 15fps | hw | 56.2C"
ligne 2 : "CPU 42% | ok / THROTTLED"
Variable globale _pi_stats protégée par _stats_lock (threading.Lock).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inférence yolov8n.pt sur chaque frame GStreamer
- Fallback frame noir numpy si Pi Zero absent (pas de download)
- Timeout 4s sur sonde GStreamer via thread dédié
- MJPEG annoté exposé sur port 5601
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Règle iptables ACCEPT udp dpt:5600 ajoutée et sauvegardée via netfilter-persistent.
sudoers mis à jour : nicoboy peut gérer iptables sans mot de passe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sans ça, un ancien processus uvicorn reste en écoute sur 8765
entre deux lancements et répond à la place du nouveau.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3 streams gRPC infinis en asyncio.gather() saturaient l'event
loop et empêchaient uvicorn d'accepter des connexions HTTP.
- monitor_telemetry : boucle while + async for...break + sleep(1)
au lieu de 3 streams continus en gather
- monitor_armed : await sleep(0) pour céder après chaque update
- ensure_future → create_task (API recommandée Python 3.7+)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sans description, Gemini mappait "position gps" sur position
au lieu de position_gps. Chaque action a maintenant un libellé
explicite, position_gps précise horodatage + fix + satellites.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gps_fix et gps_sats ajoutés à telemetry_cache
- monitor_telemetry surveille aussi gps_info() en continu
- position_gps lit depuis le cache (comme battery/mode)
et ne risque plus de bloquer sur async for ... break
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- float(cmd.get("altitude") or 15) : gère altitude=null retourné
par Gemini pour les commandes sans altitude (land, rtl, status…)
- /command attrape les exceptions et retourne JSON 500 avec le
message d'erreur, évite le "Erreur réseau" opaque dans la page
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Ajout instruction explicite : message toujours en français
- Nouveau champ delta dans le schéma JSON : "monte de 5m" → delta=5,
"descend de 3m" → delta=-3, null pour altitude absolue
- takeoff et altitude calculent target_alt = current_alt + delta
quand delta est présent, sinon utilisent altitude absolue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- altitude : change la hauteur en vol via goto_location,
ou décollage depuis le sol si alt < 2m
- position_gps : position complète avec horodatage HH:MM:SS,
fix GPS et nombre de satellites via telemetry.gps_info()
- SYSTEM_PROMPT et message d'accueil mis à jour
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- init_pymavlink() passe en run_in_executor : ne bloque plus
l'event loop asyncio pendant le démarrage de uvicorn
- Ajout monitor_telemetry() : batterie et mode mis à jour en
continu en cache (même logique que monitor_armed)
- /status et action status lisent telemetry_cache directement,
plus aucun async for bloquant dans les route handlers
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- wireclaw_core : expose drone comme global, extrait init()
depuis main() pour permettre l'import par web_server.py ;
corrige aussi le bug drone_armed manquant global dans init
- web_server.py : FastAPI avec process(text) générique
(interpret_command → execute_command), routes GET / POST /command
GET /status, page HTML sobre avec statut temps réel (2s)
et historique des commandes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Ajout de eeprom.bin, logs/, terrain/, mav.tlog.raw générés
automatiquement par ArduPilot/MAVProxy à chaque session.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Quand Gemini mappe "monte à Xm" sur takeoff et que le drone est
déjà en vol, on recalcule l'altitude absolue MSL et on utilise
goto_location pour atteindre la nouvelle altitude à position fixe.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
drone_armed reste True après une session précédente même si le drone
est au sol. Le guard utilise désormais current_pos["alt"] < 2.0
comme goto et orbit, avec un refresh_position préalable.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>