name: circuitpython-runner description: Run CircuitPython code on a connected CircuitPython device and read the output. Use when testing CircuitPython code, debugging hardware interactions, or working with microcontrollers. license: MIT metadata: version: "1.0" requires: ["python3", "circuitpython device"]
CircuitPython Runner Skill
This skill enables you to run CircuitPython code on a connected CircuitPython device and capture the output.
When to Use This Skill
Use this skill when you need to:
- Test CircuitPython code on actual hardware
- Debug sensor readings or hardware interactions
- Prototype microcontroller applications
- Verify CircuitPython library functionality
- Run code that requires physical hardware (displays, sensors, LEDs, etc.)
Requirements
- Python 3 installed on the host system
USB Workflow Runner
- A CircuitPython-compatible device connected and mounted
pyserialinstalled inside the python virtual environment- The
circuitpython_usb_runner.pyscript available in the skills scripts/ directory
Web Workflow Runner
- A CircuitPython-compatible device accessible over the local network
circup,websocket-client, andrequestsinstalled inside the python virtual environment- The
circuitpython_web_runner.pyscript available in the skills scripts/ directory
How It Works
For the USB Workflow: The CircuitPython device appears as a USB drive. Run the usb_runner script to copy a code file to the device, execute it on the CircuitPython device and capture serial output. For the Web Workflow: The CircuitPython device hosts an HTTP server with API for modifying files, and a websocket connection for communicating serial output. Run the web_runner script to copy a code file to the device, execute it, and capture the serial output.
Usage Instructions
Step 1: Write the CircuitPython Code
Save your CircuitPython code to a local file in the working directory.
Example:
# Simple blink example
import time
import board
import digitalio
led = digitalio.DigitalInOut(board.LED)
led.direction = digitalio.Direction.OUTPUT
for i in range(5):
led.value = True
print(f"LED ON - iteration {i+1}")
time.sleep(0.5)
led.value = False
print(f"LED OFF - iteration {i+1}")
time.sleep(0.5)
print("Done!")
Step 2: Use One Of the Runner Scripts
After saving the code, run the circuitpython_usb_runner.py or circuitpython_web_runner.py script to copy the code, execute it and capture the device's output:
python3 circuitpython_usb_runner.py the_code_file.py
python3 circuitpython_web_runner.py the_code_file.py
The script will:
- Copy the file specified in the filename argument to the CircuitPython device.
- Connect to the CircuitPython device's serial port or websocket. Default is serial port is /dev/ttyACM0, use --port to change.
- Run the code by issuing ctrl+C and ctrl+D inputs
- Capture and display all printed output
- Return after 10 seconds or the time specified by the --duration argument
Complete Workflow Example
# 1. Save your code to a local file
cat > example_code.py << 'EOF'
import time
print("Hello from CircuitPython!")
for i in range(3):
print(f"Count: {i}")
time.sleep(1)
print("Goodbye!")
print("~~END~~")
EOF
# 2. Run the output reader
# USB:
python3 circuitpython_usb_runner.py example_code.py
# Web:
python3 circuitpython_web_runner.py --host 192.168.1.122 --password $CIRCUITPYTHON_WW_PASSWORD example_code.py
Common Patterns
End Print
Put at the end of CircuitPython test scripts so that the runner script will be able to return without waiting the full duration when possible.
print("~~END~~")
Sensor Reading
import board
import adafruit_dht
dht = adafruit_dht.DHT22(board.D4)
try:
temperature = dht.temperature
humidity = dht.humidity
print(f"Temp: {temperature}°C, Humidity: {humidity}%")
except RuntimeError as e:
print(f"Error reading sensor: {e}")
print("~~END~~")
I2C Device Communication
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
while not i2c.try_lock():
pass
print("I2C devices found:", [hex(addr) for addr in i2c.scan()])
i2c.unlock()
print("~~END~~")
NeoPixel Control
import board
import neopixel
pixels = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3)
pixels[0] = (255, 0, 0) # Red
print("NeoPixel set to red")
print("~~END~~")
Tips and Best Practices
- Always include print statements - The output reader captures printed text, so add informative print statements to track execution
- Handle errors gracefully - Use try-except blocks to catch and report errors without crashing
- Keep code concise - CircuitPython devices have limited memory; keep your test code focused
- Add delays when needed - Some hardware operations need time to complete; use
time.sleep()appropriately - Import only what you need - Minimize imports to reduce memory usage
print("~~END~~")at end of scripts - Print this string to help the runner script finish faster when possible.
Common Edge Cases
- Device not mounted: If
CIRCUITPY/is not accessible, as the user to check that the device is properly connected and recognized by the system - Permission errors: You may need appropriate permissions to write to the mounted device path
- Code syntax errors: If the code has syntax errors, the device will display an error message that the script should capture
- Infinite loops: If your code contains an infinite loop without output, the reader script may timeout
- Import errors: If libraries are missing from the device, you'll see ImportError messages in the output
Troubleshooting
No output received:
- Ask the user to verify the device is connected and mounted, or that the web workflow device is running
- Check that the serial port is accessible
- Ensure your code contains print statements
Script timeouts:
- The output reader has a --duration argument to control the timeout length
- Ensure your CircuitPython code completes in a reasonable time
- Add a final print statement to signal completion
Help command:
- The runner script supports the
--helpargument to print information about the script and its parameters.