Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:28:30 +08:00
commit 1f575f61f9
16 changed files with 6086 additions and 0 deletions

View File

@@ -0,0 +1,252 @@
---
name: badger-2350-dev
description: Development environment setup and workflow for Universe 2025 (Tufty) Badge with MonaOS. Use when setting up the badge, flashing firmware, debugging, or working with the development toolchain.
---
# Universe 2025 Badge Development
Help develop, flash, and debug applications for the Universe 2025 (Tufty) Badge and its **MonaOS** launcher system.
## Important: MonaOS & API
The Universe 2025 Badge uses:
- **MonaOS**: Built-in app launcher that auto-discovers apps in `/system/apps/` directory
- **badgeware Module**: Custom API with screen, brushes, shapes, io, Image, PixelFont
- **Display**: 160x120 framebuffer (pixel-doubled to 320x240)
- **App Structure**: Apps are **directories** containing `__init__.py`, `icon.png`, and optional `assets/`
- **Entry Point**: Apps must implement `update()` function called every frame
When developing MonaOS apps:
1. Use the `badgeware` module API
2. Create app as directory with `__init__.py` and `icon.png`
3. Install to `/system/apps/my_app/` directory
4. HOME button exits to menu automatically
5. Default menu shows 6 apps - enable pagination for more
## Board Specifications
- **Processor**: RP2350 dual-core ARM Cortex-M33 @ 200MHz
- **Memory**: 512KB SRAM, 16MB QSPI XiP flash
- **Display**: 320x240 full-color IPS (160x120 framebuffer pixel-doubled)
- **Connectivity**: 2.4GHz WiFi, Bluetooth 5
- **Power**: USB-C charging, 1000mAh rechargeable battery (up to 8 hours runtime)
- **Special Features**: IR receiver/transmitter, 4-zone LED backlight
- **Buttons**: 5 front-facing (A, B, C, UP, DOWN) + HOME button
- **Expansion**: 4 GPIO pins, Qw/ST and SWD ports
- **Primary Language**: MicroPython with badgeware module + MonaOS
## Development Setup
### 1. Install toolchain
For MicroPython development:
```bash
# Install Thonny IDE (recommended for beginners)
brew install --cask thonny
# Or install command-line tools
pip install esptool adafruit-ampy mpremote
```
For C++ development:
```bash
# Install Pico SDK
git clone https://github.com/raspberrypi/pico-sdk.git
export PICO_SDK_PATH=/path/to/pico-sdk
```
### 2. Connect to the badge
```bash
# List connected devices
ls /dev/tty.usb*
# Connect via serial (MicroPython REPL)
screen /dev/tty.usbmodem* 115200
# Exit screen: Ctrl+A then K
```
### 3. Flash firmware
```bash
# Put badge in bootloader mode (hold BOOT button, press RESET)
# Flash MicroPython firmware
esptool.py --port /dev/tty.usbmodem* write_flash 0x0 firmware.uf2
```
## Common Development Tasks
### Test app temporarily (doesn't save)
```bash
mpremote connect /dev/tty.usbmodem* run my_app/__init__.py
```
### Install MonaOS app using USB Mass Storage Mode
**⚠️ CRITICAL**: `/system/apps/` is READ-ONLY via mpremote. You MUST use USB Mass Storage Mode to install apps.
```bash
# Step 1: Enter Mass Storage Mode
# - Press RESET button TWICE quickly (double-click)
# - Badge appears as "BADGER" drive
# Step 2: Copy app to badge
# macOS/Linux:
cp -r my_app /Volumes/BADGER/apps/
# Windows:
xcopy my_app D:\apps\my_app\ /E /I
# Step 3: Exit Mass Storage Mode
# - Eject BADGER drive safely
diskutil eject /Volumes/BADGER # macOS
# - Press RESET once to reboot
# Your app now appears in MonaOS launcher!
```
**File System Mapping**:
- `/Volumes/BADGER/apps/``/system/apps/` on badge
- `/Volumes/BADGER/assets/``/system/assets/` on badge
### List MonaOS apps (read-only view)
```bash
mpremote connect /dev/tty.usbmodem* ls /system/apps
```
**⚠️ Note**: Install the paginated menu for unlimited apps (default shows 6):
- Download: https://raw.githubusercontent.com/badger/home/refs/heads/main/badge/apps/menu/__init__.py
- Replace `/Volumes/BADGER/apps/menu/__init__.py` in Mass Storage Mode
### List files in writable storage
```bash
mpremote connect /dev/tty.usbmodem* ls /storage
```
### Download file from badge
```bash
mpremote connect /dev/tty.usbmodem* cp :/system/apps/my_app/__init__.py local_backup.py
```
## Project Structure
MonaOS app structure on your computer:
```
my_app/ # MonaOS app directory
├── __init__.py # Entry point with update() function (required)
├── icon.png # 24x24 PNG icon for launcher (required)
├── assets/ # Optional: app resources (auto-added to path)
│ ├── sprites.png
│ ├── font.ppf
│ └── data.json
└── README.md # Optional: app documentation
```
Multiple apps in development:
```
badge-project/
├── my_app/ # First MonaOS app
│ ├── __init__.py
│ └── icon.png
├── game_app/ # Second MonaOS app
│ ├── __init__.py
│ ├── icon.png
│ └── assets/
│ └── sprites.png
├── requirements.txt # Python tools
└── deploy.sh # Deployment script
```
## Debugging
### Check badge logs
```python
# In REPL
import sys
sys.print_exception(e) # Print full exception traceback
```
### Test display
```python
from badgeware import screen, display, brushes
# Clear with black
screen.brush = brushes.color(0, 0, 0)
screen.clear()
# White text
screen.brush = brushes.color(255, 255, 255)
screen.text("Hello Badge!", 10, 10, 2)
display.update()
```
### Test WiFi
```python
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('SSID', 'password')
print(wlan.isconnected())
print(wlan.ifconfig()) # IP address info
```
## Power Management
```python
import machine
import badgeware
# Check battery level
battery = badgeware.get_battery_level()
print(f"Battery: {battery}%")
# Check if USB connected
usb = badgeware.get_usb_connected()
print(f"USB: {usb}")
# Light sleep (for delays)
machine.lightsleep(1000) # Sleep 1 second
# Deep sleep (wake on button press - saves significant power)
machine.deepsleep()
```
## Tips for MonaOS Apps
- MonaOS apps use `update()` function called every frame
- Optimize `update()` - it runs continuously
- Use `io.ticks` for animations instead of time.time()
- Minimize allocations in `update()` to reduce GC pauses
- Use `try/except` blocks to prevent crashes
- Test with USB power first, then battery
- Apps automatically return to menu when HOME button pressed
## Common Issues
**Badge not detected**: Check USB cable, try different port, press RESET button
**Out of memory**: Reduce allocations in `update()`, use generators, call `gc.collect()`, free variables with `del`
**Display not updating**: MonaOS automatically updates after `update()` returns - no manual update needed
**App not in menu**: Check uploaded to `/system/apps/my_app/`, verify icon.png exists, may need pagination: https://badger.github.io/hack/menu-pagination/
**WiFi connection fails**: Check credentials, verify 2.4GHz band, restart badge
## Resources
### Official Badge Resources
- **Getting Started**: https://badger.github.io/get-started/ - Overview and setup
- **About Badge**: https://badger.github.io/about-badge/ - Hardware specifications
- **Hacks**: https://badger.github.io/hacks/ - Beginner to advanced customization tutorials
- **Apps**: https://badger.github.io/apps/ - Loadable MonaOS apps (Commits, Snake)
- **Source Code**: https://github.com/badger/home/tree/main/badgerware - Official MonaOS app code and API docs
### API Documentation
- **badgeware modules**: https://github.com/badger/home/tree/main/badgerware - shapes, brushes, io, Image, PixelFont, Matrix
### Development Resources
- **MicroPython docs**: https://docs.micropython.org/
- **WiFi/Network**: https://docs.micropython.org/en/latest/rp2/quickref.html#wlan
- **Community**: Badger GitHub discussions

View File

@@ -0,0 +1,774 @@
---
name: badger-app-creator
description: Create MicroPython applications for Universe 2025 (Tufty) Badge including display graphics, button handling, and MonaOS app structure. Use when building badge apps, creating interactive displays, or developing MicroPython programs.
---
# Universe 2025 Badge App Creator
Create well-structured MicroPython applications for the **Universe 2025 (Tufty) Badge** with MonaOS integration, display graphics, button handling, and proper app architecture.
## Important: MonaOS App Structure
**Critical**: MonaOS apps follow a specific structure. Each app is a directory in `/system/apps/` containing:
```
/system/apps/my_app/
├── icon.png # 24x24 PNG icon
├── __init__.py # Entry point with update() function
└── assets/ # Optional: app assets (auto-added to path)
└── ...
```
### Required Functions
Your `__init__.py` must implement:
**`update()`** - Required, called every frame by MonaOS:
```python
def update():
# Called every frame
# Draw your UI, handle input, update state
pass
```
**`init()`** - Optional, called once when app launches:
```python
def init():
# Initialize app state, load resources
pass
```
**`on_exit()`** - Optional, called when HOME button pressed:
```python
def on_exit():
# Save state, cleanup resources
pass
```
## MonaOS App Template
```python
# __init__.py - MonaOS app template
from badgeware import screen, brushes, shapes, io, PixelFont, Image
# App state
app_state = {
"counter": 0,
"color": (255, 255, 255)
}
def init():
"""Called once when app launches"""
# Load font
screen.font = PixelFont.load("nope.ppf")
# Load saved state if exists
try:
with open("/storage/myapp_state.txt", "r") as f:
app_state["counter"] = int(f.read())
except:
pass
print("App initialized!")
def update():
"""Called every frame by MonaOS"""
# Clear screen
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw UI
screen.brush = brushes.color(255, 255, 255)
screen.text("My App", 10, 10)
screen.text(f"Count: {app_state['counter']}", 10, 30)
# Handle buttons (checked every frame)
if io.BUTTON_A in io.pressed:
app_state["counter"] += 1
if io.BUTTON_B in io.pressed:
app_state["counter"] = 0
# HOME button exits automatically
def on_exit():
"""Called when returning to MonaOS menu"""
# Save state
with open("/storage/myapp_state.txt", "w") as f:
f.write(str(app_state["counter"]))
print("App exiting!")
```
## Display API (badgeware)
### Import Modules
```python
from badgeware import screen, brushes, shapes, Image, PixelFont, Matrix, io
```
### Screen Drawing (160x120 framebuffer)
The screen is a 160×120 RGB framebuffer that MonaOS automatically pixel-doubles to 320×240.
**Basic Drawing**:
```python
# Set brush color (RGB 0-255)
screen.brush = brushes.color(r, g, b)
# Clear screen
screen.clear()
# Draw text
screen.text("Hello", x, y)
# Draw shapes
screen.draw(shapes.rectangle(x, y, width, height))
screen.draw(shapes.circle(x, y, radius))
screen.draw(shapes.line(x1, y1, x2, y2))
screen.draw(shapes.arc(x, y, radius, start_angle, end_angle))
screen.draw(shapes.pie(x, y, radius, start_angle, end_angle))
```
**Antialiasing** (smooth edges):
```python
screen.antialias = Image.X4 # Enable 4x antialiasing
screen.antialias = Image.NONE # Disable
```
**No Manual Update Needed**: MonaOS automatically updates the display after each `update()` call.
### Shapes Module
Full documentation: https://github.com/badger/home/blob/main/badgerware/shapes.md
**Available Shapes**:
```python
from badgeware import shapes
# Rectangle
shapes.rectangle(x, y, width, height)
# Circle
shapes.circle(x, y, radius)
# Line
shapes.line(x1, y1, x2, y2)
# Arc (portion of circle outline)
shapes.arc(x, y, radius, start_angle, end_angle)
# Pie (filled circle segment)
shapes.pie(x, y, radius, start_angle, end_angle)
# Rounded rectangle
shapes.rounded_rectangle(x, y, width, height, radius)
# Regular polygon (pentagon, hexagon, etc.)
shapes.regular_polygon(x, y, sides, radius)
# Squircle (smooth rectangle-circle hybrid)
shapes.squircle(x, y, width, height)
```
**Transformations**:
```python
from badgeware import Matrix
# Create shape
rect = shapes.rectangle(-1, -1, 2, 2)
# Apply transformation
rect.transform = Matrix() \
.translate(80, 60) \ # Move to center
.scale(20, 20) \ # Scale up
.rotate(io.ticks / 100) # Animated rotation
screen.draw(rect)
```
### Brushes Module
Full documentation: https://github.com/badger/home/blob/main/badgerware/brushes.md
**Solid Colors**:
```python
from badgeware import brushes
# RGB color (0-255 per channel)
screen.brush = brushes.color(r, g, b)
# Examples
screen.brush = brushes.color(255, 0, 0) # Red
screen.brush = brushes.color(0, 255, 0) # Green
screen.brush = brushes.color(0, 0, 255) # Blue
screen.brush = brushes.color(255, 255, 255) # White
screen.brush = brushes.color(0, 0, 0) # Black
```
### Fonts
Full documentation: https://github.com/badger/home/blob/main/PixelFont.md
**30 Licensed Pixel Fonts Included**:
```python
from badgeware import PixelFont
# Load font
screen.font = PixelFont.load("nope.ppf")
# Draw text with loaded font
screen.text("Styled text", x, y)
# Measure text width
width = screen.font.measure("text to measure")
# Reset to default font
screen.font = None
```
### Images & Sprites
Full documentation: https://github.com/badger/home/blob/main/badgerware/Image.md
**Loading Images**:
```python
from badgeware import Image
# Load PNG image
img = Image.load("sprite.png")
# Blit to screen
screen.blit(img, x, y)
# Scaled blit
screen.scale_blit(img, x, y, width, height)
```
**Sprite Sheets**:
```python
# Using SpriteSheet helper (from examples)
from lib import SpriteSheet
# Load sprite sheet (7 columns, 1 row)
sprites = SpriteSheet("assets/mona-sprites.png", 7, 1)
# Blit specific sprite (column 0, row 0)
screen.blit(sprites.sprite(0, 0), x, y)
# Scaled sprite
screen.scale_blit(sprites.sprite(3, 0), x, y, 30, 30)
```
## Button Handling (io module)
Full documentation: https://github.com/badger/home/blob/main/badgerware/io.md
### Button Constants
```python
from badgeware import io
# Available buttons
io.BUTTON_A # Left button
io.BUTTON_B # Middle button
io.BUTTON_C # Right button
io.BUTTON_UP # Up button
io.BUTTON_DOWN # Down button
io.BUTTON_HOME # HOME button (exits to MonaOS)
```
### Button States
Check button states within your `update()` function:
```python
def update():
# Button just pressed this frame
if io.BUTTON_A in io.pressed:
print("A was just pressed")
# Button just released this frame
if io.BUTTON_B in io.released:
print("B was just released")
# Button currently held down
if io.BUTTON_C in io.held:
print("C is being held")
# Button state changed this frame (pressed or released)
if io.BUTTON_UP in io.changed:
print("UP state changed")
```
**No Debouncing Needed**: The io module handles button debouncing automatically.
### Menu Navigation Example
```python
menu_items = ["Option 1", "Option 2", "Option 3", "Option 4"]
selected = 0
def update():
global selected
# Clear screen
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw title
screen.brush = brushes.color(255, 255, 255)
screen.text("Menu", 10, 5)
# Draw menu items
y = 30
for i, item in enumerate(menu_items):
if i == selected:
# Highlight selected item
screen.brush = brushes.color(255, 255, 0)
screen.text("> " + item, 10, y)
else:
screen.brush = brushes.color(200, 200, 200)
screen.text(" " + item, 10, y)
y += 20
# Handle navigation
if io.BUTTON_UP in io.pressed:
selected = (selected - 1) % len(menu_items)
if io.BUTTON_DOWN in io.pressed:
selected = (selected + 1) % len(menu_items)
if io.BUTTON_A in io.pressed:
print(f"Selected: {menu_items[selected]}")
```
## Animation & Timing
### Using io.ticks
```python
from badgeware import io
import math
def update():
# io.ticks increments every frame
# Use for smooth animations
# Oscillating value
y = (math.sin(io.ticks / 100) * 30) + 60
# Rotating shape
angle = io.ticks / 50
rect = shapes.rectangle(-1, -1, 2, 2)
rect.transform = Matrix().translate(80, 60).rotate(angle)
screen.draw(rect)
# Pulsing size
scale = (math.sin(io.ticks / 60) * 10) + 20
circle = shapes.circle(80, 60, scale)
screen.draw(circle)
```
## State Management
### Persistent Storage
Store app data in the writable LittleFS partition at `/storage/`:
```python
import json
CONFIG_FILE = "/storage/myapp_config.json"
def save_config(data):
"""Save configuration to persistent storage"""
try:
with open(CONFIG_FILE, "w") as f:
json.dump(data, f)
print("Config saved!")
except Exception as e:
print(f"Save failed: {e}")
def load_config():
"""Load configuration from persistent storage"""
try:
with open(CONFIG_FILE, "r") as f:
return json.load(f)
except:
# Return defaults if file doesn't exist
return {
"name": "Badge User",
"theme": "light",
"counter": 0
}
# Usage in app
config = {}
def init():
global config
config = load_config()
print(f"Loaded: {config}")
def on_exit():
save_config(config)
```
### State Machine Pattern
```python
class AppState:
MENU = 0
GAME = 1
SETTINGS = 2
GAME_OVER = 3
state = AppState.MENU
game_data = {"score": 0, "level": 1}
def update():
global state
if state == AppState.MENU:
draw_menu()
if io.BUTTON_A in io.pressed:
state = AppState.GAME
elif state == AppState.GAME:
update_game()
draw_game()
if game_data["score"] < 0:
state = AppState.GAME_OVER
elif state == AppState.SETTINGS:
draw_settings()
if io.BUTTON_B in io.pressed:
state = AppState.MENU
elif state == AppState.GAME_OVER:
draw_game_over()
if io.BUTTON_A in io.pressed:
state = AppState.MENU
game_data = {"score": 0, "level": 1}
def draw_menu():
screen.brush = brushes.color(0, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
screen.text("MAIN MENU", 40, 50)
screen.text("Press A to start", 30, 70)
def update_game():
# Game logic
game_data["score"] += 1
def draw_game():
screen.brush = brushes.color(0, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
screen.text(f"Score: {game_data['score']}", 10, 10)
def draw_settings():
# Settings UI
pass
def draw_game_over():
screen.brush = brushes.color(0, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 0, 0)
screen.text("GAME OVER", 40, 50)
screen.text(f"Score: {game_data['score']}", 40, 70)
```
## WiFi Integration
Use standard MicroPython network module:
```python
import network
import time
def connect_wifi(ssid, password):
"""Connect to WiFi network"""
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
if wlan.isconnected():
print("Already connected:", wlan.ifconfig()[0])
return True
print(f"Connecting to {ssid}...")
wlan.connect(ssid, password)
# Wait for connection (with timeout)
timeout = 10
while not wlan.isconnected() and timeout > 0:
time.sleep(1)
timeout -= 1
if wlan.isconnected():
print("Connected:", wlan.ifconfig()[0])
return True
else:
print("Connection failed")
return False
def fetch_data(url):
"""Fetch data from URL"""
try:
import urequests
response = urequests.get(url)
data = response.json()
response.close()
return data
except Exception as e:
print(f"Error fetching data: {e}")
return None
# Usage in app
def init():
if connect_wifi("MyWiFi", "password123"):
data = fetch_data("https://api.example.com/data")
if data:
print("Got data:", data)
```
Full WiFi docs: https://docs.micropython.org/en/latest/rp2/quickref.html#wlan
## Performance Optimization
### Reduce Draw Calls
```python
# Bad - many individual draws
def update():
for i in range(100):
screen.draw(shapes.rectangle(i, i, 2, 2))
# Better - batch or optimize
def update():
# Draw fewer, larger shapes
screen.draw(shapes.rectangle(0, 0, 100, 100))
```
### Cache Computed Values
```python
# Cache expensive calculations
_cached_sprites = None
def get_sprites():
global _cached_sprites
if _cached_sprites is None:
_cached_sprites = SpriteSheet("sprites.png", 8, 8)
return _cached_sprites
def update():
sprites = get_sprites() # Fast after first call
screen.blit(sprites.sprite(0, 0), 10, 10)
```
### Minimize Memory Allocation
```python
# Bad - creates new lists every frame
def update():
items = [1, 2, 3, 4, 5] # Don't do this in update()
for item in items:
process(item)
# Good - create once, reuse
items = [1, 2, 3, 4, 5] # Module level
def update():
for item in items: # Reuse existing list
process(item)
```
## Project Structure Best Practices
### Simple App (Single File)
```
my_app/
├── icon.png
└── __init__.py
```
### Complex App (Multiple Files)
```
my_app/
├── icon.png
├── __init__.py # Entry point
└── assets/
├── sprites.png
├── font.ppf
└── config.json
```
Access assets using relative paths (assets/ is auto-added to sys.path):
```python
# In __init__.py
from badgeware import Image
# Load from assets/
sprite = Image.load("assets/sprites.png")
# Or if assets/ in path:
sprite = Image.load("sprites.png")
```
## Error Handling
```python
def update():
"""Update with error handling"""
try:
# Your update code
draw_ui()
handle_input()
except Exception as e:
# Show error on screen
screen.brush = brushes.color(255, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
screen.text("Error:", 10, 10)
screen.text(str(e)[:30], 10, 30)
# Log to console for debugging
import sys
sys.print_exception(e)
```
## Testing & Debugging
### Test Locally
```bash
# Run app temporarily without installing
mpremote run my_app/__init__.py
```
### REPL Debugging
```bash
# Connect to REPL
mpremote
# Test imports
>>> from badgeware import screen, brushes
>>> screen.brush = brushes.color(255, 0, 0)
>>> screen.clear()
```
### Print Debugging
```python
def update():
# Print statements appear in REPL/serial console
print(f"State: {state}, Counter: {counter}")
# Draw debug info on screen
screen.text(f"Debug: {value}", 0, 110)
```
## Official Examples
Study official examples: https://github.com/badger/home/tree/main/badge/apps
Key examples:
- **Commits Game**: Sprite animations, collision detection
- **Snake Game**: Grid-based movement, state management
- **Menu System**: Navigation, app launching
## Official API Documentation
- **Image class**: https://github.com/badger/home/blob/main/badgerware/Image.md
- **shapes module**: https://github.com/badger/home/blob/main/badgerware/shapes.md
- **brushes module**: https://github.com/badger/home/blob/main/badgerware/brushes.md
- **PixelFont class**: https://github.com/badger/home/blob/main/PixelFont.md
- **Matrix class**: https://github.com/badger/home/blob/main/Matrix.md
- **io module**: https://github.com/badger/home/blob/main/badgerware/io.md
## Complete Example App
```python
# __init__.py - Complete counter app with persistence
from badgeware import screen, brushes, shapes, io, PixelFont
import json
# State
counter = 0
high_score = 0
def init():
"""Load saved state"""
global counter, high_score
screen.font = PixelFont.load("nope.ppf")
try:
with open("/storage/counter_state.json", "r") as f:
data = json.load(f)
counter = data.get("counter", 0)
high_score = data.get("high_score", 0)
except:
pass
print(f"Counter initialized: {counter}, High: {high_score}")
def update():
"""Update every frame"""
global counter, high_score
# Clear screen
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw title
screen.brush = brushes.color(255, 255, 255)
screen.text("COUNTER APP", 30, 10)
# Draw counter (large)
screen.text(f"{counter}", 60, 40, scale=3)
# Draw high score
screen.text(f"High: {high_score}", 40, 80)
# Draw instructions
screen.text("A: +1 B: Reset", 20, 105)
# Handle buttons
if io.BUTTON_A in io.pressed:
counter += 1
if counter > high_score:
high_score = counter
if io.BUTTON_B in io.pressed:
counter = 0
def on_exit():
"""Save state before exit"""
try:
with open("/storage/counter_state.json", "w") as f:
json.dump({
"counter": counter,
"high_score": high_score
}, f)
print("State saved!")
except Exception as e:
print(f"Save failed: {e}")
```
## Next Steps
- **See Official Hacks**: https://badger.github.io/hacks/
- **Explore Badge Hardware**: Use `badger-hardware` skill
- **WiFi & Bluetooth**: See MicroPython docs
- **Deploy Your App**: Use `badger-deploy` skill
Happy coding! 🦡

View File

@@ -0,0 +1,809 @@
---
name: badger-deploy
description: Deployment workflows, file management, and project organization for Universe 2025 (Tufty) Badge. Use when deploying apps to MonaOS, managing files on device, syncing projects, organizing code, or setting up deployment pipelines.
---
# Universe 2025 Badge Deployment and File Management
Efficient workflows for deploying applications to **MonaOS**, managing files, and organizing projects on the Universe 2025 (Tufty) Badge.
## Deploying to MonaOS
MonaOS apps are **directories** in `/system/apps/`, not single files. Each app directory must contain:
- `__init__.py` - Entry point with `update()` function
- `icon.png` - 24x24 PNG icon for the launcher
- `assets/` - Optional directory for app resources
### ⚠️ CRITICAL: USB Mass Storage Mode Required
**The `/system/apps/` directory is READ-ONLY via mpremote.** You MUST use USB Mass Storage Mode to install, update, or delete apps.
```bash
# Step 1: Enter USB Mass Storage Mode
# - Connect badge via USB-C
# - Press RESET button TWICE quickly (double-click)
# - Badge appears as "BADGER" drive at /Volumes/BADGER (macOS)
# Step 2: Copy your app to the badge
cp -r my_app /Volumes/BADGER/apps/
# Or manually via Finder:
# - Open BADGER drive
# - Navigate to apps/ folder
# - Drag my_app folder into apps/
# Step 3: Exit Mass Storage Mode
diskutil eject /Volumes/BADGER # Or eject via Finder
# Press RESET button once to reboot into MonaOS
# Your app will now appear in the MonaOS launcher!
```
**File System Mapping**:
- `/Volumes/BADGER/apps/``/system/apps/` on badge (MonaOS apps)
- `/Volumes/BADGER/assets/``/system/assets/` on badge (system resources)
- `/Volumes/BADGER/main.py``/system/main.py` on badge (boot script)
**⚠️ Important**: Install the paginated menu to show unlimited apps (default shows only 6):
- Download: https://raw.githubusercontent.com/badger/home/refs/heads/main/badge/apps/menu/__init__.py
- Replace `/Volumes/BADGER/apps/menu/__init__.py` in Mass Storage Mode
## File Transfer Methods
### Using mpremote for Development (NOT for installing apps)
**⚠️ IMPORTANT**: You CANNOT use `mpremote` to install apps to `/system/apps/` because it's read-only. Use Mass Storage Mode for apps.
`mpremote` is useful for:
- **Testing**: Running code temporarily without saving
- **App Data**: Copying files to `/storage/` (writable partition)
- **Development**: Quick iteration and debugging
```bash
# Run app temporarily (doesn't save to badge)
mpremote run my_app/__init__.py
# Copy app data to writable storage
mpremote cp data.txt :/storage/data.txt
# Create directory in writable storage
mpremote mkdir :/storage/mydata
# Copy from badge to computer
mpremote cp :/storage/data.txt local_backup.txt
# List files on badge
mpremote ls # Root directory
mpremote ls /system/apps # MonaOS apps (read-only)
mpremote ls /storage # Writable storage
# Remove file from storage (NOT /system/)
mpremote rm :/storage/data.txt
# Remove directory from storage (NOT /system/)
mpremote rm -rf :/storage/mydata
```
**What you CANNOT do with mpremote**:
- ❌ Install apps to `/system/apps/` (read-only)
- ❌ Modify `/system/apps/menu/` (read-only)
- ❌ Edit files in `/system/` (read-only)
- ✅ Use USB Mass Storage Mode instead for these operations
### Using ampy
```bash
# Install ampy
pip install adafruit-ampy
# Set port (or use --port flag)
export AMPY_PORT=/dev/tty.usbmodem*
# Upload file
ampy put main.py
ampy put config.py /lib/config.py
# Upload directory
ampy put lib/
# Download file
ampy get main.py
# List files
ampy ls
ampy ls /lib
# Remove file
ampy rm old_file.py
```
### Using rshell
```bash
# Install rshell
pip install rshell
# Connect
rshell --port /dev/tty.usbmodem*
# Commands in rshell
/your/computer> boards # List connected boards
/your/computer> connect serial /dev/tty.usbmodem*
/your/computer> cp main.py /pyboard/
/your/computer> cp -r lib /pyboard/
/your/computer> ls /pyboard
/your/computer> cat /pyboard/main.py
/your/computer> rm /pyboard/old.py
```
### Manual File Sync via REPL
```python
# In REPL - create/edit file directly
f = open('config.py', 'w')
f.write('''
CONFIG = {
'wifi_ssid': 'MyNetwork',
'wifi_password': 'password123',
'version': '1.0.0'
}
''')
f.close()
```
## Project Organization
### MonaOS App Structure
Each MonaOS app is a **directory** with this structure:
```
my_app/ # Your app directory
├── __init__.py # Entry point with update() function (required)
├── icon.png # 24x24 PNG icon for launcher (required)
├── assets/ # Optional: app resources (auto-added to path)
│ ├── sprites.png
│ ├── font.ppf
│ └── config.json
└── README.md # Optional: app documentation
```
### Local Development Structure
Your development directory on your computer:
```
badge-project/
├── my_app/ # MonaOS app directory
│ ├── __init__.py # App entry point
│ ├── icon.png # 24x24 icon
│ └── assets/ # App assets
│ └── sprites.png
├── another_app/ # Another MonaOS app
│ ├── __init__.py
│ └── icon.png
├── requirements.txt # Python dependencies for development
├── venv/ # Virtual environment
└── deploy.sh # Deployment script
```
### Deploy Script for MonaOS Apps
**⚠️ IMPORTANT**: Since `/system/apps/` is read-only via mpremote, this script uses USB Mass Storage Mode.
Create `deploy.sh`:
```bash
#!/bin/bash
# deploy.sh - Deploy MonaOS app to badge via USB Mass Storage Mode
if [ -z "$1" ]; then
echo "Usage: ./deploy.sh <app_name>"
echo "Example: ./deploy.sh my_app"
exit 1
fi
APP_NAME=$1
BADGE_MOUNT="/Volumes/BADGER"
if [ ! -d "$APP_NAME" ]; then
echo "Error: App directory '$APP_NAME' not found"
exit 1
fi
# Check if badge is in Mass Storage Mode
if [ ! -d "$BADGE_MOUNT" ]; then
echo "⚠️ Badge not found in Mass Storage Mode"
echo ""
echo "Please enter Mass Storage Mode:"
echo " 1. Connect badge via USB-C"
echo " 2. Press RESET button TWICE quickly"
echo " 3. Wait for BADGER drive to appear"
echo " 4. Run this script again"
exit 1
fi
echo "Deploying $APP_NAME to MonaOS..."
# Verify required files exist
if [ ! -f "$APP_NAME/__init__.py" ]; then
echo "Error: __init__.py not found in $APP_NAME/"
exit 1
fi
if [ ! -f "$APP_NAME/icon.png" ]; then
echo "Warning: icon.png not found (required for launcher display)"
fi
# Remove old version if it exists
if [ -d "$BADGE_MOUNT/apps/$APP_NAME" ]; then
echo "Removing old version..."
rm -rf "$BADGE_MOUNT/apps/$APP_NAME"
fi
# Copy app to badge
echo "Copying app to badge..."
cp -r "$APP_NAME" "$BADGE_MOUNT/apps/"
echo "✓ Deployment complete!"
echo ""
echo "Next steps:"
echo " 1. Eject BADGER drive: diskutil eject /Volumes/BADGER"
echo " 2. Press RESET once on badge to reboot"
echo " 3. Your app will appear in MonaOS launcher"
echo ""
echo "Note: Install paginated menu for unlimited apps:"
echo "https://raw.githubusercontent.com/badger/home/refs/heads/main/badge/apps/menu/__init__.py"
```
Make executable: `chmod +x deploy.sh`
Usage:
```bash
./deploy.sh my_app
```
## Deployment Workflows
### Development Workflow
Quick iteration during development:
```bash
# 1. Edit code locally
vim my_app/__init__.py
# 2. Test app temporarily (doesn't save to badge)
mpremote run my_app/__init__.py
# 3. Deploy to MonaOS launcher (use Mass Storage Mode)
# - Press RESET twice on badge
# - Copy updated files: cp -r my_app /Volumes/BADGER/apps/
# - Eject and press RESET once
# 4. Verify deployment
mpremote ls /system/apps/my_app
# 5. Launch from badge
# Use physical buttons to navigate MonaOS menu and select your app
```
### Production Deployment
Full deployment with verification:
```bash
#!/bin/bash
# production-deploy.sh
set -e # Exit on error
BADGE_PORT="/dev/tty.usbmodem*"
echo "Starting production deployment..."
# Backup existing files
echo "Creating backup..."
mpremote connect $BADGE_PORT cp :main.py :main.py.backup
mpremote connect $BADGE_PORT cp :config.py :config.py.backup
# Deploy new files
echo "Deploying new version..."
mpremote connect $BADGE_PORT cp main.py :main.py
mpremote connect $BADGE_PORT cp config.py :config.py
mpremote connect $BADGE_PORT cp -r lib/ :/lib/
# Verify deployment
echo "Verifying deployment..."
mpremote connect $BADGE_PORT exec "
import sys
print('MicroPython version:', sys.version)
# Import and verify main module
try:
import main
print('✓ main.py imports successfully')
except Exception as e:
print('✗ Error importing main.py:', e)
sys.exit(1)
"
# Restart with new code
echo "Restarting badge..."
mpremote connect $BADGE_PORT exec "import machine; machine.soft_reset()"
echo "Production deployment complete!"
```
### Staged Deployment
Test on one badge before deploying to multiple:
```bash
#!/bin/bash
# staged-deploy.sh
# Stage 1: Deploy to test badge
echo "Stage 1: Deploying to test badge..."
./deploy.sh --port /dev/tty.usbmodem1
# Stage 2: Run automated tests
echo "Stage 2: Running tests..."
python test_suite.py --badge /dev/tty.usbmodem1
# Stage 3: Deploy to production badges
echo "Stage 3: Deploying to all badges..."
for port in /dev/tty.usbmodem*; do
echo "Deploying to $port..."
./deploy.sh --port $port
done
echo "Staged deployment complete!"
```
## File Management
### Sync Local and Badge Files
```python
# sync.py - Sync files between computer and badge
import os
import subprocess
import hashlib
def get_local_files(path='.'):
"""Get list of local .py files"""
files = []
for root, dirs, filenames in os.walk(path):
# Skip test directories
if 'test' in root:
continue
for filename in filenames:
if filename.endswith('.py'):
filepath = os.path.join(root, filename)
files.append(filepath)
return files
def sync_to_badge(port='/dev/tty.usbmodem*'):
"""Sync all Python files to badge"""
files = get_local_files()
print(f"Syncing {len(files)} files to badge...")
for filepath in files:
# Determine badge path
if filepath.startswith('./lib/'):
badge_path = f":/{filepath[2:]}"
elif filepath.startswith('./'):
badge_path = f":/{filepath[2:]}"
else:
badge_path = f":/{filepath}"
# Upload file
cmd = f"mpremote connect {port} cp {filepath} {badge_path}"
print(f" {filepath} -> {badge_path}")
subprocess.run(cmd, shell=True, check=True)
print("Sync complete!")
if __name__ == '__main__':
sync_to_badge()
```
### Clean Up Badge Files
```python
# cleanup.py - Remove unused files from badge
def cleanup_badge():
"""Remove temporary and cache files from badge"""
import os
# Files to remove
patterns = [
'.pyc', # Compiled Python
'.backup', # Backup files
'.tmp', # Temporary files
'debug_', # Debug files
]
removed = []
for path in ['/', '/lib']:
try:
files = os.listdir(path)
for file in files:
# Check if file matches cleanup pattern
if any(file.endswith(p) or file.startswith(p) for p in patterns):
filepath = f"{path}/{file}".replace('//', '/')
os.remove(filepath)
removed.append(filepath)
except:
pass
print(f"Cleanup complete! Removed {len(removed)} files:")
for f in removed:
print(f" {f}")
# Run on badge via mpremote:
# mpremote exec "$(cat cleanup.py)"
```
### List Badge Files
```bash
# list_files.sh - Comprehensive file listing
echo "Files on badge:"
echo "==============="
echo -e "\nRoot directory:"
mpremote ls
echo -e "\n/lib directory:"
mpremote ls /lib 2>/dev/null || echo " (not found)"
echo -e "\n/assets directory:"
mpremote ls /assets 2>/dev/null || echo " (not found)"
echo -e "\n/data directory:"
mpremote ls /data 2>/dev/null || echo " (not found)"
```
## Backup and Restore
### Backup Badge to Computer
```bash
#!/bin/bash
# backup.sh - Backup all files from badge
BACKUP_DIR="backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$BACKUP_DIR"
echo "Backing up badge to $BACKUP_DIR..."
# Backup root Python files
for file in $(mpremote ls | grep '.py'); do
echo " Backing up $file..."
mpremote cp ":$file" "$BACKUP_DIR/$file"
done
# Backup lib directory
mkdir -p "$BACKUP_DIR/lib"
for file in $(mpremote ls /lib 2>/dev/null | grep '.py'); do
echo " Backing up /lib/$file..."
mpremote cp ":/lib/$file" "$BACKUP_DIR/lib/$file"
done
echo "Backup complete: $BACKUP_DIR"
```
### Restore Badge from Backup
```bash
#!/bin/bash
# restore.sh - Restore files to badge from backup
if [ -z "$1" ]; then
echo "Usage: ./restore.sh <backup_directory>"
exit 1
fi
BACKUP_DIR=$1
echo "Restoring from $BACKUP_DIR..."
# Restore root files
for file in "$BACKUP_DIR"/*.py; do
if [ -f "$file" ]; then
filename=$(basename "$file")
echo " Restoring $filename..."
mpremote cp "$file" ":$filename"
fi
done
# Restore lib directory
if [ -d "$BACKUP_DIR/lib" ]; then
mpremote mkdir /lib 2>/dev/null || true
for file in "$BACKUP_DIR/lib"/*.py; do
if [ -f "$file" ]; then
filename=$(basename "$file")
echo " Restoring /lib/$filename..."
mpremote cp "$file" ":/lib/$filename"
fi
done
fi
echo "Restore complete!"
```
## Version Management
### Version File
Create `version.py` on badge:
```python
# version.py
VERSION = '1.2.3'
BUILD_DATE = '2024-01-15'
COMMIT = 'abc123f'
def show():
print(f"Version: {VERSION}")
print(f"Build: {BUILD_DATE}")
print(f"Commit: {COMMIT}")
```
### Auto-generate Version
```bash
#!/bin/bash
# generate_version.sh - Auto-generate version file
VERSION=$(git describe --tags --always)
BUILD_DATE=$(date +%Y-%m-%d)
COMMIT=$(git rev-parse --short HEAD)
cat > version.py <<EOF
# Auto-generated version file
VERSION = '$VERSION'
BUILD_DATE = '$BUILD_DATE'
COMMIT = '$COMMIT'
EOF
echo "Generated version.py: $VERSION ($BUILD_DATE)"
```
Add to deploy script:
```bash
# Generate version before deploy
./generate_version.sh
mpremote cp version.py :version.py
```
## Multi-Badge Deployment
### Deploy to Multiple Badges
```bash
#!/bin/bash
# multi-deploy.sh - Deploy to multiple connected badges
echo "Scanning for connected badges..."
BADGES=$(ls /dev/tty.usbmodem* 2>/dev/null)
if [ -z "$BADGES" ]; then
echo "No badges found!"
exit 1
fi
echo "Found badges:"
echo "$BADGES"
echo
read -p "Deploy to all? (y/n) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 0
fi
for PORT in $BADGES; do
echo "================================"
echo "Deploying to $PORT..."
echo "================================"
mpremote connect $PORT cp main.py :main.py
mpremote connect $PORT cp config.py :config.py
mpremote connect $PORT cp -r lib/ :/lib/
echo "✓ Deployed to $PORT"
echo
done
echo "All badges updated!"
```
## CI/CD Integration
### GitHub Actions Example
```yaml
# .github/workflows/deploy.yml
name: Deploy to Badge
on:
push:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.11'
- name: Install dependencies
run: |
pip install mpremote pytest
- name: Run tests
run: |
pytest tests/
deploy:
needs: test
runs-on: self-hosted # Runner with badge connected
steps:
- uses: actions/checkout@v3
- name: Deploy to badge
run: |
./generate_version.sh
./deploy.sh
```
### Pre-commit Hook
```bash
# .git/hooks/pre-commit
#!/bin/bash
# Run tests before allowing commit
echo "Running tests..."
python -m pytest tests/
if [ $? -ne 0 ]; then
echo "Tests failed! Commit aborted."
exit 1
fi
echo "Tests passed!"
exit 0
```
Make executable: `chmod +x .git/hooks/pre-commit`
## Hot Reload Development
### Watch and Auto-deploy
```python
# watch.py - Watch for file changes and auto-deploy
import time
import os
import subprocess
from pathlib import Path
def get_file_hash(filepath):
"""Get hash of file contents"""
import hashlib
with open(filepath, 'rb') as f:
return hashlib.md5(f.read()).hexdigest()
def watch_and_deploy(watch_files, port='/dev/tty.usbmodem*'):
"""Watch files and deploy on change"""
print(f"Watching {len(watch_files)} files for changes...")
file_hashes = {f: get_file_hash(f) for f in watch_files}
try:
while True:
time.sleep(1) # Check every second
for filepath in watch_files:
current_hash = get_file_hash(filepath)
if current_hash != file_hashes[filepath]:
print(f"\n{filepath} changed! Deploying...")
# Deploy changed file
badge_path = f":/{filepath}"
cmd = f"mpremote connect {port} cp {filepath} {badge_path}"
subprocess.run(cmd, shell=True)
# Soft reset badge
cmd = f"mpremote connect {port} exec 'import machine; machine.soft_reset()'"
subprocess.run(cmd, shell=True)
# Update hash
file_hashes[filepath] = current_hash
print("Deploy complete!")
except KeyboardInterrupt:
print("\nStopped watching.")
if __name__ == '__main__':
watch_files = ['main.py', 'config.py', 'lib/display.py']
watch_and_deploy(watch_files)
```
## Best Practices
### Pre-deployment Checklist
- [ ] Test code locally (if possible)
- [ ] Update version number
- [ ] Run linter/formatter
- [ ] Commit changes to git
- [ ] Backup current badge code
- [ ] Deploy to test badge first
- [ ] Verify deployment success
- [ ] Test on badge
- [ ] Deploy to production badges
### File Organization Tips
1. **Keep main.py simple** - Import from modules
2. **Use /lib for reusable code** - Separate concerns
3. **Store data in /data** - Keep code and data separate
4. **Version control everything** - Except secrets
5. **Use .gitignore** - Don't commit badge-specific configs
### Deployment Optimization
```bash
# Minimize deployment time
# Deploy only changed files
git diff --name-only HEAD~1 | grep '.py$' | while read file; do
mpremote cp "$file" ":/$file"
done
# Compress files before transfer (if network-based)
# gzip files, transfer, decompress on badge
# Parallel deployment to multiple badges
parallel -j 4 ./deploy.sh --port ::: /dev/tty.usbmodem*
```
## Troubleshooting Deployment
**Files not updating**: Verify file was actually copied, check paths with `mpremote ls /system/apps/my_app`
**Permission denied**: Badge may be in use by another process, close other connections
**Deployment fails silently**: Check disk space on badge, verify file syntax, ensure `__init__.py` and `icon.png` exist
**App doesn't appear in MonaOS menu**:
- Verify files uploaded to `/system/apps/my_app/`
- Check icon is exactly 24x24 PNG
- Default menu shows only 6 apps - enable pagination: https://badger.github.io/hack/menu-pagination/
**Changes not reflected**: Restart badge to reload app code
**Slow transfers**: USB cable quality matters, try different cable/port
## Official Examples
For inspiration on what to deploy to your badge:
- **Hacks**: https://badger.github.io/hacks/ - Step-by-step customization tutorials
- **Apps**: https://badger.github.io/apps/ - Loadable MonaOS apps (Commits, Snake)
- **Source Code**: https://github.com/badger/home/tree/main/badgerware - Browse official MonaOS app code and API docs
With these deployment workflows, you can efficiently manage and deploy applications to your Universe 2025 Badge's MonaOS launcher!

View File

