Photo Frame

Hackaday.io Project

Github Repository

E-Ink Display

On my way home from my third voyage to Antarctica with the Australian Antarctic Program, I realised I had accumulated thousands of photos that were just sitting on a hard drive. It felt wrong for them to live only as files. I wanted something quieter and more permanent than a monitor. Something that would sit on a desk and simply exist.

So I purchased an ESP32-S3 Photo Painter. It’s an ESP32-S3 development board paired with a 7.3-inch, six-colour E-Paper display, along with a few useful onboard peripherals. Low power, high contrast, no backlight glow at night. Perfect for a slow-refresh photo frame.

Firmware

The stock firmware allows images to be displayed from the SD card, but only after they’ve been processed using Waveshare’s supplied conversion scripts. That means removing the SD card, plugging it into a PC, running a processing tool, copying the converted files across, then reinserting the card into the frame.

It works, but it feels like 2005.

My goal was to make the display behave like a modern network device. I already run an Odroid HC2 at home hosting a shared drive over SMB. Ideally, I wanted to drop photos into a folder on that drive and have the frame handle the rest: download the image, resize it, dither it to the six-colour palette, and display it. No manual preprocessing. No SD card shuffling.

To achieve that, I built the following.

Image Downloading

To retrieve images from the network drive, I investigated several options: SMB, FTP, SFTP and HTTP.

SMB would have been elegant since the photos already live on a Samba share, but there are no lightweight, practical SMB libraries for the ESP32 that are straightforward to integrate. SFTP had similar issues. FTP was technically possible, but I wasn’t keen on enabling it purely for this purpose, particularly from a security standpoint.

HTTP ended up being the simplest and most robust solution.

Using NGINX, I configured a minimal web server to expose only the photo directory. The ESP32 can easily act as an HTTP client, so downloading images over HTTP becomes trivial. The server remains internal to the LAN, and the firmware only needs to know the URL of the image directory.

The result: drop a file into a folder on the Odroid, and the frame can fetch it without any additional tooling.

Image Decoding and Resizing

Once the image is downloaded, the ESP32 takes over.

The firmware decodes the JPEG image in memory and resizes it to the panel’s native resolution. Rather than relying on crude nearest-neighbour scaling, I implemented bilinear interpolation to preserve smoother gradients and reduce artefacts.

After resizing, the image must be reduced to the six-colour ACeP palette supported by the display. To make this look good, I apply Floyd–Steinberg dithering. Instead of simply snapping each pixel to the nearest palette colour, the algorithm distributes quantisation error to neighbouring pixels, dramatically improving perceived detail and colour transitions.

The end result is surprisingly faithful for a six-colour E-Ink panel.

And all of it happens on the ESP32 itself.

NGINX Image Hosting

On the server side, I use NGINX to serve the directory of photos over HTTP with auto-indexing turned on. This lets the ESP32 fetch a directory listing and choose a random image to download and display on each refresh cycle — without the user manually shuffling photos to an SD card. The NGINX config included in the repo (photoframe.conf) points at a folder on disk which itself can be a Samba (SMB) share from another machine. With this setup, you can literally drag-and-drop photos from your workstation into the share and the e-ink frame will pick them up on its next wake cycle.

server {
    listen 8080;
    server_name lanip:8080;

    root /your/photo/directory;
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
}

Just replace lanip:8080 and /your/photo/directory with your server’s LAN IP/port and the path to your photos. This keeps the service local to your home network.

Home Assistant Integration

Beyond simply displaying images, the firmware also turns the Photo Painter into a useful sensor device for Home Assistant. The board’s on-board SHTC3 sensor reports ambient temperature and humidity, and it even reports battery voltage, all via MQTT. By publishing MQTT auto-discovery messages on boot, the device automatically adds itself to your Home Assistant instance — no manual YAML or entity creation required. Once paired with your broker (like Mosquitto), Home Assistant will show entities for temperature, humidity, and battery level right alongside your other smart home data.

Home Assistant

Power and Sleep Behaviour

Running display refreshes on a battery-powered E-Ink device naturally involves careful power management. To maximise uptime, the firmware uses deep sleep between update cycles. Every few minutes the ESP32 wakes briefly to publish sensor data, and only every few refresh cycles will it fetch a new photo and redraw the display. There’s also a physical button that forces an immediate image refresh, letting anyone trigger a new photo without waiting for the scheduled cycle.