|  |  | @ -8,26 +8,31 @@ | 
			
		
	
		
		
			
				
					
					|  |  |  | #include "safety.h" |  |  |  | #include "safety.h" | 
			
		
	
		
		
			
				
					
					|  |  |  | #include "sntp.h" |  |  |  | #include "sntp.h" | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | #define PERIODIC_INTERVAL 1U // run control loop every 1sec |  |  |  | #define PERIODIC_INTERVAL 1U // Run control loop every 1 second | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY 30.0 |  |  |  | // Temperature thresholds | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT 25.0 |  |  |  | #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_DAY 30.0f | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #define CHAMPER_TEMPERATURE_TARGET 80.0 |  |  |  | #define RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT 25.0f | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  | #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 * 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; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | // Control table for daily schedules | 
			
		
	
		
		
			
				
					
					|  |  |  | static sControlDay aControlTable[] = { |  |  |  | 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}}}, |  |  |  |     {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, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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, CHAMPER_TEMPERATURE_TARGET}, {{22, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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, CHAMPER_TEMPERATURE_TARGET}, {{23, 0}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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, CHAMPER_TEMPERATURE_TARGET}, {{23, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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, CHAMPER_TEMPERATURE_TARGET}, {{22, 30}, RETURN_FLOW_TEMPERATURE_LOWER_LIMIT_NIGHT, CHAMPER_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); |  |  |  | void taskControl(void *pvParameters); | 
			
		
	
		
		
			
				
					
					|  |  |  | eControlWeekday getCurrentWeekday(void); |  |  |  | eControlWeekday getCurrentWeekday(void); | 
			
		
	
		
		
			
				
					
					|  |  |  | sControlTemperatureEntry getCurrentTemperatureEntry(void); |  |  |  | sControlTemperatureEntry getCurrentTemperatureEntry(void); | 
			
		
	
	
		
		
			
				
					
					|  |  | @ -53,28 +58,31 @@ void initControl(void) | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | typedef enum _BurnerState | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     BURNER_UNKNOWN, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     BURNER_FIRED, | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     BURNER_FAULT | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  | } eBurnerState; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | void taskControl(void *pvParameters) |  |  |  | void taskControl(void *pvParameters) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     bool bHeatingInAction = false; |  |  |  |     bool bHeatingInAction = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |     bool bBurnerFaultDetected = false; |  |  |  |     eBurnerState eBurnerState = BURNER_UNKNOWN; | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |     int64_t i64BurnerEnableTimestamp = esp_timer_get_time(); |  |  |  |     int64_t i64BurnerEnableTimestamp = esp_timer_get_time(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     time_t now; |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     while (1) |  |  |  |     while (1) | 
			
		
	
		
		
			
				
					
					|  |  |  |     { |  |  |  |     { | 
			
		
	
		
		
			
				
					
					|  |  |  |         // Get the current time |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         time(&now); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         ESP_LOGW(TAG, "Control loop time: %lli", now); |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); |  |  |  |         vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Handle safety faults | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (getSafetyState() != SAFETY_NO_ERROR) |  |  |  |         if (getSafetyState() != SAFETY_NO_ERROR) | 
			
		
	
		
		
			
				
					
					|  |  |  |         { |  |  |  |         { | 
			
		
	
		
		
			
				
					
					|  |  |  |             //ESP_LOGW(TAG, "Control not possible due to safety fault!"); |  |  |  |             ESP_LOGW(TAG, "Control not possible due to safety fault!"); | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             sControlState = CONTROL_FAULT_SAFETY; |  |  |  |             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; |  |  |  |                 bHeatingInAction = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setCirculationPumpState(ENABLED); |  |  |  |                 setCirculationPumpState(ENABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setBurnerState(DISABLED); |  |  |  |                 setBurnerState(DISABLED); | 
			
		
	
	
		
		
			
				
					
					|  |  | @ -83,13 +91,14 @@ void taskControl(void *pvParameters) | 
			
		
	
		
		
			
				
					
					|  |  |  |             continue; |  |  |  |             continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Handle SNTP faults | 
			
		
	
		
		
			
				
					
					|  |  |  |         if (getSntpState() != SYNC_SUCCESSFUL) |  |  |  |         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; |  |  |  |             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; |  |  |  |                 bHeatingInAction = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setCirculationPumpState(ENABLED); |  |  |  |                 setCirculationPumpState(ENABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setBurnerState(DISABLED); |  |  |  |                 setBurnerState(DISABLED); | 
			
		
	
	
		
		
			
				
					
					|  |  | @ -98,48 +107,58 @@ void taskControl(void *pvParameters) | 
			
		
	
		
		
			
				
					
					|  |  |  |             continue; |  |  |  |             continue; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |         // Get current temperature entry | 
			
		
	
		
		
			
				
					
					|  |  |  |         sControlTemperatureEntry currentControlEntry = getCurrentTemperatureEntry(); |  |  |  |         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; |  |  |  |                 bHeatingInAction = false; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setCirculationPumpState(ENABLED); |  |  |  |                 setCirculationPumpState(ENABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setBurnerState(DISABLED); |  |  |  |                 setBurnerState(DISABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setSafetyControlState(ENABLED); |  |  |  |                 setSafetyControlState(ENABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |             else |  |  |  |             else if (esp_timer_get_time() - i64BurnerEnableTimestamp >= BURNER_FAULT_DETECTION_THRESHOLD * 1000000U) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             { |  |  |  |             { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 if (bHeatingInAction) |  |  |  |                 if (eBurnerState == BURNER_UNKNOWN) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                 { |  |  |  |                 { | 
			
		
	
		
		
			
				
					
					|  |  |  |                     int64_t i64Delta = esp_timer_get_time() - i64BurnerEnableTimestamp; |  |  |  |                     if (getBurnerError() == FAULT) | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |                     if ((i64Delta / 1000000U) >= BURNER_FAULT_DETECTION_THRESHOLD) |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |                     { |  |  |  |                     { | 
			
		
	
		
		
			
				
					
					|  |  |  |                         if (getBurnerError() == FAULT) |  |  |  |                         ESP_LOGW(TAG, "Burner fault detected after threshold!"); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         { |  |  |  |                         bHeatingInAction = false; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             ESP_LOGW(TAG, "Detected burner fault after %lli seconds!", (i64Delta / 1000000U)); |  |  |  |                         eBurnerState = BURNER_FAULT; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             ESP_LOGW(TAG, "Control not possible due to burner fault: Disable burner"); |  |  |  |                         sControlState = CONTROL_FAULT_BURNER; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             sControlState = CONTROL_FAULT_BURNER; |  |  |  |                         setCirculationPumpState(ENABLED); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             bHeatingInAction = false; |  |  |  |                         setBurnerState(DISABLED); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             bBurnerFaultDetected = true; |  |  |  |                         setSafetyControlState(ENABLED); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             setCirculationPumpState(ENABLED); |  |  |  |                     } | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             setBurnerState(DISABLED); |  |  |  |                     else | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                             setSafetyControlState(ENABLED); |  |  |  |                     { | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                         } |  |  |  |                         ESP_LOGW(TAG, "No Burner fault detected after threshold!"); | 
			
				
				
			
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                         eBurnerState = BURNER_FIRED; | 
			
		
	
		
		
			
				
					
					|  |  |  |                     } |  |  |  |                     } | 
			
		
	
		
		
			
				
					
					|  |  |  |                 } |  |  |  |                 } | 
			
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |         if ((bHeatingInAction == false) && (bBurnerFaultDetected == false)) |  |  |  |         if (!bHeatingInAction && (eBurnerState != BURNER_FAULT)) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |         { |  |  |  |         { | 
			
		
	
		
		
			
				
					
					|  |  |  |             if ((getReturnFlowTemperature().average60s.fValue <= currentControlEntry.fReturnFlowTemperature) && (getChamberTemperature().fCurrentValue <= 45.0)) |  |  |  |             if (getOutdoorTemperature().average60s.fValue >= OUTDOOR_TEMPERATURE_THRESHOLD) | 
			
				
				
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             { |  |  |  |             { | 
			
		
	
		
		
			
				
					
					|  |  |  |                 ESP_LOGI(TAG, "Return Flow Target Temperature reached: Enable Burner"); |  |  |  |                 // 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"); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |                 eBurnerState = BURNER_UNKNOWN; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 bHeatingInAction = true; |  |  |  |                 bHeatingInAction = true; | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setCirculationPumpState(ENABLED); |  |  |  |                 setCirculationPumpState(ENABLED); | 
			
		
	
		
		
			
				
					
					|  |  |  |                 setBurnerState(ENABLED); |  |  |  |                 setBurnerState(ENABLED); | 
			
		
	
	
		
		
			
				
					
					|  |  | @ -165,71 +184,36 @@ eControlWeekday getCurrentWeekday(void) | 
			
		
	
		
		
			
				
					
					|  |  |  |     time_t now; |  |  |  |     time_t now; | 
			
		
	
		
		
			
				
					
					|  |  |  |     struct tm *timeinfo; |  |  |  |     struct tm *timeinfo; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Get the current time |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     time(&now); |  |  |  |     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; |  |  |  |     int day = timeinfo->tm_wday; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |     return (eControlWeekday)((day == 0) ? 6 : day - 1); | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |     // 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; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  | } |  |  |  | } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  | sControlTemperatureEntry getCurrentTemperatureEntry(void) |  |  |  | sControlTemperatureEntry getCurrentTemperatureEntry(void) | 
			
		
	
		
		
			
				
					
					|  |  |  | { |  |  |  | { | 
			
		
	
		
		
			
				
					
					|  |  |  |     sControlTemperatureEntry result = aControlTable[0].aTemperatureEntries[0]; |  |  |  |     sControlTemperatureEntry result = aControlTable[0].aTemperatureEntries[0]; | 
			
		
	
		
		
			
				
					
					|  |  |  |     eControlWeekday currentDay = getCurrentWeekday(); |  |  |  |     eControlWeekday currentDay = getCurrentWeekday(); | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     time_t now; |  |  |  |     time_t now; | 
			
		
	
		
		
			
				
					
					|  |  |  |     struct tm timeinfo; |  |  |  |     struct tm timeinfo; | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Get the current time |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     time(&now); |  |  |  |     time(&now); | 
			
		
	
		
		
			
				
					
					|  |  |  |     // Convert to local time structure |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |     localtime_r(&now, &timeinfo); |  |  |  |     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++) |  |  |  |     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++) |  |  |  |         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 aControlTable[i].aTemperatureEntries[j]; | 
			
				
				
			
		
	
		
		
			
				
					
					|  |  |  |                 return result; |  |  |  |  | 
			
		
	
		
		
	
		
		
			
				
					
					|  |  |  |             } |  |  |  |             } | 
			
		
	
		
		
			
				
					
					|  |  |  |  |  |  |  |  | 
			
		
	
		
		
			
				
					
					|  |  |  |             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]; |  |  |  |             result = aControlTable[i].aTemperatureEntries[j]; | 
			
		
	
		
		
			
				
					
					|  |  |  |         } |  |  |  |         } | 
			
		
	
		
		
			
				
					
					|  |  |  |     } |  |  |  |     } | 
			
		
	
	
		
		
			
				
					
					|  |  | 
 |