Unstable analog read

I am using this code to read from a TMP36 temperature sensor.

The code gets a reading from it, but it jumps up and down with something that looks like 4c in total. Suddenly it can then go from 25C down to 16-17C, then back up to 23C-27C again, and continue to jump around.

When I monitor the voltage from the sensor, using both a multimeter and oscilloscope, it does not seem to flicker as much as the reading from my Imp indicates. Connecting it to a Spark Core also returns a stable (around 0.5c deviation) reading. Even when connecting the sensor to my Electric Imp and Spark Core at the same time gives a stable reading on the Core, but not on the Imp.

I got a capacitor between the output pin and 3v3 to stabilize the output too, but that only seems to work for the Spark Core.

Any idea what else I can try?

`hardware.pin9.configure(ANALOG_IN);
function getTemp()
{
// get the raw voltage value from temp sensor (0-65535)
local reading = hardware.pin9.read();

// needs mapping to the the voltage we are getting from the regulator
local voltage = reading * hardware.voltage();

// divide by the raw value range
voltage /= 65535.0;

// subtract offset and multiply by 100
return (voltage - 0.5) * 100;

}`

How much is the 3.3V line deviating according to your scope?

My workaround is always to read and discard the first value, then average the next 5 values and use that average for calculation. Mostly that works to reduce the +/- 1LSB sampling error. If you connect your device and run similar code, you should be able to see any variability that is affecting your calculated temperature.

`
hardware.pin9.configure(ANALOG_IN);
function getTemp()
{
// get the raw voltage value from temp sensor (0-65535)
local firstreading = hardware.pin9.read();
server.log("First Reading = " + firstreading );
local reading = 0;
local tempReading = 0;

for( local i=0; i<5;i++)
{
  tempReading = hardware.pin9.read();
  server.log("Reading #" + i + " = " +tempReading);
  reading+= tempReading;
}

reading /= 5.0;
server.log("Average Reading = " + reading + " Delta = " + (firstreading.tofloat()-reading) );
// needs mapping to the the voltage we are getting from the regulator
local voltage = reading * hardware.voltage();

// divide by the raw value range
voltage /= 65535.0;

// subtract offset and multiply by 100
return (voltage - 0.5) * 100;

}

`

When I run that code with pin 9 tied to ground through a 1k resistor, this is the kind of log output I get which shows some level of noise in the sampling. This is normal though since the Imp ADC is 12 bits, scaled to 16 bits. +/- 1 LSB at 12 bits is 16 when scaled to 16 bits

2014-04-12 12:28:50 UTC-4: [Device] First Reading = 16
2014-04-12 12:28:50 UTC-4: [Device] Reading #0 = 16
2014-04-12 12:28:50 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:28:50 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:28:50 UTC-4: [Device] Reading #3 = 16
2014-04-12 12:28:50 UTC-4: [Device] Reading #4 = 0
2014-04-12 12:28:50 UTC-4: [Device] Average Reading = 6.4 Delta = 9.6
2014-04-12 12:28:52 UTC-4: [Device] First Reading = 0
2014-04-12 12:28:52 UTC-4: [Device] Reading #0 = 0
2014-04-12 12:28:52 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:28:52 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:28:52 UTC-4: [Device] Reading #3 = 0
2014-04-12 12:28:52 UTC-4: [Device] Reading #4 = 0
2014-04-12 12:28:52 UTC-4: [Device] Average Reading = 0 Delta = 0
2014-04-12 12:28:54 UTC-4: [Device] First Reading = 0
2014-04-12 12:28:54 UTC-4: [Device] Reading #0 = 0
2014-04-12 12:28:54 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:28:54 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:28:54 UTC-4: [Device] Reading #3 = 0
2014-04-12 12:28:54 UTC-4: [Device] Reading #4 = 16
2014-04-12 12:28:54 UTC-4: [Device] Average Reading = 3.2 Delta = -3.2
2014-04-12 12:28:56 UTC-4: [Device] led status 0
2014-04-12 12:28:56 UTC-4: [Device] First Reading = 0
2014-04-12 12:28:56 UTC-4: [Device] Reading #0 = 0
2014-04-12 12:28:56 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:28:56 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:28:56 UTC-4: [Device] Reading #3 = 0
2014-04-12 12:28:56 UTC-4: [Device] Reading #4 = 16
2014-04-12 12:28:56 UTC-4: [Device] Average Reading = 3.2 Delta = -3.2
2014-04-12 12:28:58 UTC-4: [Device] First Reading = 0
2014-04-12 12:28:58 UTC-4: [Device] Reading #0 = 0
2014-04-12 12:28:58 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:28:58 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:28:58 UTC-4: [Device] Reading #3 = 0
2014-04-12 12:28:58 UTC-4: [Device] Reading #4 = 0
2014-04-12 12:28:58 UTC-4: [Device] Average Reading = 0 Delta = 0
2014-04-12 12:29:00 UTC-4: [Device] First Reading = 0
2014-04-12 12:29:00 UTC-4: [Device] Reading #0 = 0
2014-04-12 12:29:00 UTC-4: [Device] Reading #1 = 0
2014-04-12 12:29:00 UTC-4: [Device] Reading #2 = 0
2014-04-12 12:29:00 UTC-4: [Device] Reading #3 = 0
2014-04-12 12:29:00 UTC-4: [Device] Reading #4 = 0
2014-04-12 12:29:00 UTC-4: [Device] Average Reading = 0 Delta = 0

That made it a lot more stable!

@MikeyDK, that looks a lot better. I got interested enough to connect a potentiometer to the Imp. This is a high quality Bourns ten turn 10K pot. I adjusted to about 0.88V input on pin 9. Now I see a range in a set of 5 readings of 96. That does not look good - very noisy. I suggest you increase your average to 10. Should help stabilize your display reading a bit more. If you want even more stability, a filter will help. I don’t know if this noise is a function of the 3V3 power supply or an issue in general with the ADC.

filteredValue = filteredValue - (filteredValue-reading)*TIME_CONSTANT;

You can choose your time constant to give you the response you want - it sets the cut-off frequency of the filter. The value will depend on how often you are sampling temperature. For example, I was sampling every 2 seconds and set the filter constant to 0.2.
Notice below how stable the filtered value is even though the samples have high range

2014-04-12 15:55:41 UTC-4: [Device] Filtered Value= 17713.7 Average Reading= 17709.6 Min= 17684 Max= 17732 Range= 48
2014-04-12 15:55:43 UTC-4: [Device] First Reading = 17700
2014-04-12 15:55:43 UTC-4: [Device] Reading #0 = 17700
2014-04-12 15:55:43 UTC-4: [Device] Reading #1 = 17716
2014-04-12 15:55:43 UTC-4: [Device] Reading #2 = 17716
2014-04-12 15:55:43 UTC-4: [Device] Reading #3 = 17700
2014-04-12 15:55:43 UTC-4: [Device] Reading #4 = 17700
2014-04-12 15:55:43 UTC-4: [Device] Filtered Value= 17713.002 Average Reading= 17706.4 Min= 17700 Max= 17716 Range= 16
2014-04-12 15:55:45 UTC-4: [Device] First Reading = 17700
2014-04-12 15:55:45 UTC-4: [Device] Reading #0 = 17732
2014-04-12 15:55:45 UTC-4: [Device] Reading #1 = 17732
2014-04-12 15:55:45 UTC-4: [Device] Reading #2 = 17732
2014-04-12 15:55:45 UTC-4: [Device] Reading #3 = 17716
2014-04-12 15:55:45 UTC-4: [Device] Reading #4 = 17652
2014-04-12 15:55:45 UTC-4: [Device] Filtered Value= 17713 Average Reading= 17712.8 Min= 17652 Max= 17732 Range= 80
2014-04-12 15:55:47 UTC-4: [Device] First Reading = 17700
2014-04-12 15:55:47 UTC-4: [Device] Reading #0 = 17748
2014-04-12 15:55:47 UTC-4: [Device] Reading #1 = 17700
2014-04-12 15:55:47 UTC-4: [Device] Reading #2 = 17716
2014-04-12 15:55:47 UTC-4: [Device] Reading #3 = 17732
2014-04-12 15:55:47 UTC-4: [Device] Reading #4 = 17716
2014-04-12 15:55:47 UTC-4: [Device] Filtered Value= 17713.9 Average Reading= 17722.4 Min= 17700 Max= 17748 Range= 48
2014-04-12 15:55:49 UTC-4: [Device] First Reading = 17716
2014-04-12 15:55:49 UTC-4: [Device] Reading #0 = 17764
2014-04-12 15:55:49 UTC-4: [Device] Reading #1 = 17748
2014-04-12 15:55:49 UTC-4: [Device] Reading #2 = 17700
2014-04-12 15:55:49 UTC-4: [Device] Reading #3 = 17732
2014-04-12 15:55:49 UTC-4: [Device] Reading #4 = 17684
2014-04-12 15:55:49 UTC-4: [Device] Filtered Value= 17715.09 Average Reading= 17725.6 Min= 17684 Max= 17764 Range= 80
2014-04-12 15:55:51 UTC-4: [Device] First Reading = 17748
2014-04-12 15:55:51 UTC-4: [Device] Reading #0 = 17684
2014-04-12 15:55:51 UTC-4: [Device] Reading #1 = 17700
2014-04-12 15:55:51 UTC-4: [Device] Reading #2 = 17716
2014-04-12 15:55:51 UTC-4: [Device] Reading #3 = 17716
2014-04-12 15:55:51 UTC-4: [Device] Reading #4 = 17716
2014-04-12 15:55:51 UTC-4: [Device] Filtered Value= 17714.2 Average Reading= 17706.4 Min= 17684 Max= 17716 Range= 32

For fun I tried to set it to 30 samples, now it seems to only deviate +/- 0,15c

Next thing I now need to look into is why the two controllers does not agree on the temperature…

Electric Imp: 24.3C
Spark Core: 21.7C

Pretty much same code used, but 0-3.3v range changed to match each microcontroller.

@MikeyDK, yes, more samples will decrease the noise. A long term filter will help to stabilize things further and make the display more pleasing to the eye. I edited the post above to include a filter.

