Files
aes67-ESP32-P4/components/aes67_health/aes67_health.c
T
bsncubed f6dfa80b19 Step 5 (health): temperatures in status.temps
- aes67_health: status.temps = [{name, c, min_c, max_c, warn_c}]
  (0.1 °C), sampled every 2 s by a health task. On-die "SoC" sensor via
  driver/temperature_sensor.h, range 20..100 °C, warn at 85 °C.
  health_temp_register(name, read_cb, warn_c) for board sensors.
  Warning logged when crossing warn_c, cleared below warn_c - 5 °C.
- Verified: SoC 26-28 °C at idle with min/max since boot; with a
  temporary 27 °C threshold the warning was logged and arrived via
  syslog as <132>, status carried warn_c 27.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 07:20:02 +10:00

146 lines
4.5 KiB
C

#include "aes67_health.h"
#include <string.h>
#include "aes67_web.h"
#include "driver/temperature_sensor.h"
#include "esp_app_desc.h"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#define MAX_SENSORS 8
#define SAMPLE_MS 2000
#define HYSTERESIS_C 5.0f
#define SOC_WARN_C 85.0f
static const char *TAG = "health";
typedef struct {
char name[16];
health_temp_read_cb_t read;
float warn_c;
bool valid, hot;
float c, min_c, max_c;
} sensor_t;
static sensor_t s_sensors[MAX_SENSORS];
static int s_n;
static SemaphoreHandle_t s_lock;
static temperature_sensor_handle_t s_tsens;
esp_err_t health_temp_register(const char *name, health_temp_read_cb_t read, float warn_c)
{
if (!s_lock || s_n >= MAX_SENSORS) {
return ESP_ERR_INVALID_STATE;
}
xSemaphoreTake(s_lock, portMAX_DELAY);
sensor_t *t = &s_sensors[s_n++];
strlcpy(t->name, name, sizeof(t->name));
t->read = read;
t->warn_c = warn_c;
xSemaphoreGive(s_lock);
return ESP_OK;
}
// On-die sensor: measures the die, not ambient; expect well above room temperature.
static esp_err_t soc_read(float *c)
{
return temperature_sensor_get_celsius(s_tsens, c);
}
static void health_task(void *arg)
{
while (1) {
for (int i = 0; i < s_n; i++) {
float c;
if (s_sensors[i].read(&c) != ESP_OK) {
continue;
}
xSemaphoreTake(s_lock, portMAX_DELAY);
sensor_t *t = &s_sensors[i];
t->c = c;
t->min_c = t->valid && t->min_c < c ? t->min_c : c;
t->max_c = t->valid && t->max_c > c ? t->max_c : c;
t->valid = true;
bool was_hot = t->hot;
if (t->warn_c > 0) {
if (!t->hot && c >= t->warn_c) {
t->hot = true;
} else if (t->hot && c < t->warn_c - HYSTERESIS_C) {
t->hot = false;
}
}
xSemaphoreGive(s_lock);
// Logged outside the lock; goes to syslog like any warning.
if (t->hot != was_hot) {
if (t->hot) {
ESP_LOGW(TAG, "temperature %s %.1f °C: at or above %.0f °C", t->name, c, t->warn_c);
} else {
ESP_LOGI(TAG, "temperature %s %.1f °C: back below %.0f °C", t->name, c,
t->warn_c - HYSTERESIS_C);
}
}
}
vTaskDelay(pdMS_TO_TICKS(SAMPLE_MS));
}
}
static double round1(float v)
{
return (double)(int)(v * 10.0f + (v < 0 ? -0.5f : 0.5f)) / 10.0;
}
static void health_status(cJSON *st)
{
cJSON_AddStringToObject(st, "fw", esp_app_get_description()->version);
cJSON_AddNumberToObject(st, "uptime_s", (double)(esp_timer_get_time() / 1000000));
cJSON_AddNumberToObject(st, "heap_free", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
cJSON_AddNumberToObject(st, "psram_free", heap_caps_get_free_size(MALLOC_CAP_SPIRAM));
cJSON *temps = cJSON_AddArrayToObject(st, "temps");
xSemaphoreTake(s_lock, portMAX_DELAY);
for (int i = 0; i < s_n; i++) {
const sensor_t *t = &s_sensors[i];
if (!t->valid) {
continue;
}
cJSON *o = cJSON_CreateObject();
cJSON_AddStringToObject(o, "name", t->name);
cJSON_AddNumberToObject(o, "c", round1(t->c));
cJSON_AddNumberToObject(o, "min_c", round1(t->min_c));
cJSON_AddNumberToObject(o, "max_c", round1(t->max_c));
if (t->warn_c > 0) {
cJSON_AddNumberToObject(o, "warn_c", t->warn_c);
}
cJSON_AddItemToArray(temps, o);
}
xSemaphoreGive(s_lock);
}
esp_err_t aes67_health_init(void)
{
s_lock = xSemaphoreCreateMutex();
if (!s_lock) {
return ESP_ERR_NO_MEM;
}
// 20..100 °C (+-2 °C): the die runs well above ambient and the warning level must be in range.
temperature_sensor_config_t cfg = TEMPERATURE_SENSOR_CONFIG_DEFAULT(20, 100);
esp_err_t err = temperature_sensor_install(&cfg, &s_tsens);
if (err == ESP_OK) {
err = temperature_sensor_enable(s_tsens);
}
if (err == ESP_OK) {
health_temp_register("SoC", soc_read, SOC_WARN_C);
} else {
ESP_LOGE(TAG, "on-die temperature sensor: %s", esp_err_to_name(err));
}
if (xTaskCreate(health_task, "health", 3072, NULL, 2, NULL) != pdPASS) {
return ESP_ERR_NO_MEM;
}
return status_register(health_status);
}