Newsgroup: it.hobby.elettronica.digitale
Titolo: RS485: protocollo affidabile

Come al solito, ho una certa capacità di rendere complicate le cose
semplici.
 
Stavo pensando ad un protocollo per far comunicare N slave su un bus 
a 2 fili, con un Master per evitare i problemi legati al multimaster
(collision detection, bus arbitration, ...).  
Mentre pensavo a questo, ho messo in crisi il più semplice protocollo 
che si può ideare: quello "a domanda e risposta".
 
Diciamo che M (il Master) invia un messaggio a S (uno Slave) e aspetta
una risposta in un certo tempo.  Ci sono 3 possibilità.
 
- La domanda e la risposta vengono correttamente trasmesse e ricevute
  e la transazione termina normalmente senza ulteriori problemi.
- La domanda non arriva a destinazione.  Dopo un po' il Master, non
  vedendo la risposta, ci riprova.
- La risposta non arriva a destinazione.  Come sopra il Master ci
  riprova.
 
L'ultimo scenario ha degli effetti collaterali non trascurabili, secondo
me, che ho sempre sottovalutato.  Supponete che S sia un contatore (ora 
gli smart meter vanno tanto di moda) e che M chieda, con una certa 
frequenza, il valore del contatore *a partire* dall'ultima richiesta.  
In pratica il messaggio del Master è del tipo: dammi il valore attuale 
del contatore e azzeralo.
Nel terzo scenario potete capire bene cosa succede: S crede di aver
inviato, con la prima richiesta, il valore del contatore e lo azzera.
Ma il Master lo chiede nuovamente avendo perso la risposta, ma S non
lo sa e fornisce un valore diverso.
 
Ho fatto riferimento ad un contatore, ma ci sono altri esempi simili.
Per esempio, M chiede ad S se ci sono stati eventi (un tasto premuto,
un allarme, ...) dall'ultima richiesta.  Anche in questo caso, nel
terzo scenario M potrebbe ricevere dati sbagliati se la prima risposta
non arriva.
 
Tengo a precisare che quando dico "il messaggio o la risposta non arriva",
intendo che il pacchetto arriva corrotto per un rumore sulla linea
(classico uso della CRC).
 
Vi fate mai questo tipo di problemi?
 
 
Stavo anche pensando a possibili soluzioni, tipo associare ad ogni
messaggio del master un ID univoco.  In questo modo lo slave potrebbe 
sapere se la risposta precedente è andata a buon fine oppure no, 
guardando l'ID della successiva richiesta.
Naturalmente il sw dello slave si complica, perché il famoso contatore
non dovrebbe essere azzerato alla risposta, ma rimarrebbe congelato fino
alla successiva richiesta con ID diverso.
 
Oppure bisognerebbe prevedere una transazione a 3 messaggi: richiesta,
risposta, ack.  Ma gli scenari aumenterebbero perché anche l'ack
potrebbe non arrivare allo slave.

 
 
 
A volo direi che nei tuoi esempi
il master sta dando per scontato
lo stato interno dello slave, che
� determinante per la interpretazione
dei dati, quindi le info che trasmetti
sino insufficienti.
Il master pu� inviare una richiesta
contenente anche il valore dell'ultimo conteggio ricevuto.
 
Ciao!
-- 
Fabio Eboli nella vita reale
 
 
 
----Android NewsGroup Reader----
http://www.piaohong.tk/newsgroup
 

 lo sa e fornisce un valore diverso.
<cut>
 
Ciao,
perche' lo slave dovrebbe azzerare il contatore dopo aver mandato la 
risposta?
Se il contatore conta in modulo 255 o 65535 puoi leggere il valore dal 
master e calcolare il delta tra la lettura n-1 e n quindi non perdi mai 
niente.
 
Per gli allarmi puo' essere utilizzato lo stesso concetto andando ad 
analizzare i cambi di stato.
 
Questo se ho capito bene il problema.
 
Ciao
Giuseppe
 
 
 

 alla successiva richiesta con ID diverso.
 
S�, ti consiglio questo approccio qui. Questo � anche il sistema che 
viene usato per esempio nell'udp o nel tcp per evitare duplicazione.
 
Se per ogni slave ti tieni un contatore diverso che viene incrementato 
per ogni invio di pacchetti verso quello slave le cose non sono poi 
troppo complicate. Lo slave si tiene l'ultimo ID e se riceve un 
pacchetto con ID minore o uguale all'ultimo ID invia un ACK senza 
passare i dati allo strato applicativo.
 
Tutto quanto detto sopra funziona fintanto che il master non trasmette 
altri pacchetti prima di essere sicuro che tutti quelli precedenti siano 
stati ricevuti correttamente.
Questo fa calare la banda disponibile, ma credo che nel tuo caso sia un 
buon compromesso.
 
Ciao!
 

 contenente anche il valore dell'ultimo conteggio ricevuto.
 
Ho fatto l'esempio del contatore, ma come dicevo è un discorso generale.
 
Se il software è leggermente organizzato a strati, avrò un livello 
applicativo che consegna ai "livelli di rete" il messaggio.  Questi
"livelli di rete", in particolare quello chiamato datalink che si
preoccupa dell'affidabilità della trasmissione, *non* può prendersi
il lusso di ritrasmettere un messaggio senza permettere al destinatario
di capire se è una ripetizione del precedente.
 
In questo senso, il semplice protocollo "domanda/risposta" con 
ritrasmissione su RS485 (in caso di checksum errata) è un po' pericoloso, 
proprio perché potrebbe ripetere dei messaggi che sono stati correttamente
ricevuti.
Il livello applicativo deve essere a conoscenza di questa "pericolosità" 
intrinseca dei "livelli di rete" sottostanti e organizzare i messaggi in 
modo che ciò non provochi problemi.
 
