4 Arduino Master Slave in RS485

leo72:
Dovreste creare una serie di livelli di sicurezza. Se l'operazione è banale, come ad esempio accendere un faretto quando inizia a fare buio, potete usare un livello basso, spedendo al master la lettura del sensore ed attendendo che il master spedisca il comando.
Ma se il livello di sicurezza è alto, lo slave è abilitato ad invertire la sequenza: operare in proprio attivando una certa cosa ed informando il master del problema.

Cosa consigli???

Una semplice tabella con i livelli di sicurezza.
Ad ogni operazione che deve eseguire lo slave assegnate un livello. Se il livello è base, fate la solita trafila: spedire al master il dato, attendere la decisione del master. Questo va bene, ad esempio, per la lettura di una fotoresistenza.
Se il livello è superiore (allarme fuga gas), potete decidere se:

  1. inviare il dato al master e mettere un timeout alla sua risposta, per evitare che una caduta dello stesso o un problema sulla linea impedisca allo slave di prendere una decisione. Oppure
  2. attivare una contromisura (es.: sirena) in maniera indipendente ed informare il master dell'accaduto.

Il master potrà avvisare l'utente con un sms/email/led, a seconda di come è stato programmato.

Visti i pochi ritagli di tempo da parte mia per iniziare a stilare questa famosa tabella di protocollo bisogna pur cominciare da qualche base ...

Ora vista la mia scarsa propensione a programmare un protocollo mi affido a persone più competenti e più afferrate nel campo e soprattutto mi affido all'unione delle menti geniali allo scopo di iniziare a lavorare su qualcosa....
Vi prego buttate giù un qualcosa che sia anche solo un'esempio basilare per iniziare a far camminare i neuroni...Grazie e comunque senza obblighi... :slight_smile: :slight_smile: :stuck_out_tongue: :stuck_out_tongue: :wink: :wink:

Ragazzi, vi prego di scusare la mia assenza a causa di svariati impegni lavorativi....dunque creiamo una base di partenza...e partiamo dal master....mettendo per ipotesi che noi vogliamo creare un protocollo semplice utilizziamo 6 byte... e quindi

byte data [6]
// il primo byte è il classico NULL, inviato per primo come meno significativo 0x00
byte Address; // il secondo byte è quello di indirizzo del dispositivo che invia
byte SlaveAddress; // il terzo byte è quello di indirizzo del dispositivo che riceve
byte Data; // il quarto byte richiama la funzione da eseguire...nel caso di invio di lettura digitale entrambi i byte di data riportanto il valore di 10 bit
byte Data2; // il quinto byte specifica cosa eseguire
byte Checksum; // l'ultimo byte invia un feedback al master indicando che l'operazione è stato eseguita

correggetemi se scrivo castronerie...(Leo72 aiutami tu che vedo che sei il più esperto qui...)
poi....

#define Address  xx; //impostiamo l'indirizzo del dispositivo 
const int PinRS485Enable = 02; //impostiamo l'abilitazione del trasferimento dati sul pin 2 ( i pin 0 e 1 sono utilizzati per la comunciazione seriale)
const int ErrorComLed = 03; //reputo molto utile l'utilizzo di un led che ci segnali eventuali errori di comunicazione

poi sul void setup() scriveremo:

pinMode(PinRS485Enable, OUTPUT); // settiamo il pin di controllo della trasmissione come output
pinMode(ErrorComLed, OUTPUT); // settiamo il pin del led di errore come output

Come inizio non è male...anzi almeno sappiamo da dove partire ma adesso viene il bello...

Come costruire i byte da te citati...o meglio come costruire la tabellina di riferimento per le funzioni...aspettiamo...

byte data [6]
// il primo byte è il classico NULL, inviato per primo come meno significativo 0x00
byte Address; // il secondo byte è quello di indirizzo del dispositivo che invia
byte SlaveAddress; // il terzo byte è quello di indirizzo del dispositivo che riceve
byte Data; // il quarto byte richiama la funzione da eseguire...nel caso di invio di lettura digitale entrambi i byte di data riportanto il valore di 10 bit
byte Data2; // il quinto byte specifica cosa eseguire
byte Checksum; // l'ultimo byte invia un feedback al master indicando che l'operazione è stato eseguita

