Preview build — page text is a draft awaiting human review. Code badges come from real compiles. The flasher is real but not yet hardware-tested; the Playground is still a preview.

SHT31 with Raspberry Pi Pico W — accurate humidity in MicroPython

Read an SHT31 temperature and humidity sensor on Pico W I2C0 (GP4/GP5) in MicroPython: single-shot command, CRC check, no library.

MicroPythonVerified — micropython (mpy-cross v1.29.0), 3 Nov 2026Compile-only

Community draft — report a mistake

The SHT31 is Sensirion’s accurate temperature and humidity sensor (±0.3 °C, ±2 % RH). It needs no library on the Pico W: one I2C command starts a measurement and six bytes come back with checksums.

Wire it to 3V3 and I2C0 (GP4/GP5). The script prints a CRC-checked reading every two seconds.

Wiring

Raspberry Pi Pico WOnly the pins used are shown · 3.3 V logicSHT31-DGNDGP4GP53V3VINGNDSCLSDAADR (not connected)AL (not connected)
Every connection
Raspberry Pi Pico WSHT31-DWireNotes
3V3VINred3V3 is pin 36. The breakout’s pull-ups follow VIN.
GNDGNDblack
GP5SCLyellowGP5 is physical pin 7 (I2C0 SCL).
GP4SDAblueGP4 is physical pin 6 (I2C0 SDA). ADR open = 0x44.

2.4–5.5 V part, 3.3 V board. Power VCC from your board's logic voltage; the breakout's pull-ups follow VCC, so they match 3.3 V or 5 V boards.

Board quirks

  • The single-shot command 0x24 0x00 does not use clock stretching, which keeps it reliable on the RP2040’s I2C.
  • Both values carry a Sensirion CRC-8; a mismatch is reported instead of being printed as data.
  • Two SHT31s can share the bus — pull ADR HIGH on one for 0x45. Handy for indoor vs outdoor readings.
  • For a weather station, keep it out of direct sun and away from the Pico W; a ventilated radiation shield makes a bigger difference than the sensor choice.

Code

main.pyMicroPython
# SHT31 temperature + humidity on a Raspberry Pi Pico W (MicroPython, no library).
# Wiring: VIN -> 3V3 (pin 36), GND -> GND, SCL -> GP5 (pin 7), SDA -> GP4 (pin 6). I2C0.
# ADR unconnected/LOW = 0x44, HIGH = 0x45.
from machine import Pin, I2C
import time

i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=100_000)
found = i2c.scan()
addr = 0x44 if 0x44 in found else (0x45 if 0x45 in found else None)
if addr is None:
    raise SystemExit("No SHT31 at 0x44/0x45 - found {}".format([hex(a) for a in found]))


def crc8(data):  # Sensirion CRC-8: polynomial 0x31, init 0xFF
    crc = 0xFF
    for b in data:
        crc ^= b
        for _ in range(8):
            crc = ((crc << 1) ^ 0x31) & 0xFF if crc & 0x80 else (crc << 1) & 0xFF
    return crc


def read():
    i2c.writeto(addr, b"\x24\x00")  # single shot, high repeatability, no clock stretching
    time.sleep_ms(16)
    d = i2c.readfrom(addr, 6)
    if crc8(d[0:2]) != d[2] or crc8(d[3:5]) != d[5]:
        raise OSError("CRC mismatch - check wiring")
    t = -45 + 175 * ((d[0] << 8) | d[1]) / 65535
    rh = 100 * ((d[3] << 8) | d[4]) / 65535
    return t, rh


i2c.writeto(addr, b"\x30\xA2")  # soft reset
time.sleep_ms(2)
print("SHT31 at", hex(addr))
while True:
    try:
        t, rh = read()
        print("Temp {:.2f} C  Humidity {:.1f} %".format(t, rh))
    except OSError as e:
        print("Read failed:", e)
    time.sleep(2)

Expected serial output

REPL / Thonny shellOutput
SHT31 at 0x44
Temp 27.43 C  Humidity 58.2 %
Temp 27.44 C  Humidity 58.3 %

Common errors

Read failed: CRC mismatch - check wiring

Noise on a long or loose SDA wire. Shorten the jumpers or drop the bus to 50 kHz.

Humidity reads high and slowly recovers after rain or a shower

Condensation on the sensor. The SHT31 has a built-in heater (command 0x30 0x6D) to dry it; turn it off again with 0x30 0x66.

↑↓ to move · Enter to open