ePaper XIAO 7.5″ mit Home Assistant verbinden – ganz schnell mit Screenshot-Trick!

Du willst dein Home Assistant Dashboard auf einem stromsparenden E-Ink Display anzeigen? In diesem Beitrag zeige ich dir zwei Möglichkeiten, wie du das 7.5 Zoll große ePaper Display von Seeed Studio mit ESPHome nutzen kannst – inklusive Deep-Sleep-Unterstützung und Screenshot-Trick per Puppet Add-on.

▶️ Das passende YouTube-Video mit allen Details findest du hier:

https://youtu.be/j_XxQwnzzRU

📦 Gerät kaufen

Du bekommst das „Seeed XIAO ESP32S3 mit 7.5“ E-Ink Panel“ hier:

Zum Produkt (mit 5% Rabattcode „X0FKM5CS“)

Du unterstützt damit meinen Kanal – ohne Mehrkosten für dich, aber mit einer kleinen Provision für mich. Danke!

📀 Windows-Treiber installieren

Wenn der COM-Port beim ersten Anschluss nicht automatisch erkannt wird, benötigst du diesen Treiber:

NodeMCU USB-Treiber (GitHub)

🧵 3D-Druck-Modelle für die Wandmontage

Es gibt bereits mehrere passende Modelle für eine Wandhalterung auf Makerworld:

Standard-Wandhalterung

Magnet-Wandhalterung

⚖️ Möglichkeit #1: Individuelles Dashboard zeichnen (aufwändig)

Mit ESPHome kannst du das Display direkt per Code gestalten – pixelgenau und individuell. Das ist mächtig, aber komplex.

Hier ein vollständiges Beispiel inkl.:

  • Deep-Sleep-Support
  • Kalenderdaten
  • Wetteranzeige
  • Pool- und Gartentemperatur
  • PV-Überschuss

Folgenden Code bitte unter ota: einfügen.

Sollte oberhalb dieses Eintrages noch ein Zeilen mit wifi: oder captive_portal: stehen, diese vorher löschen!

globals:
  - id: wifi_status
    type: int
    restore_value: no
    initial_value: "0"
  - id: first_update_done
    type: bool
    restore_value: no
    initial_value: "false"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  on_connect:
    then:
      - lambda: |-
          id(wifi_status) = 1;
  on_disconnect:
    then:
      - lambda: |-
          id(wifi_status) = 0;


captive_portal:

# Here is deep sleep part
deep_sleep:
  id: deep_sleep_1
  run_duration: 1min  # Device wake up and run 60s (enough to pull data and update)
  sleep_duration: 60min  # deep sleep for 1h

script:
  - id: update_display
    then:
      - component.update: my_display

interval:
  # Condition: wifi connected && data retrieved && first time
  - interval: 10s  # Check every second
    then:
      - if:
          condition:
            and:
              - wifi.connected:
              - lambda: "return !id(ha_calendar_event_1).state.empty();"
              - lambda: "return !id(first_update_done);"
          then:
            - lambda: |-
                ESP_LOGD("Display", "Updating Display...");
            - script.execute: update_display  # Refresh immediately
            - lambda: "id(first_update_done) = true;"
  - interval: 59s  # run this command before 1s of run_duration end
    then:
      - logger.log: "Entering deep sleep now..."


image:
  - file: image/wifi.jpg
    type: BINARY
    id: esphome_logo
    resize: 400x240
    invert_alpha: true

# Connect to Home Assistant to get time
time:
  - platform: homeassistant
    id: homeassistant_time

text_sensor:
  - platform: homeassistant
    id: ha_calendar_event_1
    entity_id: calendar.familienkalender_google
    attribute: "message"
  - platform: homeassistant
    id: ha_calendar_start_time_1
    entity_id: calendar.familienkalender_google
    attribute: "start_time"
  - platform: homeassistant
    id: ha_calendar_end_time_1
    entity_id: calendar.familienkalender_google
    attribute: "end_time"

  - platform: homeassistant
    entity_id: weather.openweathermap
    id: myWeather
  - platform: homeassistant
    entity_id: sensor.pool_temperatur
    id: temp
  - platform: homeassistant
    entity_id: sensor.garten_temperatur
    id: humi
  - platform: homeassistant
    entity_id: binary_sensor.wc_fenster
    id: press
  - platform: homeassistant
    entity_id: sensor.pv_uberschuss_15min
    id: wind