La tua proposta di inviare anche il precedente valore del contatore
nasce proprio da qui: l'applicazione deve costruire dei messaggi che siano
innocui una volta consegnati al protocollo.  Quindi il livello applicativo
non può essere *indipendente* dal protocollo (su TCP/IP il messaggio "dammi
il contatore e azzeralo" sarebbe innocuo) e la separazione dello stack
a strati non sarebbe pulita.

 contenente anche il valore dell'ultimo conteggio ricevuto.
 
Altri esempi che potrebbero creare problemi nel caso
di ritrasmissione per errore:
- ruota un motore passo-passo di 3 step
- muovi un relè bistabile (per esempio nell'home automation)
- dimmi se il tasto è stato premuto dall'ultima volta
  (sempre home automation, per gestire un interruttore a parete)

 buon compromesso.
 
Assolutamente sì, non mi voglio incasinare in quel senso.
 
 
La cosa che mi fa pensare è che questo problema che ho sollevato io non
viene citato spesso.  Per esempio, stavo dando un'occhiata alle specifiche
ModBus su seriale: 
www.modbus.org/docs/Modbus_over_serial_line_V1_02.pdf
 
 
Il diagramma di stato del Master (Figura 7) non dice nulla sullo stato
"Processing error", ma nel testo si dice: "In case of an error detected 
on the frame, a retry *may* be performed", dove "may" vuol dire opzionale.
Chi decide se il frame va ritrasmesso in caso di risposta errata?  E se
viene ritrasmesso, come si fa a garantire che lo slave non lo interpreti
come un nuovo messaggio, diverso dal primo?
 
Infatti il diagramma di stato dello slave (Figura 8) non dice nulla sulla
gestione degli eventuali messaggi ripetuti.

 hai univocità e tracciabilità temporale nello stesso campo.
 
Sì, questo si può fare.

 solo quando arriva una richiesta con ID maggiore.
 
No, la cosa va gestita a livello datalink, non ai livelli superiori.
In pratica quello che dico io è che ogni pacchetto inviato, sia dal 
master che dallo slave, necessità di ACK dal ricevente.
 
Questo pacchetto di ACK viene gestito dal protocollo, non dallo strato 
applicativo. In questo modo non devi rispedire nessuna risposta. Se il 
master vuole inviare un pacchetto, lo passa allo strato di rete ed esso 
lo invia allo slave. Il pacchetto non viene considerato ricevuto 
fintanto che non si riceva l'ACK dallo slave. Allo stesso modo, lo 
slave, invia poi un pacchetto di risposta secondo un protocollo di più 
alto livello, che non considera ricevuto finché non si riceve un ACK dal 
master, e così via.
 
Ciao!
 

 master, e così via.
 
Di possibilità ce ne sono infinite.  La tua proposta prevede 4 
pacchetti, di cui due ACK, per una richiesta da parte del master ed una 
risposta da parte dello slave.
 
Io pensavo di accorpare l'ACK alla risposta da parte dello slave: 
infatti, se lo slave risponde, sicuramente ha ricevuto la richiesta e la 
sua risposta può essere considerata come un ACK.
 
L'ACK della risposta, dal master allo slave, potrebbe essere inutile (ma 
chiaramente si può implementare).  Infatti quando lo slave riceve un 
frame con un ID diverso, può considerare il precedente effettivamente 
ricevuto dal master e può eliminarlo dal buffer d'uscita.
 
L'unico difettuccio che vedo è che, se il master fa una richiesta ad uno 
slave ogni minuto, lo slave sarà tenuto a conservare l'ultima risposta 
per tutto il tempo, cioè fino a quando riceverà un'altra richiesta per 
sé con altro ID.  Ma questo si può facilmente superare con un timeout, 
oltre il quale il frame precedentemente viene considerato effettivamente 
ricevuto ed eliminato dal buffer.
 

 Vi fate mai questo tipo di problemi?
 > [...]
 
Sembra in effetti che qualcuno se li sia già fatti questi problemi.  La 
soluzione è HDLC 
(
http://en.wikipedia.org/wiki/High-Level_Data_Link_Control
) nella 
versione Normal Response Mode.
 
E' un protocollo molto flessibile che lavora su linee sincrone ed 
asincrone, half-duplex e full-duplex, con controllo di flusso, ACK e 
polling.
 
In sostanza risolvono il mio problema originario delle duplicazioni dei 
pacchetti per ritrasmissione su errore con il solito identificativo.
HDLC generalizza il concetto un po' come fa TCP: ogni trasmettitore 
(master o slave) incrementa un numero di sequenza ad ogni invio.  Il 
ricevitore manda un ACK dicendo qual è l'ultimo pacchetto ricevuto 
correttamente e il trasmettitore può ritrasmettere.
Naturalmente il ricevitore che si vede arrivare nuovamente un pacchetto 
già ricevuto (forse perché il tx s'è perso l'ACK), lo può facilmente 
scartare guardando il numero di sequenza.
 
Carino il meccanismo del token e del piggybacking delle info sul 
controllo di flusso con i dati.  In pratica il master potrebbe inviare N 
pacchetti uno dietro l'altro e nell'ultimo dare contemporaneamente il 
token allo slave per parlare.  Lo slave a questo punto invia la risposta 
includendo l'ACK dell'ultimo pacchetto ricevuto.  In questo modo, se 
devo inviare una quantità di dati molto più grande della dimensione 
massima del singolo pacchetto, l'ACK è inviato solo per l'ultimo e non 
per tutti i pacchetti.
 
Ora l'unico problema è implementare questo protocollo, o almeno un suo 
subset, per microcontrollori piccolini.  L'unica implementazione che ho 
trovato (OSHDLC) è per PC e fa largo uso di malloc() e tecniche simili.