Ritardo lettura seriale

Buonasera, devo interrogare un dispositivo tramite seriale; questo dispositivo non risponde subito e usando la funzione serial.available mi da sempre 0; se invece metto un delay(100) tra interrogazione e verifica del serial.available allora me la da disponibile e leggo correttamente i dati; volevo ovviamente NON usare il delay ma una funzione del genere per andare a leggere la risposta:

if (flag_lettura== 0)
 {
  flag_lettura= 1;
  tmr_read_delay= millis();
  Serial2.write(query_string, sizeof(query_string)); // interrogo il dispositivo
 }  
	
if ( ( (millis()-tmr_read_delay)>100)&&(flag_lettura== 1) )  // dopo 100ms vado a leggere la risposta
 {
  flag_lettura= 0; // azzero per abilitare interrogazioni successive
  while (Serial2.available())
   {
     msg_status[i] = Serial2.read();
   }
 }

premesso che sono consapevole che ci sono molti modi per leggere la seriale, il codice che ho scritto sopra è teoricamente corretto? Può funzionare?

Se si è sicuri che dopo 100ms l'intera risposta è arrivata si, va bene (c'è anche da gestire l'indice 'i'). Se invece la risposta fosse ancora in corso si rischia di perdere pezzi.

Io non capisco molto il senso di questa cosa ad essere sincero. La comunicazione seriale in questione è asincrona ed è così che è pensato il funzionamento sia hardware che software. Quando hai byte nel buffer li leggi, cosa importa se il dispositivo risponde dopo 100ms o dopo 10 secondi?

Con il while legge, dopo 100ms, quelli finora arrivati. Appena il while svuota il buffer di ricezione il ciclo termina. Gli eventuali rimanenti, se arrivassero lentamente, resterebbero nel buffer e diventerebbero i primi byte ricevuti alla query successiva.

Un ritardo nella risposta del device dovuto ad eventuali tempi di elaborazione mi sembra una cosa normale, ma che i byte arrivino in modo incostante e sparuto lo è meno.

Se così dovesse essere comunque secondo me l'unico modo affidabile e ripetibile di leggere quel messaggio sulla seriale è usare un carattere di end of trasmission

cotestatnt:
... l'unico modo affidabile e ripetibile di leggere quel messaggio sulla seriale è usare un carattere di end of trasmission

ESATTO ... occorre mettersi in testa che, se si vogliono trasmissioni seriali affidabili, occorre usare un protocollo che preveda, al minimo ... STX, testo della trasmissione, ETX, CRC, EOT ... ma quansi nessuno fa le cose bene. ::slight_smile:

In ogni caso, ALMENO un carattere che indica la fine della trasmissione (che può essere il classico LF o CR) è d'obbligo ... altri modi, basati su timeout o cose simili ... portano sempre a situazioni difficilmente gestibili.

Guglielmo

gpb01:
ESATTO ... occorre mettersi in testa che, se si vogliono trasmissioni seriali affidabili, occorre usare un protocollo che preveda, al minimo ... STX, testo della trasmissione, ETX, CRC, EOT ... ma quansi nessuno fa le cose bene. ::slight_smile:

In ogni caso, ALMENO un carattere che indica la fine della trasmissione (che può essere il classico LF o CR) è d'obbligo ... altri modi, basati su timeout o cose simili ... portano sempre a situazioni difficilmente gestibili.

Guglielmo

In realtà uso il protocollo rs485 con controllo crc e anche di fine trasmissione. Il problema è che se non metto un delay tra interrogazione e lettura della risposta di quel dispositivo (sono delle batterie al litio), queste non rispondono o rispondono male. Con il delay tra interrogazione e lettura funziona perfettamente. Purtroppo ci ho perso molte ore e non ho più tempo di fare altre prove... Neanche con il codice che ho postato sopra funziona. Ci vuole proprio il delay(). Sto usando una scheda blue pill STM.

gt4020:
In realtà uso il protocollo rs485 con controllo crc e anche di fine trasmissione.

Se hai il EOT allora è sbagliata proprio la logica del tuo codice. Purtroppo se stai stretto con i tempi di consegna e non hai tempo per buttare il tutto e ricominciare da capo, con logica differente ... c'è poco da fare ...
... ma, nel tempo e casualmente, aspettati problemi ... ::slight_smile:

