Un solo decimale in float con LiquidCrystal

Sono alle prese con un problema che è stato affrontato più volte, ma per la visualizzazione del dato con Serial.print, le soluzioni che ho trovato non mi risolvono il problema con la lib per la gestione dell'LCD.
In pratica leggo un dato analogico e lo converto nel corrispondente dato reale all'origine (p.es. 13,62 o 7,47), però vorrei tagliare il secondo decimale ma arrotondando correttamente la cifra rimanente (quindi 13,6 o 7,5).
La necessità nasce dal fatto che la seconda cifra decimale mi risulta imprecisa rispetto allo strumento di misura (la variazione da 0,00 a 30,00V effettivi sul partitore è di circa ±10mV), e siccome non mi serve preferisco farne a meno piuttosto che dare un risultato sbagliato, seppure di pochissimo.
Ho seguito un consiglio di Leo in questo Topic e sono riuscito ad ottenere il risultato desiderato ma la seconda cifra decimale mi mostra costantemente 0 invece di sparire, come credevo dovesse essere.
Ho scovato e testato le funzioni floor(x) e ceil(x), usandole al posto dell'int, ma non cambia nulla.
Vorrei evitare di trasformare il numero in stringa per scartare questo 0, ma la soluzione mi sembra davvero misera e quindi preferirei qualcosa di tecnicamente più valido.
Inoltre vorrei capire perché il float mi da come risultato fisso 2 decimali mentre potrebbe darne molti di più. Purtroppo non ho da mostrarvi le poche righe di codice perché le ho scritte ieri in lab e le ho lasciate lì, ma non ho fatto altro che trasformare in codice i passaggi di Leo. Per la conversione DAC ho usato la classica formula Lettura5/102410.0; la moltiplicazione x10 mi serve per compensare la riduzione operata dal partitore in ingresso, che è appunto 10:1.

Quello che da sempre due cifre decimali di default è il metodo print, puoi modificarli aggiungendo il numero di decimali, p.e. serial.print("123.123",1).
Con i display ti conviene usare la sprintf() che ti permette di creare una stringa da visualizzare/stampare formattata come ti pare.

La soluzione per il serial.print l'avevo vista ma a me non serve, invece andrò a guardarmi questa sprintf() che non ho mai usato. Grazie!

E "floor" e "ceil"? Non possono fare anche al caso tuo?

As_Needed:
E "floor" e "ceil"? Non possono fare anche al caso tuo?

Mi autoquoto.....

Ho scovato e testato le funzioni floor(x) e ceil(x), usandole al posto dell'int, ma non cambia nulla.

come ha chiarito Astro il problema è che il print ha due decimali di default, ma mentre il serial.print prevede un parametro col quale si imposta il numero di decimali su lcd.print lo stesso parametro serve per impostare il tipo di contenuto da stampare (decimale, binario, ecc.).
Sto cercando qualcosa che spieghi in modo semplice questa funzione sprintf, che fa certamente al caso mio, ma trovo solo esempi che in questo momento non posso testare per decifrare il suo comportamento.
Grazie.

Michele guarda che la LCD.print() funziona come la Serial.print() ... quindi se specifichi il numero di decimali ti visualizza solo quelli (ho appena finito di mettere a punto un programma che visualizza 3 valori con 1 solo decimale sul LCD) :smiley:

Guglielmo

La sprintf() è la stessa cosa della printf() del C/C++ con la differenza che invece di inviare sulla periferica out standard, nel caso di Arduino la seriale, pone il risultato in array di char.
Esempio pratico per il tuo caso, ipotizziamo di voler scrivere su un display con righe da 20 caratteri.
Come prima cosa è necessario creare un array di char, può essere sia locale che globale, con una dimensione pari alla lunghezza massima di caratteri da inviare più uno per il terminatore 0x00, conviene sempre creare un arrary con dimensione pari alla riga del display, p.e. "char str[20]", rammento che usare 20 come dimensione di un array vuol dire in totale 21 locazioni in quanto la prima è la numero 0.
Una volta preparato l'array per inserirci i dati da visualizzare sul display usiamo la sprintf(), il suo prototipo è: "int sprintf(char *str, const char *format, ...)", il valore ritornato è il numero di caratteri inseriti nell'array, se vale -1 l'operazione non è andata a buon fine, "char *str" è il puntatore all'array in cui vanno posti i dati, basta inserire il suo nome, "const char *format" è una stringa descrittore come deve essere formattata la stampa, può contenere sia del testo da stampare che delle variabili.
Nel tuo caso ti serve una cosa come questa:

char str[20];
float MyVal = 123.123;


sprintf(str, "valore = %7.1f", MyVal);

"valore = " è la parte di testo da visualizzare, "%f" dice dove inserire la variabile float, 7.1 dice quanti caratteri usare per tutto il campo e quanti per la parte decimale, nel caso specifico 7 caratteri totali, incluso il punto, e un carattere per i decimali.
E' possibile far inserire in automatico degli zeri o degli spazi, ai numeri, se si vuole ottenere una formattazione a lunghezza costante, per gli zero basta aggiungere "0" prima del numero di caratteri, p.e. "%07.1f".

Oltre alla sprintf

Puoi provare con una funzione di arrotondamento (vale solo per X > 0):

