lettura dati da dispositivo su seriale

Buongiorno a tutti.
Avrei necessità di leggere sulla porta seriale (serial1) di arduino mega alcuni dati provenienti da un dispositivo basato su Pic.
Con il codice seguente fornito dal costruttore riesco a leggere correttamente:

//--------------------------------
int ui8Buffer[8][2];
int ui16Idx = 0;
int ui8CheckSum = 0;
//--------------------------------

void setup()  {


  Serial1.begin(9600);
  Serial.begin(9600);

}
void loop() {

  unsigned long currentMillis = millis();
  unsigned long previousMillis = 0;
  

  ////////////////////////// Temporizzazione d1 1 secondo /////////////////////////
  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    Ph();
    Rdox();
  }
}
int CalcChannel(unsigned char ui8type, unsigned char ui8Msb, unsigned char ui8Lsb)  {
  int ui8Temp;
  if (ui8type == 0) {
    ui8Temp = ui8Msb * 256 + ui8Lsb;
  }
  if (ui8type == 1) {
    ui8Temp = ui8Msb * 256 + ui8Lsb;
    ui8Temp = ui8Temp - 2048;
  }
  return (ui8Temp);
}
int Ph() {
  int PH;
  lettura();
  if (((unsigned char)ui8CheckSum) == ui8Buffer[7][0]) {

    Serial.println("Lettura del primo canale:");
    if (ui8Buffer[1][0] == 0) Serial.println("ph:");
    if (ui8Buffer[1][0] == 1) Serial.println("rx:");
    PH = (CalcChannel((unsigned char) ui8Buffer[1][0], (unsigned char) ui8Buffer[2][0], (unsigned char) ui8Buffer[3][0])); //calcola il valore
    Serial.println(PH);

  }
  else {
    Serial.println("checksum error:");
  }
  ui16Idx = 0;
  return (PH);
}


int Rdox() {
  int RDOX;
  lettura();
  if (((unsigned char)ui8CheckSum) == ui8Buffer[7][0]) {

    Serial.println("Lettura del secondo canale:");
    if (ui8Buffer[4][0] == 0) Serial.println("ph:");
    if (ui8Buffer[4][0] == 1)Serial.println("rx:");
    RDOX = (CalcChannel((unsigned char) ui8Buffer[4][0], (unsigned char) ui8Buffer[5][0], (unsigned char) ui8Buffer[6][0])); //calcola il valore
    Serial.println(RDOX);

  }
  else {
    Serial.println("checksum error:");
  }
  ui16Idx = 0;
  return (RDOX);
}


void lettura() {
  if (Serial1.available() > 0) {
    if (Serial1.read() == 0x55) {
      ui8Buffer[0][0] = 0x55;
      ui8Buffer[0][1] = 0;
      do {
        if (Serial1.available() > 0) {
          ui16Idx++;
          ui8Buffer[ui16Idx][0] = Serial1.read();
          ui8Buffer[ui16Idx][1] = ui16Idx;
        }
      } while (ui16Idx <= 8);
      ui16Idx = 0;
      ui8CheckSum = 0;

      for (ui16Idx = 0; ui16Idx < 7; ui16Idx++) {

        ui8CheckSum = ui8CheckSum + ui8Buffer[ui16Idx][0];
      }


      ui16Idx = 0;

    }

  }
}

Il problema è che quando arrivano i dati, questi vengono stampati in maniera ripetuta più volte sul monitor. In pratica ad ogni ciclo arrivano le letture in questo modo:
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343

Vorrei che ad ogni ciclo venga stampata solo una coppia di valori cioè:
Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343

In questo modo la seriale non si saturerebbe e mi consentirebbe altre operazioni, cosa che adesso no avviene perchè "impegnata" a ricevere sempre gli stessi dati.

Chiedo a voi un consiglio su come fare per risolvere questo problema, non avendo esperienza sulla comunicazione seriale.
Grazie saluti
Fabio

Mah, a me sembra che ad ogni ciclo già adesso stampi una volta sola. Ma esegue circa un ciclo al secondo, tu ogni quanto vuoi stampare?

ciao,

scusa ma non ho capito...per com'è fatto lo sketch verifichi e stampi una volta al secondo...secondo me il vero problema sta nel fatto che nella seriale, dal sensore, arrivano "continuamente" dati...cioè il sensore NON risponde ad una tua richiesta...tu controlli se nel buffer della seriale c'è qualche cosa e quando questo qualche cosa inizia per 0x55 inizi a popolare l'array bidimensionale etc etc etc...
hai un datasheet del sensore?...magari c'è un metodo per gestire la trasmissione dati....diversamente, avendo una MEGA, per i restanti devices dovrai usare un'altra Serial (2 o 3).

