Zum Hauptinhalt springen
Smarthome 22 min

Mein neuer ESPHome Multisensor mit Display für Home Assistant

Mit der ESPHome Plattform kann man in kurzer Zeit umfangreiche Projekte auf Basis der preiswerten ESP8266 und ESP32 Controller umsetzen. Ich zeige in diesem Beitrag eines meiner umfangreicheren ESPHome Projekte.

Mein neuer ESPHome Multisensor mit Display für Home Assistant
Inhaltsverzeichnis

Mein eigentliches Ziel war ein Gerät auf Basis eines Wemos Mini D1 und ESPHome ESPHomeFramework zur Konfiguration von ESP32/ESP8266-Mikrocontrollern, die sich dann automatisch in Home Assistant integrieren. Ideal für DIY-Sensoren und AktorenMehr dazu → , um mehrere 1-Wire Temperatursensoren auslesen zu können, die ich an den Vor- und Rückläufen unserer Heizungsanlage, sowie oben, in der Mitte und unten in unserem Pufferspeicher montiert habe. Die 1-Wire Sensoren vom Typ DS18B20 sind nicht nur preiswert und für diese Aufgabe hinreichend genau, sondern können auch gut verlängert werden und es lassen sich etwa 100 Sensoren an einer einzigen Datenleitung betreiben.

Bei mir geht es hingegen nur um 9 Sensoren: Fußbodenheizung Vorlauf/Rücklauf, Wandheizkörper Vorlauf/Rücklauf, wassergeführter Kamin Vorlauf/Rücklauf und eben die 3 Sensoren im Pufferspeicher.

ESPHome Empfehlungen
−9%
Deal
SONOFF SWV-ZFE Hydro ONE Zigbee Bewässerungscomputer Home Assistant Ready

SONOFF SWV-ZFE Hydro ONE Zigbee Bewässerungscomputer

  • Durable Brass Inlet: Dieser water timer verfügt über einen robusten Messinganschluss für zuverlässige Leistung, sorgt für stabilen Betrieb in Ihrem bewässerungssystem garten oder bewässerungssystem, ideal für den langfristigen Einsatz.
  • Flexible Zeitpläne & Mengensteuerung: Erstellen Sie bis zu sechs Pläne für zeit- oder mengenbasierte Bewässerung. Wetter- und saisonale Anpassungen optimieren Ihr bewässerungssystem garten und sparen Wasser – ein intelligenter sprinkler-timer.
SONOFF NSPanel Pro Gen2 Smart Home Bedienfeld Home Assistant Ready

SONOFF NSPanel Pro Gen2 Smart Home Bedienfeld

  • Multi-App Unterstützung & Home Assistant-Integration: 2GB RAM + 32GB Speicher für flüssige Nutzung mehrerer Drittanbieter-Apps – kein Root erforderlich, einfach über F-Droid als Home Assistant Steuerzentrale nutzbar. All-in-One Smart Panel ersetzt zusätzliche Tablets.
  • Matter Bridge für universelle Kompatibilität: Integriertes Matter Gateway verbindet Zigbee‑ & Wi‑Fi‑Geräte mit Apple Home, Google Home, Alexa & Home Assistant. Vereinheitlicht alle Marken und Protokolle für ein nahtloses Smart Home Erlebnis.
Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Preis inkl. MwSt., zzgl. Versandkosten.

Nun finde ich es aber immer schade, wenn man einen ESP für nur eine Aufgabe verbrät und die anderen Pins ungenutzt lässt. Also warum nicht gleich noch einen BME280 Sensor nutzen, um die Luftfeuchtigkeit und Temperatur im Technikraum zu messen? Dank des eingebauten Luftdrucksensors kann man dann auch gleich noch die absolute Luftfeuchtigkeit und den Taupunkt ausrechnen lassen.

Und wenn man schon die Daten hat, wäre doch ein Display ganz nett? Dazu hatte ich noch einige Nokia 5110 Displays vom PCD 8544 herumliegen. Mit seinen 84x48 Pixeln ist es für diese Aufgabe optimal geeignet. Mit der passenden Schriftart kann man 6 Zeilen zu 14 Zeichen gut lesbar darstellen. Man bekommt hier einfach mehr unter als bei einem zweizeiligen HD44780 Display und kann auch Grafiken einsetzen.

Zudem unterstützt die ESPHome Plattform bei diesem Display auch “Pages”, sodass man sehr einfach zwischen mehreren Seiten und Anzeigen durchblättern und somit jede Menge Informationen übersichtlich darstellen kann. Zum Blättern braucht es aber auch ein Bedienelement und hier habe ich mich für einen einfachen Taster entschieden, denn für einen Endlos-Encoder waren nicht mehr genug Pins vorhanden. Dafür kann das Display über Home Assistant in der Helligkeit gesteuert werden und ein WS2812 LED-Streifen mit 8 RGB-LEDs gibt Auskunft über verschiedene Betriebszustände.

Der Aufbau

Es ist immer eine gute Idee, solche umfangreicheren Projekte zunächst auf einem Breadboard aufzubauen und zu testen, bevor man sich an den Lötkolben wagt. Funktioniert der fliegende Aufbau wie gewünscht, kann man sich an die Umsetzung auf einer Platine machen.

Ich bin von jeher ein Fan von Streifenrasterplatinen. Damit habe ich schon mit 9 Jahren gebastelt, in meiner Ausbildung damit gearbeitet und ich mag die Streifenraster-Philosophie lieber als diese Lötzinnbäche auf Punktraster-Boards. Dabei verfolge auch noch die “reine Lehre”, nämlich dass Drahtbrücken nur in einer Richtung verlaufen. Wemos Mini, Display und BME280 Sensor werden gesteckt, sodass man sie auch schnell mal tauschen könnte.

Für die Anbindung der 9 1-Wire Sensoren habe ich mich für 9-polige Federklemmblöcke entschieden. 3 Stück davon passen nebeneinander auf eine Europlatinenbreite und sind für die dünnen Adern der DS18B20 Sensoren ideal geeignet. Die Datenleitung der Sensoren muss mit einem 4,7 kOhm Widerstand auf High (3,3 Volt Betriebsspannung) gelegt werden und ein 10 uF Elko direkt an der Spannungsversorgung der Sensoren sorgt für einen stabileren Betrieb. Die 1-Wire Sensoren werden immer in der Reihenfolge DATA (Gelb), VCC (Rot), GND (Schwarz) von links nach rechts angeschlossen.

Der komplette Aufbau passte auf weniger als eine halbe Eurokarte und sieht meiner Meinung nach ganz ordentlich aus.

Natürlich könnte man auch eine richtige Platine gestalten und herstellen (lassen). Allerdings werde ich diesen Aufbau nur ein einziges Mal benötigen, sodass der Aufwand hier nicht gerechtfertigt ist und eine sauber aufgebaute Streifenrasterplatine ist ebenso zuverlässig.

Die Programmierung mit ESPHome

ESPHome ist eine grandiose Lösung für eigene Sensoren und Aktoren. Es basiert auf Arduino Bibliotheken, wird aber in der Hauptsache mit YAML YAMLDatenformat (YAML Ain’t Markup Language). Wird in Home Assistant für Konfigurationen, Automationen und Szenen verwendet. Leicht lesbar aber fehleranfällig bei falschen Einrückungen eher konfiguriert, als programmiert. Trotzdem lassen sich auch komplexe Funktionen und Berechnungen damit umsetzen, in dem man mit sogenannten Lambdas auch C-Code innerhalb der YAML Konfiguration einsetzen kann. Was die Programmierung jedoch noch vereinfacht, ist die Integration IntegrationIn Home Assistant: die Anbindung eines Geräts oder Dienstes über ‚Geräte & Dienste’. Jede Integration stellt Entitäten bereit (z.B. Shelly, Zigbee, MCP). Nicht zu verwechseln mit dem allgemeinen IT-BegriffMehr dazu → der ESPHome Plattform direkt in Home Assistant. Hat man die ESPHome Integration in HA, kann man auf Flash-Tools etc. verzichten.

Dazu benötigt man nur den Chrome-Browser oder Edge (vermutlich gehen auch andere Browser, die auf Chromium basieren), denn diese bieten die Web Serial Schnittstelle. Damit verbindet man den ESP per USB mit dem Rechner, an dem man gerade sitzt und kann, öffnet die ESPHome Integration in HA im Browser und kann damit den ESP direkt flashen. Wichtig dabei ist nur, dass die Verbindung zu Home Assistant per SSL erfolgt.

Hat man das erledigt, wird der neu angelegte und geflashte ESP in der ESPHome Oberfläche als “online” angezeigt und kann von da an direkt über die Weboberfläche programmiert werden. Auch die Logs des ESP werden darüber ausgegeben und ESPs können von dort aus auch upgedatet werden. Ich habe früher Tasmota TasmotaOpen-Source-Firmware für ESP8266/ESP32-basierte Smarthome-Geräte (Steckdosen, Lampen, Relais). Ersetzt die Hersteller-Firmware für lokale Steuerung ohne Cloud und ESPeasy eingesetzt, aber ESPHome ist so viel einfacher, eleganter und flexibler in der Nutzung - besonders wenn man mehrere solcher Geräte im Einsatz hat. Die ESPHome Geräte können praktisch von überall aus programmiert werden, wo man Zugriff auf seinen Home Assistant Server hat.

Der Code für den Multisensor mit Display

Hier zunächst einmal der komplette Programmcode meines ESPHome Multisensors:

