Skip to content

About

Extensible firmware for M5Stack Cardputer-Adv with mini apps, Bluetooth host control, a macOS companion, AI usage monitoring, and NFC inventory.

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

68 Commits

Folders and files

Repository files navigation

Cardputer Hub

Cardputer Hub is an extensible firmware platform for the M5Stack Cardputer-Adv.

The project is designed as a small personal device platform built around reusable Services and independent Mini Apps.

The long-term goal is to support functionality such as:

  • Bluetooth host control
  • multiple paired devices
  • Wi-Fi connectivity
  • weather information
  • VPS monitoring
  • media controls
  • RGB status indication
  • microSD-backed file storage
  • Telegram integration
  • remote control through a Web UI
  • additional Mini Apps and hardware extensions

The project is intentionally not designed as a single-purpose MacBook remote.


Architecture

The primary dependency direction is:

Mini Apps
    ↓
Services
    ↓
Connectivity
    ↓
Hardware Adapters

The System Core provides shared infrastructure such as:

Application lifecycle
Launcher
App Registry
Navigation
Input routing
Action Bus
Configuration interfaces
Record and file-storage primitives
Logging
Capabilities

Example

WeatherApp
    ↓
WeatherService
    ↓
WiFiService
    ↓
ESP32 Wi-Fi

Mini Apps should remain thin.

Reusable logic, state, integrations, and background behavior belong in Services.


Documentation

Project documentation is the source of truth for architecture and engineering decisions.

Read:

  • docs/ARCHITECTURE.md — system architecture, Services, Mini Apps, connectivity, host management, remote control, and planned development phases
  • docs/ENGINEERING.md — testing, CI/CD, build system, versioning, releases, and development requirements
  • docs/UI_REQUIREMENTS.md — delivered UI behavior and the remaining visual, motion, sound, display-power and input requirements
  • docs/plans/README.md — implementation-plan status index
  • docs/manuals/ — installation, supported features, device controls, and user-facing procedures
  • AGENTS.md — instructions for Codex and other coding agents working in this repository

Architectural changes should update the relevant documentation in the same pull request.


Project Structure

src/
├── core/
├── connectivity/
├── services/
├── apps/
├── hardware/
└── main.cpp

companion/macos/
protocol/companion/
test/

docs/
├── ARCHITECTURE.md
├── ENGINEERING.md
├── UI_REQUIREMENTS.md
└── plans/

.github/
└── workflows/

scripts/

The apps, connectivity, and services directories contain the built-in system UI, connectivity foundations, and host/configuration/battery/audio/companion Services. companion/macos holds the optional macOS menu-bar Cardputer Companion.app. protocol/companion holds the shared wire contract, its fingerprint generator and binary fixtures; firmware and Companion are built from one commit and refuse a peer built from another protocol definition. The MAC STATUS Mini App shows live Mac system metrics, charging time and four detail pages (CPU and top apps, power, network, memory and disk) while the Companion is connected; polling runs only while the app is open. AI combines STATUS and USAGE, switched with Left/Right, and remembers the last page within a firmware session. USAGE shows discovered Codex/Cursor/Claude quotas from the Companion and publishes a remaining-quota gauge on Unit Puzzle while that page or its details are visible. Provider collection is automatic on the Mac; firmware receives only normalized usage numbers. STATUS shows whether Codex, Claude Code and Cursor desktop agents are working, need you, or are done, from lifecycle hooks that Companion installs on request; only application states cross BLE. Status polling continues on USAGE, where a fresh wait/error adds ! beside STATUS. Puzzle follows the visible page. Escape returns from usage details to USAGE, then closes AI. SERVICES HEALTH shows the public status pages of GitHub, Anthropic, OpenAI and Cursor while it is open. The Cardputer fetches them over HTTPS when Wi-Fi is connected, and through the Companion otherwise. A page that gets worse plays a fault cue. NFC is a personal inventory for boxes and bags: a writable NTAG213/215/216 sticker carries only a random inventory ID, and the container's name and item list live as a JSON record on the Cardputer's microSD card. NfcApp renders InventoryService, which registers blank stickers (writes and verifies the ID, then saves the record) and shows a tapped container's list offline; NfcService owns the optional M5Stack Unit NFC (ST25R3916) behind INfcReader and withdraws NFC_READER if the unit disappears. The Companion's Inventory window edits each container's name and free-text description (including Cyrillic) over the live BLE session; the Cardputer validates and commits every revision-checked write.


Local Setup

The locked development environment uses:

  • Python 3.12.14;
  • uv 0.12.13;
  • ESP-IDF 5.5.5 with its recommended compiler, CMake, and Ninja tools;
  • PlatformIO Core 6.1.19 only for native host tests and static analysis;
  • clang-format 23.1.0;
  • a C++17 host compiler for native tests and C++17 project-owned firmware components;
  • exact managed-component and Git-submodule dependency locks.

On macOS, install Git and the host compiler with Xcode Command Line Tools:

xcode-select --install

On Ubuntu or Debian Linux, install the native build prerequisites:

sudo apt-get update
sudo apt-get install --yes build-essential git curl wget flex bison gperf \
  python3 python3-pip python3-venv ccache libffi-dev libssl-dev dfu-util \
  libusb-1.0-0

Install the repository's exact uv version using its versioned installer, then let uv install Python and the locked tools. Review downloaded installation scripts before running them when required by your environment's security policy.

curl -LsSf https://astral.sh/uv/0.12.13/install.sh | sh
uv python install 3.12.14
bash scripts/install_esp_idf.sh \
  "$HOME/.espressif/frameworks/esp-idf-v5.5.5"
source "$HOME/.espressif/frameworks/esp-idf-v5.5.5/export.sh"
make setup

The locked virtual environment is local to the checkout; these commands do not replace the macOS system Python. Verify the resolved versions with:

uv --version
uv run --frozen python --version
uv run --frozen pio --version
uv run --frozen clang-format --version
idf.py --version

Do not install project-specific ESP32 or M5Stack libraries globally. The ESP-IDF component manager resolves the exact production graph from main/idf_component.yml and dependencies.lock; Git submodules retain the exact pinned hardware-support sources. The short build/check/upload/monitor commands activate ESP-IDF automatically. Source its export.sh before calling make setup, make configure, or ESP-IDF commands directly.


Install on a Cardputer-Adv from a Fresh Machine

Follow the maintained docs/manuals/installing-firmware.md guide for prerequisites, USB installation, verification, and troubleshooting.

To keep Cardputer Hub installed beside Codex Microputer ADV, Bruce, or another application in CRUB's shared extra slot, use the separate Cardputer Firmware Manager. It owns the shared crub layout, stages all or selected local builds and GitHub Release images on microSD, and keeps application data partitions intact during routine updates.

For a local multiboot build, start CRUB's usbsd mode and validate/stage the Hub image through the sibling manager repository:

cd ../cardputer-firmware-manager
python3 -m firmware_manager doctor
python3 -m firmware_manager local --app hub --build --sd /Volumes/CARDPUTER

The manager calls scripts/build_firmware.sh, which activates the pinned ESP-IDF 5.5.5 installation from the documented setup path, or a repository-local build-tools/ installation when present, without inheriting another application's ESP-IDF environment. Nonstandard installations may set CARDPUTER_HUB_IDF_PATH and, when needed, CARDPUTER_HUB_IDF_TOOLS_PATH. For local manager builds, the splash version includes the local build date and time as VERSION+YYYYMMDD-HHMM; release builds retain their assigned semantic version unchanged.

After safely ejecting the card and exiting usbsd, run sd so CRUB remounts the card and reloads its aliases. Then run uphub and require both app: ok and flash complete before running go. Hub shares CRUB's extra slot with the other applications, so uphub replaces whichever one was installed. To install a published build instead, use:

python3 -m firmware_manager release --app hub --sd /Volumes/CARDPUTER

Do not copy or flash an unvalidated raw image directly. If the USB Serial/JTAG console does not enumerate after a CRUB launch, follow the install guide's gofast diagnostic boot procedure.


Build

Run make or make help for the short command list. No Node.js or npm is needed.

make build
make firmware-size
make check
make flash SD=/Volumes/CARDPUTER
make stage SD=/Volumes/CARDPUTER
make doctor SD=/Volumes/CARDPUTER

make build, make check, make firmware-size, make monitor, and make flash-usb UPLOAD_PORT=/dev/cu.usbmodem... activate the installed pinned ESP-IDF automatically through scripts/build_firmware.sh. Initial dependency installation is still required (see Local Setup). Host tests and analysis keep their PlatformIO packages in .cache/platformio to avoid conflicts with the tool versions used by other firmware repositories.

