# Protocollo locale ECOBOT-3, versione 2

HTTP su `192.168.4.1:80`, rete AP aperta, fino a due stazioni. L’app Android usa
`Network.openConnection` sulla rete Wi-Fi selezionata. Lock e token coordinano
i client: **non sono autenticazione**. Chi è nella rete può usare lo STOP
globale. Nessuna password WPA2 introdotta.

## Sessione e movimento

1. `GET /getinfo`: JSON con `protocol:2`, `board`, `version`, `otaMaxBytes`,
   `watchdogMs`, `ssid`, `debugSerial`. Firmware precedenti senza protocollo 2
   richiedono l’aggiornamento iniziale USB.
2. Il client genera `cid` casuale non zero, 1–8 cifre esadecimali, diverso per
   ogni app/tab. `GET /lock?cid=…` risponde
   `OK;sid=1234abcd;seq=0;protocol=2` oppure HTTP 423 `LOCKED`.
3. Tutti i comandi protetti includono `cid` e `sid`. Il lock scade dopo 8 s
   senza richieste valide. L’identità non è dedotta dall’indirizzo IP o dal
   numero di telefoni connessi.
4. `GET /jsData.html?x=…&y=…&seq=…&cid=…&sid=…`: assi interi da -100 a 100,
   sequenza da 1 a 65535, poi riparte da 1. Confronto con aritmetica a 16 bit.
   Risposta `ACK;seq=N;x=X;y=Y;ms=M;estop=0`, oppure `STOP;…` per `(0,0)`.
   Duplicati o comandi vecchi di movimento: 409 `OLD;…`.
5. Uno STOP sequenziato arresta anche se duplicato e non arretra il contatore.
   `/S` arresta senza lock e invalida la sessione: i pacchetti della vecchia
   sessione non possono rimettere in moto il robot. L’app lo usa al rilascio,
   poi riacquisisce a STOP confermato; il pulsante STOP e il background
   richiedono ripresa esplicita.

I client mantengono al massimo un movimento ordinario in volo, sostituiscono
i movimenti pendenti con l’ultimo e danno priorità allo STOP. Un ACK valido
deve avere stato e sequenza attesi e arrivare entro 900 ms. Nessun riavvio del
movimento da ACK tardivi dopo invalidazione locale. Cadenza di movimento
300 ms, heartbeat da fermo 1000 ms Android / 1200 ms WebUI.

Il firmware attenua il comando dopo 700 ms senza aggiornamento e arresta
entro il watchdog 1800 ms più un ciclo di controllo. HTTP e flash non tengono
il mutex durante attese di rete. Il pulsante di emergenza fisico, se
configurato, impedisce il movimento e richiede una nuova sessione al rilascio.

## Altri endpoint

| Metodo e percorso | Parametri / risultato |
| --- | --- |
| `GET /lock` | `cid`, opzionali `sid&release=1`; rilascio verificato e arresto |
| `GET /servo` | protetto; `pan`, `tilt` da 0 a 180 |
| `GET /getservo` | JSON `pan`, `tilt` |
| `GET /action` | protetto; `type=1` cannone, 2 sirena, 3 luci toggle, 4 infrarossi |
| `GET /getstates` | JSON booleani `f1`…`f4` |
| `GET /getname` | JSON `name`; SSID completo in `/getinfo` |
| `GET /rename` | protetto; `name` 1–32 caratteri ASCII alfanumerici, spazio, `-_.`; arresto e riavvio AP |
| `GET /reboot` | protetto; arresto prima del riavvio |
| `GET /getdrive` | JSON configurazione |
| `GET /savedrive` | protetto; tutti i campi sotto, arresto prima del salvataggio |
| `GET /otaon` | protetto; arresta le uscite e abilita OTA per quella sessione, 120 s |
| `POST /update` | protetto; binario grezzo, `Content-Type: application/octet-stream` |
| `GET /update` | pagina di aggiornamento locale, anche Safari |
| `GET /`, `/joystick.html` | WebUI gzip |
| `GET /move`, `/movedir` | legacy: solo `S=1`; per muoversi usare `/jsData.html` sequenziato |

Campi obbligatori `/savedrive`: `pwmMin` 0–200, `deadZone` 0–50, `expo`
0.5–3, `turnMix` 0.1–1, `spinBand` 0–0.5, `slewRate` 600–3000, `brakeZero`
0–50, `wifiChannel` 1–13, `invertLeft`, `invertRight`, `invertSteering` 0/1.
Parametri non validi: 400; errore NVS: 500 `STORAGE_FAILED`; cambio canale:
`OK:CH_CHANGED`, richiede riconnessione.

Durante OTA i comandi di movimento e azione sono bloccati. Dimensione
massima 0x180000; magic E9, validazione immagine SDK e `project_name` della
stessa variante hardware. La partizione attiva cambia solo dopo verifica.
Un upload incompleto lascia selezionata l’app precedente. Non si attesta
un rollback automatico dopo un’immagine formalmente valida ma non avviabile:
in quel caso è disponibile il ripristino USB.

Il firmware risponde 400 a parametri errati, 403 se OTA non armato, 409 se
occupato o sequenza vecchia, 413 a dimensione errata, 415 a formato HTTP errato,
423 a controllo occupato/scaduto o emergenza, 503 a funzione non disponibile.

## Installazione USB Android

Driver `usb-serial-for-android` 3.10.0, porta 115200 8N1, reset DTR/RTS oppure
BOOT manuale. Sincronizzazione ROM e identificazione ESP8266 prima di caricare
lo stub Espressif 1.2.1 in RAM. Lettura JEDEC: flash almeno 4 MB. Scrittura
sequenziata SLIP con checksum XOR, verifica MD5 dell’intera immagine scritta,
riavvio solo dopo digest corrispondente. Il catalogo delle immagini incluse
ha SHA-256; anche lo stub incluso viene verificato per SHA-256.

Il servizio Android `connectedDevice` mantiene connessione e wake lock durante
il flash iniziato dall’utente. Nessuna installazione automatica al collegamento
USB e nessun download runtime. Un processo terminato o un cavo scollegato
richiedono di ripetere la scrittura: non viene mostrato un successo presunto.

Riferimenti: [protocollo Espressif](https://docs.espressif.com/projects/esptool/en/latest/esp8266/advanced-topics/serial-protocol.html),
[USB host Android](https://developer.android.com/develop/connectivity/usb/host),
[servizio connectedDevice](https://developer.android.com/develop/background-work/services/fgs/service-types#connected-device).
