Probleme mit OpenDisplay

Hallo zusammen,
ich hoffe mir kann jemand helfen. Ich habe mir ein E-Ink Display geholt um es genau zu nehmen dieses hier: E-Ink Display

Und ich bekomme diesen Log wenn ich die Firmware von Open Display installiere:

ESP-ROM:esp32s3-20210327
Build:Mar 27 2021
rst:0x1 (POWEE (106) esp_littlefs: ./managed_components/joltwallet__littlefs/src/littlefs/lfs.c:1382:error: Corrupted dir pair at {0x0, 0x1}

E (106) esp_littlefs: mount failed, (-84)
E (110) esp_littlefs: Failed to initialize LittleFS
[ 462][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 470][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 478][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 486][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 494][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 502][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 510][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 518][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 526][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 534][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 606][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 614][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 622][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 630][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 638][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 646][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 654][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 662][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 670][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!
[ 678][E][esp32-hal-adc.c:303] __analogRead(): Pin 0 is not ADC pin!

Das klingt für mich irgendwie falsch ist das ein Bug oder normal? Weiß das jemand zufällig

Viele Grüße

hast du danach im webflasher die konfiguration via bluetooth angestoßen?

ja das habe ich, aber trotzdem bleiben die Probleme, selbst mit der neusten Firmware 2.0 glaube so. langsam das etwas mit der Verkabelung nicht stimmt :see_no_evil_monkey:

Update:
Hallo zusammen,

hier nochmal eine ausführliche Beschreibung eines Problems mit dem Display vielleicht kann mir irgendjemand helfen denn so langsam verzweifele ich.

Ich betreibe ein Seeed reTerminal E1002 (ESP32-S3, E-Ink) mit der
OpenDisplay-Firmware im Akkubetrieb und binde es über die offizielle
OpenDisplay-Integration in Home Assistant ein (Verbindung über einen
ESPHome-Bluetooth-Proxy, Empfang ist gut). Das Display zeigt eine
Tagesübersicht (Wetter, Termine, Spritpreise), die HA per drawcustom
hochlädt. Grundsätzlich funktioniert das auch – aber ich habe zwei
Probleme, an denen ich mir die Zähne ausbeiße:

Problem 1: Tasten seit Firmware 2.1 komplett tot
Nach dem Update auf 2.1 senden die drei Hardware-Tasten keine Events mehr.
Vor dem Update hat ein Tastendruck das Gerät aus dem Deep Sleep geweckt und
die Events kamen als BLE-Advertisement in HA an. Jetzt: gar nichts mehr –
weder weckt ein Druck das Gerät, noch ändern sich die Button-Zähler im
Advertisement, wenn das Gerät wach ist. Die Konfiguration ist nachweislich
intakt (binary_inputs: Pins 3/4/5, isused 0x7, per Export geprüft), und
auch ein kompletter Neuflash von 2.1 inkl. Neukonfiguration hat nichts
geändert. Sensordaten (Batterie, Temperatur) kommen weiterhin sauber an –
nur die Tasten sind stumm.

Problem 2: Deep-Sleep-Aufwachen nimmt keine Verbindungen an
Das Display wacht zuverlässig im eingestellten Rhythmus (30 Min) aus dem
Deep Sleep auf und sendet dabei Advertisements – aber es akzeptiert in
diesen Wachfenstern KEINE Verbindungen. Jeder Upload-Versuch von HA endet
mit „Timeout waiting for connect response after 20.0s" (5 Versuche, alle
erfolglos). Direkt nach einem Neustart klappen Uploads dagegen sofort, und
vor 2.1 auch nach einem Tastendruck. Die Wachzeit auf 60 s zu erhöhen hilft
nicht – das Fenster scheint grundsätzlich nur Beacon-Betrieb zu sein
(Status-Flag connReq=0 im Advertisement). In den sleep_flags der Config
gibt es auch keine Option dafür (alles reserved). Ergebnis: Zeitgesteuerte
Display-Updates sind im Akkubetrieb faktisch unmöglich, HA kann nie
zustellen.

Meine Fragen:

  1. Hat noch jemand ein E1002 mit OpenDisplay 2.1 und funktionierenden
    Tasten – oder könnt ihr die Regression bestätigen?
  2. Kennt jemand eine ältere Firmware-Version, bei der die Tasten sicher
    funktionieren (fürs Downgrade)?
  3. Bekommt irgendjemand geplante Uploads in die Deep-Sleep-Wachfenster
    durch? Falls ja: mit welcher Firmware/Config und welchem BT-Adapter
    (direkt statt ESPHome-Proxy)?

USB-Dauerstrom ist bei mir leider keine Option, am Montageort gibt es
keine Steckdose – deshalb ist es ja ein E-Ink mit Akku geworden. :slight_smile:

Danke euch!

:crayon:by HarryP: Zusammenführung Doppelpost (bei Änderungen oder hinzufügen von Inhalten bitte die „Bearbeitungsfunktion“ anstatt „Antworten“ zu nutzen)

Nutze die nativen Möglichkeiten, dann hast du keine Abhängigkeiten zu Dritt-Addons

Danke!
Ja, das ist eine Möglichkeit die ich gestern über google raus bekommen habe aber eigentlich gibt es doch genau dafür die OpenDisplay Integration. Würde es halt sehr gerne darüber laufen lassen :sweat_smile:

Ja, kann man machen, aber selbstbauen, und sich nicht auf andere verlassen müssen und nebenbei was lernen hat doch auch was :wink:

Als ich mein erstes Display angefangen hab, hatte ich auch die Wahl das mit OpenHASP zu machen, aber das sieht einfach nur hässlich aus. Ein paar Stunden in LVGL investiert und mittlerweile hab ich das 4.Display am Laufen mit ESPHome und diese sind vom Design und Funktionsumfang vom Home-Assistant Dashboard kaum mehr zu unterscheiden. Ich mag das gar nicht wenn jedes Device mit einer anderen UI etc daherkommt. Da bleibt nur selbst bauen wenn es stimmig aussehen soll.

Ja, recht hast du wohl und anders werde ich es wohl auch nicht hinbekommen :sweat_smile: gebe es langsam über OpenDisplay auf

Es war ein langer weg aber über deinen Weg und die Links hat es nun geklappt das Display läuft Stabil, es wird das angezeigt was angezeigt werden soll und die Knöpfe auf dem Display machen auch das was sie sollen :slight_smile: .
Danke für die Links sie haben mir weitergeholfen :slight_smile:
Ob ich irgendwann mal wieder den weg über OpenDisplay probiere weiß ich nicht denn dafür funktioniert es gerade super gut :slight_smile:

Bei mir ist genau das gleiche Problem aufgetreten.
Daraufhin habe ich:
→ Seeed e1002 aufgeschraubt

→ Boot und Reset Knopf gedrückte

→ unter Windows in einer Powershell das folgende Kommando abgesetzt:

→ esptool.py --chip esp32s3 --port COM3 --baud 921600 write_flash 0x0 photoframe-firmware-seeedstudio_reterminal_e1002-merged.bin

Danach hat das Display wieder gebootet. Nun bin ich aber immer noch nicht weiter mit der OpenDisplay installation. Ich habe erneut versucht über die Web Seite OpenDisplay zu installieren, habe aber den gleichen Fehler bekommen, wie Wambo. Mit dem esptool konnte ich das aber neu flashen.

Ich bin erstmal von OpenDisplay weg gegangen und habe über ESP Builder und viel YAML Code mir eine funktionierende Version gebaut die läuft auch gut und flüssig bin damit sehr zufrieden bisher :slight_smile: vielleicht solltest du so eine Variante mal ausprobieren

Könntest du den YAML Code zur Verfügung stellen?

Na klar, das geht schon.
Hier mal ein Bild von meinem Display

und hier nun der YAML Code dazu einige stellen sind einige Passagen sind mit Platzhaltern versehen die musst du dann noch für deine Sachen bearbeiten.

esphome:
name: e-ink-display
friendly_name: E-Ink Display
on_boot:
- priority: 600
then:
- output.turn_on: batterie_enable
- delay: 500ms
- component.update: batterie_spannung
- component.update: batterie_prozent
- priority: -100
then:
- wait_until:
condition:
lambda: ‘return id(ha_time).now().is_valid();’
timeout: 20s
- lambda: |-
auto now = id(ha_time).now();
id(ist_nacht) = (now.is_valid() && (now.hour >= 22 || now.hour < 5));
ESP_LOGI(“boot”, “Nacht? %d | Stunde: %d”, id(ist_nacht), now.hour);
- if:
condition:
lambda: ‘return id(ist_nacht);’
then:
- wait_until:
condition:
lambda: ‘return !id(ha_bild_url).state.empty();’
timeout: 15s
- if:
condition:
lambda: ‘return !id(ha_bild_url).state.empty();’
then:
- online_image.set_url:
id: nachtbild
url: !lambda ‘return id(ha_bild_url).state;’
- component.update: nachtbild
else:
- logger.log: “Keine URL erhalten - schlafe”
- script.execute: schlafen_planen
else:
- component.update: epaper_display
- script.execute: schlafen_planen

esp32:
variant: esp32s3
framework:
type: esp-idf

psram:
mode: octal
speed: 80MHz

logger:

api:
encryption:
key: “DEIN_API_KEY”

ota:

platform: esphome

wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: E-Ink Display Fallback Hotspot
password: “DEIN_FALLBACK_PASSWORT”

captive_portal:

http_request:
verify_ssl: false
timeout: 20s
watchdog_timeout: 25s

deep_sleep:
id: ds
sleep_duration: 60min

time:

platform: homeassistant
id: ha_time

output:

platform: gpio
pin: GPIO21
id: batterie_enable

switch:

platform: template
name: “Display wachhalten”
id: sw_wachhalten
optimistic: true
restore_mode: RESTORE_DEFAULT_OFF

globals:

id: letzte_temp
type: float
restore_value: yes
initial_value: “0.0”

id: letzter_zustand
type: std::string
restore_value: yes
initial_value: ‘“…”’

id: g_wetter_extra
type: std::string
restore_value: yes
initial_value: ‘“”’

id: g_termine
type: std::string
restore_value: yes
initial_value: ‘“”’

id: g_sprit
type: std::string
restore_value: yes
initial_value: ‘“”’

id: ist_nacht
type: bool
restore_value: no
initial_value: “false”

online_image:

id: nachtbild
url: “http://DEINE_HA_IP:8123/local/bilder/platzhalter.png”
format: PNG
type: RGB565
resize: 800x480
buffer_size: 65536
on_download_finished:

component.update: epaper_display

script.execute: schlafen_planen
on_error:

logger.log: “FEHLER: Bild konnte NICHT geladen werden”

script.execute: schlafen_planen

script:

id: schlafen_planen
mode: restart
then:

delay: 45s

if:
condition:
lambda: ‘return !id(sw_wachhalten).state;’
then:
- deep_sleep.enter: ds

text_sensor:

platform: homeassistant
id: wetter_zustand
entity_id: weather.DEIN_WETTER
on_value:
then:
- lambda: |-
if (x != “unknown” && x != “unavailable” && x != “”) {
id(letzter_zustand) = x;
}

platform: homeassistant
id: ha_bild_url
entity_id: input_text.display_nachtbild_url

platform: homeassistant
id: ha_sprit
entity_id: input_text.display_sprit
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_sprit) = x;’

