Complete documentation for the e-paper watch project.
- Watchface Gallery - Browse all 54 watchfaces with preview images
- WatchFace API Reference - Complete API documentation for creating custom watchfaces
- Hardware Guide - Detailed hardware specifications, wiring, and assembly
- Display: 200×200 monochrome e-paper (1.54", 200 DPI)
- Watchfaces: 54+ pre-configured with custom fonts
- Time Keeping: DS3231 RTC (±2 minutes/year accuracy)
- Refresh Modes: Partial (fast) and full (ghosting-free)
- Development Tools: Python scripts for creating custom watchfaces
Edit epaper_watch.ino to customize behavior:
// Enable/disable features
const bool ENABLE_WATCHFACE_CYCLING = true; // Auto-cycle every 10 mins
const bool ENABLE_PARTIAL_REFRESH = true; // Fast updates
const bool ENABLE_SERIAL_DEBUG = false; // Disable for battery life
// Timing
const uint8_t FULL_REFRESH_INTERVAL = 10; // Minutes (10 = :00, :10, :20...)
const uint8_t WAKE_BEFORE_MINUTE_SEC = 57; // Wake at :57s (3s poll window)
const uint16_t POLL_INTERVAL_MS = 200; // Poll every 200msMaximum Battery Life:
const uint8_t WAKE_BEFORE_MINUTE_SEC = 50; // 10-second window
const uint16_t POLL_INTERVAL_MS = 300; // Poll every 300ms
const bool ENABLE_SERIAL_DEBUG = false; // Disable serialBalanced - Default:
const uint8_t WAKE_BEFORE_MINUTE_SEC = 57; // 3-second window
const uint16_t POLL_INTERVAL_MS = 200; // Poll every 200ms
const bool ENABLE_SERIAL_DEBUG = false; // Disable serialFast Response:
const uint8_t WAKE_BEFORE_MINUTE_SEC = 59; // 1-second window
const uint16_t POLL_INTERVAL_MS = 100; // Poll every 100mscd watchfaceutils
# Interactive configurator with live preview
python3 configure_watchface.py
# Test single watchface
python3 render_watchface.py \
--watchface ../mywatchfaces/custom.h \
--time-font ../myfonts/Font20pt7b.h \
--date-font ../myfonts/Font10pt7b.h \
--output custom_preview.png
# Generate all previews
python3 generate_all_previews.pyStep 1: Prepare 200×200 monochrome image
Step 2: Convert to bitmap array using image2cpp
Step 3: Create watchface header file
// mywatchfaces/custom.h
const unsigned char custom_bitmap[] PROGMEM = {
// ... bitmap data ...
};
// Required array wrapper
static const int custom_bitmap_allArray_LEN = 1;
static const unsigned char* custom_bitmap_allArray[1] = {
custom_bitmap
};
// WatchFace class
struct WatchFace_custom : public WatchFace {
WatchFace_custom() {
bitmap = custom_bitmap;
layout = 1; // 0=single-line, 1=two-line
// Time positioning
text1x = -1; // -1=center, 0-100=percentage
text1y = 40; // 40% from top
text1font = &Font20pt7b;
// Date positioning
text2x = -1;
text2y = 60; // 60% from top
text2font = &Font10pt7b;
}
};Step 4: Include in myutils.h
#include "mywatchfaces/custom.h"Step 5: Add to epaper_watch.ino
WatchFace* allWatchFaces[] = {
// ... existing watchfaces ...
new WatchFace_custom(),
};Preview of available watchfaces:
![]() AT-AT |
![]() Harry Potter |
![]() Planets |
![]() Beatles Penguins |
![]() Hogwarts |
![]() Guitar |
Collections: Star Wars (6) • Harry Potter (6) • Club Penguin (6) • Animals (9) • Characters (14) • Music (3) • Nature (5) • Patterns (3) • Cultural (2)
Display not updating
- Check SPI connections (MOSI, SCK, CS, DC, RST, BUSY)
- Verify 3.3V power supply
- Try disabling partial refresh:
const bool ENABLE_PARTIAL_REFRESH = false;
Display is garbled
- Verify correct model (GDEH0154D67)
- Check ground connection
- Ensure proper SPI wiring
Ghosting artifacts
- Increase full refresh frequency:
const uint8_t FULL_REFRESH_INTERVAL = 5; - Temperature affects ghosting (worse in cold)
Time is incorrect after upload
- Enable time setup:
const bool ENABLE_TIME_SETUP = true; - Upload and open Serial Monitor (115200 baud)
- Follow prompts to set time
RTC not detected
- Verify I2C connections (SDA, SCL)
- Check I2C address (0x68)
- Test with I2C scanner
- Try different RTC module
Battery drains too quickly
- Disable serial debug:
const bool ENABLE_SERIAL_DEBUG = false; - Optimize wake time:
const uint8_t WAKE_BEFORE_MINUTE_SEC = 50; - Check for shorts or bad connections
- Measure current with multimeter
Time updates miss minute changes
- Increase polling window:
const uint8_t WAKE_BEFORE_MINUTE_SEC = 55; - Decrease poll interval:
const uint16_t POLL_INTERVAL_MS = 100;
"library not found"
- Install missing library via Library Manager
"multiple definition"
- Ensure bitmap arrays in watchface files have
statickeyword:static const unsigned char* bitmap_allArray[1] = { ... };
"struct redefinition"
- Check for duplicate struct names
- Ensure watchface files included only once
Flash (1MB total): ~800KB used (78%)
- Sketch: ~85KB
- Libraries: ~68KB
- Fonts (66): ~380KB
- Watchfaces (54): ~270KB
RAM (256KB total): ~23KB used (9%)
- Global variables: ~12KB
- Display buffer: ~5KB
- Stack: ~4KB
| Mode | Current | Notes |
|---|---|---|
| Deep Sleep | ~50µA | Between polling windows |
| Active Polling | ~1mA | Last 3 seconds of each minute |
| Display Partial | ~5mA | 200-500ms duration |
| Display Full | ~8mA | 1-2s duration every 10 mins |
| Average | 200-500µA | Typical usage |
| Operation | Duration |
|---|---|
| Partial refresh | 200-500ms |
| Full refresh | 1-2s |
| Text rendering | 50-100ms |
| Bitmap drawing | 100-200ms |
| RTC read | <1ms |
- Create header file in
mywatchfaces/ - Include in
myutils.h:#include "mywatchfaces/your_watchface.h"
- Add to array in
epaper_watch.ino:WatchFace* allWatchFaces[] = { // ... existing ... new WatchFace_your_watchface(), };
- Comment out in
epaper_watch.ino:// new WatchFace_unused(), - Optionally comment out include in
myutils.hto save memory
configure_watchface.py: Interactive step-by-step configurator
- Select watchface and fonts
- Position text interactively
- Generate C++ code
- Save configuration
render_watchface.py: Command-line renderer for testing
- Render single watchface
- Test different positions
- Generate preview images
generate_all_previews.py: Batch preview generator
- Generate all watchface previews
- Auto-detects configurations
- Useful for documentation
pip3 install Pillow| Specification | Details |
|---|---|
| MCU | Nordic nRF52840 (ARM Cortex-M4, 64MHz, 1MB Flash, 256KB RAM) |
| Display | GoodDisplay GDEH0154D67 (1.54", 200×200, SPI, E-Ink) |
| RTC | Maxim DS3231 (I2C, TCXO, ±3.5ppm) |
| Power | 3.7V LiPo, 300-500mAh recommended |
| Update Latency | <3 seconds during watch face refresh |
| Refresh Rate | Partial updates every minute, full every 10 minutes |





