2021-03-22 14:11:42 +01:00
|
|
|
#include "BleFingerprint.h"
|
2021-04-07 02:13:13 +02:00
|
|
|
#include "util.h"
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-25 16:46:19 +02:00
|
|
|
#define Sprintf(f, ...) ( \
|
|
|
|
{ \
|
|
|
|
char *s; \
|
|
|
|
asprintf(&s, f, __VA_ARGS__); \
|
|
|
|
String r = s; \
|
|
|
|
free(s); \
|
|
|
|
r; \
|
|
|
|
})
|
|
|
|
|
|
|
|
#define SMacf(f) ( \
|
|
|
|
{ \
|
|
|
|
auto nativeAddress = f.getNative(); \
|
|
|
|
Sprintf("%02x%02x%02x%02x%02x%02x", nativeAddress[5], nativeAddress[4], nativeAddress[3], nativeAddress[2], nativeAddress[1], nativeAddress[0]); \
|
|
|
|
})
|
|
|
|
|
2021-09-25 16:08:30 +02:00
|
|
|
static std::string hexStr(const char *data, int len)
|
|
|
|
{
|
|
|
|
constexpr char hexmap[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
|
|
|
|
|
|
|
|
std::string s(len * 2, ' ');
|
|
|
|
for (int i = 0; i < len; ++i)
|
|
|
|
{
|
|
|
|
s[2 * i] = hexmap[(data[i] & 0xF0) >> 4];
|
|
|
|
s[2 * i + 1] = hexmap[data[i] & 0x0F];
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::string hexStr(std::string s)
|
|
|
|
{
|
|
|
|
return hexStr(s.c_str(), s.length());
|
|
|
|
}
|
|
|
|
|
2021-09-25 16:46:19 +02:00
|
|
|
String BleFingerprint::getMac() { return SMacf(address); }
|
|
|
|
|
2021-04-05 04:12:46 +02:00
|
|
|
BleFingerprint::BleFingerprint(BLEAdvertisedDevice *advertisedDevice, float fcmin, float beta, float dcutoff) : oneEuro{one_euro_filter<double, unsigned long>(1, fcmin, beta, dcutoff)}
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-09 14:16:40 +02:00
|
|
|
if (advertisedDevice->getAddressType() == BLE_ADDR_PUBLIC)
|
|
|
|
macPublic = true;
|
|
|
|
|
2021-04-02 13:56:27 +02:00
|
|
|
firstSeenMicros = esp_timer_get_time();
|
2021-03-28 20:35:54 +02:00
|
|
|
address = advertisedDevice->getAddress();
|
2021-04-07 02:13:13 +02:00
|
|
|
newest = recent = oldest = rssi = advertisedDevice->getRSSI();
|
2021-03-28 20:35:54 +02:00
|
|
|
|
2021-09-15 18:11:32 +02:00
|
|
|
fingerprint(advertisedDevice);
|
|
|
|
}
|
|
|
|
|
|
|
|
void BleFingerprint::fingerprint(BLEAdvertisedDevice *advertisedDevice)
|
|
|
|
{
|
2021-03-22 14:11:42 +01:00
|
|
|
if (advertisedDevice->haveName())
|
2021-03-24 18:16:49 +01:00
|
|
|
name = String(advertisedDevice->getName().c_str());
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-11 20:04:43 +02:00
|
|
|
if (advertisedDevice->haveServiceUUID())
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-16 01:19:50 +02:00
|
|
|
if (advertisedDevice->isAdvertisingService(tileUUID))
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = "tile:" + getMac();
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
2021-09-16 01:19:50 +02:00
|
|
|
else if (advertisedDevice->isAdvertisingService(exposureUUID))
|
2021-09-11 20:04:43 +02:00
|
|
|
{ // found covid exposure tracker
|
2021-09-16 01:19:50 +02:00
|
|
|
std::string strServiceData = advertisedDevice->getServiceData(exposureUUID);
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = "exp:" + String(strServiceData.length());
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
2021-09-16 01:19:50 +02:00
|
|
|
else if (advertisedDevice->isAdvertisingService(sonosUUID))
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-16 01:19:50 +02:00
|
|
|
pid = "sonos:" + getMac();
|
|
|
|
}
|
|
|
|
else if (advertisedDevice->isAdvertisingService(eddystoneUUID))
|
2021-09-11 20:04:43 +02:00
|
|
|
{ // found Eddystone UUID
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-16 01:19:50 +02:00
|
|
|
std::string strServiceData = advertisedDevice->getServiceData(eddystoneUUID);
|
2021-09-15 18:11:32 +02:00
|
|
|
if (strServiceData[0] == EDDYSTONE_URL_FRAME_TYPE && strServiceData.length() <= 18)
|
2021-09-11 20:04:43 +02:00
|
|
|
{
|
|
|
|
BLEEddystoneURL oBeacon = BLEEddystoneURL();
|
|
|
|
oBeacon.setData(strServiceData);
|
|
|
|
url = String(oBeacon.getDecodedURL().c_str());
|
2021-09-17 01:36:52 +02:00
|
|
|
calRssi = oBeacon.getPower() - 41;
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
2021-09-15 18:11:32 +02:00
|
|
|
else if (strServiceData[0] == EDDYSTONE_TLM_FRAME_TYPE)
|
2021-09-11 20:04:43 +02:00
|
|
|
{
|
|
|
|
BLEEddystoneTLM oBeacon = BLEEddystoneTLM();
|
|
|
|
oBeacon.setData(strServiceData);
|
2021-09-15 18:11:32 +02:00
|
|
|
temp = oBeacon.getTemp();
|
|
|
|
volts = oBeacon.getVolt();
|
|
|
|
#ifdef VERBOSE
|
|
|
|
Serial.println(oBeacon.toString().c_str());
|
|
|
|
#endif
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
2021-03-24 18:16:49 +01:00
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-11 20:04:43 +02:00
|
|
|
String fingerprint = "sid:";
|
2021-03-24 18:16:49 +01:00
|
|
|
for (int i = 0; i < advertisedDevice->getServiceUUIDCount(); i++)
|
|
|
|
{
|
|
|
|
std::string sid = advertisedDevice->getServiceUUID(i).toString();
|
2021-09-11 20:04:43 +02:00
|
|
|
fingerprint = fingerprint + String(sid.c_str());
|
2021-03-24 18:16:49 +01:00
|
|
|
}
|
2021-09-11 20:04:43 +02:00
|
|
|
if (advertisedDevice->haveTXPower())
|
|
|
|
fingerprint = fingerprint + String(-advertisedDevice->getTXPower());
|
2021-09-15 18:11:32 +02:00
|
|
|
sid = fingerprint;
|
2021-03-24 18:16:49 +01:00
|
|
|
}
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
2021-09-18 19:05:07 +02:00
|
|
|
|
|
|
|
if (advertisedDevice->haveManufacturerData())
|
2021-09-11 20:04:43 +02:00
|
|
|
{
|
|
|
|
std::string strManufacturerData = advertisedDevice->getManufacturerData();
|
2021-09-25 16:08:30 +02:00
|
|
|
#ifdef VERBOSE
|
|
|
|
Serial.printf("Verbose | %-58sMD: %s\n", getId().c_str(), hexStr(strManufacturerData).c_str());
|
|
|
|
#endif
|
2021-09-11 20:04:43 +02:00
|
|
|
if (strManufacturerData.length() > 2)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-15 18:11:32 +02:00
|
|
|
String manuf = Sprintf("%02x%02x", strManufacturerData[1], strManufacturerData[0]);
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-11 20:04:43 +02:00
|
|
|
if (manuf == "004c") // Apple
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
2021-09-11 20:04:43 +02:00
|
|
|
if (strManufacturerData.length() == 25 && strManufacturerData[2] == 0x02 && strManufacturerData[3] == 0x15)
|
2021-03-22 14:11:42 +01:00
|
|
|
{
|
|
|
|
BLEBeacon oBeacon = BLEBeacon();
|
|
|
|
oBeacon.setData(strManufacturerData);
|
2021-09-16 15:11:35 +02:00
|
|
|
pid = Sprintf("iBeacon:%s-%d-%d", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor()));
|
2021-08-24 00:10:53 +02:00
|
|
|
calRssi = oBeacon.getSignalPower();
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (advertisedDevice->haveTXPower())
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = Sprintf("apple:%02x%02x:%d%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length(), -advertisedDevice->getTXPower());
|
|
|
|
else
|
|
|
|
pid = Sprintf("apple:%02x%02x:%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
2021-09-11 20:04:43 +02:00
|
|
|
else if (manuf == "05a7") //Sonos
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = "sonos:" + getMac();
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
2021-09-15 22:27:02 +02:00
|
|
|
else if (manuf == "0157") //Mi-fit
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 22:27:02 +02:00
|
|
|
pid = "mifit:" + getMac();
|
|
|
|
}
|
2021-09-11 20:04:43 +02:00
|
|
|
else if (manuf == "0006" && strManufacturerData.length() == 29) //microsoft
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = Sprintf("microsoft:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
|
|
|
|
strManufacturerData[6], strManufacturerData[7], strManufacturerData[8], strManufacturerData[9], strManufacturerData[10], strManufacturerData[11],
|
|
|
|
strManufacturerData[12], strManufacturerData[13], strManufacturerData[14], strManufacturerData[15], strManufacturerData[16], strManufacturerData[17],
|
|
|
|
strManufacturerData[18], strManufacturerData[19], strManufacturerData[20], strManufacturerData[21], strManufacturerData[22], strManufacturerData[23],
|
|
|
|
strManufacturerData[24], strManufacturerData[25], strManufacturerData[26], strManufacturerData[27], strManufacturerData[28]);
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
|
|
|
else if (manuf == "0075") //samsung
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
pid = "samsung:" + getMac();
|
2021-09-11 20:04:43 +02:00
|
|
|
}
|
2021-03-22 14:11:42 +01:00
|
|
|
else
|
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
calcRssi = advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() - 65 : NO_RSSI;
|
2021-09-15 18:11:32 +02:00
|
|
|
String fingerprint = Sprintf("md:%s:%d", manuf.c_str(), strManufacturerData.length());
|
2021-09-11 20:04:43 +02:00
|
|
|
if (advertisedDevice->haveTXPower())
|
|
|
|
fingerprint = fingerprint + String(-advertisedDevice->getTXPower());
|
2021-09-15 18:11:32 +02:00
|
|
|
sid = fingerprint;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
bool BleFingerprint::filter()
|
|
|
|
{
|
|
|
|
Reading<float> inter1, inter2;
|
|
|
|
|
|
|
|
if (tsFilter.push(&raw, &inter1))
|
|
|
|
{
|
|
|
|
inter2.timestamp = inter1.timestamp;
|
|
|
|
inter2.value = oneEuro(inter1.value);
|
|
|
|
if (diffFilter.push(&inter2, &output))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-22 14:11:42 +01:00
|
|
|
void BleFingerprint::seen(BLEAdvertisedDevice *advertisedDevice)
|
|
|
|
{
|
2021-04-02 13:56:27 +02:00
|
|
|
lastSeenMicros = esp_timer_get_time();
|
2021-03-29 20:50:23 +02:00
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
oldest = recent;
|
|
|
|
recent = newest;
|
|
|
|
newest = advertisedDevice->getRSSI();
|
|
|
|
rssi = median_of_3(oldest, recent, newest);
|
|
|
|
|
2021-09-15 18:11:32 +02:00
|
|
|
fingerprint(advertisedDevice);
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-17 01:36:52 +02:00
|
|
|
float ratio = (get1mRssi() - rssi) / 35.0f;
|
2021-03-30 00:12:59 +02:00
|
|
|
raw = pow(10, ratio);
|
2021-03-24 14:10:33 +01:00
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
if (filter())
|
2021-03-22 23:56:29 +01:00
|
|
|
{
|
2021-04-07 02:13:13 +02:00
|
|
|
hasValue = true;
|
|
|
|
reported = false;
|
2021-03-22 23:56:29 +01:00
|
|
|
}
|
2021-03-29 20:50:23 +02:00
|
|
|
}
|
|
|
|
|
2021-04-07 02:13:13 +02:00
|
|
|
void BleFingerprint::setInitial(int initalRssi, float initalDistance)
|
2021-03-30 00:12:59 +02:00
|
|
|
{
|
2021-04-07 02:13:13 +02:00
|
|
|
newest = recent = oldest = rssi = initalRssi;
|
|
|
|
raw = initalDistance;
|
|
|
|
hasValue = filter() || filter();
|
2021-03-30 00:12:59 +02:00
|
|
|
}
|
|
|
|
|
2021-09-25 05:05:12 +02:00
|
|
|
bool BleFingerprint::report(JsonDocument *doc, float maxDistance)
|
2021-03-29 20:50:23 +02:00
|
|
|
{
|
2021-09-15 18:11:32 +02:00
|
|
|
if (pid.isEmpty() && sid.isEmpty() && !macPublic)
|
2021-03-29 20:50:23 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
if (!hasValue)
|
|
|
|
return false;
|
2021-03-24 14:10:33 +01:00
|
|
|
|
2021-08-24 01:22:33 +02:00
|
|
|
if (maxDistance > 0 && output.value.position > maxDistance)
|
|
|
|
return false;
|
2021-03-31 19:36:20 +02:00
|
|
|
|
2021-04-02 13:56:27 +02:00
|
|
|
if (reported)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
auto now = esp_timer_get_time();
|
|
|
|
|
2021-08-24 00:10:53 +02:00
|
|
|
if (abs(output.value.position - lastReported) < 0.1f && abs(now - lastReportedMicros) < 15000000)
|
2021-04-02 13:56:27 +02:00
|
|
|
return false;
|
|
|
|
|
|
|
|
lastReportedMicros = now;
|
|
|
|
lastReported = output.value.position;
|
|
|
|
reported = true;
|
|
|
|
|
2021-03-30 00:12:59 +02:00
|
|
|
String mac = SMacf(address);
|
2021-03-29 21:51:18 +02:00
|
|
|
if (output.value.position < 0.5)
|
|
|
|
{
|
2021-08-23 15:48:40 +02:00
|
|
|
if (!close)
|
2021-03-29 21:51:18 +02:00
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
Display.close(this);
|
2021-08-23 15:48:40 +02:00
|
|
|
close = true;
|
2021-03-29 21:51:18 +02:00
|
|
|
}
|
|
|
|
}
|
2021-08-23 15:48:40 +02:00
|
|
|
else if (close && output.value.position > 1.5)
|
2021-03-29 21:51:18 +02:00
|
|
|
{
|
2021-09-17 02:29:43 +02:00
|
|
|
Display.left(this);
|
2021-08-23 15:48:40 +02:00
|
|
|
close = false;
|
2021-03-29 21:51:18 +02:00
|
|
|
}
|
2021-03-22 14:11:42 +01:00
|
|
|
|
2021-09-15 18:11:32 +02:00
|
|
|
(*doc)[F("id")] = getId();
|
2021-08-24 05:59:48 +02:00
|
|
|
if (!name.isEmpty()) (*doc)[F("name")] = name;
|
2021-03-30 00:12:59 +02:00
|
|
|
|
2021-09-17 01:36:52 +02:00
|
|
|
(*doc)[F("rssi@1m")] = get1mRssi();
|
2021-03-30 00:12:59 +02:00
|
|
|
(*doc)[F("rssi")] = rssi;
|
2021-03-29 20:50:23 +02:00
|
|
|
|
2021-03-30 00:12:59 +02:00
|
|
|
(*doc)[F("mac")] = mac;
|
|
|
|
(*doc)[F("raw")] = round(raw * 100.0f) / 100.0f;
|
|
|
|
(*doc)[F("distance")] = round(output.value.position * 100.0f) / 100.0f;
|
|
|
|
(*doc)[F("speed")] = round(output.value.speed * 1e7f) / 10.0f;
|
2021-03-29 20:50:23 +02:00
|
|
|
|
2021-09-15 18:11:32 +02:00
|
|
|
if (volts) (*doc)[F("volts")] = volts;
|
|
|
|
if (temp) (*doc)[F("temp")] = temp;
|
|
|
|
|
2021-03-30 00:12:59 +02:00
|
|
|
return true;
|
2021-03-22 14:11:42 +01:00
|
|
|
}
|