Neuer Wasserzähler (Wireless M-Bus und Home Assistant)

,

Bisher habe ich meinen klassischen Wasserzähler mit AI on the EDGE ausgelesen. Nun stand heute der Zählerwechsel an und ich habe einen nagelneuen digitalen Wasserzähler bekommen.

Der Zähler ist ein Kamstrup Multical 21. Der seinen Zählerstand wohl alle paar Sekunden per Wireless M-Bus durch die gegend funkt. Soweit so gut, nur wie bekomme ich den jetzt ausgelesen?

Ich habe jetzt ein Home Assistant AddOn gefunden, was die Signale auf MQTT umsetzt. Das wäre schon mal ein Anfang. Leider ist da nicht Beschrieben welcher USB Adapter dafür nötig ist?

Hat das so schon jemand am laufen und kann mir Tipps geben?

Die Liste der unterstützten Dongles und (Wasser-) Zähler findet sich hier (Link) ca. 3/4 am Ende der Seite. Dort sind folgende Dongles erwähnt:
IMST 871a (im871a)
IMST 891a (iu891a)
Amber 8465-M/8665-M/8626-M/Metis-II (amb8465) 868MHz
Amber 3665-M (amb3665) 169MHz
CUL family (cul)
Radiocraft (rc1180)
rtl_wmbus (rtlwmbus)
rtl_433 (rtl433)

Evtl. geht auch dieser Dongle: Link

@anon1674786 Bist Du weiter gekommen?

Danke Dir

Gibt es dazu was neues?

Ich habe auch einen Multical21 und lese den mit ESPHome aus und einem Heltec V2. Der Heltec hängt im Keller und sendet die Daten per WLAN, mein HA läuft auf dem Dachboden.

Hat etwas gedauert bis ich das am Start hatte, aber jetzt läuft das sauber und da ESPHome das direkt an HA schickt brauch ich dazu auch nicht dem Umweg über MQTT gehen. Den Key habe ich vom Versorger bekommen.

Hab für den Anfang erst mal ein paar Werte eingebaut (heute, gestern, diese Woche, usw.) und schau mir das gerade mal in Ruhe an was da so an Werten kommt.

Hallo Geordi,

Bei mir wurde heute auch ein Kamstrup-Wasserzähler eingebaut und mein Versorger will mir in der kommenden Zeit den Schlüssel zusenden. Ich habe mir einen Heltec V3 bestellt und lese mich gerade in die komplette Dokumentation von SzczepanLeon ein, was noch etwas sperrig ist.

Würdest du mir vielleicht deine yaml schicken? Ich habe zwar den Heltec V3 und muss daher die ganzen GPIOs umschreiben sowie es auf den SX1262 umschreiben. Meine Hoffnung ist aber, dass ich mir so ein wenig Gesuche sparen kann.

Grüße

Andreas

esphome:
  name: wasserzaehler
  friendly_name: Wasserzaehler

esp32:
  board: heltec_wifi_lora_32_V2
  framework:
    type: esp-idf

logger:
  level: INFO

api:
  encryption:
    key: "apikey"

ota:
  - platform: esphome
    password: "otapass"

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  power_save_mode: none
  ap:
    ssid: "Wasserzaehler Fallback Hotspot"
    password: "SKi36fDXMzCR"

web_server:
  port: 80
  version: 3

globals:
  - id: frame_counter
    type: int
    restore_value: no
    initial_value: "0"

  - id: last_rssi
    type: int
    restore_value: no
    initial_value: "0"

i2c:
  sda: GPIO4
  scl: GPIO15
  scan: true

font:
  - file: "gfonts://Roboto"
    id: font1
    size: 12

spi:
  clk_pin: GPIO5
  miso_pin: GPIO19
  mosi_pin: GPIO27

external_components:
  - source:
      type: git
      url: https://github.com/IoTLabs-pl/ESPHome-Components
      ref: v1.0.6
    components:
      - wmbus_common
      - wmbus_radio
      - wmbus_meter
    refresh: 0s

wmbus_common:
  drivers: all