yaml
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
esphome:
  name: technik
  platform: ESP8266
  board: d1_mini

# Logging an RX/TX ausschalten, da diese Pins als GPIOs verwendet werden
logger:
  baud_rate: 0

#  Home Assistant API aktivieren
api:

ota:
  password: "Dein-OTA-Passwort"

wifi:
  ssid: "DEINE-SSID"
  password: "DEIN-WLAN-PASSWORT"

  # Fallback hotspot (captive portal) aktivieren, falls WLAN fehlt
  ap:
    ssid: "Technik Fallback Hotspot"
    password: "DEIN_PASSWORT"

captive_portal:

# GPIO Übersicht und Verbindungen
# D0 = RST Display
# D1 = I2C SCL BME280
# D2 = I2C SDA BME280
# D3 = DC Display
# D4 = 1Wire Data für DS18B20 Sensoren
# D5 = SPI CLK Display
# D6 = WS2812 LED Data
# D7 = CS/CE Display
# D8 = SPI MOSI Display Din
# RX = Backlight Display = GPIO3
# TX = Switch = GPIO1
# A1 = noch nicht im Einsatz

light:
    # WS2812 LED Infostrip
  - platform: fastled_clockless
    chipset: WS2812B
    id: light_fastled
    pin: D6
    num_leds: 8
    rgb_order: GRB
    name: "Infostrip"
    effects:
      - pulse:
  # LEDs definieren
  - platform: partition
    name: "PL0"
    segments:
      - id: light_fastled
        from: 0
        to: 0
    effects:
      - pulse:
  - platform: partition
    name: "PL1"
    segments:
      - id: light_fastled
        from: 1
        to: 1
    effects:
      - pulse:
  - platform: partition
    name: "PL2"
    segments:
      - id: light_fastled
        from: 2
        to: 2
    effects:
      - pulse:
  - platform: partition
    name: "PL3"
    segments:
      - id: light_fastled
        from: 3
        to: 3
    effects:
      - pulse:
  - platform: partition
    name: "PL4"
    segments:
      - id: light_fastled
        from: 4
        to: 4
    effects:
      - pulse:
  - platform: partition
    name: "PL5"
    segments:
      - id: light_fastled
        from: 5
        to: 5
    effects:
      - pulse:
  - platform: partition
    name: "PL6"
    segments:
      - id: light_fastled
        from: 6
        to: 6
    effects:
      - pulse:
  - platform: partition
    name: "PL7"
    segments:
      - id: light_fastled
        from: 7
        to: 7
    effects:
      - pulse:

# Displaybeleuchtung
  - platform: monochromatic
    name: "Technik Display"
    output: technik_display

output:
  - platform: esp8266_pwm
    id: technik_display
    pin: GPIO03
    inverted: true

# 1-Wire Sensoren
dallas:
  - pin: D4

# I2C-Bus für BME280
i2c:
  sda: D2
  scl: D1
  scan: true
  id: bus_a

# SPI-Bus für Display
spi:
  clk_pin: D5
  mosi_pin: D8

# Statusdaten für den ESP holen
text_sensor:
  - platform: template
    name: Uptime Human Readable
    id: uptime_human
    icon: mdi:clock-start

  - platform: wifi_info
    ip_address:
      name: ESP IP Address
      id: my_ip
    ssid:
      name: ESP Connected SSID
      id: my_SSID
    bssid:
      name: ESP Connected BSSID
    mac_address:
      name: ESP Mac Wifi Address
      id: my_mac

# 1-Wire Sensoren einbinden
sensor:
  - platform: dallas
    address: 0x42030297792CC028
    resolution: 12
    name: "Vorlauf FBH"
    id: fbh_vl

  - platform: dallas
    address: 0x16030297794F1A28
    resolution: 12
    name: "Rücklauf FBH"
    id: fbh_rl

# BME280 Sensor
  - platform: bme280
    temperature:
      name: "ESP1 Technik Temperatur"
      id: bme280_temperature
      oversampling: 16x
    pressure:
      name: "ESP1 Technik Luftdruck"
      id: bme280_pressure
    humidity:
      name: "ESP1 Technik Feuchte"
      id: bme280_humidity
    address: 0x76
    update_interval: 120s #längere Updateintervalle verhindern Erwärmung des Sensors
    iir_filter: 4x # Messwerte glätten

# WLAN-Signalstärke holen
  - platform: wifi_signal
    name: "Technik ESP Wifi Signal"
    update_interval: 30s
    id: wlan_signal

# Uptime des ESP auslesen und menschenlesbar formatieren
  - platform: uptime
    name: Uptime ESP Technik
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? String(days) + "d " : "") +
                (hours ? String(hours) + "h " : "") +
                (minutes ? String(minutes) + "m " : "") +
                (String(seconds) + "s")
              ).c_str();

