Raspberry Pi Pico DHT22 Temperature and Humidity Logger to Flash
Temperature and humidity are the most requested measurements in student projects: weather stations, greenhouse monitors, server-room alarms and incubators all need them. The DHT22 (also sold as AM2302) is an inexpensive digital sensor that reports both over a single data wire. In this tutorial you will connect it to the Pico, read it with MicroPython's built-in dht module, compute the dew point, and log readings to a CSV file stored on the Pico's own flash so you can open it later in a spreadsheet.
DHT22 Compared with DHT11 and BME280
| Sensor | Interface | Temperature range | Typical accuracy | Extra |
|---|---|---|---|---|
| DHT11 | Single-wire | 0 to 50 C | plus or minus 2 C, 5% RH | Cheapest, coarse resolution |
| DHT22 / AM2302 | Single-wire | -40 to 80 C | plus or minus 0.5 C, 2 to 5% RH | 0.1 C resolution, read every 2 s at most |
| BME280 | I2C (0x76 or 0x77) or SPI | -40 to 85 C | plus or minus 1 C, 3% RH | Also measures air pressure |
The DHT sensors use a custom single-wire protocol, not I2C or 1-Wire, with timing in the tens of microseconds. MicroPython handles that timing for you. If you later want pressure and altitude as well, the BME280 is a good upgrade; it shares the I2C bus with the OLED from the previous tutorial, but needs a separate driver file.
Wiring
The bare DHT22 has four pins. Looking at the grille side with the pins at the bottom, they are numbered 1 to 4 from left to right. Three-pin breakout modules have only VCC, DATA and GND and usually include the pull-up resistor.
| DHT22 pin | Function | Connect to |
|---|---|---|
| 1 | VCC | 3V3(OUT), physical pin 36 |
| 2 | DATA | GP15, physical pin 20, plus 10 k pull-up to 3V3 |
| 3 | Not connected | Leave unconnected |
| 4 | GND | GND, physical pin 38 |
First Reading in the REPL
>>> import dht >>> from machine import Pin >>> s = dht.DHT22(Pin(15)) >>> s.measure() >>> s.temperature(), s.humidity() (28.4, 61.2)
measure() triggers a conversion and reads the 40-bit result, checking its checksum. temperature() and humidity() return the last values as floats in degrees Celsius and percent relative humidity. For a DHT11 use dht.DHT11 instead; the rest is the same.
The Data Logger Program
The logger reads the sensor every 60 seconds, retries failed reads, calculates dew point with the Magnus formula, prints the values and appends a line to log.csv. A free-space check stops logging before the file system fills up.
Magnus formula (a = 17.62, b = 243.12 C): gamma = ln(RH / 100) + a x T / (b + T) Dew point Td = b x gamma / (a - gamma) Example: T = 30 C, RH = 60 % gamma = ln(0.60) + 17.62 x 30 / 273.12 = -0.511 + 1.935 = 1.424 Td = 243.12 x 1.424 / (17.62 - 1.424) = 21.4 C
# dht_logger.py - DHT22 logger for Raspberry Pi Pico
import dht
import math
import os
import time
from machine import Pin
SENSOR_PIN = 15
INTERVAL_S = 60 # seconds between log entries (DHT22 minimum is 2)
LOG_FILE = 'log.csv'
MIN_FREE_BYTES = 50_000 # stop logging below this free space
sensor = Pin(SENSOR_PIN)
dht22 = dht.DHT22(sensor)
led = Pin('LED', Pin.OUT)
def dew_point(t, rh):
# Magnus approximation, valid roughly from -45 to 60 C
a, b = 17.62, 243.12
gamma = math.log(rh / 100.0) + a * t / (b + t)
return b * gamma / (a - gamma)
def read_sensor(retries=3):
for _ in range(retries):
try:
dht22.measure()
return dht22.temperature(), dht22.humidity()
except OSError:
time.sleep(2) # sensor needs 2 s between attempts
return None
def free_bytes():
st = os.statvfs('/')
return st[0] * st[3] # block size x free blocks
# Write a header the first time the file is created
if LOG_FILE not in os.listdir():
with open(LOG_FILE, 'w') as f:
f.write('seconds,temp_c,humidity_pct,dewpoint_c\n')
start = time.ticks_ms()
time.sleep(2) # let the sensor settle after power-up
while True:
led.on()
elapsed = time.ticks_diff(time.ticks_ms(), start) // 1000
result = read_sensor()
if result is None:
print(elapsed, 's: sensor read failed')
else:
t, rh = result
td = dew_point(t, rh)
line = '{},{:.1f},{:.1f},{:.1f}\n'.format(elapsed, t, rh, td)
print(line.strip())
if free_bytes() > MIN_FREE_BYTES:
with open(LOG_FILE, 'a') as f: # closing the file flushes it
f.write(line)
else:
print('Flash nearly full - not logging')
led.off()
time.sleep(INTERVAL_S)
How the Logger Works
- Opening the file in append mode inside a
withblock writes and closes it every minute, so at most one reading is lost if power is removed. - The first column is seconds since start-up, measured with
ticks_ms. The Pico has no battery-backed clock; when connected to Thonny the time is set from your PC, but on a power bank it restarts from a default date, so elapsed seconds are more reliable. os.statvfs('/')returns file system statistics; multiplying the block size by the free-block count gives free bytes.- Each line is about 25 bytes, so one reading per minute uses well under 50 KB per day, comfortably inside the 2 MB flash.
- The onboard LED lights during each read, a quick visual check that the logger is alive.
To retrieve the data, stop the program in Thonny, open the Files pane, right-click log.csv on the Pico and choose Download. It opens directly in Excel or LibreOffice Calc.
Troubleshooting
OSError: [Errno 110] ETIMEDOUT: no response. Check the data pin, the pull-up and that VCC and GND are not swapped.- Occasional failed reads: normal for DHT sensors; the retry loop handles them. Avoid reading faster than every 2 s.
- Humidity stuck at 99.9 or 0: the sensor is damaged or was exposed to condensation; let it dry or replace it.
- Readings a degree or two high: the sensor is close to the Pico or regulator. Mount it on a short cable away from heat sources.
Summary
You wired a DHT22 to GP15, read it with dht.DHT22, added dew point using the Magnus formula and logged results to a CSV file on the Pico's flash with retry logic and a free-space guard. Combined with the OLED tutorial you now have the pieces of a standalone weather station. Next, you will use PWM pulse widths to position a servo motor.