EDIT: ho scritto sensore...ma intendevo il dispositivo con il PIC...

Grazie per le risposte.
Purtroppo non posso controllare la trasmissione dal pic che invia dati a raffica.
Vorrei che una volta ogni tot tempo, legga i due valori che mi servono (Ph e Redox) e poi basta.
Questo postato è una parte di un codice più complesso che richiama questa lettura.
Dato che il mega fa altre operazioni, i dati in arrivo mi saturano arduino (a cui è collegato anche un touch nextion che va sulla seriale 2) e quindi nel momento in cui sta leggendo, molte volte non mi permette di effettuare operazioni sul touch.

Suppongo che il problema sia questo perchè, appena elimino la parte della lettura del pic dal codice, il touch funziona perfettamente.

Pensavo ad una soluzione del tipo: quando mi arrivano questi due dati (una sola volta), ferma la ricezione, vai avanti fino al successivo ciclo.
Ma non saprei come procedere.....
Grazie Fabio

ciao

potresti usare il metodo end() della Serial1, guarda QUI, e riabilitarla solo quando ne hai necessità.

Non credo che così ne esci
Se ho capito bene la trasmissione è continua
Quindi "quando" arrivano i dati è: "sempre"
Tu devi decidere "se" li vuoi

ORSO2001:
ciao

potresti usare il metodo end() della Serial1, guarda QUI, e riabilitarla solo quando ne hai necessità.

bella, non ci avevo pensato, da mettere nella cassetta dei ferri

Con la end() chiudo la seriale ma dopo aver ricevuto il dato.......anzi lo stesso dato per diverse volte.
Già provato anche con serial flush.

Se vi vengono in mente altre idee io sono qui :disappointed_relieved:

fafidio:
Con la end() chiudo la seriale ma dopo aver ricevuto il dato.......anzi lo stesso dato per diverse volte.

Ovviamente, visto che la seriale ha un buffer e che, anche se tu non leggi, questo si riempie comunque ... ::slight_smile:

fafidio:
Già provato anche con serial flush.

Uso errato della flush() ... il nome inganna, ma, se si legge il reference, ci si accorge che fa tutt'altro da quanto ci si aspetta ... la flush() non fa altro che aspettare lo svuotamento del buffer in trasmissione!

L'unica soluzione è ...

  1. apri la seriale
  2. leggi i due dati
  3. chiudi la seriale
  4. ciclo veloce di svuotamento del buffer senza prendere in considerazione ciò che si legge

Quando serve si riparte da 1.

Guglielmo

Grazie per i consigli.
Il problema è proprio questo: non so come leggere due dati,
mentre per aprire e chiudere la seriale non ci sono problemi.

Mi pui aiutare postando un esempio di come leggere due dati?
Grazie Fabio

... scusa, ma non li leggi già ? ? ? :o :o :o

Lettura del primo canale:
ph:
430
Lettura del secondo canale:
rx:
343

... letti i due valori, chiudi e svuoti il buffer dai dati in più ricevuti.

Guglielmo

Quando faccio la lettura, mi arrivano almeno dieci stampe di quei dati sulla porta seriale;

Vorrei che invece fossero solo due: Ph (valore) Redox (valore). Sto

Come dico ad arduino che dopo le preme due stampe chiudi la seriale e stai quieto fin quando te lo dico io?

Questo è il problema......

fafidio:
Come dico ad arduino che dopo le preme due stampe chiudi la seriale e stai quieto fin quando te lo dico io?

... ma il programma che stampa quei dati lo hai scritto tu o lo hai solo copiato ? ? ? :o :o :o Perché dalle tue domande viene il dubbio ... ::slight_smile:

Leggi il primo valore, leggi il secondo e poi fai Serial.end() e chiudi la seriale, dopo di che, dato che sicuramete nel frattempo saranno arrivati altri caratteri, fai un ciclo veloce di Serial.read() per svuotare il buffer di ricezione e buttarli via.

Guglielmo

il programma me lo ha fornito il costruttore del dispositivo basato su pic (non sono così bravo....).

Faccio un test con serial end e vi aggiorno,
grazie ancora

ma un semplice if non basta?

int lastPh;
int lastRdox;

io la butto lì ::slight_smile:

ma un semplice if non basta?
Code: [Select]

int lastPh;
int lastRdox;

io la butto lì ::slight_smile:

