162 lines
6.9 KiB
Markdown
162 lines
6.9 KiB
Markdown
# 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)
|