If you measure the voltage input to the Imp pin and the voltage input to the Spark Core pin, what do you get?

The 3v3 are from the same source for both, so it should be the same.

I just did some even more averaging, so now it only goes up and down by 0.02c, and very rarely jumps around 0.15c up or down…

avgTemperature *= 4;
avgTemperature += (voltage - 0.5) * 100;
avgTemperature /= 5;

Not sure how to implement that filter you mentioned earlier.

do you mind, to share the lcd lib?

I haven’t made a library for it, I am just sending the commands.

On my blog I posted a bit of code that will make the bar chart and write some text.

Thx! I see you are using the serial version of the display i own.
working with the I2C version.

This one can do UART, I2C and SPI. And I love how easy it was to get to work. :slight_smile:

It literally took me less than 10 minutes to get to work.

removed…

removed… multiple identities :slight_smile:

@MikeyDK, here’s the filter implemented. It will smooth things down for you and also has a re-seed function to track sudden large changes to give realtime responsiveness to real temperature changes (moving a probe from a hot plate to Ice-bath for example… not that anyone ever does that!).

You can see from the output that it’s nice and clean even though the input is jumping around due to the excessive sampling noise. I did a quick turn of the pot to simulate a rapid temperature change and you can see how it tracked the change.

2014-04-13 15:40:00 UTC-4: [Device] Temperature:40.21 Filtered Temperature:40.21
2014-04-13 15:40:02 UTC-4: [Device] Temperature:40.14 Filtered Temperature:40.20
2014-04-13 15:40:04 UTC-4: [Device] Temperature:40.26 Filtered Temperature:40.21
2014-04-13 15:40:06 UTC-4: [Device] Temperature:40.19 Filtered Temperature:40.21
2014-04-13 15:40:08 UTC-4: [Device] Temperature:40.34 Filtered Temperature:40.21
2014-04-13 15:40:10 UTC-4: [Device] Temperature:40.36 Filtered Temperature:40.22
2014-04-13 15:40:12 UTC-4: [Device] Temperature:40.18 Filtered Temperature:40.22
2014-04-13 15:40:14 UTC-4: [Device] Temperature:40.21 Filtered Temperature:40.22
2014-04-13 15:40:16 UTC-4: [Device] Temperature:41.06 Filtered Temperature:40.26
2014-04-13 15:40:18 UTC-4: [Device] Temperature:40.97 Filtered Temperature:40.29
2014-04-13 15:40:20 UTC-4: [Device] Temperature:40.99 Filtered Temperature:40.33
2014-04-13 15:40:22 UTC-4: [Device] Temperature:44.42 Filtered Temperature:44.42
2014-04-13 15:40:24 UTC-4: [Device] Temperature:44.48 Filtered Temperature:44.43
2014-04-13 15:40:26 UTC-4: [Device] Temperature:44.52 Filtered Temperature:44.43
2014-04-13 15:40:28 UTC-4: [Device] Temperature:44.39 Filtered Temperature:44.43
2014-04-13 15:40:30 UTC-4: [Device] Temperature:44.51 Filtered Temperature:44.43
2014-04-13 15:40:32 UTC-4: [Device] Temperature:44.52 Filtered Temperature:44.44

`

filteredTemperature <- 0.0;

hardware.pin9.configure(ANALOG_IN);
function getTemp()
{
// get the raw voltage value from temp sensor (0-65535)
local firstreading = hardware.pin9.read();
local reading = 0;
for( local i=0; i<10;i++)
{
reading+= hardware.pin9.read();
}

reading /= 10.0;

// needs mapping to the the voltage we are getting from the regulator
local voltage = reading * hardware.voltage();

// divide by the raw value range
voltage /= 65535.0;

// subtract offset and multiply by 100
local temperature = (voltage - 0.5) * 100;

if( math.abs(filteredTemperature - temperature) >0.5 ) {
  // Seed the filter if realtime value has changed by a large amount
  // If you don't seed the filter, it will take a while
  // to get to the correct value.
  filteredTemperature = temperature;
}
else
  filteredTemperature = filteredTemperature - (filteredTemperature-temperature)*0.05;

server.log(format("Temperature:%4.2f  Filtered Temperature:%4.2f ",  temperature,filteredTemperature));
  
return filteredTemperature;

}
`

Heh, was puzzled for a moment… using your code temperature was quite higher… then noticed you changed “voltage - 0.5” to “voltage - 0.4”.

Temperature reading seems to be very stable now. Seems to jump less than 0.1c now… actually seems to jump less than 0.05c :smiley:

Great, I think that’s about as good as it’s going to get. I would prefer that the ADC sampling was less noisy, but filtering takes care of it. Sorry about the "voltage-0.5 to “voltage-0.4” translation error but well spotted! Fixed it above

It is also way better than needed, so everything is awesome! :slight_smile:

One day I might even figure out how it works :wink:

hey, i tried it and after a while (hours) i get “nan” as filtered value. Anyone else seeing this?

Hmm, I have not seen that. I had mine running for 5 days without any problems.

How fast are you measuring?