# LCN-WIH Außentemperatursensor von HA holen
  - platform: homeassistant
    id: temp_aussen
    entity_id: sensor.wih_temp_aussen

# Luftdruck mit Höhenkorrektur auf 516 Meter berechnen

  - platform: template
    name: "Technik Luftdruck"
    update_interval: 60s
    lambda: |-
      const float STANDARD_ALTITUDE = 516; // in Meter
      return id(bme280_pressure).state / powf(1 - ((0.0065 * STANDARD_ALTITUDE) /
        (id(bme280_temperature).state + (0.0065 * STANDARD_ALTITUDE) + 273.15)), 5.257); // in hPa
    unit_of_measurement: 'hPa'

# Absolute Luftfeuchtigkeit in g/m3 berechnen
  - platform: template
    name: "Technik Absolute Luftfeuchtigkeit"
    lambda: |-
      const float mw = 18.01528;    // molare Masse von Wasser g/mol
      const float r = 8.31447215;   // universelle Gas-Konstante J/mol/K
      return (6.112 * powf(2.718281828, (17.67 * id(bme280_temperature).state) /
        (id(bme280_temperature).state + 243.5)) * id(bme280_humidity).state * mw) /
        ((273.15 + id(bme280_temperature).state) * r); // in Gramm/m^3
    accuracy_decimals: 2
    update_interval: 60s
    icon: 'mdi:water'
    unit_of_measurement: 'g/m³'
    id: wsabs

# Taupunkt berechnen
  - platform: template
    name: "Technik Taupunkt"
    lambda:
      return (243.5*(log(id(bme280_humidity).state/100)+((17.67*id(bme280_temperature).state)/
      (243.5+id(bme280_temperature).state)))/(17.67-log(id(bme280_humidity).state/100)-
      ((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state))));
    unit_of_measurement: °C
    icon: 'mdi:thermometer-alert'

# Uhrzeit von Home Assistant holen (für Uptime-Berechnung)
time:
  - platform: homeassistant
    id: homeassistant_time

# Display-Fonts definieren
font:
  - file: "fonts/hd44780.ttf"
    id: font_a
    size: 8
  - file: "fonts/VCR_OSD_MONO.ttf"
    id: font_b
    size: 20

# Icons definieren
image:
  - file: "icons/water-percent.gif"
    id: water_percent
  - file: "icons/thermometer.gif"
    id: thermometer
  - file: "icons/sm_sad.gif"
    id: sad
    resize: 22x24
  - file: "icons/sm_neutral.gif"
    id: neutral
    resize: 22x24
  - file: "icons/sm_happy.gif"
    id: happy
    resize: 22x24

# Nokia 5110 Display
display:
  - platform: pcd8544
    id: my_display
    reset_pin: D0
    cs_pin: D7
    dc_pin: D3
    contrast: 0x3f # Wichtig: zu hohe Werte zeigen schwarzes Display!

    # Mehrere Display-Seiten
    pages:
      - id: page1
        lambda: |-
          it.image(0, 0, id(thermometer));
          it.printf(14, 0, id(font_b), "%.1f°C", id(bme280_temperature).state);
          it.image(0, 20, id(water_percent));
          it.printf(14, 20, id(font_b), "%.0f", id(bme280_humidity).state);
          it.printf(38, 22, id(font_a), "%.1f", id(wsabs).state);
          it.print(38, 30, id(font_a), "g/m3");
          if ((id(bme280_humidity).state <= 55)) {
            it.image(63, 18, id(happy));
          }

          if ((id(bme280_humidity).state >= 56) and (id(bme280_humidity).state <= 68))    {
            it.image(63, 18, id(neutral));
          }
          if ((id(bme280_humidity).state >= 69))    {
            it.image(63, 18, id(sad));
          }

          it.strftime(0, 40, id(font_a), "%H:%M-%d.%m.%y", id(homeassistant_time).now());

      - id: page2
        lambda: |-
          it.print(0, 0, id(font_a), "   HK1 - FBH");
          it.print(0, 6, id(font_a), "______________");
          it.printf(0, 18, id(font_a), "FBH VL:%.1f C", id(fbh_vl).state);
          it.printf(0, 28, id(font_a), "FBH RL:%.1f C", id(fbh_rl).state);
          it.printf(0, 38, id(font_a), "Aussen:%.1f C", id(temp_aussen).state);

      - id: page3
        lambda: |-
          it.print(0, 0, id(font_a), "  HK2 - Wand");
          it.print(0, 6, id(font_a), "______________");
          it.printf(0, 18, id(font_a), "HK VL:%.1f C", id(fbh_vl).state);
          it.printf(0, 28, id(font_a), "HK RL:%.1f C", id(fbh_rl).state);
          it.printf(0, 38, id(font_a), "Aussen:%.1f C", id(temp_aussen).state);
      - id: page4
        lambda: |-
          it.print(0, 0, id(font_a), " HK3 - Kamin");
          it.print(0, 6, id(font_a), "______________");
          it.printf(0, 18, id(font_a), "KAM VL:%.1f C", id(fbh_vl).state);
          it.printf(0, 28, id(font_a), "KAM RL:%.1f C", id(fbh_rl).state);
          it.printf(0, 38, id(font_a), "Aussen:%.1f C", id(temp_aussen).state);
      - id: page5
        lambda: |-
          it.print(0, 0, id(font_a), "    Puffer");
          it.print(0, 6, id(font_a), "______________");
          it.printf(0, 18, id(font_a), "Oben:  %.1f C", id(fbh_vl).state);
          it.printf(0, 28, id(font_a), "Mitte: %.1f C", id(fbh_rl).state);
          it.printf(0, 38, id(font_a), "Unten: %.1f C", id(fbh_rl).state);

      - id: page6
        lambda: |-
          it.print(0, 0, id(font_a), "Name: Technik");
          it.print(0, 6, id(font_a), "______________");
          it.printf(0, 16, id(font_a), "Up:%s", id(uptime_human).state.c_str());
          it.printf(0, 24, id(font_a), "%s", id(my_ip).state.c_str());
          it.printf(0, 32, id(font_a), "%s", id(my_mac).state.c_str());
          it.printf(0, 40, id(font_a), "WLAN: %.0f dBm", id(wlan_signal).state);