Ok dai, quindi siccome vogliamo fare un SimpleProtocol propongo di fare questa tabellina in un file.h da includere nel sorgente, il problema reale che ho riscontrato nei sorgenti che si trovano in giro nel web è appunto il non capire a che serve quella determinata stringa che magari sta solamente dichiarando cosa deve fare quel comando....quindi propongo di semplificare al massimo e lasciare all'utente che va a modificare il codice sorgente soltanto decidere cosa eseguire su quel pin ecc....che ne dici?...un sorta di brutta copia del Wire...

dimenticavo....ovviamente nel void setup()

Serial.begin(9600); //inizializzo la porta seriale e la imposto a 9600 baud...

Concordo pienamente nel compilare un file.h per i comandi a livello "open" per lasciare la possibilità a chiunque di personalizzare anche il sorgente...
Per la porta seriale pensavo chiaramente di poter usare almeno per il Master la classe SoftwareSerial per il bus485 e quindi usa re una porta virtuale e lasciare chiaramente la Com sui pin 0 e 1 per la comunicazione con PC e monitoraggio delle serialPrint ... che pensi si può fare ??

E intanto andiamo avanti in attesa che si introduca sempre più gente...+ne siamo e meglio è ....

Ottima idea quella della SoftwareSerial... quindi procediamo in questo senso...abbiamo bisogno di altra gente che si aggreghi però...qui di lavoro ce n'è....ma il mio tempo è molto limitato purtroppo...quindi quando posso butto giù qualche idea....fattevi avanti anche voi...con pezzi di codice...a pezzo a pezzo buttiamo giù qualcosa di concreto...

Come promesso ho scritto un paio di sketch, uno per il master e uno per lo slave per fare qualche prova. Si tratta di poche righe di codice tanto per cominciare a partire. In particolare, a mio avviso bisogna rivedere il byte di start (attualmente un solo byte sempre 255) per fare in modo che la sequenza di start non possa presentarsi all'interno del pacchetto.

Il codice simula l'interrogazione di 16 slaves (numerati da 1 a 16 perchè 0 è il master...). Quando interroga uno slave, attende la risposta per 5 secondi, se non la riceve invia nuovamente la richiesta allo stesso slave. Questo è utile solo per questo semplice sketch, il protocollo finale dovrà funzionare in maniera diversa (tipo isolare lo slave che non risponde...).

Il protocollo è simile a quello già discusso. Probabilmente un solo byte di dati è poco ma, ancora una volta, è solo per provare.

La funzione RS485Write disabilita la ricezione della seriale e attiva il pin che abilita la trasmissione. Attende quindi la fine della trasmissione per ripristinare il funzionamento in ricezione.

Ho usato un Arduino Mega sfruttando la Serial2. Lo slave invece utilizza Serial1. Non c'e' alcun motivo per fare questo. Nel mio caso dipende semplicemente dal fatto che ho usato due shields autocostruiti in tempi diversi sui quali avevo utilizzato due seriali diverse. Basta modificare lo sketch facendo attenzione a modificare anche i flag utilizzati nella RS485Write.

Ecco il codice del master. Scrivo quello dello slave in un post successivo.

// Protocollo
// 0 - start = 255
// 1 - sender address
// 2 - destination address
// 3 - command
// 4 - data
// 5 - checksum

int pinEnableWrite = 5;
byte idxSlave;
byte idxRead;
byte command = 1;
bool nextRequest;
unsigned long lastRequest;
unsigned long last1;

void setup() 
{
	Serial.begin(9600);
	Serial2.begin(115200);

	pinMode(pinEnableWrite, OUTPUT);     
	digitalWrite(pinEnableWrite, LOW);
	
	idxSlave = 1;
	idxRead = 0;
	
	nextRequest = true;
}