font:
  - file: "fonts/Montserrat-Black.ttf"
    id: web_font
    size: 20
  - file: "fonts/Montserrat-Black.ttf"
    id: data_font
    size: 30
  - file: "fonts/Montserrat-Black.ttf"
    id: sensor_font
    size: 22

  - file: "gfonts://Inter@700" #
    id: font1
    size: 24

  - file: 'fonts/materialdesignicons-webfont.ttf' # Directory to save ttf file
    id: font_mdi_large
    size: 200
    glyphs: &mdi-weather-glyphs # https://pictogrammers.com/library/mdi/
      - "\U000F050F" # Thermometer
      - "\U000F058E" # Humidity
      - "\U000F059D" # Wind speed
      - "\U000F0D60" # Atmospheric pressure
      - "\U000F0590" # Cloudy weather
      - "\U000F0596" # Rainy weather
      - "\U000F0598" # Snowy weather
      - "\U000F0599" # Sunny weather
      - "\U000F11DC" # Windows
      - "\U000F0D9C" # Solarpanel
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: font_weather # Copy the above icon and change the size to 40
    size: 200
    glyphs: *mdi-weather-glyphs
  - file: 'fonts/materialdesignicons-webfont.ttf'
    id: img_font_sensor # Copy the above icon and change the size to 40
    size: 70
    glyphs: *mdi-weather-glyphs

spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    id: my_display
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: 
      number: GPIO4
      inverted: true
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: 50s
    lambda: |-
      if(id(wifi_status) == 0){
        it.image(180, 0, id(esphome_logo));
        it.print(230, 300, id(data_font), "WI-FI CONNECTING");
      }else{
        // Draw weather images here
        std::string weather_string = id(myWeather).state.c_str();
        if(weather_string == "rainy" || weather_string == "lightning" || weather_string == "pouring"){
          // Draw rainy weather image
          it.printf(120, 85, id(font_weather), TextAlign::CENTER, "\U000F0596");
        }else if(weather_string == "snowy"){
          // Draw snowy weather image
          it.printf(120, 85, id(font_weather), TextAlign::CENTER, "\U000F0598");
        }else if(weather_string == "sunny" || weather_string == "windy"){
          // Draw sunny weather image
          it.printf(120, 85, id(font_weather), TextAlign::CENTER, "\U000F0599");
        }else{
          // Draw cloudy weather image
          it.printf(120, 85, id(font_weather), TextAlign::CENTER, "\U000F0590");
        }

        auto time_now = id(homeassistant_time).now();
        // Month conversion
        const char* months[] = {
          "Januar", "Februar", "März", "April", "Mai", "Juni",
          "Juli", "August", "September", "Oktober", "November", "Dezember"
        };
        const char* month_str = months[time_now.month - 1];  // Month index starts from 0
        // Get the day
        int day = time_now.day_of_month;
        // Draw the date
        it.printf(250, 110, id(data_font), "%s %d", month_str, day);
        // Get the day of the week
        const char* days[] = {"Samstag", "Sonntag", "Montag", "Dienstag", "Mittwoch", "Donnerstag", "Freitag"};
        const char* day_of_week = days[time_now.day_of_week];
        it.printf(250, 70, id(data_font), "%s", day_of_week);

        int x = 20, y = 180, w = 180, h = 120, r = 10, thickness = 4;
        // Draw four borders
        it.filled_rectangle(x + r, y, w - 2 * r, thickness); // Top border
        it.filled_rectangle(x + r, y + h - thickness, w - 2 * r, thickness); // Bottom border
        it.filled_rectangle(x, y + r, thickness, h - 2 * r); // Left border
        it.filled_rectangle(x + w - thickness, y + r, thickness, h - 2 * r); // Right border
        // Draw four rounded corners
        it.filled_circle(x + r, y + r, r); // Top-left corner
        it.filled_circle(x + w - r, y + r, r); // Top-right corner
        it.filled_circle(x + r, y + h - r, r); // Bottom-left corner
        it.filled_circle(x + w - r, y + h - r, r); // Bottom-right corner
        // Fill the inside with black to form a border
        it.filled_rectangle(x + thickness, y + thickness, w - 2 * thickness, h - 2 * thickness, COLOR_OFF);
        // Temperature
        it.printf(x+10, y+10, id(sensor_font), "Temp. Pool");
        it.printf(x+45, y+75, id(img_font_sensor), TextAlign::CENTER, "\U000F050F");
        // Get temperature data
        it.printf(x+75,y+65, id(data_font), "%s°C", id(temp).state.c_str());

        x = 220;
        y = 180;
        // Draw four borders
        it.filled_rectangle(x + r, y, w - 2 * r, thickness); // Top border
        it.filled_rectangle(x + r, y + h - thickness, w - 2 * r, thickness); // Bottom border
        it.filled_rectangle(x, y + r, thickness, h - 2 * r); // Left border
        it.filled_rectangle(x + w - thickness, y + r, thickness, h - 2 * r); // Right border
        // Draw four rounded corners
        it.filled_circle(x + r, y + r, r); // Top-left corner
        it.filled_circle(x + w - r, y + r, r); // Top-right corner
        it.filled_circle(x + r, y + h - r, r); // Bottom-left corner
        it.filled_circle(x + w - r, y + h - r, r); // Bottom-right corner
        // Fill the inside with black to form a border
        it.filled_rectangle(x + thickness, y + thickness, w - 2 * thickness, h - 2 * thickness, COLOR_OFF);
        // Humidity
        it.printf(x+10, y+10, id(sensor_font), "Temp. Garten");
        it.printf(x+45, y+75, id(img_font_sensor), TextAlign::CENTER, "\U000F050F");
        // Get humidity data
        it.printf(x+75,y+65, id(data_font), "%s°C", id(humi).state.c_str());

        x = 20;
        y = 320;
        // Draw four borders
        it.filled_rectangle(x + r, y, w - 2 * r, thickness); // Top border
        it.filled_rectangle(x + r, y + h - thickness, w - 2 * r, thickness); // Bottom border
        it.filled_rectangle(x, y + r, thickness, h - 2 * r); // Left border
        it.filled_rectangle(x + w - thickness, y + r, thickness, h - 2 * r); // Right border
        // Draw four rounded corners
        it.filled_circle(x + r, y + r, r); // Top-left corner
        it.filled_circle(x + w - r, y + r, r); // Top-right corner
        it.filled_circle(x + r, y + h - r, r); // Bottom-left corner
        it.filled_circle(x + w - r, y + h - r, r); // Bottom-right corner
        // Fill the inside with black to form a border
        it.filled_rectangle(x + thickness, y + thickness, w - 2 * thickness, h - 2 * thickness, COLOR_OFF);
        // Air Pressure
        it.printf(x+10, y+10, id(sensor_font), "Fenster offen");
        it.printf(x+45, y+75, id(img_font_sensor), TextAlign::CENTER, "\U000F11DC");
        // Get atmospheric pressure data
        it.printf(x+85,y+50, id(data_font), "%s", id(press).state.c_str());
        //it.printf(x+85,y+78, id(sensor_font), "inHg");

        x = 220;
        y = 320;
        // Draw four borders
        it.filled_rectangle(x + r, y, w - 2 * r, thickness); // Top border
        it.filled_rectangle(x + r, y + h - thickness, w - 2 * r, thickness); // Bottom border
        it.filled_rectangle(x, y + r, thickness, h - 2 * r); // Left border
        it.filled_rectangle(x + w - thickness, y + r, thickness, h - 2 * r); // Right border
        // Draw four rounded corners
        it.filled_circle(x + r, y + r, r); // Top-left corner
        it.filled_circle(x + w - r, y + r, r); // Top-right corner
        it.filled_circle(x + r, y + h - r, r); // Bottom-left corner
        it.filled_circle(x + w - r, y + h - r, r); // Bottom-right corner
        // Fill the inside with black to form a border
        it.filled_rectangle(x + thickness, y + thickness, w - 2 * thickness, h - 2 * thickness, COLOR_OFF);
        // Wind Speed
        it.printf(x+10, y+10, id(sensor_font), "PV Ueberschuss");
        it.printf(x+45, y+75, id(img_font_sensor), TextAlign::CENTER, "\U000F0D9C");
        // Get wind speed data
        it.printf(x+85,y+50, id(data_font), "%s", id(wind).state.c_str());

        // Draw a vertical line
        it.filled_rectangle(430, 30, 5, 430);
        // Right section
        it.printf(540, 40, id(data_font), "Kalender");

        // Define event structure
        struct Event {
            std::string message;
            std::string start_time;
            std::string end_time;
            time_t start_timestamp;
        };

        // Parse time string to time_t (UNIX timestamp)
        auto parse_time = [](const std::string &time_str) -> time_t {
            struct tm timeinfo = {};
            if (strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &timeinfo) == nullptr) {
                return 0;  // Invalid time
            }
            return mktime(&timeinfo);
        };

        // Create event list
        std::vector<Event> events = {
            {id(ha_calendar_event_1).state, id(ha_calendar_start_time_1).state, id(ha_calendar_end_time_1).state, parse_time(id(ha_calendar_start_time_1).state)}
        };
        ESP_LOGD("myCalendar", "Start Time: %s -> %ld", id(ha_calendar_start_time_1).state.c_str(), parse_time(id(ha_calendar_start_time_1).state));

        // Filter invalid events (start_timestamp == 0)
        events.erase(std::remove_if(events.begin(), events.end(), [](const Event &e) { return e.start_timestamp == 0; }), events.end());

        // Sort by `start_timestamp` (earliest to latest)
        std::sort(events.begin(), events.end(), [](const Event &a, const Event &b) {
            return a.start_timestamp < b.start_timestamp;
        });

        // Define a function to format time
        auto format_time = [](std::string time_str) -> std::string {
            struct tm timeinfo;
            if (strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &timeinfo) == nullptr) {
                return "Invalid";
            }
            char buffer[10];
            strftime(buffer, sizeof(buffer), "%I:%M%p", &timeinfo); // Convert to 12-hour format
            return std::string(buffer);
        };
        // Parse date
        auto format_date = [](const std::string &time_str) -> std::string {
            struct tm timeinfo = {};
            if (strptime(time_str.c_str(), "%Y-%m-%d %H:%M:%S", &timeinfo) == nullptr) {
                return "Invalid";
            }
            char buffer[6];  // Need to store "MM-DD\0"
            strftime(buffer, sizeof(buffer), "%m-%d", &timeinfo);
            return std::string(buffer);
        };

        // Draw events
        int even_x_start_offset = 460;
        int even_y_start_offset = 80;
        for (const auto &event : events) {
          if(even_y_start_offset >= 420){
            break;
          }

          // Format time
          std::string formatted_date = format_date(event.start_time);
          std::string formatted_start_time = format_time(event.start_time);
          std::string formatted_end_time = format_time(event.end_time);

          // Combine time range string
          std::string time_range = formatted_start_time + " - " + formatted_end_time;
          time_range = formatted_date + "  " + time_range;
          if(formatted_start_time == "Invalid" || formatted_end_time == "Invalid"){
            time_range.clear();
          }
          // Display time range, e.g., "10:00AM - 11:00AM"
          it.printf(even_x_start_offset, even_y_start_offset, id(sensor_font), "%s", time_range.c_str());
          even_y_start_offset += 30;
          // Display event name
          it.printf(even_x_start_offset, even_y_start_offset, id(sensor_font), "%s", event.message.c_str());
          even_y_start_offset += 40;
        }
      }

