Stato del documento: descrive il protocollo realmente implementato nel firmware attuale. Le funzioni non ancora presenti (motore logico, variabili, timer, contatori) sono elencate in fondo alla pagina come funzioni escluse, non come promesse.
Scopo del protocollo
Il protocollo di Expansum definisce il modo in cui un programma comunica con un dispositivo Expansum attraverso una connessione USB seriale.
Il protocollo permette oggi di:
- identificare il dispositivo collegato (nome, gruppo, modello, versione firmware);
- leggere uno o più ingressi digitali in un’unica richiesta;
- comandare una o più uscite digitali in un’unica richiesta;
- assegnare un nome e un gruppo al dispositivo, in modo persistente;
- passare a una modalità silenziosa pensata per software e script automatici.
Timer, contatori, variabili, logiche interne e comunicazione fra più dispositivi non fanno parte di questa versione del protocollo.
Livelli di comunicazione
Expansum espone due canali equivalenti sulla stessa connessione seriale:
- Comandi nativi, in italiano, pensati per l’uso diretto da terminale;
- Comando JSON, pensato per essere usato da un programma.
Entrambi i canali richiamano internamente le stesse funzioni: non esiste una differenza di comportamento fra i due, solo di sintassi.
Parametri della connessione seriale
| Parametro | Valore |
|---|---|
| Interfaccia | USB seriale (adattatore CP210x) |
| Velocità | 115200 baud |
| Formato | 8 bit dati, nessuna parità, 1 bit di stop (8N1) |
| Prompt | expansum:~$ (disattivabile in modalità silenziosa, vedi sotto) |
Comandi nativi
I comandi nativi si digitano direttamente al prompt della shell, in italiano:
alto UD1
basso UD1
stato ID1 ID2 ID3
id
setnome quadro1
setgruppo linea1
silente
Accettano più pin separati da spazio dove ha senso (stato, alto, basso).
Comando JSON
Il comando shell e accetta come argomento un singolo oggetto JSON, inviato per intero sulla stessa riga:
e {"funzione":"STATO","ingresso":["ID1","ID2"]}
La chiave funzione è la chiave canonica del dispatcher. Accetta come alias f e fn, oltre a traduzioni in diverse lingue (vedi sezione Multilinguismo). Il valore di funzione è sempre uno fra: ALTO, BASSO, STATO, ID, SETNOME, SETGRUPPO, SILENTE.
Corrispondenza fra comando nativo e comando JSON
| Comando nativo | funzione JSON |
Campi aggiuntivi |
|---|---|---|
alto <pin> [<pin> ...] |
ALTO |
uscita: nome pin o array di nomi |
basso <pin> [<pin> ...] |
BASSO |
uscita: nome pin o array di nomi |
stato <pin> [<pin> ...] |
STATO |
ingresso: array di nomi pin |
id |
ID |
nessuno |
setnome <nome> |
SETNOME |
valore: stringa |
setgruppo <gruppo> |
SETGRUPPO |
valore: stringa |
silente |
SILENTE |
nessuno |
Struttura delle risposte
Ogni risposta è un singolo oggetto JSON, in una delle tre forme seguenti.
Successo senza dati:
{"ok":1}
Errore:
{"e":<codice>}
Il codice è un numero intero:
| Codice | Categoria | Significato |
|---|---|---|
| 100 | Comando/parsing | La riga inviata non è un JSON valido |
| 101 | Comando/parsing | Manca il campo funzione (o l’alias f/fn), oppure non è una stringa |
| 102 | Comando/parsing | Il valore di funzione non corrisponde a nessun alias noto |
| 200 | Oggetto | Il nome del pin (ingresso o uscita) non è riconosciuto |
| 201 | Oggetto | Il campo atteso non è né una stringa né un array |
Le bande 3xx (motore logico) e 4xx (persistenza) sono riservate per funzioni non ancora implementate: al momento non esistono codici in queste fasce.
Dati diretti:
{"ID1":"1","ID2":"0"}
I valori booleani sono sempre rappresentati come stringhe "1" o "0", mai come numeri o come true/false.
Lettura degli ingressi
Il comando STATO legge uno o più ingressi in un’unica richiesta.
e {"funzione":"STATO","ingresso":["ID1","ID2","ID3","ID4","ID5","ID6","ID7","ID8"]}
{"ID1":"1","ID2":"0","ID3":"0","ID4":"1","ID5":"0","ID6":"0","ID7":"0","ID8":"0"}
La lettura richiede sempre un array esplicito dei pin desiderati: non esiste un comando che restituisca “tutti gli ingressi” senza elencarli.
Comando delle uscite
I comandi ALTO e BASSO impostano una o più uscite e rispondono con lo stato appena scritto:
e {"funzione":"ALTO","uscita":["UD1","UD2"]}
{"UD1":"1","UD2":"1"}
Non esiste un comando di rilettura delle uscite: l’hardware non può leggere indietro lo stato fisico di un’uscita. Un programma che vuole conoscere lo stato delle uscite deve tenerne traccia in memoria propria, aggiornandola a ogni risposta di ALTO/BASSO.
Identificazione del dispositivo
Il comando ID restituisce le informazioni del dispositivo, sempre nello stesso ordine di campi:
e {"funzione":"ID"}
{"gruppo":"linea1","nome":"quadro1","modello":"8ID8UD","fw":"1.0.0"}
| Campo | Modificabile | Note |
|---|---|---|
gruppo |
sì, tramite SETGRUPPO |
pensato per future reti multi-dispositivo |
nome |
sì, tramite SETNOME |
|
modello |
no | determina il numero di ingressi/uscite lato software |
fw |
no | versione firmware |
Nome e gruppo del dispositivo
e {"funzione":"SETNOME","valore":"quadro1"}
{"ok":1}
SETGRUPPO funziona allo stesso modo, con funzione diverso. Entrambi i valori vengono salvati in memoria persistente (NVS): sopravvivono allo spegnimento e a un successivo aggiornamento firmware. Solo una cancellazione esplicita della partizione di storage li azzera.
Modalità silenziosa
Il comando SILENTE disattiva l’eco dei caratteri digitati e rimuove il prompt dalla shell:
e {"funzione":"SILENTE"}
Dopo questo comando la shell non stampa più expansum:~$ prima di ogni riga. È pensata per essere il primo comando inviato da un programma o da uno script, in modo da poter interpretare l’output senza dover filtrare prompt ed eco residui.
Multilinguismo
Il canale JSON (non i comandi nativi) accetta alias in più lingue su tre livelli indipendenti:
- nomi dei pin;
- nomi dei campi (
funzione/uscita/ingresso/valore); - valori della funzione (
ALTO/BASSO/STATO/…).
Le lingue attualmente supportate, oltre all’italiano (canonico): inglese, spagnolo, francese, portoghese, indonesiano, tedesco, swahili, turco, vietnamita, olandese, polacco, rumeno, ceco, ungherese, svedese, norvegese, danese, finlandese, croato.
Un limite tecnico della shell Zephyr esclude le lingue che non usano l’alfabeto latino (cinese, arabo, russo, hindi, giapponese, coreano e altre): i caratteri non stampabili in ASCII esteso vengono scartati dalla shell prima ancora che il comando raggiunga il parser JSON. Per lo stesso motivo, accenti e diacritici vengono sempre rimossi anche nelle lingue latine supportate.
I comandi nativi restano disponibili solo in italiano.
Prestazioni
Il tempo di andata e ritorno (round-trip) di un comando è fisso a circa 12 ms su adattatore CP210x, indipendentemente da baud rate e dimensione del payload. Per cicli di lettura/scrittura continui si consiglia un intervallo minimo di 50 ms per ciclo, per restare con margine sotto il limite fisico osservato.
Esempio da terminale
$ e {"funzione":"ID"}
{"gruppo":"linea1","nome":"quadro1","modello":"8ID8UD","fw":"1.0.0"}
$ e {"funzione":"STATO","ingresso":["ID1"]}
{"ID1":"0"}
$ e {"funzione":"ALTO","uscita":["UD1"]}
{"UD1":"1"}
Esempio Python minimale
import json
import serial
port = serial.Serial("/dev/ttyUSB0", 115200, timeout=1)
def invia(funzione, **campi):
richiesta = {"funzione": funzione, **campi}
linea = "e " + json.dumps(richiesta) + "\n"
port.write(linea.encode("utf-8"))
risposta = port.readline().decode("utf-8").strip()
return json.loads(risposta)
# Modalità silenziosa, da inviare per prima
invia("SILENTE")
# Identificazione del dispositivo
print(invia("ID"))
# Lettura di un ingresso
print(invia("STATO", ingresso=["ID1"]))
# Attivazione di un'uscita
print(invia("ALTO", uscita=["UD1"]))
port.close()
Elenco dei comandi
funzione |
Effetto | Campi aggiuntivi |
|---|---|---|
ID |
Identifica il dispositivo | nessuno |
STATO |
Legge uno o più ingressi | ingresso (array) |
ALTO |
Attiva una o più uscite | uscita (array) |
BASSO |
Disattiva una o più uscite | uscita (array) |
SETNOME |
Imposta il nome del dispositivo | valore |
SETGRUPPO |
Imposta il gruppo del dispositivo | valore |
SILENTE |
Disattiva eco e prompt | nessuno |
Elementi da confermare o completare
- comportamento delle uscite alla disconnessione USB o al riavvio (oggi: tutte a OFF all’avvio, per specifica generale del progetto — da confermare come comportamento firmware effettivo);
- dimensione massima del buffer comando in caratteri (attualmente confermata fino a circa 4095 caratteri utili);
- eventuale numero di versione del protocollo, non ancora previsto.
Funzioni escluse da questa versione
Le seguenti funzioni sono previste nella visione generale del progetto ma non fanno ancora parte del firmware:
- variabili persistenti e volatili;
- contatori e timer;
- logiche AND/OR/NOT fra oggetti;
- comunicazione fra più dispositivi (RS485);
- aggiornamento firmware via protocollo;
- diagnostica avanzata e registrazione eventi.
Verranno documentate separatamente quando saranno disponibili sul firmware.
Sintesi
programma -> e {"funzione":"...", ...} -> Expansum
programma <- {"ok":1} | {"e":<codice>} | {"chiave":"valore"} <- Expansum
Ogni comando ha un equivalente nativo in italiano e un equivalente JSON con gli stessi effetti. Il canale JSON aggiunge il supporto multilingua sugli alias, restando comunque tradotto verso la stessa chiave canonica funzione.