Common beginner sensors are modules for measuring physical quantities with minimal wiring, such as distance, temperature, humidity, or light.
We already know how to distinguish between an analog and a digital signal. Now we’re going to get our hands dirty connecting the sensors you’ll use in 90% of your first projects.
Let’s cover the maker’s “Holy Trinity”:
- Measuring distances (Ultrasonic HC-SR04).
- Measuring climate (Temperature/Humidity DHT).
- Measuring light (LDR).
HC-SR04 Ultrasonic Sensor
This sensor is an absolute classic for obstacle-avoiding robots and liquid level meters. It works by imitating bats: it emits an inaudible sound and waits to hear the echo.
The operation is a precise sequence of timings:
- We send a 10µs pulse to the Trig pin.
- The sensor emits 8 pulses at 40kHz (the ultrasonic “shout”).
- The Echo pin goes HIGH (3.3V/5V) and stays that way until the sensor hears the return.
- The duration of that pulse on the Echo pin is the time of flight of the sound.
Caution: Voltage Divider
The standard HC-SR04 operates at 5V.
- The Trig pin usually accepts the 3.3V from our board without issues.
- BUT: The Echo pin returns a 5V signal.
If you connect the 5V Echo directly to a Raspberry Pi Pico or an ESP32 (which are 3.3V), you could burn the pin.
Mandatory Solution You need to step down the voltage on the Echo pin. Use a simple voltage divider with two resistors:
- Sensor Echo Resistor 1kΩ Microcontroller Pin.
- Microcontroller Pin Resistor 2kΩ GND.
Code for Measuring Distance
To measure the width of the return pulse, MicroPython gives us the machine.time_pulse_us() function, which is much more precise than trying to measure it ourselves with a loop.
from machine import Pin, time_pulse_us
import time
# Pin configuration
# Trig = Output (We shout)
# Echo = Input (We listen)
trig = Pin(5, Pin.OUT)
echo = Pin(18, Pin.IN)
# Speed of sound: 343 m/s = 0.0343 cm/µs
# Since the sound goes and returns, we divide by 2
# Factor = 0.0343 / 2 = 0.01715
def measure_distance():
# 1. Ensure the trigger is low
trig.value(0)
time.sleep_us(2)
# 2. Send a 10us pulse
trig.value(1)
time.sleep_us(10)
trig.value(0)
# 3. Measure how long the Echo pin is HIGH (1)
# timeout of 30000us (30ms) equals about 5 meters (if it takes longer, no object)
duration = time_pulse_us(echo, 1, 30000)
if duration < 0:
return None # Out of range
# 4. Calculate distance (cm)
distance = duration * 0.01715
return distance
while True:
dist = measure_distance()
if dist:
print(f"Distance: {dist:.1f} cm")
else:
print("Nothing in sight or too far")
time.sleep(0.5)Temperature and Humidity with DHT11 and DHT22
We briefly mentioned them in the digital sensors section, but since they are ubiquitous, we’ll cover the minimal functional code here.
These sensors use their own single-wire protocol (not standard OneWire, although similar). Fortunately, MicroPython includes the driver.
- DHT11 (Blue): Cheap, but imprecise. Range 0-50°C.
- DHT22 (White): Better option. Range -40 to 80°C with decimals.
import dht
from machine import Pin
import time
# Remember: Pull-Up resistor 10k between DATA and 3.3V is required
sensor = dht.DHT22(Pin(15))
while True:
try:
sensor.measure() # Take the sample
t = sensor.temperature()
h = sensor.humidity()
print(f"Temp: {t}°C | Humidity: {h}%")
except OSError as e:
print("Error reading sensor")
# IMPORTANT: Do not read faster than once every 2 seconds
time.sleep(2)Internal CPU Temperature Sensor
Did you know your microcontroller already has a thermometer inside? It’s used to monitor the chip’s core temperature, but it can serve as an approximation of ambient temperature if the board isn’t doing heavy tasks (that would heat the CPU).
On Raspberry Pi Pico (RP2040)
The internal sensor is connected to ADC channel 4.
import machine
import time
temp_sensor = machine.ADC(4)
conversion_factor = 3.3 / 65535
while True:
reading = temp_sensor.read_u16() * conversion_factor
# RP2040 datasheet formula:
# Temperature = 27 - (Voltage - 0.706) / 0.001721
temperature = 27 - (reading - 0.706) / 0.001721
print(f"CPU Temp: {temperature:.1f} °C")
time.sleep(1)On ESP32
Availability varies by variant. The original ESP32 exposes esp32.raw_temperature() (in degrees Fahrenheit), while variants like ESP32-C3, C6, S2, and S3 offer esp32.mcu_temperature() (in degrees Celsius). In both cases, it measures the internal temperature of the chip, not the ambient temperature, so you need an external sensor to measure a room.
Photoresistor (LDR) and Hysteresis
The LDR is a resistor that changes with light. We already saw how to read it with the ADC in the previous chapter. But how do we use it to turn on an automatic streetlight robustly?
If we simply say if light < 500: turn_on, when the light is exactly at 500, any electrical noise will cause it to read 499, 501, 499… and the lamp will flicker like crazy.
To avoid this, we use Hysteresis (or a margin window).
from machine import Pin, ADC
import time
ldr = ADC(Pin(26)) # ADC Pin (GP26 on Pico, GPIO34 on ESP32)
relay = Pin(16, Pin.OUT)
# Separate thresholds
NIGHT_THRESHOLD = 20000 # If it drops below this, we turn on
DAY_THRESHOLD = 25000 # If it rises above this, we turn off
# Current state
lights_on = False
while True:
light = ldr.read_u16()
print(f"Light level: {light}")
if lights_on and light > DAY_THRESHOLD:
print("It's daytime now. Turning off.")
relay.value(0)
lights_on = False
elif not lights_on and light < NIGHT_THRESHOLD:
print("It's nighttime. Turning on.")
relay.value(1)
lights_on = True
time.sleep(0.5)With this code, the light must change significantly to change the state, avoiding flickering at dusk.
Precautions Worth Remembering
With these three components (HC-SR04, DHT22, and LDR), you have the basic needs for environmental perception covered:
- Use voltage dividers with 5V sensors (like the HC-SR04).
- Respect refresh times (2s for the DHT).
- Implement hysteresis in analog sensors to avoid instability.
Now your code can react to what’s happening outside the chip!