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,3 +1,3 @@
|
|||||||
idf_component_register(SRCS "aes67_health.c"
|
idf_component_register(SRCS "aes67_health.c"
|
||||||
INCLUDE_DIRS "include"
|
INCLUDE_DIRS "include"
|
||||||
PRIV_REQUIRES aes67_web esp_app_format esp_timer heap)
|
PRIV_REQUIRES aes67_web esp_app_format esp_driver_tsens esp_timer heap)
|
||||||
|
|||||||
@@ -1,9 +1,98 @@
|
|||||||
#include "aes67_health.h"
|
#include "aes67_health.h"
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "aes67_web.h"
|
#include "aes67_web.h"
|
||||||
|
#include "driver/temperature_sensor.h"
|
||||||
#include "esp_app_desc.h"
|
#include "esp_app_desc.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_log.h"
|
||||||
#include "esp_timer.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)
|
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, "uptime_s", (double)(esp_timer_get_time() / 1000000));
|
||||||
cJSON_AddNumberToObject(st, "heap_free", heap_caps_get_free_size(MALLOC_CAP_INTERNAL));
|
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_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)
|
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);
|
return status_register(health_status);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,5 +4,11 @@
|
|||||||
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
|
|
||||||
// Registers the health fields (fw, uptime_s, heap_free, psram_free) in /api/status.
|
// Read one temperature in °C. Return ESP_OK on success.
|
||||||
|
typedef esp_err_t (*health_temp_read_cb_t)(float *celsius);
|
||||||
|
|
||||||
|
// Registers the health fields (fw, uptime_s, heap_free, psram_free, temps) in /api/status,
|
||||||
|
// adds the on-die "SoC" sensor and starts the 2 s sampling task.
|
||||||
esp_err_t aes67_health_init(void);
|
esp_err_t aes67_health_init(void);
|
||||||
|
// Add a temperature sensor (e.g. an external I2C one from the board). warn_c <= 0: no warning.
|
||||||
|
esp_err_t health_temp_register(const char *name, health_temp_read_cb_t read, float warn_c);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ Reference devices for UI and defaults: Riedel Bolero (PTP status), Riedel Direct
|
|||||||
## Temperatures
|
## Temperatures
|
||||||
- `status.temps` = array of {name, c, min_c, max_c, warn_c}; min/max since boot, warn_c optional. Any number of sensors; the UI renders one row per sensor and marks HIGH (red) at or above warn_c.
|
- `status.temps` = array of {name, c, min_c, max_c, warn_c}; min/max since boot, warn_c optional. Any number of sensors; the UI renders one row per sensor and marks HIGH (red) at or above warn_c.
|
||||||
- ESP32-P4: on-die sensor via the `temperature_sensor` driver (driver/temperature_sensor.h). Pick the measurement range to suit (e.g. -10..80 °C), sample every ~2 s in the health task, and report it as "SoC". It measures the die, not ambient: expect it to read well above room temperature.
|
- ESP32-P4: on-die sensor via the `temperature_sensor` driver (driver/temperature_sensor.h). Pick the measurement range to suit (e.g. -10..80 °C), sample every ~2 s in the health task, and report it as "SoC". It measures the die, not ambient: expect it to read well above room temperature.
|
||||||
|
- Implemented with range 20..100 °C (±2 °C; the warning level must lie inside the range), warn_c 85 °C. On the test board the die reads ~27 °C at idle in a cool room.
|
||||||
- Boards may add external sensors (e.g. I2C) by appending to the same array; the project or board component registers them.
|
- Boards may add external sensors (e.g. I2C) by appending to the same array; the project or board component registers them.
|
||||||
- Log a warning (goes to syslog) when crossing warn_c, with hysteresis (e.g. clear at warn_c - 5).
|
- Log a warning (goes to syslog) when crossing warn_c, with hysteresis (e.g. clear at warn_c - 5).
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user