error handling and cleanup

This commit is contained in:
2026-01-10 13:32:49 +01:00
parent f8f6af53bd
commit 1d4e272d80
15 changed files with 867 additions and 309 deletions

View File

@ -1,3 +1,8 @@
/**
* @file inputs.c
* @brief Implementation of input handling module.
*/
#include "inputs.h"
#include "freertos/FreeRTOS.h"
@ -9,22 +14,38 @@
#include <string.h>
#include <math.h>
/** @brief Maximum number of DS18B20 sensors supported. */
#define MAX_DN18B20_SENSORS 4U
#define ONE_WIRE_LOOPS 4U // try to read the 1-Wire sensors that often
#define PERIODIC_INTERVAL 1U // read and compute the inputs every 1sec
static const char *TAG = "smart-oil-heater-control-system-inputs";
const uint8_t uBurnerFaultPin = CONFIG_GPIO_BURNER_FAULT;
const uint8_t uDS18B20Pin = CONFIG_GPIO_DS18B20_ONEWIRE;
/** @brief Number of retry attempts for 1-Wire read. */
#define ONE_WIRE_LOOPS 4U
const onewire_addr_t uChamperTempSensorAddr = CONFIG_ONEWIRE_ADDR_CHAMBER_TEMP;
const onewire_addr_t uOutdoorTempSensorAddr = CONFIG_ONEWIRE_ADDR_OUTDOOR_TEMP;
const onewire_addr_t uInletFlowTempSensorAddr = CONFIG_ONEWIRE_ADDR_INLET_FLOW_TEMP;
const onewire_addr_t uReturnFlowTempSensorAddr = CONFIG_ONEWIRE_ADDR_RETURN_FLOW_TEMP;
/** @brief Task interval in seconds. */
#define PERIODIC_INTERVAL 1U
onewire_addr_t uOneWireAddresses[MAX_DN18B20_SENSORS];
float fDS18B20Temps[MAX_DN18B20_SENSORS];
size_t sSensorCount = 0U;
static const char *TAG = "inputs";
/** @brief Burner fault GPIO pin (from Kconfig). */
static const uint8_t uBurnerFaultPin = CONFIG_GPIO_BURNER_FAULT;
/** @brief DS18B20 1-Wire GPIO pin (from Kconfig). */
static const uint8_t uDS18B20Pin = CONFIG_GPIO_DS18B20_ONEWIRE;
/** @brief Chamber temperature sensor address (from Kconfig). */
static const onewire_addr_t uChamperTempSensorAddr = CONFIG_ONEWIRE_ADDR_CHAMBER_TEMP;
/** @brief Outdoor temperature sensor address (from Kconfig). */
static const onewire_addr_t uOutdoorTempSensorAddr = CONFIG_ONEWIRE_ADDR_OUTDOOR_TEMP;
/** @brief Inlet flow temperature sensor address (from Kconfig). */
static const onewire_addr_t uInletFlowTempSensorAddr = CONFIG_ONEWIRE_ADDR_INLET_FLOW_TEMP;
/** @brief Return flow temperature sensor address (from Kconfig). */
static const onewire_addr_t uReturnFlowTempSensorAddr = CONFIG_ONEWIRE_ADDR_RETURN_FLOW_TEMP;
static onewire_addr_t uOneWireAddresses[MAX_DN18B20_SENSORS];
static float fDS18B20Temps[MAX_DN18B20_SENSORS];
static size_t sSensorCount = 0U;
static SemaphoreHandle_t xMutexAccessInputs = NULL;
static eBurnerErrorState sBurnerErrorState;
@ -33,34 +54,34 @@ static sMeasurement sOutdoorTemperature;
static sMeasurement sInletFlowTemperature;
static sMeasurement sReturnFlowTemperature;
void taskInput(void *pvParameters);
void initMeasurement(sMeasurement *pMeasurement);
void updateAverage(sMeasurement *pMeasurement);
void updatePrediction(sMeasurement *pMeasurement);
float linearRegressionPredict(const float *samples, size_t count, size_t bufferIndex, float futureIndex);
/* Private function prototypes */
static void taskInput(void *pvParameters);
static void initMeasurement(sMeasurement *pMeasurement);
static void updateAverage(sMeasurement *pMeasurement);
static void updatePrediction(sMeasurement *pMeasurement);
static float linearRegressionPredict(const float *samples, size_t count, size_t bufferIndex, float futureIndex);
void initInputs(void)
esp_err_t initInputs(void)
{
gpio_config_t ioConfBurnerFault = {
.pin_bit_mask = (1ULL << uBurnerFaultPin), // Pin mask
.mode = GPIO_MODE_INPUT, // Set as inout
.pull_up_en = GPIO_PULLUP_ENABLE, // Enable pull-up
.pull_down_en = GPIO_PULLDOWN_DISABLE, // Disable pull-down
.intr_type = GPIO_INTR_DISABLE // Disable interrupts
};
.pin_bit_mask = (1ULL << uBurnerFaultPin),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE};
esp_err_t ret = gpio_config(&ioConfBurnerFault);
if (ret != ESP_OK)
{
ESP_LOGE(TAG, "GPIO config failed: %s", esp_err_to_name(ret));
return;
return ESP_FAIL;
}
xMutexAccessInputs = xSemaphoreCreateRecursiveMutex();
if (xMutexAccessInputs == NULL)
{
ESP_LOGE(TAG, "Unable to create mutex");
ESP_LOGE(TAG, "Failed to create mutex");
return ESP_FAIL;
}
xSemaphoreGiveRecursive(xMutexAccessInputs);
@ -70,25 +91,28 @@ void initInputs(void)
initMeasurement(&sReturnFlowTemperature);
BaseType_t taskCreated = xTaskCreate(
taskInput, // Function to implement the task
"taskInput", // Task name
4096, // Stack size (in words, not bytes)
NULL, // Parameters to the task function (none in this case)
5, // Task priority (higher number = higher priority)
NULL // Task handle (optional)
);
taskInput,
"taskInput",
4096,
NULL,
5,
NULL);
if (taskCreated == pdPASS)
{
ESP_LOGI(TAG, "Task created successfully!");
}
else
if (taskCreated != pdPASS)
{
ESP_LOGE(TAG, "Failed to create task");
return ESP_FAIL;
}
ESP_LOGI(TAG, "Initialized successfully");
return ESP_OK;
}
void initMeasurement(sMeasurement *pMeasurement)
/**
* @brief Initialize a measurement structure to default values.
* @param pMeasurement Pointer to measurement structure.
*/
static void initMeasurement(sMeasurement *pMeasurement)
{
if (!pMeasurement)
return;
@ -113,12 +137,16 @@ void initMeasurement(sMeasurement *pMeasurement)
memset(pMeasurement->predict60s.samples, 0U, sizeof(float) * PRED60S_SAMPLE_SIZE);
}
void updateAverage(sMeasurement *pMeasurement)
/**
* @brief Update average values and damped value for a measurement.
* @param pMeasurement Pointer to measurement structure.
*/
static void updateAverage(sMeasurement *pMeasurement)
{
if (!pMeasurement)
return;
// Average form the last 10sec
/* 10-second average */
pMeasurement->average10s.samples[pMeasurement->average10s.bufferIndex] = pMeasurement->fCurrentValue;
pMeasurement->average10s.bufferIndex = (pMeasurement->average10s.bufferIndex + 1) % AVG10S_SAMPLE_SIZE;
@ -142,7 +170,7 @@ void updateAverage(sMeasurement *pMeasurement)
pMeasurement->average10s.fValue = sum / pMeasurement->average10s.bufferCount;
}
// Average form the last 60sec
/* 60-second average */
pMeasurement->average60s.samples[pMeasurement->average60s.bufferIndex] = pMeasurement->fCurrentValue;
pMeasurement->average60s.bufferIndex = (pMeasurement->average60s.bufferIndex + 1) % AVG60S_SAMPLE_SIZE;
@ -166,7 +194,7 @@ void updateAverage(sMeasurement *pMeasurement)
pMeasurement->average60s.fValue = sum / pMeasurement->average60s.bufferCount;
}
// Damped current value
/* Damped current value */
if (pMeasurement->fDampedValue == INITIALISATION_VALUE)
{
pMeasurement->fDampedValue = pMeasurement->fCurrentValue;
@ -185,7 +213,11 @@ void updateAverage(sMeasurement *pMeasurement)
}
}
void updatePrediction(sMeasurement *pMeasurement)
/**
* @brief Update 60-second prediction using linear regression.
* @param pMeasurement Pointer to measurement structure.
*/
static void updatePrediction(sMeasurement *pMeasurement)
{
if (!pMeasurement)
return;
@ -205,7 +237,11 @@ void updatePrediction(sMeasurement *pMeasurement)
predict60s->bufferCount + 60.0f);
}
void taskInput(void *pvParameters)
/**
* @brief Input task - reads sensors periodically.
* @param pvParameters Task parameters (unused).
*/
static void taskInput(void *pvParameters)
{
while (1)
{
@ -307,20 +343,27 @@ void taskInput(void *pvParameters)
}
}
float linearRegressionPredict(const float *samples, size_t count, size_t bufferIndex, float futureIndex)
/**
* @brief Predict future value using linear regression.
* @param samples Sample buffer.
* @param count Number of valid samples.
* @param bufferIndex Current buffer write index.
* @param futureIndex Future time index to predict.
* @return Predicted value.
*/
static float linearRegressionPredict(const float *samples, size_t count, size_t bufferIndex, float futureIndex)
{
if (count == 0)
return INITIALISATION_VALUE; // No prediction possible with no data
return INITIALISATION_VALUE;
float sumX = INITIALISATION_VALUE, sumY = INITIALISATION_VALUE, sumXY = INITIALISATION_VALUE, sumX2 = INITIALISATION_VALUE;
for (size_t i = 0; i < count; i++)
{
// Calculate the circular buffer index for the current sample
size_t circularIndex = (bufferIndex + i + 1) % count;
float x = (float)i; // Time index
float y = samples[circularIndex]; // Sample value
float x = (float)i;
float y = samples[circularIndex];
sumX += x;
sumY += y;
@ -328,15 +371,13 @@ float linearRegressionPredict(const float *samples, size_t count, size_t bufferI
sumX2 += x * x;
}
// Calculate slope (m) and intercept (b) of the line: y = mx + b
float denominator = (count * sumX2 - sumX * sumX);
if (fabs(denominator) < 1e-6) // Avoid division by zero
return samples[bufferIndex]; // Return the latest value as prediction
if (fabs(denominator) < 1e-6)
return samples[bufferIndex];
float m = (count * sumXY - sumX * sumY) / denominator;
float b = (sumY - m * sumX) / count;
// Predict value at futureIndex
return m * futureIndex + b;
}