diff --git a/inc/sp140/system_monitors.h b/inc/sp140/system_monitors.h index a7dec9af..3e09e68b 100644 --- a/inc/sp140/system_monitors.h +++ b/inc/sp140/system_monitors.h @@ -7,7 +7,14 @@ void addInternalMonitors(); void addAltimeterMonitors(); -// Thread-safe cached CPU temperature reading (updates max once per second) +// Prime the CPU temperature cache during single-threaded setup. +void primeCpuTemperatureCache(); + +// Refresh the sensor at most once per second. ctrlSensorTask is the sole +// runtime owner; all other tasks must use getCachedCpuTemperature(). +float refreshCpuTemperature(); + +// Cross-core read of the last published CPU temperature; never touches tsens. float getCachedCpuTemperature(); #endif // INC_SP140_SYSTEM_MONITORS_H_ diff --git a/src/sp140/main.cpp b/src/sp140/main.cpp index 6551b851..d347012b 100644 --- a/src/sp140/main.cpp +++ b/src/sp140/main.cpp @@ -309,10 +309,9 @@ void throttleTask(void *pvParameters) { } // Lightweight task: reads controller sensors and writes to TelemetryHub at 10Hz. -// Uses CACHED barometer/CPU values — uiTask is the sole I2C reader, and -// getCachedCpuTemperature throttles tsens access. This avoids i2cMutex -// contention and the "tsens: Do not configure..." error that arises when -// multiple tasks call temperatureRead() concurrently. +// Uses CACHED barometer/CPU values — uiTask is the sole I2C reader, and this +// task is the sole runtime owner of tsens refresh. Other tasks only consume the +// atomically published CPU temperature. void ctrlSensorTask(void *pvParameters) { (void)pvParameters; TickType_t lastWake = xTaskGetTickCount(); @@ -326,7 +325,7 @@ void ctrlSensorTask(void *pvParameters) { float bt = getBaroTemperature(); float bp = getBaroPressure(); float vs = getCachedVerticalSpeed(); - float mt = getCachedCpuTemperature(); + float mt = refreshCpuTemperature(); uint16_t pr = getLastThrottleRaw(); telemetryHubWriteController(alt, bt, bp, vs, mt, pr, now); vTaskDelayUntil(&lastWake, sensorTicks); diff --git a/src/sp140/simple_monitor.cpp b/src/sp140/simple_monitor.cpp index 1ab75032..dea602e1 100644 --- a/src/sp140/simple_monitor.cpp +++ b/src/sp140/simple_monitor.cpp @@ -308,6 +308,9 @@ void initSimpleMonitor() { addESCMonitors(); addBMSMonitors(); addAltimeterMonitors(); + // Prime while setup is still single-threaded so monitor readers never see + // the zero-initialized cache and no runtime task races tsens initialization. + primeCpuTemperatureCache(); addInternalMonitors(); USBSerial.printf("Monitoring %d sensors\n", monitors.size()); } diff --git a/src/sp140/system_monitors.cpp b/src/sp140/system_monitors.cpp index f5ad704d..66214732 100644 --- a/src/sp140/system_monitors.cpp +++ b/src/sp140/system_monitors.cpp @@ -3,6 +3,7 @@ #include "sp140/altimeter.h" #include "sp140/time_utils.h" #include +#include // External references to core monitoring infrastructure extern std::vector monitors; @@ -11,18 +12,28 @@ extern MultiLogger multiLogger; // External reference to BMP sensor status extern bool bmpPresent; -// Cached CPU temperature to avoid "tsens: Do not configure the temp sensor when it's running!" error -static float cachedCpuTemp = 0.0f; +// ESP32 tsens configuration is not safe from concurrent tasks. One task owns +// temperatureRead(); readers consume this atomic cross-core publication. +static std::atomic cachedCpuTemp{0.0f}; static unsigned long lastCpuTempRead = 0; static const unsigned long CPU_TEMP_READ_INTERVAL = 1000; // Read every 1 second -float getCachedCpuTemperature() { +void primeCpuTemperatureCache() { + cachedCpuTemp.store(temperatureRead(), std::memory_order_relaxed); + lastCpuTempRead = timeMillis(); +} + +float refreshCpuTemperature() { unsigned long now = timeMillis(); if (now - lastCpuTempRead >= CPU_TEMP_READ_INTERVAL) { - cachedCpuTemp = temperatureRead(); + cachedCpuTemp.store(temperatureRead(), std::memory_order_relaxed); lastCpuTempRead = now; } - return cachedCpuTemp; + return cachedCpuTemp.load(std::memory_order_relaxed); +} + +float getCachedCpuTemperature() { + return cachedCpuTemp.load(std::memory_order_relaxed); } void addInternalMonitors() {