void loop() 
{
	if (nextRequest)
	{
                            if (idxSlave == 1) last1 = millis();
                
		//Serial.print("Richiesta slave: ");
		//Serial.println(idxSlave, DEC);
		
		byte buffer[6];
		buffer[0] = 255;
		buffer[1] = 0;
		buffer[2] = idxSlave;
		buffer[3] = command;
		buffer[4] = 0;
		buffer[5] = buffer[1] ^ buffer[2] ^ buffer[3] ^ buffer[4];
		
		RS485Write(buffer, 6);
		
		nextRequest = false;
		lastRequest = millis();
	}
	else
	{
		unsigned long Now = millis();
		if (Now - lastRequest > 5000)
		{
			nextRequest = true;
		}
		else
		{
			if (Serial2.available())
			{
				byte B = Serial2.read();
				
				//if (idxRead == 0) Serial.print("Risposta: ");
				
				//Serial.print(B, DEC);
				//Serial.print(" ");
				
				idxRead++;
				if (idxRead == 6) 
				{
					//Serial.println(" ");
					
					idxRead = 0;
					
					idxSlave++;
					if (idxSlave == 17) 
					{
						Now = millis() - last1;
						Serial.print("Durata ciclo = ");
						Serial.println(Now, DEC);
						idxSlave = 1;
					}

					nextRequest = true;
				}
			}
		}
	}
	
	delay(1);
}

void RS485Write(byte* packet, byte len)
{
	UCSR2B &= ~(1<<RXEN0);  //disable RX   
	UCSR2A = UCSR2A |(1 << TXC0);  
	Serial2.flush();

	digitalWrite(pinEnableWrite, HIGH);
	Serial2.write(packet, len);

	while (!(UCSR2A & (1 << TXC0))); // attendi fine trasm.

	digitalWrite(pinEnableWrite, LOW);
	UCSR2B |= (1<<RXEN0);   //enable RX
}

ecco invece il codice dello slave.

Non avendo 16 arduino, semplicemente il codice risponde sempre a prescindere dal destination address. Tanto per customizzare la risposta, il byte di dati è uguale all'ID slave moltiplicato per due.

Il byte di checksum viene calcolato (sia nel master che nello slave) ma non viene controllato.

Il codice però attende un byte 255 per memorizzare i byte ricevuti.

// Protocollo
// 0 - sender address
// 1 - destination address
// 2 - command
// 3 - data
// 4 - checksum

int pinEnableWrite = 5;
byte command = 2;
byte idxSlave;
byte idxRead;
byte buffer[6];

void setup() 
{
	Serial.begin(9600);
	Serial1.begin(115200);

	pinMode(pinEnableWrite, OUTPUT);     
	digitalWrite(pinEnableWrite, LOW);
	
	idxRead = 0;
}

void loop() 
{
	if (Serial1.available())
	{
		buffer[idxRead] = Serial1.read();
		//if (idxRead == 0) Serial.print("Richiesta: ");
		//Serial.print(buffer[idxRead], DEC);
		//Serial.print(" ");
		if (idxRead > 0  ||  buffer[0] == 255) idxRead++;
	}
	
	if (idxRead == 6)
	{
		//Serial.println(" ");
		//delay(10);
		
		idxSlave = buffer[2];
		buffer[0] = 255;
		buffer[1] = idxSlave;
		buffer[2] = 0;
		buffer[3] = command;
		buffer[4] = idxSlave * 2;
		buffer[5] = buffer[1] ^ buffer[2] ^ buffer[3] ^ buffer[4];
		
		RS485Write(buffer, 6);
		
		idxRead = 0;
	}

	delay(1);
}

void RS485Write(byte* packet, byte len)
{
	UCSR1B &= ~(1<<RXEN0);  //disable RX   
	UCSR1A = UCSR1A |(1 << TXC0);  
	Serial1.flush();

	digitalWrite(pinEnableWrite, HIGH);
	Serial1.write(packet, len);

	while (!(UCSR1A & (1 << TXC0))); // attendi fine trasm.

	digitalWrite(pinEnableWrite, LOW);
	UCSR1B |= (1<<RXEN0);   //enable RX
}

5 secondi mi sembrano un'eternità..
Ed in quei 5 secondi che cosa farebbe il master? resterebbe in attesa della risposta dallo slave? Ma così potrebbe ignorare eventuali segnali di allarme degli altri slave.