platform: homeassistant
id: ha_wetter_extra
entity_id: input_text.display_wetter_extra
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_wetter_extra) = x;’

platform: homeassistant
id: ha_termine_woche
entity_id: input_text.display_termine_woche
on_value:
then:
- lambda: ‘if (!x.empty()) id(g_termine) = x;’

sensor:

platform: homeassistant
id: wetter_temp
entity_id: weather.DEIN_WETTER
attribute: temperature
on_value:
then:
- lambda: |-
if (!isnan(x)) {
id(letzte_temp) = x;
}

platform: adc
pin: GPIO1
name: “Akku Spannung”
id: batterie_spannung
update_interval: 60s
attenuation: 12db
filters:

multiply: 2.0
on_value:
then:

component.update: batterie_prozent

platform: template
name: “Akku”
id: batterie_prozent
unit_of_measurement: “%”
device_class: battery
state_class: measurement
update_interval: never
lambda: ‘return id(batterie_spannung).state;’
filters:

calibrate_linear:

4.15 → 100.0

3.96 → 90.0

3.91 → 80.0

3.85 → 70.0

3.80 → 60.0

3.75 → 50.0

3.68 → 40.0

3.58 → 30.0

3.49 → 20.0

3.41 → 10.0

3.30 → 5.0

3.27 → 0.0