Hinweis: Diese Variante lohnt sich, wenn du ein sehr spezielles Layout willst. Für alle anderen empfehle ich…

Möglichkeit #2 mit Puppet

🤖 Möglichkeit #2: Puppet Add-on + Screenshot = Dashboard ohne Aufwand!

Mit dem Puppet Add-on kannst du einfach dein Home Assistant Dashboard als Screenshot anzeigen lassen. Du brauchst:

Folgend der Code für mein Dashboard

  - type: sections
    max_columns: 4
    title: Flur
    path: flur
    sections:
      - type: grid
        cards:
          - type: tile
            entity: sensor.pv_uberschuss_15min
            features_position: bottom
            vertical: true
            name: PV Überschuss
            icon: mdi:solar-panel-large
            show_entity_picture: false
          - type: tile
            entity: sensor.pool_temperatur
            features_position: bottom
            vertical: true
            icon: mdi:pool-thermometer
          - type: entity-filter
            entities:
              - entity: binary_sensor.wc_fenster
              # - ... eure zu überwachenden Entitäten für "Fenster / Tür auf"
            state_filter:
              - 'on'
            card:
              type: entities
          - type: entity-filter
            entities:
              - switch.wz_mehrfachsteckdose_aquariumlicht
              - light.bad_stripe
              # - ... eure zu überwachenden Entitäten für "Licht an"

            state_filter:
              - 'on'
            card:
              type: entities
      - type: grid
        cards:
          - clock_size: small
            show_seconds: false
            type: clock
            title: Letzte Aktualisierung
            grid_options:
              columns: 12
              rows: 1
          - initial_view: dayGridDay
            type: calendar
            entities:
              - calendar.familienkalender_google # eure Kalenderentität

