DIY smarte LED-Kerzen mit ESP32 & ESPHome

100 % lokal, ohne Cloud, (ohne Kabel) – und mit maximalem WAF ( Wife Acceptance Factor :-))

Ich habe in unserem Haus wirklich vieles smart gemacht: Stromzähler, Gaszähler, Automationen, Dashboards. Technik gibt es bei mir genug.
Und trotzdem kam irgendwann diese eine Antwort von meiner Frau, die mich ehrlicherweise mehr herausgefordert hat als jeder Zählerschrank:

„Was wünschst du dir eigentlich mal als smartes Projekt?“

Die Antwort kam direkt hinterher: smarte Kerzen.
Aber natürlich mit Bedingungen. Keine Kabel. Keine App. Keine Cloud. Und bitte nicht hässlich und sie soll selbstständig angehen wenn es dunkel wird.

Damit war klar: Das wird kein klassisches Bastelprojekt, sondern eine echte Smart-Home-Challenge mit WAF-Prüfung. Und genau deshalb ist dieses Projekt so spannend – weil es Technik, Design und Alltag zusammenbringt.


Warum smarte Kerzen ein unterschätztes Smart-Home-Projekt sind

Smarte Kerzen sind kein „Must-Have“. Aber sie sind ein perfekter Test, ob dein Smart Home wirklich alltagstauglich ist. Denn eine Kerze steht sichtbar im Raum, sie beeinflusst Atmosphäre und Stimmung – und sie darf nicht nerven.

Was ich wollte, war keine weitere Insellösung, sondern eine Kerze, die:

  • automatisch reagiert, ohne dass jemand eingreifen muss
  • vollständig lokal funktioniert
  • auch dann sinnvoll arbeitet, wenn Home Assistant gerade nichts tut
  • aussieht wie eine ganz normale Kerze

Gerade bei Lichtprojekten merkt man schnell, wie viele Fertigprodukte an genau diesen Punkten scheitern. Entweder hängt alles an einer Cloud, oder die Timer sind so starr, dass man sie nach zwei Wochen wieder deaktiviert.


Home Assistant als Orchester – die Kerze als eigenständiges Instrument

Ich nutze Home Assistant als Zentrale meines Smart Homes. Für mich ist das wie ein Orchester: Home Assistant ist der Dirigent, Sensoren und Aktoren sind die Instrumente. Aber ein gutes Instrument sollte im Zweifel auch solo spielen können.

Genau deshalb habe ich bei diesem Projekt möglichst viel Logik direkt auf den ESP32 gepackt. Nicht, weil Home Assistant das nicht könnte – sondern weil ich Geräte mag, die eigenständig funktionieren.

Konkret bedeutet das:

  • Die Kerze entscheidet selbst, ob sie leuchten darf
  • Home Assistant liefert nur zusätzliche Informationen (Uhrzeit, Anwesenheit)
  • Fällt Home Assistant kurz aus, bleibt die Kerze trotzdem logisch

Das ist für mich der Unterschied zwischen „smart angebunden“ und wirklich smart.

Den Code habe ich hier weiter unten eingefügt.


Hardware-Setup: Was steckt in der smarten Kerze?

Als Basis habe ich ganz normale LED-Kerzen aus dem Gartenmarkt verwendet. Preislich lagen die bei etwa 10 Euro – und sie sehen optisch völlig okay aus. Wichtig war vor allem: genug Platz im Inneren.

Die wichtigsten Komponenten im Überblick:

Sämtliche mit „*“ markierten Links hängen mit Werbung oder Amazon Partnerprogrammen zusammen. Sie dienen dir als potentiellen Käufer als Orientierung und verweisen explizit auf bestimmte Produkte. Sofern diese Links genutzt werden, kann im Falle einer Kaufentscheidung eine Provision an mich ausgeschüttet werden. Bei einem Kauf über diese Links fallen natürlich keine Mehrkosten an. Danke!!

Der ESP32C3 ist für solche Projekte ideal, weil er klein, stromsparend und perfekt für Deep-Sleep-Anwendungen geeignet ist. Gerade wenn du ohne Kabel arbeiten willst, ist der Stromverbrauch entscheidend.


