A cross-platform command-line tool for flashing firmware to Xteink X4 e-paper reader devices (ESP32-C3). Works with any ESP32-C3 compatible firmware, including Papyrix and CrossPoint Reader.
- Simple: Just run
papyrix-flasher flash firmware.bin- bootloader and partition table are embedded - Auto-detect: Automatically finds connected ESP32-C3 devices
- Cross-platform: Works on Windows, Linux, and macOS
- Verification: MD5 verification after flashing (enabled by default)
- Progress bar: Visual progress during flashing
Download the latest release for your platform from the Releases page.
git clone https://github.com/bigbag/papyrix-flasher.git
cd papyrix-flasher
make build# Auto-detect device and flash
papyrix-flasher flash firmware.bin
# Specify port explicitly
papyrix-flasher flash -p /dev/ttyUSB0 firmware.bin # Linux
papyrix-flasher flash -p /dev/cu.usbserial-* firmware.bin # macOS
papyrix-flasher flash -p COM3 firmware.bin # Windows
# Flash firmware only (skip bootloader/partitions for faster updates)
papyrix-flasher flash --firmware-only firmware.bin
# Skip verification (faster, but risky)
papyrix-flasher flash --verify=false firmware.bin# Auto-detect and show device info
papyrix-flasher info
# Check specific port
papyrix-flasher info -p /dev/ttyUSB0papyrix-flasher listpapyrix-flasher versionThe Xteink X4 has 16MB of flash memory, organized as:
- Bootloader at
0x0000(~12KB) - ESP32-C3 second-stage bootloader - Partitions at
0x8000(3KB) - Partition table - App (OTA 0) at
0x10000(~6.3MB) - Main application - App (OTA 1) at
0x650000(~6.3MB) - OTA update partition - SPIFFS at
0xC90000(~3.3MB) - File storage
By default, papyrix-flasher writes to:
- Bootloader at 0x0000 (embedded in tool)
- Partition table at 0x8000 (embedded in tool)
- Firmware at 0x10000 (your file)
This tool implements the ESP32 ROM bootloader protocol to flash firmware over a USB serial connection.
- Serial: 921600 baud, 8N1, no flow control
- Framing: SLIP (Serial Line Internet Protocol) with
0xC0delimiters and escape sequences for special bytes - Protocol: ESP32 ROM bootloader binary protocol with request/response packets and XOR checksum
The ESP32-C3 enters bootloader mode via DTR/RTS signal sequence that controls the EN (reset) and GPIO0 (boot mode) pins through transistor drivers:
- Assert EN low (reset the chip)
- Assert GPIO0 low while releasing EN (boot into download mode)
- Release GPIO0 (chip stays in bootloader)
- Reset to bootloader - DTR/RTS signal sequence
- SYNC - Establish communication with bootloader (up to 10 retries)
- SPI_ATTACH - Attach the SPI flash chip
- SPI_SET_PARAMS - Configure flash size (16MB)
- FLASH_DEFL_BEGIN - Start compressed flash session, erase sectors
- FLASH_DEFL_DATA - Send zlib-compressed firmware in 1KB blocks (with retry on failure)
- FLASH_DEFL_END - Finalize flash session
- Hard reset - Reboot into the new firmware
Firmware is compressed using zlib (deflate) before transfer. The bootloader decompresses data on-the-fly, reducing transfer time significantly (typically 2-4x compression ratio).
- Ensure the device is connected via USB
- Check if the device appears in
papyrix-flasher list - On Linux, you may need to add yourself to the
dialoutgroup:Then log out and back in.sudo usermod -a -G dialout $USER
On Linux/macOS, you may need to run with sudo or fix port permissions:
sudo papyrix-flasher flash firmware.bin
# Or fix permissions permanently (Linux)
sudo chmod 666 /dev/ttyUSB0- Try pressing the reset button on the device while the tool is connecting
- Make sure no other program is using the serial port
- Try a different USB cable
papyrix-flasher/
├── cmd/papyrix-flasher/ # CLI entry point
├── internal/
│ ├── slip/ # SLIP protocol encoding/decoding
│ │ ├── slip.go
│ │ └── slip_test.go
│ ├── protocol/ # ESP32 bootloader protocol
│ │ ├── commands.go
│ │ ├── commands_test.go
│ │ ├── packet.go
│ │ ├── packet_test.go
│ │ └── esp32c3.go
│ ├── serial/ # Serial port abstraction
│ ├── detect/ # Device auto-detection
│ └── flasher/ # High-level flash operations
├── embedded/ # Embedded bootloader and partitions
├── Makefile
└── README.md
# Build for current platform
make build
# Build for all platforms
make build-all
# Update embedded binaries from papyrix-reader
make update-embedded
# Create and push a release tag (triggers GitHub release workflow)
make tag# Run all tests
make test
# Run tests with verbose output
go test -v ./...
# Run tests for specific packages
go test -v ./internal/slip ./internal/protocolUnit tests cover the core protocol packages:
- slip: SLIP framing encode/decode, escape sequences, frame extraction
- protocol: Packet encoding/decoding, checksum calculation, command data generation
MIT License - see LICENSE for details.