no, no, aspetta... come ho scritto, questa cosa l'ho inserita solo in questo sketch di prova e soltanto perchè all'inizio ho fatto qualche prova collegando lo slave solo in un secondo momento. Fatto così, il master mi interrogava sempre lo stesso slave fino a che non lo collegavo.

Gli sketch che ho postato non vogliono essere un esempio di funzionamento reale, ma solo un punto di partenza per vedere due arduino che parlano tra loro secondo una architettura master/slave, cioè con uno che interroga e l'altro che risponde.

Avrai notato che non viene nemmeno gestito un timeout entro il quale lo slave interrogato deve rispondere.

Come ho scritto, il protocollo reale deve provvedere per esempio ad isolare lo slave che non risponde ossia deve evitare di interrogarlo ulteriormente. In particolare, il comportamento potrebbe essere il seguente:

  • quando uno slave non risponde entro pochissimi ms, l'evento viene ignorato per un certo numero di volte (per esempio 3) per gestire il caso in cui lo slave è momentaneamente occupato (se questo è possibile).
  • se non risponde per 3 tentativi consecutivi, viene messo in stato "DOWN" ed il master passa ad interrogarlo solo ad intervalli molto più lunghi, per esempio ogni 10 secondi (per implementare una sorta di plug&play) oppure su richiesta di una supervisione (per gestire il collegamento di un nuovo slave in maniera manuale).

Cerco di riprendere l'argomento caduto in un sonno profondo...
Abbiamo detto che l'intento è quello di creare un simple RS485 protocollo...ovviamente io non sarei in grado di svilupparlo da solo ma m'immagino che la necessità primaria sia quella di sviluppare una libreria ad-hoc.

Il progetto è quello di scrivere una libreria per un sistema di domotica e controllo domestico che preveda:

1- Il cuore del sistema il(il MASTER)penso possa essere un'Arduino Mega o Arduino 1 che colloquia con almeno 8 dispositivi SLAVE,(altri Arduino 1 o anche Arduino mini...)

2- Il progetto deve prevedere l'uso dei PIN arduino Slave come semplici IN-OUT e per stardardizzare si può anche stabilire che ogni Arduino 1 Slave debba prevedere 6 IN e 6 OUT digitali per l'interfacciamento con sensori fumo,sensori d'allarme,stati luce...e attuatori a relè.

3- Il terminale Master riceve in tempo reale lo stato degli ingressi e uscite PIN di ogni slave e ad ogni cambio di stato di un ingresso PIN slave deve processare l'evento e far corrispondere una risposta in base alla esigenze del costruttore...

Ora partiamo col far funzionare un semplice Arduino1 come Master che preveda questo:

MASTER Arduino1:

Pin 0,1 --- non usati per lasciare libera la seriale per il debug con l'IDE
Pin 2,3 --- usati come Tx Rx per la seriale da collegare ai Max485
Pin 4,5,6,7,8 --- configurati come ingressi digitali
Pin 9,10,11,12 --- configurati come uscite digitali

Supponiamo di usare per cominciare solo due apparati Slave con altri due Arduino1 e di configurarli come di seguito :

SLAVE1 e SLAVE2

Pin 0,1 --- non usati per lasciare libera la seriale per il debug con l'IDE
Pin 2,3 --- usati come Tx Rx per la seriale da collegare ai Max485
Pin 4,5,6,7,8 --- configurati come ingressi digitali ( collegati a pulsanti luce )
Pin 9,10,11,12 --- configurati come uscite digitali (collegati a relè passo-passo per accensione luci)
Pin A0,A1,A2,A3 --- configurati come stato sensori analogici (stato luminosità,temperatura...)

A questo punto stabiliamo le prestazioni dell'impianto :

ARDUINO slave 1 :

-Se si preme un pulsante collegato al pin 4 e si passa dallo stato Low allo stato High si deve accendere un led sul pin9 in modalità passo passo ovvero premo pulsante---accendo luce....ripremo pulsante---spengo luce
-Manda in tempo reale il valore del Pin A0 al quale è collegato un sensore luce...

ARDUINO slave 2 :

-Se si preme un pulsante collegato al pin 5 e si passa dallo stato Low allo stato High si deve accendere un led sul pin9 per 5 secondi ovvero premo pulsante---accendo luce e dopo 5 secondi si spegne
-Manda in tempo reale il valore del Pin A0 al quale è collegato un sensore temperatura...

