Leggere 3 bytes da seriale

Salve

Ho un device che invia 3 bytes alla volta via seriale che dovrei leggere per poi lavorare su questo frame di dati.
Il primo byte è un intero semplice
Il secondo e terzo byte è un intero con segno.

Premesso che devo usare la SoftwareSerial, sono un po confuso circa la miglior tecnica da usare per leggere questi bytes in ingresso.
Avevo letto di readBytes() che mi potrebbe semplificare un po la lettura ma ho problemi.

Avevo pensato ad una cosa del genere (che non funziona):

void loop() {
  byte id; //canale sensore
  signed int data; //valore sensore

  while (biogas.available() < 3) {} //3 bytes arrivati
  biogas.readBytes(id, 1); 
  biogas.readBytes(data, 2);
  Serial.println(id);
  Serial.println(data);
}

Mi potreste dare un aiuto?

Prova semplicemente, dopo il while:

id = Serial.read();
data = ((signed int) Serial.read() << 8) | Serial.read();

Senza nessun sincronismo? Timeout o terminatore?
Auguri

skaxxo:
Ho un device che invia 3 bytes alla volta via seriale che dovrei leggere per poi lavorare su questo frame di dati.

Ok, chiaro.

Il primo byte è un intero semplice

Un byte è un byte, lo leggi come singolo carattere, che problema c'è?

Il secondo e terzo byte è un intero con segno.

Ok, quindi devi leggere 2 byte, ma come sono codificati? Little o Big Endian (ossia il primo byte è quello più significativo o lo è il secondo)?
In pratica se ricevi ad esempio 0x27 come primo byte e 0xA0 come secondo, i due byte (che rappresentano il tuo intero con segno) è 0x27A0 o 0xA027?
Ossia diciamo che leggi i due byte nelle variabili b1 e b2:

int valore = b1*256 + b2; // Big Endian

oppure:

int valore = b2*256 + b1; // Little Endian

ed ottieni un intero senza segno. Per convertirlo basta vedere se valore >= 0x8000 allora farai valore -= 65536;

Ciao

Grazie per le risposte.

Ho dovuto fare prima un po di debugging hardware perché dovrei usare un max232.

Nel mentre sono riuscito a leggere la seriale direttamente dal pc e da terminale ho questo (decodificato in binario):

[19/02/2019 15:08:44] Read data (COM7) 
    00000001 01010011 01001000

Il primo byte più significativo rappresenta il “canale”.
Quindi: 00000001
Canale 1

Gli ultimi due intero con segno:
01010011 01001000= 21320

Appena risolvo questi problemini hardware, come strutturereste la lettura?
Dopo l’available come leggereste velocemente il frame?

SukkoPera:
Prova semplicemente, dopo il while:

id = Serial.read();

data = ((signed int) Serial.read() << 8) | Serial.read();

Molto elegante, lo proverò :slight_smile:

devo leggere n bytes e poi gestirli? Nin ho problemi di "il byte non é ancora arrivato"? Bene
Creo un array di 3 bytes

if (Serial.available())
{
stringacreata[0]=Serial.read();
stringacreata[1]=Serial.read();
stinggacreata[2]=Serial.read();
}

E ora gestisco i byte nella stringa. Più veloce dovrei parlare in linguaggio macchina, ergo non lo so fare. Sicurezza=0 però.

Veramente in quel modo entreresti nell'if appena arriva anche un singolo byte e poi ne leggi 3...

Infatti manca ancora l'informazione su come avviene la sincronizzazione, cioè come deve fare il ricevitore a capire quale è il primo byte dei tre (l'ha chiesto Standardoil a mezzogiorno).

Claudio_FF:
Infatti manca ancora l’informazione su come avviene la sincronizzazione, cioè come deve fare il ricevitore a capire quale è il primo byte dei tre (l’ha chiesto Standardoil a mezzogiorno).

Ciao

Non è uno stream continuo di byte.
Viene inviato un frame composto da 3byte ogni secondo.

