From 98d6abeb1f88a68930fa4738a5edf0b6af4c5309 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 12 Nov 2023 09:48:46 -0600 Subject: [PATCH 1/3] Changed storage of required and optional Characteristics from unordered_set to vector Significantly reduces memory usage - unordered_sets take up much more memory and using std::find from is as efficient as using find() method of an unordered_set. To do: Remove all uses of unordered_set across HomeSpan wherever possible --- src/HomeSpan.cpp | 13 +++++-------- src/HomeSpan.h | 4 ++-- src/Span.h | 4 ++-- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 8576666..2de8c34 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -39,6 +39,7 @@ #include "HomeSpan.h" #include "HAP.h" +#include const __attribute__((section(".rodata_custom_desc"))) SpanPartition spanPartition = {HOMESPAN_MAGIC_COOKIE,0}; @@ -889,9 +890,7 @@ void Span::processSerialCommand(const char *c){ } else if((*acc)->aid==1) // this is an Accessory with aid=1, but it has more than just AccessoryInfo. So... isBridge=false; // ...this is not a bridge device - - unordered_set hapChar; - + for(auto chr=(*svc)->Characteristics.begin(); chr!=(*svc)->Characteristics.end(); chr++){ LOG0(" \u21e8 Characteristic %s(%s): IID=%d, %sUUID=\"%s\", %sPerms=", (*chr)->hapName,(*chr)->uvPrint((*chr)->value).c_str(),(*chr)->iid,(*chr)->isCustom?"Custom-":"",(*chr)->type,(*chr)->perms!=(*chr)->hapChar->perms?"Custom-":""); @@ -915,13 +914,13 @@ void Span::processSerialCommand(const char *c){ LOG0(" (nvs)"); LOG0("\n"); - if(!(*chr)->isCustom && !(*svc)->isCustom && (*svc)->req.find((*chr)->hapChar)==(*svc)->req.end() && (*svc)->opt.find((*chr)->hapChar)==(*svc)->opt.end()) + if(!(*chr)->isCustom && !(*svc)->isCustom && std::find((*svc)->req.begin(),(*svc)->req.end(),(*chr)->hapChar)==(*svc)->req.end() && std::find((*svc)->opt.begin(),(*svc)->opt.end(),(*chr)->hapChar)==(*svc)->opt.end()) LOG0(" *** WARNING #%d! Service does not support this Characteristic ***\n",++nWarnings); else if(invalidUUID((*chr)->type,(*chr)->isCustom)) LOG0(" *** ERROR #%d! Format of UUID is invalid ***\n",++nErrors); else - if(hapChar.find((*chr)->hapChar)!=hapChar.end()) + if(std::find_if((*svc)->Characteristics.begin(),chr,[chr](SpanCharacteristic *c)->boolean{return(c->hapChar==(*chr)->hapChar);})!=chr) LOG0(" *** ERROR #%d! Characteristic already defined for this Service ***\n",++nErrors); if((*chr)->setRangeError) @@ -932,13 +931,11 @@ void Span::processSerialCommand(const char *c){ if((*chr)->format!=STRING && ((*chr)->uvGet((*chr)->value) < (*chr)->uvGet((*chr)->minValue) || (*chr)->uvGet((*chr)->value) > (*chr)->uvGet((*chr)->maxValue))) LOG0(" *** WARNING #%d! Value of %g is out of range [%g,%g] ***\n",++nWarnings,(*chr)->uvGet((*chr)->value),(*chr)->uvGet((*chr)->minValue),(*chr)->uvGet((*chr)->maxValue)); - - hapChar.insert((*chr)->hapChar); } // Characteristics for(auto req=(*svc)->req.begin(); req!=(*svc)->req.end(); req++){ - if(hapChar.find(*req)==hapChar.end()) + if(std::find_if((*svc)->Characteristics.begin(),(*svc)->Characteristics.end(),[req](SpanCharacteristic *c)->boolean{return(c->hapChar==*req);})==(*svc)->Characteristics.end()) LOG0(" *** WARNING #%d! Required '%s' Characteristic for this Service not found ***\n",++nWarnings,(*req)->hapName); } diff --git a/src/HomeSpan.h b/src/HomeSpan.h index 2fb0f97..a61b5ca 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -426,8 +426,8 @@ class SpanService{ protected: virtual ~SpanService(); // destructor - unordered_set req; // unordered set of pointers to all required HAP Characteristic Types for this Service - unordered_set opt; // unordered set of pointers to all optional HAP Characteristic Types for this Service + vector req; // vector of pointers to all required HAP Characteristic Types for this Service + vector opt; // vector of pointers to all optional HAP Characteristic Types for this Service public: diff --git a/src/Span.h b/src/Span.h index ff43382..015a408 100644 --- a/src/Span.h +++ b/src/Span.h @@ -35,8 +35,8 @@ #define CREATE_SERV(NAME,UUID) struct NAME : SpanService { NAME() : SpanService{#UUID,#NAME}{ #define END_SERV }}; -#define REQ(HAPCHAR) req.insert(&hapChars.HAPCHAR) -#define OPT(HAPCHAR) opt.insert(&hapChars.HAPCHAR) +#define REQ(HAPCHAR) req.push_back(&hapChars.HAPCHAR) +#define OPT(HAPCHAR) opt.push_back(&hapChars.HAPCHAR) namespace Service { From 1e62a038f13cc2e47d6f17fde1e8b8f94e6cf307 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 12 Nov 2023 12:33:09 -0600 Subject: [PATCH 2/3] Changed unordered_set to vector when checking for duplicate AID This eliminated the use of unordered_set everywhere in HomeSpan --- src/HomeSpan.cpp | 6 +++--- src/HomeSpan.h | 2 -- src/Network.h | 3 --- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 2de8c34..78929dd 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -865,7 +865,7 @@ void Span::processSerialCommand(const char *c){ int nErrors=0; int nWarnings=0; - unordered_set aidValues; + vector aidValues; char pNames[][7]={"PR","PW","EV","AA","TW","HD","WR"}; for(auto acc=Accessories.begin(); acc!=Accessories.end(); acc++){ @@ -875,10 +875,10 @@ void Span::processSerialCommand(const char *c){ if(acc==Accessories.begin() && (*acc)->aid!=1) LOG0(" *** ERROR #%d! AID of first Accessory must always be 1 ***\n",++nErrors); - if(aidValues.find((*acc)->aid)!=aidValues.end()) + if(std::find(aidValues.begin(),aidValues.end(),(*acc)->aid)!=aidValues.end()) LOG0(" *** ERROR #%d! AID already in use for another Accessory ***\n",++nErrors); - aidValues.insert((*acc)->aid); + aidValues.push_back((*acc)->aid); for(auto svc=(*acc)->Services.begin(); svc!=(*acc)->Services.end(); svc++){ LOG0(" \u279f Service %s: IID=%d, %sUUID=\"%s\"\n",(*svc)->hapName,(*svc)->iid,(*svc)->isCustom?"Custom-":"",(*svc)->type); diff --git a/src/HomeSpan.h b/src/HomeSpan.h index a61b5ca..00636d0 100644 --- a/src/HomeSpan.h +++ b/src/HomeSpan.h @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include @@ -55,7 +54,6 @@ using std::vector; using std::unordered_map; -using std::unordered_set; using std::list; enum { diff --git a/src/Network.h b/src/Network.h index 679ecfe..1cb17d4 100644 --- a/src/Network.h +++ b/src/Network.h @@ -28,11 +28,8 @@ #pragma once #include -#include #include "Settings.h" -using std::unordered_set; - const int MAX_SSID=32; // max number of characters in WiFi SSID const int MAX_PWD=64; // max number of characters in WiFi Password From 4ab7503a72e277fd6f1a56cefeff81350051e425 Mon Sep 17 00:00:00 2001 From: Gregg Date: Sun, 12 Nov 2023 21:37:33 -0600 Subject: [PATCH 3/3] Added error checking for NVS storage Also added NVS storage diagnostic to 'm' command. --- src/HomeSpan.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/HomeSpan.cpp b/src/HomeSpan.cpp index 78929dd..e1684ef 100644 --- a/src/HomeSpan.cpp +++ b/src/HomeSpan.cpp @@ -855,6 +855,9 @@ void Span::processSerialCommand(const char *c){ case 'm': { LOG0("Free Heap=%d bytes (low=%d)\n",heap_caps_get_free_size(MALLOC_CAP_DEFAULT),heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT)); + nvs_stats_t nvs_stats; + nvs_get_stats(NULL, &nvs_stats); + LOG0("NVS: %d of %d records used\n",nvs_stats.used_entries,nvs_stats.total_entries-126); } break; @@ -1611,6 +1614,11 @@ boolean Span::updateDatabase(boolean updateMDNS){ } } + nvs_stats_t nvs_stats; + nvs_get_stats(NULL, &nvs_stats); + if(nvs_stats.free_entries<=130) + LOG0("\n*** WARNING: NVS is running low on space. Try erasing with 'E'. If that fails, increase size of NVS partition or reduce NVS usage.\n\n"); + Loops.clear(); for(auto acc=Accessories.begin(); acc!=Accessories.end(); acc++){ // identify all services with over-ridden loop() methods