Wer sein Smart Home mit Apple Geräten steuert, kennt das Problem: Viele Bastel-Lösungen benötigen eine „Brücke“ (Bridge) wie Homebridge, um mit Apple HomeKit zu sprechen. Mit der quelloffenen (Open-Source) Bibliothek HomeSpan gehört das der Vergangenheit an.

HomeSpan ist eine Bibliothek, die die Kommunikation zwischen einem ESP32 und Apple HomeKit übernimmt. Sie implementiert das dafür benötigte HomeKit Accessory Protocol (HAP) direkt auf dem ESP32. Dadurch kann sich der Mikrocontroller über das Heimnetzwerk mit HomeKit verbinden, ohne dass wir das komplexe HomeKit-Protokoll selbst programmieren müssen.
Eine zusätzliche Bridge oder ein Server ist dafür erstmal nicht erforderlich.
In unserem Sketch legen wir lediglich fest, welches Gerät der ESP32 darstellen soll und was bei einer Änderung passieren soll. Wird beispielsweise eine Lampe in der Apple-Home-App eingeschaltet, erhält HomeSpan diese Änderung und unser Programm kann darauf reagieren, etwa indem es einen GPIO schaltet.
Vereinfacht gesagt: HomeSpan baut die Brücke zwischen Apple HomeKit und unserem eigenen ESP32-Code. Eine Umfangreiche Dokumentation bietet der Library Anbieter hier. Hier wird berichtet das der Standard ESP32 ausreicht, doch an der ein oder anderen Stelle wurde bei mir angezeigt das der Speicher knapp wird und daher habe ich hier experimentell mit 3 unterschiedlichen Entwicklungsboards getestet, um zu zeigen das es mit allen drei funktioniert. Je nach dem welches Board gewählt wird, vorab prüfen, welche GPIO Schnittstellen verwendet werden können. Weitere Informationen siehe hier
In diesem Tutorial zeige ich dir anhand von fünf kleinen Projekten, wie du deinen ESP32 in Apple Home integrieren kannst. Wir bauen einen einfachen Schalter, eine dimmbare RGB-Lampe, einen Temperatur- und Feuchtigkeitssensor, eine Relaissteuerung und einen Präsenzmelder.
Du kannst sie danach direkt mit deinem iPhone koppeln und per Apple Home App oder Siri steuern – ganz ohne extra Server. Bei mir hat das auf einen iPhone 14 gut und einfach geklappt.
Was du für die ersten zwei Projekte brauchst:
- 1x ESP32-C6 N8 Mikrocontroller
- 1x RGB-LED 10mm
- 3x Vorwiderstand (220 bis 330 Ohm, passend zur LED).
- Steckbrett (Breadboard) und ein paar Kabel.
Erstes Gerät: Einfacher Schalter
Was passiert gleich? ESP32 startet HomeSpan und meldet unsere LED bei Apple Home an, Apple Home sendet „Ein“ oder „Aus“
Unser ESP32 schaltet den entsprechenden GPIO PIN.
Der Aufbau
Der Aufbau ist absolut minimalistisch.
Verwendet wird hier eine RGB-LED mit gemeinsamer Kathode (Common Cathode). Der gemeinsame Anschluss wird mit GND verbunden. Die Anschlüsse für Rot, Grün und Blau werden jeweils über einen Vorwiderstand mit GPIO Pin: 4,5,6 des ESP32 verbunden.
Prüfe im Zweifel das Datenblatt deiner RGB-LED, da die Anschlussreihenfolge je nach Modell unterschiedlich sein kann.

Die Software: So bereitest du alles vor
Wir nutzen die klassische Arduino IDE (oder Visual Studio Code mit PlatformIO).
- Öffne die Bibliotheksverwaltung (Library Manager).
- Suche nach HomeSpan (von Gregg) und installiere die aktuellste Version.
Der Programmcode (Sketch)
HomeSpan nutzt eine sehr logische Struktur. Jedes Gerät (Accessory) hat Eigenschaften (Characteristics) wie „An/Aus“ oder „Helligkeit“. Hier werden auch viele weitere Sketche zum Testen angeboten.

