4 Arduino Master Slave in RS485

Salve a tutti ragazzi, rinnovo il mio entusiasmo nel far parte di questo forum e sottopongo subito il mio problema. Sto realizzando un sistema domotico ed ho bisogno di un master che mi gestisca 3 slave connessi fra di loro tramite seriale. Ho trovato questo utilissimo articolo: Tinkering with Electronics...: Arduino and RS485 (ENGLISH VERSION) ma devo dirvi la verità...non c'ho capito molto... il mio master oltre ad impartire i comandi per l'accensione/spegnimento dei carichi deve anche fare una lettura dei sensori analogici...ovvero sensori gas e fumo (MQ-7 e MQ-2) sensori PIR e sensori reed per l'antifurto, temperatura ecc le letture sono richiamate esclusivamente dal master...quindi da aggiungere a quel codice mi servirebbe una sorta di: se lo slave riceve quel dato fa la lettura dei sensori e la invia al master...ma non so come fare utilizzando la tecnica dell'articolo...spero possiate aiutarmi...grazie 1000 a tutti voi...un saluto

La tecnica è abbastanza semplice.
Il tizio ha creato un protocollo di trasmissione:

Byte 1: Start Byte ( 0 hexadecimal ).
Byte 2-3: ASCII Arduino's address.
Byte 4: Byte ENQ, ACK or NAK (0x05h, 0x06h y 0x15h) .
Byte 5: ASCII Requested command.
Byte 6 y 7: ASCII Function number.
Byte 8: Sign byte (Positive 0x20h y Negative 2D)
Byte 9-12: ASCII of data bytes (0x00h-0xFFFFh)
Byte 13: Byte EOT (End of Text) (0x03h)
Byte 14-15: Checksum (addition from 2nd byte to 13th byte)

Come vedi, il dialogo avviene mediante l'invio di 16 byte.
Il 2° e 3° byte compongono l'indirizzo del dispositivo a cui sono spediti i dati.
Nel codice degli slave c'è, dopo la parte di ricezione dei dati, l'analisi dell'indirizzo. Se uno slave trova corrispondenza fra l'indirizzo del comando che ha ricevuto ed il suo indirizzo (che tu hai memorizzato nello sketch), allora sa che quel comando è destinato a lui e lo esegue.

leo72:
La tecnica è abbastanza semplice.
Il tizio ha creato un protocollo di trasmissione:

Byte 1: Start Byte ( 0 hexadecimal ).
Byte 2-3: ASCII Arduino's address.
Byte 4: Byte ENQ, ACK or NAK (0x05h, 0x06h y 0x15h) .
Byte 5: ASCII Requested command.
Byte 6 y 7: ASCII Function number.
Byte 8: Sign byte (Positive 0x20h y Negative 2D)
Byte 9-12: ASCII of data bytes (0x00h-0xFFFFh)
Byte 13: Byte EOT (End of Text) (0x03h)
Byte 14-15: Checksum (addition from 2nd byte to 13th byte)

Come vedi, il dialogo avviene mediante l'invio di 16 byte.
Il 2° e 3° byte compongono l'indirizzo del dispositivo a cui sono spediti i dati.
Nel codice degli slave c'è, dopo la parte di ricezione dei dati, l'analisi dell'indirizzo. Se uno slave trova corrispondenza fra l'indirizzo del comando che ha ricevuto ed il suo indirizzo (che tu hai memorizzato nello sketch), allora sa che quel comando è destinato a lui e lo esegue.

Grazie innanzi tutto per la risposta...per i byte 1,2,3 ci sono...non ho capito a che serve il 4 il 5 dovrebbe richiamare il comando (ad esempio digtalwrite, analogread ecc)???? giusto? il 6 e 7 non ho capito nemmeno e poi i successivi sono uguali per tutte le comunicazioni...giusto? nel codice che c'è li sul sito qual'è la parte che analizza il codice ricevuto con quello memorizzato nello sketch ed esegue l'operazione richiesta? che ovviamente è la parte che interessa a me per inserire i nuovi comandi? ed un ultima cosa....mettiamo caso che io dovessi fare la lettura di un sensore da uno slave e riportare il valore sul master...come posso fare? grazie davvero per l'aiuto...