wmbus_radio:
  id: radio_component
  radio_type: SX1276
  cs_pin: GPIO18
  reset_pin: GPIO14
  irq_pin: GPIO35
  on_frame:
    - then:
        - lambda: |-
            id(frame_counter) += 1;
            id(last_rssi) = frame->rssi();
        - logger.log:
            level: WARN
            format: "Meter ID: %s"
            args: ["frame->meter_id().c_str()"]
        - logger.log:
            level: WARN
            format: "Frame: https://wmbusmeters.org/analyze/%s"
            args: ["frame->as_hex().c_str()"]

wmbus_meter:
  - id: wasserzaehler_kg
    meter_id: !secret multical_id
    type: multical21
    key: !secret multical_key

text_sensor:
  - platform: wifi_info
    ip_address:
      name: "Wasserzaehler ESP IP"
      id: esp32_ip
      entity_category: diagnostic

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: current_status
    name: "Wasserzaehler Status"
    icon: mdi:information-outline

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: timestamp
    name: "Wasserzaehler Letztes Telegramm"
    entity_category: diagnostic

sensor:
  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: total_m3
    id: wasser_total_m3
    name: "Wasserzaehler Gesamtverbrauch"
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    device_class: water
    state_class: total_increasing
    icon: mdi:water

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: target_m3
    name: "Wasserzaehler Stichtagswert"
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    device_class: water
    state_class: total
    icon: mdi:counter

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: flow_temperature_c
    id: wasser_flow_temp
    name: "Wasserzaehler Wassertemperatur"
    unit_of_measurement: "°C"
    accuracy_decimals: 0
    device_class: temperature
    state_class: measurement

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: external_temperature_c
    id: wasser_external_temp
    name: "Wasserzaehler Umgebungstemperatur"
    unit_of_measurement: "°C"
    accuracy_decimals: 0
    device_class: temperature
    state_class: measurement

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: rssi_dbm
    id: wasser_rssi
    name: "Wasserzaehler RSSI"
    unit_of_measurement: "dBm"
    accuracy_decimals: 0
    device_class: signal_strength
    state_class: measurement
    entity_category: diagnostic

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    reset_pin: GPIO16
    address: 0x3C
    update_interval: 30s
    lambda: |-
      if (!isnan(id(wasser_total_m3).state)) {
        it.printf(0, 0, id(font1), "Gesamt: %.2f m3", id(wasser_total_m3).state);
      } else {
        it.printf(0, 0, id(font1), "Gesamt: --.-- m3");
      }

      if (!isnan(id(wasser_flow_temp).state)) {
        it.printf(0, 28, id(font1), "Wasser: %.0f C", id(wasser_flow_temp).state);
      } else {
        it.printf(0, 28, id(font1), "Wasser: -- C");
      }

      if (!isnan(id(wasser_external_temp).state)) {
        it.printf(0, 42, id(font1), "Umgbg.: %.0f C", id(wasser_external_temp).state);
      } else {
        it.printf(0, 42, id(font1), "Umgbg.: -- C");
      }

Klar, hier der Code.

Ich hatte massive Probleme mit dem Repo von SzczepanLeon und habe das von IoTLabs genutzt, damit lief es auf Anhieb. Zuvor hatte ich ständig Probleme beim compilieren dass was gefehlt hat oder bestimmte Dateien nicht da waren.

Danke dir!

Ich hoffe, dass mein Heltec-Board dann schnell ankommt. Wenn ich es am laufen habe, stelle ich meine yaml dann auch hier ein.

Hoffentlich wird das Repo von SzczepanLeon mitmachen. In dem von dir genutzten scheint keine Unterstützung für den 1262 vorhanden zu sein.

HI, ich sehe das Du in Deinem File den key als !secret multical_key drin hast. Ist das nur hier fürs Forum gemacht oder brauchst Du den nicht? Ich weis noch nicht ob ich den Key bekomme, denn vorher hab ich das mit einem ESP32 ausgelesen und da brauchte ich keinen Key, nur ESPHome hat irgendwas geändert so das ich die Programmierung nicht mehr in ESP rein bekomme.

