Skip to content

Commit 648d049

Browse files
Copilotnorthpower25
andcommitted
fix: guarantee IST-Status PIDs in first cellular packet via inject flag
Fixes a race condition where process() adds LED/Beep/CONN_TYPE/SD PIDs to Buffer A after sentinel reset, but the cellular OTA check delays getNewest() long enough for process() to fill newer buffers B/C/D (sentinel already updated → no IST PIDs). getNewest() picks Buffer D; Buffer A is overwritten. HA never receives PIDs 0x84–0x88 → "Unbekannt". Fix: add s_send_state_pids flag (set at every sentinel reset site) and module-level SD cache. Telemetry loop injects LED/Beep/CONN_TYPE/SD directly into the CStorage packet before store.tailer() on first TX after each new connection. Both WiFi and cellular are now reliable. Co-authored-by: northpower25 <36472486+northpower25@users.noreply.github.com>
1 parent aa9791c commit 648d049

1 file changed

Lines changed: 76 additions & 12 deletions

File tree

firmware_v5/telelogger/telelogger.ino

Lines changed: 76 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,23 @@ static int8_t s_lastLedWhite = -1;
199199
static int8_t s_lastBeep = -1;
200200
static int8_t s_lastConnType = -1; // PID_CONN_TYPE sentinel: 1=WiFi, 2=Cellular
201201

202+
// Inject-on-next-packet flag: set whenever the sentinels are reset (new
203+
// connection established or session start). The telemetry task checks this
204+
// flag and injects LED/beep/conn-type/SD PIDs directly into the outgoing
205+
// CStorage packet before store.tailer(), guaranteeing these IST-Status values
206+
// are in the FIRST transmitted packet regardless of which buffer getNewest()
207+
// picks up. Without this, a race between process() (updating sentinels and
208+
// filling buffers) and the telemetry loop (slow OTA check over cellular delays
209+
// getNewest()) causes the sentinel-triggered buffer to be overwritten before it
210+
// is transmitted — leaving HA with "Unbekannt" for LED/beep/SD indefinitely.
211+
static volatile bool s_send_state_pids = false;
212+
213+
// Cached SD card capacity/free space (MiB) — updated by process() each time it
214+
// emits PID_SD_TOTAL_MB / PID_SD_FREE_MB. Read (not written) by the telemetry
215+
// inject block so it does not need to access the SD SPI bus from the wrong task.
216+
static uint32_t s_cachedSdTotalMb = 0;
217+
static uint32_t s_cachedSdFreeMb = 0;
218+
202219
// Pull-OTA constants used in initialize(), standby(), performPullOtaFlash(),
203220
// and performPullOtaCheck(). Defined here (before any function body) so that
204221
// all translation-unit uses see them regardless of source order.
@@ -650,9 +667,12 @@ void initialize()
650667
// "Unbekannt" IST-Status when HA is reloaded while the device was connected
651668
// (HA loses diag state, device never resends unchanged values unless the
652669
// sentinels are reset).
653-
s_lastLedWhite = -1;
654-
s_lastBeep = -1;
655-
s_lastConnType = -1;
670+
s_lastLedWhite = -1;
671+
s_lastBeep = -1;
672+
s_lastConnType = -1;
673+
// Signal the telemetry task to inject IST-Status PIDs into the very next
674+
// transmitted packet so they are not lost to the getNewest() race.
675+
s_send_state_pids = true;
656676

657677
#if ENABLE_MEMS
658678
if (state.check(STATE_MEMS_READY)) {
@@ -1035,6 +1055,11 @@ void process()
10351055
sdTotalMb = (uint32_t)(tot >> 20);
10361056
sdFreeMb = (uint32_t)((tot > used ? tot - used : 0) >> 20);
10371057
}
1058+
// Update module-level cache so the telemetry inject block can include
1059+
// the SD values in the guaranteed first-packet injection without
1060+
// touching the SD SPI bus from the wrong task.
1061+
s_cachedSdTotalMb = sdTotalMb;
1062+
s_cachedSdFreeMb = sdFreeMb;
10381063
buffer->add(PID_SD_TOTAL_MB, ELEMENT_UINT32, &sdTotalMb, sizeof(sdTotalMb));
10391064
buffer->add(PID_SD_FREE_MB, ELEMENT_UINT32, &sdFreeMb, sizeof(sdFreeMb));
10401065
}
@@ -1276,9 +1301,10 @@ void telemetry(void* inst)
12761301
// reset the state-change detection would suppress the PIDs (value
12771302
// unchanged) and Home Assistant would keep showing "Unbekannt" for the
12781303
// IST-Status and the connection-type timestamps.
1279-
s_lastLedWhite = -1;
1280-
s_lastBeep = -1;
1281-
s_lastConnType = -1;
1304+
s_lastLedWhite = -1;
1305+
s_lastBeep = -1;
1306+
s_lastConnType = -1;
1307+
s_send_state_pids = true;
12821308