@@ -0,0 +1,766 @@
---
name: badger-diagnostics
description: System diagnostics, verification, and troubleshooting for Badger 2350. Use when checking firmware version, verifying installations, diagnosing hardware issues, troubleshooting errors, or performing system health checks on Badger 2350.
---
# Badger 2350 Diagnostics and Troubleshooting
Comprehensive diagnostics and troubleshooting tools for verifying your Badger 2350 setup, checking installations, and resolving common issues.
## ⚠️ When to Use This Skill
**Use this skill FIRST** in these situations:
1. **Starting a new session** - Verify everything works before coding
2. **After setup** - Confirm installation completed correctly
3. **Before debugging** - Rule out environment issues
4. **When errors occur** - Diagnose the root cause
5. **After firmware updates** - Verify everything still works
**Best Practice**: Run diagnostics at the start of EVERY development session. It takes 30 seconds and prevents hours of debugging.
## Quick Verification Command
**Run this FIRST every session** (doesn't require any files to exist):
### Level 1: Basic Connection Test
```bash
# Simplest test - just verify badge responds
mpremote exec "print('Badge connected!')"
# Should print: Badge connected!
# If this fails, badge isn't connected or mpremote not installed
```
### Level 2: Full Verification
```bash
# Complete verification (auto-detects port on macOS/Linux)
mpremote exec "import sys, gc; from badgeware import screen, brushes, shapes, io; print('=== VERIFICATION ==='); print('✓ MicroPython:', sys.version[:30]); print('✓ Memory:', gc.mem_free(), 'bytes'); print('✓ badgeware: loaded'); print('✓ Display: 160x120'); print('=== ALL OK ===')"
```
**Expected output**: All checks with ✓ marks and no errors.
**With explicit port** (if auto-detect fails):
```bash
mpremote connect /dev/cu.usbmodem1101 exec "from badgeware import screen; print('✓ Badge OK')"
# Replace /dev/cu.usbmodem1101 with your port
```
**If this fails**: Continue with detailed diagnostics below.
## Quick System Check
Run this complete system diagnostic in REPL:
```python
# diagnostic.py - Complete system check
import sys
import gc
import os
from machine import freq, unique_id
import ubinascii
def system_info():
"""Display complete system information"""
print("=" * 50)
print("BADGER 2350 SYSTEM DIAGNOSTICS")
print("=" * 50)
# MicroPython version
print(f"\n[MicroPython]")
print(f" Version: {sys.version}")
print(f" Implementation: {sys.implementation}")
print(f" Platform: {sys.platform}")
# Hardware info
print(f"\n[Hardware]")
print(f" CPU Frequency: {freq():,} Hz ({freq() / 1_000_000:.0f} MHz)")
uid = ubinascii.hexlify(unique_id()).decode()
print(f" Unique ID: {uid}")
# Memory
gc.collect()
print(f"\n[Memory]")
print(f" Free: {gc.mem_free():,} bytes ({gc.mem_free() / 1024:.1f} KB)")
print(f" Allocated: {gc.mem_alloc():,} bytes ({gc.mem_alloc() / 1024:.1f} KB)")
total = gc.mem_free() + gc.mem_alloc()
print(f" Total: {total:,} bytes ({total / 1024:.1f} KB)")
# File system
print(f"\n[File System]")
try:
stat = os.statvfs('/')
block_size = stat[0]
total_blocks = stat[2]
free_blocks = stat[3]
total_bytes = block_size * total_blocks
free_bytes = block_size * free_blocks
used_bytes = total_bytes - free_bytes
print(f" Total: {total_bytes:,} bytes ({total_bytes / 1024 / 1024:.2f} MB)")
print(f" Used: {used_bytes:,} bytes ({used_bytes / 1024 / 1024:.2f} MB)")
print(f" Free: {free_bytes:,} bytes ({free_bytes / 1024 / 1024:.2f} MB)")
except:
print(" Unable to check filesystem")
# Module path
print(f"\n[Module Search Paths]")
for path in sys.path:
print(f" {path}")
print("\n" + "=" * 50)
# Run diagnostic
system_info()
```
## Firmware Version Check
### Check MicroPython Firmware
```python
import sys
# Full version info
print(sys.version)
# Example: 3.4.0; MicroPython v1.20.0 on 2023-04-26
# Implementation details
print(sys.implementation)
# (name='micropython', version=(1, 20, 0), _machine='Raspberry Pi Pico W with RP2040', _mpy=6182)
# Extract version number
version = sys.implementation.version
print(f"MicroPython {version[0]}.{version[1]}.{version[2]}")
```
### Check Badger Library Version
```python
import badger2040
# Check if version attribute exists
if hasattr(badger2040, '__version__'):
print(f"Badger library version: {badger2040.__version__}")
else:
print("Badger library version not available")
# Check file location
print(f"Badger library: {badger2040.__file__}")
```
### Recommended Firmware Versions
Verify you have compatible firmware:
```python
def check_firmware_compatibility():
"""Check if firmware is compatible with Badger 2350"""
version = sys.implementation.version
if version[0] >= 1 and version[1] >= 20:
print("✓ MicroPython version is compatible")
return True
else:
print("✗ MicroPython version may be outdated")
print(" Recommended: MicroPython 1.20+")
print(f" Current: {version[0]}.{version[1]}.{version[2]}")
return False
check_firmware_compatibility()
```
## Module Verification
### Check Core Modules
```python
def verify_core_modules():
"""Verify essential modules are available"""
required_modules = {
'badger2040': 'Badger display library',
'machine': 'Hardware interface',
'time': 'Time functions',
'gc': 'Garbage collection',
'sys': 'System functions',
'os': 'Operating system interface'
}
optional_modules = {
'network': 'WiFi support',
'urequests': 'HTTP client',
'ujson': 'JSON parsing',
'ubinascii': 'Binary/ASCII conversion'
}
print("Checking required modules...")
all_ok = True
for module, description in required_modules.items():
try:
__import__(module)
print(f"{module:15s} - {description}")
except ImportError:
print(f"{module:15s} - MISSING - {description}")
all_ok = False
print("\nChecking optional modules...")
for module, description in optional_modules.items():
try:
__import__(module)
print(f"{module:15s} - {description}")
except ImportError:
print(f"{module:15s} - Not installed - {description}")
return all_ok
verify_core_modules()
```
### List All Installed Packages
```python
import os
def list_installed_packages():
"""List all installed packages in /lib"""
print("Installed packages:")
# Check /lib directory
try:
lib_contents = os.listdir('/lib')
if lib_contents:
for item in sorted(lib_contents):
# Try to get more info
path = f'/lib/{item}'
try:
stat = os.stat(path)
size = stat[6] # File size
print(f" {item:30s} {size:8,} bytes")
except:
print(f" {item}")
else:
print(" (no packages in /lib)")
except OSError:
print(" /lib directory not found")
# Check root directory for .py files
print("\nRoot directory modules:")
root_contents = os.listdir('/')
py_files = [f for f in root_contents if f.endswith('.py')]
for f in sorted(py_files):
stat = os.stat(f)
size = stat[6]
print(f" {f:30s} {size:8,} bytes")
list_installed_packages()
```
## Hardware Diagnostics
### Display Test
```python
import badger2040
def test_display():
"""Test display functionality"""
print("Testing display...")
badge = badger2040.Badger2040()
# Test 1: Clear screen
badge.set_pen(15)
badge.clear()
badge.update()
print(" ✓ Clear screen")
# Test 2: Draw text
badge.set_pen(0)
badge.text("Display Test", 10, 10, scale=2)
badge.update()
print(" ✓ Draw text")
# Test 3: Draw shapes
badge.line(10, 40, 100, 40)
badge.rectangle(10, 50, 50, 30)
badge.update()
print(" ✓ Draw shapes")
print("Display test complete!")
test_display()
```
### Button Test
```python
import badger2040
import time
def test_buttons():
"""Test all buttons"""
print("Button test - Press each button:")
print(" A, B, C, UP, DOWN")
print("Press Ctrl+C to exit")
badge = badger2040.Badger2040()
tested = set()
while len(tested) < 5:
if badge.pressed(badger2040.BUTTON_A) and 'A' not in tested:
print(" ✓ Button A works")
tested.add('A')
elif badge.pressed(badger2040.BUTTON_B) and 'B' not in tested:
print(" ✓ Button B works")
tested.add('B')
elif badge.pressed(badger2040.BUTTON_C) and 'C' not in tested:
print(" ✓ Button C works")
tested.add('C')
elif badge.pressed(badger2040.BUTTON_UP) and 'UP' not in tested:
print(" ✓ Button UP works")
tested.add('UP')
elif badge.pressed(badger2040.BUTTON_DOWN) and 'DOWN' not in tested:
print(" ✓ Button DOWN works")
tested.add('DOWN')
time.sleep(0.1)
print("All buttons tested successfully!")
test_buttons()
```
### GPIO Test
```python
from machine import Pin
def test_gpio():
"""Test GPIO pins"""
print("Testing GPIO pins...")
# Test output
test_pin = Pin(25, Pin.OUT)
test_pin.value(1)
print(f" ✓ Pin 25 set to HIGH: {test_pin.value()}")
test_pin.value(0)
print(f" ✓ Pin 25 set to LOW: {test_pin.value()}")
# Test input with pull-up
input_pin = Pin(15, Pin.IN, Pin.PULL_UP)
print(f" ✓ Pin 15 input (pull-up): {input_pin.value()}")
print("GPIO test complete!")
test_gpio()
```
### I2C Bus Scan
```python
from machine import I2C, Pin
def scan_i2c():
"""Scan for I2C devices"""
print("Scanning I2C bus...")
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
devices = i2c.scan()
if devices:
print(f" Found {len(devices)} device(s):")
for device in devices:
print(f" 0x{device:02X} ({device})")
else:
print(" No I2C devices found")
return devices
scan_i2c()
```
## Network Diagnostics
### WiFi Connection Test
```python
import network
import time
def test_wifi(ssid, password, timeout=10):
"""Test WiFi connection"""
print(f"Testing WiFi connection to '{ssid}'...")
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
# Check if already connected
if wlan.isconnected():
print(" ✓ Already connected")
print(f" IP: {wlan.ifconfig()[0]}")
return True
# Attempt connection
print(" Connecting...")
wlan.connect(ssid, password)
# Wait for connection
start = time.time()
while not wlan.isconnected() and (time.time() - start) < timeout:
time.sleep(0.5)
print(".", end="")
print() # New line
if wlan.isconnected():
config = wlan.ifconfig()
print(" ✓ Connected successfully")
print(f" IP Address: {config[0]}")
print(f" Subnet Mask: {config[1]}")
print(f" Gateway: {config[2]}")
print(f" DNS: {config[3]}")
print(f" Signal Strength: {wlan.status('rssi')} dBm")
return True
else:
print(" ✗ Connection failed")
status = wlan.status()
print(f" Status code: {status}")
return False
# Usage
# test_wifi('YourSSID', 'YourPassword')
```
### Network Speed Test
```python
import urequests
import time
def test_network_speed():
"""Test network download speed"""
print("Testing network speed...")
url = "http://httpbin.org/bytes/10000" # 10KB test file
try:
start = time.ticks_ms()
response = urequests.get(url)
end = time.ticks_ms()
size = len(response.content)
duration = time.ticks_diff(end, start) / 1000 # Convert to seconds
speed = (size / duration) / 1024 # KB/s
print(f" Downloaded: {size} bytes")
print(f" Time: {duration:.2f}s")
print(f" Speed: {speed:.2f} KB/s")
response.close()
return True
except Exception as e:
print(f" ✗ Network test failed: {e}")
return False
# test_network_speed()
```
## Memory Diagnostics
### Memory Usage Analysis
```python
import gc
def analyze_memory():
"""Analyze memory usage"""
print("Memory Analysis:")
# Before collection
free_before = gc.mem_free()
alloc_before = gc.mem_alloc()
# Collect garbage
gc.collect()
# After collection
free_after = gc.mem_free()
alloc_after = gc.mem_alloc()
print(f"\nBefore garbage collection:")
print(f" Free: {free_before:,} bytes ({free_before / 1024:.1f} KB)")
print(f" Allocated: {alloc_before:,} bytes ({alloc_before / 1024:.1f} KB)")
print(f"\nAfter garbage collection:")
print(f" Free: {free_after:,} bytes ({free_after / 1024:.1f} KB)")
print(f" Allocated: {alloc_after:,} bytes ({alloc_after / 1024:.1f} KB)")
freed = free_after - free_before
print(f"\nReclaimed: {freed:,} bytes ({freed / 1024:.1f} KB)")
# Total memory
total = free_after + alloc_after
usage_percent = (alloc_after / total) * 100
print(f"\nTotal memory: {total:,} bytes ({total / 1024:.1f} KB)")
print(f"Usage: {usage_percent:.1f}%")
# Warning if low
if free_after < 10000:
print("\n⚠ WARNING: Low memory!")
elif free_after < 50000:
print("\n⚠ CAUTION: Memory running low")
else:
print("\n✓ Memory usage looks good")
analyze_memory()
```
### Find Memory Leaks
```python
import gc
def find_memory_leaks(function, iterations=10):
"""Test function for memory leaks"""
print(f"Testing for memory leaks ({iterations} iterations)...")
gc.collect()
initial_mem = gc.mem_free()
for i in range(iterations):
function()
gc.collect()
current_mem = gc.mem_free()
leaked = initial_mem - current_mem
if leaked > 0:
print(f" Iteration {i+1}: Leaked {leaked} bytes")
gc.collect()
final_mem = gc.mem_free()
total_leaked = initial_mem - final_mem
if total_leaked > 100: # Allow small variance
print(f"⚠ Possible memory leak: {total_leaked} bytes leaked")
else:
print(f"✓ No significant memory leak detected")
# Usage
# def test_func():
# data = [i for i in range(100)]
# find_memory_leaks(test_func)
```
## Error Diagnosis
### Common Error Patterns
```python
def diagnose_error(error):
"""Provide diagnosis for common errors"""
error_str = str(error)
diagnostics = {
'ImportError': """
Module not found. Check:
- Module is installed (use mip.install())
- Module is in /lib or root directory
- Module name is spelled correctly
- File has .py extension
""",
'MemoryError': """
Out of memory. Try:
- Run gc.collect() before allocation
- Reduce variable scope
- Use generators instead of lists
- Break large operations into smaller chunks
- Delete unused objects with 'del'
""",
'OSError': """
File/Hardware operation failed. Check:
- File path is correct
- File exists (for reading)
- Filesystem not full (for writing)
- Hardware is connected properly
- Pins are not already in use
""",
'AttributeError': """
Attribute not found. Check:
- Object has the attribute/method
- Spelling is correct
- Module is imported correctly
- Object is initialized
""",
'ValueError': """
Invalid value. Check:
- Parameter values are in valid range
- Data types match expected types
- String formats are correct
"""
}
# Find matching error type
for error_type, advice in diagnostics.items():
if error_type in error_str:
print(f"Diagnosis for {error_type}:")
print(advice)
return
print("Error type not recognized. Common debugging steps:")
print("- Check error message carefully")
print("- Print variable values before error")
print("- Simplify code to isolate problem")
print("- Check MicroPython documentation")
# Usage
# try:
# import nonexistent_module
# except Exception as e:
# diagnose_error(e)
```
### System Health Check
```python
def health_check():
"""Comprehensive system health check"""
print("=" * 50)
print("SYSTEM HEALTH CHECK")
print("=" * 50)
issues = []
# Memory check
gc.collect()
free_mem = gc.mem_free()
if free_mem < 10000:
issues.append("CRITICAL: Very low memory")
elif free_mem < 50000:
issues.append("WARNING: Low memory")
else:
print("✓ Memory: OK")
# Filesystem check
try:
stat = os.statvfs('/')
free_blocks = stat[3]
block_size = stat[0]
free_bytes = free_blocks * block_size
if free_bytes < 100000:
issues.append("WARNING: Low disk space")
else:
print("✓ Filesystem: OK")
except:
issues.append("ERROR: Cannot check filesystem")
# Core modules check
required = ['badger2040', 'machine', 'time', 'gc', 'sys', 'os']
for module in required:
try:
__import__(module)
except:
issues.append(f"ERROR: Missing module '{module}'")
if not issues:
print("✓ Core modules: OK")
# Display results
if issues:
print("\n" + "!" * 50)
print("ISSUES FOUND:")
for issue in issues:
print(f" {issue}")
print("!" * 50)
else:
print("\n" + "=" * 50)
print("✓ ALL SYSTEMS HEALTHY")
print("=" * 50)
health_check()
```
## Recovery Procedures
### Safe Mode Boot
If badge won't boot normally:
1. **Hold BOOTSEL button** while connecting USB
2. Badge appears as USB drive
3. Delete `main.py` if it's causing crashes
4. Copy new firmware `.uf2` file to drive
5. Badge will reboot automatically
### Factory Reset
```python
import os
def factory_reset():
"""Remove all user files (DANGEROUS!)"""
print("WARNING: This will delete all files!")
print("Type 'CONFIRM' to proceed:")
# In interactive mode, get user confirmation
# confirm = input()
# if confirm != 'CONFIRM':
# print("Reset cancelled")
# return
print("Removing files...")
for f in os.listdir('/'):
if f not in ['boot.py']: # Keep boot.py
try:
os.remove(f)
print(f" Removed {f}")
except:
pass
print("Factory reset complete. Reboot badge.")
# Uncomment to use:
# factory_reset()
```
### Firmware Reflash
```bash
# From your computer (badge in BOOTSEL mode)
# Download latest MicroPython firmware
# Visit: https://micropython.org/download/rp2-pico-w/
# Flash firmware
# Drag .uf2 file to RPI-RP2 drive
# Or use picotool:
picotool load firmware.uf2
# Verify
picotool info
```
## Troubleshooting Checklist
When encountering issues, work through this checklist:
- [ ] Check MicroPython version (`sys.version`)
- [ ] Verify core modules load (`import badger2040`)
- [ ] Run memory diagnostic (`gc.mem_free()`)
- [ ] Check filesystem space (`os.statvfs('/')`)
- [ ] Test display (`badge.update()`)
- [ ] Test buttons (button test function)
- [ ] Scan I2C bus (if using sensors)
- [ ] Test WiFi connection (if using network)
- [ ] Review error messages carefully
- [ ] Check documentation for API changes
- [ ] Try soft reset (Ctrl+D in REPL)
- [ ] Try hard reset (power cycle)
This comprehensive diagnostic approach will help you quickly identify and resolve issues with your Badger 2350!

View File

@@ -0,0 +1,527 @@
---
name: badger-hardware
description: Hardware integration for Badger 2350 including GPIO, I2C sensors, SPI devices, and electronic components. Use when connecting external hardware, working with sensors, controlling LEDs, reading buttons, or interfacing with I2C/SPI devices on Badger 2350.
---
# Badger 2350 Hardware Integration
Interface with GPIO pins, sensors, and external hardware on the Badger 2350 badge using I2C, SPI, and digital I/O.
## GPIO Basics
### Pin Configuration
```python
from machine import Pin
# Configure pin as output (LED, relay, etc.)
led = Pin(25, Pin.OUT)
led.value(1) # Turn on (HIGH)
led.value(0) # Turn off (LOW)
led.toggle() # Toggle state
# Configure pin as input (button, switch, etc.)
button = Pin(15, Pin.IN, Pin.PULL_UP)
if button.value() == 0: # Button pressed (pulled to ground)
print("Button pressed!")
# Configure pin as input with pull-down
sensor = Pin(16, Pin.IN, Pin.PULL_DOWN)
```
### PWM (Pulse Width Modulation)
```python
from machine import Pin, PWM
# Control LED brightness or servo motor
pwm = PWM(Pin(25))
pwm.freq(1000) # Set frequency to 1kHz
# Set duty cycle (0-65535, where 65535 is 100%)
pwm.duty_u16(32768) # 50% brightness
pwm.duty_u16(16384) # 25% brightness
pwm.duty_u16(65535) # 100% brightness
# Cleanup
pwm.deinit()
```
### Interrupts
```python
from machine import Pin
button = Pin(15, Pin.IN, Pin.PULL_UP)
def button_callback(pin):
print(f"Button pressed! Pin: {pin}")
# Trigger on falling edge (button press)
button.irq(trigger=Pin.IRQ_FALLING, handler=button_callback)
# Trigger on rising edge (button release)
button.irq(trigger=Pin.IRQ_RISING, handler=button_callback)
# Trigger on both edges
button.irq(trigger=Pin.IRQ_RISING | Pin.IRQ_FALLING, handler=button_callback)
```
## I2C Communication
### I2C Setup
```python
from machine import I2C, Pin
# Initialize I2C (QWIIC connector uses specific pins)
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
# Scan for connected devices
devices = i2c.scan()
print(f"Found {len(devices)} I2C devices:")
for device in devices:
print(f" Address: 0x{device:02x}")
```
### Reading from I2C Device
```python
# Read data from I2C device
address = 0x48 # Example: Temperature sensor
data = i2c.readfrom(address, 2) # Read 2 bytes
print(f"Raw data: {data}")
# Read from specific register
register = 0x00
i2c.writeto(address, bytes([register])) # Select register
data = i2c.readfrom(address, 2) # Read data
```
### Writing to I2C Device
```python
# Write single byte
address = 0x48
data = bytes([0x01, 0xA0])
i2c.writeto(address, data)
# Write to specific register
register = 0x01
value = 0xFF
i2c.writeto(address, bytes([register, value]))
```
## Common I2C Sensors
### BME280 (Temperature, Humidity, Pressure)
```python
from machine import I2C, Pin
import time
class BME280:
def __init__(self, i2c, address=0x76):
self.i2c = i2c
self.address = address
def read_temp(self):
# Read temperature register
data = self.i2c.readfrom_mem(self.address, 0xFA, 3)
temp_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
# Apply calibration (simplified)
temp_c = temp_raw / 100.0
return temp_c
def read_humidity(self):
# Read humidity register
data = self.i2c.readfrom_mem(self.address, 0xFD, 2)
hum_raw = (data[0] << 8) | data[1]
humidity = hum_raw / 1024.0
return humidity
# Usage
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
sensor = BME280(i2c)
temp = sensor.read_temp()
humidity = sensor.read_humidity()
print(f"Temp: {temp:.1f}°C, Humidity: {humidity:.1f}%")
```
### APDS9960 (Gesture, Proximity, Color Sensor)
```python
class APDS9960:
def __init__(self, i2c, address=0x39):
self.i2c = i2c
self.address = address
self._init_sensor()
def _init_sensor(self):
# Enable device
self.i2c.writeto_mem(self.address, 0x80, bytes([0x01]))
# Enable gesture mode
self.i2c.writeto_mem(self.address, 0x93, bytes([0x01]))
def read_gesture(self):
# Read gesture FIFO
fifo_level = self.i2c.readfrom_mem(self.address, 0xAE, 1)[0]
if fifo_level > 0:
data = self.i2c.readfrom_mem(self.address, 0xFC, 4)
# Process gesture data
return self._detect_gesture(data)
return None
def _detect_gesture(self, data):
# Simplified gesture detection
if data[0] > data[2]:
return "UP"
elif data[0] < data[2]:
return "DOWN"
elif data[1] > data[3]:
return "LEFT"
else:
return "RIGHT"
# Usage
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
gesture_sensor = APDS9960(i2c)
gesture = gesture_sensor.read_gesture()
if gesture:
print(f"Gesture detected: {gesture}")
```
### VL53L0X (Time-of-Flight Distance Sensor)
```python
class VL53L0X:
def __init__(self, i2c, address=0x29):
self.i2c = i2c
self.address = address
def read_distance(self):
# Start measurement
self.i2c.writeto_mem(self.address, 0x00, bytes([0x01]))
# Wait for measurement
time.sleep(0.05)
# Read distance (mm)
data = self.i2c.readfrom_mem(self.address, 0x14, 2)
distance = (data[0] << 8) | data[1]
return distance
# Usage
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
tof = VL53L0X(i2c)
distance = tof.read_distance()
print(f"Distance: {distance}mm")
```
## SPI Communication
### SPI Setup
```python
from machine import SPI, Pin
# Initialize SPI
spi = SPI(0, baudrate=1000000, polarity=0, phase=0,
sck=Pin(2), mosi=Pin(3), miso=Pin(4))
# Chip select pin
cs = Pin(5, Pin.OUT)
cs.value(1) # Deselect initially
# Read/write data
cs.value(0) # Select device
spi.write(bytes([0x01, 0x02, 0x03])) # Write data
data = spi.read(3) # Read 3 bytes
cs.value(1) # Deselect device
print(f"Received: {data}")
```
### SD Card Reader (SPI)
```python
from machine import SPI, Pin
import sdcard
import os
# Initialize SPI and SD card
spi = SPI(0, baudrate=1000000, sck=Pin(2), mosi=Pin(3), miso=Pin(4))
cs = Pin(5, Pin.OUT)
sd = sdcard.SDCard(spi, cs)
# Mount SD card
os.mount(sd, '/sd')
# Write file
with open('/sd/data.txt', 'w') as f:
f.write('Hello from Badger!')
# Read file
with open('/sd/data.txt', 'r') as f:
print(f.read())
# Unmount
os.umount('/sd')
```
## Analog Input (ADC)
```python
from machine import ADC, Pin
# Initialize ADC on GPIO pin
adc = ADC(Pin(26))
# Read raw value (0-65535 for 16-bit ADC)
raw_value = adc.read_u16()
print(f"Raw ADC: {raw_value}")
# Convert to voltage (assuming 3.3V reference)
voltage = (raw_value / 65535) * 3.3
print(f"Voltage: {voltage:.2f}V")
# Example: Read potentiometer
while True:
value = adc.read_u16()
percentage = (value / 65535) * 100
print(f"Pot: {percentage:.1f}%")
time.sleep(0.1)
```
## NeoPixel (WS2812B) LEDs
```python
from machine import Pin
import neopixel
import time
# Initialize NeoPixel strip (8 LEDs on pin 25)
num_leds = 8
np = neopixel.NeoPixel(Pin(25), num_leds)
# Set individual LED color (R, G, B)
np[0] = (255, 0, 0) # Red
np[1] = (0, 255, 0) # Green
np[2] = (0, 0, 255) # Blue
np[3] = (255, 255, 0) # Yellow
np.write() # Update LEDs
# Rainbow effect
def rainbow_cycle(wait):
for j in range(255):
for i in range(num_leds):
pixel_index = (i * 256 // num_leds) + j
np[i] = wheel(pixel_index & 255)
np.write()
time.sleep(wait)
def wheel(pos):
"""Generate rainbow colors across 0-255 positions"""
if pos < 85:
return (pos * 3, 255 - pos * 3, 0)
elif pos < 170:
pos -= 85
return (255 - pos * 3, 0, pos * 3)
else:
pos -= 170
return (0, pos * 3, 255 - pos * 3)
rainbow_cycle(0.001)
```
## Servo Motor Control
```python
from machine import Pin, PWM
import time
class Servo:
def __init__(self, pin):
self.pwm = PWM(Pin(pin))
self.pwm.freq(50) # 50Hz for servo
def angle(self, degrees):
"""Set servo angle (0-180 degrees)"""
# Convert angle to duty cycle
# 0° = 1ms (3.2% duty)
# 90° = 1.5ms (7.5% duty)
# 180° = 2ms (10% duty)
min_duty = 1638 # 2.5% of 65535
max_duty = 8192 # 12.5% of 65535
duty = int(min_duty + (degrees / 180) * (max_duty - min_duty))
self.pwm.duty_u16(duty)
def deinit(self):
self.pwm.deinit()
# Usage
servo = Servo(25)
# Sweep servo
for angle in range(0, 181, 10):
servo.angle(angle)
time.sleep(0.1)
servo.deinit()
```
## Relay Control
```python
from machine import Pin
import time
class Relay:
def __init__(self, pin):
self.pin = Pin(pin, Pin.OUT)
self.off()
def on(self):
self.pin.value(1)
def off(self):
self.pin.value(0)
def toggle(self):
self.pin.toggle()
# Usage
relay = Relay(25)
relay.on()
time.sleep(2)
relay.off()
# Pulse relay
for i in range(5):
relay.toggle()
time.sleep(0.5)
```
## Integration with Badge Display
### Display Sensor Data
```python
import badger2040
from machine import I2C, Pin
import time
badge = badger2040.Badger2040()
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
def display_sensor_data():
# Read sensor (example)
temp = read_temperature() # Your sensor function
humidity = read_humidity()
badge.set_pen(15)
badge.clear()
badge.set_pen(0)
badge.text("Sensor Monitor", 10, 10, scale=2)
badge.text(f"Temp: {temp:.1f}C", 10, 40, scale=2)
badge.text(f"Humidity: {humidity:.1f}%", 10, 70, scale=2)
badge.update()
while True:
display_sensor_data()
time.sleep(1)
```
### Interactive Hardware Control
```python
import badger2040
from machine import Pin
import time
badge = badger2040.Badger2040()
led = Pin(25, Pin.OUT)
led_state = False
def draw_ui():
badge.set_pen(15)
badge.clear()
badge.set_pen(0)
badge.text("LED Control", 10, 10, scale=2)
status = "ON" if led_state else "OFF"
badge.text(f"Status: {status}", 10, 50, scale=2)
badge.text("A: Toggle", 10, 100, scale=1)
badge.update()
while True:
draw_ui()
if badge.pressed(badger2040.BUTTON_A):
led_state = not led_state
led.value(1 if led_state else 0)
time.sleep(0.2) # Debounce
```
## Power Management for Hardware
```python
from machine import Pin
import machine
# Power control for external hardware
power_pin = Pin(23, Pin.OUT)
def power_on():
power_pin.value(1)
def power_off():
power_pin.value(0)
# Use with sleep modes
power_on()
# ... do work with sensor ...
power_off()
machine.lightsleep(5000) # Sleep 5 seconds
```
## Troubleshooting
**I2C device not detected**: Check wiring, verify device address, ensure pull-up resistors are present
**GPIO not working**: Verify pin is not used by internal badge functions, check if pin is input/output capable
**SPI communication fails**: Check clock polarity and phase, verify baudrate is within device specs
**PWM not smooth**: Increase PWM frequency, ensure duty cycle calculations are correct
**Sensor readings unstable**: Add delays between readings, use averaging, check power supply stability
## Hardware Safety
- **Never exceed 3.3V** on GPIO pins
- Use level shifters for 5V devices
- Add current-limiting resistors for LEDs
- Use flyback diodes with motors and relays
- Keep I2C wires short (< 20cm) or use bus extenders
- Use proper power supply for high-current devices
## Pinout Reference
Common Badger 2350 pins available for external hardware:
- GPIO 0-22: General purpose I/O
- GPIO 26-28: ADC capable (analog input)
- I2C QWIIC: SCL (Pin 5), SDA (Pin 4)
- SPI: SCK, MOSI, MISO (check documentation)
Refer to official Badger 2350 pinout diagram for complete details.

View File

@@ -0,0 +1,587 @@
---
name: badger-quickstart
description: Complete getting started guide for Universe 2025 (Tufty) Badge from zero to first app. Use when helping absolute beginners, providing step-by-step first-time setup, or when users ask "how do I get started", "where do I begin", or "first steps with the badge".
---
# Universe 2025 (Tufty) Badge Quickstart Guide
Complete step-by-step guide to go from zero to creating your first app for **MonaOS** on the Universe 2025 (Tufty) Badge. Perfect for absolute beginners!
## About the Badge
The Universe 2025 Badge is a custom version of the Pimoroni Tufty 2350, created for GitHub Universe 2025. It comes pre-loaded with **MonaOS**, a MicroPython-based operating system with an app launcher.
### Hardware Specifications
- **RP2350** Dual-core ARM Cortex-M33 @ 200MHz
- **512kB SRAM** and **16MB QSPI XiP flash**
- **320x240 full colour IPS display** (framebuffer pixel doubled from 160x120)
- **2.4GHz WiFi and Bluetooth 5**
- **1000mAh rechargeable battery** (up to 8 hours runtime)
- **IR receiver and transmitter** for beacon hunting
- **Five front-facing buttons** (A, B, C, UP, DOWN)
- **4-zone LED backlight**
- **USB-C** for charging and programming
## About MonaOS
Your badge runs **MonaOS**, which provides:
- An app launcher that auto-discovers apps in `/system/apps/`
- Each app is a directory containing `__init__.py`, `icon.png` (24x24), and optional `assets/`
- Apps implement `update()` function called every frame
- Navigate apps using physical buttons
## What You'll Need
### Hardware
- ✓ Universe 2025 (Tufty) Badge
- ✓ USB-C cable (data capable, not just charging)
- ✓ Computer (macOS, Linux, or Windows)
### Software (we'll install together)
- Python 3.8 or newer
- Development tools (mpremote)
- Text editor or IDE
### Time Required
- First-time setup: 30-45 minutes
- Your first app: 20-30 minutes
## Step-by-Step Setup
### Step 1: Install Python on Your Computer
**Why**: You need Python on your computer to run the tools that communicate with your badge.
**macOS:**
```bash
# Install using Homebrew
brew install python3
# Verify
python3 --version # Should show 3.8 or higher
```
**Linux (Ubuntu/Debian):**
```bash
sudo apt update
sudo apt install python3 python3-pip python3-venv
python3 --version
```
**Windows:**
1. Download from https://www.python.org/downloads/
2. Run installer
3. ✓ CHECK "Add Python to PATH"
4. Complete installation
5. Open PowerShell and verify: `python --version`
**Checkpoint**: Run `python3 --version` (or `python --version` on Windows). You should see version 3.8 or higher.
> **Need help?** See the `python-setup` skill for detailed installation guides.
### Step 2: Create Your First Project
**Why**: Keeping projects organized and using virtual environments prevents conflicts.
```bash
# Create a directory for your project
mkdir ~/badger-projects
cd ~/badger-projects
mkdir hello-badge
cd hello-badge
# Create a virtual environment
python3 -m venv venv
# Activate it
# macOS/Linux:
source venv/bin/activate
# Windows (PowerShell):
venv\Scripts\Activate.ps1
# Windows (Command Prompt):
venv\Scripts\activate.bat
```
**Checkpoint**: Your terminal prompt should now show `(venv)` at the beginning.
### Step 3: Install Badge Tools
**Why**: These tools let you communicate with your badge, upload files, and run code.
```bash
# Make sure venv is activated (you should see "(venv)" in prompt)
# Install essential tool
pip install mpremote
# Verify installation
mpremote --version
```
**Checkpoint**: Command should show version number without errors.
### Step 4: Connect Your Badge
**Why**: Let's make sure your computer can talk to your badge.
1. **Connect badge to computer** using USB-C cable
2. **Find the device**:
**macOS/Linux:**
```bash
ls /dev/tty.usb*
# Should see something like: /dev/tty.usbmodem14201
```
**Windows:**
```powershell
# In PowerShell
[System.IO.Ports.SerialPort]::getportnames()
# Should see something like: COM3
```
3. **Test connection**:
**macOS/Linux:**
```bash
mpremote connect /dev/tty.usbmodem* exec "print('Hello from Badge!')"
```
**Windows:**
```powershell
mpremote connect COM3 exec "print('Hello from Badge!')"
```
**Checkpoint**: You should see "Hello from Badge!" printed in your terminal.
> **Troubleshooting**:
> - Badge not found? Try different USB ports
> - Permission denied? See Step 4a below
> - Still stuck? See the `badger-diagnostics` skill
### Step 4a: Fix Permissions (Linux only)
If you get "Permission denied" on Linux:
```bash
# Add yourself to dialout group
sudo usermod -a -G dialout $USER
# Log out and log back in for changes to take effect
# Or restart your computer
```
### Step 5: Verify Badge is Ready (CRITICAL)
**Why**: We must verify everything is working before writing code.
```bash
# macOS/Linux:
mpremote connect /dev/tty.usbmodem* exec "
import sys
print('✓ MicroPython:', sys.version)
# Test badgeware module
from badgeware import screen, brushes, shapes
print('✓ badgeware module: loaded')
print('✓ Display size: 160x120')
print('✓ ALL CHECKS PASSED!')
"
# Windows:
mpremote connect COM3 exec "import sys; from badgeware import screen, brushes, shapes; print('✓ MicroPython:', sys.version); print('✓ badgeware loaded'); print('✓ ALL CHECKS PASSED!')"
```
**Checkpoint - ALL must pass**:
- ✓ MicroPython version shown
- ✓ badgeware module loads
- ✓ No error messages
**DO NOT PROCEED until all checks pass.**
### Step 6: Understand MonaOS App Structure
MonaOS apps must follow this structure:
```
my_app/
├── icon.png # 24x24 PNG icon for launcher
├── __init__.py # Entry point with update() function
└── assets/ # Optional: app assets (auto-added to path)
└── ...
```
Your `__init__.py` must implement:
- **`init()`** - Optional, called once when app launches
- **`update()`** - Required, called every frame by MonaOS
- **`on_exit()`** - Optional, called when returning to menu
### Step 7: Create Your First App
Create the app directory structure:
```bash
mkdir hello_app
cd hello_app
```
Create `hello_app/__init__.py`:
```python
# hello_app/__init__.py - Your first MonaOS app!
from badgeware import screen, brushes, shapes, io, PixelFont
import math
# Optional: called once when app launches
def init():
screen.font = PixelFont.load("nope.ppf")
print("Hello app initialized!")
# Required: called every frame by MonaOS
def update():
# Clear the framebuffer
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw animated sine wave
y = (math.sin(io.ticks / 100) * 20) + 60
screen.brush = brushes.color(0, 255, 0)
for x in range(160):
screen.draw(shapes.rectangle(x, int(y), 1, 1))
# Draw text
screen.brush = brushes.color(255, 255, 255)
screen.text("Hello, Badge!", 10, 10)
screen.text("Press HOME to exit", 10, 100)
# Handle button presses
if io.BUTTON_A in io.pressed:
print("Button A pressed!")
if io.BUTTON_HOME in io.pressed:
# HOME button exits to MonaOS menu automatically
pass
# Optional: called before returning to menu
def on_exit():
print("App exiting!")
```
Create `hello_app/icon.png`:
- 24x24 pixel PNG image
- Use any image editor to create a simple icon
- Or download a free icon and resize it
**Checkpoint**: Files created:
- `hello_app/__init__.py`
- `hello_app/icon.png` (24x24 PNG)
### Step 8: Test Your App Locally
```bash
# From your project directory (not inside hello_app/)
cd ~/badger-projects/hello-badge
# Run the app temporarily (doesn't save to badge)
# macOS/Linux:
mpremote connect /dev/tty.usbmodem* run hello_app/__init__.py
# Windows:
mpremote connect COM3 run hello_app/__init__.py
```
**Checkpoint**: Your badge display should show "Hello, Badge!" with an animated wave. Press HOME to exit.
### Step 9: Install Your App to MonaOS
**Why**: Install it permanently so it appears in the MonaOS launcher menu!
**⚠️ IMPORTANT**: The `/system/apps/` directory is READ-ONLY via mpremote. You MUST use USB Mass Storage Mode.
#### Enter USB Mass Storage Mode
1. **Connect badge** via USB-C (if not already connected)
2. **Press RESET button TWICE** quickly (double-click the RESET button on the back)
3. **Wait 2-3 seconds** - Badge will appear as **"BADGER"** drive
4. **Verify**: Drive should appear in Finder (macOS), File Explorer (Windows), or file manager (Linux)
#### Install Your App
**macOS/Linux:**
```bash
# Copy your entire app directory to the badge
cp -r hello_app /Volumes/BADGER/apps/
# OR manually via Finder:
# 1. Open BADGER drive in Finder
# 2. Navigate to apps/ folder
# 3. Drag hello_app folder into apps/
```
**Windows:**
```powershell
# Copy your entire app directory
# (Replace D: with your actual BADGER drive letter)
xcopy hello_app D:\apps\hello_app\ /E /I
# OR manually via File Explorer:
# 1. Open BADGER drive
# 2. Navigate to apps\ folder
# 3. Drag hello_app folder into apps\
```
#### Exit Mass Storage Mode
**macOS:**
```bash
# Eject the drive
diskutil eject /Volumes/BADGER
# Or right-click BADGER in Finder → Eject
```
**Windows:**
- Right-click BADGER drive → "Eject"
- Or use "Safely Remove Hardware" in system tray
**Linux:**
```bash
# Eject the drive
sudo umount /media/$USER/BADGER
```
**All Platforms:**
- Press **RESET button once** on the badge
- Badge reboots into MonaOS with your app installed!
**Checkpoint**:
- BADGER drive was successfully ejected
- Badge reboots normally (you see MonaOS menu)
### Step 10: Launch Your App from the Badge
1. **On your badge**: Press HOME if needed to return to MonaOS launcher
2. **Navigate**: Use UP/DOWN buttons to find your app
3. **Launch**: Press the select button to run your app!
**Note**: The default MonaOS menu shows 6 apps. You may need to expand pagination (see https://badger.github.io/hack/menu-pagination/)
🎉 **Congratulations!** Your first MonaOS app is installed and running!
## 🎉 Congratulations!
You just:
- ✓ Set up your Python development environment
- ✓ Installed badge communication tools
- ✓ Connected to your badge
- ✓ Created your first MonaOS app with proper structure
- ✓ Installed your app into MonaOS launcher
- ✓ Launched your custom app from the badge!
## Quick Reference Card
Save these commands - you'll use them a lot:
```bash
# Activate your virtual environment
source venv/bin/activate # macOS/Linux
venv\Scripts\Activate.ps1 # Windows
# Test app temporarily (doesn't save)
mpremote run my_app/__init__.py
# Install app to MonaOS launcher (USB Mass Storage Mode REQUIRED)
# 1. Press RESET button twice on badge (enters Mass Storage Mode)
# 2. Copy app to badge:
cp -r my_app /Volumes/BADGER/apps/ # macOS/Linux
xcopy my_app D:\apps\my_app\ /E /I # Windows
# 3. Eject BADGER drive safely
# 4. Press RESET once to reboot
# List files (read-only view)
mpremote ls /system/apps
# Connect to REPL (interactive mode)
mpremote
# (Ctrl+C to interrupt, Ctrl+D to soft reset, Ctrl+X to exit)
```
**⚠️ Remember**: You CANNOT use `mpremote` to install apps to `/system/apps/` - it's read-only! Always use USB Mass Storage Mode.
## Your First Improvements
### 1. Add Button Interactions
```python
def update():
screen.brush = brushes.color(20, 40, 60)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
if io.BUTTON_A in io.held:
screen.text("Button A held!", 10, 50)
elif io.BUTTON_B in io.pressed:
screen.text("Button B pressed!", 10, 50)
elif io.BUTTON_C in io.released:
screen.text("Button C released!", 10, 50)
```
### 2. Draw Shapes
```python
def update():
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# Draw a circle
screen.brush = brushes.color(255, 0, 0)
screen.draw(shapes.circle(80, 60, 30))
# Draw a rectangle
screen.brush = brushes.color(0, 255, 0)
screen.draw(shapes.rectangle(10, 10, 50, 30))
# Draw a line
screen.brush = brushes.color(255, 255, 255)
screen.draw(shapes.line(0, 0, 160, 120))
```
### 3. Create a Counter
```python
# Add at top level
counter = 0
def update():
global counter
screen.brush = brushes.color(0, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
screen.text(f"Count: {counter}", 30, 50)
if io.BUTTON_A in io.pressed:
counter += 1
if io.BUTTON_B in io.pressed:
counter = 0
```
## Common Beginner Questions
### Q: Do I need to activate venv every time?
**Yes**, activate it each time you open a new terminal.
### Q: What if I make a mistake in my code?
Edit locally, test with mpremote, then reinstall via Mass Storage Mode:
```bash
# 1. Edit your code locally
# 2. Test it temporarily
mpremote run my_app/__init__.py
# 3. Reinstall via Mass Storage Mode
# - Press RESET twice (enters Mass Storage Mode)
# - Replace files in /Volumes/BADGER/apps/my_app/
# - Eject and press RESET once
```
### Q: Can I edit code directly on the badge?
Yes! Enter USB Mass Storage Mode (press RESET twice). Badge appears as "BADGER" disk. Edit files in `/Volumes/BADGER/apps/` using any text editor.
### Q: What if my badge freezes?
Press the RESET button on the back of the badge.
### Q: How do I see errors?
Connect to REPL:
```bash
mpremote
```
Then test importing: `import my_app`. Errors will show in terminal.
### Q: How do I remove an app?
```bash
mpremote rm -rf :/system/apps/my_app
```
### Q: My app doesn't appear in the menu?
The default menu shows 6 apps. Expand pagination: https://badger.github.io/hack/menu-pagination/
## Troubleshooting Common Issues
### Badge not detected
1. Check USB cable (must be data cable, not just charging)
2. Try different USB port
3. Restart badge (press RESET)
4. Check connection:
- **macOS/Linux**: `ls /dev/tty.usb*`
- **Windows**: Check Device Manager
### "Permission denied" error
**Linux**: Add yourself to dialout group (see Step 4a)
**Windows**: Run PowerShell as Administrator
### "Module not found" error
Activate your virtual environment:
```bash
source venv/bin/activate # macOS/Linux
venv\Scripts\Activate.ps1 # Windows
```
### App doesn't appear in MonaOS menu
1. Verify upload: `mpremote ls /system/apps/my_app`
2. Check files exist: `__init__.py` and `icon.png`
3. Icon must be 24x24 PNG
4. May need to expand menu pagination
## Next Steps
### Learn More About App Development
→ See `badger-app-creator` skill
- Advanced button handling
- Working with sprites and images
- WiFi integration
- State management and persistence
### Connect Hardware
→ See `badger-hardware` skill
- GPIO pins and sensors
- I2C/SPI devices
- IR transmitter/receiver
- LED backlight control
### Use the REPL
→ See `micropython-repl` skill
- Interactive development
- Quick testing
- Install packages
### Improve Your Workflow
→ See `badger-deploy` skill
- Automated deployment scripts
- Project organization
- Multi-file apps
## Official Resources
- **Getting Started**: https://badger.github.io/get-started/
- **Hacks & Tutorials**: https://badger.github.io/hacks/
- **Official Apps**: https://badger.github.io/apps/
- **Source Code**: https://github.com/badger/home/tree/main/badgerware
- **API Docs**: https://github.com/badger/home/blob/main/badgerware/
## You're Ready!
You now have everything you need to create amazing projects with your Universe 2025 Badge. Happy coding! 🦡

View File

@@ -0,0 +1,612 @@
---
name: micropython-repl
description: MicroPython REPL usage, package management, module inspection, and interactive debugging for Universe 2025 (Tufty) Badge. Use when installing MicroPython packages, testing code interactively, checking installed modules, or using the REPL for development.
---
# MicroPython REPL and Package Management
Master the MicroPython REPL (Read-Eval-Print Loop) for interactive development, package management, and quick testing on the Universe 2025 (Tufty) Badge.
## Connecting to REPL
### Using screen (macOS/Linux)
```bash
# Find the device
ls /dev/tty.usb*
# Connect (115200 baud)
screen /dev/tty.usbmodem* 115200
# Exit screen: Ctrl+A then K, then Y to confirm
```
### Using mpremote
```bash
# Install mpremote
pip install mpremote
# Connect to REPL
mpremote connect /dev/tty.usbmodem*
# Or auto-detect
mpremote
```
### Using Thonny IDE
1. Open Thonny
2. Tools → Options → Interpreter
3. Select "MicroPython (RP2040)"
4. Choose correct port
5. Shell window shows REPL
## REPL Basics
### Special Commands
```python
# Ctrl+C - Interrupt running program
# Ctrl+D - Soft reboot
# Ctrl+E - Enter paste mode (for multi-line code)
# Ctrl+B - Exit paste mode and execute
# Help system
help() # General help
help(modules) # List all modules
help(badgeware) # Help on specific module
# Quick info
import sys
sys.implementation # MicroPython version
sys.platform # Platform info
```
### Interactive Testing
```python
# Test code immediately
>>> from badgeware import screen, display, brushes
>>> screen.brush = brushes.color(255, 255, 255)
>>> screen.text("Test", 10, 10, 2)
>>> display.update()
# Test calculations
>>> temp = 23.5
>>> temp_f = temp * 9/5 + 32
>>> print(f"{temp}C = {temp_f}F")
# Test GPIO
>>> from machine import Pin
>>> led = Pin(25, Pin.OUT)
>>> led.toggle() # Toggle LED immediately
```
### Paste Mode for Multi-line Code
```bash
# Enter paste mode: Ctrl+E
# Paste your code:
def calculate_distance(x1, y1, x2, y2):
import math
dx = x2 - x1
dy = y2 - y1
return math.sqrt(dx*dx + dy*dy)
print(calculate_distance(0, 0, 3, 4))
# Exit paste mode: Ctrl+D
```
## Package Management
### Using mip (MicroPython Package Installer)
```python
# Install package from micropython-lib
import mip
mip.install("urequests") # HTTP client
mip.install("logging") # Logging module
mip.install("umqtt.simple") # MQTT client
# Install from GitHub
mip.install("github:org/repo/package.py")
# Install to specific location
mip.install("urequests", target="/lib")
```
### Using upip (older method)
```python
# Connect to WiFi first
import network
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect('SSID', 'password')
# Wait for connection
while not wlan.isconnected():
pass
# Install package
import upip
upip.install("micropython-logging")
upip.install("picoweb")
```
### Manual Package Installation
```bash
# From your computer, copy files to badge
mpremote cp mymodule.py :/lib/mymodule.py
# Or using ampy
ampy --port /dev/tty.usbmodem* put mymodule.py /lib/mymodule.py
# Then use in REPL
>>> import mymodule
```
## Module Inspection
### List Available Modules
```python
# List all built-in and installed modules
help('modules')
# Check if module exists
import sys
'badgeware' in sys.modules # False until imported
import badgeware
'badgeware' in sys.modules # True after import
```
### Inspect Module Contents
```python
# See what's in a module
import badgeware
dir(badgeware) # List all attributes
# Check specific attributes
hasattr(badgeware, 'screen') # True
hasattr(badgeware, 'brushes') # True
# Get function signature
help(badgeware.screen)
# Explore submodules
from badgeware import shapes
dir(shapes) # See all shape functions
# View source (if available)
import inspect
inspect.getsource(mymodule.myfunction) # May not work on compiled modules
```
### Check Module Location
```python
# Find where module is located
import badgeware
badgeware.__file__ # Shows file path
# List files in system directory
import os
os.listdir('/system')
os.listdir('/system/apps') # MonaOS apps
```
## Interactive Debugging
### Quick Variable Inspection
```python
# Run code and inspect
>>> x = [1, 2, 3, 4, 5]
>>> len(x)
5
>>> type(x)
<class 'list'>
>>> sum(x)
15
# Object inspection
>>> import badgeware
>>> type(badgeware.screen)
<class 'Screen'>
>>> dir(badgeware.screen) # See all methods
>>> dir(badgeware.brushes) # See brush functions
>>> dir(badgeware.shapes) # See shape functions
```
### Print Debugging
```python
# Test function with prints
def process_data(data):
print(f"Input: {data}")
result = data * 2
print(f"Result: {result}")
return result
>>> process_data(5)
Input: 5
Result: 10
10
```
### Exception Handling
```python
# Test error handling
>>> try:
... 1 / 0
... except ZeroDivisionError as e:
... print(f"Error: {e}")
Error: division by zero
# Get full traceback
import sys
try:
buggy_function()
except Exception as e:
sys.print_exception(e)
```
### Memory Debugging
```python
# Check memory usage
import gc
gc.collect() # Run garbage collection
gc.mem_free() # Free memory in bytes
gc.mem_alloc() # Allocated memory
# Monitor memory during operation
before = gc.mem_free()
# ... do something
after = gc.mem_free()
print(f"Memory used: {before - after} bytes")
```
## Useful REPL Helpers
### Create a helpers.py file
```python
# helpers.py - Load in REPL for common tasks
import gc
import sys
import os
from machine import Pin, freq
def info():
"""Display system information"""
print(f"Platform: {sys.platform}")
print(f"Version: {sys.version}")
print(f"CPU Frequency: {freq()} Hz")
print(f"Free Memory: {gc.mem_free()} bytes")
def ls(path='/'):
"""List files in directory"""
try:
files = os.listdir(path)
for f in files:
print(f)
except:
print(f"Error listing {path}")
def cat(filename):
"""Display file contents"""
try:
with open(filename, 'r') as f:
print(f.read())
except Exception as e:
print(f"Error: {e}")
def rm(filename):
"""Remove file"""
try:
os.remove(filename)
print(f"Removed {filename}")
except Exception as e:
print(f"Error: {e}")
def blink(pin=25, times=3):
"""Blink LED for testing"""
import time
led = Pin(pin, Pin.OUT)
for i in range(times):
led.toggle()
time.sleep(0.5)
led.value(0)
# Load in REPL with:
# >>> from helpers import *
# >>> info()
```
### Auto-run at REPL start
Create `boot.py` to run code on startup:
```python
# boot.py - Runs on every boot
import gc
gc.collect()
# Optional: Auto-import common modules
# from badgeware import screen, display, brushes, shapes
# import time
print("Universe 2025 Badge Ready!")
print(f"Free memory: {gc.mem_free()} bytes")
```
## Quick Testing Workflows
### Test Hardware Function
```python
# Test I2C scan
from machine import I2C, Pin
i2c = I2C(0, scl=Pin(5), sda=Pin(4), freq=400000)
devices = i2c.scan()
print(f"Found devices: {[hex(d) for d in devices]}")
# Test display
from badgeware import screen, display, brushes
screen.brush = brushes.color(0, 0, 0)
screen.clear()
screen.brush = brushes.color(255, 255, 255)
screen.text("REPL Test", 10, 10, 2)
display.update()
```
### Test Network Connection
```python
import network
import time
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
print("Connecting to WiFi...")
wlan.connect('YOUR_SSID', 'YOUR_PASSWORD')
timeout = 10
while not wlan.isconnected() and timeout > 0:
print(".", end="")
time.sleep(1)
timeout -= 1
if wlan.isconnected():
print("\nConnected!")
print(f"IP: {wlan.ifconfig()[0]}")
else:
print("\nConnection failed")
```
### Test API Call
```python
import urequests
import json
response = urequests.get('https://api.github.com/zen')
print(response.text)
response.close()
# JSON API
response = urequests.get('https://api.example.com/data')
data = response.json()
print(data)
response.close()
```
## File System Operations
### List and Navigate Files
```python
import os
# Current directory
os.getcwd()
# List files
os.listdir()
os.listdir('/lib')
# File info
os.stat('main.py') # Returns (mode, ino, dev, nlink, uid, gid, size, atime, mtime, ctime)
# Check if file exists
try:
os.stat('main.py')
print("File exists")
except:
print("File not found")
```
### Read/Write Files
```python
# Write file
with open('test.txt', 'w') as f:
f.write('Hello from REPL!\n')
# Read file
with open('test.txt', 'r') as f:
content = f.read()
print(content)
# Append to file
with open('log.txt', 'a') as f:
f.write(f'Log entry: {time.time()}\n')
```
### Remove Files
```python
import os
# Remove file
os.remove('test.txt')
# Remove directory (must be empty)
os.rmdir('mydir')
```
## Checking Installed Packages
### Verify Package Installation
```python
# Method 1: Try to import
try:
import urequests
print("urequests is installed")
print(f"Location: {urequests.__file__}")
except ImportError:
print("urequests not installed")
# Method 2: Check file system
import os
lib_files = os.listdir('/lib')
print("Installed in /lib:")
for f in lib_files:
print(f" {f}")
# Method 3: Check sys.path
import sys
print("Module search paths:")
for path in sys.path:
print(f" {path}")
```
### Package Version Info
```python
# Some packages have __version__
import urequests
if hasattr(urequests, '__version__'):
print(f"urequests version: {urequests.__version__}")
# Check MicroPython version
import sys
print(sys.version)
print(sys.implementation)
```
## REPL Tips and Tricks
### History Navigation
- **Up/Down arrows**: Navigate command history
- **Tab**: Auto-completion (limited support)
### Copy Output from REPL
```python
# Run command and capture output
>>> result = []
>>> for i in range(10):
... result.append(i * 2)
>>> print(result)
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18]
# Copy from terminal window
```
### Run Script from REPL
```python
# Import and run
import myapp
myapp.main()
# Or reload after changes
import sys
if 'myapp' in sys.modules:
del sys.modules['myapp']
import myapp
```
### Timing Code
```python
import time
start = time.ticks_ms()
# ... your code here
for i in range(1000):
x = i * 2
end = time.ticks_ms()
print(f"Execution time: {time.ticks_diff(end, start)}ms")
```
## Common REPL Issues
**REPL not responding**: Press Ctrl+C to interrupt, or Ctrl+D to soft reset
**Can't import module**: Check `sys.path`, verify file is in `/lib` or root directory
**Out of memory**: Run `gc.collect()`, reduce variable usage, delete large objects
**Module changes not reflected**: Delete from `sys.modules` and re-import
**Connection lost**: Reconnect with screen or mpremote, check USB cable
## Using mpremote for Quick Operations
```bash
# Execute Python code remotely
mpremote exec "import machine; print(machine.freq())"
# Run local script on device
mpremote run test.py
# Copy file to device
mpremote cp test.py :main.py
# Copy file from device
mpremote cp :main.py local.py
# Mount local directory on device
mpremote mount .
# Filesystem operations
mpremote ls
mpremote mkdir /data
mpremote rm test.txt
# Chain commands
mpremote connect /dev/tty.usbmodem* cp main.py :main.py exec "import main"
```
## REPL Best Practices
1. **Save work frequently** - REPL state is lost on reboot
2. **Use paste mode** for multi-line code
3. **Run `gc.collect()`** before memory-intensive operations
4. **Test incrementally** - Build up complex code piece by piece
5. **Create helper functions** in a dedicated module
6. **Use `print()` liberally** for debugging
7. **Soft reset (Ctrl+D)** to clear state between tests
8. **Keep REPL sessions short** - Move working code to files
## Integration with Development Workflow
1. **Prototype in REPL** - Test ideas interactively
2. **Save to file** - Once code works, save to .py file
3. **Test from file** - Import and run from REPL
4. **Iterate** - Make changes, reload, test
5. **Deploy** - Upload final version to badge
The REPL is your best friend for rapid experimentation and debugging on the Badger 2350!

View File

@@ -0,0 +1,965 @@
---
name: python-setup
description: Python environment setup on your computer for Badger 2350 development. Use when installing Python, setting up virtual environments, installing development tools like mpremote or ampy, or configuring the computer-side development environment for Badger 2350 projects.
---
# Python Development Environment Setup
Complete guide to setting up Python on your computer for Universe 2025 (Tufty) Badge development, including virtual environments and all necessary tools.
## Quick Start (First Time Setup)
If you're brand new and just want to get started quickly:
```bash
# 1. Check if Python is installed
python3 --version
# If not installed, see "Install Python" section below
# 2. Create project directory
mkdir ~/badge-projects
cd ~/badge-projects
# 3. Create virtual environment
python3 -m venv venv
# 4. Activate it
source venv/bin/activate # macOS/Linux
# venv\Scripts\Activate.ps1 # Windows
# 5. Install badge tools
pip install mpremote
# 6. Test badge connection
mpremote exec "print('Badge connected!')"
# Should print: Badge connected!
# ✓ You're ready! Continue to badger-quickstart skill
```
If any command fails, continue with the detailed instructions below.
## Prerequisites Check
Before starting detailed setup, check what you already have:
```bash
# Check Python version
python3 --version
# Check pip
pip3 --version
# Check if tools are installed
which mpremote
which ampy
which rshell
```
## Install Python
### macOS
**Option 1: Using Homebrew (Recommended)**
```bash
# Install Homebrew if not already installed
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
# Install Python
brew install python3
# Verify installation
python3 --version
pip3 --version
```
**Option 2: Using python.org installer**
1. Download from https://www.python.org/downloads/
2. Run installer
3. Check "Add Python to PATH"
4. Complete installation
### Linux (Ubuntu/Debian)
```bash
# Update package list
sudo apt update
# Install Python 3 and pip
sudo apt install python3 python3-pip python3-venv
# Verify installation
python3 --version
pip3 --version
```
### Linux (Fedora/RHEL)
```bash
# Install Python 3
sudo dnf install python3 python3-pip
# Verify installation
python3 --version
pip3 --version
```
### Windows
**Option 1: Using winget (Windows 10/11)**
```powershell
# Install Python
winget install Python.Python.3.11
# Restart terminal, then verify
python --version
pip --version
```
**Option 2: Using python.org installer**
1. Download from https://www.python.org/downloads/
2. Run installer
3. **IMPORTANT**: Check "Add Python to PATH"
4. Check "Install pip"
5. Complete installation
6. Restart terminal
**Option 3: Using Microsoft Store**
1. Open Microsoft Store
2. Search for "Python 3.11"
3. Install
4. Verify in terminal
## Create Project Directory
Set up a dedicated directory for Badger 2350 projects:
```bash
# Create project directory
mkdir -p ~/badger-projects
cd ~/badger-projects
# Create your first project
mkdir my-badge-app
cd my-badge-app
```
## Set Up Virtual Environment
Virtual environments isolate project dependencies and prevent conflicts.
### Create Virtual Environment
```bash
# Create venv in project directory
python3 -m venv venv
# Alternative name
python3 -m venv .venv
```
### Activate Virtual Environment
**macOS/Linux:**
```bash
# Activate
source venv/bin/activate
# Your prompt should change to show (venv)
(venv) user@computer:~/badger-projects/my-badge-app$
# Deactivate when done
deactivate
```
**Windows (PowerShell):**
```powershell
# Enable script execution (first time only)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Activate
venv\Scripts\Activate.ps1
# Deactivate when done
deactivate
```
**Windows (Command Prompt):**
```cmd
# Activate
venv\Scripts\activate.bat
# Deactivate when done
deactivate
```
### Verify Virtual Environment
```bash
# Should show venv Python, not system Python
which python3 # macOS/Linux
where python # Windows
# Should be venv location like:
# ~/badger-projects/my-badge-app/venv/bin/python3
```
## Install Badger Development Tools
With virtual environment activated:
### Core Tools
```bash
# Install mpremote (recommended primary tool)
pip install mpremote
# Install ampy (alternative file management)
pip install adafruit-ampy
# Install rshell (interactive shell)
pip install rshell
# Install esptool (firmware flashing)
pip install esptool
# Verify installations
mpremote --version
ampy --version
rshell --version
esptool.py version
```
### Optional Development Tools
```bash
# Thonny IDE (beginner-friendly)
pip install thonny
# Code quality tools
pip install black # Code formatter
pip install pylint # Linter
pip install mypy # Type checker
# Testing tools
pip install pytest # Testing framework
pip install pytest-cov # Coverage reporting
# Documentation tools
pip install mkdocs # Documentation generator
pip install sphinx # Alternative documentation
```
### Save Dependencies
Create `requirements.txt` to track dependencies:
```bash
# Generate requirements.txt
pip freeze > requirements.txt
```
**Example requirements.txt:**
```
mpremote==1.20.0
adafruit-ampy==1.1.0
rshell==0.0.32
esptool==4.6.2
black==23.12.1
pylint==3.0.3
pytest==7.4.3
```
### Install from requirements.txt
```bash
# Install all dependencies at once
pip install -r requirements.txt
# Or upgrade existing
pip install -r requirements.txt --upgrade
```
## Configure Tools
### mpremote Configuration
Create alias for easier use:
**macOS/Linux (.bashrc or .zshrc):**
```bash
# Add to ~/.bashrc or ~/.zshrc
alias badge='mpremote connect /dev/tty.usbmodem*'
# Reload shell
source ~/.bashrc # or source ~/.zshrc
# Usage
badge ls
badge cp main.py :main.py
```
**Windows (PowerShell profile):**
```powershell
# Open profile
notepad $PROFILE
# Add alias
function badge { mpremote connect COM3 @args }
# Reload
. $PROFILE
# Usage
badge ls
```
### ampy Configuration
Set default port to avoid typing it each time:
**macOS/Linux:**
```bash
# Add to ~/.bashrc or ~/.zshrc
export AMPY_PORT=/dev/tty.usbmodem*
# Reload
source ~/.bashrc
```
**Windows:**
```powershell
# Add to PowerShell profile
$env:AMPY_PORT = "COM3"
# Or set permanently
[Environment]::SetEnvironmentVariable("AMPY_PORT", "COM3", "User")
```
## Verify Complete Setup
Run this verification script:
```bash
# verify_setup.sh (macOS/Linux)
#!/bin/bash
echo "Verifying Badger 2350 Development Setup"
echo "========================================"
# Check Python
if command -v python3 &> /dev/null; then
echo "✓ Python: $(python3 --version)"
else
echo "✗ Python not found"
exit 1
fi
# Check pip
if command -v pip3 &> /dev/null; then
echo "✓ pip: $(pip3 --version)"
else
echo "✗ pip not found"
exit 1
fi
# Check virtual environment
if [[ "$VIRTUAL_ENV" != "" ]]; then
echo "✓ Virtual environment: active"
else
echo "⚠ Virtual environment: not active"
fi
# Check tools
tools=(mpremote ampy rshell esptool.py)
for tool in "${tools[@]}"; do
if command -v $tool &> /dev/null; then
echo "$tool: installed"
else
echo "$tool: not installed"
fi
done
echo "========================================"
echo "Setup verification complete!"
```
Make executable and run:
```bash
chmod +x verify_setup.sh
./verify_setup.sh
```
**Windows PowerShell version:**
```powershell
# verify_setup.ps1
Write-Host "Verifying Badger 2350 Development Setup"
Write-Host "========================================"
# Check Python
if (Get-Command python -ErrorAction SilentlyContinue) {
$version = python --version
Write-Host "✓ Python: $version"
} else {
Write-Host "✗ Python not found"
exit 1
}
# Check pip
if (Get-Command pip -ErrorAction SilentlyContinue) {
Write-Host "✓ pip: installed"
} else {
Write-Host "✗ pip not found"
exit 1
}
# Check virtual environment
if ($env:VIRTUAL_ENV) {
Write-Host "✓ Virtual environment: active"
} else {
Write-Host "⚠ Virtual environment: not active"
}
# Check tools
$tools = @("mpremote", "ampy", "rshell", "esptool.py")
foreach ($tool in $tools) {
if (Get-Command $tool -ErrorAction SilentlyContinue) {
Write-Host "$tool: installed"
} else {
Write-Host "$tool: not installed"
}
}
Write-Host "========================================"
Write-Host "Setup verification complete!"
```
## Test Badge Connection
Once tools are installed, test connection to badge:
```bash
# List serial ports (macOS/Linux)
ls /dev/tty.usb*
# List serial ports (Windows PowerShell)
[System.IO.Ports.SerialPort]::getportnames()
# Test connection with mpremote
mpremote connect /dev/tty.usbmodem* exec "print('Hello from Badger!')"
# Or on Windows
mpremote connect COM3 exec "print('Hello from Badger!')"
# If successful, you should see: Hello from Badger!
```
## Project Template
Create a standard project structure:
```bash
# Create structure
mkdir -p my-badge-app/{lib,assets,data,tests}
cd my-badge-app
# Create files
touch main.py config.py README.md requirements.txt
touch lib/__init__.py
touch tests/test_main.py
# Create .gitignore
cat > .gitignore <<EOF
# Virtual environment
venv/
.venv/
env/
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Local config
config.local.py
.env
EOF
# Initialize git
git init
```
**Project structure:**
```
my-badge-app/
├── venv/ # Virtual environment (gitignored)
├── main.py # Main application
├── config.py # Configuration
├── requirements.txt # Python dependencies
├── README.md
├── .gitignore
├── lib/ # Reusable modules
│ └── __init__.py
├── assets/ # Images, fonts, etc.
├── data/ # Runtime data
└── tests/ # Test files
└── test_main.py
```
## IDE Setup
### VS Code (Recommended)
```bash
# Install VS Code
# macOS
brew install --cask visual-studio-code
# Linux
sudo snap install code --classic
# Windows
winget install Microsoft.VisualStudioCode
```
**Recommended Extensions:**
1. Python (Microsoft)
2. Pylance
3. Python Debugger
4. MicroPython (for syntax)
5. GitLens
**VS Code Settings (.vscode/settings.json):**
```json
{
"python.defaultInterpreterPath": "${workspaceFolder}/venv/bin/python",
"python.formatting.provider": "black",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"editor.formatOnSave": true,
"files.exclude": {
"**/__pycache__": true,
"**/*.pyc": true
}
}
```
### PyCharm
1. Download from https://www.jetbrains.com/pycharm/
2. Open project directory
3. Configure interpreter: Settings → Project → Python Interpreter
4. Select existing venv or create new one
### Thonny (Beginner-Friendly)
```bash
# Install Thonny
pip install thonny
# Or download from https://thonny.org
# Run
thonny
```
**Configure Thonny for Badger:**
1. Tools → Options → Interpreter
2. Select "MicroPython (RP2040)"
3. Select correct port
4. Click OK
## Workflow Scripts
### Activate Script
Create `activate.sh` in project root:
```bash
#!/bin/bash
# activate.sh - Quick project activation
# Activate virtual environment
source venv/bin/activate
# Set badge port
export BADGE_PORT="/dev/tty.usbmodem*"
# Show status
echo "Badge development environment activated!"
echo "Python: $(which python3)"
echo "Badge port: $BADGE_PORT"
# Quick commands
alias badge='mpremote connect $BADGE_PORT'
alias deploy='./deploy.sh'
echo "Ready to develop!"
```
Usage:
```bash
source activate.sh
# Now you're ready to work!
```
### Quick Deploy Script
Create `deploy.sh`:
```bash
#!/bin/bash
# deploy.sh - Quick deploy to badge
if [ -z "$BADGE_PORT" ]; then
BADGE_PORT="/dev/tty.usbmodem*"
fi
echo "Deploying to badge..."
mpremote connect $BADGE_PORT cp main.py :main.py
mpremote connect $BADGE_PORT cp config.py :config.py
echo "Deployment complete!"
```
Make executable:
```bash
chmod +x deploy.sh activate.sh
```
## Common Setup Issues
### "command not found: python"
**macOS/Linux:**
```bash
# Use python3 instead of python
python3 --version # This should work
# If python3 also not found, install Python (see Install Python section above)
# Optional: Create alias for convenience
alias python=python3
alias pip=pip3
# Add to ~/.bashrc or ~/.zshrc to make permanent
```
**Windows:**
```powershell
# Use python instead of python3
python --version
# If not found, reinstall Python with "Add to PATH" checked
```
**Important**: On macOS/Linux, always use `python3` and `pip3` (not `python` and `pip`).
### Python not in PATH
**macOS/Linux:**
```bash
# Add to PATH in ~/.bashrc or ~/.zshrc
export PATH="/usr/local/bin:$PATH"
export PATH="/opt/homebrew/bin:$PATH" # For M1/M2 Macs
# Reload shell
source ~/.bashrc # or source ~/.zshrc
```
**Windows:**
- Reinstall Python with "Add to PATH" checked
- Or manually add to PATH:
- System Properties → Environment Variables
- Add `C:\Python311` and `C:\Python311\Scripts`
### pip install fails with permissions error
```bash
# Don't use sudo! Use virtual environment instead
python3 -m venv venv
source venv/bin/activate
pip install mpremote
```
### Virtual environment activation fails (Windows)
```powershell
# Enable scripts (run as Administrator)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Then activate normally
venv\Scripts\Activate.ps1
```
### Badge not detected
**macOS:**
```bash
# Check if driver needed (usually automatic)
ls /dev/tty.usb*
# Grant permissions
sudo chmod 666 /dev/tty.usbmodem*
```
**Linux:**
```bash
# Add user to dialout group
sudo usermod -a -G dialout $USER
# Logout and login for changes to take effect
# Or use sudo temporarily
sudo mpremote connect /dev/ttyACM0
```
**Windows:**
- Install USB driver if needed
- Check Device Manager for COM port
- Try different USB ports
### mpremote connection fails
```bash
# Try explicit port
mpremote connect /dev/tty.usbmodem14201
# List available ports
mpremote connect list
# Auto-detect port (usually works)
mpremote exec "print('Hello')"
# Check if badge is in bootloader mode
# (Hold BOOTSEL button while connecting)
```
### "can't open file" or "No such file" errors
If you see errors like `can't open file 'test_connection.py'`:
```bash
# This means the script doesn't exist yet
# Option 1: Use basic verification instead
mpremote exec "print('Badge connected!')"
# Option 2: Create the missing script (see badger-diagnostics skill)
# Option 3: Use direct mpremote commands instead of scripts
mpremote run my_app/__init__.py # Instead of make commands
```
**Common missing files on first setup**:
- `test_connection.py` - Use `mpremote exec` for basic testing instead
- `deploy.sh` - Use direct `mpremote cp` commands instead
- Apps/examples - Create them as you go
**Don't let missing scripts block you** - use the direct mpremote commands shown in CLAUDE.md and skills.
## Update Tools
Keep tools updated:
```bash
# Activate virtual environment first
source venv/bin/activate
# Update pip itself
pip install --upgrade pip
# Update all packages
pip install --upgrade mpremote ampy rshell esptool
# Or update from requirements.txt
pip install -r requirements.txt --upgrade
```
## ⚠️ Verify Your Setup
**CRITICAL**: Always verify your setup is working correctly before starting development.
### Complete Verification Checklist
Run through this checklist every time you start a new session:
```bash
# 1. Verify Python installation
python3 --version
# Should show: Python 3.8.0 or higher
# 2. Verify virtual environment is activated
which python3
# Should show path to venv/bin/python3 (not system Python)
# 3. Verify tools are installed
mpremote --version
ampy --version
# Both should show version numbers
# 4. Verify badge is connected
ls /dev/tty.usb* # macOS/Linux
# OR
# [System.IO.Ports.SerialPort]::getportnames() # Windows
# 5. Test badge connection
mpremote connect /dev/tty.usbmodem* exec "print('Hello from Badge!')"
# Should print: Hello from Badge!
# 6. Verify badgeware module
mpremote connect /dev/tty.usbmodem* exec "import badgeware; print('badgeware OK')"
# Should print: badgeware OK
```
### Automated Verification Script
Create `verify_setup.sh` in your project:
```bash
#!/bin/bash
# verify_setup.sh - Complete environment verification
echo "=========================================="
echo "Badger 2350 Environment Verification"
echo "=========================================="
errors=0
# Check Python
if command -v python3 &> /dev/null; then
version=$(python3 --version)
echo "✓ Python: $version"
else
echo "✗ Python not found"
((errors++))
fi
# Check virtual environment
if [[ "$VIRTUAL_ENV" != "" ]]; then
echo "✓ Virtual environment: active ($VIRTUAL_ENV)"
else
echo "⚠ Virtual environment: not active"
echo " Run: source venv/bin/activate"
((errors++))
fi
# Check mpremote
if command -v mpremote &> /dev/null; then
echo "✓ mpremote: installed"
else
echo "✗ mpremote: not installed"
echo " Run: pip install mpremote"
((errors++))
fi
# Check badge connection
if mpremote connect list 2>&1 | grep -q "usb"; then
echo "✓ Badge: detected"
# Test REPL
if mpremote exec "print('OK')" 2>&1 | grep -q "OK"; then
echo "✓ Badge REPL: working"
else
echo "✗ Badge REPL: not responding"
((errors++))
fi
# Test badgeware module
if mpremote exec "import badgeware" 2>&1; then
echo "✓ badgeware module: available"
else
echo "✗ badgeware module: not found"
((errors++))
fi
else
echo "✗ Badge: not detected"
echo " Check USB connection"
((errors++))
fi
echo "=========================================="
if [ $errors -eq 0 ]; then
echo "✓ ALL CHECKS PASSED - Ready for development!"
exit 0
else
echo "$errors ERROR(S) FOUND - Fix issues before proceeding"
exit 1
fi
```
Make executable: `chmod +x verify_setup.sh`
**Run this script before every development session**: `./verify_setup.sh`
### What to Do If Verification Fails
| Issue | Solution |
|-------|----------|
| Python not found | Reinstall Python, check PATH |
| venv not active | Run `source venv/bin/activate` |
| Tools not installed | Run `pip install -r requirements.txt` |
| Badge not detected | Check USB cable, try different port |
| REPL not responding | Restart badge, check for other programs using port |
| badgeware missing | Badge firmware may need reflashing |
**Never skip verification** - It catches 90% of issues before they become problems.
## Best Practices
1. **Always verify setup first** - Run verification script at start of session
2. **Always use virtual environments** - Isolate project dependencies
3. **Keep requirements.txt updated** - `pip freeze > requirements.txt`
4. **Use version control (git)** - Track changes
5. **Document your setup** - Update README.md
6. **Test on clean environment** - Verify requirements.txt is complete
7. **Don't commit venv/** - Add to .gitignore
8. **Pin versions** - Avoid "works on my machine" issues
## Next Steps
After setup is complete:
1. ✓ Python installed
2. ✓ Virtual environment created
3. ✓ Tools installed (mpremote, ampy, etc.)
4. ✓ Badge connected and detected
5. ✓ Project structure created
Now you're ready to:
- Flash firmware to badge (see `badger-2350-dev` skill)
- Create your first app (see `badger-app-creator` skill)
- Connect sensors (see `badger-hardware` skill)
Your development environment is ready! 🎉