#include #include "evse_session.h" #include "evse_meter.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "esp_log.h" #include "evse_events.h" #include "esp_event.h" #include "evse_limits.h" #include "esp_timer.h" #define EVSE_EVENT_POST_TIMEOUT_MS 50 static const char *TAG = "evse_session"; static TickType_t session_start_tick = 0; // Tempo real (microsegundos) static int64_t session_start_us = 0; static int64_t last_tick_us = 0; // Energia integrada com tempo real: soma de (W * us) static uint64_t watt_microseconds = 0; static evse_session_t last_session; static bool last_session_valid = false; static uint32_t session_counter = 0; static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED; static void post_session_event(const evse_session_event_data_t *evt) { esp_err_t err = esp_event_post( EVSE_EVENTS, EVSE_EVENT_SESSION, evt, sizeof(*evt), portMAX_DELAY); if (err != ESP_OK) { ESP_LOGW(TAG, "esp_event_post(EVSE_EVENT_SESSION) failed: %s", esp_err_to_name(err)); } } void evse_session_init(void) { portENTER_CRITICAL(&session_mux); session_start_tick = 0; session_start_us = 0; last_tick_us = 0; watt_microseconds = 0; last_session_valid = false; session_counter = 0; portEXIT_CRITICAL(&session_mux); } void evse_session_start(void) { TickType_t tick = xTaskGetTickCount(); int64_t now_us = esp_timer_get_time(); portENTER_CRITICAL(&session_mux); session_start_tick = tick; session_start_us = now_us; last_tick_us = now_us; watt_microseconds = 0; session_counter++; uint32_t id = session_counter; portEXIT_CRITICAL(&session_mux); evse_set_limit_reached(false); ESP_LOGI(TAG, "Session started (id=%" PRIu32 ") tick=%u us=%" PRId64, id, (unsigned)tick, now_us); evse_session_event_data_t evt = { .type = EVSE_SESSION_EVENT_STARTED, .session_id = id, .duration_s = 0, .energy_wh = 0, .avg_power_w = 0, .is_current = true, }; post_session_event(&evt); } void evse_session_end(void) { TickType_t start_tick; int64_t start_us; uint64_t w_us; uint32_t id; int64_t end_us = esp_timer_get_time(); portENTER_CRITICAL(&session_mux); if (session_start_tick == 0) { portEXIT_CRITICAL(&session_mux); ESP_LOGW(TAG, "evse_session_end called without active session"); return; } start_tick = session_start_tick; start_us = session_start_us; w_us = watt_microseconds; id = session_counter; session_start_tick = 0; session_start_us = 0; last_tick_us = 0; watt_microseconds = 0; portEXIT_CRITICAL(&session_mux); uint32_t duration_s = (end_us > start_us) ? (uint32_t)((end_us - start_us) / 1000000LL) : 0; uint32_t energy_wh = (uint32_t)(w_us / (3600ULL * 1000000ULL)); uint64_t watt_seconds = (uint64_t)(w_us / 1000000ULL); uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0; portENTER_CRITICAL(&session_mux); last_session.start_tick = start_tick; last_session.duration_s = duration_s; last_session.energy_wh = energy_wh; last_session.avg_power_w = avg_power; last_session.is_current = false; last_session_valid = true; portEXIT_CRITICAL(&session_mux); ESP_LOGI(TAG, "Session ended (id=%" PRIu32 "): duration=%" PRIu32 " s, energy=%" PRIu32 " Wh, avg_power=%" PRIu32 " W", id, duration_s, energy_wh, avg_power); evse_session_event_data_t evt = { .type = EVSE_SESSION_EVENT_FINISHED, .session_id = id, .duration_s = duration_s, .energy_wh = energy_wh, .avg_power_w = avg_power, .is_current = false, }; post_session_event(&evt); } void evse_session_tick(void) { // Potência instantânea pode ser negativa (ruído/overflow de sensor) -> clamp int p = evse_meter_get_instant_power(); uint32_t power_w = (p > 0) ? (uint32_t)p : 0; int64_t now_us = esp_timer_get_time(); portENTER_CRITICAL(&session_mux); if (session_start_tick != 0) { if (last_tick_us == 0) { last_tick_us = now_us; } int64_t dt_us = now_us - last_tick_us; if (dt_us > 0) { // Energia incremental: W * us (64-bit) watt_microseconds += ((uint64_t)power_w * (uint64_t)dt_us); last_tick_us = now_us; } else { // relógio não devia andar para trás; ignora last_tick_us = now_us; } } portEXIT_CRITICAL(&session_mux); } bool evse_session_get(evse_session_t *out) { if (out == NULL) return false; TickType_t start_tick; int64_t start_us; uint64_t w_us; bool has_current; evse_session_t last_copy; bool last_valid; int64_t now_us = esp_timer_get_time(); portENTER_CRITICAL(&session_mux); start_tick = session_start_tick; start_us = session_start_us; w_us = watt_microseconds; has_current = (session_start_tick != 0); last_copy = last_session; last_valid = last_session_valid; portEXIT_CRITICAL(&session_mux); if (has_current) { uint32_t duration_s = (now_us > start_us) ? (uint32_t)((now_us - start_us) / 1000000LL) : 0; uint32_t energy_wh = (uint32_t)(w_us / (3600ULL * 1000000ULL)); uint64_t watt_seconds = (uint64_t)(w_us / 1000000ULL); uint32_t avg_power = (duration_s > 0) ? (uint32_t)(watt_seconds / duration_s) : 0; out->start_tick = start_tick; out->duration_s = duration_s; out->energy_wh = energy_wh; out->avg_power_w = avg_power; out->is_current = true; return true; } if (last_valid) { *out = last_copy; return true; } return false; }