Loads of improvements (#372)

* Disable unused ble functions to free up some memory
* Added Count of devices present in room
* Added known macs
* Reorg settings
* Improvements to ignoring (trailing spaces matched everything)
* Add battery percent to ATC sensors
* Remove sprite usage to clean up memory usage
* Initial support for samsung smart tag (may not actually work) (#351)
* DHT Sensor temp related changes (#385)
* Add interval, fix rounding
* Add batt for MACCHINA_A0
* Add running to macchina
* Add Garmin watch (Fenix 6) id
* Updgrade ArduinoJson

Co-authored-by: tr-v-r <83373608+tr-v-r@users.noreply.github.com>
This commit is contained in:
Darrell 2022-03-13 21:54:50 -04:00 committed by GitHub
parent e8699f0279
commit 54721b03be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 512 additions and 357 deletions

View File

@ -4,10 +4,9 @@ import time
#this list contains array of esp32 clients, #this list contains array of esp32 clients,
# and each client contains mDNS name and the path to .bin file # and each client contains mDNS name and the path to .bin file
#I only have 1 ESP so I duplicate mDNS entry for testing
esps = [ esps = [
#mDNS name of ESP #path to ".bin" file #mDNS name of ESP #path to ".bin" file
['192.168.128.114', 'macchina-a0'], ['192.168.128.112', 'macchina-a0'],
['192.168.128.124', 'm5stickc'], ['192.168.128.124', 'm5stickc'],
['192.168.128.64', 'm5atom-matrix'], ['192.168.128.64', 'm5atom-matrix'],
['192.168.128.84', 'm5atom-matrix'] ['192.168.128.84', 'm5atom-matrix']

View File

@ -4,23 +4,15 @@
#include "strings.h" #include "strings.h"
#include "util.h" #include "util.h"
bool prefixExists(const String& prefixes, const String& id) class ClientCallbacks : public BLEClientCallbacks
{ {
unsigned int start = 0; bool onConnParamsUpdateRequest(NimBLEClient *pClient, const ble_gap_upd_params *params)
unsigned int space = 0;
while ((space = prefixes.indexOf(" ", start)) != -1)
{ {
if (space > start) return true;
{ };
auto sub = prefixes.substring(start, space); };
if (sub == "*" || id.indexOf(sub) != -1) return true;
} static ClientCallbacks clientCB;
start = space + 1;
}
auto sub = prefixes.substring(start);
return (sub == "*" || id.indexOf(sub) != -1);
}
bool BleFingerprint::shouldHide(const String& s) bool BleFingerprint::shouldHide(const String& s)
{ {
@ -30,13 +22,14 @@ bool BleFingerprint::shouldHide(const String& s)
bool BleFingerprint::setId(const String& newId, short newIdType, const String& newName) bool BleFingerprint::setId(const String& newId, short newIdType, const String& newName)
{ {
if (newIdType < idType) return false; if (newIdType < idType && idType > 0) return false;
hidden = shouldHide(newId); hidden = shouldHide(newId);
ignore = newIdType < 0;
if (!allowQuery) if (!allowQuery && !ignore)
{ {
if (BleFingerprintCollection::query.length() > 0 && prefixExists(BleFingerprintCollection::query, newId)) if (!BleFingerprintCollection::query.isEmpty() && prefixExists(BleFingerprintCollection::query, newId))
{ {
allowQuery = true; allowQuery = true;
qryAttempts = 0; qryAttempts = 0;
@ -48,6 +41,7 @@ bool BleFingerprint::setId(const String& newId, short newIdType, const String& n
} }
} }
countable = !ignore && !BleFingerprintCollection::countIds.isEmpty() && prefixExists(BleFingerprintCollection::countIds, newId);
id = newId; id = newId;
idType = newIdType; idType = newIdType;
if (!newName.isEmpty()) name = newName; if (!newName.isEmpty()) name = newName;
@ -66,9 +60,25 @@ BleFingerprint::BleFingerprint(const BleFingerprintCollection *parent, BLEAdvert
{ {
firstSeenMillis = millis(); firstSeenMillis = millis();
address = NimBLEAddress(advertisedDevice->getAddress()); address = NimBLEAddress(advertisedDevice->getAddress());
macPublic = advertisedDevice->getAddressType() == BLE_ADDR_PUBLIC;
newest = recent = oldest = rssi = advertisedDevice->getRSSI(); newest = recent = oldest = rssi = advertisedDevice->getRSSI();
seenCount = 1; seenCount = 1;
auto mac = getMac();
if (!BleFingerprintCollection::knownMacs.isEmpty() && prefixExists(BleFingerprintCollection::knownMacs, mac))
setId("known:" + mac, ID_TYPE_KNOWN_MAC);
else
{
switch (advertisedDevice->getAddressType())
{
case BLE_ADDR_PUBLIC:
case BLE_ADDR_PUBLIC_ID:
setId(mac, ID_TYPE_PUBLIC_MAC);
break;
default:
setId(mac, ID_TYPE_MAC);
break;
}
}
} }
void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice) void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice)
@ -81,13 +91,15 @@ void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice)
size_t serviceAdvCount = advertisedDevice->getServiceUUIDCount(); size_t serviceAdvCount = advertisedDevice->getServiceUUIDCount();
size_t serviceDataCount = advertisedDevice->getServiceDataCount(); size_t serviceDataCount = advertisedDevice->getServiceDataCount();
bool haveTxPower = advertisedDevice->haveTXPower();
int8_t txPower = advertisedDevice->getTXPower();
if (serviceAdvCount > 0) fingerprintServiceAdvertisements(advertisedDevice, serviceAdvCount); if (serviceAdvCount > 0) fingerprintServiceAdvertisements(advertisedDevice, serviceAdvCount, haveTxPower, txPower);
if (serviceDataCount > 0) fingerprintServiceData(advertisedDevice, serviceDataCount); if (serviceDataCount > 0) fingerprintServiceData(advertisedDevice, serviceDataCount, haveTxPower, txPower);
if (advertisedDevice->haveManufacturerData()) fingerprintManufactureData(advertisedDevice); if (advertisedDevice->haveManufacturerData()) fingerprintManufactureData(advertisedDevice, haveTxPower, txPower);
} }
void BleFingerprint::fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount) void BleFingerprint::fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount, bool haveTxPower, int8_t txPower)
{ {
for (size_t i = 0; i < serviceAdvCount; i++) for (size_t i = 0; i < serviceAdvCount; i++)
{ {
@ -118,135 +130,142 @@ void BleFingerprint::fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *ad
} }
else if (uuid == sonosUUID) else if (uuid == sonosUUID)
{ {
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("sonos:" + getMac(), ID_TYPE_SONOS); setId("sonos:" + getMac(), ID_TYPE_SONOS);
return; return;
} }
else if (uuid == itagUUID) else if (uuid == itagUUID)
{ {
asRssi = BleFingerprintCollection::refRssi + (advertisedDevice->haveTXPower() ? advertisedDevice->getTXPower() : ITAG_TX); asRssi = BleFingerprintCollection::refRssi + (haveTxPower ? txPower : ITAG_TX);
setId("itag:" + getMac(), ID_TYPE_ITAG); setId("itag:" + getMac(), ID_TYPE_ITAG);
return; return;
} }
else if (uuid == trackrUUID) else if (uuid == trackrUUID)
{ {
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("trackr:" + getMac(), ID_TYPE_TRACKR); setId("trackr:" + getMac(), ID_TYPE_TRACKR);
return; return;
} }
else if (uuid == vanmoofUUID) else if (uuid == vanmoofUUID)
{ {
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("vanmoof:" + getMac(), ID_TYPE_VANMOOF); setId("vanmoof:" + getMac(), ID_TYPE_VANMOOF);
return; return;
} }
else if (uuid == (meaterService)) else if (uuid == (meaterService))
{ {
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("meater:" + getMac(), ID_TYPE_MEATER); setId("meater:" + getMac(), ID_TYPE_MEATER);
return; return;
} }
} }
String fingerprint = "ad:"; String fingerprint = "ad:";
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
for (int i = 0; i < serviceAdvCount; i++) for (int i = 0; i < serviceAdvCount; i++)
{ {
std::string sid = advertisedDevice->getServiceUUID(i).toString(); std::string sid = advertisedDevice->getServiceUUID(i).toString();
fingerprint = fingerprint + sid.c_str(); fingerprint = fingerprint + sid.c_str();
} }
if (advertisedDevice->haveTXPower()) fingerprint = fingerprint + String(-advertisedDevice->getTXPower()); if (haveTxPower) fingerprint = fingerprint + String(-txPower);
setId(fingerprint, ID_TYPE_AD); setId(fingerprint, ID_TYPE_AD);
} }
void BleFingerprint::fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount) void BleFingerprint::fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount, bool haveTxPower, int8_t txPower)
{ {
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
String fingerprint = "sd:";
for (int i = 0; i < serviceDataCount; i++)
{ {
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; BLEUUID uuid = advertisedDevice->getServiceDataUUID(i);
String fingerprint = "sd:"; std::string strServiceData = advertisedDevice->getServiceData(i);
for (int i = 0; i < serviceDataCount; i++)
{
BLEUUID uuid = advertisedDevice->getServiceDataUUID(i);
std::string strServiceData = advertisedDevice->getServiceData(i);
#ifdef VERBOSE #ifdef VERBOSE
Serial.printf("Verbose | %-58sSD: %s/%s\n", getId().c_str(), uuid.toString().c_str(), hexStr(strServiceData).c_str()); Serial.printf("Verbose | %-58sSD: %s/%s\n", getId().c_str(), uuid.toString().c_str(), hexStr(strServiceData).c_str());
#endif #endif
if (uuid == exposureUUID) if (uuid == exposureUUID)
{ // found COVID-19 exposure tracker { // found COVID-19 exposure tracker
calRssi = BleFingerprintCollection::refRssi + EXPOSURE_TX; calRssi = BleFingerprintCollection::refRssi + EXPOSURE_TX;
setId("exp:" + String(strServiceData.length()), ID_TYPE_EXPOSURE); setId("exp:" + String(strServiceData.length()), ID_TYPE_EXPOSURE);
disc = hexStr(strServiceData).c_str(); disc = hexStr(strServiceData).c_str();
}
else if (uuid == miThermUUID)
{
asRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI;
if (strServiceData.length() == 15)
{ // custom format
auto serviceData = strServiceData.c_str();
temp = float(*(int16_t *)(serviceData + 6)) / 100.0f;
humidity = float(*(uint16_t *)(serviceData + 8)) / 100.0f;
mv = *(uint16_t *)(serviceData + 10);
#ifdef VERBOSE
Serial.printf("Temp: %.2f°, Humidity: %.2f%%, Vbatt: %d, Battery: %d%%, flg: 0x%02x, cout: %d\n", temp, humidity, mv, serviceData[12], serviceData[14], serviceData[13]);
#endif
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
}
else if (strServiceData.length() == 13)
{ // format atc1441
auto serviceData = strServiceData.c_str();
int16_t x = (serviceData[6] << 8) | serviceData[7];
temp = float(x) / 10.0f;
mv = x = (serviceData[10] << 8) | serviceData[11];
#ifdef VERBOSE
Serial.printf("Temp: %.1f°, Humidity: %d%%, Vbatt: %d, Battery: %d%%, cout: %d\n", temp, serviceData[8], mv, serviceData[9], serviceData[12]);
#endif
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
}
}
else if (uuid == eddystoneUUID && strServiceData.length() > 0)
{
if (strServiceData[0] == EDDYSTONE_URL_FRAME_TYPE && strServiceData.length() <= 18)
{
BLEEddystoneURL oBeacon = BLEEddystoneURL();
oBeacon.setData(strServiceData);
calRssi = EDDYSTONE_ADD_1M + oBeacon.getPower();
}
else if (strServiceData[0] == EDDYSTONE_TLM_FRAME_TYPE)
{
BLEEddystoneTLM oBeacon = BLEEddystoneTLM();
oBeacon.setData(strServiceData);
temp = oBeacon.getTemp();
mv = oBeacon.getVolt();
#ifdef VERBOSE
Serial.println(oBeacon.toString().c_str());
#endif
}
else if (strServiceData[0] == 0x00)
{
auto serviceData = strServiceData.c_str();
int8_t rss0m = *(int8_t *)(serviceData + 1);
calRssi = EDDYSTONE_ADD_1M + rss0m;
setId(Sprintf("eddy:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x-%02x%02x%02x%02x%02x%02x",
strServiceData[2], strServiceData[3], strServiceData[4], strServiceData[5], strServiceData[6],
strServiceData[6], strServiceData[7], strServiceData[8], strServiceData[9], strServiceData[10],
strServiceData[11], strServiceData[12], strServiceData[13], strServiceData[14], strServiceData[15],
strServiceData[16], strServiceData[17]),
ID_TYPE_EBEACON);
}
}
else
{
fingerprint = fingerprint + uuid.toString().c_str();
}
}
if (advertisedDevice->haveTXPower())
fingerprint = fingerprint + String(-advertisedDevice->getTXPower());
setId(fingerprint, ID_TYPE_SD);
} }
else if (uuid == smartTagUUID)
{ // found Samsung smart tag
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId(String("smarttag:") + hexStr(strServiceData).c_str(), ID_TYPE_SMARTTAG);
}
else if (uuid == miThermUUID)
{
asRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
if (strServiceData.length() == 15)
{ // custom format
auto serviceData = strServiceData.c_str();
temp = float(*(int16_t *)(serviceData + 6)) / 100.0f;
humidity = float(*(uint16_t *)(serviceData + 8)) / 100.0f;
mv = *(uint16_t *)(serviceData + 10);
battery = serviceData[12];
#ifdef VERBOSE
Serial.printf("Temp: %.1f°, Humidity: %.1f%%, mV: %hu, Battery: %hhu%%, flg: 0x%02hhx, cout: %hhu\n", temp, humidity, mv, battery, serviceData[14], serviceData[13]);
#endif
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
}
else if (strServiceData.length() == 13)
{ // format atc1441
auto serviceData = strServiceData.c_str();
int16_t x = (serviceData[6] << 8) | serviceData[7];
temp = float(x) / 10.0f;
humidity = serviceData[8];
mv = x = (serviceData[10] << 8) | serviceData[11];
battery = serviceData[9];
#ifdef VERBOSE
Serial.printf("Temp: %.1f°, Humidity: %.1f%%, mV: %hu, Battery: %hhu%%, cout: %hhu\n", temp, humidity, mv, battery, serviceData[12]);
#endif
setId("miTherm:" + getMac(), ID_TYPE_MITHERM);
}
}
else if (uuid == eddystoneUUID && strServiceData.length() > 0)
{
if (strServiceData[0] == EDDYSTONE_URL_FRAME_TYPE && strServiceData.length() <= 18)
{
BLEEddystoneURL oBeacon = BLEEddystoneURL();
oBeacon.setData(strServiceData);
calRssi = EDDYSTONE_ADD_1M + oBeacon.getPower();
}
else if (strServiceData[0] == EDDYSTONE_TLM_FRAME_TYPE)
{
BLEEddystoneTLM oBeacon = BLEEddystoneTLM();
oBeacon.setData(strServiceData);
temp = oBeacon.getTemp();
mv = oBeacon.getVolt();
#ifdef VERBOSE
Serial.println(oBeacon.toString().c_str());
#endif
}
else if (strServiceData[0] == 0x00)
{
auto serviceData = strServiceData.c_str();
int8_t rss0m = *(int8_t *)(serviceData + 1);
calRssi = EDDYSTONE_ADD_1M + rss0m;
setId(Sprintf("eddy:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x-%02x%02x%02x%02x%02x%02x",
strServiceData[2], strServiceData[3], strServiceData[4], strServiceData[5], strServiceData[6],
strServiceData[6], strServiceData[7], strServiceData[8], strServiceData[9], strServiceData[10],
strServiceData[11], strServiceData[12], strServiceData[13], strServiceData[14], strServiceData[15],
strServiceData[16], strServiceData[17]),
ID_TYPE_EBEACON);
}
}
else
{
fingerprint = fingerprint + uuid.toString().c_str();
}
}
if (haveTxPower)
fingerprint = fingerprint + String(-txPower);
setId(fingerprint, ID_TYPE_SD);
} }
void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice) void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice, bool haveTxPower, int8_t txPower)
{ {
std::string strManufacturerData = advertisedDevice->getManufacturerData(); std::string strManufacturerData = advertisedDevice->getManufacturerData();
#ifdef VERBOSE #ifdef VERBOSE
@ -262,46 +281,43 @@ void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertis
{ {
BLEBeacon oBeacon = BLEBeacon(); BLEBeacon oBeacon = BLEBeacon();
oBeacon.setData(strManufacturerData); oBeacon.setData(strManufacturerData);
setId(Sprintf("iBeacon:%s-%d-%d", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_IBEACON); setId(Sprintf("iBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_IBEACON);
calRssi = oBeacon.getSignalPower(); calRssi = oBeacon.getSignalPower();
} }
else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10) else if (strManufacturerData.length() >= 4 && strManufacturerData[2] == 0x10)
{ {
ignore = false; String pid = Sprintf("apple:%02x%02x:%zu", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
{ if (haveTxPower) pid += -txPower;
String pid; setId(pid, ID_TYPE_APPLE_NEARBY);
if (advertisedDevice->haveTXPower())
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());
setId(pid, ID_TYPE_APPLE_NEARBY);
}
disc = hexStr(strManufacturerData.substr(4)).c_str(); disc = hexStr(strManufacturerData.substr(4)).c_str();
mdRssi = BleFingerprintCollection::refRssi + APPLE_TX; mdRssi = BleFingerprintCollection::refRssi + APPLE_TX;
} }
else else if (strManufacturerData.length() >= 4)
{ {
if (advertisedDevice->haveTXPower()) String pid = Sprintf("apple:%02x%02x:%zu", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length());
setId(Sprintf("apple:%02x%02x:%d%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length(), -advertisedDevice->getTXPower()), ID_TYPE_MISC_APPLE + ID_TYPE_TX_POW); if (haveTxPower) pid += -txPower;
else setId(pid, ID_TYPE_MISC_APPLE);
setId(Sprintf("apple:%02x%02x:%d", strManufacturerData[2], strManufacturerData[3], strManufacturerData.length()), ID_TYPE_MISC_APPLE);
mdRssi = BleFingerprintCollection::refRssi + APPLE_TX; mdRssi = BleFingerprintCollection::refRssi + APPLE_TX;
ignore = true;
} }
} }
else if (manuf == "05a7") // Sonos else if (manuf == "05a7") // Sonos
{ {
mdRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("sonos:" + getMac(), ID_TYPE_SONOS); setId("sonos:" + getMac(), ID_TYPE_SONOS);
} }
else if (manuf == "0087") // Garmin
{
mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("garmin:" + getMac(), ID_TYPE_GARMIN);
}
else if (manuf == "0157") // Mi-fit else if (manuf == "0157") // Mi-fit
{ {
mdRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("mifit:" + getMac(), ID_TYPE_MIFIT); setId("mifit:" + getMac(), ID_TYPE_MIFIT);
} }
else if (manuf == "0006" && strManufacturerData.length() == 29) // microsoft else if (manuf == "0006" && strManufacturerData.length() == 29) // microsoft
{ {
mdRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId(Sprintf("msft:cdp:%02x%02x", strManufacturerData[3], strManufacturerData[5]), ID_TYPE_MSFT); setId(Sprintf("msft:cdp:%02x%02x", strManufacturerData[3], strManufacturerData[5]), ID_TYPE_MSFT);
disc = Sprintf("%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", disc = Sprintf("%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[6], strManufacturerData[7], strManufacturerData[8], strManufacturerData[9], strManufacturerData[10],
@ -311,22 +327,21 @@ void BleFingerprint::fingerprintManufactureData(NimBLEAdvertisedDevice *advertis
} }
else if (manuf == "0075") // samsung else if (manuf == "0075") // samsung
{ {
mdRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
setId("samsung:" + getMac(), ID_TYPE_MISC); setId("samsung:" + getMac(), ID_TYPE_MISC);
} }
else if (strManufacturerData.length() == 26 && strManufacturerData[2] == 0xBE && strManufacturerData[3] == 0xAC) else if (manuf == "beac" && strManufacturerData.length() == 26)
{ {
BLEBeacon oBeacon = BLEBeacon(); BLEBeacon oBeacon = BLEBeacon();
oBeacon.setData(strManufacturerData.substr(0, 25)); oBeacon.setData(strManufacturerData.substr(0, 25));
setId(Sprintf("altBeacon:%s-%d-%d", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_ABEACON); setId(Sprintf("altBeacon:%s-%u-%u", std::string(oBeacon.getProximityUUID()).c_str(), ENDIAN_CHANGE_U16(oBeacon.getMajor()), ENDIAN_CHANGE_U16(oBeacon.getMinor())), ID_TYPE_ABEACON);
calRssi = oBeacon.getSignalPower(); calRssi = oBeacon.getSignalPower();
} }
else if (manuf != "0000") else if (manuf != "0000")
{ {
mdRssi = advertisedDevice->haveTXPower() ? BleFingerprintCollection::refRssi + advertisedDevice->getTXPower() : NO_RSSI; mdRssi = haveTxPower ? BleFingerprintCollection::refRssi + txPower : NO_RSSI;
String fingerprint = Sprintf("md:%s:%d", manuf.c_str(), strManufacturerData.length()); String fingerprint = Sprintf("md:%s:%zu", manuf.c_str(), strManufacturerData.length());
if (advertisedDevice->haveTXPower()) if (haveTxPower) fingerprint = fingerprint + String(-txPower);
fingerprint = fingerprint + String(-advertisedDevice->getTXPower());
setId(fingerprint, ID_TYPE_MD); setId(fingerprint, ID_TYPE_MD);
} }
} }
@ -392,7 +407,7 @@ void BleFingerprint::setInitial(int initalRssi, float initalDistance)
bool BleFingerprint::report(JsonDocument *doc) bool BleFingerprint::report(JsonDocument *doc)
{ {
if (ignore || (idType == 0 && !macPublic) || hidden) if (ignore || idType == 0 || hidden)
return false; return false;
if (reported || !hasValue) if (reported || !hasValue)
@ -410,7 +425,7 @@ bool BleFingerprint::report(JsonDocument *doc)
lastReported = output.value.position; lastReported = output.value.position;
reported = true; reported = true;
(*doc)[F("id")] = getId(); (*doc)[F("id")] = id;
if (!name.isEmpty()) (*doc)[F("name")] = name; if (!name.isEmpty()) (*doc)[F("name")] = name;
if (!disc.isEmpty()) (*doc)[F("disc")] = disc; if (!disc.isEmpty()) (*doc)[F("disc")] = disc;
if (idType) (*doc)[F("idType")] = idType; if (idType) (*doc)[F("idType")] = idType;
@ -423,9 +438,12 @@ bool BleFingerprint::report(JsonDocument *doc)
(*doc)[F("speed")] = round(output.value.speed * 1e5f) / 100.0f; (*doc)[F("speed")] = round(output.value.speed * 1e5f) / 100.0f;
(*doc)[F("mac")] = SMacf(address); (*doc)[F("mac")] = SMacf(address);
(*doc)[F("interval")] = (now - firstSeenMillis) / seenCount;
if (mv) (*doc)[F("mV")] = mv; if (mv) (*doc)[F("mV")] = mv;
if (temp) (*doc)[F("temp")] = temp; if (battery != 0xFF) (*doc)[F("batt")] = battery;
if (humidity) (*doc)[F("rh")] = humidity; if (temp) (*doc)[F("temp")] = round(temp*10)/10;
if (humidity) (*doc)[F("rh")] = round(humidity*10)/10;
return true; return true;
} }
@ -436,7 +454,7 @@ bool BleFingerprint::query()
if (rssi < -90) return false; if (rssi < -90) return false;
auto now = millis(); auto now = millis();
if (now - lastSeenMillis > 5) return false; if (now - lastSeenMillis > 10) return false;
if (now - lastQryMillis < qryDelayMillis) return false; if (now - lastQryMillis < qryDelayMillis) return false;
didQuery = true; didQuery = true;
@ -444,14 +462,18 @@ bool BleFingerprint::query()
bool success = false; bool success = false;
Serial.printf("%d Query | MAC: %s, ID: %-60s rssi %d\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), rssi); Serial.printf("%u Query | MAC: %s, ID: %-60s %lums\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), now - lastSeenMillis);
NimBLEClient *pClient = NimBLEDevice::getClientListSize() ? NimBLEDevice::getClientByPeerAddress(address) : nullptr; NimBLEClient *pClient = NimBLEDevice::getClientListSize() ? NimBLEDevice::getClientByPeerAddress(address) : nullptr;
if (!pClient) pClient = NimBLEDevice::getDisconnectedClient(); if (!pClient) pClient = NimBLEDevice::getDisconnectedClient();
if (!pClient) pClient = NimBLEDevice::createClient(); if (!pClient) pClient = NimBLEDevice::createClient();
pClient->setClientCallbacks(&clientCB, false);
pClient->setConnectionParams(12, 12, 0, 200);
pClient->setConnectTimeout(5); pClient->setConnectTimeout(5);
NimBLEDevice::getScan()->stop();
if (pClient->connect(address)) if (pClient->connect(address))
{ {
delay(100);
bool iphone = true; bool iphone = true;
if (allowQuery) if (allowQuery)
{ {
@ -461,14 +483,18 @@ bool BleFingerprint::query()
if (!sName.empty() && sMdl.find(sName) == std::string::npos && sName != "Apple Watch") if (!sName.empty() && sMdl.find(sName) == std::string::npos && sName != "Apple Watch")
{ {
if (setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_QUERY_NAME, String(sName.c_str()))) if (setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_QUERY_NAME, String(sName.c_str())))
Serial.printf("\u001b[38;5;104m%d Name | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sName.c_str()); {
Serial.printf("\u001b[38;5;104m%u Name | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sName.c_str());
}
success = true; success = true;
} }
if (!sMdl.empty()) if (!sMdl.empty())
{ {
if (setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_QUERY_MODEL, String(sMdl.c_str()))) if (setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_QUERY_MODEL, String(sMdl.c_str())))
Serial.printf("\u001b[38;5;136m%d Model | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sMdl.c_str()); {
Serial.printf("\u001b[38;5;136m%u Model | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sMdl.c_str());
}
success = true; success = true;
} }
} }
@ -479,7 +505,9 @@ bool BleFingerprint::query()
if (!sRmAst.empty()) if (!sRmAst.empty())
{ {
if (setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST)) if (setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST))
Serial.printf("\u001b[38;5;129m%d RmAst | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), sRmAst.c_str()); {
Serial.printf("\u001b[38;5;129m%u RmAst | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), sRmAst.c_str());
}
success = true; success = true;
} }
} }
@ -490,7 +518,7 @@ bool BleFingerprint::query()
if (success) return true; if (success) return true;
qryAttempts++; qryAttempts++;
Serial.printf("%d QryErr| MAC: %s, ID: %-60s rssi %d, try %d, retry after %dms\n", xPortGetCoreID(), getMac().c_str(), getId().c_str(), rssi, qryAttempts, qryDelayMillis); Serial.printf("%u QryErr| MAC: %s, ID: %-60s rssi %d, try %d, retry after %dms\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, qryAttempts, qryDelayMillis);
if (qryDelayMillis < 30000) if (qryDelayMillis < 30000)
qryDelayMillis += (1000 * qryAttempts * qryAttempts); qryDelayMillis += (1000 * qryAttempts * qryAttempts);
@ -500,3 +528,23 @@ bool BleFingerprint::query()
return true; return true;
} }
bool BleFingerprint::shouldCount()
{
bool prevCounting = counting;
if (ignore || !countable)
counting = false;
else if (getMsSinceFirstSeen() <= BleFingerprintCollection::countMs || getMsSinceLastSeen() > BleFingerprintCollection::countMs)
counting = false;
else if (counting && output.value.position > BleFingerprintCollection::countExit)
counting = false;
else if (!counting && output.value.position <= BleFingerprintCollection::countEnter)
counting = true;
if (prevCounting != counting)
{
counting ? GUI::plusOne(this) : GUI::minusOne(this);
}
return counting;
}

View File

@ -15,17 +15,18 @@
#define NO_RSSI (-32768) #define NO_RSSI (-32768)
#define ID_TYPE_TX_POW short(1) #define ID_TYPE_TX_POW short(1)
#define ID_TYPE_MISC_APPLE short(-5)
#define ID_TYPE_MAC short(0) #define ID_TYPE_MAC short(0)
#define ID_TYPE_AD short(10) #define ID_TYPE_AD short(10)
#define ID_TYPE_SD short(15) #define ID_TYPE_SD short(15)
#define ID_TYPE_MD short(20) #define ID_TYPE_MD short(20)
#define ID_TYPE_MISC_APPLE short(25)
#define ID_TYPE_MISC short(30) #define ID_TYPE_MISC short(30)
#define ID_TYPE_NAME short(35) #define ID_TYPE_NAME short(35)
#define ID_TYPE_PUBLIC_MAC short(50) #define ID_TYPE_PUBLIC_MAC short(50)
#define ID_TYPE_MSFT short(100) #define ID_TYPE_MSFT short(100)
#define ID_TYPE_SONOS short(105) #define ID_TYPE_SONOS short(105)
#define ID_TYPE_GARMIN short(107)
#define ID_TYPE_MITHERM short(110) #define ID_TYPE_MITHERM short(110)
#define ID_TYPE_MIFIT short(115) #define ID_TYPE_MIFIT short(115)
#define ID_TYPE_EXPOSURE short(120) #define ID_TYPE_EXPOSURE short(120)
@ -34,6 +35,7 @@
#define ID_TYPE_TILE short( 135) #define ID_TYPE_TILE short( 135)
#define ID_TYPE_MEATER short(140) #define ID_TYPE_MEATER short(140)
#define ID_TYPE_VANMOOF short(145) #define ID_TYPE_VANMOOF short(145)
#define ID_TYPE_SMARTTAG short(146)
#define ID_TYPE_APPLE_NEARBY short(150) #define ID_TYPE_APPLE_NEARBY short(150)
#define ID_TYPE_QUERY_MODEL short(155) #define ID_TYPE_QUERY_MODEL short(155)
#define ID_TYPE_QUERY_NAME short(160) #define ID_TYPE_QUERY_NAME short(160)
@ -41,6 +43,7 @@
#define ID_TYPE_ABEACON short(170) #define ID_TYPE_ABEACON short(170)
#define ID_TYPE_IBEACON short(175) #define ID_TYPE_IBEACON short(175)
#define ID_TYPE_RM_ASST short(180) #define ID_TYPE_RM_ASST short(180)
#define ID_TYPE_KNOWN_MAC short(185)
class BleFingerprintCollection; class BleFingerprintCollection;
@ -56,13 +59,7 @@ public:
bool query(); bool query();
String getId() String getId() { return id; }
{
if (!id.isEmpty() && idType > 10) return id;
if (macPublic) return getMac();
if (!id.isEmpty()) return id;
return getMac();
}
bool setId(const String &newId, short int newIdType, const String &newName = ""); bool setId(const String &newId, short int newIdType, const String &newName = "");
@ -82,7 +79,9 @@ public:
NimBLEAddress const getAddress() { return address; } NimBLEAddress const getAddress() { return address; }
long getAge() const { return millis() - lastSeenMillis; }; unsigned long getMsSinceLastSeen() const { return millis() - lastSeenMillis; };
unsigned long getMsSinceFirstSeen() const { return millis() - firstSeenMillis; };
bool getAdded() const { return added; }; bool getAdded() const { return added; };
@ -92,26 +91,30 @@ public:
bool getRmAsst() const { return rmAsst; }; bool getRmAsst() const { return rmAsst; };
int getSeenCount() unsigned int getSeenCount()
{ {
auto sc = seenCount; auto sc = seenCount - lastSeenCount;
seenCount = 0; lastSeenCount = seenCount;
return sc; return sc;
} }
bool shouldCount();
private: private:
static bool shouldHide(const String &s); static bool shouldHide(const String &s);
bool hasValue = false, added = false, close = false, reported = false, macPublic = false, ignore = false, allowQuery = false, didQuery = false, rmAsst = false, hidden = false, connectable = false; bool hasValue = false, added = false, close = false, reported = false, ignore = false, allowQuery = false, didQuery = false, rmAsst = false, hidden = false, connectable = false, countable = false, counting = false;
NimBLEAddress address; NimBLEAddress address;
String id, name, disc; String id, name, disc;
short int idType = 0; short int idType = 0;
int rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI, newest = NO_RSSI, recent = NO_RSSI, oldest = NO_RSSI; int rssi = -100, calRssi = NO_RSSI, mdRssi = NO_RSSI, asRssi = NO_RSSI, newest = NO_RSSI, recent = NO_RSSI, oldest = NO_RSSI;
int qryAttempts = 0, seenCount = 1, qryDelayMillis = 0; unsigned int qryAttempts = 0, qryDelayMillis = 0;
float raw = 0, lastReported = 0, temp = 0, humidity = 0; float raw = 0, lastReported = 0, temp = 0, humidity = 0;
unsigned long firstSeenMillis, lastSeenMillis = 0, lastReportedMillis = 0, lastQryMillis = 0; unsigned long firstSeenMillis, lastSeenMillis = 0, lastReportedMillis = 0, lastQryMillis = 0;
unsigned long seenCount = 1, lastSeenCount = 0;
uint16_t mv = 0; uint16_t mv = 0;
uint8_t battery = 0xFF;
Reading<Differential<float>> output; Reading<Differential<float>> output;
@ -122,11 +125,11 @@ private:
void fingerprint(NimBLEAdvertisedDevice *advertisedDevice); void fingerprint(NimBLEAdvertisedDevice *advertisedDevice);
void fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount); void fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceAdvCount, bool haveTxPower, int8_t txPower);
void fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount); void fingerprintServiceData(NimBLEAdvertisedDevice *advertisedDevice, size_t serviceDataCount, bool haveTxPower, int8_t txPower);
void fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice); void fingerprintManufactureData(NimBLEAdvertisedDevice *advertisedDevice, bool haveTxPower, int8_t txPower);
}; };
#endif #endif

View File

@ -8,7 +8,7 @@ void BleFingerprintCollection::cleanupOldFingerprints()
auto it = fingerprints.begin(); auto it = fingerprints.begin();
while (it != fingerprints.end()) while (it != fingerprints.end())
{ {
long age = (*it)->getAge(); auto age = (*it)->getMsSinceLastSeen();
if (age > forgetMs) if (age > forgetMs)
{ {
GUI::removed((*it)); GUI::removed((*it));
@ -54,7 +54,7 @@ BleFingerprint *BleFingerprintCollection::getFingerprint(BLEAdvertisedDevice *ad
return f; return f;
} }
std::list<BleFingerprint *> BleFingerprintCollection::getCopy() const std::list<BleFingerprint *> BleFingerprintCollection::getCopy()
{ {
if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE) if (xSemaphoreTake(fingerprintSemaphore, 1000) != pdTRUE)
log_e("Couldn't take semaphore!"); log_e("Couldn't take semaphore!");
@ -62,8 +62,13 @@ std::list<BleFingerprint *> BleFingerprintCollection::getCopy()
std::list<BleFingerprint *> copy(fingerprints); std::list<BleFingerprint *> copy(fingerprints);
if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE) if (xSemaphoreGive(fingerprintSemaphore) != pdTRUE)
log_e("Couldn't give semaphore!"); log_e("Couldn't give semaphore!");
return copy; return std::move(copy);
}
String BleFingerprintCollection::include{}, BleFingerprintCollection::exclude{}, BleFingerprintCollection::query{}, BleFingerprintCollection::knownMacs{}, BleFingerprintCollection::countIds{};
float BleFingerprintCollection::skipDistance = 0.0f, BleFingerprintCollection::maxDistance = 0.0f, BleFingerprintCollection::absorption = 3.5f, BleFingerprintCollection::countEnter = 2, BleFingerprintCollection::countExit = 4;
int BleFingerprintCollection::refRssi = 0, BleFingerprintCollection::forgetMs = 0, BleFingerprintCollection::skipMs = 0, BleFingerprintCollection::countMs = 10000;
const std::list<BleFingerprint *>* const BleFingerprintCollection::getNative()
{
return &fingerprints;
} }
String BleFingerprintCollection::include{}, BleFingerprintCollection::exclude{}, BleFingerprintCollection::query{};
float BleFingerprintCollection::skipDistance = 0.0f, BleFingerprintCollection::maxDistance = 0.0f, BleFingerprintCollection::absorption = 3.5f;
int BleFingerprintCollection::refRssi = 0, BleFingerprintCollection::forgetMs = 0, BleFingerprintCollection::skipMs = 0;

View File

@ -18,12 +18,15 @@ public:
} }
BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice); BleFingerprint *getFingerprint(BLEAdvertisedDevice *advertisedDevice);
void cleanupOldFingerprints(); void cleanupOldFingerprints();
std::list<BleFingerprint *> getCopy(); const std::list<BleFingerprint *>* const getNative();
const std::list<BleFingerprint *> getCopy();
void setDisable(bool disable) { _disable = disable; } void setDisable(bool disable) { _disable = disable; }
static String knownMacs, include, exclude, query;
static String include, exclude, query;
static float skipDistance, maxDistance, absorption; static float skipDistance, maxDistance, absorption;
static int refRssi, forgetMs, skipMs; static int refRssi, forgetMs, skipMs;
static String countIds;
static float countEnter, countExit;
static int countMs;
private: private:
bool _disable = false; bool _disable = false;

