- decodeTelemetry() ora scarta frame incompleti o con CRC8 errata (prima non validava nulla: frame corti/corrotti da ESP-NOW venivano decodificati leggendo oltre il frame -> telemetria spazzatura, e OOB read su FLIGHT_MODE). - test_decode.cpp: vettori ricostruiti come frame reali con CRC valida (calcolata offline), battery ri-quadrato a lth=10, assert bat_fuel_drawn corretto a 350 (era 89678, pre-patch uint24), test "non valida CRC" invertito in "rigetta CRC errata", aggiunto test su frame troncato. - Emulatore (03) e backpack reale usano lo stesso framing/CRC -> passano. DA RI-ESEGUIRE A BANCO: 'pio test -e esp32dev_test' e test RF reale. Se la telemetria reale sparisce, il backpack potrebbe non rilasciare una CRC valida nel frame ESP-NOW -> in tal caso ripiegare su validazione solo-lunghezza. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Telemetry Bridge — firmware unico multi-modo
ESP32 DevKit che riceve la telemetria CRSF dal TX Backpack ELRS via ESP-NOW e la espone in 4 modalità selezionabili a runtime (comando seriale, scelta salvata in NVS). Un solo firmware da compilare; nessuna ricompilazione per cambiare uso.
Modalità
| # | Nome | USB | Cosa fa |
|---|---|---|---|
| 0 | HUMAN | testo | Dashboard leggibile (GPS, batteria, assetto, link) a 1 Hz |
| 1 | CRSF_DUMP | testo | Dump esadecimale grezzo di ogni frame CRSF (debug/reverse) |
| 2 | LTM | binario | Traduce CRSF → LTM v2 per mwptools (monitoraggio volo) |
| 3 | MAVLINK | binario | Traduce CRSF → MAVLink v2 per mwptools (include corrente in A) |
Selezione della modalità (comando seriale)
La scelta è persistente (NVS). Dal monitor seriale @115200 digita:
!seguito da0..3→ seleziona quel modo direttamente;!seguito dam→ passa al modo successivo (ciclico).
La board salva la scelta in NVS e si riavvia nel nuovo modo. Senza comando, riparte sull'ultimo modo salvato.
Perché un comando seriale e non il pulsante BOOT. BOOT è GPIO0, strapping pin del bootloader: tenerlo premuto al reset manda l'ESP32 in download mode invece di eseguire il firmware, quindi il cambio modo via BOOT non era affidabile. Il prefisso
!è obbligatorio perché mwp in modo INAV interroga via MSP (byte$M<...): senza prefisso laMverrebbe interpretata come comando e farebbe cambiare modo da sola.
Uso con mwptools
Entrambi i modi LTM e MAVLink sono downlink unidirezionale — perfetti per il path downlink-only del backpack. In mwp: Connect → porta USB dell'ESP32 → protocollo INAV, baud 115200.
- Modo LTM (#2): posizione, assetto, batteria (V, mAh). LTM in INAV è opera di Jonathan Hudson, lo stesso autore di mwp.
- Modo MAVLINK (#3): come LTM più la corrente istantanea in Ampere
(
SYS_STATUS/BATTERY_STATUS), RSSI (RADIO_STATUS) e % batteria — campi che LTM non trasporta. Messaggi emessi: HEARTBEAT, SYS_STATUS, GPS_RAW_INT, ATTITUDE, VFR_HUD, BATTERY_STATUS, RADIO_STATUS.
Problema noto (aperto): mwp mostra la corrente come ~0.12 A invece di 1.2 A (fattore ÷10). La codifica del firmware è spec-corretta (
current_batteryin cA: 120 cA = 1.2 A, verificato a byte). Sembra un'interpretazione non standard lato mwp; da indagare.
Cosa non fa: missioni, lettura/scrittura parametri, qualunque cosa MSP request/response (bidirezionale). Vedi i limiti qui sotto.
Limiti uplink (importante, verificato)
Il backpack ELRS via ESP-NOW è downlink-only by design. I maintainer ELRS (discussion #3510, gen 2026) confermano che il backpack non inietta pacchetti arbitrari nell'uplink OTA, e che l'uplink verso l'FC è possibile solo dal flusso CRSF dell'handset (radio → TX → OTA → RX → FC), con telemetria attiva.
Conseguenza: MSP bidirezionale verso l'FC attraverso il backpack non è realizzabile con ELRS stock (per questo il vecchio modo MSP sperimentale è stato rimosso). Per vero MSP bidirezionale con mwp serve un link seriale trasparente dedicato (coppia SiK/3DR 433-915 MHz, oppure mLRS con MSPX): architettura separata, non questo ESP32 sul backpack.
Struttura del codice
platformio.ini
src/
main.cpp # dispatcher: comando seriale '!' + NVS, loop sui modi
my_config.h # UID (bind phrase) <-- DA COMPILARE
modes.h # interfaccia Mode {begin,onFrame,tick}
mode_human.cpp # modo 0
mode_crsfdump.cpp # modo 1
mode_ltm.cpp # modo 2 (usa lib/Ltm)
mode_mavlink.cpp # modo 3 (usa lib/mavlink)
lib/
EspNowCrsf/ # init ESP-NOW + ring buffer dei frame CRSF (condiviso)
Ltm/ # encoder LTM v2 (layout fedele a inav/ltm.c)
terseCRSF/ # parser CRSF VENDORIZZATO e patchato (vedi sotto)
mavlink/ # libreria MAVLink ufficiale (c_library_v2, vendorizzata)
Aggiungere un nuovo modo = nuovo mode_xxx.cpp che esporta un Mode, più una
riga nell'array MODES[] in main.cpp.
Librerie vendorizzate
terseCRSF e mavlink sono copiate in lib/ (non più via lib_deps) così le
patch locali sono permanenti e versionate:
- terseCRSF — patch documentate in cima a
lib/terseCRSF/src/terseCRSF.h: (1) disattivati i#define SHOW_*di debug, che stampavano testo dentro lo stream binario LTM/MAVLink corrompendolo; (2) correttobat_fuel_drawn(leggeva 4 byte includendo il byte percent → mAh consumati errati). - mavlink — dialetti
minimal+common+standarddic_library_v2; include base-I lib/mavlinkinplatformio.ini(si include come<common/mavlink.h>).
Build / upload
Apri la cartella in VSCode/PlatformIO, Build → Upload → Monitor (115200).
Le librerie sono già in lib/ (la prima build scarica solo il core 2.0.17).
Ricorda di mettere il tuo UID in src/my_config.h.
Upload: questa board entra in download mode solo tenendo BOOT premuto durante il
Connecting...di esptool (poi rilascia). Non tenerlo premuto fino al reset finale.