Musicbox is an NFC‑triggered music player aimed at running on a Raspberry Pi (32‑bit OS on Pi 2/3). It is being built with TDD and commits should remain small and frequent.
Looking for setup and day-to-day usage instructions? Visit the mdBook-based Musicbox User Guide or build it locally with mdbook serve docs.
controller: Pure domain logic mapping card UIDs to tracks. Works with anyAudioPlayerimplementation so we can exercise it thoroughly in unit tests without bringing hardware along.config: Loads card→track mappings from a TOML file and produces aLibrary.audio: Optional backends implementingAudioPlayer.RodioPlayeris enabled via theaudio-rodioCargo feature; otherwise a silent stub is available, letting the app boot in CI or on dev laptops without ALSA.reader: Defines theNfcReadertrait. A PC/SC implementation behind thenfc-pcscfeature polls an attached ACR122U reader; a noop reader is used otherwise so we can still run and observe telemetry on machines without the hardware.app: Glue code that loads config, wires the controller to a reader, and runs the event loop with callback hooks. Also hosts the optional debug dashboard when enabled.main: CLI entry point built on clap. Allows selecting reader backend, poll interval, config path, and silent mode so the same binary can serve development, test rigs, and the Pi image.
# Run tests (default features only; no audio/NFC backends required)
cargo test
# Map a new card to a track (use either explicit --config or positional CONFIG)
./bin/musicbox add --config config/demo.toml --track songs/track.mp3 --card deadbeef
./bin/musicbox config/demo.toml add --track songs/track.mp3 --card deadbeef
# On laptops without a reader, omit --card to generate a synthetic UID automatically.
./bin/musicbox add --config config/demo.toml --track songs/track.mp3 --reader noop
# Cross-compile for Raspberry Pi (armv7)
scripts/build-armv7.sh --release
# Include optional features when needed
CARGO_FEATURES="audio-rodio nfc-pcsc" scripts/build-armv7.sh --release
# Enable the debug HTTP surface while cross-compiling
CARGO_FEATURES="debug-http" scripts/build-armv7.sh --release
# Trigger playback without an NFC reader (manual test on Pi)
./bin/musicbox manual trigger --config config/demo.toml deadbeef
# Launch the debug JSON API + dashboard (requires `debug-http` feature)
./bin/musicbox --debug-http 0.0.0.0:3000 --reader noop --silent config/demo.toml
# Visit http://<host>:3000/ for the Tailwind dashboard (status, config editor, controls)Tagged builds publish tarballs for both x86_64-unknown-linux-gnu (developer laptops) and armv7-unknown-linux-gnueabihf (Raspberry Pi). Each archive ships with the musicbox binary, config.example.toml, and the sample systemd unit so you can drop the bundle straight onto a Pi or run it locally without compiling from source.
- Download the assets from the GitHub Releases page:
curl -LO https://github.com/sholiday/musicbox/releases/download/v0.1.0/musicbox-v0.1.0-armv7-unknown-linux-gnueabihf.tar.gz curl -LO https://github.com/sholiday/musicbox/releases/download/v0.1.0/musicbox-v0.1.0-armv7-unknown-linux-gnueabihf.tar.gz.sha256
- Verify the checksum and unpack:
sha256sum -c musicbox-v0.1.0-armv7-unknown-linux-gnueabihf.tar.gz.sha256 tar -xzf musicbox-v0.1.0-armv7-unknown-linux-gnueabihf.tar.gz
- Copy
musicboxinto~/musicbox/bin/, and place the bundled config/service files wherever your deployment expects them.
Substitute the desired version/tag and target architecture in the commands above. The x86_64 tarball is convenient for quick local smoke tests with the noop reader, while the armv7 build includes the ALSA and PC/SC backends required on the Pi.
# Copy the binary and sample config to the Pi (host `carter` shown here)
scp target/armv7-unknown-linux-gnueabihf/release/musicbox carter:~/musicbox/bin/musicbox
scp examples/config.example.toml carter:~/musicbox/config/demo.toml
# Start the service with the debug HTTP endpoint exposed
ssh carter 'cd ~/musicbox && ./bin/musicbox --debug-http 0.0.0.0:3000 --reader noop --silent ./config/demo.toml'
# Confirm the server is reachable (or open http://carter:3000/status in a browser)
ssh carter 'curl http://127.0.0.1:3000/status'# Run with Rodio audio support (requires system audio libs)
cargo test --features audio-rodio
# Run with PC/SC reader support (requires libpcsclite headers)
cargo test --features nfc-pcsc
# Combine features as needed
cargo test --features "audio-rodio nfc-pcsc"
# Include the optional debug HTTP server
cargo test --features "audio-rodio nfc-pcsc debug-http"To automatically run the test suite before every commit, point Git at the bundled hooks:
git config core.hooksPath .githooks- mdBook sources live in
docs/src. Install mdBook withcargo install mdbookif it is not already available. - Use
mdbook serve docsfor a live-reloading preview while editing. .github/workflows/docs.ymlbuilds the book and publishes it to GitHub Pages on pushes tomain.
# Example using clap CLI options
cargo run --release --features "audio-rodio nfc-pcsc" -- \
--poll-interval-ms 200 \
--reader auto \
--silent \
/path/to/config.tomlOptions:
CONFIG(positional): path to the TOML config mapping card UIDs to tracks.--poll-interval-ms: adjust NFC polling interval (default200ms).--reader {auto|pcsc|noop}: force reader choice;autotries PC/SC then falls back to noop.--silent: skip audio playback regardless of backend availability.--debug-http <addr>(requiresdebug-httpfeature): expose telemetry via Axum (e.g.127.0.0.1:3000).
A starter config can be found in examples/config.example.toml.
Add a [volume] table to the config to control playback levels throughout the day. The default
value (0.0–1.0) is applied during normal hours, while one or more [[volume.quiet_hours]] entries
can override the volume between a start and end time (24-hour HH:MM format). Windows may span
midnight, and each entry must declare its own volume multiplier. Example:
[volume]
default = 1.0
[[volume.quiet_hours]]
start = "19:30"
end = "07:00"
volume = 0.4With this configuration, every track will play at 40% volume after 7:30 PM until 7:00 AM; outside of that window playback returns to the default level automatically.
The binary needs to run in a few different contexts:
- CI / developer laptops – usually missing ALSA and PC/SC headers. The default build therefore avoids those dependencies so
cargo teststays fast and hermetic. - Raspberry Pi image – supply
--features "audio-rodio nfc-pcsc"so the concrete Rodio player and PC/SC reader are compiled in and the hardware works at runtime. - Debug rigs – when you want the Axum status surface or UI, enable
debug-httpand pass--debug-http <addr>on the CLI. The server lives on a separate thread to avoid blocking the reader loop.
Keeping these concerns behind feature flags lets us ship one codebase while still producing lightweight binaries for automated pipelines.
- Build everything from your dev machine; the Pi only needs the deployed binaries. Use
scripts/build-armv7.shto produce thearmv7-unknown-linux-gnueabihfartifacts and copy the resulting files undertarget/armv7-unknown-linux-gnueabihf/{debug,release}to the Pi (e.g., viarsync). The script wires up the requiredpkg-configenvironment so ALSA and PC/SC libraries resolve correctly when optional features are enabled. - For on-device testing without installing Rust, cross-compile the test harnesses with
scripts/build-armv7.sh --tests, copy the executables fromtarget/armv7-unknown-linux-gnueabihf/debug/deps/to the Pi, and run them there. - Until the NFC reader is connected, invoke the manual trigger subcommand to play tracks straight
from the command line:
./bin/musicbox manual trigger --config <path/to/config> <card_uid>. - Pi 2/3, standard 32‑bit Raspberry Pi OS.
- NFC reader: ACR122U (PC/SC).
- Audio: Raspberry Pi audio output via Rodio/CPAL (requires ALSA).
When cross-compiling, ensure the appropriate system libraries (libpcsclite, ALSA) are available in the target sysroot if the corresponding features are enabled.
- Use tests to drive new behavior (
cargo testruns quickly without hardware). - Commit early and often with descriptive messages.
- Avoid mocks unless hardware interaction cannot be reasonably replicated.
- If external libraries are missing on the dev machine, prefer optional features so the default build stays portable.
- Implement a real PC/SC reader loop on the Pi and validate with
--features nfc-pcsc. - Integrate Rodio playback on the Pi (
--features audio-rodio), handling error reporting for missing audio devices. - Extend configuration (TOML or CLI) to control logging/debug output and prep the Axum-based status UI.