You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: _drafts/2025-03-02-building-your-own-ai-and-e-ink-powered-art-gallery-local.md
+96-20
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
layout: post
3
3
title: "Building Your Own AI & E-Ink Powered Art Gallery: A Local DIY Guide"
4
4
date: 2025-03-02
5
-
categories: AI art eink esp32 homeassistant
5
+
categories: AI art e-ink esp32 home-assistant
6
6
author: Jimmy & Peter
7
7
---
8
8
@@ -14,6 +14,9 @@ Everything is run locally on our network, ensuring the process is private and ke
14
14
Having dynamic, AI-generated art on your walls is straightforward without compromising privacy.
15
15
This setup naturally extends our Home Assistant smart home setup, which hosts the image server and monitors the ESP32s.
16
16
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
+
17
20

18
21
19
22
Want to build your own? Here is the recipe.
@@ -258,8 +261,8 @@ $$
258
261
\end{bmatrix}
259
262
$$
260
263
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.
263
266
264
267
With experience we found that the algorithm used in old Macs, [Atkinson Dithering](https://en.wikipedia.org/wiki/Atkinson_dithering),
265
268
works really well for low resolution photos.
@@ -329,43 +332,85 @@ But using Numba we can get something working really quick.
329
332
330
333
If you are doing multiple colors you can diffuse the error per color channel.
331
334
335
+
## Displaying the image
332
336
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.
334
338
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.
336
342
337
-
Start with RPi
343
+
The second option is to use an ESP32 microprocessor, which can be battery-powered. No visible cords.
338
344
339
345
### Setting up Raspberry Pi API frame
340
346
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.
> **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;
356
377
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
358
382
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).
360
385
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
361
387
362
-
basic example for showing image over wifi
388
+
@reboot /path/to/your_script.sh
363
389
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);
365
409
366
410
<detailsmarkdown="1">
367
411
<summary><b>GPIO Configuration for FireBettle2 ESP32-E</b></summary>
368
412
413
+
```yaml
369
414
substitutions:
370
415
device_id: "example_e"
371
416
wifi_ssid: !secret wifi_ssid
@@ -393,12 +438,14 @@ advanced example of showing esphome connection
393
438
friendly_name: "eink frame ${device_id}"
394
439
platformio_options:
395
440
build_flags: "-DBOARD_HAS_PSRAM"
441
+
```
396
442
397
443
</details>
398
444
399
445
<details markdown="1">
400
446
<summary><b>GPIO Configuration for FireBettle2 ESP3S3</b></summary>
401
447
448
+
```yaml
402
449
substitutions:
403
450
device_id: "example_s"
404
451
wifi_ssid: !secret wifi_ssid
@@ -420,13 +467,38 @@ advanced example of showing esphome connection
420
467
framework:
421
468
type: arduino
422
469
version: recommended
470
+
```
423
471
424
472
</details>
425
473
474
+
To flash it, install esphome with;
426
475
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.
428
495
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.
429
498
499
+
For Configuration of a simple, fetching image over wifi and displaying it;
500
+
501
+
```yaml
430
502
http_request:
431
503
id: fetch_image_request
432
504
timeout: 5s
@@ -477,7 +549,7 @@ For Configuration of a simple, fetching over wifi and displaying it
477
549
deep_sleep:
478
550
run_duration: 40s
479
551
sleep_duration: 25200s# 7h
480
-
552
+
```
481
553
482
554
and config with ESPHome
483
555
@@ -674,6 +746,10 @@ It was expensive, but worth it for the final touch.
674
746
- Not on the black-white-red screen
675
747
- Check the +/- on the lipo battery, needs to fit. You might need to change it.
0 commit comments