ARDUINO master

  • Controlla in tempo reale lo stato dei pulsanti degli attuatori slave e comanda le accensioni dei led sui Pin9...

E' fattibile tutto ciò ???...sviluppiamo questo protocollo oppure direttamente la libreria e se mai iniziamo mai potremo dire di averlo fatto...
Ciao un saluto a tutti...

Per quanto riguarda il protocollo da creare stò ancora incontrando molte difficoltà nel capire i criteri per stabilire una tabella dei comandi...

Volevo provare a stilare una tabella di comandi e un protocollo molto ma molto snello senza l'uso di byte superflui...
Purtroppo senza esempi su cui lavorare e sviluppare per migliorare l'idea del simple protocollo non riesco a proseguire ...
Il tempo purtroppo è barbaro e non mi lascia troppi spazi ma se qualcuno vuole aiutarmi ne sarei grato ma solo per poter aiutare insieme qualcun'altro nella realizzazione del progetto...

Nel frattempo ho continuato nelle prove di colloquio tra un master Arduino mega e 3 slave Arduino1 e sinceramente tra una nota intonata e una stonata ho capito che comunque la missione è ardua e ho provato ad usare le librerie Simple Modbus e Modbus ma sono almeno per me molto complesse e non prevedono una gestione snella dei comandi di ingressi-uscite per una semplice domotica...

Cerchiamo di non abbandonarlo il progetto...buona giornata a tutti...

pietro78:
Per quanto riguarda il protocollo da creare stò ancora incontrando molte difficoltà nel capire i criteri per stabilire una tabella dei comandi...

Hai deciso quali saranno i comandi da trasmettere? Partiamo da lì.
Hai fatto un elenco? Pubblicalo e guardiamolo.

Hai deciso quali saranno i comandi da trasmettere? Partiamo da lì.
Hai fatto un elenco? Pubblicalo e guardiamolo.

scusami leo, non sarebbe più opportuno cominciare con il definire il formato del pacchetto dati? Immagino infatti che i comandi possono anche essere aggiunti uno alla volta man mano che, durante lo sviluppo del software, si presenta la necessità di averne uno nuovo.

Tanto per essere concreti nella mia proposta sul modo di procedere, provo anche ad ipotizzare un primo formato (rivedendo e modificando quello già descritto qualche post fa...). Naturalmente se pensate sia più opportuno procedere diversamente facciamolo pure.

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

Start1 e Start2 devono avere un valore fisso, per esempio, 0xFFFF. Non so se conviene scegliere valori particolari, eviterei, per esempio, il doppio zero perchè mi sembra probabile che possa presentarsi all'interno dei dati.

Con un tale formato immagino che gli slaves restino in ascolto sulla seriale in attesa della coppia Start1/Start2. Quando la ricevono, si mettono in attesa dei quattro bytes che identificano il mittente, il destinatario, il comando e la lunghezza del pacchetto dati. Poi attendono tanti bytes quanti sono indicati nel campo LunghezzaPacchetto. Quindi attendono il Checksum e lo confrontano con quello che hanno calcolato sui dati ricevuti.

A questo punto scartano i dati ricevuti nei casi seguenti:

  • l'ID destinatario non coincide con il loro (il pacchetto è destinato ad un altro slave... attenzione, non possiamo scartare subito il pacchetto perchè altrimenti rimarrebbero bytes non letti nel buffer di ricezione della seriale)
  • il checksum non corrisponde (il pacchetto è corrotto...)
  • durante la ricezione dei bytes attesi capita un timeout (in questo caso possono essersi persi alcuni bytes oppure la sequenza Start1/Start2 non identificava l'inizio di un pacchetto ma si è presentata all'interno dei dati e lo slave si è messo in ascolto a trasmissione iniziata. Caso estremo e forse improbabile ma da considerare per la robustezza del protocollo).

