Skip to content

Add LILYGO and Waveshare Touch-LCD-2 board support#12

Open
sandman21vs wants to merge 3 commits into
HermannBjorgvin:mainfrom
sandman21vs:lilygo-t-display-s3-support
Open

Add LILYGO and Waveshare Touch-LCD-2 board support#12
sandman21vs wants to merge 3 commits into
HermannBjorgvin:mainfrom
sandman21vs:lilygo-t-display-s3-support

Conversation

@sandman21vs

@sandman21vs sandman21vs commented May 15, 2026

Copy link
Copy Markdown

Summary

  • Ports the firmware to two additional ESP32-S3 boards: LILYGO T-Display S3 Basic and Waveshare ESP32-S3-Touch-LCD-2.
  • Keeps the original Waveshare ESP32-S3-Touch-AMOLED-2.16 target working from the same source tree.
  • Introduces the board abstraction needed for multiple displays, input schemes, power sources, and UI layouts without spreading one-off #ifdefs throughout the app.
image image image image image

What changed

Board support

  • Adds board headers under firmware/src/boards/ for the original Waveshare AMOLED, LILYGO T-Display S3, and Waveshare ESP32-S3-Touch-LCD-2.
  • Adds PlatformIO environments for waveshare_amoled_216, lilygo_t_display_s3, and waveshare_esp32_s3_touch_lcd_2.
  • Updates display_cfg.h and main.cpp so each board selects the right GFX bus/driver, display size, touch driver, backlight behavior, PMU/ADC path, and optional peripherals.

Input and navigation

  • Centralizes physical button handling in buttons.cpp / buttons.h.
  • Keeps the original Waveshare AMOLED button behavior: side buttons for BLE HID Space / Shift+Tab and PMU PKEY for screen cycling.
  • Adds LILYGO two-button navigation with short/long press actions for splash, screen cycling, Space hold, and Shift+Tab.
  • Adds Touch-LCD-2 touch-only navigation because KEY1 is hardware reset and KEY2 shares GPIO0 with BOOT/LCD reset: tap Usage toggles splash, long-press Usage opens Bluetooth, and tap Bluetooth returns to Usage.
  • Fixes the Touch-LCD-2 long-press release path so Usage -> Bluetooth does not immediately bounce back to Usage.

UI

  • Adds ui_layout.h with per-board layout tokens for margins, panel heights, font choices, icon sizes, BLE labels, and credits.
  • Scales the existing UI down for 170x320 and 240x320 panels while preserving the 480x480 AMOLED layout.
  • Bounds and stretches battery, Bluetooth, trash, and Claude Code logo icons so compact panels do not clip or reserve the wrong source-size boxes.
  • Tightens the Touch-LCD-2 Bluetooth screen with shorter Name: / MAC: labels, bounded status text, smaller fonts, and a retained touch reset zone.
  • Updates splash rendering so pixel-art animations scale from the smaller panel edge instead of assuming 480x480.

Power and battery

  • Keeps the AXP2101 PMU path for the original Waveshare AMOLED.
  • Adds ADC-based battery estimation for boards without a PMU using analogReadMilliVolts(), board-specific divider ratios, and a simple LiPo voltage-to-percent curve.
  • Adds USB CDC external-power detection for the Touch-LCD-2 so the UI shows the plugged-in/charging icon while USB is present. This is an external-power hint, not charger IC telemetry.

Build and tooling

  • Splits platformio.ini into a shared [common] block plus per-board environments and dependencies.
  • Updates screenshot.sh to read panel dimensions from the firmware SCREENSHOT_START header instead of hardcoding 480x480.
  • Updates README hardware support and flashing instructions for all supported targets.

Test plan

  • pio run -d firmware -e waveshare_amoled_216 builds clean
  • pio run -d firmware -e lilygo_t_display_s3 builds clean
  • pio run -d firmware -e waveshare_esp32_s3_touch_lcd_2 builds clean
  • Flashed and tested LILYGO T-Display S3: boots, splash animation runs, Usage/Bluetooth render at 170x320, BLE pairing works, and both buttons fire the documented actions
  • Flashed and tested Waveshare ESP32-S3-Touch-LCD-2 over /dev/ttyACM0: Usage/Bluetooth render at 240x320 with scaled logo/icons and corrected Bluetooth layout
  • Verified Touch-LCD-2 touch navigation: long-press Usage -> Bluetooth, release does not bounce back, tap Bluetooth -> Usage
  • Verified Touch-LCD-2 battery UI: plugged-in/charging icon appears with USB connected and battery bars appear when USB is disconnected

Ports the firmware to the LILYGO T-Display S3 (Basic, no touch): 170x320
ST7789 over 8-bit i80 parallel, two front buttons (BOOT on GPIO0, IO14),
battery voltage on GPIO4. No PMU, no IMU, no touch.

The original Waveshare AMOLED 2.16" target is unchanged — both boards
build and flash from the same source tree.

Hardware abstraction
- New firmware/src/boards/board_waveshare_amoled_216.h and
  board_lilygo_t_display_s3.h hold pinouts and capability flags
  (BOARD_HAS_TOUCH / BOARD_HAS_PMU / BOARD_HAS_IMU / BOARD_HAS_BATTERY_ADC).
