Replaces the build-flag-driven #ifdef sprawl (~30 blocks across 6 files)
with a small HAL in firmware/src/hal/ and per-board folders under
firmware/src/boards/. Shared code (main.cpp, ui.cpp, splash.cpp) no
longer contains a single `#ifdef BOARD_*` — optional features are
guarded by BoardCaps (runtime) and BOARD_HAS_* macros (compile-time,
inside the board's own files).
Why: lets community contributors port to new ESP32 + AMOLED + touch
combos by dropping in a boards/<name>/ folder + a PlatformIO env,
without touching shared files. See docs/porting/adding-a-board.md.
Highlights:
- New HAL: display_hal, touch_hal, input_hal, power_hal, imu_hal,
board_caps. Each board provides display.cpp, touch.cpp, input.cpp,
power.cpp, imu.cpp, caps.cpp, board_init.cpp + private hardware
drivers (e.g. io_expander.{h,cpp} on AMOLED-1.8).
- PlatformIO build_src_filter selects each board's folder per env.
- ui.cpp picks fonts and layout from board_caps() via compute_layout()
with screen-height breakpoints (>= 460 → large, else compact).
- splash.cpp computes CELL = min(W,H)/20 — responsive instead of two
hardcoded values.
- idle.cpp (from #24) rewired through display_hal + power_hal — no
longer depends on the deleted display_cfg.h / power.h.
- power_hal gains power_hal_is_vbus_in() for idle's
IDLE_SLEEP_WHEN_CHARGING gate.
- boards/template/ + docs/porting/{adding-a-board,hal-contract,
capability-flags}.md to bootstrap new ports.
- display_cfg.h, power.{h,cpp}, imu.{h,cpp}, io_expander.{h,cpp}
deleted from src/ root (moved into boards/<name>/ or hal/).
Verification: both `pio run -e waveshare_amoled_216` and
`pio run -e waveshare_amoled_18` succeed unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5.1 KiB
Porting Clawdmeter to a new board
A board port is a folder under firmware/src/boards/ plus a new
[env:...] block in firmware/platformio.ini. You should never need
to edit firmware/src/main.cpp, firmware/src/ui.cpp, or anything
under firmware/src/hal/. If you find yourself wanting to, that's a
gap in the HAL — open an issue.
Hardware you need
At minimum:
- An ESP32-S3 (other ESP32 family members may work; this is what the upstream firmware is tested on). OPI PSRAM is required — partial flush buffers and the splash canvas are allocated from PSRAM.
- A QSPI AMOLED panel with a driver supported by GFX Library for Arduino (CO5300, SH8601, NV3041A, etc.). Other interfaces aren't supported yet.
- A touch controller over I2C. The HAL just needs init + read; you can use any driver you can compile.
- A primary button (typically the BOOT/GPIO 0 push button).
Optional:
- A second physical button (e.g. for HID Shift+Tab mode toggle).
- An AXP2101 PMU for battery monitoring + a power button.
- A QMI8658 (or compatible) IMU for automatic rotation.
- An XCA9554 / PCA9554 IO expander if reset / enable lines are routed through one (the AMOLED-1.8 board does this).
Step-by-step
-
Copy the template folder.
cp -r firmware/src/boards/template firmware/src/boards/my_board -
Fill in
boards/my_board/board.h. Replace everyTODOwith your board's pins, I2C addresses, dimensions, and capability flags. The capability flags drive both compile-time dead-stripping in the HAL implementations and runtime UI decisions viaBoardCaps. -
Implement the per-board sources. Each one corresponds to a HAL header in
firmware/src/hal/. Look at one of the reference ports for a worked example:File Reference port (start here) display.cppboards/waveshare_amoled_216/display.cpp(with CPU rotation) or_18/display.cpp(no rotation)touch.cpp_216/touch.cpp(library-based) or_18/touch.cpp(vendored I2C reader)input.cpp_216/input.cpp(two buttons) or_18/input.cpp(one button)power.cpp_216/power.cpp(PMU IRQ) or_18/power.cpp(PMU + IO expander button)imu.cpp_216/imu.cpp(full rotation) or_18/imu.cpp(init-only stub)caps.cppeither reference — just edit the struct literal board_init.cpp_216/board_init.cpp(no expander) or_18/board_init.cpp(with expander) -
Add a PlatformIO env. In
firmware/platformio.ini, copy one of the existing[env:waveshare_amoled_*]blocks and adjust:[env:my_board] ; ... platform / board / framework as before ... build_src_filter = +<*> -<boards/> +<boards/my_board/> ; the only line you change here build_flags = -DBOARD_MY_BOARD ; identity-only — the shared code never ; branches on this; per-board code mayIf your panel needs flash > 4 MB (extra animations, larger fonts), copy the
board_upload.*block from the AMOLED-1.8 env. -
Build.
pio run -d firmware -e my_board. The link step is the real verification — any missing HAL symbol or duplicated definition shows up here. -
Flash + smoke test. The first boot should land on the splash screen. If it doesn't, check
pio device monitorfor HAL init messages — every reference port logs OK / failure for display, touch, PMU, IMU duringsetup(). -
Visual QA.
./screenshot.sh out.pngover USB serial captures the live framebuffer at the active resolution. The UI is responsive (see hal-contract.md for breakpoint details); most ports will look acceptable out of the box. If your screen size doesn't match an existing breakpoint, you may want to add one tocompute_layout()infirmware/src/ui.cpp.
Common pitfalls
- Display stays black, no panic. Usually one of: OPI PSRAM not enabled
in platformio.ini (check
board_build.arduino.memory_type = qio_opi); IO expander not released beforegfx->begin()(runio_expander_init()fromboard_init()); GFX library version too old to know about your panel chip. - Touch reads zeros / wrong coordinates. The HAL hands LVGL whatever
the controller reports — apply any axis swap / mirror inside your
touch.cpp. CST9220 needssetSwapXY(true)+setMirrorXY(true, false)on the AMOLED-2.16 board; your controller will likely differ. - GPL warning when picking a touch driver. The project intentionally
avoids copyleft dependencies. If the only available library is GPL,
vendor a minimal I2C reader instead (see
_18/touch.cpp). - Both boards built fine but one runs and the other doesn't. The
build_src_filter is per-env — re-check you copied the existing env
blocks correctly and the
-<boards/>then+<boards/your_one/>ordering is right (filters apply in declaration order).