Startseite » günstige Sensorik mit ESP8266 und ESPHome – eine Conf für alle Geräte

günstige Sensorik mit ESP8266 und ESPHome – eine Conf für alle Geräte

Du willst Temperatur, Luftfeuchte und andere Werte in deinen Zimmern überwachen und auswerten, aber bist nicht bereit, mehr als unbedingt nötig dafür zu bezahlen? Du kannst auch vieles einfach selbst bauen.

Hardware

Aufgrund von Wartbarkeit verwende ich kleine Breadboards und „D1 Mini„-Module mit ESP8266. Damit wär auch schon fast alles über notwendige Hardware gesagt. Alles übrige ist optional.

Sensoren

Es gibt eine Vielzahl von Sensoren. Anbei will ich auflisten, was ich bis zum Datum dieses Artikels schon ausprobiert habe.

  • BME280 / BMP280 / BME680
    • dieser misst Luftfeuchte, Temperatur und Luftdruck und kommuniziert mittels I2C. Nicht zu verwechseln mit dem BMP280, der nur Temperatur und Luftdruck misst.
    • Der BME680 kann neben den Funktionen des BME280 auf noch Luftqualität messen. Er ist ein hervorragender Sensor für jeden Raum.
  • MQ Gassensoren
    • Allgemeines: benötigen mehr Strom, als das D1 Mini mittels USB liefern kann. Eine zusätzliche Stromversorgung mit Netzteil für dein Breadboard ist ggf. nötig. Nach einer Laufzeit von etwa einem Jahr mussten bei mir die MQ-Module getauscht werden, da sie keine guten Messergebnisse mehr lieferten. MQ-Module werden aufgeheizt und Messergenisse erst im warmen Zustand genau. Nach dem Start ist eine Wartezeit von min. 60s notwendig, bevor die Ergebnisse auswertbar sind.
    • MQ2: reagiert auf Konzentrationen von auf LPG, i-Butan, Propan, Methan, Alkohol, Wasserstoff und Rauch und ist damit gut als Alarm-Sensor für Gaslecks und Rauch geeignet
    • MQ3: reagiert auf Konzentrationen von Ethanol, Benzol, Methan, Hexan, LPG und Kohlenmonoxid. Eignet sich gut als Alkoholsensor.
    • MQ4: Methan, Wassersoff, Kohlenmonoxid, Ethanol, LPG und Rauch
    • MQ5: Methan, Wassersoff, Kohlenmonoxid, Ethanol und LPG (wie MQ4 ohne Rauch)
    • MQ6: Methan, Wassersoff, Kohlenmonoxid, Ethanol und LPG (wie MQ5)
    • MQ7: Methan, Wassersoff, Kohlenmonoxid, Ethanol und LPG (wie MQ5, jedoch mit empfindlicherem Messbereich)
    • MQ8: Methan, Wassersoff, Kohlenmonoxid, Ethanol und LPG (wie MQ5)
    • MQ9: Methan, Kohlenmonoxid (empfindlicher als obere Sensoren), LPG
    • MQ131: Ozon
    • MQ135: CO2 (400-600ppm), Kohlenmonoxid, Ethanol, Ammonium, Toluol, Aceton
    • MQ303A: Isobutanol, Wasserstoff, Ethanol
    • MQ309A: Wasserstoff, Methan, Kohlenmonoxid, Ethanol
  • AS3935 Lightning Detector
    • Blitzschlag-Sensor
  • Geiger counter RadiationD v1.1 (CAJOE)
    • Geigerzähler für Radioaktivität
  • Anemometer (Selbstbau)
    • Windstärke
  • SDS011
    • Feinstaub
  • verdrahtete Fensterkontakte
  • Dallas Temperature Sensor (One Wire)
    • geschützter Temperaturfühler (für Wassertemperaturen)
  • EC-Sensor
    • zum Messen elektrischer Leitfähigkeit von Flüssigkeiten. Anwendungsbereich: Nährstoffe in Wasser
  • PH-Sensor
    • PH-Wert von Flüssigkeiten messen. Um die Wasserqualität für Pflanzen zu messen.
  • TEMT6000
    • Helligkeitsmesser

