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>
This commit is contained in:
@@ -1,9 +1,98 @@
|
||||
#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)
|
||||
{
|
||||
@@ -11,9 +100,46 @@ static void health_status(cJSON *st)
|
||||
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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user