The BornHack 2026 badge is the Cyber Ægg. It is an egg-shaped, low-power hacker badge, inspired by the Tamagotchi of the 1990s. One battery charge runs the badge for the full BornHack camp. A long-range LoRa mesh keeps you connected to everyone else on the field.
Under the playful shell is a serious radio computer. A Nordic nRF52840 drives a 1.54 inch black, red and white e-paper display. The badge talks Bluetooth Low Energy to your phone, and it emulates an NFC tag on its back. A dedicated SX1262 LoRa radio connects the badge to the wider MeshCore network. Between messages, BornPets entertains you. BornPets is a virtual pet with a set of mini-games.
Front (display and buttons) and back (nRF52840, USB-C connectors, NFC coil).
Expansion connector pinout
The board has an I²C expansion connector of the QWIIC type. We made a design mistake: the 3.3 V and the GND signals are swapped. Before you connect a QWIIC peripheral, make a corrected cable and use that.
Features
Egg-shaped badge, inspired by the classic Tamagotchi
Nordic nRF52840 microcontroller (BLE, USB and NFC)
1.54 inch 152 × 152 black, red and white e-paper display
SX1262 LoRa radio, part of the MeshCore mesh network
Bluetooth Low Energy companion connection to the MeshCore app
NFC tag on the back, for location games and station taps
5-way joystick, Select, Execute and Cancel buttons, RGB LED and a piezo buzzer
USB-C for charging and for file transfer with drag and drop
Start with the Getting started guide. The Games page describes the virtual pet. The Hardware page tells you what is inside the badge.
Update your badge from the browser
The Flash page installs the firmware and the badge’s asset files from a Chromium-family browser over USB. You need no toolchain and no command line. The page offers the standard firmware and the Community Edition, a fork with more BornPets mechanics.
Source code
The Cyber Ægg is open source. The hardware design and the firmware are on Codeberg:
Bad Apple!! — annejan/aegg-apple, the animation on the e-paper panel with the tune on the buzzer
Hardware sponsors
Nordic Semiconductor sponsored their low power yet very capable and fast NRF52840 microcontroller with Bluetooth Low Energy and NFC, making it possible for us to build a device that runs on one battery charge, the whole camp long!
ALLNET China is our production partner, they take care of sourcing most components and oversee the production process in China, saving us a lot of work and potential headaches and allowing us to focus on the product!
Procolix sponsored the SX1262 LoRa radio chips, converting the badge into a capable LoRa communications device. Check out their managed hosting solutions for a truly sovereign cloud built on European open source solutions!
deFEEST sponsored part of the badge hardware, helping us get the components we needed to build it. Find out more at defeest.nl!
Mollerup Automation sponsored the 3D printed housing for the badge. They are automation, robotics and PLC specialists from Odense, Denmark — see mollerup.info!
1 - Getting started
This guide takes you from the unboxing of the BornHack 2026 Cyber Ægg badge to the first use. It takes a few minutes.
First power-on
A new badge runs a factory self-test on the first start. The badge shows a FACTORY TEST screen with a small PASS/FAIL grid, and then ALL PASS. After that, the badge starts the application directly at every start. The self-test does not run again, unless you erase the firmware.
The first start then plays a sponsor slideshow. It shows the event logos and then the badge-sponsor logos, a few seconds each. The slideshow plays only once. To see it again, select Main → Badge sponsors.
The LED sequence at every start is:
Pulsing orange — the hardware initializes
Pulsing blue — the display and the LoRa radio start (about 13 seconds)
One green flash — the badge is ready
The badge then shows the Main screen.
Controls
The badge has a 5-way joystick on the left and two thumb buttons on the right:
Control
Action
Execute / joystick press (Fire)
Select or activate
Cancel
Go back or dismiss
Up / Down
Move the cursor in the current screen
Left / Right
Go to the next top-level screen
Top-level screens
The interface is a carousel. Left and Right move through the top-level screens:
Screen
What it is
Game
BornPets — the virtual pet, the mini-games and the hatchery
Main
Root menu: Bornagotchi · Settings · About · Badge sponsors
PMs
Inbox for private mesh messages
Channel
Group or room mesh chat
Adverts
Mesh adverts the badge heard recently
Tokens
The NFC tokens you collected
Clock
Digital or analog watch face, and the alarm
Calendar
Month grid and the timeline for each day
Name
Large conference-badge name view
My QR
Your mesh identity as a QR code, to share with other badges
Pair with the MeshCore app
The badge speaks the MeshCore companion protocol over Bluetooth Low Energy. Install the MeshCore app on Android or iOS. You can also open https://app.meshcore.nz/ in a browser with Web Bluetooth, such as Chrome or Edge on desktop or on Android.
Make sure that Bluetooth is on. Main → Settings → Bluetooth must show BLE: ON.
Scan for devices in the app. The badge advertises as Cyber Ægg XXYY. XXYY is four hex characters, unique to your badge.
The phone shows a passkey prompt, and the badge shows a 6-digit passkey on its display. Type that number into the phone.
After the bond, the app can set the clock, manage contacts, send and receive mesh messages, and change the LoRa preset.
Set the time
The badge has no real-time clock with a backup battery. The clock therefore returns to “not set” at every start. You can set it in two ways:
With the MeshCore app. Connect over Bluetooth, and the app sends the time of your phone to the badge.
Near a synchronized repeater. A known-good mesh repeater advertises its time regularly, and your badge takes it automatically.
Set your timezone once, under Main → Settings → Timezone. The badge keeps that setting.
Charging
Connect any USB-C cable to the badge to charge it. The battery icon on the screen shows the charge state. There is no separate charge LED.
Two effects look like faults, but they are correct. If the charge symbol disappears while USB stays connected, the charge is complete. The symbol comes back when the cell drains. The battery icon can also be up to a minute behind, because the badge measures the battery only every 60 seconds.
USB drag-and-drop
When you connect USB-C, the badge appears as a small drive with the name CYBR<4 hex>. You can put these files in its root:
File
Effect
ALARMS.ICS
iCalendar file — imports alarms and calendar events
030000.PCX … 030009.PCX
Sponsor slides for the splash carousel
<6 hex>.PCX
Game sprites
PETS.CFG
Adds or renames pets, with their sprite PCX files — see Games
BORNPETS.CFG
Replaces the BornPets game balance
LUT.CFG
Custom e-paper waveform (advanced — a calibrated display LUT)
After you copy files, restart the badge with a new connection of the USB cable. The changes then take effect.
LUT.CFG is an advanced change. It replaces the built-in display waveform of the panel with a calibrated one, for example for a faster refresh. If a custom LUT gives a bad image, hold Fire (the joystick press) during the start. The badge then uses the safe built-in waveform for that start. Delete the file, or correct it. The badge rejects a damaged LUT.CFG, or one for a different panel, automatically.
Firmware update
The Flash page is the easiest method. It writes the firmware and the badge’s asset files from a Chromium-family browser. You install no toolchain.
To do it by hand, enter the bootloader (DFU) mode first. Slide the ON/OFF switch at the top left of the front off, then back on, while you hold Execute. The LED then blinks red. The battery keeps the badge running, so a disconnection of USB does not restart it. You can then write a new firmware image with dfu-util:
dfu-util -d 1915:521f -D cyber-aegg.bin
The firmware is open source, and we build it with Rust and Embassy. See Ranzbak/bornhack-firmware-2026 for the source, the build instructions and prebuilt images.
2 - Quick reference
This is a one-page cheat sheet for the Cyber Ægg. Print it and put it under the strap.
Game → Main → PMs → Channel → Adverts → Tokens → Clock → Calendar → Name → My QR
LED meanings
Color
Meaning
Pulsing orange
Start — the hardware initializes
Pulsing blue
The display and LoRa start (about 13 s)
One green flash
The start is complete
Red flicker
The screen refreshes
Blue flicker
The badge writes to the USB drive
Blinking green
The badge erases the contacts
One red, green or blue flash
A person pinged you over the mesh (blinkme)
Power-on combos
Hold the button while you start the badge. Slide the ON/OFF switch at the top left of the front off, then back on. The battery keeps the badge running, so a disconnection of USB does not restart it.
Hold
Result
Execute
USB firmware update (DFU mode)
Fire (joystick press)
Forces the safe e-paper waveform, and ignores a bad LUT.CFG for that start
Execute + Cancel + Fire
Factory reset (about 40 s — erases the data and the settings)
If the application slot is empty, the badge enters DFU mode without a button.
USB drag-and-drop
Connect USB-C. The badge mounts as the CYBR<4 hex> drive.
File you put on the drive
What it does
ALARMS.ICS
Imports alarms and calendar events
030000.PCX … 030009.PCX
Sponsor slides
<6 hex>.PCX
Game sprite asset
PETS.CFG
Adds or renames pets, with sprite PCX files
BORNPETS.CFG
Custom pet balance (KEY=VALUE)
LUT.CFG
Custom e-paper waveform (advanced)
Restart the badge after you copy the files.
Firmware update (DFU)
dfu-util -d 1915:521f -D cyber-aegg.bin
The bootloader LEDs in DFU mode are: a red blink for idle, solid blue during the write, solid green when the write is complete. Then power cycle the badge.
Charging
USB-C in any port charges the badge. There is no separate charge LED. The battery icon on the watch face and in the status bar shows the level.
3 - Mesh & messaging
The badge has a LoRa SX1262 radio, and it speaks the MeshCore mesh protocol. Other badges, MeshCore phones and separate repeaters all appear as peers. Four carousel screens use the mesh: PMs, Channel, Adverts and My QR. The Contacts list holds the peers behind them.
Getting on the same network
Three things must be the same on every badge in the local mesh:
LoRa preset. This is the frequency, the bandwidth and the spreading factor. The default preset is BornHack 2026, and the firmware includes it. Change it under Main → Settings → LoRa Radio.
Public channel key. The preset shares this key automatically.
Antenna. Make sure that the LoRa antenna is connected.
If you see no adverts from other badges after a minute, check the preset first.
Adverts
Every badge, phone and repeater on the mesh broadcasts an advert at regular intervals. The advert holds the public name, the identity hash and the capabilities. Your badge lists the adverts it receives on the Adverts screen.
Key
Action
Up / Down
Move through the advert list
Execute / Fire
Saves the selected advert as a contact
Cancel
Go back
Left / Right
Go to the next carousel screen
Private messages (PMs)
The PMs screen is your private inbox. Each row is a peer that sent you a message.
Marker
Meaning
●
Heard less than 5 minutes ago
*
Favorite
+
Discovered, but not saved as a contact
R
Repeater
#
Room or channel server
S
Sensor
Use Up and Down to move through the list. Use Execute / Fire to open a thread or to start a reply. Use Cancel to go back. A reply uses the keyboard on the screen. Move the joystick to select a character, press Execute to accept it, and press Cancel to delete the last character. About 70 emoji are available.
RAM-only
The inbox holds up to 32 messages from 16 peers, in RAM. The badge keeps saved contacts and their threads through a restart. Messages from peers you did not save disappear when the badge restarts. Save every peer you want to keep.
Channels (group chat)
The Channel screen is the group chat, or room chat. It uses the same protocol with a broadcast scope. Each row is a channel, for example the default Public channel in the preset. The controls are the same as on the PMs screen. Every badge with the same preset hears every message in a public channel.
My QR
The My QR screen shows your mesh identity as a QR code. Show the code to another MeshCore phone or badge for an immediate pairing. The other device does not have to wait for an advert.
Contacts
The Contacts list shows every peer the badge heard or knows: nearby strangers, saved friends, repeaters and rooms. Open it under Main → Bornagotchi → Contacts, or from the Adverts screen.
Key
Action
Up / Down
Move through the list
Up on the top row
Opens the filter: All, Favorites, People, Repeaters, Rooms or Sensors
Execute / Fire
Opens a popup: PM · Info · Add · Save / Unsave · Forget
Cancel
Go back
The popup actions are:
PM. Opens the message thread.
Info. Shows the hex identity prefix, the last-heard time and the advert capabilities.
Add / Save. Writes the contact to flash, so it survives a restart.
Unsave. Removes the contact from flash. It stays in the discovery cache until the next restart.
Forget. Removes the contact immediately, also from the discovery cache.
Save what you want to keep
The discovery cache holds up to 32 unsaved peers in RAM. It is empty after every restart, until new adverts arrive. A peer you did not Save is gone after a restart, together with its message history. When the cache is full, the badge removes the oldest unsaved entry for a new advert.
Pinging & visibility
When another badge pings you with the mesh blinkme command, your LED flashes shortly in the requested color. This helps you find friends in a crowd. Your badge also sends its own adverts, so other people can see you.
Battery note
The LoRa radio uses the most battery power. To save power, mute the notification sounds under Main → Settings → MeshCore. The e-paper display uses no power after it shows an image.
Using your phone instead
You can also chat from your phone. Install MeshCore on Android or iOS, or open https://app.meshcore.nz/. Then pair the phone over Bluetooth. See Getting started. After the bond, the app gives you the contacts, the chat and the settings.
4 - NFC & tokens
The back of the badge has an NFC antenna. Touch a phone or a station reader against it to interact. You do nothing on the badge, because it is always ready.
Two things happen on a tap
A phone reads your broadcast profile
Any standard NFC reader, such as the built-in reader of Android or iOS, sees your broadcast profile. The default profile is the badge’s own documentation page, https://badge.team/docs/badges/bornhack-2026/. You can replace it with your own vanity URL, a vCard or another record. See Set your own broadcast data. A tap with the reader of the operating system only reads the profile. It is harmless.
BadgeCtl runs a station command
A phone with the BadgeCtl companion app and the correct event key sends signed commands to the badge. The event stations use these commands, and they improve the statistics of your BornPet:
Command
Effect on your BornPet
more food
Sets hunger to 0
more drugs
Sets sick to 0
more inspiration
Sets drained to 0
sleep like a bear
Sets tired to 0
A short message on the badge confirms the result. Each command has a cooldown of 5 minutes. A second tap in that period does nothing.
Station commands need an active game. Select a pet first. The egg countdown counts as an active game. If your pet left, start a new egg. The badge ignores a tap when no pet is active.
Tokens
Tokens from station taps and from other badges go to the Tokens screen in the carousel. The badge collects many tokens and keeps them until the next start. The list is a record of the stations and the badges you tapped during the camp.
When a person pushes a token: to your badge, the badge shows it for about 10 seconds. The badge then broadcasts your own profile again. A pushed token cannot replace your profile.
Set your own broadcast data
The default documentation URL is not permanent. The badge can broadcast any record you like. Use an NFC writer app on your phone, such as NFC Tools, and write to the back of the badge:
Vanity URL. Write a URL or URI record, for example annejan.com. A Text record set:https://your.link also works, for writer apps that write text only.
vCard. Write a Contact or vCard record. Phones that tap your badge then get your contact card.
Wi-Fi, or any other record. The badge serves it without a change.
The rule is simple. The badge keeps what you write, and the record survives a restart. The one exception is a token: record, which goes to your Tokens screen instead. Keep the record short. The limit is about 127 bytes, which is enough for a URL or a small vCard. The badge also cuts a long URL in the set: text form to about 118 characters after the scheme. For a longer URL, write a plain URI record, or use a link shortener.
NFC has no erase function that returns the factory default. An empty write, or the “format tag” function of a writer app, does not restore the built-in documentation URL. It keeps your current profile, or it stores the empty record. To change the broadcast, write the new record over the old one.
This function is unauthenticated. Any person who touches your badge with a writer app can change the record. The badge is in your pocket, so control physical access to it.
What the reader side needs
The badge is always ready. The reader needs two things:
The BadgeCtl app.
The correct Ed25519 private key in the app. BornHack staff hold this key for the official stations.
Other NFC reader apps cannot send these commands, because they do not have the key. They see only the public URL.
Running your own station
You can run your own station. Build the badge firmware again with your own Ed25519 public key. Then sign the commands with your private key in your reader app. The protocol specification, the wire format and a signing recipe in Kotlin, Python and Rust are in the firmware’s NFC_README.md.
How it works (hardware)
The nRF52840 includes an NFC tag PHY. It drives a coil on the PCB of about 2.8 µH, tuned with capacitors to 13.56 MHz. The PHY supports tag mode only, not reader mode. See the Hardware page.
5 - Clock, alarm & calendar
Three apps use the watch slots of the carousel: Clock, Alarm and Calendar. You open the Alarm from the Clock.
Clock
The Clock has two watch faces, digital and analog. A small bell icon in the header appears when an alarm is on.
To open it: press Left or Right until the badge shows the Clock screen.
Key
Action
Up / Down
Changes between the digital face and the analog face
Execute / Fire
Opens the alarm editor (slot 0)
Left / Right
Goes to the next or the previous carousel screen
Setting the time
The badge has no backup battery for its real-time clock. The clock therefore returns to None at every start, and it shows “Clock not set” until you set it. You can set it in two ways:
With the MeshCore app over Bluetooth. The phone sends its time. This is the easy method.
With a mesh time advert. Stay near a synchronized LoRa repeater, and the badge takes the time over the air. The badge accepts a time over the air only from a trusted source. That is a repeater or companion advert with a verified signature, or a channel that you hold the key for. Other badges near you cannot set your clock.
Set the timezone once, under Main → Settings → Timezone. The badge keeps that setting through a restart. The default is +2, which is CEST for BornHack.
No seconds hand
A time from BLE overrides the refinement over the air until the next restart. The watch face has no seconds hand, because the e-paper refresh is too slow for one.
Alarm
Press Execute / Fire on the Clock screen to open the alarm editor.
Key
Action
Up / Down
Moves between the fields: Hour, Minute, Days, Tone, Enabled
Execute / Fire
Enters or leaves the edit mode of a field
Cancel
Returns to the watch face
The Days field steps through Daily, Weekdays, Weekends, None and Custom. The Tone field has ten built-in tunes: Beep, Imperial March, Rickroll, Pink Panther, Sandstorm, Startup, Trololo, Daisy Bell, Nokia and Samsung.
At the alarm time, the buzzer plays the selected tone up to five times, with 8 seconds between them. Any button press stops the alarm. If you do nothing, the alarm stops after about 32 seconds.
Set the clock first
An alarm sounds only when the clock is set. After a restart, pair the badge or wait for a time advert. Until then, the alarm does not sound.
Calendar
The Calendar is a month grid with a timeline for each day. It shows the iCalendar events you imported.
To open it: press Left or Right to the Calendar screen, to the right of the Clock. The grid appears without a cursor. Press Execute / Fire to enter the active mode.
Active mode:
Key
Action
Up / Down
Moves the cursor 7 days, one week
Left / Right
Moves the cursor 1 day
Execute / Fire
Opens the timeline of the day
Cancel
Returns to the passive view
Day detail (timeline):
Key
Action
Up / Down
Moves 1 hour
Left / Right
Moves a long event title horizontally
Execute / Fire
Shows the full day list, with all events
Cancel
Returns to the month view
Loading events
The badge imports events at the start, from a file with the name ALARMS.ICS in the root of the USB drive:
Connect the USB-C cable to your computer.
Open the drive with the name CYBR<4 hex>.
Copy your .ics file to the root, with the name ALARMS.ICS.
Eject the drive.
Restart the badge. Slide the ON/OFF switch at the top left of the front off, then back on.
The badge stores up to 31 events. It cuts an event that covers more than one day. The event then ends at 23:59 on the first day, because the e-paper view draws no event over more than one day. All events are in RAM only, and the badge imports them again from ALARMS.ICS at every start.
Import limits & quirks
The parser is minimal on purpose. If events are absent or wrong, one of these limits is usually the cause:
File size: 16 KiB maximum. The parser cuts the rest of the file, in the middle of an event, without a message. A full conference program is larger than this. Cut it first with the firmware’s scripts/strip_ics.py. That script removes DESCRIPTION, UID and similar fields, and it accepts --from, --to and --max to select a range.
31 events maximum. The import stops at the limit without a message. Later events in the file never appear.
No recurrence. The parser ignores RRULE. A repeated event imports as its first occurrence only. Export an expanded ICS file with one entry for each occurrence. The BornHack program is already expanded.
No all-day events. The parser drops a DTSTART with a date and no time, without a message. Give the event a real start time.
ASCII only. The parser removes non-ASCII characters from titles. It does not transliterate them, so Æ, accented letters and emoji disappear.
Timezones. The parser shifts only timestamps with the Z suffix, which are UTC, to local time. It always uses the built-in default of UTC+2, which is correct for BornHack, because the import runs before the badge applies your saved timezone. The parser takes floating times and TZID= times without a change. Export in UTC if you are not sure.
An event that fired disappears from the Calendar until the next start. Imported events are single-shot alarms. After one fires, the grid and the day view no longer show it. A restart imports everything again.
Changes take effect at the start only. Replace ALARMS.ICS, eject the drive correctly, then power cycle the badge.
6 - Games
The Game screen runs BornPets, a virtual pet in the style of the Tamagotchi of the 1990s. It also holds a set of mini-games. You start the mini-games from the pet’s Play menu.
BornPets
Hatch a snail or a cat. Then keep the pet fed, healthy, rested and entertained.
Hatching
The first time you open the Game screen, the badge shows the hatchery. Press Execute / Fire to start. Select a pet from the roster. The built-in pets are Bartholomeus, Cat and Slug. The egg then hatches in about one minute. After the hatching, give your pet a name of up to 12 characters, with the keyboard on the screen. The badge keeps the pet through a restart. You can rename the roster, and you can add your own pets. See Custom pet roster below.
Stats
Your pet has stats that increase with time. A high value is bad for the pet. Watch these stats:
Stat
Fix with
Notes
Hunger
Feed
The pet gets hungry, and the other stats get worse
Tired
Rest or sleep
Use Hibernate for a long sleep
Drained
Play, or a mini-game
The pet has no inspiration. Play also clears “miserable”
Sick
Heal
Use this when the sick icon appears
Miserable
Play
The other stats then get worse faster, so correct this early
The stats interact. When several stats are bad, the pet becomes miserable faster, and a miserable pet makes every other stat worse. Stay ahead of this spiral.
Controls
Key
Action
Up / Down
Changes between the top row (the actions) and the bottom icon row
Left / Right
Moves along the current row
Execute / Fire
Activates the selected icon
Cancel
Goes back
Hibernate
Before you store the badge for more than a few hours, open the action menu and select Hibernate. The stats then freeze until you wake the pet. If you store the badge without hibernation, the stats continue to decay, and the pet can starve before you find the badge again.
Game modes
There are two difficulty settings, under Main → Bornagotchi → Mode:
Classic. The original balance of the badge.
Casual. About half the decay speed, and more relief for each action. Use this if you do not want to care for the pet often.
The badge keeps the setting in flash. A * next to the mode name means that the change waits. Restart the badge to apply it.
Turning the pet off
You can remove the pet. Main → Bornagotchi → Disable Game hides the whole Game screen from the carousel. The badge keeps this setting through a restart, and the label changes to Enable Game. While the game is off, an NFC station tap can no longer open the pet. Select Enable Game to get the pet screen back.
Mini-games
Open the Play menu in the bottom row of BornPets and select a game. Each win lowers the drained stat, and it does not raise hunger. The games are therefore free entertainment. Cancel always leaves a mini-game.
Game
Goal
Tic-Tac-Toe
Draw against the computer, or win (Normal or Impossible difficulty)
Lights Out
Switch a 5×5 grid until every light is off
Nim
Force the computer to take the last stick
Maze
Reach any border exit of an 18×18 maze
Black Hole
Beat the sum of the AI on a pyramid of 21 cells
Triple Born
A merge game in the style of Triple Town, on a 6×6 board
BornJeweled
An accessible match-3 game with a limit of 30 moves
In a game, the joystick moves the cursor. Execute / Fire places or selects. Cancel returns to the Play menu.
Make your own pet
You can build a companion that is not a snail or a cat. The CyberÆgg Pet Maker is a sprite and animation editor for BornPets, and it runs in the browser:
Start from a preset (Bartholomeus, Cat or Slug), or from a Blank canvas.
Draw each animation frame with the badge’s palette: black, red, white and transparent. White is the e-paper background, and transparent means that the badge draws nothing.
Preview the animation states, for example Idle, with controls for the speed and the onion skin.
Change the game balance (BORNPETS.CFG), which sets how fast the stats decay and recover.
Select Download ZIP to get all the files for the badge.
Installing a custom pet
The badge shows a USB drive when you connect USB-C. See Getting started. Unpack the export of the Pet Maker. Copy the sprite files to the root of the CYBR<4 hex> drive, together with PETS.CFG and, if you made one, BORNPETS.CFG. Then restart the badge.
Hand-editing sprites (GIMP etc.)
The badge accepts one specific PCX format: 2 bits per pixel, one plane, RLE. Editors such as GIMP export a PCX with 16 or 256 colors instead, and the badge skips those files without a message. The firmware’s scripts/ folder has helpers for this problem:
fix_badge_pcx.py writes a PCX with the wrong depth, for example a 4 bpp or 8 bpp GIMP export, back to the badge’s 2 bpp format. It keeps the dimensions.
check_badge_pcx.py checks a file before you copy it. You then find a bad export on your computer, not on the badge.
Custom pet roster: PETS.CFG
PETS.CFG is the pet roster in the “Choose your Pet” screen. It is a plain-text file with one PREFIX=NAME on each line. You can edit it without a new firmware:
# --- current pets (rename if you like) ---
0=Bartholomeus
1=Cat
2=Slug
# --- add your own (needs 05xxxx.PCX / 06xxxx.PCX sprites) ---
5=Dragon
6=Ghost
PREFIX is the sprite-prefix byte of the pet, in decimal:
0, 1, 2 are the built-in pets. A line for one of these renames it.
3, 4 are reserved for sponsors and menu icons, and the badge ignores them.
5 to 7 are your own pets.
NAME is up to 16 ASCII characters.
The sprites of a pet are the PPAAFF.PCX files on the badge. PP is the prefix, AA is the animation part, and FF is the frame. The firmware counts the PCX files that are present, for example 050100 to 050104 for five idle frames. There is therefore no fixed frame count and no header to maintain. Export the sprites at the prefix of the pet from the Pet Maker, and copy them to the drive next to PETS.CFG. The firmware ignores lines that start with #, and reserved or damaged lines.
Custom balance: BORNPETS.CFG
To change only the difficulty, and not the sprites, copy a plain-text BORNPETS.CFG file to the root of the badge’s USB drive. Write one KEY=VALUE on each line:
# speed up hunger decay, slow down the drained stat
HUNGER_RATE=4
DRAINED_INTERVAL=180
Eject the drive and restart the badge. While a configuration is active, a small * appears after the name of the pet. To return to a preset, delete the file and restart the badge. The Pet Maker can write this file for you. The firmware’s USER_GAMES.md documents every key and its range.
Three points are important:
Changes take effect at the start only. Eject the drive correctly, so that the computer writes the data. Then power cycle the badge. If no * follows the name of the pet, the badge applied no override.
The parser is silent. It skips an unknown key. It also drops the whole line for a value that is not a plain whole number, so no units, no decimals and no minus sign. It shows no error on the screen.
The documented “reasonable range” is advice, not a limit. The firmware limits a value only to the range of the integer type. HUNGER_RATE=1000 really fills hunger about 300 times faster, and your pet starves before you disconnect the cable. If a large value damaged your pet, delete the file and restart the badge to return to the preset.
7 - Hardware
The Cyber Ægg is a low-power LoRa badge around a Nordic nRF52840 microcontroller. The design goal is one battery charge for the full week of BornHack. The hardware and the firmware therefore keep the power consumption low.
The design is a prototype at this time. Some of the RF circuits, for NFC, LoRa and Bluetooth, still have U.FL and IPEX connectors, which make the tuning easier. We also did not fully characterize the antennas on the PCB. Treat this design as beta. Do not order your own boards yet.
Overview
Component
Part
Interface
Microcontroller
Nordic nRF52840
—
Display
1.54 inch black, red and white e-paper, 152 × 152, SSD1675 / SSD1675B controller
SPI
LoRa radio
Semtech SX1262
SPI
Bluetooth Low Energy
Built-in radio of the nRF52840
—
NFC
NFC tag PHY of the nRF52840, with a coil on the PCB
—
Input
5-way joystick, Execute and Cancel buttons
GPIO
Feedback
RGB LED, piezo buzzer
GPIO / PWM
Power
Li-ion battery, USB-C for power and data, ON/OFF slide switch
—
Expansion connector pinout
The board has an I²C expansion connector of the QWIIC type. We made a design mistake: the 3.3 V and the GND signals are swapped. Before you connect a QWIIC peripheral, make a corrected cable and use that.
Display
The badge has a 1.54 inch tri-color e-paper display in black, red and white. The resolution is 152 × 152 pixels, and an SSD1675 or SSD1675B controller drives it. E-paper stays readable in bright sunlight at the camp, and it uses no power to hold an image. Both properties help the battery goal of one week.
The panel uses the waveform LUT in its own OTP memory by default. Advanced users can replace that waveform with a calibrated one, for example for a faster refresh. Copy a LUT.CFG file to the USB drive. See the firmware’s LUT.md. You can make a waveform with the ssd1675-calibration tool. If you hold Fire during the start, the badge always uses the safe built-in waveform.
Manual input
The Tamagotchi egg toy of the 1990s inspired the Cyber Ægg, so the buttons have the same names:
Button
Function
Select
Moves through the menu options
Execute
Starts the option under the cursor
Cancel
Cancels the current operation
The Select button is a 5-way joystick with a press action, which makes the navigation easier.
Bluetooth
The nRF52840 provides Bluetooth Low Energy directly. It works in the 2.4 GHz band, and it uses an antenna on the PCB. That antenna comes from a Texas Instruments reference design in the standard KiCad 9 library. For the details, see the TI application note SWRA228.
LoRa
A dedicated Semtech SX1262 radio provides the long-range connection. The matching and balun circuit follows the Semtech application note AN1200.54. The LoRa antenna is a Texas Instruments design, documented in SWRA416. On the network side the badge speaks MeshCore, so it joins the camp mesh immediately.
NFC
The nRF52840 includes an NFC PHY. It drives a resonant circuit: a coil on the PCB of about 2.8 µH, with tuning capacitors. This tank circuit is matched to 13.56 MHz. The nRF52840 supports tag functionality only, not reader mode. The firmware uses the tag for location games and station taps.
Expansion connector
The board has an I²C expansion connector of the QWIIC type. We made a design mistake: the 3.3 V and the GND signals are swapped. Before you connect a QWIIC peripheral, make a corrected cable and use that.
Fix the cable — cross the two power wires
A standard QWIIC or JST-SH 4-pin cable carries GND · 3V3 · SDA · SCL. The badge swaps 3.3 V and GND, so a standard cable sends reversed power to your peripheral. Do not use one.
To make a corrected cable, swap the two power wires at one end only. Leave the other end standard, so that it still fits the peripheral.
At the badge end of the cable, lift the small locking tab of the JST-SH housing. Then pull the GND and the 3V3 contacts out carefully, with a fine pick or with tweezers.
Swap the two contacts. The wire from the GND slot goes into the 3V3 slot, and the wire from the 3V3 slot goes into the GND slot. The two data wires, SDA and SCL, stay in position.
Push both contacts back until they click, and make sure that nothing is loose.
The data lines do not change, so you move only the two power contacts. Mark the corrected cable, so that you do not use it as a standard one. As an alternative, cut the red (3V3) and the black (GND) wires and solder them crossed. You can also keep one cable for the badge only.
The connector on the badge should have been compatible with QWIIC. QWIIC is a standard I²C connector, used by many SparkFun breakout boards and by boards from other suppliers. The board has two 10 kΩ pull-up resistors. You can also enable the internal pull-ups of the nRF52840 when the bus capacitance is high.
The firmware can also drive an optional Nicolai-Electronics I²C keyboard on this bus, for names and mesh messages. Connect the keyboard with the corrected cable, and the text entry uses the physical keys. Shift and Alt are one-shot toggles, and the alt-symbol layer matches the silkscreen. Without a keyboard, the badge uses the joystick picker on the screen automatically.
Power
A LiPo battery powers the badge, and USB-C charges it. An ON/OFF slide switch at the top left of the front disconnects the battery power. Slide the switch off and back on to power cycle the badge. The battery keeps the badge running when USB is disconnected, so this switch is the only way to restart it. Hold Execute while you switch the badge on to enter DFU mode for a firmware write.
8 - FAQ & troubleshooting
These are the answers to the questions people ask most often about the Cyber Ægg. If no answer helps, report a bug. The bottom of this page tells you where.
General
The badge does not wake up, and the display stays empty.
Connect USB-C. If the LED does not blink, hold Execute while you connect the cable, and write the firmware again with dfu-util. See Getting started → Firmware update.
After a firmware write or a factory reset, the badge shows the factory test, then a “ready to ship” screen, and it stops there.
This is correct behavior. After a self-test with all tests passed, the badge sets its pass flag, draws the ship screen and stops. The green LED pulses. Power cycle the badge once more. The second start skips the test and runs the application.
The badge shows “Battery voltage critical” at the start, and it goes no further.
The cell measured less than 3.0 V at the start, so the firmware stops to protect the cell. The hardware controls the charge, and it continues. Leave USB connected for some time, then power cycle the badge by hand. The badge does not restart itself from this screen.
The clock resets at every start.
This is expected, because the badge has no RTC with a backup battery. Pair the badge over Bluetooth with the MeshCore app once per start, or stay near a synchronized mesh repeater. The badge accepts a time over the air only from a trusted source. That is a repeater or companion advert with a verified signature, or a channel that you hold the key for. Other badges near you cannot set your clock. A pairing with a phone always sets it.
Bluetooth is not visible, and I cannot pair.
Make sure that Bluetooth is on, under Main → Settings → Bluetooth. The badge keeps this setting through a restart, so set it to BLE: ON.
My alarm did not sound.
The clock is not set for this start, and an alarm sounds only when the time is known. If the clock is set, look at the Days field of the alarm. None never sounds. A Weekdays, Weekends or Custom mask sounds on the selected days only. The bell in the header appears for every alarm that is on, whatever its day mask is.
No mesh peers appear.
Walk around. The LoRa range changes with the terrain and with the orientation of the antenna. Also check your LoRa preset, under Main → Settings → LoRa Radio. It must be the same as on the rest of the local mesh. See Mesh.
I formatted the badge’s USB drive, and all files are gone.
Do not format the drive. The badge understands only its own FAT12 layout. If the start finds another layout, such as exFAT, NTFS or unusual sector sizes, the badge formats the whole partition again and erases every file. It gives no message. To remove files, delete them in the normal way. If the badge already formatted the drive, copy your .PCX, .ICS and .CFG files back and restart it.
The charge symbol disappeared while USB is connected.
The charge is complete. The symbol comes back when the cell drains. The battery icon can also be up to a minute behind, because the badge measures the battery only every 60 seconds.
The red LED flashes often, most of all on the BornPet screen.
The LED gives one short flash every time the e-paper repaints. Most screens are static, so the flash is rare there. The pet has a slow idle animation, so the badge repaints, and flashes, every few seconds. The frames look almost equal, but each one is a repaint. This is not a fault, and it is usually not an incoming message. The Ignore blink setting, under MeshCore, mutes the flash for incoming mesh messages only. It does not stop the flash for a repaint. To stop the flash, stay on a static screen.
Which screen gives the longest battery life?
The e-paper uses almost no power to hold an image. The power goes into the repaints. The most economical screens therefore never repaint by themselves: My QR, Name, Tokens and Calendar stay idle until you press a button. Main and Watch repaint once a minute for the clock. BornPet repaints every few seconds for its animation, and it uses the most power. To make a charge last, stay on My QR or on Name. Both are idle and do not flash. My QR is also useful, because other people can scan you into the mesh.
Display (e-paper)
Red appears only sometimes, for example after a change of screen.
This is correct behavior. On one screen the badge uses fast black and white refreshes, which do not repaint the red plane. Red repaints on a full refresh, at a change of screen or at regular intervals. Red is not off. It refreshes less often than black and white.
The whole screen is inverted, and it stays inverted.
A full refresh cycles the whole panel for a short time. If that cycle stops early, the image can stay inverted. Go to another screen and back to force a clean redraw. A permanent inversion on the red-capable “B” panels was a firmware bug, so make sure that you use the current firmware.
There is no red, or the screen is washed out, after I added a custom LUT.
Your LUT.CFG is a fast waveform without red. Delete LUT.CFG from the badge’s USB drive. You can also hold Fire (the joystick press) during the start, which forces the built-in tri-color waveform for that start. See Hardware → Display.
The white LED blinks, and the badge does not finish the start, after I copied a LUT.CFG.
The badge rejects a damaged LUT.CFG, or one for a different panel, automatically. If the screen is unreadable, hold Fire during the start to force the safe built-in waveform. Then delete the file, or correct it.
The badge starts correctly, but it ignores my LUT.CFG.
The badge rejects the file without a message. The usual causes are a wrong variant letter for your panel, or the wrong key from the ssd1675-calibration tool. The badge wants the flat band_lut hex field, not stage_luts. A wrong hex length is also a cause, because each LUT value must be exactly 214 hex characters. The file size is not a limit. The badge streams the file from flash, so a full 16-band export of about 3.7 KB, with all comments, loads correctly. If you hold Fire at the start, the badge uses the built-in waveform for that start. The firmware’s LUT.md has the details.
Mesh / Bluetooth
The MeshCore app reports “connected”, but nothing works. I cannot set the clock, I see no contacts, and messages do not go out.
The pairing is stale. The phone removed the bond, because you deleted it in the Bluetooth settings or changed phones, and the badge still has it. The badge therefore rejects every command as unauthenticated. To correct this, select Main → Settings → Bluetooth → Clear pairings. That erases all bonds and restarts the badge. Then pair again. The badge holds a maximum of 4 bonds. A fifth phone does not stay, and the badge gives no message.
The Channel screen shows “BLE client connected”, and the buttons do nothing.
This is correct behavior. While the phone app is connected, the channel browser on the badge locks, and only Left, Right and Cancel work. Close the app or disconnect it, and the screen unlocks immediately.
My PMs and the peers I heard are gone after a restart.
The PM inbox and the list of recently-heard peers are in RAM only. The badge keeps saved contacts. When you meet a person you want to message later, open their entry and save them before you switch the badge off.
NFC
My vanity URL or vCard does not stay.
Write it with an NFC writer app, as a normal URL/URI, vCard or Wi-Fi record. Every record you write becomes your broadcast profile and survives a restart. The one exception is a token: record. See NFC & tokens → Set your own broadcast data.
A token I received disappeared, or a URL I tapped came back.
A token: write is temporary on purpose. The token goes to the Tokens screen, where the badge keeps it until the next restart. The broadcast returns to your own profile after about 10 seconds. A pushed token cannot replace your profile.
A station tap did nothing. There was no message, and the pet did not change.
Station commands work only with an active game. Select a pet first, and note that the egg countdown counts as active. If your pet left, start a new egg. Station commands also come through the signed BadgeCtl reader. If you write the phrase, for example more food, as a plain text record with a general NFC app, it does not feed your pet. It becomes your broadcast profile, and your badge then gives “more food” to every phone that taps it. Write a new URL or vCard to correct this.
Game / BornPets
The pet area shows “No sprites on flash”.
The scan at the start found no .PCX files. This happens after a factory reset, after a format of the drive, or after a firmware write without the asset set. Copy the sprite .PCX files back to the CYBR<hex> drive and restart the badge.
A sprite I made has the wrong colors, or it does not appear.
The badge needs one specific PCX format: 2 bits per pixel, one plane, RLE. The palette order is fixed: 0 = black, 1 = red, 2 = white, 3 = transparent. The badge ignores the palette in the file. It skips a normal 256-color or 24-bit export without a message. The Pet Maker and the firmware’s asset tool write the correct format. If you edited a sprite by hand in GIMP, write it again with the firmware’s scripts/fix_badge_pcx.py, or check it first with check_badge_pcx.py. See Games → Hand-editing sprites.
BORNPETS.CFG, or a change of mode, has no effect.
Both take effect at the start only. Eject the drive correctly, so that the computer writes the file, and power cycle the badge. If no * follows the name of the pet, the badge applied no override. See Games.
Where to file bugs
If you find a fault, open an issue on the firmware repository:
This page writes firmware to your badge. You need no toolchain, no drivers and
no command line. The browser talks to the badge over USB and writes the image.
It can install the badge’s asset files in the same session.
Chromium-family browsers only
This page uses WebUSB.
Only Chrome, Edge, Brave, Opera and Arc have it. Firefox and Safari do not, and
they never show the device chooser. On those browsers, write the firmware from a
terminal with dfu-util.
On Linux you also need a udev rule that gives your user access to the
bootloader. Without that rule the device appears in the chooser, but it does not
open.
This browser cannot flash. WebUSB is required, and only
Chromium-family browsers ship it (Chrome, Edge, Brave, Opera, Arc).
Firefox and Safari do not. You can still flash from a terminal with
dfu-util.
Do not unplug the badge while it is writing. The
bootloader writes straight to the application partition — there is no
backup slot — so an interrupted flash leaves the badge in DFU mode
until you flash it again. That is recoverable; just re-run the flash.
5 Add the asset files
Firmware alone leaves the badge without artwork. In DFU mode it also
appears as a small USB drive named CYBR followed by four hex
digits; the sprites, sponsor slides and the event programme belong in the
root of that drive. Each firmware image has its own set — the one below
follows your choice in step 3.
Copy them straight to the drive
Pick the CYBR… drive when your browser asks, and the files
are written for you — no download, no unzipping.
idle
This browser has no File System Access API, so it cannot write to the
drive directly. Download the zip below instead.
Unpack it into the root of the badge drive, not into a
subfolder. Eject the drive when you are done, then power-cycle the badge.
5 Add the data it needs
Diagnostics
If something goes wrong
The device chooser is empty. The badge is not in DFU mode. Put it in the
bootloader first. On the Cyber Ægg, slide the ON/OFF switch at the top left
of the front off, then back on, while you hold Execute. The LED then blinks
red. Click Connect again. The battery keeps the badge running, so a
disconnection of USB does not restart it.
The page connects, but it reports “application firmware (CDC)”. The cause is
the same. You reached the running firmware, not the bootloader. Power cycle the
badge into DFU mode.
“Failed to open the device” on Linux. A udev rule is missing. The badge’s own
repository has one. Install the rule, connect the badge again, and retry.
The write stopped in the middle. Nothing is damaged. The bootloader writes
directly to the application partition, so an interrupted write leaves the badge
in DFU mode. Connect again and write again.
Checksum mismatch. The download did not match the published hash, and the
page refused it before it wrote anything. Load the page again and retry. If the
error continues, report it.
The badge reports “No sprites on flash”. The firmware is installed, but the
asset files are not. Do step 5 above, then power cycle the badge.
I flashed DOOM, and nothing happens or it asks for game data. The firmware is
only half of DOOM. DOOM does not use the USB drive. Its game data goes into the
badge’s QSPI flash over a serial connection. Upload a WAD before you
play.
Sprites are missing or wrong after a change of edition. Each firmware image
has its own asset set, and the Community Edition draws many more sprites than
the standard image. Install the assets of the image you wrote. Step 5 follows
your choice in step 3 automatically.
No drive appears for the assets. The badge shows its USB drive only in DFU
mode, the mode you write from. If you already power cycled into the new
firmware, enter DFU mode again.
The copy finished, but the badge looks empty. Eject the drive in your file
manager before you disconnect it. Until you do that, your operating system can
still hold the data in its cache.
Flashing without a browser
Every image on this page is a plain .bin file for the application partition,
so dfu-util takes it directly:
dfu-util -d 1915:521f -D cyber-aegg.bin
Adding a badge or a firmware image
data/firmwares.toml in the
website repository drives the flasher.
Put the image in static/firmware/<badge-id>/, record it in that file with its
sha256sum, and it appears here. There is no backend to deploy.
Asset payloads work in the same way. Build the archive with flat,
deflate-compressed entries. Put it in static/assets/<badge-id>/, and point the
badge’s assets entry at it:
cd assets/to-badge && zip -rX -9 cyber-aegg-assets.zip .
Two constraints are important:
This site must serve the images. A cross-origin download needs CORS
headers, and the release assets of Codeberg and GitHub do not send them.
Application images only. A combined image that holds the bootloader
(cyber-aegg-full.bin on the Cyber Ægg) starts at address 0x00000000, and
it is for SWD or J-Link recovery. DFU writes the application partition, so a
combined image here would damage badges.
10 - DOOM
The badge runs DOOM. E1M1, Knee-Deep in the Dead, appears on the badge’s
e-paper panel at a few frames per second, in four levels of gray. The piezo
buzzer plays the music of the level.
The result is a slideshow, and you can play it.
DOOM replaces the firmware and erases your saved data
DOOM is a separate firmware image. It is not an app inside the normal firmware.
While DOOM is installed, the badge has no BornPets, no mesh and no clock. Write
the standard firmware again from the Flash page to get the badge
back.
A change to DOOM is destructive. The WAD fills almost all of the badge’s
2 MB QSPI flash. That same chip holds your pet and your settings (the KV store)
and the badge’s asset files, and the WAD writes over them. You lose the pet and
the settings. After you write a normal firmware again, install its assets from
the Flash page before the sprites come back.
How to install it
DOOM needs two steps, because the game data is much too large for the badge’s
USB drive. The firmware goes into the badge’s program memory over USB. The game
data goes into the separate 2 MB QSPI flash chip over a serial connection.
Write the DOOM firmware on the Flash page, as you write any
other image.
Power cycle the badge.
Upload a WAD with the loader below.
A firmware write erases the game data
A write of any firmware over USB formats the QSPI region that holds the WAD.
Upload the WAD again after every firmware write. A normal start never touches
the WAD, so it stays through power cycles.
Upload a WAD
This browser cannot upload. Web Serial is required, and
only Chromium-family browsers ship it (Chrome, Edge, Brave, Opera, Arc).
Firefox and Safari do not. You can still send the blob from a terminal
with a YMODEM tool such as sb or lrzsz.
1 Choose a WAD
No WAD chosen yet.
2 Put the badge in upload mode
Reset the badge while holding Cancel. It does this on
its own too, whenever no WAD has been uploaded yet. The screen says it is
waiting for a transfer.
disconnected
Pick the badge's serial port when the browser asks — on Linux it is
usually /dev/ttyACM0, on macOS a usbmodem
entry, on Windows a COM port.
3 Upload
idle
Nearly two megabytes over a serial line takes a few minutes. Leave the
tab in the foreground and do not unplug the badge.
Diagnostics
Controls
Input
Action
Joystick
Move, strafe and turn
Joystick press (Fire)
Shoot
Execute
Use, and open doors
Cancel
Change weapon. Held during the start, it enters WAD upload mode
Execute + Cancel
Turns the music on or off
If something goes wrong
The browser shows no serial port. The badge shows its serial console only in
WAD upload mode. Reset the badge while you hold Cancel, then try again.
“Timed out waiting for the badge to ask for data”. The badge is connected,
but it is not in upload mode, so it asks for no transfer. Reset the badge while
you hold Cancel.
The upload stops in the middle. Nothing is damaged. The badge has no usable
WAD, and it returns to upload mode. Connect again and send the file again.
DOOM starts, but it reports missing game data. The firmware is installed and
the WAD is not. You get this state after every firmware write. Upload the WAD
above.
Firefox and Safari show no Connect button. Neither browser has Web Serial.
Use a Chromium-family browser, or send the blob from a terminal with a YMODEM
tool:
Only one level fits. The blob above is about 1.8 MiB, which is about 92% of the
badge’s QSPI flash. The asset pipeline in the
cyberaegg-doom repository builds
it from the freely distributable sharewaredoom1.wad, and it cuts and
compresses the data. DOOM and its game data stay the property of id Software.
To build your own blob from a copy of doom1.wad that you have:
Then select that file in the loader instead of the prepared one.
Source
The port is at rarenerd/cyberaegg-doom.
It is a fork of next-hack/nRF52840Doom,
which comes from prBoom and GBADoom. The fork fits the engine to the badge’s
e-paper display, its buttons and its power budget. The DOOM engine is under the
GPL.
11 - CircuitPython
The Cyber Ægg has a CircuitPython build. It turns the badge into a device you
program in Python, with no toolchain. Flash it once. The badge then mounts as a
CIRCUITPY drive. Put a code.py file on the drive, and the badge runs it when
you save.
Ordinary CircuitPython modules reach the display, the buttons, the joystick, the
LEDs, the buzzer and the battery. The same is true for the LoRa radio, NFC,
Bluetooth Low Energy and deep sleep.
It replaces the firmware and erases your saved data
CircuitPython is a separate image. It is not an app inside the normal firmware.
While CircuitPython is installed, the badge has no BornPets, no mesh and no
clock. Flash the standard firmware again from the Flash page to get
the badge back.
The CIRCUITPY drive uses the same QSPI flash that holds your pet, your settings
(the KV store) and the badge’s asset files. CircuitPython writes over all of
them. You lose the pet and the settings. After you flash a normal firmware
again, install its assets before the sprites come back.
Install it
Flash the CircuitPython image from the Flash page.
Reset the badge with no button held.
The badge appears as a CIRCUITPY drive. It also gives a serial console
for the REPL: /dev/ttyACM0 on Linux, a usbmodem device on macOS, a COM
port on Windows.
Copy the libraries and the examples to the drive, as below.
Power cycle the badge, do not reboot it from the flasher
After you flash the badge, restart it with the ON/OFF switch at the top left
of the front. Slide the switch off, then back on. Do not ask the tool to reboot
the badge. The battery keeps the badge powered when you disconnect USB. A power
cycle is the reliable way to start the new firmware.
Install the libraries and examples
The examples do not run without the support libraries. An import of
cyberaegg_epd or the LoRa driver fails until the libraries are in
CIRCUITPY/lib. Install them first. The same archive holds the examples. They
go to CIRCUITPY/examples, so you can open one and save it as code.py.
Install the libraries and examples
The e-paper and LoRa drivers and adafruit_display_text for lib, plus the fourteen examples. They go in lib and
examples folders in the root of the CIRCUITPY
drive, which this creates for you.
idle
This browser has no File System Access API, so it cannot write to the
drive directly. Download the zip instead and unpack it into the root of
the drive — it already contains the lib and
examples folders.
get_display() returns a standard displayio.EPaperDisplay. Build a
displayio group as usual. Assign the group to display.root_group, then
refresh the display.
The example epd_hello.py draws a bordered white field with one black square
and one red square. The example hwtest.py tests the LED, the buzzer, the
charger, the battery, I²C and all the buttons.
The install above puts every example in CIRCUITPY/examples. Copy one to
code.py, and the badge runs it when you save. You can also
read the examples online
and the libraries,
or paste an example into the console below.
The firmware includes terminalio and fontio, so adafruit_display_text can
draw text labels. That library is in the repository’s lib/ folder.
The serial console
CircuitPython prints print() output and tracebacks to a USB serial console. It
also gives a REPL there. The console tells you why a program did not run. Errors
never appear on the display.
You can open the console here, without a terminal program:
This browser has no serial support. Web Serial is required,
and only Chromium-family browsers ship it (Chrome, Edge, Brave, Opera, Arc).
Firefox and Safari do not. Use a terminal program instead —
screen /dev/ttyACM0 115200, picocom, or PuTTY on
Windows.
disconnected
Click the console to type into it. Ctrl-C stops the running
program and drops you at the >>> prompt;
Ctrl-D restarts code.py from the top.
Ctrl-V pastes into the console once it has focus, and the
Paste button does the same without needing focus — handy
for pasting an example straight from this page. Multi-line code goes in
through the REPL's paste mode, so its indentation survives and it runs as
one block.
Drag the bottom-right corner to resize the console, or use
Fullscreen for the whole screen (Esc leaves it).
Ctrl-C stops the running program and gives you the >>> prompt. At that
prompt you can test the hardware one line at a time. Ctrl-D starts code.py
again from the top. This is the quickest way to run a program again after an
edit.
The console is only available while CircuitPython is installed. The firmware
gives the console, not the bootloader.
Work with the e-paper display
E-paper does not behave like a normal screen. Most first-time surprises come
from this.
The badge shows the last image it drew. E-ink holds its image with no power.
An EPaperDisplay object does not change the glass when you create it. To start
from a clean panel, use this code:
importcyberaegg_epdcyberaegg_epd.clear()# white panel, one full refresh
If your program clears the screen and then stops, call
displayio.release_displays() before it stops. If you do not, CircuitPython
draws its own start logo over the clean panel.
Refreshes are slow, and each one wears the panel. A full tri-color refresh
takes about twenty seconds. Obey these rules:
Use one refresh for each start. Draw the final image with the white
background in the same frame, instead of one refresh to clear and a second
refresh to draw.
Do not refresh more often than every 180 seconds.
Use full refreshes only. This panel does not do partial updates.
If the badge stays unused for a long time, leave the panel white. This
prevents image retention.
A refresh does not block the program.display.refresh() returns
immediately, and the panel continues to update for about six seconds. Read
display.busy if you must wait. You can also call
cyberaegg_epd.refresh(display). That function waits for the minimum interval
of the panel, then blocks until the refresh ends.
LoRa
The repository’s lib/ folder has a driver for the SX1262 radio. The driver
uses MeshCore-compatible EU settings: 869.618 MHz, SF8, 62.5 kHz bandwidth,
coding rate 4/5, sync word 0x1424, no TCXO.
The driver and the SX126x files it needs are part of the library set that you
installed above. You copy no more files:
examples/lora_tx.py sends a counter. examples/lora_rx.py receives, and
prints the size and the signal strength of each packet. The receiver works
against a second badge that runs lora_tx.py. It also works against live
MeshCore traffic. lora_dashboard.py shows live packet statistics on the
e-paper display.
NFC
The firmware includes NFC, so you copy nothing into lib/. The badge serves a
read-only tag. A phone that touches the badge opens a URL. See
examples/nfc_tag.py and the repository’s docs/NFC.md.
Bluetooth
The badge advertises and accepts Bluetooth Low Energy connections from
CircuitPython, through the standard _bleio API. It uses the factory address
from FICR.DEVICEADDR. The normal Rust firmware advertises with the same
address.
import_bleioadapter=_bleio.adapterprint(adapter.address)# Flags: LE General Discoverable, BR/EDR not supported, then the complete name.advertisement=bytes((2,0x01,0x06))+bytes((10,0x09))+b"CyberAegg"adapter.start_advertising(advertisement,scan_response=None,connectable=True,anonymous=False,timeout=0,interval=0.1,tx_power=0,directed_to=None)
A phone scanner then shows the badge. On Linux, use this command:
bluetoothctl --timeout 20 scan le
examples/ble_advertise.py does the same, and advertises again after a central
disconnects. Connections and GATT also work. examples/ble_uart.py serves a
Nordic UART Service. Any BLE terminal app, such as nRF Connect or Adafruit
Bluefruit Connect, connects to CyberAegg. The text you send arrives on the
badge’s serial console.
Talk to the badge from a phone
Three examples use that same UART service, so they need no library and no app
of your own.
examples/ble_remote.py controls the badge. Send a color name to set the LED,
or beep for the buzzer. The badge reports each button press back to the
phone. examples/ble_repl.py gives you a Python prompt. Send a line, and the
badge runs it and returns the result. Names stay for the whole session, so you
build up state line by line. This is not the CircuitPython REPL. The REPL of the
supervisor cannot run over Bluetooth on this firmware, because it starts before
the MicroPython heap exists.
examples/ble_telemetry.py needs no connection at all. It puts the battery
voltage, the buttons and the uptime in the advertising packet, and it refreshes
that packet every two seconds. Any scanner reads the values. Advertising is the
most dependable part of Bluetooth here. Nothing holds the single connection
slot, so many badges report at the same time, and any number of phones read
them. This is the right shape for a field full of badges.
Find and connect to other badges
The badge also scans, which is the observer role. It connects out, which is the
central role. It reads what the other device offers, which is the GATT client.
Two badges therefore find each other and inspect each other.
examples/ble_scan.py lists what the badge hears, and it decodes the beacon of
a badge that runs ble_telemetry.py. It scans and advertises at the same time,
so two badges watch each other with no connection between them.
examples/ble_central.py connects to a second badge that runs
examples/ble_uart.py. The link comes up in about 0.2 seconds, and both badges
report the same state. examples/ble_explore.py connects and then lists every
service and characteristic of the other device. It works against any device, not
only against a badge.
Scanning uses the multirole controller library, because the peripheral library
has no scanning. That library costs about 31 KB more flash.
Four limits are important:
The badge does not read a remote value. It finds the services and the
characteristics of a device it connects to. Characteristic.value on a remote
characteristic is not implemented. The badge therefore knows what a device
offers, and not yet what it holds.
The badge does not find a remote descriptor, so it cannot subscribe to a
notification from another device.
Legacy advertising only. The advertisement must fit in 31 bytes.
Bluetooth does not work together with the display. A full tri-color
refresh keeps the panel busy for tens of seconds. The background work of
displayio competes with the Bluetooth poll, and a connection does not
survive a redraw. Keep Bluetooth and the display in separate programs.
Bluetooth starts when a program first imports _bleio, and then stays on. This
uses battery power.
How it works without the SoftDevice
The native nRF Bluetooth of CircuitPython needs Nordic’s S140 SoftDevice at
flash address 0x1000. The badge’s bootloader owns that region, and DFU writes
only from 0x10000 up. Thus you cannot install the S140 without an SWD reflash
of the bootloader.
This limit belongs to the S140 binary, not to Bluetooth. This firmware links
Nordic’s SoftDevice Controller instead. That controller is the link layer alone,
an ordinary library with no fixed address. The firmware connects the controller
to the HCI Bluetooth host that CircuitPython already has for boards with an
off-chip radio. The host and the controller then run on the same chip. The
bootloader stays untouched, and you flash the badge with plain USB DFU.
The MeshCore companion app cannot talk to the badge while CircuitPython is
installed. MeshCore is part of the normal badge firmware, and this image
replaces that firmware.
Deep sleep
The alarm module works, with pin wake-up and time wake-up. A battery-powered
program can sleep between refreshes instead of a busy loop:
E-paper keeps its image with no power. The display stays readable during the
sleep.
If something goes wrong
No CIRCUITPY drive appears. The badge is still in the bootloader, or the
flashing tool rebooted it instead of a reset. Reset the badge with no button
held.
My code did not run. CircuitPython runs code.py from the root of the
drive. Check the file name. Open the serial console. The console prints syntax
errors and tracebacks. The display does not show them.
The display shows the CircuitPython logo over my drawing. Your program
cleared the panel and then stopped. Call displayio.release_displays() before
the program stops, or keep the program running.
The screen did not change. The firmware limits the refresh rate. It ignores
a request that comes too soon after the last refresh. Wait for the interval, or
use cyberaegg_epd.refresh(display). That function does the wait for you.
A Bluetooth connection times out, but scanning still works. Check the other
computer or phone first. A connected Bluetooth headset takes almost all of the
radio time there, and connection attempts then fail while scans still succeed.
Disconnect the headset and try again.
Source
The firmware, the libraries and the examples are at
rarenerd/cyberaegg-circuitpython.
Go directly to the
examples
or the libraries.
The image on the Flash page is that repository’s prebuilt binary, byte for byte.
Its checksum matches the published SHA256SUMS file. The repository’s
docs/BUILDING.md has the build instructions.
CircuitPython is MIT, and the badge port is Apache-2.0. The Bluetooth build also
links Nordic’s SoftDevice Controller and MPSL under
LicenseRef-Nordic-5-Clause. That license allows redistribution, but only for
use on Nordic silicon. The badge’s nRF52840 is Nordic silicon.