LM35DZ - temperature sensor. Big differences while reading it

I would scope the output of the sensor. I will predict you will see a significant AC on the LM35 output signal, I have see this many times. The easiest way to solve it is to decouple it using a 0.1uf cap to gnd.

You're still averaging 8 samples. The difference comes from the count of samples, not the formula. The formula is needed to maintain mathematical precision across thousands of samples.

This is what I had in mind:

int sample_count;
float val;
float sample_avg;
float temp;

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

void loop()
{
  sample_count++;
  val = analogRead(1);
  sample_avg = sample_avg + (val - sample_avg) / sample_count;

  if (sample_count >= 4096){
    temp=(1.1 * sample_avg * 100.0) / 1024.0;
    Serial.print(temp,DEC);
    Serial.println("º");
    sample_count = 0;
    delay(1000); 
  }
}

Good morning

Thanks to all!

analogReference(INTERNAL);

not work on Mega (my case)... http://www.arduino.cc/en/Reference/AnalogReference

INTERNAL: an built-in reference, equal to 1.1 volts on the ATmega168 or ATmega328 and 2.56 volts on the ATmega8 (not available on the Arduino Mega)

instead use

INTERNAL1V1: a built-in 1.1V reference (Arduino Mega only)

At the moment I'm using the following code:

int sample_count; float val; float sample_avg; float temp;

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

void loop(){

sample_count++; val = analogRead(1); sample_avg = sample_avg + (val - sample_avg) / sample_count;

if (sample_count>=4096){ temp=(1.1 * sample_avg * 100.0) / 1024.0; Serial.print(temp,DEC); Serial.println("º"); sample_count=0; } }

with the following results:

21.7517642974º 21.8397789001º 21.6071357727º 21.6462440490º 21.9182052612º 21.5186367034º 21.4956054687º 21.5795688629º 21.7270050048º 21.7120819091º 21.8656406402º 21.7869682312º 21.7573280334º 21.3469219207º 21.6581382751º 21.6491451263º 21.8310565948º 21.8769569396º 21.6874866485º 21.4888286590º 21.8148403167º 21.6632823944º 21.9248332977º 21.7272529602º 21.5622024536º 21.6742362976º 21.9223327636º 21.7440147399º 21.7992439270º 21.8053760528º 21.6359863281º 21.6092872619º 21.6726818084º 21.7128028869º 21.6772994995º 21.7238197326º 21.5995445251º 21.6944599151º 21.9314517974º 21.9353237152º 21.6809310913º 21.6946468353º 21.7912731170º 21.7200946807º 21.8130283355º 21.7228450775º 21.6906337738º 21.7354259490º 21.8063049316º 21.7635898590º 21.6838798522º 21.8985519409º 21.7086772918º 21.8521080017º 21.8069458007º 21.6680202484º 21.5060596466º 21.6929054260º 21.6416168212º 21.6140098571º 21.6849155426º 21.6466236114º 21.7007369995º 21.8061904907º 21.6499042510º 21.6719341278º 21.7773628234º 21.6784534454º 21.8603801727º 21.8354701995º 21.7990589141º 21.7120246887º 21.7960605621º 21.5650711059º 21.8893489837º 21.6773605346º 21.9319019317º 21.7376194000º 21.7940521240º 21.7638874053º 21.8846740722º 21.6499366760º 21.6243877410º 21.6514186859º 21.5869674682º 21.5570831298º 21.7493019104º 21.7388210296º 21.7910137176º 21.6423130035º 21.6636428833º 21.4642677307º 21.5848770141º 21.8051662445º 21.9307136535º 21.5620422363º 21.5547714233º 21.5966053009º 21.6848735809º 21.4665508270º 21.5726337432º 21.5996551513º 21.7107944488º 21.6048946380º 21.6620063781º 21.6681022644º 21.6178131103º 21.3954982757º 21.4469547271º 21.7632446289º 21.6807022094º 21.7202568054º 21.7815170288º 21.6968841552º 21.7859973907º 21.6573352813º 21.6181240081º 21.7958011627º 21.4995384216º 21.8133258819º 21.7820625305º 21.9121074676º 21.7330188751º 21.8351535797º 21.8324012756º 21.6706733703º 21.6437759399º 21.7632808685º 21.7751846313º 21.6557731628º 22.0018768310º 21.6623477935º 21.4358425140º 21.4864177703º 21.8115024566º 21.6672458648º 21.7172889709º 21.7049694061º 21.5239562988º 21.7810649871º 21.6769676208º 21.7803306579º 21.7806873321º 21.6148014068º

Much, but much better now!!!

Now... I'm using the 1.1V reference... ok it works. But what will happen now the other analogue readings...like my LCD Buttons that enter on A0? http://www.dfrobot.com/index.php?route=product/product&path=53&product_id=51

Once again, thanks by your great help! Best regards Pedro Ferrer

wrt the code:

  • samplecount is not initialized to 0;
  • sample_avg is not initialized to 0.0 and not reset when samplecount is reset to 0

The formula used gives a different weigth to different readings,
sample_avg = sample_avg + (val - sample_avg) / sample_count;

Note: the first counts for ~10%, the second for 5%, the 3rd for ~3%, 4th for 2.5% while the last counts for ~ 0.01% (spreadsheet wisdom).
This means that the first 4 readings make up 20% of your avg , (11 → 30%; 50 → 50%) , so if there is a misreading in your first 4 readings it affects the final average quite much.

try the code below (closer to your original code) to determine the samples needed for a stable reading ; the output is comma separated so you can copy it into a spreadsheet and make a graph.

I expect that depending on the precision you will need between 50-500 samples iso 4096

int sample_count = 0;
float sample_sum = 0.0;
float sample_avg = 0.0;
float temp = 0.0;

void setup()
{
  Serial.begin(115200); // start serial communication
  analogReference(INTERNAL1V1);
}

void loop()
{
  sample_count++;
  sample_sum += analogRead(1);

  Serial.print(sample_count);
  Serial.print(", ");  // sep
  sample_avg = sample_sum / sample_count;
  temp=(1.1 * sample_avg * 100.0) / 1024.0;
  Serial.print(temp,DEC);

  if (sample_count >= 4096)
  {
    sample_count = 0;
    sample_sum = 0;
  }
}

robtillaart: wrt the code: - samplecount is not initialized to 0; - sample_avg is not initialized to 0.0 and not reset when samplecount is reset to 0

The formula used gives a different weigth to different readings, sample_avg = sample_avg + (val - sample_avg) / sample_count;

All static variables in C not explicitly initialized by user code will be initialized to zero. This is part of the C/C++ language definition. Explicit initialization does no harm, but will generate additional code and increase the size of your program.

There is no need to reset sample_avg as the formula assigns 100% weight to the difference once sample_count gets reset (that is sample_avg = val for the first sample).

A problem with the proposed change is that precision drops off as sample_sum increases to the extent that additional samples eventually carry no weight. Running a test with print for every sample delays time between samples and so is not representative for how you would run the final program and results may be quite different.

In short, I would leave experiments regarding sampling theory to a project in itself as it is not trivial to determine what is best.

As for reading button states, you will have to change analogReference prior to reading and then change back once finished. A single read of button state may be sufficient and so this could be done when you reset the sample counter for your LM35.

Hello

Thanks once again to all!

As for reading button states, you will have to change analogReference prior to reading and then change back once finished. A single read of button state may be sufficient and so this could be done when you reset the sample counter for your LM35.

I understand that. Do and Undo state to analogReference, no problem.

At the moment I'm using the following lines code on temp sketch. It seems to me that is stable.

int sample_count; float val; float sample_avg; float temp;

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

void loop() { sample_count++; val = analogRead(1); sample_avg = sample_avg + (val - sample_avg) / sample_count;

if (sample_count >= 4096){ temp=(1.1 * sample_avg * 100.0) / 1024.0; Serial.print(temp,3); Serial.println(" C"); sample_count = 0; delay(1000); } }

Thanks on advance Best regards Pedro Ferrer

All static variables in C not explicitly initialized by user code will be initialized to zero. This is part of the C/C++ language definition. Explicit initialization does no harm, but will generate additional code and increase the size of your program.

100% true, but it is no good coding practice. Always initializing vars prevents errors, as for local vars it is a must. Further if a platform does not proporly implements the language definition there is trouble.

There is no need to reset sample_avg as the formula assigns 100% weight to the difference once sample_count gets reset (that is sample_avg = val for the first sample).

Didn't see that, nice trick ...

A problem with the proposed change is that precision drops off as sample_sum increases to the extent that additional samples eventually carry no weight.

True (but only marginally for 4000 samples) if you use float for the sum as I proposed., the precision of a float is ~6 digits = 0-1.000.000; so if the sum exceeds this you loose significant digits, but these are lost anyway by the division by 4096. Better is to use a unsigned long to summerize the raw samples. In fact an unsigned long can hold minimal 2^32 / 2^10 = 2^22 = 4 M samples without loss of precision (until divided by samplecount)

Running a test with print for every sample delays time between samples and so is not representative for how you would run the final program and results may be quite different.

True, but if the variation in the individual samples is "big" as one can read in original post, the code I posted will give an indication of how much samples are needed for a certain precision and i'm quite confident (but could be wrong) that the number is lower than 4096. Imho it cannot be true that a sensor needs to be averaged over 4000 samples to become reliable.

In short, I would leave experiments regarding sampling theory to a project in itself as it is not trivial to determine what is best.

It allways depend on the project requirements and the sensor used.

A small test shows no loss of precision when using a float for 4096 samples.

void setup() 
{
  Serial.begin(115200);
  Serial.print("start");
}

unsigned long sumL = 0;;
float sumF = 0;
int t = 0;

void loop()
{
  unsigned long start = millis();
  for (int i = 0; i< 4096; i++)
  {
    t = 1024; //analogRead(A0);  // 1024 = max sample value + 1 !!
    sumL += t;
    sumF += t;
  }
  Serial.println(millis() - start);
  Serial.println(sumF);
  Serial.println(sumL); 
  sumL = 0;
  sumF = 0;
  while(1);
}

robtillaart: A small test shows no loss of precision when using a float for 4096 samples.

The loss of precision comes from float data type limitations when you add a high value (sum) to a small value (single sample). This is because floats are normalized and can only maintain precision with respect to its exponent. If you change sum to an integer type, you avoid this issue, but risk overflowing the sum variable sooner. Also if the analog integral stays close to zero (such as with a gyro and accelerometer), your proposal works fine.

What I proposed is often preferred when averaging runs continuously for the lifetime of the application (temperature is a good example). An initial test is done to determine a reasonable number of samples required for a target precision/accuracy. Once you reach this count (lets say 256), you simply stop incrementing sample_count so that each additional sample carries an equal weight. sample_count then becomes a means to control sensitivity to change. A high max count equals low sensitivity whereas a low max count will respond quickly to change.

The loss of precision comes from float data type limitations when you add a high value (sum) to a small value (single sample). This is because floats are normalized and can only maintain precision with respect to its exponent. If you change sum to an integer type, you avoid this issue, but risk overflowing the sum variable sooner.

I know the theory - including the risks, my sample sketch only shows that adding up 4096 AnalogRead() samples (as was proposed) does not show this effect in practice when using a float for ("normal") averaging.

What I proposed is often preferred when averaging runs continuously for the lifetime of the application (temperature is a good example). An initial test is done to determine a reasonable number of samples required for a target precision/accuracy. Once you reach this count (lets say 256), you simply stop incrementing sample_count so that each additional sample carries an equal weight. sample_count then becomes a means to control sensitivity to change. A high max count equals low sensitivity whereas a low max count will respond quickly to change.

Agree 100%,

Good morning

So... what do you suggest for a final sketch? I'm confuse...

Thanks on advance Best regards Pedro Ferrer

Something like this may be a start:

#define SAMPLE_SENSITIVITY 4096
#define TEMP_INTERVAL 1000

float sample_avg;

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

void sampleTemp()
{
  static int sample_count;
 
  if (sample_count < SAMPLE_SENSITIVITY) sample_count++;  
  sample_avg = sample_avg + (analogRead(1) - sample_avg) / sample_count;
}

void loop()
{
  static uint16_t  t1;
  uint16_t  t2 = (uint16_t)millis();

  sampleTemp();
  
  if ((t2 - t1) >= TEMP_INTERVAL) {
    float  temp = (1.1 * 100.0 / 1024.0) * sample_avg;
    Serial.print(temp, 1);
    Serial.println("°C");
    
    t1 += TEMP_INTERVAL;
  }
}

Hello

Thanks once again by all replies.

BenF, perhaps miss a reset to ‘sample_count’ and ‘sample_avg’ var…

void sampleTemp()
{
static int sample_count;

if (sample_count < SAMPLE_SENSITIVITY){
** sample_count++; **
** }**
** else**
** {**
sample_count=1; //to avoid divide by 0
** sample_avg=0;**
** }**
sample_avg = sample_avg + (analogRead(1) - sample_avg) / sample_count;
}

What do you think?

Thanks on advance
Best regards
Pedro Ferrer

Pedro,

the code should be good as is and reflects what was dicscussed (no divide by zero and neither count nor average should be reset).

More important though is that you're happy and shape it to work the way you think is best. :)

Good morning

Today, travelling around www, I've found that some analog values should be read with 1023 instead 1024... In this case, 1024 it's correct?

Thanks on advance Best regards Pedro Ferrer

Hi, I am not qualified to answer this, as a beginner, however the spec says that the analog input scale is 0-1023, and this is bourne out by my experiments. I divided 1023 by 5 to get a voltage reading, and got 0.0000000000-5.0000000000 in my output, but when using 1024/5 the result was 4.9951171875 so I think the results speak for themselves...

lowbyte

The range of values we get from the AtMega analog to digital converter (ADC) is 0 to 1023. This represents 1024 distinct levels between Gnd (0V) and Aref (5V). The best approximation to voltage is given by the following formula:

Vin = ADC * Aref / 1024

Vin - Voltage on the analog input pin ADC - The value we obtain from analogRead() Aref - Our voltage reference as set by analogReference()

Good morning

Thanks BenF. Understood.

Best regards Pedro Ferrer

Good afternoon

I’ve stop used the temperature sensor, since I have conflicts to other probes…
To read well the LM35DZ I have to use ‘analogReference(INTERNAL1V1)’… ok…
But when I try to read other probes… PH, ORP… I need 5V… than I use analogReference(DEFAULT)…
Literature says that on 1st reading, after changing analogReference, will have some strange values… but it seems is that the wrong values remains forever…

What do you suggest?

Thanks on advance
Best regards
Pedro Ferrer