Files
meshtastic_firmware/variants/nrf52840/heltec_mesh_solar/platformio.ini
T
Ben MeadorsandGitHub 12b2d973a4 Add central memory-class ladder (MemClass.h) with fail-safe-small defaults (#10901)
* Right-size nRF52 heap tiers after 2.8.0 heap-exhaustion field reports

Field reports on 2.8.0 show nRF52840 devices at 99% heap (114/115 KB)
within minutes of boot; operator new asserts on OOM, so these devices
are one allocation from a reboot. The 2.8.0 cache sizing ladders gave
nRF52 the largest non-PSRAM tiers on the assumption that a BLE-only
part has a roomy heap - the arena is actually ~125 KB shared with the
FreeRTOS task stacks.

Per-target retiers (nRF52840 unless noted):
- Traffic Management cache 1000 -> 250 entries (10 KB -> 2.5 KB); the
  unclassified fallthrough drops 1000 -> 400 to match the classic-ESP32
  tier (also affects RP2040/RP2350)
- Warm node store 200 -> 100 entries (8 KB -> 4 KB); the non-XXAA
  fallthrough drops 320 -> 100 so an unclassified RAM-constrained part
  can't boot-allocate 12.8 KB
- MESSAGE_HISTORY_LIMIT 20 -> 10 (text pool 4.4 KB -> 2.2 KB), the tier
  classic ESP32 already ships
- MAX_RX_TOPHONE 32 -> 16, shrinking the static packet pool 70 -> 54
  slots (~6.6 KB of .bss returned to the heap arena)
- PacketHistory hash index off arch-wide (1 KB); O(n) over 240 records
  is negligible at LoRa packet rates
- OLEDDISPLAY_REDUCE_MEMORY arch-wide (~1 KB OLED back buffer); the five
  TFT variants -U it because TFTDisplay.cpp needs buffer_back for
  dirty-window diffing
- Drop the stale "for testing" 1024-entry TMM override on T1000-E

Measured on rak4631: heap arena grows 124,572 -> 131,180 B and boot
allocations drop ~15.7 KB, roughly +22 KB free heap on the field-report
device class.

Migration: the nRF52840 warm flash ring replays through place() (LRU),
so the newest 100 identities survive the shrink; the file backend
rejects oversized snapshots cleanly (new test covers this). Native
suites pass (536/536 Docker, 13/13 native-macos warm store); rak4631,
heltec-mesh-node-t114 (TFT) and tracker-t1000-e build green.

* Add central memory-class ladder (MemClass.h) with fail-safe-small defaults

The 2.8.0 nRF52840 heap exhaustion happened because each RAM-sized cache
picked its per-platform tier from its own chip #ifdef ladder, and every
ladder's fallthrough default was its largest non-PSRAM tier - nRF52 was
never named, so it silently got 1000-entry caches on a ~115 KB arena.

This introduces src/memory/MemClass.h: a single MESHTASTIC_MEM_CLASS
(TINY / SMALL / MEDIUM / LARGE) ranked by usable app heap after platform
overheads, with the deliberate property that an unclassified chip lands
in SMALL - a new target boots with small caches until someone opts it up
in one visible place.

The TMM cache, warm store, MAX_RX_TOPHONE and MAX_SATELLITE_NODES
ladders in mesh-pb-constants.h now key off the class; branches pinned by
something other than RAM stay explicit and say why (nRF52840's SoftDevice
arena, RP2040's warm.dat watchdog bound). MAX_NUM_NODES intentionally
stays separate - it is flash-shaped (nodes.proto vs LittleFS), not
heap-shaped.

A per-class MESHTASTIC_BOOT_CACHE_BUDGET static_assert now covers the
three big boot-allocated caches, so the next cache-adding PR that would
blow a small platform's budget fails to compile instead of exhausting
heap in the field.

No values change for any existing target: rak4631, tbeam, rak11310 and
wio-e5 build byte-identical before/after; all ladders remain
#ifndef-guarded so variant overrides keep working.

* Address review: fix RP2350 class-table doc, add PacketRecord static_assert

- MemClass.h's class table claimed RP2350 was MEDIUM while the mapping
  ladder classifies it SMALL (with RP2040) - the table now matches the
  ladder, with a note that RP2350 is a MEDIUM candidate whenever someone
  wants to tune it up (kept SMALL here so this header stays a behavioral
  no-op).
- The boot-cache budget comment referenced a static_assert pinning
  PacketHistory::PacketRecord at 20 B that did not exist (only a layout
  comment). Add the real static_assert so the budget math in
  mesh-pb-constants.h fails to compile if the record layout changes.

Also merges develop (the base #10898 landed there as a squash, which is
what made this stacked branch conflict); develop's mesh-pb-constants.h
is byte-identical to this branch's base, so the resolution keeps the
MemClass ladder unchanged. rak4631 and wio-e5 build green;
test_packet_history 47/47.

* Address review: share PACKETHISTORY_MAX, trim policy comments

- Hoist PACKETHISTORY_MAX from PacketHistory.cpp into
  mesh-pb-constants.h (next to the MAX_NUM_NODES it derives from) so the
  constructor clamp and the boot-cache budget static_assert use one
  definition instead of hand-mirrored arithmetic that could drift. The
  expression stays valid where MAX_NUM_NODES resolves at runtime
  (ESP32-S3, portduino); the pointless 2.0 double math becomes integer.
- Trim the MemClass.h header (36 -> 16 comment lines) and the budget /
  sizing-policy comments per the repo comment-length guideline, keeping
  the class table, the fail-safe-small rule, the override mechanism, and
  the include-order constraint.

rak4631 (compile-time MAX_NUM_NODES) and heltec-v3 (runtime) build
green; test_packet_history 47/47.
2026-07-06 14:51:41 -05:00

138 lines
4.8 KiB
INI

; First prototype nrf52840/sx1262 device
[heltec_mesh_solar_base]
extends = nrf52840_base
board = heltec_mesh_solar
board_level = pr
debug_tool = jlink
# add -DCFG_SYSVIEW if you want to use the Segger systemview tool for OS profiling.
build_flags = ${nrf52840_base.build_flags}
-Ivariants/nrf52840/heltec_mesh_solar
-DHELTEC_MESH_SOLAR
build_src_filter = ${nrf52_base.build_src_filter} +<../variants/nrf52840/heltec_mesh_solar>
lib_deps =
${nrf52840_base.lib_deps}
# renovate: datasource=git-refs depName=NMIoT-meshsolar packageName=https://github.com/NMIoT/meshsolar gitBranch=main
https://github.com/NMIoT/meshsolar/archive/dfc5330dad443982e6cdd37a61d33fc7252f468b.zip
# renovate: datasource=custom.pio depName=ArduinoJson packageName=bblanchon/library/ArduinoJson
bblanchon/ArduinoJson@6.21.6
[env:heltec-mesh-solar]
custom_meshtastic_hw_model = 108
custom_meshtastic_hw_model_slug = HELTEC_MESH_SOLAR
custom_meshtastic_architecture = nrf52840
custom_meshtastic_actively_supported = false
custom_meshtastic_support_level = 1
custom_meshtastic_display_name = Heltec MeshSolar
custom_meshtastic_images = heltec-mesh-solar.svg
custom_meshtastic_tags = Heltec
extends = heltec_mesh_solar_base
build_flags = ${heltec_mesh_solar_base.build_flags}
-DSPI_INTERFACES_COUNT=1
[env:heltec-mesh-solar-eink]
extends = heltec_mesh_solar_base
build_flags = ${heltec_mesh_solar_base.build_flags}
-DHELTEC_MESH_SOLAR_EINK
-DSPI_INTERFACES_COUNT=2
-DUSE_EINK
-DPIN_SCREEN_VDD_CTL=3
-DPIN_EINK_CS=41
-DPIN_EINK_BUSY=11
-DPIN_EINK_DC=13
-DPIN_EINK_RES=40
-DPIN_EINK_SCLK=12
-DPIN_EINK_MOSI=2
-DPIN_SPI1_MISO=-1
-DPIN_SPI1_MOSI=PIN_EINK_MOSI
-DPIN_SPI1_SCK=PIN_EINK_SCLK
-DEINK_DISPLAY_MODEL=GxEPD2_213_E0213A367
-DEINK_WIDTH=250
-DEINK_HEIGHT=122
-DUSE_EINK_DYNAMICDISPLAY ; Enable Dynamic EInk
-DEINK_LIMIT_FASTREFRESH=10 ; How many consecutive fast-refreshes are permitted
-DEINK_LIMIT_RATE_BACKGROUND_SEC=30 ; Minimum interval between BACKGROUND updates
-DEINK_LIMIT_RATE_RESPONSIVE_SEC=1 ; Minimum interval between RESPONSIVE updates
-DEINK_BACKGROUND_USES_FAST ; (Optional) Use FAST refresh for both BACKGROUND and RESPONSIVE, until a limit is reached.
-DEINK_HASQUIRK_GHOSTING ; Display model is identified as "prone to ghosting"
-DENABLE_MESSAGE_PERSISTENCE=0 ; Disable flash persistence for space-limited build
-DMESHTASTIC_EXCLUDE_WIFI=1
-DMESHTASTIC_EXCLUDE_EXTERNALNOTIFICATION=1
-DMESHTASTIC_EXCLUDE_PAXCOUNTER=1
-DMESHTASTIC_EXCLUDE_REMOTEHARDWARE=1
-DMESHTASTIC_EXCLUDE_STOREFORWARD=1
-DMESHTASTIC_EXCLUDE_CANNEDMESSAGES=1
-DMESHTASTIC_EXCLUDE_WAYPOINT=1
lib_deps =
${heltec_mesh_solar_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-GxEPD2 packageName=https://github.com/meshtastic/GxEPD2 gitBranch=master
https://github.com/meshtastic/GxEPD2/archive/c7eb4c3c167cf396ef4f541cc5d4c6aa42f3c46b.zip
[env:heltec-mesh-solar-inkhud]
extends = heltec_mesh_solar_base, inkhud
build_src_filter = ${heltec_mesh_solar_base.build_src_filter} ${inkhud.build_src_filter}
build_flags = ${heltec_mesh_solar_base.build_flags}
${inkhud.build_flags}
-DHELTEC_MESH_SOLAR_INKHUD
-DSPI_INTERFACES_COUNT=2
-DPIN_SCREEN_VDD_CTL=3
-DPIN_EINK_CS=41
-DPIN_EINK_BUSY=11
-DPIN_EINK_DC=13
-DPIN_EINK_RES=40
-DPIN_EINK_SCLK=12
-DPIN_EINK_MOSI=2
-DPIN_SPI1_MISO=-1
-DPIN_SPI1_MOSI=PIN_EINK_MOSI
-DPIN_SPI1_SCK=PIN_EINK_SCLK
lib_deps =
${inkhud.lib_deps} ; InkHUD libs first, so we get GFXRoot instead of AdafruitGFX
${heltec_mesh_solar_base.lib_deps}
[env:heltec-mesh-solar-oled]
extends = heltec_mesh_solar_base
build_flags = ${heltec_mesh_solar_base.build_flags}
-DHELTEC_MESH_SOLAR_OLED
-DSPI_INTERFACES_COUNT=1
-DPIN_SCREEN_VDD_CTL=3
-DHAS_SCREEN=1
-DRESET_OLED=40
-DPIN_WIRE_SDA=2
-DPIN_WIRE_SCL=12
[env:heltec-mesh-solar-tft]
extends = heltec_mesh_solar_base
build_flags = ${heltec_mesh_solar_base.build_flags}
-UOLEDDISPLAY_REDUCE_MEMORY ; TFTDisplay.cpp needs the lib's buffer_back for dirty-window diffing
-DHELTEC_MESH_SOLAR_TFT
-DSPI_INTERFACES_COUNT=2
-DHAS_SPI_TFT=1
-DUSE_ST7789
-DST7789_NSS=41
-DST7789_RS=13
-DST7789_SDA=2
-DST7789_SCK=12
-DST7789_RESET=40
-DST7789_MISO=-1
-DST7789_BUSY=-1
-DVTFT_CTRL=3
-DVTFT_LEDA=11
-DTFT_BACKLIGHT_ON=HIGH
-DST7789_SPI_HOST=SPI2_HOST
-DSPI_FREQUENCY=10000000
-DSPI_READ_FREQUENCY=10000000
-DTFT_HEIGHT=170
-DTFT_WIDTH=320
-DTFT_OFFSET_X=0
-DTFT_OFFSET_Y=0
-DBRIGHTNESS_DEFAULT=100
-DPIN_SPI1_MISO=ST7789_MISO
-DPIN_SPI1_MOSI=ST7789_SDA
-DPIN_SPI1_SCK=ST7789_SCK
lib_deps =
${heltec_mesh_solar_base.lib_deps}
# renovate: datasource=git-refs depName=meshtastic-st7789 packageName=https://github.com/meshtastic/st7789 gitBranch=main
https://github.com/meshtastic/st7789/archive/92bae2e4a307afb430c3b0bc3d661c55ee1565f0.zip