Skip to content

alfiedennen/poem1-stoppedmod

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Poem/1: Stopped Clocks Mod

A mod for the Poem/1 e-paper device that displays AI-generated poems overlaid on photographs of stopped public clocks from stoppedclocks.org.

Living Clock Display

What This Is

The Poem/1 is an e-paper device by Acts Not Facts that displays time-specific AI-generated poetry. This mod replaces the standard display with photographs of stopped clocks showing the current time, with the poem overlaid in detected whitespace zones.

At 10:15, the device shows a photograph of a clock stopped at 10:15, with a poem about 10:15 rendered on the image.

Technical Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         Poem/1 Device                                │
│                    (M5PaperS3 / ESP32-S3)                           │
└─────────────────────────────────────────────────────────────────────┘
                                │
                                │ Every minute:
                                │
        ┌───────────────────────┴───────────────────────┐
        │                                               │
        ▼                                               ▼
┌───────────────────┐                      ┌───────────────────────┐
│   poem.town API   │                      │  stoppedclocks.org    │
│                   │                      │                       │
│ POST /api/v1/     │                      │ GET /living-clock/    │
│   clock/compose   │                      │  living-clock-index.  │
│                   │                      │       json            │
│ Returns:          │                      │                       │
│ - poem text       │                      │ Returns:              │
│ - font preference │                      │ - image URLs          │
└───────────────────┘                      │ - text zone coords    │
                                           └───────────────────────┘

Hardware

Target Device: Poem/1 by Acts Not Facts (M5PaperS3 variant)

Specification Value
MCU ESP32-S3 with PSRAM
Display ED047TC2 4.7" e-paper (960x540)
Color depth 4-bit grayscale
Interface 8-bit parallel EPD bus
Memory 16MB flash, 8MB PSRAM

Key Technical Details

Button Support (GPIO 2)

Important: The physical button is on GPIO 2, not GPIO 38 as M5Stack documentation states. This was verified through GPIO scanning.

Action Function
Single click Show/dismiss notes from poem.town dashboard
Double click Like the current poem

Notes are displayed full-screen with dynamic font sizing (72→24px) to fit the content. When a note is displayed, the firmware automatically sends a "seen" receipt to the poem.town API.

Custom Display Driver

The M5PaperS3's GT911 touch controller doesn't respond on some units, which breaks M5GFX auto-detection. This firmware uses a custom LGFX_M5PaperS3 class that directly initializes the display hardware:

class LGFX_M5PaperS3 : public lgfx::LGFX_Device {
    // Explicit Bus_EPD and Panel_EPD configuration
    // Bypasses GT911 detection failure
};

Image Loading

  • Images served via CloudFront CDN
  • Downloaded to PSRAM buffer before rendering (stream-based loading unreliable)
  • Source images: 480x270 PNG, displayed at 2x scale (960x540)

Text Zone Detection

Each clock image has pre-analyzed whitespace zones for poem placement:

{
  "t": "0930",
  "i": [{
    "url": "https://stoppedclocks.org/living-clock/images/0930_clock-name.png",
    "tz": { "x": 0, "y": 0, "w": 256, "h": 296 },
    "strip": "top",
    "rec": "ZONE_TOP"
  }]
}

Zone analysis uses dither density detection (8x8 cell blocks) to find areas suitable for text overlay.

Custom Font Rendering

The firmware supports custom TrueType fonts via OpenFontRender:

  • Inter - Clean sans-serif for modern poems
  • Playfair Display - Elegant serif for classic poems
  • Fonts downloaded from CDN at boot (~500KB total)
  • poem.town API returns font field ("INTER" or "PLAYFAIR")
  • Font preference controlled via poem.town dashboard
  • Dynamic font sizing: tries sizes 72→24px, picks largest that fits

Text Rendering

The firmware automatically:

  • Downloads and caches TTF fonts to PSRAM
  • Re-wraps poem text using actual font metrics
  • Tries font sizes 72→24px, picks largest that fits within 75% of zone
  • Centers text in the detected whitespace zone
  • Falls back to strip positioning if no zone detected
  • Only reloads fonts when preference changes (prevents memory leaks)

12-Hour Time Matching

