297 lines
8.9 KiB
C
297 lines
8.9 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 MIN(x, y) (((x) < (y)) ? (x) : (y))
|
|
#define MAX(x, y) (((x) > (y)) ? (x) : (y))
|
|
#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 _Measurement
|
|
{
|
|
float lastValue;
|
|
float average10s;
|
|
float average60s;
|
|
float samples[MAX(AVG10_SAMPLE_SIZE, AVG60_SAMPLE_SIZE)];
|
|
size_t bufferIndex;
|
|
size_t bufferCount;
|
|
|
|
} 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 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");
|
|
}
|
|
}
|
|
|
|
// Function to add a new temperature value to the buffer
|
|
void updateAverage(sMeasurement* pMeasurement)
|
|
{
|
|
pMeasurement->samples[pMeasurement->bufferIndex] = pMeasurement->lastValue;
|
|
pMeasurement->bufferIndex = (pMeasurement->bufferIndex + 1) % MAX(AVG10_SAMPLE_SIZE, AVG60_SAMPLE_SIZE);
|
|
|
|
if (pMeasurement->bufferCount < MAX(AVG10_SAMPLE_SIZE, AVG60_SAMPLE_SIZE))
|
|
{
|
|
pMeasurement->bufferCount++;
|
|
}
|
|
|
|
if (pMeasurement->bufferCount == 0U)
|
|
{
|
|
pMeasurement->average10s = pMeasurement->lastValue;
|
|
pMeasurement->average60s = pMeasurement->lastValue;
|
|
}
|
|
|
|
float sum = 0.0;
|
|
for (int i = 0; i < MIN(pMeasurement->bufferCount, AVG10_SAMPLE_SIZE); i++)
|
|
{
|
|
sum += pMeasurement->samples[i];
|
|
}
|
|
|
|
pMeasurement->average10s = sum / MIN(pMeasurement->bufferCount, AVG10_SAMPLE_SIZE);
|
|
|
|
sum = 0.0;
|
|
for (int i = 0; i < MIN(pMeasurement->bufferCount, AVG60_SAMPLE_SIZE); i++)
|
|
{
|
|
sum += pMeasurement->samples[i];
|
|
}
|
|
pMeasurement->average60s = sum / MIN(pMeasurement->bufferCount, AVG60_SAMPLE_SIZE);
|
|
}
|
|
|
|
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.lastValue = temp_c;
|
|
updateAverage(&fChamperTemperature);
|
|
break;
|
|
case ((uint64_t)uOutdoorTempSensorAddr):
|
|
fOutdoorTemperature.lastValue = temp_c;
|
|
updateAverage(&fOutdoorTemperature);
|
|
break;
|
|
case ((uint64_t)uInletFlowTempSensorAddr):
|
|
fInletFlowTemperature.lastValue = temp_c;
|
|
updateAverage(&fInletFlowTemperature);
|
|
break;
|
|
case ((uint64_t)uReturnFlowTempSensorAddr):
|
|
fReturnFlowTemperature.lastValue = 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.lastValue;
|
|
break;
|
|
case AVERAGE_10S:
|
|
ret = fChamperTemperature.average10s;
|
|
break;
|
|
case AVERAGE_60S:
|
|
ret = fChamperTemperature.average60s;
|
|
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.lastValue;
|
|
break;
|
|
case AVERAGE_10S:
|
|
ret = fOutdoorTemperature.average10s;
|
|
break;
|
|
case AVERAGE_60S:
|
|
ret = fOutdoorTemperature.average60s;
|
|
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.lastValue;
|
|
break;
|
|
case AVERAGE_10S:
|
|
ret = fInletFlowTemperature.average10s;
|
|
break;
|
|
case AVERAGE_60S:
|
|
ret = fInletFlowTemperature.average60s;
|
|
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.lastValue;
|
|
break;
|
|
case AVERAGE_10S:
|
|
ret = fReturnFlowTemperature.average10s;
|
|
break;
|
|
case AVERAGE_60S:
|
|
ret = fReturnFlowTemperature.average60s;
|
|
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;
|
|
} |