Ich hab den Key und die Multical-ID (Seriennummer) im ESPHome in den Secrets hinterlegt, so wie auch meine WLAN-Daten, damit ruft man die dann ja auf.

Ohne den Key würde er (zumindest bei mir bzw. meinem Versorger) die Daten nicht entschlüsseln können.

OK, das ist dann doch ein wenig anders als bei mir. Hier war es möglich, dank eines Forenusers, das der Zähler gelesen werden konnte ohne Key. Leider hat das mit den Updates von ESPHome dann mal funktioniert und dann mal wieder nicht. Im Moment hatte ich auch das Update gemacht und schon ging nichts mehr. Leider hab ich nicht so alte Backups, daß ich wieder mal so 10 Updates zurückgehen kann um das zu testen. Wie man das mit einer “äleteren” Version zu Fuß installieren kann weis ich nicht sonst könnte man ja auf Github eien alte Version mal installieren. Mit den neueren gibt es da Probleme.

Das es bei einzelnen Usern auch ohne Key geht habe ich auch schon gelesen. Dann ist es aber so, dass das Datenpaket gar nicht erst verschlüsselt ist. Datenschutzrechtlich ist das aber mehr als bedenklich. Einzelne Versorger nutzen angeblich auch nur einen einzigen Schlüssel für all ihre Zähler.

Mein Versorger war sehr hilfreich bislang und gibt den key recht einfach raus. Ich musste einen Zusatzvertrag unterschreiben, dass ich den Zähler auf meine Kosten tauschen lasse, sollte ich das Haus verkaufen. Anderenfalls könnte ich ja weiterhin den Wasserverbrauch der Nachbesitzer auslesen → Datenschutz.

Das erklärt dann sicherlich auch, warum sich ein paar Versorger schwer tun damit die Schlüssel herauszugeben.

Nebenher: Mein Heltec ist heute angekommen. Mal sehen ob er mein yaml schluckt. In ein paar Tagen sollte dann der Key da sein und ich kann weiter meinen Home Assistant füttern.

Danke für die YAML, damit konnte ich meinen Heltec WiFi LoRa 32 V2 intitial mit W M-Bus zum laufen bekommen.
Leider lässt er sich aktuell nicht mit angepasster Config updaten.
Die Firmware compiliert problemlos, allerdings ist nach dem OTA update noch der alte Stand aktiv.
Ein direktes upload der Firmware macht das Board arbeitsunfähig, laut serial log scheint nun der bootloader fehler zu verursachen.
Die erste Firmware läuft nach reflash, neuere nicht.
Hat jemand Erfahrung mit nicht funktionierenden Bootloader beim Heltec V2?
Die Änderungen waren eigentlich minimal.

Wie siehr deine config aus?

Hi, eine Frage zu dem Code. Warum steht da bei der id: wasserzaehler_kg? KG ist doch kein Wert für einen Wasserzähler, dort müsste doch Kubikmeter oder ähnliches stehen.

Moin

Ich hab bewusst, seitdem es läuft, kein Update mehr gemacht, obwohl mein innerer Monk das gerne würde.

Das kg im Name steht bei mir für Kellergeschoss.

Ich habe die Lösung für mein Problem gefunden:
Der Log zeigten den restart Grund:
”A stack overflow in task loopTask has been detected”, was mich zu https://github.com/SzczepanLeon/esphome-components/issues/342 führte.
Für das Ausführen von decodern wird mehr Stack Speicher als normal benötigt.

Mit folgendem Code

esp32:
framework:
type: esp-idf
advanced:
loop_task_stack_size: 32768

funktioniert die Firmware wieder. :wink:

:crayon:by HarryP: Code-/Logzeilen formatiert (bitte immer in </> einbinden)
s.a.: (Neues Update & Features - Hier in der Community 🫶)

Eine neue Info für alle die einen Funk auslesbaren Wasserzähler haben. Endlich habe ich das Heltec Modul bekommen und viele Versche unternommen um das Teil nutzen zu können. Es ist dieses: heltec_wifi_lora_32_V2

