A clock that tells the time with literature: 4,800+ curated book passages, one for every minute of the day, on a paper-like e-ink display — with the date and weather.
LitClock is built for people who want a literary clock on their shelf and never want to think about it again: setup is a two-minute, phone-only affair, and after that the clock configures, updates, and heals itself. The defaults are the product.
Build one · Living with it · Give one away · Under the hood · Credits
Four steps, roughly an afternoon (most of it waiting for the 3D printer).
| Part | Notes | ~Cost |
|---|---|---|
| Raspberry Pi Zero 2 W | Zero 2 W only — the only board this is tested on (see note). For the 40-pin header: buy the WH variant pre-soldered, solder your own, or tap in a solderless hammer header — no iron needed | $15 list |
| Waveshare 7.5" e-Paper HAT (V2) | V2 only, the one tested panel — Waveshare SKU 13504, Amazon ASIN B075R4QY3L. 800×480, black/white. Not the tri-colour B/C, not the 7.5" HD, not V1 (see note) (product page) | $60 |
| microSDHC card | 32 GB recommended | $10 |
| Micro-USB power supply | 5V/2A minimum | $10 |
| 3D-printed case (optional) | PLA — see step 3 | $5–30 |
| M2.5 threaded inserts + screws, USB-C→Micro-USB adapter (case only) | Inserts secure the case; the adapter puts a clean USB-C port on the back | $8 |
Full details in Hardware Assembly.
The Raspberry Pi Zero 2 W is the only board LitClock is tested on. Everything here — the image, the display code, the wiring, the case — is built and verified against that one board. Other Pis are not supported and have not been tried: the case is cut for the Zero's footprint, and nothing about the e-ink path has been validated anywhere else. A larger 64-bit Pi (3/4/5) may boot the image, but none of it is verified, so treat that as your own experiment rather than a supported build.
A 32-bit Pi cannot run it at all. The image is 64-bit (arm64), so an original Pi Zero W or a Pi 1/2 (ARMv6/ARMv7) shows no boot activity whatsoever — no display, no WiFi, nothing on the network, and no error message to go on. Worth ruling out first if a freshly flashed card seems dead.
The display must be the 7.5" V2, 800×480, black/white — Waveshare SKU 13504, sold on Amazon as ASIN B075R4QY3L. That is the only panel this is tested on, and the clock drives it with Waveshare's
epd7in5_V2driver. Waveshare sells several different 7.5" panels under similar names and they are not interchangeable: V1 is 640×384, the 7.5" HD is 880×528, and the B and C models are tri-colour. The layout is built for exactly 800×480, so a mismatched panel will not simply render smaller — it will not work. Whatever the listing is called, confirm it states 800×480 and two colours (black/white) before you buy.On price and stock: $15 is the list price, but as of July 2026 the Zero 2 W is frequently out of stock and often resold well above list. Check the authorized resellers before committing to the build, and be wary of marketplace listings at several times the price.
- Download the latest
.img.xzfrom Releases - Flash it to the microSD card with Raspberry Pi Imager or balenaEtcher
That's it — no config files to edit, no SSH to set up. Everything else happens from your phone after power-on.
Running your own code on the clock (terminal required)
The one-line
curl … | bashinstaller has been retired. It had to make system-level decisions — SPI overlays, sudo rules, group membership — on a machine whose settings belong to you, which made it impossible to test honestly. It had also drifted from the image build: it never writes thedtoverlay=spi0-1csline the image does, which pins the display to CE0.scripts/install.shhas now been removed outright — flashing the released image is the only supported install path.
Turn the clock's self-repair off as soon as you have a shell, before you check anything out. Left on, it will reboot the Pi under you and eventually throw your branch away. Two units matter:
litclock-update.timerships enabled and fires Sunday 03:00, with up to seven days of jitter. It resolves the latest release tag andgit reset --hards onto it.litclock-bootcheck.timeris the one people miss. Starting ~12 minutes after boot and every 5 minutes after that, it checks whether the clock has painted a quote — and a branch that doesn't paint is indistinguishable from a broken clock. The first two failed boots each triggersudo systemctl reboot, so your SSH session dies with no explanation. On the third it startslitclock-update.servicein rollback mode, which resets the checkout to the last known-good SHA.sudo systemctl disable --now litclock-update.timer litclock-bootcheck.timer sudo systemctl mask litclock-update.serviceMasking the service, and not just disabling its timer, also closes the third route to the same
git reset --hard: the Apply update button in the control app, which starts the service directly.Put it back when you're done, or you'll hand over a clock with no recovery net — this is what stops a bad update bricking a device with no keyboard:
sudo systemctl unmask litclock-update.service sudo systemctl enable --now litclock-update.timer litclock-bootcheck.timer
The flashed image contains a git checkout at /home/pi/litclock, so it doubles
as a development environment:
- Flash the released image and finish WiFi setup
- SSH in — SSH ships disabled, see Recovering a LitClock for how to turn it on
- Fetch your fork and check out your branch:
cd /home/pi/litclock
git remote add fork https://github.com/<you>/litclock.git
git fetch --depth 1 fork <your-branch>
git checkout -b <your-branch> FETCH_HEAD
sudo systemctl restart litclock.service litclock-control.servicelitclock.service paints the panel; litclock-control.service serves the
control app. Restart both, or changes to the app will look like they never
applied. The checkout does not rebuild the venv, so if your branch changes
requirements.txt you have to reinstall — but not with a plain
pip install -r requirements.txt. The packages named in requirements-apt.txt
come from apt on the image; pip would try to build them from source, which
needs a compiler the image does not have, and a successful build would shadow
the apt copy. Every install path filters them out first, so do the same:
cd /home/pi/litclock
REQ=$(mktemp)
EXCLUDE_RE=$(grep -vE '^[[:space:]]*(#|$)' requirements-apt.txt | sed 's/\./\\./g' | paste -sd'|')
grep -vE "^(${EXCLUDE_RE})==" requirements.txt > "$REQ"
./venv/bin/pip install --upgrade -r "$REQ"
rm -f "$REQ"Print-ready STLs are in 3d-models/ — three PLA parts, lightly modified from Arthur Gassner's Time Teller case design (CC BY). Then:
- Connect the e-Paper HAT to the Pi's 40-pin header, and the display to the HAT via the ribbon cable
- Insert the flashed SD card into the Pi — do this before closing the case
- Assemble the case — threaded inserts, the USB-C adapter on the back, screws
Arthur's guide covers the case assembly beautifully, and our Hardware Assembly page has the LitClock-specific details (ribbon-cable orientation, e-ink handling notes). No case? The bare HAT sandwich works fine on a shelf while you decide.
- Power on. Within a minute the display shows a WiFi network named "LitClock-Setup" with its password and a QR code.
- Join it from your phone's WiFi list — the setup page opens automatically (or browse to the address shown on the display).
iPhone note: current iOS (26.x) often doesn't open the sign-in page on its own — a known iOS change affecting local setup networks like this one, not something the clock can fix. If nothing pops up within a minute, just open Safari: the setup page appears immediately. Android opens it automatically.
- Pick your home WiFi and enter its password. That's the whole form — location, timezone, and temperature units auto-detect once the clock is online.
- The display shows "Ready to read." with a QR code to the clock's control app. Scan it, tap "Done — Start the Clock" (or just wait — it starts on its own), and add the app to your home screen.
Anything the auto-detection got wrong — city, units, mature-content filter — takes a few taps to fix in the app's Settings tab. Prefer paper instructions? Print the quick-start booklet (A4 / US Letter) — a one-sheet, fold-in-half guide written for non-technical users.
Every LitClock serves a small web app on your home network at http://litclock.local (or the clock's IP — the QR in the corner of every quote points there). Open it in any browser, or add it to your phone's home screen. No account, no cloud — the app is served by the clock itself and works only on your LAN.
| Screen | What it does |
|---|---|
| Status | Current quote, weather, WiFi, and version at a glance |
| Settings | Location (automatic by IP, or type any place worldwide), weather on/off, Fahrenheit/Celsius, mature-content filter |
| Updates | Current version, release notes, and a button to apply an update now |
| System | Restart, power off, reset WiFi, factory reset, and "Prepare for Gifting" |
| Diagnostics (separate page) | Read-only health panel at /diagnostics — last render, network, services, recent logs, and a downloadable support-log bundle. Linked from the app's live-status ribbon |
Weather works out of the box — no signup, no API key. The clock uses Open-Meteo as its default forecast provider, and location, timezone, and units are auto-detected during setup. Day-to-day changes are made in the control app's Settings tab. That's the whole configuration.
Under the hood, settings live in /home/pi/litclock/env.sh. You only need to touch this file for the advanced options below:
# Weather location (written by setup / the control app; override here if you want)
export WEATHER_LATITUDE=30.27
export WEATHER_LONGITUDE=-97.74
export WEATHER_UNITS=imperial
# Quote content
export ALLOW_NSFW_QUOTES=false
# Weather cache duration in seconds (default: 3600)
export WEATHER_TTL=3600
# Optional: use OpenWeatherMap instead of the default Open-Meteo (leave blank for Open-Meteo)
# export OPENWEATHERMAP_APIKEY=| Variable | Description |
|---|---|
WEATHER_LATITUDE |
Location latitude (set by setup / the app; see "Overriding location coordinates" below) |
WEATHER_LONGITUDE |
Location longitude (set by setup / the app; see "Overriding location coordinates" below) |
WEATHER_UNITS |
imperial (Fahrenheit) or metric (Celsius) |
ALLOW_NSFW_QUOTES |
Show quotes with mature content (default: false) |
WEATHER_TTL |
Weather cache duration in seconds (default: 3600) |
OPENWEATHERMAP_APIKEY |
Optional — use OpenWeatherMap instead of Open-Meteo (see below) |
Advanced: using OpenWeatherMap instead of Open-Meteo
By default the clock uses Open-Meteo, which is free and requires no signup. If you'd rather use OpenWeatherMap (for example, you already have a key or prefer its forecast model):
- Sign up for a free account at OpenWeatherMap and grab a key from API Keys.
- Edit
/home/pi/litclock/env.shand setOPENWEATHERMAP_APIKEY=your_key_here. - Restart the timer:
sudo systemctl restart litclock.timer.
The OpenWeatherMap free tier allows 1,000 calls per day; the clock caches forecasts for an hour by default, so usage stays well within limits.
Advanced: overriding location coordinates
The control app's Location setting handles geocoding for most users (it accepts city names, postal codes, and has an Advanced panel for raw coordinates). If you'd rather edit the file directly over SSH, set WEATHER_LATITUDE and WEATHER_LONGITUDE in env.sh.
Finding coordinates:
- Google Maps: right-click your location; the coordinates appear at the top of the menu (e.g., "30.27, -97.74"). Latitude first, longitude second.
- latlong.net: search an address and copy the values.
- iPhone: open the Compass app; coordinates are at the bottom.
- Android: long-press your location in Google Maps; coordinates appear at the top.
Short version: you don't need to do anything. The clock updates itself.
LitClock pulls the latest blessed release once a week, Sunday 03:00 local time, with up to 7 days of randomization so the fleet doesn't all update at the same moment. You can also apply an update immediately from the control app's Updates tab, or from a shell: /home/pi/litclock/scripts/update.sh.
What updates automatically:
- LitClock code (clock logic, control app, setup server, shell scripts)
- Python packages in the venv
- systemd units (new timers/services are auto-enabled)
env.sh— new variables fromenv.sh.sampleare merged in; your existing values are preserved- Quote images, pinned by
.images-version
What does NOT update automatically — by design:
- OS packages (
apt upgrade) and Raspberry Pi firmware. On the flashed image, OS auto-updates are explicitly disabled: a surprise apt upgrade could break the display stack with nobody at the keyboard. OS-level updates happen when you flash a newer image (or run them yourself over SSH).
Two safety nets protect every update:
- A pre-wiring smoke test: after rebuilding the venv, the updater renders an in-memory quote image. If the render fails, the update reverts to the previous SHA before touching the running clock. If that happens, a subtle "!" glyph appears in the top-left corner of the clock face until the next successful update.
- A boot check with automatic rollback: if the clock ever boots and can't paint a quote, it retries, and after repeated failures automatically reinstalls the last release that is known to have painted. A bad update can't brick a clock that has no keyboard and no SSH.
Inspecting or opting out of auto-updates
Check when the timer last fired and its next scheduled run:
systemctl status litclock-update.timerSee the logs of the last update attempt:
journalctl -u litclock-update.service --since "1 week ago"If you'd rather manage updates yourself, disable the timer via SSH:
sudo systemctl disable --now litclock-update.timerThe clock keeps working on whatever SHA it's pinned to; manual updates via the app or update.sh still work, and the updater respects a manually-disabled timer — it won't silently re-enable it. Re-enable with sudo systemctl enable --now litclock-update.timer.
Most resets don't need a shell — use the control app's System tab:
- Reset WiFi — forget saved networks and return to the LitClock-Setup network (your settings — location, weather, gift mode — are kept)
- Factory reset — wipe all settings and start over from the first-boot experience
- Prepare for Gifting — wipe WiFi, write a welcome message for the recipient, and power off ready to box up
From a shell, the equivalent is:
sudo ./scripts/reset-setup.sh
sudo rebootFlags:
--yes— skip the confirmation prompt--reboot— reboot automatically after reset--wipe-wifi— also delete saved WiFi networks (full fresh-flash simulation)--gift-mode— prepare for shipping: wipes WiFi, paints a welcome splash on the e-ink, and powers off. Implies--wipe-wifi --yes.
Start here (no shell needed): open http://litclock.local/diagnostics (or tap "Open full diagnostics" in the control app). It shows version, last render time, WiFi + weather status, error flags, and recent logs. Screenshot it, or use "Download full logs" to export a redacted support bundle safe to attach to an issue.
- Flashed the card and nothing happens at all (no display activity, no WiFi, no
litclock.local): check which board you have. The image is 64-bit only, so an original Pi Zero W or a Pi 1/2 shows no boot activity whatsoever — it cannot execute the 64-bit kernel. You need a Pi Zero 2 W. - Wrong city, units, or timezone: fix it in the app → Settings. If setup couldn't detect your location at all (some networks block IP geolocation), the app offers a one-tap "use my browser's timezone" fallback so the clock runs correctly with weather off.
- Display not updating: Check SPI is enabled with
ls /dev/spi* - Check service status:
systemctl status litclock.timer - View service logs:
journalctl -u litclock.service --since today - Force weather update:
rm /run/litclock/weather-cache-*.json(the cache lives in tmpfs and rebuilds on the next minute tick) - WiFi disconnects (Pi Zero): see WiFi stability, or check
dmesg | grep brcmfmacfor errors - Setup page doesn't open on iPhone: a known iOS 26 behavior with offline setup networks (not fixable clock-side). Open Safari while joined to
LitClock-Setup— the page loads right away, or go to the address shown on the display. - WiFi fails during setup: The setup page shows an error banner and lets you fix the password and resubmit. If it keeps failing, restart the Pi closer to your router.
- Start setup over: app → System → Factory reset, or
sudo ./scripts/reset-setup.sh && sudo rebootfrom a shell - Clock stuck, or you need a shell: SSH ships off. See Recovering a LitClock for getting a shell via the console, enabling SSH from the SD card, resetting to first-boot, and the read-only Diagnostics tab.
For hardware issues, refer to the Waveshare wiki and demo repository.
LitClock is designed to be gifted to someone who will never open a terminal:
- In the control app: System → Prepare for Gifting — wipes your WiFi, writes an optional welcome message, and powers the clock down ready to box up
- Print the quick-start booklet and enclose it — one folded sheet covers everything the recipient needs
- The recipient plugs it in and gets the same two-minute setup you did, on their own WiFi
To produce several clocks, see SD Card Cloning for duplicating pre-configured cards. (If you received a pre-configured card: just insert it and power on — setup starts at the LitClock-Setup step.)
LitClock is an appliance, not an SSH-first developer tool. If you are a non-technical user, you should never need to do anything after first boot: everything configures itself, updates itself, and recovers itself. If you are a technically comfortable user, you can enable SSH (it ships off — see Recovering a LitClock) and disable or customize any of this. That's why updates are silent and automatic, why the failure story is automatic rollback rather than error messages, and why day-to-day control happens from a phone rather than a terminal.
On boot, the clock goes through this sequence:
- Splash screen — "LitClock / Starting..." shown on the e-ink display
- First-boot setup (first time only) — the Pi broadcasts a WiFi network named "LitClock-Setup" and shows joining instructions on the e-ink. After you submit your WiFi, it connects, auto-detects location/timezone/units by IP geolocation, and shows the "Ready to read." handoff screen with a QR code to the control app.
- Clock timer — updates the display at the top of every minute with a new literary quote
- Control app — served continuously on your LAN at
http://litclock.local
Each frame is composed like this — quote with the time reference bolded, date, weather, and the control-app QR:
The Pi has no hardware clock, so NTP time sync is enabled automatically during installation and first boot to ensure accurate time after every power cycle.
This is managed by systemd services — the important ones:
| Service | Purpose |
|---|---|
litclock.timer / litclock.service |
Renders a quote to the display every minute at :00 |
litclock-control.service |
Serves the control app on port 80 |
litclock-splash.service |
Shows the welcome splash on every boot |
litclock-firstboot.service |
Runs first-boot setup (disables itself after) |
litclock-update.timer / .service |
Weekly self-update |
litclock-lkg.service / litclock-bootcheck.service |
Record the last release that painted; auto-rollback if a boot can't paint |
litclock-reresolve-location.service |
Re-checks IP geolocation on boot (Automatic location mode only) |
litclock-shutdown.service |
Displays a shutdown splash on poweroff/halt |
wifi-watchdog.timer / .service |
Reboots the Pi if WiFi becomes unreachable (Pi Zero W stability fix) |
Useful commands:
# Check timer status and next trigger
systemctl status litclock.timer
# View clock update logs
journalctl -u litclock.service -f
# Manually trigger a display update
sudo systemctl start litclock.service
# Run the clock by hand
cd /home/pi/litclock && ./scripts/runtheclock.shThe Raspberry Pi Zero W and Zero 2 W have a known WiFi stability issue with the Broadcom WiFi chip that can cause the system to hang and become unreachable. The flashed image applies these mitigations out of the box:
- Driver parameters: Disables roaming and problematic power features
- Power management: Disables WiFi power saving
- Watchdog: Automatically reboots if WiFi becomes unreachable
If you experience WiFi disconnections or system hangs, confirm all three mitigations are in place: cat /etc/modprobe.d/brcmfmac.conf, iwconfig wlan0 | grep "Power Management" (should read off), and systemctl status wifi-watchdog.timer.
- Script Reference — every script and its usage
- Building the Image — build your own
.img.xzfrom source - SD Card Cloning — duplicating configured cards
- Recovering a LitClock — console access, enabling SSH, reset paths
This project was originally forked from jadonn/literary-clock and has since been extensively rewritten. The literary clock concept originates from Jaap Meijers's Instructables project (2018).
Quote database sources:
- JohannesNE/literature-clock (CC BY-NC-SA 2.5)
- cdmoro/literature-clock (MIT)
- The Guardian "Books blog" reader thread — community-sourced time-referential quotes from the 2011–2018 reader comments
Case design:
- Arthur Gassner's Time Teller (CC BY) — the 3D-printed case; lightly modified STLs ship in
3d-models/
Code & display inspiration:
Assets:
- Dhole's Monochrome Weather Icons (CC BY-SA 4.0)
- Google Fonts — Literata (OFL 1.1)
Because the quote corpus derives from CC BY-NC-SA material, the assembled database (image-gen/litclock_annotated.csv) and the generated quote images are distributed under CC BY-NC-SA 4.0 — non-commercial use only. The code is MIT.
See NOTICE.md for full license details on third-party components.
LitClock was developed privately before this repository was published, so issue/PR numbers referenced in the CHANGELOG predate the public issue tracker.
LitClock is a hobbyist, open-source project provided free of charge. The code is released under the MIT License. The quote database (image-gen/litclock_annotated.csv) and the quote images derived from it are not MIT — they are distributed under CC BY-NC-SA 4.0, which prohibits commercial use. See NOTICE.md.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, including but not limited to warranties of merchantability, fitness for a particular purpose, non-infringement, or that the software is free of defects, errors, or vulnerabilities.
By downloading, flashing, building, modifying, or otherwise using LitClock, its pre-built images, or any associated scripts or documentation, you acknowledge and agree that:
- You use it entirely at your own risk. The authors, contributors, and copyright holders shall not be liable for any claim, damages, or other liability, whether in contract, tort, or otherwise, arising from or in connection with the software or its use. This includes, without limitation: damage to hardware (Raspberry Pi, e-Paper display, SD cards, power supplies, or other connected equipment), data loss, network-related issues, fire, electrical damage, property damage, personal injury, or any indirect, incidental, special, consequential, or punitive damages.
- No support is guaranteed. Bug reports and pull requests are welcome but responses, fixes, and continued maintenance are entirely at the maintainer's discretion.
- Third-party services and content are out of scope. LitClock integrates with third-party services (e.g., Open-Meteo, OpenWeatherMap, IP geolocation providers) and displays literary quotes derived from public sources. The maintainer is not responsible for the availability, accuracy, licensing, or content of any third-party service or quote corpus. You are responsible for complying with the terms of any service you enable and for ensuring your use of any included content is lawful in your jurisdiction.
- Not fit for safety-critical, commercial, or production use. LitClock is designed as a novelty clock for personal, non-commercial use. It must not be relied upon for timekeeping, scheduling, safety, medical, industrial, or any other purpose where failure could cause harm or loss.
- Trademarks, copyrights, and attributions for any quoted works, referenced products, or third-party libraries remain the property of their respective owners. Inclusion does not imply endorsement.
If you do not agree with these terms, do not install or use the software.










