Robot Unboxing
Time: 135 min
Learning Objectives
By the end of this tutorial you will be able to:
- Connect to a microcontroller via USB and use the MicroPython REPL
- Identify the main components of an embedded system (MCU, sensors, actuators)
- Control digital outputs: LEDs, buzzer, and motors
- Write and run a simple MicroPython program on the robot
- Observe the limitations of open-loop motor commands
You will establish a reliable connection to the Pico, explore its outputs, build a short program that combines LEDs, sound, and motion, and attempt your first precision task — driving a square.
Introduction
Today you meet your robot. The goal is simple: get code running, see immediate results, and start building intuition about what embedded systems can and cannot do.
Background: What Is an Embedded System?
An embedded system is a computer that exists to control or monitor something else — it's inside a product, not the product itself. Your robot has a microcontroller (MCU) containing a CPU, memory, and hardware peripherals on a single chip. Every embedded system follows the same loop: SENSE (read sensors) → DECIDE (run your logic) → ACT (drive motors, LEDs) — repeating continuously. Today you'll experience all three stages hands-on.
By the end of this session, you will have controlled LEDs, played sound, moved the robot, and observed something important about motor commands that sets up everything that follows.
Hardware Reference
Keep the robot schematic (PDF) handy — it shows every pin connection, sensor circuit, and motor driver on the board. When you wonder "what's connected to GP5?" or "how does the motor driver work?", the schematic has the answer. See also the Robot Electronics Overview for a guided walkthrough.
New to Python?
This tutorial introduces Python concepts as you need them — no prior experience required. Each new syntax element gets a brief explanation when it first appears. For a complete reference you can revisit later, see Python Basics.
What will you build?
Think of the demos and code snippets below as ingredients. By Part 4, you will combine at least two outputs (LEDs, buzzer, motors) into a behavior of your own design. Here are some ideas — or invent your own:
- Mood Robot: LEDs show mood, buzzer reacts to input, robot wiggles when "happy"
- Alarm Bot: Flash red, beep fast, back up when triggered
- Light Show: Color sweep → spin → tone → stop
The only rule: combine at least two outputs. Keep it small and make it yours.
Part 1: Connect (15 min)
Step 1: Physical Setup
- Connect the Pi PIco2 board on the robot to your computer with the USB cable (USB connector of the Pico2 board not the robot!)
Step 2: Open Terminal
Open a terminal (Command Prompt on Windows, Terminal on Mac/Linux).
Install mpremote — the tool that talks to the Pico over USB:
Already installed?
If pip install mpremote says "already satisfied", you're good — skip to the next command.
Verify the Pico is connected:
Expected output:
No device found?
If mpremote connect list shows nothing or the Pico doesn't appear:
- Check the USB cable (some cables are charge-only — use a data cable)
- Try a different USB port
- If the Pico has never been used with MicroPython, you need to flash the firmware first — see the Firmware Update guide
- On Linux: you may need serial port access —
sudo usermod -a -G dialout $USERthen log out and back in
Step 3: Get the Source Code
Clone the picobot repository from GitHub:
This creates a picobot/ folder with all the source code, labs, and tools you'll use throughout the course.
No git?
Install with winget install Git.Git (Windows) / sudo apt install git (Linux) / git-scm.com.
Step 4: Clean the Pico
If someone used this Pico before, old files (like main.py) may still be on it. A leftover main.py runs automatically on boot and can cause confusing behavior. Let's wipe it clean:
This removes all user files from the Pico and reboots it. You'll see what gets deleted:
Nothing to remove?
If the Pico is brand new, the script just prints "clean" and reboots — that's fine.
Step 5: Enter the REPL
You'll see:
Connected to MicroPython at /dev/ttyACM0
Use Ctrl-] or Ctrl-x to exit this shell
MicroPython v1.27.0 on 2025-12-09; Raspberry Pi Pico 2 W with RP2350
Type "help()" for more information.
>>>
This is the REPL (Read-Eval-Print-Loop). You type Python, it runs immediately on the microcontroller.
No >>> prompt?
If the prompt doesn't appear, press Enter or Ctrl+C. The Pico may be running old code — Ctrl+C stops it and gives you the prompt back.
REPL Survival Guide
| Keys | What it does |
|---|---|
Ctrl+C |
Stop running code |
Ctrl+D |
Soft reboot |
Ctrl+X |
Exit REPL |
↑ / ↓ |
Command history |
Tab |
Auto-complete |
Press Ctrl+X to exit from REPL.
Step 6: Upload the Picobot Library
The robot uses a library called picobot — it provides simple commands for motors, LEDs, sensors, and more. Upload it to the Pico (make sure you're in the picobot/ directory you cloned in Step 3):
Already uploaded?
Run mpremote fs ls :lib/ — if you see files listed, skip this step.
Step 7: First Sign of Life
Open REPL again:
Enter the following lines in the REPL after the >>> prompt. If the prompt doesn't appear, press Enter or Ctrl+C.
If you see Robot ready! and the LEDs flash green — you're connected.
Python Syntax: Importing and Creating Objects
Let's unpack those two lines — they'll appear in every program you write:
from picobot import Robot # Get the Robot class from the picobot library
robot = Robot() # Create a robot object and name it "robot"
from ... import ...loads code from another file.picobotis a Python file (actually a folder of files) that lives on the Pico's filesystem — you uploaded it in the previous step. It contains the code that knows how to talk to motors, LEDs, and sensors. No magic, just files. You'll also useimport timelater to load Python's built-in timing functions.Robot()with parentheses calls the class to create a new object. Think of it as "build me a robot controller." The parentheses are important — without them, Python just looks at the class without doing anything.robot = ...stores the result in a variable namedrobot. From now on,robot.something()means "tell the robot to do something."
See Python Basics → Importing for more on imports and modules.
Checkpoint — Connected
You should see the >>> prompt and Robot ready! after creating the Robot instance. The LEDs flash green briefly to confirm.
In future labs, you'll repeat Steps 1–7 at the start of each session. See the Lab Setup Quick Reference for the condensed version.
Stuck?
- "No module named picobot" — The library isn't on the Pico. Go back to Step 4 and upload it.
- "Permission denied" (Linux) — Run
sudo usermod -a -G dialout $USERthen log out and back in. - Nothing happens — Try a different USB cable (some are charge-only).
mpremote connect listshows nothing — Check USB cable, power switch, try a different port.
Part 2: First Signs of Life (15 min)
Your robot came pre-loaded with demo programs. Let's see what it can do — and start noticing how Python function calls work.
Where to type these?
All code blocks in Part 2 and Part 3 are meant for the REPL — type (or paste) them after the >>> prompt in your terminal. Do not copy the >>> itself if you see it in examples elsewhere.
Light Show
Watch the LEDs cycle through colors. The parentheses () mean "run this function now." Without them, Python would just tell you the function exists without actually running it.
Now try passing an argument — a value that changes how the function behaves:
Here duration=10 is a keyword argument: you're telling the function which setting to change and what value to use. This is one of Python's most readable features — you can immediately see what duration=10 means, even without reading the documentation.
Dance
The robot wiggles around. Make sure it has room to move, or hold it in your hand.
Music
The text in quotes — "twinkle" — is a string, Python's way of representing text. Strings must always be wrapped in quotes (single '...' or double "..." — both work).
Other melodies to try:
robot.play_melody("scale") # Musical scale
robot.play_melody("alert") # Warning beeps
robot.play_melody("success") # Victory sound
The # symbol starts a comment — everything after it on that line is ignored by Python. Comments are notes for humans reading the code.
DJ Mode
The robot has a built-in DJ mode — techno riff with lights and dance moves:
Want it faster? Change the BPM:
Live DJ Mode
Play music from your phone near the robot's microphone and watch it react to the beat — LEDs flash and motors move with the sound:
Make it louder
If the robot doesn't react, turn up the volume or hold the phone closer to the microphone. You can also lower the sensitivity threshold:
Checkpoint — Demos Working
You should have seen LEDs cycling colors, the robot wiggling, heard melodies, and experienced DJ mode. If any demo failed, check that the robot is powered on and connected.
What just happened?
Each demo combined multiple outputs — LEDs, motors, buzzer — running in a timed sequence. Behind the scenes, these demo functions contain loops, timing, and hardware commands — exactly the kind of code you'll write yourself by Part 4. Later in the course, you'll build behaviors driven by sensors instead of timers.
Part 3: Take Control (25 min)
Now let's control things directly instead of running pre-built demos.
LEDs
# Set all LEDs to red
robot.set_leds((255, 0, 0))
# Green
robot.set_leds((0, 255, 0))
# Blue
robot.set_leds((0, 0, 255))
# Turn off
robot.leds_off()
The numbers are (Red, Green, Blue) from 0–255. Each combination is a different color.
Python Syntax: Tuples
Notice the double parentheses in set_leds((255, 0, 0)):
- The outer
( )are the function call parentheses (just likedemo_lightshow()) - The inner
(255, 0, 0)is a tuple — an ordered group of values bundled together
A tuple is Python's way of packing several values into one. Here it packs three color intensities — Red, Green, Blue — into a single RGB color. You could also write it in two steps:
my_color = (255, 0, 0) # Create a tuple and store it
robot.set_leds(my_color) # Pass it to the function
Tuples use regular parentheses (). You'll also meet lists [...] soon — they're similar but can be modified after creation.
See Python Basics → Data Types for more.
How do addressable LEDs work?
One pin controls all 8 LEDs using a special protocol — each LED passes data to the next. → WS2812B Protocol
✅ Task 1 — Mix Colors
Make the LEDs show:
| Color | RGB Value | Your Result |
|---|---|---|
| Yellow | (255, 255, 0) | |
| Cyan | (?, ?, ?) | |
| Purple | (?, ?, ?) | |
| Orange | (?, ?, ?) | |
| Your favorite color |
Pick a color and note the RGB values to try on your robot:
Individual LEDs
You can set each LED separately:
Sound
# Single beep (440 Hz = musical note A)
robot.beep(440, 500) # 440 Hz for 500ms
# Higher pitch
robot.beep(880, 500) # One octave up
# Lower pitch
robot.beep(220, 500) # One octave down
In robot.beep(440, 500), the two numbers are positional arguments — their meaning depends on their order. The first is frequency in Hertz, the second is duration in milliseconds. Swapping them (robot.beep(500, 440)) would play a different note for a different time!
How does the buzzer work?
The Pico generates a PWM (Pulse Width Modulation) signal — a square wave that toggles a pin on and off at the desired frequency. At 440 Hz, the pin switches state 440 times per second, which vibrates the buzzer at the pitch of middle A on a piano. → PWM Basics
Why does doubling the frequency sound like the same note, just higher?
Our ears perceive pitch logarithmically. The formula \(f = 440 \times 2^{n/12}\) gives any note, where \(n\) is semitones from A4. This exponential relationship means each octave doubles the frequency.
Motors (be careful!)
Everything so far — LEDs, buzzer, melodies — was powered directly from the USB cable through the Pico. The motors are different: they draw much more current than USB can provide, so they're powered by the on-board battery through a separate power circuit. You need to turn on the robot's main power switch before motor commands will do anything.
Before you try motors
- Turn on the main power switch on the robot (the Pico's USB LED alone doesn't mean the motors have power).
- Place the robot on a flat surface or hold it in your hand — it will move!
- Watch the USB cable — the robot will drive and the cable will pull tight or curl around. Keep slack in the cable and be ready to grab the robot.
# Drive forward for 1 second
robot.forward(speed=80, duration=1.0)
# Drive backward
robot.backward(speed=80, duration=1.0)
# Turn left
robot.turn_left(speed=80, duration=0.5)
# Turn right
robot.turn_right(speed=80, duration=0.5)
# Stop everything
robot.stop()
Notice how robot.forward(speed=80, duration=1.0) uses keyword arguments — you can read exactly what each number means. The speed=80 sets motor power (0–255), and duration=1.0 sets how long to drive in seconds. All movement methods follow the same pattern: speed + duration. Compare that to robot.beep(440, 500) where you have to remember the order — keyword arguments make code much more readable.
How do the motors work?
The robot has two DC motors (one per wheel), each controlled by an H-bridge circuit that can spin the motor forwards or backwards. PWM controls the speed: a higher duty cycle means more power to the motor. Setting speed=80 means the motor gets power 80% of the time.
→ Motor Control
Checkpoint — Manual Control Working
You can set LED colors with robot.set_leds(), play tones with robot.beep(), and move the robot with robot.forward(). Each command should produce an immediate physical response.
Stuck?
- LEDs don't change: Pass a tuple
(R, G, B)not three separate numbers. - No sound from buzzer: The buzzer is quiet. Hold it near your ear.
- Motors don't move: Is the main power switch on? The Pico LED being on only means USB is connected — motors need the battery switch.
- Robot drives off the table: Keep a hand near it. The USB cable can also yank the robot back unexpectedly.
Part 4: Build Something (25 min)
From REPL to Files
So far, you've been typing commands one at a time in the REPL. That's great for experimenting, but real programs go in files — you write the code in a text editor, save it as a .py file, and send it to the Pico to run.
The workflow is:
- Write your code in a file (e.g.,
my_lightshow.py) - Run it with
mpremote run my_lightshow.py - The Pico executes the whole file top to bottom
Python Syntax: Loops, Lists, and Timing
The code below uses several new concepts. Here's a quick guide:
import time # Load the time library (for sleep)
colors = [ # A LIST — ordered collection in [ ]
(255, 0, 0), # Each item is a tuple (R, G, B)
(0, 255, 0),
(0, 0, 255),
]
for _ in range(5): # FOR LOOP — repeat 5 times
for color in colors: # Inner loop: go through each color
robot.set_leds(color)
time.sleep(0.3) # Wait 0.3 seconds
import timeloads Python's time library so you can usetime.sleep()[...]creates a list — like a tuple but you can change it laterfor ... in ...:** is a for loop** — it repeats the indented code for each itemrange(5)generates the numbers 0, 1, 2, 3, 4 — so the loop runs 5 times_is a convention meaning "I don't care about the number, I just want to repeat"time.sleep(0.3)pauses execution for 0.3 seconds- Indentation matters! The indented lines are inside the loop. Python uses indentation (4 spaces) instead of curly braces
{}to group code.
See Python Basics → Loops for more on for, while, and range().
⚡Hands-on tasks
Time to make your own creation. Pick one of these or invent your own.
Option A: Custom Light Show
Create a file called my_lightshow.py:
from picobot import Robot
import time
robot = Robot()
# Your light show here!
colors = [
(255, 0, 0), # Red
(0, 255, 0), # Green
(0, 0, 255), # Blue
]
for _ in range(5): # Repeat 5 times
for color in colors:
robot.set_leds(color)
time.sleep(0.3)
robot.leds_off()
print("Done!")
Run it:
Option B: Custom Melody
Create my_song.py:
from picobot import Robot
robot = Robot()
# Notes: (frequency in Hz, duration in ms)
# C=262, D=294, E=330, F=349, G=392, A=440, B=494
my_melody = [
(262, 200), # C
(294, 200), # D
(330, 200), # E
(349, 200), # F
(392, 400), # G (longer)
(392, 400), # G
]
for freq, duration in my_melody:
robot.beep(freq, duration)
print("Done!")
Tuple Unpacking
The line for freq, duration in my_melody: is a neat Python trick called tuple unpacking. Each item in my_melody is a tuple like (262, 200). Python automatically splits it into two variables: freq gets 262 and duration gets 200. It's equivalent to writing:
Option C: Dance Routine
Create my_dance.py:
from picobot import Robot
import time
robot = Robot()
# Your dance moves!
robot.set_leds((255, 0, 0))
robot.forward(80, 0.5)
robot.set_leds((0, 255, 0))
robot.backward(80, 0.5)
robot.set_leds((0, 0, 255))
robot.turn_left(speed=80, duration=1.0)
robot.stop()
robot.leds_off()
robot.play_melody("success")
print("Done!")
Make it your own: Change colors, timings, movements. Combine at least two outputs. This is YOUR creation.
Checkpoint — Custom Script Runs
Your script should run with mpremote run my_*.py and produce visible/audible output combining at least two outputs.
Part 5: The Square Challenge (20 min)
Here's a seemingly simple task: make the robot drive a square and return to its starting position.
Try It
For this challenge, the robot needs to drive freely — the USB cable would pull it off course or curl around it. So instead of running from the REPL, you'll save the code as main.py on the Pico. The Pico runs main.py automatically every time it powers on.
Save this as square.py on your computer, then deploy it:
from picobot import Robot
import time
robot = Robot()
# Countdown — gives you time to place the robot and unplug USB
for i in range(5, 0, -1):
print(f"{i}...")
robot.set_leds((255, 255, 0)) # Yellow = waiting
time.sleep(0.5)
robot.leds_off()
time.sleep(0.5)
robot.set_leds((0, 255, 0)) # Green = go!
for i in range(4):
robot.forward(speed=80, duration=1.0)
time.sleep(0.2)
robot.turn_right(speed=80, duration=0.9)
time.sleep(0.2)
robot.stop()
robot.set_leds((0, 0, 255)) # Blue = done
Deploy and run:
Now unplug USB, mark the starting position with tape (both position AND orientation), place the robot on the mark, and press the reset button (or flip the power switch off and on). The LEDs will blink yellow during the countdown — when they turn green, the robot drives.
Why unplug?
The USB cable is physically attached to the robot. If the robot drives a square while tethered, the cable pulls it sideways or wraps around the wheels — you'd be testing the cable, not the motors. By saving the code as main.py, the Pico runs it on power-up and the robot can move freely on battery power alone.
Python Syntax: New Things in This Code
range(5, 0, -1)counts backwards: 5, 4, 3, 2, 1. The three arguments are (start, stop, step). Negative step means count down.for i in range(4):repeats the indented block 4 times, withitaking values 0, 1, 2, 3. A square has four sides, so we repeat the "drive + turn" sequence four times.f"Side {i+1}"is an f-string (formatted string). Thefbefore the quotes lets you embed Python expressions inside{...}braces. Sinceistarts at 0,i+1gives us "Side 1", "Side 2", etc.time.sleep(0.2)pauses for 0.2 seconds between driving and turning — giving the motors a moment to stop before the next command.

✅ Task 2 — Run It Three Times
Reset the robot three times (power cycle or reset button) without changing anything. Place it on the same tape mark each time. Record what happens:
| Run | Returns to Start? | Shape Quality (1–5) | Notes |
|---|---|---|---|
| 1 | |||
| 2 | |||
| 3 |
What did you observe?
- [ ] Perfect square, returns exactly to start
- [ ] Roughly square, close to start
- [ ] Misshapen, ends up somewhere else
- [ ] Different result each time!
Why isn't it a square?
The robot has no idea how far it actually moved or how much it actually turned. It just applies power for a fixed time and hopes the result is correct. Is "hope" a good engineering strategy?
In Make It Move, you'll measure exactly why this happens and build your first feedback controller to fix it. For now, just remember: the robot did what you told it to do, but you couldn't tell it what you actually wanted.
Checkpoint — Square Attempted
The robot drove roughly 4 sides and 4 turns. It almost certainly did NOT return to the starting position. This is not a bug — it's a fundamental limitation that you'll solve over the next several labs.
Part 6: Share and Reflect (10 min)
Show your creation (from Part 4) and your square attempt (from Part 5) to the person next to you.
Discuss:
- What did you make? What was tricky?
- How bad was your square? Did everyone's fail the same way?
- What would the robot need to do a proper square?
If you can, do a 20–30 second demo for the table and explain one design choice you made.
What Just Happened?
You connected to a microcontroller, ran Python on embedded hardware, controlled physical outputs, and discovered that commanding motion is not the same as controlling it.
| What You Did | The Engineering Concept |
|---|---|
robot.set_leds((255,0,0)) |
Digital communication protocol (WS2812B) |
robot.beep(440, 500) |
PWM frequency control (square wave) |
robot.forward(80, 1.0) |
H-bridge motor control (DC drive) |
robot.stop() |
Safety-critical command (fail-safe) |
| Square failed | Open-loop control — no feedback! |
That last row is the important one. Every line of code you wrote — set_leds, beep, forward — translated to electrical signals on GPIO pins. The Pico toggled voltages, generated PWM waveforms, and drove H-bridge circuits. But for the square, the robot had no way to verify what it actually did. It sent motor commands and hoped for the best. The next labs will fix that — first by understanding the hardware directly (GPIO pins, sensors), then by adding feedback so the robot can measure and correct its own behavior.
Python You Learned Today
| Concept | Example | What It Does |
|---|---|---|
| Importing | from picobot import Robot |
Load code from a library |
| Creating objects | robot = Robot() |
Build something you can use |
| Function calls | robot.beep(440, 500) |
Tell an object to do something |
| Keyword arguments | forward(speed=80, duration=1.0) |
Named parameters for clarity |
| Strings | "twinkle" |
Text in quotes |
| Tuples | (255, 0, 0) |
Fixed group of values |
| Lists | [(255,0,0), (0,255,0)] |
Changeable collection |
| For loops | for i in range(4): |
Repeat code multiple times |
| F-strings | f"Side {i+1}" |
Text with embedded values |
| Comments | # This is ignored |
Notes for humans |
| Sleep | time.sleep(0.3) |
Pause execution |
When Things Go Wrong
Python error messages are actually helpful once you learn to read them. The last line tells you what went wrong, and the lines above show where. Common ones you might hit:
SyntaxError— Typo, missing colon, or unmatched parenthesesNameError— You used a name Python doesn't recognize (typo in variable/function name?)TypeError— Wrong type of argument (e.g., passing a number where a tuple is expected)
See Typical Errors for a visual guide to reading error messages.
Embedded vs Desktop
| Desktop Programming | Embedded Programming |
|---|---|
| Runs on full OS (Windows, Linux) | Runs on minimal runtime (MicroPython) |
| Unlimited memory (GB) | Limited memory (520 KB) |
| Click buttons, type text | Control motors, read sensors |
| Errors show dialog box | Errors may cause physical damage |
| Time doesn't matter much | Time is everything |
That last point — time — will become important starting in Ultrasonic & Timing.
Background: Common Embedded Misconceptions
A few traps that catch even experienced programmers new to embedded work:
- "Real-time = fast" — Real-time means predictable deadlines, not raw speed. A system that always responds in 10 ms is real-time; one that usually responds in 1 ms but sometimes takes 500 ms is not.
- "
sleep()is timing" —sleep()blocks everything. On bare-metal (no OS), the entire system freezes during a sleep. You'll learn non-blocking alternatives in Ultrasonic & Timing. - "If it ran once, it works" — Embedded systems run forever under varying conditions — battery drains, temperature changes, sensors degrade. One successful run proves nothing.
What's Next?
In GPIO & Sensors, you'll go beyond the picobot library and work directly with GPIO pins — configuring them as inputs and outputs. You'll meet the robot's line sensors and learn how infrared optocouplers detect surfaces. Understanding GPIO and sensors is essential before tackling motor control and feedback loops.
Challenges (Optional)
Challenge: Rainbow Cycle
Make the LEDs smoothly cycle through rainbow colors. Hint: change colors gradually, not in jumps.
Challenge: Simon Says
Make a game where the robot shows a color and plays a tone, and you have to remember the sequence.
Challenge: VU Meter
Your robot has a microphone. Read it with robot.mic.read_raw() and make the LEDs react to sound level — louder music lights up more LEDs.
Challenge: Repeat the Square
Run your square 5 more times. Measure how far the robot ends up from its starting mark each time. Is the error consistent or random?
Discovery Tasks (Pick One)
These micro-projects help you explore beyond the demos. Choose one and make it your own.
-
Behavior Remix — Create a 20–30 second sequence combining LEDs, buzzer, and motion. Add at least one pause and one direction change.
-
State-Color Map — Invent 3 states (idle, active, warning) and assign a color to each. Use time to switch between them.
-
Peripheral Hunt — Pick one feature you haven't used yet and try to make it work. If it succeeds, explain why. If it fails, explain what might be needed. Use the reference: picobot API
Recap
You connected to the robot, controlled its outputs, and attempted a precision task that failed. That failure — the imperfect square — is not a problem to fix today. It is the motivation for everything that follows: sensors, timing, feedback, and control.