error handling and cleanup
This commit is contained in:
151
main/inputs.c
151
main/inputs.c
@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user