Skip to content

Contact RAM savings and RAK4631 Full capacity, 2026-10-19

Shared contact indexes save RAM across Companion profiles and terminal chat. RAK4631 Full additionally saves 2,200 static RAM bytes and 488 application bytes, preserving its existing features and SD support. Five canonical packages and a direct terminal build pass. Heltec V3 Full failed its preexisting RAM-admission limit at this revision; the subsequent V3 Full repair is measured separately. No hardware was flashed for this contact-memory campaign.

Baseline source: 31e693ec209109a930dcf8aeef89dacbe9116a63. Final source: aed4c22e51201f0ee3586e26e1007194a74b015d. The machine-readable results bind package, ELF, memory-manifest and linked-check hashes to these revisions.

Shared contact savings

BaseChatMesh uses uint16_t sort/match indexes when the complete contact table has at most 65,535 slots; larger configurations keep int. API counts retain their existing types. All Companion profiles and simple_secure_chat share this implementation across nRF52, ESP32, RP2040 and STM32. Infrastructure client/ACL tables use different record types and are outside this contact-table change.

Existing configuration Internal RAM saved by indexes PSRAM saved by indexes
Static 350-contact table 764 bytes None
PSRAM 350-contact table with internal fallback 96 bytes 716 bytes
Static 100-contact table 264 bytes None

These totals include eight anonymous slots and 24 match results. PSRAM retains its existing 24-slot internal fallback and allocation-failure behavior. Cached nRF52 ContactInfo records also shrink from 96 to 92 bytes by moving the runtime-only storage_slot field into an alignment gap; other record layouts retain their sizes. Field-based serialization, contact/channel capacities, features, queues and stack allocations remain unchanged.

RAK4631 Full comparison

Measurement Baseline Final Improvement
Actual application BIN extracted from DFU ZIP 712,600 712,112 488 fewer
Application-region capacity 712,704 712,704 Unchanged
Actual flash margin 104 592 488 more
Ordinary static RAM (.data + .bss) 156,284 154,084 2,200 fewer
Linked heap capacity 79,092 81,292 2,200 more
Modeled heap requirement 74,576 74,576 Unchanged
Modeled admission margin 4,516 6,716 2,200 more

.data stays at 1,660 bytes; ordinary .bss falls from 154,624 to 152,424. Separately reserved linker arenas are excluded from this section sum. The measured reduction includes final object alignment. Linked capacities and admission models do not measure running free heap or physical stack high-water.

Four duplicate CRC loops share the existing implementation without changing initial values, byte ranges or stored formats. Reader packing increases offline Zopfli iterations from 15 to 1,000 and selects 16-bit block offsets only when every block start fits. General 32-bit types/fallback, 4,096-byte blocks, scratch policy and all canonical text remain unchanged.

Reader flash data Baseline Final Saving
Compressed payload 38,366 38,293 73 bytes
Twenty-four block indexes 288 192 96 bytes
Payload plus index 38,654 38,485 169 bytes

The complete image saves 488 bytes; Reader payload plus index accounts for 169. The Full marker and thirteen feature-verification entries are unchanged. Existing SD support, its 2,048-byte SD/FAT runtime reserve and protected authentication range remain intact. No physical SD operation was tested.

Source-bound artifacts

Artifact SHA-256
Baseline DFU application 520cf40ec5ada891d3fe8081b23546ce777c712383f0479292bb8fac0017d4d7
Final DFU application c2080afe8874c71eb9a5d1fb253fec06463cac8a774fc776ff46453abf2da420
Baseline DFU ZIP ef641d49dc91db61f0a49912f191f6d81c2887a69950fa66a612f81b31f0e142
Final DFU ZIP d99d904430de75dd5e507a530267d933b8f0bda762afe86cdb150209f4df4499
Final ELF f002455df52a9cdf4278461685eaee3082cc14752f741428f6dfe2dcdc2bb2a7

Every admitted package's memory-bound file hashes were recomputed. Nordic UF2 payloads match the DFU applications, and initialized ELF flash-load bytes agree. The JSON records the individual hashes and actual packaged selectors/profiles.

Software verification

Contact coverage totals 26 Python unittest methods: three cache/persistence harness methods, eleven persistence-contract methods, seven stream-contract methods, two index/layout methods, one lifetime matrix, and two RP2040/STM32 uncached-storage methods. They execute 24 ASan/UBSan C++ configurations (three cache, eleven index, eight lifetime, two uncached) and separately compile eight actual cached/uncached record layouts across the four MCU families. The lifetime matrix checks 284 assertions. Index execution uses a small stand-in record; separate layout checks compile the real header. Contact code is unchanged since b0f141615; results are retained in the tool transcript, with no separate contact log claimed.