ESP Code zum Testen ohne Deep-Sleep

esphome:
  name: edisplayflur
  friendly_name: eDisplayFlur

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "HUk+qHsrRe/WROKX4Snb/3wVBPMF1sxy2R2uMSp+gwU="

ota:
  - platform: esphome
    password: "b07af141786f53d8db43bdc67b9010fc"


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

captive_portal:

# Hier ist der auskommentierte Deep-Sleep Part
#deep_sleep:
#  id: deep_sleep_1
#  run_duration: 1min  # Device wake up and run 60s (enough to pull data and update)
#  sleep_duration: 60min  # deep sleep for 1h#

http_request:
  verify_ssl: false
  timeout: 30s
  watchdog_timeout: 15s

online_image:
  - id: dashboard_image
    format: BMP
    type: BINARY
    buffer_size: 25000
    url: http://192.168.1.83:10000/lovelace/flur?viewport=800x480&eink=2&format=bmp&invert
    update_interval: 120sec
    on_download_finished:
      - delay: 0ms
      - component.update: main_display

spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    id: main_display
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: 
      number: GPIO4
      inverted: true
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));

Der fertige Code für ESP Home mit Deep Sleep

esphome:
  name: edisplayflur
  friendly_name: eDisplayFlur

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: esp-idf

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "HUk+qHsrRe/WROKX4Snb/3wVBPMF1sxy2R2uMSp+gwU="

ota:
  - platform: esphome
    password: "b07af141786f53d8db43bdc67b9010fc"


wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

captive_portal:

# Hier ist der aktivierte Deep-Sleep Part
deep_sleep:
  id: deep_sleep_1
  run_duration: 1min  # Device wake up and run 60s (enough to pull data and update)
  sleep_duration: 60min  # deep sleep for 1h

http_request:
  verify_ssl: false
  timeout: 30s
  watchdog_timeout: 15s

online_image:
  - id: dashboard_image
    format: BMP
    type: BINARY
    buffer_size: 25000
    url: http://192.168.1.83:10000/lovelace/flur?viewport=800x480&eink=2&format=bmp&invert
    update_interval: 120sec
    on_download_finished:
      - delay: 0ms
      - component.update: main_display

spi:
  clk_pin: GPIO8
  mosi_pin: GPIO10

display:
  - platform: waveshare_epaper
    id: main_display
    cs_pin: GPIO3
    dc_pin: GPIO5
    busy_pin: 
      number: GPIO4
      inverted: true
    reset_pin: GPIO2
    model: 7.50inv2
    update_interval: never
    lambda: |-
      it.image(0, 0, id(dashboard_image));

Hinweis: Beim Arbeiten mit Deep Sleep musst du für neue Uploads wie hier beschrieben vorgehen:
Seeed FAQ zu Deep Sleep

