Mandare float sulla serial e prendere i float dalla serial

Salve a tutti,
premetto che ho cercato parecchio e trovato altrettanto ma pare non funzionare nulla, forse sbaglio qualcosa di base.

Io dovrei mandare con Serial.write() dei float, il problema è che una volta fatta la procedura al posto di trovare la "stampata" -> 33 33 3B C1 o invertita; mi trovo "33;" con aggiunta in coda di caratteri strani in coda o se invertita in testa

mi baso sulla seguente discussione , dove sembra invece che a tutti vada senza problemi.

Uso un arduino pro mini 3.3v 8mhz 328 (clone), ma il discorso è il medesimo con un UNO e un MEGA.
Il mio codice è identico a quello della thread linkato, ma anche la variante presente nella stessa discussione si comporta in modo identico.

Ogni aiuto è più che apprezzato

Magari se metti il programma che stai usando TU possiamo capirci un po' di più ... :wink:

Guglielmo

typedef union {
 float floatingPoint;
 byte binary[4];
} binaryFloat;

void setup() {
 Serial.begin(115200);
  binaryFloat hi;
  hi.floatingPoint=-11.7;
  Serial.write(hi.binary,4);
}
void loop(){}

Mi da come risultato :

33;⸮

La Serial.write trasmette i dati in "binario" quindi NON li puoi visualizzare (per poterlo fare debbono essere trasmessi codificati in ASCII) ... li devi riceve da un altro programma e poi visualizzarli.

Se vuoi vedere i vari byte sul monitor seriale, devi trasmetterli uno ad uno con una Serial.print() specificando anche, magari, che li vuoi vedere in HEX.

Guglielmo

Risolto, confermo tutto ciò che ha detto Guglielmo; i dati passati erano corretti, il modo di leggerli non lo era.
Grazie mille

Ok mi trovo, sul lato server e ho più o meno gli stessi problemi; non riconosco i valori che mi butta fuori.

Sto usando c#

            SerialPort serial = new SerialPort();
            /**setup COM**/
            serial.PortName = "COM7";
            serial.BaudRate = 230400;
            serial.StopBits = StopBits.One;
            serial.Parity = 0;


            serial.Open();
            byte[] lettura =new byte[4];
            serial.Read(lettura, 0, 4);
            Console.WriteLine(lettura[0]+" " + lettura[1] + " " + lettura[2] + " " + lettura[3] );

Il risultato è:

225 147 147 187

Ho come l'impressione che da serial.write di arduino alla read di c# cambia il modo di leggere i dati.

Ho provato anche con :

BitConverter.ToSingle(lettura, 0);

che mi da:

-5,698295E-27

