Skip to content

Add AMOLED-1.8 board + device-abstraction refactor for community ports#25

Merged
HermannBjorgvin merged 2 commits into
HermannBjorgvin:mainfrom
tobby168:upstream-refactor
May 21, 2026
Merged

Add AMOLED-1.8 board + device-abstraction refactor for community ports#25
HermannBjorgvin merged 2 commits into
HermannBjorgvin:mainfrom
tobby168:upstream-refactor

Conversation

@tobby168

Copy link
Copy Markdown
Contributor

Summary

Two related changes that together make the project welcoming to new
board ports:

  1. Add Waveshare ESP32-S3-Touch-AMOLED-1.8 support (the existing
    commit you'd already see in my fork — SH8601 + FT3168 + XCA9554,
    368×448 portrait).
  2. Device-abstraction refactor that follows it: a thin HAL in
    firmware/src/hal/ + per-board folders under firmware/src/boards/
    instead of the build-flag-driven #ifdef BOARD_* sprawl the second
    port introduced. Shared code (main.cpp, ui.cpp, splash.cpp) is
    now #ifdef-free.

After this lands, adding a third board (or letting somebody else add
theirs) is cp -r firmware/src/boards/template firmware/src/boards/<new>/,
edit the TODOs, add a PlatformIO env. No shared file touches.

What changed

HAL (firmware/src/hal/) — public interfaces shared code calls:
display_hal, touch_hal, input_hal, power_hal, imu_hal,
board_caps. Compile-time selection via PlatformIO's build_src_filter,
not virtual functions — no vtable cost.

Per-board folders (firmware/src/boards/):

  • waveshare_amoled_216/ — CO5300 + CST9220 + AXP PKEY + QMI8658 rotation
  • waveshare_amoled_18/ — SH8601 + FT3168 + AXP + XCA9554 (PWR via EXIO4), no rotation
  • template/ — copy-this-to-start-a-port skeleton

Each board owns its board.h (pins, I2C addrs, BOARD_HAS_* flags),
board_init.cpp (Wire.begin + any IO expander release), display.cpp,
touch.cpp, input.cpp, power.cpp, imu.cpp, caps.cpp, plus any
board-private hardware drivers.

Responsive UI: ui.cpp::compute_layout() picks fonts and layout
from board_caps() at runtime — current breakpoints H ≥ 460 → large,
else compact. splash.cpp computes CELL = min(W, H) / 20 instead of
the two hardcoded values from the previous port.

Capability flags: BoardCaps (runtime struct) for things shared
code queries; BOARD_HAS_* (compile-time macros) for things the linker
can dead-strip per board. See docs/porting/capability-flags.md.

Docs: docs/porting/{adding-a-board, hal-contract, capability-flags}.md.

Deleted from src/ root (moved into boards// or hal/):
display_cfg.h, power.{h,cpp}, imu.{h,cpp}, io_expander.{h,cpp}.

Stats

  • 30+ #ifdef BOARD_* blocks across 6 files → 0 in shared code
  • main.cpp shrinks from ~590 lines to ~250
  • ui.cpp Layout struct replaces ~3 separate #ifdef BOARD_AMOLED_18 blocks for fonts/dimensions

Test plan

  • pio run -d firmware -e waveshare_amoled_216 succeeds
  • pio run -d firmware -e waveshare_amoled_18 succeeds
  • AMOLED-1.8 flashed + screenshots verified for splash / usage / bluetooth — pixel-identical to pre-refactor (see screenshots/amoled_18/)
  • AMOLED-2.16 board hardware smoke test (only AMOLED-1.8 was attached during this verification pass)
  • HID buttons (Space / Shift+Tab / PWR) on both boards

Notes for review

  • Other open PRs (Activity screen: live Claude Code session state from hooks #22 Activity screen, Auto-sleep screen after 30 min idle #24 auto-sleep) live on separate
    branches in my fork. They're sequenced — once Activity merges, I'll
    rebase those on top of this refactor; the refactor pattern absorbs
    the Activity-screen #ifdefs cleanly (already done in my downstream).
  • Touch driver policy is documented in docs/porting/hal-contract.md:
    contributors choose any library that fits the contract; the FT3168
    vendored reader pattern is what we used to avoid GPL'ing the project.
  • The HAL is intentionally compile-time-only — build_src_filter
    ensures only one board's display.cpp/touch.cpp/etc. links into
    each env. Runtime BoardCaps is for things UI / main loop need to
    query (button count, has_battery, etc.) so shared code doesn't need
    to branch on board identity at all.

🤖 Generated with Claude Code

@HermannBjorgvin

Copy link
Copy Markdown
Owner

Built and works perfectly on my board. This is a really nice contribution thank you, especially the template and the instructions on adapting other boards, much appreciated!

Port the firmware to a second Waveshare AMOLED board variant alongside
the original 2.16" / 480×480. Selection is at build time via a board
macro; both envs continue to build and flash independently.

Hardware delta vs AMOLED-2.16:
  * Display: SH8601 QSPI (vs CO5300), 368×448 portrait (vs 480² square),
    SCLK on GPIO 11 (vs 38), RST routed through an XCA9554 I/O expander
    (vs direct GPIO).
  * Touch: FT3168 @ 0x38 (vs CST9220 @ 0x5A). Driven by a ~30-line
    inline FocalTech-protocol reader in main.cpp — Waveshare's
    Arduino_DriveBus library is GPLv3 and would force the project's
    license, which the README explicitly avoids.
  * I/O expander: new XCA9554/PCA9554 @ I2C 0x20 gates LCD_RST, TP_RST,
    audio amp enable, and the PWR button. Wrapped in io_expander.{h,cpp}.
    Must initialize BEFORE display/touch or both stay in reset.
  * PMU + IMU: same AXP2101 + QMI8658 as 2.16, so power.cpp/imu.cpp
    largely reuse. IMU is initialized for I2C bus health but rotation
    is fixed at 0° (the portrait panel doesn't benefit from auto-rotate
    and the strip-rotation CPU path is excluded entirely).
  * Buttons: only BOOT (GPIO 0) is a direct GPIO; PWR is read via
    XCA9554 EXIO4 polling. No third button — the AMOLED-2.16's
    Shift+Tab key (GPIO 18) is dropped for this board.
  * Flash: 16MB (vs 8MB), partition table set to default_16MB.csv.

Code structure:
  * display_cfg.h hosts a #ifdef BOARD_AMOLED_18 / #else / #endif split
    with a PlatformDisplay typedef so call sites elsewhere stay clean.
  * main.cpp, power.cpp, ui.cpp, splash.cpp gate board-specific bits on
    the same macro. Layout constants (panel heights, content Y, font
    choices on the Bluetooth screen) compress for the 368×448 portrait
    footprint so two usage panels + bottom animation label fit without
    horizontal overflow.
  * Splash scales the 20×20 pixel-art grid to 360×360 (CELL=18 vs 24)
    and centers in the portrait canvas.
  * Old AMOLED-2.16 env is unchanged at the source level (gated under
    #ifndef BOARD_AMOLED_18) and continues to compile + flash on the
    original hardware.

CLAUDE.md updated with the two-board pin maps, build/flash commands
for both envs (macOS + Linux device paths), and the per-board gotchas.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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 HermannBjorgvin#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>
@tobby168 tobby168 force-pushed the upstream-refactor branch from 7d98f23 to 2035121 Compare May 21, 2026 01:27
@tobby168

Copy link
Copy Markdown
Contributor Author

@HermannBjorgvin I am not able to merge it, can you help? thanks

@HermannBjorgvin HermannBjorgvin merged commit 7ae95a8 into HermannBjorgvin:main May 21, 2026
@HermannBjorgvin

Copy link
Copy Markdown
Owner

Yes merged and rebased the activity-stream branch

jjhuff added a commit to jjhuff/Clawdmeter that referenced this pull request May 21, 2026
New port under firmware/src/boards/lilygo_t4s3/ implementing the HAL
introduced by PR HermannBjorgvin#25:

- RM690B0 QSPI panel with col_offset=16 (visible 450 cols inside 482-wide
  controller RAM), no rotation
- CST226SE touch via SensorLib TouchClassCST226
- SY6970 charger PMU (XPOWERS_CHIP_SY6970); voltage→% approximation
  since the chip is a charger, not a fuel gauge
- PMICEN (GPIO 9) raised in board_init() before display brings up, so
  the panel rail is alive when display_hal_init() probes the QSPI bus
- No IMU, no PMU push-button, no second GPIO button — screen cycling
  maps to a 1500 ms long-hold of BOOT synthesized as a PWR press inside
  power_hal, well above a comfortable voice-mode PTT hold so it doesn't
  trigger accidentally

ui.cpp::compute_layout() gets a narrow-panel override: the BT "Address:"
line at styrene_28 clipped the right margin on the 450-wide panel, so a
single-font swap to styrene_24 kicks in for c.width < 470. The two
reference ports are unchanged (480 ≥ 470; 368 was already in the compact
branch with styrene_20).

Verified on hardware: splash, usage, and BT screens all render correctly;
BLE handshakes with the host daemon and shows Connected with the MAC.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants