PSRAM: Adds custom allocator and HS_MALLOC/HS_CALLOC macros

Forces all heap requests to be drawn from PSRAM, unless the device does not contain PSRAM.

To do:  Provide easy-to-use NEW() macro; add custom allocator to unordered sets; auto-shrink vectors after updateDatabase()
This commit is contained in:
Gregg 2023-11-20 21:57:20 -06:00
parent 77e7ff2b2e
commit 8268e519dd
8 changed files with 90 additions and 113 deletions

View File

@ -1741,7 +1741,7 @@ nvs_handle HAPClient::srpNVS;
HKDF HAPClient::hkdf;
pairState HAPClient::pairStatus;
Accessory HAPClient::accessory;
list<Controller> HAPClient::controllerList;
list<Controller, Mallocator<Controller>> HAPClient::controllerList;
SRP6A HAPClient::srp;
int HAPClient::conNum;

View File

@ -94,7 +94,7 @@ struct HAPClient {
static pairState pairStatus; // tracks pair-setup status
static SRP6A srp; // stores all SRP-6A keys used for Pair-Setup
static Accessory accessory; // Accessory ID and Ed25519 public and secret keys- permanently stored
static list<Controller> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
static list<Controller, Mallocator<Controller>> controllerList; // linked-list of Paired Controller IDs and ED25519 long-term public keys - permanently stored
static int conNum; // connection number - used to keep track of per-connection EV notifications
// individual structures and data defined for each Hap Client connection

View File

@ -69,7 +69,7 @@ void Span::begin(Category catID, const char *displayName, const char *hostNameBa
if(requestedMaxCon<maxConnections) // if specific request for max connections is less than computed max connections
maxConnections=requestedMaxCon; // over-ride max connections with requested value
hap=(HAPClient **)calloc(maxConnections,sizeof(HAPClient *));
hap=(HAPClient **)HS_CALLOC(maxConnections,sizeof(HAPClient *));
for(int i=0;i<maxConnections;i++)
hap[i]=new HAPClient;
@ -854,10 +854,14 @@ void Span::processSerialCommand(const char *c){
break;
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));
LOG0("Free Heap Internal RAM : %7d bytes. Low: %7d\n",heap_caps_get_free_size(MALLOC_CAP_INTERNAL),heap_caps_get_minimum_free_size(MALLOC_CAP_INTERNAL));
#if defined(BOARD_HAS_PSRAM)
LOG0("Free Heap SPI (PS) RAM : %7d bytes. Low: %7d\n",heap_caps_get_free_size(MALLOC_CAP_SPIRAM),heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM));
#endif
LOG0("Lowest stack level : %7d bytes\n",uxTaskGetStackHighWaterMark(NULL));
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);
LOG0("NVS Flash Partition : %7d of %d records used\n\n",nvs_stats.used_entries,nvs_stats.total_entries-126);
}
break;
@ -868,7 +872,7 @@ void Span::processSerialCommand(const char *c){
int nErrors=0;
int nWarnings=0;
vector<uint32_t> aidValues;
vector<uint32_t, Mallocator<uint32_t>> aidValues;
char pNames[][7]={"PR","PW","EV","AA","TW","HD","WR"};
for(auto acc=Accessories.begin(); acc!=Accessories.end(); acc++){
@ -1825,7 +1829,7 @@ SpanCharacteristic::SpanCharacteristic(HapChar *hapChar, boolean isCustom){
service=homeSpan.Accessories.back()->Services.back();
aid=homeSpan.Accessories.back()->aid;
ev=(boolean *)calloc(homeSpan.maxConnections,sizeof(boolean));
ev=(boolean *)HS_CALLOC(homeSpan.maxConnections,sizeof(boolean));
}
///////////////////////////////
@ -2152,7 +2156,7 @@ void SpanWebLog::init(uint16_t maxEntries, const char *serv, const char *tz, con
timeServer=serv;
timeZone=tz;
statusURL="GET /" + String(url) + " ";
log = (log_t *)calloc(maxEntries,sizeof(log_t));
log = (log_t *)HS_CALLOC(maxEntries,sizeof(log_t));
if(timeServer)
homeSpan.reserveSocketConnections(1);
}
@ -2492,7 +2496,7 @@ void SpanPoint::dataReceived(const uint8_t *mac, const uint8_t *incomingData, in
uint8_t SpanPoint::lmk[16];
boolean SpanPoint::initialized=false;
boolean SpanPoint::isHub=false;
vector<SpanPoint *> SpanPoint::SpanPoints;
vector<SpanPoint *, Mallocator<SpanPoint *>> SpanPoint::SpanPoints;
uint16_t SpanPoint::channelMask=0x3FFE;
QueueHandle_t SpanPoint::statusQueue;
nvs_handle SpanPoint::pointNVS;

View File

@ -31,6 +31,14 @@
#error ERROR: HOMESPAN IS ONLY AVAILABLE FOR ESP32 MICROCONTROLLERS!
#endif
#if defined(BOARD_HAS_PSRAM)
#define HS_MALLOC ps_malloc
#define HS_CALLOC ps_calloc
#else
#define HS_MALLOC malloc
#define HS_CALLOC calloc
#endif
#pragma GCC diagnostic ignored "-Wpmf-conversions" // eliminates warning messages from use of pointers to member functions to detect whether update() and loop() are overridden by user
#pragma GCC diagnostic ignored "-Wunused-result" // eliminates warning message regarded unused result from call to crypto_scalarmult_curve25519()
@ -69,6 +77,25 @@ enum {
///////////////////////////////
template <class T>
struct Mallocator {
typedef T value_type;
Mallocator() = default;
template <class U> constexpr Mallocator(const Mallocator<U>&) noexcept {}
[[nodiscard]] T* allocate(std::size_t n) {
if(n > std::size_t(-1) / sizeof(T)) throw std::bad_alloc();
if(auto p = static_cast<T*>(HS_MALLOC(n*sizeof(T)))) return p;
throw std::bad_alloc();
}
void deallocate(T* p, std::size_t) noexcept { std::free(p); }
};
template <class T, class U>
bool operator==(const Mallocator<T>&, const Mallocator<U>&) { return true; }
template <class T, class U>
bool operator!=(const Mallocator<T>&, const Mallocator<U>&) { return false; }
///////////////////////////////
#define STATUS_UPDATE(LED_UPDATE,MESSAGE_UPDATE) {homeSpan.statusLED->LED_UPDATE;if(homeSpan.statusCallback)homeSpan.statusCallback(MESSAGE_UPDATE);}
enum HS_STATUS {
@ -251,10 +278,10 @@ class Span{
SpanOTA spanOTA; // manages OTA process
SpanConfig hapConfig; // track configuration changes to the HAP Accessory database; used to increment the configuration number (c#) when changes found
vector<SpanAccessory *> Accessories; // vector of pointers to all Accessories
vector<SpanService *> Loops; // vector of pointer to all Services that have over-ridden loop() methods
vector<SpanBuf> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
vector<SpanButton *> PushButtons; // vector of pointer to all PushButtons
vector<SpanAccessory *, Mallocator<SpanAccessory *>> Accessories; // vector of pointers to all Accessories
vector<SpanService *, Mallocator<SpanService *>> Loops; // vector of pointer to all Services that have over-ridden loop() methods
vector<SpanBuf, Mallocator<SpanBuf>> Notifications; // vector of SpanBuf objects that store info for Characteristics that are updated with setVal() and require a Notification Event
vector<SpanButton *, Mallocator<SpanButton *>> PushButtons; // vector of pointer to all PushButtons
unordered_map<uint64_t, uint32_t> TimedWrites; // map of timed-write PIDs and Alarm Times (based on TTLs)
unordered_map<char, SpanUserCommand *> UserCommands; // map of pointers to all UserCommands
@ -387,7 +414,7 @@ class SpanAccessory{
uint32_t aid=0; // Accessory Instance ID (HAP Table 6-1)
int iidCount=0; // running count of iid to use for Services and Characteristics associated with this Accessory
vector<SpanService *> Services; // vector of pointers to all Services in this Accessory
vector<SpanService *, Mallocator<SpanService*>> Services; // vector of pointers to all Services in this Accessory
int sprintfAttributes(char *cBuf, int flags); // prints Accessory JSON database into buf, unless buf=NULL; return number of characters printed, excluding null terminator, even if buf=NULL
@ -414,8 +441,8 @@ class SpanService{
const char *hapName; // HAP Name
boolean hidden=false; // optional property indicating service is hidden
boolean primary=false; // optional property indicating service is primary
vector<SpanCharacteristic *> Characteristics; // vector of pointers to all Characteristics in this Service
vector<SpanService *> linkedServices; // vector of pointers to any optional linked Services
vector<SpanCharacteristic *, Mallocator<SpanCharacteristic*>> Characteristics; // vector of pointers to all Characteristics in this Service
vector<SpanService *, Mallocator<SpanService *>> linkedServices; // vector of pointers to any optional linked Services
boolean isCustom; // flag to indicate this is a Custom Service
SpanAccessory *accessory=NULL; // pointer to Accessory containing this Service
@ -424,8 +451,8 @@ class SpanService{
protected:
virtual ~SpanService(); // destructor
vector<HapChar *> req; // vector of pointers to all required HAP Characteristic Types for this Service
vector<HapChar *> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
vector<HapChar *, Mallocator<HapChar*>> req; // vector of pointers to all required HAP Characteristic Types for this Service
vector<HapChar *, Mallocator<HapChar*>> opt; // vector of pointers to all optional HAP Characteristic Types for this Service
public:
@ -433,7 +460,7 @@ class SpanService{
SpanService *setPrimary(); // sets the Service Type to be primary and returns pointer to self
SpanService *setHidden(); // sets the Service Type to be hidden and returns pointer to self
SpanService *addLink(SpanService *svc); // adds svc as a Linked Service and returns pointer to self
vector<SpanService *> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops
vector<SpanService *, Mallocator<SpanService *>> getLinks(){return(linkedServices);} // returns linkedServices vector for use as range in "for-each" loops
virtual boolean update() {return(true);} // placeholder for code that is called when a Service is updated via a Controller. Must return true/false depending on success of update
virtual void loop(){} // loops for each Service - called every cycle if over-ridden with user-defined code
@ -589,7 +616,7 @@ class SpanCharacteristic{
uvSet(value,val);
if(nvsStore){
nvsKey=(char *)malloc(16);
nvsKey=(char *)HS_MALLOC(16);
uint16_t t;
sscanf(type,"%hx",&t);
sprintf(nvsKey,"%04X%08X%03X",t,aid,iid&0xFFF);
@ -901,7 +928,7 @@ class SpanPoint {
static uint8_t lmk[16];
static boolean initialized;
static boolean isHub;
static vector<SpanPoint *> SpanPoints;
static vector<SpanPoint *, Mallocator<SpanPoint *>> SpanPoints;
static uint16_t channelMask; // channel mask (only used for remote devices)
static QueueHandle_t statusQueue; // queue for communication between SpanPoint::dataSend and SpanPoint::send
static nvs_handle pointNVS; // NVS storage for channel number (only used for remote devices)

View File

@ -40,7 +40,7 @@ void Network::scan(){
int n=WiFi.scanNetworks();
free(ssidList);
ssidList=(char **)calloc(n,sizeof(char *));
ssidList=(char **)HS_CALLOC(n,sizeof(char *));
numSSID=0;
for(int i=0;i<n;i++){
@ -50,7 +50,7 @@ void Network::scan(){
found=true;
}
if(!found){
ssidList[numSSID]=(char *)calloc(WiFi.SSID(i).length()+1,sizeof(char));
ssidList[numSSID]=(char *)HS_CALLOC(WiFi.SSID(i).length()+1,sizeof(char));
sprintf(ssidList[numSSID],"%s",WiFi.SSID(i).c_str());
numSSID++;
}

View File

@ -88,7 +88,7 @@ int TLV<tagType, maxTags>::create(tagType tag, int maxLen, const char *name){
tlv[numTags].maxLen=maxLen;
tlv[numTags].name=name;
tlv[numTags].len=-1;
tlv[numTags].val=(uint8_t *)malloc(maxLen);
tlv[numTags].val=(uint8_t *)HS_MALLOC(maxLen);
numTags++;
return(1);

View File

@ -30,6 +30,14 @@
#include <Arduino.h>
#include <driver/timer.h>
#if defined(BOARD_HAS_PSRAM)
#define HS_MALLOC ps_malloc
#define HS_CALLOC ps_calloc
#else
#define HS_MALLOC malloc
#define HS_CALLOC calloc
#endif
namespace Utils {
char *readSerial(char *c, int max); // read serial port into 'c' until <newline>, but storing only first 'max' characters (the rest are discarded)
@ -54,7 +62,7 @@ class TempBuffer {
TempBuffer(int _nElements) : nElements(_nElements) {
nBytes=nElements*sizeof(bufType);
buf=(bufType *)malloc(nBytes);
buf=(bufType *)HS_MALLOC(nBytes);
if(buf==NULL){
Serial.print("\n\n*** FATAL ERROR: Requested allocation of ");
Serial.print(nBytes);

View File

@ -25,101 +25,39 @@
*
********************************************************************************/
#include "HomeSpan.h"
struct LED_Service : Service::LightBulb {
int ledPin;
SpanCharacteristic *power;
LED_Service(int ledPin) : Service::LightBulb(){
power=new Characteristic::On();
this->ledPin=ledPin;
pinMode(ledPin,OUTPUT);
}
boolean update(){
digitalWrite(ledPin,power->getNewVal());
WEBLOG("Power = %s",power->getNewVal()?"ON":"OFF");
return(true);
}
};
//////////////////////////////////////
void extraData(String &r){
r+="<tr><td>Free DRAM:</td><td>" + String(esp_get_free_internal_heap_size()) + " bytes</td></tr>\n";
r+="</table><p><a href=\"https://github.com/HomeSpan/HomeSpan\">Click Here to Access HomeSpan Repo</a><p>";
}
//////////////////////////////////////
void setup() {
Serial.begin(115200);
// homeSpan.setHostNameSuffix("");
homeSpan.setLogLevel(2);
// homeSpan.setControlPin(21);
homeSpan.begin(Category::Lighting,"HomeSpan Max");
homeSpan.setLogLevel(2).enableWebLog(500).setWebLogCallback(extraData);
// homeSpan.reserveSocketConnections(10);
new(HS_MALLOC(sizeof(SpanAccessory))) SpanAccessory();
new(HS_MALLOC(sizeof(SpanService))) Service::AccessoryInformation();
new(HS_MALLOC(sizeof(SpanCharacteristic))) Characteristic::Identify();
// homeSpan.setApSSID("HS_Setup");
// homeSpan.setApPassword("");
// .setStatusPin(13);
// homeSpan.setSerialInputDisable(true);
// homeSpan.enableOTA();
homeSpan.setWifiCallback(wifiCB);
homeSpan.setWifiCallbackAll(wifiCB_ALL).setVerboseWifiReconnect(true);
homeSpan.setRebootCallback( [](uint8_t c) {if(c==3) homeSpan.processSerialCommand("X");} );
new SpanUserCommand('D', " - disconnect WiFi", [](const char *buf){WiFi.disconnect();});
homeSpan.begin(Category::Lighting,"HomeSpan LED");
new SpanAccessory();
new Service::AccessoryInformation();
new Characteristic::Identify();
new LED_Service(13);
// homeSpan.autoPoll();
// for(int i=0;i<300;i++)
// WEBLOG("Here is some text of a log file %d",i);
for(int i=0;i<80;i++){
new(HS_MALLOC(sizeof(SpanAccessory))) SpanAccessory();
new(HS_MALLOC(sizeof(SpanService))) Service::AccessoryInformation();
new(HS_MALLOC(sizeof(SpanCharacteristic))) Characteristic::Identify();
char c[30];
sprintf(c,"Light-%d",i);
new(HS_MALLOC(sizeof(SpanCharacteristic))) Characteristic::Name(c);
new(HS_MALLOC(sizeof(SpanService))) Service::LightBulb();
new(HS_MALLOC(sizeof(SpanCharacteristic))) Characteristic::On();
new(HS_MALLOC(sizeof(SpanCharacteristic))) Characteristic::Brightness();
}
}
//////////////////////////////////////
void loop(){
homeSpan.poll();
//delay(10000);
//Serial.println(millis());
}
//////////////////////////////////////
void wifiCB(){
Serial.printf("\n\n****** IN WIFI CALLBACK *******\n\n");
}
//////////////////////////////////////
void wifiCB_ALL(int n){
Serial.printf("\n\n****** IN WIFI CALLBACK ALL. Count=%d *******\n\n",n);
}
//////////////////////////////////////
void rebootCB(uint8_t count){
if(count>=3){
Serial.printf("\n*** Detected 3 or more short reboots. Erasing WiFi data...\n");
homeSpan.processSerialCommand("X");
}
}