Ottenere 1 o 2 cifre decimali nel code temperatura

Ciao a tutti…mi sto perdendo in un bicchier d’acqua!
Potreste aiutarmi con questo code:

int pin = 0; // analog pin
int tempc = 0,tempf=0; // temperature variables
int samples[8]; // variables to make a better precision
int maxi = -100,mini = 100; // to start max/min temperature
int i;

void setup()
{
Serial.begin(9600); // start serial communication
}

void loop()
{

for(i = 0;i< =7;i++){ // gets 8 samples of temperature

samples[i] = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;
tempc = tempc + samples[i];
delay(1000);

}

tempc = tempc/8.0; // better precision
tempf = (tempc * 9)/ 5 + 32; // converts to fahrenheit

if(tempc > maxi) {maxi = tempc;} // set max temperature
if(tempc < mini) {mini = tempc;} // set min temperature

Serial.print(tempc,DEC);
Serial.print(" Celsius, ");

Serial.print(tempf,DEC);
Serial.print(" fahrenheit -> “);

Serial.print(maxi,DEC);
Serial.print(” Max, “);
Serial.print(mini,DEC);
Serial.println(” Min”);

tempc = 0;

delay(1000); // delay before loop
}

Riesco ad ottenere delle letture di temperatura senza decimali tipo 27°C ma vorrei ottenere un valore con un decimale (meglio due) tipo 27.5°C…sto sbattendo la testa perché non sono molto pratico e non capisco dove devo modificare e cosa nel code…help?

Ciao Flavio,
qui ne parlano..
http://www.arduino.cc/playground/Code/PrintFloats

Ciao
Whole

questa è la mia funzione

void  floatToString(float inFloat, char *outString){

        *outString = 0;
  
      char temp[5]="";
      int frac;
      float roundingFactor = 0.05;
      int mult = 10;

      if(inFloat < 0.0){
            strcat(outString,"-");
            inFloat= -inFloat;
      }

      inFloat = inFloat + roundingFactor;

      strcat(outString, itoa(int(inFloat),temp,10));  //prints the int part

      strcat(outString, "."); // print the decimal point

      frac = (inFloat- int(inFloat)) * mult;

      strcat(outString,itoa(frac,temp,10));
       
      return;
  
}

due cifre dopo la virgola e arrotondamento a 0.05

ciao

Dalla release 18 di Arduino i metodi print()/println() della classe Print, consentono ora di specificare (nel secondo parametro di ingresso), il numero di cifre dopo la virgola ovviamente per i numeri in virgola mobile. Se si lavora su release precedenti (e non si vuole fare l'upgrade alla 18) si possono sempre copiare i nuovi file di implementazione e interfaccia: Print.cpp e Print.h della release 18 sull'installazione precedente:

http://arduino.googlecode.com/svn/trunk/hardware/arduino/cores/arduino/Print.cpp

http://arduino.googlecode.com/svn/trunk/hardware/arduino/cores/arduino/Print.h

Personalmente, ho seguito questa strada sulla mia release 17 (il path della 17 è ./hardware/cores/arduino) e tutto ha funzionato perfettamente.

Esempio:

lcd.print( temp, 1 );     // stampa la var.float 'temp' su lcd con una cifra decimale

si ho usato anche io quel codice per il LM35DZ.
datasheet http://www.national.com/mpf/LM/LM35.html#Overview

devi analizzare la conversione , che restituisce il valore intero:
temperatura = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;

pin è il pin del termometro.
non ho controllato cosa arriva dall'analogRead e perchè fa quella conversione (dato che dovrebbe già arrivare in celsius), appena posso controllo cosa viene letto puro. :slight_smile:

ciao

bella questa implementazione alleggerisco il mio codice di qualche byte...

ops scusate ma pensavo che avesse bisogno di convertire una stringa in un numero float

la stampa su seriale di un float si fa come ha indicato @adalgisio ad esempio Serial.print(numero, 2) dove numero è un float e 2 sono i numero che voglio cisualizzare dopo la virgola

la formula che hai scritto nel codice sembra quella che si usa per leggere la temperatura da un lm35....se stai usando quello, sappi che ha una precisione di mezzo grado centigrado, quindi ha poco senso leggere la seconda cifra decimale

allora vi posto i valori del lm35 che invia al pin con la temperatura (c) convertita con quella formula (sia int che float, domanda da niubbo che tipo di variabile arriva da un input analogico??)

ora fa 27
57.00
ora fa 31
58.00
ora fa 31
58.00
ora fa 31
58.00
ora fa 31
58.00
ora fa 33
63.00
ora fa 35
64.00
ora fa 36
66.00
ora fa 36
67.00
ora fa 36
67.00
ora fa 36
67.00
ora fa 36
66.00

analogRead(pin) restituisce un numero compreso fra 0 e 1023, quindi basta una variabile di tipo 'int' per memorizzarlo:

http://www.arduino.cc/en/Reference/AnalogRead

se vuoi la temperatura con le cifre decimali, devi definire la variabile di tipo a virgola mobile (es. float)

Esempio

float temperatura; 
int pin=5;

.... ....   ....

temperatura = 5.0 * analogRead(pin) * 100.0 / 1024.0;

Puoi risparmiare un array in questo modo:

tempc = 0;
for(unsigned int i=0; i<8; ++i) { // gets 8 samples of temperature
  tempc += ( 5.0 * analogRead(pin) * 100.0) / 1024.0;
  delay(1000);
}

Se vuoi avere la massima precisione ed una lettura sufficientemente stabile con l'LM35 ti consiglio di riferire la conversione A/D a 3.3V (e non più a 5V), e per far questo nel setup metterai:

** //impostazione Convertitore AD con riferimento Vcc esterno (AREF collegato a 3.3V)**
** ADMUX &= B00111111;**

la lettura della sonda la otterrai così

** int sonda;**
__ sonda = (float)((float)(analogRead(1)) * 32.2265625L) + 100; //legge temperatura in gradi C * 100__

per ottenere la temperatura con 2 decimali sarà sufficiente:

** float temperatura;**
** temperatura = (float)(sonda) / 100.00;**

Per chi non conosce significato di questa sintassi: (float)(sonda) la spiego brevemente:

Per non avere inutili sprechi di memoria utilizzando sempre il formato maggiore, il C non converte automaticamente i valori tra un formato e l'altro (come avviene in linguaggi di maggior livello), e per farlo si usa appunto il costrutto cast che prevede di mettere tra parentesi il formato desiderato davanti alla variabile da convertire, ovvero:

se sonda è un int, perchè il calcolo sonda / 100 non si mangi i decimali (in quanto viene trattata come divisione tra interi) si antepone (folat)sonda che fa prima convertire sonda in float, e poi si fa eseguire la divisione utilizzando l'indicazione dei decimali nella costante del dividendo così che la divisione sia effettuata tra due numeri in virgola mobile.

oppure può usare il riferimento interno di 1,1V (Arduino con a bordo ATmega168 o ATmega328) ... a meno che non abbia necessità di misurare temperature superiori a 110 °C :slight_smile:

grazie a tutti, post utilissimi per capirne di più.
@kokiua: grazie della spiegazione di sintassi

oppure può usare il riferimento interno di 1,1V

che cosa significa?
so che qualcuno usa una proprietà dell'atmega di misurare la temp, è questa? come si sfrutta?

il riferimento interno (come specificato nel datasheet) è simile a quello che ti ho indicato con la differenza che la conversione avviene solo tra 0V e 1.1V, da li il fatto di poter leggere solo un range minimo di temperature a meno che tu non riduca l’uscita del sensore per rientrare in quel range, ma vorrebbe dire sacrificare la precisione e aumentare di molto l’incidenza dei disturbi.

La lettura di temperatura dall’interno dell’ATMega è possibile secondo le specifiche sul datasheet al cap. 23.8 pag. 262 (vedi qui: http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf) ma tieni conto che leggi la temperatura del processore che può essere influenzato da quella dell’ambiente, ma se lui stesso scalda (per troppo carico sulle uscite) la lettura è evidentemente falsata.
Diciamo che è un servizio utile se vuoi controllare che non ti si scaldi troppo, ma non per sostituire un sensore esterno.

io non ho capito perchè è più pracisa la lettura utilizzando il riferimento a 3.3V rispetto a 5V

allora non ho capito questo passaggio

ti consiglio di riferire la conversione A/D a 3.3V (e non più a 5V), e per far questo nel setup metterai:
//impostazione Convertitore AD con riferimento Vcc esterno (AREF collegato a 3.3V)
ADMUX &= B00111111;

ho un po' di lacune.
mi serve un convertitore AD che mi porti la tensione a 3.3?
o uso un piedino digitale di arduino?
AREF?
ADMUX è riferito a un pin?

@stefanosky
se non hai tensioni oltre gli 1,1V, il che corrisponde a 110 °C massimi se usi l'LM35 in configurazione "liscia"...il riferimento interno ti assicura una migliore risoluzione e stabilità rispetto alla tensione di alimentazione di 5V.

Se vuoi usare il riferimento interno devi aggiungere queste due righe nella tua setup():

void setup() {
   analogReference( INTERNAL );  
   analogRead( pin );
   ...
}

e ovviamente usare nella "formula di ricostruzione" 1.1 invece di 5.0:

temperatura = 100.0 * analogRead(pin) * 1.1 / 1024.0

@Nyo: la precisione è maggiore se consideri che il sensore alimentato a 5V ti da una lettura con la temperatura massima rilevabile (150°C) attorno ai 3V 2V, così se si fa in modo che il convertitore analogico/digitale rilevi l'intera scala di 1024 punti tra 0 e 3.3V invece che tra 0 e 5V, si ottiene una precisione maggiore.. ok?

@stefanoscy:

ho un po' di lacune.
mi serve un convertitore AD che mi porti la tensione a 3.3?
o uso un piedino digitale di arduino?
AREF?
ADMUX è riferito a un pin?

Tutto quello che serve è già presente in Arduino!
La tensione di 3.3V la trovi nel connettore dove ci sono anche i 5V.
L'ingresso AREF lo trovi nel connettore dal lato opposto (è l'ultimo dal lato della USB).
Devi collegare AREF con il 3.3V, quindi attivare il riferimento per il convertitore A/D (ovvero l'AREF) nel codice utilizzando l'istruzione di modifica del registri interno al processore (ADMUX), ovvero:

ADMUX &= B00111111;

tutto chiaro? ::slight_smile:

Errata corrige: il valore massimo del sensore nel full range (-55°C +150°C) è attorno a 2V e non a 3 come ho riportato sopra per errore di battitura.. :wink: Per l'esattezza il valore massimo previsto da datasheet è 2,05V

@adalgiso:

il riferimento interno ti assicura una migliore risoluzione e stabilità rispetto alla tensione di alimentazione di 5V.

Cosa ti fa pensare che questo sia vero? :-?
E poi: perchè mai utilizzare un valore di lettura massima nella conversione A/D inferiore a quella massima data dal sensore dovrebbe darti risultati migliori?
Ridurre la "finestra" di conversione entro i limiti dati dal segnale che emette il sensore ha un senso, al di sotto o al di sopra non mi sembra... o no?