make flash builds and stages Hub on the mounted FAT32 card through the sibling cardputer-firmware-manager; make stage stages the existing image. Both run the manager's doctor before and after staging. Safely eject the card, leave CRUB usbsd, then run sd, uphub, and go on the Cardputer. USB flashing uses Hub's existing upload command and writes only CRUB's shared extra slot. SD defaults to /Volumes/CARDPUTER; FIRMWARE_MANAGER_DIR overrides the manager checkout path and WORKSPACE overrides the parent of the firmware repositories. make -n flash shows the commands without staging an image.

Companion commands also run from this repository root:

make companion-build
make companion-check
make companion-run
make companion-install

Installation defaults to ~/Applications/Cardputer Companion.app; override with COMPANION_INSTALL_DIR=/Applications if that directory is writable. Quit a running Companion before launching a rebuilt or installed version. Installation does not launch the app or enable Start at Login.

make firmware-size inspects that completed production build without creating a second firmware variant. It prints ESP-IDF's application/partition summary and per-component flash contributions, and retains the same reports as build/firmware-size.txt and build/firmware-size-components.txt.

The production application and matching partition-table images are written to build/cardputer_hub.bin and build/partition_table/partition-table.bin. The version-controlled flash layout keeps the framework's default NVS separate from the dedicated hub_config NVS partition reserved for authoritative configuration records. On startup, the firmware enters through native ESP-IDF, initializes M5Unified directly, writes structured informational records for the product name, version, commit, and build type to serial, briefly renders the product name/version, then opens a device-centric, application-neutral Home. Its fixed status bar shows WiFi and BT dot indicators and a right-aligned estimated battery percentage (--% if unavailable). A procedural Living Orb occupies the center. A reserved row shows a round Leaf dot and the active Bluetooth host name only while HostService is Ready and the Companion session is live. The bottom bar offers APPS and SETTINGS; Left/Right selects one and Enter opens it. APPS is selected whenever Home opens. Home has no clock or connection-state text. The default Enter action opens the AppRegistry-driven Launcher. The first registered Mini App is SYSTEM, a read-only status list of battery, Bluetooth, host, Wi-Fi, and firmware version. Escape returns from SYSTEM to Launcher and from Launcher to Home. Plain Tab opens Settings; Bluetooth opens the existing BLE panel, Wi-Fi opens manual network setup, and Sound volume adjusts persistent 0-100% key-click volume in 10% steps. The remaining Settings rows control Screen timeout (Normal: 15 seconds to dim, then 2 minutes to off; Long: 60 seconds, then 5 minutes; Never: always awake), Screen brightness (20%–100% in 10% steps), and Unit Puzzle LED brightness (1%–10% in 1% steps). Left/Right changes each value immediately; all three settings survive reboot. Screen navigation uses short horizontal transitions with live input. The update loop polls semantic keyboard events, routes local settings Actions, and advances HostService/BluetoothService plus NetworkService/WiFiService independently of UI scheduling. Host selection, BLE On/Off, pairing, renaming, per-host deletion, and persisted host platform/capability/template-reference metadata are available through Services; the current UI does not edit that metadata. Keyboard-to-HID mappings are not yet implemented.

System Core also provides standalone navigation history, capability, and application-metadata registries plus record- and file-storage boundaries for later phases. The Cardputer microSD adapter compiles against the pinned board framework but is not constructed or mounted by the firmware runtime. These foundations include Mini App runtime/lifecycle, Launcher, and SYSTEM. They do not provide additional production Mini Apps, file-browser, backup, or configuration import/export behavior.

The Wi-Fi foundation provides a hardware-independent connection state machine and ESP32 station adapter. Normal firmware now composes both through NetworkService, which owns one bounded persisted station network, enabled intent, startup restoration, and credential-free domain status. Home and Wi-Fi Settings consume that Service snapshot. No credentials are compiled into firmware. Manual SSID/passphrase setup is available on the device; network scanning remains pending.