Helligkeit erkennen: grob oder präzise?

Damit die Kerze weiß, wann sie angehen soll, braucht sie einen Helligkeitssensor. Hier hast du grundsätzlich zwei Wege:

  • Fotowiderstand
    • sehr einfach
    • extrem günstig
    • ausreichend für „hell vs. dunkel“
  • Lux-Sensor (z. B. BH1750)
    • genaue Lux-Werte
    • reproduzierbare Schaltschwellen
    • ideal für saubere Logik
    • kostet aber viel Strom.

Ich habe mich für einen Lux-Sensor entschieden, weil ich genau steuern wollte, ab welchem Lichtniveau die Kerze reagiert. Für eine Kerze ist das zwar fast schon Overkill – aber genau das macht solche Projekte sauber.


Schalten der LED: warum ein MOSFET Pflicht ist

Die LED in der Kerze wird nicht direkt über einen GPIO geschaltet. Stattdessen nutze ich einen MOSFET. Das hat mehrere Vorteile:

  • der ESP wird entlastet
  • höhere Ströme sind kein Problem
  • PWM und Effekte sind möglich

Hier passieren auch die klassischen Fehler: falsche Widerstände, falsch angeschlossene Pins oder ein MOSFET, der nicht richtig durchschaltet. Ich habe genau das erlebt – und durfte mit Multimeter und Logs auf Fehlersuche gehen. Aber genau das gehört zu echten DIY-Projekten dazu.

Hardware-Setup auf einen Blick

  • LED-Kerze öffnen und Originalelektronik entfernen
  • ESP32C3 platzieren und fixieren
  • Stromversorgung anschließen (Akku/Batterie)
  • Lux-Sensor per I²C verbinden
  • MOSFET zur LED-Schaltung einbauen
  • Alles testen, isolieren und sauber verstauen

Die eigentliche Intelligenz: Wann darf die Kerze leuchten?

Die Logik der Kerze ist bewusst simpel, aber alltagstauglich. Sie basiert auf wenigen klaren Regeln:

  • Ist es hell → Kerze aus
  • Wird es dunkel → Kerze an
  • Nach einer bestimmten Uhrzeit → Kerze aus
  • Niemand zu Hause → Kerze aus

Die Uhrzeit und Anwesenheit kann Home Assistant liefern. Die Entscheidung selbst trifft aber der ESP32. Dadurch bleibt das System robust und unabhängig.


Deep Sleep: der Schlüssel für Akku-Betrieb

Ohne Deep Sleep wäre dieses Projekt kaum sinnvoll. Der ESP würde zu viel Strom verbrauchen.

Aber das Projekt ist noch nicht mega optimiert für den Batterie Betrieb.

Habe es einige Wochen getestet und empfehle erstmal immer mit USB Kabel zu arbeiten.


Umsetzung mit ESPHome: schnell, flexibel, debug-freundlich

Ich setze das Ganze mit ESPHome um. Der große Vorteil: Ich kann sehr schnell iterieren, Logs ansehen und Änderungen testen.

Typische Bausteine im Projekt sind:

  • I²C-Sensor für Lux-Werte
  • GPIO-Ausgang für den MOSFET
  • Zeit- und Anwesenheitsabfragen
  • Deep-Sleep-Konfiguration
  • Debug-Logs

Am Anfang lief natürlich nicht alles sofort. Der Code meldete „Turning On“, aber die Kerze blieb dunkel. Ursache: ein falsch gewählter Widerstand am MOSFET. Nach dem Tausch lief alles wie geplant – und genau das ist der Punkt, an dem aus „Basteln“ echtes Verständnis wird.

Der ESPHome Code

Hier findest du den aktuellen Code für die Kerze mit Akku.

Ich beobachte noch, dass der Akku etwas zu kurz für meinen Geschmack hält. Nur wenige Tage. Das muss noch weiter optimiert werden.


esphome:
  name: kerze-01
  friendly_name: Kerze-01
  on_boot:
    priority: -10
    then:
      # Erstmal NICHT schlafen, damit HA States sicher reinkommen
      - delay: 60s
      - lambda: 'id(boot_ok) = true;'

esp32:
  board: esp32-c3-devkitm-1
  framework:
    type: arduino
    

# Enable logging
logger:

# Enable Home Assistant API
api:
  encryption:
    key: "1234567/m3AghmA="

ota:
  - platform: esphome
    password: "123456"

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

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:
    ssid: "Kerze-01 Fallback Hotspot"
    password: "123"



### Ab hier bitte kopieren!####



# Zeit: bevorzugt SNTP (funktioniert auch ohne HA), HA-Zeit als Zusatz
time:
  - platform: sntp
    id: net_time
    timezone: Europe/Berlin
  - platform: homeassistant
    id: ha_time

# -----------------------
# Steuerung als ESPHome-Entities (tauchen in HA beim ESPHome-Device auf)
# -----------------------
switch:
  - platform: template
    name: "Candle Schlafmodus"
    id: sleep_mode_sw
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    name: "Candle Dauer-An"
    id: always_on_sw
    restore_mode: RESTORE_DEFAULT_OFF
    optimistic: true

  - platform: template
    name: "Candle Deep Sleep"
    id: deep_sleep_enabled_sw
    restore_mode: RESTORE_DEFAULT_OFF   # default OFF wie gewünscht
    optimistic: true

number:
  - platform: template
    name: "Candle Lux Einschaltwert"
    id: lux_on_th
    min_value: 0
    max_value: 200
    step: 1
    restore_value: true
    initial_value: 30
    optimistic: true

  - platform: template
    name: "Candle Hysterese"
    id: lux_hyst
    min_value: 5
    max_value: 80
    step: 1
    restore_value: true
    initial_value: 20
    optimistic: true

  # Ruhezeit: Minuten seit Mitternacht (0..1439)
  - platform: template
    name: "Quiet Start (Min)"
    id: quiet_start_min
    min_value: 0
    max_value: 1439
    step: 1
    restore_value: true
    initial_value: 1380   # 23:00
    optimistic: true

  - platform: template
    name: "Quiet End (Min)"
    id: quiet_end_min
    min_value: 0
    max_value: 1439
    step: 1
    restore_value: true
    initial_value: 390    # 06:30
    optimistic: true

# -----------------------
# BH1750 Lux
# -----------------------
i2c:
  sda: GPIO6
  scl: GPIO7
  scan: true

sensor:
  - platform: bh1750
    name: "Candle Lux"
    id: candle_lux
    address: 0x23
    update_interval: 10s

  
# -----------------------
# PWM Output / Light
# -----------------------
output:
  - platform: ledc
    pin: GPIO4
    id: candle_pwm
    frequency: 1000 Hz

light:
  - platform: monochromatic
    name: "Candle Light"
    id: candle_light
    output: candle_pwm
    restore_mode: ALWAYS_OFF
    default_transition_length: 0s

  - platform: fastled_clockless
    name: "Kerze 1 - WS2812"
    id: candle_pixels
    chipset: WS2812
    pin: GPIO10          # falls dein Board keinen GPIO16 hat -> hier freien GPIO eintragen
    num_leds: 100
    rgb_order: GRB
    effects:
      - addressable_rainbow:
          name: "Rainbow"
          speed: 10
          width: 20

      - addressable_color_wipe:
          name: "Color Wipe"

      - addressable_twinkle:
          name: "Twinkle"
          twinkle_probability: 8%
          progress_interval: 32ms

      - addressable_fireworks:
          name: "Fireworks"
          update_interval: 32ms
          spark_probability: 12%
          use_random_color: true

      - addressable_flicker:
          name: "Candle Flicker"
          intensity: 15%

# -----------------------
# Deep Sleep
# -----------------------
deep_sleep:
  id: ds1

globals:
  - id: candle_should_be_on
    type: bool
    restore_value: no
    initial_value: "false"

  - id: boot_ok
    type: bool
    restore_value: no
    initial_value: "false"


