diff --git a/docs/design.md b/docs/design.md new file mode 100644 index 0000000..f518056 --- /dev/null +++ b/docs/design.md @@ -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. diff --git a/docs/sdd.md b/docs/sdd.md new file mode 100644 index 0000000..e7d124c --- /dev/null +++ b/docs/sdd.md @@ -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 + + diff --git a/docs/uml/switcher.png b/docs/uml/switcher.png new file mode 100644 index 0000000..cb3c911 Binary files /dev/null and b/docs/uml/switcher.png differ diff --git a/docs/uml/switcher.puml b/docs/uml/switcher.puml new file mode 100644 index 0000000..f560816 --- /dev/null +++ b/docs/uml/switcher.puml @@ -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 diff --git a/docs/uml/tasks.png b/docs/uml/tasks.png new file mode 100644 index 0000000..fd82564 Binary files /dev/null and b/docs/uml/tasks.png differ diff --git a/docs/uml/tasks.puml b/docs/uml/tasks.puml new file mode 100644 index 0000000..4d10481 --- /dev/null +++ b/docs/uml/tasks.puml @@ -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 +