From 8ca3d9716599b1504bec9b1e854b55e5fc19bb24ed56b715450420b94d5a1042 Mon Sep 17 00:00:00 2001 From: localhorst <localhorst@mosad.xyz> Date: Sat, 1 Mar 2025 15:36:05 +0100 Subject: [PATCH] refactoring --- main/control.c | 160 +++++++++++++++++++------------------------------ 1 file changed, 60 insertions(+), 100 deletions(-) diff --git a/main/control.c b/main/control.c index 1bb1726..04ccd8b 100644 --- a/main/control.c +++ b/main/control.c @@ -8,28 +8,31 @@ #include "safety.h" #include "sntp.h" -#define PERIODIC_INTERVAL 1U // run control loop every 1sec +#define PERIODIC_INTERVAL 1U // Run control loop every 1 second +// Temperature thresholds #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY 30.0f #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT 25.0f -#define CHAMPER_TEMPERATURE_TARGET 80.0f // Max cut off temperature, burner will stop if chamber is even or higher -#define CHAMPER_TEMPERATURE_THRESHOLD 45.0f // Min threshold temperature for enabling burner, burner will only start if chamber is even or lower -#define OUTDOOR_TEMPERATURE_THRESHOLD 15.0f // Min threshold temperature for enabling burner, burner will only start if outdoor is even or lower -#define BURNER_FAULT_DETECTION_THRESHOLD (60U * 3U) // Detect burner fault if after 3 minutes no burner start detected +#define CHAMBER_TEMPERATURE_TARGET 80.0f // Max cutoff temperature +#define CHAMBER_TEMPERATURE_THRESHOLD 45.0f // Min threshold for burner enable +#define OUTDOOR_TEMPERATURE_THRESHOLD 15.0f // Min threshold for burner enable +#define BURNER_FAULT_DETECTION_THRESHOLD (60U * 3U) // Burner fault detection after 3 minutes static const char *TAG = "smart-oil-heater-control-system-control"; static eControlState sControlState = CONTROL_STARTING; +// Control table for daily schedules static sControlDay aControlTable[] = { - {MONDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {TUESDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {WEDNESDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {THURSDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {FRIDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{23, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {SATURDAY, 2U, {{{6, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{23, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, - {SUNDAY, 2U, {{{6, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMPER_TEMPERATURE_TARGET}, {{22, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_TEMPERATURE_TARGET}}}, + {MONDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {TUESDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {WEDNESDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {THURSDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {FRIDAY, 2U, {{{4, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{23, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {SATURDAY, 2U, {{{6, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{23, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, + {SUNDAY, 2U, {{{6, 45}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY, CHAMBER_TEMPERATURE_TARGET}, {{22, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMBER_TEMPERATURE_TARGET}}}, }; +// Function prototypes void taskControl(void *pvParameters); eControlWeekday getCurrentWeekday(void); sControlTemperatureEntry getCurrentTemperatureEntry(void); @@ -65,13 +68,14 @@ void taskControl(void *pvParameters) { vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); + // Handle safety faults if (getSafetyState() != SAFETY_NO_ERROR) { ESP_LOGW(TAG, "Control not possible due to safety fault!"); sControlState = CONTROL_FAULT_SAFETY; - if (bHeatingInAction == true) + if (bHeatingInAction) { - ESP_LOGW(TAG, "Control not possible due to safety fault: Disable burner"); + ESP_LOGW(TAG, "Disabling burner due to safety fault"); bHeatingInAction = false; setCirculationPumpState(ENABLED); setBurnerState(DISABLED); @@ -80,13 +84,14 @@ void taskControl(void *pvParameters) continue; } + // Handle SNTP faults if (getSntpState() != SYNC_SUCCESSFUL) { - ESP_LOGW(TAG, "Control not possible due to sntp fault!"); + ESP_LOGW(TAG, "Control not possible due to SNTP fault!"); sControlState = CONTROL_FAULT_SNTP; - if (bHeatingInAction == true) + if (bHeatingInAction) { - ESP_LOGW(TAG, "Control not possible due to sntp fault: Disable burner"); + ESP_LOGW(TAG, "Disabling burner due to SNTP fault"); bHeatingInAction = false; setCirculationPumpState(ENABLED); setBurnerState(DISABLED); @@ -95,69 +100,59 @@ void taskControl(void *pvParameters) continue; } + // Get current temperature entry sControlTemperatureEntry currentControlEntry = getCurrentTemperatureEntry(); - // ESP_LOGI(TAG, "Control Entry Hour: %i Minute: %i ChamberTemp: %lf ReturnFlowTemp: %lf", currentControlEntry.timestamp.hour, currentControlEntry.timestamp.minute, currentControlEntry.fChamberTemperature, currentControlEntry.fReturnFlowTemperature); - if (bHeatingInAction == true) + if (bHeatingInAction) { - if ((getChamberTemperature().fCurrentValue >= currentControlEntry.fChamberTemperature) || (getChamberTemperature().predict60s.fValue >= currentControlEntry.fChamberTemperature)) + if ((getChamberTemperature().fCurrentValue >= currentControlEntry.fChamberTemperature) || + (getChamberTemperature().predict60s.fValue >= currentControlEntry.fChamberTemperature)) { - ESP_LOGI(TAG, "Chamber Target Temperature reached: Disable burner"); + ESP_LOGI(TAG, "Chamber target temperature reached: Disabling burner"); bHeatingInAction = false; setCirculationPumpState(ENABLED); setBurnerState(DISABLED); setSafetyControlState(ENABLED); } - else + else if (esp_timer_get_time() - i64BurnerEnableTimestamp >= BURNER_FAULT_DETECTION_THRESHOLD * 1000000U) { - if (bHeatingInAction) + if (getBurnerError() == FAULT) { - int64_t i64Delta = esp_timer_get_time() - i64BurnerEnableTimestamp; - - if ((i64Delta / 1000000U) >= BURNER_FAULT_DETECTION_THRESHOLD) - { - if (getBurnerError() == FAULT) - { - ESP_LOGW(TAG, "Detected burner fault after %lli seconds!", (i64Delta / 1000000U)); - ESP_LOGW(TAG, "Control not possible due to burner fault: Disable burner"); - sControlState = CONTROL_FAULT_BURNER; - bHeatingInAction = false; - bBurnerFaultDetected = true; - setCirculationPumpState(ENABLED); - setBurnerState(DISABLED); - setSafetyControlState(ENABLED); - } - } + ESP_LOGW(TAG, "Burner fault detected after timeout!"); + bHeatingInAction = false; + bBurnerFaultDetected = true; + sControlState = CONTROL_FAULT_BURNER; + setCirculationPumpState(ENABLED); + setBurnerState(DISABLED); + setSafetyControlState(ENABLED); } } } - if ((bHeatingInAction == false) && (bBurnerFaultDetected == false)) + if (!bHeatingInAction && !bBurnerFaultDetected) { if (getOutdoorTemperature().average60s.fValue >= OUTDOOR_TEMPERATURE_THRESHOLD) { - ESP_LOGI(TAG, "Outdoor temperature too warm: Waiting for winter."); + ESP_LOGI(TAG, "Outdoor temperature too warm: Disabling heating"); setCirculationPumpState(DISABLED); 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"); + bHeatingInAction = true; + setCirculationPumpState(ENABLED); + setBurnerState(ENABLED); + setSafetyControlState(ENABLED); + i64BurnerEnableTimestamp = esp_timer_get_time(); + sControlState = CONTROL_HEATING; + } else { - if ((getReturnFlowTemperature().average60s.fValue <= currentControlEntry.fReturnFlowTemperature) && (getChamberTemperature().fCurrentValue <= CHAMPER_TEMPERATURE_THRESHOLD)) - { - ESP_LOGI(TAG, "Return Flow Target Temperature reached: Enable Burner"); - bHeatingInAction = true; - setCirculationPumpState(ENABLED); - setBurnerState(ENABLED); - setSafetyControlState(ENABLED); - i64BurnerEnableTimestamp = esp_timer_get_time(); - sControlState = CONTROL_HEATING; - } - else - { - sControlState = CONTROL_RETURN_FLOW_TOO_WARM; - } + sControlState = CONTROL_RETURN_FLOW_TOO_WARM; } } } @@ -173,73 +168,38 @@ eControlWeekday getCurrentWeekday(void) time_t now; struct tm *timeinfo; - // Get the current time time(&now); - timeinfo = localtime(&now); // Convert to local time + timeinfo = localtime(&now); - // Get the day of the week (0 = Sunday, 1 = Monday, ..., 6 = Saturday) int day = timeinfo->tm_wday; - - // Adjust so that Monday = 0, Sunday = 6 - if (day == 0) - { - day = 6; // Sunday becomes 6 - } - else - { - day -= 1; // Shift other days to make Monday = 0 - } - - return (eControlWeekday)day; + return (eControlWeekday)((day == 0) ? 6 : day - 1); } sControlTemperatureEntry getCurrentTemperatureEntry(void) { sControlTemperatureEntry result = aControlTable[0].aTemperatureEntries[0]; eControlWeekday currentDay = getCurrentWeekday(); + time_t now; struct tm timeinfo; - - // Get the current time time(&now); - // Convert to local time structure localtime_r(&now, &timeinfo); - // Extract hour and minute - int hour = timeinfo.tm_hour; // Hour (0-23) - int minute = timeinfo.tm_min; // Minute (0-59)u - // ESP_LOGI(TAG, "Current Day: %i Hour: %i Minute: %i", currentDay, hour, minute); + int hour = timeinfo.tm_hour; + int minute = timeinfo.tm_min; for (int i = 0; i < sizeof(aControlTable) / sizeof(aControlTable[0]); i++) { - /// loops through days - // ESP_LOGI(TAG, "Day %d: %d", i + 1, aControlTable[i].day); - // int numberOfEntries = aControlTable[i].entryCount; - // ESP_LOGI(TAG, "Number of entries: %i", numberOfEntries); - for (int j = 0; j < aControlTable[i].entryCount; j++) { - if ((aControlTable[i].day) > currentDay) + if ((aControlTable[i].day > currentDay) || + (aControlTable[i].day == currentDay && aControlTable[i].aTemperatureEntries[j].timestamp.hour > hour) || + (aControlTable[i].day == currentDay && aControlTable[i].aTemperatureEntries[j].timestamp.hour == hour && aControlTable[i].aTemperatureEntries[j].timestamp.minute >= minute)) { - // ESP_LOGI(TAG, "DAY Return Control Entry Day: %i Hour: %i Minute: %i ChamberTemp: %lf ReturnFlowTemp: %lf", aControlTable[i].day, aControlTable[i].aTemperatureEntries[j].timestamp.hour, aControlTable[i].aTemperatureEntries[j].timestamp.minute, aControlTable[i].aTemperatureEntries[j].fChamberTemperature, aControlTable[i].aTemperatureEntries[j].fReturnFlowTemperature); - return result; + return aControlTable[i].aTemperatureEntries[j]; } - - if ((aControlTable[i].day == currentDay) && (aControlTable[i].aTemperatureEntries[j].timestamp.hour > hour)) - { - // ESP_LOGI(TAG, "HOUR Return Control Entry Day: %i Hour: %i Minute: %i ChamberTemp: %lf ReturnFlowTemp: %lf", aControlTable[i].day, aControlTable[i].aTemperatureEntries[j].timestamp.hour, aControlTable[i].aTemperatureEntries[j].timestamp.minute, aControlTable[i].aTemperatureEntries[j].fChamberTemperature, aControlTable[i].aTemperatureEntries[j].fReturnFlowTemperature); - return result; - } - - if ((aControlTable[i].day == currentDay) && (aControlTable[i].aTemperatureEntries[j].timestamp.hour == hour) && (aControlTable[i].aTemperatureEntries[j].timestamp.minute == minute)) - { - // ESP_LOGI(TAG, "MINUTE Return Control Entry Day: %i Hour: %i Minute: %i ChamberTemp: %lf ReturnFlowTemp: %lf", aControlTable[i].day, aControlTable[i].aTemperatureEntries[j].timestamp.hour, aControlTable[i].aTemperatureEntries[j].timestamp.minute, aControlTable[i].aTemperatureEntries[j].fChamberTemperature, aControlTable[i].aTemperatureEntries[j].fReturnFlowTemperature); - return result; - } - - // ESP_LOGI(TAG, "SET Return Control Entry Day: %i Hour: %i Minute: %i ChamberTemp: %lf ReturnFlowTemp: %lf", aControlTable[i].day, aControlTable[i].aTemperatureEntries[j].timestamp.hour, aControlTable[i].aTemperatureEntries[j].timestamp.minute, aControlTable[i].aTemperatureEntries[j].fChamberTemperature, aControlTable[i].aTemperatureEntries[j].fReturnFlowTemperature); result = aControlTable[i].aTemperatureEntries[j]; } } return result; -} \ No newline at end of file +}