Skip to content

Beispiele

Demo-Objekt: ESP32 mit BH1750-Lichtsensor, LED, MQTT und BLE

Dieses Beispielprojekt dient als Demo-Objekt, um die Zusammenarbeit zwischen einem ESP32, der ActionConnect Library, BLE, MQTT und einem Sensor praktisch zu testen. Es zeigt, wie ein eigenes Gerät aufgebaut werden kann, das Messwerte erfasst, Zustände verwaltet, Befehle aus der App verarbeitet und seine Daten an den ConnectHub überträgt.

Als Beispielhardware werden ein BH1750-Lichtsensor und die Onboard-LED des ESP32 verwendet. Der BH1750 misst die aktuelle Helligkeit in Lux. Die Onboard-LED kann über Befehle ein- und ausgeschaltet oder in ihrer Helligkeit verändert werden. Dadurch entsteht ein einfaches, aber vollständiges Beispiel für ein verbundenes Gerät, das ohne zusätzliche Aktor-Hardware getestet werden kann.

Das Beispiel ist vor allem als Vorlage für Kunden und Partner gedacht, die eigene Hardware mit der ActionConnect Library an den ConnectHub und die App anbinden möchten. Die Struktur kann übernommen und an eigene Sensoren, Aktoren und Capabilities angepasst werden.

Wichtig ist dabei, dass die verwendeten Capabilities im Code zur Thing-Model-Vorlage und zur Registrierung im ConnectHub passen. Nur wenn diese Namen übereinstimmen, können die Daten später korrekt angezeigt und gesteuert werden.

Verwendete Hardware

Für das Demo-Objekt wird ein ESP32 mit seiner Onboard-LED sowie ein BH1750-Lichtsensor verwendet. Die Onboard-LED dient als einfacher Aktor, der über die App oder den ConnectHub ein- und ausgeschaltet beziehungsweise in der Helligkeit verändert werden kann.

Der BH1750 misst die Umgebungshelligkeit in Lux und liefert damit einen realen Sensorwert für die Telemetrie. Im Beispiel wird der Sensor regelmäßig ausgelesen, sodass der aktuelle Helligkeitswert kontinuierlich an den ConnectHub übertragen werden kann.

Ziel des Beispiels

Das Demo-Objekt zeigt, wie ein ESP32 mit der App und dem ConnectHub zusammenarbeitet. Dabei werden mehrere Aufgaben kombiniert:

Der ESP32 verbindet sich über die ActionConnect Library mit den vorgesehenen Kommunikationswegen. Über BLE kann die App direkt mit dem ESP32 kommunizieren. Über MQTT können Telemetrie-Daten an den Broker beziehungsweise an den ConnectHub gesendet werden. Gleichzeitig liest der ESP32 regelmäßig Sensordaten vom BH1750 aus und verarbeitet Steuerbefehle für die LED.

Damit zeigt das Beispiel den grundlegenden Ablauf, den auch andere Geräte später verwenden können: Hardware initialisieren, Kommunikation starten, Befehle empfangen, Zustand ändern und aktuelle Werte zurückmelden.

Ablauf des gesamten Beispiels

Der Ablauf des Demo-Objekts lässt sich so zusammenfassen:

Beim Start initialisiert der ESP32 seine Geräteidentität, die LED, den BH1750-Sensor und die Kommunikation über die ActionConnect Library. Danach wartet das Gerät dauerhaft auf eingehende Befehle und liest regelmäßig den Sensor aus.

Wenn die App oder der ConnectHub einen Befehl sendet, wird dieser über den gemeinsamen Callback verarbeitet. Der ESP32 prüft, welche Capability angesprochen wurde, ändert den internen Zustand und veröffentlicht anschließend den neuen Zustand.

Wenn ein neuer Sensorwert gemessen wird, wird dieser ebenfalls veröffentlicht. Dadurch bleiben App und ConnectHub über den aktuellen Gerätezustand informiert.

sequenceDiagram
    participant Boot
    participant HW as Hardware (LED, BH1750)
    participant AC as ActionConnect
    participant App/Hub as App / ConnectHub

    Boot->>HW: setupHardware()
    Boot->>AC: setupConnectivity()

    loop Hauptschleife
        App/Hub-->>AC: Befehl (BLE oder MQTT)
        AC-->>Boot: onTransportMessage()
        Boot->>HW: Zustand ändern (LED)
        Boot->>App/Hub: publishCurrentState()

        HW-->>Boot: neuer Lux-Wert
        Boot->>App/Hub: publishTelemetry()
    end

Bedeutung für eigene Geräte

Dieses Beispiel zeigt, wie eine Gruppe ein eigenes Gerät aufbauen kann. Die Grundstruktur bleibt dabei meistens gleich:

  • Hardware definieren und initialisieren
  • Zustandsvariablen für das Gerät anlegen
  • Telemetrie-Payload passend zum Thing Model erzeugen
  • Eingehende Befehle auswerten
  • Änderungen am Gerät ausführen
  • Den neuen Zustand zurückmelden

Für ein eigenes Projekt müssen vor allem die Capabilities, die Hardwarefunktionen und das Telemetrie-Payload angepasst werden. Die grundlegende Verbindung über die ActionConnect Library und die Verarbeitung über BLE und MQTT können als Vorlage übernommen werden.

Wichtig für die App-Anbindung

Damit ein Gerät korrekt mit der App verbunden werden kann, müssen Code, Thing Model und Registrierung zusammenpassen. Die Capability-Namen im Code müssen exakt den Namen entsprechen, die im Thing Model verwendet werden.

Wenn die App zum Beispiel eine Capability brightness sendet, muss der ESP32 im Code ebenfalls nach brightness suchen. Wenn der ESP32 einen Wert light_lux sendet, muss dieser Wert auch im Thing Model vorgesehen sein.

Das Demo-Objekt zeigt deshalb nicht nur die technische Kommunikation, sondern auch die notwendige Struktur für die spätere Integration in die App.

Aufbau des Geräte-Zustands

Das Demo-Objekt speichert intern den aktuellen Zustand der Onboard-LED, die eingestellte Helligkeit, eine optionale Gerätenotiz sowie den zuletzt gemessenen Lux-Wert des BH1750-Sensors. Diese Werte bilden den aktuellen Gerätezustand ab und werden immer dann aktualisiert, wenn ein neuer Sensorwert gemessen oder ein Befehl über die App beziehungsweise den ConnectHub empfangen wird.

Telemetrie und Capabilities

Die aktuellen Gerätedaten werden als Telemetrie an den ConnectHub übertragen. Dazu gehören der Status der LED, die Helligkeit, der gemessene Lux-Wert und die Gerätenotiz. Die verwendeten Bezeichnungen wie power, brightness, light_lux und device_note müssen mit den Capabilities im Thing Model übereinstimmen, damit die Werte in der App korrekt angezeigt und gesteuert werden können.

Wenn sich ein Zustand ändert, wird der aktuelle Gerätezustand erneut veröffentlicht. Dadurch erhält die App direkt eine Rückmeldung und zeigt den neuen Stand des Geräts an.

Verarbeitung eingehender Befehle

Eingehende Nachrichten können entweder über BLE oder über MQTT empfangen werden. Beide Kommunikationswege werden im Beispiel über dieselbe zentrale Verarbeitung weitergegeben. Dadurch muss die eigentliche Gerätelogik nicht unterscheiden, ob ein Befehl direkt aus der App per BLE oder über den ConnectHub per MQTT kommt.

Das Demo-Objekt kann einfache LED-Befehle sowie JSON-basierte Befehle verarbeiten. Über diese Befehle können beispielsweise die LED ein- oder ausgeschaltet, die Helligkeit angepasst oder eine Gerätenotiz gesetzt werden. Entscheidend ist dabei, dass der ESP32 die gleichen Capability-Namen verwendet wie das zugehörige Thing Model.

Hardware- und Kommunikations-Setup

Beim Start werden zunächst die Onboard-LED und der BH1750-Sensor initialisiert. Die LED wird über PWM gesteuert, wodurch nicht nur Ein- und Ausschalten, sondern auch verschiedene Helligkeitsstufen möglich sind. Der BH1750 wird für die regelmäßige Messung der Umgebungshelligkeit vorbereitet.

Anschließend wird die Kommunikation über die ActionConnect Library eingerichtet. Dabei wird festgelegt, welche Funktion eingehende Nachrichten verarbeitet, und danach werden die vorgesehenen Kommunikationswege wie BLE, WLAN und MQTT gestartet.

Programmablauf

Nach dem Start ist der ESP32 bereit, Befehle zu empfangen und Sensordaten zu senden. In der Hauptschleife werden die Kommunikationswege regelmäßig verarbeitet und der BH1750-Sensor in festen Abständen ausgelesen.

Wenn ein gültiger Lux-Wert gemessen wird, veröffentlicht das Gerät diesen zusammen mit dem aktuellen Zustand der LED. Wenn ein Befehl empfangen wird, wird der entsprechende Zustand angepasst und ebenfalls direkt an den ConnectHub zurückgemeldet.

Das Beispiel zeigt damit den typischen Ablauf für ein eigenes verbundenes Gerät: Hardware initialisieren, Kommunikation starten, Befehle empfangen, Gerätezustand ändern und aktuelle Daten an App und ConnectHub übertragen.

Beispielcode (gekürzt, Bh1750LedExample.cpp)

Die Hilfsfunktionen jsonEscape(), publishCurrentState() und handleLedTextCommand() sind hier zugunsten der Übersicht ausgelassen; sie folgen demselben einfachen Muster wie buildTelemetryPayload() bzw. handleJsonCommand().

#include "Bh1750LedExample.h"
#include "../Sensor/bh1750/bh1750.h"

// Dieses Beispiel zeigt die Integration des BH1750 Lichtsensors und einer LED mit MQTT und BLE. Es verbindet sich mit einem WLAN,
// liest die Helligkeit vom BH1750 Sensor, steuert eine LED basierend auf MQTT-Befehlen und veröffentlicht Telemetrie-Daten an einen MQTT-Broker.
// Außerdem ermöglicht es die Steuerung über BLE.

// Am Ende muss jede Gruppe eine ähnliche Datei erstellen welches folgenende Thing Vorlage enthält. Es können weitere Capabilities hinzugefügt werden, um mehr Funktionalität zu demonstrieren.
// Wichtig ist, dass die Thing Model Vorlage mit der Registrierung übereinstimmt, damit die Daten korrekt im ConnectHub angezeigt werden. Bitte auch Namen und ID anpassen, damit es eindeutig ist.

namespace {
const int LED_PIN = 2;
const int LEDC_CHANNEL = 0;
const int LEDC_FREQ_HZ = 5000;
const int LEDC_RES_BITS = 8;

const unsigned long SENSOR_INTERVAL_MS = 5000;

bool ledPower = false;
int ledBrightness = 0;
String deviceNote = "";
float lastLux = NAN;
unsigned long lastSensorSend = 0;

void setupDeviceIdentity() {
  String deviceMac = WiFi.macAddress();
  String sanitizedMac = deviceMac;
  sanitizedMac.replace(":", "");

  Serial.print("[Device] MAC: ");
  Serial.println(deviceMac);
  Serial.print("[Device] ID: ");
  Serial.println("esp32-" + sanitizedMac);
}

void setLedState(bool on, int brightness = -1) {
  ledPower = on;
  if (brightness >= 0) {
    ledBrightness = constrain(brightness, 0, 255);
  }
  ledcWrite(LEDC_CHANNEL, ledPower ? ledBrightness : 0);
}

String buildTelemetryPayload(float lux) {
  String payload = "{\"data\":{\"power\":\"";
  payload += ledPower ? "ON" : "OFF";
  payload += "\",\"brightness\":";
  payload += String(ledBrightness);
  payload += ",\"light_lux\":";
  payload += String(lux, 2);
  payload += ",\"device_note\":\"";
  payload += jsonEscape(deviceNote);
  payload += "\"}}";
  return payload;
}

void publishTelemetry(float lux) {
  lastLux = lux;
  publish_telemetry(buildTelemetryPayload(lux));
}

Eingehende Befehle laufen über einen zentralen Dispatcher, egal ob sie per BLE-Zeile (LED:...) oder als JSON-Objekt ({"capability": ..., "value": ...}) über BLE oder MQTT hereinkommen:

flowchart TD
    Msg["onTransportMessage(source, topic, payload)"] --> IsJson{"beginnt mit '{'?"}
    IsJson -->|ja| Json["handleJsonCommand()<br/>capability + value auswerten"]
    IsJson -->|nein| Text["handleLedTextCommand()<br/>'LED:ON' / 'LED:OFF' / 'LED:&lt;wert&gt;'"]
    Json --> Cap{"capability"}
    Cap -->|power| SetPower[setLedState]
    Cap -->|brightness| SetBrightness[setLedState]
    Cap -->|device_note| SetNote[deviceNote setzen]
    SetPower --> Publish[publishCurrentState]
    SetBrightness --> Publish
    SetNote --> Publish
    Text --> Publish
void handleJsonCommand(const String& json) {
  String capability = connectHubExtractJsonValue(json, "capability");
  String value = connectHubExtractJsonValue(json, "value");
  capability.trim();
  capability.toLowerCase();

  if (capability == "power") {
    setLedState(value == "ON" || value == "true", ledBrightness > 0 ? ledBrightness : 255);
  } else if (capability == "brightness") {
    setLedState(true, value.toInt());
  } else if (capability == "device_note") {
    deviceNote = value;
  } else {
    return;
  }
  publishCurrentState();
}

void onTransportMessage(const char* source, const char* topic, const String& payload) {
  if (payload.startsWith("{")) {
    handleJsonCommand(payload);
  } else {
    handleLedTextCommand(payload);
  }
}

void setupHardware() {
  ledcSetup(LEDC_CHANNEL, LEDC_FREQ_HZ, LEDC_RES_BITS);
  ledcAttachPin(LED_PIN, LEDC_CHANNEL);
  setLedState(false, 0);
  setupBh1750(21, 22, SENSOR_INTERVAL_MS);
}

void setupConnectivity() {
  // Initialize transports (WiFi, MQTT, BLE, etc.) via the ActionConnect library.
  connectHubSetReceiveCallback(onTransportMessage);
  connectHubInit();
}
}  // namespace

void setupBh1750LedExample() {
  Serial.begin(115200);
  delay(50);
  setupDeviceIdentity();
  setupHardware();
  setupConnectivity();
  Serial.println("Setup complete: MQTT + BLE + BH1750 + LED");
}

void loopBh1750LedExample() {
  // Handle web server client (AP web config) and transports (MQTT/BLE)
  server.handleClient();
  connectHubProcessTransport();

  // Sensor read/send loop
  if (bh1750IsReady() && millis() - lastSensorSend >= SENSOR_INTERVAL_MS) {
    const float lux = readBh1750Lux();
    if (!isnan(lux)) {
      publishTelemetry(lux);
      lastSensorSend = millis();
    }
  }

  delay(10);
}

Siehe auch