L'utente ha creato quel protocollo.
I byte ACK, ENQ e NAQ sono solo per la gestione della comunicazione. A dir la verità, per una semplice rete di sensori, non ha molto senso. In pratica: spedisci un comando; tu lo ricevi e rispondi di averlo ricevuto e capito; il master ri-riceve la risposta. Manco dovesse autorizzare il lancio di una testata nucleare :stuck_out_tongue_closed_eyes:

Puoi ridurre enormemente il tutto.
Byte di start
Byte di indirizzo (con un byte puoi indirizzare 256 dispositivi)
Byte per il comando (come sopra, con 1 byte puoi creare 256 comandi)
Dati per il comando (qui deciderai tu)
Byte di stop
Byte di checksum (una semplice somma dei byte)

Per i comandi, devi fare quel che ti serve.
Vuoi fare la lettura di un sensore?
Byte 0x00 -> mettiamo: lettura analogica
A seguire il byte del sensore/porta da leggere.

Una volta fatta la lettura, spedisci il dato sulla linea. La RS485 altro non è che un'estensione della seriale. Quindi tu devi solo fare un serial.write e spedire i dati con lo stesso protocollo.
Come indirizzo metterai quello del master. Il master vedrà che i dati sono indirizzati a lui e li leggerà.

In effetti devo fare esattamente ciò che hai descritto...ma sai com'è...quando uno non sa come fare si affida a ciò che è già scritto...anche se poi carica un sacco di cose inutili...tra l'altro mi è molto comodo un codice più semplice...

quindi per quanto riguarda quello che mi hai detto:
"Per i comandi, devi fare quel che ti serve.
Vuoi fare la lettura di un sensore?
Byte 0x00 -> mettiamo: lettura analogica
A seguire il byte del sensore/porta da leggere."

ad esempio per la lettura devo dare(dal master):
byte di start: 0x00
byte di indirizzo: 0x30 0x31 (01)
byte di comando:
byte dati comando:
byte di stop: 0x01
byte di checksum: 0xEE

continuo a non capire i byte del comando e quello dei dati...cioè il byte del comando specifica il tipo di comando? digitalWrite, analogRead ecc...oppure specifica solo che si sta eseguendo un operazione e quale che viene poi elaborata da chi riceve il comando? oppure il byte di comando e quello dei dati formano un unica cosa ad esempio D04 (0x44 0x30 0x34) e poi bisogna specificare sullo slave?

if ((function=='D') && (function_code==0)){
if (data_received==4){
digitalWrite(1,HIGH);

peppe91:
ad esempio per la lettura devo dare(dal master):
byte di start: 0x00
byte di indirizzo: 0x30 0x31 (01)
byte di comando:
byte dati comando:
byte di stop: 0x01
byte di checksum: 0xEE

Per l'indirizzo ti basta 1 byte. L'indirizzo è un semplice codice numerico che identifica lo slave.
Nel codice slave avrai nella dichiarazione delle variabili globali un qualcosa tipo:

const byte DEVICEID = 0x00;

Ecco, questo è l'indirizzo dello slave su cui caricherai quello sketch.
Ovviamente per un secondo dispositivo, il DEVICEID diventa 0x01 ecc...
Se non devi interfacciare un intero condominio, 256 indirizzi ti bastano ed avanzano :wink:

continuo a non capire i byte del comando e quello dei dati...cioè il byte del comando specifica il tipo di comando? digitalWrite, analogRead ecc...oppure specifica solo che si sta eseguendo un operazione e quale che viene poi elaborata da chi riceve il comando? oppure il byte di comando e quello dei dati formano un unica cosa ad esempio D04 (0x44 0x30 0x34) e poi bisogna specificare sullo slave?

if ((function=='D') && (function_code==0)){
if (data_received==4){
digitalWrite(1,HIGH);

Questo dipende da te.
I tuoi slave dovranno fare determinate operazioni.
Mettiamo che le operazioni siano 4.
Lettura di un pin analogico;
lettura di un pin digitale
scrittura su un pin digitale
scrittura su un pin pwm.

Nell'ordine, quindi, i comandi possono essere 0x00, 0x01, 0x02 e 0x03. Oppure 0x30, 0x31, 0x32 e 0x33.
I dati relativi al comando da eseguire sono appunto legati a ciò che devi fare.
Se vuoi dire allo slave "leggi sul pin analogico A0" spedirai qualcosa tipo 0x30 0x00
Il primo indica appunto la lettura sul pin analogico (secondo lo schemino e la codifica che ti ho mostrato). Il secondo il canale da leggere.
Quindi lo slave farà:

if (byte_comando == 0x30) { //ha letto il byte comando, ed ha visto che è uguale a 0x30
  temp = analogRead(byte_dato_comando);
}

Ecc...

Ciao, innanzitutto ancora grazie per il tempo che mi stai dedicando..
ciò che devono fare i miei slave è molto semplice:
analogRead. (0x30)
digitalWrite, (0x31)
digitalRead, (0x32)

quindi io associo le funzioni ai byte...e poi li richiamo non appena lo slave li riceve...mente con il byte_dato_comando specifico ciò che devo fare
ad esempio mettiamo caso che io debba accendere un led sul pin 5 devo scrivere:

if (byte_comando == 0x31) { 
  digitalWrite(byte_dato_comando, HIGH);
}

dove byte_dato_comando è 0x35

Ho l'impressione che sto facendo un pò di confusione...

No, è giusto.

leo72:
No, è giusto.

ok fantastico....grazie a te comincio a capirci qualcosa...quindi per quanto riguarda la ricezione dei comandi ci sono...adesso il problema reale è snellire al massimo il programma dello slave e quello del master togliendo quello che non serve...
per l'invio dei comandi utilizzo il sendMSG scivendo il codice in decimale o il Serial.print?
mettiamo caso che io premendo il pulsante sulla porta 4 del master voglio accendere il led sulla porta 5 dello slave devo scrivere:

if (digitalRead (4) == HIGH){  
      sendMSG(48,49,49,53); /* 48 = 0x00 byte di start; 49 = 0x31 specifico l'indirizzo dello slave 1; 49 = 0x31 indico che sto inviando il comando di digitalWrite; 53 = 0x35 specifico la porta del led */
    }

le comunicazioni seriali sono impossibili...scrivo qualsiasi cosa senza alcun problema...ma la seriale per me è arabo...non si trova nemmeno un codice sorgente scritto su cui prendere spunto...l'unico è questo ma è complicato... uff... comunque ti ringrazio nuovamente per l'aiuto...

Per leggere dal master devi fare l'equivalente che stai facendo sugli slave, né più né meno.
Controlli se è arrivato qualcosa, lo scarichi, e controlli se l'indirizzo è quello del master.
Nel caso estrai i dati.
Ovviamente qui non avrai dei comandi da eseguire ma dovrai gestire dei dati in risposta, per cui il protocollo prevederà una serie di codici per istruire il master.
Facciamo un esempio.
Byte di start
Byte di indirizzo
Byte identificativo slave
Byte identificazione dato
2 o più byte per i dati
Byte di stop
Checksum

Nel byte di identificazione dello slave, ci sarà lo slave che ha spedito i dati.
Questa era una cosa a cui non pensavo prima, ma ti serve per sapere se ciò che è arrivato è arrivato dallo slave giusto. Il master potrebbe aver fatto un'interrogazione ad un altro slave.
Sono sicuro che almeno in questa prima fase iniziale, tu non farai una cosa del genere perché altrimenti ti intreccerai come pochi :stuck_out_tongue_closed_eyes:
Quindi mettiamo quel dato per futuri usit.

Ora ci sarà il byte identificativo del dato che lo slave ha spedito. Per capire insomma cos'è.
Se tu hai chiesto la lettura analogica sul pin 5, ti aspetti una lettura analogica indietro, giusto?
Ecco appunto, farai una tabella come quella dei comandi per sapere cos'è arrivato:
0x00 lettura digitale
0x01 lettura analogica ecc...
Mettiamo che sia una lettura analogica, sono 3 byte. Perché?
Perché hai il numero di pin, se ti serve, e la lettura stessa, che deve stare in 2 byte perché la lettura analogica restituisce un valore a 10 bit, e non puoi salvarlo in un unico byte (8 bit) per cui ti serve un tipo int, che sono 2 byte (16 bit).

Ovviamente questo se vuoi fare le cose a modino. Altrimenti, se chiedi di leggerti il pin 5, e ricevi 1000, sai che quella è la lettura del pin A5 per cui puoi anche risparmiarti un byte :wink:

Ti consiglio di usare il Serial.write per spedire i dati, e scomporre i dati nei vari byte.
Se devi spedire un tipo int, passalo in trasmissione con lowByte(dato) e highByte(dato). Per ricomporlo basta fare una moltiplicazione per 256 della parte alta del byte.
datoletto = parteBassa + (parteAlta*256)

Su questa ultima parte, leggi il reference, ci sono spiegati i comandi lowByte e highByte, nonché la sezione Serial sempre del reference, così vedi come leggere e scrivere i dati. Cerca anche sul forum la spedizione dei dati composti da più di un byte, è un argomento trattato tante volte.

Perdonami ma non ho capito...allora per quanto riguarda il protocollo quando invio i dati dal master avrò il byte di indirizzo che ci serve a far capire allo slave che sta comunicando con lui....mentre quando lo ricevo dallo slave serve a far capire al master da quale sto ricevendo i dati...giusto? per il resto non sono riuscito a capire...ti chiedo un favore enorme...se potesti spiegarmi con una bozza di codice in modo che capisco meglio...perchè davvero ho una confusione in mente assurda...per carità...non mi confondo con ciò che dici...ma è una questione mia che non riesco...è come insegnare l'arabo ad un inglese...abbi pazienza...grazie ancora...

Ora è tardino, io sono cotto e sto per staccare. Domani, se non hai capito, se ne riparla.

Ciao a tutti.

Intervengo nella discussione per affrontare con voi un aspetto dell'architettura proposta che mi pare più problematico del protocollo di comunicazione. Naturalmente non voglio confondere le idee a peppe91 per cui se volete, possiamo aprire un thread a parte oppure rimandare la questione a quando peppe91 avrà dissipato i suoi dubbi. Per il momento mi limito ad esporre la cosa.

In una installazione reale, oltre al master ci potranno essere numerosi slaves (diciamo fino a circa 250). Il master è l'oggetto sul quale gira la logica di controllo, gli slaves fanno invece da sensori (pulsanti, termometri, igrometri, sensori di luminosità...) o da attuatori (relè, dimmer...) o magari da entrambe le cose insieme.

Il master è l'unico che può avviare una comunicazione verso uno slave; quest'ultimo si limita a rispodere quando interrogato. In questo modo si evitano collisioni sulla linea che sono la ragione per cui si ricorre all'architettura master/slave. Questo però vuol dire che il master deve ciclicamente interrogare gli slaves per conoscere il loro stato ed eventualmente per comandare una o più attuazioni in caso di necessità.

Per esempio, l'utente preme un pulsante sullo slave 1; il master comanda la chiusura di un relè sullo slave 2 e di un relè sullo slave 5 (accensione di due lampade diverse, per esempio).

Tutto questo però dovrebbe avvenire in tempi brevissimi per garantire una reattività del sistema accettabile. D'altro canto però se il master impiegasse anche solo 10ms per interrogare ogni slave (e mi sembrano davvero pochi dato che bisognerebbe comunque concedere un certo timeout allo slave per rispondere), l'interrogazione di tutti gli slaves richiederebbe 2,5 secondi (ovviamente nel caso peggiore in cui ci siano davvero 250 slaves sulla rete). Questo tempo mi pare inaccettabile per un utente che potrebbe vedere le luci accendersi con un ritardo considerevole.

Accenno ad una possibile soluzione: gli slaves hanno priorità diversa, cioè alcuni vengono interrogati più spesso di altri. Un esempio: lo slave con i pulsanti deve essere interrogato molto più spesso di uno slave che fa da termometro. Anche così però mi pare che i tempi siano troppo stretti.

Cosa ve ne pare? Mi sfugge qualcosa?

Ciao.
Vittorio.

vittorio68:
In una installazione reale, oltre al master ci potranno essere numerosi slaves (diciamo fino a circa 250).

Il limite massimo, teorico, di slave sulla RS85 è 255, però esistono importanti limiti hardware che portano tale valore a meno di 100 e comunque i protocolli raramente permettono più di 32 slave sulla stessa linea.
Ovvero, se ti servono molti slave devi usare più linee RS485 indipendenti.

questo non lo sapevo... è già un ottimo punto di partenza.

Tuttavia mi pare che il problema iniziale rimanga... anche se abbassiamo a 30 il limite massimo dobbiamo comunque considerare che 10ms erano soltanto un esempio fatto per mostrare come anche con tempi così ridotti, si raggiungono tempi totali considerevoli. Se immaginiamo un tempo più realistico, per esempio 50ms, torniamo nuovamente a 1,5 secondi per interrogare 30 slaves.

vittorio68:
Se immaginiamo un tempo più realistico, per esempio 50ms, torniamo nuovamente a 1,5 secondi per interrogare 30 slaves.

50 ms sono una eternità, il tempo medio per la transazione master/slave è 4-5 ms, il timeout mediamente si imposta tra 10-20 ms ed è una condizione di errore, non il caso standard, pertanto per interrogare 32 slave, con relative risposte, ci metto al massimo 200 ms, ovvero almeno 5 cicli al secondo.
Ti rammento che la velocità di comunicazione sulla RS485 arriva fino a 10 Mbps con linea non più lunga di 10 metri, 1Mbps con linea tra 10 metri e 100 metri, fino a 115200 bps con linea di 1.2km, ovviamente utilizzando il giusto cavo e non il primo cavaccio che troviamo nel cassetto :slight_smile:

E poi non è che il master debba terminare tutto il ciclo di interrogazioni quando deve spedire un comando.
Il controllo degli slave sarà fatto nei momenti di idle del sistema. Quando l'utente comanda un'azione, quell'azione deve sospendere il ciclo di idle (test degli slave ed altro) per essere eseguita immediatamente.
IMHO

leo72:
Il controllo degli slave sarà fatto nei momenti di idle del sistema. Quando l'utente comanda un'azione, quell'azione deve sospendere il ciclo di idle (test degli slave ed altro) per essere eseguita immediatamente.

Diciamo che nel mondo reale solitamente c'è un ciclo continuo di interrogazione degli slave, non necessariamente tutti all'interno dello stesso ciclo, all'interno del quale vengono anche prese tutte le decisioni del caso ed eseguite le operazioni previste.
Quanto dura il ciclo dipende dalle esigenze del processo, in ambito industriale si spazia tranquillamente da qualche secondo a pochi ms, tutto dipende da cosa stai controllando e da quanto lavoro viene svolto localmente dagli slave.
Va da se che in caso di applicazioni time critical si arriva ad utilizzare un bus dedicato ad altissima velocità, 5-10 Mbps, per pochi slave e uno secondario per tutti gli altri in modo da ottimizzare la risposta del sistema.

Ma nel caso appunto di un sistema home-made non penso che bloccare il ciclo di interrogazioni per 1/2 secondi porti a chissà quali catastrofi :wink:

leo72:
Ma nel caso appunto di un sistema home-made non penso che bloccare il ciclo di interrogazioni per 1/2 secondi porti a chissà quali catastrofi :wink:

Io sinceramente me la ero pensata diversamente...appunto per evitare il tempo di latenza tra comando e reazione, il microcontroller integrato nello slave esegue direttamente il comando in questione inviando solo un feedback al master per informarlo dell'azione seguita, il master praticamente mi deve gestire soltanto l'invio di comandi da remoto ethernet shield e company...e interviene nel caso dell'attivazione del risparmio energetico disattivando le luci e mettendo in condizione gli slave di rispondere allo stato dei sensori PIR per l'accensione delle luci ecc...in pratica gli slave...oltre ad essere slave e quindi gestiti dal master hanno al loro interno un codice che eseguono per conto loro...