smart-oil-heating-control-s.../main/inputs.c

315 lines
9.4 KiB
C

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/gpio.h"
#include "esp_log.h"
#include <ds18x20.h>
#include "inputs.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MAX_DN18B20_SENSORS 4U
#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";
const uint8_t uBurnerFaultPin = 19U;
const uint8_t uDS18B20Pin = 4U;
const onewire_addr_t uChamperTempSensorAddr = 0x3e0000001754be28;
const onewire_addr_t uOutdoorTempSensorAddr = 0x880000001648e328;
const onewire_addr_t uInletFlowTempSensorAddr = 0xe59cdef51e64ff28;
const onewire_addr_t uReturnFlowTempSensorAddr = 0xa7a8e1531f64ff28;
onewire_addr_t uOneWireAddresses[MAX_DN18B20_SENSORS];
float fDS18B20Temps[MAX_DN18B20_SENSORS];
size_t sSensorCount = 0U;
static SemaphoreHandle_t xMutexAccessInputs = NULL;
static eBurnerErrorState sBurnerErrorState;
static sMeasurement fChamperTemperature;
static sMeasurement fOutdoorTemperature;
static sMeasurement fInletFlowTemperature;
static sMeasurement fReturnFlowTemperature;
void taskInput(void *pvParameters);
void updateAverage(sMeasurement *pMeasurement);
void 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
};
gpio_config(&ioConfBurnerFault);
xMutexAccessInputs = xSemaphoreCreateBinary();
if (xMutexAccessInputs == NULL)
{
ESP_LOGE(TAG, "Unable to create mutex");
}
xSemaphoreGive(xMutexAccessInputs);
BaseType_t taskCreated = xTaskCreate(
taskInput, // Function to implement the task
"taskInput", // Task name
2048, // 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)
);
if (taskCreated == pdPASS)
{
ESP_LOGI(TAG, "Task created successfully!");
}
else
{
ESP_LOGE(TAG, "Failed to create task");
}
}
void updateAverage(sMeasurement *pMeasurement)
{ /* Average form the last 10sec */
pMeasurement->average10s.samples[pMeasurement->average10s.bufferIndex] = pMeasurement->value;
pMeasurement->average10s.bufferIndex = (pMeasurement->average10s.bufferIndex + 1) % AVG10_SAMPLE_SIZE;
if (pMeasurement->average10s.bufferCount < AVG10_SAMPLE_SIZE)
{
pMeasurement->average10s.bufferCount++;
}
if (pMeasurement->average10s.bufferCount == 0U)
{
pMeasurement->average10s.value = pMeasurement->value;
}
float sum = 0.0;
for (int i = 0; i < pMeasurement->average10s.bufferCount; i++)
{
sum += pMeasurement->average10s.samples[i];
}
pMeasurement->average10s.value = sum / pMeasurement->average10s.bufferCount;
/* Average form the last 60sec */
pMeasurement->average60s.samples[pMeasurement->average60s.bufferIndex] = pMeasurement->value;
pMeasurement->average60s.bufferIndex = (pMeasurement->average60s.bufferIndex + 1) % AVG60_SAMPLE_SIZE;
if (pMeasurement->average60s.bufferCount < AVG60_SAMPLE_SIZE)
{
pMeasurement->average60s.bufferCount++;
}
if (pMeasurement->average60s.bufferCount == 0U)
{
pMeasurement->average60s.value = pMeasurement->value;
}
sum = 0.0;
for (int i = 0; i < pMeasurement->average60s.bufferCount; i++)
{
sum += pMeasurement->average60s.samples[i];
}
pMeasurement->average60s.value = sum / pMeasurement->average60s.bufferCount;
}
void taskInput(void *pvParameters)
{
while (1)
{
vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS);
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;
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
{
for (int j = 0; j < sSensorCount; j++)
{
float temp_c = fDS18B20Temps[j];
ESP_LOGI(TAG, "Sensor: %08" PRIx64 " reports %.3f°C", (uint64_t)uOneWireAddresses[j], temp_c);
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
switch ((uint64_t)uOneWireAddresses[j])
{
case ((uint64_t)uChamperTempSensorAddr):
fChamperTemperature.value = temp_c;
updateAverage(&fChamperTemperature);
break;
case ((uint64_t)uOutdoorTempSensorAddr):
fOutdoorTemperature.value = temp_c;
updateAverage(&fOutdoorTemperature);
break;
case ((uint64_t)uInletFlowTempSensorAddr):
fInletFlowTemperature.value = temp_c;
updateAverage(&fInletFlowTemperature);
break;
case ((uint64_t)uReturnFlowTempSensorAddr):
fReturnFlowTemperature.value = temp_c;
updateAverage(&fReturnFlowTemperature);
break;
default:
break;
}
xSemaphoreGive(xMutexAccessInputs);
}
}
}
}
}
}
float getChamberTemperature(eMeasurementMode mode)
{
float ret = 0.0f;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
switch (mode)
{
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);
}
return ret;
}
float getOutdoorTemperature(eMeasurementMode mode)
{
float ret = 0.0f;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
switch (mode)
{
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);
}
return ret;
}
float getInletFlowTemperature(eMeasurementMode mode)
{
float ret = 0.0f;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
switch (mode)
{
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);
}
return ret;
}
float getReturnFlowTemperature(eMeasurementMode mode)
{
float ret = 0.0f;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
switch (mode)
{
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);
}
return ret;
}
eBurnerErrorState getBurnerError(void)
{
eBurnerErrorState ret = FAULT;
if (xSemaphoreTake(xMutexAccessInputs, portMAX_DELAY) == pdTRUE)
{
ret = sBurnerErrorState;
xSemaphoreGive(xMutexAccessInputs);
}
return ret;
}