All code and setup instructions are available in the GitHub repository linked on the left. There’s also a YouTube overview video that walks through the project.
I recently ordered one of Waveshare’s E-Ink Photo Frames. The stock firmware allows images to be displayed from the SD card, but the major drawback is that photos must first be converted into a specific format using their supplied scripts.
My goal was to write custom firmware that would allow images to be updated simply by dropping them into a folder on a network drive.
For a few years I’ve had an Odroid HC2 running Ubuntu with a 6TB 3.5” drive shared across my home network via Samba. Initially, I considered having the ESP32 connect directly over SMB, SSH, or SFTP, but the development overhead for those protocols was more than I wanted for this project. The simplest and most reliable solution turned out to be hosting the image folder with NGINX as a basic HTTP server. The ESP32 can easily act as an HTTP client and download images from that directory onto its SD card.
Next came image rendering. I didn’t want to pre-process or resize images on the PC before placing them in the folder. Fortunately, Larry Bank’s JPEGDEC library makes decoding JPEG images on the ESP32 straightforward. BMP files are also easy to handle. I experimented with PNGDEC, but in my implementation I ran into RAM limitations when decoding PNG files. Since JPEG will be the primary format used, converting the occasional PNG to BMP is an acceptable compromise.
Once the ESP32 downloads an image into a temporary file on the SD card, it decodes and resizes it to match the E-Ink display resolution. Images are cover-cropped to fill the screen appropriately without requiring manual resizing beforehand.
The final stage of the display pipeline is dithering. Unlike LCD, LED, or OLED panels, this E-Ink display cannot blend colours at the pixel level. Each pixel can only display one of six discrete colours. Dithering creates the illusion of a wider colour range by distributing neighbouring pixels of different colours in carefully calculated patterns. This implementation uses the Floyd–Steinberg dithering algorithm.
As an additional feature, I integrated the frame with Home Assistant as a temperature and humidity sensor. The Waveshare board includes an onboard temperature and humidity sensor, so it made sense to expose that data via MQTT. The firmware publishes configuration data to the Home Assistant auto-discovery topic on startup, meaning no manual configuration is required.
Currently, the device wakes from deep sleep every 30 minutes to select a random image from the hosted folder and update the display. It also wakes every five minutes to record and publish temperature and humidity readings.
The GitHub repository includes instructions for flashing the board as well as an example NGINX configuration file for /etc/nginx/sites-available. Once configured, adding new images is as simple as copying JPEG or BMP files into the hosted folder. No resizing or pre-processing is required.
Lawrence Manning
Mike Szczys