Clock faces show 12-hour format without AM/PM. The matching algorithm:

  • Converts 24-hour current time to 12-hour
  • Finds closest clock image (handles wraparound)
  • Treats 10:15 clock as valid for both 10:15 AM and 10:15 PM

Loading Screen

The firmware displays an elegant loading screen during boot with:

  • Vintage clock graphic - Decorative bezel, dot hour markers, thick elegant hands at 07:07
  • Large typography - "Poem/1" title at 8x scale, "Stopped Clocks Mod" subtitle at 3x
  • Asymmetric layout - Clock on left, text on right with decorative separator line
  • Status messages - Shows "Loading fonts...", "Loading clock index...", etc.

Note Display

Notes from the poem.town dashboard are displayed full-screen when the button is pressed:

  • Dynamic font sizing - Tries sizes 72→24px, picks largest that fits
  • Proper text metrics - Accounts for font ascender/descender space
  • Vertical centering - Text block centered within margins (30px top/bottom)
  • Auto-dismiss - Returns to clock view after 10 seconds or on button press
  • Read receipts - Automatically marks notes as "seen" via API

File Structure

living-clock/
├── firmware/
│   ├── src/main.cpp        # Main firmware (~1000 lines)
│   └── platformio.ini      # Build configuration
├── generate_text_zones.py  # Whitespace analysis tool
├── create-combined-index.js # Index builder
├── text-zones.json         # Per-image zone data
├── living-clock-index.json # Combined time→image→zone index
├── LICENSE                 # MIT License
└── README.md               # This file

Building

Prerequisites

  • PlatformIO CLI or VSCode extension
  • USB-C cable for flashing

Configure WiFi

Edit firmware/src/main.cpp:

const char* WIFI_SSID = "your-wifi-ssid";
const char* WIFI_PASS = "your-wifi-password";

Build and Flash

cd firmware
pio run -t upload

Monitor Serial Output

pio device monitor --baud 115200

API Endpoints

poem.town Compose API

POST https://poem.town/api/v1/clock/compose
Authorization: Bearer <token>
Content-Type: application/json

{ "screenId": "...", "time24": "09:30" }

Returns time-specific rhyming poem with font preference ("INTER" or "PLAYFAIR"), plus any pending notes.

Note: The screenId must be the device MAC address in reverse byte order to match the poem.town dashboard format.

poem.town Notes API

POST https://poem.town/api/v1/clock/notes/{noteId}/seen
Authorization: Bearer <token>
Content-Type: application/json

{ "screenId": "..." }

Marks a note as seen. Called automatically when note is displayed on device.

poem.town Likes API

POST https://poem.town/api/v1/clock/likes/{poemId}/mark
Authorization: Bearer <token>
Content-Type: application/json

{ "screenId": "..." }

Records a like for the current poem. Triggered by double-click on device button.

Stopped Clocks Index

GET https://stoppedclocks.org/living-clock/living-clock-index.json

Returns compact time→image→zone mapping for firmware consumption.

Data Sources

Clock Images

  • Source: stoppedclocks.org collection (200+ UK public clocks)
  • Processing: Cropped to 480x270, converted to grayscale PNG
  • CDN: CloudFront at https://stoppedclocks.org/living-clock/images/

Fonts

  • Inter: Downloaded from CDN (~412KB TTF)
  • Playfair Display: Downloaded from CDN (~96KB TTF)
  • CDN: https://stoppedclocks.org/living-clock/fonts/

Text Zones

  • Generation: generate_text_zones.py analyzes each image
  • Method: Dither density analysis with 8x8 cell blocks
  • Coverage: 99.6% of images have detected whitespace zones

Dependencies

lib_deps =
    m5stack/M5GFX@^0.2.17
    bblanchon/ArduinoJson@^7.0.0
    https://github.com/takkaO/OpenFontRender.git

Credits

  • poem.town - AI poetry generation by Acts Not Facts
  • Stopped Clocks - Clock photography collection by Alfie Dennen
  • M5Stack - M5PaperS3 hardware and M5GFX library
  • OpenFontRender - TrueType font rendering by takkaO

License

MIT License - see LICENSE for details.

Related Projects


Last Updated: 2025-12-15

About

Poem/1 mod: AI poems overlaid on stopped clock photographs from stoppedclocks.org

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors