Comunicazione seriale tra Arduino Mega e Arduino Due

Buongiorno.
Sto implementando una comunicazione seriale tra un Arduino Mega e un Arduino Due. Utlilizzo a tale scopo la Serial1 su entrambi i lati, con la classica inversione Rx Tx. Ho naturalmente provveduto a parificare le tensioni tra i pin Rx Tx delle due schede. Premetto che ho inserito negli sketch la classica:

byte a;
while (Serial.available()) {
  a=Serial.read();
}

Il lato Arduino Due legge la stringa dal Mega, e fin qui tutto bene. La stringa ricevuta attiva una funzione, che rimarrà in loop fino a che, dalla seriale 1 e sempre tramite comando impartito dal Mega, il loop verrà interrotto. Il problema è questo:
all'interno della funzione ho ri-predisposto la lettura dalla seriale 1, per la ricezione della stringa di interruzione del loop, stringa che riceve regolarmente. L'impiccio sta nel fatto che da quel momento in poi lo svuotamento del buffer all'interno della funzione stessa non è possibile, in nessun modo.
La mia domanda è questa: è un errore interrogare la seriale due volte, prima nel loop principale, poi all'interno di una funzione secondaria, oppure mi sfugge qualcosa a tal proposito?

Update codice:

while (stepEnable)
{
  stepperUno.runSpeed(); // Comunica di procedere a una velocità costante
  if (Serial1.available() > 0)
  {
    recString[c++] = Serial1.read();
    delay(5);
    while ((recString[c++] = Serial1.read()) != ';') {
      delay(5);
    }
    recString[c] = '\0';
    if (recString[0] == ':' && recString[1] == 'Z' && recString[2] == '2' && recString[3] == ';')
    {
      stepEnable = !stepEnable; // Esce dal while e dalla funzione
    }
    if (recString[0] == ':' && recString[1] == 'Y' && recString[2] == '3' && recString[3] == ';')
    {
      // Esegue un altro comando
    }
    while (Serial1.available()) // Svuota il buffer
    {
      byte a = Serial1.read();
    }
  }
}

Per interrogare intendi usare il metodo Serial.available() ? ... se SI lo puoi usare quante volte vuoi :wink:

Anche la Serial.read() può essere usata quante volte vuoi, se non c'è nulla da leggere semplicemente ti ritorna -1.

Probabilmente quindi il problema è nel codice ... :roll_eyes:

Guglielmo

Ah, dimenticavo ... nella sezione in lingua Inglese, si può scrivere SOLO in Inglese ... quindi, per favore, la prossima volta presta più attenzione in quale sezione metti i tuoi post; questa volta esso è stato spostato, da un moderatore della sezione di lingua Inglese, nella sezione di lingua Italiana ... la prossima volta potrebbe venire direttamente eliminato :roll_eyes:

Guglielmo

Secondo me il problema è nel primo while dentro l'if. Leggi senza essere sicuro che ci sia qualcosa.

Vero, però se c'è qualcosa nel buffer questo viene svuotato la prima volta che viene chiamato il metodo e quindi se l'OP si aspetta di trovare i dati li in attesa anche le seconda volta di sicuro non funziona come si aspetterebbe.

Vi ringrazio anzitutto per il riscontro...
@fratt @cotestatnt
La dinamica è questa: invio la stringa ":Z2;" e tutto fila liscio, ma quando poi invio la seconda stringa (":Y3;") tramite seriale, riscontro in Monitor ":Z2;:Y3;" e così via se continuo a instradare comandi stringa dall'Arduino Mega. Chiaramente c'è una mancata pulizia del buffer, ma proprio non c'è modo di effettuarla a dovere. In ogni caso questa parte di codice è integralmente riprodotta nella prima lettura dell'Arduino, nel loop principale, e funziona a dovere. Il problema si riscontra richiamando la seriale una seconda volta all'interno della funzione.
N.

Ho chiaramente scritto che, se non c'è più nulla, si riceve il valore -1 :wink:

Guglielmo

Quale buffer? NON certo quello circolare della seriale di Arduino che è perfettamente gestito, quindi? Qualche tuo buffer nel programma? Effettui lo svuotamento di 'recString' tra una ricezione e l'altra?

... t'ho già chiesto che cosa significa questa frase ... che vuol dire richiamare la seriale? :open_mouth: La classe Serial mette a disposizione una serie di metodi, quale richiami più volte?

Guglielmo

