decodifica dati da sensore ultrasuoni

Buonasera a tutti.

Ho l’esigenza di leggere un sensore di distanza (MB7060 datasheet: http://www.maxbotix.com/documents/MB7060-MB7070_Datasheet.pdf) che ha come possibilità di uscita, fra l’altro, una RS232. (Dico subito che ho scartato l’uscita analogica perchè l’ambiente di installazione del sensore è altamente rumoroso e le oscillazioni sembravano un’altalena… :slight_smile: )

Osservo anche che mi è noto che il protocollo RS232 standard utilizza tensioni fuori dal range di Arduino, ma il sensore, come riporta il datasheet emette un segnale 0-Vcc, quindi entro i 5V utilizzati per la sua alimentazione, senza friggermi i pin di Arduino.

Nella sezione dedicata all’uscita RS232 si dice che il sensore emette una R maiuscola e tre cifre in formato ASCII che lette una dietro l’altra mi danno la distanza in cm letta dallo stesso. Praticamente per una distanza di 35 cm dovrei avere R035, quindi un carattere di “a capo”.

Ho scritto un breve listato che legge a caso 10 volte sulla seriale software (uso la libreria SerialSoftware perchè la seriale HW è occupata da un LCD).

#include <SoftwareSerial.h>

SoftwareSerial mySerial(6, 7); // RX, TX

void setup() {

  Serial.begin(9600);
  mySerial.begin(9600);

//impostazioni LCD
  Serial.write(0x13);
  Serial.write(4);
  Serial.write(30);
  Serial.write(200);
  pinMode(6, INPUT);
  pinMode(7, OUTPUT);

}

void lcd_pos(byte x, byte y)
{
  Serial.write(3);
  Serial.write(x);
  Serial.write(y);
}

void loop() {

  if (mySerial.available() > 0) {
  lcd_pos(1,2);
 for (int i = 0; i < 10; i++) {
   Serial.print(mySerial.read());
   Serial.print(" ");
 }
  mySerial.flush();
}

  delay(1000);
  Serial.write(12);  //cancella l'LCD
  

}

e il risultato è:

43 0 100 76 171 06 7 0 43 0

ossia 10 caratteri, che nella codifica ASCII sono apparentemente a caso. Si ripete “0 43 0” (come se questa fosse la “R” ad inizio segnale") e poi ci sono 5 numeri che variano al variare della distanza di ostacoli dal sensore.
Sono certo che il problema sia una cattiva decodifica dei dati in arrivo dalla seriale, anche perchè se il sensore sta fermo quei numeri che vi ho scritto restano costanti. Quindi rappresentano, in codice, una lettura.

C’è qualcuno che può aiutarmi?

Grazie ragazzi!

Che Arduino stai usando ?

Guglielmo

Giusto! Mancava qualcosa :)

Scusate.

Arduino uno. Il pin impostato come Rx è, come si vede nel listato, il 6.

Ok, quindi non ci sono le limitazioni sui pin della SoftwareSerial ...

Sicuramente hai un problema con la lettura dei valori ... ... tu difatti aspetti che si disponibile qualche cosa sulla seriale, ma poi ... leggi a raffica 10 valori senza sapere se c'è qualche cosa di valido o no (... e quasi sicuramente NON c'è visto che Arduino è molto più veloce a fare quel ciclo che non il sensore a scrivere carattere a carattere sulla seriale) !!! :astonished:

OGNI lettura deve essere preceduta da "if (mySerial.available() ) { ... }" altrimenti rischi che "mySerial.read()" ti ritorni valori non validi. Introduci questa modifica e verifica cosa succede ...

Guglielmo

P.S. : ... e scusa ... quale sarebbe lo scopo della "mySerial.flush()" che fai alla fine ??? :astonished:

Per quel che ho capito dal datasheet, il sensore invia in continuo la lettura. Quindi dopo che ho preso i valori, vorrei svuotare il buffer, per leggere al prossimo ciclo valori "freschi". Questo ragionamento naturalmente ipotizza una velocità di invio del sensore alta, compatibile o maggiore di quella di Arduino.

Provo invece la strada suggerita da te limitando la lettura con una verifica della disponibilità dati e ti faccio sapere...

Ecco fatta la prova col seguente codice:

#include <SoftwareSerial.h>

SoftwareSerial mySerial(6, 7); // RX, TX

void setup() {

  Serial.begin(9600);
  mySerial.begin(9600);

//impostazioni LCD
  Serial.write(0x13);
  Serial.write(4);
  Serial.write(30);
  Serial.write(200);
  pinMode(6, INPUT);
  pinMode(7, OUTPUT);

}

void lcd_pos(byte x, byte y)
{
  Serial.write(3);
  Serial.write(x);
  Serial.write(y);
}

void loop() {

  if (mySerial.available() > 0) {
   byte dato = mySerial.read();
  lcd_pos(1,1);
   Serial.print(dato, BIN);
   Serial.print(" ");
  lcd_pos(2,1);
   Serial.print(dato, DEC);
   Serial.print(" ");

}

  delay(2000);
  Serial.write(12);  //cancella l'LCD
  
}

