Skip to content

Commit bdab279

Browse files
committed
Merge branch 'ho-phase2' into heap-optimize
2 parents b48a3ea + 3d34f7c commit bdab279

23 files changed

+15172
-15046
lines changed

interface/src/lib/types/models.ts

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,6 @@ export type NetworkItem = {
4040
encryption_type: number;
4141
};
4242

43-
export type EthernetStatus = {
44-
status: number;
45-
ip_address: string;
46-
mac_address: string;
47-
};
48-
49-
export type EthernetSettings = {
50-
local_ip: string;
51-
gateway_ip: string;
52-
subnet_mask: string;
53-
};
54-
5543
export type ApStatus = {
5644
status: number;
5745
ip_address: string;
@@ -152,6 +140,8 @@ export type StaticSystemInformation = {
152140
flash_chip_size: number;
153141
flash_chip_speed: number;
154142
cpu_reset_reason: string;
143+
heap_info_app: string;
144+
heap_info_dma: string;
155145
};
156146

157147
export type SystemInformation = Analytics & StaticSystemInformation;

interface/src/routes/system/status/SystemStatus.svelte

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,16 @@
161161
{/snippet}
162162
{#snippet title()}
163163
<span>System Status</span>
164-
<div class="absolute right-5"><a href="https://{page.data.github.split("/")[0]}.github.io/{page.data.github.split("/")[1]}{page.url.pathname}" target="_blank" title="Documentation"><Help class="lex-shrink-0 mr-2 h-6 w-6 self-end" /></a></div> <!-- 🌙 link to docs -->
164+
<div class="absolute right-5">
165+
<a
166+
href="https://{page.data.github.split('/')[0]}.github.io/{page.data.github.split(
167+
'/'
168+
)[1]}{page.url.pathname}"
169+
target="_blank"
170+
title="Documentation"><Help class="lex-shrink-0 mr-2 h-6 w-6 self-end" /></a
171+
>
172+
</div>
173+
<!-- 🌙 link to docs -->
165174
{/snippet}
166175

167176
<div class="w-full overflow-x-auto">
@@ -191,39 +200,39 @@
191200
<div>
192201
<div class="font-bold">Performance</div>
193202
<div class="text-sm opacity-75">
194-
{systemInformation.lps} loops/s <!-- 🌙 -->
203+
{systemInformation.lps} loops/s <!-- 🌙 -->
195204
</div>
196205
</div>
197206
</div>
198207

