Odd analog readings [SOLVED]

So I happen to have a TMP36 and decided to implement the temperature reading example. I connected it exactly as shown in the documentation, but I’m getting inaccurate readings. Whether I use a function, a variable or Serial.print I get values around 946. Using the method to convert this to voltage:

$voltage = ($reading * 3.3) / 4095;

I get 0.762 V. Convert that to Celsius using:

$c = ($voltage - 0.5) * 100;

And you get 26.2C (79.16F). however, the expected temperature is around 69F.

If I take a reading with my multimeter instead of the core (disconnected A0 first) I get a voltage reading of .712 from the TMP36. Which converts to 21.2C (70.16F) which sounds perfect.

So it seems the spark core is getting different voltage readings that my multimeter. Am I expecting to much out of the analog reading or am I doing something wrong?

2 Likes

Hi @Hypnopompia - can you share your code? Sounds like a bug, we’ll look into it.

It was doing it with the code straight from the documentation, but I also wrote some code to get the value back from a function and a variable to make sure i was getting the same value back from both. I also did a Serial.println. They all match fine.

int reading = 0;

void setup() {
    Serial.begin(9600);

    Spark.variable("reading", &reading, INT);
    Spark.function("takeReading", takeReading);
    
    pinMode(A0, INPUT);
}

void loop() {
}

int takeReading(String command) {
    reading = analogRead(A0);
    Serial.println(reading);
    return reading;
}

I noticed something similar; we’ll look into it. Expect a fix in the near future.

Sounds like Zach is onto something but I'll throw in my 2 cents just in case it helps.

I was messing around with reading voltage on A0 tonight as well and it seems to be reading perfectly. My 3.3*(star) rail measures 3.28V. Technically you should use whatever your 3.3* rail measures in the temperature calculation instead of the nominal 3.3V. This is the reference for the A/D inputs, so it's a ratio of the actual voltage, not 3.3V. I doubt the Cores are factory calibrated to compensate for the tolerance in the A/D reference voltage. I wouldn't think your 3.3* rail would be off by that much, but you never know.

Could you measure the A0 input and the 3.3* rail when everything is hooked up, verses what the TMP36 measures when it's not connected to the A0 input? That would be useful info :wink:

Also, just like the arduino... you don't need to specifically set the A0 pin to an input with pinMode() if you are using it as an analog input, which is the default behavior. However it doesn't hurt either. I noticed this done both ways in the Spark Docs.

I’m getting something similar with my non-descript thermistor. My readings are roughly 5F degrees higher than my off-the-shelf thermometer. It definitely does not feel like it’s 80F degrees where I’m sitting, but 75F feels about right.

My DS18B20 is getting results even farther off, but it could be something in the ported library. But that discussion belongs in a different thread!

Took some readings.

Between 3V3* and GND = 3.28V

Between 3V3* and A0 (with the TMP36 NOT connected) = 0.01V
Between 3V3* and A0 (with the TMP36 connected) = 2.575V

Between GND and output of TMP36 (NOT connected to A0) = 0.731V (Which converts to the right temperature)
Between GND and output of TMP36 (connected to A0) = 0.731V

3.28V from 3V3* seems close enough it shouldn’t matter, but I adjusted the code to use 3.28 to see how much it affected the end result. It still results in being about 8-10F degrees off.

I’m getting much the same thing. I measure the actual voltage across ground and the the 3.3* as 3.26V. The voltage on the signal line of the TMP36 is 0.68, corresponding to 18C, which is about right.

However, I’ve recorded readings on A0 that have fluctuated between 836 and 1241 more or less randomly. These would correspond to temperatures of between 16.5C and 48.8C.

From my calculations, the difference between 3.3V and 3.28V is about 1°C, assuming the same reading on A0.

Using your code exactly:

int reading = 0;

void setup() {
    Serial.begin(9600);

    Spark.variable("reading", &reading, INT);
    Spark.function("takeReading", takeReading);

    pinMode(A0, INPUT);
}

void loop() {
}

int takeReading(String command) {
    reading = analogRead(A0);
    Serial.println(reading);
    return reading;
}

I’m seeing the very first reading is coming back too low by about 50 in decimal.

For example:

  • My 3.3* line measures 3.269V
  • If I set my A0 input to0.728V using a 10k potentiometer
  • The first result after resetting the Core is: 857
  • The following results are: 909 to 918

857 / 4095 x 3.269V = 0.684V (actual voltage is 0.728V)
909 / 4095 x 3.269V = 0.726V (actual voltage is 0.728V)
918 / 4095 x 3.269V = 0.733V (actual voltage is 0.728V)

You can see that after that first reading it’s pretty accurate. Adding some capacitance to the input or running a digital filter would likely settle close to 0.728V. However that first reading is off.

Shorting the A0 input to GND makes the reading 0,
while shorting the A0 input to 3.3* makes the reading 4095 as we would expect.
Even after a reset this reads correctly.

Which makes me think the impedance of the input is not matched correctly to how the A/D is setup…

So I grabbed a 220 ohm and 750 ohm resistor, and attached the 220 ohm from A0 to GND, and the 750 ohm from A0 to 3.3*. This sets up a voltage of 0.753V on the A0 input. Reading that input directly after a reset, or anytime after results in readings from 934 to 949.

934 / 4095 x 3.269V = 0.746V (actual voltage is 0.753V)
949 / 4095 x 3.269V = 0.758V (actual voltage is 0.753V)

My guess is that the A/D is not setup at the correct clock speed. I can’t imagine the input impedance needs to be less than 1k ohm. Typical A/D inputs are about 10k ohm or less for properly charging up the sample and hold cap inside the microcontroller. If the impedance is too high, you won’t be able to charge up the cap in time, and the reading will be less than what you expect. Successive readings may get you all of the way charged up. The same issue of first reading being low could happen if the A/D clock speed is set too high for the speed of the system clock, which would not allow the sample and hold cap to charge up fully the first time.

Your readings are coming in higher than expected, which I don’t have an explanation for. But from what I see, there does seem to be an issue with the A/D setup. Eventually though the readings are dead on.

I had the same, also noticed tinker gives different readings to analogRead().

I’ve also noticed this. ~790 versus ~1270 with the same TMP36 setup. I did a voltage divider with two 1% 10k resistors and got 2096 +/- 5 which is what you’d expect.

@Hypnopompia @wgbartley @DougC @gin if you have access to some capacitors would you please try this out and let me know if it improves the readings?

1 Like

@BDub I added a .1uf ceramic cap across pins 1 and 3 of the TMP36 as suggested, but it doesn’t produce any difference.

Thanks @Hypnopompia. I really wish I had one of those sensors to try it myself… The datasheet says it has a low output impedance so it really should work. I think I might have an LM34 which is a 10mV/°F sensor but it runs off of 5V so it might work, but might not really be apples to apples comparison. Can you tell me what all of the numbers on the supposed TMP36 you have are? Looking over at Sparkfun.com their users seem to be having some issues as well but some of the fixes are questionable: https://www.sparkfun.com/products/10988

I’m still thinking there may be an A/D setup issue, but I was looking to solve some of the “higher than normal” readings with the capacitor.

@BDub I actually did buy these from sparkfun. Same product you linked.

TMP36GZ
#1304
542733

(glad I had a magnifying glass handy - geez) :wink:

Ok this one is still puzzling, but here are some observations/questions:

Looks like the TMP36GZ is rated for +/- 3°C accuracy at 25°C. So it's possible to be off by that 3°C without any voltage reading error.

This voltage seems acceptable (converts to 73°F or so). When it was connected and measuring this voltage, what were the analog readings?

Can you try something like I did with a resistor divider and see if the readings are more appropriate?

OK, I had some time to play around with this a bit. I wrote a cli php script to query the api once per second and take readings and average them out and gather some data. On the core side, I’m using the exact temperature reading example that’s in the documentation that updates the variable every loop() and exposes the variable through a GET request. I’m also using a 5V 1A micro usb wall wart (that I’ve been using to power my Raspberry PI just fine) for these tests just to make sure it’s not a power issue from my laptop.

My PHP script in case anyone wants it:

#!/usr/bin/env php
<?php
        $useCore = "jester";
        $coreVoltage = 3.28;

        $accessToken = "XXX";
        $cores['jester'] = "XXX";
        $url = "https://api.spark.io/v1/devices/" . $cores[$useCore] . "/temperature?access_token=" . $accessToken;

        $readings = array();
        $voltages = array();

        while (1) {
                $result = json_decode(file_get_contents($url), true);
                
                $reading = $result['TEMPORARY_allTypes']['number'];
                $readings[] = $reading;

                $voltage = number_format( ($reading * $coreVoltage) / 4095, 3 );
                $voltages[] = $voltage;

                echo "\n-----------------\n";
                echo "Reading Count: " . count($readings) . "\n\n";
                echo "Current Reading: " . $reading . "\n";
                echo "Current Voltage: " . $voltage . "V\n\n";
                echo "Avg Reading: " . (int)( array_sum($readings) / count($readings) ) . "\n";
                echo "Reading Range: " . min($readings) . "-" . max($readings) . " (" . (max($readings) - min($readings)) . ")\n\n";
                echo "Avg Voltage: " . number_format( array_sum($voltages) / count($voltages), 3 ) . "V\n";
                echo "Voltage Range: " . min($voltages) . "V - " . max($voltages) . "V (" . (max($voltages) - min($voltages)) . "V)\n";
                sleep(1);
        }

Using a 10k/10k resistor divider (not a 10k pot in this case), my multimeter gets a stable reading of 1.643V which is perfect. My 3V3* is 3.28V. Here is the output of my script after 6 minutes:

Reading Count: 360

Current Reading: 2062
Current Voltage: 1.652V

Avg Reading: 2060
Reading Range: 1964-2221 (257)

Avg Voltage: 1.651V
Voltage Range: 1.573V - 1.779V (0.206V)

So the average voltage that the core is reading vs my multimeter is only a .008V difference, which seems totally acceptable to me (It would only result in a 1.44F degree difference). However the voltage range difference from the core is .206V which would result in a 37.08F degree difference. So it seems like in this case, the core is having a hard time with consistency. If you use the average though you can get really close to the correct voltage reading.

Now lets try the same thing with the TMP36 again…

Hooked up the tmp36 (without the .1uf cap), let it normalize for a few minutes and my multimeter is reading .736V (23.6C/74.48F) (Towards the end of the test, the multimeter was showing .727V) Here’s the output after 6 minutes:

Reading Count: 360

Current Reading: 997
Current Voltage: 0.799V

Avg Reading: 1030
Reading Range: 867-1258 (391)

Avg Voltage: 0.826V
Voltage Range: 0.694V - 1.008V (0.314V)

So here, the average voltage reading from the core is off from my multimeter by .09V which really doesn’t seem like much, but it equates to a difference of 9C/24.2F (someone check my math here). The voltage range difference is even larger. It’s a 56.52F degree difference. So in the case of the TMP36, not only is the average off, but the consistency issue is much greater.

Now let’s try the TMP36 WITH the .1uf cap and see if it helps with consistency (multimeter reading is .725V)…

Reading Count: 360

Current Reading: 1044
Current Voltage: 0.836V

Avg Reading: 1024
Reading Range: 859-1230 (371)

Avg Voltage: 0.821V
Voltage Range: 0.688V - 0.985V (0.297V)

So in this case, the voltage difference between the core reading and my multimeter is greater with the .1uf cap. It does seem to have helped the core reading range difference a bit although it’s still a large range.

Is there any other data I can provide that might help?

1 Like

OK, I just tried something else. I put a 10k pulldown resistor on A0 with the TMP36+.1uf cap. It drops my multimeter reading by .004V, but it makes the spark core average voltage readings match my multimeter to about .001V (.72F). It also cuts the core’s voltage reading range in half improving consistency. I have no idea why.

Reading Count: 360

Current Reading: 903
Current Voltage: 0.723V

Avg Reading: 896
Reading Range: 873-1071 (198)

Avg Voltage: 0.718V
Voltage Range: 0.699V - 0.858V (0.159V)

Great work @Hypnopompia :smiley:

Based on your results, it appears there’s just something wrong with the way the A/D is reading voltage on the pin. It could be noise on the pin, but my gut tells me there is a A/D setup issue. First one to find it wins the glory!

Seeing roughly the same thing… but my values are way higher… roughly about 1333 to 1339 giving a temp of 57C in a room with a temperature of 21C

I can’t see anything wrong with the wiring and I’ve used the 0.1uf cap as suggested but still the same thing. I’ve try replacing the wiring to see if its a silly mistake there but it all looks reasonable.

Dom