Alles Pins am Wemos Mini D1 ausgenutzt

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.
Bitte beachte: Dieser Beitrag ist mehr als 3 Jahre alt. Manche Links, Preise, Produkte und Aussagen sind vielleicht nicht mehr aktuell!

Mein eigentliches Ziel war ein Gerät auf Basis eines Wemos Mini D1 und ESPHome, 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.

Der fertig aufgebaute ESPHome Multisensor mit Display
Der fertig aufgebaute ESPHome Multisensor mit Display

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 84×48 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.

Melde dich zu meinem Newsletter an!

Du kannst dich jederzeit abmelden und ich verspreche: Kein Spam!

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.

Testaufbau auf einem Breadboard
Testaufbau auf einem Breadboard

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.

Der grundlegende Aufbau und Schaltplan meines Multisensors mit Display

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 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 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.

ESPHome direkt aus Home Assistant über den Browser flashen

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 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:

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"
  
Code-Sprache: YAML (yaml)

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.

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:

...Code-Sprache: PHP (php)

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.

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

output:
  - platform: esp8266_pwm
    id: technik_display
    pin: GPIO03
    inverted: trueCode-Sprache: PHP (php)

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.

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

...Code-Sprache: PHP (php)

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.

# 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();Code-Sprache: PHP (php)

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.

# LCN-WIH Außentemperatursensor von HA holen
  - platform: homeassistant
    id: temp_aussen
    entity_id: sensor.wih_temp_aussenCode-Sprache: CSS (css)

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.

# 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'
Code-Sprache: PHP (php)

Hier wieder ein Einsatz für die Lambdas und etwas C-Code zur Berechnung der absoluten Luftfeuchtigkeit etc. Mitlambda: |- 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

# Display-Fonts definieren
font:
  - file: "fonts/hd44780.ttf"
    id: font_a
    size: 8
  - file: "fonts/VCR_OSD_MONO.ttf"
    id: font_b
    size: 20Code-Sprache: PHP (php)

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 7×5 (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ürVCR_OSD_MONO.ttf bei dafont.com entschieden.

Fonts-Verzeichnis unter esphome im VScode Editor

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.

# 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: 22x24Code-Sprache: PHP (php)

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.

Das Hauptmenü meines Multisensors
# 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());

          
Code-Sprache: PHP (php)

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. Mitcontrast: 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.

Vor- und Rücklauf der Fußbodenheitung sowie Außentemperatur

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.

<meta charset="utf-8"/># 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_displayCode-Sprache: YAML (yaml)

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.

Eine Seite mit Systeminformationen

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: https://nachbelichtet.com/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.

Aqara Multisensoren für Temperatur, Luftfeuchtigkeit und Luftdruck
Aqara Multisensoren für Temperatur, Luftfeuchtigkeit und Luftdruck

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 Euro. 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.

Melde dich zu meinem Newsletter an!

Du kannst dich jederzeit abmelden und ich verspreche: Kein Spam!


Die mit Sternchen (*) gekennzeichneten Verweise sind sogenannte Provision-Links. Als Amazon-Partner verdiene ich an qualifizierten Verkäufen.Wenn du auf so einen Verweislink klickst und über diesen Link einkaufst, bekomme ich von deinem Einkauf eine Provision. Für dich verändert sich der Preis nicht und du unterstützt damit meine Arbeit. Preisänderungen und Irrtümer vorbehalten.

5 Kommentare

  1. Excellent setup! You helped me a lot with your code, especially with calc for Apsolute humidity and Dew point. Also mentioning .gif for Nokia display was very helpful. Hope to see more your works somewhere!
    Cheers,

  2. Supercool. Das hat mir mal wirklich weitergeholfen. Ich ersetzte gerade nach und nach meine Netatmo Geräte durch NodeMCU mit SCD30, BME280 samt LED Ring und Buzzer. Display habe ich nicht nicht geplant.

    Meine bescheidenen Anfängerkenntnisse reichen bisher aus, das über ESPHome soweit zum laufen zu bekommen. Die LEDs steuere ich allerdings über Node Red an. Alle gleichzeitig mit verschiedenen Schwellwerten als CO2 Ampel.

    Ich würde gerne die Steuerung der RGB LED (von Blau über grün, gelb, orange auf rot) den 8266 machen lassen. Dann wäre die CO2 Ampel wirklich autark, auch wenn Home Assistant mal nicht läuft.

    Leider hapert es da noch am Know How.

    Trotzdem mal vielen Dank. Ich hab mir von dir die Taupunktberechnung und den Luftdruck aus der Yaml geklaut.

  3. Vielen Dank für dem super Artikel! Ich hätte eine Frage: Wo gibt es solche Federklemmen? Ich finde da bei dem gängigen Anbietern leider wenig, gerade für normales Rastermaß.

  4. Wow! Das ist eine echt gute Anleitung. Ich bin schon am schwarzen Display verzweifelt.
    Vielen Dank und ich freue mich über weitere Anleitungen aus diesem Bereich.

Schreibe einen Kommentar

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