From 3e5aff150299a334c93ae003aabb5b71715a51c5 Mon Sep 17 00:00:00 2001 From: MoveCall <15621173+MoveCall@users.noreply.github.com> Date: Mon, 31 Aug 2026 19:23:38 +0800 Subject: [PATCH 1/2] feat(board): add OnePage ESP32-C61 board profile and support - Add FREEINK_DEVICE_ONEPAGE and FREEINK_MCU_C61 definitions - Add BoardProfile ONEPAGE (SSD1677 800x480, GPIO4 ADC ladder, GPIO6/9/2 side keys, GPIO27 SD rail) - Implement InputStyle::OnePageAdcLadder in InputManager - Add charge-pause sampling support in BatteryMonitor - Add power-rail settle time and retry loop in SDCardManager - Add ESP32-C61 deep sleep guard in PowerManager - Update README.md and add docs/onepage-c61-support.md --- README.md | 23 ++++-- docs/onepage-c61-support.md | 82 +++++++++++++++++++ .../BatteryMonitor/src/BatteryMonitor.cpp | 13 ++- .../BoardConfig/include/BoardConfig.h | 74 ++++++++++++++--- .../InputManager/src/InputManager.cpp | 40 +++++++++ .../PowerManager/src/PowerManager.cpp | 3 + .../SDCardManager/src/SDCardManager.cpp | 10 ++- platformio.sample.ini | 20 ++++- 8 files changed, 245 insertions(+), 20 deletions(-) create mode 100644 docs/onepage-c61-support.md diff --git a/README.md b/README.md index 0b8a063c..e01979d9 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,7 @@ so the SD manager itself stays device-agnostic. |---|---|---|---|---| | **Xteink X4** | ESP32-C3 | SSD1677 | 800×480 | B/W + 4-level grayscale | | **Xteink X3** | ESP32-C3 | UC8253 | 792×528 | B/W + 4-level grayscale; BQ27220 I²C battery gauge; shares the C3 binary with X4 | +| **OnePage** | ESP32-C61 | SSD1677 | 800×480 | B/W + 4-level grayscale, 4-key front resistor ADC ladder + 3 side keys, shared SPI MicroSD with power gating, Wi-Fi 6 + BLE 5.4 wireless page-turner remote host | | **de-link** | ESP32-S3 | SSD1677 | 800×480 | B/W + grayscale, PWM frontlight, native 4-bit SDMMC SD | | **M5Stack PaperColor** | ESP32-S3 | ED2208 | 400×600 Spectra-6 color | native interrupted-refresh driver, optional M5GFX backend, built-in speaker (ES8311 codec + AW8737A amp), 2x RGB LEDs | | **Murphy M3** | ESP32-S3 | UC8253 | 240×416 | B/W (90°-rotated framebuffer, full/fast LUTs), CHSC6x touch, PWM frontlight | @@ -145,8 +146,8 @@ found so the caller can `display.setDisplayX3()`. Call it before profile. In builds without an Xteink profile the helpers compile to no-ops that return false without touching any pins, so an unconditional call is safe on every device. Devices on a different MCU build their own binary, selected with a -`-DFREEINK_DEVICE_*` flag. A build targets exactly one of the three MCU families — ESP32-C3 (X3/X4), -ESP32-S3 (de-link/PaperColor/Murphy/LilyGo/Sticky/X4 Pro/Paper Mono/PaperS3), or classic ESP32 (M5Paper); +`-DFREEINK_DEVICE_*` flag. A build targets exactly one of the four MCU families — ESP32-C3 (X3/X4), +ESP32-C61 (OnePage), ESP32-S3 (de-link/PaperColor/Murphy/LilyGo/Sticky/X4 Pro/Paper Mono/PaperS3), or classic ESP32 (M5Paper); `BoardConfig` rejects mixing families at compile time. #### Per-batch panel controllers (`applyXteinkDisplayController()`) @@ -160,9 +161,7 @@ batch. `XteinkDetect` resolves which silicon a unit carries at boot: ```cpp #include -// Before FreeInkDisplay::begin() — promotes BoardConfig::ACTIVE.displayController -// to the UltraChip sibling when this unit carries it, so begin() selects the -// matching driver. No-op (returns false) on builds without a probe-capable device. +// Detect and apply the display controller for this unit's batch: freeink::applyXteinkDisplayController(); display.begin(); ``` @@ -295,7 +294,18 @@ wasTouchReleased/getTouchPoint`; it delivers coordinates raw-panel-oriented and app owns display-orientation mapping. GT911 additionally provides allocation-free multi-contact snapshots, completed 2-4 finger translation gestures, and completed two-finger rotations with a signed angle, center, and duration. The GT911 boards set -their `TouchConfig` in the board profile (e.g. `BoardConfig::LILYGO_T5_PRO_GT911`). +### OnePage Reader board support (ESP32-C61) + +The OnePage Reader is an open-hardware DIY e-reader built around the **ESP32-C61** +RISC-V wireless SoC. Complete hardware specifications, pinout, power gating, and +board profile details are documented in [docs/onepage-c61-support.md](docs/onepage-c61-support.md). + +Key characteristics: +- **Display**: SSD1677 800×480 on SPI @ 20MHz. +- **MicroSD**: SPI mode sharing the bus with EPD, powered via GPIO27 power rail. +- **Input**: 4-key front resistor ADC ladder on GPIO4 (`OnePageAdcLadder`) + 3 discrete active-low side keys (`UP=6, DOWN=9, POWER=2`). +- **Battery / Power**: ADC sampling on GPIO5 with charge-pause control on GPIO10 (`BAT_CHG_EN`). +- **Bluetooth**: Wi-Fi 6 + BLE 5.4 with BLE HID Central / page-turner remote host support. ## Build composition — devices × capabilities @@ -310,6 +320,7 @@ MCU (a C3-vs-S3 mix is a compile error): |---|---| | `-DFREEINK_DEVICE_X4` | X4 only — links just SSD1677 (tightest) | | `-DFREEINK_DEVICE_X3 -DFREEINK_DEVICE_X4` | X3 **and** X4 in one C3 binary, runtime-selected via `setDisplayX3()` | +| `-DFREEINK_DEVICE_ONEPAGE` | OnePage (C61, SSD1677 800×480 + 4-key ADC ladder + 3 side keys + shared SD) | | `-DFREEINK_DEVICE_DELINK` | de-link (S3, SSD1677 + frontlight) | | `-DFREEINK_DEVICE_M5` | M5 PaperColor (S3, ED2208 + color) | | `-DFREEINK_DEVICE_MURPHY` | Murphy M3 (S3, UC8253 + touch + frontlight) | diff --git a/docs/onepage-c61-support.md b/docs/onepage-c61-support.md new file mode 100644 index 00000000..e9e44c9c --- /dev/null +++ b/docs/onepage-c61-support.md @@ -0,0 +1,82 @@ +# OnePage ESP32-C61 Board Support + +## Overview + +**OnePage Reader** is an open-hardware DIY E-reader built around the **ESP32-C61** RISC-V wireless SoC. +Main ecosystem repo: [MoveCall/onepage-reader](https://github.com/MoveCall/onepage-reader) + +This document describes the FreeInk SDK board support profile for OnePage (`FREEINK_DEVICE_ONEPAGE`). + +## Hardware Specifications + +| Subsystem | Specification / Pinout | +|:---|:---| +| **SoC** | ESP32-C61 (ESP32-C61HR2, 160MHz RISC-V, Wi-Fi 6, BLE 5.4, 16MB Flash, 2MB PSRAM @ 40MHz) | +| **Display** | 4.26" E-Ink Display (GDEQ0426T82), **SSD1677** controller, **800 × 480** resolution, 4-level grayscale | +| **Display SPI** | SCLK: `GPIO22`, MOSI: `GPIO23`, CS: `GPIO25`, DC: `GPIO8`, RST: `GPIO27`, BUSY: `GPIO29` · **20 MHz** | +| **MicroSD Card** | SPI mode (shared SPI bus with EPD): SCLK: `GPIO22`, MISO: `GPIO24`, MOSI: `GPIO23`, CS: `GPIO26` · **20 MHz** | +| **Buttons (Front 4-Key)** | Single resistor ADC ladder on `GPIO4` (ADC1_CH2):
• `BACK`: 2400..2800 mV (~2592 mV)
• `LEFT`: 1780..2140 mV (~1956 mV)
• `RIGHT`: 1140..1500 mV (~1316 mV)
• `CONFIRM` (ENTER): 0..250 mV (~0 mV) | +| **Buttons (Side 3-Key)** | Active-low GPIO buttons with internal pull-up:
• `UP` (PREV): `GPIO6`
• `DOWN` (NEXT): `GPIO9`
• `POWER` (WAKE): `GPIO2` (Deep-sleep wakeup source) | +| **Battery & Charging** | • Battery ADC: `GPIO5` (ADC1_CH3, multiplier ×2.0)
• USB / Charge Detect: `GPIO11` (UART0_RXD, LM66200 ST open-drain, low = USB present)
• Charge Pause: `GPIO10` (`BAT_CHG_EN`, drive LOW during ADC sample to avoid charger offset) | +| **Power Architecture** | `GPIO27` serves as both EPD RST and shared SD power rail enable. Silenced before deep sleep. | +| **Bluetooth Remote** | BLE HID Central / Host support for wireless page turners | + +## FreeInk SDK Profile + +In FreeInk SDK (`BoardConfig.h`): + +```cpp +constexpr BoardProfile ONEPAGE = { + Board::OnePage, + "onepage", + InputStyle::OnePageAdcLadder, + DisplayController::SSD1677, + 800, + 480, + {22, 23, 25, 8, 27, 29, PIN_UNASSIGNED}, // display SPI + 20000000, // 20 MHz + {22, 24, 23, 26, PIN_UNASSIGNED, false, 20000000, true}, // microSD + {PIN_UNASSIGNED, PIN_UNASSIGNED, PIN_UNASSIGNED, PIN_UNASSIGNED, 6, 9, 2, false, 4}, // input + 5, // batteryAdc (GPIO5) + 11, // batteryChargeStatus (GPIO11) + 2.0f, // batteryDividerMultiplier + 11, // usbDetect (GPIO11) + NO_TOUCH, + NO_FRONTLIGHT, + NO_AUDIO, + NO_LEDS, + NO_FLIP, + NO_SDMMC, + NO_GAUGE, + NO_MIC, + NO_SENSORS, + 1.0f, // uiScale + {PIN_UNASSIGNED, PIN_UNASSIGNED, 10, true},// chargeEnable (GPIO10) + 0, + {9, 3, 3, 3}, + false // batteryChargeStatusActiveHigh: false +}; +``` + +## Build Configuration + +Add the following to your project's `platformio.ini`: + +```ini +[env:onepage] +extends = base +board = onepage-c61 +board_build.mcu = esp32c61 +custom_sdkconfig = + CONFIG_SPIRAM=y + CONFIG_SPIRAM_MODE_QUAD=y + CONFIG_SPIRAM_SPEED_40M=y + CONFIG_SPIRAM_USE_MALLOC=y + CONFIG_SPIRAM_BOOT_INIT=y + CONFIG_SPIRAM_BOOT_HW_INIT=y + CONFIG_BT_LE_SLEEP_ENABLE=y + CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +build_flags = + ${base.build_flags} + -DFREEINK_DEVICE_ONEPAGE=1 +``` diff --git a/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp b/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp index b6d78bbc..4005e6a2 100644 --- a/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp +++ b/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp @@ -456,7 +456,18 @@ uint16_t BatteryMonitor::readMillivolts() const { const uint16_t mv = esp_adc_cal_raw_to_voltage(raw, &adc_chars); #else // ESP-IDF 5.x has analogReadMilliVolts - const uint16_t mv = analogReadMilliVolts(_adcPin); + uint16_t mv = 0; + if (BoardConfig::ACTIVE.power.chargeEnable >= 0) { + const int8_t ce = BoardConfig::ACTIVE.power.chargeEnable; + const bool activeHigh = BoardConfig::ACTIVE.power.chargeEnableActiveHigh; + pinMode(ce, OUTPUT); + digitalWrite(ce, activeHigh ? LOW : HIGH); // pause charging + delay(5); + mv = analogReadMilliVolts(_adcPin); + digitalWrite(ce, activeHigh ? HIGH : LOW); // resume charging + } else { + mv = analogReadMilliVolts(_adcPin); + } #endif return static_cast(mv * _dividerMultiplier); diff --git a/libs/hardware/BoardConfig/include/BoardConfig.h b/libs/hardware/BoardConfig/include/BoardConfig.h index 1b9935fc..5489a653 100644 --- a/libs/hardware/BoardConfig/include/BoardConfig.h +++ b/libs/hardware/BoardConfig/include/BoardConfig.h @@ -72,39 +72,43 @@ #ifndef FREEINK_DEVICE_EEGO_A4 #define FREEINK_DEVICE_EEGO_A4 0 #endif +#ifndef FREEINK_DEVICE_ONEPAGE +#define FREEINK_DEVICE_ONEPAGE 0 +#endif // --- 2) Coherence: exactly one MCU family, at least one device --------------- #if !(FREEINK_DEVICE_X4 || FREEINK_DEVICE_X3 || FREEINK_DEVICE_X4PRO || FREEINK_DEVICE_X4CLASSIC || FREEINK_DEVICE_M5 || \ FREEINK_DEVICE_MURPHY || FREEINK_DEVICE_DELINK || FREEINK_DEVICE_LILYGO || FREEINK_DEVICE_M5PAPER || \ FREEINK_DEVICE_STICKY || FREEINK_DEVICE_PAPERMONO || FREEINK_DEVICE_PAPERS3 || FREEINK_DEVICE_MURPHY_M4 || \ - FREEINK_DEVICE_EEGO_A4) + FREEINK_DEVICE_EEGO_A4 || FREEINK_DEVICE_ONEPAGE) #error \ - "FreeInk: no device selected. Pass at least one -DFREEINK_DEVICE_ (X4, X3, X4PRO, X4CLASSIC, M5, MURPHY, DELINK, LILYGO, M5PAPER, STICKY, PAPERMONO, PAPERS3, MURPHY_M4, EEGO_A4) in your build env — see platformio.sample.ini." + "FreeInk: no device selected. Pass at least one -DFREEINK_DEVICE_ (X4, X3, X4PRO, X4CLASSIC, M5, MURPHY, DELINK, LILYGO, M5PAPER, STICKY, PAPERMONO, PAPERS3, MURPHY_M4, EEGO_A4, ONEPAGE) in your build env — see platformio.sample.ini." #endif // Each device belongs to one MCU family; a binary targets exactly one. X3/X4 are // ESP32-C3; M5 PaperColor/Murphy/de-link/LilyGo are ESP32-S3; M5Paper v1.1 is the -// classic ESP32 (ESP32-D0WDQ6). The three families differ in deep-sleep wakeup, -// SPI peripheral count, and toolchain, so they never share a binary. +// classic ESP32 (ESP32-D0WDQ6); OnePage is ESP32-C61. The families differ in +// deep-sleep wakeup, SPI peripheral count, and toolchain, so they never share a binary. #define FREEINK_MCU_C3 (FREEINK_DEVICE_X3 || FREEINK_DEVICE_X4) +#define FREEINK_MCU_C61 (FREEINK_DEVICE_ONEPAGE) #define FREEINK_MCU_S3 \ (FREEINK_DEVICE_M5 || FREEINK_DEVICE_MURPHY || FREEINK_DEVICE_DELINK || FREEINK_DEVICE_LILYGO || \ FREEINK_DEVICE_STICKY || FREEINK_DEVICE_X4PRO || FREEINK_DEVICE_X4CLASSIC || FREEINK_DEVICE_PAPERMONO || \ FREEINK_DEVICE_PAPERS3 || FREEINK_DEVICE_MURPHY_M4 || FREEINK_DEVICE_EEGO_A4) #define FREEINK_MCU_ESP32 (FREEINK_DEVICE_M5PAPER) -#if (FREEINK_MCU_C3 + FREEINK_MCU_S3 + FREEINK_MCU_ESP32) != 1 +#if (FREEINK_MCU_C3 + FREEINK_MCU_C61 + FREEINK_MCU_S3 + FREEINK_MCU_ESP32) != 1 #error \ - "FreeInk: all selected devices must share one MCU family — ESP32-C3 (X3/X4), ESP32-S3 (M5/Murphy/de-link/LilyGo/Sticky/X4Pro), or ESP32 (M5Paper). Build one binary per family." + "FreeInk: all selected devices must share one MCU family — ESP32-C3 (X3/X4), ESP32-C61 (OnePage), ESP32-S3 (M5/Murphy/de-link/LilyGo/Sticky/X4Pro), or ESP32 (M5Paper). Build one binary per family." #endif // --- 3) Derive panel drivers from the device set ----------------------------- // Sticky reuses SSD1677: its 800x480 panel rides a 24-pin FPC whose GDR/RESE/BS1 // + dual VSH1/VSH2 + external VGH/VGL/VSL/VCOM charge pump is the SSD1677 -// application circuit (same controller + resolution as X4 / de-link). +// application circuit (same controller + resolution as X4 / de-link / OnePage). // X4 Pro is a distinct ESP32-S3 device (NOT the C3 X4): its 800x480 panel may // use SSD1677, UC8179, or UC8279, recovered from OEM firmware and hardware // references — see docs/xteink-x4pro-support.md. #if FREEINK_DEVICE_X4 || FREEINK_DEVICE_DELINK || FREEINK_DEVICE_STICKY || FREEINK_DEVICE_X4PRO || \ - FREEINK_DEVICE_X4CLASSIC || FREEINK_DEVICE_MURPHY_M4 + FREEINK_DEVICE_X4CLASSIC || FREEINK_DEVICE_MURPHY_M4 || FREEINK_DEVICE_ONEPAGE #define FREEINK_DRIVER_SSD1677 1 #else #define FREEINK_DRIVER_SSD1677 0 @@ -376,6 +380,7 @@ enum class Board : uint8_t { PaperMono, M5PaperS3, // ESP32-S3 sibling of M5Paper v1.1: same ED047TC1 glass, no IT8951 — raw parallel via LovyanGFX EegoA4, // EEGO Reader A4: ESP32-S3, UC8279C 768x552 SPI panel, GSLX680 touch, PCF8563 RTC + OnePage, // OnePage: ESP32-C61, SSD1677 800x480 SPI panel, 4-key ADC ladder + 3 side keys }; // How the board reports button presses. @@ -386,6 +391,7 @@ enum class InputStyle : uint8_t { DigitalConfirmPowerHold, // confirm click, power hold on a shared GPIO DigitalFiveKey, // 3 physical GPIO keys + synthesized events (Murphy M3) DigitalTwoButton, // short up/down; holds synthesize back/confirm/power + OnePageAdcLadder, // OnePage: 4 front keys on GPIO4 ADC ladder + 3 side GPIO keys }; // Panel controller silicon. Drivers are selected from this at begin(). @@ -498,6 +504,7 @@ struct InputPins { int8_t down; int8_t power; bool powerActiveHigh; // true = pressed reads HIGH (INPUT_PULLDOWN); false = active-LOW (INPUT_PULLUP) + int8_t adcLadderPin = PIN_UNASSIGNED; // ADC pin for single resistor ladder (e.g. OnePage GPIO4) }; // Capacitive touch panel description (TouchController::None disables it). @@ -1698,6 +1705,44 @@ constexpr BoardProfile XTEINK_X4_CLASSIC = { {9, 7, 3, 7}, // bezel insets: carried from X4 Pro (same glass), pending measurement true}; // batteryChargeStatusActiveHigh: GPIO21 STAT driven HIGH while charging +constexpr BoardProfile ONEPAGE = { + Board::OnePage, + "onepage", + InputStyle::OnePageAdcLadder, + DisplayController::SSD1677, + 800, + 480, + // Display SPI: SCLK 22, MOSI 23, CS 25, DC 8, RST 27, BUSY 29, powerEnable PIN_UNASSIGNED + {22, 23, 25, 8, 27, 29, PIN_UNASSIGNED}, + 20000000, // displaySpiHz: 20MHz + // MicroSD (shared SPI bus): SCLK 22, MISO 24, MOSI 23, CS 26, powerEnable 27 + {22, 24, 23, 26, 27, false, 20000000, true}, + // Input: 4-key front ADC ladder on GPIO4 + 3 side GPIO keys (UP=6, DOWN=9, POWER=2) + // {back, confirm, left, right, up, down, power, powerActiveHigh, adcLadderPin} + {PIN_UNASSIGNED, PIN_UNASSIGNED, PIN_UNASSIGNED, PIN_UNASSIGNED, 6, 9, 2, false, 4}, + 5, // batteryAdc: GPIO5 (ADC1_CH3) + 11, // batteryChargeStatus: GPIO11 (LM66200 ST open-drain, low=USB present) + 2.0f, // batteryDividerMultiplier + 11, // usbDetect: GPIO11 (LM66200 ST) + NO_TOUCH, // touch: no touch + NO_FRONTLIGHT, // frontlight: none + NO_AUDIO, // audio: none + NO_LEDS, // leds: none + NO_FLIP, // orientation + NO_SDMMC, // sdmmc: none (SPI) + NO_GAUGE, // batteryGauge: none (ADC) + NO_MIC, // mic + NO_SENSORS, // sensors + 1.0f, // uiScale: 1.0 + // Power: latch0, latch1, chargeEnable (GPIO10, active-high) + {PIN_UNASSIGNED, PIN_UNASSIGNED, 10, true}, + 0, // displayControllerVariant + {0, 0, 0, 0}, // viewableInsets: full 800x480 panel frame + false}; // batteryChargeStatusActiveHigh: false (low = USB present) + +static_assert(ONEPAGE.displayWidth / 8 * ONEPAGE.displayHeight == 48000, + "OnePage framebuffer must be 48,000 bytes (800/8 x 480)"); + // Largest framebuffer (bytes) over the devices compiled into this build, derived // from the profiles above. The display facade sizes its static framebuffer to // this so one binary holds whichever panel is runtime-selected; a single-device @@ -1720,12 +1765,15 @@ constexpr uint32_t MAX_FRAMEBUFFER_BYTES = cmax( FREEINK_DEVICE_PAPERMONO ? panelBytes(PAPER_MONO) : 0u), cmax(cmax(FREEINK_DEVICE_PAPERS3 ? panelBytes(M5PAPER_S3) : 0u, FREEINK_DEVICE_MURPHY_M4 ? panelBytes(MURPHY_M4) : 0u), - FREEINK_DEVICE_EEGO_A4 ? panelBytes(EEGO_A4) : 0u)))); + cmax(FREEINK_DEVICE_EEGO_A4 ? panelBytes(EEGO_A4) : 0u, + FREEINK_DEVICE_ONEPAGE ? panelBytes(ONEPAGE) : 0u))))); // Compile-time default device — the profile ACTIVE starts as. With a single // device in the build this is the only device; with several same-MCU devices it // is the boot default until the consumer calls selectDevice(). -#if FREEINK_DEVICE_PAPERMONO +#if FREEINK_DEVICE_ONEPAGE +constexpr BoardProfile DEFAULT_DEVICE = ONEPAGE; +#elif FREEINK_DEVICE_PAPERMONO constexpr BoardProfile DEFAULT_DEVICE = PAPER_MONO; #elif FREEINK_DEVICE_EEGO_A4 constexpr BoardProfile DEFAULT_DEVICE = EEGO_A4; @@ -1840,6 +1888,11 @@ inline bool selectDevice(Board which) { case Board::EegoA4: ACTIVE = EEGO_A4; break; +#endif +#if FREEINK_DEVICE_ONEPAGE + case Board::OnePage: + ACTIVE = ONEPAGE; + break; #endif default: return false; @@ -1862,6 +1915,7 @@ inline bool isX4Pro() { return ACTIVE.board == Board::XteinkX4Pro; } inline bool isX4Classic() { return ACTIVE.board == Board::XteinkX4Classic; } inline bool isPaperMono() { return ACTIVE.board == Board::PaperMono; } inline bool isEegoA4() { return ACTIVE.board == Board::EegoA4; } +inline bool isOnePage() { return ACTIVE.board == Board::OnePage; } inline bool hasTouch() { return ACTIVE.touch.controller != TouchController::None; } inline bool hasHomeKey() { return ACTIVE.touch.hasHomeKey; } inline bool hasPwmFrontlight() { return ACTIVE.frontlight.gpio != PIN_UNASSIGNED || ACTIVE.frontlight.viaPm1Pwm; } diff --git a/libs/hardware/InputManager/src/InputManager.cpp b/libs/hardware/InputManager/src/InputManager.cpp index 219f0039..4219266f 100644 --- a/libs/hardware/InputManager/src/InputManager.cpp +++ b/libs/hardware/InputManager/src/InputManager.cpp @@ -99,6 +99,21 @@ void InputManager::begin() { return; } + if (BoardConfig::ACTIVE.inputStyle == BoardConfig::InputStyle::OnePageAdcLadder) { + if (BoardConfig::ACTIVE.input.adcLadderPin >= 0) { + pinMode(BoardConfig::ACTIVE.input.adcLadderPin, INPUT); + } + analogSetAttenuation(ADC_11db); + if (BoardConfig::ACTIVE.input.up >= 0) pinMode(BoardConfig::ACTIVE.input.up, INPUT_PULLUP); + if (BoardConfig::ACTIVE.input.down >= 0) pinMode(BoardConfig::ACTIVE.input.down, INPUT_PULLUP); + if (BoardConfig::ACTIVE.input.power >= 0) { + pinMode(BoardConfig::ACTIVE.input.power, + BoardConfig::ACTIVE.input.powerActiveHigh ? INPUT_PULLDOWN : INPUT_PULLUP); + } + beginTouch(); + return; + } + const int8_t pins[] = {BoardConfig::ACTIVE.input.back, BoardConfig::ACTIVE.input.confirm, BoardConfig::ACTIVE.input.left, BoardConfig::ACTIVE.input.right, BoardConfig::ACTIVE.input.up, BoardConfig::ACTIVE.input.down}; @@ -146,6 +161,31 @@ void InputManager::readButtonAdc(ButtonAdcSample& group1, ButtonAdcSample& group uint8_t InputManager::getState() { uint8_t state = 0; + if (BoardConfig::ACTIVE.inputStyle == BoardConfig::InputStyle::OnePageAdcLadder) { + if (BoardConfig::ACTIVE.input.adcLadderPin >= 0) { + const int mv = analogReadMilliVolts(BoardConfig::ACTIVE.input.adcLadderPin); + if (mv >= 2400 && mv <= 2800) state |= (1 << BTN_BACK); // ~2592 mV + else if (mv >= 1780 && mv <= 2140) state |= (1 << BTN_LEFT); // ~1956 mV + else if (mv >= 1140 && mv <= 1500) state |= (1 << BTN_RIGHT); // ~1316 mV + else if (mv >= 0 && mv <= 250) state |= (1 << BTN_CONFIRM); // ~0 mV (ENTER) + } + if (BoardConfig::ACTIVE.input.up >= 0 && digitalRead(BoardConfig::ACTIVE.input.up) == LOW) { + state |= (1 << BTN_UP); + } + if (BoardConfig::ACTIVE.input.down >= 0 && digitalRead(BoardConfig::ACTIVE.input.down) == LOW) { + state |= (1 << BTN_DOWN); + } + if (BoardConfig::ACTIVE.input.power >= 0) { + const int activeLevel = BoardConfig::ACTIVE.input.powerActiveHigh ? HIGH : LOW; + if (digitalRead(BoardConfig::ACTIVE.input.power) == activeLevel) { + state |= (1 << BTN_POWER); + } + } + state |= serviceTouch(); + if (s_buttonHook) state |= s_buttonHook(); + return state; + } + if (BoardConfig::ACTIVE.inputStyle != BoardConfig::InputStyle::XteinkAdcLadder) { state = getDigitalState(); state |= serviceTouch(); // run the touch machine; OR any synthesized button diff --git a/libs/hardware/PowerManager/src/PowerManager.cpp b/libs/hardware/PowerManager/src/PowerManager.cpp index f3f8601d..e361c630 100644 --- a/libs/hardware/PowerManager/src/PowerManager.cpp +++ b/libs/hardware/PowerManager/src/PowerManager.cpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -92,7 +93,9 @@ void PowerManager::powerDownRailsForSleep() { void PowerManager::deepSleep() { esp_sleep_config_gpio_isolate(); +#if !FREEINK_MCU_C61 gpio_deep_sleep_hold_en(); +#endif esp_deep_sleep_start(); while (true) { } // esp_deep_sleep_start() does not return; satisfy [[noreturn]] diff --git a/libs/hardware/SDCardManager/src/SDCardManager.cpp b/libs/hardware/SDCardManager/src/SDCardManager.cpp index 8a2af346..eafca57f 100644 --- a/libs/hardware/SDCardManager/src/SDCardManager.cpp +++ b/libs/hardware/SDCardManager/src/SDCardManager.cpp @@ -113,7 +113,7 @@ bool SDCardManager::begin() { pinMode(BoardConfig::ACTIVE.sd.powerEnable, OUTPUT); // ON level: HIGH for active-high enables, LOW for active-low ones. digitalWrite(BoardConfig::ACTIVE.sd.powerEnable, BoardConfig::ACTIVE.sd.powerActiveHigh ? HIGH : LOW); - delay(10); + delay(80); } // Shared SPI bus: when the display controller sits on the same SCLK as the SD @@ -142,7 +142,13 @@ bool SDCardManager::begin() { if (SD_SCLK >= 0 && SD_MOSI >= 0 && SD_MISO >= 0) { SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); } - cardReady = sd.begin(SD_CS, SPI_FQ); + for (int attempt = 0; attempt < 5; ++attempt) { + if (sd.begin(SD_CS, SPI_FQ)) { + cardReady = true; + break; + } + delay(80); + } #if FREEINK_MCU_S3 || FREEINK_MCU_ESP32 } #endif diff --git a/platformio.sample.ini b/platformio.sample.ini index 73a9e5cc..81709832 100644 --- a/platformio.sample.ini +++ b/platformio.sample.ini @@ -317,7 +317,25 @@ build_flags = ; SD is 1-bit SDMMC (FREEINK_SD_SDMMC auto-enables); SdFat needs its generic ; block-device interface for the FsVolume mount: -DUSE_BLOCK_DEVICE_INTERFACE=1 - -DFREEINK_DEVICE_PAPERMONO=1 +; --- OnePage Reader — ESP32-C61, SSD1677 800x480, 4-key ADC ladder + 3 side keys - +; Open-hardware DIY e-reader ecosystem (ESP32-C61HR2, 16MB flash, 2MB PSRAM @40MHz). +; MicroSD shares SPI bus with EPD (20MHz). BLE HID host support for page-turner remotes. +[env:onepage] +extends = base +board = onepage-c61 +board_build.mcu = esp32c61 +custom_sdkconfig = + CONFIG_SPIRAM=y + CONFIG_SPIRAM_MODE_QUAD=y + CONFIG_SPIRAM_SPEED_40M=y + CONFIG_SPIRAM_USE_MALLOC=y + CONFIG_SPIRAM_BOOT_INIT=y + CONFIG_SPIRAM_BOOT_HW_INIT=y + CONFIG_BT_LE_SLEEP_ENABLE=y + CONFIG_ESPTOOLPY_FLASHFREQ_80M=y +build_flags = + ${base.build_flags} + -DFREEINK_DEVICE_ONEPAGE=1 ; --- Example: enable the wolfSSL TLS 1.3 networking stack ---------------------- ; Add SecureNet to lib_deps + a wolfSSL source lib, then: From 40503d598190b832a31b3b72e27268c57bdcb6fe Mon Sep 17 00:00:00 2001 From: Justin Mitchell Date: Tue, 1 Sep 2026 14:02:21 -0400 Subject: [PATCH 2/2] Make battery and SD card init board-specific OnePage board requires charge pause during battery reads and longer SD card init delays with retries. Other boards (e.g. Sticky) should not have charge glitches and can fail fast on missing SD cards. Add board-specific logic to handle these hardware differences. --- libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp | 4 +++- libs/hardware/SDCardManager/src/SDCardManager.cpp | 12 ++++++------ platformio.sample.ini | 2 ++ 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp b/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp index 4005e6a2..1687668c 100644 --- a/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp +++ b/libs/hardware/BatteryMonitor/src/BatteryMonitor.cpp @@ -456,8 +456,10 @@ uint16_t BatteryMonitor::readMillivolts() const { const uint16_t mv = esp_adc_cal_raw_to_voltage(raw, &adc_chars); #else // ESP-IDF 5.x has analogReadMilliVolts + // OnePage only: pause charging around the read. Other boards with a + // chargeEnable pin (e.g. Sticky) must NOT have it glitched during battery reads. uint16_t mv = 0; - if (BoardConfig::ACTIVE.power.chargeEnable >= 0) { + if (BoardConfig::isOnePage() && BoardConfig::ACTIVE.power.chargeEnable >= 0) { const int8_t ce = BoardConfig::ACTIVE.power.chargeEnable; const bool activeHigh = BoardConfig::ACTIVE.power.chargeEnableActiveHigh; pinMode(ce, OUTPUT); diff --git a/libs/hardware/SDCardManager/src/SDCardManager.cpp b/libs/hardware/SDCardManager/src/SDCardManager.cpp index eafca57f..480a9388 100644 --- a/libs/hardware/SDCardManager/src/SDCardManager.cpp +++ b/libs/hardware/SDCardManager/src/SDCardManager.cpp @@ -113,7 +113,7 @@ bool SDCardManager::begin() { pinMode(BoardConfig::ACTIVE.sd.powerEnable, OUTPUT); // ON level: HIGH for active-high enables, LOW for active-low ones. digitalWrite(BoardConfig::ACTIVE.sd.powerEnable, BoardConfig::ACTIVE.sd.powerActiveHigh ? HIGH : LOW); - delay(80); + delay(BoardConfig::isOnePage() ? 80 : 10); } // Shared SPI bus: when the display controller sits on the same SCLK as the SD @@ -142,11 +142,11 @@ bool SDCardManager::begin() { if (SD_SCLK >= 0 && SD_MOSI >= 0 && SD_MISO >= 0) { SPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS); } - for (int attempt = 0; attempt < 5; ++attempt) { - if (sd.begin(SD_CS, SPI_FQ)) { - cardReady = true; - break; - } + // Other boards keep the single attempt so a missing card fails fast. + const int attempts = BoardConfig::isOnePage() ? 5 : 1; + for (int attempt = 0; attempt < attempts; ++attempt) { + cardReady = sd.begin(SD_CS, SPI_FQ); + if (cardReady || attempt + 1 == attempts) break; delay(80); } #if FREEINK_MCU_S3 || FREEINK_MCU_ESP32 diff --git a/platformio.sample.ini b/platformio.sample.ini index 81709832..7705c182 100644 --- a/platformio.sample.ini +++ b/platformio.sample.ini @@ -317,6 +317,8 @@ build_flags = ; SD is 1-bit SDMMC (FREEINK_SD_SDMMC auto-enables); SdFat needs its generic ; block-device interface for the FsVolume mount: -DUSE_BLOCK_DEVICE_INTERFACE=1 + -DFREEINK_DEVICE_PAPERMONO=1 + ; --- OnePage Reader — ESP32-C61, SSD1677 800x480, 4-key ADC ladder + 3 side keys - ; Open-hardware DIY e-reader ecosystem (ESP32-C61HR2, 16MB flash, 2MB PSRAM @40MHz). ; MicroSD shares SPI bus with EPD (20MHz). BLE HID host support for page-turner remotes.