#include #include "esp_timer.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_wifi.h" #include "esp_log.h" #include "metrics.h" #include "outputs.h" #include "inputs.h" #include "safety.h" static const char *TAG = "smart-oil-heater-control-system-metrics"; char caHtmlResponse[HTML_RESPONSE_SIZE]; SemaphoreHandle_t xMutexAccessMetricResponse = NULL; static sMetric aMetrics[METRIC_MAX_COUNT]; static uint16_t u16MetricCounter = 0U; void taskMetrics(void *pvParameters); httpd_handle_t setup_server(void); esp_err_t get_metrics_handler(httpd_req_t *req); void initMetrics(void) { setup_server(); BaseType_t taskCreated = xTaskCreate( taskMetrics, // Function to implement the task "taskMetrics", // Task name 16384, // 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 taskMetrics(void *pvParameters) { while (1) { vTaskDelay(1000U / portTICK_PERIOD_MS); u16MetricCounter = 0U; /*Uptime*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "uptime_seconds"); aMetrics[u16MetricCounter].fMetricValue = (esp_timer_get_time() / 1000000U); u16MetricCounter++; /*Wifi RSSI*/ wifi_ap_record_t ap; esp_wifi_sta_get_ap_info(&ap); // printf("WiFi RSSI: %d\n", ap.rssi); strcpy(aMetrics[u16MetricCounter].caMetricName, "wifi_rssi"); aMetrics[u16MetricCounter].fMetricValue = ap.rssi; u16MetricCounter++; /*Burner State*/ if (getBurnerState() == ENABLED) { strcpy(aMetrics[u16MetricCounter].caMetricName, "burner_enabled"); aMetrics[u16MetricCounter].fMetricValue = 1.0f; u16MetricCounter++; } else { strcpy(aMetrics[u16MetricCounter].caMetricName, "burner_enabled"); aMetrics[u16MetricCounter].fMetricValue = 0.0f; u16MetricCounter++; } /*Circulation Pump State*/ if (getCirculationPumpState() == ENABLED) { strcpy(aMetrics[u16MetricCounter].caMetricName, "circulation_pump_enabled"); aMetrics[u16MetricCounter].fMetricValue = 1.0f; u16MetricCounter++; } else { strcpy(aMetrics[u16MetricCounter].caMetricName, "circulation_pump_enabled"); aMetrics[u16MetricCounter].fMetricValue = 0.0f; u16MetricCounter++; } /*Burner Error State*/ if (getBurnerError() == FAULT) { strcpy(aMetrics[u16MetricCounter].caMetricName, "burner_fault_pending"); aMetrics[u16MetricCounter].fMetricValue = 1.0f; u16MetricCounter++; } else { strcpy(aMetrics[u16MetricCounter].caMetricName, "burner_fault_pending"); aMetrics[u16MetricCounter].fMetricValue = 0.0f; u16MetricCounter++; } /*Chamber Temperature*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature"); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().fCurrentValue; u16MetricCounter++; /*Outdoor Temperature*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature"); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().fCurrentValue; u16MetricCounter++; /*Chamber Temperature*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature"); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().fCurrentValue; u16MetricCounter++; /*Chamber Temperature*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature"); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().fCurrentValue; u16MetricCounter++; /*Chamber Temperature Average 10s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg10"); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().average10s.fValue; u16MetricCounter++; /*Outdoor Temperature Average 10s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg10"); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().average10s.fValue; u16MetricCounter++; /*Chamber Temperature Average 10s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg10"); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().average10s.fValue; u16MetricCounter++; /*Chamber Temperature Average 10s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg10"); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average10s.fValue; u16MetricCounter++; /*Chamber Temperature Average 60s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "chamber_temperature_avg60"); aMetrics[u16MetricCounter].fMetricValue = getChamberTemperature().average60s.fValue; u16MetricCounter++; /*Outdoor Temperature Average 60s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "outdoor_temperature_avg60"); aMetrics[u16MetricCounter].fMetricValue = getOutdoorTemperature().average60s.fValue; u16MetricCounter++; /*Chamber Temperature Average 60s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "inlet_flow_temperature_avg60"); aMetrics[u16MetricCounter].fMetricValue = getInletFlowTemperature().average60s.fValue; u16MetricCounter++; /*Chamber Temperature Average 60s*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "return_flow_temperature_avg60"); aMetrics[u16MetricCounter].fMetricValue = getReturnFlowTemperature().average60s.fValue; u16MetricCounter++; /*Sensor status*/ sSensorSanityCheck aChecks[NUMBER_OF_SENSOR_SANITY_CHECKS]; getSensorSanityStates(aChecks); for (size_t i = 0; i < NUMBER_OF_SENSOR_SANITY_CHECKS; i++) { strcpy(aMetrics[u16MetricCounter].caMetricName, aChecks[i].name); strcat(aMetrics[u16MetricCounter].caMetricName, "_status"); aMetrics[u16MetricCounter].fMetricValue = aChecks[i].status; u16MetricCounter++; } /*Safety state*/ strcpy(aMetrics[u16MetricCounter].caMetricName, "safety_state"); aMetrics[u16MetricCounter].fMetricValue = getSafetyState(); u16MetricCounter++; vSetMetrics(aMetrics, u16MetricCounter); } } void vSetMetrics(sMetric *paMetrics, uint16_t u16Size) { if (xSemaphoreTakeRecursive(xMutexAccessMetricResponse, pdMS_TO_TICKS(5000)) == pdTRUE) { memset(caHtmlResponse, 0, strlen(caHtmlResponse)); for (uint16_t u16Index = 0U; u16Index < u16Size; u16Index++) { char caValueBuffer[64]; sprintf(caValueBuffer, " %f", paMetrics[u16Index].fMetricValue); // printf("%s\n", caValueBuffer); strcat(caHtmlResponse, paMetrics[u16Index].caMetricName); strcat(caHtmlResponse, caValueBuffer); strcat(caHtmlResponse, "\n"); } xSemaphoreGiveRecursive(xMutexAccessMetricResponse); } else { ESP_LOGI(TAG, "[SET] Unable to obtain mutex for metric response"); } } esp_err_t get_metrics_handler(httpd_req_t *req) { if (xSemaphoreTakeRecursive(xMutexAccessMetricResponse, pdMS_TO_TICKS(5000)) == pdTRUE) { esp_err_t err = httpd_resp_send(req, caHtmlResponse, HTTPD_RESP_USE_STRLEN); xSemaphoreGiveRecursive(xMutexAccessMetricResponse); return err; } else { ESP_LOGI(TAG, "[GET] Unable to obtain mutex for metric response"); return httpd_resp_send(req, 0, 0); } } httpd_handle_t setup_server(void) { httpd_config_t config = HTTPD_DEFAULT_CONFIG(); config.server_port = 9100; httpd_handle_t server = NULL; httpd_uri_t uri_get = { .uri = "/metrics", .method = HTTP_GET, .handler = get_metrics_handler, .user_ctx = NULL}; xMutexAccessMetricResponse = xSemaphoreCreateRecursiveMutex(); if (xMutexAccessMetricResponse == NULL) { ESP_LOGE(TAG, "Unable to create mutex for metric response"); } xSemaphoreGiveRecursive(xMutexAccessMetricResponse); if (httpd_start(&server, &config) == ESP_OK) { httpd_register_uri_handler(server, &uri_get); } return server; }