Problemi con comunicazione seriale

Buonasera a tutti!
Scrivo perché ho un problema che mi sta facendo impazzire. In breve:
Ho sviluppato un prototipo con un Arduino Mega che comunica in seriale con un dispositivo GPS tracker (del cliente) con protocollo RS232 (ho utilizzato un MAX3232 e, collegando a un convertitore USB-Seriale e al monitor seriale funziona egregiamente).
Sostanzialmente cosa deve fare questa scheda: prende in input dei segnali digitali da una centralina e scrive su seriale una stringa che segnala l'evento al tracker. il dispositivo deve anche ricevere in seriale dal tracker una stringa per "pilotare" la centralina, ma è qui che nasce il problema: Il tracker manda stringhe costantemente sulla linea seriale e questo mi blocca completamente lo sketch, quindi alla variazione degli ingressi della mia Mega, non accade nulla perché continua a ricevere dati sulla seriale in ricezione...
C'è un modo per bloccare la ricezione (ho provato con gli interrupt, ma non va) quando un pin della mia MEGA varia di stato, mandi la stringa sulla seriale e poi si rimetta a leggere?

Grazie a tutti in anticipo, spero di essermi spiegato bene

Puoi provare il metodo end(), cioè Serial.end();
Per usare la Serial nuovamente serve chiamare Serial.begin();

Se il tracker invia byte in continuazione e il codice non li legge si arriva a saturare il buffer di ricezione che comunque è un buffer ring (ad anello), se ricevi a 9600 bit per secondo tra un byte ricevuto e il successivo ci dovrebbe essere tempo per eseguire altro codice. Inoltre se leggi gli ingressi della mega in polling non ti servono gli interrupt, per cui noInterrupts(); disabilita gli interrupt incluso il contatore millis(). interrupts(); riabilita gli interrupt globale. Rispettivamente cli(); clear interrupt e sei(); set interrupt.

Mentre chiamare Serial.end(); disabilita solo gli interrupt della UART sia in trasmissione che ricezione.

Ciao.

Il problema è che se disabilito la seriale magari mi perdo una stringa di comando mandata dal tracker nel tempo in cui è disattivata... Comunque faccio una prova

Come già scritto da @Maurotec, i tracker GPS tipicamente hanno un baud rate di 9600 e con queste velocità di trasmissione così basse il Mega dovrebbe avere tutto il tempo di fare altro tra un byte e il successivo.

Prova a mettere lo sketch completo, così possiamo avere una situazione più chiara sotto gli occhi.

Il problema principale è che vengono inviate continuamente stringhe e non solo quelle stabilite, ma tantissimi dati inutili

Il problema è qui.
Questo pezzo di codice legge in modo bloccante tutta la stringa fino al carattere di terminazione che tipicamente è il new line '\n'.
Il problema è che appena finito, c'è di nuovo un'altra stringa in coda da leggere.

Tu invece devi necessariamente leggere byte per byte senza bloccare nel frattempo il resto e gestire manualmente il carattere di new line per sapere quando fare il parsing dei dati ricevuti.

Per quanto riguarda il resto invece, ti consiglio di creare una classe per gestire gli input di cui vuoi fare il debounce ed una per le uscite temporizzate in modo da non dover duplicare n volte lo stesso pezzo di codice (dovrebbe aumentare anche la velocità di esecuzione del loop() che invece immagino adesso sia piuttosto lento)

Esempio di quello che intendo, ma scritta come funzione da richiamare.
Lavorando ora con un array di char e non più con la classe String, non puoi più usare l'operatore == per il confronto delle stringhe, ma dovrai usare le funzioni C standard strcmp() o simili.

char buffer[512];
int bufferIndex = 0;

bool readSerialNonBlocking() {
  // Controlla se è disponibile un byte da leggere
  if (Serial.available() > 0) {
    char c = Serial.read();  // Legge un byte dalla seriale
    
    // Aggiunge il byte al buffer, se non si è superata la dimensione massima
    if (bufferIndex < bufferSize - 1) {
      buffer[bufferIndex++] = c;
    }

    // Controlla se il carattere ricevuto è '\n' (newline)
    if (c == '\n') {
      buffer[bufferIndex] = '\0';  // Termina la stringa
      bufferIndex = 0;  // Reset dell'indice per il prossimo utilizzo
      return true;  // Linea completata
    }
  }
  return false;  // Linea non ancora completata
}
1 Like

Grazie mille per la risposta :slight_smile:
Tieni conto che questo è un prototipo, una volta che il codice funzionerà come si deve penserò alla sua ottimizzazione.
Da quel che vedo tu metti la lettura della stringa sulla seriale in una funzione booleana e prendi byte per byte salvando ogni carattere nella stringa "buffer"; se rileva il carattere \n ritorna vero ed esce dalla funzione, altrimenti falso e ripete la funzione, corretto?

Esatto, quando la funzione ti ritorna true, significa che puoi fare il parsing delle stringa.

