error handling and cleanup

This commit is contained in:
2026-01-10 13:32:49 +01:00
parent f8f6af53bd
commit 1d4e272d80
15 changed files with 867 additions and 309 deletions

View File

@ -1,3 +1,8 @@
/**
* @file control.c
* @brief Implementation of heating control module.
*/
#include "control.h"
#include "inputs.h"
#include "outputs.h"
@ -9,11 +14,16 @@
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#define PERIODIC_INTERVAL 1U // Run control loop every 1 second
#include <time.h>
/** @brief Task interval in seconds. */
#define PERIODIC_INTERVAL 1U
static const char *TAG = "control";
static const char *TAG = "smart-oil-heater-control-system-control";
static eControlState gControlState = CONTROL_STARTING;
// Control table for daily schedules
/** @brief Weekly schedule table (from Kconfig). */
static const sControlDay gControlTable[] = {
{MONDAY,
2U,
@ -72,45 +82,49 @@ static const sControlDay gControlTable[] = {
RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT,
CHAMBER_TEMPERATURE_TARGET}}},
};
static sControlTemperatureEntry gCurrentControlEntry =
gControlTable[0].aTemperatureEntries[0];
static SemaphoreHandle_t xMutexAccessControl = NULL;
// Function prototypes
void taskControl(void *pvParameters);
void findControlCurrentTemperatureEntry(void);
void setControlState(eControlState state);
/* Private function prototypes */
static void taskControl(void *pvParameters);
static void findControlCurrentTemperatureEntry(void);
static void setControlState(eControlState state);
void initControl(void)
esp_err_t initControl(void)
{
xMutexAccessControl = xSemaphoreCreateRecursiveMutex();
if (xMutexAccessControl == NULL)
{
ESP_LOGE(TAG, "Unable to create mutex");
ESP_LOGE(TAG, "Failed to create mutex");
return ESP_FAIL;
}
xSemaphoreGiveRecursive(xMutexAccessControl);
BaseType_t taskCreated =
xTaskCreate(taskControl, // Function to implement the task
"taskControl", // Task name
8192, // Stack size (in words, not bytes)
NULL, // Parameters to the task function (none in this case)
5, // Task priority (higher number = higher priority)
NULL // Task handle (optional)
);
BaseType_t taskCreated = xTaskCreate(
taskControl,
"taskControl",
8192,
NULL,
5,
NULL);
if (taskCreated == pdPASS)
{
ESP_LOGI(TAG, "Task created successfully!");
}
else
if (taskCreated != pdPASS)
{
ESP_LOGE(TAG, "Failed to create task");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Initialized successfully");
return ESP_OK;
}
void taskControl(void *pvParameters)
/**
* @brief Main control task.
* @param pvParameters Task parameters (unused).
*/
static void taskControl(void *pvParameters)
{
bool bHeatingInAction = false;
bool bSummerMode = false;
@ -121,7 +135,7 @@ void taskControl(void *pvParameters)
{
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS);
// Check for safety faults
/* Check for safety faults */
if (getSafetyState() != SAFETY_NO_ERROR)
{
ESP_LOGW(TAG, "Control not possible due to safety fault!");
@ -136,7 +150,7 @@ void taskControl(void *pvParameters)
continue;
}
// Check for SNTP faults
/* Check for SNTP faults */
if (getSntpState() != SYNC_SUCCESSFUL)
{
ESP_LOGW(TAG, "Control not possible due to SNTP fault!");
@ -153,35 +167,30 @@ void taskControl(void *pvParameters)
findControlCurrentTemperatureEntry();
if (getOutdoorTemperature().fDampedValue >=
SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH)
/* Summer mode hysteresis */
if (getOutdoorTemperature().fDampedValue >= SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH)
{
bSummerMode = true;
}
else if (getOutdoorTemperature().fDampedValue <=
SUMMER_MODE_TEMPERATURE_THRESHOLD_LOW)
else if (getOutdoorTemperature().fDampedValue <= SUMMER_MODE_TEMPERATURE_THRESHOLD_LOW)
{
bSummerMode = false;
}
// Enable burner if outdoor temperature is low and return flow temperature
// is cooled down
/* Enable burner if needed */
if (!bHeatingInAction && (burnerState != BURNER_FAULT))
{
if (bSummerMode)
{
// ESP_LOGI(TAG, "Outdoor temperature too warm: Disabling heating");
setBurnerState(DISABLED);
setSafetyControlState(DISABLED);
setControlState(CONTROL_OUTDOOR_TOO_WARM);
}
else if ((getReturnFlowTemperature().average60s.fValue <=
getControlCurrentTemperatureEntry().fReturnFlowTemperature) &&
(getChamberTemperature().fCurrentValue <=
CHAMBER_TEMPERATURE_THRESHOLD))
(getChamberTemperature().fCurrentValue <= CHAMBER_TEMPERATURE_THRESHOLD))
{
ESP_LOGI(TAG,
"Enabling burner: Return flow temperature target reached");
ESP_LOGI(TAG, "Enabling burner: Return flow temperature target reached");
burnerState = BURNER_UNKNOWN;
bHeatingInAction = true;
setBurnerState(ENABLED);
@ -191,12 +200,11 @@ void taskControl(void *pvParameters)
}
else
{
// ESP_LOGI(TAG, "Return flow temperature too warm: Disabling heating");
setControlState(CONTROL_RETURN_FLOW_TOO_WARM);
}
}
// Disable burner if target temperature is reached or a fault occurred
/* Disable burner if target reached or fault */
if (bHeatingInAction)
{
if ((getChamberTemperature().fCurrentValue >=
@ -233,9 +241,8 @@ void taskControl(void *pvParameters)
}
}
// Manage circulation pump
if (getChamberTemperature().fCurrentValue <=
CIRCULATION_PUMP_TEMPERATURE_THRESHOLD)
/* Manage circulation pump */
if (getChamberTemperature().fCurrentValue <= CIRCULATION_PUMP_TEMPERATURE_THRESHOLD)
{
// ESP_LOGI(TAG, "Burner cooled down: Disabling circulation pump");
setCirculationPumpState(DISABLED);
@ -248,9 +255,12 @@ void taskControl(void *pvParameters)
} // End of while(1)
}
void setControlState(eControlState state)
/**
* @brief Set the control state with mutex protection.
* @param state New control state.
*/
static void setControlState(eControlState state)
{
if (xSemaphoreTakeRecursive(xMutexAccessControl, pdMS_TO_TICKS(5000)) == pdTRUE)
{
gControlState = state;
@ -264,7 +274,6 @@ void setControlState(eControlState state)
eControlState getControlState(void)
{
eControlState ret = CONTROL_FAULT_SAFETY;
if (xSemaphoreTakeRecursive(xMutexAccessControl, pdMS_TO_TICKS(5000)) == pdTRUE)
@ -282,7 +291,6 @@ eControlState getControlState(void)
eControlWeekday getControlCurrentWeekday(void)
{
// Get current time
time_t now;
struct tm timeinfo;
time(&now);
@ -293,24 +301,12 @@ eControlWeekday getControlCurrentWeekday(void)
}
/**
* @brief Finds the active temperature control entry for the current time.
*
* Searches through the weekly schedule to find the most recent entry
* that should be active at the current date/time. Falls back to the
* last entry in the week if no suitable entry is found.
* @brief Find the currently active temperature entry based on time.
*/
/**
* @brief Finds the active temperature control entry for the current time.
*
* Searches through the weekly schedule to find the most recent entry
* that should be active at the current date/time. Falls back to the
* last entry in the week if no suitable entry is found.
*/
void findControlCurrentTemperatureEntry(void)
static void findControlCurrentTemperatureEntry(void)
{
eControlWeekday currentDay = getControlCurrentWeekday();
// Get current time
time_t now;
struct tm timeinfo;
time(&now);
@ -361,14 +357,15 @@ void findControlCurrentTemperatureEntry(void)
const sControlDay *sunday = &gControlTable[6];
gCurrentControlEntry = sunday->aTemperatureEntries[sunday->entryCount - 1];
}
xSemaphoreGiveRecursive(xMutexAccessControl);
/*
ESP_LOGI(TAG, "Active entry found - Time: %02d:%02d, "
"Return Temp: %lf, Chamber Temp: %lf",
gCurrentControlEntry.timestamp.hour,
gCurrentControlEntry.timestamp.minute,
gCurrentControlEntry.fReturnFlowTemperature,
gCurrentControlEntry.fChamberTemperature);
*/
"Return Temp: %lf, Chamber Temp: %lf",
gCurrentControlEntry.timestamp.hour,
gCurrentControlEntry.timestamp.minute,
gCurrentControlEntry.fReturnFlowTemperature,
gCurrentControlEntry.fChamberTemperature);
*/
return;
}
}