#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "driver/gpio.h" #include "esp_log.h" #include #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; }