✅ Fazit

Das XIAO 7.5″ ePaper Display ist eine super Möglichkeit, dein Home Assistant Dashboard elegant und stromsparend anzuzeigen. Du hast zwei Optionen:

  • Individuelle Anzeige per Code: Flexibel, aber aufwändig
  • Screenshot mit Puppet: Schnell, einfach, wartungsarm

Ich nutze die zweite Methode für die meisten Anwendungsfälle – vor allem mit Deep Sleep für lange Batterielaufzeit.


Kommentare

3 Antworten zu „ePaper XIAO 7.5″ mit Home Assistant verbinden – ganz schnell mit Screenshot-Trick!“

  1. Avatar von LazyBee

    Danke für den ausführlichen Bericht.
    Ich versuche die erste variante zum laufen zu bekommen und scheitere an dieser Stelle:
    image:
    – file: image/wifi.jpg
    type: BINARY
    id: esphome_logo
    resize: 400×240
    invert_alpha: true

    Wo bekomme ich das image her und wo lege ich das ab?

  2. Ich habe mir ebenfalls das ePaper Display angeschafft und es mit der Methode 2 versucht.
    In deinem Video sagst du, du hast es mit PNG nicht hinbekommen, das hat allerdings nichts mit dem Addon Puppet zu tun.

    Wir starten aber erst mal vorne.

    Das XIAO 7.5″ ePaper Panel von Seed Studio läuft mit einem ESP32-C3.
    (in deiner Anleitung schreibst du „Du bekommst das „Seeed XIAO ESP32S3 mit 7.5“ E-Ink Panel“ hier:“ Es ist allerdings kein ESP32S3)

    Wir kriegen also einen ESP32-C3, dieser hat allerdings nur 400 kB SRAM.
    Das ist schon ziemlich grenzwertig für ein 7,5″ ePaper Display.

    Ich habe es anfangs auch mit BMP versucht und die buffer size auf 25000 gesetzt.
    Es spielt übrigens keine Rolle wie hoch man die buffer size setzt, dieser wird dynamisch angepasst, wenn er nicht ausreicht.

    [15:49:04] Downloading image (Size: 48062)
    [15:49:08] Image fully downloaded, read 48062 bytes, width/height = 800/480

    Das Problem ist nur, der geringe SRAM reicht nicht immer um Bilder mit der Größe zu verarbeiten.

    [E][online_image:092]: allocation of 48000 bytes failed. Biggest block in heap: 43008 Bytes
    In dem Fall hatte der ESP32-C3 im Heat nur 43 008 Bytes am Stück frei, reicht also nicht. und die Allokation scheitert.

    Ergebnis ist, manchmal wird ein Bild übertragen, manchmal nicht.
    Je nachdem, wie der ESP gerade so ausgelastet ist.

    Also habe ich es mit PNG versucht.
    Puppet liefert das Bild mit 496 Bytes also super klein.
    Der PNG-Decoder muss das Bild aber im Ram größer machen.
    Bei 800×480 braucht er trotzdem ~48 kB Puffer, egal wie stark komprimiert die Datei ist.
    Auch da, passiert letztlich das gleiche.
    Abbruch weil zu wenig Ram.
    [16:14:00][D][online_image:089]: Allocating new buffer of 48000 bytes
    [16:14:00][E][online_image:092]: allocation of 48000 bytes failed. Biggest block in heap: 34816 Bytes
    [16:14:00][E][online_image.png:086]: Error decoding image: N\xc6V²\x89\xae\x8aPII“\xcc*\x84&\xcaJ\xc8R\xc4\xce6\x89\xba\x84
    [16:14:00]\xef\xf0?\xebc\x8c
    [16:14:00][E][online_image:250]: Error when decoding image.

    Die einzige Lösung hierbei ist mit der Auflösung runter zu gehen.
    Damit wird das Ergebnis aber nicht wirklich schön

    Vielleicht hat noch wer eine andere Idee?

    Mit dem ESP32-C3 ist die Methode 2 jedenfalls nicht wirklich umsetzbar.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert