From 18fb79d954feed5ab052a74d112cf4093285f08c Mon Sep 17 00:00:00 2001 From: Roman Bazalevskiy Date: Wed, 4 Nov 2020 08:50:11 +0300 Subject: [PATCH] Initial support for iBeacons (Sensor52) on ESP32 using internal BLE module. Differs from MI32 (Sensor62): - Dynamically detects beacons, no preset MACs needed - Reports detected beacon immediately, not on tele period. --- tasmota/xsns_52_ibeacon.ino | 318 +++++++++++++++++++++++++++++++----- 1 file changed, 274 insertions(+), 44 deletions(-) diff --git a/tasmota/xsns_52_ibeacon.ino b/tasmota/xsns_52_ibeacon.ino index a733e97da..9d1850da9 100755 --- a/tasmota/xsns_52_ibeacon.ino +++ b/tasmota/xsns_52_ibeacon.ino @@ -19,44 +19,13 @@ #ifdef USE_IBEACON - - #define XSNS_52 52 -#include - -#define TMSBSIZ52 512 - -#define HM17_BAUDRATE 9600 - -#define IBEACON_DEBUG - -// use this for Version 110 -#define HM17_V110 - - // keyfob expires after N seconds #define IB_TIMEOUT_INTERVAL 30 // does a passive scan every N seconds #define IB_UPDATE_TIME_INTERVAL 10 -TasmotaSerial *IBEACON_Serial = nullptr; - - -uint8_t hm17_found,hm17_cmd,hm17_flag; - -#ifdef IBEACON_DEBUG -uint8_t hm17_debug=0; -#endif - - -// 78 is max serial response -#define HM17_BSIZ 128 -char hm17_sbuffer[HM17_BSIZ]; -uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; -uint32_t hm17_lastms; -char ib_mac[14]; - // should be in Settings #if 1 uint8_t ib_upd_interval,ib_tout_interval; @@ -69,9 +38,71 @@ uint8_t ib_upd_interval,ib_tout_interval; #define IB_TIMEOUT_TIME Settings.ib_tout_interval #endif +char ib_mac[14]; + +#ifdef USE_IBEACON_ESP32 + +struct { + union { + struct { + uint32_t init:1; + uint32_t autoScan:1; + uint32_t canScan:1; + uint32_t runningScan:1; + uint32_t shallClearResults:1; // BLE scan results + uint32_t firstAutodiscoveryDone:1; + uint32_t activeBeacon; + }; + uint32_t all = 0; + } mode; + struct { + uint8_t sensor; // points to to the number 0...255 + uint8_t beaconScanCounter; // countdown timer in seconds + } state; +} ESP32BLE; + +#include +#include +#include "NimBLEEddystoneURL.h" +#include "NimBLEEddystoneTLM.h" +#include "NimBLEBeacon.h" + +#define ENDIAN_CHANGE_U16(x) ((((x)&0xFF00) >> 8) + (((x)&0xFF) << 8)) + +BLEScan *ESP32BLEScan; + +#else + +#include + +#define TMSBSIZ52 512 + +#define HM17_BAUDRATE 9600 + +#define IBEACON_DEBUG + +// use this for Version 110 +#define HM17_V110 + +TasmotaSerial *IBEACON_Serial = nullptr; + +uint8_t hm17_found,hm17_cmd,hm17_flag; + +#ifdef IBEACON_DEBUG +uint8_t hm17_debug=0; +#endif + +// 78 is max serial response +#define HM17_BSIZ 128 +char hm17_sbuffer[HM17_BSIZ]; +uint8_t hm17_sindex,hm17_result,hm17_scanning,hm17_connecting; +uint32_t hm17_lastms; + enum {HM17_TEST,HM17_ROLE,HM17_IMME,HM17_DISI,HM17_IBEA,HM17_SCAN,HM17_DISC,HM17_RESET,HM17_RENEW,HM17_CON}; #define HM17_SUCESS 99 +#endif + struct IBEACON { char FACID[8]; char UID[32]; @@ -82,7 +113,11 @@ struct IBEACON { char RSSI[4]; }; -#define MAX_IBEACONS 16 +#ifdef USE_IBEACON_ESP32 + #define MAX_IBEACONS 32 +#else + #define MAX_IBEACONS 16 +#endif struct IBEACON_UID { char MAC[12]; @@ -94,9 +129,154 @@ struct IBEACON_UID { uint8_t TIME; } ibeacons[MAX_IBEACONS]; +#ifdef USE_IBEACON_ESP32 + +uint32_t ibeacon_add(struct IBEACON *ib); + +void ESP32BLE_ReverseStr(uint8_t _mac[], uint8_t len=6){ + uint8_t _reversedMAC[len]; + for (uint8_t i=0; i>4) & 0xF]; + pout[1] = hex[ pgm_read_byte(pin) & 0xF]; + } +} + + +class ESP32BLEScanCallback : public BLEAdvertisedDeviceCallbacks +{ + void onResult(BLEAdvertisedDevice *advertisedDevice) + { + struct IBEACON ib; + ESP32BLEScan->erase(advertisedDevice->getAddress()); + if (advertisedDevice->haveManufacturerData() == true) { + std::string strManufacturerData = advertisedDevice->getManufacturerData(); + + uint8_t cManufacturerData[100]; + strManufacturerData.copy((char *)cManufacturerData, strManufacturerData.length(), 0); + + int16_t RSSI = advertisedDevice->getRSSI(); + char sRSSI[6]; + itoa(RSSI,sRSSI,10); + + DumpHex(cManufacturerData,2,ib.FACID); + + uint8_t MAC[6]; + memcpy(MAC,advertisedDevice->getAddress().getNative(),6); + ESP32BLE_ReverseStr(MAC,6); + + if (strManufacturerData.length() == 25 && cManufacturerData[0] == 0x4C && cManufacturerData[1] == 0x00) + { + BLEBeacon oBeacon = BLEBeacon(); + oBeacon.setData(strManufacturerData); + + uint8_t UUID[16]; + memcpy(UUID,oBeacon.getProximityUUID().getNative()->u128.value,16); + ESP32BLE_ReverseStr(UUID,16); + + uint16_t Major = ENDIAN_CHANGE_U16(oBeacon.getMajor()); + uint16_t Minor = ENDIAN_CHANGE_U16(oBeacon.getMinor()); + + uint8_t PWR = oBeacon.getSignalPower(); + + AddLog_P2(LOG_LEVEL_DEBUG, PSTR("MAC: %s Major: %d Minor: %d UUID: %s Power: %d RSSI: %d"), + advertisedDevice->getAddress().toString().c_str(), + Major, Minor, + oBeacon.getProximityUUID().toString().c_str(), + PWR, RSSI); + + DumpHex((const unsigned char*)&UUID,16,ib.UID); + DumpHex((const unsigned char*)&Major,2,ib.MAJOR); + DumpHex((const unsigned char*)&Minor,2,ib.MINOR); + DumpHex((const unsigned char*)&PWR,1,ib.PWR); + DumpHex((const unsigned char*)&MAC,6,ib.MAC); + memcpy(ib.RSSI,sRSSI,4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR); + } + + } else { + + memset(ib.UID,'0',32); + memset(ib.MAJOR,'0',4); + memset(ib.MINOR,'0',4); + memset(ib.PWR,'0',2); + DumpHex((const unsigned char*)&MAC,6,ib.MAC); + memcpy(ib.RSSI,sRSSI,4); + + if (ibeacon_add(&ib)) { + ibeacon_mqtt(ib.MAC,ib.RSSI,ib.UID,ib.MAJOR,ib.MINOR); + } + } + } + } +}; + +void ESP32StartScanTask(){ + ESP32BLE.mode.runningScan = 1; + xTaskCreatePinnedToCore( + ESP32ScanTask, /* Function to implement the task */ + "ESP32ScanTask", /* Name of the task */ + 2048, /* Stack size in words */ + NULL, /* Task input parameter */ + 0, /* Priority of the task */ + NULL, /* Task handle. */ + 0); /* Core where the task should run */ + AddLog_P2(LOG_LEVEL_DEBUG,PSTR("%s: Start scanning"),"IBEACON_ESP32"); +} + +void ESP32scanEndedCB(NimBLEScanResults results); + +void ESP32ScanTask(void *pvParameters){ + if (ESP32BLEScan == nullptr) ESP32BLEScan = NimBLEDevice::getScan(); + ESP32BLEScan->setInterval(70); + ESP32BLEScan->setWindow(50); + ESP32BLEScan->setAdvertisedDeviceCallbacks(new ESP32BLEScanCallback()); + ESP32BLEScan->setActiveScan(false); + ESP32BLEScan->setDuplicateFilter(false); + + for (;;) { + ESP32BLEScan->start(0, ESP32scanEndedCB, false); + } + +} + +void ESP32scanEndedCB(NimBLEScanResults results) { + ESP32BLE.mode.runningScan = 0; +} + +#endif void IBEACON_Init() { +#ifdef USE_IBEACON_ESP32 + + ESP32BLE.mode.init = false; + if (!ESP32BLE.mode.init) { + NimBLEDevice::init(""); + + ESP32BLE.mode.canScan = 1; + ESP32BLE.mode.init = 1; + + ESP32StartScanTask(); // Let's get started !! + + IB_UPDATE_TIME=IB_UPDATE_TIME_INTERVAL; + IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; + } + +#else + hm17_found=0; // actually doesnt work reliably with software serial @@ -113,8 +293,27 @@ void IBEACON_Init() { IB_TIMEOUT_TIME=IB_TIMEOUT_INTERVAL; } } + +#endif + } +#ifdef USE_IBEACON_ESP32 + +void esp32_every_second(void) { + for (uint32_t cnt=0;cntIB_TIMEOUT_TIME) { + ibeacons[cnt].FLAGS=0; + ibeacon_mqtt(ibeacons[cnt].MAC,"0000",ibeacons[cnt].UID,ibeacons[cnt].MAJOR,ibeacons[cnt].MINOR); + } + } + } +} + +#else + void hm17_every_second(void) { if (!IBEACON_Serial) return; @@ -196,6 +395,8 @@ void hm17_sendcmd(uint8_t cmd) { } } +#endif + uint32_t ibeacon_add(struct IBEACON *ib) { /* if (!strncmp(ib->MAJOR,"4B1C",4)) { return 0; @@ -242,6 +443,8 @@ uint32_t ibeacon_add(struct IBEACON *ib) { return 0; } +#ifndef USE_IBEACON_ESP32 + void hm17_decode(void) { struct IBEACON ib; switch (hm17_cmd) { @@ -438,8 +641,16 @@ hm17_v110: } } +#endif + void IBEACON_loop() { +#ifdef USE_IBEACON_ESP32 + + return; + +#else + if (!IBEACON_Serial) return; uint32_t difftime=millis()-hm17_lastms; @@ -464,6 +675,8 @@ uint32_t difftime=millis()-hm17_lastms; } } +#endif + } #ifdef USE_WEBSERVER @@ -523,7 +736,20 @@ bool xsns52_cmd(void) { uint16_t len=XdrvMailbox.data_len; if (len > 0) { char *cp=XdrvMailbox.data; - if (*cp>='0' && *cp<='8') { + if (*cp=='u') { + cp++; + if (*cp) IB_UPDATE_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); + } else if (*cp=='t') { + cp++; + if (*cp) IB_TIMEOUT_TIME=atoi(cp); + Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); + } else if (*cp=='c') { + for (uint32_t cnt=0;cnt='0' && *cp<='8') { hm17_sendcmd(*cp&7); Response_P(S_JSON_IBEACON, XSNS_52,"hm17cmd",*cp&7); } else if (*cp=='s') { @@ -536,18 +762,8 @@ bool xsns52_cmd(void) { IBEACON_Serial->write((uint8_t*)cp,len); hm17_cmd=99; Response_P(S_JSON_IBEACON1, XSNS_52,"hm17cmd",cp); - } else if (*cp=='u') { - cp++; - if (*cp) IB_UPDATE_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"uintv",IB_UPDATE_TIME); - } else if (*cp=='t') { - cp++; - if (*cp) IB_TIMEOUT_TIME=atoi(cp); - Response_P(S_JSON_IBEACON, XSNS_52,"lintv",IB_TIMEOUT_TIME); - } else if (*cp=='c') { - for (uint32_t cnt=0;cnt