Come dicevo all'inizio del post, una volta identificato il formato, aggiungere nuovi comandi significa soltanto definire un nuovo IDComando (probabilmente l'ultimo usato + 1) ed il relativo pacchetto dati di cui avrà bisogno. Il fatto di aver definito un protocollo con pacchetto dati di lunghezza variabile ci consentirà di gestire comandi che non necessitano di parametri (LunghezzaPacchetto = 0) oppure di un numero arbitrario di bytes (LunghezzaPacchetto = n). L'interpretazione di ciascun comando poi si occuperà di capire il significato dei bytes di dati. La definizione degli IDComando avverrà nel codice del master che deve conoscerli tutti; gli slaves possono conoscere soltanto quelli che gli servono (magari ci sono slaves diversi che fanno uso di comandi diversi...) e scartano pacchetti che contengono comandi che non conoscono.

Aggiungo infine che dovremmo anche definire il comportamento del master e degli slaves in relazione ai timeout per gestire situazioni in cui uno slave interrogato non risponde. Ma questo magari è un aspetto successivo.

Hai deciso quali saranno i comandi da trasmettere? Partiamo da lì.
Hai fatto un elenco? Pubblicalo e guardiamolo.

Caro Leo72 io in mente ho deciso quello che almeno per il momento devono essere i comandi da trasmettere e provo a pubblicare un'elenco...speriamo che possa essere comprensibile e possa essere lo spunto per iniziare finalmente a stilare uno sketch di prova molto ma molto basilare:

Arduino Mega MASTER ID 0x00
Arduino1 SLAVE ID 0x01
Arduino1 SLAVE ID 0x02
Arduino1 SLAVE ID 0x03

Ogni arduino SLAVE gestisce :
-4 Ingressi Digitali pin 2,3,4,5
-4 Uscite Digitali pin 6,7,8,9,10,11
-4 Ingressi Analogici pin A0,A1,A2,A3

Arduino Mega MASTER gestisce:
-4 Ingressi Digitali pin 2,3,4,5
-12 Uscite Digitali pin 6,7,8,9,10,11,12,13,14,15,16,17
L'arduino Master interroga in tempo reale lo stato degli ingressi digitali,analogici e delle uscite di ogni SLAVE
Il legame che ci dovrà essere tra i 4 dispositivi almeno per adesso potrebbe essere semplicemente questo:

Step 1
ARDUINO1 Slave Ingresso Pin2 Riposo LOW ----- Uscita Pin6 LOW ----- Master Mega Uscita pin6 LOW
ARDUINO1 Slave Ingresso Pin3 Riposo LOW ----- Uscita Pin7 LOW ----- Master Mega Uscita pin7 LOW
ARDUINO1 Slave Ingresso Pin4 Riposo LOW ----- Uscita Pin8 LOW ----- Master Mega Uscita pin8 LOW
ARDUINO1 Slave Ingresso Pin5 Riposo LOW ----- Uscita Pin9 LOW ----- Master Mega Uscita pin9 LOW
ARDUINO1 Slave Ingresso A0 ---- misura temperatura ---- trasmette il Byte all'Arduino Mega MASTER

Cambio di stato:

Step 2
ARDUINO1 Slave Ingresso Pin2 Attivo HIGH ----- Uscita Pin6 HIGH ----- Master Mega Uscita pin6 HIGH
ARDUINO1 Slave Ingresso Pin3 Attivo HIGH ----- Uscita Pin7 HIGH ----- Master Mega Uscita pin7 HIGH
ARDUINO1 Slave Ingresso Pin4 Attivo HIGH ----- Uscita Pin8 HIGH ----- Master Mega Uscita pin8 HIGH
ARDUINO1 Slave Ingresso Pin5 Attivo HIGH ----- Uscita Pin9 HIGH ----- Master Mega Uscita pin9 HIGH
ARDUINO1 Slave Ingresso A0 ---- misura temperatura ---- trasmette il Byte all'Arduino Mega MASTER

Step 3
ARDUINO1 Slave Ingresso Pin2 Riposo LOW ----- Uscita Pin6 LOW ----- Master Mega Uscita pin6 LOW
ARDUINO1 Slave Ingresso Pin3 Riposo LOW ----- Uscita Pin7 LOW ----- Master Mega Uscita pin7 LOW
ARDUINO1 Slave Ingresso Pin4 Riposo LOW ----- Uscita Pin8 LOW ----- Master Mega Uscita pin8 LOW
ARDUINO1 Slave Ingresso Pin5 Riposo LOW ----- Uscita Pin9 LOW ----- Master Mega Uscita pin9 LOW
ARDUINO1 Slave Ingresso A0 ---- misura temperatura ---- trasmette il Byte all'Arduino Mega MASTER

