Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions .github/firmware-targets.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,6 @@
"flash_freq": "80m",
"bootloader_offset": "0x0000"
},
{
"env": "esp32-s3-E1004",
"family": "esp32",
"chip": "esp32-s3",
"flash_size": "32MB",
"flash_mode": "dio",
"flash_freq": "80m",
"bootloader_offset": "0x0000"
},
{
"env": "esp32-c6-N4",
"family": "esp32",
Expand Down
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pio run # build every environment

Common envs: `nrf52840custom`, `esp32-s3-N16R8`, `esp32-s3-N8R8`,
`esp32-c3-N16`, `esp32-c6-N4`. CI (`.github/workflows/main.yaml`) builds every
environment in `.github/firmware-targets.json` on every push — **12** of them —
keep them all green. Note `platformio.ini`'s `default_envs` lists only 11:
environment in `.github/firmware-targets.json` on every push — **11** of them —
keep them all green. Note `platformio.ini`'s `default_envs` lists only 10:
`esp32-wrover-e-N4R8` ships but is NOT in it, so a bare `pio run` silently
skips the target most likely to catch a broken `#ifndef OPENDISPLAY_HAS_WIFI`
path. Build it explicitly (`pio run -e esp32-wrover-e-N4R8`) before claiming a
Expand Down
30 changes: 19 additions & 11 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,19 @@ default_envs =
esp32-s3-N32R8
esp32-s3-N32R8-extuart
esp32-s3-N16R8-extuart
esp32-s3-E1004
esp32-c3-N4
esp32-c3-N16
esp32-c6-N4
esp32-N4

[env]
lib_deps =
https://github.com/bitbank2/bb_epaper.git
h2zero/NimBLE-Arduino@^2.5.0
; Pinned: bb_epaper has no releases, so an unpinned URL means CI and local
; builds get whatever HEAD was at install time. 5dccfbb is "Added support for
; the Seeed reTerminal E1004 and its 13.3 Spectra6 1200x1600 panel" -- the
; commit that landed E1004 upstream, replacing the limengdu fork pin.
https://github.com/bitbank2/bb_epaper.git#5dccfbb
h2zero/NimBLE-Arduino@^2.5.0
extra_scripts =
pre:scripts/factory_config_gen.py
build_src_filter =
Expand Down Expand Up @@ -303,16 +306,21 @@ board_upload.maximum_ram_size = 327680
board_upload.flash_size = 32MB
monitor_speed = 115200

; Seeed reTerminal E1004 — same hardware as N32R8-extuart; pinned bb_epaper for T133A01.
[env:esp32-s3-E1004]
; Debug logging on the same hardware. Not in default_envs and not in
; .github/firmware-targets.json -- like esp32-s3-N16R8-extuart-debug, this is a
; bench build, not a shipped target. Worth having here specifically because
; -extuart builds print no panic backtrace over USB (logging goes out GPIO43/44),
; so debug-level logs are the only running commentary during panel bring-up.
[env:esp32-s3-N32R8-extuart-debug]
extends = env:esp32-s3-N32R8-extuart
; Pin bb_epaper to limengdu PR bitbank2#32 (T133A01) until merged upstream.
lib_deps =
https://github.com/limengdu/bb_epaper.git#95fd94afe39cd7db32bef7c70eea06d654264ff6
h2zero/NimBLE-Arduino@^2.5.0
build_flags =
${env:esp32-s3-N32R8-extuart.build_flags}
-DOPENDISPLAY_ZLIB_WINDOW_BITS=15
${env:esp32-s3-N32R8-extuart.build_flags}
-DOD_LOG_LEVEL=OD_LOG_DEBUG

; The Seeed reTerminal E1004 has no env of its own: it is the same hardware as
; esp32-s3-N32R8-extuart, and its 13.3" Spectra6 panel is selected at runtime by
; panel_ic_type in the device config, not at build time. Flash it with
; esp32-s3-N32R8-extuart (or the -debug env above for bring-up).

; ESP targets pin pioarduino 55.03.39 (Arduino 3.3.9 / IDF 5.5.4), by exact version
; so it cannot drift CI vs local.
Expand Down
59 changes: 30 additions & 29 deletions src/boot_screen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <bb_epaper.h>
#include "qr/qrcode.h"
#include "display_service.h"
#include "split_panel.h"
#include "od_log.h"
#if __has_include("logo_bitmap.h")
#include "logo_bitmap.h"
Expand Down Expand Up @@ -905,10 +906,14 @@ bool writeBootScreenWithQr() {
int textStartY = textY;
const uint16_t footerInfoY = (uint16_t)(footerY0 + (footerPadTop + footerInfoH - 7 * footerInfoScale) / 2);

// Dual-controller E1004 (bwgbry_split): left half-plane then right (continuous DTM).
const bool e1004Stream = e1004_panel_used();
const int e1004HalfPasses = e1004Stream ? 2 : 1;
const uint16_t e1004HalfPitch = (uint16_t)(pitch / 2);
// Dual-controller panels (bwgbry_split): paint and emit the left half-plane,
// then the right. That is exactly the wire order splitPanelSinkBytes() expects,
// so the halves land in the right columns of the framebuffer with no extra
// bookkeeping here -- and painting one half at a time still avoids rasterising
// the full frame twice.
const bool splitStream = splitPanelUsed();
const int splitHalfPasses = splitStream ? 2 : 1;
const uint16_t splitHalfPitch = (uint16_t)(pitch / 2);

uint8_t* row = staticRowBuffer;
// bb_epaper 4-gray (scheme 5) needs the packed 2bpp image split into two
Expand All @@ -925,31 +930,25 @@ bool writeBootScreenWithQr() {
return false;
}
const int planePasses = (gray4Split || colorSwatchPlane1) ? 2 : 1;
for (int halfPass = 0; halfPass < e1004HalfPasses; halfPass++) {
if (e1004Stream) {
if (halfPass == 0) {
if (!e1004_begin_plane()) {
od_log_error("Boot screen: E1004 dual-CS plane open failed");
return false;
}
} else if (!e1004_advance_to_cs2()) {
od_log_error("Boot screen: E1004 CS2 advance failed");
e1004_end_plane();
return false;
}
for (int halfPass = 0; halfPass < splitHalfPasses; halfPass++) {
// Only pass 0 opens the frame; the sink crosses to the right half on its
// own once the left half-plane's worth of bytes has arrived.
if (splitStream && halfPass == 0 && !splitPanelBeginFrame()) {
od_log_error("Boot screen: split panel frame open failed");
return false;
}
for (int pass = 0; pass < planePasses; pass++) {
const int bitSel = pass; // pass 0 -> LSB/PLANE_0, pass 1 -> MSB/PLANE_1
const int targetPlane = gray4Split ? (pass == 0 ? PLANE_0 : PLANE_1)
: (colorSwatchPlane1 ? (pass == 0 ? PLANE_0 : PLANE_1)
: (useBitplanes ? PLANE_0 : getplane()));
#if defined(TARGET_ESP32) && defined(OPENDISPLAY_FASTEPD)
if (!fastepd_driver_used() && !e1004Stream) {
if (!fastepd_driver_used() && !splitStream) {
bbepSetAddrWindow(&bbep, 0, 0, w, h);
bbepStartWrite(&bbep, targetPlane);
}
#else
if (!e1004Stream) {
if (!splitStream) {
bbepSetAddrWindow(&bbep, 0, 0, w, h);
bbepStartWrite(&bbep, targetPlane);
}
Expand All @@ -963,13 +962,13 @@ bool writeBootScreenWithQr() {
: (uint16_t)(fwY + ls);
const uint16_t k2Y = (uint16_t)(k1Y + ls);
const bool colorPlanePass = colorSwatchPlane1 && pass == 1;
// E1004 half-pass: only paint the half we will stream (avoids 2× full-frame work).
const uint16_t xPaint0 = e1004Stream ? (uint16_t)(halfPass * (w / 2u)) : (uint16_t)0;
const uint16_t xPaint1 = e1004Stream ? (uint16_t)(xPaint0 + (w / 2u)) : w;
// Split half-pass: only paint the half we will emit (avoids 2× full-frame work).
const uint16_t xPaint0 = splitStream ? (uint16_t)(halfPass * (w / 2u)) : (uint16_t)0;
const uint16_t xPaint1 = splitStream ? (uint16_t)(xPaint0 + (w / 2u)) : w;
for (uint16_t y_native = 0; y_native < h; y_native++) {
if (e1004Stream) {
memset(row + (size_t)halfPass * e1004HalfPitch,
colorPlanePass ? 0x00 : whiteValue, e1004HalfPitch);
if (splitStream) {
memset(row + (size_t)halfPass * splitHalfPitch,
colorPlanePass ? 0x00 : whiteValue, splitHalfPitch);
} else {
memset(row, colorPlanePass ? 0x00 : whiteValue, pitch);
}
Expand Down Expand Up @@ -1030,17 +1029,17 @@ bool writeBootScreenWithQr() {
fastepd_boot_write_row(y_native, row, pitch);
} else if (gray4Split) {
writeGray4PlaneRow(row, pitch, planePitch, w, bitSel);
} else if (e1004Stream) {
e1004_write_stream_bytes(row + (size_t)halfPass * e1004HalfPitch, e1004HalfPitch);
} else if (splitStream) {
splitPanelSinkBytes(row + (size_t)halfPass * splitHalfPitch, splitHalfPitch);
} else {
odWatchdogBreadcrumb(OD_WDT_PHASE_STREAM);
bbepWriteData(&bbep, row, pitch);
}
#else
if (gray4Split) {
writeGray4PlaneRow(row, pitch, planePitch, w, bitSel);
} else if (e1004Stream) {
e1004_write_stream_bytes(row + (size_t)halfPass * e1004HalfPitch, e1004HalfPitch);
} else if (splitStream) {
splitPanelSinkBytes(row + (size_t)halfPass * splitHalfPitch, splitHalfPitch);
} else {
odWatchdogBreadcrumb(OD_WDT_PHASE_STREAM);
bbepWriteData(&bbep, row, pitch);
Expand All @@ -1050,7 +1049,9 @@ bool writeBootScreenWithQr() {
}
} // halfPass

if (e1004Stream) e1004_end_plane();
// No close step here: the chip selects stay held until splitPanelCloseFrame()
// releases them just before the refresh, in refreshBootScreenFull(). Closing
// early would make that call report an already-closed frame and skip the refresh.

#if defined(TARGET_ESP32) && defined(OPENDISPLAY_FASTEPD)
if (fastepd_driver_used()) {
Expand Down
Loading
Loading