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:
flush 2023-08-22 12:44:43 +02:00 committed by GitHub
parent d55c1451c0
commit 15a3f8c529
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 324 additions and 118 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;
};

View File

@ -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

View File

@ -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]); \
})

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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);
}

View File

@ -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 =

View File

@ -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) {

View File

@ -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;

View File

@ -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:");

View File

@ -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:");

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -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();