# Taster zum wechseln der Display-Pages
binary_sensor:
  - platform: gpio
    pin:
      number: GPIO01
      mode: INPUT_PULLUP # internen Pullup einschalten - spart einen Widerstand :-)
      inverted: True
    name: "Technik Taster"
    on_press:
      then:
        - display.page.show_next: my_display
        - component.update: my_display

# Status des ESP an HA mitteilen (verbunden oder nicht)
  - platform: status
    name: "ESP Technik"

Da ich die serielle Schnittstelle des ESP nicht benötige, schalte ich das Loggen dafür in Zeile 8 aus. Damit kann ich die beiden Pins als normale GPIOs nutzen.

Die wichtigsten Konfigurationen und Funktionen im Detail

Viel im Code sind Standardfunktionen, die man ganz einfach nachlesen kann. Ich möchte hier die interessanteren Teile kurz einzeln erklären.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
light:
    # WS2812 LED Infostrip
  - platform: fastled_clockless
    chipset: WS2812B
    id: light_fastled
    pin: D6
    num_leds: 8
    rgb_order: GRB
    name: "Infostrip"
    effects:
      - pulse:
  # LEDs definieren
  - platform: partition
    name: "PL0"
    segments:
      - id: light_fastled
        from: 0
        to: 0
    effects:
      - pulse:

...

Hier wird der WS2812 LED-Stripe mit 8 RGB-LEDs eingerichtet. Für jedes LED wird die Platform: partition angelegt. Damit kann jedes LED später individuell angesprochen werden. Aktuell ist dafür noch kein weiterer Code enthalten und die Nutzung dieser Funktion, wird erst später hinzukommen. So könnte man darüber etwa anzeigen, wenn man Lüften sollte oder wenn der Brenner der Heizung aktiv ist.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Displaybeleuchtung
  - platform: monochromatic
    name: "Technik Display"
    output: technik_display

output:
  - platform: esp8266_pwm
    id: technik_display
    pin: GPIO03
    inverted: true

Die Displaybeleuchtung ist an dem früheren RX-Pin angeschlossen, der nun auf GPIO03 hört. Damit kann das Display von HA aus gesteuert und gedimmt werden.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
dallas:
  - pin: D4

# 1-Wire Sensoren einbinden
sensor:
  - platform: dallas
    address: 0x42030297792CC028
    resolution: 12
    name: "Vorlauf FBH"
    id: fbh_vl

  - platform: dallas
    address: 0x16030297794F1A28
    resolution: 12
    name: "Rücklauf FBH"
    id: fbh_rl

...

Die DS18D20 1-Wire Temperatursensoren werden hier definiert. Dazu benötigt man die eindeutige ID des jeweiligen Sensors. Entweder schließt man dazu jeden Sensor einmal einzeln an, notiert sich die ID und markiert sie am Sensor oder man schließt alle Sensoren gleichzeitig an, erwärmt jeden Sensor kurz und sieht im Log nach, welche ID gerade eine höhere Temperatur anzeigt.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Uptime des ESP auslesen und menschenlesbar formatieren
  - platform: uptime
    name: Uptime ESP Technik
    id: uptime_sensor
    update_interval: 60s
    on_raw_value:
      then:
        - text_sensor.template.publish:
            id: uptime_human
            state: !lambda |-
              int seconds = round(id(uptime_sensor).raw_state);
              int days = seconds / (24 * 3600);
              seconds = seconds % (24 * 3600);
              int hours = seconds / 3600;
              seconds = seconds % 3600;
              int minutes = seconds /  60;
              seconds = seconds % 60;
              return (
                (days ? String(days) + "d " : "") +
                (hours ? String(hours) + "h " : "") +
                (minutes ? String(minutes) + "m " : "") +
                (String(seconds) + "s")
              ).c_str();

