A standalone Pico SDK C++ project that drives the PicoVector vector renderer on the Pimoroni RP2350 Badgeware boards, from one codebase:
| Board | Display | Notes |
|---|---|---|
| Tufty 2350 | 320x240 colour parallel ST7789 | LORES 160x120 (pixel-doubled) or HIRES 320x240 |
| Blinky 2350 | 39x26 mono LED matrix | PIO+DMA BCD driver; brightness-mapped from RGB |
| Badger 2350 | 264x176 mono SSD1680 e-ink | slow static refresh; present sparingly |
It provides a small badge runtime - mirroring the Badgeware MicroPython
screen / badge API - on top of a thin per-board hardware layer, plus C++
ports of the Badgeware PicoVector demos.
The runtime is written against a uniform board:: interface. Each board lives
in board/<name>/ and supplies its Pico board header, its display driver, and
the board:: glue (init / framebuffer / present / width / height and
the COLOR / EPAPER capability constants). Everything shared between boards
(PSRAM, power/RTC, buttons, panic) is header-driven and lives in lib/.
Select the target at configure time with -DBADGE_BOARD=<tufty|blinky|badger>
(default tufty).
PicoVector is a git submodule, so clone recursively:
git clone --recursive https://github.com/pimoroni/badgeware-cpp
# or, in an existing checkout:
git submodule update --init.
├── assets/ fonts + images, embedded at build time
├── cmake/ bin2c + badge_embed_asset (asset embedding)
├── lib/ board-agnostic runtime + shared support
│ ├── picovector/ the PicoVector renderer (git submodule, no-bindings)
│ ├── badge.* the screen/badge/shape runtime (uses board::)
│ ├── psram.* APS6404 PSRAM bring-up over the RP2350 QMI
│ ├── buttons.* debounced A/B/C/Up/Down/Home input
│ ├── powman.* power button long-press -> sleep, and the RTC
│ └── panic.c a panic hook to print out errors
├── board/ one directory per target board
│ ├── tufty/ pimoroni_tufty2350.h, st7789.*, board.*
│ ├── blinky/ pimoroni_blinky2350.h, blinky.*, board.*
│ └── badger/ pimoroni_badger2350.h, ssd1680.*, board.*
└── demos/ one executable per directory
Each board/<name>/board.cmake defines the board target for that target; the
top-level CMakeLists.txt picks the board header before the SDK is pulled in.
Needs the Arm GCC toolchain and CMake. The Pico SDK is located from
PICO_SDK_PATH, or fetched from git automatically if that is unset. Use a
separate build directory per board.
export PICO_SDK_PATH=/path/to/pico-sdk # optional; omit to fetch the SDK
cmake -S . -B build/tufty -DBADGE_BOARD=tufty
cmake --build build/tufty -j4 # build every demo for Tufty
cmake -S . -B build/blinky -DBADGE_BOARD=blinky
cmake --build build/blinky --target text # ...or just one demoEach demo produces its own UF2 under build/<board>/demos/<name>/. Flash by
drag-and-drop in BOOTSEL mode, or picotool load -x <path>.uf2.
-DPICOVECTOR_DUAL_CORE=ON rasterises on core1 as well as core0 (~2x faster).
C++ ports of the Badgeware PicoVector demos, written against the badge runtime.
Demos marked * scale their layout by k = min(width,height)/120 (so Tufty at
160x120 is k=1 and unchanged) and also build for Blinky's 39x26 matrix; the
rest assume Tufty's colour framebuffer and are Tufty-only:
| primitives | circles* lines* triangles* shape vector_shapes* |
| brushes | gradient* pattern* pixelate blur_brush lighten_darken |
| filters | blur* dither |
| strokes | stroke joins_caps |
| fill rules / clipping | fill_rules intersect line_clip |
| images | sprites* transform_sprite* image_brush |
| other | raycast · vector_text · buttons (input viz) |
text and clock go further: they use badge::epaper() to pick a cadence and
also target Badger's e-ink. text builds on all three boards; clock targets
Tufty + Badger only (a clock face is meaningless on Blinky's 39x26 matrix). Use
them as the template when porting more.
Which demo targets which board is declared in one place -
BOARDS_<demo> in demos/CMakeLists.txt (default: Tufty
only). A demo not listed for the current board has no build target at all. To
see the whole map for a configured build:
cmake --build build/blinky --target list-demos # or: make list-demoswhich prints every demo and its supported boards, marking (*) the ones built
for the board you configured.
Own your loop (mirrors the Python while True: ...; badge.update() style):
#include "badge.hpp"
using namespace badge;
int main() {
badge::init();
while (true) {
clear();
/* draw on `screen` */
present(); // gate on badge::epaper() for static e-ink
poll();
}
}badge::run(update) is also available as a convenience loop (clear -> update ->
present -> poll, with Home rebooting to BOOTSEL by default).
Query the target with badge::width / badge::height, badge::color() (false
on the mono panels) and badge::epaper() (true on Badger).
Fonts and images live in assets/ and are embedded as C arrays at build
time (badge_embed_asset() -> cmake/bin2c.cmake); nothing is read from a
filesystem at runtime. The runtime embeds its fonts - the Sins .ppf pixel
font (text()) and DynaPuff .af vector font (text_v()). Demos embed their
own assets, e.g. the sprite demos decode skull.png via load_png() (PNGDEC).
The framebuffer is RGBA8888 in SRAM (PicoVector's native format); each board's
present() converts it to what the panel needs (RGB565 for the ST7789, a
brightness map for the mono LED matrix / e-ink).
- Displays - Tufty: 8-bit parallel (8080) ST7789 via PIO+DMA, landscape framebuffer transposed into the panel's portrait GRAM. Blinky: 39x26 LED matrix refreshed continuously by a PIO+DMA BCD bitstream. Badger: 264x176 SSD1680 e-ink over SPI, full-refresh (blocking, ~1s).
- Power rail - the peripherals sit on a switched rail enabled via
BW_SW_POWER_EN, brought up inboard::init(). - PSRAM -
psram_init()maps the APS6404 at0x11000000(available for large/static data); the rendered framebuffer stays in SRAM for bandwidth. - Buttons - A/B/C/Up/Down/Home, debounced, with
pressed/held/released. - RTC - PCF85063 on I2C;
rtc_get_datetime()/rtc_set_datetime(). - Power off - the power button is wired to RESET;
powman_boot_check()(run at startup) detects a held button and powers the badge down (sleep, or shipping mode with Up+Down held).powman_sleep()and friends are also callable directly.
screen (the image_t framebuffer) · width / height · color() /
epaper() · ticks() · pen(colour|brush) · text / text_v · custom()
and the PicoVector shape helpers (circle, star, rounded_rectangle, ...) ·
rnd / frnd · load_png · buttons::* · rtc_* / powman_*.