Skip to content

Latest commit

 

History

History
67 lines (49 loc) · 3.99 KB

AGENTS.md

File metadata and controls

67 lines (49 loc) · 3.99 KB

AGENTS.md — CrossPoint Reader

Open-source EPUB e-reader firmware for Xteink X4 (ESP32-C3 RISC-V, 160MHz, 380KB RAM, no PSRAM, 16MB flash, 800×480 E-Ink monochrome single-buffer).

See CLAUDE.md for the full development guide. This file is the condensed "things you'd otherwise guess wrong" reference.

Build & Verify

pio run                        # build (default env, LOG_LEVEL=2)
pio run -t clean && pio run    # clean build
pio run -e gh_release          # production build (LOG_LEVEL=1)
pio run -t upload              # build + flash device
pio run -t upload && pio device monitor   # flash + serial (115200 baud)
pio check                      # cppcheck static analysis (--fail-on-defect=low)

Formatting: clang-format 21 via project script (not raw clang-format -i):

./bin/clang-format-fix          # all tracked .c/.cpp/.h/.hpp (excludes generated/)
./bin/clang-format-fix -g       # only git-modified files

CI runs on push to master and all PRs: clang-format → cppcheck → build (gated by test-status job). PR titles must follow conventional commits (feat:, fix:, etc.) — enforced by semantic-pull-request bot.

Files You Must Never Edit

Generated file Source Generator
lib/I18n/I18nKeys.h, I18nStrings.h, I18nStrings.cpp lib/I18n/translations/*.yaml scripts/gen_i18n.py (runs pre: build)
src/network/html/*.generated.h data/html/*.html scripts/build_html.py (runs pre: build)

All generated files + .pio/, build/, compile_commands.json, platformio.local.ini are in .gitignore.

To add translations: edit YAML, run python scripts/gen_i18n.py lib/I18n/translations lib/I18n/, commit only the YAML.

Architecture (Non-Obvious)

  • Activity lifecycle: Activities are heap-allocated (new), not singletons. Navigation = delete old + new next. Pattern: onEnter() allocates + renders, loop() handles input, onExit() frees everything. FreeRTOS tasks created in onEnter() MUST be vTaskDelete()'d in onExit() BEFORE activity destruction.
  • HAL layer: Always use HalDisplay, HalGPIO, HalStorage — never the raw SDK classes (EInkDisplay, InputManager, SDCardManager).
  • Button mapping: Use MappedInputManager::Button enums (Button::Confirm, Button::PageForward), never raw GPIO indices.
  • File I/O: FsFile (SdFat), NOT Arduino File. Must call .close() explicitly.
  • UI strings: All user-facing text uses tr(STR_XXX). Log messages (LOG_DBG/LOG_ERR) may be hardcoded.
  • Single framebuffer: EINK_DISPLAY_SINGLE_BUFFER_MODE=1. Grayscale rendering requires renderer.storeBwBuffer() / renderer.restoreBwBuffer().

Font ID Convention

Font IDs are integer #defines in src/fontIds.h (NOT string identifiers). Use like:

#include "fontIds.h"
renderer.drawText(UI_10_FONT_ID, x, y, "text");

Available: NOTOSERIF_12_FONT_ID through 18, NOTOSANS_*, OPENDYSLEXIC_8 through 14, UI_10_FONT_ID, UI_12_FONT_ID, SMALL_FONT_ID.

Critical Resource Constraints

  • C++20, no exceptions (-fno-exceptions), no RTTI.
  • Stack locals < 256 bytes. std::vector → call .reserve(N) before push_back loops.
  • constexpr/static const for large data (lives in flash, not DRAM).
  • std::string_view is NOT null-terminated. Convert via std::string(v).c_str() before any C API.
  • ESP32-C3 = RISC-V → faults on unaligned multi-byte loads. Use memcpy, never pointer casts on raw buffers.
  • IRAM_ATTR for ISRs, DRAM_ATTR for data accessed by ISR code.
  • xSemaphoreTake() crashes in ISR context → use xSemaphoreGiveFromISR() / xQueueSendFromISR().

Cache

SD-based binary cache at .crosspoint/epub_<hash>/ on SD card root. Auto-invalidated on format version bump or render setting change. Current versions: BOOK_CACHE_VERSION = 5 (BookMetadataCache.cpp:12), SECTION_FILE_VERSION = 21 (Section.cpp:13). When modifying cache structs, increment the version and update docs/file-formats.md.