📱 Level 5 – App Communication

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:

  1. Setup DHT11 on Pin 26.
  2. Setup ADC on Pin 32.
  3. Setup raindrop digital input on Pin 35.
  4. 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:

  1. Use the values from Microproject 5.8.1.
  2. Print “TEMP:xx”, “SOIL:%xx”, “RAIN:0/1”.
  3. 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:

  1. Set thresholds: temp_high = 32°C, soil_low = 30%, rain_wet = 1.
  2. If crossing → print alerts.
  3. 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:

  1. Create history = [].
  2. Append (temp, soil%, rain).
  3. Limit to last 5 entries.
  4. 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:

  1. Setup pump pin on 23.
  2. Define handle_method to read incoming command.
  3. If ON → pin HIGH; if OFF → pin LOW.
  4. 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:

  1. Setup sensors on 26, 32, 35 and actuator on 23.
  2. Read and print values (TEMP, SOIL%, RAIN).
  3. Check thresholds and print alerts.
  4. Save to history and print it.
  5. 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.
On this page