4 Arduino Master Slave in RS485

vittorio68:
Avevo pensato a due byte per ridurre la possibilità che la combinazione scelta come start potesse presentarsi all'interno del pacchetto. In effetti, lo start del pacchetto, ci serve per avviare l'automa a stati che deve interpretare il pacchetto. Se il valore di start si presenta all'interno del pacchetto, l'automa comincia ad interpretare i bytes in arrivo attribuendo loro un significato diverso. Pertanto, non capirà che un certo bytes ricevuto è il cheksum del pacchetto. In effetti però mi hai fatto riflettere sul fatto che non scarterà il pacchetto per checksum errato ma piuttosto per timeout.

Non so se sono stato chiaro... ma ti chiedo comunque: che ne pensi?

Se lo slave sta ricevendo un pacchetto, dati non deve interpretare i byte contenuti in esso. Lui deve solo riconoscere:

  1. il byte di start, ed iniziare a leggere;
  2. il byte contenente il numero di byte ulteriori che deve ricevere, e questo valore non può essere confuso con altri perché è in una posizione ben precisa, e poi i byte di stop e di checksum, che arrivano in determinate posizioni (dipendenti da quanti byte sono stati trasmessi).

Qui mi chiedo: è possibile che uno slave risponda tardi?

E' possibile, pensa se ad esempio va in crash per un errore logico del programma oppure perché gli è stato chiesto un accesso ad un sensore, immaginiamo, e questo sta impiegando più tempo perché anch'esso è difettoso.
Quindi è una catena da verifica con timeout in ogni operazione critica, come hai capito anche tu.

pietro78:
Peppe91...dove sei...

eccomi...io ci sono...purtroppo ho avuto un po di impegni e ho trascurato questo topic...non mi ero nemmeno accorto che lo avevate "riesumato"...io ancora non ho sviluppato nulla...al momento mi sto concentrando sul webserver PHP da connettere via rs232 ad arduino master....purtroppo sulla seriale sto a zero...

eccomi...io ci sono...purtroppo ho avuto un po di impegni e ho trascurato questo topic...non mi ero nemmeno accorto che lo avevate "riesumato"...io ancora non ho sviluppato nulla...

Per Peppe91 ...Purtroppo anche io ho poco tempo da dedicare a questo progetto...
Per Leo72...si può spostare il post magari nella sezione Megatopic o altra sezione tipo "Progetti in sviluppo" e il progetto sarebbe chiaramente "Realizzazione Protocollo rs485" ??
Caro Leo lo scopo ripeto dovrebbe essere quello di attirare l'attenzione di più utenti perchè il titolo di questo topic
( "4 Arduino Master Slave in RS485" )è generico e magari viene ignorato da molti...

Domanda tecnica sull'argomento:ma per mandare i byte di comando tra Arduino Master e Arduini Slave oltre al metodo "serial.Write byte(x,x,x,x,x,x)" esiste un'altro modo più semplice e più comprensibile oppure devo soffrire a inventare i numeri a caso ??

La mia domanda sicuramente a molti sembrerà cretina ma senza sketch di esempio di riferimento e di prova chiaramente brancolo nel buio...buon lavoro a tutti e guarda caso oggi mi troverò a lavorare con dei componenti elettronici che colloquiano con una seriale Rs485...sistemi d'allarme...

pietro78:
Per Peppe91 ...Purtroppo anche io ho poco tempo da dedicare a questo progetto...
Per Leo72...si può spostare il post magari nella sezione Megatopic o altra sezione tipo "Progetti in sviluppo" e il progetto sarebbe chiaramente "Realizzazione Protocollo rs485" ??
Caro Leo lo scopo ripeto dovrebbe essere quello di attirare l'attenzione di più utenti perchè il titolo di questo topic
( "4 Arduino Master Slave in RS485" )è generico e magari viene ignorato da molti...

