Project 5.12: "Integration N1 to N5"
Â
What youâll learn
- â Integration: Combine sensors, actuators, app, routines, and preferences into one system.
- â Home automation: Simulate lights, fans, and alerts controlled by app and sensors.
- â Multiâfunction service robot: Switch between assistant, monitor, and game modes.
- â Security monitoring: Detect rain, soil, or sound and send alerts.
- â Interactive game: Use app commands and sensors for multiplayer fun.
- â Creative project: Leave space for students to invent their own integration.
Key ideas
- Short definition: Integration means connecting all parts so they work together.
- Realâworld link: Smart homes and robots combine sensors, apps, and routines to serve people in many ways.
Blocks glossary (used in this project)
- Digital inputs: Rain sensor, sound sensor.
- Analog input: Soil moisture sensor.
- DHT11: Temperature sensor.
- Digital outputs: Motors, LED, buzzer.
- OLED shows: Display status and modes.
- Bluetooth callback: Receive app commands.
- Variables + lists: Store routines, preferences, and modes.
- Serial println: Send âKEY:VALUEâ messages for app display.
What you need
| Part | How many? | Pin connection |
|---|---|---|
| D1 R32 | 1 | USB cable (30 cm) |
| DHT11 | 1 | Signal â Pin 26, VCC, GND |
| Soil sensor | 1 | Signal â Pin 32 (ADC), VCC, GND |
| Raindrop sensor | 1 | DO â Pin 35, VCC, GND |
| Sound sensor | 1 | DO â Pin 34, VCC, GND |
| L298N motor driver + motors | 1 | IN1â18, IN2â19, IN3â5, IN4â23 |
| LED module | 1 | Signal â Pin 13, VCC, GND |
| Buzzer module | 1 | Signal â Pin 23, VCC, GND |
| OLED SSD1306 128Ă64 | 1 | I2C: SCLâ22, SDAâ21, VCC, GND |
| Smartphone (Bluetooth) | 1 | Connect to âCluâBotsâ |
Before you start
- USB cable is plugged in
- Serial monitor is open
- Test print shows:
print("Ready!") # Confirm serial is working
Microprojects 1â5
Microproject 5.12.1 â Home automation system (simulated)
Goal: Control LED (light) and buzzer (alarm) with app commands.
Blocks used:
- Digital outputs: LED, buzzer.
- Serial println: Print âHOME:LIGHT_ON/OFFâ, âHOME:ALARM_ON/OFFâ.
MicroPython code:
import machine # Import machine for pins
led = machine.Pin(13, machine.Pin.OUT) # LED on Pin 13
buzzer = machine.Pin(23, machine.Pin.OUT) # Buzzer on Pin 23
print("Microproject 5.12.1: Home automation outputs ready") # Confirm setup
def home_light(on): # Function to control light
led.value(1 if on else 0) # Set LED ON if on=True else OFF
print("HOME:LIGHT_ON" if on else "HOME:LIGHT_OFF") # Print status
def home_alarm(on): # Function to control alarm
buzzer.value(1 if on else 0) # Set buzzer ON if on=True else OFF
print("HOME:ALARM_ON" if on else "HOME:ALARM_OFF") # Print status
Reflection: Simple ON/OFF commands simulate smart home devices.
Challenge:
- Easy: Add âHOME:FAN_ON/OFFâ.
- Harder: Add automatic alarm when rain sensor triggers.
Microproject 5.12.2 â Multiâfunction service robot
Goal: Switch between modes: ASSISTANT, MONITOR, GAME.
Blocks used:
- Variable mode: Store current mode.
- Serial println: Print âMODE:âŠâ.
MicroPython code:
mode = "ASSISTANT" # Start in assistant mode
print("Microproject 5.12.2: Mode initialized to", mode) # Confirm start mode
def set_mode(new_mode): # Function to change mode
global mode # Use global mode variable
mode = new_mode # Update mode
print("MODE:", mode) # Print new mode
Reflection: Modes let one robot do many jobsâlike switching hats.
Challenge:
- Easy: Add âMODE:SECURITYâ.
- Harder: Add OLED display showing current mode.
Microproject 5.12.3 â Security and monitoring system with app
Goal: Use rain, soil, and sound sensors to trigger alerts.
Blocks used:
- Digital/analog inputs: Rain, soil, sound.
- Serial println: Print âALERT:RAIN/DRY_SOIL/NOISEâ.
MicroPython code:
import dhtx # Import DHT11 library
import machine # Import machine for ADC and Pin
sensor = dhtx.DHT11(26) # DHT11 on Pin 26
adc32 = machine.ADC(machine.Pin(32)) # Soil sensor on Pin 32
adc32.atten(machine.ADC.ATTN_11DB) # Configure ADC attenuation
adc32.width(machine.ADC.WIDTH_12BIT) # Use 12-bit resolution
rain = machine.Pin(35, machine.Pin.IN) # Raindrop sensor on Pin 35
sound = machine.Pin(34, machine.Pin.IN) # Sound sensor on Pin 34
print("Microproject 5.12.3: Sensors ready") # Confirm setup
temp_c = sensor.temperature() # Read temperature
soil_raw = adc32.read() # Read soil raw value
soil_pct = int((4095 - soil_raw) * 100 / 4095) # Map soil to percent
rain_state = rain.value() # Read rain state
sound_state = sound.value() # Read sound state
print("TEMP:", temp_c) # Print temperature
print("SOIL(%):", soil_pct) # Print soil percent
print("RAIN:", rain_state) # Print rain state
print("SOUND:", sound_state) # Print sound state
if rain_state == 1: # If rain detected
print("ALERT:RAIN") # Print rain alert
if soil_pct < 25: # If soil too dry
print("ALERT:DRY_SOIL") # Print dry soil alert
if sound_state == 1: # If loud sound detected
print("ALERT:NOISE") # Print noise alert
Reflection: Multiple sensors make monitoring smarterâalerts show risks clearly.
Challenge:
- Easy: Add âALERT:HEATâ if temp > 32.
- Harder: Add combined âALERT:FLOOD_RISKâ if rain=1 and soil>80.
Microproject 5.12.4 â Multiâdevice interactive game
Goal: Use app commands and sensors for a multiplayer game.
Blocks used:
- Bluetooth callback: Receive commands.
- Variables: Track scores.
- Serial println: Print âGAME:âŠâ.
MicroPython code:
scoreA, scoreB = 0, 0 # Initialize scores
turn = "A" # Start with Player A
print("Microproject 5.12.4: Game initialized") # Confirm setup
def game_score(player): # Function to add score
global scoreA, scoreB, turn # Use global variables
if player == "A": # If Player A scores
scoreA += 1 # Increase A score
print("SCORE_A:", scoreA) # Print A score
else: # If Player B scores
scoreB += 1 # Increase B score
print("SCORE_B:", scoreB) # Print B score
turn = "B" if turn == "A" else "A" # Switch turn
print("TURN:", turn) # Print whose turn it is
Reflection: Multiplayer games make robots socialâturns keep it fair.
Challenge:
- Easy: Add OLED display showing scores.
- Harder: Add âWINNER?â command to print who leads.
Microprojects 1â5
Microproject 5.12.5 â Free creative project
Goal: Build âGarden Guardianâ: a creative combo that watches rain/soil, announces status, and plays a celebratory sound/light pattern when conditions are ideal.
Blocks used:
- Inputs: DHT11, soil ADC, rain DO.
- Outputs: LED, buzzer, OLED.
- Serial println: NOTIFY/DETAIL/STATUS lines.
Block sequence:
- Read TEMP, SOIL(%), RAIN.
- If soil is 40â70% and no rain, show âGARDEN GOODâ on OLED and celebrate with LED/buzzer.
- Else, print status and any alert (DRY_SOIL or RAIN).
MicroPython code:
import dhtx # Import DHT11 library to read temperature
import machine # Import machine for ADC, Pin, and hardware control
import time # Import time to add tiny delays for effects
import oled128x64 # Import OLED driver for SSD1306 128x64
sensor = dhtx.DHT11(26) # Create DHT11 on Pin 26 for temperature
adc32 = machine.ADC(machine.Pin(32)) # Create ADC on Pin 32 for soil sensor
adc32.atten(machine.ADC.ATTN_11DB) # Configure ADC attenuation for full-scale readings
adc32.width(machine.ADC.WIDTH_12BIT) # Set ADC resolution to 12-bit (0â4095)
rain = machine.Pin(35, machine.Pin.IN) # Create raindrop digital input on Pin 35
led = machine.Pin(13, machine.Pin.OUT) # Create LED output on Pin 13
buzzer = machine.Pin(23, machine.Pin.OUT) # Create buzzer output on Pin 23
i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000) # Setup I2C bus for OLED on pins 22/21
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0) # Initialize OLED at address 0x3C
print("Microproject 5.12.5: Garden Guardian ready") # Print project status
temp_c = sensor.temperature() # Read temperature in Celsius from DHT11
raw = adc32.read() # Read raw soil moisture value (0â4095) from ADC
soil_pct = int((4095 - raw) * 100 / 4095) # Convert raw soil value to percent (higher number = wetter)
rain_state = rain.value() # Read raindrop state (1=wet trigger, 0=dry)
print("TEMP:", temp_c) # Print temperature for app
print("SOIL(%):", soil_pct) # Print soil percentage for app
print("RAIN:", rain_state) # Print rain state for app
oled.clear() # Clear OLED screen before showing information
oled.shows('Garden Guardian', x=0, y=0, size=1, space=0, center=False) # Show project title
oled.shows('T:'+str(temp_c)+' S:'+str(soil_pct)+' R:'+str(rain_state), x=0, y=12, size=1, space=0, center=False) # Show values
oled.show() # Refresh OLED to display text
if (soil_pct >= 40) and (soil_pct <= 70) and (rain_state == 0): # Check ideal garden condition
print("STATUS:GARDEN_GOOD") # Print positive status
print("NOTIFY:GARDEN_GOOD") # Print notification tag for app
print("DETAIL:TEMP="+str(temp_c)+",SOIL="+str(soil_pct)+",RAIN="+str(rain_state)) # Print details line
for i in range(3): # Run a short celebration pattern three times
led.value(1) # Turn LED ON to celebrate
buzzer.value(1) # Turn buzzer ON for a quick beep
time.sleep(0.1) # Hold state for 100 ms
led.value(0) # Turn LED OFF to blink
buzzer.value(0) # Turn buzzer OFF to stop beep
time.sleep(0.1) # Pause for 100 ms before next blink
oled.shows('GARDEN GOOD!', x=0, y=28, size=2, space=0, center=False) # Show big positive message
oled.show() # Refresh OLED to display celebration text
else: # If conditions are not ideal
if soil_pct < 30: # If soil is too dry
print("ALERT:DRY_SOIL") # Print dry soil alert
oled.shows('DRY SOIL', x=0, y=28, size=2, space=0, center=False) # Show alert text
if rain_state == 1: # If rain is detected
print("ALERT:RAIN") # Print rain alert
oled.shows('RAIN DETECTED', x=0, y=48, size=1, space=0, center=False) # Show rain alert text
oled.show() # Refresh OLED to display alerts
Reflection: You combined reading, deciding, and celebratingâthis is your robotâs âpersonality.â
Challenge:
- Easy: Add âPLACE:GARDEN_Aâ in the details print.
- Harder: Add a âMOODâ variable (HAPPY/ALERT) and change LED/buzzer patterns by mood.
Main project â Full integration showcase
Blocks steps (with glossary)
- Inputs: DHT11, soil, rain, sound on official pins.
- Outputs: Motors, LED, buzzer, OLED.
- App control: BLE callback accepts MODE and TASK commands.
- Routines: Named sequences like MORNING/EVENING.
- Guardian mode: âGarden Guardianâ celebration when conditions are ideal.
Block sequence:
- Setup all hardware (sensors, outputs, OLED, BLE).
- Define helper functions (motors, tasks, routines, guardian).
- Handle app commands and run the right feature.
- Print status and keep OLED updated.
- Keep loop responsive with short delays.
MicroPython code (mirroring blocks)
# Project 5.12 â Integration N1 to N5 (Full showcase)
import dhtx # Import DHT11 temperature sensor library
import machine # Import machine for ADC, Pin, and hardware control
import oled128x64 # Import OLED driver for SSD1306 128x64
import ble_central # Import BLE central role
import ble_peripheral # Import BLE peripheral role
import ble_handle # Import BLE handler for receive callbacks
import time # Import time for delays and timing windows
# Hardware setup: sensors
sensor = dhtx.DHT11(26) # DHT11 sensor on Pin 26
adc32 = machine.ADC(machine.Pin(32)) # Soil sensor ADC on Pin 32
adc32.atten(machine.ADC.ATTN_11DB) # ADC attenuation configured for full scale
adc32.width(machine.ADC.WIDTH_12BIT) # ADC resolution set to 12-bit (0â4095)
rain = machine.Pin(35, machine.Pin.IN) # Raindrop DO input on Pin 35
sound = machine.Pin(34, machine.Pin.IN) # Sound sensor DO input on Pin 34
print("Sensors: DHT11=26, Soil=32, Rain=35, Sound=34") # Confirm sensor pins
# Hardware setup: outputs
left_in1 = machine.Pin(18, machine.Pin.OUT) # Left motor IN1
left_in2 = machine.Pin(19, machine.Pin.OUT) # Left motor IN2
right_in3 = machine.Pin(5, machine.Pin.OUT) # Right motor IN3
right_in4 = machine.Pin(23, machine.Pin.OUT) # Right motor IN4
led = machine.Pin(13, machine.Pin.OUT) # LED output
buzzer = machine.Pin(23, machine.Pin.OUT) # Buzzer output (shared pin used carefully)
print("Outputs: Motors=18/19/5/23, LED=13, Buzzer=23") # Confirm output pins
# OLED setup
i2c = machine.SoftI2C(scl=machine.Pin(22), sda=machine.Pin(21), freq=100000) # I2C bus on pins 22/21
oled = oled128x64.OLED(i2c, address=0x3c, font_address=0x3A0000, types=0) # OLED initialized at 0x3C
print("OLED ready at 0x3C") # Confirm OLED setup
# BLE setup
ble_c = ble_central.BLESimpleCentral() # Create BLE central object
ble_p = ble_peripheral.BLESimplePeripheral('Clu-Bots') # Create BLE peripheral named 'Clu-Bots'
ble_c.scan() # Start scanning for BLE peripheral
ble_c.connect(name='Clu-Bots') # Connect to BLE peripheral by name
handle = ble_handle.Handle() # Create BLE handler for callbacks
print("BLE connected to Clu-Bots") # Confirm BLE connection
# Helpers: motors
def motors_stop(): # Stop both motors
left_in1.value(0) # Left IN1 LOW
left_in2.value(0) # Left IN2 LOW
right_in3.value(0) # Right IN3 LOW
right_in4.value(0) # Right IN4 LOW
print("MOTORS:STOP") # Print stop status
def motors_forward(): # Move forward
left_in1.value(1) # Left IN1 HIGH
left_in2.value(0) # Left IN2 LOW
right_in3.value(1) # Right IN3 HIGH
right_in4.value(0) # Right IN4 LOW
print("MOTORS:FWD") # Print forward status
# Helpers: routines
routines = { # Define named routine steps
"MORNING": ["ANNOUNCE:Good morning", "WATCH", "BRING"], # Morning routine
"EVENING": ["ANNOUNCE:Good evening", "WATCH", "STOP"] # Evening routine
}
print("Routines:", list(routines.keys())) # Print routine names
def run_routine(name): # Execute a named routine
print("ROUTINE:START", name) # Announce start
steps = routines.get(name, []) # Get steps or empty
for step in steps: # Loop steps
print("ROUTINE:STEP", step) # Print current step
execute(step) # Execute command
print("ROUTINE:DONE", name) # Announce completion
# Helpers: tasks and announce
def task_bring(): # BRING task (forward 1 s)
print("TASK:BRING_START") # Announce start
oled.clear() # Clear OLED
oled.shows('BRING...', x=0, y=20, size=1, space=0, center=False) # Show task text
oled.show() # Refresh OLED
motors_forward() # Move forward
time.sleep(1.0) # Drive for 1 second
motors_stop() # Stop movement
oled.shows('BRING DONE', x=0, y=35, size=1, space=0, center=False) # Show completed text
oled.show() # Refresh OLED
print("TASK:BRING_DONE") # Announce completion
def task_watch(): # WATCH task (simulate scan)
print("TASK:WATCH_START") # Announce start
oled.clear() # Clear OLED
oled.shows('WATCH...', x=0, y=20, size=1, space=0, center=False) # Show task text
oled.show() # Refresh OLED
time.sleep(0.5) # Simulate scanning delay
print("STATUS:AREA_CLEAR") # Print status
oled.shows('AREA CLEAR', x=0, y=35, size=1, space=0, center=False) # Show status
oled.show() # Refresh OLED
print("TASK:WATCH_DONE") # Announce completion
def task_announce(text="HELLO"): # ANNOUNCE task (print message)
print("TASK:ANNOUNCE", text) # Announce message
oled.clear() # Clear OLED
oled.shows('ANNOUNCE:', x=0, y=10, size=1, space=0, center=False) # Label text
oled.shows(text, x=0, y=25, size=1, space=0, center=False) # Show message
oled.show() # Refresh OLED
# Garden Guardian celebration helper
def garden_guardian(): # Celebrate good garden conditions
temp_c = sensor.temperature() # Read temperature
raw = adc32.read() # Read soil raw value
soil_pct = int((4095 - raw) * 100 / 4095) # Map to percent
rain_state = rain.value() # Read rain state
print("TEMP:", temp_c) # Print temperature
print("SOIL(%):", soil_pct) # Print soil percent
print("RAIN:", rain_state) # Print rain state
oled.clear() # Clear OLED
oled.shows('Garden Guardian', x=0, y=0, size=1, space=0, center=False) # Title
oled.shows('T:'+str(temp_c)+' S:'+str(soil_pct)+' R:'+str(rain_state), x=0, y=12, size=1, space=0, center=False) # Values
oled.show() # Refresh OLED
if (soil_pct >= 40) and (soil_pct <= 70) and (rain_state == 0): # Ideal condition
print("STATUS:GARDEN_GOOD") # Print good status
print("NOTIFY:GARDEN_GOOD") # Notification tag
print("DETAIL:TEMP="+str(temp_c)+",SOIL="+str(soil_pct)+",RAIN="+str(rain_state)) # Details
for i in range(3): # Celebration pattern
led.value(1) # LED ON
buzzer.value(1) # Buzzer ON
time.sleep(0.1) # Short on time
led.value(0) # LED OFF
buzzer.value(0) # Buzzer OFF
time.sleep(0.1) # Short off time
oled.shows('GARDEN GOOD!', x=0, y=28, size=2, space=0, center=False) # Big message
oled.show() # Refresh OLED
else: # Not ideal
if soil_pct < 30: # Dry soil case
print("ALERT:DRY_SOIL") # Dry alert
oled.shows('DRY SOIL', x=0, y=28, size=2, space=0, center=False) # OLED alert
if rain_state == 1: # Rain case
print("ALERT:RAIN") # Rain alert
oled.shows('RAIN DETECTED', x=0, y=48, size=1, space=0, center=False) # OLED alert
oled.show() # Refresh OLED
# Executor: run commands from app
def execute(cmd): # Execute a command string
print("EXEC:", cmd) # Print command for app
if cmd == "BRING": # If BRING requested
task_bring() # Run BRING task
elif cmd == "WATCH": # If WATCH requested
task_watch() # Run WATCH task
elif cmd.startswith("ANNOUNCE:"): # If ANNOUNCE with text
task_announce(cmd.split(":", 1)[1]) # Run ANNOUNCE with message
elif cmd == "GUARDIAN": # If Garden Guardian requested
garden_guardian() # Run celebration/status
elif cmd == "STOP": # If STOP requested
motors_stop() # Stop motors
else: # Unknown command
print("CMD:UNKNOWN", cmd) # Report unknown
# BLE callback: handle app commands
def handle_method(key1, key2, key3, keyx): # BLE receive callback for app
msg = str(key1) # Convert payload to text
print("APP->R32:", msg) # Print command from app
if msg.startswith("ROUTINE:"): # If routine call
run_routine(msg.split(":", 1)[1]) # Run routine by name
else: # Otherwise treat as task
execute(msg) # Execute command
handle.recv(handle_method) # Attach BLE receive callback
# Main loop: periodic guardian check to keep system lively
while True: # Continuous loop
print("HB:RUNNING") # Heartbeat message
time.sleep(3) # Small delay to avoid spamming
External explanation
- What it teaches: Integration means connecting everythingâsensors, actuators, app, routinesâand giving them simple rules to work together.
- Why it works: Clear helper functions and short, labeled messages keep the system understandable; BLE callbacks and tiny delays keep it responsive; OLED makes the state visible at a glance.
- Key concept: âRead â decide â act â show.â
Story time
Your robot wakes up like a tiny butler. It checks the garden, flashes a happy light when conditions are perfect, announces the plan, and runs your routine. Itâs not just partsâitâs a personality.
Debugging (2)
Debugging 5.12.1 â Complexity in integration
Problem: Features step on each other (e.g., motors and buzzer share timing and feel messy).
Clues: Messages overlap, OLED flickers, actions interrupt.
Broken code:
execute("BRING") # Starts motors
execute("GUARDIAN") # Immediately runs celebration and prints over BRING
Fixed code:
def safe_run(seq): # Helper to run steps in order
for step in seq: # Iterate through steps
execute(step) # Execute current step
time.sleep(0.3) # Small gap to avoid overlap
# Example: safe_run(["BRING", "GUARDIAN"]) # Run BRING then GUARDIAN cleanly
Why it works: A tiny gap between actions reduces clashes and makes outputs readable.
Avoid next time: Sequence multiâstep actions with short delays and clear prints.
Debugging 5.12.2 â Cascading failures
Problem: One error triggers many more (e.g., bad sensor read breaks multiple checks).
Clues: Soil percent prints strange values; alerts all trigger at once.
Broken code:
soil_pct = int((4095 - raw) * 100 / 4095) # No guard for invalid raw
print("SOIL(%):", soil_pct) # Might print negative or >100 if raw bad
Fixed code:
raw = adc32.read() # Read raw value
raw = 0 if raw < 0 else (4095 if raw > 4095 else raw) # Clamp to valid range
soil_pct = int((4095 - raw) * 100 / 4095) # Map to 0â100 percent
print("SOIL(%):", soil_pct) # Print clamped percent
Why it works: Clamping keeps values in range, preventing chain reactions of wrong alerts.
Avoid next time: Add simple guards and sanity checks around sensor reads.
Final checklist
- Garden Guardian celebration shows when soil is 40â70% and rain=0
- BRING/WATCH/ANNOUNCE run cleanly with prints and OLED updates
- BLE commands execute immediately, including ROUTINE and GUARDIAN
- Heartbeat prints keep the app UI in sync without spam
- Sensor value prints are in range and understandable
Extras
- đ§ Student tip: Add âPROFILE:QUIETâ to use LED only (no buzzer) during quiet hours.
- đ§âđ« Instructor tip: Ask students to draw their integration map (inputs â decisions â actions â displays) before coding; it prevents overlaps.
- đ Glossary:
- Integration: Combining multiple features so they cooperate.
- Heartbeat: A periodic print that shows the system is alive.
- Clamping: Limiting a value to a safe range to prevent bad behavior.
- đĄ Mini tips:
- Keep prints short and labeled (STATUS, ALERT, NOTIFY, DETAIL).
- Use tiny delays between actions to avoid flicker.
- Reuse helpers and routines so the code stays small and clear.