Skip to content

Project: Your Robot

Time: 135 min per session

Background: The Integration Challenge

Components work alone; systems fail together. The integration iceberg means the happy path is visible, but hidden issues lurk below: timing conflicts, resource contention (e.g., I2C bus shared by OLED and IMU), motor noise corrupting ADC readings, and race conditions. Defensive patterns help: validate sensor readings (reject impossible values), timeout operations (fail-safe if incomplete), and use a watchdog timer (reset if main loop hangs). Always test edge cases: line lost, obstacle sudden, battery low.

→ Deep dive: Testing Embedded Code

Lab Setup

Connect your Pico via USB, then from the picobot/ directory:

mpremote run clean_pico.py          # optional: wipe old files
mpremote cp -r lib :               # upload libraries

Verify in REPL: from picobot import Robot; Robot() → "Robot ready!" First time? See the full setup guide.


Learning Objectives

By the end of this lab you will be able to:

  • Apply concepts from previous labs (sensors, control, state machines) to a new problem
  • Define clear success criteria for a project
  • Design a state machine for a chosen application
  • Implement and test a complete embedded system project
  • Document design decisions and trade-offs

You will choose or define a project, design success criteria, sketch a state machine, and build incrementally. The lab is about integration and project discipline.


Introduction

This lab provides time to design and build a project that combines concepts from the course: motor control, sensor feedback, control algorithms, state machines, and code organization. You will choose from several project options or propose your own.


Why This Lab Matters

This is where integration happens. Real embedded systems are not a single feature — they are multiple subsystems working together. You will practice defining success criteria, designing behavior as a state machine, and managing trade-offs under real constraints.


Choose Your Project

Option A: Enhanced Delivery Robot

Extend the State Machines delivery robot with new capabilities: - Multiple delivery zones (A, B, C) - Return to start automatically - Avoid obstacles on the track - Speed optimization (fastest delivery time) - Status display on LEDs (progress indicator)

Option B: Maze Solver

Make the robot navigate a simple maze: - Right-hand rule (always turn right at walls) - Left-hand rule (always turn left) - Dead-end detection and backtracking - Record the path and optimize on second run

Option C: Remote Control

Add wireless control via Bluetooth or IR: - Control motors with buttons/joystick - Display sensor readings on phone - Switch between manual and autonomous modes - Record and replay movements

Option D: Musical Robot

Create a performance robot: - Play songs with buzzer while dancing - React to obstacles with sounds - Light show synchronized with music - Rhythm-based movement patterns

Option E: Your Own Idea

Something we haven't thought of!

Requirements: - Uses at least 3 things you learned (sensors, control, state machine, etc.) - Has clear success criteria - Achievable in one lab session


⚡Hands-on tasks

Design Your Project

✅ Task 1 - Define Your Project

Project name: _____

One-sentence description: _____

Success criteria (how will you know it works?): - [ ] Criterion 1: ___ - [ ] Criterion 2: ___ - [ ] Criterion 3: _____

State machine sketch:

Draw your states and transitions here:

Checkpoint

You have a project name, a one-sentence description, at least 3 testable success criteria, and a state machine sketch with named states and transition conditions. Each criterion is binary (pass/fail), not vague ("works well").


Starter Template

Don't start from a blank file! Copy this skeleton and modify it:

# PROJECT: [Your Project Name]
# Description: [One sentence]
# Author: [Your name]

from picobot import Robot
import time

# Configuration
BASE_SPEED = 80
KP = 32
OBSTACLE_DISTANCE = 15
LOOP_PERIOD_MS = 20

# Initialization
robot = Robot()
print("Calibrating IMU...")
robot.imu.calibrate()

# Non-blocking distance (from Timing & Ultrasonic)
last_distance_check = 0
DISTANCE_CHECK_INTERVAL = 100
cached_distance = 100

# State machine
state = "IDLE"
state_start = time.ticks_ms()

def enter_state(new_state):
    global state, state_start
    print(f"[{time.ticks_ms():6d}] {state}{new_state}")
    state = new_state
    state_start = time.ticks_ms()
    robot.leds.show_state(state)

def time_in_state():
    return time.ticks_diff(time.ticks_ms(), state_start)

def update_distance():
    global cached_distance, last_distance_check
    now = time.ticks_ms()
    if time.ticks_diff(now, last_distance_check) >= DISTANCE_CHECK_INTERVAL:
        cached_distance = robot.read_distance()
        last_distance_check = now
    return cached_distance

# Main loop
print("\nReady! Press Enter to start...")
input()
enter_state("START")

try:
    while True:
        update_distance()

        # YOUR STATE MACHINE GOES HERE
        if state == "START":
            pass  # TODO: What happens first?

        elif state == "YOUR_STATE":
            pass  # TODO: Add your states

        elif state == "DONE":
            robot.stop()
            robot.set_leds((0, 255, 0))
            print("Complete!")
            break

        time.sleep(LOOP_PERIOD_MS / 1000)

except KeyboardInterrupt:
    robot.stop()
    robot.leds_off()
    print("\nStopped by user")