The Bluetooth lifecycle foundation similarly adds a hardware-independent single-peer state machine and a compiled direct ESP-NimBLE peripheral adapter. The same boundary now supports explicit authenticated pairing, stable opaque bond references, a 16-bond registry, selected-bond reconnection, explicit bond removal, and a shared hardware-neutral HID report contract. The direct ESP-NimBLE adapter exposes a secured keyboard and consumer-control HID service, accepts reports only for the selected authenticated and subscribed peer, and releases active reports before controlled disconnects. HostService composes this boundary with ConfigurationService: profiles, selection, and BLE On/Off are stored in internal hub_config NVS. First use defaults to Off and imports existing pairs without advertising. Switching closes the old connection before allowing the selected host. Plain Tab opens Settings, whose Bluetooth entry exposes these operations through ActionBus. A hardware-separated AudioService supplies precomputed, asynchronous key clicks at a persistent default of 60%; see the device guide.


Tests

The host-management suites cover persistence, selected-host switching and Off, error handling, pairing cancellation/completion, and local settings Actions through real Services with fake hardware. Physical two-host acceptance is tracked separately in plan 017.

Native tests run on the host and require no Cardputer hardware:

make test

Behavior changes follow red-green-refactor TDD and should test observable behavior. The native suites cover stable firmware build metadata, log-level filtering, keyboard event translation and deduplication, opaque record-storage validation and forwarding, owned navigation history and Back traversal, dynamic capability registration and enumeration, owned application metadata validation and lookup, bounded logical file-storage operations, and System Core boot and update orchestration. They also cover version-6 configuration persistence and legacy migration, storage-before-radio failure atomicity, domain status, configuration validation, connection state, timeout and capped retry timing, connected-only and link-loss-safe RSSI access, disconnect-error propagation, and credential-free diagnostics. The Bluetooth suite covers side-effect-free construction, explicit lifecycle results, callback-event isolation, bonded single-peer policy, unbonded-peer rejection, pairing-window timing, all authenticated challenge modes, strict security completion, stable-reference finalization, capacity, target selection, bond deletion, reconnect timing, capped advertising backoff, fatal cleanup, stale event isolation, retained cleanup retries, and identity-free diagnostics. Peer-rejection coverage verifies that advertising cannot resume until all asynchronous disconnects for rejected peers have completed. The HID suites additionally cover neutral and six-key keyboard reports, consumer usages, invalid and duplicate usage rejection, selected-peer and dual subscription readiness, report ownership, retryable backpressure, neutral release, stale callbacks, controlled target changes, and clean re-enable.

Run formatting and static analysis separately with:

make format
make format-check
make lint

Before opening a pull request, run the complete CI-equivalent suite:

make check

make check verifies the lock, formatting, Cppcheck analysis, native tests, strict compiler warnings, and the Cardputer-Adv firmware build. make clean removes ESP-IDF production build output. PlatformIO remains scoped to host-side tests and analysis.


Flash

Connect the Cardputer-Adv with a USB-C cable that supports data. On a CRUB multiboot device, write only the Hub application into CRUB's shared extra slot:

make upload UPLOAD_PORT=<device>

That command does not rewrite the bootloader, partition table, or otadata. Do not run make upload-standalone or idf.py flash on a CRUB device: those install Hub's standalone table and hide saved settings at 0x7a0000. make upload refuses an image larger than the 6.5 MiB CRUB extra slot.

For a Hub-only device that has never used CRUB, the first installation or a one-time upgrade from the earlier flash layout uses make migrate-storage-layout UPLOAD_PORT=<device>. The explicit port ensures the standalone upload and targeted erase reach the same Cardputer. It provisions the standalone hub_config range; routine CRUB upgrades must continue to use make upload so stored configuration is preserved. See the installation guide for CRUB staging, standalone migration, and release-asset flashing.

ESP-IDF's flash command normally resets the device automatically. If it cannot enter download mode, hold the G0 button, press and release reset, release G0, and retry the upload. You may need to grant access to the serial device on Linux.


Serial Monitor

make monitor

When multiple serial devices are connected, list the ports with python -m serial.tools.list_ports and select the Cardputer's fixed USB Serial/JTAG port explicitly:

make monitor UPLOAD_PORT=<port>

Replace <port> with the detected device path, such as /dev/ttyACM0 on Linux or /dev/cu.usbmodem... on macOS.

The configured baud rate is 115200. Exit the monitor with Ctrl+].


CI/CD

Pull requests targeting any branch, pushes to main, and manual CI runs execute the make host-check and make firmware-check portions of make check in parallel on Ubuntu 24.04. This includes stacked pull requests whose base is another feature branch. A final required status succeeds only when both paths pass. CI prints the application and per-component size summaries, then uploads those reports with the compiled application and partition-table images as an artifact retained for seven days. Size is observable but does not yet have an arbitrary pass/fail budget. All third-party Actions use full commit SHA pins, and Dependabot proposes reviewed updates.