se non ho capito male tu intendi dire...se diverso da ultimo valore leggo e stampo altrimenti no...il problema sta nella funzione lettura() che viene richiamata ogni secondo (in teoria) e che contiene un do/while...che diventa pensante se nella Seriale1 continuano ad arrivare byte...come ho velatamente suggerito, poi spiegato meglio e più detagliatamente da Guglielmo, in questa funzione dovrebbe inserire il Serial1.end(), dopo avere letto le due variabili interessate, e con un ciclo for o while svuotare il buffer...dopo tutto questo deve fare un Serial1.begin(xxxxx) nel loop, trascorso tot millis(), prima del richiamo alle due funzioni Ph() e Rdox().

provato con serial end, dopo la void lettura e serial1 begin nel loop: il serial monitor non stampa più dati...

Secondo me stai andando nella direzione sbagliata. Quelle corrette sono stampare i dati solo quando cambiano, ogni tot tempo oppure una volta ogni N ricezioni.

E per nessuna di queste cose serve chiudere la seriale, bisogna semplicemente subordinare la stampa a qualche condizione.

fafidio:
Grazie per le risposte.
...omissis

Vorrei che una volta ogni tot tempo, legga i due valori che mi servono (Ph e Redox) e poi basta.

...omissis

Pensavo ad una soluzione del tipo: quando mi arrivano questi due dati (una sola volta), ferma la ricezione, vai avanti fino al successivo ciclo.
Ma non saprei come procedere.....
Grazie Fabio

un paio di cose...
qui definisci come interi

//--------------------------------
int ui8Buffer[8][2];
int ui16Idx = 0;
int ui8CheckSum = 0;
//--------------------------------

poi qui vai a trasformarli

 PH = (CalcChannel((unsigned char) ui8Buffer[1][0], (unsigned char) ui8Buffer[2][0], (unsigned char) ui8Buffer[3][0]));
...
  if (((unsigned char)ui8CheckSum) == ui8Buffer[7][0]) {
...
    RDOX = (CalcChannel((unsigned char) ui8Buffer[4][0], (unsigned char) ui8Buffer[5][0], (unsigned char) ui8Buffer[6][0]));

allora perchè non li definisci subito così?

//--------------------------------
unsigned char ui8Buffer[8][2];
int ui16Idx = 0;
unsigned char ui8CheckSum = 0;
//--------------------------------

e poi usi

PH = (CalcChannel(ui8Buffer[1][0], ui8Buffer[2][0], ui8Buffer[3][0]));
...
if (ui8CheckSum == ui8Buffer[7][0]) {
...
RDOX = (CalcChannel(ui8Buffer[4][0], ui8Buffer[5][0], ui8Buffer[6][0]));

se aggiungi alle variabili globali

const unsigned long TEMPOESIGUILETTURA = 20000 //  cito: "Vorrei che una volta ogni tot tempo"

e poi

void loop() {

  unsigned long previousMillis = 0;
 
  ////////////////////////// Temporizzazione come voglio io /////////////////////////
  if (Millis() - previousMillis >= TEMPOESIGUILETTURA ) {
    previousMillis = Millis();
    Ph();
    Rdox();
  }
}

non fa esattamente quello che vuoi tu nei tempi che vuoi tu?

SukkoPera:
Secondo me stai andando nella direzione sbagliata. Quelle corrette sono stampare i dati solo quando cambiano, ogni tot tempo oppure una volta ogni N ricezioni.

E per nessuna di queste cose serve chiudere la seriale, bisogna semplicemente subordinare la stampa a qualche condizione.

la funzione if suggerita da me prima...
ovvio la deve inserire in lettura() se ha capito dove, escludendo quindi tutta la parte dopo questa funzione in Rdox() e Ph(), altrimenti ne deve inserire due relativamente a Rdox() e Ph(), costringendo lettura() a due cicli di esecuzione. Anche se sarebbe giusto dato che in lettura() viene calcolato il checksum che potrebbe essere errato per uno ma non per l'altro... (penso che l'abbia inserito per avere indicazioni circa il corretto funzionamento del sensore).

se non ho capito male tu intendi dire...se diverso da ultimo valore leggo e stampo altrimenti no

Il senso era quello... :wink:

allora perchè non li definisci subito così?

perché il codice non lo ha scritto lui ::slight_smile:
poi è chiara la richiesta, lui vuole che i sensori vengano interrogati ogni secondo, ma la seriale deve popolarsi solo in caso di aggiornamenti.. Questo non vuol dire che non arrivano dati in seriale, (che a sua volta) non vuol dire che non puoi usarla...