Idem per Arduino Slave 2,3, con l'unica differenza che sul Master Mega le uscite da pilotare sono per corrispondenza Slave2-pin10,11,12,13 e Slave3-pin14,15,16,17

Ora l'elenco dei comandi dovrebbe questo:

Cmd 1 = master interroga Pin Ingresso ? Slave ?
Cmd 2 = master interroga Pin Uscita ? Slave ?
Cmd 3 = master interroga Pin analogico ? Slave ?
Cmd 4 = master comanda stato LOW Pin Ingresso ? Slave ?
Cmd 5 = master comanda stato HIGH Pin Ingresso ? Slave ?
Cmd 6 = master .... e qui mi blocco perchè non saprei più andare avanti e comunque sia spero che quello che ho scritto sia comprensibile...

Chiaramente lo scopo di tale progetto è prettamente per uso domestico dove l'Arduino MEGA farebbe da Nodo Master centrale di casa e gli Arduini1 dislocati nelle varie stanze per monitorare pulsanti e attivare luci e rilevamento temperatura ambientale...poi a progetto avviato lo scopo sarebbe quello di installare su Arduino Mega una shield Ethernet per far visualizzare da client browser lo stato delle temperature di ogni ambente e lo stato delle luci on-off...

Dai che forse partiamo...

vittorio68:
scusami leo, non sarebbe più opportuno cominciare con il definire il formato del pacchetto dati? Immagino infatti che i comandi possono anche essere aggiunti uno alla volta man mano che, durante lo sviluppo del software, si presenta la necessità di averne uno nuovo.

Concordo.

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

Perché 2 byte di start? Mi sembra inutile. Tanto hai comunque il checksum finale che ti dice se hai intercettato dati spuri sulla linea oppure no.

A questo punto scartano i dati ricevuti nei casi seguenti:

  • l'ID destinatario non coincide con il loro (il pacchetto è destinato ad un altro slave... attenzione, non possiamo scartare subito il pacchetto perchè altrimenti rimarrebbero bytes non letti nel buffer di ricezione della seriale)

Questo è ovvio, devi ricevere sempre tutto e poi solo dopo fare l'analisi.

  • il checksum non corrisponde (il pacchetto è corrotto...)
  • durante la ricezione dei bytes attesi capita un timeout (in questo caso possono essersi persi alcuni bytes oppure la sequenza Start1/Start2 non identificava l'inizio di un pacchetto ma si è presentata all'interno dei dati e lo slave si è messo in ascolto a trasmissione iniziata. Caso estremo e forse improbabile ma da considerare per la robustezza del protocollo).

Introdurrei comunque anche un massimo nella lunghezza del pacchetto dati. Oppure un timeout di uscita, per evitare che il ciclo di ricezione resti inutilmente in attesa di una trasmissione che si interrompe a metà.

Giusto anche questo. Ci vuole il timeout perché devi sempre prevedere che la trasmissione possa interrompersi. E ci vuole anche una dimensione max del pacchetto, visto che di default la seriale ha un buffer di 32 byte (dall'IDE 1.0 in poi) per cui se arrivano più byte ed il codice non li estrae subito dal buffer, rischi che gli ultimi sovrascrivano i primi.

Aggiungo infine che dovremmo anche definire il comportamento del master e degli slaves in relazione ai timeout per gestire situazioni in cui uno slave interrogato non risponde. Ma questo magari è un aspetto successivo.

Si potrebbero definire 2 o 3 livelli di allarme, ad esempio se il comando inviato è un comando critico, si richiede una risposta entro un certo lasso di tempo, altrimenti il master interpreta la mancanza di risposta come uno stallo o problema del relativo slave.

@pietro78:
il tuo post è arrivato mentre stavo rispondendo a vittorio68.
Con il suo metodo uno stabilisce un protocollo generico, adattabile poi alle esigenze dei singoli utenti.