View File

@ -1,4 +1,7 @@
#include "GUI.h" #include "GUI.h"
#ifdef M5STICK
#include "tb_display.h"
#endif
#if defined M5STICK #if defined M5STICK
@ -57,8 +60,6 @@ void GUI::erased()
void GUI::connecting() void GUI::connecting()
{ {
status("Connecting...");
connected(false, false);
#ifdef LED_BUILTIN #ifdef LED_BUILTIN
if (GUI::statusLed) digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); if (GUI::statusLed) digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN));
#endif #endif
@ -85,26 +86,36 @@ void GUI::connected(bool wifi = false, bool mqtt = false)
void GUI::added(BleFingerprint *f) void GUI::added(BleFingerprint *f)
{ {
if (f->getIgnore()) return; if (f->getIgnore()) return;
Serial.printf("%d New %s | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), f->getRmAsst() ? "R" : (f->getAllowQuery() ? "Q" : " "), f->getMac().c_str(), f->getId().c_str(), f->getDiscriminator().c_str()); Serial.printf("%u New %s | MAC: %s, ID: %-60s %s\n", xPortGetCoreID(), f->getRmAsst() ? "R" : (f->getAllowQuery() ? "Q" : " "), f->getMac().c_str(), f->getId().c_str(), f->getDiscriminator().c_str());
} }
void GUI::removed(BleFingerprint *f) void GUI::removed(BleFingerprint *f)
{ {
if (f->getIgnore() || !f->getAdded()) return; if (f->getIgnore() || !f->getAdded()) return;
Serial.printf("\u001b[38;5;236m%d Del | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDiscriminator().c_str()); Serial.printf("\u001b[38;5;236m%u Del | MAC: %s, ID: %-60s %s\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDiscriminator().c_str());
}
void GUI::plusOne(BleFingerprint *f)
{
Serial.printf("\u001b[36m%u C# +1 | MAC: %s, ID: %-60s (%.2fm) %lums\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getMsSinceLastSeen());
}
void GUI::minusOne(BleFingerprint *f)
{
Serial.printf("\u001b[35m%u C# -1 | MAC: %s, ID: %-60s (%.2fm) %lums\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getMsSinceLastSeen());
} }
void GUI::close(BleFingerprint *f) void GUI::close(BleFingerprint *f)
{ {
if (f->getIgnore()) return; if (f->getIgnore()) return;
Serial.printf("\u001b[32m%d Close | MAC: %s, ID: %-60s (%.2fm) %ddBm\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getNewestRssi()); Serial.printf("\u001b[32m%u Close | MAC: %s, ID: %-60s (%.2fm) %ddBm\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getNewestRssi());
status("C: %s", f->getId().c_str()); status("C: %s", f->getId().c_str());
} }
void GUI::left(BleFingerprint *f) void GUI::left(BleFingerprint *f)
{ {
if (f->getIgnore()) return; if (f->getIgnore()) return;
Serial.printf("\u001b[33m%d Left | MAC: %s, ID: %-60s (%.2fm) %ddBm\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getNewestRssi()); Serial.printf("\u001b[33m%u Left | MAC: %s, ID: %-60s (%.2fm) %ddBm\u001b[0m\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getDistance(), f->getNewestRssi());
status("L: %s", f->getId().c_str()); status("L: %s", f->getId().c_str());
} }
@ -112,18 +123,18 @@ void GUI::added(BleFingerprint *f)
{ {
begin(); begin();
#ifdef M5STICK #ifdef M5STICK
sprite.fillSprite(TFT_BLACK);
sprite.setTextDatum(MC_DATUM);
char *message; char *message;
va_list args; va_list args;
va_start(args, format); va_start(args, format);
vasprintf(&message, format, args); vasprintf(&message, format, args);
va_end(args); va_end(args);
tb_display_print_String(message);
tb_display_print_String("\n");
#ifdef PLUS #ifdef PLUS
sprite.drawString(message, sprite.width() / 2, sprite.height() / 2, 4); //drawString(message, sprite.width() / 2, sprite.height() / 2, 4);
#else #else
sprite.drawString(message, sprite.width() / 2, sprite.height() / 2, 1);
//sprite.drawString(message, sprite.width() / 2, sprite.height() / 2, 1);
#endif #endif
free(message); free(message);
dirty = true; dirty = true;
@ -143,29 +154,15 @@ void GUI::added(BleFingerprint *f)
{ {
#ifdef M5STICK #ifdef M5STICK
M5.begin(true, true, false); M5.begin(true, true, false);
M5.Lcd.setRotation(3); M5.Axp.ScreenBreath(12);
sprite.createSprite(M5.Lcd.width(), M5.Lcd.height()); tb_display_init(3);
sprite.setSwapBytes(true);
#elif defined M5ATOM #elif defined M5ATOM
M5.begin(false, false, true); M5.begin(false, false, true);
M5.dis.drawpix(0, CRGB(64, 0, 0));
#endif #endif
GUI::init = true; GUI::init = true;
} }
} }
void GUI::blit()
{
begin();
#ifdef M5STICK
if (dirty)
{
sprite.pushSprite(0, 0);
M5.Axp.ScreenBreath(12);
dirty = false;
}
#endif
}
void GUI::updateStart() void GUI::updateStart()
{ {
@ -193,5 +190,4 @@ bool GUI::statusLed=false;
#ifdef M5STICK #ifdef M5STICK
bool GUI::dirty = false; bool GUI::dirty = false;
TFT_eSprite GUI::sprite(&M5.Lcd);
#endif #endif

View File

@ -40,10 +40,12 @@ public:
static void connected(bool wifi, bool mqtt); static void connected(bool wifi, bool mqtt);
static void status(const char *message, ...); static void status(const char *message, ...);
static void blit();
static bool statusLed; static bool statusLed;
static void plusOne(BleFingerprint *f);
static void minusOne(BleFingerprint *f);
private: private:
static void begin(); static void begin();

View File

@ -74,3 +74,49 @@ std::string hexStr(const std::string& s)
{ {
return hexStr(s.c_str(), s.length()); return hexStr(s.c_str(), s.length());
} }
std::string hexStr(const uint8_t *&s, unsigned int len)
{
return hexStr(reinterpret_cast<const char *>(s), len);
}
std::string hexStrRev(const char *data, unsigned 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[len - (2 * i + 1)] = hexmap[(data[i] & 0xF0) >> 4];
s[len - (2 * i + 2)] = hexmap[data[i] & 0x0F];
}
return s;
}
std::string hexStrRev(const uint8_t *&s, unsigned int len)
{
return hexStrRev(reinterpret_cast<const char *>(s), len);
}
std::string hexStrRev(const std::string &s)
{
return hexStrRev(s.c_str(), s.length());
}
bool prefixExists(const String& prefixes, const String& s)
{
unsigned int start = 0;
unsigned int space;
while ((space = prefixes.indexOf(" ", start)) != -1)
{
if (space > start)
{
auto sub = prefixes.substring(start, space);
if (s.indexOf(sub) != -1) return true;
}
start = space + 1;
}
auto sub = prefixes.substring(start);
return !sub.isEmpty() && s.indexOf(sub) != -1;
}

View File

@ -16,6 +16,11 @@ std::string slugify(const std::string& text);
String slugify(const String& text); String slugify(const String& text);
std::string kebabify(const std::string& text); std::string kebabify(const std::string& text);
String kebabify(const String& text); String kebabify(const String& text);
std::string hexStr(const uint8_t *data, int len);
std::string hexStr(const char *data, int len); std::string hexStr(const char *data, int len);
std::string hexStr(const std::string& s); std::string hexStr(const std::string& s);
std::string hexStrRev(const uint8_t *data, int len);
std::string hexStrRev(const char *data, int len);
std::string hexStrRev(const std::string &s);
bool prefixExists(const String& prefixes, const String& s);
#endif #endif

View File

@ -7,6 +7,7 @@
static BLEUUID eddystoneUUID((uint16_t)0xFEAA); static BLEUUID eddystoneUUID((uint16_t)0xFEAA);
static BLEUUID tileUUID((uint16_t)0xFEED); static BLEUUID tileUUID((uint16_t)0xFEED);
static BLEUUID exposureUUID((uint16_t)0xFD6F); static BLEUUID exposureUUID((uint16_t)0xFD6F);
static BLEUUID smartTagUUID((uint16_t)0xFD5A);
static BLEUUID sonosUUID((uint16_t)0xFE07); static BLEUUID sonosUUID((uint16_t)0xFE07);
static BLEUUID itagUUID((uint16_t)0xffe0); static BLEUUID itagUUID((uint16_t)0xffe0);
static BLEUUID miThermUUID(uint16_t(0x181A)); static BLEUUID miThermUUID(uint16_t(0x181A));

View File

@ -14,19 +14,23 @@ default_envs = esp32
[common] [common]
debug_build_flags = -O0 -ggdb3 -g3 -DDEBUG_TLS_MEM debug_build_flags = -O0 -ggdb3 -g3 -DDEBUG_TLS_MEM
build_flags = build_flags =
-D MQTT_MAX_PACKET_SIZE=1024 -D MQTT_MIN_FREE_MEMORY=8128
-D SECURE_CLIENT=SECURE_CLIENT_BEARSSL -D SECURE_CLIENT=SECURE_CLIENT_BEARSSL
-D BEARSSL_SSL_BASIC -D BEARSSL_SSL_BASIC
-D CONFIG_BT_NIMBLE_PINNED_TO_CORE=1 -D CONFIG_BT_NIMBLE_PINNED_TO_CORE=1
-D CONFIG_BT_NIMBLE_MAX_BONDS=0
-D CONFIG_BT_NIMBLE_MAX_CCCDS=0
-D CONFIG_BT_NIMBLE_ROLE_PERIPHERAL_DISABLED
-D CONFIG_BT_NIMBLE_ROLE_BROADCASTER_DISABLED
-D CONFIG_ASYNC_TCP_USE_WDT=0 -D CONFIG_ASYNC_TCP_USE_WDT=0
platform = espressif32@3.2 platform = espressif32@3.2
framework = arduino framework = arduino
lib_deps = lib_deps =
haimoz/SoftFilters@^0.1.0 haimoz/SoftFilters@^0.1.0
marvinroger/AsyncMqttClient@^0.9.0 marvinroger/AsyncMqttClient@^0.9.0
bblanchon/ArduinoJson@^6.17.3 bblanchon/ArduinoJson@^6.19.3
https://github.com/ESPresense/ESP-WiFiSettings.git https://github.com/ESPresense/ESP-WiFiSettings.git
https://github.com/h2zero/NimBLE-Arduino.git#1.3.3 https://github.com/ESPresense/NimBLE-Arduino.git
https://github.com/pbolduc/AsyncTCP.git @ 1.2.0 https://github.com/pbolduc/AsyncTCP.git @ 1.2.0
bbx10/DNSServer@^1.1.0 bbx10/DNSServer@^1.1.0
@ -50,7 +54,7 @@ lib_deps = ${common.lib_deps}
board_build.partitions = partitions_singleapp.csv board_build.partitions = partitions_singleapp.csv
monitor_port = /dev/cu.usbserial-*1 monitor_port = /dev/cu.usbserial-*1
monitor_speed = 115200 monitor_speed = 115200
monitor_filters = esp32_exception_decoder monitor_filters = esp32_exception_decoder, time
debug_build_flags = ${common.debug_build_flags} debug_build_flags = ${common.debug_build_flags}
build_flags = ${common.build_flags} build_flags = ${common.build_flags}
@ -101,6 +105,7 @@ framework = ${common.framework}
board = m5stick-c board = m5stick-c
lib_deps = lib_deps =
m5stack/M5StickC@^0.2.0 m5stack/M5StickC@^0.2.0
https://github.com/ESPresense/M5StickC-TB_Display.git
${common.lib_deps} ${common.lib_deps}
board_build.partitions = partitions_singleapp.csv board_build.partitions = partitions_singleapp.csv
monitor_speed = 115200 monitor_speed = 115200
@ -116,6 +121,7 @@ framework = ${common.framework}
board = m5stick-c board = m5stick-c
lib_deps = lib_deps =
m5stack/M5StickCPlus@^0.0.2 m5stack/M5StickCPlus@^0.0.2
https://github.com/ESPresense/M5StickC-TB_Display.git
${common.lib_deps} ${common.lib_deps}
board_build.partitions = partitions_singleapp.csv board_build.partitions = partitions_singleapp.csv
monitor_speed = 115200 monitor_speed = 115200
@ -211,6 +217,7 @@ framework = ${common.framework}
board = m5stick-c board = m5stick-c
lib_deps = lib_deps =
m5stack/M5StickC@^0.2.0 m5stack/M5StickC@^0.2.0
https://github.com/ESPresense/M5StickC-TB_Display.git
${common.lib_deps} ${common.lib_deps}
${common_sensors.lib_deps} ${common_sensors.lib_deps}
board_build.partitions = partitions_singleapp.csv board_build.partitions = partitions_singleapp.csv
@ -228,6 +235,7 @@ framework = ${common.framework}
board = m5stick-c board = m5stick-c
lib_deps = lib_deps =
m5stack/M5StickCPlus@^0.0.2 m5stack/M5StickCPlus@^0.0.2
https://github.com/ESPresense/M5StickC-TB_Display.git
${common.lib_deps} ${common.lib_deps}
${common_sensors.lib_deps} ${common_sensors.lib_deps}
board_build.partitions = partitions_singleapp.csv board_build.partitions = partitions_singleapp.csv

View File

@ -1,31 +1,27 @@
#ifdef VERBOSE // Replace with your MQTT Broker address
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#endif
//Replace with your MQTT Broker address
#define DEFAULT_MQTT_HOST "mqtt.z13.org" #define DEFAULT_MQTT_HOST "mqtt.z13.org"
//Replace with your MQTT Broker port // Replace with your MQTT Broker port
#define DEFAULT_MQTT_PORT 1883 #define DEFAULT_MQTT_PORT 1883
//Replace with your MQTT Broker user // Replace with your MQTT Broker user
#define DEFAULT_MQTT_USER "" #define DEFAULT_MQTT_USER ""
//Replace with your MQTT Broker password // Replace with your MQTT Broker password
#define DEFAULT_MQTT_PASSWORD "" #define DEFAULT_MQTT_PASSWORD ""
// Maximum distance (in meters) to report. Devices that are calculated to be further than this distance in meters will not be reported // Maximum distance (in meters) to report. Devices that are calculated to be further than this distance in meters will not be reported
#define DEFAULT_MAX_DISTANCE 16 #define DEFAULT_MAX_DISTANCE 16
//Define the base topic for room detection. Usually "espresense" // Define the base topic for room detection. Usually "espresense"
#define CHANNEL String("espresense") #define CHANNEL String("espresense")
#define DEFAULT_QUERY "" #define DEFAULT_QUERY ""
#define DEFAULT_INCLUDE "" #define DEFAULT_INCLUDE ""
#define DEFAULT_EXCLUDE "" #define DEFAULT_EXCLUDE ""
#define BLE_SCAN_INTERVAL 40 // Used to determine antenna sharing between Bluetooth and Wi-Fi. Do not modify unless you are confident you know what you're doing #define BLE_SCAN_INTERVAL 0x80
#define BLE_SCAN_WINDOW 30 // Used to determine antenna sharing between Bluetooth and Wi-Fi. Do not modify unless you are confident you know what you're doing #define BLE_SCAN_WINDOW 0x80
#define DEFAULT_REF_RSSI (-65) #define DEFAULT_REF_RSSI (-65)
#define DEFAULT_ABSORPTION (3.5) #define DEFAULT_ABSORPTION (3.5)
@ -48,8 +44,8 @@
#ifdef VERSION #ifdef VERSION
#define DEFAULT_AUTO_UPDATE true #define DEFAULT_AUTO_UPDATE true
#define DEFAULT_OTA_UPDATE false #define DEFAULT_ARDUINO_OTA false
#else #else
#define DEFAULT_AUTO_UPDATE false #define DEFAULT_AUTO_UPDATE false
#define DEFAULT_OTA_UPDATE true #define DEFAULT_ARDUINO_OTA true
#endif #endif

View File

@ -2,7 +2,7 @@
#include "MotionSensors.h" #include "MotionSensors.h"
bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int totalFpReported) bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int totalFpReported, int count)
{ {
if (!online) if (!online)
{ {
@ -19,7 +19,11 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total
if (discovery && !sentDiscovery) if (discovery && !sentDiscovery)
{ {
if (sendDiscoveryConnectivity() && sendDiscoveryUptime() && sendDiscoveryFreeMem() && sendButtonDiscovery("Restart", "diagnostic") && sendSwitchDiscovery("Status LED", "config") && sendNumberDiscovery("Max Distance", "config") && sendNumberDiscovery("Absorption", "config") && sendSwitchDiscovery("Active Scan", "config") && sendSwitchDiscovery("Auto Update", "config") && sendSwitchDiscovery("OTA Update", "config") && sendSwitchDiscovery("Prerelease", "config") && sendDeleteDiscovery("switch", "Query") && Motion::SendDiscovery(doc) if (sendDiscoveryConnectivity() && sendTeleSensorDiscovery("Uptime", EC_DIAGNOSTIC, "{{ value_json.uptime }}", "s") && sendTeleSensorDiscovery("Free Mem", EC_DIAGNOSTIC, "{{ value_json.freeHeap }}", "bytes") && (BleFingerprintCollection::countIds.isEmpty() ? sendDeleteDiscovery("sensor", "Count") : sendTeleSensorDiscovery("Count", "", "{{ value_json.count }}", "")) && sendButtonDiscovery("Restart", EC_DIAGNOSTIC) && sendSwitchDiscovery("Status LED", EC_CONFIG) && sendNumberDiscovery("Max Distance", EC_CONFIG) && sendNumberDiscovery("Absorption", EC_CONFIG) && sendSwitchDiscovery("Active Scan", EC_CONFIG) && sendSwitchDiscovery("Auto Update", EC_CONFIG) && sendSwitchDiscovery("Arduino OTA", EC_CONFIG) && sendSwitchDiscovery("Prerelease", EC_CONFIG) && sendDeleteDiscovery("switch", "OTA Update") && Motion::SendDiscovery(doc)
#ifdef MACCHINA_A0
&& sendTeleSensorDiscovery("Battery", "", "{{ value_json.batt }}", "%")
&& sendTeleBinarySensorDiscovery("Running", "", "{{ value_json.run }}", "running")
#endif
#ifdef SENSORS #ifdef SENSORS
&& sendDiscoveryHumidity() && sendDiscoveryTemperature() && sendDiscoveryLux() && sendDiscoveryBME280Temperature() && sendDiscoveryBME280Humidity() && sendDiscoveryBME280Pressure() && sendDiscoveryTSL2561Lux() && sendDiscoveryHumidity() && sendDiscoveryTemperature() && sendDiscoveryLux() && sendDiscoveryBME280Temperature() && sendDiscoveryBME280Humidity() && sendDiscoveryBME280Pressure() && sendDiscoveryTSL2561Lux()
#endif #endif
@ -48,11 +52,19 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total
#endif #endif
doc["rssi"] = WiFi.RSSI(); doc["rssi"] = WiFi.RSSI();
#ifdef MACCHINA_A0 #ifdef MACCHINA_A0
doc["batt"] = a0_read_batt_mv() / 1000.0f; auto mv = a0_read_batt_mv();
doc["mV"] = mv;
bool run = (mv > 13200);
unsigned int soc = round(-13275.04 + 2.049731 * mv - (0.00007847975 * mv) * mv);
doc["batt"] = run ? (unsigned int)100 : max((unsigned int)0, min((unsigned int)100, soc));
doc["run"] = run ? "ON" : "OFF";
#endif #endif
#ifdef VERSION #ifdef VERSION
doc["ver"] = String(VERSION); doc["ver"] = String(VERSION);
#endif #endif
if (!BleFingerprintCollection::countIds.isEmpty())
doc["count"] = count;
if (totalSeen > 0) if (totalSeen > 0)
doc["adverts"] = totalSeen; doc["adverts"] = totalSeen;
if (totalFpSeen > 0) if (totalFpSeen > 0)
@ -90,12 +102,7 @@ bool sendTelemetry(int totalSeen, int totalFpSeen, int totalFpQueried, int total
void connectToWifi() void connectToWifi()
{ {
Serial.printf("Connecting to WiFi (%s)...\n", WiFi.macAddress().c_str()); Serial.printf("Connecting to WiFi (%s)...\n", WiFi.macAddress().c_str());
GUI::blit(); GUI::connected(false, false);
WiFiSettings.onConnect = []()
{
GUI::connected(false, false);
};
WiFiSettings.onFailure = []() WiFiSettings.onFailure = []()
{ {
@ -118,46 +125,54 @@ void connectToWifi()
#endif #endif
room = WiFiSettings.string("room", ESPMAC, "Room"); room = WiFiSettings.string("room", ESPMAC, "Room");
WiFiSettings.heading("MQTT Connection"); WiFiSettings.heading("MQTT <a href='https://espresense.com/settings#mqtt' target='_blank'></a>", false);
mqttHost = WiFiSettings.string("mqtt_host", DEFAULT_MQTT_HOST, "Server"); mqttHost = WiFiSettings.string("mqtt_host", DEFAULT_MQTT_HOST, "Server");
mqttPort = WiFiSettings.integer("mqtt_port", DEFAULT_MQTT_PORT, "Port"); mqttPort = WiFiSettings.integer("mqtt_port", DEFAULT_MQTT_PORT, "Port");
mqttUser = WiFiSettings.string("mqtt_user", DEFAULT_MQTT_USER, "Username"); mqttUser = WiFiSettings.string("mqtt_user", DEFAULT_MQTT_USER, "Username");
mqttPass = WiFiSettings.string("mqtt_pass", DEFAULT_MQTT_PASSWORD, "Password"); mqttPass = WiFiSettings.string("mqtt_pass", DEFAULT_MQTT_PASSWORD, "Password");
discovery = WiFiSettings.checkbox("discovery", true, "Send to discovery topic");
WiFiSettings.heading("Preferences");
GUI::statusLed = WiFiSettings.checkbox("status_led", true, "Status LED");
autoUpdate = WiFiSettings.checkbox("auto_update", DEFAULT_AUTO_UPDATE, "Automatically update");
prerelease = WiFiSettings.checkbox("prerelease", false, "Include pre-released versions in auto-update");
otaUpdate = WiFiSettings.checkbox("ota_update", DEFAULT_OTA_UPDATE, "Arduino OTA Update");
discovery = WiFiSettings.checkbox("discovery", true, "Home Assistant Discovery");
activeScan = WiFiSettings.checkbox("active_scan", false, "Active scanning (uses more battery but more results)");
publishTele = WiFiSettings.checkbox("pub_tele", true, "Send to telemetry topic"); publishTele = WiFiSettings.checkbox("pub_tele", true, "Send to telemetry topic");
publishRooms = WiFiSettings.checkbox("pub_rooms", true, "Send to rooms topic"); publishRooms = WiFiSettings.checkbox("pub_rooms", true, "Send to rooms topic");
publishDevices = WiFiSettings.checkbox("pub_devices", true, "Send to devices topic"); publishDevices = WiFiSettings.checkbox("pub_devices", true, "Send to devices topic");
WiFiSettings.heading("Filtering"); WiFiSettings.heading("Room Count <a href='https://espresense.com/settings#room-count' target='_blank'></a>", false);
BleFingerprintCollection::countIds = WiFiSettings.string("count_ids", "", "Include device ids (space seperated ids)");
BleFingerprintCollection::countEnter = WiFiSettings.floating("count_enter", 0, 100, 2, "Start counting devices less than distance (in meters)");
BleFingerprintCollection::countExit = WiFiSettings.floating("count_exit", 0, 100, 4, "Stop counting devices greater than distance (in meters)");
BleFingerprintCollection::countMs = WiFiSettings.integer("count_ms", 0, 3000000, 30000, "Include devices with age less than (in ms)");
WiFiSettings.heading("Updating <a href='https://espresense.com/settings#updating' target='_blank'></a>", false);
autoUpdate = WiFiSettings.checkbox("auto_update", DEFAULT_AUTO_UPDATE, "Automatically update");
prerelease = WiFiSettings.checkbox("prerelease", false, "Include pre-released versions in auto-update");
arduinoOta = WiFiSettings.checkbox("arduino_ota", DEFAULT_ARDUINO_OTA, "Arduino OTA Update");
WiFiSettings.heading("Scanning <a href='https://espresense.com/settings#scanning' target='_blank'></a>", false);
activeScan = WiFiSettings.checkbox("active_scan", false, "Request scan results (usually not needed)");
BleFingerprintCollection::knownMacs = WiFiSettings.string("known_macs", "", "Known BLE mac addresses (no colons, space seperated)");
BleFingerprintCollection::query = WiFiSettings.string("query", DEFAULT_QUERY, "Query device ids for characteristics (eg. apple:1005:9-26)"); BleFingerprintCollection::query = WiFiSettings.string("query", DEFAULT_QUERY, "Query device ids for characteristics (eg. apple:1005:9-26)");
WiFiSettings.heading("Filtering <a href='https://espresense.com/settings#filtering' target='_blank'></a>", false);
if (BleFingerprintCollection::query == "1") BleFingerprintCollection::query = "apple:10"; // This is to keep query=true doing the same thing as older firmwares if (BleFingerprintCollection::query == "1") BleFingerprintCollection::query = "apple:10"; // This is to keep query=true doing the same thing as older firmwares
BleFingerprintCollection::include = WiFiSettings.string("include", DEFAULT_INCLUDE, "If set will only send matching to mqtt (eg. apple:iphone10-6 apple:iphone13-2)"); BleFingerprintCollection::include = WiFiSettings.string("include", DEFAULT_INCLUDE, "Include only sending these ids to mqtt (eg. apple:iphone10-6 apple:iphone13-2)");
BleFingerprintCollection::exclude = WiFiSettings.string("exclude", DEFAULT_EXCLUDE, "Exclude sending these ids to mqtt (eg. exp:20 apple:iphone10-6)"); BleFingerprintCollection::exclude = WiFiSettings.string("exclude", DEFAULT_EXCLUDE, "Exclude sending these ids to mqtt (eg. exp:20 apple:iphone10-6)");
BleFingerprintCollection::maxDistance = WiFiSettings.floating("max_dist", 0, 100, DEFAULT_MAX_DISTANCE, "Maximum distance to report (in meters)"); BleFingerprintCollection::maxDistance = WiFiSettings.floating("max_dist", 0, 100, DEFAULT_MAX_DISTANCE, "Maximum distance to report (in meters)");
BleFingerprintCollection::skipDistance = WiFiSettings.floating("skip_dist", 0, 10, DEFAULT_SKIP_DISTANCE, "Report early if beacon has moved more than this distance (in meters)"); BleFingerprintCollection::skipDistance = WiFiSettings.floating("skip_dist", 0, 10, DEFAULT_SKIP_DISTANCE, "Report early if beacon has moved more than this distance (in meters)");
BleFingerprintCollection::skipMs = WiFiSettings.integer("skip_ms", 0, 3000000, DEFAULT_SKIP_MS, "Skip reporting if message age is less that this (in milliseconds)"); BleFingerprintCollection::skipMs = WiFiSettings.integer("skip_ms", 0, 3000000, DEFAULT_SKIP_MS, "Skip reporting if message age is less that this (in milliseconds)");
WiFiSettings.heading("Calibration"); WiFiSettings.heading("Calibration <a href='https://espresense.com/settings#calibration' target='_blank'></a>", false);
BleFingerprintCollection::refRssi = WiFiSettings.integer("ref_rssi", -100, 100, DEFAULT_REF_RSSI, "Rssi expected from a 0dBm transmitter at 1 meter"); BleFingerprintCollection::refRssi = WiFiSettings.integer("ref_rssi", -100, 100, DEFAULT_REF_RSSI, "Rssi expected from a 0dBm transmitter at 1 meter");
BleFingerprintCollection::absorption = WiFiSettings.floating("absorption", -100, 100, DEFAULT_ABSORPTION, "Factor used to account for absorption, reflection, or diffraction"); BleFingerprintCollection::absorption = WiFiSettings.floating("absorption", -100, 100, DEFAULT_ABSORPTION, "Factor used to account for absorption, reflection, or diffraction");
BleFingerprintCollection::forgetMs = WiFiSettings.integer("forget_ms", 0, 3000000, DEFAULT_FORGET_MS, "Forget beacon if not seen for (in milliseconds)"); BleFingerprintCollection::forgetMs = WiFiSettings.integer("forget_ms", 0, 3000000, DEFAULT_FORGET_MS, "Forget beacon if not seen for (in milliseconds)");
WiFiSettings.heading("Additional Sensors"); WiFiSettings.heading("Misc <a href='https://espresense.com/settings#misc' target='_blank'></a>", false);
GUI::statusLed = WiFiSettings.checkbox("status_led", true, "Status LED");
Motion::ConnectToWifi(); Motion::ConnectToWifi();
#ifdef SENSORS #ifdef SENSORS
dht11Pin = WiFiSettings.integer("dht11_pin", 0, "DHT11 sensor pin (0 for disable)"); dht11Pin = WiFiSettings.integer("dht11_pin", 0, "DHT11 sensor pin (0 for disable)");
dht22Pin = WiFiSettings.integer("dht22_pin", 0, "DHT22 sensor pin (0 for disable)"); dht22Pin = WiFiSettings.integer("dht22_pin", 0, "DHT22 sensor pin (0 for disable)");
dhtTempOffset = WiFiSettings.floating("dhtTemp_offset", -40, 125, 0.0, "DHT temperature offset");
WiFiSettings.heading("I2C Settings"); WiFiSettings.heading("I2C Settings <a href='https://espresense.com/settings#i2c-settings' target='_blank'></a>", false);
I2CDebug = WiFiSettings.checkbox("I2CDebug", false, "Debug I2C addreses. Look at the serial log to get the correct address"); I2CDebug = WiFiSettings.checkbox("I2CDebug", false, "Debug I2C addreses. Look at the serial log to get the correct address");
@ -170,7 +185,7 @@ void connectToWifi()
I2C_Bus_2_SDA = WiFiSettings.integer("I2C_Bus_2_SDA", 0, "SDA pin (0 to disable)"); I2C_Bus_2_SDA = WiFiSettings.integer("I2C_Bus_2_SDA", 0, "SDA pin (0 to disable)");
I2C_Bus_2_SCL = WiFiSettings.integer("I2C_Bus_2_SCL", 0, "SCL pin (0 to disable)"); I2C_Bus_2_SCL = WiFiSettings.integer("I2C_Bus_2_SCL", 0, "SCL pin (0 to disable)");
WiFiSettings.heading("I2C Sensors"); WiFiSettings.heading("I2C Sensors <a href='https://espresense.com/settings#i2c-sensors' target='_blank'></a>", false);
WiFiSettings.html("h4", "BH1750 - Ambient Light Sensor:"); WiFiSettings.html("h4", "BH1750 - Ambient Light Sensor:");
BH1750_I2c_Bus = WiFiSettings.integer("BH1750_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus"); BH1750_I2c_Bus = WiFiSettings.integer("BH1750_I2c_Bus", 1, 2, DEFAULT_I2C_BUS, "I2C Bus");
@ -190,9 +205,11 @@ void connectToWifi()
if (!WiFiSettings.connect(true, 60)) if (!WiFiSettings.connect(true, 60))
ESP.restart(); ESP.restart();
#ifdef FIRMWARE
Serial.println("Firmware: " + String(FIRMWARE));
#endif
#ifdef VERSION #ifdef VERSION
Serial.println("Version: " + String(VERSION)); Serial.println("Version: " + String(VERSION));
#endif #endif
Serial.print("IP address: "); Serial.print("IP address: ");
Serial.println(WiFi.localIP()); Serial.println(WiFi.localIP());
@ -204,20 +221,14 @@ void connectToWifi()
Serial.println(room); Serial.println(room);
Serial.printf("MQTT server: %s:%d\n", mqttHost.c_str(), mqttPort); Serial.printf("MQTT server: %s:%d\n", mqttHost.c_str(), mqttPort);
Serial.printf("Max Distance: %.2f\n", BleFingerprintCollection::maxDistance); Serial.printf("Max Distance: %.2f\n", BleFingerprintCollection::maxDistance);
Serial.print("Telemetry: ");
Serial.println(publishTele ? "enabled" : "disabled");
Serial.print("Rooms: ");
Serial.println(publishRooms ? "enabled" : "disabled");
Serial.print("Devices: ");
Serial.println(publishDevices ? "enabled" : "disabled");
Serial.print("Discovery: ");
Serial.println(discovery ? "enabled" : "disabled");
Motion::SerialReport(); Motion::SerialReport();
#ifdef SENSORS #ifdef SENSORS
Serial.print("DHT11 Sensor: "); Serial.print("DHT11 Sensor: ");
Serial.println(dht11Pin ? "enabled" : "disabled"); Serial.println(dht11Pin ? "enabled" : "disabled");
Serial.print("DHT22 Sensor: "); Serial.print("DHT22 Sensor: ");
Serial.println(dht22Pin ? "enabled" : "disabled"); Serial.println(dht22Pin ? "enabled" : "disabled");
Serial.print("DHT Temp Offset: ");
Serial.println(dhtTempOffset ? "enabled" : "disabled");
Serial.print("BH1750_I2c Sensor: "); Serial.print("BH1750_I2c Sensor: ");
Serial.println(BH1750_I2c + " on bus " + BH1750_I2c_Bus); Serial.println(BH1750_I2c + " on bus " + BH1750_I2c_Bus);
Serial.print("BME280_I2c Sensor: "); Serial.print("BME280_I2c Sensor: ");
@ -232,6 +243,10 @@ void connectToWifi()
Serial.println(BleFingerprintCollection::include); Serial.println(BleFingerprintCollection::include);
Serial.print("Exclude: "); Serial.print("Exclude: ");
Serial.println(BleFingerprintCollection::exclude); Serial.println(BleFingerprintCollection::exclude);
Serial.print("Known Macs: ");
Serial.println(BleFingerprintCollection::knownMacs);
Serial.print("Count Ids: ");
Serial.println(BleFingerprintCollection::countIds);
localIp = WiFi.localIP().toString(); localIp = WiFi.localIP().toString();
id = slugify(room); id = slugify(room);
@ -309,16 +324,28 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties
spurt("/exclude", pay); spurt("/exclude", pay);
online = false; online = false;
} }
else if (command == "known_macs")
{
BleFingerprintCollection::knownMacs = pay;
spurt("/known_macs", pay);
online = false;
}
else if (command == "count_ids")
{
BleFingerprintCollection::countIds = pay;
spurt("/count_ids", pay);
online = false;
}
else if (command == "status_led") else if (command == "status_led")
{ {
GUI::statusLed = pay == "ON"; GUI::statusLed = pay == "ON";
spurt("/status_led", String(GUI::statusLed)); spurt("/status_led", String(GUI::statusLed));
online = false; online = false;
} }
else if (command == "ota_update") else if (command == "arduino_ota")
{ {
otaUpdate = pay == "ON"; arduinoOta = pay == "ON";
spurt("/ota_update", String(otaUpdate)); spurt("/arduino_ota", String(arduinoOta));
online = false; online = false;
} }
else if (command == "auto_update") else if (command == "auto_update")
@ -345,7 +372,7 @@ void onMqttMessage(char *topic, char *payload, AsyncMqttClientMessageProperties
void reconnect(TimerHandle_t xTimer) void reconnect(TimerHandle_t xTimer)
{ {
Serial.printf("%d Reconnect timer\n", xPortGetCoreID()); Serial.printf("%u Reconnect timer\n", xPortGetCoreID());
if (updateInProgress) return; if (updateInProgress) return;
if (WiFi.isConnected() && mqttClient.connected()) return; if (WiFi.isConnected() && mqttClient.connected()) return;
@ -357,12 +384,12 @@ void reconnect(TimerHandle_t xTimer)
if (!WiFi.isConnected()) if (!WiFi.isConnected())
{ {
Serial.printf("%d Reconnecting to WiFi...\n", xPortGetCoreID()); Serial.printf("%u Reconnecting to WiFi...\n", xPortGetCoreID());
if (!WiFiSettings.connect(true, 60)) if (!WiFiSettings.connect(true, 60))
ESP.restart(); ESP.restart();
} }
Serial.printf("%d Reconnecting to MQTT...\n", xPortGetCoreID()); Serial.printf("%u Reconnecting to MQTT...\n", xPortGetCoreID());
mqttClient.connect(); mqttClient.connect();
} }
@ -407,29 +434,14 @@ bool reportDevice(BleFingerprint *f)
return false; return false;
} }
void scanForDevices(void *parameter) int totalFpReported = 0;
int totalSeen = 0;
int totalFpSeen = 0;
int totalFpQueried = 0;
void reportTask(void *parameter)
{ {
connectToMqtt(); connectToMqtt();
BLEDevice::init("");
for (esp_ble_power_type_t i = ESP_BLE_PWR_TYPE_CONN_HDL0; i <= ESP_BLE_PWR_TYPE_CONN_HDL8; i = esp_ble_power_type_t((int)i + 1))
NimBLEDevice::setPower(ESP_PWR_LVL_P9, i);
NimBLEDevice::setSecurityAuth(false, false, false);
auto pBLEScan = BLEDevice::getScan();
pBLEScan->setInterval(BLE_SCAN_INTERVAL);
pBLEScan->setWindow(BLE_SCAN_WINDOW);
pBLEScan->setAdvertisedDeviceCallbacks(&fingerprints, true);
if (activeScan) pBLEScan->setActiveScan(true);
pBLEScan->setDuplicateFilter(false);
pBLEScan->setMaxResults(0);
// pBLEScan->setFilterPolicy(BLE_HCI_SCAN_FILT_NO_WL_INITA);
if (!pBLEScan->start(0, nullptr, false))
log_e("Error starting continuous ble scan");
int totalSeen = 0;
int totalFpSeen = 0;
int totalFpQueried = 0;
int totalFpReported = 0;
while (true) while (true)
{ {
@ -439,28 +451,14 @@ void scanForDevices(void *parameter)
yield(); yield();
auto copy = fingerprints.getCopy(); auto copy = fingerprints.getCopy();
sendTelemetry(totalSeen, totalFpSeen, totalFpQueried, totalFpReported); int count = 0;
for (auto i: copy)
if (i->shouldCount())
count++;
yield();
sendTelemetry(totalSeen, totalFpSeen, totalFpQueried, totalFpReported, count);
yield(); yield();
if (millis() - lastQueryMillis > 3000)
{
auto started = millis();
for (auto f : copy)
{
if (f->query())
totalFpQueried++;
if (millis() - started > 3000) break;
}
if (!pBLEScan->isScanning())
{
if (!pBLEScan->start(0, nullptr, true))
log_e("Error re-starting continuous ble scan");
lastQueryMillis = millis(); // If we stopped scanning, don't query for 3 seconds in order for us to catch any missed broadcasts
}
}
auto reported = 0; auto reported = 0;
for (auto f : copy) for (auto f : copy)
@ -476,6 +474,45 @@ void scanForDevices(void *parameter)
totalFpReported++; totalFpReported++;
reported++; reported++;
} }
yield();
}
}
}
void scanTask(void *parameter)
{
NimBLEDevice::init("");
for (esp_ble_power_type_t i = ESP_BLE_PWR_TYPE_CONN_HDL0; i <= ESP_BLE_PWR_TYPE_CONN_HDL8; i = esp_ble_power_type_t((int)i + 1))
NimBLEDevice::setPower(ESP_PWR_LVL_P9, i);
NimBLEDevice::setSecurityAuth(true, true, true);
NimBLEDevice::setSecurityRespKey(BLE_SM_PAIR_KEY_DIST_ENC | BLE_SM_PAIR_KEY_DIST_ID);
NimBLEDevice::setMTU(255);
auto pBLEScan = NimBLEDevice::getScan();
pBLEScan->setInterval(BLE_SCAN_INTERVAL);
pBLEScan->setWindow(BLE_SCAN_WINDOW);
pBLEScan->setAdvertisedDeviceCallbacks(&fingerprints, true);
pBLEScan->setActiveScan(activeScan);
pBLEScan->setDuplicateFilter(false);
pBLEScan->setMaxResults(0);
if (!pBLEScan->start(0, nullptr, false))
log_e("Error starting continuous ble scan");
while (true)
{
for (auto f : *fingerprints.getNative())
if (f->query())
totalFpQueried++;
if (!pBLEScan->isScanning())
{
if (!pBLEScan->start(0, nullptr, true))
log_e("Error re-starting continuous ble scan");
delay(3000); // If we stopped scanning, don't query for 3 seconds in order for us to catch any missed broadcasts
}
else
{
delay(100);
} }
} }
} }
@ -540,7 +577,9 @@ void setup()
setClock(); setClock();
#endif #endif
Motion::Setup(); Motion::Setup();
#if MACCHINA_A0
pinMode(GPIO_NUM_35, INPUT);
#endif
#ifdef SENSORS #ifdef SENSORS
if (dht11Pin) dhtSensor.setup(dht11Pin, DHTesp::DHT11); if (dht11Pin) dhtSensor.setup(dht11Pin, DHTesp::DHT11);
if (dht22Pin) dhtSensor.setup(dht22Pin, DHTesp::DHT22); //(AM2302) if (dht22Pin) dhtSensor.setup(dht22Pin, DHTesp::DHT22); //(AM2302)
@ -596,7 +635,7 @@ void setup()
{ {
// Init BH1750 (witch default l2c adres) // Init BH1750 (witch default l2c adres)
int rc; // Returncode int rc; // Returncode
long m; // milli for calibration unsigned long m; // milli for calibration
bool state = false; bool state = false;
// if (! BH1750.begin(BH1750_TO_GROUND)) // if (! BH1750.begin(BH1750_TO_GROUND))
@ -646,7 +685,8 @@ void setup()
} }
} }
#endif #endif
xTaskCreatePinnedToCore(scanForDevices, "scanForDevices", 6000, nullptr, 1, &scannerTask, 1); xTaskCreatePinnedToCore(scanTask, "scanTask", 7168, nullptr, 2, &scanTaskHandle, CONFIG_BT_NIMBLE_PINNED_TO_CORE);
xTaskCreatePinnedToCore(reportTask, "reportTask", 7168, nullptr, 1, &reportTaskHandle, 1);
configureOTA(); configureOTA();
} }
@ -658,11 +698,11 @@ void dhtLoop()
if (gotNewTemperature) if (gotNewTemperature)
{ {
float humidity = dhtSensorData.humidity; float humidity = dhtSensorData.humidity;
float temperature = dhtSensorData.temperature; float temperature = dhtSensorData.temperature + dhtTempOffset;
Serial.println("Temp: " + String(temperature, 2) + "'C Humidity: " + String(humidity, 1) + "%"); Serial.println("Temp: " + String(temperature, 1) + "'C Humidity: " + String(humidity, 1) + "%");
mqttClient.publish((roomsTopic + "/humidity").c_str(), 0, 1, String(humidity).c_str()); mqttClient.publish((roomsTopic + "/humidity").c_str(), 0, 1, String(humidity, 1).c_str());
mqttClient.publish((roomsTopic + "/temperature").c_str(), 0, 1, String(temperature).c_str()); mqttClient.publish((roomsTopic + "/temperature").c_str(), 0, 1, String(temperature, 1).c_str());
gotNewTemperature = false; gotNewTemperature = false;
} }
@ -897,12 +937,11 @@ void l2cScanner()
void loop() void loop()
{ {
int freeHeap = ESP.getFreeHeap(); uint32_t freeHeap = ESP.getFreeHeap();
if (otaUpdate && freeHeap > 4096) if (arduinoOta && freeHeap > 4096)
ArduinoOTA.handle(); ArduinoOTA.handle();
if (freeHeap < 10000) Serial.printf("Low memory: %d bytes free", freeHeap); if (freeHeap < 10000) Serial.printf("Low memory: %u bytes free", freeHeap);
firmwareUpdate(); firmwareUpdate();
GUI::blit();
Motion::Loop(mqttClient); Motion::Loop(mqttClient);
#ifdef SENSORS #ifdef SENSORS
dhtLoop(); dhtLoop();

View File

@ -41,7 +41,7 @@ unsigned long sensorInterval = 60000;
//GY-302 lux sensor //GY-302 lux sensor
#include <hp_BH1750.h> #include <hp_BH1750.h>
hp_BH1750 BH1750; hp_BH1750 BH1750;
long ms_BH1750; unsigned long ms_BH1750;
float lux_BH1750; float lux_BH1750;
int lux_BH1750_MQTT; int lux_BH1750_MQTT;
String BH1750_I2c; String BH1750_I2c;
@ -63,41 +63,37 @@ String TSL2561_I2c_Gain;
unsigned long tsl2561PreviousMillis = 0; unsigned long tsl2561PreviousMillis = 0;
#endif #endif
static const char *const EC_DIAGNOSTIC = "diagnostic";
static const char *const EC_CONFIG = "config";
AsyncMqttClient mqttClient; AsyncMqttClient mqttClient;
TimerHandle_t reconnectTimer; TimerHandle_t reconnectTimer;
TaskHandle_t scannerTask; TaskHandle_t scanTaskHandle, reportTaskHandle;
DynamicJsonDocument doc(2048); DynamicJsonDocument doc(2048);
char buffer[2048]; char buffer[2048];
bool updateInProgress = false; bool updateInProgress = false;
String localIp; String localIp;
unsigned long lastTeleMillis, lastQueryMillis; unsigned long lastTeleMillis;
int reconnectTries = 0; int reconnectTries = 0;
int teleFails = 0; int teleFails = 0;
bool online = false; // Have we successfully sent status=online bool online = false; // Have we successfully sent status=online
bool sentDiscovery = false; // Have we successfully sent discovery bool sentDiscovery = false; // Have we successfully sent discovery
String offline = "offline"; String offline = "offline";
String mqttHost;
int mqttPort; String mqttHost, mqttUser, mqttPass;
String mqttUser; uint16_t mqttPort;
String mqttPass; String room, id, statusTopic, teleTopic, roomsTopic, setTopic;
String room;
String id; bool autoUpdate, arduinoOta, prerelease;
String statusTopic; bool discovery, activeScan, publishTele, publishRooms, publishDevices;
String teleTopic;
String roomsTopic;
String setTopic;
bool autoUpdate, otaUpdate, prerelease;
bool discovery;
bool activeScan;
bool publishTele;
bool publishRooms;
bool publishDevices;
#ifdef SENSORS #ifdef SENSORS
int dht11Pin;
int dht22Pin; uint8_t dht11Pin;
uint8_t dht22Pin;
float dhtTempOffset;
/** Initialize DHT sensor 1 */ /** Initialize DHT sensor 1 */
DHTesp dhtSensor; DHTesp dhtSensor;
@ -185,7 +181,7 @@ void setClock()
void configureOTA() void configureOTA()
{ {
if (!otaUpdate) return; if (!arduinoOta) return;
ArduinoOTA ArduinoOTA
.onStart([]() .onStart([]()
{ {
@ -296,7 +292,7 @@ void spiffsInit()
int flashes = 0; int flashes = 0;
unsigned long debounceDelay = 250; unsigned long debounceDelay = 250;
long lastDebounceTime = millis(); unsigned long lastDebounceTime = millis();
while (digitalRead(BUTTON) == BUTTON_PRESSED) while (digitalRead(BUTTON) == BUTTON_PRESSED)
{ {
if ((millis() - lastDebounceTime) > debounceDelay) if ((millis() - lastDebounceTime) > debounceDelay)
@ -336,7 +332,7 @@ bool pub(const char *topic, uint8_t qos, bool retain, const char *payload, size_
bool sendOnline() bool sendOnline()
{ {
return pub(statusTopic.c_str(), 0, true, "online") && pub((roomsTopic + "/max_distance").c_str(), 0, true, String(BleFingerprintCollection::maxDistance).c_str()) && pub((roomsTopic + "/absorption").c_str(), 0, true, String(BleFingerprintCollection::absorption).c_str()) && pub((roomsTopic + "/query").c_str(), 0, true, BleFingerprintCollection::query.c_str()) && pub((roomsTopic + "/include").c_str(), 0, true, BleFingerprintCollection::include.c_str()) && pub((roomsTopic + "/exclude").c_str(), 0, true, BleFingerprintCollection::exclude.c_str()) && pub((roomsTopic + "/status_led").c_str(), 0, true, String(GUI::statusLed ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/ota_update").c_str(), 0, true, String(otaUpdate ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/auto_update").c_str(), 0, true, String(autoUpdate ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/prerelease").c_str(), 0, true, String(prerelease ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/active_scan").c_str(), 0, true, String(activeScan ? "ON" : "OFF").c_str()); return pub(statusTopic.c_str(), 0, true, "online") && pub((roomsTopic + "/max_distance").c_str(), 0, true, String(BleFingerprintCollection::maxDistance).c_str()) && pub((roomsTopic + "/absorption").c_str(), 0, true, String(BleFingerprintCollection::absorption).c_str()) && pub((roomsTopic + "/query").c_str(), 0, true, BleFingerprintCollection::query.c_str()) && pub((roomsTopic + "/include").c_str(), 0, true, BleFingerprintCollection::include.c_str()) && pub((roomsTopic + "/exclude").c_str(), 0, true, BleFingerprintCollection::exclude.c_str()) && pub((roomsTopic + "/known_macs").c_str(), 0, true, BleFingerprintCollection::knownMacs.c_str()) && pub((roomsTopic + "/count_ids").c_str(), 0, true, BleFingerprintCollection::countIds.c_str()) && pub((roomsTopic + "/status_led").c_str(), 0, true, String(GUI::statusLed ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/arduino_ota").c_str(), 0, true, String(arduinoOta ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/auto_update").c_str(), 0, true, String(autoUpdate ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/prerelease").c_str(), 0, true, String(prerelease ? "ON" : "OFF").c_str()) && pub((roomsTopic + "/active_scan").c_str(), 0, true, String(activeScan ? "ON" : "OFF").c_str());
} }
void commonDiscovery() void commonDiscovery()
@ -378,38 +374,40 @@ bool sendDiscoveryConnectivity()
return pub(discoveryTopic.c_str(), 0, true, buffer); return pub(discoveryTopic.c_str(), 0, true, buffer);
} }
bool sendDiscoveryUptime() bool sendTeleBinarySensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &devClass)
{ {
auto slug = slugify(name);
commonDiscovery(); commonDiscovery();
doc["~"] = roomsTopic; doc["~"] = roomsTopic;
doc["name"] = "ESPresense " + room + " Uptime"; doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str());
doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_uptime", ESP.getEfuseMac() >> 24); doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_%s", ESP.getEfuseMac() >> 24, slug.c_str());
doc["avty_t"] = "~/status"; doc["avty_t"] = "~/status";
doc["stat_t"] = "~/telemetry"; doc["stat_t"] = "~/telemetry";
doc["entity_category"] = "diagnostic"; if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory;
doc["value_template"] = "{{ value_json.uptime }}"; doc["value_template"] = temp;
doc["unit_of_measurement"] = "s"; doc["dev_cla"] = devClass;
serializeJson(doc, buffer); serializeJson(doc, buffer);
String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/uptime/config"; String discoveryTopic = "homeassistant/binary_sensor/espresense_" + ESPMAC + "/" + slug + "/config";
return pub(discoveryTopic.c_str(), 0, true, buffer); return pub(discoveryTopic.c_str(), 0, true, buffer);
} }
bool sendDiscoveryFreeMem() bool sendTeleSensorDiscovery(const String &name, const String &entityCategory, const String &temp, const String &units)
{ {
auto slug = slugify(name);
commonDiscovery(); commonDiscovery();
doc["~"] = roomsTopic; doc["~"] = roomsTopic;
doc["name"] = "ESPresense " + room + " Free Memory"; doc["name"] = Sprintf("ESPresense %s %s", room.c_str(), name.c_str());
doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_free_mem", ESP.getEfuseMac() >> 24); doc["uniq_id"] = Sprintf("espresense_%06" PRIx64 "_%s", ESP.getEfuseMac() >> 24, slug.c_str());
doc["avty_t"] = "~/status"; doc["avty_t"] = "~/status";
doc["stat_t"] = "~/telemetry"; doc["stat_t"] = "~/telemetry";
doc["entity_category"] = "diagnostic"; if (!entityCategory.isEmpty()) doc["entity_category"] = entityCategory;
doc["value_template"] = "{{ value_json.maxAllocHeap }}"; doc["value_template"] = temp;
doc["unit_of_measurement"] = "bytes"; if (!units.isEmpty()) doc["unit_of_measurement"] = units;
serializeJson(doc, buffer); serializeJson(doc, buffer);
String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/free_mem/config"; String discoveryTopic = "homeassistant/sensor/espresense_" + ESPMAC + "/" + slug + "/config";
return pub(discoveryTopic.c_str(), 0, true, buffer); return pub(discoveryTopic.c_str(), 0, true, buffer);
} }
@ -654,9 +652,15 @@ bool spurt(const String &fn, const String &content)
} }
#ifdef MACCHINA_A0 #ifdef MACCHINA_A0
int smoothMilliVolts;
int a0_read_batt_mv() int a0_read_batt_mv()
{ {
float vout = ((float)analogRead(GPIO_NUM_35) + 35) / 215.0; int mv = round(((float)analogRead(GPIO_NUM_35) + 35) / 0.215);
return vout * 1100; // V to mV with +10% correction if (smoothMilliVolts)
smoothMilliVolts = round(0.1 * (mv - smoothMilliVolts) + smoothMilliVolts);
else
smoothMilliVolts = mv;
return smoothMilliVolts;
} }
#endif #endif