Hier wird die Zeit, die der ESP online ist, in ein menschenlesbares Format umgewandelt, sodass man es später schön im Display und in der HA Oberfläche anzeigen lassen kann.

yaml
1
2
3
4
# LCN-WIH Außentemperatursensor von HA holen
  - platform: homeassistant
    id: temp_aussen
    entity_id: sensor.wih_temp_aussen

Natürlich kann man auch Sensoren aus HA holen. Hier lese ich den Außentemperatursensor meiner LCN-WIH Wetterstation aus, um die Werte im Display anzeigen zu lassen.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# Luftdruck mit Höhenkorrektur auf 516 Meter berechnen

  - platform: template
    name: "Technik Luftdruck"
    update_interval: 60s
    lambda: |-
      const float STANDARD_ALTITUDE = 516; // Höhe über Null am Einsatzort in Meter
      return id(bme280_pressure).state / powf(1 - ((0.0065 * STANDARD_ALTITUDE) /
        (id(bme280_temperature).state + (0.0065 * STANDARD_ALTITUDE) + 273.15)), 5.257); // in hPa
    unit_of_measurement: 'hPa'

# Absolute Luftfeuchtigkeit in g/m3 berechnen
  - platform: template
    name: "Technik Absolute Luftfeuchtigkeit"
    lambda: |-
      const float mw = 18.01528;    // molare Masse von Wasser g/mol
      const float r = 8.31447215;   // universelle Gas-Konstante J/mol/K
      return (6.112 * powf(2.718281828, (17.67 * id(bme280_temperature).state) /
        (id(bme280_temperature).state + 243.5)) * id(bme280_humidity).state * mw) /
        ((273.15 + id(bme280_temperature).state) * r); // in Gramm/m^3
    accuracy_decimals: 2
    update_interval: 60s
    icon: 'mdi:water'
    unit_of_measurement: 'g/m³'
    id: wsabs

# Taupunkt berechnen
  - platform: template
    name: "Technik Taupunkt"
    lambda:
      return (243.5*(log(id(bme280_humidity).state/100)+((17.67*id(bme280_temperature).state)/
      (243.5+id(bme280_temperature).state)))/(17.67-log(id(bme280_humidity).state/100)-
      ((17.67*id(bme280_temperature).state)/(243.5+id(bme280_temperature).state))));
    unit_of_measurement: °C
    icon: 'mdi:thermometer-alert'

Hier wieder ein Einsatz für die Lambdas und etwas C-Code zur Berechnung der absoluten Luftfeuchtigkeit etc. Mit lambda: |- teilt man mit, dass alles, was danach kommt und eingerückt ist, als C-Code interpretiert wird. Die Formeln dafür stammen aus dem Internet. Beim Luftdruck ist darauf zu achten, dass man die richtige Höhenangabe einträgt. Der Luftdruck, den der BME280 direkt ausgibt, ist nicht höhenkompensiert.

Das Nokia 5110 Display - Fonts, Icons und Darstellung

yaml
1
2
3
4
5
6
7
8
# Display-Fonts definieren
font:
  - file: "fonts/hd44780.ttf"
    id: font_a
    size: 8
  - file: "fonts/VCR_OSD_MONO.ttf"
    id: font_b
    size: 20

Für das Nokia 5110 PCD8544 Display müssen Schriftarten definiert werden. Grundsätzlich kann man jeden TrueType-Font (.ttf) dafür nutzen. Allerdings klappt das nur für große Texte und auch hier sieht nicht jeder Font gut aus. Für die kleinstmögliche leserliche Darstellung sind 7x5 (Pixel) Fonts ideal. So findet man z. B. bei wfonts.com die hd44780.ttf Schriftart, welche klar lesbar ist und 6 Zeilen zu 14 Zeichen erlaubt. Für großen Text habe ich mich für VCR_OSD_MONO.ttf bei dafont.com entschieden.

Die Fonts lädt man in ein Verzeichnis unterhalb des esphome Verzeichnisses. Das geht ganz einfach per VSCode Integration in HA. Einfach einen Ordner fonts (der Name ist egal) anlegen, rechten Mausklick darauf und Upload auswählen.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
# Icons definieren
image:
  - file: "icons/water-percent.gif"
    id: water_percent
  - file: "icons/thermometer.gif"
    id: thermometer
  - file: "icons/sm_sad.gif"
    id: sad
    resize: 22x24
  - file: "icons/sm_neutral.gif"
    id: neutral
    resize: 22x24
  - file: "icons/sm_happy.gif"
    id: happy
    resize: 22x24

