Skip to content

Commit 8953596

Browse files
authored
Update 2025-03-02-building-your-own-ai-and-e-ink-powered-art-gallery-local.md
1 parent 4f6c81b commit 8953596

File tree

1 file changed

+96
-20
lines changed

1 file changed

+96
-20
lines changed

_drafts/2025-03-02-building-your-own-ai-and-e-ink-powered-art-gallery-local.md

+96-20
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
layout: post
33
title: "Building Your Own AI & E-Ink Powered Art Gallery: A Local DIY Guide"
44
date: 2025-03-02
5-
categories: AI art eink esp32 homeassistant
5+
categories: AI art e-ink esp32 home-assistant
66
author: Jimmy & Peter
77
---
88

@@ -14,6 +14,9 @@ Everything is run locally on our network, ensuring the process is private and ke
1414
Having dynamic, AI-generated art on your walls is straightforward without compromising privacy.
1515
This setup naturally extends our Home Assistant smart home setup, which hosts the image server and monitors the ESP32s.
1616

17+
In some places we dive deep (e.i. dithering algorithms); other places, we take shortcuts (e.i. not writing C-code),
18+
because [we need to finish our projects](https://www.youtube.com/watch?v=4jgTCayWlwc).
19+
1720
![Showing the transition of E-ink screen]({{ site.baseurl }}/assets/images/eink_art/video/output2.gif)
1821

1922
Want to build your own? Here is the recipe.
@@ -258,8 +261,8 @@ $$
258261
\end{bmatrix}
259262
$$
260263

261-
However, in practise the numerically correct method dithers the error out in a very dense way, making the picture look very grey-ish.
262-
This is especially prominant in low-resolution images, in which we have.
264+
However, in practice, the numerically correct method dithers the error very densely, making the picture look greyish.
265+
This is especially prominent in our low-resolution images/displays.
263266

264267
With experience we found that the algorithm used in old Macs, [Atkinson Dithering](https://en.wikipedia.org/wiki/Atkinson_dithering),
265268
works really well for low resolution photos.
@@ -329,43 +332,85 @@ But using Numba we can get something working really quick.
329332

330333
If you are doing multiple colors you can diffuse the error per color channel.
331334

335+
## Displaying the image
332336

333-
## Choice of hosting model
337+
For displaying the image on the e-ink, we have two options. Push, with powercable, or pull with battery.
334338

335-
RPi and ESP32. Push or pull.
339+
Personally, the iteration was done with a Raspberry Pi, but given that required a USB power cable it removed the immersion as a photoframe.
340+
Note that a white USB cable was used and only one person ever noticed it. However, we knew it was there, and that was enough.
341+
But if you want live updates, like a notification, this is the option you want.
336342

337-
Start with RPi
343+
The second option is to use an ESP32 microprocessor, which can be battery-powered. No visible cords.
338344

339345
### Setting up Raspberry Pi API frame
340346

347+
For the Raspberry Pi, the simplest setup would be to setup a FastAPI Python client to receive request and display them.
348+
We use an [Raspberry Pi Zero](https://www.raspberrypi.com/products/raspberry-pi-zero/) because of the small form-factor, to be hidden behind the frame.
349+
Waveshare provides quite good example codes for Python (And other implementations), and is easily the fastest way to get something shown on your screen.
350+
[github.com/waveshareteam/e-Paper](https://github.com/waveshareteam/e-Paper).
341351

342-
### Setting up ESPHome and ESP32 frame
352+
For the Raspberry Pi, [install Debian OS](https://www.raspberrypi.com/documentation/computers/getting-started.html) and `ssh` into the it.
353+
354+
<details markdown="1">
355+
<summary><b>Setting up Raspberry Pi</b></summary>
343356

344-
We could not use the waveshare, as it does not have psram
357+
# Enable SPI
358+
# Choose Interfacing Options -> SPI -> Yes
359+
sudo raspi-config
360+
sudo reboot
345361

362+
# Setup Python and dependencies
363+
sudo apt install python3-pip python3-setuptools python3-venv python3-wheel libopenjp2-7
346364

347-
Why not just write it in C?
348-
It could be done without PNG downloader, in pure binary, but we wanted fast iterations
349-
Choose your battles. ESPHome come with a lot of features out of the box.
365+
# Create a python env
366+
python3 -m venv project_name
350367

351-
idea
368+
# Activate python env
369+
source ./project_name/bin/activate
352370

353-
basic example for showing a image
371+
# Install the main dependencies with the activated env, but really, use a git repo for this
372+
pip install pillow numpy RPi.GPIO spidev gpiozero spidev
354373

355-
> **Note:** To use the image downloader you need vram on your
374+
</details>
375+
376+
If you have a problem creating a `venv`, because of missing pip, you can;
356377

357-
> **Note:** If your picture is getting less visible the more complicated the picture is, you are using the wrong config
378+
python3 -m venv --without-pip project_name
379+
source env/bin/activate
380+
wget bootstrap.pypa.io/get-pip.py
381+
python get-pip.py
358382

359-
> **Note:** If your picture is not doing the full-refresh, please check your soldering connections
383+
With this setup, it should be pretty straightforward to set up a FastAPI solution.
384+
For inspiration, refer to Jimmy's github solution [github.com/charnley/eink_art_gallery](https://github.com/charnley/eink_art_gallery).
360385

386+
Note, because you need to start the API every time the Raspberry Pi is booted, it is worth setting up a `crontab -e` to start you service at boot
361387

362-
basic example for showing image over wifi
388+
@reboot /path/to/your_script.sh
363389

364-
advanced example of showing esphome connection
390+
391+
### Setting up ESPHome and ESP32 frame
392+
393+
Why didn't we write it in C?
394+
Because A) the project needs to end at some point, and B) we both use Home Assistant, it made sense to get all the free stuff out of the box with ESPHome.
395+
[Choose your battles](https://www.youtube.com/watch?v=4jgTCayWlwc) and finish your projects.
396+
397+
There are many, many, many options for ESP32s.
398+
Firstly, we tried the example [ESP32 development board](https://www.waveshare.com/e-paper-esp32-driver-board.htm) from Waveshare, which, of course, can display pictures. However, it was not possible to download images over the Internet with the standard ESPHome libraries.
399+
Because this requires that the ESP32 has [PSRAM](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/external-ram.html).
400+
We iterated through a couple and found that the FireBettle2 ESP32-E and FireBettle2 ESP32S have PSRAM and are well-documented by the producer.
401+
402+
> **Note:** If your picture gets less visible (greyish), the more complicated the image is, you are using the wrong display-config. [waveshare.com/wiki/E-Paper_Driver_HAT](https://www.waveshare.com/wiki/E-Paper_Driver_HAT).
403+
404+
> **Note:** If your picture does not refresh entirely when changing photos, you might have a loose connection. Check your soldering connections.
405+
406+
To connect the ESP32 to the E-Paper Driver HAT, select which GPIO pins are connected to each pin defined for the Waveshare HAT (table above).
407+
So, solder solder. Remember your flux.
408+
The configuration that worked for us was (as defined by the yaml esphome-substitutions);
365409

366410
<details markdown="1">
367411
<summary><b>GPIO Configuration for FireBettle2 ESP32-E</b></summary>
368412

413+
```yaml
369414
substitutions:
370415
device_id: "example_e"
371416
wifi_ssid: !secret wifi_ssid
@@ -393,12 +438,14 @@ advanced example of showing esphome connection
393438
friendly_name: "eink frame ${device_id}"
394439
platformio_options:
395440
build_flags: "-DBOARD_HAS_PSRAM"
441+
```
396442
397443
</details>
398444
399445
<details markdown="1">
400446
<summary><b>GPIO Configuration for FireBettle2 ESP3S3</b></summary>
401447
448+
```yaml
402449
substitutions:
403450
device_id: "example_s"
404451
wifi_ssid: !secret wifi_ssid
@@ -420,13 +467,38 @@ advanced example of showing esphome connection
420467
framework:
421468
type: arduino
422469
version: recommended
470+
```
423471
424472
</details>
425473
474+
To flash it, install esphome with;
426475
427-
For Configuration of a simple, fetching over wifi and displaying it
476+
```bash
477+
pip install esphome
478+
```
479+
480+
Setup a `secrets.yaml`
481+
482+
```yaml
483+
wifi_ssid: YourWiFiSSID
484+
wifi_password: YourWiFiPassword
485+
```
486+
487+
Then, you flash the ESP with ESPHome using the command;
488+
489+
```bash
490+
esphome run --device /dev/ttyACM0 ./configuration.yaml
491+
```
492+
493+
Where the device is mounted on either `/dev/ttyACM0-2` or `/dev/ttyUSB0-2`.
494+
You need to define the device argument; otherwise, ESPHome will try to flash the device over the ethernet using the device ID.
428495

496+
With the GPIO soldered and configured, we can try different ESPHome configurations.
497+
Prepend the above GPIO configuration to the configuration you want to flash.
429498

499+
For Configuration of a simple, fetching image over wifi and displaying it;
500+
501+
```yaml
430502
http_request:
431503
id: fetch_image_request
432504
timeout: 5s
@@ -477,7 +549,7 @@ For Configuration of a simple, fetching over wifi and displaying it
477549
deep_sleep:
478550
run_duration: 40s
479551
sleep_duration: 25200s # 7h
480-
552+
```
481553
482554
and config with ESPHome
483555
@@ -674,6 +746,10 @@ It was expensive, but worth it for the final touch.
674746
- Not on the black-white-red screen
675747
- Check the +/- on the lipo battery, needs to fit. You might need to change it.
676748
749+
750+
751+
752+
677753
## References
678754
679755
- Dithering references

0 commit comments

Comments
 (0)