Skip to content

Deployment Guide

How to get code onto your robot - from simple USB to wireless OTA.


The Journey

Lab 1-2:  USB cable → mpremote run
Lab 3+:   WiFi → OTA upload (USB fallback)

Start simple, graduate to wireless when you're comfortable.


Method 1: USB with mpremote (Labs 1-2)

Run Code Directly

Best for quick testing - code runs but isn't saved to Pico:

# Run a script (doesn't save to Pico)
mpremote connect /dev/ttyACM0 run my_script.py

Save Code to Pico

To make code run on boot:

# Copy file to Pico (as main.py = runs on boot)
mpremote connect /dev/ttyACM0 cp my_script.py :main.py

# Copy library to lib folder
mpremote connect /dev/ttyACM0 mkdir lib
mpremote connect /dev/ttyACM0 cp picobot.py :lib/picobot.py

Interactive REPL

# Start interactive Python shell
mpremote connect /dev/ttyACM0 repl

# Shortcuts:
# Ctrl+C - Stop running code
# Ctrl+D - Soft reset
# Ctrl+X - Exit mpremote

Common mpremote Commands

mpremote connect /dev/ttyACM0 ls          # List files on Pico
mpremote connect /dev/ttyACM0 cat main.py # View file contents
mpremote connect /dev/ttyACM0 rm main.py  # Delete file
mpremote connect /dev/ttyACM0 reset       # Reboot Pico

Method 2: WiFi with OTA (Labs 3+)

Once you've set up WiFi, you can deploy wirelessly!

First-Time Setup (via USB)

Upload the OTA server and WiFi boot code:

# 1. Upload OTA server library
mpremote connect /dev/ttyACM0 cp ota.py :ota.py

# 2. Create boot.py with WiFi + OTA
mpremote connect /dev/ttyACM0 cp boot_wifi.py :boot.py

boot.py template:

import network
import time
from ota import OTAServer

# Connect to WiFi
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('YOUR_SSID', 'YOUR_PASSWORD')

# Wait for connection
for _ in range(10):
    if wlan.isconnected():
        break
    time.sleep(1)

if wlan.isconnected():
    print(f"Connected: {wlan.ifconfig()[0]}")
    # Start OTA server
    ota = OTAServer()
else:
    print("WiFi failed - USB only mode")
    ota = None

Using OTA Client

Once your Pico is on WiFi, find its IP (shown on boot or use OLED):

# Check if robot is reachable
python ota_client.py 192.168.1.100 ping

# Upload your code
python ota_client.py 192.168.1.100 upload main.py

# Upload and restart
python ota_client.py 192.168.1.100 upload main.py
python ota_client.py 192.168.1.100 restart

# List files on Pico
python ota_client.py 192.168.1.100 list

# Delete a file
python ota_client.py 192.168.1.100 delete old_file.py

# Execute code remotely
python ota_client.py 192.168.1.100 exec "machine.freq()"

OTA Protocol

The OTA server uses UDP port 5007:

Command Description
PING Check if robot is online
UPLOAD:<file>:<size> Upload file
LIST List files
DELETE:<file> Delete file
RESTART Soft reboot
EXEC:<code> Run Python code

Quick Deploy Script

For common workflows, create a simple deploy script:

deploy.sh:

#!/bin/bash
# Quick deploy to robot

ROBOT_IP="${ROBOT_IP:-192.168.1.100}"
PICO_PORT="${PICO_PORT:-/dev/ttyACM0}"

# Try WiFi first, fall back to USB
if python ota_client.py "$ROBOT_IP" ping 2>/dev/null | grep -q PONG; then
    echo "Deploying via WiFi..."
    python ota_client.py "$ROBOT_IP" upload "$1"
    python ota_client.py "$ROBOT_IP" restart
else
    echo "WiFi unavailable, using USB..."
    mpremote connect "$PICO_PORT" cp "$1" :main.py
    mpremote connect "$PICO_PORT" reset
fi

Usage:

# Set your robot's IP once
export ROBOT_IP=192.168.1.100

# Deploy any script
./deploy.sh my_code.py


pico-deploy Tool

For a more integrated experience, use the pico-deploy tool:

# Install (one-time)
pip install pico-deploy  # or copy from tools/

# Basic usage
pico-deploy run my_script.py     # Run without saving
pico-deploy push my_script.py    # Save as main.py
pico-deploy push -l picobot.py   # Save to lib/
pico-deploy repl                 # Interactive shell
pico-deploy ls                   # List files

# Automatic connection (tries WiFi, falls back to USB)
pico-deploy --auto push main.py

Configuration (~/.pico-deploy.yaml):

# Default connection settings
usb_port: /dev/ttyACM0
wifi_ip: 192.168.1.100

# Auto mode: wifi_first or usb_first
auto_mode: wifi_first

# Known robots (for multi-robot setups)
robots:
  red: 192.168.1.101
  blue: 192.168.1.102


Troubleshooting

USB Issues

Problem Solution
"Device not found" Check USB cable, try different port
"Permission denied" Add user to dialout group: sudo usermod -a -G dialout $USER
"Device busy" Close other programs (Thonny, etc.)

WiFi Issues

Problem Solution
"Timeout" Check robot is on, correct IP
"No response" Verify WiFi connection, check boot.py
Robot IP unknown Connect via USB, check wlan.ifconfig()

Recovery Mode

If your Pico won't boot (bad boot.py):

  1. Hold BOOTSEL button while connecting USB
  2. Pico appears as USB drive
  3. Copy new MicroPython UF2 to reflash
  4. Or use mpremote to fix boot.py

Workflow Summary

Development Cycle:
┌─────────────────────────────────────────────────┐
│                                                 │
│  Edit code  →  Deploy  →  Test  →  Iterate     │
│      ↑                              │          │
│      └──────────────────────────────┘          │
│                                                 │
│  Lab 1-2: mpremote run script.py               │
│  Lab 3+:  ota_client.py upload + restart       │
│                                                 │
└─────────────────────────────────────────────────┘

Key insight: You don't need to constantly swap USB cables once WiFi is set up. Just edit, upload, restart - all wirelessly!


← Reference Index | mpremote Cheatsheet →