Da ich bis hete noch nicht den API Key für die Uhr bekommen hab, wollte ich mal testen ob es auch einfacher geht. Nach vielen Fehlversuchen hab ich das Teil zum laufen bekommen. Die Anleitung dafür ist folgende: In ESPHome Builder das Modul ganz normal “anlernen” , alsoals neues Modul integrieren, danach, wenn alles normal ohne Fehlermeldung durchglaufen ist, in ESPHome bei dem Modul die Edit - Funktion aufrufen und den Begriff Captive Portal löschen. Darunter dann folgendes einfügen:

web_server:
  port: 80
  version: 3

globals:
  - id: frame_counter
    type: int
    restore_value: no
    initial_value: "0"

  - id: last_rssi
    type: int
    restore_value: no
    initial_value: "0"

i2c:
  sda: GPIO4
  scl: GPIO15
  scan: true

font:
  - file: "gfonts://Roboto"
    id: font1
    size: 12

spi:
  clk_pin: GPIO5
  miso_pin: GPIO19
  mosi_pin: GPIO27

external_components:
  - source:
      type: git
      url: https://github.com/IoTLabs-pl/ESPHome-Components
      ref: v1.0.6
    components:
      - wmbus_common
      - wmbus_radio
      - wmbus_meter
    refresh: 0s

wmbus_common:
  drivers: all

wmbus_radio:
  id: radio_component
  radio_type: SX1276
  cs_pin: GPIO18
  reset_pin: GPIO14
  irq_pin: GPIO35
  on_frame:
    - then:
        - lambda: |-
            id(frame_counter) += 1;
            id(last_rssi) = frame->rssi();
        - logger.log:
            level: WARN
            format: "Meter ID: %s"
            args: ["frame->meter_id().c_str()"]
        - logger.log:
            level: WARN
            format: "Frame: https://wmbusmeters.org/analyze/%s"
            args: ["frame->as_hex().c_str()"]

wmbus_meter:
  - id: wasserzaehler_m3
# Seriennummer des eigenen Zählers
    meter_id: 00000000
    type: izar
 

text_sensor:
  - platform: wifi_info
    ip_address: 
      name: "Wasserzaehler ESP IP"
      id: esp32_ip
      entity_category: diagnostic

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: current_status
    name: "Wasserzaehler Status"
    icon: mdi:information-outline

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: timestamp
    name: "Wasserzaehler Letztes Telegramm"
    entity_category: diagnostic

sensor:
  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: total_m3
    id: wasser_total_m3
    name: "Wasserzaehler Gesamtverbrauch"
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    device_class: water
    state_class: total_increasing
    icon: mdi:water

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: target_m3
    name: "Wasserzaehler Stichtagswert"
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    device_class: water
    state_class: total
    icon: mdi:counter

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: flow_temperature_c
    id: wasser_flow_temp
    name: "Wasserzaehler Wassertemperatur"
    unit_of_measurement: "°C"
    accuracy_decimals: 0
    device_class: temperature
    state_class: measurement

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: external_temperature_c
    id: wasser_external_temp
    name: "Wasserzaehler Umgebungstemperatur"
    unit_of_measurement: "°C"
    accuracy_decimals: 0
    device_class: temperature
    state_class: measurement

  - platform: wmbus_meter
    parent_id: wasserzaehler_m3
    field: rssi_dbm
    id: wasser_rssi
    name: "Wasserzaehler RSSI"
    unit_of_measurement: "dBm"
    accuracy_decimals: 0
    device_class: signal_strength
    state_class: measurement
    entity_category: diagnostic

