Project 5.4: "Fan Control by Temperature"
What you’ll learn
- ✅ Read temperature: Use DHT11 to get temperature in degrees Celsius.
- ✅ Control a fan: Turn the L9110 fan ON/OFF when it’s too hot.
- ✅ Speed by temperature: Adjust fan speed with PWM based on the temperature.
- ✅ Modes: Switch between automatic and manual control.
- ✅ App-ready: Print clean status messages for easy app monitoring.
Key ideas
- Short definition: A sensor gives numbers; we use those numbers to decide and act.
- Real-world link: Thermostats turn fans on when rooms get warm to stay comfortable.
Blocks glossary (used in this project)
- DHT11 temperature: Reads the current temperature from the sensor.
- Digital output (Pin): Turns the fan ON/OFF.
- PWM (fan speed): Sets how fast the fan spins by changing duty.
- if / else: Decides actions based on a threshold.
- Variable: Stores mode, threshold, and speed.
- Serial println: Shows what the robot is doing (for debugging and apps).
What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| DHT11 sensor | 1 | Signal → Pin 26, VCC, GND |
| L9110 fan module | 1 | IN1 → Pin 23 (fan control), VCC, GND |
🔌 Wiring tip: Keep DHT11 data wire short and stable. Connect DHT11 signal to Pin 26. Connect L9110 IN1 to Pin 23 for ON/OFF or PWM control.
📍 Pin map snapshot: Using pins 26 (DHT11) and 23 (fan). Others remain free for displays/apps.
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.4.1 – Temperature reading
Goal: Read and print the temperature from DHT11.
Blocks used:
- DHT11 temperature: Get degrees Celsius.
- Serial println: Show the value.
Block sequence:
- Initialize DHT11 on Pin 26.
- Read temperature.
- Print it.
MicroPython code:
import dhtx # Import DHT library for temperature
sensor = dhtx.DHT11(26) # Create DHT11 sensor on Pin 26
temp_c = sensor.temperature() # Read temperature in Celsius
print("Microproject 5.4.1: Temperature (C) =", temp_c) # Show the reading
Reflection: Start by trusting the numbers—this is your thermometer.
Challenge:
- Easy: Read twice and print the average.
- Harder: Label “HOT” if temp ≥ 30, else “OK”.
Microproject 5.4.2 – Fan activation by temperature
Goal: Turn the fan ON if temperature is above a threshold.
Blocks used:
- Digital output: Control Pin 23.
- if / else: Compare temp to threshold.
Block sequence:
- Set threshold = 28°C.
- Read temperature.
- If temp ≥ threshold → fan ON; else OFF.
- Print status.
MicroPython code:
import dhtx # Import DHT library
import machine # Import machine for Pin control
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
fan = machine.Pin(23, machine.Pin.OUT) # Fan control pin on 23
threshold_c = 28 # Temperature threshold in Celsius
print("Microproject 5.4.2: Threshold (C) =", threshold_c) # Show chosen threshold
temp_c = sensor.temperature() # Read current temperature
print("Temperature (C) =", temp_c) # Show temp
if temp_c >= threshold_c: # Decide based on threshold
fan.value(1) # Turn fan ON
print("Fan: ON (too warm)") # Explain action
else:
fan.value(0) # Turn fan OFF
print("Fan: OFF (comfortable)") # Explain action
Reflection: Thresholds are simple rules—above means “act,” below means “wait.”
Challenge:
- Easy: Change threshold to 26°C.
- Harder: Print “ALERT:HEAT” when temp ≥ 32°C.
Microproject 5.4.3 – Temperature-controlled speed (PWM)
Goal: Use PWM to set fan speed from 0–100% based on temperature.
Blocks used:
- PWM duty: Change speed smoothly.
- Math map: Convert temperature to a duty value.
Block sequence:
- Create PWM on Pin 23, freq=2000 Hz.
- Map temp range to duty (0–1023).
- Apply duty and print speed %.
MicroPython code:
import dhtx # Import DHT library
import machine # Import machine for PWM
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
pwm = machine.PWM(machine.Pin(23)) # Create PWM on Pin 23
pwm.freq(2000) # Set PWM frequency to 2000 Hz
print("Microproject 5.4.3: PWM initialized at 2000 Hz") # Confirm PWM setup
temp_c = sensor.temperature() # Read temperature
print("Temperature (C) =", temp_c) # Show temp
temp_min = 20 # Start of fan ramp (no spin below this)
temp_max = 32 # Full speed at this temperature
print("Ramp range (C):", temp_min, "to", temp_max) # Explain ramp
# Constrain temperature to the ramp range
if temp_c < temp_min: # Below ramp start
temp_c = temp_min # Clamp to minimum
print("Clamped to temp_min:", temp_c) # Explain clamp
if temp_c > temp_max: # Above ramp end
temp_c = temp_max # Clamp to maximum
print("Clamped to temp_max:", temp_c) # Explain clamp
# Map temperature to duty (0–1023)
duty = int((temp_c - temp_min) * 1023 / (temp_max - temp_min)) # Linear map to duty
print("PWM duty (0-1023) =", duty) # Show duty
pwm.duty(duty) # Apply duty to control speed
speed_pct = int(duty * 100 / 1023) # Convert duty to percent
print("Fan speed % =", speed_pct) # Show speed percent
Reflection: PWM lets the fan spin gently or fast—smooth control feels smart.
Challenge:
- Easy: Change temp_max to 30°C for quicker full speed.
- Harder: Add a minimum duty (e.g., 200) so the fan starts reliably.
Microproject 5.4.4 – Automatic and manual mode
Goal: Switch between AUTO (sensor decides) and MANUAL (you decide).
Blocks used:
- Variable mode: Store “AUTO” or “MANUAL”.
- if / else: Choose behavior.
- PWM/digital: Set speed or ON/OFF based on mode.
Block sequence:
- mode = “AUTO”.
- If AUTO → use temperature ramp.
- If MANUAL → set fixed speed or ON/OFF.
- Print mode and action.
MicroPython code:
import dhtx # Import DHT library
import machine # Import machine for PWM
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
pwm = machine.PWM(machine.Pin(23)) # PWM on Pin 23
pwm.freq(2000) # Set PWM frequency
print("Microproject 5.4.4: PWM ready (2000 Hz)") # Confirm PWM
mode = "AUTO" # Choose mode: "AUTO" or "MANUAL"
print("Mode =", mode) # Show current mode
if mode == "AUTO": # Automatic control by temperature
temp_c = sensor.temperature() # Read temperature
print("Temperature (C) =", temp_c) # Show temp
temp_min, temp_max = 20, 32 # Ramp range
print("Ramp:", temp_min, "to", temp_max) # Explain range
if temp_c < temp_min: # Clamp low
temp_c = temp_min # Clamp
print("Clamped low to", temp_c) # Explain
if temp_c > temp_max: # Clamp high
temp_c = temp_max # Clamp
print("Clamped high to", temp_c) # Explain
duty = int((temp_c - temp_min) * 1023 / (temp_max - temp_min)) # Map temp to duty
print("Duty =", duty) # Show duty
pwm.duty(duty) # Apply duty
print("Fan: AUTO speed applied") # Confirm
else: # Manual control
duty = 700 # Fixed duty in manual mode
print("Manual duty =", duty) # Show chosen duty
pwm.duty(duty) # Apply duty
print("Fan: MANUAL speed applied") # Confirm
Reflection: Modes give you flexibility—sometimes you want full control, sometimes you want automation.
Challenge:
- Easy: Set MANUAL duty to 400.
- Harder: Add a manual ON/OFF toggle variable.
Microproject 5.4.5 – Integration with monitoring app
Goal: Print clean strings your app can display.
Blocks used:
- Serial println: Output “TEMP”, “FAN”, and “MODE” data.
- Variable: Store current duty and state.
Block sequence:
- Read temp.
- Compute duty and speed %.
- Print strings: TEMP:xx, FAN:ON/OFF or FAN:SPEED:xx, MODE:xxx.
MicroPython code:
import dhtx # Import DHT library
import machine # Import machine for PWM
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
pwm = machine.PWM(machine.Pin(23)) # PWM on Pin 23
pwm.freq(2000) # Set PWM frequency
mode = "AUTO" # Choose current mode
print("Microproject 5.4.5: Mode =", mode) # Show mode
temp_c = sensor.temperature() # Read temperature
print("TEMP:", temp_c) # App-friendly temperature string
# Compute duty based on AUTO mode ramp (or fixed in MANUAL)
if mode == "AUTO": # Automatic mapping
temp_min, temp_max = 20, 32 # Ramp range
if temp_c < temp_min: # Clamp low
temp_c = temp_min # Clamp
if temp_c > temp_max: # Clamp high
temp_c = temp_max # Clamp
duty = int((temp_c - temp_min) * 1023 / (temp_max - temp_min)) # Map temp to duty
else: # Manual fixed duty
duty = 700 # Fixed manual speed
pwm.duty(duty) # Apply duty
speed_pct = int(duty * 100 / 1023) # Duty to percent
print("FAN:SPEED:", speed_pct) # App-friendly fan speed percent
print("MODE:", mode) # App-friendly mode string
Reflection: Clean “KEY:VALUE” strings make apps simple and reliable.
Challenge:
- Easy: Add “ALERT:HEAT” when temp ≥ 32°C.
- Harder: Print “TARGET:xx” when in MANUAL to show desired speed.
Main project – Fan control by temperature
Blocks steps (with glossary)
- DHT11 temperature: Read temp from Pin 26.
- Decision (if): Compare to threshold for ON/OFF.
- PWM duty: Adjust speed based on temp ramp.
- Modes: AUTO uses sensor; MANUAL uses fixed duty.
- Serial prints: Output TEMP, FAN, MODE for app monitoring.
Block sequence:
- Setup DHT11 (Pin 26), PWM (Pin 23, 2000 Hz).
- Read temperature and print it.
- If AUTO → map temp to duty and set speed.
- If MANUAL → set fixed duty.
- Print TEMP, FAN:SPEED, MODE for app.
MicroPython code (mirroring blocks)
# Project 5.4 – Fan Control by Temperature
import dhtx # Read DHT11 temperature sensor
import machine # Control PWM for the fan
import time # Add small delays to stabilize readings
sensor = dhtx.DHT11(26) # DHT11 on Pin 26 (temperature source)
print("DHT11 ready on Pin 26") # Confirm sensor setup
pwm = machine.PWM(machine.Pin(23)) # PWM channel on Pin 23 (fan control)
pwm.freq(2000) # Set frequency to 2000 Hz for smooth fan control
print("PWM ready on Pin 23 (2000 Hz)") # Confirm PWM setup
mode = "AUTO" # Choose control mode: "AUTO" or "MANUAL"
print("Mode =", mode) # Show current mode
temp_min, temp_max = 20, 32 # Temperature ramp range for AUTO mode
print("AUTO ramp (C):", temp_min, "to", temp_max) # Explain ramp
while True: # Continuous control loop
temp_c = sensor.temperature() # Read temperature from DHT11
print("TEMP:", temp_c) # App-friendly temperature print
if mode == "AUTO": # Sensor-based speed control
t = temp_c # Work on a local copy for clamping
if t < temp_min: # Clamp temperature to lower bound
t = temp_min # Clamp low
print("Clamped low to", t) # Explain clamp
if t > temp_max: # Clamp to upper bound
t = temp_max # Clamp high
print("Clamped high to", t) # Explain clamp
duty = int((t - temp_min) * 1023 / (temp_max - temp_min)) # Map temp to duty
print("Duty (0-1023) =", duty) # Show duty
pwm.duty(duty) # Apply duty to fan
speed_pct = int(duty * 100 / 1023) # Convert duty to percent
print("FAN:SPEED:", speed_pct) # App-friendly speed string
print("MODE:", mode) # App-friendly mode string
else: # Manual fixed speed
duty = 700 # Choose a fixed duty for manual mode
print("Manual duty =", duty) # Show manual setting
pwm.duty(duty) # Apply duty to fan
speed_pct = int(duty * 100 / 1023) # Convert duty to percent
print("FAN:SPEED:", speed_pct) # App-friendly speed
print("MODE:", mode) # App-friendly mode
time.sleep(1) # Wait 1 second between updates for stability
External explanation
- What it teaches: Your robot reads temperature, decides if the fan should run, and sets speed based on how hot it is.
- Why it works: The DHT11 gives a temperature; we compare it to a threshold or map it to a PWM duty; PWM changes the power to the fan, so it spins faster when hotter.
- Key concept: “Sense → decide → control.”
Story time
You just built climate control. Your robot keeps its cool—literally—speeding up the fan when things heat up.
Debugging (2)
Debugging 5.4.A – Fan does not activate at the threshold
Problem: Temperature is above the threshold, but the fan stays OFF.
Clues: Serial shows “TEMP: 30” and “Threshold: 28,” but no change.
Broken code:
if temp_c > threshold_c: # Strictly greater than
fan.value(1) # Turn on
Fixed code:
if temp_c >= threshold_c: # Greater than or equal includes exact threshold
fan.value(1) # Turn fan ON
print("Fan: ON at threshold") # Confirm behavior
Why it works: Using >= triggers the fan exactly at the threshold instead of skipping that value.
Avoid next time: Decide whether equality should count as “ON” and code it clearly.
Debugging 5.4.B – Irregular speed control (fan stuttering)
Problem: Fan speed jumps or stutters when temperature fluctuates.
Clues: Duty changes too quickly; speed feels choppy.
Broken code:
duty = int((temp_c - temp_min) * 1023 / (temp_max - temp_min)) # No clamp, noisy input
pwm.duty(duty) # Apply directly
Fixed code:
# Clamp first, then map
t = temp_c # Local temp for processing
if t < temp_min: # Clamp lower
t = temp_min # Clamp
if t > temp_max: # Clamp upper
t = temp_max # Clamp
duty = int((t - temp_min) * 1023 / (temp_max - temp_min)) # Stable mapping
pwm.duty(duty) # Apply duty
print("Smoothed duty =", duty) # Explain stabilizing
Why it works: Clamping and mapping reduce sudden duty jumps, stabilizing speed.
Avoid next time: Add small delays (500–1000 ms) and avoid recalculating too fast.
Final checklist
- I saw the temperature value in serial
- The fan turned ON at or above the threshold
- The fan speed changed smoothly with temperature
Extras
- 🧠 Student tip: Add “MODE:MANUAL” and a variable duty to pick your own speed.
- 🧑🏫 Instructor tip: Have students record temp vs duty to see the linear mapping.
- 📖 Glossary:
- DHT11: A simple sensor that reads temperature and humidity.
- PWM (Pulse Width Modulation): A way to control speed/power by changing duty.
- Threshold: The decision line where behavior changes.
- 💡 Mini tips:
- Read every 1–2 seconds to avoid jitter.
- Use a minimum duty (e.g., 200) so the fan starts reliably.
- Keep serial messages short for easy app parsing.