picobot API Reference (Robot Class)
The picobot library provides a high-level Robot class used throughout the labs. It is designed in layers so you can start with simple calls and later access lower-level components.
Quick Start
from picobot import Robot
import time
robot = Robot()
robot.demo_lightshow()
robot.beep(880, 150)
robot.forward(80, 1.0)
robot.stop()
Layers of Use
Layer 0 (Magic):
- demo_lightshow(), demo_dance(), demo_dj(), demo_dj_live(), play_melody(), drive_square()
Layer 1 (Control):
- forward(), backward(), turn_left(), turn_right()
- turn_degrees(), line_follow(), line_follow_step()
- set_motors(), stop(), read_distance()
- get_heading(), reset_heading(), at_junction(), line_lost()
Layer 2 (Direct Access):
- robot.motors — dual motor controller
- robot.sensors.line — 4-channel line sensor array
- robot.sensors.distance — HC-SR04 ultrasonic sensor
- robot.imu — BMI160 IMU (gyroscope + accelerometer)
- robot.leds — 8× WS2812B NeoPixel strip
- robot.buzzer — piezo buzzer
- robot.mic — electret microphone (ADC)
Common Methods
Layer 0 — Demos
| Method | Purpose | Notes |
|---|---|---|
demo_lightshow(duration=5) |
LED animation | Rainbow cycling |
demo_dance(duration=5) |
Movement sequence | Forward, back, spin |
demo_dj(duration=10, bpm=128) |
Techno beats, lights, dance | Four-on-the-floor + build-ups + drops |
demo_dj_live(duration=60, sensitivity=80) |
React to music via microphone | Play music near robot |
play_melody(name="twinkle") |
Buzzer melody | Names: twinkle, scale, alert, success, error |
drive_square(side_cm=30, speed=80) |
Drive in a square | Time-based, approximate |
Layer 1 — Control
| Method | Purpose | Notes |
|---|---|---|
forward(speed=80, duration=None) |
Drive forward | Speed 0–255. If duration given, stops after. |
backward(speed=80, duration=None) |
Drive backward | Speed 0–255 |
turn_left(speed=80, duration=0.5) |
Turn left in place | Adjust speed + duration to calibrate |
turn_right(speed=80, duration=0.5) |
Turn right in place | Same pattern as forward/backward |
turn_degrees(degrees, speed=80) |
IMU-based turn | Positive = left, negative = right |
set_motors(left, right) |
Set motor speeds | -255 to 255 |
stop() |
Stop all motors | |
line_follow(speed=80, kp=30, duration=None) |
Blocking line follow | Ctrl+C to stop |
line_follow_step(speed=80, kp=30) |
One control step | Returns error or None |
read_distance() |
Ultrasonic distance (cm) | Slow (~25 ms) |
get_heading() |
Current IMU heading (degrees) | Calls imu.update() internally |
reset_heading() |
Reset heading to zero | |
at_junction() |
All sensors see black? | Returns True/False |
line_lost() |
No sensors see black? | Returns True/False |
set_leds(colors) |
Set LED colors | Single tuple (all same) or list of 8 |
set_led(index, color) |
Single LED | Index 0–7, color = (R, G, B) |
leds_off() |
LEDs off | |
beep(freq=440, duration=100) |
Simple tone | Duration in ms |
Peripherals (Direct Access)
Motors
robot.motors.set_speed(left, right) # -255 to 255
robot.motors.stop()
robot.motors.left.set_speed(100) # Individual motor
robot.motors.right.set_speed(100)
robot.motors.left.set_pwm(50) # Raw duty cycle 0-100%
Line Sensors
robot.sensors.line.read_raw() # [0,1,1,0] — 0=black, 1=white
robot.sensors.line.get_error() # -1.5 to +1.5, None if lost
robot.sensors.line.at_junction() # True if all see black
robot.sensors.line.line_lost() # True if none see black
robot.sensors.line.get_pattern() # "█░░█" visual representation
Ultrasonic Distance
robot.sensors.distance.read_cm() # Distance in cm (slow ~25ms!)
robot.sensors.distance.last_reading # Last value without blocking
robot.sensors.distance.read_raw_us() # Raw pulse duration (µs)
IMU (BMI160 Gyroscope + Accelerometer)
robot.imu.calibrate(samples=100) # Calibrate bias — keep robot still!
robot.imu.update() # Update heading (call every loop)
robot.imu.heading # Current heading in degrees (property)
robot.imu.reset_heading() # Reset to zero
robot.imu.gyro_z() # Raw angular velocity (°/s)
robot.imu.accel() # (ax, ay, az) in g units
robot.imu.bias # Calibrated bias value (property)
LEDs
robot.leds.fill((0, 255, 0)) # All LEDs one color
robot.leds.set(index, (r, g, b)) # Single LED
robot.leds.set_all([(r,g,b), ...]) # List of 8 colors
robot.leds.off() # All off
robot.leds.show_state("FOLLOW") # Color by state name
robot.leds.show_error(error) # Visualize line error
robot.leds.show_distance(cm) # Visualize distance
robot.leds.count # Number of LEDs (property)
Predefined colors: LEDStrip.RED, GREEN, BLUE, YELLOW, CYAN, MAGENTA, WHITE, ORANGE, OFF
Buzzer
robot.buzzer.tone(440) # Start playing frequency
robot.buzzer.off() # Stop
robot.buzzer.beep(440, 100) # Play for duration (ms)
robot.buzzer.note('A4') # Play by note name (C4-C6)
robot.buzzer.play_sequence([(440, 200), (0, 100), (880, 200)]) # Sequence
Microphone
robot.mic.calibrate() # Re-calibrate (keep quiet, motors off!)
robot.mic.read_raw() # Raw ADC value (0-65535, ~32768 = silence)
robot.mic.amplitude() # Distance from silence (0-32768)
robot.mic.level() # Smoothed sound level (0-100)
robot.mic.beat(threshold=40, cooldown_ms=200) # True if beat detected
Helper Classes
DataLogger
from picobot import DataLogger
logger = DataLogger("experiment.csv")
logger.start("error", "speed", "heading") # Column names
for i in range(100):
logger.log(error, speed, heading) # One row per call
time.sleep(0.02)
logger.stop()
# Download: mpremote cp :experiment.csv .
Timer (Non-blocking)
from picobot import Timer
fast_timer = Timer(10) # 10ms period
slow_timer = Timer(100) # 100ms period
while True:
if fast_timer.ready(): # True when period elapsed
do_fast_thing()
if slow_timer.ready():
do_slow_thing()
StateMachine
from picobot import Robot, StateMachine
robot = Robot()
sm = StateMachine(robot)
sm.add_state("IDLE", action=robot.stop)
sm.add_state("FOLLOW", action=lambda: robot.line_follow_step())
sm.add_transition("IDLE", "FOLLOW", condition=lambda: True)
sm.run(loop_period_ms=20) # Runs until Ctrl+C
# Properties and methods:
sm.state # Current state name
sm.time_in_state() # ms since entering current state
sm.set_state("IDLE") # Force state change
sm.get_history() # Recent transitions
Notes
read_distance()is slow by physics (~25ms). Do not call it every loop.turn_left(speed, duration)/turn_right(speed, duration)are time-based. Calibrate by tweaking both parameters. Precision comes withturn_degrees()(IMU-based).get_heading()callsimu.update()internally. If accessingrobot.imu.headingdirectly, callrobot.imu.update()first.- Motor speeds are -255 to 255. Values below the deadband (~20-30%) won't move the motors.