Fix capacity matrix build regressions

This commit is contained in:
mikecarper committed 2026-09-17 23:54:11 -07:00
1 parent e4db57dbef
commit 5f7d03373c
11 files changed
+75 -13

No files matched your search

+4 -1
View File
@@ -37,7 +37,10 @@ The [small-screen message layout](v4_pixel5_font_trial.md) retains complete
160-byte messages. Its expanded preview records add 2,816 bytes to the
startup allowance and increase the contiguous history allocation budget.
The V4 can allocate that history in PSRAM; the guard conservatively reserves
internal capacity so that PSRAM availability cannot hide a RAM shortage.
internal capacity so that PSRAM availability cannot hide a RAM shortage. A
target may replace the generic UI allowance only with
`MESH_COMPANION_SCREEN_STARTUP_BYTES`; a source-side assertion then covers its
actual startup screens plus ESP32 allocator overhead.
These are engineering allowances for supported configurations, not measured
free heap after boot or a guarantee against every future allocation failure.
@@ -1757,6 +1757,17 @@ public:
}
};
#if defined(MESH_COMPANION_SCREEN_STARTUP_BYTES)
// The target may replace the generic UI allowance with a tighter measured
// bound. Keep the check next to the actual allocations so a future screen
// growth cannot silently invalidate its runtime-RAM budget. ESP32's checked
// heap allocator can consume up to 16 bytes per allocation.
static_assert(sizeof(SplashScreen) + sizeof(HomeScreen)
+ sizeof(MsgPreviewScreen) + 3 * 16
<= MESH_COMPANION_SCREEN_STARTUP_BYTES,
"Increase MESH_COMPANION_SCREEN_STARTUP_BYTES and its RAM budget");
#endif
void UITask::begin(DisplayDriver* display, SensorManager* sensors, CompanionNodePrefs* node_prefs) {
_display = display;
_sensors = sensors;
@@ -3,6 +3,7 @@
#if MESH_ENABLE_ROOM_FLOOD_RULE_ENGINE
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include <helpers/FloodFilterPolicy.h>
@@ -394,8 +395,13 @@ static bool verifyWrittenFile(FILESYSTEM* fs, const char* path,
} // namespace
FloodRuleEngine::FloodRuleEngine() : _fs(NULL), _regions(NULL) {
memset(_entries, 0, sizeof(_entries));
FloodRuleEngine::FloodRuleEngine()
: _fs(NULL), _regions(NULL),
_entries(new (std::nothrow) Entry[RULE_SLOTS]) {
// MyMesh is constructed before setup(), while the internal heap is
// unfragmented. The runtime RAM gate budgets this exact allocation.
if (_entries == NULL) abort();
memset(_entries, 0, sizeof(Entry) * RULE_SLOTS);
}
void FloodRuleEngine::begin(FILESYSTEM* fs, RegionMap* regions) {
@@ -405,7 +411,7 @@ void FloodRuleEngine::begin(FILESYSTEM* fs, RegionMap* regions) {
}
void FloodRuleEngine::seedDefaults() {
memset(_entries, 0, sizeof(_entries));
memset(_entries, 0, sizeof(Entry) * RULE_SLOTS);
Entry& entry = _entries[0];
entry.active = true;
entry.payload_type = PAYLOAD_TYPE_OTA;
@@ -423,7 +429,7 @@ void FloodRuleEngine::load() {
enum class FileState : uint8_t { Missing, Valid, Invalid, Unreadable };
auto loadFile = [this](const char* path) -> FileState {
memset(_entries, 0, sizeof(_entries));
memset(_entries, 0, sizeof(Entry) * RULE_SLOTS);
if (!_fs->exists(path)) return FileState::Missing;
File file = openRead(_fs, path);
if (!file) return FileState::Unreadable;
@@ -655,7 +661,7 @@ void FloodRuleEngine::load() {
&& file.available() == 0;
}
file.close();
if (!success) memset(_entries, 0, sizeof(_entries));
if (!success) memset(_entries, 0, sizeof(Entry) * RULE_SLOTS);
return success ? FileState::Valid : FileState::Invalid;
};
@@ -1746,7 +1752,7 @@ void FloodRuleEngine::remove(const char* args, char* reply) {
if (asciiEqual(selector, "all")) {
Entry previous[RULE_SLOTS];
memcpy(previous, _entries, sizeof(previous));
memset(_entries, 0, sizeof(_entries));
memset(_entries, 0, sizeof(Entry) * RULE_SLOTS);
if (!save()) {
memcpy(_entries, previous, sizeof(_entries));
copyString(reply, "Err - unable to save flood filter", 160);
@@ -6,6 +6,7 @@
#include <Arduino.h>
#include <Mesh.h>
#include <new>
#include <helpers/IdentityStore.h>
#include <helpers/RegionMap.h>
@@ -71,9 +72,16 @@ private:
bool rate_window_active;
};
// This table is 6,200 bytes. Keep it in the constructor-time heap rather
// than inside the global MyMesh object: classic ESP32 has a small static
// DRAM window but substantially more internal heap at this point in boot.
// Allocation is deliberately all-or-nothing. A node must never fall back
// to silently running without its configured flood rules.
static_assert(sizeof(Entry) == 200,
"Update the flood-rule runtime RAM budget in check_firmware_ram.py");
FILESYSTEM* _fs;
RegionMap* _regions;
Entry _entries[RULE_SLOTS];
Entry* _entries;
void seedDefaults();
void load();
+15 -3
View File
@@ -98,11 +98,22 @@ def requirements(platform, defines, target):
engine = integer(defines, "MESH_ENABLE_FLOOD_RULE_ENGINE", int(platform != "STM32_PLATFORM"))
slots = integer(defines, "FLOOD_PACKET_FILTER_SLOTS", 63 if engine else 16)
parts["flood_filter_table"] = slots * (200 if engine else 40) + 16
if "room_server" in target.lower() and integer(
defines, "MESH_ENABLE_ROOM_FLOOD_RULE_ENGINE", 0):
# FloodRuleEngine keeps all 31 persisted rules in one startup heap
# allocation. Its C++ assertion pins Entry to the budgeted 200 bytes.
parts["room_flood_rule_table"] = 31 * 200 + 16
if "ENABLE_OTA" in defines and "OTA_HEAP_CONTEXT" in defines:
parts["ota_context"] = 16384 + 16
if display and display != "NullDisplayDriver":
parts["display_pixels_and_driver"] = display_heap
parts["screen_objects_and_history"] = 8192 if companion else 2048
screen_budget = integer(defines, "MESH_COMPANION_SCREEN_STARTUP_BYTES", 0)
if companion and screen_budget:
# A target using this override has a source-side static assertion
# that covers its concrete startup screens and allocator overhead.
parts["screen_objects_and_history"] = screen_budget
else:
parts["screen_objects_and_history"] = 8192 if companion else 2048
if companion:
# ui-new retains 32 previews in one heap allocation. The baseline
# covers 78 bytes per message; budget larger buffers explicitly,
@@ -113,7 +124,7 @@ def requirements(platform, defines, target):
small_font = integer(defines, "UI_SMALL_MESSAGE_FONT", int(small_display))
default_preview = 161 if small_font else 78
extra = max(0, integer(defines, "UI_MSG_PREVIEW_SIZE", default_preview) - 78)
if extra:
if extra and not screen_budget:
parts["expanded_message_previews"] = 32 * ((extra + 7) // 8) * 8
parts["allocation_and_transient_margin"] = 16384 if platform == "ESP32_PLATFORM" else 4096
required = sum(parts.values())
@@ -124,7 +135,8 @@ def requirements(platform, defines, target):
largest = max(display_heap, parts["radio_packet_pool"], 8192 if platform == "ESP32_PLATFORM" else 0)
if "expanded_message_previews" in parts:
largest = max(largest, 8192 + parts["expanded_message_previews"])
for name in ("client_table", "flood_filter_table", "ota_context"):
for name in ("client_table", "flood_filter_table", "room_flood_rule_table",
"ota_context", "screen_objects_and_history"):
largest = max(largest, parts.get(name, 0))
return {"required_heap_bytes": required, "required_contiguous_bytes": largest,
"components": parts, "display": display, "full_companion": full}
+1 -1
View File
@@ -151,7 +151,7 @@ void SerialBLEInterface::enable() {
if (_isEnabled) return;
_isEnabled = true;
clearBuffers();
BLE.startAdvertising(true);
BLE.startAdvertising();
}
void SerialBLEInterface::disconnect() {
@@ -421,6 +421,9 @@ extends = env:heltec_tracker_v2_companion_radio_wifi_femon
board_build.flash_mode = ${esp32_s3_full.board_build.flash_mode}
build_flags =
${env:heltec_tracker_v2_companion_radio_wifi_femon.build_flags}
; Bound by the ui-new source assertion: Splash + Home + full 32-message
; preview history and ESP32 allocator overhead, without reducing UI state.
-D MESH_COMPANION_SCREEN_STARTUP_BYTES=9808
-D MESHCORE_REQUIRES_COMPANION_RADIO_FULL=1
[env:heltec_tracker_v2_companion_radio_wifi_femoff]
@@ -1,8 +1,15 @@
[LilyGo_TBeam_SX1262]
extends = esp32_base
board = ttgo-t-beam
; MeshCore and its source-built dependencies do not throw C++ exceptions.
; Avoid linking the unused exception runtime and use the supported compact
; ROM formatter so the feature-complete dual-OTA observer remains deployable.
build_unflags =
-fexceptions
build_flags =
${esp32_base.build_flags}
-fno-exceptions
-D PORTABLE_ESP32_ROM_NANO_FORMAT=1
-I variants/lilygo_tbeam_SX1262
-D TBEAM_SX1262
-D P_LORA_DIO_0=26
@@ -1,8 +1,15 @@
[LilyGo_TBeam_SX1276]
extends = esp32_base
board = ttgo-t-beam
; MeshCore and its source-built dependencies do not throw C++ exceptions.
; Avoid linking the unused exception runtime and use the supported compact
; ROM formatter so the feature-complete dual-OTA observer remains deployable.
build_unflags =
-fexceptions
build_flags =
${esp32_base.build_flags}
-fno-exceptions
-D PORTABLE_ESP32_ROM_NANO_FORMAT=1
-I variants/lilygo_tbeam_SX1276
-D TBEAM_SX1276
-D P_LORA_DIO_0=26
+3 -1
View File
@@ -122,7 +122,9 @@ build_src_filter = ${rpi_picow.build_src_filter}
+<../examples/companion_radio/*.cpp>
lib_deps = ${rpi_picow.lib_deps}
densaugeo/base64 @ ~1.4.0
lib_ignore = BLE
lib_ignore =
${rp2040_base.lib_ignore}
BLE
[env:PicoW_terminal_chat]
extends = rpi_picow
+3
View File
@@ -49,6 +49,9 @@ build_flags =
; G2 roles remain below each legacy 0x140000 (1.25 MiB) application slot.
-D LIGHTWEIGHT_WIFI_OTA=1
-D WEBCONFIG_DISABLED=1
; The ESP32-S3 ROM formatter preserves the normal libc ABI while keeping
; the complete portable LoRa-OTA repeater inside its legacy app slot.
-D PORTABLE_ESP32_ROM_NANO_FORMAT=1
build_src_filter = ${esp32_base.build_src_filter}
+<../variants/station_g2>
+<helpers/sensors>