Software

Nun ist es müßig, für all deine Sensoren oder all deine ESPs separate Firmware zu programmieren. Also bin ich mit folgender Strategie daran gegangen:

Alle ESPs müssen die gleiche Firmware verwenden. Sie dürfen sich lediglich im Namen voneinander unterscheiden. Jedes ESP muss also all diese Geräte oben unterstützen und ob es vorhanden ist oder nicht automatisch erkennen.

Alle ESPs werden mit ESPHome konfiguriert. Ich habe ursprünglich die Firmware auch selbst geschrieben. Aber gute offene Arbeit nicht wiederzuverwenden ist dumm. ESPHome spart mir in der Entwicklung enorm Zeit. Außerdem kann ich Firmwareupdates direkt übers WLAN senden.

ESPHome ist Python-basiert. Verwendest du Linux, ist python schon vorinstalliert. Aber auch unter Windows lässt sich Python installieren. Sehr bequem geht das auch über PortablePython, wie ich es verwende.

pip install wheel
pip install esphome

Mehr ist es auch nicht.

Die ESPs kommunizieren ihre Werte mittels MQTT. Um Last zu verringern, lasse ich sie nicht laufend jeden Wert einzeln senden, sondern in regelmäßigen Abständen einen JSON-Datensatz mit allen verfügbaren Daten senden.

ESPHome-Konfiguration für ESP8266

Folgende Konfiguration kannst du verwenden, um relativ allgemein ESP8266 in deinen Räumen zu verteilen und mit jeweils im Kontext sinnvollen Sensoren auszustatten.

Vielleicht benötigst du in einigen Räumen spezialisierte Sensoren, in einigen Anderen aber nur Temperatur und Luftfeuchte. Folgender Konfigurationsvorschlag hilft dir, für alle deine Sensoren nur eine Firmware zu verwenden und damit auch nur einmal zu warten.

#**************************************************************
#* SmartHome ESP8266 Sensorik-Kern
#**************************************************************
#* ESP -- Sensor
#* 
#*  TX1 -- 
#*  RX0 -- 
#*  D1  -- SCL (AS3935 Lightning Detector) && 
#*         [ SCL (BMP280) ] ODER [  SCL (TCA9548A Multiplexer) ]
#*  D2  -- SDA (AS3935 Lightning Detector) &&
#*         [ SDA (BMP280) ] ODER [  SDA (TCA9548A Multiplexer) ]
#*  D3  -- IRQ (AS3935 Lightning Detector)
#*  D4  -- VIN (Geiger Counter) &&
#*         S (Anemometer) [Anemometer Power: middle; Ground: -]
#*         [or other pulse countables]
#*  GND -- GND (AS3935 Lightning Detector)
#*         GND (Geigerzähler)
#*         GND (SDS011)
#*  5V  -- 5V  (Geigerzähler)
#*         5V (SDS011)
#*  
#*  RST -- 
#*  A0  -- [Free to use] [3V3-Level]
#*  D0  -- [Free to use] [3V3-Level]
#*  D5  -- OneWire Data
#*  D6  -- UART TX (SDS011 TX)
#*  D7  -- UART RX (SDS011 RX)
#*  D8  -- [Free to use, with interrupt]
#*  3V3 -- SI  (AS3935 Lightning Detector, activate I2C)
#*         VCC (AS3935 Lightning Detector)
#*         VIN (BME280)
#*  
#*  Fensterkontakte Anschließen:
#*  1 Pin auf 5V, der andere auf den Dx-Eingang
#*
#**************************************************************/
substitutions:
  name: "YOUR DEVICE NAME"
  wifi_ssid: "YOUR SSID"
  wifi_password: "YOUR WIFI PASSWORD"
  mqtt_broker: "YOUR MQTT BROKER"
  mqttPath: "YOUR MQTT PATH FOR THE DEVICE"
  mqtt_user: "YOUR USERNAME"
  mqtt_passwort: "YOUR PASSWORD"
  
esphome:
  name: "${name}"
  includes:
    - arduino_port_expander.h