Guglielmo

gt4020:
Neanche con il codice che ho postato sopra funziona. Ci vuole proprio il delay()

Il problema è quello che dicevo, il while per la lettura del buffer è più veloce dei dati in arrivo e termina prima che siano arrivati tutti, per assurdo può leggere un solo byte e poi terminare perché la condizione Serial.available() diventa subito falsa (fino all'arrivo del prossimo byte).

La logica corretta è incamerare i byte (anche uno al secondo se per assurdo ne venisse trasmesso solo uno al secondo) fino al riconoscimento della trama completa (alla ricezione però aggiungerei anche un timeout).

si potrebbe usare un Serial.readBytes()...che aspetta tutti i bytes indicati fino al timeut impostato su seriale.
in entrambi casi si sanno i bytes ricevuti quindi in caso di fallimento (non arrivati in tempo) sai quanti saranno i bytes "garbage" che ti arriveranno prima di iniziare una richiesta successiva...e quindi li filtri.

comunque anche con la semplice Serial.read() sai quanti ne arrivano...e se la stringa di ritorno ha lunghezza fissa riesci a fare il filtro su quanti non sono arrivati in tempo.

se la stringa di risposta ha lunghezza variabile devi calcolarli di volta in volta.

PS:...ma nel datasheet del pacco batterie non è indicato il tempo necessario al sistema per fornire una nuova informazione?...oppure...non c'è un registro che indica che una nuova informazione è disponibile?

ORSO2001:
si potrebbe usare un Serial.readBytes()...che aspetta tutti i bytes indicati fino al timeut impostato su seriale.
in entrambi casi si sanno i bytes ricevuti quindi in caso di fallimento (non arrivati in tempo) sai quanti saranno i bytes "garbage" che ti arriveranno prima di iniziare una richiesta successiva...e quindi li filtri.

comunque anche con la semplice Serial.read() sai quanti ne arrivano...e se la stringa di ritorno ha lunghezza fissa riesci a fare il filtro su quanti non sono arrivati in tempo.

se la stringa di risposta ha lunghezza variabile devi calcolarli di volta in volta.

PS:...ma nel datasheet del pacco batterie non è indicato il tempo necessario al sistema per fornire una nuova informazione?...oppure...non c'è un registro che indica che una nuova informazione è disponibile?Nel datasheet non è indicato nulla riguardo tempistiche o registro di informazione disponibile.

Nel datasheet non è indicato nulla riguardo tempistiche o registro di informazione disponibile.

Puoi postare il datasheet così vediamo cosa passa la seriale?

Ecco

PYLON LFP Battery communication protocol - RS485 V2.8 20161216.pdf (227 KB)

Ribadisco quanto scritto al mio post #7 ... sei partito con il piede sbagliato ed hai sviluppato un codice errato (almeno ... da quello che si capisce dallo spezzone che hai messo) che NON controlla nulla di quello che invece quel device ti invia ed in special modo ...

... che invece sono elementi fondamentali per mettere in piedi una corretta routine di ricezione del pacchetto.

Purtroppo, se come hai detto, non hai più tempo a disposizione ... c'è poco da fare ...

Guglielmo

gt4020:
Ecco

Potresti provare cosi, oppure leggere byte per byte e valutare quando hai il carattere di fine riga ed agire di conseguenza.

#define DIMENSIONE_MASSIMA   256        // Valuta quanto può essere lungo al massimo il messaggio di risposta
uint8_t msg_status[DIMENSIONE_MASSIMA+1];
Serial.readBytesUntil(0x0D, msg_status, DIMENSIONE_MASSIMA);

@gpb01 lo spezzone che ho postato era semplificato solo perché mi interessava sapere se quel tipo di delay potesse essere applicabile nel contesto al posto di un brutale delay(). In realtà tengo conto di SOI ed EOI (e successivamente calcolo e confronto anche il CRC). Allo stato attuale mi funziona solo mettendo un delay bruto tra interrogazione e risposta. Però è in funzione da giorni così e credo che non avrò sorprese anche se mi tocca tenere il delay() brutale. Come detto non ho più tempo per approfondire.