Project 4.8: "Environmental Monitoring System"
What you’ll learn
- Goal 1: Continuously monitor temperature, humidity, and motion using official DHT11 and PIR blocks.
- Goal 2: Trigger alerts when movement or environmental conditions exceed safe limits.
- Goal 3: Show integrated status on the OLED screen with clear, framed text.
- Goal 4: Keep a simple event log via Serial prints so nothing is missed.
- Goal 5: Transmit data to the PC using short, readable messages.
Blocks glossary
- dhtx.DHT11(pin).temperature(): Reads temperature in Celsius directly from the sensor.
- dhtx.DHT11(pin).humidity(): Reads humidity in percent directly from the sensor.
- machine.Pin(pin, machine.Pin.IN).value(): Digital read for PIR motion (0/1).
- oled128x64.OLED(i2c,…).shows(…), rect(…), show(): Draws framed text and updates the OLED.
- print(…): Serial output to PC for event logs and data messages.
- while True / if / time.sleep_ms(ms): Control flow and pacing for continuous monitoring.
What you need
| Part | How many? | Pin connection (R32) |
|---|---|---|
| D1 R32 | 1 | USB cable |
| DHT11 | 1 | Signal → Pin 26 (VCC 5V, GND) |
| PIR motion sensor | 1 | Signal → Pin 23 (VCC 5V, GND) |
| OLED 128×64 | 1 | SCL → Pin 22, SDA → Pin 21, VCC, GND |
- Share GND for all components.
- Place the PIR facing the monitored area; keep the OLED cables short for stability.
Before you start
- Wire DHT11 to Pin 26, PIR to Pin 23, and OLED I2C to SCL 22 and SDA 21.
- Open the Serial monitor to see logs and data messages.
- Quick test:
print("Ready!") # Serial: confirm the board and monitor are working
Microprojects 1–5
Microproject 4.8.1 – Continuous monitoring of conditions
import dhtx # Load official DHT11 library
import machine # Load hardware library for PIR input
import time # Load time library for pacing
print("[Monitor] Start") # Serial: start message
t = dhtx.DHT11(26).temperature() # Read temperature (°C) directly from Pin 26
h = dhtx.DHT11(26).humidity() # Read humidity (%) directly from Pin 26
pir = machine.Pin(23, machine.Pin.IN) # Create PIR input on Pin 23
print("[Monitor] T=", t, "C H=", h, "%") # Serial: show environmental values
print("[Monitor] PIR=", pir.value()) # Serial: show motion state (0/1)
time.sleep_ms(800) # Small delay to observe output calmly
Reflection: You performed a clean, single pass read of environment and motion.
Challenge: Repeat the read two more times (with 800 ms delay) and observe if PIR changes.
Microproject 4.8.2 – Alerts for movement and environmental conditions
import dhtx # Load DHT11 library
import machine # Load hardware for PIR
import time # Load time for pacing
TH_T_MAX = 30 # Temperature alert threshold in °C
TH_H_MAX = 80 # Humidity alert threshold in %
pir = machine.Pin(23, machine.Pin.IN) # PIR motion sensor on Pin 23
t = dhtx.DHT11(26).temperature() # Read temperature
h = dhtx.DHT11(26).humidity() # Read humidity
m = pir.value() # Read motion state (0/1)
print("[Alert] T=", t, "C H=", h, "% PIR=", m) # Serial: compact status line
if (t >= TH_T_MAX) or (h >= TH_H_MAX): # If environment exceeds thresholds
print("ALERT:ENV:T="+str(t)+";H="+str(h)) # Serial: environment alert message
else: # Otherwise environment ok
print("OK:ENV:T="+str(t)+";H="+str(h)) # Serial: environment ok message
if m == 1: # If motion detected by PIR
print("ALERT:MOTION=1") # Serial: motion alert message
else: # No motion detected
print("OK:MOTION=0") # Serial: motion ok message
time.sleep_ms(800) # Calm cadence before next cycle
Reflection: You created clear alert messages for environment and motion.
Challenge: Add a single “ALERT:ALL” when both environment and motion are triggered at once.
Microproject 4.8.3 – Integrated OLED display
import machine # Load hardware for I2C/OLED
import oled128x64 # Load official OLED library
import dhtx # Load DHT11 library
import time # Load time for pacing
i2c_extend = machine.SoftI2C( # Create I2C bus for OLED
scl = machine.Pin(22), # SCL pin set to 22
sda = machine.Pin(21), # SDA pin set to 21
freq = 100000 # Bus frequency at 100 kHz
) # End I2C setup
oled = oled128x64.OLED( # Create OLED object
i2c_extend, # Pass the I2C bus
address = 0x3c, # OLED I2C address
font_address = 0x3A0000, # Font resource address
types = 0 # SSD1306 type
) # End OLED creation
t = dhtx.DHT11(26).temperature() # Read temperature from DHT11 (Pin 26)
h = dhtx.DHT11(26).humidity() # Read humidity from DHT11 (Pin 26)
oled.rect(0,0,128,64,1) # Draw a border frame on the screen
oled.shows("EnvMon",4,4,1,0,False) # Title text “EnvMon” at top-left
oled.shows("T:"+str(t)+"C",4,24,1,0,False) # Show temperature value line
oled.shows("H:"+str(h)+"%",4,40,1,0,False) # Show humidity value line
oled.show() # Update screen to display the content
print("[OLED] T/H rendered") # Serial: confirm OLED draw
time.sleep_ms(800) # Short pause to observe
Reflection: You framed the OLED and displayed the key readings in a tidy layout.
Challenge: Add “M:0” or “M:1” (motion state) under humidity so the screen shows all three.
Microproject 4.8.4 – Event log (Serial)
import dhtx # Load DHT11 library
import machine # Load hardware for PIR input
import time # Load time for pacing
pir = machine.Pin(23, machine.Pin.IN) # PIR input pin on 23
print("[Log] Start cycle") # Serial: event log start
t = dhtx.DHT11(26).temperature() # Read temperature
h = dhtx.DHT11(26).humidity() # Read humidity
m = pir.value() # Read motion
print("LOG:T="+str(t)+";H="+str(h)+";M="+str(m)) # Serial: structured one-line event
print("[Log] End cycle") # Serial: event log end
time.sleep_ms(800) # Calm delay for readability
Reflection: You kept a simple text log line so the PC can record events without complex data structures.
Challenge: Prefix log lines with “ALERT” when thresholds or motion are triggered, otherwise “OK”.
Microproject 4.8.5 – Data transmission to PC
import dhtx # Load DHT11 library
import machine # Load PIR input
import time # Load time for pacing
pir = machine.Pin(23, machine.Pin.IN) # PIR pin input on 23
t = dhtx.DHT11(26).temperature() # Read temperature
h = dhtx.DHT11(26).humidity() # Read humidity
m = pir.value() # Read motion state
msg = "DATA:T="+str(t)+";H="+str(h)+";M="+str(m) # Compose short data message for PC
print(msg) # Serial: transmit the data line
time.sleep_ms(800) # Small delay to keep cadence friendly
Reflection: A single, compact “DATA:” line makes it easy for a PC program to read values.
Challenge: Send “DATA:” once per second for 5 seconds, observing stability (pace with sleep_ms).
Main project
Environmental monitoring system with continuous reads, alerts, OLED display, event log, and PC transmission
- Read sensors: DHT11 for T/H and PIR for motion.
- Alerts: Environment thresholds and motion trigger clear messages.
- OLED: Framed status page for T/H/M.
- Log: Serial one-line events for PC collection.
- Transmission: Short “DATA:” messages sent regularly.
import dhtx # Load DHT11 sensor library
import machine # Load hardware for PIR and I2C
import oled128x64 # Load official OLED display library
import time # Load time library for pacing
# --- Setup PIR ---
pir = machine.Pin(23, machine.Pin.IN) # Create PIR input on Pin 23
print("[Main] PIR=23 ready") # Serial: confirm PIR setup
# --- Setup OLED ---
i2c_extend = machine.SoftI2C( # Create I2C bus for OLED
scl = machine.Pin(22), # SCL pin 22
sda = machine.Pin(21), # SDA pin 21
freq = 100000 # I2C frequency 100 kHz
) # End I2C setup
oled = oled128x64.OLED( # Create OLED object
i2c_extend, # Pass I2C bus
address = 0x3c, # OLED I2C address
font_address = 0x3A0000, # Font resource address
types = 0 # SSD1306 type
) # End OLED creation
# --- Thresholds ---
TH_T_MAX = 30 # Temperature alert threshold in °C
TH_H_MAX = 80 # Humidity alert threshold in %
print("[Main] TH_T_MAX=", TH_T_MAX, "TH_H_MAX=", TH_H_MAX) # Serial: show thresholds
# --- Continuous monitoring loop ---
while True: # Begin main loop
t = dhtx.DHT11(26).temperature() # Read temperature (°C) from DHT11 on Pin 26
h = dhtx.DHT11(26).humidity() # Read humidity (%) from DHT11 on Pin 26
m = pir.value() # Read motion state (0/1) from PIR on Pin 23
env_alert = (t >= TH_T_MAX) or (h >= TH_H_MAX) # Compute environment alert condition (True/False)
mot_alert = (m == 1) # Compute motion alert condition (True/False)
# --- OLED page ---
oled.rect(0,0,128,64,1) # Draw frame around display
oled.shows("EnvMon",4,4,1,0,False) # Title text
oled.shows("T:"+str(t)+"C",4,24,1,0,False) # Show temperature
oled.shows("H:"+str(h)+"%",4,40,1,0,False) # Show humidity
if m == 1: # If motion detected
oled.shows("M:1",84,4,1,0,False) # Show motion flag at top-right
else: # If no motion
oled.shows("M:0",84,4,1,0,False) # Show no motion flag
oled.show() # Update screen content
# --- Alerts + Log ---
if env_alert and mot_alert: # If both alerts are active
print("ALERT:ALL:T="+str(t)+";H="+str(h)+";M="+str(m)) # Serial: combined alert
elif env_alert: # If only environment alert
print("ALERT:ENV:T="+str(t)+";H="+str(h)) # Serial: environment alert
elif mot_alert: # If only motion alert
print("ALERT:MOTION=1") # Serial: motion alert
else: # Otherwise all ok
print("OK:T="+str(t)+";H="+str(h)+";M="+str(m)) # Serial: ok status line
# --- Data message for PC ---
print("DATA:T="+str(t)+";H="+str(h)+";M="+str(m)) # Serial: data line for PC
time.sleep_ms(1000) # 1-second cadence for calm monitoring
External explanation
This system uses only official blocks: DHT11 for temperature/humidity, a PIR for motion, and the OLED for display. Alerts are simple boolean checks and printed messages, while the OLED shows a tidy status page. The event log and data lines use Serial prints, which the PC can capture without any extra data structures.
Story time
Your robot quietly watches the room. It breathes out numbers, frames them on a little screen, and speaks up when something’s off—heat, humidity, motion. Calm, watchful, and helpful.
Debugging (2)
Debugging 4.8.A – Alert overload
# Reduce alert spam by pacing updates and only printing once per second
print("[Debug] Pace at 1000 ms to avoid spam") # Serial: pacing tip
# If PIR is noisy, add a short confirmation pause before declaring motion:
# time.sleep_ms(200) # Brief confirmation delay
# if pir.value() == 1: print("ALERT:MOTION=1") # Only alert if still high
Debugging 4.8.B – Data loss
# Keep messages short and consistent so the PC can parse them easily
print("[Debug] Use compact DATA lines like DATA:T=..;H=..;M=..") # Serial: format tip
# Maintain a steady cadence (1000 ms) and avoid long bursts of prints in one loop
Final checklist
- DHT11 reads temperature and humidity reliably at Pin 26.
- PIR motion on Pin 23 reports 0/1 correctly.
- OLED shows T/H and motion flag with a clear frame.
- Alerts print clearly for environment and motion.
- DATA lines transmit to the PC once per second.
Extras
- Student tip: Adjust thresholds (e.g., T=28–35, H=70–85) to match your room conditions.
- Instructor tip: Ask students to design a consistent message format and explain their choices.
- Glossary:
- Threshold: Value that triggers a state change (e.g., alert).
- Cadence: The rhythm of readings and prints in the loop.
- PIR: Passive Infrared sensor for motion.
- Mini tips:
- Face the PIR toward the area of interest; avoid direct sunlight.
- Keep the OLED text short to avoid clipping.
- Hold a stable 1000 ms loop for readable updates.