199-
{#if page.data.features.battery && ($telemetry.battery.soc>=0 || $telemetry.battery.voltage>=0 || $telemetry.battery.current>=0)}
208+
{#if page.data.features.battery && ($telemetry.battery.soc >= 0 || $telemetry.battery.voltage >= 0 || $telemetry.battery.current >= 0)}
200209
<div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
201210
<div class="mask mask-hexagon bg-primary h-auto w-10 flex-none">
202211
<Battery class="text-primary-content h-auto w-full scale-75" />
203212
</div>
204-
{#if $telemetry.battery.soc>=0}
205-
<div>
206-
<div class="font-bold">Battery</div>
207-
<div class="text-sm opacity-75">
208-
{$telemetry.battery.soc} %
213+
{#if $telemetry.battery.soc >= 0}
214+
<div>
215+
<div class="font-bold">Battery</div>
216+
<div class="text-sm opacity-75">
217+
{$telemetry.battery.soc} %
218+
</div>
209219
</div>
210-
</div>
211220
{/if}
212-
{#if $telemetry.battery.voltage>=0}
213-
<div>
214-
<div class="font-bold">Voltage</div>
215-
<div class="text-sm opacity-75">
216-
{$telemetry.battery.voltage.toFixed(1)} V <!-- // 🌙 -->
221+
{#if $telemetry.battery.voltage >= 0}
222+
<div>
223+
<div class="font-bold">Voltage</div>
224+
<div class="text-sm opacity-75">
225+
{$telemetry.battery.voltage.toFixed(1)} V <!-- // 🌙 -->
226+
</div>
217227
</div>
218-
</div>
219228
{/if}
220-
{#if $telemetry.battery.current>=0}
221-
<div>
222-
<div class="font-bold">Current</div>
223-
<div class="text-sm opacity-75">
224-
{$telemetry.battery.current.toFixed(1)} A <!-- // 🌙 -->
229+
{#if $telemetry.battery.current >= 0}
230+
<div>
231+
<div class="font-bold">Current</div>
232+
<div class="text-sm opacity-75">
233+
{$telemetry.battery.current.toFixed(1)} A <!-- // 🌙 -->
234+
</div>
225235
</div>
226-
</div>
227236
{/if}
228237
</div>
229238
{/if}
@@ -250,6 +259,32 @@
250259
</div>
251260
</div>
252261

262+
<!-- 🌙 -->
263+
<div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
264+
<div class="mask mask-hexagon bg-primary h-auto w-10 flex-none">
265+
<Heap class="text-primary-content h-auto w-full scale-75" />
266+
</div>
267+
<div>
268+
<div class="font-bold">App RAM</div>
269+
<div class="text-sm opacity-75">
270+
{systemInformation.heap_info_app}
271+
</div>
272+
</div>
273+
</div>
274+
275+
<!-- 🌙 -->
276+
<div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
277+
<div class="mask mask-hexagon bg-primary h-auto w-10 flex-none">
278+
<Heap class="text-primary-content h-auto w-full scale-75" />
279+
</div>
280+
<div>
281+
<div class="font-bold">DMA RAM</div>
282+
<div class="text-sm opacity-75">
283+
{systemInformation.heap_info_dma}
284+
</div>
285+
</div>
286+
</div>
287+
253288
{#if systemInformation.psram_size}
254289
<div class="rounded-box bg-base-100 flex items-center space-x-3 px-4 py-2">
255290
<div class="mask mask-hexagon bg-primary h-auto w-10 flex-none">
@@ -322,7 +357,8 @@
322357
<Sketch class="text-primary-content h-auto w-full scale-75" />
323358
</div>
324359
<div>
325-
<div class="font-bold">Firmware Size</div> <!-- 🌙 -->
360+
<div class="font-bold">Firmware Size</div>
361+
<!-- 🌙 -->
326362
<div class="flex flex-wrap justify-start gap-1 text-sm opacity-75">
327363
<span>
328364
{(

interface/vite.config.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ const config: UserConfig = {
1818
proxy: {
1919
// Proxying REST: http://localhost:5173/rest/bar -> http://192.168.1.83/rest/bar
2020
'/rest': {
21-
target: 'http://192.168.1.105',
21+
target: 'http://192.168.1.152',
2222
changeOrigin: true
2323
},
2424
// Proxying websockets ws://localhost:5173/ws -> ws://192.168.1.83/ws
2525
'/ws': {
26-
target: 'ws://192.168.1.105',
26+
target: 'ws://192.168.1.152',
2727
changeOrigin: true,
2828
ws: true
2929
}

lib/framework/EventSocket.cpp

Lines changed: 33 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -108,36 +108,51 @@ esp_err_t EventSocket::onFrame(PsychicWebSocketRequest *request, httpd_ws_frame
108108
return ESP_OK;
109109
}
110110

111-
void EventSocket::emitEvent(String event, JsonObject &jsonObject, const char *originId, bool onlyToSameOrigin)
111+
void EventSocket::emitEvent(const String& event, const JsonObject &jsonObject, const char *originId, bool onlyToSameOrigin)
112112
{
113113
JsonDocument doc;
114114
doc["event"] = event;
115115
doc["data"] = jsonObject;
116116

117-
#if FT_ENABLED(EVENT_USE_JSON)
118-
size_t len = measureJson(doc);
119-
#else
120-
size_t len = measureMsgPack(doc);
121-
#endif
122-
123-
char *output = new char[len + 1];
117+
emitEvent(doc, originId, onlyToSameOrigin);
118+
}
124119

125-
#if FT_ENABLED(EVENT_USE_JSON)
126-
serializeJson(doc, output, len + 1);
127-
#else
128-
serializeMsgPack(doc, output, len);
129-
#endif
120+
// 🌙 extracted from above function so the caller can prepare the JsonDocument, which saves on heap usage
121+
void EventSocket::emitEvent(const JsonDocument &doc, const char *originId, bool onlyToSameOrigin)
122+
{
123+
#if FT_ENABLED(EVENT_USE_JSON)
124+
static String outBuffer; // reused across calls to avoid repeated allocation
125+
outBuffer.clear(); // keep capacity, reset length
126+
outBuffer.reserve(measureJson(doc)); // pre-reserve exact size (optional, improves speed for large JSON)
127+
serializeJson(doc, outBuffer);
130128

131-
// null terminate the string
132-
output[len] = '\0';
129+
emitEvent(doc["event"], outBuffer.c_str(), outBuffer.length(), originId, onlyToSameOrigin);
130+
#else
131+
// --- MsgPack path ---
132+
struct VecWriter {
133+
std::vector<uint8_t> &v;
134+
size_t write(uint8_t c) {
135+
v.push_back(c);
136+
return 1;
137+
}
138+
size_t write(const uint8_t *buf, size_t size) {
139+
v.insert(v.end(), buf, buf + size);
140+
return size;
141+
}
142+
};
133143

134-
emitEvent(event, output, len, originId, onlyToSameOrigin); // 🌙
144+
static std::vector<uint8_t> outBuffer; // reuse across calls
145+
outBuffer.clear(); // reset length but keep capacity
146+
outBuffer.reserve(measureMsgPack(doc));// optional: pre-reserve based on measureMsgPack
147+
VecWriter writer{outBuffer};
148+
serializeMsgPack(doc, writer);
135149

136-
delete[] output;
150+
emitEvent(doc["event"], (char *)outBuffer.data(), outBuffer.size(), originId, onlyToSameOrigin);
151+
#endif
137152
}
138153

139154
// 🌙 extracted from above function for FT_MONITOR, which uses char *output
140-
void EventSocket::emitEvent(String event, char *output, size_t len, const char *originId, bool onlyToSameOrigin)
155+
void EventSocket::emitEvent(const String& event, const char *output, size_t len, const char *originId, bool onlyToSameOrigin)
141156
{
142157
// Only process valid events
143158
if (!isEventValid(event))

lib/framework/EventSocket.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ class EventSocket
4040

4141
void onSubscribe(String event, SubscribeCallback callback);
4242

43-
void emitEvent(String event, JsonObject &jsonObject, const char *originId = "", bool onlyToSameOrigin = false);
44-
// if onlyToSameOrigin == true, the message will be sent to the originId only, otherwise it will be broadcasted to all clients except the originId
45-
void emitEvent(String event, char *output, size_t len, const char *originId = "", bool onlyToSameOrigin = false); // 🌙
43+
void emitEvent(const String& event, const JsonObject& jsonObject, const char *originId = "", bool onlyToSameOrigin = false);
44+
void emitEvent(const JsonDocument &doc, const char *originId = "", bool onlyToSameOrigin = false); // 🌙 jsonDocument contains event
45+
// if onlyToSameOrigin == true, the message will be sent to the originId only, otherwise it will be broadcasted to all clients except the originId
46+
void emitEvent(const String& event, const char *output, size_t len, const char *originId = "", bool onlyToSameOrigin = false); // 🌙 char output directly emitted
4647

4748
unsigned int getConnectedClients();
4849

lib/framework/SystemStatus.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,5 +170,64 @@ esp_err_t SystemStatus::systemStatus(PsychicRequest *request)
170170
root["cpu_reset_reason"] = verbosePrintResetReason(esp_reset_reason());
171171
root["uptime"] = millis() / 1000;
172172

173+
heapHealth(root["heap_info_app"].to<JsonVariant>(), MALLOC_CAP_INTERNAL);
174+
heapHealth(root["heap_info_dma"].to<JsonVariant>(), MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
175+
173176
return response.send();
174177
}
178+
179+
// 🌙
180+
void SystemStatus::heapHealth(JsonVariant variant, uint32_t caps) {
181+
multi_heap_info_t mhi;
182+
heap_caps_get_info(&mhi, caps);
183+
184+
// Normalize everything to 0–1
185+
float f1 = mhi.largest_free_block / 8192.0; // ideal largest block: ≥ 8 KB
186+
float f2 = mhi.total_free_bytes / 16384.0; // ideal free heap: ≥ 16 KB
187+
float f3 = mhi.minimum_free_bytes / 8192.0; // ideal minimum free: ≥ 8 KB
188+
189+
// Clamp
190+
if (f1 > 1) f1 = 1;
191+
if (f2 > 1) f2 = 1;
192+
if (f3 > 1) f3 = 1;
193+
194+
uint8_t health1 = 100 * (f1 + f2 + f3) / 3;
195+
196+
// largest_free_block → fragmentation
197+
// total_free_bytes → headroom
198+
// minimum_free_bytes → how close you came to death
199+
200+
// Condition Meaning
201+
// lfb < 5 KB Heavy fragmentation → unstable
202+
// tfb < 10–15 KB malloc/WiFi/TLS failures possible
203+
// mfb < 8 KB You were close to crashing before
204+
205+
// So the formula gives:
206+
// GOOD (🟢) → plenty of room & low fragmentation
207+
// WARNING (🟠) → danger if load spikes
208+
// BAD (🔴) → fragmentation or low RAM already dangerous
209+
210+
uint8_t health2 = 100 * mhi.largest_free_block / mhi.total_free_bytes;
211+
212+
// Interpretation:
213+
// ≥ 66% → 🟢 good
214+
// < 33% → 🔴 bad
215+
// else → 🟠 okay
216+
217+
char s[128];
218+
snprintf(s, sizeof(s),
219+
"%s %zu+%zu=%zu🧱| %d+%d=%d KB |🔹%dKB→%d%%%s (🔻%dKB)",
220+
health1>66?"🟢":(health1<33?"🔴":"🟠"),
221+
mhi.allocated_blocks,
222+
mhi.free_blocks,
223+
mhi.total_blocks,
224+
mhi.total_allocated_bytes / 1024,
225+
mhi.total_free_bytes / 1024,
226+
(mhi.total_allocated_bytes +mhi.total_free_bytes) / 1024,
227+
mhi.largest_free_block / 1024,
228+
health2,
229+
health2>66?"🟢":(health2<33?"🔴":"🟠"),
230+
mhi.minimum_free_bytes / 1024
231+
);
232+
variant.set(s);
233+
}

lib/framework/SystemStatus.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class SystemStatus
3535
PsychicHttpServer *_server;
3636
SecurityManager *_securityManager;
3737
esp_err_t systemStatus(PsychicRequest *request);
38+
void heapHealth(JsonVariant variant, uint32_t caps); // 🌙
3839
};
3940

4041
#endif // end SystemStatus_h

0 commit comments

Comments
 (0)