After CI validates a merged pull request on main, the protected Release firmware workflow uses the source branch prefix to assign the next semantic version: major/ or breaking/ bumps major, feat/ or minor/ bumps minor, and fix/, perf/, or patch/ bumps patch. Other branch types do not release firmware. The first eligible release is v0.1.0. The workflow repeats full validation, embeds release metadata, creates the version tag, and publishes the application image, its matching partition-table image, and SHA256SUMS covering both files.

Only the two newest GitHub Release records and their assets are retained. All official Git tags are preserved. To rebuild an older version, manually run Rebuild tagged firmware with its existing vMAJOR.MINOR.PATCH tag. That read-only workflow checks out the tag and provides the application image, partition table, and checksums as a temporary seven-day artifact; it does not recreate or delete Releases or tags.

The primary local commands are:

make setup         resolve locked repository and ESP-IDF dependencies
make build         compile Cardputer-Adv firmware
make firmware-size report application, partition, and component sizes for build/
make test          run native tests
make format        format owned C/C++ sources
make format-check  verify formatting
make lint          run static analysis
make host-check    run lock, format, lint, and native test checks
make firmware-check
                   build firmware and verify its effective ESP-IDF configuration
make check         run all required validation
make upload UPLOAD_PORT=<port>
                   compile and write Hub into the CRUB extra slot only
make upload-standalone
                   Hub-only factory flash; replaces the CRUB partition table
make migrate-storage-layout UPLOAD_PORT=<device>
                   one-time migration from the earlier standalone flash layout
make monitor       open the 115200-baud serial monitor
make clean         remove ESP-IDF production build output

See docs/ENGINEERING.md for the complete workflow.


Current Status

Reviewed on 2026-09-15. The authoritative phase checklist records completed steps and remaining work; the plan index links implementation and validation history.

Phase Status
1 — System Core Complete
2 — Connectivity Software complete; physical acceptance partial
3 — Core Services HostService, v4 configuration with host/Wi-Fi data, NetworkService, battery and audio delivered; broader Services pending
4 — Application Shell Home, Settings, Launcher, live Wi-Fi status, key feedback, Tab navigation and page transitions delivered; power/remaining cues pending
5 — Mini App Infrastructure Partial; runtime, Launcher, and SYSTEM Mini App delivered; Service lifecycle composition pending
6 — Device Manager Built-in host list/pair/rename/delete/select delivered; full Mini App integration pending
7–12 — Host Control, Companion, Weather, RGB, Remote, Extensions Pending

Normal firmware provides a BLE-only host connection, saved profiles and On/Off, Home telemetry, and local Settings. USB is for power, flashing and fixed serial diagnostics. USB HID and arbitration are removed; IHidTransport remains the future extension boundary. Services can persist bounded per-host platform/capability/template-reference metadata, but the UI does not edit it or execute mappings. On-device Wi-Fi status and manual setup are composed; clock synchronization, Wi-Fi scanning, Action-to-HID mappings, and a Mac companion are not implemented.

Physical checks confirmed fresh pairing, one local Off/On cycle, adding and switching two computers, and reconnection to the last selected host after Reset and power-on. The operator accepted the Home/navigation appearance. Plan 017 keeps longer Off/reboot-Off checks, report/interruption reruns, long-duration and equipment-limited cases, and USB serial hotplug open. Historical harness passes are not represented as final-image acceptance. Latest local checks passed: 72 Python tests, 274 native tests, formatting, static analysis, and a clean ESP-IDF production compilation. The final Plan-026 image is 1,146,080 bytes; detailed application and component reports are available through make firmware-size.


Initial Hardware

Target device:

  • M5Stack Cardputer-Adv

External hardware:

  • M5Stack Unit Puzzle 8×8 WS2812E RGB LED matrix
  • M5Stack Unit NFC (ST25R3916, SKU U216) for the NFC Mini App. It shares Grove port A with the Unit Puzzle, so use one at a time.

Additional sensors and modules may be added later.


License

License has not been selected yet.

About

Extensible firmware for M5Stack Cardputer-Adv with mini apps, Bluetooth host control, a macOS companion, AI usage monitoring, and NFC inventory.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages