Migrate to Waveshare ESP32-S3-Touch-AMOLED-2.16 with auto-rotation, splash, and battery

Full hardware swap from Panlee SC01 Plus (480×320 IPS) to Waveshare 2.16"
square AMOLED (480×480, CO5300 + CST9220 + AXP2101 + QMI8658). Library
stack moves to Arduino_GFX, SensorLib, XPowersLib on the pioarduino
platform 55.03.38-1 (Arduino Core 3.x).

UI:
- 4 screens (splash, usage, controller, bluetooth) with 3-button physical
  navigation: GPIO 0 = prev, AXP PKEY = cycle, GPIO 18 = next.
- IMU-driven 90° auto-rotation. CO5300 can't rotate via MADCTL, so flush
  callback does CPU strip-rotation in PARTIAL render mode. Rotation
  transitions use AMOLED brightness flash (instant black → redraw → ramp).
- Battery indicator (Lucide icons) in upper-right, RGB565A8 alpha so it
  blends over the splash animations.
- USB plugged/unplugged auto-switches between Usage and Controller
  screens (suppressed while on splash).
- Fonts and icons re-scaled ~1.9× for the higher-DPI panel; 20px margins
  to clear rounded corners.

Splash:
- 13 × 20×20 pixel-art creature animations sourced from
  claudepix.vercel.app via tools/scrape_claudepix.js (scraper handles
  both PRESET creature-engine and standalone FRAMES+PAL formats).
  tools/convert_to_c.js emits firmware/src/splash_animations.h.
  Attribution preserved in README and the generated header.

Tooling:
- tools/png_to_lvgl.js converts alpha PNGs to LVGL RGB565A8 (planar
  layout, with --tint to colorize Lucide black-on-transparent sources).
- tools/scrape_claudepix.js, tools/convert_to_c.js for the splash
  pipeline.

Docs:
- New worktree CLAUDE.md with hardware pin map, file inventory, build
  commands, and the 8 critical gotchas (CO5300 rotation, OPI PSRAM,
  pioarduino requirement, LVGL 9 font patching, centralized touch read,
  even-aligned flush regions, touch swap/mirror values, RGB565A8 layout).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Hermann Björgvin Haraldsson
2026-05-09 22:04:04 +00:00
co-authored by Claude Opus 4.7
parent 10b8052bcc
commit 97a5443601
65 changed files with 121542 additions and 801 deletions
+99
View File
@@ -0,0 +1,99 @@
#include "power.h"
#include "display_cfg.h"
#include <Arduino.h>
// Poll intervals
#define BATTERY_POLL_MS 2000
#define VBUS_POLL_MS 500
static int cached_pct = -1;
static bool cached_charging = false;
static bool cached_usb = false;
static bool usb_changed_flag = false;
static bool pwr_pressed_flag = false;
static uint32_t last_battery_ms = 0;
static uint32_t last_vbus_ms = 0;
static uint32_t last_pwr_ms = 0;
#define PWR_POLL_MS 50
void power_init(void) {
if (!pmu.begin(Wire, AXP2101_ADDR, IIC_SDA, IIC_SCL)) {
Serial.println("AXP2101 init failed");
return;
}
Serial.println("AXP2101 init OK");
// Enable battery and VBUS ADC measurements
pmu.enableBattDetection();
pmu.enableVbusVoltageMeasure();
pmu.enableBattVoltageMeasure();
// Enable PWR button short-press IRQ (mid button for cycling screens)
pmu.disableIRQ(XPOWERS_AXP2101_ALL_IRQ);
pmu.clearIrqStatus();
pmu.enableIRQ(XPOWERS_AXP2101_PKEY_SHORT_IRQ);
// Read initial state
cached_usb = pmu.isVbusIn();
cached_charging = pmu.isCharging();
cached_pct = pmu.getBatteryPercent();
}
void power_tick(void) {
uint32_t now = millis();
// Poll VBUS status
if (now - last_vbus_ms >= VBUS_POLL_MS) {
last_vbus_ms = now;
bool usb_now = pmu.isVbusIn();
if (usb_now != cached_usb) {
cached_usb = usb_now;
usb_changed_flag = true;
}
cached_charging = pmu.isCharging();
}
// Poll battery level
if (now - last_battery_ms >= BATTERY_POLL_MS) {
last_battery_ms = now;
cached_pct = pmu.getBatteryPercent();
}
// Poll PWR button (AXP2101 short-press IRQ)
if (now - last_pwr_ms >= PWR_POLL_MS) {
last_pwr_ms = now;
pmu.getIrqStatus();
if (pmu.isPekeyShortPressIrq()) {
pwr_pressed_flag = true;
}
pmu.clearIrqStatus();
}
}
int power_battery_pct(void) {
return cached_pct;
}
bool power_is_charging(void) {
return cached_charging;
}
bool power_is_usb_connected(void) {
return cached_usb;
}
bool power_usb_changed(void) {
if (usb_changed_flag) {
usb_changed_flag = false;
return true;
}
return false;
}
bool power_pwr_pressed(void) {
if (pwr_pressed_flag) {
pwr_pressed_flag = false;
return true;
}
return false;
}