esp8266:
  board: d1_mini
  #board: nodemcuv2

# Enable logging
logger:
  #tx_buffer_size: 256
  level: NONE

web_server:
  port: 80
  include_internal: true

ota:
#  password: ""

wifi:
  networks:
  - ssid: "${wifi_ssid}"
    password: "${wifi_password}"
  reboot_timeout: 2min
  fast_connect: true
  power_save_mode: light

mqtt:
  broker: "${mqtt_broker}"
  username: "${mqtt_user}"
  password: "${mqtt_password}"
  topic_prefix: "${mqttPath}${name}"
  discovery: false


#https://esphome.io/components/i2c.html
i2c:
  id: i2c_main
  scl: D1
  sda: D2
  scan: true
  frequency: 20kHz

#https://esphome.io/components/sensor/sds011.html
uart:
  rx_pin: D6
  tx_pin: D7
  baud_rate: 9600

#Dallas Temperature Sensor (One Wire)
#https://esphome.io/components/sensor/dallas.html
dallas:
  - pin: D5

#https://esphome.io/components/sensor/as3935.html
as3935_i2c:
  i2c_id: i2c_main
  irq_pin: D3
  indoor: false #a lot of errors otherwise
  noise_level: 2
  spike_rejection: 2
  lightning_threshold: 1
  mask_disturber: false
  div_ratio: 0
  capacitance: 0
  watchdog_threshold: 2

# Arduino Port Extender
# https://esphome.io/cookbook/arduino_port_extender.html
custom_component:
  - id: expander1
    lambda: |-
      auto expander = new ArduinoPortExpander(i2c_main, 0x08, true);
      return {expander};
      
sensor:
  - platform: uptime
    id: uptime_id
    name: "Uptime"
    update_interval: 5s
    internal: true
    
  #Analog Pins
  #https://esphome.io/components/sensor/adc.html
  - platform: adc
    id: a0
    pin: A0
    name: "A0"
    raw: true
    filters:
      - throttle_average: 10s
      - filter_out: NaN
    internal: true

  #https://esphome.io/components/sensor/sds011.html
  - platform: sds011
    pm_2_5:
      name: "Particulate Matter <2.5µm Concentration"
      id: pm25
      filters:
        - filter_out: NaN
      internal: true
    pm_10_0:
      name: "Particulate Matter <10.0µm Concentration"
      id: pm10
      filters:
        - filter_out: NaN
      internal: true
    update_interval: 2min
    
  #Dallas Temperature Sensor (One Wire)
  #https://esphome.io/components/sensor/dallas.html
  - platform: dallas
    id: onewire
    index: 0
    filters:
      - filter_out: NaN
    name: "OneWire"
    internal: true
    
  #as3935 Lightning Sensor
  #https://esphome.io/components/sensor/as3935.html
  - platform: as3935
    lightning_energy:
      id: lightning_energy
      name: "Lightning Energy"
      filters:
        - filter_out: NaN
      internal: true
    distance:
      id: distance_stormfront
      name: "Distance Stormfront"
      filters:
        - filter_out: NaN
      internal: true
      
  #BME/P280
  #https://esphome.io/components/sensor/bme280.html
  - platform: bme280
    i2c_id: i2c_main
    temperature:
      name: "BME-T"
      id: bme_t
      oversampling: 8x
      filters:
        - filter_out: NaN
      internal: true
    pressure:
      name: "BME_P"
      id: bme_p
      oversampling: 8x
      filters:
        - filter_out: NaN
      internal: true
    humidity:
      name: "BME_H"
      id: bme_h
      oversampling: 8x
      filters:
        - filter_out: NaN
      internal: true
    address: 0x76
    update_interval: 10s
  - platform: template
    name: "Absolute Humidity"
    id: absolute_humidity
    lambda: |-
      const float mw = 18.01534;    // molar mass of water g/mol
      const float r = 8.31447215;   // Universal gas constant J/mol/K
      return (6.112 * powf(2.718281828, (17.67 * id(bme_t).state) /
        (id(bme_t).state + 243.5)) * id(bme_h).state * mw) /
        ((273.15 + id(bme_t).state) * r); // in grams/m^3
    accuracy_decimals: 2
    update_interval: 10s
    icon: 'mdi:water'
    unit_of_measurement: 'g/m³'
    filters:
      - filter_out: NaN
    internal: true
  - platform: template
    name: "Dew Point"
    id: dew_point
    lambda: |-
      return (243.5*(log(id(bme_h).state/100)+((17.67*id(bme_t).state)/
      (243.5+id(bme_t).state)))/(17.67-log(id(bme_h).state/100)-
      ((17.67*id(bme_t).state)/(243.5+id(bme_t).state))));
    unit_of_measurement: °C
    accuracy_decimals: 2
    update_interval: 10s
    icon: 'mdi:thermometer-alert'
    filters:
      - filter_out: NaN
    internal: true

  #https://esphome.io/components/sensor/pulse_counter.html
  #https://esphome.io/cookbook/geiger-counter.html
  - platform: pulse_counter
    pin: D4
    name: "D4"
    id: d4
    count_mode:
      rising_edge: DISABLE
      falling_edge: INCREMENT
    internal: true

  - platform: custom
    lambda: |-
      return {ape_analog_input(expander1, 0),  // = A0
              ape_analog_input(expander1, 1),  // = A1
              ape_analog_input(expander1, 2),  // = A2
              ape_analog_input(expander1, 3),  // = A3
              ape_analog_input(expander1, 6),  // = A6
              ape_analog_input(expander1, 7),  // = A7
              ape_analog_input(expander1, 21), // = D2 pulsecounter
              ape_analog_input(expander1, 22)  // = D3 pulsecounter
              };
    sensors:
      - id: arduino_a0
        name: "Arduino A0"
        filters:
          # update every 30s
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_a1
        name: "Arduino A1"
        filters:
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_a2
        name: "Arduino A2"
        filters:
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_a3
        name: "Arduino A3"
        filters:
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_a6
        name: "Arduino A6"
        filters:
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_a7
        name: "Arduino A7"
        filters:
          - throttle_average: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_geiger_counter
        name: "Geiger Counter"
        filters:
          - throttle: 20s
          - filter_out: NaN
        internal: true
      - id: arduino_anemometer
        name: "Anemometer"
        filters:
          - throttle: 20s
          - filter_out: NaN
        internal: true
  - platform: template
    id: arduino_a0_delayed
    name: "Arduino A0 delayed"
    lambda: |-
      if (id(uptime_id).state > 120) { return id(arduino_a0).state; }
      return NAN;
    update_interval: 30s
    filters:
      - filter_out: NaN
    internal: true
  - platform: template
    id: arduino_a1_delayed
    name: "Arduino A1 delayed"
    lambda: |-
      if (id(uptime_id).state > 120) { return id(arduino_a1).state; }
      return NAN;
    update_interval: 30s
    filters:
      - filter_out: NaN
    internal: true
  - platform: template
    id: arduino_a2_delayed
    name: "Arduino A2 delayed"
    lambda: |-
      if (id(uptime_id).state > 120) { return id(arduino_a2).state; }
      return NAN;
    update_interval: 30s
    filters:
      - filter_out: NaN
    internal: true
  - platform: template
    id: arduino_a3_delayed
    name: "Arduino A3 delayed"
    lambda: |-
      if (id(uptime_id).state > 120) { return id(arduino_a3).state; }
      return NAN;
    update_interval: 30s
    filters:
      - filter_out: NaN
    internal: true
      
      

button:
  #https://esphome.io/components/button/restart.html
  - platform: restart
    name: "Restart"
    
binary_sensor:
  #as3935 Lightning Sensor
  #https://esphome.io/components/sensor/as3935.html
  - platform: as3935
    name: "Storm Alert"
    
  #Digital Pins
  - platform: gpio
    pin: D0
    id: d0
    name: "D0"
    internal: true
  - platform: gpio
    pin: D8
    id: d8
    name: "D8"
    internal: true
    
interval:
  - interval: 2min
    then:
      - mqtt.publish:
          topic: "${mqttPath}${name}/json"
          payload: !lambda |-
            DynamicJsonDocument root(2048);
            if( !std::isnan(id(uptime_id).state) )               { root["uptime"]                = lrint(id(uptime_id).state); }
            if( !std::isnan(id(a0).state) )                      { root["a0"]                    = lrint(id(a0).state); }
            if( !std::isnan(id(pm25).state) )                    { root["pm25"]                  = id(pm25).state; }
            if( !std::isnan(id(pm10).state) )                    { root["pm10"]                  = id(pm10).state; }
            if( !std::isnan(id(onewire).state) )                 { root["onewire"]               = id(onewire).state; }
            if( !std::isnan(id(lightning_energy).state) )        { root["lightning_energy"]      = id(lightning_energy).state; }
            if( !std::isnan(id(distance_stormfront).state) )     { root["distance_stormfront"]   = id(distance_stormfront).state; }
            if( !std::isnan(id(bme_t).state) )                   { root["bme_t"]                 = id(bme_t).state; }
            if( !std::isnan(id(bme_p).state) )                   { root["bme_p"]                 = id(bme_p).state; }
            if( !std::isnan(id(bme_h).state) )                   { root["bme_h"]                 = id(bme_h).state; }
            if( !std::isnan(id(absolute_humidity).state) )       { root["absolute_humidity"]     = id(absolute_humidity).state; }
            if( !std::isnan(id(dew_point).state) )               { root["dew_point"]             = id(dew_point).state; }
            if( !std::isnan(id(d4).state) )                      { root["d4"]                    = id(d4).state; }
            if( !std::isnan(id(arduino_a0).state) )              { root["arduino_a0"]            = id(arduino_a0).state; }
            if( !std::isnan(id(arduino_a1).state) )              { root["arduino_a1"]            = id(arduino_a1).state; }
            if( !std::isnan(id(arduino_a2).state) )              { root["arduino_a2"]            = id(arduino_a2).state; }
            if( !std::isnan(id(arduino_a3).state) )              { root["arduino_a3"]            = id(arduino_a3).state; }
            if( !std::isnan(id(arduino_a6).state) )              { root["arduino_a6"]            = id(arduino_a6).state; }
            if( !std::isnan(id(arduino_a7).state) )              { root["arduino_a7"]            = id(arduino_a7).state; }
            if( !std::isnan(id(arduino_geiger_counter).state) )  { root["arduino_geiger_counter"]= id(arduino_geiger_counter).state; }
            if( !std::isnan(id(arduino_anemometer).state) )      { root["arduino_anemometer"]    = id(arduino_anemometer).state; }
            if( !std::isnan(id(arduino_a0_delayed).state) )      { root["arduino_a0_delayed"]    = id(arduino_a0_delayed).state; }
            if( !std::isnan(id(arduino_a1_delayed).state) )      { root["arduino_a1_delayed"]    = id(arduino_a1_delayed).state; }
            if( !std::isnan(id(arduino_a2_delayed).state) )      { root["arduino_a2_delayed"]    = id(arduino_a2_delayed).state; }
            if( !std::isnan(id(arduino_a3_delayed).state) )      { root["arduino_a3_delayed"]    = id(arduino_a3_delayed).state; }
            if( !std::isnan(id(d0).state) )                      { root["d0"]                    = id(d0).state; }
            if( !std::isnan(id(d8).state) )                      { root["d8"]                    = id(d8).state; }
            std::string message;
            serializeJson(root, message);
            return message;
      

2 Kommentare zu „günstige Sensorik mit ESP8266 und ESPHome – eine Conf für alle Geräte“

  1. Pingback: Mini-WLAN-Kamera mit ESPHome und ESP32-Cam - Smarthome DIY - Heimautomatisierung selbst gemacht

  2. Pingback: Ultra-leiser Heizungs-Booster - Smarthome DIY - Heimautomatisierung selbst gemacht

Kommentar verfassen

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

Diese Seite verwendet Akismet, um Spam zu reduzieren. Erfahre, wie deine Kommentardaten verarbeitet werden..

Translate »