Detto ciò con questo metodo il dato cambia anche se non dovrebbe (cambia l'ordine dei 225 147 147 187); deduco che tentando di trattare il dato perdo tempo e salta qualche byte.

C'è una soluzione migliore? io avevo pensato di usare gli Stream ma non ne trovo da "collegare" alla SerialPort.Sono un po perplesso su come avanzare..

... come li stai trasmettendo??? Perché, nel caso di colloquio tra due programmi (e non nel caso del terminale seriale), li devi trasmettere in binario puro (Serial.write() e NON Serial.print()), un byte per volta.

Guglielmo

sisi, li trasmetto come scritto all'inizio con la write();con la print ho fatto tutto ciò di cui stiamo parlato ma mi crolla la frequenza dei dati inviati a causa dei simboli per dividere i dati e perdo qualcosa anche nel parsing quindi sono passato ad usare i float che sicuro al 100% ogni 4byte cambia la "variabile".

Il problema è che non so come verificare che il dato inviato sia stato recepito correttamente; a monte so che mando -11.7 ma a valle non riesco a decodificarlo in modo da leggerlo.

Il codice che uso è quello del 3° post, solo scritto nel loop anzichè nel setup, e al posto della Serial uso una SoftwareSerial che comunica usando un HC-05 al pc con il programma in c#.

Trasmetti da Arduino a 115200 Serial.begin(115200);
e nel C# a 230400 ?? serial.BaudRate = 230400;

Comunque, C# deriva dal C, quindi usa la tecnica della union ANCHE in C#
(ovviamente in C# verifica la dimensione a 4 byte di un numero con virgola penso sia un float e non un double mentre in Arduino float e double sono uguali, ovvero sempre 4 byte soli)

Vuoi risparmiarti un sacco di problemi? Spedisci il numero come testo e fai il parsing dall'altra parte.

son tutti e due a 230400, ho dimenticato di aggiornarlo qui.
Come stringa lo ripeto ho già provato ma perdo almeno 20hz. Considera che devo continuare ad inviare un pacchetto di 20float ->80byte, se lo mando come stringa nel caso migliore devo mandare in aggiunta 18caratteri separatori, uno di inizio e uno di fine. Quindi oltre a perdere di precisione sul float passo da 80byte a 100/140byte(consideriamo -17.78 sono 6byte anzichè 4, quindi un valore tra 100 e 140byte per pacchetto).

Aggiornamento..

Tentando di ottimizzare al massimo il programma con le stringhe (che devo cercare di evitare, ma non sapevo più che fare) ho scoperto uno strano comportamento.

Il comportamento molesto è il seguente: quando ricevo (o invio?) i dati con il bluetooth (HC-05) a 230400 di baudrate i dati sono riportati in maniera a dir poco folle, anche leggendoli con ArduinoIDE i simboli non hanno senso.

per ora sono tornato a 115200 sia su client(arduino C) che su server(pc C#), il programma basato sul parsing delle stringhe del serial è tornato a funzionare(e ora implementa anche il multiThread così da perdere poco o nulla). Al che ormai era ovvio che qualcosa dovesse cambiare anche sul passaggio dei float su write().

Infatti ora leggendo il byte[], da indice 0 al 3 ottengo:
59 193 51 51
La felicità di vedere che qualcosa torna!
infatti se portiamo da decimale a hex otteniamo 3B C1 33 33
confronto a ciò che mi aspettavo non è perfetto ma è molto simile: (33 33 3B C1) o (C1 3B 33 33)

Ora, ammettendo anche di cambiare l'ordine sono perplesso, con questo codice esce -11.7:

        static float ToFloatCorrectionReverse(byte[] input)
        {
            byte[] newArray = new[] { input[3], input[2], input[0], input[1] };
            return BitConverter.ToSingle(newArray, 0);
        }

1)Qualcuno sa spiegarmi se è sempre vero che il dato inviato da arduino è ordinato così?
2)Magari c'è anche qualche anima pia, anche se off topic, che mi spiega perchè è successo quel che è successo a 230400bps, visto che volevo addirittura passare ai 460800 visto che l HC-05 dovrebbe poterlo fare..

Secondo me i float su PC sono da 8 byte, per questo non riesci a venirne a capo. Inoltre non è nemmeno detto che la rappresentazione binaria sia la stessa. Per questo esistono un sacco di protocolli di serializzazione per scambiare dati tra sistemi diversi.

Usare stringhe ti evita tutto ciò, per cui io cercherei di andare in questa direzione, non con String ma con char[].

Altra idea: cambiare tipo numerico. Che valori devono gestire questi float? Quante cifre dopo la virgola? Non è che si possono convertire in interi trasmettendo dei più gestibili int32_t?

i float in c#, ma credo nel 90% dei linguaggi, sono da 4byte altrimenti usi il double (single precision, e double precision).
Sulla rappresentazione binaria hai ragione in quanto mi cambia l'ordine ma comunque ora sono riuscito a recuperare il dato.
Tra String e Char[] non vedo differenze sinceramente, probabilmente ci sono ma io non le noto (ovvio i metodi per trattarli sn diversi, ma neanche troppo). Comunque su arduino li gestisce in automatico con la print e sul pc la differenza di potenza è sufficiente a non creare problemi nel trattamento del dato.

I dati variano di moltissimo, si parla di valori tipo +/-1000,0353546 e altri 0,0000075; il fatto è che per varie ragioni in taluni casi mi serve possibilmente anche l'ultimo decimale, in altri no ma non è possibile saperlo a priori.

Ad esempio un cambio di valore repentino da -0,0000075 a +900,555555 non mi fa grossa differenza quel 5 periodico che ho scritto ma nel caso di mantenimento mi interessa sapere fino al più piccolo decimale possibile.(I dati prendeteli con le pinze non ho fatto copia e incolla ma la variazione è davvero estrema)

Utilizzando le stringhe ho ottenuto un errore di circa il 10% sulle misurazioni fatte; per i miei scopi è davvero tanto, dovrei stare intorno allo 0,1% o meglio 0,01%.Per questo motivo e per la frequenza che sto cercando di passare ai float anche se faticando .

Tra String e Char[] non vedo differenze sinceramente, probabilmente ci sono ma io non le noto (ovvio i metodi per trattarli sn diversi, ma neanche troppo). Comunque su arduino li gestisce in automatico con la print e sul pc la differenza di potenza è sufficiente a non creare problemi nel trattamento del dato.

Fino ad ora ho notato che ciò che pago in tempo lo pago su arduino, e ignorando un minimo di trattamento di dati fatto su arduino (che sposterò su server) i problemi sono 2:
La quantità di byte, e i cast.
Io genero dati in float, qundi se li mando su write ho minimo numero di byte e 1 modifica sui dati ma neanche troppo vera per via di come è rappresentato un float.
Nel caso della stringa devo effettivamente (e già lo faccio) moltiplicarli per 10000 devo fare almeno un cast e poi vengono in automatico castati a char e inviati su su serial tramite print.

Ho ancora una perplessità su tutto questo discorso ma prima la devo verificare poi se no la risolvo proverò ad esprimerla.
Se qualcuno sa dirmi qualcosa del baudrate di 230400 e l'hc-05 sono tutto orecchi ( modulo difettato o sono tutti così?)

Ilnomade:
Nel caso della stringa devo effettivamente (e già lo faccio) moltiplicarli per 10000 devo fare almeno un cast e poi vengono in automatico castati a char e inviati su su serial tramite print.

Perché mai? Basta la print()...

String si porta dietro tutta la gestione della memoria dinamica, che oltre a non essere una grande idea quando hai 2k di RAM, perde tempo ad allocare e deallocare.

Alla peggio puoi provare questa implementazione di protobuf per microcontrollori: GitHub - nanopb/nanopb: Protocol Buffers with small code size.

PS: Giusto sui 4 byte, per qualche motivo ero convinto che la precisione singola fossero 8 e la doppia 16. Comunque io eviterei di fare affidamento sulle rappresentazioni binarie, credo si sia capito :D. Se quel protobuf funziona e non richiede troppe risorse può essere davvero utile.

Ilnomade:
I dati variano di moltissimo, si parla di valori tipo +/-1000,0353546 e altri 0,0000075; il fatto è che per varie ragioni in taluni casi mi serve possibilmente anche l'ultimo decimale, in altri no ma non è possibile saperlo a priori.

La precisione di un float su Arduino se non sbaglio è di 7 cifre, quindi più di 7 decimali non possiamo avere.
Moltiplichiamo per 10milioni la sola parte dopo la virgola e abbiamo un numero intero rappresentabile in 24 bit. Non so il valore massimo per la parte intera, se entro i 32mila allora questa la possiamo rappresentare con due byte. Fanno 5 byte al posto di 4, sempre molto meno che usare le stringhe e riconvertibili senza problemi.

Claudio_FF:
La precisione di un float su Arduino se non sbaglio è di 7 cifre, quindi più di 7 decimali non possiamo avere.

La cosa è un po' più articolata ... se ne è parlato QUI :wink:

Guglielmo

gpb01:
La cosa è un po' più articolata ... se ne è parlato QUI :wink:

È per quello che mi chiedevo quale fosse il range per l'applicazione in questione. Se non deve trattare numeri come 6.67e-11, ma un insieme più ristretto, diciamo con valori non minori di 0.0000001 e non maggiori di 32000, si potrebbe applicare la semplificazione che ho proposto.

Ilnomade:
(I dati prendeteli con le pinze non ho fatto copia e incolla ma la variazione è davvero estrema)

Lo sapevo che scrivere dei dati a memoria. era una pessima idea; io so che i dati variano tantissimo, ma non li tratto manualmente.
Ora come ora uso il dato -11.7 perchè so quanto vale in binario/hex/qualunquecosavengainmente, ma di fatto mi preoccupo solo che venga riportato correttamente.

Il mio dubbio era sulla perdita di byte da cui ero afflitto, leggevo il 50% corretto e il restante no, ma ho risolto dedicando un thread all'ascolto e stoccaggio su una struttura semplice e circolare con bit di lock, infatti ora o non perde dati o ne perde 4 a ogni ciclo, lo scoprirò solo controllando la frequenza a monte e a valle..

@Claudio_FF, per ora ho risolto così e sono piuttosto soddisfatto avendo guadagnato 20hz (lato arduino) e avendo mantenuto la stessa precisione di quando viene prodotto il dato.Però terrò senza dubbio conto del tuo metodo, ogni trucco è sempre ben accetto non si sa mai quando se ne ha bisogno.
Comunque leggendo velocemente i dati ho un -0,05938295 scelto per il numero di decimali e per i massimi mi aggiro sui +/- 1200-1300 ma faccio fatica a recuperarli manualmente, con questi la virgola è poco rilvente ma visto che c'è la considero nei calcoli successivi

Per quanto riguarda l'HC-05 forse è meglio se apro un thread in hardware, però prima provo a recuperare informazione da solo vediamo se ne esco..

Grazie a tutti, posso dire tutto RISOLTO