Ich habe für den Haupt-Screen meines ESP einige Icons eingebaut. Ein Thermometer, Wassertropfen und 3 Smilies, die Auskunft über die Luftfeuchtigkeit geben. Auch hier legt man sich wieder ein Verzeichnis unterhalb von esphome an - in meinem Fall icons - und lädt die entsprechenden Grafiken hoch. Ich hatte allerdings nur mit GIF-Dateien Erfolg. PNGs, egal ob mit oder ohne Alphakanal, zeigten immer nur einen schwarzen Block an.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Nokia 5110 Display
display:
  - platform: pcd8544
    id: my_display
    reset_pin: D0
    cs_pin: D7
    dc_pin: D3
    contrast: 0x3f # Wichtig: zu hohe Werte zeigen schwarzes Display!

    # Mehrere Display-Seiten
    pages:
      - id: page1
        lambda: |-
          it.image(0, 0, id(thermometer));
          it.printf(14, 0, id(font_b), "%.1f°C", id(bme280_temperature).state);
          it.image(0, 20, id(water_percent));
          it.printf(14, 20, id(font_b), "%.0f", id(bme280_humidity).state);
          it.printf(38, 22, id(font_a), "%.1f", id(wsabs).state);
          it.print(38, 30, id(font_a), "g/m3");
          if ((id(bme280_humidity).state <= 55)) {
            it.image(63, 18, id(happy));
          }

          if ((id(bme280_humidity).state >= 56) and (id(bme280_humidity).state <= 68))    {
            it.image(63, 18, id(neutral));
          }
          if ((id(bme280_humidity).state >= 69))    {
            it.image(63, 18, id(sad));
          }

          it.strftime(0, 40, id(font_a), "%H:%M-%d.%m.%y", id(homeassistant_time).now());

Nun wird das Display konfiguriert. Wichtig ist die contrast Einstellung. In der offiziellen Dokumentation wird hier der Wert contrast: 0x7f angegeben. Damit erhält man aber nur ein komplett schwarz gefülltes Display und glaubt erst einmal an einen Verkabelungsfehler. Tatsächlich handelt es sich aber nur um einen viel zu hohen Kontrast. Mit contrast: 0x3f wird die Schrift meiner Ansicht nach ideal dargestellt.

Bei den print- und image-Anweisungen bedeutet printf(14,20 … ), dass die Schrift 14 Pixel nach rechts und 20 Pixel nach unten, ausgehend von der linken oberen Ecke der Anzeige dargestellt werden soll.

Mit pages: und id: page1 nutze ich die Möglichkeit, Inhalte auf mehrere Displayseiten zu verteilen. Page1 ist mein Hauptscreen, der beim Start erscheint und Raumtemperatur sowie Luftfeuchtigkeit groß mit einem Icon davor darstellt. Neben der Luftfeuchtigkeit zeige ich noch kleiner die absolute Luftfeuchtigkeit an. Daneben, je nach Luftfeuchte, einen passenden Smilie und darunter die aktuelle Uhrzeit und das Datum.

Mit it.printf(14, 0, id(font_b), “%.1f°C”, id(bme280_temperature).state); wird die Temperatur im C-Stil auf eine Nachkommastelle formatiert (%.1f).

Mit den if-Anweisungen werden die Smilies nach einem Schwellwert geändert und zu guter Letzt die Uhrzeit angezeigt.

yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# Taster zum wechseln der Display-Pages
binary_sensor:
  - platform: gpio
    pin:
      number: GPIO01
      mode: INPUT_PULLUP # internen Pullup einschalten - spart einen Widerstand :-)
      inverted: True
    name: "Technik Taster"
    on_press:
      then:
        - display.page.show_next: my_display
        - component.update: my_display

Hier wird der Taster am GPIO01 abgefragt. Mit INPUT_PULLUP kann man den internen Pullup aktivieren und man spart sich damit die externe Beschaltung mit einem Widerstand, der für einen definierten Logikpegel sorgt. Wenn der Taster gedrückt wird, sorgen die beiden letzten Zeilen dafür, dass die Seiten (Pages) nacheinander durchgeschaltet und anzeigt werden.

Tipps

Zum BME280 Sensor gibt es im Netz viele Diskussionen. Ursprünglich wurde der Sensor von Bosch entwickelt. Geht man nach dem Datenblatt der Firma Bosch , müssen die Sensoren in einem speziellen Prozess verlötet und dann bei definierten Bedingungen getrocknet und eingebrannt werden.

