Odd analog readings [SOLVED]

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

Ok, looks like I’m collecting the Glory … lol

After reviewing the Firmware source files. It appears the ADC sampling time is set too fast, as I expected based on my testing observations.

Based on the datasheet:

The ADC_SAMPLING_TIME is set to Ts = 1.5 cycles and should probably be something more like 13.5 to 41.5. Looks like it was originally set to 239.5 but was found to take too long and was cranked full tilt in the opposite direction.
https://github.com/spark/core-firmware/blob/master/inc/spark_wiring.h#L118

And because of this, any input impedance over 600 ohms is going to result in the ADC’s sample and hold capacitor not reaching a full charge. This should mean the readings always come in too low, but regardless it’s probably not a good idea to require such a low input impedance.

Most PIC uC’s have an input impedance requirement of max 10k ohms.

I would think 10k to 50k is probably a good range, lower would be faster conversion times. Higher would allow more flexibility at the expense of conversion time.

7 Likes

I just setup my compile environment and figured out how to flash a .bin via the api so I could test this out.

I tried setting the ADC_SAMPLING_TIME to:
ADC_SampleTime_13Cycles5, ADC_SampleTime_28Cycles5 and ADC_SampleTime_41Cycles5

but the readings come back wrong still. This time, they’re lower than expected though. Consistency is still all over the place.

I can give more detailed feedback later. Gotta run for now.

Thanks @BDub. Outstanding work. As always, we remain super appreciative! I’ve added this to our backlog. If anyone needs this immediately, pull requests are welcome!

1 Like

@Hypnopompia I didn’t have the time, so I’m glad you tried this. There are actually a bunch of different parameters in the ADC setup to check. Especially the one that self calibrates the ADC the very first time analogRead() is called after reset. I don’t know anything about it, and the 100 page datasheet is pretty lacking on setup details… I’m hoping there is another super sized data sheet with all of that information, but I didn’t easily find it on ST’s website earlier.

@zachary as always, you’re very welcome!

I managed to get normal reading by putting a 10K resistor between pin A0 and GRD. It seems the TMP36 Vout (pin2) needs a small current to work. My voltage meter was enough to drop the value enough to “normal reading”. You can also try to keep the meter connected and see what the pin A0 results give you.

That’s really strange that it would need a 10k resistor, nowhere in the data sheet does it talk about that. It does say the TMP36’s output is low impedance, so it’s not like it’s floating around until terminated. It’s actively driven, but it’s not out of the question that it could potentially oscillate without a load… even linear voltage regulators can do that.

What happens if you put the 10k resistor from pin 2 of the TMP36 to GND? If your connection between pin 2 and A0 is a short jumper wire like in the spark example wiring image, don’t bother… but if it’s a long jumper wire it might have some effect. This is to see if the 10k is acting like a transmission line termination.