# -----------------------
# Flicker Pattern: 0.5s schnell, 1-2s langsames helles Faden
# -----------------------
script:
  - id: pwm_flicker
    mode: restart
    then:
      - lambda: |-
          id(candle_light).turn_on().set_brightness(0.90).perform();

      - while:
          condition:
            lambda: 'return id(candle_should_be_on);'
          then:
            - lambda: |-
                auto rand_range = [](uint32_t min_v, uint32_t max_v) -> uint32_t {
                  uint32_t r = esp_random();
                  return min_v + (r % (max_v - min_v + 1));
                };

                // Quick 0.5s
                uint32_t t_end = millis() + 500;
                while (millis() < t_end && id(candle_should_be_on)) {
                  float b = rand_range(65, 100) / 100.0f;
                  auto call = id(candle_light).turn_on();
                  call.set_brightness(b);
                  call.perform();
                  delay(rand_range(18, 60));
                }

                // Slow bright fade 1-2s
                uint32_t fade_ms = rand_range(1000, 2000);

                float cur = id(candle_light).current_values.get_brightness();
                if (cur <= 0.01f) cur = 0.90f;

                float target = rand_range(85, 100) / 100.0f;
                const int steps = 18;
                float step = (target - cur) / steps;
                uint32_t step_delay = fade_ms / steps;

                for (int i = 0; i < steps && id(candle_should_be_on); i++) {
                  cur += step;
                  auto call = id(candle_light).turn_on();
                  call.set_brightness(cur);
                  call.perform();
                  delay(step_delay);
                }

            - delay: 150ms

      - light.turn_off: candle_light

# -----------------------
# Main Loop
# -----------------------
interval:
  - interval: 10s
    then:
      - lambda: |-
          // Zeitquelle wählen: HA-Time wenn gültig, sonst SNTP
          auto now = id(ha_time).now();
          if (!now.is_valid()) now = id(net_time).now();

          bool quiet_hours = false;
          if (now.is_valid()) {
            int now_min = now.hour * 60 + now.minute;
            int start_min = (int) id(quiet_start_min).state;
            int end_min   = (int) id(quiet_end_min).state;

            if (start_min < end_min) {
              quiet_hours = (now_min >= start_min && now_min < end_min);
            } else if (start_min > end_min) {
              quiet_hours = (now_min >= start_min || now_min < end_min);
            } else {
              quiet_hours = false; // start==end => keine Ruhezeit
            }
          }

          // Priorität 1: Schlafmodus -> immer AUS
          if (id(sleep_mode_sw).state) {
            id(candle_should_be_on) = false;
            return;
          }

          // Priorität 2: Ruhezeit -> AUS
          if (quiet_hours) {
            id(candle_should_be_on) = false;
            return;
          }

          // Priorität 3: Dauer-An -> AN
          if (id(always_on_sw).state) {
            id(candle_should_be_on) = true;
            return;
          }

          // Lux Logik: EIN Wert + Hysterese
          float th_on  = id(lux_on_th).state;
          float th_off = th_on + id(lux_hyst).state;

          if (!id(candle_should_be_on) && id(candle_lux).state < th_on)  id(candle_should_be_on) = true;
          if ( id(candle_should_be_on) && id(candle_lux).state > th_off) id(candle_should_be_on) = false;

      - if:
          condition:
            lambda: 'return id(candle_should_be_on);'
          then:
            - script.execute: pwm_flicker
          else:
            - script.stop: pwm_flicker
            - light.turn_off: candle_light

            - if:
                condition:
                  lambda: 'return id(boot_ok) && id(deep_sleep_enabled_sw).state;'
                then:
                  - delay: 2s
                  - deep_sleep.enter:
                      id: ds1
                      # nachts länger sparen, tagsüber kürzer:
                      sleep_duration: !lambda |-
                        auto now = id(ha_time).now();
                        if (!now.is_valid()) now = id(net_time).now();
                        if (!now.is_valid()) return 300000000; // 5min fallback in us

                        int now_min = now.hour * 60 + now.minute;
                        int start_min = (int) id(quiet_start_min).state;
                        int end_min   = (int) id(quiet_end_min).state;

                        bool quiet = false;
                        if (start_min < end_min) quiet = (now_min >= start_min && now_min < end_min);
                        else if (start_min > end_min) quiet = (now_min >= start_min || now_min < end_min);

                        return quiet ? 900000000 : 300000000; // 15min oder 5min (in µs)

👉 Wenn du ESPHome von Grund auf verstehen willst, dann ist mein
ESPHome Meisterkurs – Lokale Hardware-Schmiede spannend für dich.
Dort zeige ich dir Schritt für Schritt, wie du eigene, stabile ESPHome-Geräte baust – von der Board-Auswahl über YAML bis zum fertigen Sensor im Home Assistant.
Hier findest du alle Infos zum Kurs



Candle-Flicker: warum konstantes Licht langweilig ist

Eine LED, die einfach nur konstant leuchtet, wirkt künstlich. Deshalb habe ich einen Flacker-Effekt eingebaut, der per Software erzeugt wird:

  • leichte, zufällige Helligkeitsschwankungen
  • keine festen Muster
  • realistisch aus ein paar Metern Entfernung

Am Anfang war der Effekt zu aggressiv, aber mit etwas Feintuning wirkt das Licht erstaunlich authentisch.


Bonus-Level: RGB & WLED als Spaß-Upgrade

Natürlich konnte ich es nicht lassen und habe noch ein kleines Gimmick eingebaut: WS2812-LEDs im WLED-Style. Damit sind Effekte möglich wie:

  • Candle Flicker
  • Twinkle
  • Rainbow
  • Color Wipe

Technisch großartig, emotional eher Kategorie „Kinder feiern’s“. Der WAF ist hier… sagen wir mal… stark effektabhängig 😉


Fazit: Smart Home – auf was es ankommt

Dieses Projekt hat mir wieder gezeigt, worum es beim Smart Home eigentlich geht. Nicht um möglichst viele Geräte oder möglichst viel Technik – sondern darum, dass Dinge einfach funktionieren und dabei nicht auffallen.

Die smarte Kerze:

  • funktioniert komplett lokal
  • braucht keine App
  • kommt ohne Kabel aus
  • reagiert automatisch auf ihre Umgebung

Und genau das ist für mich die Essenz eines guten Smart-Home-Projekts.

Du hast dieses Projekt erfolgreich umgesetzt? Mega – dann bist du schon weiter als 90 % der Leute, die sich „Smart Home“ auf die Fahne schreiben 😉

Wenn du jetzt merkst:
Ich will das nicht nur nachbauen, ich will’s wirklich checken und eigene Projekte umsetzten
dann lade ich dich in meinen
ESPHome Meisterkurs – Meisterwerkstatt für lokale Hardware ein.

Dort bekommst du:

  • eine klare Roadmap von den Grundlagen bis zu eigenen Projekten
  • fertige YAML-Vorlagen, die wir im Kurs Stück für Stück auseinandernehmen
  • Praxisbeispiele aus der Community: Präsenzmelder, Garten, Lüftung, PV & Co.

👉 Hier geht’s zum ESPHome Meisterkurs »

Lokale Smart Home LLM-Vision in Home Assistant: So versteht deine Kamera endlich, was sie sieht – ohne Cloud
Deine Überwachungskamera ist „smart“… aber eigentlich sagt sie dir nur zwei Dinge: …
Frigate mit Home Assistant einrichten: Lokale KI-Überwachung (Proxmox/RPi, Google Coral, NAS)
Stell dir vor, deine Kameras haben nicht nur Augen – sondern bekommen …
Wer hat die schnellste Nulleinspeisung? – Mein großer Praxis-Test mit Anker Solix, OpenDTU & Lumentree
Hast du dich schon mal gefragt, welches Balkonkraftwerk am schnellsten auf 0 …
Die beste Preis-Leistungs-Wallbox für lokales Laden: go-e mit Home Assistant & EVCC
Wenn du eine Wallbox kaufst, ohne auf echte lokale Steuerbarkeit zu achten, …
Alkly Dumme Dinge Smart machen

Alex Kly (Alkly)

Ich glaube an ein Smart Home, das dir gehört – nicht der Cloud. Ich zeige dir, wie du Technik nutzt, um Energie zu sparen, Solar optimal einzubinden und dein Zuhause nachhaltig zu steuern.
Ein Zuhause, das mitdenkt, dich entlastet – und dich jeden Tag ein Stück freier macht.

Hat dir der Artikel geholfen?

Ja! 👍
Ne 🙁
Danke für dein Feedback!

Schreibe einen Kommentar

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