diff --git a/README_dev.md b/README_dev.md new file mode 100644 index 0000000..6809ce9 --- /dev/null +++ b/README_dev.md @@ -0,0 +1,373 @@ +# CrossPoint Reader - Developer Guide + +This guide provides essential information for developers working on the CrossPoint Reader firmware for the Xteink X4 e-paper device. + +## Project Overview + +CrossPoint Reader is an open-source firmware replacement for the Xteink X4 e-paper reader, built using PlatformIO and targeting the ESP32-C3 microcontroller. The firmware provides EPUB reading capabilities with a focus on memory efficiency due to the ESP32-C3's limited RAM (~380KB). + +## Architecture + +### Main Components + +1. **Activity System**: The firmware uses an activity-based architecture where each screen/state is an `Activity` subclass +2. **Display System**: E-ink display handling via `EInkDisplay` and `GfxRenderer` from the OpenX4 SDK +3. **Input System**: Button handling via `InputManager` and `MappedInputManager` with configurable button layouts +4. **File System**: SD card management via `SDCardManager` +5. **State Management**: `CrossPointState` and `CrossPointSettings` for persistent configuration + +### Key Directories + +- `src/activities/` - All screen activities (Home, Reader, Settings, etc.) +- `src/network/` - Web server and OTA update functionality +- `src/util/` - Utility functions +- `src/images/` - Embedded image assets +- `open-x4-sdk/` - Community SDK with hardware libraries (submodule) + +## Display Programming + +### E-Ink Display Basics + +The display is a 480x800 e-paper panel controlled via SPI. Key display-related files: + +- `src/main.cpp` - Display initialization and SPI pin definitions +- Uses `EInkDisplay` from OpenX4 SDK +- Uses `GfxRenderer` for drawing operations + +### Display Initialization + +```cpp +// Display SPI pins (custom pins for XteinkX4) +#define EPD_SCLK 8 // SPI Clock +#define EPD_MOSI 10 // SPI MOSI +#define EPD_CS 21 // Chip Select +#define EPD_DC 4 // Data/Command +#define EPD_RST 5 // Reset +#define EPD_BUSY 6 // Busy + +EInkDisplay einkDisplay(EPD_SCLK, EPD_MOSI, EPD_CS, EPD_DC, EPD_RST, EPD_BUSY); +GfxRenderer renderer(einkDisplay); +``` + +### Drawing Operations + +The `GfxRenderer` class provides drawing primitives: + +```cpp +// Basic drawing functions +renderer.clearScreen(); // Clear display buffer +renderer.drawText(fontId, x, y, text); // Draw text +renderer.drawRect(x, y, width, height); // Draw rectangle outline +renderer.fillRect(x, y, width, height); // Fill rectangle +renderer.drawCenteredText(fontId, y, text); // Center text horizontally +renderer.displayBuffer(); // Update physical display + +// Screen dimensions +int width = renderer.getScreenWidth(); // 480 in portrait +int height = renderer.getScreenHeight(); // 800 in portrait +``` + +### Font System + +Multiple font families are available (Bookerly, NotoSans, OpenDyslexic) in various sizes. Fonts are registered in `main.cpp`: + +```cpp +// Font registration example +renderer.insertFont(BOOKERLY_14_FONT_ID, bookerly14FontFamily); +``` + +Font IDs are defined in `src/fontIds.h`. + +### Display Refresh Optimization + +Due to e-paper characteristics: +- Use `renderer.displayBuffer()` sparingly (causes screen refresh) +- Partial refreshes are handled automatically by the SDK +- Full refresh frequency is configurable in settings + +## Button Input Handling + +### Hardware Button Mapping + +The Xteink X4 has: +- **Front buttons**: 4 buttons (Back, Confirm, Left, Right) +- **Side buttons**: 2 buttons (Up/Down for page turning) +- **Power button**: Separate power/sleep control + +### Input Manager System + +Two-layer input system: + +1. **`InputManager`** (OpenX4 SDK): Raw button state +2. **`MappedInputManager`**: Configurable button mapping + +### Button Configuration + +Button layouts are configurable in settings: +- **Front button layout**: Back/Confirm/Left/Right can be rearranged +- **Side button layout**: Page up/down can be swapped + +### Using MappedInputManager + +```cpp +// In Activity constructor +MappedInputManager& mappedInput; + +// Check button states +if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + // Handle confirm press +} + +if (mappedInput.isPressed(MappedInputManager::Button::Power)) { + // Power button currently held +} + +// Get button labels for UI +auto labels = mappedInput.mapLabels("Back", "Select", "Prev", "Next"); +``` + +### Available Button Constants + +```cpp +MappedInputManager::Button::Back +MappedInputManager::Button::Confirm +MappedInputManager::Button::Left +MappedInputManager::Button::Right +MappedInputManager::Button::Up +MappedInputManager::Button::Down +MappedInputManager::Button::Power +MappedInputManager::Button::PageBack +MappedInputManager::Button::PageForward +``` + +### Power Button Behavior + +- **Short press**: Configurable (ignore, sleep, or page turn) +- **Long press** (>400ms): Always enters deep sleep +- **Wake from sleep**: Requires long press verification + +## Activity System + +### Creating a New Activity + +1. Create header file in appropriate `src/activities/` subdirectory +2. Inherit from `Activity` base class +3. Implement required methods + +### Activity Template + +```cpp +// MyActivity.h +#pragma once +#include "activities/Activity.h" + +class MyActivity : public Activity { +public: + MyActivity(GfxRenderer& renderer, MappedInputManager& mappedInput); + void onEnter() override; + void onExit() override; + void loop() override; + +private: + void render(); +}; + +// MyActivity.cpp +MyActivity::MyActivity(GfxRenderer& renderer, MappedInputManager& mappedInput) + : Activity("MyActivity", renderer, mappedInput) {} + +void MyActivity::onEnter() { + Activity::onEnter(); + // Initialization code +} + +void MyActivity::onExit() { + // Cleanup code + Activity::onExit(); +} + +void MyActivity::loop() { + // Handle input + if (mappedInput.wasPressed(MappedInputManager::Button::Confirm)) { + // Handle action + } + + // Update and render if needed + render(); +} + +void MyActivity::render() { + renderer.clearScreen(); + // Drawing code + renderer.displayBuffer(); +} +``` + +### Activity Navigation + +Activities are managed in `main.cpp`: + +```cpp +// Switch to new activity +exitActivity(); // Clean up current +enterNewActivity(new MyActivity(renderer, mappedInputManager)); +``` + +## Memory Management + +### Constraints + +- ESP32-C3 has ~380KB usable RAM +- Display buffer: ~45KB (480x800 / 8 bits per pixel) +- Must cache EPUB data to SD card + +### Caching Strategy + +EPUB content is cached to `.crosspoint/` directory on SD card: +- Chapter data cached after first load +- Book metadata stored in `book.bin` +- Reading progress in `progress.bin` +- Cover images in `cover.bmp` + +### Best Practices + +1. **Avoid dynamic allocations in loop()** +2. **Use stack allocation when possible** +3. **Free resources in `onExit()`** +4. **Monitor memory usage** (serial output shows free heap) + +## Configuration System + +### CrossPointSettings + +Singleton class for user settings: +- Display settings (orientation, refresh rate) +- Reader settings (font, spacing, alignment) +- Button layouts +- Sleep timeouts + +```cpp +// Access settings +SETTINGS.fontFamily; // Current font family +SETTINGS.getSleepTimeoutMs(); // Auto-sleep timeout +SETTINGS.getPowerButtonDuration(); // Long-press duration +``` + +### CrossPointState + +Singleton for runtime state: +- Currently open EPUB path +- Last sleep screen image index + +## Development Workflow + +### Building and Flashing + +```bash +# Build project +pio run + +# Flash to device +pio run --target upload + +# Monitor serial output +pio device monitor +``` + +### Debugging + +1. **Serial Monitor**: View logs at 115200 baud +2. **Memory Monitoring**: Heap usage printed every 10 seconds +3. **Error Handling**: Full-screen error activities for SD card failures + +### Common Pitfalls + +1. **Memory Exhaustion**: Watch for heap fragmentation +2. **Display Artifacts**: Ensure proper buffer management +3. **Button Debouncing**: Handled by InputManager +4. **SD Card Access**: Use `SdMan` helper for file operations + +## Testing + +### Manual Testing Areas + +1. **Button mappings** in different layout configurations +2. **Memory usage** with large EPUB files +3. **Sleep/wake cycles** with power button +4. **SD card** removal/insertion during operation +5. **Battery indicator** accuracy + +### Automated Testing + +- PlatformIO test framework available +- Run tests: `pio test` +- Test directory: `test/` + +## Contributing Guidelines + +### Code Style + +- Follow existing code patterns +- Use `clang-format` for formatting +- Add serial logging for major operations +- Include error handling for SD card operations + +### Adding Features + +1. Check [ideas discussion board](https://github.com/daveallie/crosspoint-reader/discussions/categories/ideas) +2. Create feature branch +3. Implement with memory constraints in mind +4. Test on actual hardware +5. Submit PR with clear description + +### Performance Considerations + +- Minimize display refreshes +- Cache computed values (text measurements, etc.) +- Use `skipLoopDelay()` for activities needing fast response (webserver) +- Implement `preventAutoSleep()` for activities with background work + +## Hardware Reference + +### Pin Definitions (from main.cpp) + +```cpp +// Display SPI +#define EPD_SCLK 8 +#define EPD_MOSI 10 +#define EPD_CS 21 +#define EPD_DC 4 +#define EPD_RST 5 +#define EPD_BUSY 6 + +// SD Card SPI +#define SD_SPI_MISO 7 + +// Battery monitoring +#define BAT_GPIO0 0 + +// USB detection +#define UART0_RXD 20 +``` + +### Power Management + +- Deep sleep mode for power saving +- Wake on power button press (GPIO low) +- Auto-sleep after configurable inactivity period +- Battery monitoring via ADC on GPIO0 + +## Useful Resources + +- [OpenX4 SDK Documentation](https://github.com/open-x4-epaper/community-sdk) +- [PlatformIO Documentation](https://docs.platformio.org/) +- [ESP32-C3 Reference](https://docs.espressif.com/projects/esp-idf/en/latest/esp32c3/) +- [EPUB Format Specification](https://www.w3.org/publishing/epub3/epub-overview.html) + +## Getting Help + +- Check existing issues and discussions +- Review `docs/` directory for internal documentation +- Test with actual Xteink X4 hardware when possible +- Enable serial logging for debugging +