Project 5.8: "Weather Station with App"
What you’ll learn
- ✅ Multi-sensor reading: Collect data from DHT11 (temp), soil moisture (analog), and raindrop (digital).
- ✅ App messages: Send clean “KEY:VALUE” strings the app can display.
- ✅ Smart alerts: Trigger app notifications when conditions cross thresholds.
- ✅ History logs: Keep a short memory of recent readings.
- ✅ Actuator control: Turn a fan or “pump” ON/OFF from the app.
Key ideas
- Short definition: Sensors measure the world; we send simple messages so the app can understand and act.
- Real-world link: Weather stations and smart gardens report conditions and trigger alerts automatically.
Blocks glossary (used in this project)
- DHT11 temperature: Reads air temperature in Celsius.
- Analog input (ADC): Reads soil moisture as a number (0–4095).
- Digital input: Reads raindrop sensor (1 = wet trigger, 0 = dry).
- Serial println: Sends clear text messages to the app.
- Variables: Store thresholds, logs, and actuator states.
- if / else: Create alerts and control actuators based on conditions.
What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| DHT11 | 1 | Signal → Pin 26, VCC, GND |
| Soil humidity sensor | 1 | Signal → Pin 32 (ADC), VCC, GND |
| Raindrop detection sensor | 1 | DO (digital out) → Pin 35 (input), VCC, GND |
| L9110 fan module (optional) | 1 | IN1 → Pin 23 (actuator control), VCC, GND |
🔌 Wiring tip: Pins 34–39 are input-only; use Pin 35 for raindrop DO. Keep the soil sensor signal on Pin 32 (ADC). DHT11 goes to Pin 26.
📍 Pin map snapshot: Using 26 (DHT), 32 (soil ADC), 35 (raindrop DO), 23 (actuator). Other pins are free.
Before you start
- USB cable is plugged in
- Serial monitor is open
- Test print shows:
print("Ready!") # Confirm serial is working
Microprojects (5 mini missions)
Microproject 5.8.1 – Acquisition of data from multiple sensors
Goal: Read DHT11, soil moisture (raw + %), and raindrop (wet/dry).
Blocks used:
- DHT11 temperature: Get “temp_c”.
- Analog input (ADC): Read soil “raw”.
- Digital input: Read raindrop “wet” (1/0).
- Serial println: Print all values.
Block sequence:
- Setup DHT11 on Pin 26.
- Setup ADC on Pin 32.
- Setup raindrop digital input on Pin 35.
- Read and print all values.
MicroPython code:
import dhtx # Import DHT11 sensor library
import machine # Import machine to access ADC and Pin
sensor = dhtx.DHT11(26) # Create DHT11 on Pin 26
print("Microproject 5.8.1: DHT11 ready on Pin 26") # Status message
adc32 = machine.ADC(machine.Pin(32)) # Create ADC on Pin 32 for soil sensor
adc32.atten(machine.ADC.ATTN_11DB) # Set ADC attenuation for full-scale reading
adc32.width(machine.ADC.WIDTH_12BIT) # Use 12-bit resolution (0–4095)
print("ADC ready on Pin 32 (soil)") # Status message
rain = machine.Pin(35, machine.Pin.IN) # Create digital input for raindrop on Pin 35
print("Raindrop input ready on Pin 35") # Status message
temp_c = sensor.temperature() # Read temperature in Celsius
print("TEMP(C):", temp_c) # App-friendly temperature output
raw = adc32.read() # Read raw soil moisture value
print("SOIL_RAW:", raw) # Show raw number for calibration
moisture_pct = int((4095 - raw) * 100 / 4095) # Convert to percentage (higher=wet)
print("SOIL(%):", moisture_pct) # Show mapped moisture percent
wet = rain.value() # Read raindrop wet/dry (1=wet trigger, 0=dry)
print("RAIN:", wet) # App-friendly raindrop state
Reflection: You now have three senses: temperature, soil moisture, and rain—great weather data!
Challenge:
- Easy: Print humidity later using DHT11 if available in your library.
- Harder: Average the soil reading from 3 samples.
Microproject 5.8.2 – Data transmission to app
Goal: Send clear “KEY:VALUE” strings for app display.
Blocks used:
- Serial println: Output TEMP, SOIL, RAIN messages.
Block sequence:
- Use the values from Microproject 5.8.1.
- Print “TEMP:xx”, “SOIL:%xx”, “RAIN:0/1”.
- Keep consistent formats.
MicroPython code:
temp_c = 25 # Simulated temperature value for demonstration
print("Microproject 5.8.2: Send to app") # Status message
moisture_pct = 42 # Simulated soil moisture percent
print("TEMP:", temp_c) # App-friendly temperature string
print("SOIL:", moisture_pct) # App-friendly soil percent string
rain_state = 0 # Simulated raindrop state (0=dry, 1=wet)
print("RAIN:", rain_state) # App-friendly raindrop state
Reflection: Short labels + numbers are easiest for apps to read and show.
Challenge:
- Easy: Add “TIME:ms” using a system counter.
- Harder: Add “PLACE:GARDEN_A” to tag your station.
Microproject 5.8.3 – App alerts based on conditions
Goal: Print alert strings when thresholds are crossed.
Blocks used:
- if / else: Decide alerts.
- Serial println: Output “ALERT:…” messages.
Block sequence:
- Set thresholds: temp_high = 32°C, soil_low = 30%, rain_wet = 1.
- If crossing → print alerts.
- Else → print “STATUS:OK”.
MicroPython code:
temp_c = 33 # Example temp for testing alerts
moisture_pct = 25 # Example soil moisture percent
rain_state = 1 # Example rain state (1 = wet trigger)
print("Microproject 5.8.3: Checking alerts...") # Status message
temp_high = 32 # Threshold for heat alert in Celsius
soil_low = 30 # Threshold for dry soil alert in percent
rain_wet = 1 # Value indicating rain detected
if temp_c >= temp_high: # Check temperature condition
print("ALERT:HEAT") # Print heat alert
if moisture_pct < soil_low: # Check soil moisture condition
print("ALERT:DRY_SOIL") # Print dry soil alert
if rain_state == rain_wet: # Check rain condition
print("ALERT:RAIN") # Print rain alert
if (temp_c < temp_high) and (moisture_pct >= soil_low) and (rain_state != rain_wet): # All OK
print("STATUS:OK") # Show normal status
Reflection: Alerts help the app react fast—set thresholds that make sense for your place.
Challenge:
- Easy: Add “ALERT:COLD” if temp < 15°C.
- Harder: Add “ALERT:FLOOD_RISK” if rain_state==1 and soil > 80%.
Microproject 5.8.4 – Historical data in app
Goal: Keep a short log of recent readings and print it.
Blocks used:
- Variables (list): Store tuples of readings.
- Serial println: Print last few entries.
Block sequence:
- Create history = [].
- Append (temp, soil%, rain).
- Limit to last 5 entries.
- Print “LOG:…”.
MicroPython code:
history = [] # Create an empty list for recent readings
print("Microproject 5.8.4: History log ready") # Status message
def add_reading(temp_c, soil_pct, rain_state): # Define function to add a reading tuple
history.append((temp_c, soil_pct, rain_state)) # Append tuple to history
if len(history) > 5: # Keep only the last 5 entries
history.pop(0) # Remove the oldest entry
print("LOG:ADDED", (temp_c, soil_pct, rain_state)) # Confirm addition
def print_history(): # Define function to print current history
print("LOG:CURRENT") # Header for log
for item in history: # Iterate through saved readings
print("LOG:ITEM", item) # Print each reading tuple
add_reading(25, 40, 0) # Add example reading
add_reading(26, 38, 0) # Add example reading
add_reading(28, 33, 1) # Add example reading
print_history() # Print the history entries
Reflection: Small logs make apps more useful—users like seeing trends, not just one number.
Challenge:
- Easy: Increase history length to 10.
- Harder: Print averages over the history before the list.
Microproject 5.8.5 – Control of actuators from an app
Goal: Turn the fan/pump ON/OFF using app words.
Blocks used:
- Digital output: Control Pin 23.
- Receive callback: Detect “PUMP:ON/OFF” or “FAN:ON/OFF”.
- Serial println: Print the result.
Block sequence:
- Setup pump pin on 23.
- Define handle_method to read incoming command.
- If ON → pin HIGH; if OFF → pin LOW.
- Print “PUMP:ON/OFF”.
MicroPython code:
import machine # Import machine to control the actuator pin
pump = machine.Pin(23, machine.Pin.OUT) # Create actuator control pin on 23
print("Microproject 5.8.5: Actuator pin ready on 23") # Status message
def handle_method(key1, key2, key3, keyx): # Define callback for app control
msg = str(key1) # Convert main payload to text
print("APP->R32:", msg) # Show incoming message
if msg == "PUMP:ON": # Check for ON command
pump.value(1) # Turn actuator ON
print("PUMP:ON") # Confirm state
elif msg == "PUMP:OFF": # Check for OFF command
pump.value(0) # Turn actuator OFF
print("PUMP:OFF") # Confirm state
elif msg == "FAN:ON": # Alternative label
pump.value(1) # Turn ON
print("FAN:ON") # Confirm state
elif msg == "FAN:OFF": # Alternative label
pump.value(0) # Turn OFF
print("FAN:OFF") # Confirm state
else: # Not a known actuator command
print("CMD:UNKNOWN") # Explain unknown command
Reflection: Simple words can control real hardware—keep command names clear and safe.
Challenge:
- Easy: Add “PUMP:TOGGLE”.
- Harder: Add “PUMP:ON:3s” to run for a timed cycle.
Main project – Weather station with app
Blocks steps (with glossary)
- DHT11 + soil + raindrop: Read all sensors and map soil to percent.
- App messages: Print TEMP, SOIL, RAIN.
- Alerts: Print ALERT strings when thresholds are crossed.
- History: Keep last 5 readings and print them.
- Actuator control: Respond to “PUMP/FAN” commands from the app.
Block sequence:
- Setup sensors on 26, 32, 35 and actuator on 23.
- Read and print values (TEMP, SOIL%, RAIN).
- Check thresholds and print alerts.
- Save to history and print it.
- Listen for actuator commands and act.
MicroPython code (mirroring blocks)
# Project 5.8 – Weather Station with App
import dhtx # Read DHT11 temperature sensor
import machine # Control ADC and Pins
import time # Provide timing for loops and delays
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
print("DHT11 ready on Pin 26") # Confirm sensor setup
adc32 = machine.ADC(machine.Pin(32)) # Soil sensor ADC on Pin 32
adc32.atten(machine.ADC.ATTN_11DB) # Configure attenuation for full scale
adc32.width(machine.ADC.WIDTH_12BIT) # Use 12-bit resolution (0–4095)
print("ADC ready on Pin 32 (soil)") # Confirm ADC setup
rain = machine.Pin(35, machine.Pin.IN) # Raindrop DO input on Pin 35
print("Raindrop input ready on Pin 35") # Confirm raindrop input
pump = machine.Pin(23, machine.Pin.OUT) # Actuator (fan/pump) control on Pin 23
pump.value(0) # Ensure actuator starts OFF
print("Actuator ready on Pin 23 (OFF)") # Confirm actuator setup
temp_high = 32 # Temperature alert threshold (Celsius)
soil_low = 30 # Soil dryness threshold (percent)
print("Thresholds -> TEMP_HIGH:", temp_high, "SOIL_LOW:", soil_low) # Show thresholds
history = [] # Short list of recent readings
print("History log initialized") # Confirm history setup
def add_reading(temp_c, soil_pct, rain_state): # Helper to add a reading
history.append((temp_c, soil_pct, rain_state)) # Append the reading tuple
if len(history) > 5: # Keep last 5
history.pop(0) # Remove oldest entry
print("LOG:ADDED", (temp_c, soil_pct, rain_state)) # Confirm addition
def print_history(): # Helper to print history
print("LOG:CURRENT") # Header
for item in history: # Iterate entries
print("LOG:ITEM", item) # Print each item
def handle_method(key1, key2, key3, keyx): # Callback to control actuator via app
msg = str(key1) # Convert payload to text
print("APP->R32:", msg) # Show incoming app message
if msg == "PUMP:ON": # Turn actuator ON
pump.value(1) # Set pin HIGH
print("PUMP:ON") # Confirm
elif msg == "PUMP:OFF": # Turn actuator OFF
pump.value(0) # Set pin LOW
print("PUMP:OFF") # Confirm
elif msg == "FAN:ON": # Alternative label
pump.value(1) # Set pin HIGH
print("FAN:ON") # Confirm
elif msg == "FAN:OFF": # Alternative label
pump.value(0) # Set pin LOW
print("FAN:OFF") # Confirm
else: # Unknown command
print("CMD:UNKNOWN") # Explain
while True: # Main loop to read sensors and report to the app
temp_c = sensor.temperature() # Read temperature from DHT11
print("TEMP:", temp_c) # App-friendly temperature output
raw = adc32.read() # Read soil raw value
print("SOIL_RAW:", raw) # Print raw for calibration
soil_pct = int((4095 - raw) * 100 / 4095) # Convert raw to percent (higher=wet)
print("SOIL:", soil_pct) # App-friendly soil percent
rain_state = rain.value() # Read raindrop (1=wet trigger)
print("RAIN:", rain_state) # App-friendly rain state
if temp_c >= temp_high: # Check heat alert
print("ALERT:HEAT") # Print heat alert
if soil_pct < soil_low: # Check dry soil alert
print("ALERT:DRY_SOIL") # Print dry soil alert
if rain_state == 1: # Check rain alert
print("ALERT:RAIN") # Print rain alert
if (temp_c < temp_high) and (soil_pct >= soil_low) and (rain_state != 1): # All OK
print("STATUS:OK") # Normal status message
add_reading(temp_c, soil_pct, rain_state) # Save to history
print_history() # Print the last few readings
time.sleep(2) # Wait 2 seconds before next reading for readability
External explanation
- What it teaches: You built a small weather station that reads sensors, reports to an app, triggers alerts, logs history, and controls a simple actuator.
- Why it works: The D1 R32 reads digital and analog signals; mapping the soil value to percent makes it human-friendly; short “KEY:VALUE” prints let apps parse and display data easily; condition checks trigger alerts; a list keeps your last readings.
- Key concept: “Sense → report → alert → remember → act.”
Story time
You’ve turned your robot into a tiny meteorologist. It watches the sky (rain), checks the ground (soil), feels the air (temperature), and sends updates like a smart garden station.
Debugging (2)
Debugging 5.8.A – Data loss during transmission
Problem: The app shows missing values or mixed lines.
Clues: Prints are too fast; the app misses some messages.
Broken code:
print("TEMP:", temp_c) # Rapid prints
print("SOIL:", soil_pct) # Rapid prints
print("RAIN:", rain_state) # Rapid prints
# No pacing between messages
Fixed code:
print("TEMP:", temp_c) # Send temperature
time.sleep(0.05) # Short pause helps the app read
print("SOIL:", soil_pct) # Send soil percent
time.sleep(0.05) # Short pause
print("RAIN:", rain_state) # Send rain state
time.sleep(0.05) # Short pause
Why it works: Tiny delays let the app process messages in order and avoid drops.
Avoid next time: Pace your prints or bundle them into one line if needed.
Debugging 5.8.B – Alerts are not being sent
Problem: Conditions cross thresholds, but no “ALERT” appears.
Clues: You see temperature/soil messages, but no alert lines.
Broken code:
if temp_c > temp_high: # Excludes equal case
print("ALERT:HEAT") # Heat alert
Fixed code:
if temp_c >= temp_high: # Include equal case
print("ALERT:HEAT") # Heat alert fires reliably
Why it works: Including equality prevents missing the exact threshold value.
Avoid next time: Decide and code whether equality should trigger an alert.
Final checklist
- Temperature, soil percent, and rain state printed clearly
- Alerts appeared when crossing thresholds
- History showed the last few readings
- Actuator responded to “PUMP/FAN” commands
- App displayed “KEY:VALUE” messages without confusion
Extras
- 🧠 Student tip: Add “UNIT:C” or “UNIT:F” to switch temperature units (convert when needed).
- 🧑🏫 Instructor tip: Have students measure raw soil values wet vs dry to set a realistic soil_low threshold.
- 📖 Glossary:
- Percent mapping: Convert a raw number into 0–100% so humans can read it easily.
- Alert: A message printed when a condition crosses a threshold.
- History log: A short list of past readings to show trends.
- 💡 Mini tips:
- Use short delays between prints so apps don’t miss messages.
- Keep wiring stable; noisy wires cause bad readings.
- Label your station (PLACE) when using multiple setups.