Lettura pin analogico

Ciao a tutti,
sono nuovo del forum. Spero di non aver sbagliato sezione,nel caso vi chiedo scusa.
Nei miei primi esperimenti, sto cercando di utilizzare un sensore di temperatura lm35dz.
Il sensore, a me sembra funzionare egregiamente, ma al momento di applicare la formula per la stima del voltaggio sul pin analogico trovo una certa difformità.
La formula che applico è quella che si trova comunemente su internet.
milliVolts= ( sensor/1023)*5000;

Tuttavia questa tensione risulta essere sensibilmente inferiore a quella misurata con il multimetro, misurata sul piedino di uscita Vout che è perfettamente in linea e proporzionata ai 500mV a 25°C.
Per ottenere un valore in linea con quello misurato con il multimetro (temperatura ambientale nell'istante di misurazione) devo applicare un piccolo coefficiente moltiplicativo.

Sapete darmi un'indicazione su eventuali errori che potrei star compiendo? Esiste una funzione che permette direttamente di leggere i millVolts forniti a un pin analogico?

Grazie infinite. Colgo l'occasione per salutare tutti i partecipanti al forum.

Lorenzo

L'errore è assumere che il fondo scala dell'ADC sia esattamente 5.00 v. Usa il secret voltmeter (Googla) per avere il valore preciso.

Grazie mille,
In effetti era qualcosa che avevo sospettato.
La tensione erogata da Arduino è 5.06 e a me veniva come fattore di correzione 5.39V.
Grazie per la dritta. Mi documento e ti faccio sapere.
Grazie mille ancora.
Lorenzo

Se hai un Arduino UNo usa la tensione di riferimento interno da 1,1V; se ahi un Micro o leonardo usa la tensione interna da 2,54V. avrai una lettura piú precisa perché hai un finescala piú grande.

Ciao Uwe

codabat:
Ciao a tutti,
sono nuovo del forum. Spero di non aver sbagliato sezione,nel caso vi chiedo scusa.

Ti invitiamo a presentarti (dicci quali conoscenze hai di elettronica e di programmazione) qui: Presentazioni
e a leggere il regolamento se non lo hai già fatto: Regolamento
Qui una serie di link utili, NON inerenti al tuo problema:

Ciao a tutti,
vi ringrazio per le pronte risposte e per le indicazioni che mi avete offerto.
Ma devo dire che non mi è chiaro ancora l'approccio che devo usare per risolvere la problematica che sto riscontrando e che vi ho illustrato.
Mi sono documentato leggendo la documentazione di riferimento che mi avete fornito, ma ancora non mi è chiaro l'approccio che devo tenere.

Mi pare di intuire che il problema sia legato alla tensione di riferimento utilizzata da Arduino (io sto utilizzando Arduino UNO).

Ho letto l'articolo "Secret voltometer", ma da quando desumo, quanto scritto consente di ottenere un valore della tensione di riferimento in quel momento detenuta dal microcontrollore e verificare come e quanto sia alimentata in quel momento la scheda. Ma non riesco a comprendere come posso risolvere l'errata lettura della tensione erogata a un pin analogico.

L'errore di lettura lo rilevato eseguendo un piccolo esercizio di utilizzo del sensore di temperatura LM35DZ. Come scrivevo la tensione del Vout del sensore, rilevata con un multimetro, è perfettamente in linea con la temperatura ambientale rilevata con altri termometri piuttosto precisi di cui sono in possesso. Ma al di là della precisione dei termometri non riesco a capire come mai il valore letto (derivato) leggendo il pin analogico, che nel mio caso è il pin A0, sia inferiore di non poco.

Premesso che la tensione di alimentazione ottenuta dalla porta USB è di 5.06V eseguo il seguente calcolo:

vPin = analogPin(A0)*(5.06/1023) ottenendo una tensione stimata di 252mV.

analogPin(A0) = 51

Misurando con il multimetro la tensione presente sul Vout del sensore ottengo 267mV, assolutamente in linea con la temperatura ambientale presente nel momento della misura.

Preciso che le misure vengono effettuare nel codice campionando 10 misure intervallate da 600 millisecondi e effettuando la media.

Come potete vedere dal codice di seguito illustrato per ottenere valori "corretti" devo utilizzare un valore di 5,29 anziché di 5.

float tempC;      //dichiarazione di variabile
int tempPin = A0;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); //apertura porta seriale, set della velocità a 9600 bps
  //analogReference(EXTERNAL);
}