Adjacent checks pass: 16 Reader tests, 19 Beacon tests, a radio-profile matrix with 32 scenarios, seven Morse-codec tests and sixteen persistent-format tests. These focused counts do not establish a complete hosted CI run.

Final linked ARM checks all bind to the ELF above:

  • Crypto: both RFC8032 vectors pass; keypair/sign frames are 1,152/1,928 bytes, within the existing 2,048-byte function-frame bound.
  • OTA workspace: four acquisition/release lifetimes pass, preserving protected SD authentication range 0x20006008–0x20006050.
  • Reader: four disjoint partitions reconstruct all 879 references and every continuation page: 95,689 canonical bytes through 1,091 calls and 380,103,543 ARM instructions. Exact disjoint reference coverage is verified. Maximum callee frame is 6,016 bytes; with the existing 1,536-byte caller reserve this is 7,552 of the 8,192-byte loop-stack model. Canonical source SHA-256 remains 916bf9d1feae0ad410d69f61e6c5f328ac6acba10651d751b5bb29719732c49e.

The partitioned campaign invokes the unchanged linked Reader checker with all-page reconstruction; partitioning is not an additional release gate. Equivalent complete reproduction against the matching ELF uses Unicorn 2.1.4:

python scripts/check_nrf52_companion_crypto.py path/to/firmware.elf
python scripts/check_nrf52_companion_ota_workspace.py path/to/firmware.elf
python scripts/check_nrf52_companion_reader.py path/to/firmware.elf --every-page

Final-source build matrix

Canonical packages used v1.18.0.1-halo-keymind-cascade-dev, radio preset usa-cascade-fixed and profile cascade; Full selections required OTA. PlatformIO commands ran one at a time. For example:

bash build.sh build-firmware RAK_4631_companion_radio_full --firmware-version v1.18.0.1-halo-keymind-cascade-dev --radio-preset usa-cascade-fixed --profile cascade --require-ota
Requested target Actual application bytes Modeled RAM margin Result
RAK_4631_companion_radio_full 712,112 6,716 PASS
RAK_3401_companion_radio_usb 523,864 50,940 PASS
heltec_v4_2_v4_3_companion_radio_full_femon 2,558,856 34,536 PASS, Full PSRAM profile
Xiao_rp2040_companion_radio_usb 472,204 144,340 PASS
RAK_3x72_companion_radio_usb 229,372 7,112 PASS; four actual flash bytes free
Heltec_v3_companion_radio_full No admitted package -21,024 Verified preexisting RAM-gate failure

Matched baseline V3 Full also fails: 177,144 available against 198,432 required (21,288 short), versus final 177,408 (21,024 short). This wrapper preserves 100 contacts, gaining 264 bytes. The final failed ELF was cleaned by a later build; the failure is bound to its build log; that ELF hash is not recorded.

The direct pio run -e Heltec_v3_terminal_chat default build passes with 350 contacts: 263,800 available, 77,856 required and 185,944 margin. ELF SHA-256 is 1a193ae1bee829d0bf7d140310b8487d2a4671f0c85715d16cfb764850e06cb6. This qualifies a no-PSRAM/shared-terminal compile path; it is not a Full package or a firmware-picker release-version/preset qualification.

Retained failures and limits

Intermediate b0f141615 failed the STM32 size check. Shared little-endian Morse serialization and an equivalent branchless CRC loop restore the packaged fit. STM32 static RAM falls from 40,256 to 39,992 bytes; baseline actual BIN was 229,284/229,376 bytes (92 free), although PlatformIO's section summary showed 444 free. Acceptance uses actual BIN/ELF extent. The final four-byte margin and RAK4631 Full's 592-byte margin require the unchanged hard gates for subsequent versions.

The separate legacy V3 BLE target has the same five unresolved WiFi setup/seeder symbols on matched baseline and intermediate source. Initial disk-quota failures and an unsupported V4 command flag remain separate failed attempts; successful retries establish the results above. No features, capacities or budgets were reduced to obtain these passes. There is no six-canonical-pass claim.

These are software/build/linked-code results. No board was flashed. Physical contact/client operation, PSRAM pressure/fallback, storage persistence, SD/radio operation, power, timing and full-firmware ISR/SoftDevice stack high-water remain outside this qualification.