clamp:
min_value: 0
max_value: 100

font:

file: “gfonts://Inter@700”
id: font_gross
size: 40
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "

file: “gfonts://Inter@700”
id: font_riesig
size: 64
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "

file: “gfonts://Inter@500”
id: font_normal
size: 28
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "

file: “gfonts://Inter@600”
id: font_titel
size: 24
glyphs: "!"%()+,-.:/°0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyzÄÖÜäöü߀ "

file: “fonts/MaterialDesignIconsDesktop.ttf”
id: font_wettericon
size: 90
glyphs: [
“\U000F0599”, “\U000F0594”, “\U000F0590”, “\U000F0595”,
“\U000F0597”, “\U000F0596”, “\U000F0593”, “\U000F067E”,
“\U000F0598”, “\U000F067F”, “\U000F0591”, “\U000F0592”,
“\U000F059D”, “\U000F0F31”,
]

color:

id: col_black
red: 0%
green: 0%
blue: 0%

id: col_red
red: 100%
green: 0%
blue: 0%

id: col_blue
red: 0%
green: 0%
blue: 100%

spi:
clk_pin: GPIO7
mosi_pin: GPIO9

display:

platform: epaper_spi
model: Seeed-reTerminal-E1002
id: epaper_display
update_interval: never
lambda: |-
auto now = id(ha_time).now();

// ======== NACHT-MODUS: Bild aus HA ========
if (id(ist_nacht)) {
it.image(0, 0, id(nachtbild));
return;
}

// ======== ÜBERSICHT ========
it.fill(Color::WHITE);
static const char *wochentage
 = {“Sonntag”,“Montag”,“Dienstag”,“Mittwoch”,“Donnerstag”,“Freitag”,“Samstag”};
static const char *monate
 = {“Januar”,“Februar”,“März”,“April”,“Mai”,“Juni”,“Juli”,“August”,“September”,“Oktober”,“November”,“Dezember”};

if (now.is_valid()) {
// Wochentag selbst berechnen (Sakamoto) - unabhängig von day_of_week
int jy = now.year;
int jm = now.month;
int jd = now.day_of_month;
static const int tt
 = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4};
if (jm < 3) jy -= 1;
int wt = (jy + jy/4 - jy/100 + jy/400 + tt[jm-1] + jd) % 7;

it.printf(20, 15, id(font_gross), id(col_black),
          "%s, %d. %s", wochentage[wt], now.day_of_month, monate[now.month - 1]);
it.printf(600, 15, id(font_gross), id(col_black), TextAlign::TOP_RIGHT,
          "%02d:%02d", now.hour, now.minute);

}
if (!isnan(id(batterie_prozent).state)) {
auto akku_farbe = id(batterie_prozent).state < 20 ? id(col_red) : id(col_black);
it.printf(780, 15, id(font_gross), akku_farbe, TextAlign::TOP_RIGHT,
“%.0f%%”, id(batterie_prozent).state);
}

it.filled_rectangle(20, 70, 760, 3, id(col_black));

std::string z = id(letzter_zustand);
std::string z_de = “Unbekannt”;
const char* icon = “\U000F0F31”;
if      (z == “sunny”)          { z_de = “Sonnig”;           icon = “\U000F0599”; }
else if (z == “clear-night”)    { z_de = “Klar”;             icon = “\U000F0594”; }
else if (z == “cloudy”)         { z_de = “Bewölkt”;          icon = “\U000F0590”; }
else if (z == “partlycloudy”)   { z_de = “Teils bewölkt”;    icon = “\U000F0595”; }
else if (z == “rainy”)          { z_de = “Regen”;            icon = “\U000F0597”; }
else if (z == “pouring”)        { z_de = “Starkregen”;       icon = “\U000F0596”; }
else if (z == “lightning”)      { z_de = “Gewitter”;         icon = “\U000F0593”; }
else if (z == “lightning-rainy”){ z_de = “Gewitter, Regen”;  icon = “\U000F067E”; }
else if (z == “snowy”)          { z_de = “Schnee”;           icon = “\U000F0598”; }
else if (z == “snowy-rainy”)    { z_de = “Schneeregen”;      icon = “\U000F067F”; }
else if (z == “fog”)            { z_de = “Nebel”;            icon = “\U000F0591”; }
else if (z == “hail”)           { z_de = “Hagel”;            icon = “\U000F0592”; }
else if (z == “windy”)          { z_de = “Windig”;           icon = “\U000F059D”; }
else if (z == “windy-variant”)  { z_de = “Windig”;           icon = “\U000F059D”; }
else if (z == “exceptional”)    { z_de = “Außergewöhnlich”;  icon = “\U000F0F31”; }

