The Raspberry Pico Badger is a consumer product by the Raspberry Pi company Pimorino. Advertised as a portable badge with a 2.9 inch e-ink display and a Pico W, its a portable, programmable microcomputer. This article investigates this unique product from its hardware side as well as the software, apptly named Badger OS.
The technical context of this article is MicroPython v1.21
and Badger OS v0.04
. All examples should work with newer releases too.
This article originally appeared at my blog admantium.com.
Raspberry Pico Badger
The Pico Badger consumer product, advertised and offered for sale on pimoroni.com is a complete portable mini computer with these components:
- 2.9inch e-ink display with 296x128 pixels
- 5 onboard buttons
- JST-PH battery connector (e.g. for LiPo, max 5.5V)
- Qwiic/STEMMA QT connector
- Raspberry Pico W (133Mhz Dual Arm Cortex M0+, 264kB SRAM, 2MB Flash RAM, BLE and WiFi)
All components are soldered, the board is robust and a decent amount of varnish protects all surfaces. It can be powered with the battery connector, or the Micro-USB port of the Raspberry Pico W.
This is the frontside:
And this is the backside:
Badger OS
The Pico Badger comes with a preinstalled software called Bader OS. This software is completely written in Micropython, and its code can be accessed on github.com. When powered up, it boots into a main menu (or the last screen that was shown). By simultaneously pressing the buttons A and C, the main menu is shown, and the following applications are accessible:
- Badge: Show a customizable badge with different text fields and a rendered image
- Clock: A real-time clock that synchronizes via Wi-Fi
- eBook: A full-fledge eBook reader with a preinstalled project Gutenberg version of "The Wind in the Willows"
- Fonts: Showcasing how different fonts can be rendered on the screen
- Help: Manual showing how to navigate via the onboard buttons
- Image: Renders a provided image file
- Info: System and hardware information
- List: A todo app displaying items and enabling to mark them as done
- Net Info: Show connectivity information about the Wi-Fi connection
- News: An RSS reader showing headlines from BBC
- QRGen: A QR code generator
- Weather: Using your Wi-Fi hotspot geo information, show the current temperature, wind and rain status
Configuring Badger OS
All Badger OS functions and configuration are stored as Python and configuration files on the board itself. Like a normal Raspberry Pico, you need to keep pressing the bootsel button, then connect the device to a computer, and its internal file system becomes mountable as a USB drive.
The recommended way to program MicroPython on the Raspberry Pico is to use the Thonny IDE. Accessing the Badger reveals the following file structure:
.
├── WIFI_CONFIG.py
├── badges
│ ├── badge.jpg
│ └── badge.txt
├── books
│ └── 289-0-wind-in-the-willows-abridged.txt
├── checklist.txt
├── examples
│ ├── badge.py
│ ├── clock.py
│ ├── ebook.py
│ ├── fonts.py
│ ├── help.py
│ ├── icon-badge.jpg
│ ├── icon-clock.jpg
│ ├── icon-ebook.jpg
│ ├── icon-fonts.jpg
│ ├── icon-help.jpg
│ ├── icon-image.jpg
│ ├── icon-info.jpg
│ ├── icon-list.jpg
│ ├── icon-net-info.jpg
│ ├── icon-news.jpg
│ ├── icon-qrgen.jpg
│ ├── icon-weather.jpg
│ ├── image.py
│ ├── info.py
│ ├── list.py
│ ├── net_info.py
│ ├── news.py
│ ├── qrgen.py
│ └── weather.py
├── icon-cloud.jpg
├── icon-rain.jpg
├── icon-snow.jpg
├── icon-storm.jpg
├── icon-sun.jpg
├── icons
├── images
│ └── badgerpunk.jpg
├── launcher.py
├── main.py
└── state
├── ebook.json
├── image.json
├── launcher.json
├── list.json
└── news.json
6 directories, 42 files
The first thing that you will want to do is to configure access to a Wi-Fi hotspot:
- Open the file
WIFI_CONFIG.py
- Enter the Wi-Fi Credentials
- Reboot and try the builtin RSS reader
If you see the log message Connecting...
, followed by Connected!
, the Badger is ready to access any website and other internet resources.
Exploring Badger OS: Main Program
Note: all of the following source code stems from the Github repository pimoroni/badger2040
Lets investigate the Badger OS main.py
program to understand how to add a new application.
First, several modules are imported, and global variables defined, for example for the display.
import gc
import os
import time
import math
import badger2040
import badger_os
import jpegdec
APP_DIR = "/examples"
FONT_SIZE = 2
display = badger2040.Badger2040()
display.set_font("bitmap8")
display.led(128)
jpeg = jpegdec.JPEG(display.display)
Next, the available applications are determined by crawling the ./examples
directory for every Python file.
examples = [x[:-3] for x in os.listdir("/examples") if x.endswith(".py")]
Two methods for drawing the on-screen menu bars follow:
def draw_disk_usage(x):
def render():
display.set_pen(15)
display.clear()
display.set_pen(0)
max_icons = min(3, len(examples[(state["page"] * 3):]))
...
Halfway through the example, we find the method that actually starts an app.
def launch_example(index):
wait_for_user_to_release_buttons()
file = examples[(state["page"] * 3) + index]
file = f"{APP_DIR}/{file}"
for k in locals().keys():
if k not in ("gc", "file", "badger_os"):
del locals()[k]
gc.collect()
badger_os.launch(file)
Lets note this down: badger_os.launch(file)
. Continuing the main program to its end, several more methods define how to interpret pressed buttons:
def button(pin):
global changed
changed = True
if pin == badger2040.BUTTON_A:
launch_example(0)
if pin == badger2040.BUTTON_B:
launch_example(1)
if pin == badger2040.BUTTON_C:
launch_example(2)
if pin == badger2040.BUTTON_UP:
if state["page"] > 0:
state["page"] -= 1
render()
if pin == badger2040.BUTTON_DOWN:
if state["page"] < MAX_PAGE - 1:
state["page"] += 1
render()
And finally, a continuous running while
loop that constantly monitors for pressed buttons:
while True:
# Sometimes a button press or hold will keep the system
# powered *through* HALT, so latch the power back on.
display.keepalive()
if display.pressed(badger2040.BUTTON_A):
button(badger2040.BUTTON_A)
if display.pressed(badger2040.BUTTON_B):
button(badger2040.BUTTON_B)
if display.pressed(badger2040.BUTTON_C):
button(badger2040.BUTTON_C)
if display.pressed(badger2040.BUTTON_UP):
button(badger2040.BUTTON_UP)
if display.pressed(badger2040.BUTTON_DOWN):
button(badger2040.BUTTON_DOWN)
if changed:
badger_os.state_save("launcher", state)
changed = False
display.halt()
Now, lets check how an app is started, and how an app file actually looks like.
Exploring Badger OS: Badger OS Module an App File
Interestingly, the imported badger_os
module is not part of the device files, but included in the Micropython distribution that is flashed onto the device. This module, and the badger2040
and network_manager.py
can be found in projects GitHub repository at path main/firmware/PIMORONI_BADGER2040W/lib.
The aforementioned badger_os.launch()
function is this:
# Source: https://github.com/pimoroni/badger2040/blob/main/firmware/PIMORONI_BADGER2040W/lib/badger_os.py
def launch(file):
state_set_running(file)
gc.collect()
button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN)
button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN)
def quit_to_launcher(pin):
if button_a.value() and button_c.value():
machine.reset()
button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher)
try:
__import__(file)
except ImportError:
# If the app doesn't exist, notify the user
warning(None, f"Could not launch: {file}")
time.sleep(4.0)
except Exception as e:
# If the app throws an error, catch it and display!
print(e)
warning(None, str(e))
time.sleep(4.0)
# If the app exits or errors, do not relaunch!
state_clear_running()
machine.reset() # Exit back to launcher
As you see, it will run a garbage collection routing to cleanup RAM resources, then define buttons, and simply imports
the launched app file.
So, what inside this? Lets check the source code of the info
page.
import badger2040
from badger2040 import WIDTH
TEXT_SIZE = 1
LINE_HEIGHT = 15
display = badger2040.Badger2040()
display.led(128)
# Clear to white
display.set_pen(15)
display.clear()
display.set_font("bitmap8")
display.set_pen(0)
display.rectangle(0, 0, WIDTH, 16)
display.set_pen(15)
display.text("badgerOS", 3, 4, WIDTH, 1)
display.text("info", WIDTH - display.measure_text("help", 0.4) - 4, 4, WIDTH, 1)
display.set_pen(0)
y = 16 + int(LINE_HEIGHT / 2)
display.text("Made by Pimoroni, powered by MicroPython", 5, y, WIDTH, TEXT_SIZE)
y += LINE_HEIGHT
display.text("Dual-core RP2040, 133MHz, 264KB RAM", 5, y, WIDTH, TEXT_SIZE)
y += LINE_HEIGHT
display.text("2MB Flash (1MB OS, 1MB Storage)", 5, y, WIDTH, TEXT_SIZE)
y += LINE_HEIGHT
display.text("296x128 pixel Black/White e-Ink", 5, y, WIDTH, TEXT_SIZE)
y += LINE_HEIGHT
y += LINE_HEIGHT
display.text("For more info:", 5, y, WIDTH, TEXT_SIZE)
y += LINE_HEIGHT
display.text("https://pimoroni.com/badger2040", 5, y, WIDTH, TEXT_SIZE)
display.update()
# Call halt in a loop, on battery this switches off power.
# On USB, the app will exit when A+C is pressed because the launcher picks that up.
while True:
display.keepalive()
display.halt()
This source code contains instructions how to draw shapes and texts on the screen. It starts by importing the rp20240
library, then activates the onboard led with display.led()
, and clears the screen with display.clear()
. This is followed by a series of statements using display.pen()
, a method that defines a new origin, and display.rectangle()
as well as display.text()
to fill the screen. Finally, the screen is drawn on with display.update()
and kept in this state forever with display.update()
. Since this app is purely static, no additional code regarding button interactions is defined.
So, in essence an app files accesses help classes from rp2040
to render text on the screen and, if required, define the behavior of the onboard buttons.
Other Use Cases
While Badger OS is the official MicroPython based library, it is not the only option you have. To give you some ideas what else can be accomplished:
- You can install a custom port of CircuitPython
- The BadOS project is a custom OS with Circuit Python, its GitHub repository shows how to develop different kind of apps too
- There is a programming course how to create a Tic-Tac-Toe computer game showing the same capabilities as the official software
Conclusion
The Badger Pico W is a unique product. With its 2.9 inch e-ink display and access to Wi-Fi and Bluetooth, you can program stateless application that remain shown even when no power is provided. The aptly named Badger OS is a complete open-source MicroPython project that shows examples for all necessary interactions: Navigating menus by pressing a button, drawing text and images, connect to Wi-Fi, make HTTP request, parse the results and show them. You just need to add portable batteries and have a credit-card sized wearable computer. This board can be used for many projects, for example to build a small e-book reader, a portable RSS reader, a badge.
This article started to explore the Badger OS source code, which is written entirely in MicroPython. You learned that the programs main method imports required libraries, setups method for handling the buttons, defines a state and then display all installed apps. And these apps are single MicroPython files as well that access the builtin library rp2040
to draw text and shapes on the screen as well as to define button behavior.
Following this understanding, the next article will investigate how to program a custom app.