diff --git a/code/espurna/config/prototypes.h b/code/espurna/config/prototypes.h
index 74bfcc8b..fa8760bc 100644
--- a/code/espurna/config/prototypes.h
+++ b/code/espurna/config/prototypes.h
@@ -66,6 +66,10 @@ extern "C" {
#endif
}
+void infoMemory(const char* , unsigned int, unsigned int);
+unsigned int getFreeHeap();
+unsigned int getInitialFreeHeap();
+
// -----------------------------------------------------------------------------
// Domoticz
// -----------------------------------------------------------------------------
diff --git a/code/espurna/espurna.ino b/code/espurna/espurna.ino
index 852aa4d0..6992e216 100644
--- a/code/espurna/espurna.ino
+++ b/code/espurna/espurna.ino
@@ -22,6 +22,8 @@ along with this program. If not, see .
#include "config/all.h"
#include
+#include "libs/HeapStats.h"
+
std::vector _loop_callbacks;
std::vector _reload_callbacks;
@@ -65,7 +67,7 @@ void setup() {
// -------------------------------------------------------------------------
// Cache initial free heap value
- getInitialFreeHeap();
+ setInitialFreeHeap();
// Serial debug
#if DEBUG_SUPPORT
@@ -81,6 +83,9 @@ void setup() {
// Init persistance
settingsSetup();
+ // Return bogus free heap value for broken devices
+ // XXX: device is likely to trigger other bugs! tread carefuly
+ wtfHeap(getSetting("wtfHeap", 0).toInt());
// Init Serial, SPIFFS and system check
systemSetup();
diff --git a/code/espurna/libs/HeapStats.h b/code/espurna/libs/HeapStats.h
new file mode 100644
index 00000000..41bca690
--- /dev/null
+++ b/code/espurna/libs/HeapStats.h
@@ -0,0 +1,114 @@
+/*
+
+Show extended heap stats when EspClass::getHeapStats() is available
+
+*/
+
+#pragma once
+
+#include
+
+struct heap_stats_t {
+ uint32_t available;
+ uint16_t usable;
+ uint8_t frag_pct;
+};
+
+namespace EspClass_has_getHeapStats {
+ struct _detector {
+ template().getHeapStats(0,0,0))>
+ static std::true_type detect(int);
+
+ template
+ static std::false_type detect(...);
+ };
+
+ template
+ struct detector : public _detector {
+ using result = decltype(
+ std::declval().detect(0));
+ };
+
+ template
+ struct typed_check : public detector::result {
+ };
+
+ typed_check check{};
+};
+
+template
+void _getHeapStats(std::true_type&, T& instance, heap_stats_t& stats) {
+ instance.getHeapStats(&stats.available, &stats.usable, &stats.frag_pct);
+}
+
+template
+void _getHeapStats(std::false_type&, T& instance, heap_stats_t& stats) {
+ stats.available = instance.getFreeHeap();
+ stats.usable = 0;
+ stats.frag_pct = 0;
+}
+
+void getHeapStats(heap_stats_t& stats) {
+ _getHeapStats(EspClass_has_getHeapStats::check, ESP, stats);
+}
+
+// WTF
+// Calling ESP.getFreeHeap() is making the system crash on a specific
+// AiLight bulb, but anywhere else it should work as expected
+static bool _heap_value_wtf = false;
+
+heap_stats_t getHeapStats() {
+ heap_stats_t stats;
+ if (_heap_value_wtf) {
+ stats.available = 9999;
+ stats.usable = 9999;
+ stats.frag_pct = 0;
+ return stats;
+ }
+ getHeapStats(stats);
+ return stats;
+}
+
+void wtfHeap(bool value) {
+ _heap_value_wtf = value;
+}
+
+unsigned int getFreeHeap() {
+ return getHeapStats().available;
+}
+
+static unsigned int _initial_heap_value = 0;
+void setInitialFreeHeap() {
+ _initial_heap_value = getFreeHeap();
+}
+
+unsigned int getInitialFreeHeap() {
+ if (0 == _initial_heap_value) {
+ setInitialFreeHeap();
+ }
+ return _initial_heap_value;
+}
+
+void infoMemory(const char* name, const heap_stats_t& stats) {
+ infoMemory(name, getInitialFreeHeap(), stats.available);
+}
+
+void infoHeapStats(const char* name, const heap_stats_t& stats) {
+ DEBUG_MSG_P(
+ PSTR("[MAIN] %-6s: %5u bytes available | %5u bytes lost (%2u%%) | %5u bytes free (%2u%%)\n"),
+ name,
+ stats.available,
+ (stats.available - stats.usable),
+ stats.frag_pct,
+ stats.usable,
+ (100 - stats.frag_pct)
+ );
+}
+
+void infoHeapStats(bool show_frag_stats = true) {
+ infoMemory("Heap", getHeapStats());
+ if (show_frag_stats && EspClass_has_getHeapStats::check) {
+ infoHeapStats("Heap", getHeapStats());
+ }
+}
diff --git a/code/espurna/terminal.ino b/code/espurna/terminal.ino
index c67d6fd6..9081a4d8 100644
--- a/code/espurna/terminal.ino
+++ b/code/espurna/terminal.ino
@@ -12,6 +12,7 @@ Copyright (C) 2016-2019 by Xose Pérez
#include "libs/EmbedisWrap.h"
#include
#include "libs/StreamInjector.h"
+#include "libs/HeapStats.h"
StreamInjector _serial = StreamInjector(TERMINAL_BUFFER_SIZE);
EmbedisWrap embedis(_serial, TERMINAL_BUFFER_SIZE);
@@ -124,12 +125,12 @@ void _terminalInitCommand() {
});
terminalRegisterCommand(F("HEAP"), [](Embedis* e) {
- infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
+ infoHeapStats();
terminalOK();
});
terminalRegisterCommand(F("STACK"), [](Embedis* e) {
- infoMemory("Stack", 4096, getFreeStack());
+ infoMemory("Stack", CONT_STACKSIZE, getFreeStack());
terminalOK();
});
diff --git a/code/espurna/utils.ino b/code/espurna/utils.ino
index ca280b8f..a0e9258f 100644
--- a/code/espurna/utils.ino
+++ b/code/espurna/utils.ino
@@ -7,6 +7,7 @@ Copyright (C) 2017-2019 by Xose Pérez
*/
#include
+#include "libs/HeapStats.h"
String getIdentifier() {
char buffer[20];
@@ -63,26 +64,6 @@ unsigned char getHeartbeatInterval() {
return getSetting("hbInterval", HEARTBEAT_INTERVAL).toInt();
}
-// WTF
-// Calling ESP.getFreeHeap() is making the system crash on a specific
-// AiLight bulb, but anywhere else...
-unsigned int getFreeHeap() {
- if (getSetting("wtfHeap", 0).toInt() == 1) return 9999;
- return ESP.getFreeHeap();
-}
-
-unsigned int getInitialFreeHeap() {
- static unsigned int _heap = 0;
- if (0 == _heap) {
- _heap = getFreeHeap();
- }
- return _heap;
-}
-
-unsigned int getUsedHeap() {
- return getInitialFreeHeap() - getFreeHeap();
-}
-
String getEspurnaModules() {
return FPSTR(espurna_modules);
}
@@ -186,10 +167,10 @@ namespace Heartbeat {
void heartbeat() {
unsigned long uptime_seconds = getUptime();
- unsigned int free_heap = getFreeHeap();
-
+ heap_stats_t heap_stats = getHeapStats();
+
UNUSED(uptime_seconds);
- UNUSED(free_heap);
+ UNUSED(heap_stats);
#if MQTT_SUPPORT
unsigned char _heartbeat_mode = getHeartbeatMode();
@@ -204,7 +185,7 @@ void heartbeat() {
if (serial) {
DEBUG_MSG_P(PSTR("[MAIN] Uptime: %lu seconds\n"), uptime_seconds);
- infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
+ infoHeapStats();
#if ADC_MODE_VALUE == ADC_VCC
DEBUG_MSG_P(PSTR("[MAIN] Power: %lu mV\n"), ESP.getVcc());
#endif
@@ -264,7 +245,7 @@ void heartbeat() {
#endif
if (hb_cfg & Heartbeat::Freeheap)
- mqttSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
+ mqttSend(MQTT_TOPIC_FREEHEAP, String(heap_stats.available).c_str());
if (hb_cfg & Heartbeat::Relay)
relayMQTT();
@@ -311,7 +292,7 @@ void heartbeat() {
idbSend(MQTT_TOPIC_UPTIME, String(uptime_seconds).c_str());
if (hb_cfg & Heartbeat::Freeheap)
- idbSend(MQTT_TOPIC_FREEHEAP, String(free_heap).c_str());
+ idbSend(MQTT_TOPIC_FREEHEAP, String(heap_stats.available).c_str());
if (hb_cfg & Heartbeat::Rssi)
idbSend(MQTT_TOPIC_RSSI, String(WiFi.RSSI()).c_str());
@@ -455,11 +436,15 @@ void info() {
// -------------------------------------------------------------------------
+ static bool show_frag_stats = false;
+
infoMemory("EEPROM", SPI_FLASH_SEC_SIZE, SPI_FLASH_SEC_SIZE - settingsSize());
- infoMemory("Heap", getInitialFreeHeap(), getFreeHeap());
- infoMemory("Stack", 4096, getFreeStack());
+ infoHeapStats(show_frag_stats);
+ infoMemory("Stack", CONT_STACKSIZE, getFreeStack());
DEBUG_MSG_P(PSTR("\n"));
+ show_frag_stats = true;
+
// -------------------------------------------------------------------------
DEBUG_MSG_P(PSTR("[MAIN] Boot version: %d\n"), ESP.getBootVersion());