Quindi il primo byte è sempre il canale e gli altri 2 il valore.

skaxxo:
Non è uno stream continuo di byte.
Viene inviato un frame composto da 3byte ogni secondo.

Ok, quindi per la sincronizzazione bisogna aspettare almeno mezzo secondo senza dati e poi mettersi in ricezione. Una volta ricevuti i tre byte attendere un altro mezzo secondo senza dati ecc. E allo stesso tempo la ricezione non deve mai durare più di 100ms (per stare larghissimi). Con questi timeout si legge sempre il pacchetto corretto dall'inizio.

Esattamente.

Anche se giocare con il timing non lo vedo di buon occhio.
Quando non invia frame la seriale e libera... Tanto vale giocare sulla funziona available. Credo!

Serial.available() segnala "libero" anche nel tempo intercorrente tra la lettura di un byte e l'arrivo del successivo. Se non ti basi sul timing è esattamente come avere uno streaming continuo... che va a singhiozzo :slight_smile:

A me sembra che occorra una macchinetta a stati per riconoscere i diversi momenti:

  • attesa silenzio 500ms
  • attesa arrivo primo byte (tempo indeterminato)
  • attesa arrivo secondo byte entro 100ms dal primo
  • attesa arrivo terzo byte entro 100ms dal primo
  • attesa silenzio per i successivi 200ms

Solo così sappiamo di aver beccato tre byte singoli ravvicinati tra loro e isolati da altri byte.

concordo in pieno

Grazie per i consigli.

Allora mi sfugge cosa fa di preciso available.

Se io mettessi un while available>2 (aspetto almeno 2 byte in input) e subito dopo 3 read... Cosa cambia rispetto a alle sincronizzazioni bloccanti utilizzando dei delay suggerite da voi?

Aspetto i 3 byte in input, li decodifico e torno al available di prima...

Ovviamente chiedo ciò per capire bene il meccanismo.

Ad ogni modo grazie ancora per le risposte di tutti.

Forse il timing che suggerite è per evitare la decodifica di 2 frame parziali tra un secondo e l altro?

skaxxo:
Se io mettessi un while available>2 (aspetto almeno 2 byte in input) e subito dopo 3 read...

Che i >2 byte segnalati da Available potrebbero anche essere l'ultimo del pacchetto precedente e i primi due del successivo.

Cosa cambia rispetto a alle sincronizzazioni bloccanti utilizzando dei delay suggerite da voi?

Bloccanti? Delay? :open_mouth: :open_mouth: :open_mouth:
Ho detto tenere conto del tempo, ma bloccante o non bloccante dipende da come si struttura la logica :slight_smile:

Io ad esempio la lettura la farei con questa funzione che dura sempre una manciata di µs:

bool ricevutoTrama()
{
    uint8_t  static  s  = 0;
    uint32_t static  t  = now;
    bool             av = (Serial.available() > 0);
    bool             rxOK = false;

    if      ((0 == s) && av)            { Serial.read();                t = now;         }
    else if ((0 == s) && (now-t > 500)) {                                         s = 1; }
    else if ((1 == s) && av)            { rxBuffer[0] = Serial.read();  t = now;  s = 2; }
    else if ((2 == s) && av)            { rxBuffer[1] = Serial.read();            s = 3; }
    else if ((2 == s) && (now-t > 100)) {                                         s = 0; }
    else if ((3 == s) && av)            { rxBuffer[2] = Serial.read();  t = now;  s = 4; }
    else if ((3 == s) && (now-t > 100)) {                                         s = 0; }
    else if ((4 == s) && av)            {                                         s = 0; }
    else if ((4 == s) && (now-t > 200)) { rxOK = true;                            s = 0; }
    else                                {                                                }

    return rxOK;
}


void loop()
{
    now = millis();
    if (ricevutoTrama()) { processData(); }
}

Claudio_FF:
Che i >2 byte segnalati da Available potrebbero anche essere l'ultimo del pacchetto precedente e i primi due del successivo.

Intendevo proprio questo!
Grazie per il supporto!