#include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include #include "safety.h" #define PERIODIC_INTERVAL 1U // run safety checks every 1sec #define SENSOR_GRACE_PERIOD (60U * 30U) // period that a sensor can report the same reading in seconds static const char *TAG = "smart-oil-heater-control-system-safety"; static SemaphoreHandle_t xMutexAccessSafety = NULL; static sSensorSanityCheck sanityChecks[NUMBER_OF_SENSOR_SANITY_CHECKS] = { {SENSOR_NO_ERROR, "chamber_temperature", {95.0f, -10.0f}, 0.0f, 0U, getChamberTemperature}, {SENSOR_NO_ERROR, "outdoor_temperature", {45.0f, -20.0f}, 0.0f, 0U, getOutdoorTemperature}, {SENSOR_NO_ERROR, "inlet_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getInletFlowTemperature}, {SENSOR_NO_ERROR, "return_flow_temperature", {95.0f, -10.0f}, 0.0f, 0U, getReturnFlowTemperature}}; static eSafetyState sSafetyState = SAFETY_NO_ERROR; void taskSafety(void *pvParameters); void checkSensorSanity(void); void setSafeState(void); void initSafety(void) { xMutexAccessSafety = xSemaphoreCreateRecursiveMutex(); if (xMutexAccessSafety == NULL) { ESP_LOGE(TAG, "Unable to create mutex"); } xSemaphoreGiveRecursive(xMutexAccessSafety); BaseType_t taskCreated = xTaskCreate( taskSafety, // Function to implement the task "taskSafety", // 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) ); if (taskCreated == pdPASS) { ESP_LOGI(TAG, "Task created successfully!"); } else { ESP_LOGE(TAG, "Failed to create task"); } setSafeState(); // Set inital state } void taskSafety(void *pvParameters) { while (1) { vTaskDelay(PERIODIC_INTERVAL * 1000U / portTICK_PERIOD_MS); if (xSemaphoreTakeRecursive(xMutexAccessSafety, portMAX_DELAY) == pdTRUE) { checkSensorSanity(); if (sSafetyState != SAFETY_NO_ERROR) { setSafeState(); } xSemaphoreGiveRecursive(xMutexAccessSafety); } } } void checkSensorSanity(void) { sSafetyState = SAFETY_NO_ERROR; for (int i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) { // printf("Check sanity of sensor %s:\n", sanityChecks[i].name); // printf(" Status: %u\n", sanityChecks[i].status); // printf(" Sensor Limits: Max = %.2f, Min = %.2f\n", sanityChecks[i].sSensorLimit.max, sanityChecks[i].sSensorLimit.min); // printf(" Last Sensor Temperature: %.2f\n", sanityChecks[i].fSensorTemperatureLast); const sMeasurement sCurrentMeasurement = sanityChecks[i].getSensor(); if (sCurrentMeasurement.state == MEASUREMENT_FAULT) { ESP_LOGE(TAG, "%s Sensor not found!", sanityChecks[i].name); sanityChecks[i].status = SENSOR_NOT_FOUND; sSafetyState = SAFETY_SENSOR_ERROR; } else { if (sCurrentMeasurement.fCurrentValue == sanityChecks[i].fSensorTemperatureLast) { sanityChecks[i].uUnchangedCounter++; if (sanityChecks[i].uUnchangedCounter >= (SENSOR_GRACE_PERIOD / PERIODIC_INTERVAL)) { ESP_LOGE(TAG, "%s Sensor reported unchanged value! %lf == %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].fSensorTemperatureLast); sanityChecks[i].status = SENSOR_UNCHANGED; sSafetyState = SAFETY_SENSOR_ERROR; } } else { sanityChecks[i].fSensorTemperatureLast = sCurrentMeasurement.fCurrentValue; if (sCurrentMeasurement.fCurrentValue > sanityChecks[i].sSensorLimit.max) { ESP_LOGE(TAG, "%s Sensor reported too high value! %lf > %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.max); sanityChecks[i].status = SENSOR_TOO_HIGH; sSafetyState = SAFETY_SENSOR_ERROR; } else if (sCurrentMeasurement.fCurrentValue < sanityChecks[i].sSensorLimit.min) { ESP_LOGE(TAG, "%s Sensor reported too low value! %lf < %lf", sanityChecks[i].name, sCurrentMeasurement.fCurrentValue, sanityChecks[i].sSensorLimit.min); sanityChecks[i].status = SENSOR_TOO_LOW; sSafetyState = SAFETY_SENSOR_ERROR; } else { sanityChecks[i].uUnchangedCounter = 0U; sanityChecks[i].status = SENSOR_NO_ERROR; } } } // printf(" Status: %u\n", sanityChecks[i].status); } } void setSafeState(void) { setCirculationPumpState(ENABLED); // To cool down system setBurnerState(DISABLED); // Deactivate burner setSafetyControlState(DISABLED); // Disable power to Burner } void getSensorSanityStates(sSensorSanityCheck *pSensorSanityChecks) { if (xSemaphoreTakeRecursive(xMutexAccessSafety, pdMS_TO_TICKS(5000)) == pdTRUE) { for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) { // Copy only the needed attributes pSensorSanityChecks[i].status = sanityChecks[i].status; strcpy(pSensorSanityChecks[i].name, sanityChecks[i].name); } xSemaphoreGiveRecursive(xMutexAccessSafety); } else { ESP_LOGE(TAG, "Unable to take mutex: getSensorSanityStates()"); } } eSafetyState getSafetyState(void) { eSafetyState state = SAFETY_NO_ERROR; if (xSemaphoreTakeRecursive(xMutexAccessSafety, pdMS_TO_TICKS(5000)) == pdTRUE) { state = sSafetyState; xSemaphoreGiveRecursive(xMutexAccessSafety); } else { state = SAFETY_INTERNAL_ERROR; ESP_LOGE(TAG, "Unable to take mutex: getSafetyState()"); } return state; }