display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    reset_pin: GPIO16
    address: 0x3C
    update_interval: 30s
    lambda: |-
      if (!isnan(id(wasser_total_m3).state)) {
        it.printf(0, 0, id(font1), "Gesamt: %.2f m3", id(wasser_total_m3).state);
      } else {
        it.printf(0, 0, id(font1), "Gesamt: --.-- m3");
      }

      if (!isnan(id(wasser_flow_temp).state)) {
        it.printf(0, 28, id(font1), "Wasser: %.0f C", id(wasser_flow_temp).state);
      } else {
        it.printf(0, 28, id(font1), "Wasser: -- C");
      }

      if (!isnan(id(wasser_external_temp).state)) {
        it.printf(0, 42, id(font1), "Umgbg.: %.0f C", id(wasser_external_temp).state);
      } else {
        it.printf(0, 42, id(font1), "Umgbg.: -- C");
      }  

Danach kann man die Seite Visit aufrufen und die Telegramme sollten zu sehen sein. Auch auf der rechten Seite stehen dann die Daten. Warum noch nicht angezeigt wird letztes Telegramm muss ich noch nachsehen.

Mahlzeit,

langsam drehe ich mit meinem Vorhaben durch. Das Heltec-Board ist da, der Schlüssel für den Zähler auch. Nur will nichts so funktionieren wie es soll.

Mein Board ist die V3 und nutzt den Funkchip SX1262, daher kann ich das IoTLabs-Repository nicht nutzen (die Unterstützen noch nicht den SX1262), sondern nutze das von SzczepanLeon. Ich kann auch alles kompilieren und einspielen, nachdem ich festgestellt habe, dass seine wmbus_meter zwingend die Zeilen “time: - platform: homeassistant” benötigen.