Für den ersten Test verwende ich diesen Code und nehme erstmal nur GPIO Pin 4 und 5 zum schauen ob einfaches Rot/Grün Schalten funktioniert.
Unser Projekt besteht aus zwei Dateien:
HomeKit_RGB.ino → Hauptprogramm
DEV_LED.h → Steuerung unserer LED
Beide Dateien liegen im selben Sketch-Ordner. In der Arduino IDE erscheinen sie als zwei Registerkarten.
Hier Inhalt der Ino-Datei:
#include "HomeSpan.h"
#include "DEV_LED.h"
void setup() {
Serial.begin(115200);
homeSpan.begin(Category::Bridges,"Prilchens RGB LED");
homeSpan.setPairingCode("46637726");
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new Characteristic::Name("Rote LED");
new DEV_LED(4);
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new Characteristic::Name("Grüne LED");
new DEV_LED(5);
}
void loop(){
homeSpan.poll();
} // end of loop()Zweiter Reiter DEV_LED.h liegt im gleichen Ordner wie Ino-Datei
struct DEV_LED : Service::LightBulb {
int ledPin;
SpanCharacteristic *power;
DEV_LED(int ledPin) : Service::LightBulb() {
power = new Characteristic::On();
this->ledPin = ledPin;
pinMode(ledPin, OUTPUT);
Serial.print("LED Erfolgreich initialisiert ");
Serial.println(ledPin);
}
boolean update() {
bool state = power->getNewVal();
digitalWrite(ledPin, state);
Serial.print("LED on pin ");
Serial.print(ledPin);
Serial.println(state ? "mache an" : "mache aus");
return true;
}
};Bevor ich hochlade, passe ich diese drei Einstellungen in der Arduino IDE an:
Arduino IDE → Werkzeuge → „USB CDC on Boot“ → „Enabled“
Arduino IDE → Werkzeuge → „Flash Size“ → passend zu eurem Board bei mir „8MB“ oder später auf „4MB“
Arduino IDE → Werkzeuge → „Partition Scheme“ → „Huge App“
Das Pairing mit Apple Home
Jetzt kommt der magische Moment:
- Spiele den Code auf deinen ESP32 und öffne den Seriellen Monitor (Baudrate auf 115200 stellen).
11:46:02.203 -> Service UUID AID IID Update Loop Button Linked Services
11:46:02.203 -> —————————— ——– ———- — —— —- —— —————
11:46:02.203 -> AccessoryInformation 3E 1 1 NO NO NO –
11:46:02.203 -> AccessoryInformation 3E 2 1 NO NO NO –
11:46:02.203 ->
11:46:02.203 -> Configured as Bridge: YES
11:46:02.203 ->
11:46:02.203 -> Database Validation: Warnings=0, Errors=0
11:46:02.203 ->
11:46:02.203 -> *** End Info ***
11:46:02.203 ->
11:46:02.203 ->
11:46:02.203 -> Generating new SRP verification data for Setup Code: 466-37-726 …
11:46:02.328 -> Setup Payload for Optional QR Code: X-yz://001234…
11:46:02.328 ->
11:46:02.328 -> Generating new random Accessory ID and Long-Term Ed1234 Signature Keys…
11:46:02.328 ->
11:46:02.328 -> Accessory ID: 7B:95:3E:10:CF:FF LTPK: A16E…
11:46:02.328 -> No Paired Controllers
11:46:02.328 -> Resetting Database Hash…
11:46:02.328 ->
11:46:02.328 -> Accessory configuration has changed. Updating configuration number to 1
11:46:02.328 ->
11:46:02.328 -> *** WIFI CREDENTI. DATA NOT FOUND. YOU MAY CONFIGURE BY TYPING ‚W ‚.
11:46:02.328 ->
11:46:02.328 -> HomeSpan Bridge is READY!
- Hier sollte HomeSpan Bridge is READY am Ende stehen
- Im Seriellen Monitor kann man in der Eingabezeile nun mit Zeichen:? weitere Zeichen abrufen um zB. den Status, Neustart, Reset und auch Wifi Konfiguration zu starten.
- Tippe „W“ ein, suche dir dein AccessPoint raus und gib Passwort ein, so das das Gerät deinem Netzwerk hinzugefügt wird. Jetzt kommt folgende Meldung:
14:37:33.362 -> Trying to connect to Accespoint. Waiting 5 sec…
14:37:37.545 -> Received IPv4 Address: 192.168.0.115
14:37:37.545 -> WiFi Connected! (RSI=-51 BSSID=E7:FF:70:72:AB:7B)
14:37:37.545 -> Starting MDNS…
14:37:37.545 -> HostName: HomeSpan-111222333.local:80
14:37:37.545 -> Display Name: Prilchens RGB LED
14:37:37.545 -> Model Name: HomeSpan-ESP32
14:37:37.545 -> Setup ID: HSPN
14:37:37.545 -> Starting HAP Server on port 80…
14:37:37.545 ->
14:37:37.545 -> DEVICE NOT YET PAIRED — PLEASE PAIR WITH HOMEKIT APP
- Öffne nun die Apple Home App auf deinem iPhone und wähle „Gerät hinzufügen“.
- Wähle „Weitere Optionen“. Deine „Prilchens RGB LED“ sollte dort auftauchen.
- Der Standard-Setup-Code für HomeSpan lautet: 466-37-726
- Anschließend nach dem Testen, sollte der Code über die HomeSpan-Konsole mit S auf eine nur dir bekannten PIN geändert werden.
Nach wenigen Sekunden ist der ESP32 fest in dein Apple-System integriert. Du kannst die LED´s nun schalten, in Automationen einbinden oder einfach sagen: „Hey Siri, schalte die grüne LED ein.“

Und wer die Optionen hier bei mir auf Prilchen.de mit den vielen Erweiterungsmöglichkeiten kennt, erahnt, was nun damit alles direkt im Apple-Ökosystem möglich ist!
Also statt LED kann ein Relay verwendet werden. Oder eine Mehrfarbige Lampe in bunten Farben leuchten lassen oder dimmen.
Nachteil bei dieser direkten Ansteuerung ist, das entfernte zugriffe nicht möglich sind.
Das würde mit einen HomePod möglich werden.
Sicherheit:
Die Eingabe der WLAN-Zugangsdaten über den seriellen Monitor durch das Drücken der Taste „W“ ist sicherheitstechnisch erst mal unbedenklich und sogar eine sehr gute Praxis.
Im Vergleich zum direkten Einschreiben von Netzwerkname (SSID) und Passwort in den Programmcode (Sketch) bietet diese Methode einen entscheidenden Vorteil: Wenn du den Code später auf Plattformen wie GitHub teilst, können die privaten WLAN-Daten nicht versehentlich veröffentlicht werden.
Hier sind die wichtigsten Aspekte, die du wissen solltest:
- Lokale Übertragung: Die Daten fließen ausschließlich über das lokale USB-Kabel vom PC zum ESP32-C6. Es gibt keine Funkübertragung, die Hacker aus der Ferne abfangen könnten.
- Sichtbarkeit bei der Eingabe: Das Passwort wird bei der Eingabe im seriellen Monitor im Klartext angezeigt. Hier besteht lediglich die Gefahr, dass jemand im Raum direkt auf den Bildschirm schaut.
- Speicherung auf dem Chip: HomeSpan speichert die Daten im sogenannten NVS (Non-Volatile Storage, ein nicht-flüchtiger Speicherbereich auf dem Mikrocontroller). Dieser ist standardmäßig unverschlüsselt. Ein Angreifer müsste dein gebasteltes Gerät aber physisch stehlen und den Speicher mit spezieller Software auslesen, um an das Passwort zu gelangen. Für kommerzielle oder sicherheitskritische Anwendungen sollten daher auch Flash-Verschlüsselung und Secure Boot berücksichtigt werden.
- Also keine ungelöschten ESP32 aus der Hand geben!
Löschung / Zurücksetzen:
Weitere smarte Lösungen können mit weiteren ESP32-C6 Modulen erstellt werden. Sollte erst mal nur zum Testen nur ein ESP32 vorliegen, muss das vorab eingerichtete Geräte wieder im Home App gelöscht werden und im Seriellen Monitor mit Auswahl „E“ = Löscht den gesamten NVS einschließlich des HomeKit-Setup-Codes.
Zweites Gerät: Farbwahl
Um eine RGB-LED in Apple Home nicht nur ein- und auszuschalten, sondern auch dimmen und die Farbe ändern zu können, müssen nun .ino-Datei und .h-Datei angepasst werde. Hardwareaufbau / Verdrahtungsplan bleibt unverändert:
- Das Gerät (Accessory) muss die Apple-Eigenschaften für Helligkeit (Brightness), Farbton (Hue) und Farbsättigung (Saturation) unterstützen. Die RGB-LED kann das.
- Die Ansteuerung der GPIO-Pins (General Purpose Input/Output) darf nicht mehr nur über
digitalWriteerfolgen. Stattdessen wird Pulsweitenmodulation (PWM) benötigt. Damit wird der Strom extrem schnell an- und ausgeschaltet, was wir als Dimmen oder Farbmischen wahrnehmen. HomeSpan bietet dafür die komfortable KlasseLedPin, die die komplizierte Timer-Konfiguration des ESP32-C6 automatisch übernimmt.
Die Hauptdatei (.ino)
Wir fassen die drei separaten Pins zu einem einzigen Gerät zusammen und ändern die Kategorie auf Lighting (Beleuchtung).
#include "HomeSpan.h"
#include "DEV_LED.h"
void setup() {
// Serielle Kommunikation für die Fehlersuche im Serial Monitor starten
Serial.begin(115200);
// 1. HomeSpan initialisieren
// Category::Lighting sagt der Apple Home App: "Hey, ich bin eine Lampe!"
// Dadurch zeigt Apple automatisch das richtige Icon (eine Glühbirne) an.
homeSpan.begin(Category::Lighting, "Prilchens RGB LED");
// Der Code, den wir in der Apple Home App eingeben müssen, um das Gerät zu koppeln.
homeSpan.setPairingCode("46637726");
// 2. Das Gerät (Accessory) zusammenbauen
// Apple HomeKit ist wie eine Matroschka-Puppe aufgebaut:
// Accessory (das physische Gerät) -> Service (die Funktion, z.B. Licht) -> Characteristic (Eigenschaften wie Farbe)
new SpanAccessory(); // Erstellt ein neues, leeres physisches Gerät in HomeKit
// Jeder HomeKit-Artikel braucht zwingend diese Basis-Informationen (Hersteller, Modell, etc.)
new Service::AccessoryInformation();
new Characteristic::Identify(); // Pflichtfeld: Lässt das Gerät blinken, wenn man in der App auf "Identifizieren" drückt
new Characteristic::Name("RGB Licht"); // Der Name, der später auf der Kachel in der App steht
// Hier binden wir unsere eigene Logik aus der DEV_LED.h Datei ein.
// Wir übergeben die GPIO-Pin-Nummern unseres ESP32-C6 für Rot (4), Grün (5) und Blau (6).
new DEV_RGB_LED(4, 5, 6);
}
void loop(){
// homeSpan.poll() ist das Herzstück unseres Programms.
// Diese Funktion muss ständig und so schnell wie möglich wiederholt werden.
// Sie lauscht, ob das iPhone über das Netzwerk Befehle schickt, und antwortet darauf.
// Ein delay() im Loop würde diese Kommunikation stören!
homeSpan.poll();
}Die Header-Datei (DEV_LED.h)
Die Logik rechnet nun die Werte, die Apple sendet (Farbton in Grad, Sättigung in Prozent, Helligkeit in Prozent), in entsprechende PWM-Werte für die drei einzelnen Farben um. HomeSpan bringt dafür die praktische Hilfsfunktion HSVtoRGB mit.
// Wir erstellen unsere eigene LED-Struktur, die von der HomeSpan "LightBulb" (Glühbirne) erbt.
// Dadurch weiß HomeKit sofort: "Aha, dieses Gerät hat Eigenschaften wie An/Aus, Helligkeit und Farbe."
struct DEV_RGB_LED : Service::LightBulb {
// Normale GPIO-Pins können nur "Strom an" (HIGH) oder "Strom aus" (LOW).
// Um Farben zu mischen, müssen wir die LEDs dimmen. Das macht der ESP32 mit PWM (Pulsweitenmodulation).
// HomeSpan bietet dafür die fertige Klasse "LedPin", die uns die komplizierte Timer-Arbeit im ESP32 abnimmt.
LedPin *redPin, *greenPin, *bluePin;
// Das sind die Apple-Home-Eigenschaften ("Characteristics"), die wir auslesen wollen:
SpanCharacteristic *power; // Ist die Lampe an oder aus? (Boolean: true/false)
SpanCharacteristic *H; // Hue (Farbton am Farbrad in Grad: 0 bis 360)
SpanCharacteristic *S; // Saturation (Farbsättigung: 0 bis 100%)
SpanCharacteristic *V; // Value/Brightness (Helligkeit: 0 bis 100%)
// Der Konstruktor: Dieser Code läuft genau einmal, wenn das Gerät im setup() der .ino-Datei erstellt wird.
DEV_RGB_LED(int r_pin, int g_pin, int b_pin) : Service::LightBulb() {
// Wir verknüpfen unsere Variablen mit den offiziellen Apple HomeKit Eigenschaften
power = new Characteristic::On();
H = new Characteristic::Hue();
S = new Characteristic::Saturation();
V = new Characteristic::Brightness();
// Wir initialisieren die Pins als PWM-Pins für weiche Farbübergänge
redPin = new LedPin(r_pin);
greenPin = new LedPin(g_pin);
bluePin = new LedPin(b_pin);
Serial.println("RGB LED erfolgreich initialisiert und bereit für HomeKit!");
}
// Die update() Funktion wird vollautomatisch von homeSpan.poll() aufgerufen,
// sobald man in der Apple Home App auf dem Handy einen Regler verschiebt.
boolean update() {
// 1. Die aktuellsten Werte vom iPhone abholen
boolean state = power->getNewVal(); // true (an) oder false (aus)
float v = V->getNewVal<float>(); // Helligkeit
float h = H->getNewVal<float>(); // Farbton
float s = S->getNewVal<float>(); // Sättigung
float r, g, b; // Hier speichern wir gleich unsere errechneten Rot, Grün, Blau Werte (0 bis 1)
// 2. Hardware steuern
if (state) { // Wenn die Lampe laut App AN sein soll...
// Apple denkt im sogenannten HSV-Farbraum (Farbton, Sättigung, Helligkeit),
// weil das für uns Menschen logischer am Farbrad auf dem Display einzustellen ist.
// Unsere Hardware-LED braucht aber RGB-Werte (Rot, Grün, Blau) zum Mischen.
// Die HomeSpan-Funktion "HSVtoRGB" rechnet diese Werte präzise für uns um.
// Wichtig: S und V müssen für diese Formel Werte zwischen 0.0 und 1.0 sein, daher teilen wir durch 100.
LedPin::HSVtoRGB(h, s / 100.0, v / 100.0, &r, &g, &b);
// Jetzt schicken wir die umgerechneten Werte (multipliziert mit 100, für 0 bis 100%) an die PWM-Pins.
redPin->set(r * 100.0);
greenPin->set(g * 100.0);
bluePin->set(b * 100.0);
} else {
// Wenn die Lampe AUS sein soll, setzen wir einfach alle drei Farben auf 0% Leuchtkraft.
redPin->set(0);
greenPin->set(0);
bluePin->set(0);
}
// Zur Kontrolle im Serial Monitor ausgeben, was das iPhone gerade gesendet hat.
Serial.printf("RGB LED: Status=%s, Hue=%.0f, Sat=%.0f, Helligkeit=%.0f\n", state ? "AN" : "AUS", h, s, v);
// WICHTIG: Bei HomeSpan muss die update() Funktion am Ende immer 'true' zurückgeben.
// Das ist das Signal an HomeKit, dass der Befehl erfolgreich am ESP32 angekommen ist und verarbeitet wurde.
return true;
}
};Dann hochladen und wieder mit Auswahl „W“ wieder Wifi verbinden.
Zusammenfassung der technischen Änderungen
- Struktur: Anstatt für jede Farbe ein eigenes new SpanAccessory() anzulegen, übergibst du die drei Pins (4, 5, 6) nun an die Klasse DEV_RGB_LED.
- Farbraum: Apple Home arbeitet intern nicht mit RGB, sondern mit dem HSV-Farbraum. Die Funktion LedPin::HSVtoRGB übersetzt diese Werte vollautomatisch so, dass die rote, grüne und blaue LED im richtigen Verhältnis angemischt werden.

funktioniert ganz hervorragend 🙂
Drittes Gerät: Luftfeuchtigkeit und Temperaturanzeige
Nachdem wir bisher hauptsächlich Ausgänge des ESP32 gesteuert haben, drehen wir die Richtung jetzt um.
Diesmal soll Apple Home einen Messwert vom ESP32 erhalten. Dazu schließen wir einen SHTC3 Temperatursensor an einem Standard ESP32 mit 4MB Flash-Speicher an. Der ESP32 liest die aktuelle Temperatur aus und HomeSpan stellt diesen Wert Apple Home zur Verfügung.
Das Prinzip sieht jetzt also etwas anders aus:
Temperatursensor → ESP32 → HomeSpan → Apple Home
Damit lernen wir einen wichtigen Unterschied kennen:
Bei unserer RGB-Lampe sendet Apple Home einen Befehl zum ESP32. Bei der Temperaturmessung entsteht die Information dagegen auf dem ESP32 und wird anschließend an Apple Home übergeben.
Was passiert gleich?
- Der ESP32 liest den Temperatursensor aus.
- Wir speichern den gemessenen Temperaturwert.
- HomeSpan stellt diesen Wert als Temperatur in HomeKit bereit.
- Apple Home kann die aktuelle Temperatur anzeigen oder vorlesen
- Ändert sich die Temperatur, aktualisieren sich der entsprechenden Wert.
So lernen wir neben einem weiteren HomeKit-Gerät auch, wie wir Sensordaten vom ESP32 an Apple Home übergeben.
Der Aufbau
Der Temperatursensor wird mit 3,3V und GND so wie I2C an SDA GPIO 21 und SCL an GPIO 22 verbunden.
(Die hier verwendeten GPIOs 21 und 22 beziehen sich auf das in diesem Beispiel verwendete ESP32-Board. Bei anderen ESP32-Varianten kann die I²C-Belegung abweichen) Siehe: Belegungspläne ESP32
Benötigte Library:

Der Programmcode
Auch dieses Projekt teilen wir wieder logisch auf.
Hauptdatei Ino-Sketch:
#include "HomeSpan.h"
#include <Wire.h> // Standard-Bibliothek für die I2C-Kommunikation
#include <Adafruit_SHTC3.h> // Bibliothek für den SHTC3 Sensor
#include "DEV_Sensor.h"
// Globale Instanz für den SHTC3 Sensor anlegen
Adafruit_SHTC3 shtc3;
void setup() {
Serial.begin(115200);
// 1. Hardware initialisieren
// Der SHTC3 hat eine feste I2C-Adresse (meist 0x70), die Bibliothek findet ihn automatisch.
if (!shtc3.begin()) {
Serial.println("Kein SHTC3 Sensor gefunden! Bitte Verkabelung prüfen.");
while (1); // Stoppt das Programm bei Hardware-Fehlern
}
homeSpan.begin(Category::Sensors, "Prilchens SHTC3 Sensor");
homeSpan.setPairingCode("46637726");
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new Characteristic::Name("Raumklima");
// 1. Service: Temperatur
// Wir übergeben die Speicheradresse (&) unseres gestarteten SHTC3-Sensors
new DEV_TempSensor(&shtc3);
// 2. Service: Luftfeuchtigkeit
new DEV_HumSensor(&shtc3);
}
void loop(){
homeSpan.poll();
}Die Hauptdatei initialisiert HomeSpan und erstellt unser HomeKit-Gerät. Die eigentliche Sensorlogik übernimmt anschließend diese DEV_Sensor.h
// ---------------------------------------------------------
// 1. STRUKTUR: TEMPERATUR
// ---------------------------------------------------------
struct DEV_TempSensor : Service::TemperatureSensor {
SpanCharacteristic *temp;
unsigned long lastUpdate;
Adafruit_SHTC3 *shtc3Sensor;
DEV_TempSensor(Adafruit_SHTC3 *sensor) : Service::TemperatureSensor() {
shtc3Sensor = sensor;
temp = new Characteristic::CurrentTemperature(20.0);
// Der SHTC3 kann von -40 bis +125 Grad messen. Wir passen die Apple-Grenzen entsprechend an.
temp->setRange(-40, 125);
lastUpdate = millis();
Serial.println("SHTC3 Temperatursensor für HomeKit bereit!");
}
void loop() {
if(millis() - lastUpdate > 10000) {
lastUpdate = millis();
// Die Adafruit-Bibliothek nutzt "Events", um die Datenstrukturen zu füllen
sensors_event_t humidity, temp_event;
shtc3Sensor->getEvent(&humidity, &temp_event);
// Die Temperatur steckt nun in der Variable temp_event.temperature
float realTemp = temp_event.temperature;
if (isnan(realTemp)) {
Serial.println("Fehler beim Auslesen der Temperatur!");
return;
}
temp->setVal(realTemp);
Serial.printf("SHTC3 Temperatur: %.2f °C\n", realTemp);
}
}
};
// ---------------------------------------------------------
// 2. STRUKTUR: LUFTFEUCHTIGKEIT
// ---------------------------------------------------------
struct DEV_HumSensor : Service::HumiditySensor {
SpanCharacteristic *hum;
unsigned long lastUpdate;
Adafruit_SHTC3 *shtc3Sensor;
DEV_HumSensor(Adafruit_SHTC3 *sensor) : Service::HumiditySensor() {
shtc3Sensor = sensor;
hum = new Characteristic::CurrentRelativeHumidity(50.0);
// Offset von 500 Millisekunden, damit I2C-Anfragen an den Chip nicht kollidieren
lastUpdate = millis() + 500;
Serial.println("SHTC3 Luftfeuchtigkeitssensor für HomeKit bereit!");
}
void loop() {
if(millis() - lastUpdate > 10000) {
lastUpdate = millis();
// Auch hier rufen wir das Event ab
sensors_event_t humidity_event, temp_event;
shtc3Sensor->getEvent(&humidity_event, &temp_event);
// Die relative Luftfeuchtigkeit steckt in humidity_event.relative_humidity
float realHum = humidity_event.relative_humidity;
if (isnan(realHum)) {
Serial.println("Fehler beim Auslesen der Luftfeuchtigkeit!");
return;
}
hum->setVal(realHum);
Serial.printf("SHTC3 Luftfeuchtigkeit: %.2f %%\n", realHum);
}
}
};Neu in diesem dritten Teil
Bei unseren bisherigen Beispielen haben wir auf eine Aktion aus Apple Home reagiert. Jetzt müssen wir zusätzlich daran denken, dass sich ein Sensorwert von alleine ändern kann.
Damit haben wir die Abfrage von Sensoren Werten für HomeKit geschaffen.

Viertes Gerät: Relay schalten
Zum schalten von Geräten sind Relay´s gut zu gebrauchen. Auf der einen Seite mit nur 3,3V kann eine andere unabhängige Schaltung unterbrochen oder aktiviert werden.
Hier hatte ich das mal detaillierter gezeigt: Arduino Projekt: Wie funktioniert ein Relay
Hier mal verdeutlicht an einem angeschlossenen Elektromotor Model

Verdrahtung der Testumgebung in etwa so:

Hauptdatei Ino-Sketch:
#include "HomeSpan.h"
#include "DEV_Relay.h"
void setup() {
Serial.begin(115200);
// Kategorie für einfache Schalter oder Steckdosen
homeSpan.begin(Category::Switches, "Prilchens Schalter");
homeSpan.setPairingCode("46637726");
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
// Dieser Name erscheint als Schalter in der Apple Home App
new Characteristic::Name("Ventilator Relais");
// Wir rufen die Relais-Struktur auf und übergeben den GPIO-Pin 8
new DEV_Relay(8);
}
void loop(){
homeSpan.poll();
}und ihre DEV_Relay.h
// Wir erben vom Service::Switch (Schalter)
struct DEV_Relay : Service::Switch {
// Eigenschaft für Strom an/aus
SpanCharacteristic *power;
// Variable für den GPIO-Pin
int relayPin;
DEV_Relay(int pin) : Service::Switch() {
relayPin = pin;
// Die An/Aus-Eigenschaft initialisieren
power = new Characteristic::On();
// Den Pin als Ausgang (OUTPUT) definieren, da wir Strom hinausschicken wollen
pinMode(relayPin, OUTPUT);
// Das Relais beim Start des ESP32 aus Sicherheitsgründen immer erst einmal ausschalten (LOW)
digitalWrite(relayPin, LOW);
Serial.println("3,3V Relais für HomeKit bereit!");
}
// Die update() Funktion wird aufgerufen, wenn man in der Home App drückt
boolean update() {
// Den neuen Befehl von Apple Home auslesen (true = an, false = aus)
boolean state = power->getNewVal();
// Den Strom am Pin entsprechend ein- oder ausschalten
// state ist entweder 1 (HIGH) oder 0 (LOW)
digitalWrite(relayPin, state);
// Den Vorgang zur Kontrolle im seriellen Monitor protokollieren
Serial.printf("Relais wurde über Apple Home geschaltet: %s\n", state ? "AN" : "AUS");
return true;
}
};Das Grundprinzip ist:
Switch → ESP32 → HomeSpan → Apple Home
5tes Gerät: Anwesenheitserkennung
Hier soll anstatt eines Bewegungsmelder, der nur in eine Richtung schaut, ein etwas sensibleres Modul prüfen, ob eine Anwesenheit vorhanden ist. Nämlich mit dem Radar Modul LD2410C, das ich schon mal in diesem Projekt vorgestellt habe:
Hochsensible Erfassung von menschlicher Präsenz mit Radar
Die Stromversorgung beim Radar Modul ist 5V mit bis zu 200mA, aber abweichend muss man wissen, das der Ausgangspegel der seriellen Schnittstelle nur zwischen 3,3V und 0V liegt. Somit kann bedenkenlos RX,TX und Out auch an ESP32 ohne ein Logik-Level-Konverter angeschlossen werden.
Verdrahtung:
Radar Pin Out –> ESP32 Pin 7
Radar Pin GND –> ESP32 Pin GND
Radar Pin VCC –> ESP32 Pin 5V

Anders als bei der Temperatur gibt es hier keinen Wert wie beispielsweise 21,5 °C, sondern nur einen Zustand:
JA → Anwesenheit erkannt
NEIN → keine Anwesenheit erkannt
Der Programmcode
Hauptdatei Ino-Sketch:
#include "HomeSpan.h"
#include "DEV_Sensor.h"
void setup() {
Serial.begin(115200);
// Kategorie für Sensoren
homeSpan.begin(Category::Sensors, "Prilchens Radar");
homeSpan.setPairingCode("46637726");
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new Characteristic::Name("Präsenz Labor");
// Wir rufen unsere neue Struktur auf und übergeben den GPIO-Pin 7 (verbunden mit OUT am LD2410C)
new DEV_OccupancySensor(7);
}
void loop(){
homeSpan.poll();
}DEV_Senor.h:
struct DEV_OccupancySensor : Service::OccupancySensor {
// Eigenschaft für die Belegungserkennung (Occupancy)
SpanCharacteristic *occupancy;
int sensorPin; // Hier speichern wir die übergebene Pin-Nummer (z.B. 7)
boolean lastState; // Hier merken wir uns den letzten Zustand (Niemand da / Jemand da)
DEV_OccupancySensor(int pin) : Service::OccupancySensor() {
sensorPin = pin;
// Den Pin als reinen Eingang (INPUT) definieren, um das Signal vom Radar zu lesen
pinMode(sensorPin, INPUT);
// Initialisierung in Apple HomeKit: 0 bedeutet "Niemand da", 1 bedeutet "Präsenz erkannt"
occupancy = new Characteristic::OccupancyDetected(0);
// Aktuellen Zustand beim Start einmalig einlesen und speichern
lastState = digitalRead(sensorPin);
Serial.println("LD2410C Präsenzsensor für HomeKit bereit!");
}
void loop() {
// 1. Den aktuellen Wert am OUT-Pin des Radars auslesen
boolean currentState = digitalRead(sensorPin);
// 2. Prüfen: Hat sich der Zustand seit der letzten Messung verändert?
// Nur wenn jemand neu den Raum betritt oder ihn endgültig verlässt, werden wir aktiv.
if (currentState != lastState) {
// Den neuen Zustand als aktuellen Zustand speichern
lastState = currentState;
// Den neuen Wert an Apple HomeKit senden (true = 1 = belegt, false = 0 = frei)
occupancy->setVal(currentState);
// Zur Kontrolle eine Meldung im Serial Monitor ausgeben
if (currentState) {
Serial.println("Radar meldet: Präsenz erkannt! Jemand ist im Raum.");
} else {
Serial.println("Radar meldet: Raum ist leer. Niemand mehr da.");
}
}
}
};In der Home App wird lediglich angezeigt, wenn Präsenz erkannt wurde. Feld bleibt leer wenn im Raum nichts los ist.
Als Alternative App finde ich „eve“ erwähnenswert. Hier sehe ich aus allen 5 smarten Lösungen eine schöne Zusammenstellung

Automatisierung
Für eine Automatisierung, zB. Ist im Keller Temperatur über 20 Grad schalte den Ventilator ein, ist zwingend eine Steuerzentrale erforderlich. Denn damit Wenn-Dann-Automationen in Apple Home funktionieren sollen, muss zwingend ein Apple TV oder ein HomePod als „Steuerzentrale“ (Home Hub) im Netzwerk aktiv sein. Ein iPhone allein kann Sensordaten nur live anzeigen, aber keine Automationen im Hintergrund ausführen.
Entferntes steuern geht nur mit zusätzlicher Apple Hardware
Für reine Statusabfragen im eigenen WLAN reicht dein iPhone völlig aus. Sobald du den Sensor aber für Automationen nutzen oder von unterwegs abfragen willst, zwingt Apple dich tatsächlich zu zusätzlicher Hardware.
Hier ist die genaue technische Aufteilung bei Apple HomeKit:
- Was ohne Zusatz-Hardware geht (nur im lokalen WLAN): Du kannst den Status des Sensors direkt auf dem iPhone, iPad oder Mac ablesen. Das Apple-Gerät kommuniziert dabei lokal über dein Netzwerk direkt mit dem ESP32-C6.
- Was eine Apple „Steuerzentrale“ erfordert: Wenn du Regeln erstellen willst (z.B. „Wenn der LD2410C Präsenz meldet, schalte eine smarte Steckdose ein“) oder den Status von unterwegs abfragen möchtest, benötigst du einen dauerhaft aktiven Hub. Das iPhone verarbeitet keine Automationsregeln im Hintergrund, wenn du das Haus verlässt.
- Zugelassene Apple-Hardware: In der aktuellen HomeKit-Architektur fungieren ausschließlich ein Apple TV oder ein HomePod (bzw. HomePod mini) als Steuerzentrale.
Die lokale Maker-Alternative: Da du in deinem Heimnetzwerk ohnehin auf lokale Dienste (wie Pi-hole auf deinem Raspberry Pi) setzt und cloudfreie Edge-Lösungen bevorzugst, kannst du diesen Hardware-Zwang umgehen. Eine Smart-Home-Zentrale wie Home Assistant auf einem Raspberry Pi kann deinen HomeSpan-ESP32 über die lokale Integration „HomeKit Device“ direkt auslesen. Home Assistant übernimmt dann die komplette Automationslogik, ganz ohne Apple TV.
Alternativ kannst du einfache Aktionen (wie das Schalten eines lokalen Relais bei erkannter Präsenz) auch hardwarenah direkt im C++ Code des ESP32 mitprogrammieren.
Test mit altem Apple TV (A1625):
Funktioniert als Steuerzentrale für HomeKit-Automationen und Fernzugriff.
Einschränkung: Das Betriebssystem tvOS ist bei diesem Gerät spürbar langsamer. Es unterstützt kein Thread. Da es jedoch die aktuelle tvOS-Version erhält, schaltet es deinen LD2410C-Präsenzsensor zuverlässig.
