/* Major thank you to the following contributors for their efforts: pcbreflux for the original version of this code, as well as the eddystone handlers. Andreis Speiss for his work on YouTube and his invaluable github at sensorsiot Based on Neil Kolban example for IDF: https://github.com/nkolban/esp32-snippets/blob/master/cpp_utils/tests/BLE%20Tests/SampleScan.cpp Ported to Arduino ESP32 by Evandro Copercini */ #include extern "C" { #include "freertos/FreeRTOS.h" #include "freertos/timers.h" } #include "soc/timer_group_struct.h" #include "soc/timer_group_reg.h" #include #include #include #include #include #include #include #include "BLEBeacon.h" #include "BLEEddystoneTLM.h" #include "BLEEddystoneURL.h" #include "Settings_local.h" BLEScan* pBLEScan; int scanTime = 5; //In seconds int waitTime = scanInterval; //In seconds uint16_t beconUUID = 0xFEAA; #define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00)>>8) + (((x)&0xFF)<<8)) WiFiClient espClient; AsyncMqttClient mqttClient; TimerHandle_t mqttReconnectTimer; TimerHandle_t wifiReconnectTimer; String getProximityUUIDString(BLEBeacon beacon) { std::string serviceData = beacon.getProximityUUID().toString().c_str(); int serviceDataLength = serviceData.length(); String returnedString = ""; int i = serviceDataLength; while (i > 0) { if (serviceData[i-1] == '-') { i--; } char a = serviceData[i-1]; char b = serviceData[i-2]; returnedString += b; returnedString += a; i -= 2; } return returnedString; } float calculateDistance(int rssi, int txPower) { if (rssi == 0) { return -1.0; } if (!txPower) { // somewhat reasonable default value txPower = -59; } const float ratio = rssi * 1.0 / txPower; if (ratio < 1.0) { return pow(ratio, 10); } else { return (0.89976) * pow(ratio, 7.7095) + 0.111; } } void connectToWifi() { Serial.println("Connecting to WiFi..."); WiFi.begin(ssid, password); WiFi.setHostname(hostname); } void connectToMqtt() { Serial.println("Connecting to MQTT"); mqttClient.setCredentials(mqttUser, mqttPassword); mqttClient.connect(); } void WiFiEvent(WiFiEvent_t event) { Serial.print("[WiFi-event] event:"); Serial.println(event); switch(event) { case SYSTEM_EVENT_STA_GOT_IP: Serial.println("WiFi connected"); Serial.println("IP address: "); Serial.println(WiFi.localIP()); connectToMqtt(); break; case SYSTEM_EVENT_STA_DISCONNECTED: Serial.println("WiFi lost connection"); xTimerStop(mqttReconnectTimer, 0); // ensure we don't reconnect to MQTT while reconnecting to Wi-Fi xTimerStart(wifiReconnectTimer, 0); break; } } void onMqttConnect(bool sessionPresent) { Serial.println("Connected to MQTT."); Serial.print("Session present: "); Serial.println(sessionPresent); if (mqttClient.publish(availabilityTopic, 0, 0, "CONNECTED") == true) { Serial.print("Success sending message to topic:\t"); Serial.println(availabilityTopic); } else { Serial.println("Error sending message"); } } void onMqttDisconnect(AsyncMqttClientDisconnectReason reason) { Serial.println("Disconnected from MQTT."); if (WiFi.isConnected()) { xTimerStart(mqttReconnectTimer, 0); } } void onMqttPublish(uint16_t packetId) { Serial.println("Publish acknowledged."); Serial.print(" packetId: "); Serial.println(packetId); } void reportDevice(BLEAdvertisedDevice advertisedDevice) { // Serial.printf("\n\n"); StaticJsonBuffer<500> JSONbuffer; JsonObject& JSONencoder = JSONbuffer.createObject(); String mac_address = advertisedDevice.getAddress().toString().c_str(); mac_address.replace(":",""); mac_address.toLowerCase(); int rssi = advertisedDevice.getRSSI(); JSONencoder["id"] = mac_address; JSONencoder["uuid"] = mac_address; JSONencoder["rssi"] = rssi; if (advertisedDevice.haveName()){ String nameBLE = String(advertisedDevice.getName().c_str()); // Serial.print("Name: "); // Serial.println(nameBLE); JSONencoder["name"] = nameBLE; } else { JSONencoder["name"] = "unknown"; } // Serial.printf("\n\n"); // Serial.printf("Advertised Device: %s \n", advertisedDevice.toString().c_str()); std::string strServiceData = advertisedDevice.getServiceData(); uint8_t cServiceData[100]; strServiceData.copy((char *)cServiceData, strServiceData.length(), 0); if (advertisedDevice.getServiceDataUUID().equals(BLEUUID(beconUUID))==true) { // found Eddystone UUID // Serial.printf("is Eddystone: %d %s length %d\n", advertisedDevice.getServiceDataUUID().bitSize(), advertisedDevice.getServiceDataUUID().toString().c_str(),strServiceData.length()); if (cServiceData[0]==0x10) { BLEEddystoneURL oBeacon = BLEEddystoneURL(); oBeacon.setData(strServiceData); // Serial.printf("Eddystone Frame Type (Eddystone-URL) "); // Serial.printf(oBeacon.getDecodedURL().c_str()); JSONencoder["url"] = oBeacon.getDecodedURL().c_str(); } else if (cServiceData[0]==0x20) { BLEEddystoneTLM oBeacon = BLEEddystoneTLM(); oBeacon.setData(strServiceData); // Serial.printf("Eddystone Frame Type (Unencrypted Eddystone-TLM) \n"); // Serial.printf(oBeacon.toString().c_str()); } else { for (int i=0;i (waitTime * 1000) || last == 0)) { Serial.print("Scanning...\t"); BLEScanResults foundDevices = pBLEScan->start(scanTime); Serial.printf("Scan done! Devices found: %d\t",foundDevices.getCount()); for (uint32_t i = 0; i < foundDevices.getCount(); i++) { // Serial.printf("Getting device %d",i); reportDevice(foundDevices.getDevice(i)); } last = millis(); Serial.println("Reports sent"); } } } void setup() { Serial.begin(115200); mqttReconnectTimer = xTimerCreate("mqttTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast(connectToMqtt)); wifiReconnectTimer = xTimerCreate("wifiTimer", pdMS_TO_TICKS(2000), pdFALSE, (void*)0, reinterpret_cast(connectToWifi)); WiFi.onEvent(WiFiEvent); mqttClient.onConnect(onMqttConnect); mqttClient.onDisconnect(onMqttDisconnect); mqttClient.setServer(mqttHost, mqttPort); mqttClient.setWill(availabilityTopic, 0, 0, "DISCONNECTED"); connectToWifi(); BLEDevice::init(""); pBLEScan = BLEDevice::getScan(); //create new scan // pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks()); pBLEScan->setActiveScan(true); //active scan uses more power, but get results faster // pBLEScan->setInterval(100); // pBLEScan->setWindow(200); xTaskCreatePinnedToCore( scanForDevices, "BLE Scan", 4096, pBLEScan, 1, &BLEScan, 1); } void loop() { TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; TIMERG0.wdt_feed=1; TIMERG0.wdt_wprotect=0; }