From 4fb44f401b53f5faad39240cee637d7ae3050de4 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 28 Jul 2026 12:06:38 -0600 Subject: [PATCH 1/3] docs(knowledge): ESP32-S3 1.8 AMOLED host notes (CO5300, corners, boot) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Capture field lessons from first metal on the Waveshare/Amazon 1.8″ QSPI AMOLED class so the next agent does not re-discover black panels, PSRAM aborts, or clipped banner labels on rounded glass. --- silico/knowledge/INDEX.md | 1 + silico/knowledge/README.md | 1 + silico/knowledge/esp32s3-amoled-1.8.md | 120 +++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 silico/knowledge/esp32s3-amoled-1.8.md diff --git a/silico/knowledge/INDEX.md b/silico/knowledge/INDEX.md index 9d4d847..78523d8 100644 --- a/silico/knowledge/INDEX.md +++ b/silico/knowledge/INDEX.md @@ -6,6 +6,7 @@ | esp32-usb-serial | ESP32 USB-UART duplex ladder, lockout recovery, UART0 footgun | [esp32-usb-serial.md](esp32-usb-serial.md) | | esp32-audio | ESP32 DAC lifecycle, soft-park silence, smooth PCM / music; IDF dac_continuous queue/drain | [esp32-audio.md](esp32-audio.md) | | esp32-lcd-ips | SPI IPS color pack (R/B, INVON), partial blit | [esp32-lcd-ips.md](esp32-lcd-ips.md) | +| esp32s3-amoled-1.8 | Waveshare/Amazon 1.8″ AMOLED: CO5300 QSPI, PSRAM, rounded-corner inset, post-flash black | [esp32s3-amoled-1.8.md](esp32s3-amoled-1.8.md) | | m5-core | M5GO/Core face pins, buttons, MPU6886 WHO_AM_I + temp formula | [m5-core.md](m5-core.md) | | deploy-assets | Large binary asset deploy + size verify | [deploy-assets.md](deploy-assets.md) | | m5go-power | M5GO / Core v2.7 red button on/off (double-click off) | [m5go-power.md](m5go-power.md) | diff --git a/silico/knowledge/README.md b/silico/knowledge/README.md index a378f14..e8fd6f3 100644 --- a/silico/knowledge/README.md +++ b/silico/knowledge/README.md @@ -10,6 +10,7 @@ Product domain (idle control, drone songs, vehicle acceptance) stays in the **GC |------|------| | ESP32 / DAC / speaker / PWM tone / sample playback | [esp32-audio.md](esp32-audio.md) | | SPI IPS color / INVON / partial paint | [esp32-lcd-ips.md](esp32-lcd-ips.md) | +| ESP32-S3 1.8″ QSPI AMOLED (Waveshare/Amazon class) | [esp32s3-amoled-1.8.md](esp32s3-amoled-1.8.md) | | M5GO / Core face pins, buttons, MPU6886 temp | [m5-core.md](m5-core.md) | | Large binary assets (PCM, images) deploy verify | [deploy-assets.md](deploy-assets.md) | | M5GO / Core v2.7 power button | [m5go-power.md](m5go-power.md) | diff --git a/silico/knowledge/esp32s3-amoled-1.8.md b/silico/knowledge/esp32s3-amoled-1.8.md new file mode 100644 index 0000000..045634b --- /dev/null +++ b/silico/knowledge/esp32s3-amoled-1.8.md @@ -0,0 +1,120 @@ +# ESP32-S3 1.8″ Touch AMOLED (Waveshare / Amazon class) + +**Board class:** ESP32-S3-Touch-AMOLED-1.8 (e.g. Amazon ASIN B0F242GFHK, Waveshare ESP32-S3-Touch-AMOLED-1.8). +**Native panel:** **368 × 448** QSPI AMOLED, capacitive touch, **8 MB OPI PSRAM**, **16 MB** flash. +**Product UI often:** landscape **448 × 368** with USB + hard keys on the **top** edge (rotate content, not the panel timings). + +Open this file when bringing up a GCU face on this board class. Do not invent pin maps from chat. + +## Controllers (read this first) + +Current Waveshare demos drive the panel with **CO5300** over **QSPI**, not the older-only SH8601 path. + +| Revision signal | How to detect | Driver / notes | +|-----------------|---------------|----------------| +| **V2** (newer) | I2C probe **0x15** (CST816) responds | Use **CO5300** + optional `x_gap = 0x10` | +| **Original** | 0x15 absent; touch often **FT3168** at other addr | Still **CO5300** init sequence from Waveshare colorbar in practice | + +**Field lesson (Aether first metal, 2026-07):** +- `waveshare/esp_lcd_sh8601` can report “create success” and still leave a **black** panel. +- Port the official **`13_display_colorbar`** path: `espressif/esp_lcd_co5300` + Waveshare init table works on IDF **5.3.2**. +- Newer Waveshare ESP-IDF examples ask for IDF **≥ 5.5** for BSP packages; the **CO5300 component alone** is enough for a custom GCU on 5.3.x. + +IDF component (pinned by product): + +```text +espressif/esp_lcd_co5300: "^1.0.0" # resolved 1.0.2 on IDF 5.3.2 +``` + +Do **not** require IDF 5.5 solely to light the panel. + +## Pins (display QSPI) + +| Signal | GPIO | +|--------|------| +| CS | 12 | +| PCLK / SCLK | 11 | +| D0 | 4 | +| D1 | 5 | +| D2 | 6 | +| D3 | 7 | +| RST | not wired (software reset) | + +Touch / sensors I2C: **SDA 15**, **SCL 14**. + +SPI host: typically **SPI2_HOST**. RGB565, QSPI mode. + +## Host / silico + +| Item | Fact | +|------|------| +| USB-Serial/JTAG | Preferred serial on Windows often **COM** with vid `303a` pid `1001` | +| Identity (C plate) | App must answer host word `identity` with `fw_name=… fw_version=…` on the link | +| Deploy | `silico deploy --port COMx --yes` → `idf.py -C firmware -p COMx flash` | +| PSRAM | Enable **SPIRAM OCT 80 MHz** + `SPIRAM_USE_MALLOC` for full-frame RGB565 buffers (~330 KB × N) | +| Flash size | Configure **16 MB** in sdkconfig (defaults matter; old 2 MB images mislead tools) | + +## Framebuffers + +Full native RGB565 frame is **368 × 448 × 2 ≈ 330 KB**. Dual logical+panel buffers ≈ **660 KB** — **PSRAM required** or first boot `abort()`s on `heap_caps_malloc` failure. + +Pattern that worked: + +1. Draw product face in **logical landscape** (e.g. 448×368). +2. Blit with **90° rotation** into panel buffer. +3. Push panel buffer in **horizontal stripes** (height even; 16 px is fine). +4. Byte-swap RGB565 for QSPI the same way Waveshare colorbar does (`SPI_SWAP_DATA_TX` / hi-lo swap). + +### Rotation + +If the face is **upside-down**, flip the blit (CW vs CCW), do not re-layout the product UI. +Canonical product intent: USB + MODE/units labels on the **top** edge of the landscape face. + +### Rounded corners (chrome inset) + +The physical AMOLED has **rounded corners**. Banner labels flush to x=0 / x=W−1 **clip** (e.g. only the last stem of “M” in MODE visible). + +**Rule:** inset left/right banner text by roughly **one large glyph** (~24–32 device px at 448-wide face). Do **not** move labels down to fix clipping — only horizontal inset. + +## First-boot black after flash + +Observed: after `idf.py` / `silico deploy` USB-JTAG reset, panel stays **black/noop** until **unplug/replug** power. + +Mitigations that help in firmware: + +1. Short settle (**~80 ms**) between `panel_reset` and `panel_init`. +2. Sleep-out delay in init table (**~100 ms** on 0x11) as in Waveshare colorbar. +3. `disp_on_off(true)` then another short delay + **second** `disp_on_off(true)`. +4. Host: if inspect only sees identity but operator reports black face, ask for a **power cycle**, then re-check — do not thrash full-erase redeploys for “blank” alone when identity is healthy. + +If identity fails and serial shows `abort()` at framebuffer alloc: **PSRAM not enabled** in sdkconfig (defaults not applied until clean reconfigure). + +## Units hard key + +Module **BOOT** is often **GPIO0** (active low, internal pull-up). Map product “right key / LAMBDA|AFR toggle” to that for host demos when physical product keys are not yet wired in firmware. + +## Init sequence reference (CO5300 QSPI) + +Copy from Waveshare `examples/esp-idf/13_display_colorbar` (command table with `0xFE/0xC4/0x3A/…/0x11 sleep out/0x29 display on`, brightness `0x51=0xFF`). Keep that table in the GCU, not reinvented from memory. + +## What “good” looks like on metal + +- Panel shows a non-black face after power-on (or after power cycle if first post-flash boot was dark). +- Landscape product face: dial + primary mixture number + unit + RPM/TPS. +- Banner MODE / units labels fully legible (inset past rounded corners). +- `silico inspect --port COMx` → `fw_name` / `fw_version` match host. + +## Anti-patterns + +- Assuming SH8601 because the product listing says “SH8601” — **probe behavior and Waveshare current demos**. +- Full internal-RAM double framebuffer without PSRAM. +- Flushing full-panel transfers larger than SPI `max_transfer_sz` without striping. +- Banner text at x=0 on rounded AMOLED. +- Treating post-flash black + healthy identity as “flash failed” and erasing again without a power cycle. + +## See also + +- [esp32-lcd-ips.md](esp32-lcd-ips.md) — SPI IPS (different class) +- [esp32-usb-serial.md](esp32-usb-serial.md) — duplex / console +- [first-flash.md](first-flash.md) — esptool path +- Upstream: [waveshareteam/ESP32-S3-Touch-AMOLED-1.8](https://github.com/waveshareteam/ESP32-S3-Touch-AMOLED-1.8) From 35bc1bc6fe2365ba3e2fcd515f5b15369b4937f7 Mon Sep 17 00:00:00 2001 From: Tig Date: Tue, 28 Jul 2026 17:34:26 -0600 Subject: [PATCH 2/3] docs(knowledge): AMOLED 1.8 upright map is CCW (closes field upside-down) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Document operator-confirmed CCW 90° present formulas for ESP32-S3 Touch AMOLED 1.8 class. CW left the Aether LVGL face upside-down; agents must flip present only, not re-layout UI. Closes #107 --- silico/knowledge/esp32s3-amoled-1.8.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/silico/knowledge/esp32s3-amoled-1.8.md b/silico/knowledge/esp32s3-amoled-1.8.md index 045634b..4392689 100644 --- a/silico/knowledge/esp32s3-amoled-1.8.md +++ b/silico/knowledge/esp32s3-amoled-1.8.md @@ -65,11 +65,19 @@ Pattern that worked: 3. Push panel buffer in **horizontal stripes** (height even; 16 px is fine). 4. Byte-swap RGB565 for QSPI the same way Waveshare colorbar does (`SPI_SWAP_DATA_TX` / hi-lo swap). -### Rotation +### Rotation (operator-confirmed upright map) -If the face is **upside-down**, flip the blit (CW vs CCW), do not re-layout the product UI. Canonical product intent: USB + MODE/units labels on the **top** edge of the landscape face. +| Direction | Panel ← logical (forward) | Logical ← panel (inverse, for present loops) | +|-----------|---------------------------|-----------------------------------------------| +| **CCW 90° (default upright for this board class)** | `px = FACE_H - 1 - ly`, `py = lx` | `lx = py`, `ly = FACE_H - 1 - px` | +| CW 90° (wrong on Aether first metal — face upside-down) | `px = ly`, `py = FACE_W - 1 - lx` | `lx = FACE_W - 1 - py`, `ly = px` | + +**Field lesson (Aether LVGL face, 2026-07):** first metal + first LVGL pass both shipped **CW** and the operator reported **upside-down**. Switching present() to **CCW** fixed it. Agents: default to **CCW** on this board class; if the face is upside-down, flip CW↔CCW in the present path only — do **not** re-layout product UI. + +Document any counter-example (unit that needs CW) here with date + product, do not leave it only in chat. + ### Rounded corners (chrome inset) The physical AMOLED has **rounded corners**. Banner labels flush to x=0 / x=W−1 **clip** (e.g. only the last stem of “M” in MODE visible). From 0c86103439b70e97ee938b55ab2da88b02a00f5c Mon Sep 17 00:00:00 2001 From: Tig Date: Wed, 29 Jul 2026 08:34:41 -0600 Subject: [PATCH 3/3] docs(knowledge): address PR #105 CR on AMOLED 1.8 notes Split SH8601 vs CO5300 by revision, drop product-domain controls from the board guide, and use the canonical product face term. --- silico/knowledge/esp32s3-amoled-1.8.md | 65 +++++++++++++++----------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/silico/knowledge/esp32s3-amoled-1.8.md b/silico/knowledge/esp32s3-amoled-1.8.md index 4392689..64cd43d 100644 --- a/silico/knowledge/esp32s3-amoled-1.8.md +++ b/silico/knowledge/esp32s3-amoled-1.8.md @@ -4,29 +4,30 @@ **Native panel:** **368 × 448** QSPI AMOLED, capacitive touch, **8 MB OPI PSRAM**, **16 MB** flash. **Product UI often:** landscape **448 × 368** with USB + hard keys on the **top** edge (rotate content, not the panel timings). -Open this file when bringing up a GCU face on this board class. Do not invent pin maps from chat. +Open this file when bringing up a GCU **product face** on this board class. Do not invent pin maps from chat. ## Controllers (read this first) -Current Waveshare demos drive the panel with **CO5300** over **QSPI**, not the older-only SH8601 path. +Driver selection is by **hardware revision**, not by the retail listing name alone. Product pages often say “SH8601” for the whole SKU family; that is not enough to pick the init path. | Revision signal | How to detect | Driver / notes | |-----------------|---------------|----------------| -| **V2** (newer) | I2C probe **0x15** (CST816) responds | Use **CO5300** + optional `x_gap = 0x10` | -| **Original** | 0x15 absent; touch often **FT3168** at other addr | Still **CO5300** init sequence from Waveshare colorbar in practice | +| **V2** (newer) | I2C probe **0x15** (CST816) responds | **CO5300** over QSPI + optional `x_gap = 0x10`. Port Waveshare **`13_display_colorbar`**: `espressif/esp_lcd_co5300` + Waveshare init table (works on IDF **5.3.2**). | +| **Original** | 0x15 absent; touch often **FT3168** at another address | **SH8601** path (`waveshare/esp_lcd_sh8601` or equivalent). Do **not** point original units at the V2 CO5300 colorbar sequence. | -**Field lesson (Aether first metal, 2026-07):** -- `waveshare/esp_lcd_sh8601` can report “create success” and still leave a **black** panel. -- Port the official **`13_display_colorbar`** path: `espressif/esp_lcd_co5300` + Waveshare init table works on IDF **5.3.2**. -- Newer Waveshare ESP-IDF examples ask for IDF **≥ 5.5** for BSP packages; the **CO5300 component alone** is enough for a custom GCU on 5.3.x. +**Field lesson (first metal, 2026-07, V2-class unit):** +- On units that match the V2 probe, `waveshare/esp_lcd_sh8601` can report “create success” and still leave a **black** panel. +- On that revision, the **CO5300** colorbar path lights the panel; SH8601 alone is the wrong fallback. +- Newer Waveshare ESP-IDF examples ask for IDF **≥ 5.5** for BSP packages; the **CO5300 component alone** is enough for a custom GCU on 5.3.x when the unit is V2. -IDF component (pinned by product): +IDF component when the unit is V2 / CO5300 (pinned by product): ```text espressif/esp_lcd_co5300: "^1.0.0" # resolved 1.0.2 on IDF 5.3.2 ``` -Do **not** require IDF 5.5 solely to light the panel. +Do **not** require IDF 5.5 solely to light the panel. +Do **not** apply the V2 CO5300 sequence to original / FT3168 units — keep those on **SH8601**. ## Pins (display QSPI) @@ -60,29 +61,29 @@ Full native RGB565 frame is **368 × 448 × 2 ≈ 330 KB**. Dual logical+panel b Pattern that worked: -1. Draw product face in **logical landscape** (e.g. 448×368). +1. Draw **product face** in **logical landscape** (e.g. 448×368). 2. Blit with **90° rotation** into panel buffer. 3. Push panel buffer in **horizontal stripes** (height even; 16 px is fine). 4. Byte-swap RGB565 for QSPI the same way Waveshare colorbar does (`SPI_SWAP_DATA_TX` / hi-lo swap). ### Rotation (operator-confirmed upright map) -Canonical product intent: USB + MODE/units labels on the **top** edge of the landscape face. +Canonical board intent: USB + banner chrome on the **top** edge of the landscape **product face**. | Direction | Panel ← logical (forward) | Logical ← panel (inverse, for present loops) | |-----------|---------------------------|-----------------------------------------------| | **CCW 90° (default upright for this board class)** | `px = FACE_H - 1 - ly`, `py = lx` | `lx = py`, `ly = FACE_H - 1 - px` | -| CW 90° (wrong on Aether first metal — face upside-down) | `px = ly`, `py = FACE_W - 1 - lx` | `lx = FACE_W - 1 - py`, `ly = px` | +| CW 90° (wrong on first metal — **product face** upside-down) | `px = ly`, `py = FACE_W - 1 - lx` | `lx = FACE_W - 1 - py`, `ly = px` | -**Field lesson (Aether LVGL face, 2026-07):** first metal + first LVGL pass both shipped **CW** and the operator reported **upside-down**. Switching present() to **CCW** fixed it. Agents: default to **CCW** on this board class; if the face is upside-down, flip CW↔CCW in the present path only — do **not** re-layout product UI. +**Field lesson (LVGL product face, 2026-07):** first metal + first LVGL pass both shipped **CW** and the operator reported **upside-down**. Switching present() to **CCW** fixed it. Agents: default to **CCW** on this board class; if the **product face** is upside-down, flip CW↔CCW in the present path only — do **not** re-layout product UI. -Document any counter-example (unit that needs CW) here with date + product, do not leave it only in chat. +Document any counter-example (unit that needs CW) here with date + board revision, do not leave it only in chat. ### Rounded corners (chrome inset) -The physical AMOLED has **rounded corners**. Banner labels flush to x=0 / x=W−1 **clip** (e.g. only the last stem of “M” in MODE visible). +The physical AMOLED has **rounded corners**. Banner labels flush to x=0 / x=W−1 **clip** (e.g. only the last stem of a wide glyph visible). -**Rule:** inset left/right banner text by roughly **one large glyph** (~24–32 device px at 448-wide face). Do **not** move labels down to fix clipping — only horizontal inset. +**Rule:** inset left/right banner text by roughly **one large glyph** (~24–32 device px at 448-wide **product face**). Do **not** move labels down to fix clipping — only horizontal inset. ## First-boot black after flash @@ -93,32 +94,42 @@ Mitigations that help in firmware: 1. Short settle (**~80 ms**) between `panel_reset` and `panel_init`. 2. Sleep-out delay in init table (**~100 ms** on 0x11) as in Waveshare colorbar. 3. `disp_on_off(true)` then another short delay + **second** `disp_on_off(true)`. -4. Host: if inspect only sees identity but operator reports black face, ask for a **power cycle**, then re-check — do not thrash full-erase redeploys for “blank” alone when identity is healthy. +4. Host: if inspect only sees identity but operator reports a black **product face**, ask for a **power cycle**, then re-check — do not thrash full-erase redeploys for “blank” alone when identity is healthy. If identity fails and serial shows `abort()` at framebuffer alloc: **PSRAM not enabled** in sdkconfig (defaults not applied until clean reconfigure). -## Units hard key +## BOOT / user button (GPIO0) -Module **BOOT** is often **GPIO0** (active low, internal pull-up). Map product “right key / LAMBDA|AFR toggle” to that for host demos when physical product keys are not yet wired in firmware. +Module **BOOT** is often **GPIO0** (active low, internal pull-up). Treat it as a generic user button on this board class when product hard keys are not yet wired. **Map product meaning in the GCU** — do not hard-code vertical control labels in board knowledge. -## Init sequence reference (CO5300 QSPI) +## Init sequence reference -Copy from Waveshare `examples/esp-idf/13_display_colorbar` (command table with `0xFE/0xC4/0x3A/…/0x11 sleep out/0x29 display on`, brightness `0x51=0xFF`). Keep that table in the GCU, not reinvented from memory. +| Revision | Where to copy from | +|----------|--------------------| +| **V2 / CO5300** | Waveshare `examples/esp-idf/13_display_colorbar` (command table with `0xFE/0xC4/0x3A/…/0x11` sleep out / `0x29` display on, brightness `0x51=0xFF`) | +| **Original / SH8601** | Waveshare SH8601 example / `esp_lcd_sh8601` init for that revision — not the CO5300 colorbar table | + +Keep the chosen table in the GCU, not reinvented from memory. ## What “good” looks like on metal -- Panel shows a non-black face after power-on (or after power cycle if first post-flash boot was dark). -- Landscape product face: dial + primary mixture number + unit + RPM/TPS. -- Banner MODE / units labels fully legible (inset past rounded corners). +- Panel shows a non-black **product face** after power-on (or after power cycle if first post-flash boot was dark). +- Landscape **product face** paints correctly (logical 448×368 present, upright with USB on the top edge). +- Banner chrome fully legible (inset past rounded corners). - `silico inspect --port COMx` → `fw_name` / `fw_version` match host. +Product-specific dials, units, and key semantics belong in the **GCU**, not here. + ## Anti-patterns -- Assuming SH8601 because the product listing says “SH8601” — **probe behavior and Waveshare current demos**. +- Applying **one** init path to both revisions: original/FT3168 needs **SH8601**; V2/CST816 needs **CO5300**. Listing text alone is not a probe. +- On V2 units, falling back to SH8601 after a black panel and calling that “done” without trying the CO5300 colorbar path. +- On original units, forcing the V2 CO5300 sequence and black-screening a panel that would work on SH8601. - Full internal-RAM double framebuffer without PSRAM. - Flushing full-panel transfers larger than SPI `max_transfer_sz` without striping. - Banner text at x=0 on rounded AMOLED. -- Treating post-flash black + healthy identity as “flash failed” and erasing again without a power cycle. +- Treating post-flash black + healthy identity as “flash failed” and erasing again without a power cycle. +- Embedding one GCU’s vertical controls or acceptance UI in this board topic. ## See also