safety state and input error detection
This commit is contained in:
parent
a7f6973efd
commit
8205253b5a
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@ -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"
|
||||||
|
218
main/inputs.c
218
main/inputs.c
@ -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;
|
||||||
|
@ -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);
|
@ -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);
|
||||||
|
102
main/safety.c
102
main/safety.c
@ -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;
|
||||||
|
}
|
@ -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);
|
Loading…
Reference in New Issue
Block a user