Sostanzialmente se il dato c’è, lo legge e lo scrive sia in forma binaria che decimale al rigo successivo. Dopo due secondi legge il prossimo, se c’è.

Il risultato è il seguente (tenendo fermo il sensore in modo da lasciare invariata la lettura)
I seguenti valori si ripetono ciclicamente.

101011 43
0 0
110 6
1100110 102
1011001 89
11110 30
0 0

e ricomincia (fino a bloccarsi dopo circa 2 minuti, credo perchè si esaurisca il buffer, senza flush)

I dati letti sono sempre coerenti a loro stessi, ma senza significato.

Mi è venuto il dubbio che lo standard RS232 abbia una logica invertita rispetto a TTL.
HELP! :~

nfloppy: Mi è venuto il dubbio che lo standard RS232 abbia una logica invertita rispetto a TTL. ...

Ah .. questo SI ... una a riposo è LOW (RS232), e, se leggi il datasheet, c'è anche scritto : "Low IDLE state for RS232", mentre la seriale TTL, a riposo è HIGH ... devi mettere un inverter ...

Guglielmo

Eccoci!

Nella dichiarazione della porta seriale virtuale via software, come terzo parametro specificando 'true' si imposta l'inversione della logica (per rendere TTL compatibile con RS232 !!!salvo tensioni naturalmente!!!).

SoftwareSerial mySerial(6, 7, true); // RX, TX, inv. logica

Ho fatto al volo una prova e sembra leggere valori ASCII pari a 82 (l'attesa R maiuscola) e compresi nel range 48-57, ossia i digit. Quindi a occhio sembra funzionare. Ottimizzo il codice e lo posto appena possibile perchè resti a disposizione della comunità.

Vi aggiorno.......

Soluzione interessante.

Posto il listato finale (una prova veloce, naturalmente da modificare per le esigenze di ciascuno e senz’altro ottimizzabile da programmatori più esperti.).

Funziona. E risolve il problema di un ambiente davvero rumoroso (vi garantisco che l’uscita analogica oscilla di 1.5 - 2.5 V in continuazione…). E’ così stabile che probabilmente non servono ulteriori manovre di normalizzazione del dato, come ne ho trovate in rete (ad esempio campionando la lettura ad n intervalli e scartando valori troppo lontani dalla media…). Naturalmente però dipende dall’applicazione.

L’inversione della logica rende compatibile il TTL col protocollo RS-232 (a meno, lo sottolineo per chi dovesse leggere questa replica senza il resto della discussione, dei valori di tensione, che nel caso del sensore in uso sono 0-Vcc, mentre il protocollo RS-232 standard prevede range più estesi ed anche negativi, non compatibili direttamente con Arduino).

#include <SoftwareSerial.h>

SoftwareSerial mySerial(6, 7, true); // RX, TX, inv. logica

void setup() {

//inizializzazione LCD
  Serial.begin(9600);
  Serial.write(0x13);
  Serial.write(4);
  Serial.write(30);
  Serial.write(200);

//impostazioni per Serial Software
  mySerial.begin(9600);
  pinMode(6, INPUT);
  pinMode(7, OUTPUT);

}

void lcd_pos(byte x, byte y)
{
  Serial.write(3);
  Serial.write(x);
  Serial.write(y);
}

void loop() {

  long tempo = millis();
  boolean valore = false;
  byte dato = 0;
  char c1;
  char c2;
  char c3;
  
do
{
if (mySerial.available() > 8) {   //poichè il sensore invia 5 caratteri (R)(xxx)(13) in 9 byte ho la certezza di avere un dato completo
do
{
dato = mySerial.read();
} while (dato != 82);
c1 = mySerial.read();
c2 = mySerial.read();
c3 = mySerial.read();
valore = true;
}
} while (!valore || ((millis() - tempo) > 10000));  // dopo 10 secondi esce dal loop

   lcd_pos(1,1);
   Serial.print("Distanza: ");
   Serial.print(c1);
   Serial.print(c2);
   Serial.print(c3);
   Serial.print("cm ");

  delay(1000);
  mySerial.flush();  //svuota il buffer della serial software
  valore = false;
  Serial.write(12);   //cancella l'LCD
   
}

Grazie a Guglielmo per la consueta assistenza!
Spero che la mia esperienza possa tornare utile a qualcuno.

PS per il motivo del “rumore” che mi mandava in tilt Arduino ho sostituito l’LCD precedente che funzionava con protocollo i2c con questo attuale che ha anche protocollo Seriale (ne parlavamo in una discussione qualche giorno fa… → http://forum.arduino.cc/index.php?topic=214493.msg1570527#msg1570527)

Alla prossima!

Ottimo nfloppy, inoltre mi hai fatto notare che quel parametro mi sembra sia omesso nel reference ed invece ... è molto utile !

Strano che non lo abbiano documentato ... guardando il codice è chiaramente visibile :

SoftwareSerial::SoftwareSerial(uint8_t receivePin, uint8_t transmitPin, bool inverse_logic /* = false */)

Mah ... :roll_eyes:

Guglielmo