Merge pull request 'Add control loop state' (#7) from feature/control-status into main

Reviewed-on: #7
This commit is contained in:
Hendrik Schutter 2024-12-21 22:12:44 +01:00
commit 859611f991
6 changed files with 130 additions and 25 deletions

View File

@ -40,6 +40,7 @@ Sntp <|-- Metrics
class Control{
+taskControl()
-timetable
+getControlState()
}
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 |

View File

@ -10,7 +10,7 @@
#define PERIODIC_INTERVAL 1U // run safety checks every 1sec
static const char *TAG = "smart-oil-heater-control-system-control";
static eControlState sControlState = CONTROL_STARTING;
void taskControl(void *pvParameters);
void initControl(void)
@ -44,12 +44,14 @@ void taskControl(void *pvParameters)
if (getSafetyState() != SAFETY_NO_ERROR)
{
ESP_LOGW(TAG, "Control not possible due to safety fault!");
sControlState = CONTROL_FAULT;
continue;
}
if (getSntpState() != SYNC_SUCCESSFUL)
{
ESP_LOGW(TAG, "Control not possible due to sntp fault!");
sControlState = CONTROL_FAULT;
continue;
}
@ -58,4 +60,9 @@ void taskControl(void *pvParameters)
setBurnerState(ENABLED);
setSafetyControlState(ENABLED);
}
}
}
eControlState getControlState(void)
{
return sControlState;
}

View File

@ -1,3 +1,14 @@
#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);

View File

@ -12,6 +12,7 @@
#include "inputs.h"
#include "safety.h"
#include "sntp.h"
#include "control.h"
static const char *TAG = "smart-oil-heater-control-system-metrics";
@ -181,15 +182,15 @@ void taskMetrics(void *pvParameters)
aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average60s.fValue;
u16MetricCounter++;
/*Sensor status*/
/*Sensor State*/
sSensorSanityCheck aChecks[NUMBER_OF_SENSOR_SANITY_CHECKS];
getSensorSanityStates(aChecks);
for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++)
{
strcpy(aMetrics[u16MetricCounter].caMetricName, aChecks[i].name);
strcat(aMetrics[u16MetricCounter].caMetricName, "_status");
strcat(aMetrics[u16MetricCounter].caMetricName, "_state");
aMetrics[u16MetricCounter].type = INTEGER_U8;
aMetrics[u16MetricCounter].u8MetricValue = aChecks[i].status;
aMetrics[u16MetricCounter].u8MetricValue = aChecks[i].state;
u16MetricCounter++;
}
@ -199,8 +200,14 @@ void taskMetrics(void *pvParameters)
aMetrics[u16MetricCounter].u8MetricValue = getSafetyState();
u16MetricCounter++;
/*SNTP Status*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "sntp_status");
/*Control State*/
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].u8MetricValue = getSntpState();
u16MetricCounter++;

View File

@ -77,7 +77,7 @@ void checkSensorSanity(void)
for (int i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++)
{
// 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(" Last Sensor Temperature: %.2f\n", sanityChecks[i].fSensorTemperatureLast);
@ -86,7 +86,7 @@ void checkSensorSanity(void)
if (sCurrentMeasurement.state == MEASUREMENT_FAULT)
{
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;
}
else
@ -97,7 +97,7 @@ void checkSensorSanity(void)
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);
sanityChecks[i].status = SENSOR_UNCHANGED;
sanityChecks[i].state = SENSOR_UNCHANGED;
sSafetyState = SAFETY_SENSOR_ERROR;
}
}
@ -108,23 +108,23 @@ void checkSensorSanity(void)
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);
sanityChecks[i].status = SENSOR_TOO_HIGH;
sanityChecks[i].state = SENSOR_TOO_HIGH;
sSafetyState = SAFETY_SENSOR_ERROR;
}
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);
sanityChecks[i].status = SENSOR_TOO_LOW;
sanityChecks[i].state = SENSOR_TOO_LOW;
sSafetyState = SAFETY_SENSOR_ERROR;
}
else
{
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++)
{
// Copy only the needed attributes
pSensorSanityChecks[i].status = sanityChecks[i].status;
pSensorSanityChecks[i].state = sanityChecks[i].state;
strcpy(pSensorSanityChecks[i].name, sanityChecks[i].name);
}
xSemaphoreGiveRecursive(xMutexAccessSafety);

View File

@ -30,7 +30,7 @@ typedef struct _TemperatureSensorLimit
} sTemperatureSensorLimit;
typedef struct _SensorSanityCheck
{
eSensorErrorState status;
eSensorErrorState state;
char name[MAX_ERROR_STRING_SIZE];
sTemperatureSensorLimit sSensorLimit;
float fSensorTemperatureLast;