12831309
uint32_t t = millis();
12841310
do {
@@ -1360,9 +1386,10 @@ void telemetry(void* inst)
13601386
// LED/beep state and connection type to HA. Without this, the
13611387
// sentinels retain their values from the previous cellular session
13621388
// and HA would keep showing stale IST-Status values.
1363-
s_lastLedWhite = -1;
1364-
s_lastBeep = -1;
1365-
s_lastConnType = -1;
1389+
s_lastLedWhite = -1;
1390+
s_lastBeep = -1;
1391+
s_lastConnType = -1;
1392+
s_send_state_pids = true;
13661393
// switch off cellular module when wifi connected
13671394
if (state.check(STATE_CELL_CONNECTED)) {
13681395
teleClient.cell.end();
@@ -1406,9 +1433,10 @@ void telemetry(void* inst)
14061433
// LED/beep state and connection type to HA. Without this, the sentinels
14071434
// retain their values from the previous WiFi session and HA would keep
14081435
// showing stale IST-Status values and incorrect connection timestamps.
1409-
s_lastLedWhite = -1;
1410-
s_lastBeep = -1;
1411-
s_lastConnType = -1;
1436+
s_lastLedWhite = -1;
1437+
s_lastBeep = -1;
1438+
s_lastConnType = -1;
1439+
s_send_state_pids = true;
14121440
}
14131441

14141442
if (millis() - lastRssiTime > SIGNAL_CHECK_INTERVAL * 1000) {
@@ -1484,6 +1512,42 @@ void telemetry(void* inst)
14841512
store.timestamp(buffer->timestamp);
14851513
buffer->serialize(store);
14861514
bufman.free(buffer);
1515+
// Inject IST-Status PIDs (LED/beep/conn-type/SD) directly into this
1516+
// packet whenever a new connection has just been established.
1517+
//
1518+
// Without this injection, there is a race between process() and the
1519+
// telemetry loop that reliably loses these PIDs on cellular connections:
1520+
// 1. Sentinel reset → process() adds PIDs to Buffer A, updates sentinel.
1521+
// 2. OTA meta-check over cellular takes several seconds while process()
1522+
// fills Buffers B, C, D … (sentinel already matches, no PIDs).
1523+
// 3. getNewest() returns Buffer D (newest), Buffer A is overwritten.
1524+
// 4. Result: HA never receives LED/beep/SD → "Unbekannt" forever.
1525+
// WiFi is not immune but the OTA check is much faster there, so the race
1526+
// is rarely observed. With this injection both transports are reliable.
1527+
if (s_send_state_pids) {
1528+
s_send_state_pids = false;
1529+
{
1530+
uint8_t v = enableLedWhite ? 1 : 0;
1531+
store.log(PID_LED_WHITE_STATE, &v, 1);
1532+
s_lastLedWhite = (int8_t)v;
1533+
}
1534+
{
1535+
uint8_t v = enableBeep ? 1 : 0;
1536+
store.log(PID_BEEP_STATE, &v, 1);
1537+
s_lastBeep = (int8_t)v;
1538+
}
1539+
if (state.check(STATE_NET_READY)) {
1540+
uint8_t v = state.check(STATE_WIFI_CONNECTED) ? 1 : 2;
1541+
store.log(PID_CONN_TYPE, &v, 1);
1542+
s_lastConnType = (int8_t)v;
1543+
}
1544+
#if STORAGE == STORAGE_SD
1545+
// s_cachedSdTotalMb/Free are kept current by process(); they are 0
1546+
// before the first SD read which HA correctly interprets as "no card".
1547+
store.log(PID_SD_TOTAL_MB, &s_cachedSdTotalMb, 1);
1548+
store.log(PID_SD_FREE_MB, &s_cachedSdFreeMb, 1);
1549+
#endif
1550+
}
14871551
store.tailer();
14881552
Serial.print("[DAT] ");
14891553
Serial.println(store.buffer());

0 commit comments

Comments
 (0)