Hier hört es dann aber auch schon (beinahe") auf. Validieren, kompilieren und flashen über esphome funktionieren. ABER:

Das Display gibt nichts aus

Der ESP scheint keine Daten vom Funkchip zu bekommen oder das Modul empfängt nichts.

Ein testweise eingefügter Sensor für den Wlan-Empfang funktioniert. Auf der Konsole im ESPhome bekomme ich auch sinnvolle Ausgaben. Grundlegend scheint das Board also zu funktionieren. Hat einer von euch vielleicht die Muße über meine yaml zu schauen?

external_components:
  - source: github://SzczepanLeon/esphome-components@main
    components: [ wmbus_common, wmbus_radio, wmbus_meter ]

esphome:
  name: heltec
  friendly_name: Heltec

esp32:
  board: heltec_wifi_lora_32_V3
  flash_size: 8MB
  cpu_frequency: 240MHZ
  framework:
    type: esp-idf

# Enable logging
logger:
  id: component_logger
  level: DEBUG
  baud_rate: 115200

# Enable Home Assistant API
api:
  encryption:
    key: "***"

ota:
  - platform: esphome
    password: "***"

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

# sonst Kompilierungsfehler im wmbus_meter
time:
  - platform: homeassistant
    
web_server:
  version: 3

# SPI bus for LoRa radio
spi:
  clk_pin: GPIO9
  mosi_pin: GPIO10
  miso_pin: GPIO11

# I2C for OLED display
i2c:
  sda: GPIO17
  scl: GPIO18
  frequency: 400kHz

wmbus_meter:
  - id: wasserzaehler_kg
    meter_id: ***
    type: multical21
    key: ***
    mode:
      - T1
      - C1

# uart Driver
wmbus_common:
  drivers: all
  
# LoRa Radio Configuration SX1262
wmbus_radio:
  radio_type: SX1262
  cs_pin: GPIO08
  reset_pin: GPIO12
  irq_pin: GPIO14
  busy_pin: GPIO13           # Optional but recommended for proper timing
  rx_gain: BOOSTED           # BOOSTED (default) or POWER_SAVING
  rf_switch: false           # Set to true if DIO2 controls RF switch
  has_tcxo: true             # By default, DIO3 controls an external TCXO
  on_frame:
    - then:
        - logger.log:
            format: "RSSI: %ddBm T: %s (%d)"
            args: [ frame->rssi(), frame->as_hex().c_str(), frame->data().size() ]

# OLED Display
display:
  - platform: ssd1306_i2c
    model: "SSD1306 128x64"
    reset_pin: GPIO21
    address: 0x3C
    lambda: |-
      it.printf(0, 0, id(font1), "Heltec V3");
      it.printf(0, 16, id(font1), "WiFi LoRa 32");
      it.printf(0, 32, id(font1), "%.1f dBm", id(wifi_signal_db).state);

font:
  - file: "gfonts://Roboto"
    id: font1
    size: 14

sensor:
  - platform: wifi_signal
    id: wifi_signal_db
    name: "WiFi Signal"
    update_interval: 60s

  - platform: wmbus_meter
    parent_id: wasserzaehler_kg
    field: total_m3
    id: wasser_total_m3
    name: "Wasserzaehler Gesamtverbrauch"
    unit_of_measurement: "m³"
    accuracy_decimals: 2
    device_class: water
    state_class: total_increasing
    icon: mdi:water

Das Log im ESPhome sieht aus wie folgt:

INFO ESPHome 2026.4.5
INFO Reading configuration /config/esphome/heltec.yaml...
WARNING register_action('wmbus_meter.send_telegram_with_mqtt', ...) is missing the synchronous= parameter. Defaulting to synchronous=False (safe but prevents StringRef optimization). Check the C++ class: use synchronous=False if play_next_() is deferred to a callback, timer, or loop(); use synchronous=True if play_next_() always runs before the initial play/play_complex call returns
INFO Detected timezone 'Europe/Berlin'
INFO Starting log output from 10.69.217.88 using esphome API
INFO Successfully resolved heltec @ 10.69.217.88 in 0.000s
INFO Successfully connected to heltec @ 10.69.217.88 in 0.081s
INFO Successful handshake with heltec @ 10.69.217.88 in 0.045s
[15:09:23.955][I][app:154]: ESPHome version 2026.4.3 compiled on 2026-05-11 12:09:05 +0200
[15:09:23.955][I][app:161]: ESP32 Chip: ESP32-S3 rev0.2, 2 core(s)
[15:09:23.956][C][logger:219]: Logger:
[15:09:23.956][C][logger:219]:   Max Level: DEBUG
[15:09:23.956][C][logger:219]:   Initial Level: DEBUG
[15:09:23.956][C][logger:226]:   Log Baud Rate: 115200
[15:09:23.956][C][logger:226]:   Hardware UART: UART0
[15:09:23.957][C][logger:235]:   Task Log Buffer Size: 768 bytes
[15:09:23.963][C][spi:066]: SPI bus:
[15:09:23.967][C][spi:152]:   CLK Pin: GPIO9
[15:09:23.967][C][spi:152]:   SDI Pin: GPIO11
[15:09:23.967][C][spi:152]:   SDO Pin: GPIO10
[15:09:23.971][C][spi:074]:   Using HW SPI: SPI2_HOST
[15:09:23.987][C][i2c.idf:092]: I2C Bus:
[15:09:23.987][C][i2c.idf:093]:   SDA Pin: GPIO17
[15:09:23.987][C][i2c.idf:093]:   SCL Pin: GPIO18
[15:09:23.987][C][i2c.idf:093]:   Frequency: 400000 Hz
[15:09:23.987][C][i2c.idf:103]:   Recovery: bus successfully recovered
[15:09:23.991][C][i2c.idf:113]: Results from bus scan:
[15:09:23.994][C][i2c.idf:119]: Found device at address 0x3C
[15:09:24.001][C][ssd1306_i2c:022]: I2C SSD1306
[15:09:24.001][C][ssd1306_i2c:022]:   Rotations: 0 °
[15:09:24.001][C][ssd1306_i2c:022]:   Dimensions: 128px x 64px
[15:09:24.017][C][ssd1306_i2c:023]:   Model: SSD1306 128x64
[15:09:24.017][C][ssd1306_i2c:023]:   External VCC: NO
[15:09:24.017][C][ssd1306_i2c:023]:   Flip X: YES
[15:09:24.017][C][ssd1306_i2c:023]:   Flip Y: YES
[15:09:24.017][C][ssd1306_i2c:023]:   Offset X: 0
[15:09:24.017][C][ssd1306_i2c:023]:   Offset Y: 0
[15:09:24.017][C][ssd1306_i2c:023]:   Inverted Color: NO
[15:09:24.017][C][ssd1306_i2c:033]:   Address: 0x3C
[15:09:24.017][C][ssd1306_i2c:152]:   Reset Pin: GPIO21
[15:09:24.026][C][ssd1306_i2c:451]:   Update Interval: 1.0s
[15:09:24.047][C][captive_portal:134]: Captive Portal:
[15:09:24.048][C][wifi:1505]: WiFi:
[15:09:24.048][C][wifi:1505]:   Local MAC: 3C:0F:02:EC:55:F0
[15:09:24.048][C][wifi:1505]:   Connected: YES
[15:09:24.048][C][wifi:1216]:   IP Address: 10.69.217.88
[15:09:24.051][C][wifi:1227]:   SSID: [redacted]
[15:09:24.051][C][wifi:1227]:   BSSID: [redacted]
[15:09:24.051][C][wifi:1227]:   Hostname: 'heltec'
[15:09:24.051][C][wifi:1227]:   Signal strength: -80 dB ▂▄▆█
[15:09:24.051][C][wifi:1227]:   Channel: 1
[15:09:24.051][C][wifi:1227]:   Subnet: 255.255.255.0
[15:09:24.051][C][wifi:1227]:   Gateway: 10.69.217.1
[15:09:24.051][C][wifi:1227]:   DNS1: 10.69.217.1
[15:09:24.051][C][wifi:1227]:   DNS2: 0.0.0.0
[15:09:24.075][C][esphome.ota:071]: Over-The-Air updates:
[15:09:24.075][C][esphome.ota:071]:   Address: heltec.local:3232
[15:09:24.075][C][esphome.ota:071]:   Version: 2
[15:09:24.076][C][esphome.ota:078]:   Password configured
[15:09:24.076][C][safe_mode:026]: Safe Mode:
[15:09:24.076][C][safe_mode:026]:   Successful after: 60s
[15:09:24.076][C][safe_mode:026]:   Invoke after: 10 attempts
[15:09:24.076][C][safe_mode:026]:   Duration: 300s
[15:09:24.089][C][safe_mode:043]:   Bootloader rollback: support unknown
[15:09:24.272][W][safe_mode:058]: OTA rollback detected! Rolled back from partition 'app0'
[15:09:24.275][W][safe_mode:059]: The device reset before the boot was marked successful
[15:09:24.275][C][web_server.ota:256]: Web Server OTA
[15:09:24.294][C][api:235]: Server:
[15:09:24.294][C][api:235]:   Address: heltec.local:6053
[15:09:24.294][C][api:235]:   Listen backlog: 4
[15:09:24.294][C][api:235]:   Max connections: 8
[15:09:24.297][C][api:242]:   Noise encryption: YES
[15:09:24.297][C][wifi_signal.sensor:017]: WiFi Signal 'WiFi Signal'
[15:09:24.297][C][wifi_signal.sensor:017]:   State Class: 'measurement'
[15:09:24.297][C][wifi_signal.sensor:017]:   Unit of Measurement: 'dBm'
[15:09:24.297][C][wifi_signal.sensor:017]:   Accuracy Decimals: 0
[15:09:24.297][C][wifi_signal.sensor:232]:   Device Class: 'signal_strength'
[15:09:24.312][C][mdns:194]: mDNS:
[15:09:24.312][C][mdns:194]:   Hostname: heltec
[15:09:34.945][S][sensor]: 'WiFi Signal' >> -80 dBm

Hab erst mal nur kurz geschaut, du hast ja anscheinend einiges von meinem Code verwendet, auch was das Display angeht.

Da ist aber Vorsicht geboten, die GPIO PINs zwischen dem V2 und V3 sind anders bzw. gab es beim V2 einen Dreher. Deswegen gingen viele der hier verfügbaren Konfigs nicht, erst als ich mich in Ruhe mal in den Pinout Plan des V2 eingelesen und die Konfig daraufhin eingestellt habe.

Das solltest Du mit deinem Board auch machen und mal schauen ob die GPIO Adressen überhaupt zu deinem Board passen.