/* * ESP32 iBeacon v2 - Optimiert für Indoor-Positionierung * * Dieser Sketch konfiguriert einen ESP32 als reinen iBeacon-Sender * ohne Service-Funktionalität (kein GATT, keine Verbindungen). * * Optimierungen gegenüber v1: * - Service-Teil entfernt (nur Advertising, keine Verbindungen) * - Festes 100ms Advertising-Intervall für stabile RSSI-Werte * - TX-Power explizit auf Maximum (+9 dBm) fixiert * - Kalibrierte Signal-Power (-65 dBm @ 1m) * - Korrekte Advertising-Flags (0x06) * - BLE-Sleep zwischen Advertising-Events für Akkubetrieb * * WICHTIG: Beim Flashen mehrerer ESP32s die BEACON_MINOR Konstante ändern! * * Basiert auf Analyse-Empfehlungen aus ESP32-Code-Analyse.md * (Gemini + Claude, 2026-02-09) */ #include #include #include #include "esp_bt.h" // Für esp_bt_sleep_enable() #include "esp_mac.h" // Für esp_base_mac_addr_set() (ESP-IDF v4.0+) // ===================================================================== // BEACON KONFIGURATION - VOR DEM FLASHEN ANPASSEN! // ===================================================================== // WICHTIG: Für jeden ESP32-Beacon eine ANDERE Minor-ID vergeben (1, 2, 3, ...) #define BEACON_MINOR 1 // ===================================================================== // MAC-ADRESS-OVERRIDE (optional) - AKTIVIEREN FALLS ANDROID BLACKLISTING // ===================================================================== // Wenn ein Android-Gerät diesen Beacon nicht findet (auch nicht mit nRF Connect), // obwohl andere Handys ihn sehen, könnte die MAC-Adresse geblockt sein. // Lösung: Auskommentieren + neue MAC setzen + neu flashen // // #define USE_CUSTOM_MAC uint8_t custom_mac[6] = {0x02, 0xAA, 0xBB, 0xCC, 0xDD, 0x08}; // Locally administered MAC (Byte 0=0x02), Byte 5=Minor ID // Gruppen-ID (für alle Beacons im selben System gleich) #define BEACON_MAJOR 1 // ===================================================================== // SYSTEM-KONSTANTEN (normalerweise nicht ändern) // ===================================================================== #define DEVICE_NAME "ESP32 iBeacon" #define BEACON_UUID "87b99b2c-9080-4d04-8f00-a5d9a3f9a0e7" #define BEACON_UUID_REV "e7a0f9a3-d9a5-008f-044d8090-2c9bb987" // Advertising-Intervall: 0xA0 = 160 * 0.625ms = 100ms #define ADVERTISING_INTERVAL 0xA0 // Kalibrierter RSSI-Wert bei 1m Abstand (als Signed Byte) // -65 dBm = 0xBF — muss mit RSSI_AT_ONE_METER in der Android-App übereinstimmen // TODO: Pro Beacon individuell kalibrieren (60s RSSI-Messung bei 1m Abstand) #define SIGNAL_POWER_1M 0xBF void setup() { Serial.begin(115200); delay(2000); // USB-CDC braucht Zeit zum Verbinden (ESP32-C3) Serial.println(); Serial.println("================================="); Serial.println("ESP32 iBeacon v2 - Indoor-Positioning"); Serial.println("================================="); Serial.flush(); // Optional: Custom MAC setzen (MUSS vor BLEDevice::init() passieren!) #ifdef USE_CUSTOM_MAC esp_base_mac_addr_set(custom_mac); Serial.print("Custom MAC gesetzt: "); for (int i = 0; i < 6; i++) { Serial.printf("%02X", custom_mac[i]); if (i < 5) Serial.print(":"); } Serial.println(); #endif // BLE-Stack initialisieren BLEDevice::init(DEVICE_NAME); // BLE-MAC-Adresse ausgeben (zur Diagnose von Blacklisting-Problemen) uint8_t ble_mac[6]; esp_read_mac(ble_mac, ESP_MAC_BT); Serial.printf("BLE MAC: %02X:%02X:%02X:%02X:%02X:%02X\n", ble_mac[0], ble_mac[1], ble_mac[2], ble_mac[3], ble_mac[4], ble_mac[5]); //Serial.flush(); // TX-Power auf Maximum setzen für beste Reichweite und konsistente RSSI-Werte // Wichtig: Muss VOR dem Advertising-Start gesetzt werden // TX-Power explizit für Advertising setzen (BLEDevice::setPower setzt nur DEFAULT, nicht ADV) esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_ADV, ESP_PWR_LVL_P9); // +9 dBm für Advertising esp_ble_tx_power_set(ESP_BLE_PWR_TYPE_DEFAULT, ESP_PWR_LVL_P9); // +9 dBm als Default Serial.println("TX-Power: +9 dBm (Maximum für ESP32-C3)"); // iBeacon-Paket zusammenbauen BLEBeacon myBeacon; myBeacon.setManufacturerId(0x004C); // Apple Company ID (Little-Endian) myBeacon.setMajor(BEACON_MAJOR); myBeacon.setMinor(BEACON_MINOR); myBeacon.setSignalPower(SIGNAL_POWER_1M); // Kalibrierter RSSI bei 1m myBeacon.setProximityUUID(BLEUUID(BEACON_UUID_REV)); // Advertising-Daten konfigurieren BLEAdvertisementData advertisementData; advertisementData.setFlags(0x06); // LE General Discoverable + BR/EDR Not Supported advertisementData.setManufacturerData(myBeacon.getData()); // Advertising-Parameter setzen BLEAdvertising* pAdvertising = BLEDevice::getAdvertising(); pAdvertising->setScanResponse(false); pAdvertising->setAdvertisementData(advertisementData); pAdvertising->setMinInterval(ADVERTISING_INTERVAL); // 100ms pAdvertising->setMaxInterval(ADVERTISING_INTERVAL); // Min = Max = konstantes Intervall // BLE-Sleep aktivieren: ESP32 geht automatisch in Light-Sleep zwischen // den Advertising-Events → spart Strom im Akkubetrieb esp_bt_sleep_enable(); // Advertising starten pAdvertising->start(); // Status ausgeben Serial.println(); Serial.println("iBeacon aktiv!"); Serial.println("----------------------------"); Serial.printf("UUID: %s\n", BEACON_UUID); Serial.printf("Major: %d\n", BEACON_MAJOR); Serial.printf("Minor: %d\n", BEACON_MINOR); Serial.printf("Signal: %d dBm @ 1m\n", (int8_t)SIGNAL_POWER_1M); Serial.printf("Intervall: 100ms\n"); Serial.printf("TX-Power: +9 dBm\n"); Serial.println("----------------------------"); } void loop() { // Keine Aktion nötig - ESP32 advertised kontinuierlich. // Zwischen Advertising-Events befindet sich der BLE-Stack im Light-Sleep. delay(10000); }