In Megatopic ci vanno i progetti completi. Qui siamo per ora al livello di discussione per la stesura di un protocollo, non c'è nulla di pronto.

Domanda tecnica sull'argomento:ma per mandare i byte di comando tra Arduino Master e Arduini Slave oltre al metodo "serial.Write byte(x,x,x,x,x,x)" esiste un'altro modo più semplice e più comprensibile oppure devo soffrire a inventare i numeri a caso ??

La mia domanda sicuramente a molti sembrerà cretina ma senza sketch di esempio di riferimento e di prova chiaramente brancolo nel buio...buon lavoro a tutti e guarda caso oggi mi troverò a lavorare con dei componenti elettronici che colloquiano con una seriale Rs485...sistemi d'allarme...

Prendi il codice del ModBus e studialo, non c'è nulla di astruso dietro. Si tratta di spedire dei byte e dargli un significato.
Quando tu leggi un sensore, i dati che ricevi hanno un significato per te, giusto? Eppure, sei tu a darglielo, di per sé quelli sono solo numeri. :wink:

Se lo slave sta ricevendo un pacchetto, dati non deve interpretare i byte contenuti in esso. Lui deve solo riconoscere:

  1. il byte di start, ed iniziare a leggere;

E' qui che nasce il mio problema, ma senz'altro sono stato poco chiaro. Provo a spiegarmi con un esempio. Il master invia il seguente pacchetto secondo il protocollo che proponevo: 255, 0, 3, 5, 4, 2, 255, 6, 2, X

255 = Start
0 = IDMittente, il master ha ID = 0
3 = IDDestinatario, il master vuole interrogare lo slave con ID = 3
5 = IDComando, il master sta inviando il comando ID=5
4 = LunghezzaPacchetto, di seguito ci sono 4 bytes che, per esempio, rappresentano un long
2
255
6
2
X = Checksum, calcolato per esempio con uno XOR dei 9 bytes (escluso il checksum) che compongono il pacchetto

Ora supponiamo che uno slave si avvii (per esempio venga alimentato...) durante la trasmissione del pacchetto e quindi riceva il seguente frammento: 5, 4, 2, 255, 6, 2, X (ha perso i primi 3 bytes...)

Ignorerà i bytes 5, 4, 2 perchè aspetta un 255 di start, quindi interpreterà bytes seguenti come:

255 = Start
6 = IDMittente
2 = IDDestinatario
X = IDComando

e qui continuerà ad attendere in attesa dei rimanenti bytes del pacchetto che non arriveranno mai. Tuttavia interverrà il timeout ed i bytes verranno scartati con codice di errore = TIMEOUT.

In effetti mi pare che funzioni, volevo soltanto confrontarmi anche con voi sulla correttezza della soluzione, nel caso mi sfugga qualcosa.

  1. il byte contenente il numero di byte ulteriori che deve ricevere, e questo valore non può essere confuso con altri perché è in una posizione ben precisa, e poi i byte di stop e di checksum, che arrivano in determinate posizioni (dipendenti da quanti byte sono stati trasmessi).

Non avevo previsto un byte di stop... ti sembra necessario?

Quindi è una catena da verifica con timeout in ogni operazione critica, come hai capito anche tu.

Qui non so se basta il timeout, come dicevo nel post precedente, in caso di ritardo nella risposta dello slave, potrebbe verificarsi una collisione perchè il master non ricevendo risposta entro il timeout, pensa che lo slave sia down ed inizia una nuova trasmissione, magari verso un altro slave. Contemporaneamente lo slave precedente trasmette la sua risposta generando la collisione che rileveremo attraverso il controllo del checksum. Dobbiamo però capire come fanno tutti gli oggetti sul bus (il master e gli n slaves) a ritornare in una condizione stabile.

  1. Secondo me, se lo slave si aggancia alla trasmissione quando questa è in corso la deve semplicemente ignorare non appena si accorge che il primo byte che riceve non è quello di start. Sempre secondo me, deve scaricare tutti i byte che riceve perché altrimenti non può capire se ad esempio un byte $FF ricevuto a metà di un messaggio fa parte del pacchetto di dati oppure se è il byte di start. Sarà magari cura del master, rispedire il pacchetto se non riceve una risposta dallo slave.

  2. byte di stop. Avendo una trasmissione variabile in termini di lunghezza del pacchetto, quantità di dati indicata da uno specifico byte, forse non serve metterlo.

  3. la collisione non c'è se fai come ho detto al punto 1)