Si certo. Ribadivo il concetto solo perché mi sembra che @clarum abbia ancora le idee un po' confuse.

Sì, effettuo regolarmente lo svuotamento della stringa di ricezione, anche in maniera "esagerata", giusto per non lasciare dubbi... Ma ora il dubbio mi viene. E chiedo: qual è il modo di vuotare a dovere una stringa? Forse questo:

memset(recString,0,sizeof(recString));

oppure questo, entrambi da me utilizzati:

int i=0;
for(i=0;i<strlen(recString);i++)
{
    recString[i] = 0;
}

Lo faccio correttamente?

Prima ho scritto:

...ma chiarisco meglio, evidentemente non sono stato sufficientemente preciso:

  1. Nel loop invoco il Serial1.available () e il Serial1.read, secondo lo schema, identico, della funzione in seguito richiamata...
  2. Nella funzione richiamata ripeto lo schema Serial1.available () --> Serial1.read(). Uguale.

N.

Il primo inserisce zeri in per tutta l'area di memoria dove è presente il buffer (sizeof), il secondo solamente per la parte iniziale, fino al primo byte a zero (strlen). Sostanzialmente per una stringa è la stessa cosa (il primo comunque più rapido), nel secodo devi essere sicuro che la stringa sia correttamente terminata con 0x0.

Per tornare alla tua domanda, non hai bisogno di azzerare nulla sulla Serial1 perché la Serial1.read() legge il byte dal buffer interno di Arduino togliendolo dal buffer. Poi quello che ci fai tu è compito tuo :wink:

In pratica nel tuo codice vedo che vorresti leggere tutti i caratteri ricevuti, fino al terminatore ';' incluso e mettere questo nel buffer.
Dato che ne ho fatto parecchi, vorrei darti qualche consiglio. Quando si implementano protocollini seriali come questo, è sempre bene gestire separatamente la ricezione dei caratteri dalla loro gestione.

Mi spiego meglio. Intanto nella ricezione caratteri è bene applicare sempre un "filtro" che verifichi la correttezza del pacchetto: se hai un carattere di inizio (':') è sempre bene ignorare qualsiasi cosa che non inizia con quello. Poi a te serve tutto ciò che c'è tra ':' e ';' quindi puoi anche evitare di tenere nel buffer questi due caratteri (che nel tuo codice continui a verificare inutilmente). Ed infine l'elaborazione del pacchetto la farai solamente quando riceverai il ';'.

Ossia una cosa di questo tipo:

// Nota: dimensionare i buffer
#define MAXBUF 8
char buf[MAXBUF];   // Buffer di ricezione
char cmd[MAXBUF-2]; // Ultimo comando ricevuto (esclusi ':' e ';')
...
void loop() {
  LeggiComando();
  ...
}

void LeggiComando() {
  if (Serial1.available()) {
    char ch = Serial1.read();
    // Se il primo carattere non è ':' lo ignoro
    if ( c == 0 && ch != ':' )
      return;
    // Se è il terminatore, elaboro il comando
    if ( ch == ';' ) {
      // termino la stringa del buffer
      buf[c] = '\0';
      // Copio il comando nel buffer del comando
      strcpy(cmd, buf);
      // Resetto il buffer per ricevere i prossimi
      c = 0;
      // Faccio quello che devo fare, interpretando il comando.
      EseguiComando();
    }
    else {
      // Controllo l'overflow del buffer, scartando il pacchetto errato ossia che
      // non contiene il terminatore
      if (c == MAXBUF-1) {
        c = 0;
        return;
      }
      // E' un carattere interno del comando, lo memorizzo nel buffer
      buf[c++] = ch;    
    }
  }
}

void EseguiComando() {
  // Ad esempio:
  if ( strcmp(cmd, "Z2") ) {
    // Comando Z2
    ...
    return;
  }
  if ( strcmp(cmd, "Y3") ) {
    // Comando Y3
    ...
    return;
  }
  ... eccetera
}

Grazie docdoc.
Provo senz'altro ad applicare i tuoi consigli al mio sketch. Hai fatto delle considerazioni molto interessanti che non avevo approfondito adeguatamente, soprattutto per quanto concerne la verifica dei pacchetti.
Grazie ancora, saluti.
N.

@docdoc
Aggiorno la situazione.
Il codice, ripulito secondo il tuo schema, e adattato al mio caso, funziona perfettamente.
Ti ringrazio per la precisione e la pazienza...
Saluti.
N.

Bene, piacere di esserti stato utile. :+1: