- Introduction
- Getting started
- Reading button state
- Working with the screen
- Wireless networking and Bluetooth
Your badge this year is based on our upcoming "Tufty" Badgeware product. It's the fourth, most ambitious, and best, digital badge that we've developed for GitHub Universe so far!
On the board we've packed in a bunch of great features:
- RP2350 Dual-core ARM Cortex-M33 @ 200MHz
- 512kB SRAM
- 16MB QSPI XiP flash
- 320x240 full colour IPS display (currently pixel doubled to 160x120)
- 2.4GHz WiFi and Bluetooth 5
- 1000mAh chargeable battery (8 hours runtime)
- Qw/ST and SWD ports for accessories and debugging
- Five front facing buttons
- Battery voltage monitoring and charge status
- USB-C port for charging and programming
- RESET / BOOTSEL buttons
- 4-zone LED backlight
- Durable polycarbonate case with lanyard fixings
We hope you really love tinkering with it during, and after, the event. Ping us on BlueSky and show us what you're up to!
- your friends at Pimoroni ❤️
This documentation is an early draft and both it and the API are subject to change! We're putting together a new build with more features and some performance enhancements which we'll share soon!
The board is pre-loaded with a special build of MicroPython that includes drivers for all of the built-in hardware.
We've also included a small suite of example applications to demonstrate how different features of the badge work. Feel free to poke around in the code and experiment!
For an overview of how to put the badge into different modes checkout https://badger.github.io/get-started/.
The structure for apps is simple. They live in the /system/apps directory.
/system
/apps
/my_application
icon.png
__init__.py
/assets
...
Each application has a minimal structure:
icon.pngcontains the icon for your app, it should be a 24x24 PNG image.__init__.pythe entry point for your application, this is whereupdate()will be implemented.assets/a directory to contain any assets your app uses, this is automatically added to the system path when your app launches.
Your app should implement an update() function within __init__.py which will automatically be called every frame to give it a chance to update its state and render new output to the screen.
You'll have to edit /system/apps/menu/__init__.py on your device to add your app, right now it only supports six icons - have fun expanding it!
An app is launched by main.py, which handles the intro cinematic, menu and launching your app. It'll call your init() and update() methods, and call on_exit() when you press the HOME button to leave your app.
# example __init__.py for an application
# select a font to use
screen.font = PixelFont.load("nope.ppf")
# called once when your app launches
def init():
pass
# called every frame, do all your stuff here!
def update():
# clear the framebuffer
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# calculate and draw an animated sine wave
y = (math.sin(io.ticks / 100) * 20) + 80
screen.brush = brushes.color(0, 255, 0)
for x in range(160):
screen.draw(shapes.rectangle(x, y, 1, 1))
# write a message
screen.brush = brushes.color(255, 255, 255)
screen.text("hello badge!", 10, 10)
# called before returning to the menu to allow you to save state
def on_exit():
passThe easiest way to edit the code on the device is to put it into mass storage mode:
- Connect your badge up to your computer via a USB-C cable
- Press the RESET button twice
- The USB Disk Mode screen will appear
- The badge should appear as a disk named "BADGER" on your computer
In this mode you can see the contents of the FAT32 /system/ mount. This is where all application code should live.
The badge has a writeable LittleFS partition located at / which is intended for applications to store state information and cache any data they may need to hold on to across resets.
You can use normal Python style file access from your code:
with open("/storage/myfile.txt", "w") as out:
out.write("this is some text i want to keep\n")The io module also exposes helpful information about the state of the buttons on your badge. Click here for full documentation of the io module.
Each button is represented by a constant (for example, io.BUTTON_A). The API lets you check whether a button has been pressed, released, held, or if its state has changed during the current frame.
The following example draws a small white circle in the centre of the screen and allows the user to move it using the buttons:
import math
from badgeware import screen, shapes, brushes, io
# start the cursor in the middle of the screen
x, y = 80, 60
# clamp a value to within an upper and lower bound
def clamp(value, lower, upper):
return math.max(lower, math.min(upper, value))
# called automatically every frame
def update():
global x, y
# clear the screen
screen.brush = brushes.color(20, 40, 60)
screen.draw(shapes.rectangle(0, 0, 160, 120))
# move cursor based on button states
if io.BUTTON_A in io.held: x -= 1 # left
if io.BUTTON_C in io.held: x += 1 # right
if io.BUTTON_UP in io.held: y -= 1 # up
if io.BUTTON_DOWN in io.held: y += 1 # down
# clamp position to screen bounds
x = clamp(x, 0, 160)
y = clamp(y, 0, 120)
# draw the cursor
screen.brush = brushes.color(255, 255, 255)
screen.draw(shapes.circle(x, y, 5))The framebuffer is a 160 × 120 true colour Image named screen. Click here for full documentation of the Image class.
Your application can draw to the screen during the update() function. After your code finishes executing, the badge automatically pixel-doubles the framebuffer to fit the display.
The screen supports drawing vector shapes, blitting other images, and rendering text using pixel fonts.
The shapes module provides ways to create primitive shapes which can then be drawn onto images. Click here for full documentation of the shapes module.
Currently supported shapes are rectangles, circles, arcs, pies, lines, rounded rectangles, regular polygons, and squircles.
Shapes are drawn using the currently assigned brush. Click here for full documentation of the brushes module.
# example of drawing a circle in the center of the screen
from badgeware import screen, brushes, shapes
# enable antialiasing for lush smooth edges
screen.antialias = Image.X4
def update():
# clear the background
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# draw the circle
screen.brush = brushes.color(0, 255, 0)
screen.draw(shapes.circle(80, 60, 20))Shapes can also be given a transformation matrix to adjust their scale, rotation, and skew - this is very useful for creating smooth animations. Click here for full documentation of the Matrix class.
# example of animating a rotating rectangle
from badgeware import screen, brushes, shapes
# enable antialiasing for lush smooth edges
screen.antialias = Image.X4
def update():
# clear the background
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# transform and draw the rectangle
screen.brush = brushes.color(0, 255, 0)
rectangle = shapes.rectangle(-1, -1, 2, 2)
rectangle.transform = Matrix().translate(80, 60).scale(20, 20).rotate(io.ticks / 100)
screen.draw(rectangle)The Image class can load images from files on the filesystem which can then be blitted onto the screen. Click here for full documentation of the Image class.
# example of loading a sprite and blitting it to the screen
from badgeware import screen, brushes, Image
from lib import SpriteSheet
# load an image as a sprite sheet specifying the number of rows and columns
mona = SpriteSheet(f"assets/mona-sprites/mona-default.png", 7, 1)
def update():
# clear the background
screen.brush = brushes.color(20, 40, 60)
screen.clear()
# blit the sprite at 0, 0 in the spritesheet to the screen
screen.blit(mona.sprite(0, 0), 10, 10)
# scale blit the sprite at 3, 0 in the spritesheet to the screen
screen.scale_blit(mona.sprite(0, 0), 50, 50, 30, 30)The PixelFont class provides functions for loading pixel fonts, which can then be used to render text onto images. Click here for full documentation of the PixelFont class.
There are thirty licensed pixel fonts included.
# example of drawing text to the screen
from badgeware import screen, PixelFont
screen.font = PixelFont.load("nope.ppf")
def update():
screen.brush = brushes.color(20, 40, 60)
screen.clear()
message = "this font rocks"
screen.brush(255, 255, 255)
screen.text(message, 10, 10)You can use the existing MicroPython functionality for wireless networking and bluetooth functionality.