Complete getting started guide for Universe 2025 (Tufty) Badge from zero to first app...
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!
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.
Your badge runs MonaOS, which provides:
/system/apps/__init__.py, icon.png (24x24), and optional assets/update() function called every frameWhy: You need Python on your computer to run the tools that communicate with your badge.
macOS:
# Install using Homebrew
brew install python3
# Verify
python3 --version # Should show 3.8 or higher
Linux (Ubuntu/Debian):
sudo apt update
sudo apt install python3 python3-pip python3-venv
python3 --version
Windows:
python --versionβ
Checkpoint: Run python3 --version (or python --version on Windows). You should see version 3.8 or higher.
Need help? See the
python-setupskill for detailed installation guides.
Why: Keeping projects organized and using virtual environments prevents conflicts.
# 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.
Why: These tools let you communicate with your badge, upload files, and run code.
# 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.
Why: Let's make sure your computer can talk to your badge.
macOS/Linux:
ls /dev/tty.usb*
# Should see something like: /dev/tty.usbmodem14201
Windows:
# In PowerShell
[System.IO.Ports.SerialPort]::getportnames()
# Should see something like: COM3
macOS/Linux:
mpremote connect /dev/tty.usbmodem* exec "print('Hello from Badge!')"
Windows:
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-diagnosticsskill
If you get "Permission denied" on Linux:
# 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
Why: We must verify everything is working before writing code.
# 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:
DO NOT PROCEED until all checks pass.
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 launchesupdate() - Required, called every frame by MonaOSon_exit() - Optional, called when returning to menuCreate the app directory structure:
mkdir hello_app
cd hello_app
Create hello_app/__init__.py:
# 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:
β Checkpoint: Files created:
hello_app/__init__.pyhello_app/icon.png (24x24 PNG)# 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.
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.
macOS/Linux:
# 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:
# 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\
macOS:
# Eject the drive
diskutil eject /Volumes/BADGER
# Or right-click BADGER in Finder β Eject
Windows:
Linux:
# Eject the drive
sudo umount /media/$USER/BADGER
All Platforms:
β Checkpoint:
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!
You just:
Save these commands - you'll use them a lot:
# 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.
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)
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))
# 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
Yes, activate it each time you open a new terminal.
Edit locally, test with mpremote, then reinstall via Mass Storage Mode:
# 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
Yes! Enter USB Mass Storage Mode (press RESET twice). Badge appears as "BADGER" disk. Edit files in /Volumes/BADGER/apps/ using any text editor.
Press the RESET button on the back of the badge.
Connect to REPL:
mpremote
Then test importing: import my_app. Errors will show in terminal.
mpremote rm -rf :/system/apps/my_app
The default menu shows 6 apps. Expand pagination: https://badger.github.io/hack/menu-pagination/
ls /dev/tty.usb*Linux: Add yourself to dialout group (see Step 4a) Windows: Run PowerShell as Administrator
Activate your virtual environment:
source venv/bin/activate # macOS/Linux
venv\Scripts\Activate.ps1 # Windows
mpremote ls /system/apps/my_app__init__.py and icon.pngβ See badger-app-creator skill
β See badger-hardware skill
β See micropython-repl skill
β See badger-deploy skill
You now have everything you need to create amazing projects with your Universe 2025 Badge. Happy coding! π¦‘