if (readSerialNonBlocking()) {
  if ((strcmp(buffer, alarmIn[0]) == 0) && (timer[0] == 0)) {
    ....

Tieni conto che potrebbe essere parte del problema, anche se non ne sono certo. Bisognerebbe confrontare i due approcci e misurare il tempo di ciclo della funzione loop().

Adesso provo a creare la funzione e ripetere il collaudo... Ti dirò, è molto reattivo, non è così "lento". Infatti, quando mi collegavo al convertitore USB-RS232 girava molto bene e non dava alcun tipo di problema, ma ovviamente ero io che mandavo le singole stringhe dal monitor seriale.
Ancora grazie mille! Provo e ti dico :wink:

Ancora una cosa!
Immagino che char buffer[512] e int bufferIndex = 0 siano variabili globali...
Come svuoti poi la stringa a lavoro compiuto?

Una volta che la stringa sarà "pronta" procederò con strcmp() per il confronto tra la stringa ricevuta e quella che devo controllare

Si certo.
Ho messo un valore a caso per la dimensione del buffer, valuta tu in funzione delle stringhe che devi manipolare.

Controlla anche che il carattere di terminazione sia effettivamente il new line e non il carriage return altrimenti non funziona.

A che serve svuotare la stringa? Tanto alla successiva lettura sovrascrivi i caratteri ricevuti e quando finito aggiungi il terminatore di stringa '\0' che fa funzionare come previsto strcmp().
Comunque se proprio lo vuoi fare, quando hai finito il parsing, puoi aggiungere un memset(), ma secondo me è del tutto inutile.

memset(buffer, '\0', sizeof(buffer));

Concordo, in questi casi a me non interessa azzerare tutta la stringa quindi non uso memset(), ma basta semplicemente ricominciare a scrivere i nuovi caratteri dalla posizione iniziale, quindi basta la riga:

bufferIndex = 0;  // Reset dell'indice per il prossimo utilizzo

Se proprio si volesse mantenere sempre il buffer come stringa formalmente completa (non si sa mai...), basta spostare l'aggiunta del terminatore nella prima if():

    if (bufferIndex < bufferSize - 1) {
      buffer[bufferIndex++] = c;
      buffer[bufferIndex] = 0x0;  // Termina la stringa
    }
    // Controlla se il carattere ricevuto è '\n' (newline)
    if (c == '\n') {
      bufferIndex = 0;  // Reset dell'indice per il prossimo utilizzo
      return true;  // Linea completata
    }

Ma, ripeto, sono sottigliezze che non influenzano il codice (a meno che uno non tenti di usare il buffer quando ancora non è completo...).

Vi posto le modifiche, ora lo provo...


char c;
char buffer[300];
int indiceVett = 0;

// funzione

bool serialRead(){
  if(Serial.available() > 0){
    c = serial.Read();
    if(indiceVett < (sizeof(buffer) - 1)){
      buffer[indiceVett++] = c;
    }

    if(c == '\n'){
      buffer[indiceVett] = '\0';
      indiceVett = 0;
      return = true;
    }
    else{
      return = false;
    }
  }
}

// setup

if(serialRead() == true){    

    if(strcmp(buffer, alarmIn[0]) == 0 && timer[0] == 0){
      timer[0] = tempi[0];
      digitalWrite(allarmeGenerale, HIGH);
    }
    else if(strcmp(buffer, alarmIn[1]) == 0 && timer[1] == 0){
      timer[1] = tempi[0];
      digitalWrite(resetAllarme, HIGH);
    }
    else if(strcmp(buffer, serviceIn[0]) == 0 && timer[2] == 0){
      timer[2] = tempi[0];
      digitalWrite(attivaImpianto, HIGH);
    }
    else if(strcmp(buffer, serviceIn[1]) == 0 && timer[3] == 0){
      timer[3] = tempi[0];
      digitalWrite(disattivaImpianto, HIGH);
    }
  }


Appena riesco a provare vi aggiorno, grazie ancora!

Non funziona :smiling_face_with_tear:
Faccio ulteriori prove

Come ti dicevo, controlla che la stringa inviata dal tracker abbia il carattere '\n'.

Se usi un terminale software, di solito c'è la possibilità di vedere a video anche i caratteri non stampabili come '\n' '\r' etc etc.

Adesso sto facendo un controllo simulando il tracker con il monitor seriale; volevo controllare che continuasse a funzionare con le modifiche, ma deve esserci un problema con la stringa buffer e il confronto con quelle che ho nel vettore

Beh quel codice che hai postato certo che non funziona, non dovrebbe manco compilare per via di queste righe:

      return = true;
      return = false;

Devi scrivere senza "=", ossia:

      return true;
      return false;

E poi il codice che hai postato non è completo, ma hai scritto di aver inserito nel setup la "if(serialRead() == true){", che non ha senso, va messa nel loop...

Insomma, spiegati meglio.

Si si, ho dimenticato di modificarlo, e ho sbagliato ma è nel loop, non nel setup... Scusate ma sto facendo 3 cose contemporaneamente.... Comunque è tutto corretto