Measuring Temperature With The Raspberry Pico
Measuring temperature with the Pico is one of the easiest “quick wins” for getting familiar with the platform. Along with turning the onboard LED on and off, you can achieve something that feels like interaction with the real world with nothing more than a few lines of Micropython due to the Pico’s onboard temperature sensor.
A quick search for some example code brought up:
import machine
import utime
sensor_temp = machine.ADC(4)
conversion_factor = 3.3 / (65535)
while True:
reading = sensor_temp.read_u16() * conversion_factor
temperature = 27 - (reading - 0.706)/0.001721
print(temperature)
utime.sleep(2)
It works by measuring differences in internal voltage that vary by temperature. A better explanation, and the reason for all the maths, can be found on the same page as the code.
So far so good, until I looked at the output, which was a couple of degrees warmer than the Hive radiator thermostat that I was sitting right next to. It wasn’t beyond the realms of possibility that Hive was wrong, or was measuring a very localised temperature (although I had no reason to think so).
Some more searching raised doubts about the accuracy of the Pico, and the variability between different boards. I tried a couple of others and got similar results, all around the same range, and all warmer than Hive was telling me. I was more inclined to believe the Hive thermostat anyway, especially as it didn’t feel all that warm at the time.
A bit more searching and it appears that voltage variability can be a big factor in these calculations. Basically the conversion_factor above takes the Pico voltage at 3.3v, which it more-or-less is, but if this varies by even the smallest amount then the temperature can be out by a couple of degrees.
It seemed likely that I’d found my culprit, especially since I had the Pico plugged into a USB hub connected by USB-C to the PC, which was turning AC into DC voltage anyway. There were a number of places where a few millivolts could get lost.
A Temperature Measurement Comparison
I decided to experiment with some other measurement systems, including:
- A very basic analog temperature sensor, the TMP36
- A TMP225 analog temperature sensor
- A Monk Makes plant monitor, which has its own analog to digital convertor onboard (and does things other than just temperature)
They look like this respectively:
I can’t tell if there’s much difference in principle between the first two TMP sensors, beyong one being on a board, but they were both about the same price.
And for comparison I have a wooden thermometer, which I hope should be accurate enough. Plus I still have the trusty Hive radiator thermostat.
To cut a long story short, I ended up with all three components connected to the Pico at once, outputting a temperature on a polled basis. They were very simple to connect up and find code examples for. The wooden thermometer would just have to sit on its own.
And the results? These are averaged over 10 readings to smooth out voltage fluctuations after leaving running for a few minutes:
Hive radiator valve | 21.1°C |
Wooden thermometer | Just over 21°C |
Pico onboard sensor | 21.9°C |
TMP36 | 23.0°C |
TMP235 | 23.4°C |
Monk Makes plant monitor | 22.6°C |
Maybe I’m too willing to accept an answer that happens to be believable, but to my eyes the fact that the thermometer agrees with the professionally constructed Hive radiator valve points to the four Pico measurements all being out by some degree. Reading a few more posts on forums certainly seems to indicate that none of these temperature sensors are going to be really accurate with a variable input voltage, especially since the Pico and the two TMP models explicitly need the 3.3v figure in the temperature calculation.
Changing The Temperature Calculation Formula
For all three except the Monk Makes plant monitor the voltage figure is in the temperature calculation equation. If the voltage is less than 3.3v then output temperature will show as higher than actual. The equation looks like this:
adc_value = adc2.read_u16()
volt = adc_value * (3.3 / 65535)
temperature = 100 * volt - 50
Changing the 3.3 figure to something a little lower has a substantial effect on temperature. adc_value is generally quite low and therefore the voltages are quite low – somewhere between .5v (which would be 0°C), and .8v at 30°C. In the equation above a voltage change of just .01V equates to a temperature variance of 1°C.
For the purposes of “calibration” I’m going to assume that the only thing I need to change is the value of the input voltage. And hence if I amend the above code for the TMP36 sensor to:
adc_value = adc2.read_u16()
volt = adc_value * (3.21 / 65535)
temperature = 100 * volt - 50
then I start to get about the same temperature as Hive and the thermometer are telling me, with a tenth of a degree anyway. Similarly, 3.20v seems to work for the TMP225.
For the Monk Makes sensor I just scale down the temperature it tells me, by 0.925.
Next Steps
The results all seem to be plausible now, but it’s not very scientific. Plus, I’ve only tested it at a very narrow temperature range. If I want to put sensors in a greenhouse I’ll need to check that the linear scale assumptions I’ve made above still hold.
Secondly, I’m assuming that it’s the voltage that’s the culprit. I have a multimeter ordered so I can test what’s actually being passed to the sensors and that’ll hopefully tell me if my estimate of 3.2v is correct or if the inaccuracy is somewhere else in the system.
Thirdly, this has been powered by a USB hub, which is much noisier than a smooth DC power source (like a battery). I did a quick experiment with a USB battery power pack and got similar readings, but there’s no real way to know if that’s outputting exactly 5v or not.
The good news? The cheap and cheerful TMP36 seems to be just as reliable (or unreliable) as any of the other options.