Lettura valore intero da seriale

...ma ciao!
sto provando a realizzare un programmino che muova un servo a rotazione continua alla velocità impostata dall'utente.
Per farlo, pensavo di lasciar inserire nel monitor seriale un valore e son partito da un esempio trovato in rete leggermente modificato.
Vorrei che il valore venisse considerato solo se è composto da 4 cifre.
Il "problema" è che se io inserisco 2,3 o 4 cifre vengono lette. Perchè par eche ogni numero corrisponda a 2 byte.
Mentre se inserisco 1 cifra sola, poi ne inserisco ancora una viene letta quella precedente e cosi' via.
Coem faccio a filtrare il valore immesso e cancellare quelli non validi? Anche la funzioen Flush sembra abbia cambiato significato e non fa la pulizia della porta seriale.
grazie.
Qui di seguito il codice che uso senza la parte di movimentazione.

int usr_val = 0; // dato inserito

void setup() {
  Serial.begin(9600); 
}

void loop() {
  if (Serial.available() == 4) {
    usr_val = Serial.parseInt();
    Serial.print("Ricevuto: ");
    Serial.println(usr_val);
  }
}

Dai un'occhiata alla libreria sviluppata da @gpb01 SerialCmd

La trovi anche nel gestore librerie, naturalmente.

Ma se tu questi valori li immetti da Monitor seriale tu mandi la stringa solo quando premi Invio della tastiera quindi il valore è seguito dai due caratteri CR e LF ('\r' e '\n') ossia i terminatori di linea, quindi con quel codice rischi di interpretare male il dato se nel buffer ci sono i terminatori di linea!
Scusa la domanda, ma perché ti servono per forza 4 cifre? Se hai un terminatore ('\n', io in genere ignoro il '\r') la stringa può essere composta da quante cifre vuoi. Ti basta accumulare in un buffer i caratteri che man mano ricevi dalla seriale (come dicevo, ignorando i '\r') e quando ricevi il terminatore '\n' elabori il valore accumulato, svuoti il buffer, ed attendi il successivo.
Una cosa di questo tipo:

...
#define MAXBUF 40
char inputBuf[MAXBUF];
byte pBuf = 0;
...
  char c = 0;
  if (Serial.available()) {
    c = Serial.read();
    if ( c > 0 ) {
      if ( c == '\n' || pBuf == MAXBUF) {
        // Fine dati
        if ( pBuf > 0 ) {
          // Qui elabori i dati ricevuti, presenti in inputBuf[]
        }
        // Azzera il buffer
        pBuf = 0;
      }
      else if ( c != '\r' && pBuf < MAXBUF ) {
        inputBuf[pBuf++] = c;
        inputBuf[pBuf] = 0; // Terminatore di stringa
      }
    }
  }
...

Le 4 cifre era per vincolare i valori tra 1000 e 2000 al volo senza rielaborarli evitandi che venisse inseriti valori bassi.
Ma il problema ce l'ho anche se metto Setial.available > 0 comunque mi becco prima il valore che mi interessa e subito dopo un'altra iterazione con valore 0

il tuo problema, come già successo a molte persone, è che non gestisci un "protocollo" di comunicazione

tu leggi la seriale come un "flusso unico e ininterrotto" di byte
e questo è fisiologico per la comunicazione seriale, non puoi evitarlo

ma ti serve di "interrompere" la comunicazione, o meglio il suo flusso, con dei marcatori di inizio (ed eventualmente di fine) e in ricezione riconoscere questi marcatori e far partire (ed eventualmente terminare) la conversione solo in corrispondenza dei corretti marcatori

come?

dai uno sguardo ad "aiutateci ad aiutarvi" ed i suoi link

Adesso che mi dici così però mi è sorto un dubbio...
Avendo fatto la cosa di fretta, per inviare i valori davo l'invio.
Avrei lo stesso comportamento se invece di premere invio permessi il pulsante send?
Perché è vero che leggo continuamente, ma io gli mando SOLO quello che ho scritto.
È forse il parseInt che mi separa il numero dal carattere di invio che quindi resta probabilmente in coda nella seriale.
Se anziché fare invio, usassi il pulsante send, non dovrei più avere niente oltre a quello che ho scritto

Quale parte di "il monitor seriale aggiunge i caratteri di fine riga" non era chiara?

Se tu scrivi, ad esempio due righe:

1500 (e premi Invio o Send che è la stessa cosa)
2050 (e premi Invio o Send che è la stessa cosa)

se tu leggi dal buffer solo quando vedi che available() ti dice 4, la prima volta avrai
1 5 0 0
che quindi per te è "1500", ma la seconda volta nel buffer avrai:
\r \n 2 0
quindi 20, e la terza volta
5 0 \r \n
che è "50"!

Cerca di capire il metodo che ti ho suggerito e provalo (che tra l'atro ti consente di leggere quanti caratteri vuoi, se ricevi valori minori di 1000 puoi sempre scartarli o rispondere con un errore...).

oh, sì

ovviamente docdoc ha ragione, infatti ti fa riconoscere i marcatori di inizio (e fine) che "naturalmente" (che significa senza bisogno di intervento, non significa come molti credono "ovviamente") sono presenti nell'uso del monitor seriale

Docdoc Se le metti così, non era chiara la parte che li aggiungeva sia premendo invio che premendo send, tutto lì.

Anche perché non ho il comportamento che dici tu.
Se scrivo 1000 ottengo 1000 e poi 0
Se scrivo 2000 ottengo 2000 e poi zero.
Quindi c'è qualcos'altro che non mi torna.
Qualsiasi numero fatto da 2,3 o 4 cifre me lo accetta e poi mette uno zero (che immagino sia il risultato del parseInt sul carattere terminatore, che però mi aspetterei non venga "parsato" visto che non sono 4 byte.

e poi?

voglio dire:
se scrivi "ripetutamente" 2000 cosa "ripetutamente" ottieni?

Ogni volta che scrivo 2000 e premo invio ottengo 2000 e poi 0, 2000 e poi 0 e così via

Che Vabbeh, per quello che devo fare e cioè variare la velocità di un servo a rotazione continua, mi può anche andare bene, ho filtrato il valore 0 dalla scrittura al servo, però voglio capire meglio il funzionamento sotto, anche per utilizzi futuri.

void loop() {
  if (Serial.available()) {
    usr_val = Serial.parseInt();
    
  }
  if (usr_val > 999 && usr_val <= 9999) {
    Serial.print("Ricevuto: ");
    Serial.println(usr_val);
  }
}

Così come ti sembra?

Poiché non ti servono valori negativi puoi assegnare -1 a usr_val e poi assegni la velocità al servo con qualcosa di simile a:

if (usr_val) {
   // qui assegni la velocità al servo
}

Ciao.

Mah, non so se sono io a spiegarmi male o tu anon voler capire. Se tu stai usando quel codice che hai postato, se scrivi 2000 e premi invio avrai nel buffer i caratteri:
2 0 0 0 \r \n
Quindi al terzo '0' entrerà nella if() (visto che Serial.available() varrà 4) e leggerai 2000 ma nel buffer di arduino resteranno i caratteri terminatori '\r' e '\n'!. Al successivo ciclo, visto che hai aggunto nuovamente:
2 0 0 0 \r \n
nel buffer, quando avaliable() sarà 4, leggerai:
\r \n 2 0 0 0 \r \n
e dato che la stringa "\r\n20" non inizia con caratteri con un valore intero ma solo \r e \n, restituirà zero.

Quindi ora, se ti va, usa il codice che ti ho suggerito (quindi con la lettura un carattere alla volta accumulandlo dentro ad un buffer, ed acquisizione del valore solo alla ricezione di '\n') e vedrai che con quello funziona tutto. Se non ti va, non so che farci, per cui continua pure così.

Ti sto dicendo che non è come dici tu.
Se io scrivo 2000, ottengo 2000 e poi zero senza che io inserisca niente. Ma /r e /n dovrebbero essere solo 2 caratteri, quindi non dovrebbe leggerli. E se reinserisco 2000 Leggo 2000 non 0 perché probabilmente i due caratteri terminatori lo ha già fatti fuori nella lettura 0 precedente.

Maurotec, quel sistema mi filtra i valori ma devo filtrare anche lo zero con lo stesso sistema

Emmm ... ma qulcuno si è andato veramente a studiare il reference della parseInt() ... perché a me NON sembra ... :roll_eyes:

Guglielmo

1 Like

@gepponline: Purtroppo la parseInt() è basata su un meccanismo a "tempo" piuttosto che su un meccanismo a "terminatore di linea" e questo crea un sacco di problemi.

Se, ad esempio, la usi senza controllare che ci sia qualche cosa sulla seriale, vedi che, con cadenza regolare (il timeout) restituisce zero ... anche se non tocchi la tastiera.

La soluzione più pulita è evitare queste finte "facilitazioni" (onestamente, fatte per chi ha poca voglia di impegnarsi) e, magari dopo aver studiato come funziona la classe Serial, capire ed applicare il codice che ha gentilmente scritto docdoc.

Guglielmo

Non ti seguo, filtrare lo zero?
Lo zero attualmente non passa.

Prova questo online

Ciao.