In questi giorni mi sto cimentando nel far dialogare due Arduini tra di loro attraverso la porta seriale, diciamo che sono proprio all'inizio della definizione della cosa e di codice per adesso ne ho scritto poco. Ho pensato che anzichè lavorare da solo e portare avanti la mia idea rozza di protocollo seriale avrei potuto coinvolgere (o quantomeno provarci) qualche volenteroso e definire ed implementare un qualcosa che potesse essere versatile e riutilizzabile per tutti coloro che hanno bisogno di far comunicare due microcontrollori.
Giusto per non essere un post di sole parole comincio a descrivere qualche idea:
Basarsi sulla codifica ASCII...per semplicità di utilizzo
Già che ci siamo prevedere l'orientamento al multi device, a questo punto pensavo di usare RS485
Dato che non sono un esperto di protocolli, mi stavo chiedendo se fosse meglio che la lunghezza della trama da trasferire fosse fissa oppure va bene anche variabile.
per ora la conversione in char di interi e float la affido ad una funzione sprintf e mi rendo conto che non sia un modo molto elegante.
So che come idea di partenza non è un granché però se a qualcuno dovesse interessare creare un protocollo seriale si potrebbe poi partire insieme...
Per indirizzo e comando sicuramente ti basta un solo byte, 255 address e comandi possibili, in fondo, prima dello stop, aggiungi almeno un byte per il checksum, meglio se ne prevedi due per un eventuale uso di un CRC16.
La lunghezza del pacchetto se è fissa è più semplice da gestire, sopratutto in caso di errori, se è variabile è più versatile, ma è decisamente più complessa da gestire, come minimo devi aggiungere un byte, subito dopo l'address, che indica la lunghezza reale del pacchetto dati.
@ ypkdani: quel progetto di modbus l'avevo già notato, non c'è un motivo particolare per cui ho deciso di non usarlo, vuoi un po' per la scarsità di documentazione che si trova in rete e vuoi perché mi sembra un protocollo molto specializzato. L'idea che ho è di fare qualcosa di più semplice, probabilmente meno performante, pensavo di creare un pacchetto di lunghezza fissa, dove supponiamo puoi scrivere 4 byte di informazioni, nel modo in cui più ti piace, sarà poi grazie alla distinzione del comando capire come interpretarla.
Comunque grazie per il secondo link, con alcune spiegazioni sul modbus...è una specie di riassunto ma è molto chiaro!
Stamattina ho scovato questa utility delle avr-libc che fanno già loro il calcolo del CRC16, così sembrerebbe semplice implementarlo.
@astrobeed: hai ragione per gli indirizzi basterebbe un solo byte, per i comandi pure ma usandone 2, in formato carattere i comandi secondo sarebbero più user-friendly, intendo una cosa del genere:
A0 -> canale Analogico 0
TA -> Temperatura Ambiente
ecc.
...sicuramente è uno spreco di risorse, però secondo me aiuterebbe nell'utilizzo. Mi sfugge qualcosa?
Pensavo di abolire il byte di Start e quello di Stop e fare come nel Modbus di cominciare con l'indirizzo dello Slave e terminare con un Timeout
1 byte può contenere 256 valori differenti. Ogni valore potrebbe essere un token differente, ossia un codice che identifica un comando.
L'utente potrebbe scrivere A0 ed il software convertire tale comando nel corrispondente token. A quel punto potresti anche dare all'utente l'opportunità di usare TA1 per "sensore temperatura ambiente 1", continuando ad avere 1 token da 1 byte lato programma.
Il CRC16 è un calcolo abbastanza pesante da eseguire, non va molto d'accordo con le piccole MCU a 8bit, però è bene prevederlo per avere maggiore flessibilità nell'ottica di espansioni future del protocollo, inizialmente puoi usare i due byte per un classico cheksum a 16 bit.
Meglio tenere il più leggero possibile la parte controllo perché è facile andare incontro a problemi di overhead eccessivo quando si trasmettono pochi dati, cioè devi inviare una grossa percentuale di dati dedicati al controllo rispetto a quelle del payload vero e proprio.
Il protocollo modbus è molto ben documentato, molto affidabile e largamente utilizzato anche in ambito industriale, però è pesante da gestire e se non hai una reale necessità, e non è il tuo caso, è inutile complicarsi la vita.
La questione del singolo byte per i comandi rientra nella logica dell'hoverhead, tanto mica c'è un essere umano che deve decodificare i dati quindi poco importa se arriva 0x33 invece di "A0" però c'è da tenere conto anche del parser, cioè la parte software che deve decodificare i comandi.
Se i comandi sono di un solo byte te la cavi con una switch su un singolo carattere, veloce e occupa poco spazio in memoria, invece nel caso dei due byte devi fare la switch su un array che è molto più lenta e occupa molta più memoria.
La struttura preambolo-payload-postambolo con timeout è una delle più semplici, molto affidabile e largamente usata in tutti gli ambiti.
Ok mi avete pienamente convinto!!
A questo punto la cosa più difficile sembra essere solo inventare il Parser per rendere versatile il codice ad ogni esigenza. Panico!
io toglierei il campo "comando" e lo implementerei all'inerno dei dati.
Userei anche il sistema ad un byte, tanto lo ""sbatti" lo fai una volta e poi non ci pensi più.
PS:
il CRC non esiste solo a 16 bit, esiste anche a 8 (e 4) bit. Sicuramente sarà molto più rapido nel calcolo e più semplice da imlementare. E dovrebbe bastarti per le tue esigenze.
se hai mai visto l'iso/osi, ti sarai accorto che il sistema è implementato a strati.
Quindi, lasciando perdere gli strati più basso livello (hardware, TTL, etc)
tu avrai lo strato "indirizzamento" composto da
Start - indirizzo - dati - checksum - stop
e lo strato "comandi" all'interno di dati composto da
comado - valore
In questo modo, se devi comunicare cose che non hanno un comando o viceversa cose che non hanno dati, puoi riutilizzare lo strato "indirizzamento" ma riscrivere lo strato "comandi" ad-hoc. Altrimenti dovresti riscrivere tutto
ma che ne dite invece di aggiungere un byte per la lunghezza del comando successivo?
in questo modo non si deve sottostare alla trama fissa e si è più liberi anche nel parsing, no?
anche questa è una cosa prevista nel modbus...provando ad andare avanti a scrivere un po' di codice, mi sono scontrato con lo scoglio di scrivere tramite terminale seriale dati in formato esadecimale...fino a 127 me la cavo con la tastiera...ma oltre come faccio? <Alt+numero > pare non funzionare
Beh qui c'è da capire chi è lo user quando parli di user-friendly.
Secondo me questa scelta deve esser fatta in funzione di "chi" legge il dato.
Se è destinato ad un software io propenderei verso ottimizzazione di spazio e agilità nell'interpretazione tramite algoritmi.
Se è destinato ad una persona allora comprenderei la tua propensione verso il human-friendly (giusto per distinguerlo dal generico user-friendly); ma non mi sembra sia questo il caso.
Vi posto il codice che ho scritto fin'ora, per evitare di far diventare lunghissimi i post ho deciso di aggiungerlo su un repository, lo potete trovare qui.
lo sketch è composoto da tre TAB:
il principale chiamato "ProtocolloSeriale"
uno chiamato NTC
e uno "Conversioni"
Chiunque avesse voglia di provarlo una volta aperto il terminale seriale può inserire un comando composto da:
1byte indirizzo
1 byte comando
5 byte playload
2 byte CRC
Per ora i comandi implementati nell'esempio sono solo 2 e sono i corrispondenti ASCII di "," e "!".
L'indirizzo del device è 0x46 ovvero "F"
Un comando d'esempio che potreste inviare è : F,12345_h