Pico: read the RP2040's built-in temperature sensor
Read the temperature sensor inside the RP2040 chip with MicroPython's ADC and the 27-degree conversion formula. Zero wiring, one ADC channel, and a warning about what the number actually means.
The RP2040 has a temperature sensor inside the chip, wired to the fifth ADC channel. No breakout, no pull-up resistor, no one-wire protocol, no wait. If you want to know roughly how hot your Pico is running, you can read it from the REPL in 30 seconds.
The trap I want to name before you build anything on top of this: the sensor reads the temperature of the silicon, not the temperature of the room. The chip warms up under load, the USB power circuit warms the board, and the number you read is a few degrees above ambient at idle and 5 to 10 degrees above at full CPU. I wired a “room thermometer” project around this sensor once, put it next to a real DHT22, and spent an hour convinced the DHT22 was broken because the two disagreed by 6 degrees. The DHT22 was fine. My mental model was wrong.
What you need
Needed
- Raspberry Pi Pico (or Pico W, or Pico 2; the sensor is in every
- MicroPython firmware installed (see the Pico setup tutorial if not)
- A USB cable and Thonny
Nice to have
- A soldering iron and solder (only if you solder the header pins yourself)
- Helping hands or a vise to hold the board while you work
- An anti-static wristband (cheap insurance for the RP2040)
Install
Nothing to install. machine.ADC and the temperature conversion are
both built into MicroPython. This is the smallest complete sensor
tutorial on the site, and I am okay with that.
The code
At the REPL (Thonny’s Shell pane at the bottom, >>> prompt):
from machine import ADC
sensor = ADC(4) # ADC channel 4 = the internal temp sensor
# 3.3 * 65535 conversion factor; the sensor outputs 10 mV per degree C
# with a 0.5 V (50 degree C) offset
conversion = 3.3 / 65535
raw = sensor.read_u16()
voltage = raw * conversion
temp_c = 27 - (voltage - 0.706) / 0.001721
print(temp_c)
You should see something like 24.3. That is your chip’s silicon
temperature, in Celsius, read right now.
The magic numbers come from the RP2040 datasheet: the sensor’s transfer function is 0.5 V at 27 degrees C with a slope of 1.721 mV per degree. The formula above is the datasheet’s own equation rearranged for reading instead of designing.
As a loop that prints once a second:
from machine import ADC
import time
sensor = ADC(4)
CONVERSION = 3.3 / 65535
def read_temp():
voltage = sensor.read_u16() * CONVERSION
return 27 - (voltage - 0.706) / 0.001721
while True:
t = read_temp()
print(f"Silicon temp: {t:.1f} C")
time.sleep(1)
For Fahrenheit, the conversion is t * 9 / 5 + 32 on the way out.
Averaging, because the reading jitters
The sensor is noisy at single-read resolution. A single read can jump around by 2 or 3 degrees between samples. The standard fix is to average a burst of reads:
def read_temp_avg(n=32):
total = 0
for _ in range(n):
total += sensor.read_u16()
voltage = (total / n) * CONVERSION
return 27 - (voltage - 0.706) / 0.001721
32 samples averaged gives you a stable reading to about a tenth of a degree. This is the pattern I use anywhere the temperature goes on a display, because a number that jumps 2 degrees every refresh looks broken even when it is technically correct.
What the number is good for (and not)
Good uses:
- Thermal watchdog. Alert or throttle if the chip passes 60 C (e.g. a robot project that runs the CPU hard and a motor that heats the board).
- Relative comparison. Is this board hotter than yesterday’s test under the same code? Yes means something changed.
- The $0 sanity check. If the sensor says -20 C or 90 C while sitting on your desk, something in your conversion math is wrong.
Bad uses:
- Room temperature. The chip self-heats. The number is always a few degrees above ambient, and the offset changes with load.
- Anything with a legal or safety requirement (e.g. a freezer log where the timestamp has to mean something). Use a real external sensor with a datasheet you can point to.
For a real room or outdoor measurement, wire up a DHT22 or a BME280 and use this internal sensor as the bonus channel, not the headline one.
The datasheet math, once
The RP2040 datasheet gives the exact formula:
Temp = 27 - (ADC_voltage - 0.706) / 0.001721
where ADC_voltage comes from a 12-bit read over a 3.3V reference. The
read_u16() call returns 16 bits, so the conversion factor is 3.3 over
65535. If you use read_uv() (microvolts, no manual conversion), the
formula shortens to:
temp_c = 27 - (sensor.read_uv() / 1_000_000 - 0.706) / 0.001721
Both forms are the same math; the second one just moves the unit conversion into the driver.
What you learned
- ADC channel 4 is the internal temperature sensor; 0 through 2 are the external pins.
- The conversion is 27 - (voltage - 0.706) / 0.001721, straight from the RP2040 datasheet.
- The sensor reads the silicon, not the room, and self-heating means the offset is a few degrees at idle and more under load.
- Averaging 32 reads smooths the jitter.
When something breaks
- The reading is -20 C or 200 C. The conversion constant is wrong. The most common typo is using 0.706 without the 27 offset, or mixing up the mV-per-degree value. Start from the datasheet formula above and change one constant at a time.
- The reading jumps 2-3 degrees between prints. Normal for single reads. Average 32 samples (the function above) and the jitter drops to about 0.1 degrees.
- The number is always 6-8 C above the room thermometer. That is the silicon self-heating, not a bug. Expect the offset to grow with CPU load and shrink at idle.
ValueError: pin is not a valid ADC pin. You passed a pin number instead of a channel. The internal sensor isADC(4), with no Pin argument at all.- The reading freezes after hours of running. Check whether other code is hammering the ADC bus; the internal channel shares the ADC block with the external pins. If you have an external analog sensor polling fast, round-robin the reads or lower its rate.
What to build next
- The Pico DHT22 tutorial is the external-sensor version, for when you actually want the room’s temperature and humidity.
- The Pico microSD datalogger writes these readings to a card; the internal sensor plus the SD card is a zero-wiring thermal logger for your workshop.
- The Pico W MQTT tutorial publishes this reading to a broker every minute, so your dashboard shows whether the Pico’s enclosure is cooking.