leo72:
Prendi il codice del ModBus e studialo, non c'è nulla di astruso dietro. Si tratta di spedire dei byte e dargli un significato.
Quando tu leggi un sensore, i dati che ricevi hanno un significato per te, giusto? Eppure, sei tu a darglielo, di per sé quelli sono solo numeri. :wink:

Per quanto riguarda il Modbus ci stavo lavorando io....ma sinceramente non capisco come inviare e ricevere i dati....cioè...come fargli inviare un determinato dato o una lettura di un sensore e agire di conseguenza per me è un mistero...lo reputo troppo complicato...forse anche per l'esigenza di "adattare" il protocollo a qualcosa di commerciale...non so...

Guarda, ho spulciato nel Playground:
http://playground.arduino.cc//Main/InterfacingWithHardware#Communication

Ci sono già diversi progetti sul ModBus.

Guarda, ho spulciato nel Playground:
Arduino Playground - HomePage

Quegli esempi sul playground purtoppo non sono ben documentati e non hanno influenza didattica su tante persone che come me si avvicinano per la prima volta al mondo del Modbus o dei protocolli su un arduino...

Quegli esempi almeno io come molte altre persone già li avevo visti e stravisti senza nessun risultato...
Quando parliamo di esempi tipo "buttons,analog input...etc..etc" allora il singolo maker ci mette del suo per imparare dagli esempi ma con i link citati nel playground aihmè...sfido chiunque al di fuori di programmatori e ingegneri a capire la struttura di un protocollo come il modbus che è sì uno standard ma tra la teoria e la pratica c'è di mezzo proprio la mancanza di esempi concreti...

Scusate per la minipolemica...buona giornata a tutti...e intanto continuo a sperimentare come un piccolo Leonardo con il protocollo 485...

Non capisco il problema nello spedire 1 byte e nell'interpretarlo come un comando. Speditelo con ModBus o con qualsiasi altro protocollo, ma alla fine si tratta di ricevere qualcosa e fare un'azione.

Quando spedite la lettera "A" dal computer e fate accendere un led sull'Arduino non avete spedito 1 byte ed interpretato questo byte come un "comando"?

scusami leo72 ma ho ancora qualche dubbio sulla robustezza dell'implementazione.

Secondo me, se lo slave si aggancia alla trasmissione quando questa è in corso la deve semplicemente ignorare non appena si accorge che il primo byte che riceve non è quello di start. Sempre secondo me, deve scaricare tutti i byte che riceve perché altrimenti non può capire se ad esempio un byte $FF ricevuto a metà di un messaggio fa parte del pacchetto di dati oppure se è il byte di start. Sarà magari cura del master, rispedire il pacchetto se non riceve una risposta dallo slave.

Nell'esempio che facevo prima lo slave si agganciava perdendo i primi tre bytes e riceveva 5, 4, 2, 255, 6, 2, X. Cosa succede se si aggancia perdendo i primi 6 bytes ricevendo quindi 255, 6, 2, X? In questo caso il primo byte che riceve è proprio un 255 (indistinguibile da uno start...) che però non è uno start.

Ma come dicevo, questo problema mi pare risolto dal timeout...

Veniamo invece al tuo terzo punto sul quale ho il dubbio maggiore.

  1. la collisione non c'è se fai come ho detto al punto 1)

