Added support for the Xioami Flower Care sensor (#779)
* Remove room assistant support since irks are far superior * Bunch of refactors and reorgs --------- Co-authored-by: Yashar Zenker <yashar.zenker@gmail.com> Co-authored-by: DTTerastar <DT@Terastar.biz>
This commit is contained in:
parent
d55c1451c0
commit
15a3f8c529
|
@ -1,5 +1,7 @@
|
|||
#include "BleFingerprint.h"
|
||||
|
||||
#include "../handlers/MiFloraHandler.h"
|
||||
#include "../handlers/NameModelHandler.h"
|
||||
#include "BleFingerprintCollection.h"
|
||||
#include "mbedtls/aes.h"
|
||||
#include "rssi.h"
|
||||
|
@ -76,13 +78,13 @@ BleFingerprint::BleFingerprint(BLEAdvertisedDevice *advertisedDevice, float fcmi
|
|||
addressType = advertisedDevice->getAddressType();
|
||||
newest = recent = oldest = rssi = advertisedDevice->getRSSI();
|
||||
seenCount = 1;
|
||||
|
||||
queryReport = nullptr;
|
||||
fingerprintAddress();
|
||||
}
|
||||
|
||||
void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice) {
|
||||
if (advertisedDevice->haveName()) {
|
||||
std::string name = advertisedDevice->getName();
|
||||
const std::string name = advertisedDevice->getName();
|
||||
if (!name.empty()) setId(String("name:") + kebabify(name).c_str(), ID_TYPE_NAME, String(name.c_str()));
|
||||
}
|
||||
|
||||
|
@ -100,20 +102,20 @@ void BleFingerprint::fingerprint(NimBLEAdvertisedDevice *advertisedDevice) {
|
|||
}
|
||||
|
||||
int bt_encrypt_be(const uint8_t *key, const uint8_t *plaintext, uint8_t *enc_data) {
|
||||
mbedtls_aes_context s = {0};
|
||||
mbedtls_aes_init(&s);
|
||||
mbedtls_aes_context ctx;
|
||||
mbedtls_aes_init(&ctx);
|
||||
|
||||
if (mbedtls_aes_setkey_enc(&s, key, 128) != 0) {
|
||||
mbedtls_aes_free(&s);
|
||||
if (mbedtls_aes_setkey_enc(&ctx, key, 128) != 0) {
|
||||
mbedtls_aes_free(&ctx);
|
||||
return BLE_HS_EUNKNOWN;
|
||||
}
|
||||
|
||||
if (mbedtls_aes_crypt_ecb(&s, MBEDTLS_AES_ENCRYPT, plaintext, enc_data) != 0) {
|
||||
mbedtls_aes_free(&s);
|
||||
if (mbedtls_aes_crypt_ecb(&ctx, MBEDTLS_AES_ENCRYPT, plaintext, enc_data) != 0) {
|
||||
mbedtls_aes_free(&ctx);
|
||||
return BLE_HS_EUNKNOWN;
|
||||
}
|
||||
|
||||
mbedtls_aes_free(&s);
|
||||
mbedtls_aes_free(&ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -144,7 +146,7 @@ bool ble_ll_resolv_rpa(const uint8_t *rpa, const uint8_t *irk) {
|
|||
ecb.plain_text[14] = rpa[4];
|
||||
ecb.plain_text[13] = rpa[5];
|
||||
|
||||
auto err = bt_encrypt_be(ecb.key, ecb.plain_text, ecb.cipher_text);
|
||||
bt_encrypt_be(ecb.key, ecb.plain_text, ecb.cipher_text);
|
||||
|
||||
if (ecb.cipher_text[15] != rpa[0] || ecb.cipher_text[14] != rpa[1] || ecb.cipher_text[13] != rpa[2]) return false;
|
||||
|
||||
|
@ -165,7 +167,7 @@ void BleFingerprint::fingerprintAddress() {
|
|||
break;
|
||||
case BLE_ADDR_RANDOM:
|
||||
case BLE_ADDR_RANDOM_ID: {
|
||||
auto naddress = address.getNative();
|
||||
const auto *naddress = address.getNative();
|
||||
if ((naddress[5] & 0xc0) == 0xc0)
|
||||
setId(mac, ID_TYPE_RAND_STATIC_MAC);
|
||||
else {
|
||||
|
@ -193,18 +195,7 @@ void BleFingerprint::fingerprintServiceAdvertisements(NimBLEAdvertisedDevice *ad
|
|||
#ifdef VERBOSE
|
||||
Serial.printf("Verbose | %s | %-58s%ddBm AD: %s\r\n", getMac().c_str(), getId().c_str(), rssi, advertisedDevice->getServiceUUID(i).toString().c_str());
|
||||
#endif
|
||||
if (uuid == roomAssistantService) {
|
||||
asRssi = BleFingerprintCollection::rxRefRssi + RM_ASST_TX;
|
||||
if (!rmAsst) {
|
||||
rmAsst = true;
|
||||
if (didQuery) {
|
||||
qryDelayMillis = 0;
|
||||
qryAttempts = 0;
|
||||
didQuery = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
} else if (uuid == tileUUID) {
|
||||
if (uuid == tileUUID) {
|
||||
asRssi = BleFingerprintCollection::rxRefRssi + TILE_TX;
|
||||
setId("tile:" + getMac(), ID_TYPE_TILE);
|
||||
return;
|
||||
|
@ -428,7 +419,6 @@ bool BleFingerprint::seen(BLEAdvertisedDevice *advertisedDevice) {
|
|||
auto the_min = min(min(oldest, recent), newest);
|
||||
auto the_max = max(max(oldest, recent), newest);
|
||||
auto the_median = the_max ^ the_min ^ oldest ^ recent ^ newest;
|
||||
auto range = the_max - the_min;
|
||||
|
||||
rssi = the_median;
|
||||
|
||||
|
@ -504,14 +494,14 @@ bool BleFingerprint::report(JsonObject *doc) {
|
|||
}
|
||||
|
||||
bool BleFingerprint::query() {
|
||||
if (!(allowQuery || rmAsst) || didQuery) return false;
|
||||
if (rssi < -90) return false;
|
||||
if (!allowQuery || isQuerying) return false;
|
||||
if (rssi < -90) return false; // Too far away
|
||||
|
||||
auto now = millis();
|
||||
if (now - lastSeenMillis > 5) return false; // Haven't seen lately
|
||||
if (now - lastQryMillis < qryDelayMillis) return false; // Too soon
|
||||
|
||||
if (now - lastSeenMillis > 5) return false;
|
||||
|
||||
if (now - lastQryMillis < qryDelayMillis) return false;
|
||||
didQuery = true;
|
||||
isQuerying = true;
|
||||
lastQryMillis = now;
|
||||
|
||||
bool success = false;
|
||||
|
@ -526,48 +516,25 @@ bool BleFingerprint::query() {
|
|||
pClient->setConnectTimeout(5);
|
||||
NimBLEDevice::getScan()->stop();
|
||||
if (pClient->connect(address)) {
|
||||
bool iphone = true;
|
||||
if (allowQuery) {
|
||||
std::string sMdl = pClient->getValue(deviceInformationService, modelChar);
|
||||
std::string sName = pClient->getValue(genericAccessService, nameChar);
|
||||
iphone = sMdl.find("iPhone") == 0;
|
||||
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()))) {
|
||||
Serial.printf("\u001b[38;5;104m%u Name | %s | %-58s%ddBm %s\u001b[0m\r\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, sName.c_str());
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (!sMdl.empty()) {
|
||||
if (setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_QUERY_MODEL, String(sMdl.c_str()))) {
|
||||
Serial.printf("\u001b[38;5;136m%u Model | %s | %-58s%ddBm %s\u001b[0m\r\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, sMdl.c_str());
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (rmAsst || iphone) // For some reason we often don't get room assistant's service advertisement
|
||||
{
|
||||
std::string sRmAst = pClient->getValue(roomAssistantService, rootAssistantCharacteristic);
|
||||
if (!sRmAst.empty()) {
|
||||
if (setId(String("roomAssistant:") + kebabify(sRmAst).c_str(), ID_TYPE_RM_ASST)) {
|
||||
Serial.printf("\u001b[38;5;129m%u RmAst | %s | %-58s%ddBm %s\u001b[0m\r\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, sRmAst.c_str());
|
||||
}
|
||||
success = true;
|
||||
}
|
||||
if (id.startsWith("flora:"))
|
||||
success = MiFloraHandler::requestData(pClient, this);
|
||||
else
|
||||
success = NameModelHandler::requestData(pClient, this);
|
||||
}
|
||||
}
|
||||
|
||||
NimBLEDevice::deleteClient(pClient);
|
||||
|
||||
if (success) return true;
|
||||
|
||||
qryAttempts++;
|
||||
qryDelayMillis = min(int(pow(10, qryAttempts)), 60000);
|
||||
Serial.printf("%u QryErr | %s | %-58s%ddBm Try %d, retry after %dms\r\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, qryAttempts, qryDelayMillis);
|
||||
|
||||
didQuery = false;
|
||||
|
||||
if (success) {
|
||||
qryAttempts = 0;
|
||||
qryDelayMillis = BleFingerprintCollection::requeryMs;
|
||||
} else {
|
||||
qryAttempts++;
|
||||
qryDelayMillis = min(int(pow(10, qryAttempts)), 60000);
|
||||
Serial.printf("%u QryErr | %s | %-58s%ddBm Try %d, retry after %dms\r\n", xPortGetCoreID(), getMac().c_str(), id.c_str(), rssi, qryAttempts, qryDelayMillis);
|
||||
}
|
||||
isQuerying = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
#ifndef _BLEFINGERPRINT_
|
||||
#define _BLEFINGERPRINT_
|
||||
|
||||
#include "rssi.h"
|
||||
#include "string_utils.h"
|
||||
#include <ArduinoJson.h>
|
||||
#include <NimBLEAdvertisedDevice.h>
|
||||
#include <NimBLEBeacon.h>
|
||||
|
@ -11,7 +8,13 @@
|
|||
#include <NimBLEEddystoneURL.h>
|
||||
#include <SoftFilters.h>
|
||||
|
||||
#define NO_RSSI (-128)
|
||||
#include <memory>
|
||||
|
||||
#include "QueryReport.h"
|
||||
#include "rssi.h"
|
||||
#include "string_utils.h"
|
||||
|
||||
#define NO_RSSI int8_t(-128)
|
||||
|
||||
#define ID_TYPE_TX_POW short(1)
|
||||
|
||||
|
@ -111,7 +114,10 @@ public:
|
|||
|
||||
bool getAllowQuery() const { return allowQuery; };
|
||||
|
||||
bool getRmAsst() const { return rmAsst; };
|
||||
const bool hasReport() { return queryReport != nullptr; };
|
||||
const QueryReport getReport() { return *queryReport; };
|
||||
void setReport(const QueryReport &report) { queryReport = std::unique_ptr<QueryReport>(new QueryReport {report}); };
|
||||
void clearReport() { queryReport.reset(); };
|
||||
|
||||
unsigned int getSeenCount()
|
||||
{
|
||||
|
@ -129,7 +135,7 @@ private:
|
|||
|
||||
static bool shouldHide(const String &s);
|
||||
|
||||
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;
|
||||
bool hasValue = false, added = false, close = false, reported = false, ignore = false, allowQuery = false, isQuerying = false, hidden = false, connectable = false, countable = false, counting = false;
|
||||
NimBLEAddress address;
|
||||
String id, name, disc;
|
||||
short int idType = NO_ID_TYPE;
|
||||
|
@ -147,6 +153,7 @@ private:
|
|||
OneEuroFilter<float, unsigned long> oneEuro;
|
||||
DifferentialFilter<float, unsigned long> diffFilter;
|
||||
|
||||
std::unique_ptr<QueryReport> queryReport = nullptr;
|
||||
bool filter();
|
||||
|
||||
void fingerprint(NimBLEAdvertisedDevice *advertisedDevice);
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace BleFingerprintCollection {
|
|||
String include{}, exclude{}, query{}, knownMacs{}, knownIrks{}, countIds{};
|
||||
float skipDistance = 0.0f, maxDistance = 0.0f, absorption = 3.5f, countEnter = 2, countExit = 4;
|
||||
int8_t rxRefRssi = -65, rxAdjRssi = 0, txRefRssi = -59;
|
||||
int forgetMs = 0, skipMs = 0, countMs = 10000;
|
||||
int forgetMs = 0, skipMs = 0, countMs = 10000, requeryMs = 300000;
|
||||
std::vector<DeviceConfig> deviceConfigs;
|
||||
std::vector<uint8_t *> irks;
|
||||
std::vector<BleFingerprint *> fingerprints;
|
||||
|
|
|
@ -47,7 +47,7 @@ extern TCallbackFingerprint onCountDel;
|
|||
extern String include, exclude, query, knownMacs, knownIrks, countIds;
|
||||
extern float skipDistance, maxDistance, absorption, countEnter, countExit;
|
||||
extern int8_t rxRefRssi, rxAdjRssi, txRefRssi;
|
||||
extern int forgetMs, skipMs, countMs;
|
||||
extern int forgetMs, skipMs, countMs, requeryMs;
|
||||
extern std::vector<DeviceConfig> deviceConfigs;
|
||||
extern std::vector<uint8_t *> irks;
|
||||
extern std::vector<BleFingerprint *> fingerprints;
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
#include <NimBLEAddress.h>
|
||||
|
||||
class QueryReport {
|
||||
public:
|
||||
QueryReport(const String& id, const String& payload) : id(id), payload(payload) {}
|
||||
|
||||
String getId() const { return id; }
|
||||
String getPayload() const { return payload; }
|
||||
|
||||
void setId(const String& id) { this->id = id; }
|
||||
void setPayload(const String& payload) { this->payload = payload; }
|
||||
|
||||
private:
|
||||
String id;
|
||||
String payload;
|
||||
};
|
|
@ -1,19 +1,19 @@
|
|||
#ifndef _RSSI_
|
||||
#define _RSSI_
|
||||
|
||||
#define CLOSE_RSSI (-40)
|
||||
#define LEFT_RSSI (-50)
|
||||
#define CLOSE_RSSI int8_t(-40)
|
||||
#define LEFT_RSSI int8_t(-50)
|
||||
|
||||
#define DEFAULT_TX (-6)
|
||||
#define DEFAULT_TX int8_t(-6)
|
||||
|
||||
#define APPLE_TX 0
|
||||
#define RM_ASST_TX 0
|
||||
#define TILE_TX (-4)
|
||||
#define EXPOSURE_TX (-12)
|
||||
#define ITAG_TX (-10)
|
||||
#define APPLE_TX int8_t(0)
|
||||
#define RM_ASST_TX int8_t(0)
|
||||
#define TILE_TX int8_t(-4)
|
||||
#define EXPOSURE_TX int8_t(-12)
|
||||
#define ITAG_TX int8_t(-10)
|
||||
|
||||
#define NUT_TX (-12)
|
||||
#define FLORA_TX (-10)
|
||||
#define NUT_TX int8_t(-12)
|
||||
#define FLORA_TX int8_t(-10)
|
||||
|
||||
#define EDDYSTONE_ADD_1M (-41)
|
||||
#define EDDYSTONE_ADD_1M int8_t(-41)
|
||||
#endif
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
#define Stdprintf(f, ...) ({ char* s; asprintf(&s, f, __VA_ARGS__); const std::string r = s; free(s); r; })
|
||||
#define SMacf(f) ( \
|
||||
{ \
|
||||
auto nativeAddress = (f).getNative(); \
|
||||
const auto nativeAddress = (f).getNative(); \
|
||||
Sprintf("%02x%02x%02x%02x%02x%02x", nativeAddress[5], nativeAddress[4], nativeAddress[3], nativeAddress[2], nativeAddress[1], nativeAddress[0]); \
|
||||
})
|
||||
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
#include "MiFloraHandler.h"
|
||||
|
||||
namespace MiFloraHandler {
|
||||
|
||||
std::vector<std::string> addresses;
|
||||
bool readSensorData(BLERemoteService* floraService, DynamicJsonDocument* doc) {
|
||||
BLERemoteCharacteristic* floraCharacteristic = nullptr;
|
||||
|
||||
// get the main device data characteristic
|
||||
floraCharacteristic = floraService->getCharacteristic(uuid_sensor_data);
|
||||
|
||||
if (floraCharacteristic == nullptr) {
|
||||
Serial.println("-- Can't read characteristics");
|
||||
return false;
|
||||
}
|
||||
|
||||
// read characteristic value
|
||||
NimBLEAttValue value;
|
||||
|
||||
value = floraCharacteristic->readValue();
|
||||
|
||||
if (value.size() == 0) {
|
||||
Serial.println("Reading Value failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* val = value.c_str();
|
||||
|
||||
float temperature = (val[0] + val[1] * 256) / ((float)10.0);
|
||||
uint8_t moisture = val[7];
|
||||
uint32_t brightness = val[3] + val[4] * 256;
|
||||
float conductivity = val[8] + val[9] * 256;
|
||||
|
||||
(*doc)[F("temperature")] = temperature;
|
||||
(*doc)[F("moisture")] = moisture;
|
||||
(*doc)[F("light")] = brightness;
|
||||
(*doc)[F("conductivity")] = conductivity;
|
||||
|
||||
floraService->deleteCharacteristics();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool readBatteryData(BLERemoteService* floraService, DynamicJsonDocument* doc) {
|
||||
BLERemoteCharacteristic* floraCharacteristic = nullptr;
|
||||
|
||||
floraCharacteristic = floraService->getCharacteristic(uuid_version_battery);
|
||||
|
||||
if (floraCharacteristic == nullptr) {
|
||||
Serial.println("-- Can't read characteristics");
|
||||
return false;
|
||||
}
|
||||
NimBLEAttValue val;
|
||||
|
||||
val = floraCharacteristic->readValue();
|
||||
|
||||
if (val.size() == 0) {
|
||||
Serial.println("Reading Value failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
int8_t battery = val.c_str()[0];
|
||||
(*doc)[F("battery")] = battery;
|
||||
|
||||
floraService->deleteCharacteristics();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool forceFloraServiceDataMode(BLERemoteService* floraService) { // Setting the mi flora to data reading mode
|
||||
BLERemoteCharacteristic* floraCharacteristic;
|
||||
|
||||
// get device mode characteristic, needs to be changed to read data
|
||||
// Serial.println("- Force device in data mode");
|
||||
floraCharacteristic = nullptr;
|
||||
floraCharacteristic = floraService->getCharacteristic(uuid_write_mode);
|
||||
|
||||
if (floraCharacteristic == nullptr) {
|
||||
// Serial.println("-- Failed, skipping device");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint8_t buf[2] = {0xA0, 0x1F};
|
||||
floraCharacteristic->writeValue(buf, 2, true);
|
||||
|
||||
delay(500);
|
||||
floraService->deleteCharacteristics();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void fillDeviceData(DynamicJsonDocument* doc, BleFingerprint* f) {
|
||||
(*doc)[F("id")] = f->getId();
|
||||
(*doc)[F("mac")] = f->getMac();
|
||||
(*doc)[F("rssi")] = f->getRssi();
|
||||
}
|
||||
|
||||
bool getFloraData(DynamicJsonDocument* doc, BLERemoteService* floraService, BleFingerprint* f) {
|
||||
// Force miFlora to data mode
|
||||
|
||||
fillDeviceData(doc, f);
|
||||
|
||||
if (!MiFloraHandler::readBatteryData(floraService, doc))
|
||||
Serial.println("Failed reading battery data");
|
||||
|
||||
if (MiFloraHandler::forceFloraServiceDataMode(floraService)) {
|
||||
} else {
|
||||
Serial.println("Failed to force data reading mode");
|
||||
}
|
||||
|
||||
if (!MiFloraHandler::readSensorData(floraService, doc))
|
||||
Serial.println("Failed reading sensor data");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static DynamicJsonDocument document(1024);
|
||||
bool requestData(NimBLEClient* pClient, BleFingerprint* fingerprint) // Getting mi flora data
|
||||
{
|
||||
NimBLERemoteService* floraService = pClient->getService(serviceUUID);
|
||||
|
||||
if (floraService == nullptr) {
|
||||
Serial.println("Getting MiFlora service failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
document.clear();
|
||||
// Retriving the actual data
|
||||
if (!getFloraData(&document, floraService, fingerprint)) // Getting flora data
|
||||
return false;
|
||||
String buf = String();
|
||||
serializeJson(document, buf);
|
||||
// Sending buffer over mqtt
|
||||
fingerprint->setReport(QueryReport{"miflora", buf});
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace MiFloraHandler
|
|
@ -0,0 +1,18 @@
|
|||
#pragma once
|
||||
#include <ArduinoJson.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <AsyncWiFiSettings.h>
|
||||
#include <BleFingerprint.h>
|
||||
#include <NimBLEClient.h>
|
||||
#include <NimBLEDevice.h>
|
||||
#include <SoftFilters.h>
|
||||
|
||||
namespace MiFloraHandler
|
||||
{
|
||||
static BLEUUID serviceUUID(0x00001204, 0x0000, 0x1000, 0x800000805f9b34fb);
|
||||
static BLEUUID uuid_version_battery(0x00001a02, 0x0000, 0x1000, 0x800000805f9b34fb);
|
||||
static BLEUUID uuid_sensor_data(0x00001a01, 0x0000, 0x1000, 0x800000805f9b34fb);
|
||||
static BLEUUID uuid_write_mode(0x00001a00, 0x0000, 0x1000, 0x800000805f9b34fb);
|
||||
|
||||
bool requestData(NimBLEClient* pClient, BleFingerprint* fingerprint);
|
||||
} // namespace MiFloraHandler
|
|
@ -0,0 +1,24 @@
|
|||
#include "NameModelHandler.h"
|
||||
#include "util.h"
|
||||
|
||||
namespace NameModelHandler {
|
||||
bool requestData(NimBLEClient* pClient, BleFingerprint* f) {
|
||||
std::string sMdl = pClient->getValue(deviceInformationService, modelChar);
|
||||
std::string sName = pClient->getValue(genericAccessService, nameChar);
|
||||
if (!sName.empty() && sMdl.find(sName) == std::string::npos && sName != "Apple Watch") {
|
||||
if (f->setId(String("name:") + kebabify(sName).c_str(), ID_TYPE_QUERY_NAME, String(sName.c_str()))) {
|
||||
Serial.printf("\u001b[38;5;104m%u Name | %s | %-58s%ddBm %s\u001b[0m\r\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getRssi(), sName.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!sMdl.empty()) {
|
||||
if (f->setId(String("apple:") + kebabify(sMdl).c_str(), ID_TYPE_QUERY_MODEL, String(sMdl.c_str()))) {
|
||||
Serial.printf("\u001b[38;5;136m%u Model | %s | %-58s%ddBm %s\u001b[0m\r\n", xPortGetCoreID(), f->getMac().c_str(), f->getId().c_str(), f->getRssi(), sMdl.c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
} // namespace NameModelHandler
|
|
@ -0,0 +1,14 @@
|
|||
#pragma once
|
||||
#include<NimBLEClient.h>
|
||||
#include <NimBLEDevice.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <sstream>
|
||||
#include <SoftFilters.h>
|
||||
#include <AsyncMqttClient.h>
|
||||
#include <BleFingerprint.h>
|
||||
#include <AsyncWiFiSettings.h>
|
||||
|
||||
namespace NameModelHandler {
|
||||
|
||||
bool requestData(NimBLEClient* pClient, BleFingerprint* fingerprint);
|
||||
}
|
|
@ -27,8 +27,8 @@ build_flags =
|
|||
-Wformat-truncation
|
||||
-D ARDUINOJSON_ENABLE_NAN=0
|
||||
-D CONFIG_BT_NIMBLE_TASK_STACK_SIZE=4096
|
||||
-D SCAN_TASK_STACK_SIZE=2096
|
||||
-D ARDUINO_LOOP_STACK_SIZE=6144
|
||||
-D SCAN_TASK_STACK_SIZE=2562
|
||||
-D ARDUINO_LOOP_STACK_SIZE=3584
|
||||
-D MQTT_MIN_FREE_MEMORY=12192
|
||||
-D CONFIG_ASYNC_TCP_USE_WDT=0
|
||||
-D CONFIG_BT_NIMBLE_MSYS1_BLOCK_COUNT=20
|
||||
|
@ -38,13 +38,13 @@ build_flags =
|
|||
; -D CONFIG_NIMBLE_CPP_LOG_LEVEL=4
|
||||
; -D CONFIG_NIMBLE_CPP_ENABLE_GAP_EVENT_CODE_TEXT=1
|
||||
; -D CONFIG_NIMBLE_CPP_ENABLE_RETURN_CODE_TEXT=1
|
||||
; -D CHECK_FOR_UPDATES_INTERVAL=1
|
||||
; -D CHECK_FOR_UPDATES_INTERVAL=1
|
||||
build_unflags =
|
||||
framework = arduino
|
||||
lib_deps =
|
||||
AsyncTCP = https://github.com/pbolduc/AsyncTCP.git@^1.2.0
|
||||
https://github.com/ESPresense/ESPAsyncWebServer.git
|
||||
https://github.com/ESPresense/AsyncWiFiSettings.git#1.0.6
|
||||
https://github.com/ESPresense/AsyncWiFiSettings.git@^1.0.7
|
||||
https://github.com/ESPresense/SoftFilters.git
|
||||
https://github.com/ESPresense/NimBLE-Arduino.git
|
||||
marvinroger/AsyncMqttClient@^0.9.0
|
||||
|
@ -120,8 +120,6 @@ lib_deps =
|
|||
paulstoffregen/OneWire@^2.3.7
|
||||
milesburton/DallasTemperature@^3.11.0
|
||||
|
||||
|
||||
|
||||
[env:esp32]
|
||||
extends = esp32
|
||||
lib_deps =
|
||||
|
|
|
@ -53,7 +53,7 @@ void Loop() {
|
|||
|
||||
void Added(BleFingerprint *f) {
|
||||
if (f->getIgnore()) return;
|
||||
Serial.printf("%u New %s | %s | %-58s%ddBm %s\r\n", xPortGetCoreID(), f->getRmAsst() ? "R" : (f->getAllowQuery() ? "Q" : " "), f->getMac().c_str(), f->getId().c_str(), f->getRssi(), f->getDiscriminator().c_str());
|
||||
Serial.printf("%u New %s | %s | %-58s%ddBm %s\r\n", xPortGetCoreID(), f->getAllowQuery() ? "Q" : " ", f->getMac().c_str(), f->getId().c_str(), f->getRssi(), f->getDiscriminator().c_str());
|
||||
}
|
||||
|
||||
void Removed(BleFingerprint *f) {
|
||||
|
|
|
@ -27,11 +27,11 @@ void serializeState(JsonObject &root) {
|
|||
}
|
||||
|
||||
void serializeConfigs(JsonObject &root) {
|
||||
JsonArray devices = root.createNestedArray("configs");
|
||||
JsonArray configs = root.createNestedArray("configs");
|
||||
|
||||
auto f = BleFingerprintCollection::deviceConfigs;
|
||||
for (auto it = f.begin(); it != f.end(); ++it) {
|
||||
JsonObject node = devices.createNestedObject();
|
||||
auto deviceConfigs = BleFingerprintCollection::deviceConfigs;
|
||||
for (auto it = deviceConfigs.begin(); it != deviceConfigs.end(); ++it) {
|
||||
const JsonObject& node = configs.createNestedObject();
|
||||
node["id"] = it->id;
|
||||
node["alias"] = it->alias;
|
||||
node["name"] = it->name;
|
||||
|
|
|
@ -18,7 +18,7 @@ int I2C_Bus_2_SDA = 0;
|
|||
int I2C_Bus_2_SCL = 0;
|
||||
|
||||
void ConnectToWifi() {
|
||||
AsyncWiFiSettings.heading("I2C Settings <a href='https://espresense.com/configuration/settings#i2c-settings' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#i2c-settings' target='_blank'>I2C Settings</a>", false);
|
||||
|
||||
AsyncWiFiSettings.html("h4", "Bus 1:");
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ void ConnectToWifi() {
|
|||
std::vector<String> ledTypes = {"PWM", "PWM Inverted", "Addressable GRB", "Addressable GRBW", "Addressable RGB", "Addressable RGBW"};
|
||||
std::vector<String> ledControlTypes = {"MQTT", "Status", "Motion", "Count"};
|
||||
|
||||
AsyncWiFiSettings.heading("LEDs <a href='https://espresense.com/configuration/settings#leds' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#leds' target='_blank'>LEDs</a>", false);
|
||||
|
||||
AsyncWiFiSettings.html("h4", "LED 1:");
|
||||
|
||||
|
|
|
@ -45,9 +45,9 @@ void ConnectToWifi() {
|
|||
}
|
||||
|
||||
void SerialReport() {
|
||||
Serial.print("Switch One Sensor: ");
|
||||
Serial.print("Switch One: ");
|
||||
Serial.println(switch_1Pin >= 0 ? "enabled" : "disabled");
|
||||
Serial.print("Switch Two Sensor: ");
|
||||
Serial.print("Switch Two: ");
|
||||
Serial.println(switch_2Pin >= 0 ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
|
@ -118,4 +118,4 @@ bool SendOnline() {
|
|||
online = true;
|
||||
return true;
|
||||
}
|
||||
} // namespace Switch
|
||||
} // namespace Switch
|
||||
|
|
|
@ -102,10 +102,12 @@ void firmwareUpdate() {
|
|||
case HTTP_UPDATE_FAILED:
|
||||
Serial.printf("Http Update Failed (Error=%d): %s\r\n", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());
|
||||
break;
|
||||
|
||||
case HTTP_UPDATE_NO_UPDATES:
|
||||
Serial.printf("No Update!\r\n");
|
||||
break;
|
||||
case HTTP_UPDATE_OK:
|
||||
Serial.printf("Update OK!\r\n");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
52
src/main.cpp
52
src/main.cpp
|
@ -1,6 +1,13 @@
|
|||
#define VAR_DECLS
|
||||
#include "main.h"
|
||||
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
void heapCapsAllocFailedHook(size_t requestedSize, uint32_t caps, const char *functionName)
|
||||
{
|
||||
printf("%s was called but failed to allocate %d bytes with 0x%X capabilities. \n",functionName, requestedSize, caps);
|
||||
}
|
||||
|
||||
bool sendTelemetry(unsigned int totalSeen, unsigned int totalFpSeen, int unsigned totalFpQueried, int unsigned totalFpReported, unsigned int count) {
|
||||
if (!online) {
|
||||
if (
|
||||
|
@ -107,9 +114,9 @@ bool sendTelemetry(unsigned int totalSeen, unsigned int totalFpSeen, int unsigne
|
|||
auto maxHeap = ESP.getMaxAllocHeap();
|
||||
auto freeHeap = ESP.getFreeHeap();
|
||||
doc["freeHeap"] = freeHeap;
|
||||
doc["maxAllocHeap"] = maxHeap;
|
||||
doc["memFrag"] = 100 - (maxHeap * 100.0 / freeHeap);
|
||||
doc["scanHighWater"] = uxTaskGetStackHighWaterMark(scanTaskHandle);
|
||||
doc["maxHeap"] = maxHeap;
|
||||
doc["scanStack"] = uxTaskGetStackHighWaterMark(scanTaskHandle);
|
||||
doc["loopStack"] = uxTaskGetStackHighWaterMark(nullptr);
|
||||
|
||||
serializeJson(doc, buffer);
|
||||
if (pub(teleTopic.c_str(), 0, false, buffer)) return true;
|
||||
|
@ -133,7 +140,7 @@ void setupNetwork() {
|
|||
std::vector<String> ethernetTypes = {"None", "WT32-ETH01", "ESP32-POE", "WESP32", "QuinLED-ESP32", "TwilightLord-ESP32", "ESP32Deux", "KIT-VE", "LilyGO-T-ETH-POE", "GL-inet GL-S10 v2.1 Ethernet"};
|
||||
ethernetType = AsyncWiFiSettings.dropdown("eth", ethernetTypes, 0, "Ethernet Type");
|
||||
|
||||
AsyncWiFiSettings.heading("MQTT <a href='https://espresense.com/configuration/settings#mqtt' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#mqtt' target='_blank'>MQTT</a>", false);
|
||||
mqttHost = AsyncWiFiSettings.string("mqtt_host", DEFAULT_MQTT_HOST, "Server");
|
||||
mqttPort = AsyncWiFiSettings.integer("mqtt_port", DEFAULT_MQTT_PORT, "Port");
|
||||
mqttUser = AsyncWiFiSettings.pstring("mqtt_user", DEFAULT_MQTT_USER, "Username");
|
||||
|
@ -144,28 +151,31 @@ void setupNetwork() {
|
|||
publishRooms = AsyncWiFiSettings.checkbox("pub_rooms", true, "Send to rooms topic");
|
||||
publishDevices = AsyncWiFiSettings.checkbox("pub_devices", true, "Send to devices topic");
|
||||
|
||||
AsyncWiFiSettings.heading("Updating <a href='https://espresense.com/configuration/settings#updating' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#updating' target='_blank'>Updating</a>", false);
|
||||
Updater::ConnectToWifi();
|
||||
|
||||
AsyncWiFiSettings.heading("Scanning <a href='https://espresense.com/configuration/settings#scanning' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#scanning' target='_blank'>Scanning</a>", false);
|
||||
BleFingerprintCollection::knownMacs = AsyncWiFiSettings.string("known_macs", "", "Known BLE mac addresses (no colons, space seperated)");
|
||||
BleFingerprintCollection::knownIrks = AsyncWiFiSettings.string("known_irks", "", "Known BLE identity resolving keys, should be 32 hex chars space seperated");
|
||||
BleFingerprintCollection::query = AsyncWiFiSettings.string("query", DEFAULT_QUERY, "Query device ids for characteristics (eg. apple:1005:9-26)");
|
||||
|
||||
AsyncWiFiSettings.heading("Counting <a href='https://espresense.com/configuration/settings#counting' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#querying' target='_blank'>Querying</a>", false);
|
||||
BleFingerprintCollection::query = AsyncWiFiSettings.string("query", DEFAULT_QUERY, "Query device ids for characteristics (eg. flora:)");
|
||||
BleFingerprintCollection::requeryMs = AsyncWiFiSettings.integer("requery_ms", 30, 3600, 300, "Requery interval in seconds") * 1000;
|
||||
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#counting' target='_blank'>Counting</a>", false);
|
||||
BleFingerprintCollection::countIds = AsyncWiFiSettings.string("count_ids", "", "Include id prefixes (space seperated)");
|
||||
BleFingerprintCollection::countEnter = AsyncWiFiSettings.floating("count_enter", 0, 100, 2, "Start counting devices less than distance (in meters)");
|
||||
BleFingerprintCollection::countExit = AsyncWiFiSettings.floating("count_exit", 0, 100, 4, "Stop counting devices greater than distance (in meters)");
|
||||
BleFingerprintCollection::countMs = AsyncWiFiSettings.integer("count_ms", 0, 3000000, 30000, "Include devices with age less than (in ms)");
|
||||
|
||||
AsyncWiFiSettings.heading("Filtering <a href='https://espresense.com/configuration/settings#filtering' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#filtering' target='_blank'>Filtering</a>", false);
|
||||
BleFingerprintCollection::include = AsyncWiFiSettings.string("include", DEFAULT_INCLUDE, "Include only sending these ids to mqtt (eg. apple:iphone10-6 apple:iphone13-2)");
|
||||
BleFingerprintCollection::exclude = AsyncWiFiSettings.string("exclude", DEFAULT_EXCLUDE, "Exclude sending these ids to mqtt (eg. exp:20 apple:iphone10-6)");
|
||||
BleFingerprintCollection::maxDistance = AsyncWiFiSettings.floating("max_dist", 0, 100, DEFAULT_MAX_DISTANCE, "Maximum distance to report (in meters)");
|
||||
BleFingerprintCollection::skipDistance = AsyncWiFiSettings.floating("skip_dist", 0, 10, DEFAULT_SKIP_DISTANCE, "Report early if beacon has moved more than this distance (in meters)");
|
||||
BleFingerprintCollection::skipMs = AsyncWiFiSettings.integer("skip_ms", 0, 3000000, DEFAULT_SKIP_MS, "Skip reporting if message age is less that this (in milliseconds)");
|
||||
|
||||
AsyncWiFiSettings.heading("Calibration <a href='https://espresense.com/configuration/settings#calibration' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#calibration' target='_blank'>Calibration</a>", false);
|
||||
BleFingerprintCollection::rxRefRssi = AsyncWiFiSettings.integer("ref_rssi", -100, 100, DEFAULT_RX_REF_RSSI, "Rssi expected from a 0dBm transmitter at 1 meter (NOT used for iBeacons or Eddystone)");
|
||||
BleFingerprintCollection::rxAdjRssi = AsyncWiFiSettings.integer("rx_adj_rssi", -100, 100, 0, "Rssi adjustment for receiver (use only if you know this device has a weak antenna)");
|
||||
BleFingerprintCollection::absorption = AsyncWiFiSettings.floating("absorption", -100, 100, DEFAULT_ABSORPTION, "Factor used to account for absorption, reflection, or diffraction");
|
||||
|
@ -174,7 +184,7 @@ void setupNetwork() {
|
|||
|
||||
GUI::ConnectToWifi();
|
||||
|
||||
AsyncWiFiSettings.heading("GPIO Sensors <a href='https://espresense.com/configuration/settings#gpio-sensors' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#gpio-sensors' target='_blank'>GPIO Sensors</a>", false);
|
||||
|
||||
BleFingerprintCollection::ConnectToWifi();
|
||||
Motion::ConnectToWifi();
|
||||
|
@ -185,7 +195,7 @@ void setupNetwork() {
|
|||
DHT::ConnectToWifi();
|
||||
I2C::ConnectToWifi();
|
||||
|
||||
AsyncWiFiSettings.heading("I2C Sensors <a href='https://espresense.com/configuration/settings#i2c-sensors' target='_blank'>ℹ️</a>", false);
|
||||
AsyncWiFiSettings.heading("<a href='https://espresense.com/configuration/settings#i2c-sensors' target='_blank'>I2C Sensors</a>", false);
|
||||
|
||||
AHTX0::ConnectToWifi();
|
||||
BH1750::ConnectToWifi();
|
||||
|
@ -393,6 +403,13 @@ void connectToMqtt() {
|
|||
mqttClient.connect();
|
||||
}
|
||||
|
||||
bool reportBuffer(BleFingerprint *f) {
|
||||
if (!mqttClient.connected()) return false;
|
||||
auto report = f->getReport();
|
||||
String topic = Sprintf(CHANNEL "/devices/%s/%s/%s", f->getId().c_str(), id.c_str(), report.getId().c_str());
|
||||
return mqttClient.publish(topic.c_str(), 0, false, report.getPayload().c_str());
|
||||
}
|
||||
|
||||
bool reportDevice(BleFingerprint *f) {
|
||||
doc.clear();
|
||||
JsonObject obj = doc.to<JsonObject>();
|
||||
|
@ -404,9 +421,7 @@ bool reportDevice(BleFingerprint *f) {
|
|||
|
||||
bool p1 = false, p2 = false;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
if (!mqttClient.connected())
|
||||
return false;
|
||||
|
||||
if (!mqttClient.connected()) return false;
|
||||
if (!p1 && (!publishRooms || mqttClient.publish(roomsTopic.c_str(), 0, false, buffer)))
|
||||
p1 = true;
|
||||
|
||||
|
@ -417,6 +432,7 @@ bool reportDevice(BleFingerprint *f) {
|
|||
return true;
|
||||
delay(20);
|
||||
}
|
||||
|
||||
teleFails++;
|
||||
return false;
|
||||
}
|
||||
|
@ -456,6 +472,11 @@ void reportLoop() {
|
|||
totalSeen += seen;
|
||||
totalFpSeen++;
|
||||
}
|
||||
|
||||
if (f->hasReport()) {
|
||||
if (reportBuffer(f))
|
||||
f->clearReport();
|
||||
}
|
||||
if (reportDevice(f)) {
|
||||
totalFpReported++;
|
||||
reported++;
|
||||
|
@ -515,6 +536,7 @@ void setup() {
|
|||
esp_log_level_set("*", ESP_LOG_ERROR);
|
||||
#endif
|
||||
Serial.printf("Pre-Setup Free Mem: %d\r\n", ESP.getFreeHeap());
|
||||
heap_caps_register_failed_alloc_callback(heapCapsAllocFailedHook);
|
||||
|
||||
#if M5STICK
|
||||
AXP192::Setup();
|
||||
|
|
Loading…
Reference in New Issue