Build a Raspberry Pi E-Ink Digital Photo Frame (Wired or Battery-Powered)
E-ink displays make genuinely great digital photo frames — unlike an LCD, they hold an image with zero power draw once drawn, look like actual printed paper instead of a glowing screen, and work beautifully in bright light where LCDs wash out. This build covers a Raspberry Pi driving an e-ink panel to cycle through a photo library, with options for both a plugged-in always-on frame and a battery-powered low-refresh version.
Hardware Needed
ComponentNotes Raspberry Pi Zero 2 WPlenty of power for this task and small enough to fit inside a slim frame — no need for a full-size Pi E-ink display HATWaveshare and Inky (Pimoroni) are the two common ecosystems — sizes from ~4" up to 13.3"+ depending on how large you want the frame. Color e-ink panels exist but have lower resolution/slower refresh than black-and-white/grayscale MicroSD card16GB+ is plenty since you're storing a photo library, not doing heavy compute Frame/enclosureA real picture frame with the backing modified to fit the Pi and panel, or a 3D printed enclosure — either works well PowerStandard micro-USB/USB-C power for an always-plugged-in build; a LiPo + charge board if going battery-powered with scheduled refreshesWhy E-Ink Specifically
- Zero power to hold an image — the display only draws power during the actual refresh, not while displaying a static image, which is what makes a battery-powered version genuinely practical
- No backlight glow — looks like an actual printed photo, especially appealing for black-and-white or sepia-toned images
- Readable in bright light — reflective display technology means it doesn't wash out in direct sunlight the way an LCD does
- Slow refresh is a feature here, not a bug — a photo frame doesn't need video-speed refresh, so e-ink's characteristic slowness (seconds per full refresh) is a non-issue for this specific use case
Software Setup
- Flash Raspberry Pi OS Lite (headless, no desktop needed) to your SD card
- Enable SPI, which most e-ink HATs communicate over: sudo raspi-config → Interface Options → SPI → Enable
- Install your display's Python library — Waveshare and Inky both provide dedicated libraries with example scripts to get a test image displaying quickly
- Confirm the panel works with the manufacturer's demo script before writing any custom code — this isolates hardware/wiring issues from software issues early
Basic Display Script
Once your panel's library is confirmed working, a basic photo-cycling script follows this pattern (adjust import/init calls to match your specific display library):
from PIL import Image import time import os import random PHOTO_DIR = "/home/pi/photos" DISPLAY_SIZE = (800, 480) # match your panel's actual resolution def get_random_photo(): photos = [f for f in os.listdir(PHOTO_DIR) if f.lower().endswith(('.jpg', '.png'))] return random.choice(photos) def prepare_image(path): img = Image.open(path) img = img.convert('L') # grayscale, or appropriate mode for your panel img.thumbnail(DISPLAY_SIZE, Image.LANCZOS) # Center on a properly-sized canvas if the photo doesn't exactly match panel dimensions canvas = Image.new('L', DISPLAY_SIZE, 255) offset = ((DISPLAY_SIZE[0]-img.width)//2, (DISPLAY_SIZE[1]-img.height)//2) canvas.paste(img, offset) return canvas while True: photo = get_random_photo() img = prepare_image(os.path.join(PHOTO_DIR, photo)) # display.set_image(img) / display.show() — call matching your panel's library time.sleep(3600) # refresh once per hourRefresh interval is worth thinking about deliberately — e-ink panels have a finite refresh cycle lifespan (typically rated for hundreds of thousands of refreshes, which sounds like a lot but adds up over years of frequent updates), so an hourly or even daily refresh is more appropriate for panel longevity than trying to update every few minutes.
Auto-Start on Boot
Set the script to run automatically via a systemd service so the frame starts displaying photos immediately on power-up without needing to SSH in and manually launch it:
sudo nano /etc/systemd/system/photoframe.service [Unit] Description=E-Ink Photo Frame After=network.target [Service] ExecStart=/usr/bin/python3 /home/pi/photoframe.py Restart=always User=pi [Install] WantedBy=multi-user.target sudo systemctl enable photoframe.service sudo systemctl start photoframe.serviceGetting Photos Onto the Frame
A few options depending on how hands-off you want it:
- Samba share — set up a simple network share on the Pi so you can drag-and-drop new photos from any device on your network without SSH
- Cloud sync — a scheduled script pulling from a shared Google Photos/Dropbox album, so anyone with access to the album can add photos that show up on the frame automatically
- Manual SCP/SFTP transfer — simplest to set up, most hands-on to use
Battery-Powered Version
If you want a fully wireless frame, the key changes:
- Use a script that wakes, displays one image, then puts the Pi into a low-power sleep state (or shuts down entirely and relies on an external RTC-triggered power switch to wake it) rather than running continuously
- A Pi Zero 2 W drawing power only during brief wake/refresh/sleep cycles can run for weeks on a modest LiPo battery, versus hours if left fully powered on continuously
- Add a physical power switch or button for manual refresh, since a sleeping Pi won't respond to network commands until its next scheduled wake
Image Prep Tips
E-ink panels — especially grayscale ones — benefit from some preprocessing beyond a simple resize:
- Boost contrast slightly before conversion, since e-ink's grayscale range is narrower than a typical photo's dynamic range
- Dithering (many display libraries support this natively) can meaningfully improve how photos look on a limited-grayscale panel, especially for gradients like skies
- Crop to match your panel's aspect ratio rather than letterboxing, for the cleanest look — a photo with visible white bars looks noticeably more "screen-like" than one that fills the frame
The hardware side is genuinely simple — most of the interesting work is in the image prep and refresh-scheduling logic, which is also where you can make this project distinctly yours: weather-reactive frames, "on this day" photo memories, or a shared family album that updates whenever someone adds a new photo remotely.
Related Guides
- How to Set Up OpenCV Machine Vision on a Raspberry Pi
- How to Control GPIO Pins on Raspberry Pi with Python
- How to Run a Timelapse Camera with Raspberry Pi
- Automated Plant Watering System with Raspberry Pi
- Controlling GPIO Outputs with Python — LED, Relay, and Buzzer
- Raspberry Pi GPIO: Complete Beginner Guide with Python Examples
- Getting Started with ROS2 on Raspberry Pi for Robotics
- Driving E-Paper Displays with ESP32 and Arduino: SPI Wiring, GxEPD2, and Partial Refresh