Merge pull request 'Add control loop state' (#7) from feature/control-status into main
Reviewed-on: #7
This commit is contained in:
		
							
								
								
									
										96
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										96
									
								
								README.md
									
									
									
									
									
								
							| @ -40,6 +40,7 @@ Sntp <|-- Metrics | |||||||
|     class Control{ |     class Control{ | ||||||
|         +taskControl() |         +taskControl() | ||||||
|         -timetable |         -timetable | ||||||
|  |         +getControlState() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     class Safety{ |     class Safety{ | ||||||
| @ -71,12 +72,91 @@ Sntp <|-- Metrics | |||||||
|     } |     } | ||||||
| ``` | ``` | ||||||
|  |  | ||||||
| ### Hardware | ### Prometheus Metrics | ||||||
|  | `curl http://X.X.X.X:9100/metrics` | ||||||
|  | #### Example | ||||||
|  | ``` | ||||||
|  | burner_fault_pending 1 | ||||||
|  | circulation_pump_enabled 0 | ||||||
|  | burner_enabled 1 | ||||||
|  | safety_contact_enabled 1 | ||||||
|  | chamber_temperature 21.812500 | ||||||
|  | chamber_temperature_avg10 21.837500 | ||||||
|  | chamber_temperature_avg60 21.825521 | ||||||
|  | inlet_flow_temperature 22.437500 | ||||||
|  | inlet_flow_temperature_avg10 22.437500 | ||||||
|  | inlet_flow_temperature_avg60 22.434896 | ||||||
|  | outdoor_temperature 21.937500 | ||||||
|  | outdoor_temperature_avg10 21.937500 | ||||||
|  | outdoor_temperature_avg60 21.933594 | ||||||
|  | return_flow_temperature 22.375000 | ||||||
|  | return_flow_temperature_avg10 22.375000 | ||||||
|  | return_flow_temperature_avg60 22.375000 | ||||||
|  | chamber_temperature_state 0 | ||||||
|  | outdoor_temperature_state 0 | ||||||
|  | inlet_flow_temperature_state 0 | ||||||
|  | return_flow_temperature_state 0 | ||||||
|  | safety_state 0 | ||||||
|  | control_state 5 | ||||||
|  | sntp_state 0 | ||||||
|  | system_unixtime 1734814285 | ||||||
|  | uptime_seconds 90 | ||||||
|  | wifi_rssi -63 | ||||||
|  | ``` | ||||||
|  |  | ||||||
|  | #### Status Encoding | ||||||
|  | ##### Digital Themperatur Sensor DS10B20 | ||||||
|  |  - chamber_temperature_state | ||||||
|  |  - outdoor_temperature_state | ||||||
|  |  - inlet_flow_temperature_state | ||||||
|  |  - return_flow_temperature_state | ||||||
|  |  | ||||||
|  | | Enum eSensorErrorState in [safety.h](main/safety.h) | Value | Description         | | ||||||
|  | |-----------------------------------------------------|-------|---------------------| | ||||||
|  | | SENSOR_NO_ERROR                                     | 0     |                     | | ||||||
|  | | SENSOR_TOO_HIGH                                     | 1     | Sanity check failed | | ||||||
|  | | SENSOR_TOO_LOW                                      | 2     | Sanity check failed | | ||||||
|  | | SENSOR_UNCHANGED                                    | 3     | Sanity check failed | | ||||||
|  | | SENSOR_NOT_FOUND                                    | 4     | No Communication    | | ||||||
|  |  | ||||||
|  | ##### Safety Loop | ||||||
|  |  - safety_state | ||||||
|  |  | ||||||
|  | | Enum eSafetyState in [safety.h](main/safety.h) | Value | Description                | | ||||||
|  | |------------------------------------------------|-------|----------------------------| | ||||||
|  | | SAFETY_NO_ERROR                                | 0     |                            | | ||||||
|  | | SAFETY_SENSOR_ERROR                            | 1     | At least one sensor failed | | ||||||
|  | | SAFETY_INTERNAL_ERROR                          | 2     | Internal error             | | ||||||
|  |  | ||||||
|  | ##### Control Loop | ||||||
|  |  - control_state | ||||||
|  |  | ||||||
|  | | Enum eControlState in [control.h](main/control.h) | Value | Description           | | ||||||
|  | |---------------------------------------------------|-------|-----------------------| | ||||||
|  | | CONTROL_STARTING                                  | 0     |                       | | ||||||
|  | | CONTROL_HEATING                                   | 1     | Burner running        | | ||||||
|  | | CONTROL_OUTDOOR_TOO_WARM                          | 2     | Heating not needed    | | ||||||
|  | | CONTROL_RETURN_FLOW_TOO_WARM                      | 3     | Heating not needed    | | ||||||
|  | | CONTROL_BURNER_FAULT                              | 4     | Burner reported fault | | ||||||
|  | | CONTROL_FAULT                                     | 5     | Unable to control     | | ||||||
|  |  | ||||||
|  | ##### SNTP Client | ||||||
|  |  - sntp_state | ||||||
|  |  | ||||||
|  | | Enum eSntpState in [sntp.h](main/sntp.h) | Value | Description | | ||||||
|  | |------------------------------------------|-------|-------------| | ||||||
|  | | SYNC_SUCCESSFUL                          | 0     |             | | ||||||
|  | | SYNC_NOT_STARTED                         | 1     |             | | ||||||
|  | | SYNC_FAILED                              | 2     |             | | ||||||
|  |  | ||||||
|  | ## Hardware | ||||||
|  |  | ||||||
|  | | Function                              | ESP32 | PLC ES32C14       | | ||||||
|  | |---------------------------------------|-------|-------------------| | ||||||
|  | | Output CirculationPump                | IO27  | Relay 1 NC1       | | ||||||
|  | | Output Burner Fire Signal             | IO14  | Relay 2 NC2       | | ||||||
|  | | Output Safety Contact (powers Burner) | IO12  | Relay 3 NC2       | | ||||||
|  | | Input Burner Fault                    | IO19  | Digital Input IN1 | | ||||||
|  | | Input Temperature DS10B20             | IO04  | 1-Wire            | | ||||||
|  |  | ||||||
|  |  | ||||||
| | Function                              | ESP32 | PLC ES32C14               | |  | ||||||
| |---------------------------------------|-------|---------------------------| |  | ||||||
| | Output CirculationPump                | IO27  | Relay 1 NC1               | |  | ||||||
| | Output Burner Fire Signal             | IO14  | Relay 2 NC2               | |  | ||||||
| | Output Safety Contact (powers Burner) | IO12  | Relay 3 NC2               | |  | ||||||
| | Input Burner Fault                    | IO19  | Digital Input IN1         | |  | ||||||
| | Input Temperature DS10B20             | IO04  | 1-Wire                    | |  | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ | |||||||
| #define PERIODIC_INTERVAL 1U // run safety checks every 1sec | #define PERIODIC_INTERVAL 1U // run safety checks every 1sec | ||||||
|  |  | ||||||
| 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; | ||||||
| void taskControl(void *pvParameters); | void taskControl(void *pvParameters); | ||||||
|  |  | ||||||
| void initControl(void) | void initControl(void) | ||||||
| @ -44,12 +44,14 @@ void taskControl(void *pvParameters) | |||||||
|         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; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         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; | ||||||
|             continue; |             continue; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @ -58,4 +60,9 @@ void taskControl(void *pvParameters) | |||||||
|         setBurnerState(ENABLED); |         setBurnerState(ENABLED); | ||||||
|         setSafetyControlState(ENABLED); |         setSafetyControlState(ENABLED); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | eControlState getControlState(void) | ||||||
|  | { | ||||||
|  |     return sControlState; | ||||||
|  | } | ||||||
|  | |||||||
| @ -1,3 +1,14 @@ | |||||||
| #pragma once | #pragma once | ||||||
|  |  | ||||||
| void initControl(void); | typedef enum _ControlState | ||||||
|  | { | ||||||
|  |     CONTROL_STARTING, | ||||||
|  |     CONTROL_HEATING, | ||||||
|  |     CONTROL_OUTDOOR_TOO_WARM, | ||||||
|  |     CONTROL_RETURN_FLOW_TOO_WARM, | ||||||
|  |     CONTROL_BURNER_FAULT, | ||||||
|  |     CONTROL_FAULT, | ||||||
|  | } eControlState; | ||||||
|  |  | ||||||
|  | void initControl(void); | ||||||
|  | eControlState getControlState(void); | ||||||
| @ -12,6 +12,7 @@ | |||||||
| #include "inputs.h" | #include "inputs.h" | ||||||
| #include "safety.h" | #include "safety.h" | ||||||
| #include "sntp.h" | #include "sntp.h" | ||||||
|  | #include "control.h" | ||||||
|  |  | ||||||
| static const char *TAG = "smart-oil-heater-control-system-metrics"; | static const char *TAG = "smart-oil-heater-control-system-metrics"; | ||||||
|  |  | ||||||
| @ -181,15 +182,15 @@ void taskMetrics(void *pvParameters) | |||||||
|         aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average60s.fValue; |         aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average60s.fValue; | ||||||
|         u16MetricCounter++; |         u16MetricCounter++; | ||||||
|  |  | ||||||
|         /*Sensor status*/ |         /*Sensor State*/ | ||||||
|         sSensorSanityCheck aChecks[NUMBER_OF_SENSOR_SANITY_CHECKS]; |         sSensorSanityCheck aChecks[NUMBER_OF_SENSOR_SANITY_CHECKS]; | ||||||
|         getSensorSanityStates(aChecks); |         getSensorSanityStates(aChecks); | ||||||
|         for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) |         for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) | ||||||
|         { |         { | ||||||
|             strcpy(aMetrics[u16MetricCounter].caMetricName, aChecks[i].name); |             strcpy(aMetrics[u16MetricCounter].caMetricName, aChecks[i].name); | ||||||
|             strcat(aMetrics[u16MetricCounter].caMetricName, "_status"); |             strcat(aMetrics[u16MetricCounter].caMetricName, "_state"); | ||||||
|             aMetrics[u16MetricCounter].type = INTEGER_U8; |             aMetrics[u16MetricCounter].type = INTEGER_U8; | ||||||
|             aMetrics[u16MetricCounter].u8MetricValue = aChecks[i].status; |             aMetrics[u16MetricCounter].u8MetricValue = aChecks[i].state; | ||||||
|             u16MetricCounter++; |             u16MetricCounter++; | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @ -199,8 +200,14 @@ void taskMetrics(void *pvParameters) | |||||||
|         aMetrics[u16MetricCounter].u8MetricValue = getSafetyState(); |         aMetrics[u16MetricCounter].u8MetricValue = getSafetyState(); | ||||||
|         u16MetricCounter++; |         u16MetricCounter++; | ||||||
|  |  | ||||||
|         /*SNTP Status*/ |         /*Control State*/ | ||||||
|         strcpy(aMetrics[u16MetricCounter].caMetricName, "sntp_status"); |         strcpy(aMetrics[u16MetricCounter].caMetricName, "control_state"); | ||||||
|  |         aMetrics[u16MetricCounter].type = INTEGER_U8; | ||||||
|  |         aMetrics[u16MetricCounter].u8MetricValue = getControlState(); | ||||||
|  |         u16MetricCounter++; | ||||||
|  |  | ||||||
|  |         /*SNTP State*/ | ||||||
|  |         strcpy(aMetrics[u16MetricCounter].caMetricName, "sntp_state"); | ||||||
|         aMetrics[u16MetricCounter].type = INTEGER_U8; |         aMetrics[u16MetricCounter].type = INTEGER_U8; | ||||||
|         aMetrics[u16MetricCounter].u8MetricValue = getSntpState(); |         aMetrics[u16MetricCounter].u8MetricValue = getSntpState(); | ||||||
|         u16MetricCounter++; |         u16MetricCounter++; | ||||||
|  | |||||||
| @ -77,7 +77,7 @@ void checkSensorSanity(void) | |||||||
|     for (int i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) |     for (int i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) | ||||||
|     { |     { | ||||||
|         // printf("Check sanity of sensor %s:\n", sanityChecks[i].name); |         // printf("Check sanity of sensor %s:\n", sanityChecks[i].name); | ||||||
|         // printf("  Status: %u\n", sanityChecks[i].status); |         // printf("  state: %u\n", sanityChecks[i].state); | ||||||
|         // printf("  Sensor Limits: Max = %.2f, Min = %.2f\n", sanityChecks[i].sSensorLimit.max, sanityChecks[i].sSensorLimit.min); |         // printf("  Sensor Limits: Max = %.2f, Min = %.2f\n", sanityChecks[i].sSensorLimit.max, sanityChecks[i].sSensorLimit.min); | ||||||
|         // printf("  Last Sensor Temperature: %.2f\n", sanityChecks[i].fSensorTemperatureLast); |         // printf("  Last Sensor Temperature: %.2f\n", sanityChecks[i].fSensorTemperatureLast); | ||||||
|  |  | ||||||
| @ -86,7 +86,7 @@ void checkSensorSanity(void) | |||||||
|         if (sCurrentMeasurement.state == MEASUREMENT_FAULT) |         if (sCurrentMeasurement.state == MEASUREMENT_FAULT) | ||||||
|         { |         { | ||||||
|             ESP_LOGE(TAG, "%s Sensor not found!", sanityChecks[i].name); |             ESP_LOGE(TAG, "%s Sensor not found!", sanityChecks[i].name); | ||||||
|             sanityChecks[i].status = SENSOR_NOT_FOUND; |             sanityChecks[i].state = SENSOR_NOT_FOUND; | ||||||
|             sSafetyState = SAFETY_SENSOR_ERROR; |             sSafetyState = SAFETY_SENSOR_ERROR; | ||||||
|         } |         } | ||||||
|         else |         else | ||||||
| @ -97,7 +97,7 @@ void checkSensorSanity(void) | |||||||
|                 if (sanityChecks[i].uUnchangedCounter >= (SENSOR_GRACE_PERIOD / PERIODIC_INTERVAL)) |                 if (sanityChecks[i].uUnchangedCounter >= (SENSOR_GRACE_PERIOD / PERIODIC_INTERVAL)) | ||||||
|                 { |                 { | ||||||
|                     ESP_LOGE(TAG, "%s Sensor reported unchanged value! %lf == %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].fSensorTemperatureLast); |                     ESP_LOGE(TAG, "%s Sensor reported unchanged value! %lf == %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].fSensorTemperatureLast); | ||||||
|                     sanityChecks[i].status = SENSOR_UNCHANGED; |                     sanityChecks[i].state = SENSOR_UNCHANGED; | ||||||
|                     sSafetyState = SAFETY_SENSOR_ERROR; |                     sSafetyState = SAFETY_SENSOR_ERROR; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -108,23 +108,23 @@ void checkSensorSanity(void) | |||||||
|                 if (sCurrentMeasurement.fCurrentValue > sanityChecks[i].sSensorLimit.max) |                 if (sCurrentMeasurement.fCurrentValue > sanityChecks[i].sSensorLimit.max) | ||||||
|                 { |                 { | ||||||
|                     ESP_LOGE(TAG, "%s Sensor reported too high value! %lf > %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.max); |                     ESP_LOGE(TAG, "%s Sensor reported too high value! %lf > %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.max); | ||||||
|                     sanityChecks[i].status = SENSOR_TOO_HIGH; |                     sanityChecks[i].state = SENSOR_TOO_HIGH; | ||||||
|                     sSafetyState = SAFETY_SENSOR_ERROR; |                     sSafetyState = SAFETY_SENSOR_ERROR; | ||||||
|                 } |                 } | ||||||
|                 else if (sCurrentMeasurement.fCurrentValue < sanityChecks[i].sSensorLimit.min) |                 else if (sCurrentMeasurement.fCurrentValue < sanityChecks[i].sSensorLimit.min) | ||||||
|                 { |                 { | ||||||
|                     ESP_LOGE(TAG, "%s Sensor reported too low value! %lf < %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.min); |                     ESP_LOGE(TAG, "%s Sensor reported too low value! %lf < %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.min); | ||||||
|                     sanityChecks[i].status = SENSOR_TOO_LOW; |                     sanityChecks[i].state = SENSOR_TOO_LOW; | ||||||
|                     sSafetyState = SAFETY_SENSOR_ERROR; |                     sSafetyState = SAFETY_SENSOR_ERROR; | ||||||
|                 } |                 } | ||||||
|                 else |                 else | ||||||
|                 { |                 { | ||||||
|                     sanityChecks[i].uUnchangedCounter = 0U; |                     sanityChecks[i].uUnchangedCounter = 0U; | ||||||
|                     sanityChecks[i].status = SENSOR_NO_ERROR; |                     sanityChecks[i].state = SENSOR_NO_ERROR; | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|         // printf("  Status: %u\n", sanityChecks[i].status); |         // printf("  state: %u\n", sanityChecks[i].state); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
| @ -142,7 +142,7 @@ void getSensorSanityStates(sSensorSanityCheck *pSensorSanityChecks) | |||||||
|         for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) |         for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) | ||||||
|         { |         { | ||||||
|             // Copy only the needed attributes |             // Copy only the needed attributes | ||||||
|             pSensorSanityChecks[i].status = sanityChecks[i].status; |             pSensorSanityChecks[i].state = sanityChecks[i].state; | ||||||
|             strcpy(pSensorSanityChecks[i].name, sanityChecks[i].name); |             strcpy(pSensorSanityChecks[i].name, sanityChecks[i].name); | ||||||
|         } |         } | ||||||
|         xSemaphoreGiveRecursive(xMutexAccessSafety); |         xSemaphoreGiveRecursive(xMutexAccessSafety); | ||||||
|  | |||||||
| @ -30,7 +30,7 @@ typedef struct _TemperatureSensorLimit | |||||||
| } sTemperatureSensorLimit; | } sTemperatureSensorLimit; | ||||||
| typedef struct _SensorSanityCheck | typedef struct _SensorSanityCheck | ||||||
| { | { | ||||||
|     eSensorErrorState status; |     eSensorErrorState state; | ||||||
|     char name[MAX_ERROR_STRING_SIZE]; |     char name[MAX_ERROR_STRING_SIZE]; | ||||||
|     sTemperatureSensorLimit sSensorLimit; |     sTemperatureSensorLimit sSensorLimit; | ||||||
|     float fSensorTemperatureLast; |     float fSensorTemperatureLast; | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user