Bei den günstigen China-Sensoren kann man nicht wirklich sicher sagen, ob es sich um Bosch-Typen oder Kopien handelt und der Verarbeitungsprozess wird ganz sicher nicht eingehalten. Daher wird immer davon gesprochen, dass diese China-Varianten ungenau wären. Achtet man jedoch darauf, dass der Sensor möglichst weit von Wärmequellen wie dem ESP8266 entfernt montiert wird und die Zuleitungen möglichst lang (und dünn) sind, liefert der BME280 nach meinen Erfahrungen und auch Vergleichsmessungen mit gesättigten Salzlösungen (siehe auch: /guenstige-xiaomi-mijia-ble-sensoren-mit-home-assistant-nutzen/ ) sehr genaue Werte.

Häufig ist auch die Erwartung an einen Messwert schlichtweg Unsinn. Es geht nicht darum, den besten und genausten Messwert zu erhalten, sondern den Messwert, der zur Anwendung passt. Die Lufttemperatur unterscheidet sich selbst in kleinen Räumen und je nach Messhöhe und Position im Raum nicht selten um 2 °C. Gleiches gilt für die Luftfeuchtigkeit. Sind also 22,7 °C richtiger als 23,4 °C? Nein! Denn verzichtet man auf die Nachkommastelle, kämen für diesen Einsatz vollkommen ausreichende 23 Grad als Messwert heraus.

Doch selbst hier sagt der Messwert noch nichts darüber aus, ob man sich bei dieser Temperatur auch wohlfühlt. Das Wohlfühlklima wird über die Temperatur, die Luftfeuchtigkeit und die Luftbewegung definiert. So nimmt die gefühlte Temperatur bei steigender Luftfeuchtigkeit zu und umgekehrt. 21 °C Lufttemperatur fühlen sich bei 55 % rel. Luftfeuchte schon wie 23 °C und bei 75 % wie 26 °C an. Daher fühlt sich auch recht warme, aber trockene Heizungsluft im Winter gar nicht so muggelich an, wie man oft erwarten würde. Kommt dann auch noch eine leichte Luftbewegung hinzu, können sich 23 °C wie 20 °C anfühlen.

Die DS18B20 Sensoren sollte man alle in einen Eimer Wasser mit hängen, dessen Temperatur mit einem vertrauenswürdigen Thermometer gemessen hat. So kann man die Sensoren nach Genauigkeit und Abweichung selektieren. Bei der Messung von Vor- und Rücklauf einer Niedrigtemperaturheizungen, macht 1 °C Abweichung schon etwas aus. Wenn dann die Rücklauftemperatur höher ist als der Vorlauf, kann man keine vernünftige Steuerung darauf aufbauen. Daher sollten beide Sensoren die gleichen relativen Messwerte liefern. Abweichungen vom tatsächlichen Messwert kann man dann ggf. auch noch per Software kompensieren.

Bauteile und Kosten

Das gesamte Projekt kostet weniger als 10 Euro. Die 5110 Displays bekommt man für weniger als 2 Euro bei diversen China-Versendern (auf die neuen Regelungen beim Versand aus China achten - siehe HIER !). Ein ESP Wemos Mini kostet auch nur 2 Euro. Bestellt man ihn aus Deutschland, muss man etwa 4,50 Euro rechnen. BME280 Sensor , Platine und die Klemmen sind für weitere 3-4 Euro zu haben.

Nicht in den Kosten enthalten sind die DS18B20 1-Wire Sensoren. Hier bekommt man 10 Stück für unter 20 Eur o. Auf Basis dieses Grund-Codes kann man viele universelle Smarthome -Sensoren mit Display günstig herstellen. Da man sowohl einen I2C- als auch einen SPI-Bus zur Verfügung hat, kann man problemlos noch weitere Sensoren einbinden und damit den Funktionsumfang nach den eigenen Wünschen und Anforderungen erweitern.


Hallo! Ich bin Markus – der Fricklr. ⚡ Elektrotechniker, 🎵 Musiker (Bass, Gitarre, Keyboard) und seit über 30 Jahren beruflich „irgendwas mit Internet“ 🌐 – inzwischen auch mit KI 🤖 – ein 55-jähriger Generalist aus Bayern.

Dein Feedback hilft mir, noch interessantere Beiträge zu schreiben. Bei „Ja“ findest du danach eine kleine Option, meine Arbeit zu unterstützen.

Verwandte Themen entdecken

Inhaltskarte →
Verbindungen als Liste anzeigen
💡 Ziehen, hover & klicken zum Entdecken
Größere Punkte sind relevanter

Was sagst du dazu? Kommentiere jetzt!

Teile deine Gedanken, Fragen oder Erfahrungen mit der Community.

Mit diesem Symbol gekennzeichnete Links sind Affiliate-Links (Werbung). Als Amazon-Partner verdiene ich an qualifizierten Verkäufen. Für dich entstehen keine Mehrkosten.