✅ Task 2 - Start Building

Copy the template and start building!

Expected output when the template runs successfully (before you add your own states):

Calibrating IMU...

Ready! Press Enter to start...

[   234] IDLE → START
Checkpoint

Your first core feature works in isolation. For example, if your project is a maze solver, the robot can detect a wall and turn away from it. You tested this single behavior at least twice before moving on.

Stuck?

If the template runs but your robot does nothing, check that you replaced the pass placeholders in the state machine with actual actions. A common mistake is writing code outside the while True loop -- it runs once at startup and never again. All recurring behavior must be inside the loop, inside a state.


Tips for Building

Start simple: 1. Get the core functionality working first (just one feature) 2. Then add features one at a time 3. Test after EVERY addition

Use what you know:

# State machine pattern (State Machines)
state = "START"

# Non-blocking pattern (Timing & Ultrasonic)
if timer.ready():
    do_slow_thing()

# Line following (Seeing the Line)
robot.line_follow_step(speed, kp)

# Precise turns (Precise Turns)
robot.turn_degrees(90)

Debug as you go: - Print statements are your friend - Use LEDs to show state (different colors = different states) - Log data if behavior is confusing

Expected output from a well-instrumented state machine during a run:

[   234] IDLE → START
[   235] START → FOLLOW
[  1842] FOLLOW → JUNCTION
[  2355] JUNCTION → TURN
[  2890] TURN → DELIVER
[  5102] DELIVER → DONE
Complete!

Timestamps help you diagnose timing issues -- if a state lasts too long or too short, the numbers tell you immediately.


Common Pitfalls

These are the problems students hit EVERY semester. Learn from their pain:

Common Pitfalls

Pitfall 1: Too Many Features At Once - Symptom: "I tried to add everything and now nothing works" - Fix: Add ONE feature → Test → Add next feature → Test

Pitfall 2: Forgot Non-Blocking Pattern - Symptom: "My robot stutters" or "Line following is jerky" - Fix: Use the non-blocking pattern from Ultrasonic & Timing for ANY slow operation

Pitfall 3: State Machine Doesn't Transition - Symptom: "It gets stuck in one state forever" - Fix: Is the transition condition EVER true? Add a print to check!

Pitfall 4: Works On Table, Fails On Track - Symptom: "It worked perfectly when I tested by hand!" - Fix: Test on the ACTUAL track early and often


Integration Checklist

Before combining features, verify each works independently:

Core Functions: - [ ] Line following works alone - [ ] Turns work alone (accurate to ±5°) - [ ] Obstacle detection works alone - [ ] LEDs work alone

Timing: - [ ] Loop runs at expected rate - [ ] Slow operations use non-blocking pattern - [ ] No operations take >50ms

State Machine: - [ ] All states have actions defined - [ ] All transitions have conditions - [ ] No "impossible" states - [ ] State transitions are logged

Reliability: - [ ] Works 3 times in a row - [ ] Recovers from line lost - [ ] Handles obstacles without crashing

Checkpoint

Multiple features work together in sequence. Your state machine transitions correctly between at least 3 states, and the robot completes its task end-to-end. Running it 3 times in a row produces consistent results.

Stuck?

If individual features work but break when combined, the most common cause is timing conflicts -- two features both trying to control the motors in the same loop iteration. Make sure only the active state sends motor commands. Also check that you are not blocking the loop with time.sleep() calls longer than your loop period. If the robot "forgets" sensor readings between states, verify that your sensor update code runs every iteration (outside the state-specific blocks), not only inside one state.


Document Your Project

✅ Task 3 - Document Your Work

Write a brief description:

What does it do?


What was the hardest part?


What would you improve with more time?


Add a header to your code:

# MY PROJECT: [Name]
# Description: [What it does]
# Key features: [Feature 1], [Feature 2], [Feature 3]
# Author: [Your name]


Preparation for Demo Day

Checklist: - [ ] Project works reliably (3+ successful runs) - [ ] Code is organized and commented - [ ] You can explain how it works - [ ] You know what parameters to adjust - [ ] Batteries are charged - [ ] Backup of code saved

Practice your demo: 1. Setup (30 sec): Place robot, explain what it will do 2. Run (60 sec): Show it working 3. Explain (60 sec): Key technical decisions 4. Questions (30 sec): Answer one question


Recap

Integration is where most bugs appear. Clear success criteria prevent endless tweaking, and state machines keep complex projects manageable.


Need Ideas?

Quick Wins: - Add sound effects to delivery robot - Make LEDs show distance (closer = more red) - Add "dance" celebration when delivery completes

Medium Challenge: - Two-destination delivery (A then B then home) - Speed run mode (optimize for fastest time) - "Scared robot" that backs away from hands

Hard Mode: - Learn the track (first run slow, second run fast) - Follow a moving target (using ultrasonic) - Self-calibrating Kp (measure and adjust)


Go Build!

You have the skills. You have the tools. Make something cool.


➡ Next Steps


← Architecture | Labs Overview | Next: Demo Day →