# ESP32 iBeacon Code-Analyse Analyse des ESP32-Codes (`ESP32_iBeacon/ESP32_iBeacon.ino`) auf mögliche Ursachen für instabile RSSI-Messwerte in der Android-App. Datum: 2026-02-09 --- ## Quelle: Gemini-Analyse Die folgenden Probleme wurden von Google Gemini identifiziert: ### 1. KRITISCH: Advertising-Konflikt zwischen Service und iBeacon **Gemini-Befund:** `init_service()` und `init_beacon()` überschreiben sich gegenseitig das Advertising-Paket. `init_service()` startet Advertising mit Service-UUID, dann stoppt `init_beacon()` alles und überschreibt es mit iBeacon-Daten. Das Service-Advertising geht verloren. Beim Reconnect nach Disconnect wird `pAdvertising->start()` aufgerufen, aber es ist unklar, welche Advertising-Daten dann aktiv sind. **Gemini-Empfehlung:** Kombinierte Funktion nutzen, die iBeacon-Daten ins Haupt-Paket und die Service-UUID ins Scan-Response-Paket legt. **Eigene Einschätzung:** Bestätigt. Im Code wird in Zeile 78 (`init_service`) Advertising gestartet, in Zeile 85 (`init_beacon`) sofort wieder gestoppt und neu konfiguriert. Das ist zwar für den reinen iBeacon-Betrieb kein Problem (das letzte Paket gewinnt), aber der `onDisconnect`-Callback (Zeile 29) startet Advertising erneut, ohne die iBeacon-Daten neu zu setzen. Nach einer Verbindung könnten also fehlerhafte Advertising-Pakete gesendet werden. ### 2. HOCH: Kein explizites Advertising-Intervall gesetzt **Gemini-Befund:** Ohne explizites Intervall nutzt der ESP32 den BLE-Stack-Default. Unregelmäßige Pakete führen zu variierenden Abtastraten auf der Android-Seite und damit zu instabilen RSSI-Werten. **Gemini-Empfehlung:** Festes Intervall von 100ms setzen: ```cpp pAdvertising->setMinInterval(0xA0); // 160 * 0.625ms = 100ms pAdvertising->setMaxInterval(0xA0); ``` **Eigene Einschätzung:** Bestätigt und sehr relevant. Der Default-Intervall liegt typischerweise bei 100-1280ms, was für Indoor-Positioning zu unregelmäßig ist. Ein festes, kurzes Intervall (100ms) sorgt für konsistente Datenrate auf der Android-Seite. Die Android-App scannt bereits mit `SCAN_MODE_LOW_LATENCY` (BleScanner.kt:165), profitiert aber nur davon, wenn der ESP32 auch regelmäßig sendet. ### 3. HOCH: TX-Power nicht explizit konfiguriert **Gemini-Befund:** Die Sendeleistung wird nicht festgelegt, was zu variierender Ausgangsleistung führen kann. **Gemini-Empfehlung:** ```cpp BLEDevice::setPower(ESP_PWR_LVL_P9); // +9 dBm ``` **Eigene Einschätzung:** Bestätigt. Ohne fixe TX-Power kann der ESP32 die Leistung dynamisch anpassen. Für RSSI-basierte Distanzschätzung ist eine konstante Sendeleistung zwingend notwendig, da die gesamte Berechnung auf dem Log-Distance Path Loss Model basiert. Hinweis: Der Seeed XIAO ESP32-C3 unterstützt bis zu +21 dBm – der korrekte Enum-Wert muss ggf. angepasst werden. ### 4. MITTEL: setSignalPower nicht kalibriert **Gemini-Befund:** Der Wert `0xc5` (-59 dBm) im iBeacon-Paket ist ein Standardwert und nicht für die verwendete Hardware kalibriert. **Gemini-Empfehlung:** RSSI bei exakt 1m Abstand messen (60s Durchschnitt) und den gemessenen Wert als Signed Byte in Hex setzen. **Eigene Einschätzung:** Teilweise relevant. Der `signalPower`-Wert wird in der Android-App aktuell nicht für die Distanzberechnung verwendet – stattdessen wird `BeaconData.RSSI_AT_ONE_METER = -65.0` in Kotlin hardcoded (BeaconData.kt:37). Die App liest zwar `txPower` aus dem Paket (BleScanner.kt:145), nutzt ihn aber nicht im PositionCalculator. Trotzdem sollte der Wert korrekt sein, falls er zukünftig genutzt wird. ### 5. MITTEL: Advertising-Flags **Gemini-Befund:** Flags `0x1A` statt Standard-iBeacon `0x06`. **Gemini-Empfehlung:** `0x06` verwenden (LE General Discoverable + BR/EDR Not Supported). **Eigene Einschätzung:** Geringes Risiko. `0x1A` setzt zusätzlich das Bit für "Simultaneous LE and BR/EDR (Controller)" – bei einem reinen BLE-Beacon unnötig, aber in der Praxis unproblematisch. `0x06` wäre sauberer. ### 6. NIEDRIG: Manufacturer ID Byte-Order **Gemini-Befund:** ESP32 sendet `0x004C` (Little-Endian, korrekt laut BLE-Spec). Android-App parst mit `0x4C00`. **Gemini-Empfehlung:** Android-App prüfen. **Eigene Einschätzung:** Kein Problem. Android's `getManufacturerSpecificData()` erwartet den Company Identifier in Host-Byte-Order. Der Wert `0x4C00` in der Android-App (BeaconData.kt:12) ist korrekt für Apple's Company ID und funktioniert nachweislich (die App empfängt ja Beacons). Die ESP32-Bibliothek übernimmt die Konvertierung intern. --- ## Eigene Zusatzanalyse (Claude) Über die Gemini-Analyse hinaus sind folgende Punkte aufgefallen: ### 7. HOCH: loop() sendet Notifications alle 2 Sekunden In `loop()` (Zeile 114-123) wird bei bestehender Verbindung alle 2 Sekunden eine Notification gesendet. Das ist für den iBeacon-Betrieb irrelevant, aber: Wenn sich ein Gerät verbindet, könnte das Advertising unterbrochen oder verlangsamt werden. Der ESP32 BLE-Stack kann nicht gleichzeitig eine aktive Verbindung bedienen und mit voller Rate advertisen. **Empfehlung:** Für reinen Beacon-Betrieb den gesamten Service-Teil entfernen (init_service, MyCallbacks, MyServerCallbacks, Notification-Code in loop). Der ESP32 soll nur advertisen, keine Verbindungen annehmen. ### 8. MITTEL: Kein Deep-Sleep / Light-Sleep zwischen Advertisements Für Akkubetrieb fehlt jegliche Energiespar-Logik. Der ESP32 läuft permanent im aktiven Modus, auch zwischen den Advertising-Events. **Empfehlung:** Für den Seeed XIAO ESP32-C3 im Akkubetrieb Light-Sleep aktivieren oder den ESP32 zwischen Advertisements in den Deep-Sleep schicken und per Timer aufwecken. ### 9. NIEDRIG: PositionSmoother Alpha-Wert Diskrepanz In der CLAUDE.md und im PositionSmoother-Defaultwert steht alpha=0.25, aber in BleScanner.kt:52 wird `PositionSmoother(alpha = 0.15)` instanziiert. Der tatsächlich verwendete Wert ist 0.15 (reaktiver als dokumentiert). --- ## Zusammenfassung: Prioritäten | # | Priorität | Problem | Quelle | |---|-----------|--------------------------------------|---------| | 1 | KRITISCH | Advertising-Konflikt Service/iBeacon | Gemini | | 2 | HOCH | Kein Advertising-Intervall gesetzt | Gemini | | 3 | HOCH | TX-Power nicht fixiert | Gemini | | 7 | HOCH | Verbindungen stören Advertising | Claude | | 4 | MITTEL | SignalPower nicht kalibriert | Gemini | | 5 | MITTEL | Advertising-Flags suboptimal | Gemini | | 8 | MITTEL | Kein Energiesparmodus | Claude | | 9 | NIEDRIG | Alpha-Wert Diskrepanz Doku vs Code | Claude | | 6 | NIEDRIG | Manufacturer ID Byte-Order | Gemini | ## Empfohlene Maßnahmen (Reihenfolge) 1. Service-Teil komplett entfernen (nur iBeacon-Advertising, keine Verbindungen) 2. Festes Advertising-Intervall setzen (100ms) 3. TX-Power explizit auf Maximum fixieren 4. SignalPower im iBeacon-Paket kalibrieren (RSSI @ 1m messen) 5. Flags auf 0x06 ändern 6. Light-Sleep für Akkubetrieb implementieren 7. Alpha-Wert in CLAUDE.md korrigieren (0.15 statt 0.25)