float Round(float X, byte Digit) {

float Val = 10 ^ Digit;  // 0 = 1, 1 = 10, 2 = 100, ...

return int(X * Val + 0.5) / Val;
}

gpb01:
Michele guarda che la LCD.print() funziona come la Serial.print() ... quindi se specifichi il numero di decimali ti visualizza solo quelli (ho appena finito di mettere a punto un programma che visualizza 3 valori con 1 solo decimale sul LCD) :smiley:

Guglielmo

io ieri ho provato qualcosa tipo lcd.print(valore, 2) e me lo ha sparato in binario se non ricordo male :slight_smile: , io qui non lo trovo il metodo, che sintassi hai usato?

Ciao Cyb, grazie per il link. Non ho ben capito l'esempio che mi hai postato. li vedo solo numeri interi, la successiva divisione per un float non mi produce nuovamente due decimali invece di uno?

Hai ragione Michele ... :confused:
... difatti io uso un'altra libreria per gli LCD con l'adattatore I2C basato su MCP23008, la LiquidTWI :smiley: ... che mi permette di scrivere : lcd.print(fTemp, 1) dove la sintassi è esattamente la stessa della Serial.print().

Guglielmo

P.S:: Mi rifiuto di impegnare più du DUE pin per un display ! :grin:

La print() dovrebbe funzionare allo stesso modo ovunque, visto che di solito è implementata ereditando dalla classe Print e tutti i metodi di stampa sono definiti da quest'ultima.

Se ti ha stampato il numero in binario è perché le hai passato un intero: in tal caso infatti il secondo parametro è la base da utilizzare. Se le passi un float invece il secondo parametro rappresenta il numero di cifre decimali da utilizzare. Riprova e vedrai che è così :).

PS: Mai usare sprintf(), al limite snprintf()! Comunque entrambe richiedono parecchie risorse, meglio evitare per quanto possibile. La libreria PString a volte può tornare utile.

SukkoPera:
La print() dovrebbe funzionare allo stesso modo ovunque, visto che di solito è implementata ereditando dalla classe Print e tutti i metodi di stampa sono definiti da quest'ultima.

Anche io credevo ...
... però dai un'occhiata a QUESTA pagina ... paragrafo "Syntax" ... ::slight_smile:

Ripeto, NON la uso e quindi NON posso verificare, la LiquidTWI che uso si comporta esattamente come la Serial.print() (... come giustamente deve essere).

Guglielmo

Si infatti sono andato a leggere quella pagina quando mi ha dato il binario. D'altra parte non posso giurare che in quel momento stessi usando un float o un intero, quindi farò la verifica. Comunque mi sa che alla fine quasi quasi convenga implementare un algoritmo di arrotondamento e lasciare le due cifre. Non pensavo fosse così strana la mia necessità. Sto usando un display 8x2 e ho un intero 328p a disposizione. Non mi serve a niente aggiungere un adattatore I2C.

Secondo me è imprecisa la documentazione. Se guardate il sorgente qua, vedete che anche la classe LiquidCrystal eredita da Print, e che Print funzioni in quel modo sono ragionevolmente sicuro, avendola usata ancora ieri per stampare delle coordinate GPS con 6 cifre decimali.

Consiglio a Michele di riprovare con la certezza di avere un float. Se poi non va come deve vediamo eventualmente perché.

SukkoPera:
Secondo me è imprecisa la documentazione. Se guardate il sorgente qua, vedete che anche la classe LiquidCrystal eredita da Print, e che Print funzioni in quel modo sono ragionevolmente sicuro ....

In effetti ... mi sa che è errata la documentazione.

Miché ... prova co' 'sto float e facce sape' :smiley: :grin: :smiley: :grin:

Guglielmo

Certamente! Ti faccio sapere appena passo dal lab. Grazie per ora.

...vedo solo numeri interi, la successiva divisione per un float non mi produce nuovamente due decimali invece di uno?

Premetto che non avevo provato ed ho dovuto fare qualche correzione.

La funzione restituisce un float in quanto è la divisione di un intero per un float.

Se la esegui questo skecth vedrai che il sistema funziona.

void setup() {
  Serial.begin(9600);
}

void loop() {
 
 float X = 12.45678;
 
 Serial.print(X, 4);
 Serial.print("\t");
 Serial.print(Round(X, 1), 4);
 Serial.print("\t");
 Serial.print(Round(X, 2), 4);
 Serial.println();
}

float Round(float X, byte Decimal) {
  float Val = 1;
  for(byte I = 0; I < Decimal; I ++) {
    Val = Val * 10;
  }
 Serial.print(Val);
 Serial.print("\t");
  return int(X * Val + 0.5) / Val; 
}

Grazie Cyb, ma secondo te sostituendo serial.print con lcd.print funziona ugualmente? Inoltre il risultatoche ottieni è con un solo decimale?

Nel mio sketch ho fatto l'esempio del numero float X = 12.45678.

L'istruzione Serial.print(X, 4); restituisce 12.4568, quindi questa istruzione effettua un arrotondamento alla quarta cifra decimale.

La funzione Round(X, 1) restituisce un float 12.5, mentre la Round(X, 2) restituisce 12.46.

Alcune librerie LCD operano per il print nello stesso modo della Serial, ma, se non ricordo male, non tutte.