Das Elecrow Crowpanel 2.9 HMI epaper ist ein kleines Epaper mit einem ESP32-s3 Chip welches mit Akku versorgt werden kann. Ich hab einen 200mAh LiPo Akku verwendet welcher das Display mindestens 1 Jahr lang versorgenkönnen sollte.
Das Display holt sich die Daten über die Integration “Waste Collection Schedule” immer nachts um 1 Uhr und geht dannach in den Tiefschlaf.
Mit dem Menü-knopf kann das Display auf den Tiefschlaf geholt werden und wartet 4 min. auf ein Update.
Ein Zeitstempel gibt Aufschluss darüber ob das nächtliche Update erffolgreich war.
Schwierig war die Pin-konfiguration, da der dokumentierte Busy-pin nicht funktionierte. Mithilfe von KI und viel try-and-error lief es dann irgendwann.
Da ich absolut nichts im ESPhome universum zu dem Display gefunden habe das mir weiter hilft poste ich hier mal alles was ich jetzt geschrieben habe.
Hier die Konfigurationen zum nachbauen.
muell-eink.yaml in ESPHome
globals:
- id: is_update_mode
type: bool
restore_value: no
initial_value: ‘false’
output:
- platform: gpio
pin: GPIO42
id: sd_card_power
esphome:
name: eink-muell
on_boot:
priority: 900
then:
- output.turn_off: sd_card_power # Schaltet SD-Stromkreis ab
- delay: 1s
- switch.turn_on: display_power
- lambda: |-
if (esp_sleep_get_wakeup_cause() == ESP_SLEEP_WAKEUP_EXT0) {
ESP_LOGI("custom", "Manuelles Aufwachen per Knopf - Update-Modus aktiv!");
id(is_update_mode) = true;
}
# WLAN-Verbindung abwarten
- wait_until:
condition:
api.connected:
timeout: 120s
# Schritt 3. Update Display
- delay: 2s
- component.update: eink_display
- delay: 15s
# Schritt 4: Schlafen legen mit Update-Pause
- if:
condition:
lambda: 'return id(is_update_mode);'
then:
- logger.log: "Warte 4 Minuten auf OTA-Update..."
- delay: 240s
- if:
condition:
lambda: 'return id(ha_time).now().is_valid();'
then:
- deep_sleep.enter:
id: deep_sleep_ctrl
until: "01:00:00"
time_id: ha_time
else:
- deep_sleep.enter:
id: deep_sleep_ctrl
sleep_duration: 1h
esp32:
board: esp32-s3-devkitc1-n8r8
framework:
type: arduino
flash_size: 8MB
switch:
- platform: gpio
pin: GPIO07
id: display_power
restore_mode: ALWAYS_ON
logger:
api:
encryption:
key: !secret muell_api_key
ota:
- platform: esphome
password: !secret muell_ota_password
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
font:
- file: “gfonts://Oswald”
id: font_big
size: 28
glyphs:- '!"%()+=,-_.:;? ’ # Die gängigsten Satzzeichen
- ‘0123456789’ # Zahlen
- ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’
- ‘abcdefghijklmnopqrstuvwxyz’
- ‘ÄÖÜäöüß’ # Deutsche Umlaute und das scharfe S
- file: “gfonts://Oswald”
id: font_small
size: 20
glyphs:- '!"%()+=,-_.:;? ’ # Die gängigsten Satzzeichen
- ‘0123456789’ # Zahlen
- ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’
- ‘abcdefghijklmnopqrstuvwxyz’
- ‘ÄÖÜäöüß’ # Deutsche Umlaute und das scharfe S
- file: “gfonts://Roboto”
id: font_tiny
size: 10
glyphs:- '!"%()+=,-_.:;? ’ # Die gängigsten Satzzeichen
- ‘0123456789’ # Zahlen
- ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’
- ‘abcdefghijklmnopqrstuvwxyz’
- ‘ÄÖÜäöüß’ # Deutsche Umlaute und das scharfe S
- file: “https://github.com/Templarian/MaterialDesign-Webfont/raw/master/fonts/materialdesignicons-webfont.ttf”
id: icon_font
size: 45
glyphs:- “\U000F0A7A” # Restmüll (Trash Can)
- “\U000F1CCC” # Papiertonne (Container/Box)
- “\U000F044C” # Gelber Sack (Recycle-Variante)
sensor:
- platform: homeassistant
id: t0_tage
entity_id: sensor.muell_termin_0_tage - platform: homeassistant
id: t1_tage
entity_id: sensor.muell_termin_1_tage
time: - platform: homeassistant
id: ha_time
timezone: Europe/Berlin
text_sensor:
- platform: homeassistant
id: t0_name
entity_id: sensor.muell_termin_0 - platform: homeassistant
id: t1_name
entity_id: sensor.muell_termin_1
status_led:
pin: GPIO41
spi:
clk_pin: GPIO12
mosi_pin: GPIO11
display:
-
platform: waveshare_epaper
id: eink_display
cs_pin: GPIO45
dc_pin: GPIO46
reset_pin: GPIO47
busy_pin: GPIO09
model: 2.90inv2
rotation: 270
full_update_every: 10
update_interval: never
lambda: |-
it.fill(Color::BLACK); // Hintergrund weiß (wegen Invertierung)// Header
it.print(10, -3, id(font_big), Color::WHITE, “Müllabfuhr”);
it.line(0, 35, 296, 35, Color::WHITE);// Icon-Logik Funktion
auto get_icon = (std::string name) → const char* {
// Wandelt Text in Kleinbuchstaben um für sicherere Suche
std::transform(name.begin(), name.end(), name.begin(), ::tolower);if (name.find("haus") != std::string::npos) return "\U000F0A7A"; if (name.find("papier") != std::string::npos) return "\U000F1CCC"; if (name.find("gelb") != std::string::npos) return "\U000F044C"; return "\U000F0A7A"; // Fallback};
// Spalte 1: Nächster Termin
int col1 = 15;
if (id(t0_name).has_state()) {
// Icon mittig über der Spalte
it.print(74, 40, id(icon_font), Color::WHITE, TextAlign::TOP_CENTER, get_icon(id(t0_name).state));
// Text-Infos
it.print(74, 80, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, id(t0_name).state.c_str());if (id(t0_tage).state == 0) { it.print(74, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "HEUTE"); } else if (id(t0_tage).state == 1) { it.print(74, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "MORGEN"); } else { it.printf(74, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "In %.0f Tagen", id(t0_tage).state); }}
// Spalte 2: Übernächster Termin (Zentriert bei 222px)
if (id(t1_name).has_state()) {
// Icon mittig
it.print(222, 40, id(icon_font), Color::WHITE, TextAlign::TOP_CENTER, get_icon(id(t1_name).state));
// Text-Infos
it.print(222, 80, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, id(t1_name).state.c_str());if (id(t1_tage).state == 0) { it.print(222, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "HEUTE"); } else if (id(t1_tage).state == 1) { it.print(222, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "MORGEN"); } else { it.printf(222, 100, id(font_small), Color::WHITE, TextAlign::TOP_CENTER, "In %.0f Tagen", id(t1_tage).state); }}
// — Zeitstempel —
// Nutzt TextAlign::TOP_RIGHT, um bündig an der rechten Kante (290px) zu kleben
it.strftime(290, 2, id(font_tiny), Color::WHITE, TextAlign::TOP_RIGHT, “Stand: %d.%m. %H:%M”, id(ha_time).now());
deep_sleep:
id: deep_sleep_ctrl
wakeup_pin:
number: GPIO2
mode: INPUT_PULLUP
inverted: true
Waste Collection Schedule Sensoren in sensoren.yaml um das Display mit Daten zu füttern.
-
platform: waste_collection_schedule
name: muell_termin_0
details_format: “upcoming”
event_index: 0 # Der allernächste Termin
value_template: ‘{{ value.types | join(", ") }}’ # Name der Tonne -
platform: waste_collection_schedule
name: muell_termin_0_tage
details_format: “upcoming”
event_index: 0
value_template: ‘{{ value.daysTo }}’ # Tage bis dahin -
platform: waste_collection_schedule
name: muell_termin_1
details_format: “upcoming”
event_index: 1 # Der übernächste Termin
value_template: ‘{{ value.types | join(", ") }}’ -
platform: waste_collection_schedule
name: muell_termin_1_tage
details_format: “upcoming”
event_index: 1
value_template: ‘{{ value.daysTo }}’
Waste Collection Schedule muss in der configuration.yaml mit dem lokalen Müllentsorger eingerichtet werden. Da es bei uns keine Bio-Tonne gibt muss diese evtl. erweitert werden.
