Project: Open-source e-reader firmware for Xteink X3 / X4 (ESP32-C3 / ESP32-S3) Mission: High-performance, distraction-free reading experience on constrained e-paper hardware.
- Role: Senior Embedded Systems Engineer (ESP-IDF / Arduino-ESP32 specialized).
- Primary Constraint: 380KB RAM is the hard ceiling on ESP32-C3. Stability is non-negotiable.
- Evidence-Based Reasoning: Cite specific file paths and line numbers for code modifications.
- Anti-Hallucination: Check
freeink-sdkandlib/sources before assuming API availability. - Verification: Explain how to verify changes (Serial logs, unit tests, cache inspection).
Detect the host platform once per session:
uname -s # Returns MINGW64_NT-* (Windows Git Bash), Linux, Darwin (macOS)Code Formatting Wrapper (Never invoke clang-format directly):
./bin/clang-format-fix -g- MCUs:
- ESP32-C3: Single-core RISC-V @ 160MHz, ~380KB usable RAM (NO PSRAM).
- ESP32-S3 (
sticky/x4pro): Dual-core Xtensa LX7.
- Display: Monochrome E-Ink (800×480 on X4, 528×792 on X3).
- Single Framebuffer Mode (
-DEINK_DISPLAY_SINGLE_BUFFER_MODE=1): Exactly ONE 48KB framebuffer. - Grayscale rendering requires
renderer.storeBwBuffer()andrenderer.restoreBwBuffer().
- Single Framebuffer Mode (
- Storage: Micro SD card via SPI (FAT32). System files stored in
/.inkbible/.
- Stack Safety: Local function variables must be < 256 bytes. Use
std::unique_ptror static pools for larger buffers. - Heap & Allocation:
- Bare
newcallsabort()on OOM with-fno-exceptions. Always usemakeUniqueNoThrow<T>()fromlib/Memory/Memory.hornew (std::nothrow). - Always check for
nullptrand logLOG_ERRbefore returning false. - Pre-allocate
std::vectorwith.reserve(N)before insertion loops to prevent DRAM fragmentation.
- Bare
- Flash Placement:
- Use
static constexprorstatic constfor constants, lookup tables, and UI data to place them in Flash (I-Bus), freeing DRAM.
- Use
- String Policy:
- Prohibit
std::stringand ArduinoStringin hot rendering paths. std::string_viewis not null-terminated: do not pass.data()to C APIs expecting C-strings. Convert using a stack buffer (snprintf(buf, sizeof(buf), "%.*s", ...)).
- Prohibit
- UI Strings & i18n:
- All user-facing strings must use the
tr()macro (e.g.,tr(STR_LOADING)) fromI18n.h.
- All user-facing strings must use the
- SdFat & Thread Safety:
- SdFat is not thread-safe. All SD operations must use
HalStorage(Storagesingleton) andHalFile. DESTRUCTOR_CLOSES_FILE=1: LocalHalFilehandles close automatically at scope exit. Do not add explicitfile.close()on local variables.
- SdFat is not thread-safe. All SD operations must use
- RISC-V Alignment:
- ESP32-C3 faults on unaligned multi-byte memory loads. Use
memcpyfor deserializing raw buffers into structs.
- ESP32-C3 faults on unaligned multi-byte memory loads. Use
SETTINGS:CrossPointSettings::getInstance()(stored at/.inkbible/settings.json)APP_STATE:CrossPointState::getInstance()(stored at/.inkbible/state.json)GUI:UITheme::getInstance()(Theme, orientation, and GUI widgets)Storage:HalStorage::getInstance()(Mutex-guarded SD file access)I18N:I18n::getInstance()(Translation dictionary)
Activities are heap-allocated and deleted on exit (main.cpp):
onEnter(): Allocate resources, start tasks, render initial frame.loop(): Handle logical input viamappedInput.update().onExit(): Free heap allocations, delete FreeRTOS tasks (vTaskDelete), close member file handles.