Il punto 1 si occupa di come far individuare allo slave l'inizio del pacchetto. La collisione invece può avvenire nel seguente scenario.

  • Il master invia una richiesta ad uno slave ed attende una risposta entro 10ms (è solo un esempio... se è un tempo troppo lungo lo rivediamo successivamente)
  • Lo slave è occupato ed impiega un tempo maggiore per rispondere, per problemi del tutto estranei alla trasmissione dati (nel tuo esempio precedente c'e' un sensore difettoso).
  • Il master, trascorso il timeout, avvia una nuova trasmissione. Magari riprova ad interrogare lo stesso slave, magari passa allo slave successivo, non importa, la questione è che impegna nuovamente il bus
  • Lo slave, che ha ricevuto correttamente la prima richiesta (ma l'ha elaborata in ritardo), risponde mentre il bus è occupato dalla nuova trasmissione del master generando quindi la collisione.

Mi pare che questo scenario non abbia nulla a che vedere con la gestione dello start. Mi sbaglio?
Mi pare tuttavia che vada gestita altrimenti si corre il rischio di compromettere il funzionamento dell'intero bus dato che il master e gli slaves potrebbero entrare un uno stato dal quale non riescono più ad uscire bloccandosi definitivamente.

vittorio68:
Nell'esempio che facevo prima lo slave si agganciava perdendo i primi tre bytes e riceveva 5, 4, 2, 255, 6, 2, X. Cosa succede se si aggancia perdendo i primi 6 bytes ricevendo quindi 255, 6, 2, X? In questo caso il primo byte che riceve è proprio un 255 (indistinguibile da uno start...) che però non è uno start.

Ma come dicevo, questo problema mi pare risolto dal timeout...

Non tornerebbero tante cose. Ad esempio, leggerebbe un byte che indica il numero di byte da ricevere nel posto sbagliato, stando quindi in ricezione per un quantitativo di dati che non torna. Il checksum o arriva dal byte sbagliato oppure proprio non arriva. Se i dati in ricezione terminano, il timeout taglia la comunicazione.

Veniamo invece al tuo terzo punto sul quale ho il dubbio maggiore.

Hai visto come il protocollo implementato nel ModBus gestisce (se lo fa) questo evento?
Io no, chiedo per questo.

ho trovato questo documento che mi pare molto interessante.

Al secondo punto dell'elenco che c'e' all'inizio di pag. 10, si dice che il Response Time (ossia il tempo che si concede allo slave per rispondere dopo aver elaborato la richiesta), sia tipicamente compreso tra 1 e diversi secondi. E questo sembrerebbe un tempo enorme... ma in realtà non lo è!

Infatti, in sostanza mi pare di capire che la logica sia questa: il timeout è altissimo tanto da poter ragionevolmente supporre che se la risposta non arriva entro questo tempo non arriverà più (slave bloccato, spento, scollegato...).

Questo non rallenta le prestazioni del sistema perchè lo slave risponderà tipicamente in un tempo molto più bassso (pochi ms) mantenendo buone le prestazioni. Quando si verifica il malfunzionamento di uno slave (e si spera che sia un evento molto raro...) il bus rimane "bloccato" per pochi secondi che sono un tempo, tutto sommato, molto basso per identificare ed isolare (il master esclude lo slave dalla lista di quelli da interrogare) un problema.

Non capisco il problema nello spedire 1 byte e nell'interpretarlo come un comando. Speditelo con ModBus o con qualsiasi altro protocollo, ma alla fine si tratta di ricevere qualcosa e fare un'azione.

Quando spedite la lettera "A" dal computer e fate accendere un led sull'Arduino non avete spedito 1 byte ed interpretato questo byte come un "comando"?

Caro Leo io quello che non riesco a capire è proprio questo ,se io mi invento un miniprotocollino dove decido che :

byte "a" significa accendi led2 byte "d" significa spegni led2
byte "b" significa accendi led3 byte "e" significa spegni led3
byte "c" significa accendi led4 byte "f" significa spegni led4

...a questo punto come fare il passo da gigante per decidere di mandare i byte con il formato

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)

...o meglio io vorrei un piccolo esempio dove un master con Address "0x00" invia un semplicissimo comando ad uno slave "0x01" e gli dice di accendere un led sul pin 13 per un secondo e lo spegne dopo un secondo...

MASTER "0x00" invia Byte "accendi led pin13" a SLAVE"0x01" come dovrebbe essere questo comando ??
Scusate ma proprio non riesco a capirlo e la volontà non mi manca...
Qualcuno che per favore ci scriva un esempio per il comando sopra citato...

pietro78:

Non capisco il problema nello spedire 1 byte e nell'interpretarlo come un comando. Speditelo con ModBus o con qualsiasi altro protocollo, ma alla fine si tratta di ricevere qualcosa e fare un'azione.

Quando spedite la lettera "A" dal computer e fate accendere un led sull'Arduino non avete spedito 1 byte ed interpretato questo byte come un "comando"?

Caro Leo io quello che non riesco a capire è proprio questo ,se io mi invento un miniprotocollino dove decido che :

byte "a" significa accendi led2 byte "d" significa spegni led2
byte "b" significa accendi led3 byte "e" significa spegni led3
byte "c" significa accendi led4 byte "f" significa spegni led4

...a questo punto come fare il passo da gigante per decidere di mandare i byte con il formato

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)

...o meglio io vorrei un piccolo esempio dove un master con Address "0x00" invia un semplicissimo comando ad uno slave "0x01" e gli dice di accendere un led sul pin 13 per un secondo e lo spegne dopo un secondo...

MASTER "0x00" invia Byte "accendi led pin13" a SLAVE"0x01" come dovrebbe essere questo comando ??
Scusate ma proprio non riesco a capirlo e la volontà non mi manca...
Qualcuno che per favore ci scriva un esempio per il comando sopra citato...

Ragazzi, io sono nelle stesse condizioni di pietro....sono diplomato geometra...non sono ingegnere elettronico...quindi quello che bene o male so...l'ho imparato dagli esempi commentati...(ottimo strumento didattico a mio avviso), questo protocollo per me è qualcosa fuori dagli schemi....io penso che se avessimo due sketch(uno per il master e uno per lo slave) con gli integer dichiarati, il void setup e nel void loop anche solo un comando che invia una serie di byte alla pressione di un pulsante e lo slave che esegue e restituisce risposta penso che potremmo metterci in moto anche noi...non voglio essere polemico ma inviare una "a" con il serial.write e leggerla con il serial.read è tutto un'altro discorso dall'inviare una serie di byte, integrare checksum e quant'altro e gestire il timeout....può apparentemente sembrare facile per chi sa farlo....personalmente non ne sono capace al momento...magari fra un mesetto vi dirò...ma si...che ci voleva....ma al momento no...purtroppo...

io penso che se avessimo due sketch(uno per il master e uno per lo slave) con gli integer dichiarati, il void setup e nel void loop anche solo un comando che invia una serie di byte alla pressione di un pulsante e lo slave che esegue e restituisce risposta penso che potremmo metterci in moto anche noi...non voglio essere polemico ma inviare una "a" con il serial.write e leggerla con il serial.read è tutto un'altro discorso dall'inviare una serie di byte, integrare checksum e quant'altro e gestire il timeout....

Quoto in pieno Peppe91...meno male che non sono solo...

Prima di tutto mi scuso per una cosa, se avete frainteso il mio atteggiamento all'interno di questo thread.
Dovevo specificarlo prima, ma no l'ho fatto. Partecipo come "dispensatore di idee", non era nei miei interessi prendere parte attivamente alla cosa perché non è di mio primario interesse.
Finora ho dato l'idea di quello che parla parla, ma non fa :*
Mi spiace, ma era solo per darvi delle idee, non un aiuto concreto.

