Minimal EPUB reader for the Xteink X4 device (ESP32-C3 + SSD1677 e-ink display, 480×800 portrait). Includes a desktop SDL2 emulator for development without hardware.
| MCU | ESP32-C3 (RISC-V, 160 MHz) |
| Display | 4.26" e-ink 800×480 (SSD1677), rotated → 480×800 portrait |
| Storage | SD card (FAT32, SPI) |
| Flash | 16 MB |
| Input | ADC buttons |
Books (.epub) can go anywhere on the SD card — the device scans recursively from the root. Fonts (.mfb) go in the fonts/ folder on the SD card.
Simply copy files to the SD card while it is connected to your computer, then reinsert it into the device.
The device displays an image when it enters deep sleep. Several images are built into the firmware. You can also add your own by placing BMP files in the .sleep/ folder on the SD card.
Supported BMP variants: 1 bpp monochrome, 4 bpp indexed, 8 bpp indexed, 16 bpp RGB565 / BGR555, 24 bpp BGR, 32 bpp BGRA. Use 800×480 pixels for best quality (landscape) or 480×800 (portrait — automatically rotated 90° CCW). Other sizes are scaled to fit.
The first time an image is shown it is converted and cached; subsequent sleeps load the cache directly. The cache is cleared by Settings → Clear Cache.
python tools/serial_cmd.py --port COM4 --upload-sleep "path/to/my_image.bmp"Desktop emulator: copy any .bmp file into sd/.sleep/.
Open Settings → Sleep Image:
- Auto — cycles through all images in
.sleep/, picking a different one each sleep. - <filename> — pins the device to that specific image.
lib/microreader/ shared core (platform-agnostic C++20)
content/ EPUB parsing, layout, MRB binary format
display/ Canvas, DisplayQueue, Font interfaces
screens/ UI screen implementations
platforms/desktop/ SDL2 emulator
platforms/esp32/ ESP-IDF + PlatformIO firmware
test/ Google Test suite
tools/ Python scripts
resources/ Fonts, sleep images
cmake -S platforms/desktop -B build/desktop-debug -DCMAKE_BUILD_TYPE=Debug "-DCMAKE_POLICY_VERSION_MINIMUM:STRING=3.5"
cmake --build build/desktop-debug --config Debug
.\build\desktop-debug\Debug\microreader_desktop.exe# Build + flash
$env:USERPROFILE\.platformio\penv\Scripts\pio.exe run -t upload
# Serial monitor
$env:USERPROFILE\.platformio\penv\Scripts\pio.exe device monitor --baud 115200COM4, upload baud 921600.
cd test
cmake -B build2 -DCMAKE_BUILD_TYPE=Debug -DCMAKE_POLICY_VERSION_MINIMUM:STRING=3.5
cmake --build build2 --config Debug
.\build2\Debug\unit_tests.exe # fast (~375 tests, <1s)
.\build2\Debug\microreader_tests.exe # includes real EPUB integration testsReader fonts are FNTS bundles (.mfb), generated from TTF/OTF sources via tools/generate_font.py.
Two kinds:
- Built-in (
resources/fonts/) — embedded in the firmware asset blob. Require a firmware rebuild to update. - SD card (
resources/sd fonts/) — loaded from/sdcard/fonts/at runtime. No firmware rebuild needed; just copy or upload.
The generation command is the same for both:
python tools/generate_font.py "resources/sd fonts/ttf/Cartisse-Regular.ttf" `
-o "resources/sd fonts/Cartisse.mfb" --with-styles `
--bold "resources/sd fonts/ttf/Cartisse-Bold.ttf" `
--italic "resources/sd fonts/ttf/Cartisse-Italic.ttf" `
--bold-italic "resources/sd fonts/ttf/Cartisse-BoldItalic.ttf" `
--bundle --bundle-sizes 20 22 24 26 28 30 32 --font-name Cartisse
# Regenerate all SD fonts
$ttf = "resources/sd fonts/ttf"; $out = "resources/sd fonts"
foreach ($f in @("Bitter","Cartisse","NV_Bitter","NV_Charis","NV_Cooper","NV_Garamond","NV_Jost","NV_Palatium","Readerly")) {
python tools/generate_font.py "$ttf/$f-Regular.ttf" -o "$out/$f.mfb" --with-styles `
--bold "$ttf/$f-Bold.ttf" --italic "$ttf/$f-Italic.ttf" --bold-italic "$ttf/$f-BoldItalic.ttf" `
--bundle --bundle-sizes 20 22 24 26 28 30 32 --font-name $f
}
# Font preview (generates tools/font_overview.html)
python tools/font_overview.py
# UI fonts (bitmap, bw-only)
python tools/generate_font.py resources/fonts/terminus/Terminus-Bold.ttf 14 --header lib/microreader/display/ui_font_small.h --bw-only --ranges ui
python tools/generate_font.py resources/fonts/terminus/Terminus-Bold.ttf 18 --header lib/microreader/display/ui_font_medium.h --bw-only --ranges ui
python tools/generate_font.py resources/fonts/terminus/Terminus-Bold.ttf 24 --header lib/microreader/display/ui_font_large.h --bw-only --ranges ui
python tools/generate_font.py resources/fonts/terminus/Terminus-Bold.ttf 32 --header lib/microreader/display/ui_font_header.h --bw-only --ranges uiFont partition limit: SD card fonts must fit within 3.375 MB. The font data + 4 KB header must not exceed
0x360000bytes.
# Backup running firmware partition
python -m esptool --port COM4 read_flash 0x10000 0x650000 app0_backup.bin
# Restore
python -m esptool --port COM4 write_flash 0x10000 app0_backup.bin
# Switch OTA boot partition
python tools/switch_partition.py app0 --port COM4 --flash
python tools/switch_partition.py app1 --port COM4 --flashThe reader uses the Liang hyphenation algorithm — the same algorithm used by TeX. Language-specific TeX pattern files are compiled into compact binary tries by Typst hypher and embedded as constexpr byte arrays. The language is detected automatically from the EPUB's xml:lang attribute.
Supported languages:
| Code | Language | Trie size |
|---|---|---|
en |
English | 26 KB |
de |
German | 206 KB |
fr |
French | 7 KB |
es |
Spanish | 14 KB |
it |
Italian | 2 KB |
nl |
Dutch | 64 KB |
pt |
Portuguese | 1 KB |
pl |
Polish | 16 KB |
ru |
Russian | 33 KB |
Trie data lives in lib/microreader/content/hyphenation/Liang/hyph-<lang>.trie.h as constexpr byte arrays — no heap allocation, no flash reads at runtime (data is placed in DROM on ESP32).
To add a new language:
- Download the
.binfrom typst/hypher/tries intotools/hyphenation/ - Generate the header:
python tools/generate_trie_header.py tools/hyphenation/<lang>.bin lib/microreader/content/hyphenation/Liang/hyph-<lang>.trie.h <lang> - Add the new enum value to
HyphenationLanginHyphenation.h - Add a
#include+caseinHyphenation.cpp(hyphenate_word) and anieqcheck indetect_language
# Terminal 1
python tools/run_qemu.py --with-books
# Terminal 2
python tools/test_books.py --port socket://localhost:4444 --pages 20 --delay 0.1