diff --git a/cfg/custom/board.cfg b/cfg/custom/board.cfg index 3bbfb02..29c9372 100755 --- a/cfg/custom/board.cfg +++ b/cfg/custom/board.cfg @@ -1,12 +1,12 @@ -DEVICE_NAME=Custom EVSE +DEVICE_NAME=ChargeFlow #LEDs -LED_CHARGING=n -LED_CHARGING_GPIO= -LED_ERROR=n -LED_ERROR_GPIO= -LED_STOP=n -LED_STOP_GPIO= +led_blue=n +led_blue_GPIO= +led_red=n +led_red_GPIO= +led_green=n +led_green_GPIO= #Button BUTTON_WIFI_GPIO=32 @@ -34,33 +34,4 @@ AC_RELAY_GPIO=25 SOCKET_LOCK=n SOCKET_LOCK_A_GPIO= SOCKET_LOCK_B_GPIO= -SOCKET_LOCK_DETECTION_GPIO= - -#Energy meter (none | cur | cur_vlt) -ENERGY_METER=none -ENERGY_METER_THREE_PHASES=n - -#Energy meter internal (ENERGY_METER=cur | cur_vlt) -ENERGY_METER_L1_CUR_ADC_CHANNEL= -ENERGY_METER_L2_CUR_ADC_CHANNEL= -ENERGY_METER_L3_CUR_ADC_CHANNEL= -ENERGY_METER_CUR_SCALE= - -#Energy meter internal (ENERGY_METER=cur_vlt) -ENERGY_METER_L1_VLT_ADC_CHANNEL= -ENERGY_METER_L2_VLT_ADC_CHANNEL= -ENERGY_METER_L3_VLT_ADC_CHANNEL= -ENERGY_METER_VLT_SCALE= - -#Serial (SERIAL_X=none|uart|rs485) -SERIAL_1=none -SERIAL_1_NAME=UART 1 -SERIAL_1_RXD_GPIO= -SERIAL_1_TXD_GPIO= -SERIAL_1_RTS_GPIO= - -SERIAL_2=none -SERIAL_2_NAME=UART 2 -SERIAL_2_RXD_GPIO= -SERIAL_2_TXD_GPIO= -SERIAL_2_RTS_GPIO= \ No newline at end of file +SOCKET_LOCK_DETECTION_GPIO= \ No newline at end of file diff --git a/cfg/esp32devkitc/board.cfg b/cfg/esp32devkitc/board.cfg index 14630af..6e8037c 100755 --- a/cfg/esp32devkitc/board.cfg +++ b/cfg/esp32devkitc/board.cfg @@ -1,12 +1,12 @@ -DEVICE_NAME=Plixin Evse +DEVICE_NAME=ChargeFlow #LEDs -LED_CHARGING=y -LED_CHARGING_GPIO=14 -LED_ERROR=y -LED_ERROR_GPIO=26 -LED_STOP=y -LED_STOP_GPIO=12 +led_blue=y +led_blue_GPIO=14 +led_red=y +led_red_GPIO=26 +led_green=y +led_green_GPIO=12 #BUZZER BUZZER=y @@ -41,85 +41,4 @@ AC_RELAY_GPIO=25 SOCKET_LOCK=n SOCKET_LOCK_A_GPIO= SOCKET_LOCK_B_GPIO= -SOCKET_LOCK_DETECTION_GPIO= - -#Energy meter (none | cur | cur_vlt) -ENERGY_METER=none -ENERGY_METER_THREE_PHASES=n - -#Energy meter internal (ENERGY_METER=cur | cur_vlt) -ENERGY_METER_L1_CUR_ADC_CHANNEL=7 -ENERGY_METER_L2_CUR_ADC_CHANNEL= -ENERGY_METER_L3_CUR_ADC_CHANNEL= -ENERGY_METER_CUR_SCALE=0.090909091 - -#Energy meter internal (ENERGY_METER=cur_vlt) -ENERGY_METER_L1_VLT_ADC_CHANNEL= -ENERGY_METER_L2_VLT_ADC_CHANNEL= -ENERGY_METER_L3_VLT_ADC_CHANNEL= -ENERGY_METER_VLT_SCALE=0.47 - -#AUX -AUX_IN_1=n -AUX_IN_1_NAME= -AUX_IN_1_GPIO= - -AUX_IN_2=n -AUX_IN_2_NAME= -AUX_IN_2_GPIO= - -AUX_IN_3=n -AUX_IN_3_NAME= -AUX_IN_3_GPIO= - -AUX_IN_4=n -AUX_IN_4_NAME= -AUX_IN_4_GPIO= - -AUX_OUT_1=n -AUX_OUT_1_NAME= -AUX_OUT_1_GPIO= - -AUX_OUT_2=n -AUX_OUT_2_NAME= -AUX_OUT_2_GPIO= - -AUX_OUT_3=n -AUX_OUT_3_NAME= -AUX_OUT_3_GPIO= - -AUX_OUT_4=n -AUX_OUT_4_NAME= -AUX_OUT_4_GPIO= - -AUX_AIN_1=n -AUX_AIN_1_NAME= -AUX_AIN_1_ADC_CHANNEL= - -AUX_AIN_2=n -AUX_AIN_2_NAME= -AUX_AIN_2_ADC_CHANNEL= - -#Serial (SERIAL_X=none|uart|rs485) -SERIAL_1=none -SERIAL_1_NAME=UART via USB -SERIAL_1_RXD_GPIO= -SERIAL_1_TXD_GPIO= -SERIAL_1_RTS_GPIO= - -SERIAL_2=none -SERIAL_2_NAME=RS485 -SERIAL_2_RXD_GPIO= -SERIAL_2_TXD_GPIO= -SERIAL_2_RTS_GPIO= - -SERIAL_3=none -SERIAL_3_NAME=UART -SERIAL_3_RXD_GPIO= -SERIAL_3_TXD_GPIO= -SERIAL_3_RTS_GPIO= - -#Onewire devices -ONEWIRE=n -ONEWIRE_GPIO= -ONEWIRE_TEMP_SENSOR=n +SOCKET_LOCK_DETECTION_GPIO= \ No newline at end of file diff --git a/cfg/esp32s2da/board.cfg b/cfg/esp32s2da/board.cfg index 2cbbff6..23819b0 100755 --- a/cfg/esp32s2da/board.cfg +++ b/cfg/esp32s2da/board.cfg @@ -1,12 +1,12 @@ -DEVICE_NAME=ESP32-S2-DA EVSE +DEVICE_NAME=ChargeFlow #LEDs -LED_CHARGING=y -LED_CHARGING_GPIO=36 -LED_ERROR=y -LED_ERROR_GPIO=37 -LED_STOP=y -LED_STOP_GPIO=35 +led_blue=y +led_blue_GPIO=36 +led_red=y +led_red_GPIO=37 +led_green=y +led_green_GPIO=35 #Button BUTTON_WIFI_GPIO=32 @@ -42,78 +42,3 @@ SOCKET_LOCK_MIN_BREAK_TIME=1000 RCM=n RCM_GPIO=41 RCM_TEST_GPIO=26 - -#Energy meter (none | cur | cur_vlt) -ENERGY_METER=cur_vlt -ENERGY_METER_THREE_PHASES=y - -#Energy meter internal (ENERGY_METER=cur | cur_vlt) -ENERGY_METER_L1_CUR_ADC_CHANNEL=4 -ENERGY_METER_L2_CUR_ADC_CHANNEL=5 -ENERGY_METER_L3_CUR_ADC_CHANNEL=6 -ENERGY_METER_CUR_SCALE=0.090909091 - -#Energy meter internal (ENERGY_METER=cur_vlt) -ENERGY_METER_L1_VLT_ADC_CHANNEL=7 -ENERGY_METER_L2_VLT_ADC_CHANNEL=8 -ENERGY_METER_L3_VLT_ADC_CHANNEL=9 -ENERGY_METER_VLT_SCALE=0.47 - -#AUX -AUX_IN_1=n -AUX_IN_1_NAME= -AUX_IN_1_GPIO= - -AUX_IN_2=n -AUX_IN_2_NAME= -AUX_IN_2_GPIO= - -AUX_IN_3=n -AUX_IN_3_NAME= -AUX_IN_3_GPIO= - -AUX_IN_4=n -AUX_IN_4_NAME= -AUX_IN_4_GPIO= - -AUX_OUT_1=n -AUX_OUT_1_NAME= -AUX_OUT_1_GPIO= - -AUX_OUT_2=n -AUX_OUT_2_NAME= -AUX_OUT_2_GPIO= - -AUX_OUT_3=n -AUX_OUT_3_NAME= -AUX_OUT_3_GPIO= - -AUX_OUT_4=n -AUX_OUT_4_NAME= -AUX_OUT_4_GPIO= - -AUX_AIN_1=n -AUX_AIN_1_NAME= -AUX_AIN_1_ADC_CHANNEL= - -AUX_AIN_2=n -AUX_AIN_2_NAME= -AUX_AIN_2_ADC_CHANNEL= - -#Serial (SERIAL_X=none|uart|rs485) -SERIAL_1=none -SERIAL_1_NAME=UART -SERIAL_1_RXD_GPIO= -SERIAL_1_TXD_GPIO= -SERIAL_1_RTS_GPIO= - -SERIAL_2=none -SERIAL_2_NAME=RS-485 -SERIAL_2_RXD_GPIO= -SERIAL_2_TXD_GPIO= -SERIAL_2_RTS_GPIO= - -#Onewire devices -ONEWIRE=n -ONEWIRE_GPIO= -ONEWIRE_TEMP_SENSOR=n \ No newline at end of file diff --git a/components/auth/CMakeLists.txt b/components/auth/CMakeLists.txt index c7a59d8..1c68e6d 100755 --- a/components/auth/CMakeLists.txt +++ b/components/auth/CMakeLists.txt @@ -4,4 +4,4 @@ idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "include" PRIV_INCLUDE_DIRS "src" PRIV_REQUIRES nvs_flash driver esp_timer - REQUIRES esp_event esp_idf_lib_helpers evse ocpp) \ No newline at end of file + REQUIRES esp_event evse ocpp evse_link) \ No newline at end of file diff --git a/components/auth/src/auth.c b/components/auth/src/auth.c index b4f1cdd..05fd50d 100755 --- a/components/auth/src/auth.c +++ b/components/auth/src/auth.c @@ -1,5 +1,3 @@ -// components/auth/src/auth.c - #include "auth.h" #include "auth_events.h" #include "esp_event.h" @@ -9,11 +7,13 @@ #include #include -#include // <-- necessário para strcasecmp +#include // strcasecmp #include "wiegand_reader.h" #include "nvs_flash.h" #include "nvs.h" -#include "esp_random.h" + +#include "evse_link.h" +#include "evse_link_events.h" #define MAX_TAGS 50 @@ -25,63 +25,100 @@ static bool waiting_for_registration = false; static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; static int tag_count = 0; static uint32_t s_next_req_id = 1; +static bool s_wiegand_started = false; // controla se o Wiegand já foi iniciado /* ===== NVS keys ===== */ -#define NVS_NAMESPACE "auth" -#define NVS_TAG_PREFIX "tag_" -#define NVS_TAG_COUNT_KEY "count" -#define NVS_MODE_KEY "mode" // uint8_t +#define NVS_NAMESPACE "auth" +#define NVS_TAG_PREFIX "tag_" +#define NVS_TAG_COUNT_KEY "count" +#define NVS_MODE_KEY "mode" // uint8_t /* ========================= * NVS Persistence (tags) * ========================= */ -static void load_tags_from_nvs(void) { +static void load_tags_from_nvs(void) +{ nvs_handle_t handle; - if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle) != ESP_OK) { - ESP_LOGW(TAG, "No stored tags in NVS"); + esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &handle); + if (err != ESP_OK) + { + ESP_LOGW(TAG, "No stored tags in NVS (nvs_open: %s)", esp_err_to_name(err)); return; } uint8_t count = 0; - if (nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count) != ESP_OK) { + err = nvs_get_u8(handle, NVS_TAG_COUNT_KEY, &count); + if (err != ESP_OK) + { + ESP_LOGW(TAG, "No tag count key in NVS (nvs_get_u8: %s)", esp_err_to_name(err)); nvs_close(handle); return; } tag_count = 0; - for (int i = 0; i < count && i < MAX_TAGS; i++) { + for (int i = 0; i < count && i < MAX_TAGS; i++) + { char key[16]; char tag_buf[AUTH_TAG_MAX_LEN]; size_t len = sizeof(tag_buf); snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i); - if (nvs_get_str(handle, key, tag_buf, &len) == ESP_OK) { + err = nvs_get_str(handle, key, tag_buf, &len); + if (err == ESP_OK) + { strncpy(valid_tags[tag_count], tag_buf, AUTH_TAG_MAX_LEN - 1); valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; tag_count++; } + else + { + ESP_LOGW(TAG, "Failed to load tag %d from NVS (%s)", i, esp_err_to_name(err)); + } } nvs_close(handle); ESP_LOGI(TAG, "Loaded %d tags from NVS", tag_count); } -static void save_tags_to_nvs(void) { +static void save_tags_to_nvs(void) +{ nvs_handle_t handle; - if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle) != ESP_OK) { - ESP_LOGE(TAG, "Failed to open NVS to save tags"); + esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to open NVS to save tags: %s", esp_err_to_name(err)); return; } - nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count); - - for (int i = 0; i < tag_count; i++) { - char key[16]; - snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i); - nvs_set_str(handle, key, valid_tags[i]); + err = nvs_set_u8(handle, NVS_TAG_COUNT_KEY, tag_count); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "nvs_set_u8(count) failed: %s", esp_err_to_name(err)); + nvs_close(handle); + return; + } + + for (int i = 0; i < tag_count; i++) + { + char key[16]; + snprintf(key, sizeof(key), "%s%d", NVS_TAG_PREFIX, i); + err = nvs_set_str(handle, key, valid_tags[i]); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "nvs_set_str(%s) failed: %s", key, esp_err_to_name(err)); + nvs_close(handle); + return; + } + } + + err = nvs_commit(handle); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "nvs_commit failed when saving tags: %s", esp_err_to_name(err)); + nvs_close(handle); + return; } - nvs_commit(handle); nvs_close(handle); ESP_LOGI(TAG, "Tags saved to NVS (%d tags)", tag_count); } @@ -89,95 +126,220 @@ static void save_tags_to_nvs(void) { /* ========================= * NVS Persistence (mode) * ========================= */ -static void load_mode_from_nvs(void) { +static void load_mode_from_nvs(void) +{ nvs_handle_t h; - if (nvs_open(NVS_NAMESPACE, NVS_READONLY, &h) == ESP_OK) { + esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READONLY, &h); + if (err == ESP_OK) + { uint8_t u = (uint8_t)AUTH_MODE_OPEN; - if (nvs_get_u8(h, NVS_MODE_KEY, &u) == ESP_OK) { - if (u <= (uint8_t)AUTH_MODE_OCPP_RFID) s_mode = (auth_mode_t)u; + err = nvs_get_u8(h, NVS_MODE_KEY, &u); + if (err == ESP_OK) + { + if (u <= (uint8_t)AUTH_MODE_OCPP_RFID) + s_mode = (auth_mode_t)u; + } + else + { + ESP_LOGW(TAG, "No stored auth mode in NVS (nvs_get_u8: %s). Default OPEN", esp_err_to_name(err)); } nvs_close(h); - } else { - ESP_LOGW(TAG, "No stored auth mode in NVS; default OPEN"); + } + else + { + ESP_LOGW(TAG, "No stored auth mode in NVS (nvs_open: %s). Default OPEN", esp_err_to_name(err)); } ESP_LOGI(TAG, "Loaded mode = %d (%s)", (int)s_mode, auth_mode_to_str(s_mode)); } -static void save_mode_to_nvs(auth_mode_t mode) { +static void save_mode_to_nvs(auth_mode_t mode) +{ nvs_handle_t h; - if (nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h) == ESP_OK) { - nvs_set_u8(h, NVS_MODE_KEY, (uint8_t)mode); - nvs_commit(h); - nvs_close(h); - ESP_LOGI(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode)); - } else { - ESP_LOGE(TAG, "Failed to save auth mode to NVS"); + esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &h); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to open NVS to save auth mode: %s", esp_err_to_name(err)); + return; } + + err = nvs_set_u8(h, NVS_MODE_KEY, (uint8_t)mode); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "nvs_set_u8(mode) failed: %s", esp_err_to_name(err)); + nvs_close(h); + return; + } + + err = nvs_commit(h); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "nvs_commit failed when saving mode: %s", esp_err_to_name(err)); + nvs_close(h); + return; + } + + nvs_close(h); + ESP_LOGI(TAG, "Saved mode = %d (%s)", (int)mode, auth_mode_to_str(mode)); } /* ========================= * Helpers * ========================= */ -static bool is_tag_valid(const char *tag) { - for (int i = 0; i < tag_count; i++) { - if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { +static bool is_tag_valid(const char *tag) +{ + for (int i = 0; i < tag_count; i++) + { + if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) + { return true; } } return false; } +/* ========================= + * Bridge: EVSE-Link -> AUTH (remote AUTH_GRANTED no slave) + * ========================= */ +static void on_remote_auth_grant(void *arg, esp_event_base_t base, int32_t id, void *data) +{ + if (base != EVSE_LINK_EVENTS || id != LINK_EVENT_REMOTE_AUTH_GRANTED || data == NULL) + { + return; + } + + const evse_link_auth_grant_event_t *src = (const evse_link_auth_grant_event_t *)data; + + // Só faz sentido em SLAVE; em MASTER este evento não deve aparecer + if (evse_link_get_mode() != EVSE_LINK_MODE_SLAVE) + { + return; + } + + auth_tag_event_data_t ev = {0}; + strncpy(ev.tag, src->tag, AUTH_TAG_MAX_LEN - 1); + ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0'; + ev.authorized = true; + + ESP_LOGI(TAG, "Remote auth grant on SLAVE for tag=%s", ev.tag); + + esp_err_t err = esp_event_post( + AUTH_EVENTS, + AUTH_EVENT_TAG_PROCESSED, + &ev, + sizeof(ev), + portMAX_DELAY); + + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to post AUTH_EVENT_TAG_PROCESSED (remote grant): %s", + esp_err_to_name(err)); + } +} + /* ========================= * Public API * ========================= */ -void auth_init(void) { +void auth_init(void) +{ load_mode_from_nvs(); load_tags_from_nvs(); - if (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID) { + bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID); + if (need_wiegand) + { initWiegand(); + s_wiegand_started = true; ESP_LOGI(TAG, "Wiegand reader initialized (mode=%s)", auth_mode_to_str(s_mode)); - } else { + } + else + { ESP_LOGI(TAG, "Mode OPEN: Wiegand not started"); } - auth_mode_event_data_t evt = { .mode = s_mode }; + // Registar bridge para autorizações remotas vindas do EVSE-Link + { + esp_err_t err = esp_event_handler_register( + EVSE_LINK_EVENTS, + LINK_EVENT_REMOTE_AUTH_GRANTED, + on_remote_auth_grant, + NULL); + if (err != ESP_OK) + { + ESP_LOGE(TAG, "Failed to register EVSE-Link auth grant handler: %s", + esp_err_to_name(err)); + } + } + + auth_mode_event_data_t evt = {.mode = s_mode}; esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); ESP_LOGI(TAG, "AUTH INIT sent (mode=%s)", auth_mode_to_str(s_mode)); } -void auth_set_mode(auth_mode_t mode) { - if (mode < AUTH_MODE_OPEN || mode > AUTH_MODE_OCPP_RFID) { +void auth_set_mode(auth_mode_t mode) +{ + if (mode < AUTH_MODE_OPEN || mode > AUTH_MODE_OCPP_RFID) + { ESP_LOGW(TAG, "Invalid mode: %d", (int)mode); return; } - if (mode == s_mode) { + + if (mode == s_mode) + { ESP_LOGI(TAG, "Mode unchanged: %s", auth_mode_to_str(mode)); return; } + auth_mode_t old = s_mode; s_mode = mode; save_mode_to_nvs(mode); - // Nota: se precisares, aqui podes parar/iniciar o Wiegand consoante o modo. - if (s_mode == AUTH_MODE_OPEN) { - ESP_LOGI(TAG, "Mode set to OPEN"); - } else { - ESP_LOGI(TAG, "Mode set to %s; ensure Wiegand reader is running", auth_mode_to_str(s_mode)); + bool need_wiegand = (s_mode == AUTH_MODE_LOCAL_RFID || s_mode == AUTH_MODE_OCPP_RFID); + + if (need_wiegand && !s_wiegand_started) + { + ESP_LOGI(TAG, "Mode changed %s -> %s, starting Wiegand", + auth_mode_to_str(old), auth_mode_to_str(s_mode)); + initWiegand(); + s_wiegand_started = true; + } + else if (!need_wiegand && s_wiegand_started) + { + // Aqui poderias implementar um wiegand_deinit() se o driver o expuser. + ESP_LOGI(TAG, "Mode changed %s -> %s, Wiegand remains started (no deinit implemented)", + auth_mode_to_str(old), auth_mode_to_str(s_mode)); + } + else + { + ESP_LOGI(TAG, "Mode changed %s -> %s, no change in Wiegand state", + auth_mode_to_str(old), auth_mode_to_str(s_mode)); } - auth_mode_event_data_t evt = { .mode = s_mode }; + if (s_mode == AUTH_MODE_OPEN) + { + ESP_LOGI(TAG, "Mode set to OPEN"); + } + else + { + ESP_LOGI(TAG, "Mode set to %s", auth_mode_to_str(s_mode)); + } + + auth_mode_event_data_t evt = {.mode = s_mode}; esp_event_post(AUTH_EVENTS, AUTH_EVENT_MODE_CHANGED, &evt, sizeof(evt), portMAX_DELAY); } -auth_mode_t auth_get_mode(void) { +auth_mode_t auth_get_mode(void) +{ return s_mode; } -bool auth_add_tag(const char *tag) { - if (tag_count >= MAX_TAGS) return false; - if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) return false; - if (is_tag_valid(tag)) return true; // já existe +bool auth_add_tag(const char *tag) +{ + if (!tag || strlen(tag) >= AUTH_TAG_MAX_LEN) + return false; + if (tag_count >= MAX_TAGS) + return false; + if (is_tag_valid(tag)) + return true; // já existe strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; @@ -188,11 +350,19 @@ bool auth_add_tag(const char *tag) { return true; } -bool auth_remove_tag(const char *tag) { - for (int i = 0; i < tag_count; i++) { - if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) { - for (int j = i; j < tag_count - 1; j++) { +bool auth_remove_tag(const char *tag) +{ + if (!tag) + return false; + + for (int i = 0; i < tag_count; i++) + { + if (strncmp(valid_tags[i], tag, AUTH_TAG_MAX_LEN) == 0) + { + for (int j = i; j < tag_count - 1; j++) + { strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); + valid_tags[j][AUTH_TAG_MAX_LEN - 1] = '\0'; } tag_count--; @@ -204,19 +374,26 @@ bool auth_remove_tag(const char *tag) { return false; } -bool auth_tag_exists(const char *tag) { +bool auth_tag_exists(const char *tag) +{ + if (!tag) + return false; return is_tag_valid(tag); } -void auth_list_tags(void) { +void auth_list_tags(void) +{ ESP_LOGI(TAG, "Registered Tags (%d):", tag_count); - for (int i = 0; i < tag_count; i++) { + for (int i = 0; i < tag_count; i++) + { ESP_LOGI(TAG, "- %s", valid_tags[i]); } } -void auth_wait_for_tag_registration(void) { - if (s_mode != AUTH_MODE_LOCAL_RFID) { +void auth_wait_for_tag_registration(void) +{ + if (s_mode != AUTH_MODE_LOCAL_RFID) + { ESP_LOGW(TAG, "Registration is only available in LOCAL mode"); return; } @@ -224,61 +401,82 @@ void auth_wait_for_tag_registration(void) { ESP_LOGI(TAG, "Tag registration mode enabled."); } -void auth_process_tag(const char *tag) { - if (!tag || !*tag) { +void auth_process_tag(const char *tag) +{ + if (!tag || !*tag) + { ESP_LOGW(TAG, "NULL/empty tag received"); return; } - switch (s_mode) { - case AUTH_MODE_OPEN: { - // Sem verificação; normalmente nem é necessário evento. - ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag); - break; - } + switch (s_mode) + { + case AUTH_MODE_OPEN: + { + // Sem verificação; normalmente nem é necessário evento. + ESP_LOGI(TAG, "Mode OPEN: tag=%s (no verification)", tag); + break; + } - case AUTH_MODE_LOCAL_RFID: { - if (waiting_for_registration) { - waiting_for_registration = false; + case AUTH_MODE_LOCAL_RFID: + { + if (waiting_for_registration) + { + waiting_for_registration = false; - if (auth_add_tag(tag)) { - auth_tag_event_data_t ev = {0}; - strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1); - ev.authorized = true; - esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, &ev, sizeof(ev), portMAX_DELAY); - ESP_LOGI(TAG, "Tag registered: %s", tag); - } else { - ESP_LOGW(TAG, "Failed to register tag: %s", tag); - } - return; + if (auth_add_tag(tag)) + { + auth_tag_event_data_t ev = {0}; + strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1); + ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0'; + ev.authorized = true; + esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, + &ev, sizeof(ev), portMAX_DELAY); + ESP_LOGI(TAG, "Tag registered: %s", tag); } - - auth_tag_event_data_t ev = {0}; - strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1); - ev.authorized = is_tag_valid(tag); - - ESP_LOGI(TAG, "LOCAL tag %s: %s", tag, ev.authorized ? "AUTHORIZED" : "DENIED"); - esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &ev, sizeof(ev), portMAX_DELAY); - break; + else + { + ESP_LOGW(TAG, "Failed to register tag: %s", tag); + } + return; } - case AUTH_MODE_OCPP_RFID: { - // Não decide localmente. Pede validação ao OCPP. - auth_tag_verify_event_t rq = {0}; - strncpy(rq.tag, tag, AUTH_TAG_MAX_LEN - 1); - rq.req_id = s_next_req_id++; - ESP_LOGI(TAG, "OCPP VERIFY requested for tag=%s (req_id=%u)", rq.tag, (unsigned)rq.req_id); - esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY, &rq, sizeof(rq), portMAX_DELAY); - break; - } + auth_tag_event_data_t ev = {0}; + strncpy(ev.tag, tag, AUTH_TAG_MAX_LEN - 1); + ev.tag[AUTH_TAG_MAX_LEN - 1] = '\0'; + ev.authorized = is_tag_valid(tag); + + ESP_LOGI(TAG, "LOCAL tag %s: %s", tag, + ev.authorized ? "AUTHORIZED" : "DENIED"); + esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, + &ev, sizeof(ev), portMAX_DELAY); + break; + } + + case AUTH_MODE_OCPP_RFID: + { + // Não decide localmente. Pede validação ao OCPP. + auth_tag_verify_event_t rq = {0}; + strncpy(rq.tag, tag, AUTH_TAG_MAX_LEN - 1); + rq.tag[AUTH_TAG_MAX_LEN - 1] = '\0'; + rq.req_id = s_next_req_id++; + ESP_LOGI(TAG, "OCPP VERIFY requested for tag=%s (req_id=%u)", + rq.tag, (unsigned)rq.req_id); + esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_VERIFY, + &rq, sizeof(rq), portMAX_DELAY); + break; + } } } -int auth_get_tag_count(void) { +int auth_get_tag_count(void) +{ return tag_count; } -const char *auth_get_tag_by_index(int index) { - if (index < 0 || index >= tag_count) return NULL; +const char *auth_get_tag_by_index(int index) +{ + if (index < 0 || index >= tag_count) + return NULL; return valid_tags[index]; } diff --git a/components/auth/src/wiegand.c b/components/auth/src/wiegand.c index eaf3a33..1f54396 100755 --- a/components/auth/src/wiegand.c +++ b/components/auth/src/wiegand.c @@ -1,3 +1,4 @@ +// === Início de: components/auth/src/wiegand.c === /** * @file wiegand.c * @@ -6,9 +7,13 @@ #include #include #include -#include +#include // <- para IRAM_ATTR #include "wiegand.h" +#ifndef IRAM_ATTR +#define IRAM_ATTR +#endif + static const char *TAG = "wiegand"; #define TIMER_INTERVAL_US 50000 // 50ms @@ -39,11 +44,7 @@ static void isr_enable(wiegand_reader_t *reader) gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_NEGEDGE); } -#if HELPER_TARGET_IS_ESP32 static void IRAM_ATTR isr_handler(void *arg) -#else -static void isr_handler(void *arg) -#endif { wiegand_reader_t *reader = (wiegand_reader_t *)arg; if (!reader->enabled) @@ -181,3 +182,5 @@ esp_err_t wiegand_reader_done(wiegand_reader_t *reader) return ESP_OK; } + +// === Fim de: components/auth/src/wiegand.c === diff --git a/components/auth/src/wiegand_reader.c b/components/auth/src/wiegand_reader.c index af0db78..66e1362 100755 --- a/components/auth/src/wiegand_reader.c +++ b/components/auth/src/wiegand_reader.c @@ -1,23 +1,23 @@ #include #include #include +#include + #include #include #include + #include + #include #include "auth.h" -#include -#include -#include - #define CONFIG_EXAMPLE_BUF_SIZE 50 #define IDTAG_MAX_LEN 20 static const char *TAG = "WiegandReader"; -// ---- Parâmetro global/locale para controlar a convenção de paridade ---- +// ---- Parâmetro global/local para controlar a convenção de paridade ---- static const bool PARITY_INVERTED = false; // mude para true se o seu leitor vier "invertido" static wiegand_reader_t reader; @@ -35,7 +35,7 @@ static inline uint8_t get_bit_msb_first(const uint8_t *buf, size_t bit_index) return (buf[bit_index / 8] >> (7 - (bit_index % 8))) & 0x01; } -// Versão parametrizável +// Versão parametrizável de verificação de paridade static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert) { if (bits != 26 && bits != 34) @@ -55,8 +55,8 @@ static bool check_parity_param(const uint8_t *buf, size_t bits, bool invert) if (get_bit_msb_first(buf, i)) right++; } - else - { // 34 + else // 34 bits + { for (int i = 1; i <= 16; i++) if (get_bit_msb_first(buf, i)) left++; @@ -117,24 +117,28 @@ static bool wiegand_extract_fc_cn(const uint8_t *buf, size_t bits, uint32_t *fc, { if (bits != 26 && bits != 34) return false; + uint32_t payload = 0; size_t payload_bits = bits - 2; + for (size_t i = 0; i < payload_bits; i++) { size_t bit = 1 + i; // ignora bit de paridade inicial uint8_t bitval = (buf[bit / 8] >> (7 - (bit % 8))) & 0x01; payload = (payload << 1) | bitval; } + if (bits == 26) { *fc = (payload >> 16) & 0xFF; // 8b *cn = payload & 0xFFFF; // 16b } - else + else // 34 bits { *fc = (payload >> 16) & 0xFFFF; // 16b *cn = payload & 0xFFFF; // 16b } + return true; } @@ -145,10 +149,14 @@ static bool build_idtag_w26_4B(uint32_t fc, uint32_t cn, char *out, size_t outle (uint8_t)((cn >> 8) & 0xFF), (uint8_t)(cn & 0xFF), 0}; + raw[3] = crc8_atm(raw, 3); + if (outlen < 9) return false; - int n = snprintf(out, outlen, "%02X%02X%02X%02X", raw[0], raw[1], raw[2], raw[3]); + + int n = snprintf(out, outlen, "%02X%02X%02X%02X", + raw[0], raw[1], raw[2], raw[3]); return (n > 0 && (size_t)n < outlen); } @@ -160,31 +168,42 @@ static bool build_idtag_w34_7B(uint32_t fc, uint32_t cn, char *out, size_t outle raw[2] = (uint8_t)(fc & 0xFF); raw[3] = (uint8_t)((cn >> 8) & 0xFF); raw[4] = (uint8_t)(cn & 0xFF); + uint16_t crc = crc16_ibm(raw, 5); raw[5] = (uint8_t)((crc >> 8) & 0xFF); raw[6] = (uint8_t)(crc & 0xFF); + if (outlen < 15) return false; + int n = snprintf(out, outlen, "%02X%02X%02X%02X%02X%02X%02X", - raw[0], raw[1], raw[2], raw[3], raw[4], raw[5], raw[6]); + raw[0], raw[1], raw[2], raw[3], + raw[4], raw[5], raw[6]); return (n > 0 && (size_t)n < outlen); } -// Se o callback for de ISR, troque para xQueueSendToBackFromISR. +// Callback chamado pelo esp_timer_task (contexto de task, NÃO ISR) static void reader_callback(wiegand_reader_t *r) { + if (!queue) + { + ESP_LOGW(TAG, "Queue not ready, dropping packet"); + return; + } + data_packet_t p = {0}; p.bits = r->bits; p.bytes = (r->bits + 7) / 8; + if (p.bytes > sizeof(p.data)) p.bytes = sizeof(p.data); + memcpy(p.data, r->buf, p.bytes); - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - // Se NÃO for ISR, use xQueueSendToBack(queue, &p, 0); - xQueueSendToBackFromISR(queue, &p, &xHigherPriorityTaskWoken); - if (xHigherPriorityTaskWoken) - portYIELD_FROM_ISR(); + if (xQueueSendToBack(queue, &p, 0) != pdPASS) + { + ESP_LOGW(TAG, "Queue full, dropping Wiegand packet"); + } } static void wiegand_task(void *arg) @@ -197,13 +216,20 @@ static void wiegand_task(void *arg) return; } - ESP_ERROR_CHECK(wiegand_reader_init(&reader, 21, 22, - true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); + ESP_ERROR_CHECK(wiegand_reader_init(&reader, + 21, 22, // GPIO D0, D1 + true, // internal pullups + CONFIG_EXAMPLE_BUF_SIZE, + reader_callback, + WIEGAND_MSB_FIRST, + WIEGAND_LSB_FIRST)); data_packet_t p; + for (;;) { ESP_LOGI(TAG, "Waiting for Wiegand data..."); + if (xQueueReceive(queue, &p, portMAX_DELAY) != pdPASS) continue; @@ -216,7 +242,7 @@ static void wiegand_task(void *arg) continue; } - char tag[21] = {0}; // OCPP 1.6: máx 20 chars (+NUL) + char tag[IDTAG_MAX_LEN + 1] = {0}; // OCPP 1.6: máx 20 chars (+NUL) uint32_t fc = 0, cn = 0; if (!wiegand_extract_fc_cn(p.data, p.bits, &fc, &cn)) @@ -230,8 +256,8 @@ static void wiegand_task(void *arg) { ok = build_idtag_w26_4B(fc, cn, tag, sizeof(tag)); // 8 hex } - else - { // 34 + else // 34 + { ok = build_idtag_w34_7B(fc, cn, tag, sizeof(tag)); // 14 hex } @@ -250,9 +276,9 @@ static void wiegand_sim_task(void *arg) { // lista fixa de idTags simuladas static const char *idtaglist[] = { - "0000004134962107", + "00000041349", "W2602312345", - "W34ABCDE12345", + "W34ABCDE123", }; const size_t list_size = sizeof(idtaglist) / sizeof(idtaglist[0]); @@ -263,7 +289,7 @@ static void wiegand_sim_task(void *arg) ESP_LOGI(TAG, "Simulação -> idTag: %s", idtaglist[i]); auth_process_tag(idtaglist[i]); - // espera 20 segundos entre cada tag + // espera 30 segundos entre cada tag vTaskDelay(pdMS_TO_TICKS(30000)); } } @@ -274,6 +300,8 @@ void initWiegand(void) ESP_LOGI(TAG, "Initializing Wiegand reader"); xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL); - // ESP_LOGI(TAG, "Inicializando Wiegand simulado"); - // xTaskCreate(wiegand_sim_task, "WiegandSim", configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL); + // Para testes, podes ativar o simulador: + //ESP_LOGI(TAG, "Inicializando Wiegand simulado"); + //xTaskCreate(wiegand_sim_task, "WiegandSim", + // configMINIMAL_STACK_SIZE * 3, NULL, 3, NULL); } diff --git a/components/buzzer/src/buzzer.c b/components/buzzer/src/buzzer.c index 0a2551d..300808d 100755 --- a/components/buzzer/src/buzzer.c +++ b/components/buzzer/src/buzzer.c @@ -18,50 +18,32 @@ #include #include -// ===================== Configuração padrão (pode migrar para Kconfig) ===================== -#ifndef CONFIG_BUZZER_GPIO +// ===================== Configuração padrão ===================== #define CONFIG_BUZZER_GPIO GPIO_NUM_27 -#endif -#ifndef CONFIG_BUZZER_MODE_PASSIVE // 1 = PASSIVE (PWM), 0 = ACTIVE (on/off) -#define CONFIG_BUZZER_MODE_PASSIVE 1 -#endif +// 1 = PASSIVE (PWM), 0 = ACTIVE (on/off) +#define CONFIG_BUZZER_MODE_PASSIVE 0 -#ifndef CONFIG_BUZZER_FREQ_HZ #define CONFIG_BUZZER_FREQ_HZ 3500 -#endif -#ifndef CONFIG_BUZZER_DUTY_PCT #define CONFIG_BUZZER_DUTY_PCT 70 -#endif -#ifndef CONFIG_BUZZER_QUEUE_LEN #define CONFIG_BUZZER_QUEUE_LEN 8 -#endif -#ifndef CONFIG_BUZZER_TASK_STACK #define CONFIG_BUZZER_TASK_STACK 2048 -#endif -#ifndef CONFIG_BUZZER_TASK_PRIO #define CONFIG_BUZZER_TASK_PRIO (tskIDLE_PRIORITY + 1) -#endif -#ifndef CONFIG_BUZZER_MIN_GAP_MS // anti-spam (gap mínimo entre toques) +// anti-spam (gap mínimo entre toques) #define CONFIG_BUZZER_MIN_GAP_MS 70 -#endif -#ifndef CONFIG_BUZZER_ENABLE_DEFAULT #define CONFIG_BUZZER_ENABLE_DEFAULT 1 -#endif -#ifndef CONFIG_BUZZER_QUIET_START_MIN // quiet hours start (minutos desde 00:00) +// quiet hours start (minutos desde 00:00) #define CONFIG_BUZZER_QUIET_START_MIN (22 * 60) -#endif -#ifndef CONFIG_BUZZER_QUIET_END_MIN // quiet hours end (minutos desde 00:00) +// quiet hours end (minutos desde 00:00) #define CONFIG_BUZZER_QUIET_END_MIN (7 * 60) -#endif // ===================== Tipos e estado ===================== static const char *TAG = "Buzzer"; @@ -415,11 +397,25 @@ static void network_event_handler(void *handler_args, esp_event_base_t base, int { if (base != NETWORK_EVENTS) return; - if (id == NETWORK_EVENT_AP_STARTED) + + ESP_LOGI(TAG, "Network event id=%d", (int)id); + + buzzer_event_data_t evt = {0}; + + switch ((network_event_id_t)id) { - buzzer_event_data_t evt = {.pattern = BUZZER_PATTERN_AP_START}; - esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY); + case NETWORK_EVENT_AP_STARTED: + case NETWORK_EVENT_STA_CONNECTED: + // Usa padrão de AP_START para indicar rede disponível + evt.pattern = BUZZER_PATTERN_AP_START; + break; + + default: + // Para já, ignorar outros eventos de rede + return; } + + esp_event_post(BUZZER_EVENTS, BUZZER_EVENT_PLAY_PATTERN, &evt, sizeof(evt), portMAX_DELAY); } static void auth_event_handler(void *arg, esp_event_base_t base, int32_t id, void *event_data) @@ -506,7 +502,7 @@ void buzzer_init(void) ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS, NETWORK_EVENT_AP_STARTED, network_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler, NULL)); ESP_LOGI(TAG, "Buzzer initialized on GPIO %d (%s), freq=%lu Hz, duty=%u%%, enabled=%d", s_buzzer_cfg.gpio, @@ -522,7 +518,7 @@ void buzzer_deinit(void) (void)esp_event_handler_unregister(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, evse_event_handler); (void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, auth_event_handler); (void)esp_event_handler_unregister(AUTH_EVENTS, AUTH_EVENT_TAG_SAVED, auth_event_handler); - (void)esp_event_handler_unregister(NETWORK_EVENTS, NETWORK_EVENT_AP_STARTED, network_event_handler); + (void)esp_event_handler_unregister(NETWORK_EVENTS, ESP_EVENT_ANY_ID, network_event_handler); if (s_buzzer_q) { diff --git a/components/config/board_config.c b/components/config/board_config.c index b6f34f6..a5b4b53 100755 --- a/components/config/board_config.c +++ b/components/config/board_config.c @@ -15,32 +15,6 @@ bool atob(const char *value) return value[0] == 'y'; } -board_config_energy_meter_t atoem(const char *value) -{ - if (!strcmp(value, "cur")) - { - return BOARD_CONFIG_ENERGY_METER_CUR; - } - if (!strcmp(value, "cur_vlt")) - { - return BOARD_CONFIG_ENERGY_METER_CUR_VLT; - } - return BOARD_CONFIG_ENERGY_METER_NONE; -} - -board_config_serial_t atoser(const char *value) -{ - if (!strcmp(value, "uart")) - { - return BOARD_CONFIG_SERIAL_UART; - } - if (!strcmp(value, "rs485")) - { - return BOARD_CONFIG_SERIAL_RS485; - } - return BOARD_CONFIG_SERIAL_NONE; -} - #define SET_CONFIG_VALUE(name, prop, convert_fn) \ if (!strcmp(key, name)) \ { \ @@ -94,16 +68,14 @@ void board_config_load() if (value != NULL) { SET_CONFIG_VALUE_STR("DEVICE_NAME", device_name); - SET_CONFIG_VALUE("LED_CHARGING", led_charging, atob); - SET_CONFIG_VALUE("LED_CHARGING_GPIO", led_charging_gpio, atoi); - SET_CONFIG_VALUE("LED_ERROR", led_error, atob); - SET_CONFIG_VALUE("LED_ERROR_GPIO", led_error_gpio, atoi); - SET_CONFIG_VALUE("LED_STOP", led_stop, atob); - SET_CONFIG_VALUE("LED_STOP_GPIO", led_stop_gpio, atoi); - + SET_CONFIG_VALUE("led_blue", led_blue, atob); + SET_CONFIG_VALUE("led_blue_GPIO", led_blue_gpio, atoi); + SET_CONFIG_VALUE("led_red", led_red, atob); + SET_CONFIG_VALUE("led_red_GPIO", led_red_gpio, atoi); + SET_CONFIG_VALUE("led_green", led_green, atob); + SET_CONFIG_VALUE("led_green_GPIO", led_green_gpio, atoi); SET_CONFIG_VALUE("BUZZER", buzzer, atob); SET_CONFIG_VALUE("BUZZER_GPIO", buzzer_gpio, atoi); - SET_CONFIG_VALUE("BUTTON_WIFI_GPIO", button_wifi_gpio, atoi); SET_CONFIG_VALUE("PILOT_PWM_GPIO", pilot_pwm_gpio, atoi); SET_CONFIG_VALUE("PILOT_ADC_CHANNEL", pilot_adc_channel, atoi); @@ -130,76 +102,6 @@ void board_config_load() SET_CONFIG_VALUE("RCM", rcm, atob); SET_CONFIG_VALUE("RCM_GPIO", rcm_gpio, atoi); SET_CONFIG_VALUE("RCM_TEST_GPIO", rcm_test_gpio, atoi); - SET_CONFIG_VALUE("ENERGY_METER", energy_meter, atoem); - SET_CONFIG_VALUE("ENERGY_METER_THREE_PHASES", energy_meter_three_phases, atob); - SET_CONFIG_VALUE("ENERGY_METER_L1_CUR_ADC_CHANNEL", energy_meter_l1_cur_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_L2_CUR_ADC_CHANNEL", energy_meter_l2_cur_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_L3_CUR_ADC_CHANNEL", energy_meter_l3_cur_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_CUR_SCALE", energy_meter_cur_scale, atoff); - SET_CONFIG_VALUE("ENERGY_METER_L1_VLT_ADC_CHANNEL", energy_meter_l1_vlt_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_L2_VLT_ADC_CHANNEL", energy_meter_l2_vlt_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_L3_VLT_ADC_CHANNEL", energy_meter_l3_vlt_adc_channel, atoi); - SET_CONFIG_VALUE("ENERGY_METER_VLT_SCALE", energy_meter_vlt_scale, atoff); - SET_CONFIG_VALUE("AUX_IN_1", aux_in_1, atob); - SET_CONFIG_VALUE_STR("AUX_IN_1_NAME", aux_in_1_name); - SET_CONFIG_VALUE("AUX_IN_1_GPIO", aux_in_1_gpio, atoi); - SET_CONFIG_VALUE("AUX_IN_2", aux_in_2, atob); - SET_CONFIG_VALUE_STR("AUX_IN_2_NAME", aux_in_2_name); - SET_CONFIG_VALUE("AUX_IN_2_GPIO", aux_in_2_gpio, atoi); - SET_CONFIG_VALUE("AUX_IN_3", aux_in_3, atob); - SET_CONFIG_VALUE_STR("AUX_IN_3_NAME", aux_in_3_name); - SET_CONFIG_VALUE("AUX_IN_3_GPIO", aux_in_3_gpio, atoi); - SET_CONFIG_VALUE("AUX_IN_4", aux_in_4, atob); - SET_CONFIG_VALUE_STR("AUX_IN_4_NAME", aux_in_4_name); - SET_CONFIG_VALUE("AUX_IN_4_GPIO", aux_in_4_gpio, atoi); - SET_CONFIG_VALUE("AUX_OUT_1", aux_out_1, atob); - SET_CONFIG_VALUE_STR("AUX_OUT_1_NAME", aux_out_1_name); - SET_CONFIG_VALUE("AUX_OUT_1_GPIO", aux_out_1_gpio, atoi); - SET_CONFIG_VALUE("AUX_OUT_2", aux_out_2, atob); - SET_CONFIG_VALUE_STR("AUX_OUT_2_NAME", aux_out_2_name); - SET_CONFIG_VALUE("AUX_OUT_2_GPIO", aux_out_2_gpio, atoi); - SET_CONFIG_VALUE("AUX_OUT_3", aux_out_3, atob); - SET_CONFIG_VALUE_STR("AUX_OUT_3_NAME", aux_out_3_name); - SET_CONFIG_VALUE("AUX_OUT_3_GPIO", aux_out_3_gpio, atoi); - SET_CONFIG_VALUE("AUX_OUT_4", aux_out_4, atob); - SET_CONFIG_VALUE_STR("AUX_OUT_4_NAME", aux_out_4_name); - SET_CONFIG_VALUE("AUX_OUT_4_GPIO", aux_out_4_gpio, atoi); - SET_CONFIG_VALUE("AUX_AIN_1", aux_ain_1, atob); - SET_CONFIG_VALUE_STR("AUX_AIN_1_NAME", aux_ain_1_name); - SET_CONFIG_VALUE("AUX_AIN_1_ADC_CHANNEL", aux_ain_1_adc_channel, atoi); - SET_CONFIG_VALUE("AUX_AIN_2", aux_ain_2, atob); - SET_CONFIG_VALUE_STR("AUX_AIN_2_NAME", aux_ain_2_name); - SET_CONFIG_VALUE("AUX_AIN_2_ADC_CHANNEL", aux_ain_2_adc_channel, atoi); - /* - #if CONFIG_ESP_CONSOLE_UART_NUM != 0 - SET_CONFIG_VALUE("SERIAL_1", serial_1, atoser); - SET_CONFIG_VALUE_STR("SERIAL_1_NAME", serial_1_name); - SET_CONFIG_VALUE("SERIAL_1_RXD_GPIO", serial_1_rxd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_1_TXD_GPIO", serial_1_txd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_1_RTS_GPIO", serial_1_rts_gpio, atoi); - #endif // CONFIG_ESP_CONSOLE_UART_NUM != 0 - #if CONFIG_ESP_CONSOLE_UART_NUM != 1 - SET_CONFIG_VALUE("SERIAL_2", serial_2, atoser); - SET_CONFIG_VALUE_STR("SERIAL_2_NAME", serial_2_name); - SET_CONFIG_VALUE("SERIAL_2_RXD_GPIO", serial_2_rxd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_2_TXD_GPIO", serial_2_txd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_2_RTS_GPIO", serial_2_rts_gpio, atoi); - #endif // CONFIG_ESP_CONSOLE_UART_NUM != 1 - #if SOC_UART_NUM > 2 - #if CONFIG_ESP_CONSOLE_UART_NUM != 2 - SET_CONFIG_VALUE("SERIAL_3", serial_3, atoser); - SET_CONFIG_VALUE_STR("SERIAL_3_NAME", serial_3_name); - SET_CONFIG_VALUE("SERIAL_3_RXD_GPIO", serial_3_rxd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_3_TXD_GPIO", serial_3_txd_gpio, atoi); - SET_CONFIG_VALUE("SERIAL_3_RTS_GPIO", serial_3_rts_gpio, atoi); - #endif / CONFIG_ESP_CONSOLE_UART_NUM != 2 - #endif // SOC_UART_NUM > 2 - SET_CONFIG_VALUE("ONEWIRE", onewire, atob); - SET_CONFIG_VALUE("ONEWIRE_GPIO", onewire_gpio, atoi); - SET_CONFIG_VALUE("ONEWIRE_TEMP_SENSOR", onewire_temp_sensor, atob); - - ESP_LOGE(TAG, "Unknown config value %s=%s", key, value); - */ } } } diff --git a/components/config/include/board_config.h b/components/config/include/board_config.h index ca70185..99fe001 100755 --- a/components/config/include/board_config.h +++ b/components/config/include/board_config.h @@ -5,13 +5,15 @@ #include "hal/gpio_types.h" #include "soc/soc_caps.h" -typedef enum { +typedef enum +{ BOARD_CONFIG_ENERGY_METER_NONE, BOARD_CONFIG_ENERGY_METER_CUR, BOARD_CONFIG_ENERGY_METER_CUR_VLT } board_config_energy_meter_t; -typedef enum { +typedef enum +{ BOARD_CONFIG_SERIAL_NONE, BOARD_CONFIG_SERIAL_UART, BOARD_CONFIG_SERIAL_RS485 @@ -21,12 +23,12 @@ typedef struct { char device_name[32]; - bool led_charging : 1; - gpio_num_t led_charging_gpio; - bool led_error : 1; - gpio_num_t led_error_gpio; - bool led_stop : 1; - gpio_num_t led_stop_gpio; + bool led_blue : 1; + gpio_num_t led_blue_gpio; + bool led_red : 1; + gpio_num_t led_red_gpio; + bool led_green : 1; + gpio_num_t led_green_gpio; bool buzzer : 1; gpio_num_t buzzer_gpio; @@ -63,80 +65,6 @@ typedef struct gpio_num_t rcm_gpio; gpio_num_t rcm_test_gpio; - board_config_energy_meter_t energy_meter; - bool energy_meter_three_phases : 1; - - adc_channel_t energy_meter_l1_cur_adc_channel; - adc_channel_t energy_meter_l2_cur_adc_channel; - adc_channel_t energy_meter_l3_cur_adc_channel; - float energy_meter_cur_scale; - adc_channel_t energy_meter_l1_vlt_adc_channel; - adc_channel_t energy_meter_l2_vlt_adc_channel; - adc_channel_t energy_meter_l3_vlt_adc_channel; - float energy_meter_vlt_scale; - - - bool aux_in_1 : 1; - char aux_in_1_name[8]; - gpio_num_t aux_in_1_gpio; - - bool aux_in_2 : 1; - char aux_in_2_name[8]; - gpio_num_t aux_in_2_gpio; - - bool aux_in_3 : 1; - char aux_in_3_name[8]; - gpio_num_t aux_in_3_gpio; - - bool aux_in_4 : 1; - char aux_in_4_name[8]; - gpio_num_t aux_in_4_gpio; - - bool aux_out_1 : 1; - char aux_out_1_name[8]; - gpio_num_t aux_out_1_gpio; - - bool aux_out_2 : 1; - char aux_out_2_name[8]; - gpio_num_t aux_out_2_gpio; - - bool aux_out_3 : 1; - char aux_out_3_name[8]; - gpio_num_t aux_out_3_gpio; - - bool aux_out_4 : 1; - char aux_out_4_name[8]; - gpio_num_t aux_out_4_gpio; - - bool aux_ain_1 : 1; - char aux_ain_1_name[8]; - adc_channel_t aux_ain_1_adc_channel; - - bool aux_ain_2 : 1; - char aux_ain_2_name[8]; - adc_channel_t aux_ain_2_adc_channel; - - board_config_serial_t serial_1; - char serial_1_name[16]; - gpio_num_t serial_1_rxd_gpio; - gpio_num_t serial_1_txd_gpio; - gpio_num_t serial_1_rts_gpio; - board_config_serial_t serial_2; - char serial_2_name[16]; - gpio_num_t serial_2_rxd_gpio; - gpio_num_t serial_2_txd_gpio; - gpio_num_t serial_2_rts_gpio; -#if SOC_UART_NUM > 2 - board_config_serial_t serial_3; - char serial_3_name[16]; - gpio_num_t serial_3_rxd_gpio; - gpio_num_t serial_3_txd_gpio; - gpio_num_t serial_3_rts_gpio; -#endif /* SOC_UART_NUM > 2 */ - - bool onewire : 1; - gpio_num_t onewire_gpio; - bool onewire_temp_sensor : 1; } board_config_t; extern board_config_t board_config; diff --git a/components/esp_idf_lib_helpers/.eil.yml b/components/esp_idf_lib_helpers/.eil.yml deleted file mode 100755 index eb4f9e0..0000000 --- a/components/esp_idf_lib_helpers/.eil.yml +++ /dev/null @@ -1,20 +0,0 @@ -name: esp_idf_lib_helpers -description: Common support library for esp-idf-lib -version: 1.2.0 -groups: - - common -code_owners: - - trombik - - UncleRus -depends: - - freertos -thread_safe: n/a -targets: - - esp32 - - esp8266 - - esp32s2 - - esp32c3 -license: ISC -copyrights: - - name: trombik - year: 2019 diff --git a/components/esp_idf_lib_helpers/CMakeLists.txt b/components/esp_idf_lib_helpers/CMakeLists.txt deleted file mode 100755 index 44bd43a..0000000 --- a/components/esp_idf_lib_helpers/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -idf_component_register( - INCLUDE_DIRS . - REQUIRES freertos -) diff --git a/components/esp_idf_lib_helpers/LICENSE b/components/esp_idf_lib_helpers/LICENSE deleted file mode 100755 index f81d3d2..0000000 --- a/components/esp_idf_lib_helpers/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright (c) 2019 Tomoyuki Sakurai - -Permission to use, copy, modify, and distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/components/esp_idf_lib_helpers/component.mk b/components/esp_idf_lib_helpers/component.mk deleted file mode 100755 index c03a32e..0000000 --- a/components/esp_idf_lib_helpers/component.mk +++ /dev/null @@ -1,8 +0,0 @@ -COMPONENT_ADD_INCLUDEDIRS = . - -ifdef CONFIG_IDF_TARGET_ESP8266 -COMPONENT_DEPENDS = esp8266 freertos -else -COMPONENT_DEPENDS = freertos -endif - diff --git a/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h b/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h deleted file mode 100755 index 2c74e3b..0000000 --- a/components/esp_idf_lib_helpers/esp_idf_lib_helpers.h +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright (c) 2019 Tomoyuki Sakurai - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -#if !defined(__ESP_IDF_LIB_HELPERS__H__) -#define __ESP_IDF_LIB_HELPERS__H__ - -/* XXX this header file does not need to include freertos/FreeRTOS.h. - * but without it, ESP8266 RTOS SDK does not include `sdkconfig.h` in correct - * order. as this header depends on sdkconfig.h, sdkconfig.h must be included - * first. however, the SDK includes this header first, then includes - * `sdkconfig.h` when freertos/FreeRTOS.h is not explicitly included. an - * evidence can be found in `build/${COMPONENT}/${COMPONENT}.d` in a failed - * build. - */ -#include -#include - -#if !defined(ESP_IDF_VERSION) || !defined(ESP_IDF_VERSION_VAL) -#error Unknown ESP-IDF/ESP8266 RTOS SDK version -#endif - -/* Minimal supported version for ESP32, ESP32S2 */ -#define HELPER_ESP32_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 5) -/* Minimal supported version for ESP8266 */ -#define HELPER_ESP8266_MIN_VER ESP_IDF_VERSION_VAL(3, 3, 0) - -/* HELPER_TARGET_IS_ESP32 - * 1 when the target is esp32 - */ -#if defined(CONFIG_IDF_TARGET_ESP32) \ - || defined(CONFIG_IDF_TARGET_ESP32S2) \ - || defined(CONFIG_IDF_TARGET_ESP32S3) \ - || defined(CONFIG_IDF_TARGET_ESP32C2) \ - || defined(CONFIG_IDF_TARGET_ESP32C3) \ - || defined(CONFIG_IDF_TARGET_ESP32C6) \ - || defined(CONFIG_IDF_TARGET_ESP32H2) -#define HELPER_TARGET_IS_ESP32 (1) -#define HELPER_TARGET_IS_ESP8266 (0) - -/* HELPER_TARGET_IS_ESP8266 - * 1 when the target is esp8266 - */ -#elif defined(CONFIG_IDF_TARGET_ESP8266) -#define HELPER_TARGET_IS_ESP32 (0) -#define HELPER_TARGET_IS_ESP8266 (1) -#else -#error BUG: cannot determine the target -#endif - -#if HELPER_TARGET_IS_ESP32 && ESP_IDF_VERSION < HELPER_ESP32_MIN_VER -#error Unsupported ESP-IDF version. Please update! -#endif - -#if HELPER_TARGET_IS_ESP8266 && ESP_IDF_VERSION < HELPER_ESP8266_MIN_VER -#error Unsupported ESP8266 RTOS SDK version. Please update! -#endif - -/* show the actual values for debugging */ -#if DEBUG -#define VALUE_TO_STRING(x) #x -#define VALUE(x) VALUE_TO_STRING(x) -#define VAR_NAME_VALUE(var) #var "=" VALUE(var) -#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32C3)) -#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32H2)) -#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32S2)) -#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP32)) -#pragma message(VAR_NAME_VALUE(CONFIG_IDF_TARGET_ESP8266)) -#pragma message(VAR_NAME_VALUE(ESP_IDF_VERSION_MAJOR)) -#endif - -#endif diff --git a/components/esp_idf_lib_helpers/ets_sys.h b/components/esp_idf_lib_helpers/ets_sys.h deleted file mode 100755 index eb83481..0000000 --- a/components/esp_idf_lib_helpers/ets_sys.h +++ /dev/null @@ -1,21 +0,0 @@ -#if CONFIG_IDF_TARGET_ESP32 -#include -#elif CONFIG_IDF_TARGET_ESP32C2 -#include -#elif CONFIG_IDF_TARGET_ESP32C3 -#include -#elif CONFIG_IDF_TARGET_ESP32C6 -#include -#elif CONFIG_IDF_TARGET_ESP32H2 -#include -#elif CONFIG_IDF_TARGET_ESP32H4 -#include -#elif CONFIG_IDF_TARGET_ESP32S2 -#include -#elif CONFIG_IDF_TARGET_ESP32S3 -#include -#elif CONFIG_IDF_TARGET_ESP8266 -#include -#else -#error "ets_sys: Unknown target" -#endif diff --git a/components/evse/CMakeLists.txt b/components/evse/CMakeLists.txt index 8cd1cc8..831e236 100755 --- a/components/evse/CMakeLists.txt +++ b/components/evse/CMakeLists.txt @@ -18,5 +18,5 @@ idf_component_register( SRCS ${srcs} INCLUDE_DIRS "include" PRIV_REQUIRES nvs_flash driver - REQUIRES peripherals auth loadbalancer + REQUIRES peripherals auth loadbalancer scheduler ) \ No newline at end of file diff --git a/components/evse/evse_config.c b/components/evse/evse_config.c index 1ae6bae..7b01aa7 100755 --- a/components/evse/evse_config.c +++ b/components/evse/evse_config.c @@ -2,7 +2,7 @@ #include "evse_config.h" #include "board_config.h" #include "evse_limits.h" -#include "evse_api.h" // <— para evse_get_state / evse_state_is_charging +#include "evse_api.h" #include "esp_log.h" #include "nvs.h" #include "esp_timer.h" @@ -74,8 +74,8 @@ void evse_check_defaults(void) charging_current = u16; } - // Runtime charging current initialized from persisted default - charging_current_runtime = max_charging_current; + // Runtime charging current inicializado a partir do default persistido + charging_current_runtime = charging_current; ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime); // Auth required @@ -150,7 +150,6 @@ void evse_check_defaults(void) ESP_LOGW(TAG, "Missing 'enabled' -> default=true (persisted)."); } - // Save to NVS if needed if (needs_commit) { err = nvs_commit(nvs); @@ -218,7 +217,6 @@ esp_err_t evse_set_default_charging_current(uint16_t value) // ======================== void evse_set_runtime_charging_current(uint16_t value) { - if (value > max_charging_current) { value = max_charging_current; @@ -232,7 +230,6 @@ void evse_set_runtime_charging_current(uint16_t value) ESP_LOGI(TAG, "Runtime charging current updated: %d", charging_current_runtime); - // --- PUBLICA ALTERAÇÃO DE CONFIG DO EVSE --- evse_config_event_data_t evt = { .charging = evse_state_is_charging(evse_get_state()), .hw_max_current = (float)evse_get_max_charging_current(), @@ -314,7 +311,6 @@ void evse_config_set_available(bool available) { is_available = available ? true : false; - // Persist esp_err_t err = nvs_set_u8(nvs, "available", (uint8_t)is_available); if (err == ESP_OK) err = nvs_commit(nvs); @@ -323,7 +319,6 @@ void evse_config_set_available(bool available) ESP_LOGE(TAG, "Failed to persist 'available': %s", esp_err_to_name(err)); } - // AVAILABLE_UPDATED evse_available_event_data_t e = { .available = is_available, .timestamp_us = esp_timer_get_time()}; @@ -342,7 +337,6 @@ void evse_config_set_enabled(bool enabled) { is_enabled = enabled ? true : false; - // Persist esp_err_t err = nvs_set_u8(nvs, "enabled", (uint8_t)is_enabled); if (err == ESP_OK) err = nvs_commit(nvs); @@ -351,7 +345,6 @@ void evse_config_set_enabled(bool enabled) ESP_LOGE(TAG, "Failed to persist 'enabled': %s", esp_err_to_name(err)); } - // ENABLE_UPDATED evse_enable_event_data_t e = { .enabled = is_enabled, .timestamp_us = esp_timer_get_time()}; diff --git a/components/evse/evse_core.c b/components/evse/evse_core.c index acef213..857d820 100755 --- a/components/evse/evse_core.c +++ b/components/evse/evse_core.c @@ -1,5 +1,3 @@ -// evse_core.c - Main EVSE control logic - #include "evse_fsm.h" #include "evse_error.h" #include "evse_limits.h" @@ -16,29 +14,34 @@ static const char *TAG = "evse_core"; static SemaphoreHandle_t mutex; static evse_state_t last_state = EVSE_STATE_A; +static void evse_process(void); static void evse_core_task(void *arg); -// ================================ -// Initialization -// ================================ - -void evse_init(void) { +void evse_init(void) +{ ESP_LOGI(TAG, "EVSE Init"); - mutex = xSemaphoreCreateMutex(); // Optional: use static version for deterministic memory + mutex = xSemaphoreCreateMutex(); + if (!mutex) + { + ESP_LOGE(TAG, "Failed to create EVSE core mutex"); + return; + } evse_check_defaults(); evse_fsm_reset(); - pilot_set_level(true); // Enable pilot output + pilot_set_level(true); xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL); } -// ================================ -// Main Processing Logic -// ================================ +static void evse_process(void) +{ + if (!mutex) + { + return; + } -void evse_process(void) { xSemaphoreTake(mutex, portMAX_DELAY); pilot_voltage_t pilot_voltage; @@ -49,34 +52,38 @@ void evse_process(void) { evse_error_check(pilot_voltage, is_n12v); - // Só chama FSM, que decide tudo evse_fsm_process( pilot_voltage, evse_state_get_authorized(), evse_config_is_available(), - evse_config_is_enabled() - ); + evse_config_is_enabled()); evse_limits_check(); - evse_state_t current = evse_get_state(); - if (current != last_state) { - //ESP_LOGI(TAG, "State changed: %s → %s", evse_state_to_str(last_state), evse_state_to_str(current)); - last_state = current; + if (evse_is_limit_reached()) + { + if (evse_state_get_authorized()) + { + ESP_LOGW(TAG, "Charging limit reached → revoking authorization"); + evse_state_set_authorized(false); + } } - evse_mark_error_cleared(); + evse_state_t current = evse_get_state(); + if (current != last_state) + { + last_state = current; + } xSemaphoreGive(mutex); } -// ================================ -// Background Task -// ================================ - -static void evse_core_task(void *arg) { - while (true) { +static void evse_core_task(void *arg) +{ + (void)arg; + while (true) + { evse_process(); - vTaskDelay(pdMS_TO_TICKS(100)); // 10 Hz cycle + vTaskDelay(pdMS_TO_TICKS(100)); } -} \ No newline at end of file +} diff --git a/components/evse/evse_error.c b/components/evse/evse_error.c index 4c45065..eca88f1 100755 --- a/components/evse/evse_error.c +++ b/components/evse/evse_error.c @@ -3,118 +3,227 @@ #include "freertos/FreeRTOS.h" #include "freertos/task.h" +#include "freertos/portmacro.h" #include "esp_log.h" #include "ntc_sensor.h" static const char *TAG = "evse_error"; +// Estado global de erros static uint32_t error_bits = 0; static TickType_t auto_clear_timeout = 0; + +// Sticky flag: "todos erros foram limpos" static bool error_cleared = false; -void evse_error_init(void) { - // Inicialização do sistema de erros +// Proteção contra concorrência +static portMUX_TYPE error_mux = portMUX_INITIALIZER_UNLOCKED; + +void evse_error_init(void) +{ + portENTER_CRITICAL(&error_mux); + error_bits = 0; + auto_clear_timeout = 0; + error_cleared = false; + portEXIT_CRITICAL(&error_mux); } -void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v) { - ESP_LOGD(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", +void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v) +{ + ESP_LOGD(TAG, "Verificando erro: pilot_voltage=%d, is_n12v=%s", pilot_voltage, is_n12v ? "true" : "false"); - // Falha elétrica geral no pilot - if (pilot_voltage == PILOT_VOLTAGE_1) { - if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT)) { // Verifica se o erro já foi registrado - evse_error_set(EVSE_ERR_PILOT_FAULT_BIT); + // 1) Falha elétrica geral no pilot + if (pilot_voltage == PILOT_VOLTAGE_1) + { + bool first_time = false; + + portENTER_CRITICAL(&error_mux); + if (!(error_bits & EVSE_ERR_PILOT_FAULT_BIT)) + { + error_cleared = false; + error_bits |= EVSE_ERR_PILOT_FAULT_BIT; + first_time = true; + } + portEXIT_CRITICAL(&error_mux); + + if (first_time) + { ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)"); } } + else + { + // Pilot voltou a nível válido → limpa erro de pilot fault + evse_error_clear(EVSE_ERR_PILOT_FAULT_BIT); + } - // Falta de -12V durante PWM (C ou D) - if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v) { - if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT)) { // Verifica se o erro já foi registrado - evse_error_set(EVSE_ERR_DIODE_SHORT_BIT); - ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)"); - ESP_LOGW(TAG, "Verificando erro: pilot_voltage = %d, is_n12v = %s", pilot_voltage, is_n12v ? "true" : "false"); + // 2) Falta de -12V durante PWM (C ou D) + if ((pilot_voltage == PILOT_VOLTAGE_6 || pilot_voltage == PILOT_VOLTAGE_3) && !is_n12v) + { + bool first_time = false; + + portENTER_CRITICAL(&error_mux); + if (!(error_bits & EVSE_ERR_DIODE_SHORT_BIT)) + { + error_cleared = false; + error_bits |= EVSE_ERR_DIODE_SHORT_BIT; + auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); + first_time = true; } + portEXIT_CRITICAL(&error_mux); + + if (first_time) + { + ESP_LOGW(TAG, "Erro: ausência de -12V no PWM (sem diodo)"); + } + } + else + { + // Se já não estamos em C/D sem -12V, limpa o erro de diodo curto + evse_error_clear(EVSE_ERR_DIODE_SHORT_BIT); } } -void evse_temperature_check(void) { - float temp_c = ntc_temp_sensor(); // leitura atual (última medida válida) - uint8_t threshold = evse_get_temp_threshold(); // padrão 60°C, configurável +void evse_temperature_check(void) +{ + float temp_c = ntc_temp_sensor(); + uint8_t threshold = evse_get_temp_threshold(); - // Log informativo com os valores - ESP_LOGD(TAG, "Verificando temperatura: atual = %.2f °C, limite = %d °C", temp_c, threshold); + ESP_LOGD(TAG, "Verificando temperatura: atual=%.2f °C, limite=%d °C", + temp_c, threshold); - // Se a temperatura parecer inválida, aplica erro de sensor - if (temp_c < -40.0f || temp_c > 150.0f) { - if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT)) { // Verifica se o erro já foi registrado - evse_error_set(EVSE_ERR_TEMPERATURE_FAULT_BIT); + // Temperatura inválida -> erro de sensor + if (temp_c < -40.0f || temp_c > 150.0f) + { + bool first_time = false; + + portENTER_CRITICAL(&error_mux); + if (!(error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT)) + { + error_cleared = false; + error_bits |= EVSE_ERR_TEMPERATURE_FAULT_BIT; + first_time = true; + } + portEXIT_CRITICAL(&error_mux); + + if (first_time) + { ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado"); } return; } - evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida + // Leitura válida -> limpa erro de sensor + evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); - if (temp_c >= threshold) { - if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT)) { // Verifica se o erro já foi registrado - evse_error_set(EVSE_ERR_TEMPERATURE_HIGH_BIT); - ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C", temp_c, threshold); + // Temperatura máxima + if (temp_c >= threshold) + { + bool first_time = false; + + portENTER_CRITICAL(&error_mux); + if (!(error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT)) + { + error_cleared = false; + error_bits |= EVSE_ERR_TEMPERATURE_HIGH_BIT; + auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); + first_time = true; } - } else { + portEXIT_CRITICAL(&error_mux); + + if (first_time) + { + ESP_LOGW(TAG, "Temperatura acima do limite: %.2f °C ≥ %d °C", + temp_c, threshold); + } + } + else + { evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT); } } -uint32_t evse_get_error(void) { - return error_bits; +uint32_t evse_get_error(void) +{ + portENTER_CRITICAL(&error_mux); + uint32_t val = error_bits; + portEXIT_CRITICAL(&error_mux); + return val; } -bool evse_is_error_cleared(void) { - return error_cleared; +bool evse_error_cleared_flag(void) +{ + portENTER_CRITICAL(&error_mux); + bool v = error_cleared; + portEXIT_CRITICAL(&error_mux); + return v; } -void evse_mark_error_cleared(void) { +void evse_error_reset_flag(void) +{ + portENTER_CRITICAL(&error_mux); error_cleared = false; + portEXIT_CRITICAL(&error_mux); } -// Já existentes -void evse_error_set(uint32_t bitmask) { +void evse_error_set(uint32_t bitmask) +{ + portENTER_CRITICAL(&error_mux); + + error_cleared = false; error_bits |= bitmask; - if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) { + if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) + { auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s } + + portEXIT_CRITICAL(&error_mux); } -void evse_error_clear(uint32_t bitmask) { - bool had_error = error_bits != 0; +void evse_error_clear(uint32_t bitmask) +{ + portENTER_CRITICAL(&error_mux); + + bool had_error = (error_bits != 0); error_bits &= ~bitmask; - if (had_error && error_bits == 0) { + if (had_error && error_bits == 0) + { error_cleared = true; } + + portEXIT_CRITICAL(&error_mux); } -void evse_error_tick(void) { - if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) && xTaskGetTickCount() >= auto_clear_timeout) { - evse_error_clear(EVSE_ERR_AUTO_CLEAR_BITS); +void evse_error_tick(void) +{ + portENTER_CRITICAL(&error_mux); + + if ((error_bits & EVSE_ERR_AUTO_CLEAR_BITS) && + auto_clear_timeout != 0 && + xTaskGetTickCount() >= auto_clear_timeout) + { + error_bits &= ~EVSE_ERR_AUTO_CLEAR_BITS; + + if (error_bits == 0) + { + error_cleared = true; + } + auto_clear_timeout = 0; } + + portEXIT_CRITICAL(&error_mux); } -bool evse_error_is_active(void) { - return error_bits != 0; +bool evse_error_is_active(void) +{ + return evse_get_error() != 0; } -uint32_t evse_error_get_bits(void) { - return error_bits; -} - -void evse_error_reset_flag(void) { - error_cleared = false; -} - -bool evse_error_cleared_flag(void) { - return error_cleared; +uint32_t evse_error_get_bits(void) +{ + return evse_get_error(); } diff --git a/components/evse/evse_fsm.c b/components/evse/evse_fsm.c index 875a8d1..547c669 100755 --- a/components/evse/evse_fsm.c +++ b/components/evse/evse_fsm.c @@ -20,94 +20,108 @@ static const char *TAG = "evse_fsm"; static bool c1_d1_waiting = false; static TickType_t c1_d1_relay_to = 0; -void evse_fsm_reset(void) { +void evse_fsm_reset(void) +{ evse_set_state(EVSE_STATE_A); c1_d1_waiting = false; c1_d1_relay_to = 0; } -// ... includes e defines como já estão - -static void update_outputs(evse_state_t state) { +static void update_outputs(evse_state_t state) +{ const uint16_t current = evse_get_runtime_charging_current(); uint8_t cable_max_current = evse_get_max_charging_current(); const bool socket_outlet = evse_get_socket_outlet(); - if (socket_outlet) { + if (socket_outlet) + { cable_max_current = proximity_get_max_current(); } // Segurança: relé sempre off e outputs seguros em caso de erro - if (evse_get_error() != 0) { - if (ac_relay_get_state()) { + if (evse_get_error() != 0) + { + if (ac_relay_get_state()) + { ac_relay_set_state(false); ESP_LOGW(TAG, "ERRO ativo: relé estava ligado, agora desligado por segurança!"); } - ac_relay_set_state(false); // redundância tolerável - pilot_set_level(true); // sinal pilot sempre 12V (A) - if (board_config.socket_lock && socket_outlet) { + ac_relay_set_state(false); + pilot_set_level(true); + if (board_config.socket_lock && socket_outlet) + { socket_lock_set_locked(false); } return; } // Fluxo normal - switch (state) { - case EVSE_STATE_A: - case EVSE_STATE_E: - case EVSE_STATE_F: - ac_relay_set_state(false); - pilot_set_level(state == EVSE_STATE_A); - if (board_config.socket_lock && socket_outlet) { - socket_lock_set_locked(false); - } - break; - - case EVSE_STATE_B1: - pilot_set_level(true); - ac_relay_set_state(false); - if (board_config.socket_lock && socket_outlet) { - socket_lock_set_locked(true); - } - - if (rcm_test()) { - //ESP_LOGI(TAG, "RCM self test passed"); - } else { - //ESP_LOGW(TAG, "RCM self test failed"); - } - break; - - case EVSE_STATE_B2: - pilot_set_amps(MIN(current, cable_max_current)); - ac_relay_set_state(false); - break; - - case EVSE_STATE_C1: - case EVSE_STATE_D1: { - pilot_set_amps(MIN(current, cable_max_current)); // mantém PWM - ac_relay_set_state(false); // relé ainda desligado - c1_d1_waiting = true; - c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000); - break; + switch (state) + { + case EVSE_STATE_A: + case EVSE_STATE_E: + case EVSE_STATE_F: + ac_relay_set_state(false); + pilot_set_level(state == EVSE_STATE_A); + if (board_config.socket_lock && socket_outlet) + { + socket_lock_set_locked(false); } - case EVSE_STATE_C2: - case EVSE_STATE_D2: - pilot_set_amps(MIN(current, cable_max_current)); - ac_relay_set_state(true); // Só chega aqui se não há erro! - break; + break; + + case EVSE_STATE_B1: + pilot_set_level(true); + ac_relay_set_state(false); + if (board_config.socket_lock && socket_outlet) + { + socket_lock_set_locked(true); + } + + if (rcm_test()) + { + // ESP_LOGI(TAG, "RCM self test passed"); + } + else + { + // ESP_LOGW(TAG, "RCM self test failed"); + } + break; + + case EVSE_STATE_B2: + pilot_set_amps(MIN(current, cable_max_current)); + ac_relay_set_state(false); + break; + + case EVSE_STATE_C1: + case EVSE_STATE_D1: + { + pilot_set_amps(MIN(current, cable_max_current)); + ac_relay_set_state(false); + c1_d1_waiting = true; + c1_d1_relay_to = xTaskGetTickCount() + pdMS_TO_TICKS(6000); + break; + } + + case EVSE_STATE_C2: + case EVSE_STATE_D2: + pilot_set_amps(MIN(current, cable_max_current)); + ac_relay_set_state(true); + break; } } -// FSM principal - centraliza a lógica de erro e de todos os estados +// FSM principal void evse_fsm_process( pilot_voltage_t pilot_voltage, bool authorized, bool available, - bool enabled -) { + bool enabled) +{ // Proteção total: erro força F sempre! - if (evse_get_error() != 0) { - if (evse_get_state() != EVSE_STATE_F) { + if (evse_get_error() != 0) + { + if (evse_get_state() != EVSE_STATE_F) + { ESP_LOGW(TAG, "Erro ativo detectado: forçando estado FAULT (F)"); evse_set_state(EVSE_STATE_F); } @@ -119,91 +133,97 @@ void evse_fsm_process( evse_state_t prev = evse_get_state(); evse_state_t curr = prev; - switch (curr) { - case EVSE_STATE_A: - if (!available) { - evse_set_state(EVSE_STATE_F); - } else if (pilot_voltage == PILOT_VOLTAGE_9) { - evse_set_state(EVSE_STATE_B1); - } - break; + switch (curr) + { + case EVSE_STATE_A: + if (!available) + { + evse_set_state(EVSE_STATE_F); + } + else if (pilot_voltage == PILOT_VOLTAGE_9) + { + evse_set_state(EVSE_STATE_B1); + } + break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - if (!available) { + case EVSE_STATE_B1: + case EVSE_STATE_B2: + if (!available) + { + evse_set_state(EVSE_STATE_F); + break; + } + switch (pilot_voltage) + { + case PILOT_VOLTAGE_12: + evse_set_state(EVSE_STATE_A); + break; + case PILOT_VOLTAGE_9: + evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); + break; + case PILOT_VOLTAGE_6: + evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); + break; + default: + break; + } + break; + + case EVSE_STATE_C1: + case EVSE_STATE_D1: + if (c1_d1_waiting && now >= c1_d1_relay_to) + { + ac_relay_set_state(false); + c1_d1_waiting = false; + if (!available) + { evse_set_state(EVSE_STATE_F); break; } - switch (pilot_voltage) { - case PILOT_VOLTAGE_12: - evse_set_state(EVSE_STATE_A); - break; - case PILOT_VOLTAGE_9: - evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); - break; - case PILOT_VOLTAGE_6: - evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); - break; - default: - break; - } - break; + } + __attribute__((fallthrough)); - case EVSE_STATE_C1: - case EVSE_STATE_D1: - if (c1_d1_waiting && now >= c1_d1_relay_to) { - ac_relay_set_state(false); - c1_d1_waiting = false; - if (!available) { - evse_set_state(EVSE_STATE_F); - break; - } - } - __attribute__((fallthrough)); - - case EVSE_STATE_C2: - case EVSE_STATE_D2: - if (!enabled || !available) { - evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1) - ? EVSE_STATE_D1 : EVSE_STATE_C1); - break; - } - switch (pilot_voltage) { - case PILOT_VOLTAGE_6: - evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); - break; - case PILOT_VOLTAGE_3: - evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1); - break; - case PILOT_VOLTAGE_9: - evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); - break; - case PILOT_VOLTAGE_12: - evse_set_state(EVSE_STATE_A); - break; - default: - break; - } + case EVSE_STATE_C2: + case EVSE_STATE_D2: + if (!enabled || !available) + { + evse_set_state((curr == EVSE_STATE_D2 || curr == EVSE_STATE_D1) + ? EVSE_STATE_D1 + : EVSE_STATE_C1); break; - - case EVSE_STATE_E: - // Estado elétrico grave: só reset manual + } + switch (pilot_voltage) + { + case PILOT_VOLTAGE_6: + evse_set_state((authorized && enabled) ? EVSE_STATE_C2 : EVSE_STATE_C1); break; - - case EVSE_STATE_F: - // Fault: só sai se disponível e sem erro - if (available && evse_get_error() == 0) { - evse_set_state(EVSE_STATE_A); - } + case PILOT_VOLTAGE_3: + evse_set_state((authorized && enabled) ? EVSE_STATE_D2 : EVSE_STATE_D1); break; + case PILOT_VOLTAGE_9: + evse_set_state((authorized && enabled) ? EVSE_STATE_B2 : EVSE_STATE_B1); + break; + case PILOT_VOLTAGE_12: + evse_set_state(EVSE_STATE_A); + break; + default: + break; + } + break; + + case EVSE_STATE_E: + // Estado elétrico grave: só reset manual + break; + + case EVSE_STATE_F: + // Fault: só sai se disponível e sem erro + if (available && evse_get_error() == 0) + { + evse_set_state(EVSE_STATE_A); + } + break; } evse_state_t next = evse_get_state(); update_outputs(next); - - if (next != prev) { - ESP_LOGI(TAG, "State changed: %s -> %s", - evse_state_to_str(prev), - evse_state_to_str(next)); - } } diff --git a/components/evse/evse_limits.c b/components/evse/evse_limits.c index 18c86b9..11ad5ab 100755 --- a/components/evse/evse_limits.c +++ b/components/evse/evse_limits.c @@ -7,14 +7,8 @@ #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" - - -// ======================== -// External state references -// ======================== - -//extern evse_state_t current_state; // Current EVSE FSM state -//extern TickType_t session_start_tick; // Timestamp of charging session start +#include "esp_err.h" +#include "nvs.h" // ======================== // Concurrency protection @@ -26,24 +20,17 @@ static portMUX_TYPE evse_mux = portMUX_INITIALIZER_UNLOCKED; // Runtime state (volatile) // ======================== -static bool limit_reached = false; -static uint32_t consumption_limit = 0; // Energy limit in Wh -static uint32_t charging_time_limit = 0; // Time limit in seconds -static uint16_t under_power_limit = 0; // Minimum acceptable power in W - -// ======================== -// Default (persistent) limits -// ======================== - -static uint32_t default_consumption_limit = 0; -static uint32_t default_charging_time_limit = 0; -static uint16_t default_under_power_limit = 0; +static bool limit_reached = false; +static uint32_t consumption_limit = 0; // Energy limit in Wh +static uint32_t charging_time_limit = 0; // Time limit in seconds +static uint16_t under_power_limit = 0; // Minimum acceptable power in W // ======================== // Limit status flag // ======================== -bool evse_get_limit_reached(void) { +bool evse_get_limit_reached(void) +{ bool val; portENTER_CRITICAL(&evse_mux); val = limit_reached; @@ -51,17 +38,24 @@ bool evse_get_limit_reached(void) { return val; } -void evse_set_limit_reached(bool v) { +void evse_set_limit_reached(bool v) +{ portENTER_CRITICAL(&evse_mux); limit_reached = v; portEXIT_CRITICAL(&evse_mux); } +bool evse_is_limit_reached(void) +{ + return evse_get_limit_reached(); +} + // ======================== // Runtime limit accessors // ======================== -uint32_t evse_get_consumption_limit(void) { +uint32_t evse_get_consumption_limit(void) +{ uint32_t val; portENTER_CRITICAL(&evse_mux); val = consumption_limit; @@ -69,13 +63,47 @@ uint32_t evse_get_consumption_limit(void) { return val; } -void evse_set_consumption_limit(uint32_t value) { +void evse_set_consumption_limit(uint32_t value) +{ + bool changed = false; + portENTER_CRITICAL(&evse_mux); - consumption_limit = value; + if (consumption_limit != value) + { + consumption_limit = value; + changed = true; + } portEXIT_CRITICAL(&evse_mux); + + if (!changed) + return; + + nvs_handle_t h; + esp_err_t err = nvs_open("evse", NVS_READWRITE, &h); + if (err == ESP_OK) + { + err = nvs_set_u32(h, "def_cons_lim", value); + if (err == ESP_OK) + err = nvs_commit(h); + nvs_close(h); + + if (err != ESP_OK) + { + ESP_LOGE("EVSE_LIMITS", + "Failed to persist consumption limit (%" PRIu32 " Wh): %s", + value, esp_err_to_name(err)); + } + } + else + { + ESP_LOGE("EVSE_LIMITS", + "Failed to open NVS for consumption limit: %s", + esp_err_to_name(err)); + } } -uint32_t evse_get_charging_time_limit(void) { +uint32_t evse_get_charging_time_limit(void) +{ uint32_t val; portENTER_CRITICAL(&evse_mux); val = charging_time_limit; @@ -83,13 +111,47 @@ uint32_t evse_get_charging_time_limit(void) { return val; } -void evse_set_charging_time_limit(uint32_t value) { +void evse_set_charging_time_limit(uint32_t value) +{ + bool changed = false; + portENTER_CRITICAL(&evse_mux); - charging_time_limit = value; + if (charging_time_limit != value) + { + charging_time_limit = value; + changed = true; + } portEXIT_CRITICAL(&evse_mux); + + if (!changed) + return; + + nvs_handle_t h; + esp_err_t err = nvs_open("evse", NVS_READWRITE, &h); + if (err == ESP_OK) + { + err = nvs_set_u32(h, "def_ch_time_lim", value); + if (err == ESP_OK) + err = nvs_commit(h); + nvs_close(h); + + if (err != ESP_OK) + { + ESP_LOGE("EVSE_LIMITS", + "Failed to persist charging time limit (%" PRIu32 " s): %s", + value, esp_err_to_name(err)); + } + } + else + { + ESP_LOGE("EVSE_LIMITS", + "Failed to open NVS for charging time limit: %s", + esp_err_to_name(err)); + } } -uint16_t evse_get_under_power_limit(void) { +uint16_t evse_get_under_power_limit(void) +{ uint16_t val; portENTER_CRITICAL(&evse_mux); val = under_power_limit; @@ -97,92 +159,97 @@ uint16_t evse_get_under_power_limit(void) { return val; } -void evse_set_under_power_limit(uint16_t value) { +void evse_set_under_power_limit(uint16_t value) +{ + bool changed = false; + portENTER_CRITICAL(&evse_mux); - under_power_limit = value; + if (under_power_limit != value) + { + under_power_limit = value; + changed = true; + } portEXIT_CRITICAL(&evse_mux); -} -// ======================== -// Default (persistent) limit accessors -// These values can be stored/restored via NVS -// ======================== + if (!changed) + return; -uint32_t evse_get_default_consumption_limit(void) { - return default_consumption_limit; -} + nvs_handle_t h; + esp_err_t err = nvs_open("evse", NVS_READWRITE, &h); + if (err == ESP_OK) + { + err = nvs_set_u16(h, "def_un_pwr_lim", value); + if (err == ESP_OK) + err = nvs_commit(h); + nvs_close(h); -void evse_set_default_consumption_limit(uint32_t value) { - default_consumption_limit = value; -} - -uint32_t evse_get_default_charging_time_limit(void) { - return default_charging_time_limit; -} - -void evse_set_default_charging_time_limit(uint32_t value) { - default_charging_time_limit = value; -} - -uint16_t evse_get_default_under_power_limit(void) { - return default_under_power_limit; -} - -void evse_set_default_under_power_limit(uint16_t value) { - default_under_power_limit = value; -} - -bool evse_is_limit_reached(void) { - return evse_get_limit_reached(); + if (err != ESP_OK) + { + ESP_LOGE("EVSE_LIMITS", + "Failed to persist under-power limit (%" PRIu32 " W): %s", + (uint32_t)value, esp_err_to_name(err)); + } + } + else + { + ESP_LOGE("EVSE_LIMITS", + "Failed to open NVS for under-power limit: %s", + esp_err_to_name(err)); + } } // ======================== // Limit checking logic -// This function must be called periodically while charging. -// It will flag the session as "limit reached" when thresholds are violated. // ======================== -void evse_limits_check(void) { - // Only check during an active charging session - if (!evse_state_is_charging(evse_get_state())) { + +void evse_limits_check(void) +{ + // Só faz sentido durante carregamento + if (!evse_state_is_charging(evse_get_state())) + { return; } evse_session_t sess; - // Retrieve accumulated data for the current session - if (!evse_session_get(&sess) || !sess.is_current) { - // If there's no active session, abort + if (!evse_session_get(&sess) || !sess.is_current) + { + // Sem sessão ativa → nada a fazer return; } bool reached = false; - // 1) Energy consumption limit (Wh) - if (consumption_limit > 0 && sess.energy_wh >= consumption_limit) { + // 1) Limite de energia (Wh) + if (consumption_limit > 0 && sess.energy_wh >= consumption_limit) + { ESP_LOGW("EVSE_LIMITS", "Energy limit reached: %" PRIu32 " Wh ≥ %" PRIu32 " Wh", sess.energy_wh, consumption_limit); reached = true; } - // 2) Charging time limit (seconds) - if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit) { + // 2) Limite de tempo (s) + if (charging_time_limit > 0 && sess.duration_s >= charging_time_limit) + { ESP_LOGW("EVSE_LIMITS", "Charging time limit reached: %" PRIu32 " s ≥ %" PRIu32 " s", sess.duration_s, charging_time_limit); reached = true; } - // 3) Under-power limit (instantaneous power) + // 3) Under-power (potência instantânea) uint32_t inst_power = evse_meter_get_instant_power(); - if (under_power_limit > 0 && inst_power < under_power_limit) { + if (under_power_limit > 0 && inst_power < under_power_limit) + { ESP_LOGW("EVSE_LIMITS", - "Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W", - (uint32_t)inst_power, - (uint32_t)under_power_limit); + "Under-power limit reached: %" PRIu32 " W < %" PRIu32 " W", + (uint32_t)inst_power, + (uint32_t)under_power_limit); reached = true; } - if (reached) { + if (reached) + { evse_set_limit_reached(true); } -} \ No newline at end of file +} diff --git a/components/evse/evse_manager.c b/components/evse/evse_manager.c index 641ce59..3d2f49f 100755 --- a/components/evse/evse_manager.c +++ b/components/evse/evse_manager.c @@ -7,29 +7,35 @@ #include "evse_api.h" #include "evse_meter.h" #include "evse_session.h" -#include "evse_config.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "freertos/queue.h" + #include "esp_log.h" +#include "esp_event.h" + #include #include #include "auth_events.h" #include "loadbalancer_events.h" #include "ocpp_events.h" -#include "esp_event.h" +#include "scheduler_events.h" static const char *TAG = "EVSE_Manager"; static SemaphoreHandle_t evse_mutex; -static bool auth_enabled = false; +static volatile bool auth_enabled = false; // Estado de pausa controlado pelo Load Balancer -static bool lb_paused = false; -static bool lb_prev_authorized = false; +static volatile bool lb_paused = false; +static volatile bool lb_prev_authorized = false; + +// Estado de janela do scheduler +static volatile bool s_sched_allowed = true; +static portMUX_TYPE s_sched_mux = portMUX_INITIALIZER_UNLOCKED; #define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo @@ -41,8 +47,20 @@ static void lb_clear_pause_state(void) lb_prev_authorized = false; } +// Exposto para outros módulos (se quiserem saber se o scheduler permite) +bool evse_sched_is_allowed(void) +{ + bool v; + portENTER_CRITICAL(&s_sched_mux); + v = s_sched_allowed; + portEXIT_CRITICAL(&s_sched_mux); + return v; +} + static void evse_manager_handle_auth_on_tick(void) { + bool sched_allowed = evse_sched_is_allowed(); + if (auth_enabled) { // Se o carro foi desconectado, revoga autorização @@ -53,23 +71,38 @@ static void evse_manager_handle_auth_on_tick(void) // Desconexão física invalida qualquer pausa pendente do LB lb_clear_pause_state(); } + + // Em modos RFID/OCPP, o scheduler pode também forçar paragem + if (!sched_allowed && evse_state_get_authorized()) + { + ESP_LOGI(TAG, "[SCHED] window closed (auth mode) → revoking authorization."); + evse_state_set_authorized(false); + } } else { - // Se autenticação está desativada, garante autorização sempre ativa - if (!evse_state_get_authorized()) + // Modo OPEN: só autoriza se LB e Scheduler permitirem + if (!lb_paused && sched_allowed && !evse_state_get_authorized()) { evse_state_set_authorized(true); - ESP_LOGI(TAG, "Authentication disabled → forced authorization."); - // Em modo OPEN, pausa do LB não é tão relevante, mas limpamos mesmo assim + ESP_LOGI(TAG, "Authentication disabled → forced authorization (within schedule)."); lb_clear_pause_state(); } + + // Fora da janela, garantir que não fica autorizado + if (!sched_allowed && evse_state_get_authorized()) + { + ESP_LOGI(TAG, "[SCHED] window closed (OPEN mode) → revoking authorization."); + evse_state_set_authorized(false); + } } } // ===== Task de ciclo principal ===== static void evse_manager_task(void *arg) { + (void)arg; + while (true) { evse_manager_tick(); @@ -77,8 +110,11 @@ static void evse_manager_task(void *arg) } } +// ===== Tratador de eventos de AUTH ===== static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *data) { + (void)arg; + if (base != AUTH_EVENTS || !data) return; @@ -105,7 +141,9 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da ESP_LOGI(TAG, "Auth mode = %s", auth_mode_to_str(g_mode)); if (g_mode == AUTH_MODE_OPEN) { - evse_state_set_authorized(true); + // Em OPEN, a autorização passa a ser gerida por evse_manager_handle_auth_on_tick(), + // que também respeita o scheduler. + evse_state_set_authorized(false); // vai ser forçado no próximo tick se permitido auth_enabled = false; } else @@ -121,17 +159,22 @@ static void on_auth_event(void *arg, esp_event_base_t base, int32_t id, void *da } } -// ===== Tratador de eventos de loadbalancer ===== +// ===== Tratador de eventos de Load Balancer ===== static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data) { + (void)handler_arg; + (void)event_base; + + if (!event_data) + return; + if (event_id == LOADBALANCER_EVENT_INIT || event_id == LOADBALANCER_EVENT_STATE_CHANGED) { const loadbalancer_state_event_t *evt = (const loadbalancer_state_event_t *)event_data; ESP_LOGI(TAG, "Loadbalancer %s (ts: %lld)", evt->enabled ? "ENABLED" : "DISABLED", (long long)evt->timestamp_us); - // Ações adicionais podem ser adicionadas aqui conforme necessário } else if (event_id == LOADBALANCER_EVENT_MASTER_CURRENT_LIMIT) { @@ -167,16 +210,17 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base { lb_paused = false; - // Só retomamos se EVSE estiver operacional + // Só retomamos se EVSE estiver operacional e scheduler permitir bool can_resume = (evse_get_error() == 0) && evse_config_is_available() && - evse_config_is_enabled(); + evse_config_is_enabled() && + evse_sched_is_allowed(); if (!can_resume) { ESP_LOGW(TAG, - "[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado)", + "[LB] limit=%uA → não retoma automaticamente (erro/indisponível/desabilitado/fora de horário)", evt->max_current); lb_clear_pause_state(); return; @@ -184,7 +228,7 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base if (!auth_enabled) { - // Modo OPEN: retoma sempre + // Modo OPEN: retoma sempre (se dentro da janela do scheduler) ESP_LOGI(TAG, "[LB] limit=%uA → modo OPEN, reautorizando (authorized=true)", evt->max_current); @@ -222,8 +266,11 @@ static void on_loadbalancer_event(void *handler_arg, esp_event_base_t event_base } } +// ===== Tratador de eventos de OCPP ===== static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *data) { + (void)arg; + if (base != OCPP_EVENTS) return; @@ -261,7 +308,6 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da case OCPP_EVENT_START_TX: ESP_LOGI(TAG, "[OCPP] StartTx"); - // StartTx em si não precisa mexer em auth, mas limpamos estado de pausa por segurança lb_clear_pause_state(); break; @@ -285,8 +331,6 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da // Mapear operative → enabled local (persiste e emite EVSE_EVENT_ENABLE_UPDATED) evse_config_set_enabled(ev->operative); - // Opcional: poderias também limpar a pausa aqui, dependendo da política - // lb_clear_pause_state(); break; } @@ -296,10 +340,44 @@ static void on_ocpp_event(void *arg, esp_event_base_t base, int32_t id, void *da } } +// ===== Tratador de eventos de Scheduler ===== +static void on_sched_event(void *arg, + esp_event_base_t base, + int32_t id, + void *data) +{ + (void)arg; + + if (base != SCHED_EVENTS || data == NULL) + return; + + const sched_event_state_t *ev = (const sched_event_state_t *)data; + + portENTER_CRITICAL(&s_sched_mux); + s_sched_allowed = ev->allowed_now; + portEXIT_CRITICAL(&s_sched_mux); + + ESP_LOGI(TAG, + "[SCHED] event id=%" PRIi32 " allowed_now=%d", + id, (int)ev->allowed_now); + + // Se a janela fechou, parar sessão (revogar autorização) + if (!ev->allowed_now && evse_state_get_authorized()) + { + ESP_LOGI(TAG, "[SCHED] window closed → stopping session (authorized=false)"); + evse_state_set_authorized(false); + } + + // Se a janela abriu de novo, não auto-reautorizamos aqui. + // Deixamos que o utilizador / OCPP decida iniciar nova sessão. + // (Em modo OPEN, o tick trata disso respeitando o scheduler.) +} + // ===== Inicialização ===== void evse_manager_init(void) { evse_mutex = xSemaphoreCreateMutex(); + configASSERT(evse_mutex != NULL); evse_config_init(); evse_error_init(); @@ -311,9 +389,12 @@ void evse_manager_init(void) ESP_ERROR_CHECK(esp_event_handler_register(AUTH_EVENTS, ESP_EVENT_ANY_ID, &on_auth_event, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(LOADBALANCER_EVENTS, ESP_EVENT_ANY_ID, &on_loadbalancer_event, NULL)); ESP_ERROR_CHECK(esp_event_handler_register(OCPP_EVENTS, ESP_EVENT_ANY_ID, &on_ocpp_event, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(SCHED_EVENTS, ESP_EVENT_ANY_ID, &on_sched_event, NULL)); ESP_LOGI(TAG, "EVSE Manager inicializado."); - xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL); + + BaseType_t rc = xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL); + configASSERT(rc == pdPASS); } // ===== Main Tick ===== @@ -331,4 +412,5 @@ void evse_manager_tick(void) xSemaphoreGive(evse_mutex); } + // === Fim de: components/evse/evse_manager.c === diff --git a/components/evse/evse_meter.c b/components/evse/evse_meter.c index 1f88475..b26d625 100644 --- a/components/evse/evse_meter.c +++ b/components/evse/evse_meter.c @@ -48,7 +48,7 @@ void evse_meter_on_meter_event(void *arg, void *event_data) meter_data.energy_wh = (uint32_t)(evt->total_energy); xSemaphoreGive(meter_mutex); - ESP_LOGI(TAG, + ESP_LOGD(TAG, "Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, " "voltage[V]={%.2f,%.2f,%.2f}, " "current[A]={%.2f,%.2f,%.2f}, " diff --git a/components/evse/evse_pilot.c b/components/evse/evse_pilot.c index 525a6ff..9c89635 100755 --- a/components/evse/evse_pilot.c +++ b/components/evse/evse_pilot.c @@ -23,24 +23,20 @@ #define MAX_SAMPLE_ATTEMPTS 1000 #define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior -// ADC121S021 setup -#define ADC121_VREF_MV 3300 // AJUSTE conforme Vref do seu hardware! -#define ADC121_MAX 4095 // 12 bits +#define ADC121_VREF_MV 3300 +#define ADC121_MAX 4095 static const char *TAG = "evse_pilot"; -// Memoização de estado para evitar comandos/logs desnecessários static int last_pilot_level = -1; static uint32_t last_pwm_duty = 0; -// Função para converter leitura bruta do ADC para mV static int adc_raw_to_mv(uint16_t raw) { return (raw * ADC121_VREF_MV) / ADC121_MAX; } void pilot_init(void) { - // PWM (LEDC) configuração ledc_timer_config_t ledc_timer = { .speed_mode = PILOT_PWM_SPEED_MODE, .timer_num = PILOT_PWM_TIMER, @@ -61,20 +57,18 @@ void pilot_init(void) }; ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); ESP_ERROR_CHECK(ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, 0)); - //ESP_ERROR_CHECK(ledc_fade_func_install(0)); - // Inicializa ADC121S021 externo adc121s021_dma_init(); } void pilot_set_level(bool level) { - if (last_pilot_level == level) return; // só muda se necessário + if (last_pilot_level == level) return; last_pilot_level = level; ESP_LOGI(TAG, "Set level %d", level); ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0); - last_pwm_duty = 0; // PWM parado + last_pwm_duty = 0; } void pilot_set_amps(uint16_t amps) @@ -85,13 +79,11 @@ void pilot_set_amps(uint16_t amps) } uint32_t duty_percent; - if (amps <= 51) { - duty_percent = (amps * 10) / 6; // Duty (%) = Amps / 0.6 + duty_percent = (amps * 10) / 6; } else { - duty_percent = (amps * 10) / 25 + 64; // Duty (%) = (Amps / 2.5) + 64 + duty_percent = (amps * 10) / 25 + 64; } - if (duty_percent > 100) duty_percent = 100; uint32_t duty = (PILOT_PWM_MAX_DUTY * duty_percent) / 100; @@ -101,40 +93,18 @@ void pilot_set_amps(uint16_t amps) last_pwm_duty = duty; ESP_LOGI(TAG, "Pilot set: %d A → %d/%d (≈ %d%% duty)", - amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent); + amps, (int)duty, PILOT_PWM_MAX_DUTY, (int)duty_percent); ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); } bool pilot_get_state(void) { - // true se estamos em 12V fixo; false se PWM ou -12V - // Se quiser diferenciar PWM, guarde um flag quando chamar set_amps. return (last_pilot_level == 1) && (last_pwm_duty == 0); } - - static int compare_int(const void *a, const void *b) { - return (*(int *)a - *(int *)b); -} - -static int select_low_median_qsort(int *src, int n, int percent) { - int k = (n * percent) / 100; - if (k == 0) k = 1; - int *copy = alloca(n * sizeof(int)); - memcpy(copy, src, n * sizeof(int)); - qsort(copy, n, sizeof(int), compare_int); - return copy[k / 2]; -} - -static int select_high_median_qsort(int *src, int n, int percent) { - int k = (n * percent) / 100; - if (k == 0) k = 1; - int *copy = alloca(n * sizeof(int)); - memcpy(copy, src, n * sizeof(int)); - qsort(copy, n, sizeof(int), compare_int); - return copy[n - k + (k / 2)]; + return (*(const int *)a - *(const int *)b); } void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) @@ -145,7 +115,6 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) int collected = 0, attempts = 0; uint16_t adc_sample = 0; - // Lê samples usando ADC121S021 externo while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) { adc_sample = 0; if (adc121s021_dma_get_sample(&adc_sample)) { @@ -164,13 +133,21 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) return; } - int high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT); - int low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT); + qsort(samples, collected, sizeof(int), compare_int); + + int k = (collected * PILOT_EXTREME_PERCENT) / 100; + if (k == 0) k = 1; + + int low_index = k / 2; + int high_index = collected - k + (k / 2); + if (high_index >= collected) high_index = collected - 1; + + int low_raw = samples[low_index]; + int high_raw = samples[high_index]; int high_mv = adc_raw_to_mv(high_raw); int low_mv = adc_raw_to_mv(low_raw); - // Aplica thresholds definidos em board_config (em mV) if (high_mv >= board_config.pilot_down_threshold_12) *up_voltage = PILOT_VOLTAGE_12; else if (high_mv >= board_config.pilot_down_threshold_9) diff --git a/components/evse/evse_session.c b/components/evse/evse_session.c index 180a089..7e80d12 100644 --- a/components/evse/evse_session.c +++ b/components/evse/evse_session.c @@ -1,82 +1,164 @@ -/* - * evse_session.c - * Implementation of evse_session module using instantaneous power accumulation - */ -#include // for PRIu32 +#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" static const char *TAG = "evse_session"; -// Internal state static TickType_t session_start_tick = 0; -static uint32_t watt_seconds = 0; // accumulator of W·s +static uint32_t watt_seconds = 0; static evse_session_t last_session; -static bool last_session_valid = false; +static bool last_session_valid = false; +static uint32_t session_counter = 0; -void evse_session_init(void) { - session_start_tick = 0; - watt_seconds = 0; - last_session_valid = false; +static portMUX_TYPE session_mux = portMUX_INITIALIZER_UNLOCKED; + +void evse_session_init(void) +{ + portENTER_CRITICAL(&session_mux); + session_start_tick = 0; + watt_seconds = 0; + last_session_valid = false; + session_counter = 0; + portEXIT_CRITICAL(&session_mux); } -void evse_session_start(void) { - session_start_tick = xTaskGetTickCount(); - watt_seconds = 0; - ESP_LOGI(TAG, "Session started at tick %u", (unsigned)session_start_tick); +void evse_session_start(void) +{ + TickType_t tick = xTaskGetTickCount(); + + portENTER_CRITICAL(&session_mux); + session_start_tick = tick; + watt_seconds = 0; + session_counter++; + portEXIT_CRITICAL(&session_mux); + + evse_set_limit_reached(false); + + ESP_LOGI(TAG, "Session started at tick %u", (unsigned)tick); + + evse_session_event_data_t evt = { + .type = EVSE_SESSION_EVENT_STARTED, + .session_id = session_counter, + .duration_s = 0, + .energy_wh = 0, + .avg_power_w = 0, + .is_current = true, + }; + + esp_event_post(EVSE_EVENTS, + EVSE_EVENT_SESSION, + &evt, + sizeof(evt), + portMAX_DELAY); } -void evse_session_end(void) { +void evse_session_end(void) +{ + TickType_t start_tick; + uint32_t ws; + uint32_t id; + + portENTER_CRITICAL(&session_mux); if (session_start_tick == 0) { + portEXIT_CRITICAL(&session_mux); ESP_LOGW(TAG, "evse_session_end called without active session"); return; } - TickType_t now = xTaskGetTickCount(); - uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ; - uint32_t energy_wh = watt_seconds / 3600U; - uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0; - - last_session.start_tick = session_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; - + start_tick = session_start_tick; + ws = watt_seconds; + id = session_counter; session_start_tick = 0; - ESP_LOGI(TAG, "Session ended: duration=%" PRIu32 " s, energy=%" PRIu32 " Wh, avg_power=%" PRIu32 " W", - (uint32_t)duration_s, (uint32_t)energy_wh, (uint32_t)avg_power); + portEXIT_CRITICAL(&session_mux); + + TickType_t now = xTaskGetTickCount(); + uint32_t duration_s = (now - start_tick) / configTICK_RATE_HZ; + uint32_t energy_wh = ws / 3600U; + uint32_t avg_power = duration_s > 0 ? ws / 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: duration=%" PRIu32 " s, energy=%" PRIu32 + " Wh, avg_power=%" PRIu32 " W", + 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, + }; + + esp_event_post(EVSE_EVENTS, + EVSE_EVENT_SESSION, + &evt, + sizeof(evt), + portMAX_DELAY); } -void evse_session_tick(void) { - if (session_start_tick == 0) return; - // Should be called every second (or known interval) +void evse_session_tick(void) +{ uint32_t power_w = evse_meter_get_instant_power(); - watt_seconds += power_w; + + portENTER_CRITICAL(&session_mux); + if (session_start_tick != 0) { + watt_seconds += power_w; + } + portEXIT_CRITICAL(&session_mux); } -bool evse_session_get(evse_session_t *out) { - if (out == NULL) return false; +bool evse_session_get(evse_session_t *out) +{ + if (out == NULL) + return false; - if (session_start_tick != 0) { + TickType_t start; + uint32_t ws; + bool has_current; + evse_session_t last_copy; + bool last_valid; + + portENTER_CRITICAL(&session_mux); + start = session_start_tick; + ws = watt_seconds; + has_current = (session_start_tick != 0); + last_copy = last_session; + last_valid = last_session_valid; + portEXIT_CRITICAL(&session_mux); + + if (has_current) + { TickType_t now = xTaskGetTickCount(); - uint32_t duration_s = (now - session_start_tick) / configTICK_RATE_HZ; - uint32_t energy_wh = watt_seconds / 3600U; - uint32_t avg_power = duration_s > 0 ? watt_seconds / duration_s : 0; + uint32_t duration_s = (now - start) / configTICK_RATE_HZ; + uint32_t energy_wh = ws / 3600U; + uint32_t avg_power = duration_s > 0 ? ws / duration_s : 0; - out->start_tick = session_start_tick; - out->duration_s = duration_s; - out->energy_wh = energy_wh; - out->avg_power_w = avg_power; - out->is_current = true; + out->start_tick = start; + out->duration_s = duration_s; + out->energy_wh = energy_wh; + out->avg_power_w = avg_power; + out->is_current = true; return true; } - if (last_session_valid) { - *out = last_session; + if (last_valid) + { + *out = last_copy; return true; } diff --git a/components/evse/include/evse_api.h b/components/evse/include/evse_api.h index c7eb837..a571544 100755 --- a/components/evse/include/evse_api.h +++ b/components/evse/include/evse_api.h @@ -71,6 +71,24 @@ void evse_state_set_authorized(bool authorized); */ bool evse_state_get_authorized(void); +// =============================== +// Configuration / Availability +// =============================== + +/** + * @brief Enable or disable the EVSE (software flag, persisted in NVS). + */ +void evse_set_enabled(bool value); + +/** + * @brief Returns true if the EVSE is currently available for use. + */ +bool evse_is_available(void); + +/** + * @brief Set EVSE availability flag (may be persisted in NVS). + */ +void evse_set_available(bool value); // =============================== // Limit Status @@ -85,4 +103,4 @@ bool evse_is_limit_reached(void); } #endif -#endif // EVSE_API_H +#endif // EVSE_API_H \ No newline at end of file diff --git a/components/evse/include/evse_error.h b/components/evse/include/evse_error.h index 1337f8e..a81d8e2 100755 --- a/components/evse/include/evse_error.h +++ b/components/evse/include/evse_error.h @@ -1,3 +1,4 @@ +// === Início de: components/evse/include/evse_error.h === #ifndef EVSE_ERROR_H #define EVSE_ERROR_H @@ -5,41 +6,46 @@ #include #include "evse_pilot.h" - -#define EVSE_ERR_AUTO_CLEAR_BITS ( \ - EVSE_ERR_DIODE_SHORT_BIT | \ +// Bits que auto-limpam passado um timeout +#define EVSE_ERR_AUTO_CLEAR_BITS ( \ + EVSE_ERR_DIODE_SHORT_BIT | \ EVSE_ERR_TEMPERATURE_HIGH_BIT | \ - EVSE_ERR_RCM_TRIGGERED_BIT ) + EVSE_ERR_RCM_TRIGGERED_BIT) // Error bits -#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0) -#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1) -#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2) -#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3) -#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4) -#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5) -#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6) -#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7) +#define EVSE_ERR_DIODE_SHORT_BIT (1 << 0) +#define EVSE_ERR_LOCK_FAULT_BIT (1 << 1) +#define EVSE_ERR_UNLOCK_FAULT_BIT (1 << 2) +#define EVSE_ERR_RCM_SELFTEST_FAULT_BIT (1 << 3) +#define EVSE_ERR_RCM_TRIGGERED_BIT (1 << 4) +#define EVSE_ERR_TEMPERATURE_HIGH_BIT (1 << 5) +#define EVSE_ERR_PILOT_FAULT_BIT (1 << 6) +#define EVSE_ERR_TEMPERATURE_FAULT_BIT (1 << 7) // Inicialização do módulo de erros void evse_error_init(void); // Verificações e monitoramento void evse_error_check(pilot_voltage_t pilot_voltage, bool is_n12v); - void evse_temperature_check(void); - void evse_error_tick(void); // Leitura e controle de erros uint32_t evse_get_error(void); -bool evse_is_error_cleared(void); -void evse_mark_error_cleared(void); void evse_error_set(uint32_t bitmask); void evse_error_clear(uint32_t bitmask); + bool evse_error_is_active(void); uint32_t evse_error_get_bits(void); -void evse_error_reset_flag(void); + +// ---------------------------------------------------- +// Semântica sticky: flag "todos erros limpos" +// ---------------------------------------------------- +// Fica true quando TODOS os erros são limpos. +// Volta a false assim que qualquer erro novo aparece. +// Permanece true até o consumidor limpar explicitamente. bool evse_error_cleared_flag(void); +void evse_error_reset_flag(void); #endif // EVSE_ERROR_H +// === Fim de: components/evse/include/evse_error.h === diff --git a/components/evse/include/evse_events.h b/components/evse/include/evse_events.h index 85e8823..8d9f907 100755 --- a/components/evse/include/evse_events.h +++ b/components/evse/include/evse_events.h @@ -2,6 +2,9 @@ #define EVSE_EVENTS_H #pragma once + +#include +#include #include "esp_event.h" ESP_EVENT_DECLARE_BASE(EVSE_EVENTS); @@ -12,8 +15,12 @@ typedef enum { EVSE_EVENT_CONFIG_UPDATED, EVSE_EVENT_ENABLE_UPDATED, EVSE_EVENT_AVAILABLE_UPDATED, + EVSE_EVENT_SESSION, } evse_event_id_t; +// ----------------- +// Eventos de STATE +// ----------------- typedef enum { EVSE_STATE_EVENT_IDLE, EVSE_STATE_EVENT_WAITING, @@ -25,11 +32,35 @@ typedef struct { evse_state_event_t state; } evse_state_event_data_t; +// ----------------- +// Eventos de SESSÃO +// ----------------- +typedef enum { + EVSE_SESSION_EVENT_STARTED = 0, + EVSE_SESSION_EVENT_FINISHED, +} evse_session_event_type_t; + typedef struct { - bool charging; // Estado de carregamento - float hw_max_current; // Corrente máxima suportada pelo hardware - float runtime_current; // Corrente de carregamento em uso - int64_t timestamp_us; // Momento da atualização + evse_session_event_type_t type; ///< STARTED / FINISHED + + // campos básicos da sessão, em tipos simples: + uint32_t session_id; ///< opcional, se tiveres um ID + uint32_t duration_s; ///< duração em segundos (0 no STARTED) + uint32_t energy_wh; ///< energia em Wh (0 no STARTED) + uint32_t avg_power_w; ///< potência média em W (0 no STARTED) + + bool is_current; ///< true se ainda estiver em curso +} evse_session_event_data_t; + + +// ----------------- +// Eventos de CONFIG +// ----------------- +typedef struct { + bool charging; // Estado de carregamento + float hw_max_current; // Corrente máxima suportada pelo hardware + float runtime_current; // Corrente de carregamento em uso + int64_t timestamp_us; // Momento da atualização } evse_config_event_data_t; // Eventos simples e específicos diff --git a/components/evse/include/evse_limits.h b/components/evse/include/evse_limits.h index a816449..fc9b7d9 100755 --- a/components/evse/include/evse_limits.h +++ b/components/evse/include/evse_limits.h @@ -1,3 +1,4 @@ +// === Início de: components/evse/include/evse_limits.h === #ifndef EVSE_LIMITS_H #define EVSE_LIMITS_H @@ -15,7 +16,6 @@ extern "C" { /** * @brief Sets the internal 'limit reached' flag. - * Called internally when a limit condition is triggered. */ void evse_set_limit_reached(bool value); @@ -24,6 +24,11 @@ void evse_set_limit_reached(bool value); */ bool evse_get_limit_reached(void); +/** + * @brief Convenience alias for evse_get_limit_reached(). + */ +bool evse_is_limit_reached(void); + /** * @brief Checks if any session limit has been exceeded (energy, time or power). * Should be called periodically during charging. @@ -48,30 +53,13 @@ void evse_set_charging_time_limit(uint32_t value); /** * @brief Get/set minimum acceptable power level (in Watts). - * If the power remains below this for a long time, the session may be interrupted. */ uint16_t evse_get_under_power_limit(void); void evse_set_under_power_limit(uint16_t value); -// ============================ -// Default (Persistent) Limits -// ============================ - -/** - * @brief Default values used after system boot or reset. - * These can be restored from NVS or fallback values. - */ -uint32_t evse_get_default_consumption_limit(void); -void evse_set_default_consumption_limit(uint32_t value); - -uint32_t evse_get_default_charging_time_limit(void); -void evse_set_default_charging_time_limit(uint32_t value); - -uint16_t evse_get_default_under_power_limit(void); -void evse_set_default_under_power_limit(uint16_t value); - #ifdef __cplusplus } #endif #endif // EVSE_LIMITS_H +// === Fim de: components/evse/include/evse_limits.h === diff --git a/components/evse_link/include/evse_link_events.h b/components/evse_link/include/evse_link_events.h index 9da2283..e846315 100644 --- a/components/evse_link/include/evse_link_events.h +++ b/components/evse_link/include/evse_link_events.h @@ -1,16 +1,31 @@ +// === Início de: components/evse_link/include/evse_link_events.h === #ifndef EVSE_LINK_EVENTS_H_ #define EVSE_LINK_EVENTS_H_ #include "esp_event.h" +// Base de eventos do EVSE-Link ESP_EVENT_DECLARE_BASE(EVSE_LINK_EVENTS); +// Tamanho máximo de tag propagada via EVSE-Link (inclui NUL) +#define EVSE_LINK_TAG_MAX_LEN 32 + +// IDs de eventos EVSE-Link typedef enum { LINK_EVENT_FRAME_RECEIVED, // qualquer frame válido LINK_EVENT_SLAVE_ONLINE, // heartbeat recebido primeira vez LINK_EVENT_SLAVE_OFFLINE, // sem heartbeat no timeout LINK_EVENT_MASTER_POLL_SENT, // opcional: poll enviado pelo master LINK_EVENT_CURRENT_LIMIT_APPLIED, - LINK_EVENT_SLAVE_CONFIG_UPDATED // <- NOVO evento + LINK_EVENT_SLAVE_CONFIG_UPDATED, // config atualizada pelo master + LINK_EVENT_REMOTE_AUTH_GRANTED // autorização remota (master -> slave) } evse_link_event_t; + +// Payload para LINK_EVENT_REMOTE_AUTH_GRANTED +typedef struct { + char tag[EVSE_LINK_TAG_MAX_LEN]; // idTag enviada pelo master +} evse_link_auth_grant_event_t; + #endif // EVSE_LINK_EVENTS_H_ + +// === Fim de: components/evse_link/include/evse_link_events.h === diff --git a/components/evse_link/include/evse_link_framing.h b/components/evse_link/include/evse_link_framing.h index 13c489d..700b57d 100644 --- a/components/evse_link/include/evse_link_framing.h +++ b/components/evse_link/include/evse_link_framing.h @@ -5,14 +5,20 @@ #include #include "driver/uart.h" -// UART configuration -#define UART_PORT UART_NUM_2 +// UART instance and configuration +#define UART_PORT UART_NUM_2 // Usa a UART2 #define UART_BAUDRATE 115200 #define UART_RX_BUF_SIZE 256 -// GPIO pin assignments for UART -#define TX_PIN 21 // GPIO21 -> RX on other board -#define RX_PIN 22 // GPIO22 -> TX on other board +// GPIO pin assignments for UART (ajuste conforme o hardware) +#define UART_TXD 17 // TX -> DI do MAX3485 +#define UART_RXD 16 // RX -> RO do MAX3485 +#define UART_RTS 2 // RTS -> DE+RE do MAX3485 + +// Conveniência: nomes usados no .c +#define TX_PIN UART_TXD +#define RX_PIN UART_RXD +#define RTS_PIN UART_RTS // Frame delimiters #define MAGIC_START 0x7E diff --git a/components/evse_link/src/evse_link_framing.c b/components/evse_link/src/evse_link_framing.c index 284a580..1ce5661 100644 --- a/components/evse_link/src/evse_link_framing.c +++ b/components/evse_link/src/evse_link_framing.c @@ -1,5 +1,3 @@ -// components/evse_link_framing/src/evse_link_framing.c - #include "evse_link_framing.h" #include "driver/uart.h" #include "freertos/semphr.h" @@ -8,75 +6,124 @@ static const char *TAG = "evse_framing"; -static SemaphoreHandle_t tx_mutex; +static SemaphoreHandle_t tx_mutex = NULL; static uint8_t seq = 0; static evse_link_frame_cb_t rx_cb = NULL; // CRC-8 (polynomial 0x07) -static uint8_t crc8(const uint8_t *data, uint8_t len) { +static uint8_t crc8(const uint8_t *data, uint8_t len) +{ uint8_t crc = 0; for (uint8_t i = 0; i < len; ++i) { crc ^= data[i]; for (uint8_t b = 0; b < 8; ++b) { - crc = (crc & 0x80) ? (crc << 1) ^ 0x07 : (crc << 1); + if (crc & 0x80) { + crc = (uint8_t)((crc << 1) ^ 0x07); + } else { + crc <<= 1; + } } } return crc; } -void evse_link_framing_init(void) { - // Create mutex for TX +void evse_link_framing_init(void) +{ + // Mutex para proteger TX (framings de várias tasks) tx_mutex = xSemaphoreCreateMutex(); - // Install UART driver - uart_driver_install(UART_PORT, UART_RX_BUF_SIZE * 2, 0, 0, NULL, 0); - uart_param_config(UART_PORT, &(uart_config_t){ + + // Instala driver UART + uart_driver_install(UART_PORT, + UART_RX_BUF_SIZE * 2, // RX buffer + 0, // TX buffer (0 = usa buffer interno) + 0, + NULL, + 0); + + uart_config_t cfg = { .baud_rate = UART_BAUDRATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, - .flow_ctrl = UART_HW_FLOWCTRL_DISABLE - }); - uart_set_pin(UART_PORT, TX_PIN, RX_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE); + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT, + }; + uart_param_config(UART_PORT, &cfg); + + // Define pinos: TX, RX e RTS (RTS ligado a DE+RE do transceiver RS485) + uart_set_pin(UART_PORT, + TX_PIN, // MB_UART_TXD (ex: GPIO17) + RX_PIN, // MB_UART_RXD (ex: GPIO16) + RTS_PIN, // MB_UART_RTS (ex: GPIO2, DE+RE) + UART_PIN_NO_CHANGE); + + // Modo RS485 half-duplex: driver controla RTS/DE/RE automaticamente + uart_set_mode(UART_PORT, UART_MODE_RS485_HALF_DUPLEX); + + ESP_LOGI(TAG, "Framing init: UART%d TX=%d RX=%d RTS(DE/RE)=%d baud=%d", + UART_PORT, TX_PIN, RX_PIN, RTS_PIN, UART_BAUDRATE); } bool evse_link_framing_send(uint8_t dest, uint8_t src, - const uint8_t *payload, uint8_t len) { - if (len > EVSE_LINK_MAX_PAYLOAD) return false; - if (xSemaphoreTake(tx_mutex, portMAX_DELAY) != pdTRUE) return false; + const uint8_t *payload, uint8_t len) +{ + if (len > EVSE_LINK_MAX_PAYLOAD) { + ESP_LOGW(TAG, "Payload too large: %u (max=%u)", + len, EVSE_LINK_MAX_PAYLOAD); + return false; + } + + if (xSemaphoreTake(tx_mutex, portMAX_DELAY) != pdTRUE) { + ESP_LOGW(TAG, "Failed to take TX mutex"); + return false; + } // Frame: START | DEST | SRC | LEN | SEQ | PAYLOAD | CRC | END uint8_t frame[EVSE_LINK_MAX_PAYLOAD + 7]; int idx = 0; + frame[idx++] = MAGIC_START; frame[idx++] = dest; frame[idx++] = src; - frame[idx++] = len + 1; // +1 for SEQ + frame[idx++] = (uint8_t)(len + 1); // LEN = SEQ + payload frame[idx++] = seq; - memcpy(&frame[idx], payload, len); - idx += len; - // CRC covers DEST + SRC + LEN + SEQ + PAYLOAD - uint8_t crc_input[3 + 1 + EVSE_LINK_MAX_PAYLOAD]; + if (len > 0 && payload != NULL) { + memcpy(&frame[idx], payload, len); + idx += len; + } + + // CRC cobre: DEST + SRC + LEN + SEQ + PAYLOAD + uint8_t crc_input[3 + 1 + EVSE_LINK_MAX_PAYLOAD]; memcpy(crc_input, &frame[1], 3 + 1 + len); - frame[idx++] = crc8(crc_input, 3 + 1 + len); + uint8_t crc = crc8(crc_input, (uint8_t)(3 + 1 + len)); + frame[idx++] = crc; frame[idx++] = MAGIC_END; - uart_write_bytes(UART_PORT, (const char *)frame, idx); - uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(10)); + // Envia frame completo + int written = uart_write_bytes(UART_PORT, (const char *)frame, idx); + if (written != idx) { + ESP_LOGW(TAG, "uart_write_bytes wrote %d of %d", written, idx); + } + + // Aguarda TX terminar (o driver controla DE/RE via RTS) + uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(20)); + xSemaphoreGive(tx_mutex); - ESP_LOGD(TAG, "Sent frame dest=0x%02X src=0x%02X len=%u seq=%u", + ESP_LOGI(TAG, "Sent frame dest=0x%02X src=0x%02X len=%u seq=%u", dest, src, len, seq); - seq++; // increment sequence after sending + seq++; // incrementa sequência após envio return true; } -void evse_link_framing_recv_byte(uint8_t b) { - // State machine for frame parsing +void evse_link_framing_recv_byte(uint8_t b) +{ + // Máquina de estados para parsing do frame static enum { - ST_WAIT_START, + ST_WAIT_START = 0, ST_WAIT_DEST, ST_WAIT_SRC, ST_WAIT_LEN, @@ -88,7 +135,7 @@ void evse_link_framing_recv_byte(uint8_t b) { static uint8_t rx_dest; static uint8_t rx_src; - static uint8_t rx_len; + static uint8_t rx_len; // inclui SEQ + payload static uint8_t rx_seq; static uint8_t rx_buf[EVSE_LINK_MAX_PAYLOAD]; static uint8_t rx_pos; @@ -112,19 +159,25 @@ void evse_link_framing_recv_byte(uint8_t b) { break; case ST_WAIT_LEN: - rx_len = b; // includes SEQ + payload + rx_len = b; // LEN = SEQ + payload rx_pos = 0; rx_state = ST_WAIT_SEQ; break; case ST_WAIT_SEQ: rx_seq = b; - rx_state = (rx_len > 1) ? ST_READING : ST_WAIT_CRC; + if (rx_len > 1) { + rx_state = ST_READING; + } else { + rx_state = ST_WAIT_CRC; + } break; case ST_READING: - rx_buf[rx_pos++] = b; - if (rx_pos >= (rx_len - 1)) { // all payload bytes read + if (rx_pos < EVSE_LINK_MAX_PAYLOAD) { + rx_buf[rx_pos++] = b; + } + if (rx_pos >= (uint8_t)(rx_len - 1)) { // payload completo rx_state = ST_WAIT_CRC; } break; @@ -136,33 +189,43 @@ void evse_link_framing_recv_byte(uint8_t b) { case ST_WAIT_END: if (b == MAGIC_END) { - // Build data for CRC calculation + // Monta buffer para verificar CRC: + // DEST + SRC + LEN + SEQ + PAYLOAD uint8_t temp[3 + 1 + EVSE_LINK_MAX_PAYLOAD]; int temp_len = 0; temp[temp_len++] = rx_dest; temp[temp_len++] = rx_src; temp[temp_len++] = rx_len; temp[temp_len++] = rx_seq; - memcpy(&temp[temp_len], rx_buf, rx_len - 1); - temp_len += rx_len - 1; + if (rx_len > 1) { + memcpy(&temp[temp_len], rx_buf, rx_len - 1); + temp_len += rx_len - 1; + } - uint8_t expected = crc8(temp, temp_len); + uint8_t expected = crc8(temp, (uint8_t)temp_len); if (expected == rx_crc) { + uint8_t payload_len = (uint8_t)(rx_len - 1); // exclui SEQ if (rx_cb) { - rx_cb(rx_src, rx_dest, rx_buf, rx_len - 1); + rx_cb(rx_src, rx_dest, rx_buf, payload_len); } ESP_LOGD(TAG, "Frame OK src=0x%02X dest=0x%02X len=%u seq=%u", - rx_src, rx_dest, rx_len - 1, rx_seq); + rx_src, rx_dest, payload_len, rx_seq); } else { ESP_LOGW(TAG, "CRC mismatch: expected=0x%02X got=0x%02X", expected, rx_crc); } } + // Em qualquer caso, volta a esperar novo frame + rx_state = ST_WAIT_START; + break; + + default: rx_state = ST_WAIT_START; break; } } -void evse_link_framing_register_cb(evse_link_frame_cb_t cb) { +void evse_link_framing_register_cb(evse_link_frame_cb_t cb) +{ rx_cb = cb; } diff --git a/components/evse_link/src/evse_link_master.c b/components/evse_link/src/evse_link_master.c index 4cb9fa8..6f50392 100644 --- a/components/evse_link/src/evse_link_master.c +++ b/components/evse_link/src/evse_link_master.c @@ -1,6 +1,5 @@ -// === components/evse_link/src/evse_link_master.c === - #include "evse_link.h" +#include "evse_link_events.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/timers.h" @@ -8,94 +7,142 @@ #include "esp_event.h" #include #include +#include #include "loadbalancer_events.h" +#include "auth_events.h" static const char *TAG = "evse_link_master"; // Link commands -#define CMD_POLL 0x01 -#define CMD_HEARTBEAT 0x02 -#define CMD_HEARTBEAT_ACK 0x09 +#define CMD_POLL 0x01 +#define CMD_HEARTBEAT 0x02 +#define CMD_HEARTBEAT_ACK 0x09 #define CMD_CONFIG_BROADCAST 0x03 -#define CMD_SET_CURRENT 0x08 +#define CMD_SET_CURRENT 0x08 +#define CMD_AUTH_GRANTED 0x0A // novo: master concede autorização a slave // payload lengths (exclui byte de opcode) -#define LEN_POLL_REQ 1 // [ CMD_POLL ] -#define LEN_POLL_RESP 9 // [ CMD_POLL, float V(4), float I(4) ] -#define LEN_HEARTBEAT 6 // [ CMD_HEARTBEAT, charging, hw_max_lo, hw_max_hi, run_lo, run_hi ] -#define LEN_CONFIG_BROADCAST 2 // [ CMD_CONFIG_BROADCAST, new_max_current ] -#define LEN_SET_CURRENT 3 // [ CMD_SET_CURRENT, limit_lo, limit_hi ] -#define LEN_HEARTBEAT_ACK 1 +#define LEN_POLL_REQ 1 // [ CMD_POLL ] +#define LEN_POLL_RESP 9 // [ CMD_POLL, float V(4), float I(4) ] +#define LEN_HEARTBEAT 6 // [ CMD_HEARTBEAT, charging, hw_max_lo, hw_max_hi, run_lo, run_hi ] +#define LEN_CONFIG_BROADCAST 2 // [ CMD_CONFIG_BROADCAST, new_max_current ] +#define LEN_SET_CURRENT 3 // [ CMD_SET_CURRENT, limit_lo, limit_hi ] +#define LEN_HEARTBEAT_ACK 1 // polling / heartbeat timers interval -typedef struct { +typedef struct +{ TimerHandle_t timer; - TickType_t interval; + TickType_t interval; } timer_def_t; -static timer_def_t poll_timer = { .timer = NULL, .interval = pdMS_TO_TICKS(30000) }; -static timer_def_t hb_timer = { .timer = NULL, .interval = pdMS_TO_TICKS(30000) }; +static timer_def_t poll_timer = {.timer = NULL, .interval = pdMS_TO_TICKS(30000)}; +static timer_def_t hb_timer = {.timer = NULL, .interval = pdMS_TO_TICKS(30000)}; // --- Send new limit to slave --- -static void on_new_limit(void* arg, esp_event_base_t base, int32_t id, void* data) { - if (id != LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT) return; +static void on_new_limit(void *arg, esp_event_base_t base, int32_t id, void *data) +{ + if (id != LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT) + return; const loadbalancer_slave_limit_event_t *evt = data; - uint8_t slave_id = evt->slave_id; + uint8_t slave_id = evt->slave_id; uint16_t max_current = evt->max_current; uint8_t buf[LEN_SET_CURRENT] = { CMD_SET_CURRENT, (uint8_t)(max_current & 0xFF), - (uint8_t)(max_current >> 8) - }; + (uint8_t)(max_current >> 8)}; evse_link_send(slave_id, buf, sizeof(buf)); ESP_LOGI(TAG, "Sent SET_CURRENT to 0x%02X: %uA", slave_id, max_current); } +// --- Bridge AUTH -> EVSE-Link: enviar AUTH_GRANTED para slaves --- +static void on_auth_result(void *arg, esp_event_base_t base, int32_t id, void *data) +{ + if (base != AUTH_EVENTS || id != AUTH_EVENT_TAG_PROCESSED || data == NULL) { + return; + } + + const auth_tag_event_data_t *ev = (const auth_tag_event_data_t *)data; + + if (!ev->authorized) { + ESP_LOGI(TAG, "Tag %s not authorized, not propagating to slaves", ev->tag); + return; + } + + // Construir payload: [ CMD_AUTH_GRANTED, tag..., '\0' ] + uint8_t buf[1 + EVSE_LINK_TAG_MAX_LEN]; + buf[0] = CMD_AUTH_GRANTED; + + // Copiar tag e garantir NUL + strncpy((char *)&buf[1], ev->tag, EVSE_LINK_TAG_MAX_LEN - 1); + ((char *)&buf[1])[EVSE_LINK_TAG_MAX_LEN - 1] = '\0'; + + uint8_t payload_len = 1 + (uint8_t)(strlen((char *)&buf[1]) + 1); // opcode + tag + '\0' + + // Neste exemplo: broadcast para todos os slaves (0xFF) + uint8_t dest = 0xFF; + + if (!evse_link_send(dest, buf, payload_len)) { + ESP_LOGW(TAG, "Failed to send CMD_AUTH_GRANTED to dest=0x%02X for tag=%s", + dest, (char *)&buf[1]); + } else { + ESP_LOGI(TAG, "Sent CMD_AUTH_GRANTED to dest=0x%02X for tag=%s", + dest, (char *)&buf[1]); + } +} + // --- Polling broadcast callback --- -static void poll_timer_cb(TimerHandle_t xTimer) { - ESP_LOGD(TAG, "Broadcasting CMD_POLL to all slaves");; +static void poll_timer_cb(TimerHandle_t xTimer) +{ + ESP_LOGD(TAG, "Broadcasting CMD_POLL to all slaves"); + ; // Optionally post event LINK_EVENT_MASTER_POLL_SENT } // --- Heartbeat timeout callback --- -static void hb_timer_cb(TimerHandle_t xTimer) { +static void hb_timer_cb(TimerHandle_t xTimer) +{ ESP_LOGW(TAG, "Heartbeat timeout: possible slave offline"); // post event LINK_EVENT_SLAVE_OFFLINE ??? } static void on_frame_master(uint8_t src, uint8_t dest, - const uint8_t *payload, uint8_t len) { - if (len < 1) return; + const uint8_t *payload, uint8_t len) +{ + if (len < 1) + return; uint8_t cmd = payload[0]; - switch (cmd) { - case CMD_HEARTBEAT: { - if (len != 6) { // CMD + charging + hw_max_lo + hw_max_hi + runtime_lo + runtime_hi + switch (cmd) + { + case CMD_HEARTBEAT: + { + if (len != LEN_HEARTBEAT) + { // CMD + charging + hw_max_lo + hw_max_hi + runtime_lo + runtime_hi ESP_LOGW(TAG, "HEARTBEAT len invalid from 0x%02X: %u bytes", src, len); return; } - bool charging = payload[1] != 0; - uint16_t hw_max = payload[2] | (payload[3] << 8); - uint16_t runtime = payload[4] | (payload[5] << 8); + bool charging = payload[1] != 0; + uint16_t hw_max = payload[2] | (payload[3] << 8); + uint16_t runtime = payload[4] | (payload[5] << 8); ESP_LOGI(TAG, "Heartbeat from 0x%02X: charging=%d hw_max=%uA runtime=%uA", src, charging, hw_max, runtime); loadbalancer_slave_status_event_t status = { - .slave_id = src, - .charging = charging, + .slave_id = src, + .charging = charging, .hw_max_current = (float)hw_max, - .runtime_current = (float)runtime, // corrente real medida no slave - .timestamp_us = esp_timer_get_time() - }; - + .runtime_current = (float)runtime, // corrente real medida no slave + .timestamp_us = esp_timer_get_time()}; + esp_event_post(LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_STATUS, &status, sizeof(status), portMAX_DELAY); // Enviar ACK de volta - uint8_t ack[] = { CMD_HEARTBEAT_ACK }; + uint8_t ack[] = {CMD_HEARTBEAT_ACK}; evse_link_send(src, ack, sizeof(ack)); ESP_LOGD(TAG, "Sent HEARTBEAT_ACK to 0x%02X", src); break; @@ -115,10 +162,11 @@ static void on_frame_master(uint8_t src, uint8_t dest, } } - // --- Master initialization --- -void evse_link_master_init(void) { - if (evse_link_get_mode() != EVSE_LINK_MODE_MASTER || !evse_link_is_enabled()) { +void evse_link_master_init(void) +{ + if (evse_link_get_mode() != EVSE_LINK_MODE_MASTER || !evse_link_is_enabled()) + { return; } ESP_LOGI(TAG, "Initializing MASTER (ID=0x%02X)", evse_link_get_self_id()); @@ -132,9 +180,15 @@ void evse_link_master_init(void) { LOADBALANCER_EVENTS, LOADBALANCER_EVENT_SLAVE_CURRENT_LIMIT, on_new_limit, - NULL - ) - ); + NULL)); + + // escutar resultado do AUTH para propagar autorização aos slaves + ESP_ERROR_CHECK( + esp_event_handler_register( + AUTH_EVENTS, + AUTH_EVENT_TAG_PROCESSED, + on_auth_result, + NULL)); // create and start poll timer poll_timer.timer = xTimerCreate("poll_tmr", @@ -150,3 +204,4 @@ void evse_link_master_init(void) { hb_timer_cb); xTimerStart(hb_timer.timer, 0); } + diff --git a/components/evse_link/src/evse_link_slave.c b/components/evse_link/src/evse_link_slave.c index 75639ba..f0f3755 100644 --- a/components/evse_link/src/evse_link_slave.c +++ b/components/evse_link/src/evse_link_slave.c @@ -23,6 +23,7 @@ static const char *TAG = "evse_link_slave"; #define CMD_CONFIG_BROADCAST 0x03 #define CMD_SET_CURRENT 0x08 #define CMD_HEARTBEAT_ACK 0x09 +#define CMD_AUTH_GRANTED 0x0A // novo: master concede autorização // payload lengths (exclui seq byte) #define LEN_POLL_REQ 1 // [ CMD_POLL ] @@ -82,6 +83,11 @@ static void on_frame_slave(uint8_t src, uint8_t dest, break; case CMD_SET_CURRENT: { + if (len < LEN_SET_CURRENT) { + ESP_LOGW(TAG, "SET_CURRENT from 0x%02X with invalid length %u", src, len); + break; + } + uint16_t amps = payload[1] | (payload[2] << 8); evse_set_runtime_charging_current(amps); ESP_LOGI(TAG, "Applied runtime limit: %uA from master 0x%02X", amps, src); @@ -103,6 +109,33 @@ static void on_frame_slave(uint8_t src, uint8_t dest, } break; + case CMD_AUTH_GRANTED: { + if (len < 2) { + ESP_LOGW(TAG, "CMD_AUTH_GRANTED from 0x%02X with invalid length %u", src, len); + break; + } + + const char *tag = (const char *)&payload[1]; + + evse_link_auth_grant_event_t ev = {0}; + strncpy(ev.tag, tag, EVSE_LINK_TAG_MAX_LEN - 1); + ev.tag[EVSE_LINK_TAG_MAX_LEN - 1] = '\0'; + + ESP_LOGI(TAG, "Received CMD_AUTH_GRANTED from master 0x%02X, tag='%s'", src, ev.tag); + + esp_err_t err = esp_event_post( + EVSE_LINK_EVENTS, + LINK_EVENT_REMOTE_AUTH_GRANTED, + &ev, + sizeof(ev), + portMAX_DELAY); + + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to post LINK_EVENT_REMOTE_AUTH_GRANTED: %s", esp_err_to_name(err)); + } + break; + } + default: ESP_LOGW(TAG, "Unknown command 0x%02X from master 0x%02X", cmd, src); } @@ -162,3 +195,5 @@ void evse_link_slave_init(void) { ) ); } + +// === Fim de: components/evse_link/src/evse_link_slave.c === diff --git a/components/gpio_button/CMakeLists.txt b/components/gpio_button/CMakeLists.txt deleted file mode 100755 index fe53b13..0000000 --- a/components/gpio_button/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -idf_component_register(SRCS "button/button.c" "button/button_obj.cpp" - INCLUDE_DIRS "button/include" - REQUIRES "driver") diff --git a/components/gpio_button/Kconfig b/components/gpio_button/Kconfig deleted file mode 100755 index 30b9f9b..0000000 --- a/components/gpio_button/Kconfig +++ /dev/null @@ -1,6 +0,0 @@ -menu "GPIO Button" - config IO_GLITCH_FILTER_TIME_MS - int "IO glitch filter timer ms (10~100)" - range 10 100 - default 50 -endmenu \ No newline at end of file diff --git a/components/gpio_button/button/button.c b/components/gpio_button/button/button.c deleted file mode 100755 index 5764577..0000000 --- a/components/gpio_button/button/button.c +++ /dev/null @@ -1,353 +0,0 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include -#include -#include - -#define IOT_CHECK(tag, a, ret) if(!(a)) { \ - ESP_LOGE(tag,"%s:%d (%s)", __FILE__, __LINE__, __FUNCTION__); \ - return (ret); \ - } -#define ERR_ASSERT(tag, param) IOT_CHECK(tag, (param) == ESP_OK, ESP_FAIL) -#define POINT_ASSERT(tag, param, ret) IOT_CHECK(tag, (param) != NULL, (ret)) - -typedef enum { - BUTTON_STATE_IDLE = 0, - BUTTON_STATE_PUSH, - BUTTON_STATE_PRESSED, -} button_status_t; - -typedef struct button_dev button_dev_t; -typedef struct btn_cb button_cb_t; - -struct btn_cb{ - TickType_t interval; - button_cb cb; - void* arg; - uint8_t on_press; - TimerHandle_t tmr; - button_dev_t *pbtn; - button_cb_t *next_cb; -}; - -struct button_dev{ - uint8_t io_num; - uint8_t active_level; - uint32_t serial_thres_sec; - uint8_t taskq_on; - QueueHandle_t taskq; - QueueHandle_t argq; - button_status_t state; - button_cb_t tap_short_cb; - button_cb_t tap_psh_cb; - button_cb_t tap_rls_cb; - button_cb_t press_serial_cb; - button_cb_t* cb_head; -}; - -#define BUTTON_GLITCH_FILTER_TIME_MS CONFIG_IO_GLITCH_FILTER_TIME_MS -static const char* TAG = "button"; - -static void button_press_cb(TimerHandle_t tmr) -{ - button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); - button_dev_t* btn = btn_cb->pbtn; - // low, then restart - if (btn->active_level == gpio_get_level(btn->io_num)) { - btn->state = BUTTON_STATE_PRESSED; - if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && !btn_cb->on_press) { - void *tmp = btn_cb->cb; - xQueueOverwrite(btn->taskq, &tmp); - xQueueOverwrite(btn->argq, &btn_cb->arg); - } else if (btn_cb->cb) { - btn_cb->cb(btn_cb->arg); - } - } -} - -static void button_tap_psh_cb(TimerHandle_t tmr) -{ - button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); - button_dev_t* btn = btn_cb->pbtn; - xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); - int lv = gpio_get_level(btn->io_num); - - if (btn->active_level == lv) { - // True implies key is pressed - btn->state = BUTTON_STATE_PUSH; - if (btn->press_serial_cb.tmr) { - xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY); - xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY); - } - if (btn->tap_psh_cb.cb) { - btn->tap_psh_cb.cb(btn->tap_psh_cb.arg); - } - } else { - // 50ms, check if this is a real key up - if (btn->tap_rls_cb.tmr) { - xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); - xTimerReset(btn->tap_rls_cb.tmr, portMAX_DELAY); - } - } -} - -static void button_tap_rls_cb(TimerHandle_t tmr) -{ - button_cb_t* btn_cb = (button_cb_t*) pvTimerGetTimerID(tmr); - button_dev_t* btn = btn_cb->pbtn; - xTimerStop(btn->tap_rls_cb.tmr, portMAX_DELAY); - if (btn->active_level == gpio_get_level(btn->io_num)) { - - } else { - // high, then key is up - button_cb_t *pcb = btn->cb_head; - while (pcb != NULL) { - if (pcb->tmr != NULL) { - xTimerStop(pcb->tmr, portMAX_DELAY); - } - pcb = pcb->next_cb; - } - if (btn->taskq != NULL && btn->argq != NULL && btn->taskq_on && uxQueueMessagesWaiting(btn->taskq) != 0 && btn->state != BUTTON_STATE_IDLE) { - void (*task)(void*); - void *arg; - xQueueReceive(btn->taskq, &task, 0); - xQueueReceive(btn->argq, &arg, 0); - task(arg); - } - if (btn->press_serial_cb.tmr && btn->press_serial_cb.tmr != NULL) { - xTimerStop(btn->press_serial_cb.tmr, portMAX_DELAY); - } - if (btn->tap_short_cb.cb && btn->state == BUTTON_STATE_PUSH) { - btn->tap_short_cb.cb(btn->tap_short_cb.arg); - } - if(btn->tap_rls_cb.cb && btn->state != BUTTON_STATE_IDLE) { - btn->tap_rls_cb.cb(btn->tap_rls_cb.arg); - } - btn->state = BUTTON_STATE_IDLE; - } -} - -static void button_press_serial_cb(TimerHandle_t tmr) -{ - button_dev_t* btn = (button_dev_t*) pvTimerGetTimerID(tmr); - if (btn->press_serial_cb.cb) { - btn->press_serial_cb.cb(btn->press_serial_cb.arg); - } - xTimerChangePeriod(btn->press_serial_cb.tmr, btn->press_serial_cb.interval, portMAX_DELAY); - xTimerReset(btn->press_serial_cb.tmr, portMAX_DELAY); -} - -static void button_gpio_isr_handler(void* arg) -{ - button_dev_t* btn = (button_dev_t*) arg; - portBASE_TYPE HPTaskAwoken = pdFALSE; - int level = gpio_get_level(btn->io_num); - if (level == btn->active_level) { - if (btn->tap_psh_cb.tmr) { - xTimerStopFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken); - xTimerResetFromISR(btn->tap_psh_cb.tmr, &HPTaskAwoken); - } - - button_cb_t *pcb = btn->cb_head; - while (pcb != NULL) { - if (pcb->tmr != NULL) { - xTimerStopFromISR(pcb->tmr, &HPTaskAwoken); - xTimerResetFromISR(pcb->tmr, &HPTaskAwoken); - } - pcb = pcb->next_cb; - } - } else { - // 50ms, check if this is a real key up - if (btn->tap_rls_cb.tmr) { - xTimerStopFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken); - xTimerResetFromISR(btn->tap_rls_cb.tmr, &HPTaskAwoken); - } - } - if(HPTaskAwoken == pdTRUE) { - portYIELD_FROM_ISR(); - } -} - -static void button_free_tmr(TimerHandle_t* tmr) -{ - if (tmr && *tmr) { - xTimerStop(*tmr, portMAX_DELAY); - xTimerDelete(*tmr, portMAX_DELAY); - *tmr = NULL; - } -} - -esp_err_t iot_button_delete(button_handle_t btn_handle) -{ - POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); - button_dev_t* btn = (button_dev_t*) btn_handle; - gpio_set_intr_type(btn->io_num, GPIO_INTR_DISABLE); - gpio_isr_handler_remove(btn->io_num); - - button_free_tmr(&btn->tap_rls_cb.tmr); - button_free_tmr(&btn->tap_psh_cb.tmr); - button_free_tmr(&btn->tap_short_cb.tmr); - button_free_tmr(&btn->press_serial_cb.tmr); - - button_cb_t *pcb = btn->cb_head; - while (pcb != NULL) { - button_cb_t *cb_next = pcb->next_cb; - button_free_tmr(&pcb->tmr); - free(pcb); - pcb = cb_next; - } - free(btn); - return ESP_OK; -} - -button_handle_t iot_button_create(gpio_num_t gpio_num, button_active_t active_level) -{ - IOT_CHECK(TAG, gpio_num < GPIO_NUM_MAX, NULL); - button_dev_t* btn = (button_dev_t*) calloc(1, sizeof(button_dev_t)); - POINT_ASSERT(TAG, btn, NULL); - btn->active_level = active_level; - btn->io_num = gpio_num; - btn->state = BUTTON_STATE_IDLE; - btn->taskq_on = 0; - btn->taskq = xQueueCreate(1, sizeof(void*)); - btn->argq = xQueueCreate(1, sizeof(void *)); - btn->tap_rls_cb.arg = NULL; - btn->tap_rls_cb.cb = NULL; - btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; - btn->tap_rls_cb.pbtn = btn; - btn->tap_rls_cb.tmr = xTimerCreate("btn_rls_tmr", btn->tap_rls_cb.interval, pdFALSE, - &btn->tap_rls_cb, button_tap_rls_cb); - btn->tap_psh_cb.arg = NULL; - btn->tap_psh_cb.cb = NULL; - btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; - btn->tap_psh_cb.pbtn = btn; - btn->tap_psh_cb.tmr = xTimerCreate("btn_psh_tmr", btn->tap_psh_cb.interval, pdFALSE, - &btn->tap_psh_cb, button_tap_psh_cb); - gpio_install_isr_service(0); - gpio_config_t gpio_conf; - gpio_conf.intr_type = GPIO_INTR_ANYEDGE; - gpio_conf.mode = GPIO_MODE_INPUT; - gpio_conf.pin_bit_mask = (uint64_t)1 << gpio_num; - gpio_conf.pull_down_en = GPIO_PULLDOWN_DISABLE; - gpio_conf.pull_up_en = GPIO_PULLUP_ENABLE; - gpio_config(&gpio_conf); - gpio_isr_handler_add(gpio_num, button_gpio_isr_handler, btn); - return (button_handle_t) btn; -} - -esp_err_t iot_button_rm_cb(button_handle_t btn_handle, button_cb_type_t type) -{ - button_dev_t* btn = (button_dev_t*) btn_handle; - button_cb_t* btn_cb = NULL; - if (type == BUTTON_CB_PUSH) { - btn_cb = &btn->tap_psh_cb; - } else if (type == BUTTON_CB_RELEASE) { - btn_cb = &btn->tap_rls_cb; - } else if (type == BUTTON_CB_TAP) { - btn_cb = &btn->tap_short_cb; - } else if (type == BUTTON_CB_SERIAL) { - btn_cb = &btn->press_serial_cb; - } - btn_cb->cb = NULL; - btn_cb->arg = NULL; - btn_cb->pbtn = btn; - button_free_tmr(&btn_cb->tmr); - return ESP_OK; -} - -esp_err_t iot_button_set_serial_cb(button_handle_t btn_handle, uint32_t start_after_sec, TickType_t interval_tick, button_cb cb, void* arg) -{ - button_dev_t* btn = (button_dev_t*) btn_handle; - btn->serial_thres_sec = start_after_sec; - if (btn->press_serial_cb.tmr == NULL) { - btn->press_serial_cb.tmr = xTimerCreate("btn_serial_tmr", btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, - pdFALSE, btn, button_press_serial_cb); - } - btn->press_serial_cb.arg = arg; - btn->press_serial_cb.cb = cb; - btn->press_serial_cb.interval = interval_tick; - btn->press_serial_cb.pbtn = btn; - xTimerChangePeriod(btn->press_serial_cb.tmr, btn->serial_thres_sec*1000 / portTICK_PERIOD_MS, portMAX_DELAY); - return ESP_OK; -} - -esp_err_t iot_button_set_evt_cb(button_handle_t btn_handle, button_cb_type_t type, button_cb cb, void* arg) -{ - POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); - button_dev_t* btn = (button_dev_t*) btn_handle; - if (type == BUTTON_CB_PUSH) { - btn->tap_psh_cb.arg = arg; - btn->tap_psh_cb.cb = cb; - btn->tap_psh_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; - btn->tap_psh_cb.pbtn = btn; - xTimerChangePeriod(btn->tap_psh_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY); - } else if (type == BUTTON_CB_RELEASE) { - btn->tap_rls_cb.arg = arg; - btn->tap_rls_cb.cb = cb; - btn->tap_rls_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; - btn->tap_rls_cb.pbtn = btn; - xTimerChangePeriod(btn->tap_rls_cb.tmr, btn->tap_psh_cb.interval, portMAX_DELAY); - } else if (type == BUTTON_CB_TAP) { - btn->tap_short_cb.arg = arg; - btn->tap_short_cb.cb = cb; - btn->tap_short_cb.interval = BUTTON_GLITCH_FILTER_TIME_MS / portTICK_PERIOD_MS; - btn->tap_short_cb.pbtn = btn; - } else if (type == BUTTON_CB_SERIAL) { - iot_button_set_serial_cb(btn_handle, 1, 1000 / portTICK_PERIOD_MS, cb, arg); - } - return ESP_OK; -} - -esp_err_t iot_button_add_on_press_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg) -{ - POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); - IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG); - button_dev_t* btn = (button_dev_t*) btn_handle; - button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t)); - POINT_ASSERT(TAG, cb_new, ESP_FAIL); - cb_new->on_press = 1; - cb_new->arg = arg; - cb_new->cb = cb; - cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS; - cb_new->pbtn = btn; - cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb); - cb_new->next_cb = btn->cb_head; - btn->cb_head = cb_new; - return ESP_OK; -} - -esp_err_t iot_button_add_on_release_cb(button_handle_t btn_handle, uint32_t press_sec, button_cb cb, void* arg) -{ - POINT_ASSERT(TAG, btn_handle, ESP_ERR_INVALID_ARG); - IOT_CHECK(TAG, press_sec != 0, ESP_ERR_INVALID_ARG); - button_dev_t* btn = (button_dev_t*) btn_handle; - button_cb_t* cb_new = (button_cb_t*) calloc(1, sizeof(button_cb_t)); - POINT_ASSERT(TAG, cb_new, ESP_FAIL); - btn->taskq_on = 1; - cb_new->arg = arg; - cb_new->cb = cb; - cb_new->interval = press_sec * 1000 / portTICK_PERIOD_MS; - cb_new->pbtn = btn; - cb_new->tmr = xTimerCreate("btn_press_tmr", cb_new->interval, pdFALSE, cb_new, button_press_cb); - cb_new->next_cb = btn->cb_head; - btn->cb_head = cb_new; - return ESP_OK; -} - diff --git a/components/gpio_button/button/button_obj.cpp b/components/gpio_button/button/button_obj.cpp deleted file mode 100755 index efdcff8..0000000 --- a/components/gpio_button/button/button_obj.cpp +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include - -CButton::CButton(gpio_num_t gpio_num, button_active_t active_level) -{ - m_btn_handle = iot_button_create(gpio_num, active_level); -} - -CButton::~CButton() -{ - iot_button_delete(m_btn_handle); - m_btn_handle = NULL; -} - -esp_err_t CButton::set_evt_cb(button_cb_type_t type, button_cb cb, void* arg) -{ - return iot_button_set_evt_cb(m_btn_handle, type, cb, arg); -} - -esp_err_t CButton::set_serial_cb(button_cb cb, void* arg, TickType_t interval_tick, uint32_t start_after_sec) -{ - return iot_button_set_serial_cb(m_btn_handle, start_after_sec, interval_tick, cb, arg); -} - -esp_err_t CButton::add_on_press_cb(uint32_t press_sec, button_cb cb, void* arg) -{ - return iot_button_add_on_press_cb(m_btn_handle, press_sec, cb, arg); -} - -esp_err_t CButton::add_on_release_cb(uint32_t press_sec, button_cb cb, void* arg) -{ - return iot_button_add_on_release_cb(m_btn_handle, press_sec, cb, arg); -} - -esp_err_t CButton::rm_cb(button_cb_type_t type) -{ - return iot_button_rm_cb(m_btn_handle, type); -} diff --git a/components/gpio_button/button/include/iot_button.h b/components/gpio_button/button/include/iot_button.h deleted file mode 100755 index 3e73513..0000000 --- a/components/gpio_button/button/include/iot_button.h +++ /dev/null @@ -1,272 +0,0 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - -#ifndef _IOT_BUTTON_H_ -#define _IOT_BUTTON_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -typedef void (* button_cb)(void*); -typedef void* button_handle_t; - -typedef enum { - BUTTON_ACTIVE_HIGH = 1, /*!