void loop() {
  // put your main code here, to run repeatedly:
int counter = 0;
float sumTemp = 0;
float avgTemp = 0;
float sumVoltage = 0;
float avgVoltage = 0; 
int sensorValue = 0;
float avgSensorValue = 0;
int sumSensorValue = 0;
Serial.print(readVcc());
Serial.print("Sensor Value: ");
Serial.print(analogRead(tempPin)); 
Serial.println();
for(int i=0;i<100;i++){
  counter++;
  sensorValue = analogRead(tempPin);           //lettura valore del sensore
  sumVoltage = sumVoltage + (sensorValue * (5.29 / 1023.0));
  tempC = (5.29 * sensorValue * 100.0)/1023.0;  //conversione del dato analogico in temperatura
  sumTemp = sumTemp+tempC;
  sumSensorValue = sumSensorValue + sensorValue;
  delay(600);  
}
avgTemp = sumTemp/counter;
avgVoltage = sumVoltage / counter;
avgSensorValue = sumSensorValue / counter;
Serial.print("Avg. Voltage: ");
Serial.print(avgVoltage,4);
Serial.print(" T C: ");
Serial.print(avgTemp);
Serial.print(" Sensor Value avg: ");
Serial.print(avgSensorValue,4);
Serial.print(" T C: ");
Serial.print(avgSensorValue*0.506, 4);
Serial.println();
              
}

long readVcc() {
  // Read 1.1V reference against AVcc
  // set the reference to Vcc and the measurement to the internal 1.1V reference
  #if defined(__AVR_ATmega32U4__) || defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
    ADMUX = _BV(REFS0) | _BV(MUX4) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #elif defined (__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
    ADMUX = _BV(MUX5) | _BV(MUX0);
  #elif defined (__AVR_ATtiny25__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
    ADMUX = _BV(MUX3) | _BV(MUX2);
  #else
    ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1);
  #endif  

  delay(2); // Wait for Vref to settle
  ADCSRA |= _BV(ADSC); // Start conversion
  while (bit_is_set(ADCSRA,ADSC)); // measuring

  uint8_t low  = ADCL; // must read ADCL first - it then locks ADCH  
  uint8_t high = ADCH; // unlocks both

  long result = (high<<8) | low;

  result = 1125300L / result; // Calculate Vcc (in mV); 1125300 = 1.1*1023*1000
  return result; // Vcc in millivolts
}

Leggendo la documentazione mi sembra di capire che per letture precise sia necessario fornire una tensione di riferimento precisa con il pin AREF o alimentando con una tensione maggiore di 5V in modo da fare intervenire lo stabilizzatore.

Vi chiedo scusa per l'argomento banale e/o già trattato ma non comprendo la natura di un errore così macroscopico.

Ringrazio d'avvero tutti.

Lorenzo

La lettura di un pin analogico e' basata sulla tensione presente sull'ingresso VREF della MCU ... se sul VREF hai 5.000V, allora il fondoscala dell'ADC sara' di 5.000V, e la lettura sara' di 1/1023 di quel valore per ogni step ... ovviamente, se il valore di VREF fluttua o non e' preciso, avrai per forza una lettura non precisa ...

Una possibile soluzione e' quella di usare un riferimento di tensione esterno, da applicare sul VREF, in modo che il riferimento dell'ADC sia indipendente dall'alimentazione o dal riferimento interno (che non e' mai precisissimo) ... si puo usare, ad esempio, un TL431A, o equivalente, un'itegratino a 3 pin che fa da regolatore shunt programmabile ... impostandolo, ad esempio, su 4.096V, si ottiene una lettura di 0,004V per ogni step (0.0040093, ma non esageriamo con la precisione :P) ... che mi sembra abbastanza precisa, per la maggior parte delle applicazioni :wink: ... oppure usare 2.56V, per avere 0.0025V per ogni step ...

