Fix three LoRa blockers: stack overflow, warm reset, SPI mutex

1. Stack overflow on first GPS fix: lxmf_build_message() and
   lxmf_build_announce() allocated ~1.2KB of local arrays on
   the stack (telemetry[128], payload[256], hashed_part[288],
   signed_part[320], signed_data[256]). Combined with LVGL,
   IMU, GPS, BLE this exceeded the 8KB task stack. Fixed by
   making these static — functions are never called concurrently.

2. hw_ready=0 after watchdog warm reset: device_init() required
   bt_ready which fails on warm reset (BLE hardware not fully
   reset). For T-Watch and T-Beam Supreme dev boards, skip the
   BLE requirement since we bypass signatures anyway.

3. Silent TX failure: shared_spi_mutex was removed from radio
   code but beacon_transmit() does SPI transactions that can
   collide with SD card access. Added mutex around the TX path
   in beacon_transmit() and diagnostic logging on TX failure.

Verified: T-Beam receives watch LXMF announce (↓191 B) after
GPS fix. No crash on first beacon. Interference at -74 dBm.
This commit is contained in:
GlassOnTin 2026-04-07 21:03:26 +01:00
commit 55466763bd
3 changed files with 22 additions and 7 deletions

View file

@ -220,7 +220,12 @@ bool device_firmware_ok() {
#if MCU_VARIANT == MCU_ESP32 || MCU_VARIANT == MCU_NRF52
bool device_init() {
#if BOARD_MODEL == BOARD_TWATCH_ULT || BOARD_MODEL == BOARD_TBEAM_S_V1
// Dev boards: proceed even if BLE not ready (warm reset race)
if (true) {
#else
if (bt_ready) {
#endif
#if MCU_VARIANT == MCU_ESP32
for (uint8_t i=0; i<EEPROM_SIG_LEN; i++){dev_eeprom_signature[i]=EEPROM.read(eeprom_addr(ADDR_SIGNATURE+i));}
mbedtls_md_context_t ctx;

View file

@ -376,15 +376,20 @@ static int lxmf_build_message(uint8_t *out, size_t out_cap,
double lat, double lon, double alt,
double speed, double hdop,
uint32_t timestamp, int bat_percent) {
// Static buffers to avoid stack overflow (~1KB saved)
// Safe because beacon functions are never called concurrently
static uint8_t telemetry[128];
static uint8_t payload[256];
static uint8_t hashed_part[256 + 32];
static uint8_t signed_part[256 + 32 + 32];
// 1. Pack telemetry bytes
uint8_t telemetry[128];
size_t telem_len = lxmf_pack_telemetry(telemetry, sizeof(telemetry),
lat, lon, alt, speed, hdop,
timestamp, bat_percent);
if (telem_len == 0) return -1;
// 2. Build msgpack payload: [timestamp, nil, nil, {0x02: telemetry}]
uint8_t payload[256];
MsgpackWriter pw = { payload, 0, sizeof(payload) };
pw.pack_fixarray(4);
@ -407,7 +412,6 @@ static int lxmf_build_message(uint8_t *out, size_t out_cap,
// 3. Compute signature
// hashed_part = dest_hash(16) + source_hash(16) + payload
uint8_t hashed_part[256 + 32];
size_t hp_len = 16 + 16 + payload_len;
if (hp_len > sizeof(hashed_part)) return -1;
memcpy(hashed_part, dest_hash16, 16);
@ -419,7 +423,6 @@ static int lxmf_build_message(uint8_t *out, size_t out_cap,
sha256_once(hashed_part, hp_len, message_hash);
// signed_part = hashed_part + message_hash
uint8_t signed_part[256 + 32 + 32];
size_t sp_len = hp_len + 32;
if (sp_len > sizeof(signed_part)) return -1;
memcpy(signed_part, hashed_part, hp_len);
@ -537,7 +540,7 @@ static int lxmf_build_announce(uint8_t *out, size_t out_cap, const char *display
// signature: Ed25519Sign(dest_hash + public_key + name_hash + random_hash + app_data)
// signed_data = dest_hash(16) + public_key(64) + name_hash(10) + random_hash(10) + app_data
uint8_t signed_data[256];
static uint8_t signed_data[256];
size_t sd_len = 0;
memcpy(&signed_data[sd_len], lxmf_source_hash, 16); sd_len += 16;
memcpy(&signed_data[sd_len], lxmf_x25519_pk, 32); sd_len += 32;

View file

@ -1017,12 +1017,19 @@ void beacon_transmit(uint16_t size) {
diag_beacon_post_len = size;
}
#if BOARD_MODEL == BOARD_TWATCH_ULT
if (shared_spi_mutex) xSemaphoreTake(shared_spi_mutex, portMAX_DELAY);
#endif
LoRa->beginPacket();
for (uint16_t i = 0; i < size; i++) {
LoRa->write(tbuf[i]);
}
if (!LoRa->endPacket()) {
led_indicate_error(5);
bool tx_ok = LoRa->endPacket();
#if BOARD_MODEL == BOARD_TWATCH_ULT
if (shared_spi_mutex) xSemaphoreGive(shared_spi_mutex);
#endif
if (!tx_ok) {
Serial.println("[beacon] TX failed");
}
stat_tx++;
add_airtime(size);