safety state and input error detection

This commit is contained in:
Hendrik Schutter 2024-12-13 19:26:16 +01:00
parent a7f6973efd
commit 8205253b5a
6 changed files with 236 additions and 196 deletions

View File

@ -12,7 +12,12 @@
"inputs.h": "c", "inputs.h": "c",
"cstdlib": "c", "cstdlib": "c",
"typeinfo": "c", "typeinfo": "c",
"limits": "c" "limits": "c",
"bit": "c",
"type_traits": "c",
"cmath": "c",
"*.tcc": "c",
"*.inc": "c"
}, },
"idf.openOcdConfigs": [ "idf.openOcdConfigs": [
"board/esp32-wrover-kit-3.3v.cfg" "board/esp32-wrover-kit-3.3v.cfg"

View File

@ -6,27 +6,8 @@
#include "inputs.h" #include "inputs.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MAX_DN18B20_SENSORS 4U #define MAX_DN18B20_SENSORS 4U
#define PERIODIC_INTERVAL 1U // read and compute the inputs every 1sec #define PERIODIC_INTERVAL 1U // read and compute the inputs every 1sec
#define AVG10_SAMPLE_SIZE 10U
#define AVG60_SAMPLE_SIZE 60U
typedef struct _Average
{
float value;
float samples[MAX(AVG10_SAMPLE_SIZE, AVG60_SAMPLE_SIZE)];
size_t bufferIndex;
size_t bufferCount;
} sAverage;
typedef struct _Measurement
{
float value;
sAverage average10s;
sAverage average60s;
} sMeasurement;
static const char *TAG = "smart-oil-heater-control-system-inputs"; static const char *TAG = "smart-oil-heater-control-system-inputs";
const uint8_t uBurnerFaultPin = 19U; const uint8_t uBurnerFaultPin = 19U;
@ -43,10 +24,10 @@ size_t sSensorCount = 0U;
static SemaphoreHandle_t xMutexAccessInputs = NULL; static SemaphoreHandle_t xMutexAccessInputs = NULL;
static eBurnerErrorState sBurnerErrorState; static eBurnerErrorState sBurnerErrorState;
static sMeasurement fChamperTemperature; static sMeasurement sChamperTemperature;
static sMeasurement fOutdoorTemperature; static sMeasurement sOutdoorTemperature;
static sMeasurement fInletFlowTemperature; static sMeasurement sInletFlowTemperature;
static sMeasurement fReturnFlowTemperature; static sMeasurement sReturnFlowTemperature;
void taskInput(void *pvParameters); void taskInput(void *pvParameters);
void updateAverage(sMeasurement *pMeasurement); void updateAverage(sMeasurement *pMeasurement);
@ -92,7 +73,7 @@ void initInputs(void)
void updateAverage(sMeasurement *pMeasurement) void updateAverage(sMeasurement *pMeasurement)
{ /* Average form the last 10sec */ { /* Average form the last 10sec */
pMeasurement->average10s.samples[pMeasurement->average10s.bufferIndex] = pMeasurement->value; pMeasurement->average10s.samples[pMeasurement->average10s.bufferIndex] = pMeasurement->fCurrentValue;
pMeasurement->average10s.bufferIndex = (pMeasurement->average10s.bufferIndex + 1) % AVG10_SAMPLE_SIZE; pMeasurement->average10s.bufferIndex = (pMeasurement->average10s.bufferIndex + 1) % AVG10_SAMPLE_SIZE;
if (pMeasurement->average10s.bufferCount < AVG10_SAMPLE_SIZE) if (pMeasurement->average10s.bufferCount < AVG10_SAMPLE_SIZE)
@ -102,7 +83,7 @@ void updateAverage(sMeasurement *pMeasurement)
if (pMeasurement->average10s.bufferCount == 0U) if (pMeasurement->average10s.bufferCount == 0U)
{ {
pMeasurement->average10s.value = pMeasurement->value; pMeasurement->average10s.fValue = pMeasurement->fCurrentValue;
} }
float sum = 0.0; float sum = 0.0;
@ -111,10 +92,10 @@ void updateAverage(sMeasurement *pMeasurement)
sum += pMeasurement->average10s.samples[i]; sum += pMeasurement->average10s.samples[i];
} }
pMeasurement->average10s.value = sum / pMeasurement->average10s.bufferCount; pMeasurement->average10s.fValue = sum / pMeasurement->average10s.bufferCount;
/* Average form the last 60sec */ /* Average form the last 60sec */
pMeasurement->average60s.samples[pMeasurement->average60s.bufferIndex] = pMeasurement->value; pMeasurement->average60s.samples[pMeasurement->average60s.bufferIndex] = pMeasurement->fCurrentValue;
pMeasurement->average60s.bufferIndex = (pMeasurement->average60s.bufferIndex + 1) % AVG60_SAMPLE_SIZE; pMeasurement->average60s.bufferIndex = (pMeasurement->average60s.bufferIndex + 1) % AVG60_SAMPLE_SIZE;
if (pMeasurement->average60s.bufferCount < AVG60_SAMPLE_SIZE) if (pMeasurement->average60s.bufferCount < AVG60_SAMPLE_SIZE)
@ -124,7 +105,7 @@ void updateAverage(sMeasurement *pMeasurement)
if (pMeasurement->average60s.bufferCount == 0U) if (pMeasurement->average60s.bufferCount == 0U)
{ {
pMeasurement->average60s.value = pMeasurement->value; pMeasurement->average60s.fValue = pMeasurement->fCurrentValue;
} }
sum = 0.0; sum = 0.0;
@ -133,7 +114,7 @@ void updateAverage(sMeasurement *pMeasurement)
sum += pMeasurement->average60s.samples[i]; sum += pMeasurement->average60s.samples[i];
} }
pMeasurement->average60s.value = sum / pMeasurement->average60s.bufferCount; pMeasurement->average60s.fValue = sum / pMeasurement->average60s.bufferCount;
} }
void taskInput(void *pvParameters) void taskInput(void *pvParameters)
@ -141,168 +122,133 @@ void taskInput(void *pvParameters)
while (1) while (1)
{ {
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS);
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
sChamperTemperature.state = MEASUREMENT_FAULT;
sOutdoorTemperature.state = MEASUREMENT_FAULT;
sInletFlowTemperature.state = MEASUREMENT_FAULT;
sReturnFlowTemperature.state = MEASUREMENT_FAULT;
if (gpio_get_level(uBurnerFaultPin) == 1) if (gpio_get_level(uBurnerFaultPin) == 1)
{
sBurnerErrorState = FAULT;
}
else
{
sBurnerErrorState = NO_ERROR;
}
if (ds18x20_scan_devices(uDS18B20Pin, uOneWireAddresses, MAX_DN18B20_SENSORS, &sSensorCount) != ESP_OK)
{
ESP_LOGE(TAG, "1-Wire device scan error!");
}
if (!sSensorCount)
{
ESP_LOGW(TAG, "No 1-Wire devices detected!");
}
else
{
ESP_LOGI(TAG, "%d 1-Wire devices detected", sSensorCount);
if (sSensorCount > MAX_DN18B20_SENSORS)
{ {
sSensorCount = MAX_DN18B20_SENSORS; sBurnerErrorState = FAULT;
ESP_LOGW(TAG, "More 1-Wire devices found than expected!");
}
if (ds18x20_measure_and_read_multi(uDS18B20Pin, uOneWireAddresses, sSensorCount, fDS18B20Temps) != ESP_OK)
{
ESP_LOGE(TAG, "1-Wire devices read error");
} }
else else
{ {
for (int j = 0; j < sSensorCount; j++) sBurnerErrorState = NO_ERROR;
}
if (ds18x20_scan_devices(uDS18B20Pin, uOneWireAddresses, MAX_DN18B20_SENSORS, &sSensorCount) != ESP_OK)
{
ESP_LOGE(TAG, "1-Wire device scan error!");
}
if (!sSensorCount)
{
ESP_LOGW(TAG, "No 1-Wire devices detected!");
}
else
{
ESP_LOGI(TAG, "%d 1-Wire devices detected", sSensorCount);
if (sSensorCount > MAX_DN18B20_SENSORS)
{ {
float temp_c = fDS18B20Temps[j]; sSensorCount = MAX_DN18B20_SENSORS;
ESP_LOGI(TAG, "Sensor: %08" PRIx64 " reports %lf°C", (uint64_t)uOneWireAddresses[j], temp_c); ESP_LOGW(TAG, "More 1-Wire devices found than expected!");
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE) }
if (ds18x20_measure_and_read_multi(uDS18B20Pin, uOneWireAddresses, sSensorCount, fDS18B20Temps) != ESP_OK)
{
ESP_LOGE(TAG, "1-Wire devices read error");
}
else
{
for (int j = 0; j < sSensorCount; j++)
{ {
float temp_c = fDS18B20Temps[j];
ESP_LOGI(TAG, "Sensor: %08" PRIx64 " reports %lf°C", (uint64_t)uOneWireAddresses[j], temp_c);
switch ((uint64_t)uOneWireAddresses[j]) switch ((uint64_t)uOneWireAddresses[j])
{ {
case ((uint64_t)uChamperTempSensorAddr): case ((uint64_t)uChamperTempSensorAddr):
fChamperTemperature.value = temp_c; sChamperTemperature.fCurrentValue = temp_c;
updateAverage(&fChamperTemperature); sChamperTemperature.state = MEASUREMENT_NO_ERROR;
updateAverage(&sChamperTemperature);
break; break;
case ((uint64_t)uOutdoorTempSensorAddr): case ((uint64_t)uOutdoorTempSensorAddr):
fOutdoorTemperature.value = temp_c; sOutdoorTemperature.fCurrentValue = temp_c;
updateAverage(&fOutdoorTemperature); sOutdoorTemperature.state = MEASUREMENT_NO_ERROR;
updateAverage(&sOutdoorTemperature);
break; break;
case ((uint64_t)uInletFlowTempSensorAddr): case ((uint64_t)uInletFlowTempSensorAddr):
fInletFlowTemperature.value = temp_c; sInletFlowTemperature.fCurrentValue = temp_c;
updateAverage(&fInletFlowTemperature); sInletFlowTemperature.state = MEASUREMENT_NO_ERROR;
updateAverage(&sInletFlowTemperature);
break; break;
case ((uint64_t)uReturnFlowTempSensorAddr): case ((uint64_t)uReturnFlowTempSensorAddr):
fReturnFlowTemperature.value = temp_c; sReturnFlowTemperature.fCurrentValue = temp_c;
updateAverage(&fReturnFlowTemperature); sReturnFlowTemperature.state = MEASUREMENT_NO_ERROR;
updateAverage(&sReturnFlowTemperature);
break; break;
default: default:
break; break;
} }
xSemaphoreGive(xMutexAccessInputs);
} }
} }
} }
xSemaphoreGive(xMutexAccessInputs);
} }
} }
} }
float getChamberTemperature(eMeasurementMode mode) sMeasurement getChamberTemperature(void)
{ {
float ret = 0.0f; sMeasurement ret;
ret.state = MEASUREMENT_FAULT;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{ {
switch (mode) ret = sChamperTemperature;
{
case CURRENT:
ret = fChamperTemperature.value;
break;
case AVERAGE_10S:
ret = fChamperTemperature.average10s.value;
break;
case AVERAGE_60S:
ret = fChamperTemperature.average60s.value;
break;
default:
break;
}
xSemaphoreGive(xMutexAccessInputs); xSemaphoreGive(xMutexAccessInputs);
} }
return ret; return ret;
} }
float getOutdoorTemperature(eMeasurementMode mode)
sMeasurement getOutdoorTemperature(void)
{ {
float ret = 0.0f; sMeasurement ret;
ret.state = MEASUREMENT_FAULT;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{ {
switch (mode) ret = sOutdoorTemperature;
{
case CURRENT:
ret = fOutdoorTemperature.value;
break;
case AVERAGE_10S:
ret = fOutdoorTemperature.average10s.value;
break;
case AVERAGE_60S:
ret = fOutdoorTemperature.average60s.value;
break;
default:
break;
}
xSemaphoreGive(xMutexAccessInputs); xSemaphoreGive(xMutexAccessInputs);
} }
return ret; return ret;
} }
float getInletFlowTemperature(eMeasurementMode mode)
sMeasurement getInletFlowTemperature(void)
{ {
float ret = 0.0f; sMeasurement ret;
ret.state = MEASUREMENT_FAULT;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{ {
switch (mode) ret = sInletFlowTemperature;
{
case CURRENT:
ret = fInletFlowTemperature.value;
break;
case AVERAGE_10S:
ret = fInletFlowTemperature.average10s.value;
break;
case AVERAGE_60S:
ret = fInletFlowTemperature.average60s.value;
break;
default:
break;
}
xSemaphoreGive(xMutexAccessInputs); xSemaphoreGive(xMutexAccessInputs);
} }
return ret; return ret;
} }
float getReturnFlowTemperature(eMeasurementMode mode)
sMeasurement getReturnFlowTemperature(void)
{ {
float ret = 0.0f; sMeasurement ret;
ret.state = MEASUREMENT_FAULT;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE) if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{ {
switch (mode) ret = sReturnFlowTemperature;
{
case CURRENT:
ret = fReturnFlowTemperature.value;
break;
case AVERAGE_10S:
ret = fReturnFlowTemperature.average10s.value;
break;
case AVERAGE_60S:
ret = fReturnFlowTemperature.average60s.value;
break;
default:
break;
}
xSemaphoreGive(xMutexAccessInputs); xSemaphoreGive(xMutexAccessInputs);
} }
return ret; return ret;
} }
eBurnerErrorState getBurnerError(void) eBurnerErrorState getBurnerError(void)
{ {
eBurnerErrorState ret = FAULT; eBurnerErrorState ret = FAULT;

View File

@ -1,21 +1,40 @@
#pragma once #pragma once
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define AVG10_SAMPLE_SIZE 10U
#define AVG60_SAMPLE_SIZE 60U
typedef enum _BurnerErrorState typedef enum _BurnerErrorState
{ {
NO_ERROR, NO_ERROR,
FAULT FAULT
} eBurnerErrorState; } eBurnerErrorState;
typedef enum _MeasurementMode typedef enum _MeasurementErrorState
{ {
CURRENT, MEASUREMENT_NO_ERROR,
AVERAGE_10S, MEASUREMENT_FAULT
AVERAGE_60S } eMeasurementErrorState;
} eMeasurementMode;
typedef struct _Average
{
float fValue;
float samples[MAX(AVG10_SAMPLE_SIZE, AVG60_SAMPLE_SIZE)];
size_t bufferIndex;
size_t bufferCount;
} sAverage;
typedef struct _Measurement
{
float fCurrentValue;
sAverage average10s;
sAverage average60s;
eMeasurementErrorState state;
} sMeasurement;
void initInputs(void); void initInputs(void);
float getChamberTemperature(eMeasurementMode mode); sMeasurement getChamberTemperature(void);
float getOutdoorTemperature(eMeasurementMode mode); sMeasurement getOutdoorTemperature(void);
float getInletFlowTemperature(eMeasurementMode mode); sMeasurement getInletFlowTemperature(void);
float getReturnFlowTemperature(eMeasurementMode mode); sMeasurement getReturnFlowTemperature(void);
eBurnerErrorState getBurnerError(void); eBurnerErrorState getBurnerError(void);

View File

@ -19,6 +19,7 @@
#include "metrics.h" #include "metrics.h"
#include "outputs.h" #include "outputs.h"
#include "inputs.h" #include "inputs.h"
#include "safety.h"
static EventGroupHandle_t s_wifi_event_group; static EventGroupHandle_t s_wifi_event_group;
@ -42,7 +43,7 @@ void initMetrics(void)
BaseType_t taskCreated = xTaskCreate( BaseType_t taskCreated = xTaskCreate(
taskMetrics, // Function to implement the task taskMetrics, // Function to implement the task
"taskMetrics", // Task name "taskMetrics", // Task name
4096, // Stack size (in words, not bytes) 16384, // Stack size (in words, not bytes)
NULL, // Parameters to the task function (none in this case) NULL, // Parameters to the task function (none in this case)
5, // Task priority (higher number = higher priority) 5, // Task priority (higher number = higher priority)
NULL // Task handle (optional) NULL // Task handle (optional)
@ -123,62 +124,78 @@ void taskMetrics(void *pvParameters)
/*Chamber Temperature*/ /*Chamber Temperature*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature"); strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature");
aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature(CURRENT); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().fCurrentValue;
u16MetricCounter++; u16MetricCounter++;
/*Outdoor Temperature*/ /*Outdoor Temperature*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature"); strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature");
aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature(CURRENT); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().fCurrentValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature*/ /*Chamber Temperature*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature"); strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature");
aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature(CURRENT); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().fCurrentValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature*/ /*Chamber Temperature*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature"); strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature");
aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature(CURRENT); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().fCurrentValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 10s*/ /*Chamber Temperature Average 10s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg10"); strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg10");
aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature(AVERAGE_10S); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().average10s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Outdoor Temperature Average 10s*/ /*Outdoor Temperature Average 10s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg10"); strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg10");
aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature(AVERAGE_10S); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().average10s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 10s*/ /*Chamber Temperature Average 10s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg10"); strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg10");
aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature(AVERAGE_10S); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().average10s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 10s*/ /*Chamber Temperature Average 10s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg10"); strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg10");
aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature(AVERAGE_10S); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average10s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 60s*/ /*Chamber Temperature Average 60s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg60"); strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg60");
aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature(AVERAGE_60S); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().average60s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Outdoor Temperature Average 60s*/ /*Outdoor Temperature Average 60s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg60"); strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg60");
aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature(AVERAGE_60S); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().average60s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 60s*/ /*Chamber Temperature Average 60s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg60"); strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg60");
aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature(AVERAGE_60S); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().average60s.fValue;
u16MetricCounter++; u16MetricCounter++;
/*Chamber Temperature Average 60s*/ /*Chamber Temperature Average 60s*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg60"); strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg60");
aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature(AVERAGE_60S); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average60s.fValue;
u16MetricCounter++;
/*Sensor status*/
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");
aMetrics[u16MetricCounter].fMetricValue = aChecks[i].status;
u16MetricCounter++;
}
/*Safety state*/
strcpy(aMetrics[u16MetricCounter].caMetricName, "safety_state");
aMetrics[u16MetricCounter].fMetricValue = getSafetyState();
u16MetricCounter++; u16MetricCounter++;
vSetMetrics(aMetrics, u16MetricCounter); vSetMetrics(aMetrics, u16MetricCounter);

View File

@ -1,6 +1,7 @@
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "esp_log.h" #include "esp_log.h"
#include <string.h>
#include "safety.h" #include "safety.h"
#define PERIODIC_INTERVAL 1U // run safety checks every 1sec #define PERIODIC_INTERVAL 1U // run safety checks every 1sec
@ -8,11 +9,12 @@
static const char *TAG = "smart-oil-heater-control-system-safety"; static const char *TAG = "smart-oil-heater-control-system-safety";
static SemaphoreHandle_t xMutexAccessSafety = NULL; static SemaphoreHandle_t xMutexAccessSafety = NULL;
sSensorSanityCheck sanityChecks[NUMBER_OF_SENSOR_SANITY_CHECKS] = { static sSensorSanityCheck sanityChecks[NUMBER_OF_SENSOR_SANITY_CHECKS] = {
{0U, "chamber_temperature", {95.0f, -10.0f}, 0.0f, 0U, getChamberTemperature}, {SENSOR_NO_ERROR, "chamber_temperature", {95.0f, -10.0f}, 0.0f, 0U, getChamberTemperature},
{0U, "outdoor_temperature", {45.0f, -20.0f}, 0.0f, 0U, getOutdoorTemperature}, {SENSOR_NO_ERROR, "outdoor_temperature", {45.0f, -20.0f}, 0.0f, 0U, getOutdoorTemperature},
{0U, "inlet_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getInletFlowTemperature}, {SENSOR_NO_ERROR, "inlet_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getInletFlowTemperature},
{0U, "return_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getReturnFlowTemperature}}; {SENSOR_NO_ERROR, "return_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getReturnFlowTemperature}};
static eSafetyState sSafetyState = SAFETY_NO_ERROR;
void taskSafety(void *pvParameters); void taskSafety(void *pvParameters);
void checkSensorSanity(void); void checkSensorSanity(void);
@ -30,7 +32,7 @@ void initSafety(void)
BaseType_t taskCreated = xTaskCreate( BaseType_t taskCreated = xTaskCreate(
taskSafety, // Function to implement the task taskSafety, // Function to implement the task
"taskSafety", // Task name "taskSafety", // Task name
2048, // Stack size (in words, not bytes) 4096, // Stack size (in words, not bytes)
NULL, // Parameters to the task function (none in this case) NULL, // Parameters to the task function (none in this case)
5, // Task priority (higher number = higher priority) 5, // Task priority (higher number = higher priority)
NULL // Task handle (optional) NULL // Task handle (optional)
@ -52,13 +54,18 @@ void taskSafety(void *pvParameters)
{ {
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS);
checkSensorSanity(); if (xSemaphoreTake(xMutexAccessSafety, portMAX_DELAY) == pdTRUE)
{
checkSensorSanity();
xSemaphoreGive(xMutexAccessSafety);
}
} }
} }
void checkSensorSanity(void) void checkSensorSanity(void)
{ {
sSafetyState = SAFETY_NO_ERROR;
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);
@ -66,34 +73,47 @@ void checkSensorSanity(void)
// 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);
const float fSensorTemperatureCurrent = sanityChecks[i].getSensor(CURRENT); const sMeasurement sCurrentMeasurement = sanityChecks[i].getSensor();
if (fSensorTemperatureCurrent == sanityChecks[i].fSensorTemperatureLast)
if (sCurrentMeasurement.state == MEASUREMENT_FAULT)
{ {
sanityChecks[i].uUnchangedCounter++; ESP_LOGE(TAG, "%s Sensor not found!", sanityChecks[i].name);
if (sanityChecks[i].uUnchangedCounter >= (SENSOR_GRACE_PERIOD / PERIODIC_INTERVAL)) sanityChecks[i].status = SENSOR_NOT_FOUND;
{ sSafetyState = SAFETY_SENSOR_ERROR;
ESP_LOGE(TAG, "%s Sensor reported unchanged value! %lf == %lf", sanityChecks[i].name, fSensorTemperatureCurrent, sanityChecks[i].fSensorTemperatureLast);
sanityChecks[i].status = 1U;
}
} }
else else
{ {
sanityChecks[i].fSensorTemperatureLast = fSensorTemperatureCurrent; if (sCurrentMeasurement.fCurrentValue == sanityChecks[i].fSensorTemperatureLast)
if (fSensorTemperatureCurrent > sanityChecks[i].sSensorLimit.max)
{ {
ESP_LOGE(TAG, "%s Sensor reported too high value! %lf > %lf", sanityChecks[i].name, fSensorTemperatureCurrent, sanityChecks[i].sSensorLimit.max); sanityChecks[i].uUnchangedCounter++;
sanityChecks[i].status = 1U; if (sanityChecks[i].uUnchangedCounter >= (SENSOR_GRACE_PERIOD / PERIODIC_INTERVAL))
} {
else if (fSensorTemperatureCurrent < sanityChecks[i].sSensorLimit.min) ESP_LOGE(TAG, "%s Sensor reported unchanged value! %lf == %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].fSensorTemperatureLast);
{ sanityChecks[i].status = SENSOR_UNCHANGED;
ESP_LOGE(TAG, "%s Sensor reported too low value! %lf < %lf", sanityChecks[i].name, fSensorTemperatureCurrent, sanityChecks[i].sSensorLimit.min); sSafetyState = SAFETY_SENSOR_ERROR;
sanityChecks[i].status = 1U; }
} }
else else
{ {
sanityChecks[i].uUnchangedCounter = 0U; sanityChecks[i].fSensorTemperatureLast = sCurrentMeasurement.fCurrentValue;
sanityChecks[i].status = 0U;
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;
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;
sSafetyState = SAFETY_SENSOR_ERROR;
}
else
{
sanityChecks[i].uUnchangedCounter = 0U;
sanityChecks[i].status = SENSOR_NO_ERROR;
}
} }
} }
// printf(" Status: %u\n", sanityChecks[i].status); // printf(" Status: %u\n", sanityChecks[i].status);
@ -106,9 +126,27 @@ void setSafeState(void)
setBurnerState(DISABLED); setBurnerState(DISABLED);
} }
/* void getSensorSanityStates(sSensorSanityCheck *pSensorSanityChecks)
sSafetyStateElement *getSafetyStates(void)
{ {
return safetyStates; if (xSemaphoreTake(xMutexAccessSafety, portMAX_DELAY) == pdTRUE)
{
for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++)
{
// Copy only the needed attributes
pSensorSanityChecks[i].status = sanityChecks[i].status;
strcpy(pSensorSanityChecks[i].name, sanityChecks[i].name);
}
xSemaphoreGive(xMutexAccessSafety);
}
} }
*/
eSafetyState getSafetyState(void)
{
eSafetyState state = SAFETY_NO_ERROR;
if (xSemaphoreTake(xMutexAccessSafety, portMAX_DELAY) == pdTRUE)
{
state = sSafetyState;
xSemaphoreGive(xMutexAccessSafety);
}
return state;
}

View File

@ -6,7 +6,22 @@
#define MAX_ERROR_STRING_SIZE 64U #define MAX_ERROR_STRING_SIZE 64U
#define NUMBER_OF_SENSOR_SANITY_CHECKS 4U #define NUMBER_OF_SENSOR_SANITY_CHECKS 4U
typedef float (*GetSensorValue)(eMeasurementMode); typedef enum _SensorErrorState
{
SENSOR_NO_ERROR,
SENSOR_TOO_HIGH,
SENSOR_TOO_LOW,
SENSOR_UNCHANGED,
SENSOR_NOT_FOUND
} eSensorErrorState;
typedef enum _SafetyState
{
SAFETY_NO_ERROR,
SAFETY_SENSOR_ERROR,
} eSafetyState;
typedef sMeasurement (*GetSensorValue)();
typedef struct _TemperatureSensorLimit typedef struct _TemperatureSensorLimit
{ {
float max; // Maximum temperature limit float max; // Maximum temperature limit
@ -14,7 +29,7 @@ typedef struct _TemperatureSensorLimit
} sTemperatureSensorLimit; } sTemperatureSensorLimit;
typedef struct _SensorSanityCheck typedef struct _SensorSanityCheck
{ {
uint8_t status; eSensorErrorState status;
char name[MAX_ERROR_STRING_SIZE]; char name[MAX_ERROR_STRING_SIZE];
sTemperatureSensorLimit sSensorLimit; sTemperatureSensorLimit sSensorLimit;
float fSensorTemperatureLast; float fSensorTemperatureLast;
@ -23,5 +38,5 @@ typedef struct _SensorSanityCheck
} sSensorSanityCheck; } sSensorSanityCheck;
void initSafety(void); void initSafety(void);
void getSensorSanityStates(sSensorSanityCheck *pSensorSanityChecks);
//sSensorSanityCheck *getSafetyStates(void); eSafetyState getSafetyState(void);