it.printf(30, 80, id(font_wettericon), id(col_red), “%s”, icon);
it.printf(165, 78, id(font_riesig), id(col_black), “%.1f°C”, id(letzte_temp));
it.printf(165, 150, id(font_normal), id(col_black), “%s”, z_de.c_str());

{
std::string w = id(g_wetter_extra);
int wy = 80;
size_t ws = 0;
while (ws <= w.length()) {
size_t nl = w.find(‘\n’, ws);
std::string zeile = (nl == std::string::npos) ? w.substr(ws) : w.substr(ws, nl - ws);
if (!zeile.empty()) {
it.printf(480, wy, id(font_normal), id(col_black), “%s”, zeile.c_str());
wy += 38;
}
if (nl == std::string::npos) break;
ws = nl + 1;
}
}

it.filled_rectangle(20, 188, 760, 3, id(col_black));

it.printf(20, 196, id(font_titel), id(col_blue), “Termine”);
{
std::string t2 = id(g_termine);
int y2 = 226;
size_t start = 0;
while (start <= t2.length()) {
size_t nl = t2.find(‘\n’, start);
std::string zeile = (nl == std::string::npos) ? t2.substr(start) : t2.substr(start, nl - start);
if (!zeile.empty()) {
it.printf(20, y2, id(font_normal), id(col_black), “%s”, zeile.c_str());
y2 += 33;
}
if (nl == std::string::npos) break;
start = nl + 1;
}
}

it.filled_rectangle(20, 396, 760, 3, id(col_black));

it.printf(20, 402, id(font_titel), id(col_blue), “Sprit (Super) - Top 3”);
it.printf(20, 434, id(font_normal), id(col_black), “%s”, id(g_sprit).c_str());


(Beitrag vom Verfasser gelöscht)

Nein, das Display baut sich nicht jede Minute neu auf – die Uhrzeit ist immer nur so aktuell wie der letzte Refresh.

Das Ganze ist displayintern über ESPHome gelöst. Das Display liegt im Deep Sleep und wacht stündlich von selbst auf (bei mir alle 60 Minuten). Bei jedem Aufwachen zeichnet es sich einmal komplett neu, und dabei wird auch die Uhrzeit aktualisiert. Zwischen zwei Aufwachvorgängen zeigt es also eine „eingefrorene" Uhrzeit, die bis zu eine Stunde alt sein kann.

Der Grund ist genau der, den du vermutest: Ein Full-Refresh auf dem 6-Farb-E-Ink dauert rund 30 Sekunden und kostet spürbar Strom (das Panel flackert dabei mehrfach durch alle Farben). Jede Minute neu zu zeichnen würde den Akku in Stunden leersaugen und das Display ständig flackern lassen. Deshalb nur einmal pro Aufwach-Zyklus. Technisch läuft das über deep_sleep mit sleep_duration, das Display selbst steht auf update_interval: never, und beim Aufwachen wird einmal component.update ausgelöst. Wer die Uhrzeit aktueller haben möchte, kann das Aufwach-Intervall verkürzen (z. B. auf 15 Minuten) – das kostet dann aber entsprechend mehr Akku.

Zur Rolle von Home Assistant, damit kein falscher Eindruck entsteht: Die HA-Automationen bereiten bei mir nur die Inhalte auf (Termine, Spritpreise, Wetter-Texte) und legen sie in Helfern (input_text) ab. Das Display holt sich diese Werte beim Aufwachen und zeichnet sie – aber den Zeichenbefehl selbst gibt sich das Gerät, nicht HA. Die Refresh-Steuerung sitzt also komplett im Display, HA liefert nur die Daten.