Ovviamente, poi bisogna fare in modo che il partitore con cui si effettua la lettura suddivida la tensione in modo da avere un fondoscala di 4V (o di 2.5V, o della tensione che si sceglie di usare come riferimento), invece che di 5V, ma quello e' il minore dei problemi ...

Semplicemente, il valore che leggi col secret voltmeter usalo al posto di 5000 nella formula di conversione della tensione in temperatura.

E non assumere che ad Arduino arrivino 5.06V, ci sono dei diodi di mezzo!

Grazie SukkoPera,
il fatto è che se uso quel valore (sercet voltometer), il valore risulterebbe ulteriormente sotto stimato, portandomi ad un errore sulla stima di della temperatura anche di 2°C...
Sono un po' confuso...
:o
Grazie

Etemenanki:
La lettura di un pin analogico e' basata sulla tensione presente sull'ingresso VREF della MCU ... se sul VREF hai 5.000V, allora il fondoscala dell'ADC sara' di 5.000V, e la lettura sara' di 1/1023 di quel valore per ogni step ... ovviamente, se il valore di VREF fluttua o non e' preciso, avrai per forza una lettura non precisa ...

Una possibile soluzione e' quella di usare un riferimento di tensione esterno, da applicare sul VREF, in modo che il riferimento dell'ADC sia indipendente dall'alimentazione o dal riferimento interno (che non e' mai precisissimo) ... si puo usare, ad esempio, un TL431A, o equivalente, un'itegratino a 3 pin che fa da regolatore shunt programmabile ... impostandolo, ad esempio, su 4.096V, si ottiene una lettura di 0,004V per ogni step (0.0040093, ma non esageriamo con la precisione :P) ... che mi sembra abbastanza precisa, per la maggior parte delle applicazioni :wink: ... oppure usare 2.56V, per avere 0.0025V per ogni step ...

Ovviamente, poi bisogna fare in modo che il partitore con cui si effettua la lettura suddivida la tensione in modo da avere un fondoscala di 4V (o di 2.5V, o della tensione che si sceglie di usare come riferimento), invece che di 5V, ma quello e' il minore dei problemi ...

Ciao,
mi sembrano interessanti le tue considerazioni e mi fanno sorgere alcune domande. Perdonami se le mie osservazioni sono inesatte e/o imprecise.
L'errore che ottengo potrebbe essere dovuto al rumore di fondo dell'alimentazione proveniente dalla porta USB? L'alimentazione proveniente dal cavo USB viene stabilizzata da ARDUINO (nel caso mio Arduino UNO)? Se alimentassi Arduino dal jack con un alimentatore a 7V (non stabilizzato) otterrei una tensione stabilizzata e questo potrebbe risolvere la questione sull'errore di stima della tensione del pin vout del sensore?

In alternativa potrei alimentare il VREF con il pin a 3.3V? Quest'ultima è una corrente stabilizzata?

In questo caso potrei usare:

int voltege = map(analogValue, 0, 1023, 0, 3300);

Grazie

Si, si puo anche usare il pin 3V3 per alimentarci il VREF, ovviamente in questo caso leggera' solo da o a 3.3V, quindi il partitore di tensione che usi sull'ingresso analogico andra' ricalcolato per quel valore ... per l'USB non saprei, ma potrebbe anche essere, non tutte le USB danno in uscita esattamente 5V, e basta un cavetto con un connettore un po ossidato o "lento" per aggiungerci sopra disturbi ... alimentandolo attraverso il regolatore di sicuro sarebbe un po piu pulito ...

Nel calcolo continuate ad usare una costante errata, l'ADC ha 10 bit, quindi 1024 step, non 1023
Nel datasheet c'è questa formula per calcolare il valore letto
ADC = (Vin*1024) / Vref

Quindi Vin = (ADC/1024) * Vref
oppure Vin = ADC * Vref / 1024

Per completezza sono andato a ricercare un vecchio post di @Astrobeed Link

Brunello:
Nel calcolo continuate ad usare una costante errata, l'ADC ha 10 bit, quindi 1024 step, non 1023
Nel datasheet c'è questa formula per calcolare il valore letto
ADC = (Vin*1024) / Vref

Quindi Vin = (ADC/1024) * Vref
oppure Vin = ADC * Vref / 1024

Per completezza sono andato a ricercare un vecchio post di @Astrobeed Link

Grazie, scusami ho corretto come giustamente hai indicato.
In effetti alimentando il Vref con la tensione a 3.3V ottengo un miglioramento sulla stima ma ancora c'è un certo errore.
Misurando con il multimetro il Vout dell'LM35dz è 280mV coerente con il mio termometro fisso che segna (28°C) mentre io leggo 268 mV (26,8°C).

Non capisco bene quale possa essere il problema... Potrebbe essere utile alimentare la VRef con un tensione ancora inferiore?

Sto ancora alimentando Arduino con la usb fornita nello starter kit. Verificando la tensione erogata dal pin 3.3V ottengo effettivamente una tensione 3.30V esatti...

Qual può essere la fonte di questo errore?
Ho collegato il pin 3.3V al VRef con un ponticello.

Grazie a tutti.

Lorenzo

Hai provato a fare più letture e una media dei risultati?

Sì caro,
eseguo questo codice:

int tempPin = A0;
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600); //apertura porta seriale, set della velocità a 9600 bps
  analogReference(EXTERNAL);
}

void loop() {
  // put your main code here, to run repeatedly:
  int sumV = 0;
  float avgV = 0;
  int counter = 0;
  for(int i=0;i<10;i++){
    counter++;
     int adc = analogRead(tempPin);
     int voltage = map(adc,0,1024,0,3300);
     sumV = sumV + voltage;
     delay(600);
  }
  avgV = sumV/counter;
  
  Serial.print(" Voltage average: ");
  Serial.print(avgV);
  Serial.print(" Temperatura: ");
  Serial.print(avgV/10,2);
  Serial.println();
  Serial.println();

}

Quello ce volevo provare e implementare un metodo per fare entrare nella VRef una tensione di 1500mV che è il fondo scala del sensore al 150°C

Lo hai messo un piccolo condensatore da 0.1µF tra il pin analogico e il GND ?

A dire il vero no...

Brunello:
Nel calcolo continuate ad usare una costante errata, l'ADC ha 10 bit, quindi 1024 step, non 1023
...

E' vero che la risoluzione a 10 bit da 1024 "passi" ... ma e' anche vero, se ci pensi bene, che dato che il passo "0" vale "0 volt", tu hai 1023 "intervalli" di tensione, in quei 1024 bit (il conteggio non va da 0 a 1024, ma da 0 a 1023), e non 1024 intervalli (altrimenti servirebbero 1025 "passi", zero incluso) ... su 1024 non risulta "naturale" accorgersene, ma fai una prova con un numero piu basso ed intuitivo ... ad esempio, considera 1 passo per volt da 0 a 5 volt, e ti rendi immediatamente conto che per avere tutti e 5 gli intervalli, ti servono 6 "passi", incluso lo zero (0-1-2-3-4-5), e non 5 come sembrerebbe logico (1-2-3-4-5) proprio perche' c'e' "anche" lo 0 ... il passo "0" non ha alcun valore, cioe' vale appunto 0 volt, mentre hanno un valore quelli da 1 a 1023, quindi la divisione corretta dovrebbe essere per 1023 :wink:

Ciao a tutti,
credo di aver risolto.
Ho impostato analogReference(INTERNAL), avendo dunque come tensione di riferimento 1.1V. COsì la risoluzione è di 1,07mV e Arduino legge esattamente la tensione che rilevo con il multimetro: diventando molto preciso e stabile nella lettura. Il campionamento è di dieci letture per un minuto, intervallate ogni 6 secondi e sostanzialmente la lettura coincide sempre con quanto misurato con il multimetro.

Il limite di questa soluzione è che la temperatura superiore leggibile sarà di 110°C anziché di 150°C, ma per i miei scopi è più che sufficiente. Utilizzando il condensatore tra Vout e GND otteno una piccola sovrastima e stranamente la lettura mi sembra meno stabile.

Come illustra bene Etemenanki, anch'io credo che sia corretto impostare 1023 intervalli.

Colgo l'occasione per ringraziarvi tutti per la vostra partecipazione e per i consigli le riflessioni che mi avete dato.

Lorenzo