Se siete rimasti un po' impantanati nella cosa, cerco di aiutarvi finché posso, finché cioè la cosa non diventa (secondo il mio punto di vista) troppo impegnativa come partecipazione, perché non ho il tempo materiale per partecipare attivamente.

pietro78:
...o meglio io vorrei un piccolo esempio dove un master con Address "0x00" invia un semplicissimo comando ad uno slave "0x01" e gli dice di accendere un led sul pin 13 per un secondo e lo spegne dopo un secondo...

MASTER "0x00" invia Byte "accendi led pin13" a SLAVE"0x01" come dovrebbe essere questo comando ??
Scusate ma proprio non riesco a capirlo e la volontà non mi manca...
Qualcuno che per favore ci scriva un esempio per il comando sopra citato...

Un esempio di spedizione di un dato con il protocollo che avevi proposto qualche post fa:

byte Start1;
byte IDMittente;
byte IDDestinatario;
byte IDComando;
byte LunghezzaPacchetto;
... qui un numero di bytes pari a LunghezzaPacchetto
byte Checksum;

potrebbe essere questo, assumendo l'indirizzo dello slave ipoteticamente $0001:

Serial.write(0xFF); //byte di start
Serial.write(0x00); //ID master ($00)
Serial.write(0x01); //ID slave ($01)
Serial.write(0x01); //comando: accendi led
Serial.write(0x01); //lunghezza pacchetto dati: 1 byte
Serial.write(0x0A); //pacchetto dati (qui, $0A = 13, indica il led 13)
Serial.write(0xxx); //byte di checksum (dipende dall'algoritmo usato)

Vediamo come potrebbe essere un codice di ricezione di uno slave (usa serialEvent):

void loop() {
 ... il resto del codice
}

void serialEvent() {
  byte myCommand = 0;
  boolean dataIsValid = false;
  if (Serial.read() == 0xFF) { //byte di start giusto
    if (Serial.read() == 0x00) { //accetto comandi solo dal master
      if (Serial.read() == 0x01) { //è un comando destinato a me?
        myCommand = Serial.read(); //leggo il comando
        byte lenComm = Serial.read(); //quanto è lungo il pacchetto dati?
        byte tempPack[lenComm];
        byte i = 0;
        unsigned long oldMillis = millis();
        while (lenComm && Serial.available && (millis() - oldMillis < 500) {
          tempPack[i] = Serial.read();
          i++;
          lenComm--;
        }
        if (lenComm != 0) { //se lenComm non è a 0, vuol dire che è uscito per time
          break; 
        }
        byte tempCksm = Serial.read();
        if (calcolaChecksum() != tempCksm) { //non torna il checksum
          break;
        }
        switch (myCommand) {
          case 0x00:
            .....
            break;
          case 0x01: //accendo un pin
            digitalWrite(tempPack[0], HIGH); //so che in tempPack[0] c'è il numero del pin  
            break;
        }
        dataIsValid = true;
      }
    }
  } 
  if (!dataIsValid) { //c'è stato un errore
    //svuoto il buffer
    while (Serial.available()) {
      byte temp = Serial.read();
    }
  }
}

Il codice NON è testato, il codice fa SCHIFO, il codice NON è ottimizzato, il codice è solo un ESEMPIO.
Non prendetelo per oro colato :sweat_smile: :sweat_smile:

Io forse posso contribuire un po' di più perchè prossimamente dovrò sviluppare concretamente i concetti discussi sinora (e forse si era notato dal tono del mio confronto con leo72).

Purtroppo però non posso promettere tempi certi perchè dipenderà dall'evoluzione del progetto (probabilmente prima dovrò sviluppare un po' di hardware...).

vittorio68:
Io forse posso contribuire un po' di più perchè prossimamente dovrò sviluppare concretamente i concetti discussi sinora (e forse si era notato dal tono del mio confronto con leo72).

Quale tono? XD