Compare commits
2 Commits
1181759ace
...
aabcfc6a9a
Author | SHA1 | Date | |
---|---|---|---|
aabcfc6a9a | |||
1c593aff9b |
25
README.md
25
README.md
@ -93,8 +93,33 @@ To build and flash the program to your Microbit just run:
|
||||
cargo embed
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
### Design Doc
|
||||
|
||||
The [Design Doc][./docs/design.pdf] is generated with pandoc from the markdown
|
||||
file.
|
||||
|
||||
```bash
|
||||
pandoc ./docs/design.md -o ./docs/design.pdf
|
||||
```
|
||||
|
||||
### Software Design Doc
|
||||
|
||||
The [Software Design Doc][./docs/sdd.pdf] is generated with pandoc from the markdown
|
||||
file.
|
||||
|
||||
```bash
|
||||
pandoc ./docs/sdd.md -o ./docs/sdd.pdf
|
||||
```
|
||||
|
||||
### UML
|
||||
|
||||
[UML][./docs/uml/] is generated using Plant UML on the \*.puml files.
|
||||
|
||||
```bash
|
||||
plantuml ./docs/uml/*.puml
|
||||
```
|
||||
---
|
||||
|
||||
## Copyright & License
|
||||
|
76
docs/design.md
Normal file
76
docs/design.md
Normal file
@ -0,0 +1,76 @@
|
||||
# Design Document: MicroBadge
|
||||
|
||||
## Overview
|
||||
|
||||
**MicroBadge** is an interactive embedded application built for the BBC
|
||||
micro:bit v2. It is written in **Rust** using the **Embassy** async embedded
|
||||
runtime. The project functions as a **conference name badge** that acts as a
|
||||
professional and technical icebreaker at events.
|
||||
|
||||
The application framework includes:
|
||||
* A **menu system** for switching between apps.
|
||||
* A **name scroller** for displaying your name or custom message.
|
||||
* A **Snake game** for fun and interactivity.
|
||||
* A **NFC business card** app for sharing contact info. **Still in progress**
|
||||
|
||||
## Goals
|
||||
|
||||
* Provide a custom and memorable name badge experience.
|
||||
* Showcase embedded Rust development using async and no-std.
|
||||
* Enable fun interactions and sharing via NFC.
|
||||
* Serve as a technical portfolio piece for conferences, meetings, and
|
||||
interviews.
|
||||
|
||||
## Architecture
|
||||
|
||||
### 1. Core Framework
|
||||
|
||||
* Built on **Embassy** for asynchronous concurrency.
|
||||
* Uses hardware abstraction layers (HALs) for GPIO, timers, display, and
|
||||
buttons.
|
||||
* Implements a lightweight app-switcher system with a menu UI.
|
||||
|
||||
### 2. Applications
|
||||
|
||||
#### Name Scroller
|
||||
* Scrolls a name or message across the LED matrix.
|
||||
* Configurable for speed and repeat mode.
|
||||
* Useful for identifying the wearer at a glance.
|
||||
|
||||
#### Snake Game
|
||||
* Classic Snake game rendered on the 5x5 LED display.
|
||||
* Uses `Button A` and `Button B` for turning.
|
||||
* Includes food spawning, score tracking, and difficulty selection.
|
||||
* Uses PWM for different intensity led lights.
|
||||
|
||||
#### NFC Business Card (WIP)
|
||||
* Leverages the onboard NFC peripheral.
|
||||
* Intends to share vCard/contact data over NFC.
|
||||
* Targeted at mobile phones for quick transfer of contact info
|
||||
|
||||
## User Interaction
|
||||
|
||||
* Navigation is handled via micro:bit's `Button A`, `Button B`, and long-press `Start` gesture
|
||||
* LED matrix provides visual feedback for game states and menu navigation
|
||||
* Apps are selected from a simple menu UI
|
||||
* Snake game is tuned to a slower speed (~4Hz) for usability
|
||||
|
||||
## Development Environment
|
||||
|
||||
* Written entirely in **Rust**, `#![no_std]`
|
||||
* Uses **Embassy** for async device access and task scheduling
|
||||
* Debugging output via `defmt`
|
||||
* Built and flashed using `probe-rs` and `cargo-embed`
|
||||
|
||||
## Future Improvements
|
||||
|
||||
* Finalize NFC vCard transmission
|
||||
* Add configuration storage via flash memory
|
||||
* Create a desktop companion app for editing settings
|
||||
* Support BLE for data sync or configuration
|
||||
|
||||
## Summary
|
||||
|
||||
MicroBadge combines a fun hardware platform with modern Rust development.
|
||||
It serves not only as a name badge but also as a way to demonstrate embedded
|
||||
skills, share contact info, and invite technical discussion at events.
|
BIN
docs/design.pdf
Normal file
BIN
docs/design.pdf
Normal file
Binary file not shown.
243
docs/sdd.md
Normal file
243
docs/sdd.md
Normal file
@ -0,0 +1,243 @@
|
||||
# MicroBadge Software Design Document
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
### 1.1 Purpose
|
||||
|
||||
MicroBadge is a software application suite for the BBC micro:bit v2, designed
|
||||
as a digital conference badge. It serves both functional and social purposes;
|
||||
displaying the user’s name, hosting small interactive demos, and offering
|
||||
contact sharing via NFC.
|
||||
|
||||
This project serves as a conversation starter and technical showcase during
|
||||
events like conferences, meetings, and interviews.
|
||||
|
||||
### 1.2 Scope
|
||||
|
||||
This document focuses exclusively on the software implementation of MicroBadge.
|
||||
It covers the architecture, data structures, behavior, and design choices used
|
||||
to implement the badge’s app-switching system and core applications using Rust
|
||||
and the `embassy` async runtime.
|
||||
|
||||
### 1.3 Audience
|
||||
|
||||
This document is intended for:
|
||||
* Reviewers evaluating its design.
|
||||
* Recruiters or interviewers reviewing technical work.
|
||||
* Anyone trying to learn how to write Rust on an embedded platform.
|
||||
|
||||
## 2. System Overview
|
||||
|
||||
MicroBadge is an embedded application for the micro:bit v2. It uses the
|
||||
Embassy async runtime to manage multiple cooperative tasks without a
|
||||
traditional RTOS. The system is modular and consists of an app switcher,
|
||||
an LED display task, button listeners, and multiple interactive apps.
|
||||
|
||||
### 2.1 Runtime and Concurrency
|
||||
|
||||
MicroBadge uses Embassy's async executor. It runs the following tasks:
|
||||
|
||||
* `display_task` -- Consumes frame buffers and drives the LED matrix.
|
||||
* `button_listener` -- One per button (A, B, Start). Waits for input and
|
||||
debounces it before sending an event.
|
||||
* `app_task` -- Runs the currently selected app. Allows apps to yield and
|
||||
re-enter on each loop.
|
||||
|
||||
All communication is channel-based using `embassy_sync::channel::Channel`.
|
||||
|
||||
[Task UML][./uml/tasks.png]
|
||||
|
||||
### 2.2 App Switcher
|
||||
|
||||
The `Switcher` manages app selection and transition. It displays a menu and
|
||||
uses the A, B, and Start buttons to navigate between apps.
|
||||
|
||||
Each app implements a shared `App` trait with an async `run()` method.
|
||||
Apps are isolated and run cooperatively, returning control when done.
|
||||
|
||||
Current apps:
|
||||
|
||||
* *Menu*. The top-level app that allows selecting from installed apps.
|
||||
* *Badge*. Scrolls a string (e.g. your name) across the LED matrix.
|
||||
* *Snake*. A basic snake game with food, direction control, and score.
|
||||
* *NFC Card* (in development). Will present contact info via NFC.
|
||||
|
||||
[Switcher UML][./uml/switcher.png]
|
||||
|
||||
### 2.3 Input System
|
||||
|
||||
Each button is handled by a separate `button_listener` task. When a button
|
||||
is pressed, it sends a `Button` enum into a shared channel.
|
||||
|
||||
Apps listen for button input using the receiver end of the channel.
|
||||
|
||||
* A and B buttons are mapped to actions like turn left and right.
|
||||
* Start is used to confirm or start an app. It is mapped to the capacitive touch sensor logo.
|
||||
* A debounce delay of 100 ms is used for stability.
|
||||
|
||||
### 2.4 Rendering System
|
||||
|
||||
The rendering system uses a frame buffer that is written by the active app
|
||||
and read by the `display_task`.
|
||||
|
||||
Apps write into this buffer using a `Renderer` abstraction. Drawing is done
|
||||
in an offscreen buffer that is later pushed to the display.
|
||||
|
||||
* The screen is a 5x5 LED grid.
|
||||
* Per-frame updates allow for animations and dynamic content.
|
||||
* LED brightness levels are supported.
|
||||
|
||||
### 2.5 Code Organization
|
||||
|
||||
The system is split into modules for clarity and reuse:
|
||||
|
||||
* `app`. Defines the `App` trait and shared app interface.
|
||||
* `display`. Low-level display driver and LED control.
|
||||
* `renderer`. Provides drawing primitives for apps.
|
||||
* `channel`. Shared async channels for button and frame messages.
|
||||
* `switcher`. App selection logic and switching behavior.
|
||||
* `snake`, `menu`, `badge`. App implementations.
|
||||
* `microbit`. Definitions for button identifiers and device pins.
|
||||
|
||||
Each module is self-contained and uses only the shared channels and traits
|
||||
for interaction.
|
||||
|
||||
## 3. Application Features
|
||||
|
||||
### 3.1 Name Scroller
|
||||
|
||||
* Scrolls a configured name across the LED display.
|
||||
* Uses an async timer to advance frames.
|
||||
* Simple input handling: Any Button returns to the menu.
|
||||
|
||||
### 3.2 Snake Game
|
||||
|
||||
* 5x5 LED grid snake game using a wrapped grid (`WrappedU8<0, 4>`).
|
||||
* Buttons A and B turn the snake left/right.
|
||||
* Food spawns randomly in empty grid cells.
|
||||
* On collision with self, enters game-over state and displays score.
|
||||
|
||||
### 3.3 NFC Business Card (WIP)
|
||||
|
||||
* Intended to broadcast a vCard or custom URI over NFC.
|
||||
* Plan to use the BLE softdevice on the chip.
|
||||
* Currently under development.
|
||||
|
||||
## 4. System Architecture
|
||||
|
||||
This system uses Embassy's async runtime to coordinate application execution,
|
||||
hardware input, and rendering on the micro:bit v2 board. It is divided into
|
||||
distinct tasks: input listeners, a display task, and an app task.
|
||||
|
||||
The overall architecture is message-passing oriented. Input events and screen
|
||||
updates are communicated over embassy channels.
|
||||
|
||||
Application logic is encapsulated in independent modules conforming to a shared
|
||||
`App` trait. The Switcher manages the active app and transitions between them.
|
||||
|
||||
### 4.1 Components
|
||||
|
||||
* `main.rs`: Entry point. Spawns system tasks using Embassy.
|
||||
* `Display`: Renders 5x5 LED frames from a channel receiver using PWM.
|
||||
* `ButtonListener`: Listens for button presses and sends events via channel.
|
||||
* `Switcher`: Manages app lifecycle and transitions.
|
||||
* `App`: Trait for any runnable application module.
|
||||
* `menu`, `badge`, `snake`, `nfc`: App implementations.
|
||||
|
||||
## 5. Data Structures and State
|
||||
|
||||
### 5.1 Position, Direction, and Snake Body
|
||||
|
||||
The board is a fixed 5×5 grid. Positions are stored using a custom `Position`
|
||||
struct, which holds a `ClampedU8` for both `x` and `y` axes, ensuring values
|
||||
remain within bounds.
|
||||
|
||||
* `Position`: Represents a coordinate on the board with safe bounds.
|
||||
* `Direction`: Enum for movement direction: Up, Down, Left, Right.
|
||||
* `Snake`: Maintains a list of `Position` elements representing the snake's
|
||||
body. The first item is always the head.
|
||||
|
||||
Snake direction is updated via input, and movement wraps to stay within the
|
||||
board.
|
||||
|
||||
### 5.2 Message-Passing and Input State
|
||||
|
||||
User input is handled asynchronously via Embassy channels.
|
||||
|
||||
* Button presses are detected using `button_listener` tasks.
|
||||
* Events are sent to the `ButtonChannel`.
|
||||
* Applications read input non-blockingly using `try_receive()`.
|
||||
|
||||
This decouples physical input handling from application logic and allows clean,
|
||||
testable state transitions.
|
||||
|
||||
## 6. Component Interactions
|
||||
|
||||
### 6.1 How Components Interact Over Time
|
||||
|
||||
At runtime, three core tasks are running:
|
||||
|
||||
* `display_task`: Receives rendered frames and presents them on the display.
|
||||
* `button_listener`: Spawns three tasks, one per button (A, B, Start).
|
||||
* `app_task`: Owns the app switcher and runs the current app.
|
||||
|
||||
All interactions are asynchronous and use message-passing over embassy channels.
|
||||
|
||||
### 6.2 Flow of Control
|
||||
|
||||
1. User presses a button.
|
||||
2. The button task sends a message to the channel.
|
||||
3. The app reads the button event from the channel.
|
||||
4. The app updates internal state (e.g., direction or selection).
|
||||
5. The app prepares a frame and sends it to the frame channel.
|
||||
6. The display task renders the frame.
|
||||
|
||||
This loop repeats, giving a responsive, concurrent embedded UI.
|
||||
|
||||
## 7. Development Environment
|
||||
|
||||
### 7.1 Rust + Embassy
|
||||
|
||||
This project uses Rust with the `embassy` async runtime. It provides
|
||||
interrupt-driven, non-blocking execution suitable for low-power embedded
|
||||
devices.
|
||||
|
||||
### 7.2 Tools
|
||||
|
||||
* `probe-rs`: For flashing and debugging firmware.
|
||||
* `defmt`: Lightweight logging for embedded targets.
|
||||
* `panic-probe`: Panic handler integrated with defmt output.
|
||||
* `cargo-embed`: For development workflow and flashing.
|
||||
|
||||
Development was done on Linux using vim and CLI tooling.
|
||||
|
||||
## 8. Design Decisions
|
||||
|
||||
### 8.1 Why Embassy
|
||||
|
||||
Embassy was chosen for its async-first architecture, which maps well to
|
||||
reactive, event-driven embedded applications like games and UI. It allows
|
||||
multiple concurrent tasks without needing an RTOS or blocking code.
|
||||
|
||||
### 8.2 Fixed Board Size
|
||||
|
||||
The micro:bit's 5×5 LED matrix is inherently fixed. Game logic and rendering
|
||||
are simplified by using a constant-size grid, avoiding the need for dynamic
|
||||
allocation or scaling logic.
|
||||
|
||||
### 8.3 Data Wrapping and Clamping
|
||||
|
||||
Out-of-bounds positions are prevented using custom `ClampedU8` types. These
|
||||
provide safe arithmetic that prevents overflow and keeps all positions within
|
||||
0–4 inclusive. This reduces bugs and runtime checks in critical loops.
|
||||
|
||||
## 9. Future Work
|
||||
|
||||
### 9.1 NFC Business Card App
|
||||
|
||||
An in-progress app will emulate a contact card via NFC. The goal is to allow
|
||||
devices to scan the badge and receive contact information, a URL, or a vCard.
|
||||
|
||||
### 9.3 UI Polish
|
||||
|
||||
|
BIN
docs/sdd.pdf
Normal file
BIN
docs/sdd.pdf
Normal file
Binary file not shown.
BIN
docs/uml/switcher.png
Normal file
BIN
docs/uml/switcher.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 35 KiB |
35
docs/uml/switcher.puml
Normal file
35
docs/uml/switcher.puml
Normal file
@ -0,0 +1,35 @@
|
||||
@startuml
|
||||
interface App
|
||||
|
||||
[Switcher] --> [AppID]
|
||||
[Switcher] --> [App] : use
|
||||
|
||||
package "Menu" {
|
||||
[App] <-- [Menu]
|
||||
}
|
||||
|
||||
package "Badge" {
|
||||
[App] <-- [Badge]
|
||||
}
|
||||
|
||||
package "Snake" {
|
||||
[App] <-- [Game]
|
||||
}
|
||||
|
||||
package "NFC" {
|
||||
[App] <-- [NFC]
|
||||
}
|
||||
|
||||
|
||||
package "Channel" {
|
||||
[ButtonReceiver] <-u- [Menu] : use
|
||||
[ButtonReceiver] <-u- [Badge] : use
|
||||
[ButtonReceiver] <-u- [Game] : use
|
||||
[ButtonReceiver] <-u- [NFC] : use
|
||||
[FrameSender] <-u- [Menu] : use
|
||||
[FrameSender] <-u- [Badge] : use
|
||||
[FrameSender] <-u- [Game] : use
|
||||
[FrameSender] <-u- [NFC] : use
|
||||
}
|
||||
|
||||
@enduml
|
BIN
docs/uml/tasks.png
Normal file
BIN
docs/uml/tasks.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
13
docs/uml/tasks.puml
Normal file
13
docs/uml/tasks.puml
Normal file
@ -0,0 +1,13 @@
|
||||
@startuml
|
||||
skinparam componentStyle rectangle
|
||||
[Tasks] --> [Switcher]
|
||||
[Tasks] --> [Display]
|
||||
|
||||
package "Button Listeners" {
|
||||
[Tasks] --> [A Button Listener]
|
||||
[Tasks] --> [B Button Listener]
|
||||
[Tasks] --> [Start Button Listener]
|
||||
}
|
||||
|
||||
@enduml
|
||||
|
Reference in New Issue
Block a user