- firmware/src/display_cfg.h is now a board dispatcher that selects the
  right header from a BOARD_* build flag, pulls in only the libraries
  the chosen hardware needs, and declares a base-class gfx pointer.
- firmware/src/main.cpp instantiates the correct GFX driver
  (Arduino_ESP32QSPI + CO5300 for Waveshare, Arduino_ESP32LCD8 + ST7789
  with column offset 35 for LILYGO) and exposes board_set_brightness()
  that maps to DCS on the AMOLED and to a backlight GPIO on the LCD.

Subsystem guards
- power.cpp: three branches (HAS_PMU AXP2101, HAS_BATTERY_ADC ADC read
  on GPIO4 with placeholder divider — logs raw value at boot for
  calibration, no-op fallback). Same power_*() API for ui.cpp.
- imu.cpp: HAS_IMU compiles the QMI8658 auto-rotation; otherwise
  imu_get_rotation() returns BOARD_FIXED_ROTATION as a constant.
- New buttons module (buttons.cpp/.h) centralizes input. Waveshare keeps
  the original 3-button edge polling. LILYGO gets debounced short/long
  press on GPIO0 and GPIO14: BOOT short toggles splash and hold sends
  Space; IO14 short cycles screens and long sends Shift+Tab.

UI adaptations
- New ui_layout.h holds per-board UI tokens (margins, fonts, panel
  sizes). LILYGO uses font_styrene_24 titles, font_styrene_14 body,
  24x24 stretched battery/BLE icons, label wrap on long device names
  and MAC strings, and explicit screen scroll disable.
- splash.cpp derives cell size from the smaller panel edge so the same
  20x20 pixel-art assets render at 24x scale on Waveshare (480x480) and
  8x scale (160x160 centered) on LILYGO.

Build
- platformio.ini extracts a [common] block; [env:waveshare_amoled_216]
  adds XPowersLib + SensorLib, [env:lilygo_t_display_s3] only depends
  on Arduino_GFX / LVGL / NimBLE / ArduinoJson.
- screenshot.sh now reads the panel dimensions out of the firmware's
  SCREENSHOT_START header instead of hardcoding 480x480, so the same
  tool captures both boards.

Verified on hardware: LILYGO T-Display S3 boots, displays splash + Usage
+ Bluetooth screens at 170x320, BLE pairing with the host daemon works,
buttons drive the documented actions. Battery ADC prints the raw value
so the divider ratio can be calibrated in a follow-up.
- Add a "Supported boards" table listing the Waveshare AMOLED 2.16"
  and the LILYGO T-Display S3 with their PlatformIO envs and the
  peripherals each one drives.
- Add a "Planned boards" section sketching the next candidates
  (Waveshare ESP32-S3 Touch LCD 2.0", ESP32-P4 Smart 86 Box,
  ESP32-P4-WIFI6 3.5" dev board) and a short porting checklist for
  anyone who wants to send a PR for another panel.
- Update the Linux flash steps to show `pio run -e <env> -t upload`
  for both supported boards, and document the LILYGO download-mode
  caveat (hold BOOT, tap RST, release).
- Add a LILYGO note to the macOS flash section pointing at the
  direct `pio` invocation, since `flash-mac.sh` builds the default
  env only.
@sandman21vs sandman21vs changed the title Add support for LILYGO T-Display S3 board Add LILYGO and Waveshare Touch-LCD-2 board support May 16, 2026
@HermannBjorgvin

Copy link
Copy Markdown
Owner

Hey thanks for your contribution! Here's a bit of an update:

I held off on merging any alternative hardware ports because I wanted a good abstraction layer for that. Then @tobby168 came in clutch and delivered that in #25 with instructions in the docs folder on how to implement your port.

We include detailed instructions now which hopefully would allow your clanker to refactor your PR to conform with the new standard, thank you for your patience!

P.S. if you can post a picture of your port that would be appreciated, I enjoy seeing how it looks on people's devices!

@HermannBjorgvin

Copy link
Copy Markdown
Owner

BTW could you possibly split this into two pull requests for each board?

leonardovalim added a commit to leonardovalim/Clawdmeter that referenced this pull request Jul 11, 2026
Evita que o daemon reempurre creds antigas do config por cima do que foi
setado no portal/gesto local.

- Firmware: o portal grava uma flag NVS `src=1` ao salvar (wifi_portal
  handle_save). sprint_net carrega isso em s_from_portal; se setado,
  sprint_net_provision() ignora o push BLE do daemon (log 1x) — o setup local
  é a fonte da verdade. sprint_net_forget() (gesto de reset) limpa a flag,
  voltando a aceitar o daemon. Resolve o conflito firmware-side, sem depender
  do comportamento do daemon.

- Docs: config.example agora explica os DOIS caminhos de provisionamento
  (portal recomendado vs config) e a regra "portal vence"; não misturar.
  CLAUDE.md: sprint_net/wifi_portal no índice de arquivos + gotchas HermannBjorgvin#12 (os
  dois tokens diferentes: asana_token do daemon vs device_token da placa,
  senão 401) e HermannBjorgvin#13 (autoridade do portal, gatilhos, LV_USE_QRCODE).

Fecha a feature de WiFi setup (commits 1–4). Backup no microSD e paridade do
log no macOS seguem pendentes.

Co-Authored-By: Claude Opus 4.8 <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