LM35DZ - temperature sensor. Big differences while reading it

Good morning

I'm using a LM35DZ - temperature sensor

I'm using the following code from Temperature Sensor + Arduino - Daniel Andrade

Here is the code:

int pin = 1; // 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 = ( 5.0 * analogRead(pin) * 100.0) / 1024.0;
_ tempc = tempc + samples*;_
_
delay(1000);_
_
}_
tempc = tempc/8.0; // better precision
Serial.print(tempc,DEC);
Serial.println("º");
tempc = 0;
delay(1000); // delay before loop
_
}_
_
[/quote]*_
But it happens that I have great differences between reads...
17º
16º
16º
17º
15º
19º
17º
19º
16º
21º
19º
16º
16º
13º
16º
20º
21º
16º
20º
18º
18º
18º
What can I do?
The cable that I'm using, is a 2 meters shielded cable with 3 wires, the same cable used on encoders.
Please let me know
Thanks on advance
Pedro Ferrer

You must be the lucky one who actually received something from futurlec. :wink:

I would start with changing the tempc variable to type float. As it is now you accumulate innacuracy through each analog read iteration due to integer rounding.

To further improve analog sampling, it is typically always better to increase number of samples averaged. That is if your sketch has nothing else to do, you may as well keep sampling continuesly until next output is needed. The trick to achieve this would be to model your skecth after the blink without delay example, maintain a sample counter and then eliminate all calls to delay. When your time interval is reached you calculate the average, output the result and reset the sample counter and accumulator.

Hello BenF

You must be the lucky one who actually received something from futurlec.

No... actually I've bought on Portugal...

I've changed the var to float. You're right.

But the problem remains...

22.3750000000º
14.8750000000º
18.7500000000º
16.7500000000º
23.2500000000º
19.8750000000º
21.6250000000º
20.3750000000º
21.6250000000º
16.3750000000º
19.7500000000º
20.2500000000º
18.7500000000º
16.7500000000º
18.3750000000º

What can I do? Put a pull-down resistor or else?

Please let me know
Thanks on advance
Best regards
Pedro Ferrer

The cable that I'm using, is a 2 meters shielded cable with 3 wires, the same cable used on encoders.

If you use the same sensor with short connections do you see the same behavior?

Instead of averaging the temperature you may use the median of 7 readings. The median is the middle value of a sorted array of readings.

Suppose you have 7 readings and 6 are 20C and one is 0C (misread).
Average = 6x20 +0 = 120 / 7 = 17.1C
Median = { 0,20,20,20,20,20,20 } = 20;

If the number of readings is even you should take the average of the middle 2; therefor an uneven number is easier to handle.

You can probably improve results somewhat using the internal Atmega 1V1 reference, but you may have to add an analog amplifier stage to get decent accuracy from this sensor. An unstable power supply may also be an issue. As it is now a single bit variation is equivalent to 0.5 degrees and this is likely to give you fluctuations.

You can try the 1V1 reference and calculate a running average (no delay's) from something like 4096 samples to see if a software approach is sufficient.

a running average (no delay's) from something like 4096 samples

How big should the window be? NB a running average only takes the last N readings into account... and keeping 4096 readings in memory might be too much for Arduino.

Or do you mean a 'normal' average?

robtillaart:

a running average (no delay's) from something like 4096 samples

How big should the window be? NB a running average only takes the last N readings into account... and keeping 4096 readings in memory might be too much for Arduino.

Or do you mean a 'normal' average?

I use this formula and keep it accumulating between outputs.

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

Wikipedia refers to this as cumulative running average.

Good night

Thanks for all the replies.
Hereby your posts, I've changed the code...

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

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

void loop()
{

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

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

Have the following values... still get out of interval 0.5º...

24.4140625000º
21.2402343750º
22.3388671875º
27.0385742187º
22.0947265625º
26.6113281250º
19.0429687500º
23.1323242187º
22.6440429687º
26.4282226562º
22.3999023437º
27.0385742187º
20.6298828125º
18.4936523437º
20.0195312500º
20.4467754364º
18.5546875000º
18.0664062500º
20.4467773437º
21.0571289062º
22.5830078125º
18.6767559051º

Please let me know
Thanks on advance
Best regards
Pedro Ferrer

THIS QUESTION CAPslock has been up a few times... You are not the only one with this problem. The general solution seems to be the one mentioned: take a number of samples and use the average. But as you have noticed, the average fluctuates as well :frowning:

You can get a little better resolution by using the 1.1 volt voltage reference which gives 1.1/1024 = ?? instead of the standard 5/1024 =?? volts resolution. This limits the upper temperature one can measure though.

I tried both solutions and was not satisfied.

I did try putting a resistor in series with the signal from the transmitter, and that seemed to calm the variation (but not good enough for my taste). Maybe a really small capacitor together with that ? (there are suggestions on "filtering" the signal in the sensors datasheet).

I decided that the ADC sucked... at least for directly measuring small variations in room temperature with an analog sensor like that.

One thing I did not try was use the ADC Noise Canceler function(did know about it then and it was to complicated for me at that time anyway). Basically it puts the processor in sleep mode to reduce noise from the cpu and I/O :s when it makes the conversion (page 256 in my datasheet for the processor). Thats not for beginners though (I think).

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?

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%,