formatting

This commit is contained in:
2025-11-01 17:46:33 +01:00
parent d992218a7d
commit b6150ad452

View File

@ -15,14 +15,14 @@
#define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT 25.0f #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT 25.0f
#define CHAMBER_TEMPERATURE_TARGET 80.0f // Max cutoff temperature #define CHAMBER_TEMPERATURE_TARGET 80.0f // Max cutoff temperature
#define CHAMBER_TEMPERATURE_THRESHOLD 45.0f // Min threshold for burner enable #define CHAMBER_TEMPERATURE_THRESHOLD 45.0f // Min threshold for burner enable
#define SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH \ #define SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH \
20.0f // Summer mode will be activated 20.0f // Summer mode will be activated
#define SUMMER_MODE_TEMPERATURE_THRESHOLD_LOW \ #define SUMMER_MODE_TEMPERATURE_THRESHOLD_LOW \
15.0f // Summer mode will be deactivated --> Heating starts 15.0f // Summer mode will be deactivated --> Heating starts
#define CIRCULATION_PUMP_TEMPERATURE_THRESHOLD \ #define CIRCULATION_PUMP_TEMPERATURE_THRESHOLD \
30.0f // Min threshold of chamber for circulation pump enable 30.0f // Min threshold of chamber for circulation pump enable
#define BURNER_FAULT_DETECTION_THRESHOLD \ #define BURNER_FAULT_DETECTION_THRESHOLD \
(60U * 4U) // Burner fault detection after 4 minutes (60U * 4U) // Burner fault detection after 4 minutes
static const char *TAG = "smart-oil-heater-control-system-control"; static const char *TAG = "smart-oil-heater-control-system-control";
static eControlState sControlState = CONTROL_STARTING; static eControlState sControlState = CONTROL_STARTING;
@ -92,148 +92,179 @@ static sControlTemperatureEntry currentControlEntry =
void taskControl(void *pvParameters); void taskControl(void *pvParameters);
void findControlCurrentTemperatureEntry(void); void findControlCurrentTemperatureEntry(void);
void initControl(void) { void initControl(void)
BaseType_t taskCreated = {
xTaskCreate(taskControl, // Function to implement the task BaseType_t taskCreated =
"taskControl", // Task name xTaskCreate(taskControl, // Function to implement the task
8192, // Stack size (in words, not bytes) "taskControl", // Task name
NULL, // Parameters to the task function (none in this case) 8192, // Stack size (in words, not bytes)
5, // Task priority (higher number = higher priority) NULL, // Parameters to the task function (none in this case)
NULL // Task handle (optional) 5, // Task priority (higher number = higher priority)
); NULL // Task handle (optional)
);
if (taskCreated == pdPASS) { if (taskCreated == pdPASS)
ESP_LOGI(TAG, "Task created successfully!"); {
} else { ESP_LOGI(TAG, "Task created successfully!");
ESP_LOGE(TAG, "Failed to create task"); }
} else
{
ESP_LOGE(TAG, "Failed to create task");
}
} }
void taskControl(void *pvParameters) { void taskControl(void *pvParameters)
bool bHeatingInAction = false; {
bool bSummerMode = false; bool bHeatingInAction = false;
eBurnerState eBurnerState = BURNER_UNKNOWN; bool bSummerMode = false;
int64_t i64BurnerEnableTimestamp = esp_timer_get_time(); eBurnerState eBurnerState = BURNER_UNKNOWN;
int64_t i64BurnerEnableTimestamp = esp_timer_get_time();
while (1) { while (1)
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); {
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS);
// Check for safety faults // Check for safety faults
if (getSafetyState() != SAFETY_NO_ERROR) { if (getSafetyState() != SAFETY_NO_ERROR)
ESP_LOGW(TAG, "Control not possible due to safety fault!"); {
sControlState = CONTROL_FAULT_SAFETY; ESP_LOGW(TAG, "Control not possible due to safety fault!");
if (bHeatingInAction) { sControlState = CONTROL_FAULT_SAFETY;
ESP_LOGW(TAG, "Disabling burner due to safety fault"); if (bHeatingInAction)
bHeatingInAction = false; {
setBurnerState(DISABLED); ESP_LOGW(TAG, "Disabling burner due to safety fault");
setSafetyControlState(ENABLED); bHeatingInAction = false;
} setBurnerState(DISABLED);
continue; setSafetyControlState(ENABLED);
} }
continue;
// Check for SNTP faults
if (getSntpState() != SYNC_SUCCESSFUL) {
ESP_LOGW(TAG, "Control not possible due to SNTP fault!");
sControlState = CONTROL_FAULT_SNTP;
if (bHeatingInAction) {
ESP_LOGW(TAG, "Disabling burner due to SNTP fault");
bHeatingInAction = false;
setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
}
continue;
}
findControlCurrentTemperatureEntry();
sControlTemperatureEntry currentControlEntry =
getControlCurrentTemperatureEntry();
if (getOutdoorTemperature().fDampedValue >=
SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH) {
bSummerMode = true;
} 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
if (!bHeatingInAction && (eBurnerState != BURNER_FAULT)) {
if (bSummerMode) {
// ESP_LOGI(TAG, "Outdoor temperature too warm: Disabling heating");
setBurnerState(DISABLED);
setSafetyControlState(DISABLED);
sControlState = CONTROL_OUTDOOR_TOO_WARM;
} else if ((getReturnFlowTemperature().average60s.fValue <=
currentControlEntry.fReturnFlowTemperature) &&
(getChamberTemperature().fCurrentValue <=
CHAMBER_TEMPERATURE_THRESHOLD)) {
ESP_LOGI(TAG,
"Enabling burner: Return flow temperature target reached");
eBurnerState = BURNER_UNKNOWN;
bHeatingInAction = true;
setBurnerState(ENABLED);
setSafetyControlState(ENABLED);
i64BurnerEnableTimestamp = esp_timer_get_time();
sControlState = CONTROL_HEATING;
} else {
// ESP_LOGI(TAG, "Return flow temperature too warm: Disabling heating");
sControlState = CONTROL_RETURN_FLOW_TOO_WARM;
}
}
// Disable burner if target temperature is reached or a fault occurred
if (bHeatingInAction) {
if ((getChamberTemperature().fCurrentValue >=
currentControlEntry.fChamberTemperature) ||
(getChamberTemperature().predict60s.fValue >=
currentControlEntry.fChamberTemperature)) {
ESP_LOGI(TAG, "Chamber target temperature reached: Disabling burner");
bHeatingInAction = false;
setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
} else if (esp_timer_get_time() - i64BurnerEnableTimestamp >=
BURNER_FAULT_DETECTION_THRESHOLD * 1000000U) {
if (eBurnerState == BURNER_UNKNOWN) {
if (getBurnerError() == FAULT) {
// ESP_LOGW(TAG, "Burner fault detected: Disabling burner");
bHeatingInAction = false;
eBurnerState = BURNER_FAULT;
sControlState = CONTROL_FAULT_BURNER;
setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
} else {
// ESP_LOGI(TAG, "No burner fault detected: Marking burner as
// fired");
eBurnerState = BURNER_FIRED;
}
} }
}
}
// Manage circulation pump // Check for SNTP faults
if (getChamberTemperature().fCurrentValue <= if (getSntpState() != SYNC_SUCCESSFUL)
CIRCULATION_PUMP_TEMPERATURE_THRESHOLD) { {
// ESP_LOGI(TAG, "Burner cooled down: Disabling circulation pump"); ESP_LOGW(TAG, "Control not possible due to SNTP fault!");
setCirculationPumpState(DISABLED); sControlState = CONTROL_FAULT_SNTP;
} else { if (bHeatingInAction)
// ESP_LOGI(TAG, "Burner heated: Enabling circulation pump"); {
setCirculationPumpState(ENABLED); ESP_LOGW(TAG, "Disabling burner due to SNTP fault");
} bHeatingInAction = false;
} // End of while(1) setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
}
continue;
}
findControlCurrentTemperatureEntry();
sControlTemperatureEntry currentControlEntry =
getControlCurrentTemperatureEntry();
if (getOutdoorTemperature().fDampedValue >=
SUMMER_MODE_TEMPERATURE_THRESHOLD_HIGH)
{
bSummerMode = true;
}
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
if (!bHeatingInAction && (eBurnerState != BURNER_FAULT))
{
if (bSummerMode)
{
// ESP_LOGI(TAG, "Outdoor temperature too warm: Disabling heating");
setBurnerState(DISABLED);
setSafetyControlState(DISABLED);
sControlState = CONTROL_OUTDOOR_TOO_WARM;
}
else if ((getReturnFlowTemperature().average60s.fValue <=
currentControlEntry.fReturnFlowTemperature) &&
(getChamberTemperature().fCurrentValue <=
CHAMBER_TEMPERATURE_THRESHOLD))
{
ESP_LOGI(TAG,
"Enabling burner: Return flow temperature target reached");
eBurnerState = BURNER_UNKNOWN;
bHeatingInAction = true;
setBurnerState(ENABLED);
setSafetyControlState(ENABLED);
i64BurnerEnableTimestamp = esp_timer_get_time();
sControlState = CONTROL_HEATING;
}
else
{
// ESP_LOGI(TAG, "Return flow temperature too warm: Disabling heating");
sControlState = CONTROL_RETURN_FLOW_TOO_WARM;
}
}
// Disable burner if target temperature is reached or a fault occurred
if (bHeatingInAction)
{
if ((getChamberTemperature().fCurrentValue >=
currentControlEntry.fChamberTemperature) ||
(getChamberTemperature().predict60s.fValue >=
currentControlEntry.fChamberTemperature))
{
ESP_LOGI(TAG, "Chamber target temperature reached: Disabling burner");
bHeatingInAction = false;
setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
}
else if (esp_timer_get_time() - i64BurnerEnableTimestamp >=
BURNER_FAULT_DETECTION_THRESHOLD * 1000000U)
{
if (eBurnerState == BURNER_UNKNOWN)
{
if (getBurnerError() == FAULT)
{
// ESP_LOGW(TAG, "Burner fault detected: Disabling burner");
bHeatingInAction = false;
eBurnerState = BURNER_FAULT;
sControlState = CONTROL_FAULT_BURNER;
setBurnerState(DISABLED);
setSafetyControlState(ENABLED);
}
else
{
// ESP_LOGI(TAG, "No burner fault detected: Marking burner as
// fired");
eBurnerState = BURNER_FIRED;
}
}
}
}
// Manage circulation pump
if (getChamberTemperature().fCurrentValue <=
CIRCULATION_PUMP_TEMPERATURE_THRESHOLD)
{
// ESP_LOGI(TAG, "Burner cooled down: Disabling circulation pump");
setCirculationPumpState(DISABLED);
}
else
{
// ESP_LOGI(TAG, "Burner heated: Enabling circulation pump");
setCirculationPumpState(ENABLED);
}
} // End of while(1)
} }
eControlState getControlState(void) { return sControlState; } eControlState getControlState(void) { return sControlState; }
eControlWeekday getControlCurrentWeekday(void) { eControlWeekday getControlCurrentWeekday(void)
time_t now; {
struct tm *timeinfo; time_t now;
struct tm *timeinfo;
time(&now); time(&now);
timeinfo = localtime(&now); timeinfo = localtime(&now);
int day = timeinfo->tm_wday; int day = timeinfo->tm_wday;
return (eControlWeekday)((day == 0) ? 6 : day - 1); return (eControlWeekday)((day == 0) ? 6 : day - 1);
} }
/** /**
@ -245,12 +276,13 @@ eControlWeekday getControlCurrentWeekday(void) {
*/ */
/** /**
* @brief Finds the active temperature control entry for the current time. * @brief Finds the active temperature control entry for the current time.
* *
* Searches through the weekly schedule to find the most recent entry * Searches through the weekly schedule to find the most recent entry
* that should be active at the current date/time. Falls back to the * that should be active at the current date/time. Falls back to the
* last entry in the week if no suitable entry is found. * last entry in the week if no suitable entry is found.
*/ */
void findControlCurrentTemperatureEntry(void) { void findControlCurrentTemperatureEntry(void)
{
eControlWeekday currentDay = getControlCurrentWeekday(); eControlWeekday currentDay = getControlCurrentWeekday();
// Get current time // Get current time
@ -258,38 +290,46 @@ void findControlCurrentTemperatureEntry(void) {
struct tm timeinfo; struct tm timeinfo;
time(&now); time(&now);
localtime_r(&now, &timeinfo); localtime_r(&now, &timeinfo);
int currentHour = timeinfo.tm_hour; int currentHour = timeinfo.tm_hour;
int currentMinute = timeinfo.tm_min; int currentMinute = timeinfo.tm_min;
//ESP_LOGI(TAG, "Searching for control entry - Day: %d, Time: %02d:%02d", currentDay, currentHour, currentMinute); // ESP_LOGI(TAG, "Searching for control entry - Day: %d, Time: %02d:%02d", currentDay, currentHour, currentMinute);
// Search through all days and entries // Search through all days and entries
for (int dayIndex = 0; dayIndex < 7; dayIndex++) { for (int dayIndex = 0; dayIndex < 7; dayIndex++)
const sControlDay* day = &aControlTable[dayIndex]; {
const sControlDay *day = &aControlTable[dayIndex];
for (int entryIndex = 0; entryIndex < day->entryCount; entryIndex++) { for (int entryIndex = 0; entryIndex < day->entryCount; entryIndex++)
const sControlTemperatureEntry* entry = &day->aTemperatureEntries[entryIndex]; {
const sControlTemperatureEntry *entry = &day->aTemperatureEntries[entryIndex];
// Check if this entry is in the future (next active entry) // Check if this entry is in the future (next active entry)
bool isFutureDay = (day->day > currentDay); bool isFutureDay = (day->day > currentDay);
bool isTodayFutureTime = (day->day == currentDay) && bool isTodayFutureTime = (day->day == currentDay) &&
((entry->timestamp.hour > currentHour) || ((entry->timestamp.hour > currentHour) ||
(entry->timestamp.hour == currentHour && (entry->timestamp.hour == currentHour &&
entry->timestamp.minute > currentMinute)); entry->timestamp.minute > currentMinute));
if (isFutureDay || isTodayFutureTime) { if (isFutureDay || isTodayFutureTime)
{
// Found next scheduled entry, so determine the previous (active) one // Found next scheduled entry, so determine the previous (active) one
if (entryIndex > 0) { if (entryIndex > 0)
{
// Use previous entry from same day // Use previous entry from same day
currentControlEntry = day->aTemperatureEntries[entryIndex - 1]; currentControlEntry = day->aTemperatureEntries[entryIndex - 1];
} else if (dayIndex > 0) { }
else if (dayIndex > 0)
{
// Use last entry from previous day // Use last entry from previous day
const sControlDay* previousDay = &aControlTable[dayIndex - 1]; const sControlDay *previousDay = &aControlTable[dayIndex - 1];
currentControlEntry = previousDay->aTemperatureEntries[previousDay->entryCount - 1]; currentControlEntry = previousDay->aTemperatureEntries[previousDay->entryCount - 1];
} else { }
else
{
// First entry of the week - wrap to last entry of Sunday // First entry of the week - wrap to last entry of Sunday
const sControlDay* sunday = &aControlTable[6]; const sControlDay *sunday = &aControlTable[6];
currentControlEntry = sunday->aTemperatureEntries[sunday->entryCount - 1]; currentControlEntry = sunday->aTemperatureEntries[sunday->entryCount - 1];
} }
/* /*
@ -307,14 +347,13 @@ void findControlCurrentTemperatureEntry(void) {
// If we reached here, current time is after all entries this week // If we reached here, current time is after all entries this week
// Use the last entry (Sunday evening) // Use the last entry (Sunday evening)
const sControlDay* sunday = &aControlTable[6]; const sControlDay *sunday = &aControlTable[6];
currentControlEntry = sunday->aTemperatureEntries[sunday->entryCount - 1]; currentControlEntry = sunday->aTemperatureEntries[sunday->entryCount - 1];
ESP_LOGI(TAG, "Using last entry of week - Time: %02d:%02d", // ESP_LOGI(TAG, "Using last entry of week - Time: %02d:%02d", currentControlEntry.timestamp.hour, currentControlEntry.timestamp.minute);
currentControlEntry.timestamp.hour,
currentControlEntry.timestamp.minute);
} }
sControlTemperatureEntry getControlCurrentTemperatureEntry(void) { sControlTemperatureEntry getControlCurrentTemperatureEntry(void)
return currentControlEntry; {
return currentControlEntry;
} }