From 84f106eee51d568697b0057db5a3089aebbccfcd Mon Sep 17 00:00:00 2001 From: PlxEV Date: Wed, 25 Jun 2025 06:34:03 +0100 Subject: [PATCH] fix ade7758 --- cfg/esp32devkitc/board.cfg | 2 +- components/api/src/json.c | 51 +- components/api/src/timeout_utils.c | 2 +- components/evse/CMakeLists.txt | 1 + components/evse/evse_core.c | 72 +- components/evse/evse_fsm.c | 61 +- components/evse/evse_limits.c | 5 +- components/evse/evse_manager.c | 28 +- components/evse/evse_meter.c | 105 + components/evse/evse_pilot.c | 66 +- components/evse/evse_state.c | 11 +- components/evse/include/evse_api.h | 130 +- components/evse/include/evse_core.h | 17 + components/evse/include/evse_limits.h | 51 +- components/evse/include/evse_manager.h | 32 - components/evse/include/evse_meter.h | 37 + components/evse/include/evse_state.h | 36 +- components/meter_manager/CMakeLists.txt | 2 +- .../driver/meter_ade7758/ade7758.c | 256 +- .../driver/meter_ade7758/meter_ade7758.c | 58 +- components/network/src/wifi.c | 6 +- components/network/src/wifi_2.c | 143 - components/ocpp/src/ocpp.c | 11 +- components/peripherals/CMakeLists.txt | 2 +- components/peripherals/src/ac_relay.c | 20 +- components/peripherals/src/adc121s021_dma.c | 65 +- components/peripherals/src/buzzer.c | 2 +- components/protocols/CMakeLists.txt | 1 - components/protocols/include/rest.h | 14 - components/protocols/src/mqtt.c | 53 +- components/protocols/src/rest.c | 907 -- components/rest_api/src/dashboard_api.c | 7 +- components/rest_api/src/evse_settings_api.c | 1 + components/rest_api/src/meters_settings_api.c | 6 +- components/serial_sync/src/sync_master.c | 3 + components/serial_sync/src/sync_slave.c | 3 + components/spi_bus_manager/CMakeLists.txt | 10 + .../spi_bus_manager/include/spi_bus_manager.h | 20 + .../spi_bus_manager/src/spi_bus_manager.c | 48 + main/main.c | 10 +- projeto_parte1.c | 3679 +++++-- projeto_parte10.c | 46 - projeto_parte2.c | 3678 +++++-- projeto_parte3.c | 1652 ++-- projeto_parte4.c | 896 -- projeto_parte5.c | 1066 -- projeto_parte6.c | 735 -- projeto_parte7.c | 793 -- projeto_parte8.c | 783 -- projeto_parte9.c | 1232 --- projeto_unificado.c | 8613 ----------------- projeto_unificado.zip | Bin 53746 -> 0 bytes readproject.py | 7 +- 53 files changed, 7079 insertions(+), 18456 deletions(-) create mode 100644 components/evse/evse_meter.c create mode 100644 components/evse/include/evse_core.h create mode 100644 components/evse/include/evse_meter.h delete mode 100644 components/network/src/wifi_2.c delete mode 100755 components/protocols/include/rest.h delete mode 100755 components/protocols/src/rest.c create mode 100755 components/spi_bus_manager/CMakeLists.txt create mode 100755 components/spi_bus_manager/include/spi_bus_manager.h create mode 100755 components/spi_bus_manager/src/spi_bus_manager.c mode change 100755 => 100644 projeto_parte1.c delete mode 100644 projeto_parte10.c mode change 100755 => 100644 projeto_parte2.c mode change 100755 => 100644 projeto_parte3.c delete mode 100755 projeto_parte4.c delete mode 100755 projeto_parte5.c delete mode 100755 projeto_parte6.c delete mode 100755 projeto_parte7.c delete mode 100755 projeto_parte8.c delete mode 100755 projeto_parte9.c delete mode 100755 projeto_unificado.c delete mode 100644 projeto_unificado.zip diff --git a/cfg/esp32devkitc/board.cfg b/cfg/esp32devkitc/board.cfg index 42b9270..d1014fb 100755 --- a/cfg/esp32devkitc/board.cfg +++ b/cfg/esp32devkitc/board.cfg @@ -10,7 +10,7 @@ LED_STOP_GPIO=12 #BUZZER BUZZER=y -BUZZER_GPIO=21 +BUZZER_GPIO=27 #Button BUTTON_WIFI_GPIO=32 diff --git a/components/api/src/json.c b/components/api/src/json.c index 1ea4f85..6a716b8 100755 --- a/components/api/src/json.c +++ b/components/api/src/json.c @@ -13,16 +13,21 @@ #include "timeout_utils.h" #include "evse_error.h" #include "evse_api.h" +#include "evse_limits.h" +#include "evse_state.h" +#include "evse_config.h" #include "ocpp.h" #include "board_config.h" #include "socket_lock.h" #include "proximity.h" //#include "modbus.h" //#include "modbus_tcp.h" -#include "rest.h" +//#include "rest.h" #include "temp_sensor.h" // #include "script.h" #include "date_time.h" +#include "evse_meter.h" + #define RETURN_ON_ERROR(x) \ do \ @@ -335,9 +340,9 @@ cJSON *json_get_state(void) cJSON *root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "state", evse_state_to_str(evse_get_state())); - cJSON_AddBoolToObject(root, "available", evse_is_available()); - cJSON_AddBoolToObject(root, "enabled", evse_is_enabled()); - cJSON_AddBoolToObject(root, "pendingAuth", false); + cJSON_AddBoolToObject(root, "available", evse_config_is_available()); + cJSON_AddBoolToObject(root, "enabled", evse_config_is_enabled()); + cJSON_AddBoolToObject(root, "pendingAuth", evse_is_require_auth()); cJSON_AddBoolToObject(root, "limitReached", evse_is_limit_reached()); uint32_t error = evse_error_get_bits(); @@ -383,17 +388,33 @@ cJSON *json_get_state(void) cJSON_AddItemToObject(root, "errors", errors); } -/* - cJSON_AddNumberToObject(root, "sessionTime", energy_meter_get_session_time()); - cJSON_AddNumberToObject(root, "chargingTime", energy_meter_get_charging_time()); - cJSON_AddNumberToObject(root, "consumption", energy_meter_get_consumption()); - cJSON_AddNumberToObject(root, "power", energy_meter_get_power()); - float values[3]; - energy_meter_get_voltage(values); - cJSON_AddItemToObject(root, "voltage", cJSON_CreateFloatArray(values, 3)); - energy_meter_get_current(values); - cJSON_AddItemToObject(root, "current", cJSON_CreateFloatArray(values, 3)); -*/ + + cJSON_AddNumberToObject(root, "sessionTime", evse_get_session_start()); + cJSON_AddNumberToObject(root, "chargingTime", 0); + cJSON_AddNumberToObject(root, "consumption", 0); + + // 1) Arrays temporários para ler dados do medidor + float voltage_f[EVSE_METER_PHASE_COUNT]; + float current_f[EVSE_METER_PHASE_COUNT]; + uint32_t power_w[ EVSE_METER_PHASE_COUNT]; + + // 2) Leitura dos valores via API pública + evse_meter_get_voltage(voltage_f); // já em volts + evse_meter_get_current(current_f); // já em amperes + evse_meter_get_power(power_w); // em watts por fase + + // 4) Energia acumulada em kWh + //float consumption_kwh = evse_meter_get_total_energy() / 1000.0f; // Wh → kWh + + // 6) Arrays de tensão e corrente + cJSON_AddItemToObject(root, "power", + cJSON_CreateFloatArray(power_w, EVSE_METER_PHASE_COUNT)); + cJSON_AddItemToObject(root, "voltage", + cJSON_CreateFloatArray(voltage_f, EVSE_METER_PHASE_COUNT)); + cJSON_AddItemToObject(root, "current", + cJSON_CreateFloatArray(current_f, EVSE_METER_PHASE_COUNT)); + + return root; } diff --git a/components/api/src/timeout_utils.c b/components/api/src/timeout_utils.c index 214d8ed..653e036 100755 --- a/components/api/src/timeout_utils.c +++ b/components/api/src/timeout_utils.c @@ -5,7 +5,7 @@ #include "timeout_utils.h" #include "wifi.h" -#include "rest.h" +//#include "rest.h" static void restart_func(void* arg) { diff --git a/components/evse/CMakeLists.txt b/components/evse/CMakeLists.txt index 3dac368..0dc83b4 100755 --- a/components/evse/CMakeLists.txt +++ b/components/evse/CMakeLists.txt @@ -9,6 +9,7 @@ set(srcs evse_manager.c evse_hardware.c evse_pilot.c + evse_meter.c ) idf_component_register( diff --git a/components/evse/evse_core.c b/components/evse/evse_core.c index af9cf92..fc7a76a 100755 --- a/components/evse/evse_core.c +++ b/components/evse/evse_core.c @@ -1,4 +1,4 @@ -// evse_core.c - Função principal de controle do EVSE +// evse_core.c - Main EVSE control logic #include "evse_fsm.h" #include "evse_error.h" @@ -13,23 +13,30 @@ static const char *TAG = "evse_core"; static SemaphoreHandle_t mutex; - static evse_state_t last_state = EVSE_STATE_A; static void evse_core_task(void *arg); +// ================================ +// Initialization +// ================================ + void evse_init(void) { ESP_LOGI(TAG, "EVSE Init"); - mutex = xSemaphoreCreateMutex(); + mutex = xSemaphoreCreateMutex(); // Optional: use static version for deterministic memory evse_check_defaults(); evse_fsm_reset(); - pilot_set_level(true); // Estado inicial do piloto + pilot_set_level(true); // Enable pilot output xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL); } +// ================================ +// Main Processing Logic +// ================================ + void evse_process(void) { xSemaphoreTake(mutex, portMAX_DELAY); @@ -39,42 +46,36 @@ void evse_process(void) { pilot_measure(&pilot_voltage, &is_n12v); ESP_LOGD(TAG, "Pilot: %d, -12V: %s", pilot_voltage, is_n12v ? "yes" : "no"); - if (evse_get_error() == 0 && !evse_is_error_cleared()) { + evse_error_check(pilot_voltage, is_n12v); - 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_fsm_process( - pilot_voltage, - evse_state_get_authorized(), - evse_config_is_available(), - evse_config_is_enabled() - ); + evse_limits_check(); - 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; - } - - evse_mark_error_cleared(); + 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; } + evse_mark_error_cleared(); + xSemaphoreGive(mutex); } // ================================ -// Interface pública +// Public Configuration Interface // ================================ -bool evse_is_enabled(void) { - return evse_config_is_enabled(); -} - void evse_set_enabled(bool value) { ESP_LOGI(TAG, "Set enabled %d", value); evse_config_set_enabled(value); @@ -90,21 +91,12 @@ void evse_set_available(bool value) { } // ================================ -// Tarefa principal +// Background Task // ================================ static void evse_core_task(void *arg) { while (true) { evse_process(); - vTaskDelay(pdMS_TO_TICKS(100)); + vTaskDelay(pdMS_TO_TICKS(100)); // 10 Hz cycle } -} - -uint32_t evse_get_total_energy(void) { - return 0; // Stub de 1 kWh -} - -uint32_t evse_get_instant_power(void) { - return 0; // Stub de 2 kW -} - +} \ No newline at end of file diff --git a/components/evse/evse_fsm.c b/components/evse/evse_fsm.c index 9bf8328..9867126 100755 --- a/components/evse/evse_fsm.c +++ b/components/evse/evse_fsm.c @@ -1,5 +1,3 @@ -// evse_fsm.c - Máquina de Estados EVSE com controle centralizado - #include "evse_fsm.h" #include "evse_api.h" #include "evse_pilot.h" @@ -11,6 +9,7 @@ #include "proximity.h" #include "rcm.h" #include "evse_state.h" +#include "evse_error.h" static const char *TAG = "evse_fsm"; @@ -27,6 +26,8 @@ void evse_fsm_reset(void) { c1_d1_relay_to = 0; } +// ... includes e defines como já estão + 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(); @@ -36,6 +37,21 @@ static void update_outputs(evse_state_t state) { 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()) { + 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) { + socket_lock_set_locked(false); + } + return; + } + + // Fluxo normal switch (state) { case EVSE_STATE_A: case EVSE_STATE_E: @@ -53,7 +69,6 @@ static void update_outputs(evse_state_t state) { if (board_config.socket_lock && socket_outlet) { socket_lock_set_locked(true); } - if (rcm_test()) { ESP_LOGI(TAG, "RCM self test passed"); } else { @@ -76,12 +91,28 @@ static void update_outputs(evse_state_t state) { case EVSE_STATE_C2: case EVSE_STATE_D2: pilot_set_amps(MIN(current * 10, cable_max_current * 10)); - ac_relay_set_state(true); + ac_relay_set_state(true); // Só chega aqui se não há erro! break; } } -void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { +// FSM principal - centraliza a lógica de erro e de todos os estados +void evse_fsm_process( + pilot_voltage_t pilot_voltage, + bool authorized, + bool available, + bool enabled +) { + // Proteção total: erro força F sempre! + 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); + } + update_outputs(EVSE_STATE_F); + return; + } + TickType_t now = xTaskGetTickCount(); evse_state_t prev = evse_get_state(); evse_state_t curr = prev; @@ -101,7 +132,6 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail evse_set_state(EVSE_STATE_F); break; } - switch (pilot_voltage) { case PILOT_VOLTAGE_12: evse_set_state(EVSE_STATE_A); @@ -127,15 +157,15 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail break; } } - __attribute__((fallthrough)); // Evita warning de fallthrough implícito + __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); + 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); @@ -155,18 +185,23 @@ void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool avail break; case EVSE_STATE_E: - break; // Sem transições a partir de E + // Estado elétrico grave: só reset manual + break; case EVSE_STATE_F: - if (available) { + // 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)); - update_outputs(next); + 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 255f962..91d8065 100755 --- a/components/evse/evse_limits.c +++ b/components/evse/evse_limits.c @@ -1,6 +1,7 @@ #include "evse_state.h" #include "evse_api.h" #include "evse_limits.h" +#include "evse_meter.h" #include "esp_log.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -146,8 +147,8 @@ void evse_limits_check(void) { bool reached = false; - uint32_t energy = evse_get_total_energy(); - uint32_t power = evse_get_instant_power(); + uint32_t energy = evse_meter_get_total_energy(); + uint32_t power = evse_meter_get_instant_power(); TickType_t now = xTaskGetTickCount(); TickType_t start = evse_get_session_start(); diff --git a/components/evse/evse_manager.c b/components/evse/evse_manager.c index b19702a..ad5abf1 100755 --- a/components/evse/evse_manager.c +++ b/components/evse/evse_manager.c @@ -4,6 +4,7 @@ #include "evse_hardware.h" #include "evse_config.h" #include "evse_api.h" +#include "evse_meter.h" #include "freertos/FreeRTOS.h" #include "freertos/task.h" @@ -87,6 +88,7 @@ void evse_manager_init(void) { evse_error_init(); evse_hardware_init(); evse_state_init(); + evse_meter_init(); 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)); @@ -120,29 +122,3 @@ void evse_manager_tick(void) { xSemaphoreGive(evse_mutex); } - - -// ===== API pública ===== -bool evse_manager_is_available(void) { - return evse_config_is_available(); -} - -void evse_manager_set_available(bool available) { - evse_config_set_available(available); -} - -void evse_manager_set_authorized(bool authorized) { - evse_state_set_authorized(authorized); -} - -bool evse_manager_is_charging(void) { - return evse_state_is_charging(evse_get_state()); -} - -void evse_manager_set_enabled(bool enabled) { - evse_config_set_enabled(enabled); -} - -bool evse_manager_is_enabled(void) { - return evse_config_is_enabled(); -} diff --git a/components/evse/evse_meter.c b/components/evse/evse_meter.c new file mode 100644 index 0000000..6c45113 --- /dev/null +++ b/components/evse/evse_meter.c @@ -0,0 +1,105 @@ +#include "evse_meter.h" +#include "meter_events.h" +#include "esp_event.h" +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include +#include + +static const char *TAG = "evse_meter"; +static SemaphoreHandle_t meter_mutex; + +typedef struct { + uint32_t power_watts[EVSE_METER_PHASE_COUNT]; + float voltage[EVSE_METER_PHASE_COUNT]; + float current[EVSE_METER_PHASE_COUNT]; + uint32_t energy_wh; +} evse_meter_data_t; + +static evse_meter_data_t meter_data; + +static void on_meter_event_dispatcher(void* arg, esp_event_base_t base, int32_t id, void* data) { + if (base == METER_EVENT && id == METER_EVENT_DATA_READY && data) { + const meter_event_data_t *evt = (const meter_event_data_t *)data; + if (strcmp(evt->source, "EVSE") == 0) { + evse_meter_on_meter_event(arg, data); + } + } +} + +void evse_meter_on_meter_event(void* arg, void* event_data) { + const meter_event_data_t *evt = (const meter_event_data_t *)event_data; + if (!evt) return; + + xSemaphoreTake(meter_mutex, portMAX_DELAY); + for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { + meter_data.power_watts[i] = evt->watt[i]; + meter_data.voltage[i] = evt->vrms[i]; + meter_data.current[i] = evt->irms[i]; + } + meter_data.energy_wh = (uint32_t)(evt->total_energy * 1000.0f); + xSemaphoreGive(meter_mutex); + + ESP_LOGD(TAG, + "Meter updated: power[W]={%" PRIu32 ",%" PRIu32 ",%" PRIu32 "}, " + "voltage[V]={%.2f,%.2f,%.2f}, " + "current[A]={%.2f,%.2f,%.2f}, " + "total_energy=%" PRIu32 "Wh", + meter_data.power_watts[0], meter_data.power_watts[1], meter_data.power_watts[2], + meter_data.voltage[0], meter_data.voltage[1], meter_data.voltage[2], + meter_data.current[0], meter_data.current[1], meter_data.current[2], + meter_data.energy_wh + ); +} + +void evse_meter_init(void) { + meter_mutex = xSemaphoreCreateMutex(); + ESP_ERROR_CHECK(meter_mutex ? ESP_OK : ESP_FAIL); + ESP_ERROR_CHECK(esp_event_handler_register( + METER_EVENT, METER_EVENT_DATA_READY, + on_meter_event_dispatcher, NULL)); + memset(&meter_data, 0, sizeof(meter_data)); + ESP_LOGI(TAG, "EVSE Meter listener registered."); +} + +uint32_t evse_meter_get_instant_power(void) { + xSemaphoreTake(meter_mutex, portMAX_DELAY); + uint32_t sum = 0; + for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { + sum += meter_data.power_watts[i]; + } + xSemaphoreGive(meter_mutex); + return sum; +} + +uint32_t evse_meter_get_total_energy(void) { + xSemaphoreTake(meter_mutex, portMAX_DELAY); + uint32_t val = meter_data.energy_wh; + xSemaphoreGive(meter_mutex); + return val; +} + +void evse_meter_get_power(uint32_t power[EVSE_METER_PHASE_COUNT]) { + xSemaphoreTake(meter_mutex, portMAX_DELAY); + for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { + power[i] = meter_data.power_watts[i]; + } + xSemaphoreGive(meter_mutex); +} + +void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]) { + xSemaphoreTake(meter_mutex, portMAX_DELAY); + for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { + voltage[i] = meter_data.voltage[i]; + } + xSemaphoreGive(meter_mutex); +} + +void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT]) { + xSemaphoreTake(meter_mutex, portMAX_DELAY); + for (int i = 0; i < EVSE_METER_PHASE_COUNT; ++i) { + current[i] = meter_data.current[i]; + } + xSemaphoreGive(meter_mutex); +} diff --git a/components/evse/evse_pilot.c b/components/evse/evse_pilot.c index dbf1ff1..9c57db1 100755 --- a/components/evse/evse_pilot.c +++ b/components/evse/evse_pilot.c @@ -10,7 +10,7 @@ #include "esp_rom_sys.h" #include "evse_pilot.h" -#include "adc.h" +#include "adc121s021_dma.h" #include "board_config.h" #define PILOT_PWM_TIMER LEDC_TIMER_0 @@ -23,10 +23,24 @@ #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 + 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, @@ -47,27 +61,24 @@ 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)); - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12, - }; - - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.pilot_adc_channel, &config)); + // Inicializa ADC121S021 externo + adc121s021_dma_init(); } void pilot_set_level(bool level) { + if (last_pilot_level == level) return; // só muda se necessário + 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 } void pilot_set_amps(uint16_t amps) { - ESP_LOGI(TAG, "Set amps %d", amps); - if (amps < 60 || amps > 800) { ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps); return; @@ -79,14 +90,17 @@ void pilot_set_amps(uint16_t amps) } else { duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100)); } + if (duty > PILOT_PWM_MAX_DUTY) duty = PILOT_PWM_MAX_DUTY; - if (duty > PILOT_PWM_MAX_DUTY) - duty = PILOT_PWM_MAX_DUTY; + if (last_pilot_level == 0 && last_pwm_duty == duty) return; + last_pilot_level = 0; + last_pwm_duty = duty; ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY); ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); } + static int compare_int(const void *a, const void *b) { return (*(int *)a - *(int *)b); } @@ -94,10 +108,8 @@ static int compare_int(const void *a, const void *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]; } @@ -105,10 +117,8 @@ static int select_low_median_qsort(int *src, int n, int percent) { 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)]; } @@ -119,11 +129,13 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) int samples[NUM_PILOT_SAMPLES]; int collected = 0, attempts = 0; - int sample; + uint16_t adc_sample = 0; + // Lê samples usando ADC121S021 externo while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) { - if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) { - samples[collected++] = sample; + adc_sample = 0; + if (adc121s021_dma_get_sample(&adc_sample)) { + samples[collected++] = adc_sample; esp_rom_delay_us(10); } else { esp_rom_delay_us(100); @@ -141,20 +153,10 @@ void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) int high_raw = select_high_median_qsort(samples, collected, PILOT_EXTREME_PERCENT); int low_raw = select_low_median_qsort(samples, collected, PILOT_EXTREME_PERCENT); + int high_mv = adc_raw_to_mv(high_raw); + int low_mv = adc_raw_to_mv(low_raw); - ESP_LOGD(TAG, "Final: high_raw=%d, low_raw=%d", high_raw, low_raw); - - int high_mv = 0; - int low_mv = 0; - - if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK || - adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) { - ESP_LOGW(TAG, "ADC calibration failed"); - *up_voltage = PILOT_VOLTAGE_1; - *down_voltage_n12 = false; - return; - } - + // 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_state.c b/components/evse/evse_state.c index fb04c42..3a30a7c 100755 --- a/components/evse/evse_state.c +++ b/components/evse/evse_state.c @@ -46,9 +46,14 @@ void evse_set_state(evse_state_t state) { current_state = state; changed = true; + // When entering a charging state, record the start tick if (evse_state_is_charging(state) && !evse_state_is_charging(previous_state)) { session_start_tick = xTaskGetTickCount(); } + // When exiting a charging state, reset the start tick + else if (!evse_state_is_charging(state) && evse_state_is_charging(previous_state)) { + session_start_tick = 0; + } } portEXIT_CRITICAL(&state_mux); @@ -60,7 +65,11 @@ void evse_set_state(evse_state_t state) { evse_state_event_data_t evt = { .state = map_state_to_event(state) }; - esp_event_post(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt), portMAX_DELAY); + esp_event_post(EVSE_EVENTS, + EVSE_EVENT_STATE_CHANGED, + &evt, + sizeof(evt), + portMAX_DELAY); } } diff --git a/components/evse/include/evse_api.h b/components/evse/include/evse_api.h index 00cd71a..78274d6 100755 --- a/components/evse/include/evse_api.h +++ b/components/evse/include/evse_api.h @@ -3,73 +3,77 @@ #include #include -#include "esp_err.h" -#include "evse_state.h" // Define evse_state_t +#include "evse_state.h" // Tipos e estados +#include "freertos/FreeRTOS.h" -// Inicialização -void evse_init(void); -void evse_process(void); +#ifdef __cplusplus +extern "C" { +#endif -// Estado +// =============================== +// Core EVSE State +// =============================== + +/** + * @brief Get current EVSE state (e.g., A, B1, C2). + */ evse_state_t evse_get_state(void); -const char* evse_state_to_str(evse_state_t state); -bool evse_is_connector_plugged(evse_state_t state); + +/** + * @brief Set the EVSE state (e.g., called by FSM or hardware layer). + */ +void evse_set_state(evse_state_t state); + +/** + * @brief Get timestamp when the current session started (for timing limits). + */ +TickType_t evse_get_session_start(void); + +// =============================== +// Charging Session Info +// =============================== + +/** + * @brief Returns true if the EV is charging (C1 or C2). + */ +bool evse_state_is_charging(evse_state_t state); + +/** + * @brief Returns true if the EV is connected (plugged). + */ +bool evse_state_is_plugged(evse_state_t state); + +/** + * @brief Returns true if a charging session is active (B2, C1, C2). + */ +bool evse_state_is_session(evse_state_t state); + +// =============================== +// Authorization +// =============================== + +/** + * @brief Set whether the vehicle is authorized to charge. + */ +void evse_state_set_authorized(bool authorized); + +/** + * @brief Get current authorization status. + */ +bool evse_state_get_authorized(void); + + +// =============================== +// Limit Status +// =============================== + +/** + * @brief Returns true if any runtime charging limit has been reached. + */ bool evse_is_limit_reached(void); -// Autorização e disponibilidade -bool evse_is_enabled(void); -void evse_set_enabled(bool value); -bool evse_is_available(void); -void evse_set_available(bool value); -bool evse_is_require_auth(void); -void evse_set_require_auth(bool value); - -// Corrente -uint16_t evse_get_charging_current(void); -esp_err_t evse_set_charging_current(uint16_t value); -uint16_t evse_get_default_charging_current(void); -esp_err_t evse_set_default_charging_current(uint16_t value); -uint8_t evse_get_max_charging_current(void); -esp_err_t evse_set_max_charging_current(uint8_t value); - -// Temperatura -uint8_t evse_get_temp_threshold(void); -esp_err_t evse_set_temp_threshold(uint8_t value); - -// RCM / Socket -bool evse_get_socket_outlet(void); -esp_err_t evse_set_socket_outlet(bool value); -bool evse_is_rcm(void); -esp_err_t evse_set_rcm(bool value); - -// Limites -uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); -uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); -uint16_t evse_get_under_power_limit(void); -void evse_set_under_power_limit(uint16_t value); - -void evse_set_limit_reached(bool value); - -// Energia total acumulada da sessão (em Wh) -uint32_t evse_get_total_energy(void); - -// Potência instantânea medida (em W) -uint32_t evse_get_instant_power(void); - -// Limites default -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); - - -uint32_t evse_get_total_energy(void); -uint32_t evse_get_instant_power(void); - - +#ifdef __cplusplus +} +#endif #endif // EVSE_API_H diff --git a/components/evse/include/evse_core.h b/components/evse/include/evse_core.h new file mode 100644 index 0000000..220a32b --- /dev/null +++ b/components/evse/include/evse_core.h @@ -0,0 +1,17 @@ +#ifndef EVSE_CORE_H +#define EVSE_CORE_H + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the EVSE system and starts core task loop. + */ +void evse_init(void); + +#ifdef __cplusplus +} +#endif + +#endif // EVSE_CORE_H diff --git a/components/evse/include/evse_limits.h b/components/evse/include/evse_limits.h index 4d5fb39..a816449 100755 --- a/components/evse/include/evse_limits.h +++ b/components/evse/include/evse_limits.h @@ -9,47 +9,58 @@ extern "C" { #endif -// ======================== -// Limit state control -// ======================== +// ============================ +// Limit Status & Evaluation +// ============================ /** - * @brief Sets the 'limit reached' flag. Used internally when a session exceeds defined thresholds. + * @brief Sets the internal 'limit reached' flag. + * Called internally when a limit condition is triggered. */ void evse_set_limit_reached(bool value); /** - * @brief Returns whether any session limit has been reached (energy, time or power). + * @brief Returns true if any runtime charging limit has been reached. */ bool evse_get_limit_reached(void); -// ======================== -// Limit checking -// ======================== - /** - * @brief Evaluates if the session has exceeded any configured limits. - * Should be called periodically while in charging state. + * @brief Checks if any session limit has been exceeded (energy, time or power). + * Should be called periodically during charging. */ void evse_limits_check(void); -// ======================== -// Runtime limit configuration -// ======================== +// ============================ +// Runtime Limit Configuration +// ============================ +/** + * @brief Get/set energy consumption limit (in Wh). + */ uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); // in Wh +void evse_set_consumption_limit(uint32_t value); +/** + * @brief Get/set maximum charging time (in seconds). + */ uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); // in seconds +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); // in Watts +void evse_set_under_power_limit(uint16_t value); -// ======================== -// Default (persistent) limits -// ======================== +// ============================ +// 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); diff --git a/components/evse/include/evse_manager.h b/components/evse/include/evse_manager.h index 44433f6..51af86c 100755 --- a/components/evse/include/evse_manager.h +++ b/components/evse/include/evse_manager.h @@ -26,38 +26,6 @@ void evse_manager_init(void); */ void evse_manager_tick(void); -/** - * @brief Verifica se o EVSE está disponível para uso. - * - * Isso considera falhas críticas, disponibilidade configurada, etc. - */ -bool evse_manager_is_available(void); - -/** - * @brief Define se o EVSE deve estar disponível (ex: via controle remoto). - */ -void evse_manager_set_available(bool available); - -/** - * @brief Define se o EVSE está autorizado a carregar (ex: após autenticação). - */ -void evse_manager_set_authorized(bool authorized); - -/** - * @brief Verifica se o EVSE está atualmente carregando. - */ -bool evse_manager_is_charging(void); - -/** - * @brief Ativa ou desativa logicamente o EVSE (controla habilitação geral). - */ -void evse_manager_set_enabled(bool enabled); - -/** - * @brief Verifica se o EVSE está ativado logicamente. - */ -bool evse_manager_is_enabled(void); - #ifdef __cplusplus } #endif diff --git a/components/evse/include/evse_meter.h b/components/evse/include/evse_meter.h new file mode 100644 index 0000000..dc3bbb5 --- /dev/null +++ b/components/evse/include/evse_meter.h @@ -0,0 +1,37 @@ +#ifndef EVSE_METER_H +#define EVSE_METER_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define EVSE_METER_PHASE_COUNT 3 + +/// Inicializa o módulo EVSE Meter e registra os tratadores de eventos +void evse_meter_init(void); + +/// Retorna a potência instantânea (soma das 3 fases, em watts) +uint32_t evse_meter_get_instant_power(void); + +/// Retorna a energia total acumulada (em Wh) +uint32_t evse_meter_get_total_energy(void); + +/// Retorna as potências instantâneas nas fases L1, L2 e L3 (em watts) +void evse_meter_get_power(uint32_t power[EVSE_METER_PHASE_COUNT]); + +/// Retorna as tensões medidas nas fases L1, L2 e L3 (em volts) +void evse_meter_get_voltage(float voltage[EVSE_METER_PHASE_COUNT]); + +/// Retorna as correntes medidas nas fases L1, L2 e L3 (em amperes) +void evse_meter_get_current(float current[EVSE_METER_PHASE_COUNT]); + +/// Handler interno para eventos do medidor (não chamar externamente) +void evse_meter_on_meter_event(void* arg, void* event_data); + +#ifdef __cplusplus +} +#endif + +#endif // EVSE_METER_H diff --git a/components/evse/include/evse_state.h b/components/evse/include/evse_state.h index 4b255e0..0b0c86b 100755 --- a/components/evse/include/evse_state.h +++ b/components/evse/include/evse_state.h @@ -5,6 +5,10 @@ #include "freertos/FreeRTOS.h" #include "evse_events.h" +#ifdef __cplusplus +extern "C" { +#endif + // ============================ // EVSE Pilot Signal States // ============================ @@ -22,21 +26,21 @@ typedef enum { } evse_state_t; // ============================ -// Initialization & Core Control +// Initialization // ============================ /** - * @brief Initializes the EVSE state machine. + * @brief Initializes the EVSE state machine and default state. */ void evse_state_init(void); /** - * @brief Periodic tick function for the state machine. + * @brief Periodic tick for state handling (optional hook). */ void evse_state_tick(void); // ============================ -// State Access +// State Access & Control // ============================ /** @@ -45,51 +49,55 @@ void evse_state_tick(void); evse_state_t evse_get_state(void); /** - * @brief Updates the current EVSE state and triggers events. + * @brief Sets the current EVSE state and emits a change event if needed. */ void evse_set_state(evse_state_t state); /** - * @brief Returns the tick count when charging session started. + * @brief Returns the tick count when the current charging session began. */ TickType_t evse_get_session_start(void); /** - * @brief Converts the state enum to a human-readable string. + * @brief Converts the state enum into a human-readable string. */ const char* evse_state_to_str(evse_state_t state); // ============================ -// State Evaluators +// State Evaluation Helpers // ============================ /** - * @brief Returns true if the state represents an active session (B2, C1, C2). + * @brief True if EV is in an active session (B2, C1, C2). */ bool evse_state_is_session(evse_state_t state); /** - * @brief Returns true if the state represents active charging (C1, C2). + * @brief True if EV is actively charging (C1, C2). */ bool evse_state_is_charging(evse_state_t state); /** - * @brief Returns true if the vehicle is plugged in. + * @brief True if EV is physically plugged in (B1 and beyond). */ bool evse_state_is_plugged(evse_state_t state); // ============================ -// Authorization +// Authorization Control // ============================ /** - * @brief Sets the vehicle authorization state. + * @brief Sets whether the EV is authorized to charge. */ void evse_state_set_authorized(bool authorized); /** - * @brief Returns the current vehicle authorization state. + * @brief Gets whether the EV is currently authorized. */ bool evse_state_get_authorized(void); +#ifdef __cplusplus +} +#endif + #endif // EVSE_STATE_H diff --git a/components/meter_manager/CMakeLists.txt b/components/meter_manager/CMakeLists.txt index 63d9418..1e4cdb0 100755 --- a/components/meter_manager/CMakeLists.txt +++ b/components/meter_manager/CMakeLists.txt @@ -22,4 +22,4 @@ set(includes idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "${includes}" PRIV_REQUIRES nvs_flash - REQUIRES esp_event esp-modbus) + REQUIRES esp_event esp-modbus spi_bus_manager) diff --git a/components/meter_manager/driver/meter_ade7758/ade7758.c b/components/meter_manager/driver/meter_ade7758/ade7758.c index fdff5dc..6e5a7a3 100755 --- a/components/meter_manager/driver/meter_ade7758/ade7758.c +++ b/components/meter_manager/driver/meter_ade7758/ade7758.c @@ -1,186 +1,184 @@ #include #include - #include "driver/spi_master.h" - #include "sdkconfig.h" #include "esp_log.h" #include "ade7758.h" +#include "driver/gpio.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_rom_sys.h" -spi_bus_config_t _spi_bus_cfg; -// spi_device_interface_config_t _spi_interface_cfg; -spi_device_handle_t _handle; -spi_host_device_t _spi_peripheral; -spi_transaction_t _spi_transaction; -esp_err_t transferByte(const uint8_t reg_addr, const uint8_t data, const uint8_t command) -{ - _spi_transaction.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; - _spi_transaction.length = 8; - //_spi_transaction.rxlength = 8; - _spi_transaction.cmd = command; - _spi_transaction.addr = reg_addr; - _spi_transaction.tx_data[0] = data; +#define PIN_ADUM_EN 4 // ou o pino real conectado ao VE1 do ADUM1401 - return spi_device_transmit(_handle, &_spi_transaction); + +static const char *TAG = "ade7758"; + +// --- SPI internals --- +static spi_device_handle_t ade7758_spi_handle = NULL; +static spi_host_device_t spi_host = SPI2_HOST; // default + +static spi_transaction_t spi_transaction; + +// --- Configuração SPI do dispositivo --- +static const uint8_t MODE = 2; +static const uint8_t ADDR_BITS = 7; +static const uint8_t CMD_BITS = 1; +static const uint8_t SPI_WRITE = 1; +static const uint8_t SPI_READ = 0; +static const int BUS_SPEED_HZ = 1000000; + + +static void adum1401_select(void) { + gpio_set_level(PIN_ADUM_EN, 1); + esp_rom_delay_us(2); // curto delay para estabilização } -esp_err_t transferMultiplesBytes(const uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t data_length, const uint8_t command) -{ - spi_transaction_t spi_transaction_multibyte; // spi_transaction_t to use the tx and rx buffers - - if (data_length < 1) - { - data_length = 1; - } - - spi_transaction_multibyte.flags = 0; // SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; - spi_transaction_multibyte.length = (8 * data_length); - spi_transaction_multibyte.rxlength = 0; - spi_transaction_multibyte.cmd = command; - spi_transaction_multibyte.addr = reg_addr; - spi_transaction_multibyte.tx_buffer = tx_buf; - spi_transaction_multibyte.rx_buffer = rx_buf; - - return spi_device_transmit(_handle, &spi_transaction_multibyte); +static void adum1401_deselect(void) { + esp_rom_delay_us(2); // opcional: aguarde para evitar glitch + gpio_set_level(PIN_ADUM_EN, 0); } -esp_err_t Init(const spi_host_device_t spi_peripheral, const int pin_miso, const int pin_mosi, const int pin_sclk) -{ - esp_err_t status = ESP_OK; +// === Transações básicas === - _spi_peripheral = spi_peripheral; +static esp_err_t transfer_byte(uint8_t reg_addr, uint8_t data, uint8_t command) { + adum1401_select(); - _spi_transaction.tx_buffer = NULL; - _spi_transaction.rx_buffer = NULL; + spi_transaction.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; + spi_transaction.length = 8; + spi_transaction.cmd = command; + spi_transaction.addr = reg_addr; + spi_transaction.tx_data[0] = data; - _spi_bus_cfg.mosi_io_num = pin_mosi; - _spi_bus_cfg.miso_io_num = pin_miso; - _spi_bus_cfg.sclk_io_num = pin_sclk; - _spi_bus_cfg.quadwp_io_num = -1; - _spi_bus_cfg.quadhd_io_num = -1; + esp_err_t err = spi_device_transmit(ade7758_spi_handle, &spi_transaction); - status |= spi_bus_initialize(spi_peripheral, &_spi_bus_cfg, SPI_DMA_CH_AUTO); - - return status; + adum1401_deselect(); + return err; } -esp_err_t RegisterDevice(const uint8_t mode, const int ss, const int addr_length, const int command_length, const int bus_speed) -{ - esp_err_t status = ESP_OK; - spi_device_interface_config_t _spi_interface_cfg = { - .command_bits = command_length, - .address_bits = addr_length, - .mode = mode, - .clock_speed_hz = bus_speed, - .spics_io_num = ss, +static esp_err_t transfer_bytes(uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t len, uint8_t command) { + if (len < 1) len = 1; + + spi_transaction_t t = { + .flags = 0, + .length = 8 * len, + .cmd = command, + .addr = reg_addr, + .tx_buffer = tx_buf, + .rx_buffer = rx_buf + }; + + adum1401_select(); + esp_err_t err = spi_device_transmit(ade7758_spi_handle, &t); + adum1401_deselect(); + return err; +} + + +// === Interface pública === + +esp_err_t Init(spi_host_device_t host, int pin_miso, int pin_mosi, int pin_sclk) { + // Essa função não inicializa o barramento SPI + // Apenas armazena os parâmetros + spi_host = host; + return ESP_OK; +} + +esp_err_t InitSpi(int cs_gpio) { + spi_device_interface_config_t devcfg = { + .command_bits = CMD_BITS, + .address_bits = ADDR_BITS, + .mode = MODE, + .clock_speed_hz = BUS_SPEED_HZ, + .spics_io_num = cs_gpio, .queue_size = 5, }; - status |= spi_bus_add_device(_spi_peripheral, &_spi_interface_cfg, &_handle); + gpio_config_t io_conf = { + .pin_bit_mask = BIT64(PIN_ADUM_EN), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + gpio_config(&io_conf); + gpio_set_level(PIN_ADUM_EN, 0); // inicialmente desativado - return status; + + return spi_bus_add_device(spi_host, &devcfg, &ade7758_spi_handle); } -uint8_t ReadRegister(const uint8_t reg_addr, const uint8_t command) -{ - transferByte(reg_addr, 0, command); - - return _spi_transaction.rx_data[0]; +spi_device_handle_t GetHandle(void) { + return ade7758_spi_handle; } -esp_err_t WriteRegister(const uint8_t reg_addr, const uint8_t reg_data, const uint8_t command) -{ - esp_err_t status = ESP_OK; +// === Registro de acesso === - status |= transferByte(reg_addr, reg_data, command); - - return status; +uint8_t ReadRegister(uint8_t reg_addr, uint8_t command) { + transfer_byte(reg_addr, 0, command); + return spi_transaction.rx_data[0]; } -esp_err_t WriteRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command) -{ - return transferMultiplesBytes(reg_addr, reg_data_buffer, NULL, byte_count, command); +esp_err_t WriteRegister(uint8_t reg_addr, uint8_t data, uint8_t command) { + return transfer_byte(reg_addr, data, command); } -esp_err_t ReadRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command) -{ - return transferMultiplesBytes(reg_addr, NULL, reg_data_buffer, byte_count, command); +esp_err_t WriteRegisterMultipleBytes(uint8_t reg, uint8_t *data, uint8_t count, uint8_t command) { + return transfer_bytes(reg, data, NULL, count, command); } -spi_device_handle_t GetHandle(void) -{ - return _handle; +esp_err_t ReadRegisterMultipleBytes(uint8_t reg, uint8_t *buf, uint8_t count, uint8_t command) { + return transfer_bytes(reg, NULL, buf, count, command); } -static uint8_t MODE = 2; -static uint8_t ADDR_BITS = 7; -static uint8_t CMD_BITS = 1; +// === Leitura e escrita de tamanho fixo === -static uint8_t SPI_WRITE = 1; -static uint8_t SPI_READ = 0; -static int BUSSPEED = 1000000; - -// static const char TAG[] = "ade7758"; - -esp_err_t write8(uint8_t reg, uint8_t value) -{ +esp_err_t write8(uint8_t reg, uint8_t value) { return WriteRegister(reg, value, SPI_WRITE); } -esp_err_t write16(uint8_t reg, uint32_t value) -{ - uint8_t buff[2]; - - buff[0] = (value >> 8) & 0xFF; - buff[1] = (value >> 0) & 0xFF; - - return WriteRegisterMultipleBytes(reg, buff, 3, SPI_WRITE); +esp_err_t write16(uint8_t reg, uint32_t value) { + uint8_t buf[2] = { + (value >> 8) & 0xFF, + (value >> 0) & 0xFF + }; + return WriteRegisterMultipleBytes(reg, buf, 2, SPI_WRITE); } -esp_err_t write24(uint8_t reg, uint32_t value) -{ - uint8_t buff[2]; - - buff[0] = (value >> 16) & 0xFF; - buff[1] = (value >> 8) & 0xFF; - buff[2] = (value >> 0) & 0xFF; - - return WriteRegisterMultipleBytes(reg, buff, 4, SPI_WRITE); +esp_err_t write24(uint8_t reg, uint32_t value) { + uint8_t buf[3] = { + (value >> 16) & 0xFF, + (value >> 8) & 0xFF, + (value >> 0) & 0xFF + }; + return WriteRegisterMultipleBytes(reg, buf, 3, SPI_WRITE); } -uint8_t read8(const uint8_t reg) -{ - uint8_t buff[1]; - ReadRegisterMultipleBytes(reg, buff, 2, SPI_READ); - return buff[0]; +uint8_t read8(uint8_t reg) { + uint8_t buf[1]; + ReadRegisterMultipleBytes(reg, buf, 1, SPI_READ); + return buf[0]; } -uint32_t read16(const uint8_t reg) -{ - uint8_t buff[2]; - ReadRegisterMultipleBytes(reg, buff, 3, SPI_READ); - return buff[0] << 8 | buff[1]; +uint32_t read16(uint8_t reg) { + uint8_t buf[2]; + ReadRegisterMultipleBytes(reg, buf, 2, SPI_READ); + return (buf[0] << 8) | buf[1]; } -uint32_t read24(const uint8_t reg) -{ - uint8_t buff[3]; - ReadRegisterMultipleBytes(reg, buff, 4, SPI_READ); - return buff[0] << 16 | buff[1] << 8 | buff[2]; +uint32_t read24(uint8_t reg) { + uint8_t buf[3]; + ReadRegisterMultipleBytes(reg, buf, 3, SPI_READ); + return (buf[0] << 16) | (buf[1] << 8) | buf[2]; } -esp_err_t readBlockData(const uint8_t reg, uint8_t *buf, const int length) -{ - return ReadRegisterMultipleBytes(reg, buf, length, SPI_READ); +esp_err_t readBlockData(uint8_t reg, uint8_t *buf, int len) { + return ReadRegisterMultipleBytes(reg, buf, len, SPI_READ); } -esp_err_t InitSpi(const int ss) -{ - return RegisterDevice(MODE, ss, ADDR_BITS, CMD_BITS, BUSSPEED); -} /***************************** * diff --git a/components/meter_manager/driver/meter_ade7758/meter_ade7758.c b/components/meter_manager/driver/meter_ade7758/meter_ade7758.c index 1882d46..af23f8a 100755 --- a/components/meter_manager/driver/meter_ade7758/meter_ade7758.c +++ b/components/meter_manager/driver/meter_ade7758/meter_ade7758.c @@ -1,4 +1,5 @@ #include "meter_ade7758.h" +#include "spi_bus_manager.h" #include "ade7758.h" #include "meter_events.h" @@ -11,16 +12,13 @@ #include "freertos/task.h" #include "freertos/semphr.h" -#include "driver/spi_master.h" +#include "driver/gpio.h" #define TAG "meter_ade7758" -// === Configurações de hardware === -#define PIN_NUM_CLK 15 -#define PIN_NUM_MOSI 2 -#define PIN_NUM_MISO 4 -#define PIN_NUM_CS 23 -#define EEPROM_HOST HSPI_HOST +// === Pinos === +#define PIN_NUM_CS 15 +#define PIN_ADUM_EN 4 // === Constantes de calibração === #define VRMS_CAL 4732.78f @@ -28,13 +26,13 @@ #define METER_READ_INTERVAL_MS 5000 -// === Dados internos === +// === Estrutura interna === typedef struct { float vrms[3]; float irms[3]; int watt[3]; - int var[3]; // reservados - int va[3]; // reservados + int var[3]; // reservado + int va[3]; // reservado } meter_ade7758_internal_data_t; static meter_ade7758_internal_data_t meter_data; @@ -42,7 +40,8 @@ static TaskHandle_t meter_task = NULL; static SemaphoreHandle_t meter_mutex = NULL; static uint32_t meter_watchdog_counter = 0; -// === Utilitários internos === + +// === Post de evento === static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) { meter_event_data_t evt = { .frequency = 0, @@ -54,22 +53,23 @@ static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) memcpy(evt.irms, data->irms, sizeof(evt.irms)); memcpy(evt.watt, data->watt, sizeof(evt.watt)); - esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, - &evt, sizeof(evt), pdMS_TO_TICKS(10)); - + esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10)); if (err != ESP_OK) { ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err)); } } +// === Task de leitura === static void meter_ade7758_task_func(void *param) { - ESP_LOGI(TAG, "Meter task started"); + ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada"); meter_ade7758_internal_data_t previous = {0}; while (true) { meter_ade7758_internal_data_t meterData = {0}; + //ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion()); + meterData.vrms[0] = avrms() / VRMS_CAL; meterData.vrms[1] = bvrms() / VRMS_CAL; meterData.vrms[2] = cvrms() / VRMS_CAL; @@ -97,10 +97,9 @@ static void meter_ade7758_task_func(void *param) { } } -// === Interface pública: controle === - +// === Inicialização === esp_err_t meter_ade7758_init(void) { - ESP_LOGI(TAG, "Inicializando medidor ADE7758..."); + ESP_LOGI(TAG, "Inicializando ADE7758..."); if (!meter_mutex) { meter_mutex = xSemaphoreCreateMutex(); @@ -110,21 +109,38 @@ esp_err_t meter_ade7758_init(void) { } } - esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK); + if (!spi_bus_manager_is_initialized()) { + esp_err_t err = spi_bus_manager_init(); // usa pinos padrão + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro ao inicializar SPI: %s", esp_err_to_name(err)); + return err; + } + } + + vTaskDelay(pdMS_TO_TICKS(10)); // Delay de estabilização + + esp_err_t err = Init(spi_bus_manager_get_host(), -1, -1, -1); if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro ao inicializar SPI (%d)", err); + ESP_LOGE(TAG, "Erro Init SPI ADE7758: %s", esp_err_to_name(err)); + return err; + } + + err = InitSpi(PIN_NUM_CS); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro ao registrar dispositivo SPI: %s", esp_err_to_name(err)); return err; } - InitSpi(PIN_NUM_CS); gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); setupDivs(1, 1, 1); setLcycMode(0x00); resetStatus(); + ESP_LOGI(TAG, "ADE7758 inicializado com sucesso."); return ESP_OK; } +// === Execução === esp_err_t meter_ade7758_start(void) { if (meter_task) return ESP_ERR_INVALID_STATE; diff --git a/components/network/src/wifi.c b/components/network/src/wifi.c index c619329..0935bda 100755 --- a/components/network/src/wifi.c +++ b/components/network/src/wifi.c @@ -165,7 +165,7 @@ void wifi_ini(void) char chargeid[6]; uint8_t mac[6]; esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - sprintf((char *)chargeid, MDNS_SSID, mac[5]); + sprintf((char *)chargeid, MDNS_SSID, 0); ESP_ERROR_CHECK(mdns_init()); ESP_ERROR_CHECK(mdns_hostname_set(chargeid)); @@ -188,7 +188,9 @@ esp_netif_t *wifi_get_ap_netif(void) esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) { - ESP_LOGI(TAG, "Wifi set config"); + + ESP_LOGI(TAG, "wifi_set_config(enabled=%d, ssid=\"%s\")", enabled, ssid?:""); + if (enabled) { diff --git a/components/network/src/wifi_2.c b/components/network/src/wifi_2.c deleted file mode 100644 index c958c63..0000000 --- a/components/network/src/wifi_2.c +++ /dev/null @@ -1,143 +0,0 @@ -#include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_mac.h" -#include "nvs.h" -#include "mdns.h" - -#include "wifi.h" - - -#include "nvs_flash.h" -#include - -#define WIFI_STORAGE_NAMESPACE "wifi_config" - - - -#define TAG "wifi" -#define AP_SSID "plx-%02x%02x%02x" -#define MDNS_HOSTNAME "plx%02x" - -#define NVS_NAMESPACE "wifi" - -static nvs_handle_t nvs; -static esp_netif_t *ap_netif; -EventGroupHandle_t wifi_event_group; - -// -// Event handler para modo AP -// -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - if (event_base == WIFI_EVENT) { - switch (event_id) { - case WIFI_EVENT_AP_STACONNECTED: { - wifi_event_ap_staconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " conectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - case WIFI_EVENT_AP_STADISCONNECTED: { - wifi_event_ap_stadisconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " desconectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - } - } -} - -// -// Iniciar o AP com SSID baseado no MAC -// -void wifi_ap_start(void) -{ - ESP_LOGI(TAG, "Iniciando AP"); - - ESP_ERROR_CHECK(esp_wifi_stop()); - - wifi_config_t ap_config = { - .ap = { - .ssid = "", - .ssid_len = 0, - .channel = 1, - .password = "", - .max_connection = 4, - .authmode = WIFI_AUTH_OPEN - } - }; - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - snprintf((char *)ap_config.ap.ssid, sizeof(ap_config.ap.ssid), AP_SSID, mac[3], mac[4], mac[5]); - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT); -} - -// -// Inicializar Wi-Fi em modo AP -// -void wifi_ini(void) -{ - ESP_LOGI(TAG, "Inicializando Wi-Fi (modo AP)"); - - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - wifi_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_netif_init()); - /* - if (!esp_event_loop_is_running()) { - ESP_ERROR_CHECK(esp_event_loop_create_default()); - }*/ - - ap_netif = esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - char hostname[16]; - snprintf(hostname, sizeof(hostname), MDNS_HOSTNAME, mac[5]); - - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(hostname)); - ESP_ERROR_CHECK(mdns_instance_name_set("EVSE Controller")); - - wifi_ap_start(); -} - -esp_netif_t *wifi_get_ap_netif(void) -{ - return ap_netif; -} - -esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) { - - return ESP_OK; -} - -void wifi_get_ssid(char *value) { - // Your implementation here -} - -void wifi_get_password(char *value) { - // Your implementation here -} - -bool wifi_get_enabled(void) -{ - return true; -} diff --git a/components/ocpp/src/ocpp.c b/components/ocpp/src/ocpp.c index 10edfe8..bda2723 100755 --- a/components/ocpp/src/ocpp.c +++ b/components/ocpp/src/ocpp.c @@ -4,6 +4,7 @@ #include "evse_api.h" #include "evse_error.h" #include "evse_state.h" +#include "evse_config.h" #include "esp_wifi.h" #include "nvs.h" @@ -48,10 +49,10 @@ static void ocpp_task_func(void *param) mg_mgr_poll(&mgr, 10); ocpp_loop(); - if (evse_is_enabled() != ocpp_isOperative()) + if (evse_config_is_enabled() != ocpp_isOperative()) { printf("ocpp_isOperative()"); - evse_set_enabled(ocpp_isOperative()); + evse_config_set_enabled(ocpp_isOperative()); } } vTaskDelay(pdMS_TO_TICKS(500)); @@ -129,7 +130,7 @@ void ocpp_set_rfid(char *value) bool setConnectorPluggedInput() { // ESP_LOGI(TAG, "setConnectorPluggedInput"); - return evse_is_connector_plugged(evse_get_state()); + return evse_state_is_plugged(evse_get_state()); // return true; } @@ -143,8 +144,8 @@ bool setEvReadyInput() bool setEvseReadyInput() { // ESP_LOGI(TAG, "EvseReadyInput"); - return evse_is_enabled(); - // return false; + return evse_config_is_enabled(); + //return false; } float setPowerMeterInput() diff --git a/components/peripherals/CMakeLists.txt b/components/peripherals/CMakeLists.txt index 4b72d4a..99f9a29 100755 --- a/components/peripherals/CMakeLists.txt +++ b/components/peripherals/CMakeLists.txt @@ -18,4 +18,4 @@ set(srcs idf_component_register(SRCS "${srcs}" INCLUDE_DIRS "include" PRIV_REQUIRES nvs_flash driver esp_adc esp_timer - REQUIRES config evse api ntc_driver) + REQUIRES config evse api ntc_driver spi_bus_manager) diff --git a/components/peripherals/src/ac_relay.c b/components/peripherals/src/ac_relay.c index f087fbd..6be96a8 100755 --- a/components/peripherals/src/ac_relay.c +++ b/components/peripherals/src/ac_relay.c @@ -6,6 +6,9 @@ static const char* TAG = "ac_relay"; +// Memoization do estado atual do relé (salvo em RAM) +static int last_state = -1; + /** * @brief Initialize the AC relay GPIO. * @@ -16,7 +19,7 @@ void ac_relay_init(void) gpio_config_t conf = { .pin_bit_mask = BIT64(board_config.ac_relay_gpio), .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, ///< Disabled unless required + .pull_down_en = GPIO_PULLDOWN_DISABLE, .pull_up_en = GPIO_PULLUP_DISABLE, .intr_type = GPIO_INTR_DISABLE }; @@ -27,7 +30,8 @@ void ac_relay_init(void) return; } - gpio_set_level(board_config.ac_relay_gpio, false); ///< Ensure relay starts OFF + gpio_set_level(board_config.ac_relay_gpio, 0); ///< Ensure relay starts OFF + last_state = 0; ESP_LOGI(TAG, "AC relay initialized. Pin: %d", board_config.ac_relay_gpio); } @@ -38,9 +42,15 @@ void ac_relay_init(void) */ void ac_relay_set_state(bool state) { + if (state == last_state) { + // Estado não mudou; evita log e escrita desnecessária. + return; + } + last_state = state; + ESP_LOGI(TAG, "Setting AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, state); - - esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state); + + esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state ? 1 : 0); if (ret != ESP_OK) { ESP_LOGE(TAG, "Failed to set GPIO level (error: %s)", esp_err_to_name(ret)); } @@ -55,5 +65,5 @@ bool ac_relay_get_state(void) { int level = gpio_get_level(board_config.ac_relay_gpio); ESP_LOGD(TAG, "Current AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, level); - return level; + return (level != 0); } diff --git a/components/peripherals/src/adc121s021_dma.c b/components/peripherals/src/adc121s021_dma.c index 1ae3574..419a0a8 100755 --- a/components/peripherals/src/adc121s021_dma.c +++ b/components/peripherals/src/adc121s021_dma.c @@ -1,53 +1,64 @@ #include "driver/spi_master.h" -#include "driver/gpio.h" #include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" #include "adc121s021_dma.h" +#include "spi_bus_manager.h" #define TAG "adc_dma" -#define PIN_NUM_MOSI 23 -#define PIN_NUM_MISO 19 -#define PIN_NUM_CLK 18 -#define PIN_NUM_CS 5 +#define PIN_NUM_CS 5 +#define SAMPLE_SIZE_BYTES 2 +#define ADC_BITS 12 +#define SPI_CLOCK_HZ (6 * 1000 * 1000) // 6 MHz -#define SPI_HOST_USED SPI2_HOST -#define SAMPLE_SIZE_BYTES 2 -#define ADC_BITS 12 - -static spi_device_handle_t adc_spi; +static spi_device_handle_t adc_spi = NULL; void adc121s021_dma_init(void) { - spi_bus_config_t buscfg = { - .mosi_io_num = PIN_NUM_MOSI, - .miso_io_num = PIN_NUM_MISO, - .sclk_io_num = PIN_NUM_CLK, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = SAMPLE_SIZE_BYTES, - }; + if (adc_spi) { + ESP_LOGW(TAG, "ADC121S021 já foi inicializado."); + return; + } + + if (!spi_bus_manager_is_initialized()) { + ESP_LOGI(TAG, "SPI bus não inicializado. Inicializando..."); + esp_err_t err = spi_bus_manager_init(); // 🔧 CORRIGIDO: sem argumentos + if (err != ESP_OK) { + ESP_LOGE(TAG, "Falha ao inicializar o SPI bus: %s", esp_err_to_name(err)); + return; + } + } spi_device_interface_config_t devcfg = { - .clock_speed_hz = 6000000, // 6 MHz + .clock_speed_hz = SPI_CLOCK_HZ, .mode = 0, .spics_io_num = PIN_NUM_CS, .queue_size = 2, .flags = SPI_DEVICE_NO_DUMMY, + .pre_cb = NULL, + .post_cb = NULL, }; - ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST_USED, &buscfg, SPI_DMA_CH_AUTO)); - ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST_USED, &devcfg, &adc_spi)); + esp_err_t err = spi_bus_add_device(spi_bus_manager_get_host(), &devcfg, &adc_spi); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Falha ao registrar ADC121S021 no SPI: %s", esp_err_to_name(err)); + return; + } + + ESP_LOGI(TAG, "ADC121S021 registrado no SPI com sucesso."); } bool adc121s021_dma_get_sample(uint16_t *sample) { - uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy TX + if (!adc_spi) { + ESP_LOGE(TAG, "ADC SPI não inicializado!"); + return false; + } + + uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy uint8_t rx_buffer[2] = {0}; spi_transaction_t t = { - .length = 16, // 16 bits + .length = 16, .tx_buffer = tx_buffer, .rx_buffer = rx_buffer, .flags = 0 @@ -55,12 +66,10 @@ bool adc121s021_dma_get_sample(uint16_t *sample) esp_err_t err = spi_device_transmit(adc_spi, &t); if (err != ESP_OK) { - ESP_LOGE(TAG, "SPI transmit error: %s", esp_err_to_name(err)); + ESP_LOGE(TAG, "Erro na transmissão SPI: %s", esp_err_to_name(err)); return false; } - // Extrai os 12 bits significativos da resposta do ADC121S021 *sample = ((rx_buffer[0] << 8) | rx_buffer[1]) & 0x0FFF; - return true; } diff --git a/components/peripherals/src/buzzer.c b/components/peripherals/src/buzzer.c index f9b7378..e7b5923 100755 --- a/components/peripherals/src/buzzer.c +++ b/components/peripherals/src/buzzer.c @@ -91,7 +91,7 @@ static void buzzer_worker_task(void *arg) { while (true) { if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) { - //buzzer_execute_pattern(pattern_id); + buzzer_execute_pattern(pattern_id); } } } diff --git a/components/protocols/CMakeLists.txt b/components/protocols/CMakeLists.txt index fb933e7..2ce1472 100755 --- a/components/protocols/CMakeLists.txt +++ b/components/protocols/CMakeLists.txt @@ -1,6 +1,5 @@ set(srcs "src/protocols.c" - "src/rest.c" "src/mqtt.c" "src/date_time.c" ) diff --git a/components/protocols/include/rest.h b/components/protocols/include/rest.h deleted file mode 100755 index 2cd390d..0000000 --- a/components/protocols/include/rest.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef REST_H_ -#define REST_H_ - -#include "esp_err.h" - -/** - * @brief Initialize REST server - * - * @param base_path Path on the SPIFFS filesystem where static files reside - * @return ESP_OK on success, ESP_FAIL otherwise - */ -esp_err_t rest_init(const char *base_path); - -#endif /* REST_H_ */ diff --git a/components/protocols/src/mqtt.c b/components/protocols/src/mqtt.c index 67940cd..7f80c38 100755 --- a/components/protocols/src/mqtt.c +++ b/components/protocols/src/mqtt.c @@ -31,31 +31,31 @@ static esp_err_t open_mqtt_nvs(nvs_handle_t *handle) { static void subcribe_topics(void) { - ESP_LOGI(TAG, "subcribe_topics"); + ESP_LOGI(TAG, "[MQTT] Subscribing to topics"); + + char base[32]; + mqtt_get_base_topic(base); char topic[64]; - mqtt_get_base_topic(topic); - strcat(topic, "/request/#"); + snprintf(topic, sizeof(topic), "%s/request/#", base); esp_mqtt_client_subscribe(client, topic, 0); - ESP_LOGI(TAG, "subscribed: %s", topic); + ESP_LOGI(TAG, " subscribed: %s", topic); - mqtt_get_base_topic(topic); - strcat(topic, "/set/config/#"); + snprintf(topic, sizeof(topic), "%s/set/config/#", base); esp_mqtt_client_subscribe(client, topic, 0); - ESP_LOGI(TAG, "subscribed: %s", topic); + ESP_LOGI(TAG, " subscribed: %s", topic); - mqtt_get_base_topic(topic); - strcat(topic, "/enable"); + snprintf(topic, sizeof(topic), "%s/enable", base); esp_mqtt_client_subscribe(client, topic, 0); - ESP_LOGI(TAG, "subscribed: %s", topic); + ESP_LOGI(TAG, " subscribed: %s", topic); } static void publish_message(const char* topic, cJSON* root) { char target_topic[64]; mqtt_get_base_topic(target_topic); - strcat(target_topic, topic); + strncat(target_topic, topic, sizeof(target_topic) - strlen(target_topic) - 1); const char* json = cJSON_PrintUnformatted(root); esp_mqtt_client_publish(client, target_topic, json, 0, 1, 0); @@ -67,34 +67,51 @@ static void handle_message(const char* topic, const char* data) char base_topic[32]; mqtt_get_base_topic(base_topic); + ESP_LOGI(TAG, "[MQTT] Message received"); + ESP_LOGI(TAG, " > Topic: %s", topic); + ESP_LOGI(TAG, " > Payload: %s", data); + if (strncmp(topic, base_topic, strlen(base_topic)) == 0) { const char* sub_topic = &topic[strlen(base_topic)]; + ESP_LOGI(TAG, " > Subtopic detected: %s", sub_topic); if (strcmp(sub_topic, "/request/config/evse") == 0) { + ESP_LOGI(TAG, " → Responding with EVSE configuration"); cJSON* root = json_get_evse_config(); publish_message("/response/config/evse", root); cJSON_Delete(root); + } else { + ESP_LOGW(TAG, " ! Unknown command: %s", sub_topic); } - // [Outros comandos omitidos para brevidade...] + } else { + ESP_LOGW(TAG, " ! Topic does not match base: %s", topic); } } static void event_handler(void* handler_args, esp_event_base_t base, int32_t event_id, void* event_data) { esp_mqtt_event_handle_t event = event_data; - char topic[48], data[256]; switch (event_id) { case MQTT_EVENT_CONNECTED: - ESP_LOGI(TAG, "MQTT conectado"); + ESP_LOGI(TAG, "[MQTT] Connected to broker"); if (client_task) vTaskResume(client_task); subcribe_topics(); break; - case MQTT_EVENT_DATA: - strncpy(topic, event->topic, MIN(event->topic_len, sizeof(topic)-1)); - strncpy(data, event->data, MIN(event->data_len, sizeof(data)-1)); + + case MQTT_EVENT_DATA: { + char topic[64] = {0}; + char data[256] = {0}; + + int tlen = MIN(event->topic_len, sizeof(topic) - 1); + int dlen = MIN(event->data_len, sizeof(data) - 1); + + memcpy(topic, event->topic, tlen); + memcpy(data, event->data, dlen); + handle_message(topic, data); break; + } default: break; } @@ -257,4 +274,4 @@ void mqtt_get_password(char* value) uint16_t mqtt_get_periodicity(void) { return periodicity; -} +} \ No newline at end of file diff --git a/components/protocols/src/rest.c b/components/protocols/src/rest.c deleted file mode 100755 index 2cb750c..0000000 --- a/components/protocols/src/rest.c +++ /dev/null @@ -1,907 +0,0 @@ -/* HTTP Restful API Server - - This example code is in the Public Domain (or CC0 licensed, at your option.) - - Unless required by applicable law or agreed to in writing, this - software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR - CONDITIONS OF ANY KIND, either express or implied. -*/ -#include -#include -#include "esp_http_server.h" -#include "esp_chip_info.h" -#include "esp_random.h" -#include "esp_log.h" -#include "esp_vfs.h" -#include "cJSON.h" -#include "rest.h" -#include "evse_api.h" -#include "cJSON.h" -#include "esp_log.h" -#include "esp_http_server.h" -#include "auth.h" - -static const char *REST_TAG = "esp-rest"; -#define REST_CHECK(a, str, goto_tag, ...) \ - do \ - { \ - if (!(a)) \ - { \ - ESP_LOGE(REST_TAG, "%s(%d): " str, __FUNCTION__, __LINE__, ##__VA_ARGS__); \ - goto goto_tag; \ - } \ - } while (0) - -#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) -#define SCRATCH_BUFSIZE (10240) - -typedef struct rest_server_context { - char base_path[ESP_VFS_PATH_MAX + 1]; - char scratch[SCRATCH_BUFSIZE]; -} rest_server_context_t; - -#define CHECK_FILE_EXTENSION(filename, ext) (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) - -// Estruturas para armazenar as configurações -static struct { - bool enabled; - char ssid[128]; - char password[128]; -} wifi_config = {false, "", ""}; - -static struct { - bool enabled; - char host[256]; - int port; - char username[128]; - char password[128]; - char topic[128]; -} mqtt_config = {false, "", 1883, "", "", ""}; - -// Estrutura para armazenar as configurações OCPP em memória -static struct { - char url[256]; - char chargeBoxId[128]; - char certificate[256]; - char privateKey[256]; -} ocpp_config = {"", "", "", ""}; - -// Estrutura para armazenar as configurações de energia -static struct { - int currentLimit; - int powerLimit; - int energyLimit; - int chargingTimeLimit; - int temperatureLimit; -} settings_config = {0, 0, 0, 0, 0}; - -static struct { - char username[128]; -} users[10] = {{"admin"}, {"user1"}}; -static int num_users = 2; // Contador de usuários cadastrados - - -// Set HTTP response content type according to file extension -static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) -{ - const char *type = "text/plain"; - if (CHECK_FILE_EXTENSION(filepath, ".html")) { - type = "text/html"; - } else if (CHECK_FILE_EXTENSION(filepath, ".js")) { - type = "application/javascript"; - } else if (CHECK_FILE_EXTENSION(filepath, ".css")) { - type = "text/css"; - } else if (CHECK_FILE_EXTENSION(filepath, ".png")) { - type = "image/png"; - } else if (CHECK_FILE_EXTENSION(filepath, ".ico")) { - type = "image/x-icon"; - } else if (CHECK_FILE_EXTENSION(filepath, ".svg")) { - type = "text/xml"; - } - return httpd_resp_set_type(req, type); -} - -/* Send HTTP response with the contents of the requested file */ -static esp_err_t rest_common_get_handler(httpd_req_t *req) -{ - char filepath[FILE_PATH_MAX]; - - rest_server_context_t *rest_context = (rest_server_context_t *)req->user_ctx; - strlcpy(filepath, rest_context->base_path, sizeof(filepath)); - if (req->uri[strlen(req->uri) - 1] == '/') { - strlcat(filepath, "/index.html", sizeof(filepath)); - } else { - strlcat(filepath, req->uri, sizeof(filepath)); - } - int fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - ESP_LOGW(REST_TAG, "Failed to open file : %s, redirecting to index", filepath); - /* Try to serve index.html for SPA routing */ - strlcpy(filepath, rest_context->base_path, sizeof(filepath)); - strlcat(filepath, "/index.html", sizeof(filepath)); - fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - ESP_LOGE(REST_TAG, "Failed to open index file : %s", filepath); - httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "File not found"); - return ESP_FAIL; - } - } - - set_content_type_from_file(req, filepath); - - char *chunk = rest_context->scratch; - ssize_t read_bytes; - do { - /* Read file in chunks into the scratch buffer */ - read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); - if (read_bytes == -1) { - ESP_LOGE(REST_TAG, "Failed to read file : %s", filepath); - } else if (read_bytes > 0) { - /* Send the buffer contents as HTTP response chunk */ - if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { - close(fd); - ESP_LOGE(REST_TAG, "File sending failed!"); - /* Abort sending file */ - httpd_resp_sendstr_chunk(req, NULL); - /* Respond with 500 Internal Server Error */ - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to send file"); - return ESP_FAIL; - } - } - } while (read_bytes > 0); - /* Close file after sending complete */ - close(fd); - ESP_LOGI(REST_TAG, "File sending complete"); - /* Respond with an empty chunk to signal HTTP response completion */ - httpd_resp_send_chunk(req, NULL, 0); - return ESP_OK; -} - -// Manipulador para o endpoint GET /api/v1/config/electrical -static esp_err_t electrical_config_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com as configurações de rede elétrica - cJSON *config = cJSON_CreateObject(); - cJSON *monitor = cJSON_CreateObject(); - cJSON_AddStringToObject(monitor, "voltage", "230"); - cJSON_AddStringToObject(monitor, "current", "10"); - cJSON_AddStringToObject(monitor, "quality", "1"); - cJSON_AddItemToObject(config, "monitor", monitor); - cJSON_AddBoolToObject(config, "alerts", true); - - // Adicionar mais configurações (security, loadBalancing, solar) no objeto config - // ... - - // Enviar a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberar memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - - -// Manipulador para o endpoint POST /api/v1/config/electrical -static esp_err_t electrical_config_post_handler(httpd_req_t *req) -{ - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Request body is empty"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Parse JSON recebido - cJSON *json = cJSON_Parse(buf); - if (json == NULL) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Processar os dados recebidos e atualizar as configurações (monitor, alerts, etc.) - // Exemplo de configuração: - cJSON *monitor = cJSON_GetObjectItem(json, "monitor"); - if (monitor) { - // Atualizar configurações do monitor - // ... - } - - // Atualizar outras configurações... - - // Responder com sucesso - httpd_resp_sendstr(req, "Configuração gravada com sucesso!"); - - cJSON_Delete(json); - - return ESP_OK; -} - - -// Manipulador para o endpoint /api/v1/config/load-balancing -static esp_err_t config_load_balancing_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON de configuração - cJSON *config = cJSON_CreateObject(); - - // Configuração de load balancing - cJSON_AddBoolToObject(config, "enabled", true); // Exemplo: load balancing ativado - cJSON_AddNumberToObject(config, "maxChargingCurrent", 32); // Exemplo: corrente máxima de 32A - - // Lista de dispositivos disponíveis - cJSON *devices = cJSON_CreateArray(); - cJSON_AddItemToArray(devices, cJSON_CreateString("Device 1")); // Exemplo de dispositivo - cJSON_AddItemToArray(devices, cJSON_CreateString("Device 2")); // Outro exemplo de dispositivo - cJSON_AddItemToObject(config, "devices", devices); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} -// Manipulador para o endpoint GET /api/v1/ocpp (Status do OCPP) -static esp_err_t ocpp_status_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON de status - cJSON *status = cJSON_CreateObject(); - cJSON_AddStringToObject(status, "status", "connected"); // Status de exemplo, você pode adaptar conforme sua lógica - - // Convertendo para string e enviando a resposta - const char *status_str = cJSON_Print(status); - httpd_resp_sendstr(req, status_str); - - // Liberando a memória - free((void *)status_str); - cJSON_Delete(status); - - return ESP_OK; -} - -// Manipulador para o endpoint GET /api/v1/config/ocpp (Configuração OCPP) -static esp_err_t config_ocpp_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com as configurações do OCPP - cJSON *config = cJSON_CreateObject(); - cJSON_AddStringToObject(config, "url", ocpp_config.url); - cJSON_AddStringToObject(config, "chargeBoxId", ocpp_config.chargeBoxId); - cJSON_AddStringToObject(config, "certificate", ocpp_config.certificate); - cJSON_AddStringToObject(config, "privateKey", ocpp_config.privateKey); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - -// Manipulador para o endpoint POST /api/v1/config/ocpp (Salvar configuração OCPP) -static esp_err_t config_ocpp_post_handler(httpd_req_t *req) -{ - char buf[512]; // Buffer para armazenar a requisição - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Parse JSON recebido - cJSON *json = cJSON_Parse(buf); - if (json == NULL) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizando as configurações OCPP - cJSON *url = cJSON_GetObjectItem(json, "url"); - if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url)); - - cJSON *chargeBoxId = cJSON_GetObjectItem(json, "chargeBoxId"); - if (chargeBoxId) strlcpy(ocpp_config.chargeBoxId, chargeBoxId->valuestring, sizeof(ocpp_config.chargeBoxId)); - - cJSON *certificate = cJSON_GetObjectItem(json, "certificate"); - if (certificate) strlcpy(ocpp_config.certificate, certificate->valuestring, sizeof(ocpp_config.certificate)); - - cJSON *privateKey = cJSON_GetObjectItem(json, "privateKey"); - if (privateKey) strlcpy(ocpp_config.privateKey, privateKey->valuestring, sizeof(ocpp_config.privateKey)); - - cJSON_Delete(json); - - // Responder com uma mensagem de sucesso - httpd_resp_sendstr(req, "Configuração OCPP atualizada com sucesso"); - - return ESP_OK; -} - -// Manipulador para o endpoint POST /api/v1/config/settings -static esp_err_t config_settings_post_handler(httpd_req_t *req) -{ - char buf[512]; // Buffer para armazenar a requisição - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Parse JSON recebido - cJSON *json = cJSON_Parse(buf); - if (json == NULL) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizando as configurações - cJSON *currentLimit = cJSON_GetObjectItem(json, "currentLimit"); - if (currentLimit) - evse_set_max_charging_current(currentLimit->valueint); - //settings_config.currentLimit = currentLimit->valueint; - - cJSON *powerLimit = cJSON_GetObjectItem(json, "powerLimit"); - if (powerLimit) settings_config.powerLimit = powerLimit->valueint; - - cJSON *energyLimit = cJSON_GetObjectItem(json, "energyLimit"); - if (energyLimit) settings_config.energyLimit = energyLimit->valueint; - - cJSON *chargingTimeLimit = cJSON_GetObjectItem(json, "chargingTimeLimit"); - if (chargingTimeLimit) settings_config.chargingTimeLimit = chargingTimeLimit->valueint; - - cJSON *temperatureLimit = cJSON_GetObjectItem(json, "temperatureLimit"); - if (temperatureLimit) - evse_set_temp_threshold(temperatureLimit->valueint); - - //settings_config.temperatureLimit = temperatureLimit->valueint; - - cJSON_Delete(json); - - // Responder com uma mensagem de sucesso - httpd_resp_sendstr(req, "Configurações de energia atualizadas com sucesso"); - - return ESP_OK; -} - - -// Manipulador para o endpoint GET /api/v1/config/settings -static esp_err_t config_settings_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON para enviar as configurações atuais - cJSON *config = cJSON_CreateObject(); - - - //cJSON_AddNumberToObject(config, "maxCurrentLimit", evse_get_max_charging_current()); - cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current()); - cJSON_AddNumberToObject(config, "powerLimit", settings_config.powerLimit); - cJSON_AddNumberToObject(config, "energyLimit", settings_config.energyLimit); - cJSON_AddNumberToObject(config, "chargingTimeLimit", settings_config.chargingTimeLimit); - cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold()); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - - -// Manipulador para o endpoint GET /api/v1/dashboard -static esp_err_t dashboard_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com os dados do Dashboard - cJSON *dashboard = cJSON_CreateObject(); - - // Status do sistema - cJSON_AddStringToObject(dashboard, "status", "Ativo"); - - // Carregadores (exemplo) - cJSON *chargers = cJSON_CreateArray(); - cJSON *charger1 = cJSON_CreateObject(); - cJSON_AddNumberToObject(charger1, "id", 1); - cJSON_AddStringToObject(charger1, "status", "Ativo"); - cJSON_AddNumberToObject(charger1, "current", 12); - cJSON_AddNumberToObject(charger1, "power", 2200); - cJSON_AddItemToArray(chargers, charger1); - - cJSON_AddItemToObject(dashboard, "chargers", chargers); - - // Consumo de energia - cJSON_AddNumberToObject(dashboard, "energyConsumed", 50.3); - - // Tempo de carregamento - cJSON_AddNumberToObject(dashboard, "chargingTime", 120); - - // Alertas - cJSON *alerts = cJSON_CreateArray(); - cJSON_AddItemToArray(alerts, cJSON_CreateString("Aviso: Carregador 1 está com erro.")); - cJSON_AddItemToObject(dashboard, "alerts", alerts); - - // Convertendo para string e enviando a resposta - const char *dashboard_str = cJSON_Print(dashboard); - httpd_resp_sendstr(req, dashboard_str); - - // Liberando a memória - free((void *)dashboard_str); - cJSON_Delete(dashboard); - - return ESP_OK; -} - - -static esp_err_t config_wifi_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com as configurações de Wi-Fi - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "enabled", wifi_config.enabled); - cJSON_AddStringToObject(config, "ssid", wifi_config.ssid); - cJSON_AddStringToObject(config, "password", wifi_config.password); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - -/* Manipulador para o endpoint POST /api/v1/config/wifi */ -static esp_err_t config_wifi_post_handler(httpd_req_t *req) -{ - char buf[512]; // Buffer para armazenar a requisição - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Parse JSON recebido - cJSON *json = cJSON_Parse(buf); - if (json == NULL) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizando as configurações Wi-Fi - cJSON *enabled = cJSON_GetObjectItem(json, "enabled"); - if (enabled) wifi_config.enabled = enabled->valueint; - - cJSON *ssid = cJSON_GetObjectItem(json, "ssid"); - if (ssid) strlcpy(wifi_config.ssid, ssid->valuestring, sizeof(wifi_config.ssid)); - - cJSON *password = cJSON_GetObjectItem(json, "password"); - if (password) strlcpy(wifi_config.password, password->valuestring, sizeof(wifi_config.password)); - - cJSON_Delete(json); - - // Responder com uma mensagem de sucesso - httpd_resp_sendstr(req, "Configuração Wi-Fi atualizada com sucesso"); - - return ESP_OK; -} - -/* Manipulador para o endpoint GET /api/v1/config/mqtt */ -static esp_err_t config_mqtt_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com as configurações de MQTT - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "enabled", mqtt_config.enabled); - cJSON_AddStringToObject(config, "host", mqtt_config.host); - cJSON_AddNumberToObject(config, "port", mqtt_config.port); - cJSON_AddStringToObject(config, "username", mqtt_config.username); - cJSON_AddStringToObject(config, "password", mqtt_config.password); - cJSON_AddStringToObject(config, "topic", mqtt_config.topic); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - -/* Manipulador para o endpoint POST /api/v1/config/mqtt */ -static esp_err_t config_mqtt_post_handler(httpd_req_t *req) -{ - char buf[512]; // Buffer para armazenar a requisição - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Parse JSON recebido - cJSON *json = cJSON_Parse(buf); - if (json == NULL) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizando as configurações MQTT - cJSON *enabled = cJSON_GetObjectItem(json, "enabled"); - if (enabled) mqtt_config.enabled = enabled->valueint; - - cJSON *host = cJSON_GetObjectItem(json, "host"); - if (host) strlcpy(mqtt_config.host, host->valuestring, sizeof(mqtt_config.host)); - - cJSON *port = cJSON_GetObjectItem(json, "port"); - if (port) mqtt_config.port = port->valueint; - - cJSON *username = cJSON_GetObjectItem(json, "username"); - if (username) strlcpy(mqtt_config.username, username->valuestring, sizeof(mqtt_config.username)); - - cJSON *password = cJSON_GetObjectItem(json, "password"); - if (password) strlcpy(mqtt_config.password, password->valuestring, sizeof(mqtt_config.password)); - - cJSON *topic = cJSON_GetObjectItem(json, "topic"); - if (topic) strlcpy(mqtt_config.topic, topic->valuestring, sizeof(mqtt_config.topic)); - - cJSON_Delete(json); - - // Responder com uma mensagem de sucesso - httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso"); - - return ESP_OK; -} - -// GET /api/v1/config/auth-methods -static esp_err_t config_auth_methods_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "RFID", auth_is_enabled()); - cJSON_AddBoolToObject(config, "App", false); - cJSON_AddBoolToObject(config, "Password", false); - - char *config_str = cJSON_PrintUnformatted(config); - httpd_resp_sendstr(req, config_str); - - free(config_str); - cJSON_Delete(config); - - return ESP_OK; -} - -// POST /api/v1/config/auth-methods -static esp_err_t config_auth_methods_post_handler(httpd_req_t *req) -{ - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; - - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - cJSON *RFID = cJSON_GetObjectItem(json, "RFID"); - if (cJSON_IsBool(RFID)) { - auth_set_enabled(cJSON_IsTrue(RFID)); - } - - /* - - cJSON *App = cJSON_GetObjectItem(json, "App"); - if (cJSON_IsBool(App)) { - auth_methods.App = cJSON_IsTrue(App); - } - - cJSON *Password = cJSON_GetObjectItem(json, "Password"); - if (cJSON_IsBool(Password)) { - auth_methods.Password = cJSON_IsTrue(Password); - }*/ - - cJSON_Delete(json); - - httpd_resp_sendstr(req, "Configurações de autenticação atualizadas com sucesso"); - return ESP_OK; -} - - - -// Manipulador para o endpoint GET /api/v1/config/users -static esp_err_t config_users_get_handler(httpd_req_t *req) -{ - httpd_resp_set_type(req, "application/json"); - - // Criar objeto JSON com a lista de usuários - cJSON *config = cJSON_CreateObject(); - cJSON *users_list = cJSON_CreateArray(); - - for (int i = 0; i < num_users; i++) { - cJSON *user = cJSON_CreateObject(); - cJSON_AddStringToObject(user, "username", users[i].username); - cJSON_AddItemToArray(users_list, user); - } - - cJSON_AddItemToObject(config, "users", users_list); - - // Convertendo para string e enviando a resposta - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - // Liberando a memória - free((void *)config_str); - cJSON_Delete(config); - - return ESP_OK; -} - - -// Manipulador para o endpoint POST /api/v1/config/users -static esp_err_t config_users_post_handler(httpd_req_t *req) -{ - char buf[128]; // Buffer para armazenar o nome do novo usuário - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; // Garantir que a string esteja terminada - - // Adicionar o novo usuário - if (num_users < 10) { - strlcpy(users[num_users].username, buf, sizeof(users[num_users].username)); - num_users++; - httpd_resp_sendstr(req, "Usuário adicionado com sucesso"); - } else { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Máximo de usuários atingido"); - } - - return ESP_OK; -} - - -// Manipulador para o endpoint DELETE /api/v1/config/users/{username} -static esp_err_t config_users_delete_handler(httpd_req_t *req) -{ - char username[128]; // Nome do usuário a ser removido - if (httpd_req_get_url_query_str(req, username, sizeof(username)) == ESP_OK) { - // Verificar e remover o usuário - for (int i = 0; i < num_users; i++) { - if (strcmp(users[i].username, username) == 0) { - // Remover o usuário - for (int j = i; j < num_users - 1; j++) { - users[j] = users[j + 1]; - } - num_users--; - httpd_resp_sendstr(req, "Usuário removido com sucesso"); - return ESP_OK; - } - } - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado"); - } - return ESP_FAIL; -} - - - -esp_err_t rest_init(const char *base_path) -{ - - /* - REST_CHECK(base_path, "wrong base path", err); - rest_server_context_t *rest_context = calloc(1, sizeof(rest_server_context_t)); - REST_CHECK(rest_context, "No memory for rest context", err_start); - strlcpy(rest_context->base_path, base_path, sizeof(rest_context->base_path)); - - httpd_handle_t server = NULL; - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.max_uri_handlers = 30; // Ajuste conforme necessário - config.uri_match_fn = httpd_uri_match_wildcard; - - ESP_LOGI(REST_TAG, "Starting HTTP Server"); - REST_CHECK(httpd_start(&server, &config) == ESP_OK, "Start server failed", err_start); - - // Registrar manipuladores de URI para as configurações - httpd_uri_t config_load_balancing_get_uri = { - .uri = "/api/v1/config/load-balancing", - .method = HTTP_GET, - .handler = config_load_balancing_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_load_balancing_get_uri); - - // URI handler for fetching OCPP status - httpd_uri_t ocpp_status_get_uri = { - .uri = "/api/v1/ocpp", - .method = HTTP_GET, - .handler = ocpp_status_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &ocpp_status_get_uri); - - // URI handler for fetching OCPP config - httpd_uri_t config_ocpp_get_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_GET, - .handler = config_ocpp_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_ocpp_get_uri); - - // URI handler for posting OCPP config - httpd_uri_t config_ocpp_post_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_POST, - .handler = config_ocpp_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_ocpp_post_uri); - - // Manipulador para o endpoint POST /api/v1/config/settings - httpd_uri_t config_settings_post_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_POST, - .handler = config_settings_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_settings_post_uri); - - // Manipulador para o endpoint GET /api/v1/config/settings - httpd_uri_t config_settings_get_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_GET, - .handler = config_settings_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_settings_get_uri); - - // Manipulador para o endpoint GET /api/v1/dashboard - httpd_uri_t dashboard_get_uri = { - .uri = "/api/v1/dashboard", - .method = HTTP_GET, - .handler = dashboard_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &dashboard_get_uri); - - // Register URI Handlers for electrical configuration - httpd_uri_t electrical_config_get_uri = { - .uri = "/api/v1/config/electrical", - .method = HTTP_GET, - .handler = electrical_config_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &electrical_config_get_uri); - - httpd_uri_t electrical_config_post_uri = { - .uri = "/api/v1/config/electrical", - .method = HTTP_POST, - .handler = electrical_config_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &electrical_config_post_uri); - - // URI handler for getting Wi-Fi config - httpd_uri_t config_wifi_get_uri = { - .uri = "/api/v1/config/wifi", - .method = HTTP_GET, - .handler = config_wifi_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_wifi_get_uri); - - // URI handler for posting Wi-Fi config - httpd_uri_t config_wifi_post_uri = { - .uri = "/api/v1/config/wifi", - .method = HTTP_POST, - .handler = config_wifi_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_wifi_post_uri); - - // URI handler for getting MQTT config - httpd_uri_t config_mqtt_get_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_GET, - .handler = config_mqtt_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_mqtt_get_uri); - - // URI handler for posting MQTT config - httpd_uri_t config_mqtt_post_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_POST, - .handler = config_mqtt_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_mqtt_post_uri); - - // Registrar manipuladores para as configurações de autenticação - httpd_uri_t config_auth_methods_get_uri = { - .uri = "/api/v1/config/auth-methods", - .method = HTTP_GET, - .handler = config_auth_methods_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_auth_methods_get_uri); - - httpd_uri_t config_auth_methods_post_uri = { - .uri = "/api/v1/config/auth-methods", - .method = HTTP_POST, - .handler = config_auth_methods_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_auth_methods_post_uri); - - // Registrar manipuladores para as configurações de usuários - httpd_uri_t config_users_get_uri = { - .uri = "/api/v1/config/users", - .method = HTTP_GET, - .handler = config_users_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_users_get_uri); - - httpd_uri_t config_users_post_uri = { - .uri = "/api/v1/config/users", - .method = HTTP_POST, - .handler = config_users_post_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_users_post_uri); - - httpd_uri_t config_users_delete_uri = { - .uri = "/api/v1/config/users", - .method = HTTP_DELETE, - .handler = config_users_delete_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &config_users_delete_uri); - - // URI handler for getting web server files - httpd_uri_t common_get_uri = { - .uri = "/", - .method = HTTP_GET, - .handler = rest_common_get_handler, - .user_ctx = rest_context - }; - httpd_register_uri_handler(server, &common_get_uri); - */ - return ESP_OK; -} diff --git a/components/rest_api/src/dashboard_api.c b/components/rest_api/src/dashboard_api.c index c078e90..47aaed9 100644 --- a/components/rest_api/src/dashboard_api.c +++ b/components/rest_api/src/dashboard_api.c @@ -3,6 +3,9 @@ #include "cJSON.h" #include "evse_api.h" #include "evse_error.h" +#include "evse_config.h" +#include "evse_limits.h" + static const char *TAG = "dashboard_api"; @@ -40,10 +43,10 @@ static esp_err_t dashboard_get_handler(httpd_req_t *req) { if (evse_is_limit_reached()) { cJSON_AddItemToArray(alerts, cJSON_CreateString("Limite de consumo atingido.")); } - if (!evse_is_available()) { + if (!evse_config_is_available()) { cJSON_AddItemToArray(alerts, cJSON_CreateString("Estação indisponível.")); } - if (!evse_is_enabled()) { + if (!evse_config_is_enabled()) { cJSON_AddItemToArray(alerts, cJSON_CreateString("EVSE desativado.")); } cJSON_AddItemToObject(dashboard, "alerts", alerts); diff --git a/components/rest_api/src/evse_settings_api.c b/components/rest_api/src/evse_settings_api.c index 2103d5c..3608fd1 100755 --- a/components/rest_api/src/evse_settings_api.c +++ b/components/rest_api/src/evse_settings_api.c @@ -3,6 +3,7 @@ // ========================= #include "evse_settings_api.h" #include "evse_api.h" +#include "evse_config.h" #include "esp_log.h" #include "cJSON.h" diff --git a/components/rest_api/src/meters_settings_api.c b/components/rest_api/src/meters_settings_api.c index 493b39d..b40cc90 100644 --- a/components/rest_api/src/meters_settings_api.c +++ b/components/rest_api/src/meters_settings_api.c @@ -87,7 +87,7 @@ static esp_err_t meters_config_post_handler(httpd_req_t *req) { // Registrando os manipuladores de URI para os contadores void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { - ESP_LOGI(TAG, "Registering URI handlers for meters settings"); + ESP_LOGD(TAG, "Registering URI handlers for meters settings"); // URI para o método GET httpd_uri_t meters_get_uri = { @@ -96,7 +96,7 @@ void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { .handler = meters_config_get_handler, .user_ctx = ctx }; - ESP_LOGI(TAG, "Registering GET handler for /api/v1/config/meters"); + ESP_LOGD(TAG, "Registering GET handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_get_uri); // URI para o método POST @@ -106,6 +106,6 @@ void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { .handler = meters_config_post_handler, .user_ctx = ctx }; - ESP_LOGI(TAG, "Registering POST handler for /api/v1/config/meters"); + ESP_LOGD(TAG, "Registering POST handler for /api/v1/config/meters"); httpd_register_uri_handler(server, &meters_post_uri); } diff --git a/components/serial_sync/src/sync_master.c b/components/serial_sync/src/sync_master.c index e744b35..5b75175 100755 --- a/components/serial_sync/src/sync_master.c +++ b/components/serial_sync/src/sync_master.c @@ -19,6 +19,9 @@ // #include "inc/version_autogen.h" #include "sync_master.h" #include "evse_api.h" +#include "evse_error.h" +#include "evse_state.h" +#include "evse_config.h" #define VERSION_STRING "2.2" diff --git a/components/serial_sync/src/sync_slave.c b/components/serial_sync/src/sync_slave.c index 6aaf441..386f0dc 100755 --- a/components/serial_sync/src/sync_slave.c +++ b/components/serial_sync/src/sync_slave.c @@ -20,6 +20,9 @@ #include "sync_slave.h" #include "loadbalancer.h" #include "evse_api.h" +#include "evse_error.h" +#include "evse_state.h" +#include "evse_config.h" #define VERSION_STRING "2.2" diff --git a/components/spi_bus_manager/CMakeLists.txt b/components/spi_bus_manager/CMakeLists.txt new file mode 100755 index 0000000..d69d63f --- /dev/null +++ b/components/spi_bus_manager/CMakeLists.txt @@ -0,0 +1,10 @@ +set(srcs + "src/spi_bus_manager.c" +) + +idf_component_register( + SRCS ${srcs} + INCLUDE_DIRS include + PRIV_REQUIRES driver esp_timer + REQUIRES config +) diff --git a/components/spi_bus_manager/include/spi_bus_manager.h b/components/spi_bus_manager/include/spi_bus_manager.h new file mode 100755 index 0000000..3723703 --- /dev/null +++ b/components/spi_bus_manager/include/spi_bus_manager.h @@ -0,0 +1,20 @@ +#ifndef SPI_BUS_MANAGER_H_ +#define SPI_BUS_MANAGER_H_ + +#include "driver/spi_master.h" +#include "esp_err.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +esp_err_t spi_bus_manager_init(void); +spi_host_device_t spi_bus_manager_get_host(void); +bool spi_bus_manager_is_initialized(void); + +#ifdef __cplusplus +} +#endif + +#endif // SPI_BUS_MANAGER_H_ diff --git a/components/spi_bus_manager/src/spi_bus_manager.c b/components/spi_bus_manager/src/spi_bus_manager.c new file mode 100755 index 0000000..80a1079 --- /dev/null +++ b/components/spi_bus_manager/src/spi_bus_manager.c @@ -0,0 +1,48 @@ +#include "spi_bus_manager.h" +#include "esp_log.h" +#include "esp_err.h" + +#define TAG "spi_bus_mgr" + +#define SHARED_SPI_HOST SPI2_HOST + +// Pinos padrão (podem ser futuramente configuráveis) +#define PIN_NUM_MOSI 23 +#define PIN_NUM_MISO 19 +#define PIN_NUM_CLK 18 + +static bool initialized = false; + +esp_err_t spi_bus_manager_init(void) { + if (initialized) { + ESP_LOGW(TAG, "SPI bus already initialized on host %d", SHARED_SPI_HOST); + return ESP_OK; + } + + spi_bus_config_t buscfg = { + .mosi_io_num = PIN_NUM_MOSI, + .miso_io_num = PIN_NUM_MISO, + .sclk_io_num = PIN_NUM_CLK, + .quadwp_io_num = -1, + .quadhd_io_num = -1, + .max_transfer_sz = 64, // Ajustável conforme necessidade + }; + + esp_err_t ret = spi_bus_initialize(SHARED_SPI_HOST, &buscfg, SPI_DMA_CH_AUTO); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to initialize SPI bus: %s", esp_err_to_name(ret)); + return ret; + } + + initialized = true; + ESP_LOGI(TAG, "SPI bus initialized on host %d", SHARED_SPI_HOST); + return ESP_OK; +} + +spi_host_device_t spi_bus_manager_get_host(void) { + return SHARED_SPI_HOST; +} + +bool spi_bus_manager_is_initialized(void) { + return initialized; +} diff --git a/main/main.c b/main/main.c index d04e675..9515d72 100755 --- a/main/main.c +++ b/main/main.c @@ -23,7 +23,7 @@ #include "peripherals.h" #include "protocols.h" #include "evse_manager.h" -#include "evse_api.h" +#include "evse_core.h" #include "auth.h" #include "loadbalancer.h" #include "meter_manager.h" @@ -118,12 +118,13 @@ static void user_input_task_func(void *param) { if (notification & PRESS_BIT) { press_tick = xTaskGetTickCount(); pressed = true; - ESP_LOGI(TAG, "Botão pressionado"); + ESP_LOGI(TAG, "Pressed Button"); + handle_button_press(); } if (notification & RELEASED_BIT && pressed) { pressed = false; - ESP_LOGI(TAG, "Botão liberado"); + ESP_LOGI(TAG, "Reladead Buttton"); handle_button_press(); } } @@ -175,7 +176,8 @@ static void init_modules(void) { loadbalancer_init(); meter_manager_grid_init(); meter_manager_grid_start(); - //meter_manager_evse_init(); + meter_manager_evse_init(); + meter_manager_evse_start(); // Outros módulos (descomente conforme necessário) // meter_init(); diff --git a/projeto_parte1.c b/projeto_parte1.c old mode 100755 new mode 100644 index 199f3e1..0f39e9c --- a/projeto_parte1.c +++ b/projeto_parte1.c @@ -1,4 +1,4 @@ - +. // === Início de: main/main.c === #include @@ -26,7 +26,7 @@ #include "peripherals.h" #include "protocols.h" #include "evse_manager.h" -#include "evse_api.h" +#include "evse_core.h" #include "auth.h" #include "loadbalancer.h" #include "meter_manager.h" @@ -121,12 +121,13 @@ static void user_input_task_func(void *param) { if (notification & PRESS_BIT) { press_tick = xTaskGetTickCount(); pressed = true; - ESP_LOGI(TAG, "Botão pressionado"); + ESP_LOGI(TAG, "Pressed Button"); + handle_button_press(); } if (notification & RELEASED_BIT && pressed) { pressed = false; - ESP_LOGI(TAG, "Botão liberado"); + ESP_LOGI(TAG, "Reladead Buttton"); handle_button_press(); } } @@ -178,7 +179,8 @@ static void init_modules(void) { loadbalancer_init(); meter_manager_grid_init(); meter_manager_grid_start(); - //meter_manager_evse_init(); + meter_manager_evse_init(); + meter_manager_evse_start(); // Outros módulos (descomente conforme necessário) // meter_init(); @@ -226,800 +228,3007 @@ void app_main(void) { // === Fim de: main/main.c === -// === Início de: components/evse/evse_pilot.c === -#include -#include -#include -#include -#include - -#include "driver/ledc.h" -#include "esp_err.h" +// === Início de: components/peripherals/src/ac_relay.c === #include "esp_log.h" -#include "esp_rom_sys.h" +#include "driver/gpio.h" -#include "evse_pilot.h" -#include "adc.h" -#include "board_config.h" - -#define PILOT_PWM_TIMER LEDC_TIMER_0 -#define PILOT_PWM_CHANNEL LEDC_CHANNEL_0 -#define PILOT_PWM_SPEED_MODE LEDC_LOW_SPEED_MODE -#define PILOT_PWM_DUTY_RES LEDC_TIMER_10_BIT -#define PILOT_PWM_MAX_DUTY 1023 - -#define NUM_PILOT_SAMPLES 100 -#define MAX_SAMPLE_ATTEMPTS 1000 -#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior - -static const char *TAG = "evse_pilot"; - -void pilot_init(void) -{ - ledc_timer_config_t ledc_timer = { - .speed_mode = PILOT_PWM_SPEED_MODE, - .timer_num = PILOT_PWM_TIMER, - .duty_resolution = PILOT_PWM_DUTY_RES, - .freq_hz = 1000, - .clk_cfg = LEDC_AUTO_CLK - }; - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); - - ledc_channel_config_t ledc_channel = { - .speed_mode = PILOT_PWM_SPEED_MODE, - .channel = PILOT_PWM_CHANNEL, - .timer_sel = PILOT_PWM_TIMER, - .intr_type = LEDC_INTR_DISABLE, - .gpio_num = board_config.pilot_pwm_gpio, - .duty = 0, - .hpoint = 0 - }; - 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)); - - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12, - }; - - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.pilot_adc_channel, &config)); -} - -void pilot_set_level(bool level) -{ - ESP_LOGI(TAG, "Set level %d", level); - ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0); -} - -void pilot_set_amps(uint16_t amps) -{ - ESP_LOGI(TAG, "Set amps %d", amps); - - if (amps < 60 || amps > 800) { - ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps); - return; - } - - uint32_t duty; - if (amps <= 510) { - duty = (PILOT_PWM_MAX_DUTY * amps) / 600; - } else { - duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100)); - } - - if (duty > PILOT_PWM_MAX_DUTY) - duty = PILOT_PWM_MAX_DUTY; - - ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY); - ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); - ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); -} -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)]; -} - -void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) -{ - ESP_LOGD(TAG, "pilot_measure"); - - int samples[NUM_PILOT_SAMPLES]; - int collected = 0, attempts = 0; - int sample; - - while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) { - if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) { - samples[collected++] = sample; - esp_rom_delay_us(10); - } else { - esp_rom_delay_us(100); - attempts++; - } - } - - if (collected < NUM_PILOT_SAMPLES) { - ESP_LOGW(TAG, "Timeout on sample read (%d/%d)", collected, NUM_PILOT_SAMPLES); - *up_voltage = PILOT_VOLTAGE_1; - *down_voltage_n12 = false; - 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); - - - ESP_LOGD(TAG, "Final: high_raw=%d, low_raw=%d", high_raw, low_raw); - - int high_mv = 0; - int low_mv = 0; - - if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK || - adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) { - ESP_LOGW(TAG, "ADC calibration failed"); - *up_voltage = PILOT_VOLTAGE_1; - *down_voltage_n12 = false; - return; - } - - if (high_mv >= board_config.pilot_down_threshold_12) - *up_voltage = PILOT_VOLTAGE_12; - else if (high_mv >= board_config.pilot_down_threshold_9) - *up_voltage = PILOT_VOLTAGE_9; - else if (high_mv >= board_config.pilot_down_threshold_6) - *up_voltage = PILOT_VOLTAGE_6; - else if (high_mv >= board_config.pilot_down_threshold_3) - *up_voltage = PILOT_VOLTAGE_3; - else - *up_voltage = PILOT_VOLTAGE_1; - - *down_voltage_n12 = (low_mv <= board_config.pilot_down_threshold_n12); - - ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12); -} - -// === Fim de: components/evse/evse_pilot.c === - - -// === Início de: components/evse/evse_hardware.c === -#include "evse_hardware.h" -#include "evse_pilot.h" -#include "ac_relay.h" -#include "socket_lock.h" -#include "proximity.h" - -static const char *TAG = "evse_hardware"; - -void evse_hardware_init(void) { - pilot_init(); - pilot_set_level(true); // Sinal piloto em 12V (inicial) - ac_relay_set_state(false); // Relé desligado - //socket_lock_set_locked(false); // Destrava o conector -} - -void evse_hardware_tick(void) { - // Tick para atualizações de hardware com polling, se necessário -} - -bool evse_hardware_is_pilot_high(void) { - return pilot_get_state(); // true se sinal piloto estiver em nível alto -} - -bool evse_hardware_is_vehicle_connected(void) { - // Verifica se o veículo está conectado pelo resistor do pino PP - return proximity_get_max_current() > 0; -} - -bool evse_hardware_is_energy_detected(void) { - return false; -} - -void evse_hardware_relay_on(void) { - ac_relay_set_state(true); -} - -void evse_hardware_relay_off(void) { - ac_relay_set_state(false); -} - -bool evse_hardware_relay_status(void) { - return ac_relay_get_state(); -} - -void evse_hardware_lock(void) { - socket_lock_set_locked(true); -} - -void evse_hardware_unlock(void) { - socket_lock_set_locked(false); -} - -bool evse_hardware_is_locked(void) { - return socket_lock_is_locked_state(); -} - -// === Fim de: components/evse/evse_hardware.c === - - -// === Início de: components/evse/evse_state.c === -#include "evse_api.h" -#include "evse_state.h" -#include "evse_events.h" -#include "freertos/FreeRTOS.h" -#include "freertos/portmacro.h" -#include "esp_log.h" - -// ========================= -// Internal State Variables -// ========================= - -static evse_state_t current_state = EVSE_STATE_A; -static bool is_authorized = false; -static TickType_t session_start_tick = 0; - -static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED; - -// ========================= -// Internal Mapping -// ========================= - -static evse_state_event_t map_state_to_event(evse_state_t s) { - switch (s) { - case EVSE_STATE_A: return EVSE_STATE_EVENT_IDLE; - case EVSE_STATE_B1: return EVSE_STATE_EVENT_WAITING; - case EVSE_STATE_B2: - case EVSE_STATE_C1: - case EVSE_STATE_C2: return EVSE_STATE_EVENT_CHARGING; - case EVSE_STATE_E: - case EVSE_STATE_F: return EVSE_STATE_EVENT_FAULT; - default: return EVSE_STATE_EVENT_IDLE; - } -} - -// ========================= -// Public API -// ========================= - -void evse_set_state(evse_state_t state) { - bool changed = false; - evse_state_t previous_state; - - portENTER_CRITICAL(&state_mux); - previous_state = current_state; - if (state != current_state) { - current_state = state; - changed = true; - - if (evse_state_is_charging(state) && !evse_state_is_charging(previous_state)) { - session_start_tick = xTaskGetTickCount(); - } - } - portEXIT_CRITICAL(&state_mux); - - if (changed) { - ESP_LOGI("EVSE_STATE", "State changed from %s to %s", - evse_state_to_str(previous_state), - evse_state_to_str(state)); - - evse_state_event_data_t evt = { - .state = map_state_to_event(state) - }; - esp_event_post(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt), portMAX_DELAY); - } -} - -evse_state_t evse_get_state(void) { - portENTER_CRITICAL(&state_mux); - evse_state_t s = current_state; - portEXIT_CRITICAL(&state_mux); - return s; -} - -TickType_t evse_get_session_start(void) { - portENTER_CRITICAL(&state_mux); - TickType_t t = session_start_tick; - portEXIT_CRITICAL(&state_mux); - return t; -} - -const char* evse_state_to_str(evse_state_t state) { - switch (state) { - case EVSE_STATE_A: return "A - EV Not Connected (12V)"; - case EVSE_STATE_B1: return "B1 - EV Connected (9V, Not Authorized)"; - case EVSE_STATE_B2: return "B2 - EV Connected (9V, Authorized and Ready)"; - case EVSE_STATE_C1: return "C1 - Charging Requested (6V, Relay Off)"; - case EVSE_STATE_C2: return "C2 - Charging Active (6V, Relay On)"; - case EVSE_STATE_D1: return "D1 - Ventilation Required (3V, Relay Off)"; - case EVSE_STATE_D2: return "D2 - Ventilation Active (3V, Relay On)"; - case EVSE_STATE_E: return "E - Error: Control Pilot Shorted to Ground (0V)"; - case EVSE_STATE_F: return "F - Fault: EVSE Unavailable or No Pilot Signal"; - default: return "Unknown State"; - } -} - -void evse_state_init(void) { - portENTER_CRITICAL(&state_mux); - current_state = EVSE_STATE_A; - session_start_tick = xTaskGetTickCount(); - is_authorized = true; - portEXIT_CRITICAL(&state_mux); - - ESP_LOGI("EVSE_STATE", "Initialized in state: %s", evse_state_to_str(current_state)); - - evse_state_event_data_t evt = { - .state = map_state_to_event(current_state) - }; - esp_event_post(EVSE_EVENTS, EVSE_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); -} - -void evse_state_tick(void) { - // Placeholder for future state logic -} - -bool evse_state_is_charging(evse_state_t state) { - return state == EVSE_STATE_C1 || state == EVSE_STATE_C2; -} - -bool evse_state_is_plugged(evse_state_t state) { - return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 || - state == EVSE_STATE_C1 || state == EVSE_STATE_C2 || - state == EVSE_STATE_D1 || state == EVSE_STATE_D2; -} - -bool evse_state_is_session(evse_state_t state) { - return state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2; -} - -void evse_state_set_authorized(bool authorized) { - portENTER_CRITICAL(&state_mux); - is_authorized = authorized; - portEXIT_CRITICAL(&state_mux); -} - -bool evse_state_get_authorized(void) { - portENTER_CRITICAL(&state_mux); - bool result = is_authorized; - portEXIT_CRITICAL(&state_mux); - return result; -} - -// === Fim de: components/evse/evse_state.c === - - -// === Início de: components/evse/evse_fsm.c === -// evse_fsm.c - Máquina de Estados EVSE com controle centralizado - -#include "evse_fsm.h" -#include "evse_api.h" -#include "evse_pilot.h" -#include "evse_config.h" -#include "esp_log.h" #include "ac_relay.h" #include "board_config.h" -#include "socket_lock.h" -#include "proximity.h" -#include "rcm.h" -#include "evse_state.h" -static const char *TAG = "evse_fsm"; +static const char* TAG = "ac_relay"; -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif +// Memoization do estado atual do relé (salvo em RAM) +static int last_state = -1; -static bool c1_d1_waiting = false; -static TickType_t c1_d1_relay_to = 0; +/** + * @brief Initialize the AC relay GPIO. + * + * Configures the specified GPIO pin as an output and sets its initial state to OFF (low). + */ +void ac_relay_init(void) +{ + gpio_config_t conf = { + .pin_bit_mask = BIT64(board_config.ac_relay_gpio), + .mode = GPIO_MODE_OUTPUT, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .pull_up_en = GPIO_PULLUP_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; -void evse_fsm_reset(void) { - evse_set_state(EVSE_STATE_A); - c1_d1_waiting = false; - c1_d1_relay_to = 0; -} - -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) { - cable_max_current = proximity_get_max_current(); + esp_err_t ret = gpio_config(&conf); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to configure GPIO (error: %s)", esp_err_to_name(ret)); + return; } - 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; + gpio_set_level(board_config.ac_relay_gpio, 0); ///< Ensure relay starts OFF + last_state = 0; + ESP_LOGI(TAG, "AC relay initialized. Pin: %d", board_config.ac_relay_gpio); +} - 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); - } +/** + * @brief Set the state of the AC relay. + * + * @param state True to turn the relay ON, False to turn it OFF. + */ +void ac_relay_set_state(bool state) +{ + if (state == last_state) { + // Estado não mudou; evita log e escrita desnecessária. + return; + } + last_state = state; - if (rcm_test()) { - ESP_LOGI(TAG, "RCM self test passed"); - } else { - ESP_LOGW(TAG, "RCM self test failed"); - } - break; + ESP_LOGI(TAG, "Setting AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, state); - case EVSE_STATE_B2: - pilot_set_amps(MIN(current * 10, cable_max_current * 10)); - ac_relay_set_state(false); - break; - - case EVSE_STATE_C1: - case EVSE_STATE_D1: - pilot_set_level(true); - 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 * 10, cable_max_current * 10)); - ac_relay_set_state(true); - break; + esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state ? 1 : 0); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "Failed to set GPIO level (error: %s)", esp_err_to_name(ret)); } } -void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { - TickType_t now = xTaskGetTickCount(); - 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; - - 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; - } - } - __attribute__((fallthrough)); // Evita warning de fallthrough implícito - - 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; - } - break; - - case EVSE_STATE_E: - break; // Sem transições a partir de E - - case EVSE_STATE_F: - if (available) { - evse_set_state(EVSE_STATE_A); - } - break; - } - - evse_state_t next = evse_get_state(); - if (next != prev) { - ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(prev), evse_state_to_str(next)); - update_outputs(next); - } +/** + * @brief Get the current state of the AC relay. + * + * @return true if the relay is ON, false if OFF. + */ +bool ac_relay_get_state(void) +{ + int level = gpio_get_level(board_config.ac_relay_gpio); + ESP_LOGD(TAG, "Current AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, level); + return (level != 0); } -// === Fim de: components/evse/evse_fsm.c === +// === Fim de: components/peripherals/src/ac_relay.c === -// === Início de: components/evse/evse_error.c === -#include "evse_error.h" -#include "evse_config.h" - -#include "freertos/FreeRTOS.h" +// === Início de: components/peripherals/src/ntc_sensor.c === +#include +#include #include "freertos/task.h" #include "esp_log.h" #include "ntc_sensor.h" +#include "ntc_driver.h" -static const char *TAG = "evse_error"; +#include "adc.h" -static uint32_t error_bits = 0; -static TickType_t auto_clear_timeout = 0; -static bool error_cleared = false; +static const char *TAG = "temp_sensor"; -void evse_error_init(void) { - // Inicialização do sistema de erros -} +#define MEASURE_PERIOD 15000 // 10s -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"); +static float temp = 0.0; - // 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); - ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)"); - } - } - - // 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"); +static ntc_device_handle_t ntc = NULL; + +static portMUX_TYPE temp_mux = portMUX_INITIALIZER_UNLOCKED; + +static void ntc_sensor_task_func(void *param) { + float t; + while (true) { + if (ntc_dev_get_temperature(ntc, &t) == ESP_OK) { + portENTER_CRITICAL(&temp_mux); + temp = t; + portEXIT_CRITICAL(&temp_mux); } + vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); } } -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 - - // Log informativo com os valores - 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); - ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado"); - } - return; - } - - evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida - - 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); - } - } else { - evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT); - } +float ntc_temp_sensor(void) { + float t; + portENTER_CRITICAL(&temp_mux); + t = temp; + portEXIT_CRITICAL(&temp_mux); + return t; } -uint32_t evse_get_error(void) { - return error_bits; +void ntc_sensor_init(void) +{ + + ESP_LOGI(TAG, "ntc_sensor_init"); + + // Select the NTC sensor and initialize the hardware parameters + ntc_config_t ntc_config = { + .b_value = 3950, + .r25_ohm = 10000, + .fixed_ohm = 4700, + .vdd_mv = 3300, + .circuit_mode = CIRCUIT_MODE_NTC_GND, + .atten = ADC_ATTEN_DB_12, + .channel = ADC_CHANNEL_0, + .unit = ADC_UNIT_1}; + + // Create the NTC Driver and Init ADC + // ntc_device_handle_t ntc = NULL; + // adc_oneshot_unit_handle_t adc_handle = NULL; + ESP_ERROR_CHECK(ntc_dev_create(&ntc_config, &ntc, &adc_handle)); + ESP_ERROR_CHECK(ntc_dev_get_adc_handle(ntc, &adc_handle)); + + xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 3, NULL); } -bool evse_is_error_cleared(void) { - return error_cleared; -} - -void evse_mark_error_cleared(void) { - error_cleared = false; -} - -// Já existentes -void evse_error_set(uint32_t bitmask) { - error_bits |= bitmask; - - if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) { - auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s - } -} - -void evse_error_clear(uint32_t bitmask) { - bool had_error = error_bits != 0; - error_bits &= ~bitmask; - - if (had_error && error_bits == 0) { - error_cleared = true; - } -} - -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); - auto_clear_timeout = 0; - } -} - -bool evse_error_is_active(void) { - return error_bits != 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; -} - -// === Fim de: components/evse/evse_error.c === +// === Fim de: components/peripherals/src/ntc_sensor.c === -// === Início de: components/evse/evse_core.c === -// evse_core.c - Função principal de controle do EVSE - -#include "evse_fsm.h" -#include "evse_error.h" -#include "evse_limits.h" -#include "evse_config.h" -#include "evse_api.h" -#include "evse_pilot.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" +// === Início de: components/peripherals/src/proximity.c === #include "esp_log.h" -static const char *TAG = "evse_core"; +#include "proximity.h" +#include "board_config.h" +#include "adc.h" -static SemaphoreHandle_t mutex; +static const char *TAG = "proximity"; -static evse_state_t last_state = EVSE_STATE_A; - -static void evse_core_task(void *arg); - -void evse_init(void) { - ESP_LOGI(TAG, "EVSE Init"); - - mutex = xSemaphoreCreateMutex(); - - evse_check_defaults(); - evse_fsm_reset(); - pilot_set_level(true); // Estado inicial do piloto - - xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL); +void proximity_init(void) +{ + if (board_config.proximity) + { + adc_oneshot_chan_cfg_t config = { + .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = ADC_ATTEN_DB_12}; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.proximity_adc_channel, &config)); + } } -void evse_process(void) { - xSemaphoreTake(mutex, portMAX_DELAY); +uint8_t proximity_get_max_current(void) +{ + int voltage; + adc_oneshot_read(adc_handle, board_config.proximity_adc_channel, &voltage); + adc_cali_raw_to_voltage(adc_cali_handle, voltage, &voltage); - pilot_voltage_t pilot_voltage; - bool is_n12v = false; + ESP_LOGI(TAG, "Measured: %dmV", voltage); - pilot_measure(&pilot_voltage, &is_n12v); - ESP_LOGD(TAG, "Pilot: %d, -12V: %s", pilot_voltage, is_n12v ? "yes" : "no"); + uint8_t current; - if (evse_get_error() == 0 && !evse_is_error_cleared()) { - - evse_error_check(pilot_voltage, is_n12v); - - evse_fsm_process( - pilot_voltage, - evse_state_get_authorized(), - evse_config_is_available(), - 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; - } - - evse_mark_error_cleared(); + if (voltage >= board_config.proximity_down_threshold_8) + { + current = 8; + } + else if (voltage >= board_config.proximity_down_threshold_10) + { + current = 10; } - xSemaphoreGive(mutex); + else if (voltage >= board_config.proximity_down_threshold_13) + { + current = 13; + } + else if (voltage >= board_config.proximity_down_threshold_20) + { + current = 20; + } + + else if (voltage >= board_config.proximity_down_threshold_25) + { + current = 25; + } + else if (voltage >= board_config.proximity_down_threshold_32) + { + current = 32; + } + else + { + current = 32; + } + + ESP_LOGI(TAG, "Max current: %dA", current); + + return current; +} +// === Fim de: components/peripherals/src/proximity.c === + + +// === Início de: components/peripherals/src/buzzer.c === +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "board_config.h" +#include "buzzer.h" +#include "evse_api.h" + +static gpio_num_t buzzer_gpio = GPIO_NUM_NC; +static evse_state_t last_buzzer_state = -1; +static QueueHandle_t buzzer_queue = NULL; + +void buzzer_on(void) { + if (buzzer_gpio != GPIO_NUM_NC) + gpio_set_level(buzzer_gpio, 1); } - -// ================================ -// Interface pública -// ================================ - -bool evse_is_enabled(void) { - return evse_config_is_enabled(); +void buzzer_off(void) { + if (buzzer_gpio != GPIO_NUM_NC) + gpio_set_level(buzzer_gpio, 0); } -void evse_set_enabled(bool value) { - ESP_LOGI(TAG, "Set enabled %d", value); - evse_config_set_enabled(value); +// ---------------------- +// Padrões de Buzzer +// ---------------------- + +typedef struct { + uint16_t on_ms; + uint16_t off_ms; +} buzzer_pattern_step_t; + +typedef enum { + BUZZER_PATTERN_NONE = 0, + BUZZER_PATTERN_PLUGGED, + BUZZER_PATTERN_UNPLUGGED, + BUZZER_PATTERN_CHARGING, +} buzzer_pattern_id_t; + +static const buzzer_pattern_step_t pattern_plugged[] = { + {100, 100}, {200, 0} +}; + +static const buzzer_pattern_step_t pattern_unplugged[] = { + {150, 150}, {150, 150}, {150, 0} +}; + +static const buzzer_pattern_step_t pattern_charging[] = { + {80, 150}, {100, 120}, {120, 100}, {140, 0} +}; + +// ---------------------- +// Executor de padrões +// ---------------------- + +static void buzzer_execute_pattern(buzzer_pattern_id_t pattern_id) { + const buzzer_pattern_step_t *pattern = NULL; + size_t length = 0; + + switch (pattern_id) { + case BUZZER_PATTERN_PLUGGED: + pattern = pattern_plugged; + length = sizeof(pattern_plugged) / sizeof(pattern_plugged[0]); + break; + case BUZZER_PATTERN_UNPLUGGED: + pattern = pattern_unplugged; + length = sizeof(pattern_unplugged) / sizeof(pattern_unplugged[0]); + break; + case BUZZER_PATTERN_CHARGING: + pattern = pattern_charging; + length = sizeof(pattern_charging) / sizeof(pattern_charging[0]); + break; + default: + return; + } + + for (size_t i = 0; i < length; i++) { + buzzer_on(); + vTaskDelay(pdMS_TO_TICKS(pattern[i].on_ms)); + buzzer_off(); + if (pattern[i].off_ms > 0) + vTaskDelay(pdMS_TO_TICKS(pattern[i].off_ms)); + } } -bool evse_is_available(void) { - return evse_config_is_available(); -} +// ---------------------- +// Task que toca o buzzer +// ---------------------- -void evse_set_available(bool value) { - ESP_LOGI(TAG, "Set available %d", value); - evse_config_set_available(value); -} +static void buzzer_worker_task(void *arg) { + buzzer_pattern_id_t pattern_id; -// ================================ -// Tarefa principal -// ================================ - -static void evse_core_task(void *arg) { while (true) { - evse_process(); + if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) { + buzzer_execute_pattern(pattern_id); + } + } +} + +// ---------------------- +// Task de monitoramento +// ---------------------- + +static void buzzer_monitor_task(void *arg) { + while (true) { + evse_state_t current = evse_get_state(); + + if (current != last_buzzer_state) { + buzzer_pattern_id_t pattern_id = BUZZER_PATTERN_NONE; + + switch (current) { + case EVSE_STATE_A: + if (last_buzzer_state != EVSE_STATE_A) + pattern_id = BUZZER_PATTERN_UNPLUGGED; + break; + case EVSE_STATE_B1: + case EVSE_STATE_B2: + if (last_buzzer_state != EVSE_STATE_B1 && last_buzzer_state != EVSE_STATE_B2) + pattern_id = BUZZER_PATTERN_PLUGGED; + break; + case EVSE_STATE_C2: + case EVSE_STATE_D2: + if (last_buzzer_state != EVSE_STATE_C2 && last_buzzer_state != EVSE_STATE_D2) + pattern_id = BUZZER_PATTERN_CHARGING; + break; + default: + break; + } + + if (pattern_id != BUZZER_PATTERN_NONE) { + xQueueSend(buzzer_queue, &pattern_id, 0); // Não bloqueia + } + + last_buzzer_state = current; + } + vTaskDelay(pdMS_TO_TICKS(100)); } } -uint32_t evse_get_total_energy(void) { - return 0; // Stub de 1 kWh +// ---------------------- +// Inicialização +// ---------------------- + +void buzzer_init(void) { + if (board_config.buzzer) { + buzzer_gpio = board_config.buzzer_gpio; + + gpio_config_t io_conf = { + .pin_bit_mask = BIT64(buzzer_gpio), + .mode = GPIO_MODE_OUTPUT, + .pull_down_en = GPIO_PULLDOWN_ENABLE, + .pull_up_en = GPIO_PULLUP_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + gpio_config(&io_conf); + gpio_set_level(buzzer_gpio, 0); + } + + buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t)); + + xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 3, NULL); + xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 3, NULL); } -uint32_t evse_get_instant_power(void) { - return 0; // Stub de 2 kW +// === Fim de: components/peripherals/src/buzzer.c === + + +// === Início de: components/peripherals/src/ds18x20.h === +/* + * Copyright (c) 2016 Grzegorz Hetman + * Copyright (c) 2016 Alex Stewart + * Copyright (c) 2018 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _DS18X20_H +#define _DS18X20_H + +#include +#include "onewire.h" + +typedef onewire_addr_t ds18x20_addr_t; + +/** An address value which can be used to indicate "any device on the bus" */ +#define DS18X20_ANY ONEWIRE_NONE + +/** Family ID (lower address byte) of DS18B20 sensors */ +#define DS18B20_FAMILY_ID 0x28 + +/** Family ID (lower address byte) of DS18S20 sensors */ +#define DS18S20_FAMILY_ID 0x10 + +/** + * @brief Find the addresses of all ds18x20 devices on the bus. + * + * Scans the bus for all devices and places their addresses in the supplied + * array. If there are more than `addr_count` devices on the bus, only the + * first `addr_count` are recorded. + * + * @param pin The GPIO pin connected to the ds18x20 bus + * @param addr_list A pointer to an array of ::ds18x20_addr_t values. + * This will be populated with the addresses of the found + * devices. + * @param addr_count Number of slots in the `addr_list` array. At most this + * many addresses will be returned. + * @param found The number of devices found. Note that this may be less + * than, equal to, or more than `addr_count`, depending on + * how many ds18x20 devices are attached to the bus. + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found); + +/** + * @brief Tell one or more sensors to perform a temperature measurement and + * conversion (CONVERT_T) operation. + * + * This operation can take up to 750ms to complete. + * + * If `wait=true`, this routine will automatically drive the pin high for the + * necessary 750ms after issuing the command to ensure parasitically-powered + * devices have enough power to perform the conversion operation (for + * non-parasitically-powered devices, this is not necessary but does not + * hurt). If `wait=false`, this routine will drive the pin high, but will + * then return immediately. It is up to the caller to wait the requisite time + * and then depower the bus using onewire_depower() or by issuing another + * command once conversion is done. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device on the bus. This can be set + * to ::DS18X20_ANY to send the command to all devices on the bus + * at the same time. + * @param wait Whether to wait for the necessary 750ms for the ds18x20 to + * finish performing the conversion before returning to the + * caller (You will normally want to do this). + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); + +/** + * @brief Read the value from the last CONVERT_T operation. + * + * This should be called after ds18x20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** + * @brief Read the value from the last CONVERT_T operation (ds18b20 version). + * + * This should be called after ds18x20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** + * @brief Read the value from the last CONVERT_T operation (ds18s20 version). + * + * This should be called after ds18x20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** + * @brief Read the value from the last CONVERT_T operation for multiple devices. + * + * This should be called after ds18x20_measure() to fetch the result of the + * temperature measurement. + * + * @param pin The GPIO pin connected to the ds18x20 bus + * @param addr_list A list of addresses for devices to read. + * @param addr_count The number of entries in `addr_list`. + * @param result_list An array of int16_ts to hold the returned temperature + * values. It should have at least `addr_count` entries. + * + * @returns `ESP_OK` if all temperatures were fetched successfully + */ +esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); + +/** Perform a ds18x20_measure() followed by ds18s20_read_temperature() + * + * @param pin The GPIO pin connected to the ds18s20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + */ +esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** Perform a ds18x20_measure() followed by ds18b20_read_temperature() + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + */ +esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param temperature The temperature in degrees Celsius + */ +esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); + +/** + * @brief Perform a ds18x20_measure() followed by ds18x20_read_temp_multi() + * + * @param pin The GPIO pin connected to the ds18x20 bus + * @param addr_list A list of addresses for devices to read. + * @param addr_count The number of entries in `addr_list`. + * @param result_list An array of int16_ts to hold the returned temperature + * values. It should have at least `addr_count` entries. + * + * @returns `ESP_OK` if all temperatures were fetched successfully + */ +esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); + +/** + * @brief Read the scratchpad data for a particular ds18x20 device. + * + * This is not generally necessary to do directly. It is done automatically + * as part of ds18x20_read_temperature(). + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to read. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param buffer An 8-byte buffer to hold the read data. + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); + +/** + * @brief Write the scratchpad data for a particular ds18x20 device. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to write. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * @param buffer An 3-byte buffer to hold the data to write + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); + +/** + * @brief Issue the copy scratchpad command, copying current scratchpad to + * EEPROM. + * + * @param pin The GPIO pin connected to the ds18x20 device + * @param addr The 64-bit address of the device to command. This can be set + * to ::DS18X20_ANY to read any device on the bus (but note + * that this will only work if there is exactly one device + * connected, or they will corrupt each others' transmissions) + * + * @returns `ESP_OK` if the command was successfully issued + */ +esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr); + + +#endif /* _DS18X20_H */ +// === Fim de: components/peripherals/src/ds18x20.h === + + +// === Início de: components/peripherals/src/socket_lock.c === +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" +#include "freertos/timers.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "nvs.h" + +#include "socket_lock.h" +#include "board_config.h" + +#define NVS_NAMESPACE "socket_lock" +#define NVS_OPERATING_TIME "op_time" +#define NVS_BREAK_TIME "break_time" +#define NVS_RETRY_COUNT "retry_count" +#define NVS_DETECTION_HIGH "detect_hi" + +#define OPERATING_TIME_MIN 100 +#define OPERATING_TIME_MAX 1000 +#define LOCK_DELAY 500 + +#define LOCK_BIT BIT0 +#define UNLOCK_BIT BIT1 +#define REPEAT_LOCK_BIT BIT2 +#define REPEAT_UNLOCK_BIT BIT3 + +static const char* TAG = "socket_lock"; + +static nvs_handle_t nvs; + +static uint16_t operating_time = 300; + +static uint16_t break_time = 1000; + +static bool detection_high; + +static uint8_t retry_count = 5; + +static socket_lock_status_t status; + +static TaskHandle_t socket_lock_task; + +static bool is_locked(void) +{ + gpio_set_level(board_config.socket_lock_a_gpio, 1); + gpio_set_level(board_config.socket_lock_b_gpio, 1); + + vTaskDelay(pdMS_TO_TICKS(board_config.socket_lock_detection_delay)); + + return gpio_get_level(board_config.socket_lock_detection_gpio) == detection_high; } +bool socket_lock_is_locked_state(void) +{ + return is_locked(); +} -// === Fim de: components/evse/evse_core.c === +static void socket_lock_task_func(void* param) +{ + uint32_t notification; + + TickType_t previous_tick = 0; + uint8_t attempt = 0; + + while (true) { + if (xTaskNotifyWait(0x00, 0xff, ¬ification, portMAX_DELAY)) { + if (notification & (LOCK_BIT | UNLOCK_BIT)) { + attempt = retry_count; + } + + if (notification & (UNLOCK_BIT | REPEAT_UNLOCK_BIT)) { + gpio_set_level(board_config.socket_lock_a_gpio, 0); + gpio_set_level(board_config.socket_lock_b_gpio, 1); + vTaskDelay(pdMS_TO_TICKS(operating_time)); + + if (!is_locked()) { + ESP_LOGI(TAG, "Unlock OK"); + status = SOCKED_LOCK_STATUS_IDLE; + } else { + if (attempt > 1) { + ESP_LOGW(TAG, "Not unlocked yet, repeating..."); + attempt--; + xTaskNotify(socket_lock_task, REPEAT_UNLOCK_BIT, eSetBits); + } else { + ESP_LOGE(TAG, "Not unlocked"); + status = SOCKED_LOCK_STATUS_UNLOCKING_FAIL; + } + } + + gpio_set_level(board_config.socket_lock_a_gpio, 0); + gpio_set_level(board_config.socket_lock_b_gpio, 0); + } else if (notification & (LOCK_BIT | REPEAT_LOCK_BIT)) { + if (notification & LOCK_BIT) { + vTaskDelay(pdMS_TO_TICKS(LOCK_DELAY)); //delay before first lock attempt + } + gpio_set_level(board_config.socket_lock_a_gpio, 1); + gpio_set_level(board_config.socket_lock_b_gpio, 0); + vTaskDelay(pdMS_TO_TICKS(operating_time)); + + if (is_locked()) { + ESP_LOGI(TAG, "Lock OK"); + status = SOCKED_LOCK_STATUS_IDLE; + } else { + if (attempt > 1) { + ESP_LOGW(TAG, "Not locked yet, repeating..."); + attempt--; + xTaskNotify(socket_lock_task, REPEAT_LOCK_BIT, eSetBits); + } else { + ESP_LOGE(TAG, "Not locked"); + status = SOCKED_LOCK_STATUS_LOCKING_FAIL; + } + } + + gpio_set_level(board_config.socket_lock_a_gpio, 0); + gpio_set_level(board_config.socket_lock_b_gpio, 0); + } + + TickType_t delay_tick = xTaskGetTickCount() - previous_tick; + if (delay_tick < pdMS_TO_TICKS(break_time)) { + vTaskDelay(pdMS_TO_TICKS(break_time) - delay_tick); + } + previous_tick = xTaskGetTickCount(); + } + } +} + +void socket_lock_init(void) +{ + if (board_config.socket_lock) { + ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); + + nvs_get_u16(nvs, NVS_OPERATING_TIME, &operating_time); + + nvs_get_u16(nvs, NVS_BREAK_TIME, &break_time); + + nvs_get_u8(nvs, NVS_RETRY_COUNT, &retry_count); + + uint8_t u8; + if (nvs_get_u8(nvs, NVS_DETECTION_HIGH, &u8) == ESP_OK) { + detection_high = u8; + } + + gpio_config_t io_conf = {}; + + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = BIT64(board_config.socket_lock_a_gpio) | BIT64(board_config.socket_lock_b_gpio); + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pin_bit_mask = BIT64(board_config.socket_lock_detection_gpio); + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + xTaskCreate(socket_lock_task_func, "socket_lock_task", 2 * 1024, NULL, 10, &socket_lock_task); + } +} + +bool socket_lock_is_detection_high(void) +{ + return detection_high; +} + +void socket_lock_set_detection_high(bool _detection_high) +{ + detection_high = _detection_high; + + nvs_set_u8(nvs, NVS_DETECTION_HIGH, detection_high); + nvs_commit(nvs); +} + +uint16_t socket_lock_get_operating_time(void) +{ + return operating_time; +} + +esp_err_t socket_lock_set_operating_time(uint16_t _operating_time) +{ + if (_operating_time < OPERATING_TIME_MIN || _operating_time > OPERATING_TIME_MAX) { + ESP_LOGE(TAG, "Operating time out of range"); + return ESP_ERR_INVALID_ARG; + } + + operating_time = _operating_time; + nvs_set_u16(nvs, NVS_OPERATING_TIME, operating_time); + nvs_commit(nvs); + + return ESP_OK; +} + +uint8_t socket_lock_get_retry_count(void) +{ + return retry_count; +} + +void socket_lock_set_retry_count(uint8_t _retry_count) +{ + retry_count = _retry_count; + nvs_set_u8(nvs, NVS_RETRY_COUNT, retry_count); + nvs_commit(nvs); +} + +uint16_t socket_lock_get_break_time(void) +{ + return break_time; +} + +esp_err_t socket_lock_set_break_time(uint16_t _break_time) +{ + if (_break_time < board_config.socket_lock_min_break_time) { + ESP_LOGE(TAG, "Operating time out of range"); + return ESP_ERR_INVALID_ARG; + } + + break_time = _break_time; + nvs_set_u16(nvs, NVS_BREAK_TIME, break_time); + nvs_commit(nvs); + + return ESP_OK; +} + +void socket_lock_set_locked(bool locked) +{ + ESP_LOGI(TAG, "Set locked %d", locked); + + xTaskNotify(socket_lock_task, locked ? LOCK_BIT : UNLOCK_BIT, eSetBits); + status = SOCKED_LOCK_STATUS_OPERATING; +} + +socket_lock_status_t socket_lock_get_status(void) +{ + return status; +} +// === Fim de: components/peripherals/src/socket_lock.c === + + +// === Início de: components/peripherals/src/temp_sensor.c === +#include +#include +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" + +#include "temp_sensor.h" +#include "lm75a.h" + +#define MAX_SENSORS 5 +#define MEASURE_PERIOD 10000 // 10s +#define MEASURE_ERR_THRESHOLD 3 + +static const char *TAG = "temp_sensor"; + +static uint8_t sensor_count = 0; + +static int16_t low_temp = 0; + +static int high_temp = 0; + +static uint8_t measure_err_count = 0; + +static void temp_sensor_task_func(void *param) +{ + while (true) + { + high_temp = lm75a_read_temperature(0); + + vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); + } +} + +void temp_sensor_init(void) +{ + + ESP_LOGW(TAG, "temp_sensor_init"); + + lm75a_init(); + + xTaskCreate(temp_sensor_task_func, "temp_sensor_task", 5 * 1024, NULL, 5, NULL); +} + +uint8_t temp_sensor_get_count(void) +{ + return sensor_count; +} + +int16_t temp_sensor_get_low(void) +{ + return low_temp; +} + +int temp_sensor_get_high(void) +{ + return high_temp; +} + +bool temp_sensor_is_error(void) +{ + return sensor_count == 0 || measure_err_count > MEASURE_ERR_THRESHOLD; +} +// === Fim de: components/peripherals/src/temp_sensor.c === + + +// === Início de: components/peripherals/src/aux_io.c === +#include +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" +#include "freertos/task.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "nvs.h" + +#include "aux_io.h" +#include "board_config.h" +#include "adc.h" + +#define MAX_AUX_IN 4 +#define MAX_AUX_OUT 4 +#define MAX_AUX_AIN 4 + +//static const char* TAG = "aux"; + +static int aux_in_count = 0; +static int aux_out_count = 0; +static int aux_ain_count = 0; + +static struct aux_gpio_s +{ + gpio_num_t gpio; + const char* name; +} aux_in[MAX_AUX_IN], aux_out[MAX_AUX_OUT]; + +static struct aux_adc_s +{ + adc_channel_t adc; + const char* name; +} aux_ain[MAX_AUX_AIN]; + + +void aux_init(void) +{ + // IN + + gpio_config_t io_conf = { + .mode = GPIO_MODE_INPUT, + .pull_up_en = GPIO_PULLDOWN_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE, + .pin_bit_mask = 0 + }; + + if (board_config.aux_in_1) { + aux_in[aux_in_count].gpio = board_config.aux_in_1_gpio; + aux_in[aux_in_count].name = board_config.aux_in_1_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_in_1_gpio); + aux_in_count++; + } + + if (board_config.aux_in_2) { + aux_in[aux_in_count].gpio = board_config.aux_in_2_gpio; + aux_in[aux_in_count].name = board_config.aux_in_2_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_in_2_gpio); + aux_in_count++; + } + + if (board_config.aux_in_3) { + aux_in[aux_in_count].gpio = board_config.aux_in_3_gpio; + aux_in[aux_in_count].name = board_config.aux_in_3_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_in_3_gpio); + aux_in_count++; + } + + if (board_config.aux_in_4) { + aux_in[aux_in_count].gpio = board_config.aux_in_4_gpio; + aux_in[aux_in_count].name = board_config.aux_in_4_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_in_4_gpio); + aux_in_count++; + } + + if (io_conf.pin_bit_mask > 0) { + ESP_ERROR_CHECK(gpio_config(&io_conf)); + } + + // OUT + + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = 0; + + if (board_config.aux_out_1) { + aux_out[aux_out_count].gpio = board_config.aux_out_1_gpio; + aux_out[aux_out_count].name = board_config.aux_out_1_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_out_1_gpio); + aux_out_count++; + } + + if (board_config.aux_out_2) { + aux_out[aux_out_count].gpio = board_config.aux_out_2_gpio; + aux_out[aux_out_count].name = board_config.aux_out_2_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_out_2_gpio); + aux_out_count++; + } + + if (board_config.aux_out_3) { + aux_out[aux_out_count].gpio = board_config.aux_out_3_gpio; + aux_out[aux_out_count].name = board_config.aux_out_3_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_out_3_gpio); + aux_out_count++; + } + + if (board_config.aux_out_4) { + aux_out[aux_out_count].gpio = board_config.aux_out_4_gpio; + aux_out[aux_out_count].name = board_config.aux_out_4_name; + io_conf.pin_bit_mask |= BIT64(board_config.aux_out_4_gpio); + aux_out_count++; + } + + if (io_conf.pin_bit_mask > 0) { + ESP_ERROR_CHECK(gpio_config(&io_conf)); + } + + // AIN + + adc_oneshot_chan_cfg_t config = { + .bitwidth = ADC_BITWIDTH_DEFAULT, + .atten = ADC_ATTEN_DB_12 + }; + + if (board_config.aux_ain_1) { + aux_ain[aux_ain_count].adc = board_config.aux_ain_1_adc_channel; + aux_ain[aux_ain_count].name = board_config.aux_out_1_name; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_1_adc_channel, &config)); + aux_ain_count++; + } + + if (board_config.aux_ain_2) { + aux_ain[aux_ain_count].adc = board_config.aux_ain_2_adc_channel; + aux_ain[aux_ain_count].name = board_config.aux_out_2_name; + ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_2_adc_channel, &config)); + aux_ain_count++; + } +} + +esp_err_t aux_read(const char* name, bool* value) +{ + for (int i = 0; i < aux_in_count; i++) { + if (strcmp(aux_in[i].name, name) == 0) { + *value = gpio_get_level(aux_in[i].gpio) == 1; + return ESP_OK; + } + } + return ESP_ERR_NOT_FOUND; +} + +esp_err_t aux_write(const char* name, bool value) +{ + for (int i = 0; i < aux_out_count; i++) { + if (strcmp(aux_out[i].name, name) == 0) { + return gpio_set_level(aux_out[i].gpio, value); + } + } + return ESP_ERR_NOT_FOUND; +} + +esp_err_t aux_analog_read(const char* name, int* value) +{ + for (int i = 0; i < aux_ain_count; i++) { + if (strcmp(aux_ain[i].name, name) == 0) { + int raw = 0; + esp_err_t ret = adc_oneshot_read(adc_handle, aux_ain[i].adc, &raw); + if (ret == ESP_OK) { + return adc_cali_raw_to_voltage(adc_cali_handle, raw, value); + } else { + return ret; + } + } + } + return ESP_ERR_NOT_FOUND; +} +// === Fim de: components/peripherals/src/aux_io.c === + + +// === Início de: components/peripherals/src/lm75a.c === +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/queue.h" +#include "driver/gpio.h" +#include "driver/i2c_master.h" + +#define I2C_MASTER_NUM I2C_NUM_1 +#define I2C_MASTER_SCL_IO GPIO_NUM_22 // CONFIG_EXAMPLE_I2C_SCL /*!< gpio number for I2C master clock */ +#define I2C_MASTER_SDA_IO GPIO_NUM_21 // CONFIG_EXAMPLE_I2C_SDA /*!< gpio number for I2C master data */ +#define I2C_MASTER_FREQ_HZ 100000 // CONFIG_I2C_TRANS_SPEED /*!< I2C master clock frequency */ +#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ +#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ +#define LM75A_SLAVE_ADDR 0x48 // CONFIG_LM75A_SLAVE_ADDR /*!< LM75A slave address, you can set any 7bit value */ +#define ACK_VAL 0x0 /*!< I2C ack value */ +#define NACK_VAL 0x1 /*!< I2C nack value */ +#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ +#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ +#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ +#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ + +/* +#define GPIO_INPUT_IO_0 CONFIG_LM75A_OS_PIN +#define GPIO_OUTPUT_IO_0 CONFIG_LM75A_VCC_PIN +#define GPIO_OUTPUT_PIN_SEL (1ULL << GPIO_OUTPUT_IO_0) +#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO_0) +#define ESP_INTR_FLAG_DEFAULT 0 +*/ + +// static xQueueHandle gpio_evt_queue = NULL; +// static int gpio_int_task_enable = 0; +// static TaskHandle_t gpio_int_task_handle = NULL; + +/** + * @brief test code to read esp-i2c-slave + * We need to fill the buffer of esp slave device, then master can read them out. + * + * _______________________________________________________________________________________ + * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop | + * --------|--------------------------|----------------------|--------------------|------| + * + */ +static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size) +{ + if (size == 0) + { + return ESP_OK; + } + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN); + if (size > 1) + { + i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); + } + i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief Test code to write esp-i2c-slave + * Master device write data to slave(both esp32), + * the data will be stored in slave buffer. + * We can read them out from slave buffer. + * + * ___________________________________________________________________ + * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | + * --------|---------------------------|----------------------|------| + * + */ +static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t *data_wr, size_t size) +{ + i2c_cmd_handle_t cmd = i2c_cmd_link_create(); + i2c_master_start(cmd); + i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN); + i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); + i2c_master_stop(cmd); + esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); + i2c_cmd_link_delete(cmd); + return ret; +} + +/** + * @brief i2c master initialization + */ +static void i2c_master_init() +{ + int i2c_master_port = I2C_MASTER_NUM; + i2c_config_t conf; + conf.mode = I2C_MODE_MASTER; + conf.sda_io_num = I2C_MASTER_SDA_IO; + conf.sda_pullup_en = GPIO_PULLUP_DISABLE; + conf.scl_io_num = I2C_MASTER_SCL_IO; + conf.scl_pullup_en = GPIO_PULLUP_DISABLE; + conf.master.clk_speed = I2C_MASTER_FREQ_HZ; + conf.clk_flags = 0; + + i2c_param_config(i2c_master_port, &conf); + i2c_driver_install(i2c_master_port, conf.mode, + I2C_MASTER_RX_BUF_DISABLE, + I2C_MASTER_TX_BUF_DISABLE, 0); +} + +int lm75a_read_temperature(int show) +{ + uint8_t buf[2]; + float tmp; + buf[0] = 0; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); + i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); + tmp = buf[0]; + if (buf[1] & 128) + tmp += 0.5; + if (show) + printf("lm75a_read_temperature=%.1f\n", tmp); + return tmp; +} + +/* +static void IRAM_ATTR gpio_isr_handler(void *arg) +{ + uint32_t gpio_num = (uint32_t)arg; + xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); +} + +static void gpio_int_task(void *arg) +{ + uint32_t io_num; + gpio_int_task_enable = 1; + while (gpio_int_task_enable) + { + if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) + { + + // read temperature to clean int; + if (io_num == GPIO_INPUT_IO_0) + { + printf("GPIO[%d] intr, val: %d\n\n", io_num, gpio_get_level(io_num)); + lm75a_read_temperature(0); // read to clean interrupt. + } + } + } + printf("quit gpio_int_task\n"); + if (gpio_evt_queue) + { + vQueueDelete(gpio_evt_queue); + gpio_evt_queue = NULL; + } + gpio_int_task_handle = NULL; + vTaskDelete(NULL); +} + +void init_os_gpio() +{ + printf("init_os_gpio!\n"); + + if (gpio_evt_queue == NULL) + gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); + + if (gpio_int_task_handle == NULL) + { + xTaskCreate(gpio_int_task, "gpio_int_task", 2048, NULL, 10, &gpio_int_task_handle); + // install gpio isr service + gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); + // hook isr handler for specific gpio pin again + gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0); + } +} + +static void deinit_os_gpio() +{ + printf("deinit_os_gpio!\n"); + + if (gpio_int_task_handle) + { + gpio_isr_handler_remove(GPIO_INPUT_IO_0); + gpio_uninstall_isr_service(); + gpio_int_task_enable = 0; + int io = 0; + xQueueSend(gpio_evt_queue, &io, 0); // send a fake signal to quit task. + } +} + +static void lm75a_vcc_enable() +{ + gpio_config_t io_conf; + // enable output for vcc + io_conf.intr_type = GPIO_PIN_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + + // enable input for interrupt + io_conf.intr_type = GPIO_PIN_INTR_NEGEDGE; // GPIO_PIN_INTR_ANYEDGE; + io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; + io_conf.mode = GPIO_MODE_INPUT; + io_conf.pull_up_en = 1; + gpio_set_pull_mode(GPIO_INPUT_IO_0, GPIO_FLOATING); + gpio_config(&io_conf); + gpio_set_level(GPIO_OUTPUT_IO_0, 1); +} + +static void lm75a_vcc_disable() +{ + gpio_set_level(GPIO_OUTPUT_IO_0, 0); +} +*/ + +void lm75a_init() +{ + // lm75a_vcc_enable(); + i2c_master_init(); +} + +void lm75a_deinit() +{ + // deinit_os_gpio(); + i2c_driver_delete(I2C_MASTER_NUM); + // lm75a_vcc_disable(); +} + +void lm75a_set_tos(int tos) +{ + uint8_t buf[4]; + printf("lm75a_set_tos: %d\n", tos); + // set Tos: + buf[0] = 0x3; + buf[1] = (tos & 0xff); + buf[2] = 0; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); +} + +void lm75a_set_thys(int thys) +{ + uint8_t buf[4]; + printf("lm75a_set_thys: %d\n", thys); + // set Thyst: + buf[0] = 0x2; + buf[1] = (thys & 0xff); + buf[2] = 0; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); +} + +void lm75a_get_tos() +{ + uint8_t buf[4]; + float tmp; + buf[0] = 0x3; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); + i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); + tmp = buf[0]; + if (buf[1] & 128) + tmp += 0.5; + + printf("lm75a_get_tos: %.1f\n", tmp); +} + +void lm75a_get_thys() +{ + uint8_t buf[4]; + float tmp; + buf[0] = 0x2; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); + i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); + tmp = buf[0]; + if (buf[1] & 128) + tmp += 0.5; + + printf("lm75a_get_thys: %.1f\n", tmp); +} + +void lm75a_set_int(int en) +{ + uint8_t buf[2]; + + en = !!en; + if (en) + { + printf("lm75a_set_int: %d\n", en); + buf[0] = 0x1; + buf[1] = (1 << 1); // D1 set to 1; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); + i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. + // gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE); + // init_os_gpio(); + } + else + { + printf("lm75a_set_int: %d\n", en); + // deinit_os_gpio(); + buf[0] = 0x1; + buf[1] = 0; + i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); + i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. + } +} + +void lm75a_get_osio() +{ + // printf("os_io: %d\n", gpio_get_level(GPIO_INPUT_IO_0)); +} + +// === Fim de: components/peripherals/src/lm75a.c === + + +// === Início de: components/peripherals/src/onewire.c === +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 zeroday nodemcu.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ------------------------------------------------------------------------------- + * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the + * following additional terms: + * + * Except as contained in this notice, the name of Dallas Semiconductor + * shall not be used except as stated in the Dallas Semiconductor + * Branding Policy. + */ + +#include +#include +#include +#include "rom/ets_sys.h" + +#include "onewire.h" + +#define ONEWIRE_SELECT_ROM 0x55 +#define ONEWIRE_SKIP_ROM 0xcc +#define ONEWIRE_SEARCH 0xf0 +#define ONEWIRE_CRC8_TABLE + +static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + +// Waits up to `max_wait` microseconds for the specified pin to go high. +// Returns true if successful, false if the bus never comes high (likely +// shorted). +static inline bool _onewire_wait_for_bus(gpio_num_t pin, int max_wait) +{ + bool state; + for (int i = 0; i < ((max_wait + 4) / 5); i++) { + if (gpio_get_level(pin)) + break; + ets_delay_us(5); + } + state = gpio_get_level(pin); + // Wait an extra 1us to make sure the devices have an adequate recovery + // time before we drive things low again. + ets_delay_us(1); + return state; +} + +static void setup_pin(gpio_num_t pin, bool open_drain) +{ + gpio_set_direction(pin, open_drain ? GPIO_MODE_INPUT_OUTPUT_OD : GPIO_MODE_OUTPUT); + // gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY); +} + +// Perform the onewire reset function. We will wait up to 250uS for +// the bus to come high, if it doesn't then it is broken or shorted +// and we return false; +// +// Returns true if a device asserted a presence pulse, false otherwise. +// +bool onewire_reset(gpio_num_t pin) +{ + setup_pin(pin, true); + + gpio_set_level(pin, 1); + // wait until the wire is high... just in case + if (!_onewire_wait_for_bus(pin, 250)) + return false; + + gpio_set_level(pin, 0); + ets_delay_us(480); + + portENTER_CRITICAL(&mux); + gpio_set_level(pin, 1); // allow it to float + ets_delay_us(70); + bool r = !gpio_get_level(pin); + portEXIT_CRITICAL(&mux); + + // Wait for all devices to finish pulling the bus low before returning + if (!_onewire_wait_for_bus(pin, 410)) + return false; + + return r; +} + +static bool _onewire_write_bit(gpio_num_t pin, bool v) +{ + if (!_onewire_wait_for_bus(pin, 10)) + return false; + + portENTER_CRITICAL(&mux); + if (v) { + gpio_set_level(pin, 0); // drive output low + ets_delay_us(10); + gpio_set_level(pin, 1); // allow output high + ets_delay_us(55); + } else { + gpio_set_level(pin, 0); // drive output low + ets_delay_us(65); + gpio_set_level(pin, 1); // allow output high + } + ets_delay_us(1); + portEXIT_CRITICAL(&mux); + + return true; +} + +static int _onewire_read_bit(gpio_num_t pin) +{ + if (!_onewire_wait_for_bus(pin, 10)) + return -1; + + portENTER_CRITICAL(&mux); + gpio_set_level(pin, 0); + ets_delay_us(2); + gpio_set_level(pin, 1); // let pin float, pull up will raise + ets_delay_us(11); + int r = gpio_get_level(pin); // Must sample within 15us of start + ets_delay_us(48); + portEXIT_CRITICAL(&mux); + + return r; +} + +// Write a byte. The writing code uses open-drain mode and expects the pullup +// resistor to pull the line high when not driven low. If you need strong +// power after the write (e.g. DS18B20 in parasite power mode) then call +// onewire_power() after this is complete to actively drive the line high. +// +bool onewire_write(gpio_num_t pin, uint8_t v) +{ + for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) + if (!_onewire_write_bit(pin, (bitMask & v))) + return false; + + return true; +} + +bool onewire_write_bytes(gpio_num_t pin, const uint8_t* buf, size_t count) +{ + for (size_t i = 0; i < count; i++) + if (!onewire_write(pin, buf[i])) + return false; + + return true; +} + +// Read a byte +// +int onewire_read(gpio_num_t pin) +{ + int r = 0; + + for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { + int bit = _onewire_read_bit(pin); + if (bit < 0) + return -1; + else if (bit) + r |= bitMask; + } + return r; +} + +bool onewire_read_bytes(gpio_num_t pin, uint8_t* buf, size_t count) +{ + size_t i; + int b; + + for (i = 0; i < count; i++) { + b = onewire_read(pin); + if (b < 0) + return false; + buf[i] = b; + } + return true; +} + +bool onewire_select(gpio_num_t pin, onewire_addr_t addr) +{ + uint8_t i; + + if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) + return false; + + for (i = 0; i < 8; i++) { + if (!onewire_write(pin, addr & 0xff)) + return false; + addr >>= 8; + } + + return true; +} + +bool onewire_skip_rom(gpio_num_t pin) +{ + return onewire_write(pin, ONEWIRE_SKIP_ROM); +} + +bool onewire_power(gpio_num_t pin) +{ + // Make sure the bus is not being held low before driving it high, or we + // may end up shorting ourselves out. + if (!_onewire_wait_for_bus(pin, 10)) + return false; + + setup_pin(pin, false); + gpio_set_level(pin, 1); + + return true; +} + +void onewire_depower(gpio_num_t pin) +{ + setup_pin(pin, true); +} + +void onewire_search_start(onewire_search_t* search) +{ + // reset the search state + memset(search, 0, sizeof(*search)); +} + +void onewire_search_prefix(onewire_search_t* search, uint8_t family_code) +{ + uint8_t i; + + search->rom_no[0] = family_code; + for (i = 1; i < 8; i++) { + search->rom_no[i] = 0; + } + search->last_discrepancy = 64; + search->last_device_found = false; +} + +// Perform a search. If the next device has been successfully enumerated, its +// ROM address will be returned. If there are no devices, no further +// devices, or something horrible happens in the middle of the +// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to +// start over. +// +// --- Replaced by the one from the Dallas Semiconductor web site --- +//-------------------------------------------------------------------------- +// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing +// search state. +// Return 1 : device found, ROM number in ROM_NO buffer +// 0 : device not found, end of search +// +onewire_addr_t onewire_search_next(onewire_search_t* search, gpio_num_t pin) +{ + //TODO: add more checking for read/write errors + uint8_t id_bit_number; + uint8_t last_zero, search_result; + int rom_byte_number; + int8_t id_bit, cmp_id_bit; + onewire_addr_t addr; + unsigned char rom_byte_mask; + bool search_direction; + + // initialize for search + id_bit_number = 1; + last_zero = 0; + rom_byte_number = 0; + rom_byte_mask = 1; + search_result = 0; + + // if the last call was not the last one + if (!search->last_device_found) { + // 1-Wire reset + if (!onewire_reset(pin)) { + // reset the search + search->last_discrepancy = 0; + search->last_device_found = false; + return ONEWIRE_NONE; + } + + // issue the search command + onewire_write(pin, ONEWIRE_SEARCH); + + // loop to do the search + do { + // read a bit and its complement + id_bit = _onewire_read_bit(pin); + cmp_id_bit = _onewire_read_bit(pin); + + if ((id_bit == 1) && (cmp_id_bit == 1)) + break; + else { + // all devices coupled have 0 or 1 + if (id_bit != cmp_id_bit) + search_direction = id_bit; // bit write value for search + else { + // if this discrepancy if before the Last Discrepancy + // on a previous next then pick the same as last time + if (id_bit_number < search->last_discrepancy) + search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0); + else + // if equal to last pick 1, if not then pick 0 + search_direction = (id_bit_number == search->last_discrepancy); + + // if 0 was picked then record its position in LastZero + if (!search_direction) + last_zero = id_bit_number; + } + + // set or clear the bit in the ROM byte rom_byte_number + // with mask rom_byte_mask + if (search_direction) + search->rom_no[rom_byte_number] |= rom_byte_mask; + else + search->rom_no[rom_byte_number] &= ~rom_byte_mask; + + // serial number search direction write bit + _onewire_write_bit(pin, search_direction); + + // increment the byte counter id_bit_number + // and shift the mask rom_byte_mask + id_bit_number++; + rom_byte_mask <<= 1; + + // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask + if (rom_byte_mask == 0) { + rom_byte_number++; + rom_byte_mask = 1; + } + } + } while (rom_byte_number < 8); // loop until through all ROM bytes 0-7 + + // if the search was successful then + if (!(id_bit_number < 65)) { + // search successful so set last_discrepancy,last_device_found,search_result + search->last_discrepancy = last_zero; + + // check for last device + if (search->last_discrepancy == 0) + search->last_device_found = true; + + search_result = 1; + } + } + + // if no device found then reset counters so next 'search' will be like a first + if (!search_result || !search->rom_no[0]) { + search->last_discrepancy = 0; + search->last_device_found = false; + return ONEWIRE_NONE; + } else { + addr = 0; + for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) { + addr = (addr << 8) | search->rom_no[rom_byte_number]; + } + //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr); + } + return addr; +} + +// The 1-Wire CRC scheme is described in Maxim Application Note 27: +// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" +// + +#ifdef ONEWIRE_CRC8_TABLE +// This table comes from Dallas sample code where it is freely reusable, +// though Copyright (c) 2000 Dallas Semiconductor Corporation +static const uint8_t dscrc_table[] = { + 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, + 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, + 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, + 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, + 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, + 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, + 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, + 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, + 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, + 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, + 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, + 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, + 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, + 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, + 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, + 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 +}; + +// +// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM +// and the registers. (note: this might better be done without to +// table, it would probably be smaller and certainly fast enough +// compared to all those delayMicrosecond() calls. But I got +// confused, so I use this table from the examples.) +// +uint8_t onewire_crc8(const uint8_t* data, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) + crc = dscrc_table[crc ^ *data++]; + + return crc; +} +#else +// +// Compute a Dallas Semiconductor 8 bit CRC directly. +// this is much slower, but much smaller, than the lookup table. +// +uint8_t onewire_crc8(const uint8_t* data, uint8_t len) +{ + uint8_t crc = 0; + + while (len--) + { + uint8_t inbyte = *data++; + for (int i = 8; i; i--) + { + uint8_t mix = (crc ^ inbyte) & 0x01; + crc >>= 1; + if (mix) + crc ^= 0x8C; + inbyte >>= 1; + } + } + return crc; +} +#endif /* ONEWIRE_CRC8_TABLE */ + +// Compute the 1-Wire CRC16 and compare it against the received CRC. +// Example usage (reading a DS2408): +// // Put everything in a buffer so we can compute the CRC easily. +// uint8_t buf[13]; +// buf[0] = 0xF0; // Read PIO Registers +// buf[1] = 0x88; // LSB address +// buf[2] = 0x00; // MSB address +// WriteBytes(net, buf, 3); // Write 3 cmd bytes +// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 +// if (!CheckCRC16(buf, 11, &buf[11])) { +// // Handle error. +// } +// +// @param input - Array of bytes to checksum. +// @param len - How many bytes to use. +// @param inverted_crc - The two CRC16 bytes in the received data. +// This should just point into the received data, +// *not* at a 16-bit integer. +// @param crc - The crc starting value (optional) +// @return 1, iff the CRC matches. +bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) +{ + uint16_t crc = ~onewire_crc16(input, len, crc_iv); + return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; +} + +// Compute a Dallas Semiconductor 16 bit CRC. This is required to check +// the integrity of data received from many 1-Wire devices. Note that the +// CRC computed here is *not* what you'll get from the 1-Wire network, +// for two reasons: +// 1) The CRC is transmitted bitwise inverted. +// 2) Depending on the endian-ness of your processor, the binary +// representation of the two-byte return value may have a different +// byte order than the two bytes you get from 1-Wire. +// @param input - Array of bytes to checksum. +// @param len - How many bytes to use. +// @param crc - The crc starting value (optional) +// @return The CRC16, as defined by Dallas Semiconductor. +uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) +{ + uint16_t crc = crc_iv; + static const uint8_t oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; + + uint16_t i; + for (i = 0; i < len; i++) { + // Even though we're just copying a byte from the input, + // we'll be doing 16-bit computation with it. + uint16_t cdata = input[i]; + cdata = (cdata ^ crc) & 0xff; + crc >>= 8; + + if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) + crc ^= 0xC001; + + cdata <<= 6; + crc ^= cdata; + cdata <<= 1; + crc ^= cdata; + } + return crc; +} +// === Fim de: components/peripherals/src/onewire.c === + + +// === Início de: components/peripherals/src/onewire.h === +/* + * The MIT License (MIT) + * + * Copyright (c) 2014 zeroday nodemcu.com + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * ------------------------------------------------------------------------------- + * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the + * following additional terms: + * + * Except as contained in this notice, the name of Dallas Semiconductor + * shall not be used except as stated in the Dallas Semiconductor + * Branding Policy. + */ + +#ifndef ONEWIRE_H_ +#define ONEWIRE_H_ + +#include +#include +#include "driver/gpio.h" + +/** + * Type used to hold all 1-Wire device ROM addresses (64-bit) + */ +typedef uint64_t onewire_addr_t; + +/** + * Structure to contain the current state for onewire_search_next(), etc + */ +typedef struct +{ + uint8_t rom_no[8]; + uint8_t last_discrepancy; + bool last_device_found; +} onewire_search_t; + +/** + * ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device + * (CRC mismatch), and so can be useful as an indicator for "no-such-device", + * etc. + */ +#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) + +/** + * @brief Perform a 1-Wire reset cycle. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @return `true` if at least one device responds with a presence pulse, + * `false` if no devices were detected (or the bus is shorted, etc) + */ +bool onewire_reset(gpio_num_t pin); + +/** + * @brief Issue a 1-Wire "ROM select" command to select a particular device. + * + * It is necessary to call ::onewire_reset() before calling this function. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param addr The ROM address of the device to select + * + * @return `true` if the "ROM select" command could be successfully issued, + * `false` if there was an error. + */ +bool onewire_select(gpio_num_t pin, const onewire_addr_t addr); + +/** + * @brief Issue a 1-Wire "skip ROM" command to select *all* devices on the bus. + * + * It is necessary to call ::onewire_reset() before calling this function. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @return `true` if the "skip ROM" command could be successfully issued, + * `false` if there was an error. + */ +bool onewire_skip_rom(gpio_num_t pin); + +/** + * @brief Write a byte on the onewire bus. + * + * The writing code uses open-drain mode and expects the pullup resistor to + * pull the line high when not driven low. If you need strong power after the + * write (e.g. DS18B20 in parasite power mode) then call ::onewire_power() + * after this is complete to actively drive the line high. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param v The byte value to write + * + * @return `true` if successful, `false` on error. + */ +bool onewire_write(gpio_num_t pin, uint8_t v); + +/** + * @brief Write multiple bytes on the 1-Wire bus. + * + * See ::onewire_write() for more info. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param buf A pointer to the buffer of bytes to be written + * @param count Number of bytes to write + * + * @return `true` if all bytes written successfully, `false` on error. + */ +bool onewire_write_bytes(gpio_num_t pin, const uint8_t *buf, size_t count); + +/** + * @brief Read a byte from a 1-Wire device. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @return the read byte on success, negative value on error. + */ +int onewire_read(gpio_num_t pin); + +/** + * @brief Read multiple bytes from a 1-Wire device. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * @param[out] buf A pointer to the buffer to contain the read bytes + * @param count Number of bytes to read + * + * @return `true` on success, `false` on error. + */ +bool onewire_read_bytes(gpio_num_t pin, uint8_t *buf, size_t count); + +/** + * @brief Actively drive the bus high to provide extra power for certain + * operations of parasitically-powered devices. + * + * For parasitically-powered devices which need more power than can be + * provided via the normal pull-up resistor, it may be necessary for some + * operations to drive the bus actively high. This function can be used to + * perform that operation. + * + * The bus can be depowered once it is no longer needed by calling + * ::onewire_depower(), or it will be depowered automatically the next time + * ::onewire_reset() is called to start another command. + * + * @note Make sure the device(s) you are powering will not pull more current + * than the ESP32/ESP8266 is able to supply via its GPIO pins (this is + * especially important when multiple devices are on the same bus and + * they are all performing a power-intensive operation at the same time + * (i.e. multiple DS18B20 sensors, which have all been given a + * "convert T" operation by using ::onewire_skip_rom())). + * + * @note This routine will check to make sure that the bus is already high + * before driving it, to make sure it doesn't attempt to drive it high + * while something else is pulling it low (which could cause a reset or + * damage the ESP32/ESP8266). + * + * @param pin The GPIO pin connected to the 1-Wire bus. + * + * @return `true` on success, `false` on error. + */ +bool onewire_power(gpio_num_t pin); + +/** + * @brief Stop forcing power onto the bus. + * + * You only need to do this if you previously called ::onewire_power() to drive + * the bus high and now want to allow it to float instead. Note that + * onewire_reset() will also automatically depower the bus first, so you do + * not need to call this first if you just want to start a new operation. + * + * @param pin The GPIO pin connected to the 1-Wire bus. + */ +void onewire_depower(gpio_num_t pin); + +/** + * @brief Clear the search state so that it will start from the beginning on + * the next call to ::onewire_search_next(). + * + * @param[out] search The onewire_search_t structure to reset. + */ +void onewire_search_start(onewire_search_t *search); + +/** + * @brief Setup the search to search for devices with the specified + * "family code". + * + * @param[out] search The onewire_search_t structure to update. + * @param family_code The "family code" to search for. + */ +void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); + +/** + * @brief Search for the next device on the bus. + * + * The order of returned device addresses is deterministic. You will always + * get the same devices in the same order. + * + * @note It might be a good idea to check the CRC to make sure you didn't get + * garbage. + * + * @return the address of the next device on the bus, or ::ONEWIRE_NONE if + * there is no next address. ::ONEWIRE_NONE might also mean that + * the bus is shorted, there are no devices, or you have already + * retrieved all of them. + */ +onewire_addr_t onewire_search_next(onewire_search_t *search, gpio_num_t pin); + +/** + * @brief Compute a Dallas Semiconductor 8 bit CRC. + * + * These are used in the ROM address and scratchpad registers to verify the + * transmitted data is correct. + */ +uint8_t onewire_crc8(const uint8_t *data, uint8_t len); + +/** + * @brief Compute the 1-Wire CRC16 and compare it against the received CRC. + * + * Example usage (reading a DS2408): + * @code{.c} + * // Put everything in a buffer so we can compute the CRC easily. + * uint8_t buf[13]; + * buf[0] = 0xF0; // Read PIO Registers + * buf[1] = 0x88; // LSB address + * buf[2] = 0x00; // MSB address + * onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes + * onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 + * if (!onewire_check_crc16(buf, 11, &buf[11])) { + * // TODO: Handle error. + * } + * @endcode + * + * @param input Array of bytes to checksum. + * @param len Number of bytes in `input` + * @param inverted_crc The two CRC16 bytes in the received data. + * This should just point into the received data, + * *not* at a 16-bit integer. + * @param crc_iv The crc starting value (optional) + * + * @return `true` if the CRC matches, `false` otherwise. + */ +bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); + +/** + * @brief Compute a Dallas Semiconductor 16 bit CRC. + * + * This is required to check the integrity of data received from many 1-Wire + * devices. Note that the CRC computed here is *not* what you'll get from the + * 1-Wire network, for two reasons: + * + * 1. The CRC is transmitted bitwise inverted. + * 2. Depending on the endian-ness of your processor, the binary + * representation of the two-byte return value may have a different + * byte order than the two bytes you get from 1-Wire. + * + * @param input Array of bytes to checksum. + * @param len How many bytes are in `input`. + * @param crc_iv The crc starting value (optional) + * + * @return the CRC16, as defined by Dallas Semiconductor. + */ +uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); + + +#endif /* ONEWIRE_H_ */ +// === Fim de: components/peripherals/src/onewire.h === + + +// === Início de: components/peripherals/src/ds18x20.c === +/* + * Copyright (c) 2016 Grzegorz Hetman + * Copyright (c) 2016 Alex Stewart + * Copyright (c) 2018 Ruslan V. Uss + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of itscontributors + * may be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + + +#include +#include +#include +#include +#include "ds18x20.h" + +#define ds18x20_WRITE_SCRATCHPAD 0x4E +#define ds18x20_READ_SCRATCHPAD 0xBE +#define ds18x20_COPY_SCRATCHPAD 0x48 +#define ds18x20_READ_EEPROM 0xB8 +#define ds18x20_READ_PWRSUPPLY 0xB4 +#define ds18x20_SEARCHROM 0xF0 +#define ds18x20_SKIP_ROM 0xCC +#define ds18x20_READROM 0x33 +#define ds18x20_MATCHROM 0x55 +#define ds18x20_ALARMSEARCH 0xEC +#define ds18x20_CONVERT_T 0x44 + +#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) +#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) + +static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; + +static const char* TAG = "ds18x20"; + +esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) +{ + if (!onewire_reset(pin)) + return ESP_ERR_INVALID_RESPONSE; + + if (addr == DS18X20_ANY) + onewire_skip_rom(pin); + else + onewire_select(pin, addr); + + portENTER_CRITICAL(&mux); + onewire_write(pin, ds18x20_CONVERT_T); + // For parasitic devices, power must be applied within 10us after issuing + // the convert command. + onewire_power(pin); + portEXIT_CRITICAL(&mux); + + if (wait){ + vTaskDelay(pdMS_TO_TICKS(750)); + onewire_depower(pin); + } + + return ESP_OK; +} + +esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) +{ + CHECK_ARG(buffer); + + uint8_t crc; + uint8_t expected_crc; + + if (!onewire_reset(pin)) + return ESP_ERR_INVALID_RESPONSE; + + if (addr == DS18X20_ANY) + onewire_skip_rom(pin); + else + onewire_select(pin, addr); + onewire_write(pin, ds18x20_READ_SCRATCHPAD); + + for (int i = 0; i < 8; i++) + buffer[i] = onewire_read(pin); + crc = onewire_read(pin); + + expected_crc = onewire_crc8(buffer, 8); + if (crc != expected_crc) + { + ESP_LOGE(TAG, "CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)", buffer[0], buffer[1], + buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); + return ESP_ERR_INVALID_CRC; + } + + return ESP_OK; +} + +esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) +{ + CHECK_ARG(buffer); + + if (!onewire_reset(pin)) + return ESP_ERR_INVALID_RESPONSE; + + if (addr == DS18X20_ANY) + onewire_skip_rom(pin); + else + onewire_select(pin, addr); + onewire_write(pin, ds18x20_WRITE_SCRATCHPAD); + + for (int i = 0; i < 3; i++) + onewire_write(pin, buffer[i]); + + return ESP_OK; +} + +esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) +{ + if (!onewire_reset(pin)) + return ESP_ERR_INVALID_RESPONSE; + + if (addr == DS18X20_ANY) + onewire_skip_rom(pin); + else + onewire_select(pin, addr); + + portENTER_CRITICAL(&mux); + onewire_write(pin, ds18x20_COPY_SCRATCHPAD); + // For parasitic devices, power must be applied within 10us after issuing + // the convert command. + onewire_power(pin); + portEXIT_CRITICAL(&mux); + + // And then it needs to keep that power up for 10ms. + vTaskDelay(pdMS_TO_TICKS(10)); + onewire_depower(pin); + + return ESP_OK; +} + +esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + CHECK_ARG(temperature); + + uint8_t scratchpad[8]; + int16_t temp; + + CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); + + temp = scratchpad[1] << 8 | scratchpad[0]; + + *temperature = ((int16_t)temp * 625.0) / 100; + + return ESP_OK; +} + +esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + CHECK_ARG(temperature); + + uint8_t scratchpad[8]; + int16_t temp; + + CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); + + temp = scratchpad[1] << 8 | scratchpad[0]; + temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; + + *temperature = (temp * 625) / 100 - 25; + + return ESP_OK; +} + +esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + if ((uint8_t)addr == DS18B20_FAMILY_ID) { + return ds18b20_read_temperature(pin, addr, temperature); + } else { + return ds18s20_read_temperature(pin, addr, temperature); + } +} + +esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + CHECK_ARG(temperature); + + CHECK(ds18x20_measure(pin, addr, true)); + return ds18b20_read_temperature(pin, addr, temperature); +} + +esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + CHECK_ARG(temperature); + + CHECK(ds18x20_measure(pin, addr, true)); + return ds18s20_read_temperature(pin, addr, temperature); +} + +esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) +{ + CHECK_ARG(temperature); + + CHECK(ds18x20_measure(pin, addr, true)); + return ds18x20_read_temperature(pin, addr, temperature); +} + +esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) +{ + CHECK_ARG(result_list && addr_count); + + CHECK(ds18x20_measure(pin, DS18X20_ANY, true)); + + return ds18x20_read_temp_multi(pin, addr_list, addr_count, result_list); +} + +esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, size_t* found) +{ + CHECK_ARG(addr_list && addr_count); + + onewire_search_t search; + onewire_addr_t addr; + + *found = 0; + onewire_search_start(&search); + while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) + { + uint8_t family_id = (uint8_t)addr; + if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) + { + if (*found < addr_count) + addr_list[*found] = addr; + *found += 1; + } + } + + return ESP_OK; +} + +esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) +{ + CHECK_ARG(result_list); + + esp_err_t res = ESP_OK; + for (size_t i = 0; i < addr_count; i++) + { + esp_err_t tmp = ds18x20_read_temperature(pin, addr_list[i], &result_list[i]); + if (tmp != ESP_OK) + res = tmp; + } + return res; +} + +// === Fim de: components/peripherals/src/ds18x20.c === + + +// === Início de: components/peripherals/src/led.c === +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "esp_log.h" +#include "driver/gpio.h" +#include "led.h" +#include "board_config.h" +#include "evse_error.h" +#include "evse_api.h" + +#define LED_UPDATE_INTERVAL_MS 100 +#define BLOCK_TIME pdMS_TO_TICKS(10) + +static const char *TAG = "led"; + +typedef struct { + gpio_num_t gpio; + bool on : 1; + uint16_t ontime; + uint16_t offtime; + TimerHandle_t timer; + led_pattern_t pattern; + uint8_t blink_count; +} led_t; + +static led_t leds[LED_ID_MAX] = {0}; +static TimerHandle_t led_update_timer = NULL; +static evse_state_t led_state = -1; + +// ---------------------------- +// Funções Internas +// ---------------------------- + +static void led_update_timer_callback(TimerHandle_t xTimer); +static void led_update(void); +static void led_apply_by_state(evse_state_t state); + +static inline void led_gpio_write(gpio_num_t gpio, bool level) { + if (gpio != GPIO_NUM_NC) + gpio_set_level(gpio, level); +} + +static void led_timer_callback(TimerHandle_t xTimer) +{ + led_t *led = (led_t *)pvTimerGetTimerID(xTimer); + led->on = !led->on; + led_gpio_write(led->gpio, led->on); + uint32_t next_time = led->on ? led->ontime : led->offtime; + + xTimerChangePeriod(led->timer, pdMS_TO_TICKS(next_time), BLOCK_TIME); +} + +// ---------------------------- +// Inicialização +// ---------------------------- + +void led_init(void) +{ + gpio_config_t io_conf = { + .mode = GPIO_MODE_OUTPUT, + .intr_type = GPIO_INTR_DISABLE, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_ENABLE, + .pin_bit_mask = 0 + }; + + for (int i = 0; i < LED_ID_MAX; i++) { + leds[i].gpio = GPIO_NUM_NC; + } + + if (board_config.led_stop) { + leds[LED_ID_STOP].gpio = board_config.led_stop_gpio; + io_conf.pin_bit_mask |= BIT64(board_config.led_stop_gpio); + } + + if (board_config.led_charging) { + leds[LED_ID_CHARGING].gpio = board_config.led_charging_gpio; + io_conf.pin_bit_mask |= BIT64(board_config.led_charging_gpio); + } + + if (board_config.led_error) { + leds[LED_ID_ERROR].gpio = board_config.led_error_gpio; + io_conf.pin_bit_mask |= BIT64(board_config.led_error_gpio); + } + + if (io_conf.pin_bit_mask != 0) { + ESP_ERROR_CHECK(gpio_config(&io_conf)); + } + + if (!led_update_timer) { + led_update_timer = xTimerCreate("led_update_timer", + pdMS_TO_TICKS(LED_UPDATE_INTERVAL_MS), + pdTRUE, NULL, + led_update_timer_callback); + if (led_update_timer) { + xTimerStart(led_update_timer, BLOCK_TIME); + } else { + ESP_LOGE(TAG, "Failed to create LED update timer"); + } + } +} + +// ---------------------------- +// API Pública +// ---------------------------- + +void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime) +{ + if (led_id >= LED_ID_MAX) return; + + led_t *led = &leds[led_id]; + if (led->gpio == GPIO_NUM_NC) return; + + // Evita reconfiguração idêntica + if (led->ontime == ontime && led->offtime == offtime) + return; + + if (led->timer) { + xTimerStop(led->timer, BLOCK_TIME); + } + + led->ontime = ontime; + led->offtime = offtime; + + if (ontime == 0) { + led->on = false; + led_gpio_write(led->gpio, 0); + } else if (offtime == 0) { + led->on = true; + led_gpio_write(led->gpio, 1); + } else { + led->on = true; + led_gpio_write(led->gpio, 1); + + if (!led->timer) { + led->timer = xTimerCreate("led_timer", pdMS_TO_TICKS(ontime), + pdFALSE, (void *)led, led_timer_callback); + } + + if (led->timer) { + xTimerStart(led->timer, BLOCK_TIME); + } + } +} + +void led_apply_pattern(led_id_t id, led_pattern_t pattern) +{ + if (id >= LED_ID_MAX) return; + + led_t *led = &leds[id]; + if (led->gpio == GPIO_NUM_NC) return; + + if (led->pattern == pattern) return; + + if (led->timer) { + xTimerStop(led->timer, BLOCK_TIME); + } + + led->pattern = pattern; + led->blink_count = 0; + + switch (pattern) { + case LED_PATTERN_OFF: + led_set_state(id, 0, 0); + break; + case LED_PATTERN_ON: + led_set_state(id, 1, 0); + break; + case LED_PATTERN_BLINK: + led_set_state(id, 500, 500); + break; + case LED_PATTERN_BLINK_FAST: + led_set_state(id, 200, 200); + break; + case LED_PATTERN_BLINK_SLOW: + led_set_state(id, 300, 1700); + break; + case LED_PATTERN_CHARGING_EFFECT: + led_set_state(id, 2000, 1000); + break; + } +} + +// ---------------------------- +// Controle por Estado +// ---------------------------- + +static void led_apply_by_state(evse_state_t state) +{ + // Reset todos + led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); + led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); + led_apply_pattern(LED_ID_ERROR, LED_PATTERN_OFF); + + switch (state) { + case EVSE_STATE_A: + led_apply_pattern(LED_ID_STOP, LED_PATTERN_ON); + break; + case EVSE_STATE_B1: + case EVSE_STATE_B2: + case EVSE_STATE_C1: + led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_ON); + break; + case EVSE_STATE_C2: + led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_CHARGING_EFFECT); + break; + case EVSE_STATE_D1: + case EVSE_STATE_D2: + led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_BLINK_FAST); + break; + case EVSE_STATE_E: + case EVSE_STATE_F: + led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); + break; + default: + break; + } +} + +// ---------------------------- +// Timer Update +// ---------------------------- + +static void led_update(void) +{ + if (evse_error_is_active()) { + led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); + led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); + led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); + return; + } + + evse_state_t current = evse_get_state(); + + if (current != led_state) { + led_state = current; + led_apply_by_state(current); + } +} + +static void led_update_timer_callback(TimerHandle_t xTimer) +{ + (void)xTimer; + led_update(); +} + +// === Fim de: components/peripherals/src/led.c === + + +// === Início de: components/peripherals/src/rcm.c === +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "driver/gpio.h" +#include "esp_log.h" + +#include "rcm.h" +#include "board_config.h" +#include "evse_api.h" + +// static bool do_test = false; + +// static bool triggered = false; + +// static bool test_triggered = false; + +// static void IRAM_ATTR rcm_isr_handler(void* arg) +// { +// if (!do_test) { +// triggered = true; +// } else { +// test_triggered = true; +// } +// } + +void rcm_init(void) +{ + if (board_config.rcm) { + gpio_config_t io_conf = {}; + + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = BIT64(board_config.rcm_test_gpio); + ESP_ERROR_CHECK(gpio_config(&io_conf)); + + io_conf.mode = GPIO_MODE_INPUT; + // io_conf.intr_type = GPIO_INTR_POSEDGE; + io_conf.pin_bit_mask = BIT64(board_config.rcm_gpio); + ESP_ERROR_CHECK(gpio_config(&io_conf)); + //ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.rcm_gpio, rcm_isr_handler, NULL)); + } +} + +bool rcm_test(void) +{ + // do_test = true; + // test_triggered = false; + + // gpio_set_level(board_config.rcm_test_gpio, 1); + // vTaskDelay(pdMS_TO_TICKS(100)); + // gpio_set_level(board_config.rcm_test_gpio, 0); + + // do_test = false; + + // return test_triggered; + + gpio_set_level(board_config.rcm_test_gpio, 1); + vTaskDelay(pdMS_TO_TICKS(100)); + bool success = gpio_get_level(board_config.rcm_gpio) == 1; + gpio_set_level(board_config.rcm_test_gpio, 0); + + return success; +} + +bool rcm_is_triggered(void) +{ + // bool _triggered = triggered; + // if (gpio_get_level(board_config.rcm_gpio) == 0) { + // triggered = false; + // } + // return _triggered; + if (gpio_get_level(board_config.rcm_gpio) == 1) { + vTaskDelay(pdMS_TO_TICKS(1)); + return gpio_get_level(board_config.rcm_gpio) == 1; + } + + return false; +} +// === Fim de: components/peripherals/src/rcm.c === + + +// === Início de: components/peripherals/src/adc.c === +#include "adc.h" +#include "esp_log.h" + +const static char* TAG = "adc"; + +adc_oneshot_unit_handle_t adc_handle; + +adc_cali_handle_t adc_cali_handle; + +void adc_init(void) +{ + adc_oneshot_unit_init_cfg_t conf = { + .unit_id = ADC_UNIT_1 + }; + ESP_ERROR_CHECK(adc_oneshot_new_unit(&conf, &adc_handle)); + + bool calibrated = false; + +#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED + if (!calibrated) { + ESP_LOGI(TAG, "Calibration scheme version is %s", "Curve Fitting"); + adc_cali_curve_fitting_config_t cali_config = { + .unit_id = ADC_UNIT_1, + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, + }; + if (adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { + calibrated = true; + } + } +#endif + +#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED + if (!calibrated) { + ESP_LOGI(TAG, "Calibration scheme version is %s", "Line Fitting"); + adc_cali_line_fitting_config_t cali_config = { + .unit_id = ADC_UNIT_1, + .atten = ADC_ATTEN_DB_12, + .bitwidth = ADC_BITWIDTH_DEFAULT, +#if CONFIG_IDF_TARGET_ESP32 + .default_vref = 1100 +#endif + }; + if (adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { + calibrated = true; + } + } +#endif + + if (!calibrated) { + ESP_LOGE(TAG, "No calibration scheme"); + ESP_ERROR_CHECK(ESP_FAIL); + } +} +// === Fim de: components/peripherals/src/adc.c === + + +// === Início de: components/peripherals/src/adc121s021_dma.c === +#include "driver/spi_master.h" +#include "esp_log.h" +#include "adc121s021_dma.h" +#include "spi_bus_manager.h" + +#define TAG "adc_dma" + +#define PIN_NUM_CS 5 +#define SAMPLE_SIZE_BYTES 2 +#define ADC_BITS 12 +#define SPI_CLOCK_HZ (6 * 1000 * 1000) // 6 MHz + +static spi_device_handle_t adc_spi = NULL; + +void adc121s021_dma_init(void) +{ + if (adc_spi) { + ESP_LOGW(TAG, "ADC121S021 já foi inicializado."); + return; + } + + if (!spi_bus_manager_is_initialized()) { + ESP_LOGI(TAG, "SPI bus não inicializado. Inicializando..."); + esp_err_t err = spi_bus_manager_init(); // 🔧 CORRIGIDO: sem argumentos + if (err != ESP_OK) { + ESP_LOGE(TAG, "Falha ao inicializar o SPI bus: %s", esp_err_to_name(err)); + return; + } + } + + spi_device_interface_config_t devcfg = { + .clock_speed_hz = SPI_CLOCK_HZ, + .mode = 0, + .spics_io_num = PIN_NUM_CS, + .queue_size = 2, + .flags = SPI_DEVICE_NO_DUMMY, + .pre_cb = NULL, + .post_cb = NULL, + }; + + esp_err_t err = spi_bus_add_device(spi_bus_manager_get_host(), &devcfg, &adc_spi); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Falha ao registrar ADC121S021 no SPI: %s", esp_err_to_name(err)); + return; + } + + ESP_LOGI(TAG, "ADC121S021 registrado no SPI com sucesso."); +} + +bool adc121s021_dma_get_sample(uint16_t *sample) +{ + if (!adc_spi) { + ESP_LOGE(TAG, "ADC SPI não inicializado!"); + return false; + } + + uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy + uint8_t rx_buffer[2] = {0}; + + spi_transaction_t t = { + .length = 16, + .tx_buffer = tx_buffer, + .rx_buffer = rx_buffer, + .flags = 0 + }; + + esp_err_t err = spi_device_transmit(adc_spi, &t); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro na transmissão SPI: %s", esp_err_to_name(err)); + return false; + } + + *sample = ((rx_buffer[0] << 8) | rx_buffer[1]) & 0x0FFF; + return true; +} + +// === Fim de: components/peripherals/src/adc121s021_dma.c === + + +// === Início de: components/peripherals/src/peripherals.c === +#include "peripherals.h" +#include "adc.h" +#include "led.h" +#include "buzzer.h" +#include "proximity.h" +#include "ac_relay.h" +#include "socket_lock.h" +#include "rcm.h" +#include "aux_io.h" +#include "ntc_sensor.h" + +void peripherals_init(void) +{ + ac_relay_init(); + led_init(); + buzzer_init(); + adc_init(); + proximity_init(); + // socket_lock_init(); + // rcm_init(); + //energy_meter_init(); + // aux_init(); + ntc_sensor_init(); +} +// === Fim de: components/peripherals/src/peripherals.c === + + +// === Início de: components/peripherals/include/adc121s021_dma.h === +#ifndef ADC_DMA_H_ +#define ADC_DMA_H_ + + +#include +#include + +void adc121s021_dma_init(void); +bool adc121s021_dma_get_sample(uint16_t *sample); + + +#endif /* ADC_DMA_h_ */ + +// === Fim de: components/peripherals/include/adc121s021_dma.h === + + +// === Início de: components/peripherals/include/peripherals.h === +#ifndef PERIPHERALS_H +#define PERIPHERALS_H + +void peripherals_init(void); + +#endif /* PERIPHERALS_H */ + +// === Fim de: components/peripherals/include/peripherals.h === diff --git a/projeto_parte10.c b/projeto_parte10.c deleted file mode 100644 index 24d015a..0000000 --- a/projeto_parte10.c +++ /dev/null @@ -1,46 +0,0 @@ - - -// === Início de: components/peripherals/include/temp_sensor.h === -#ifndef TEMP_SENSOR_H_ -#define TEMP_SENSOR_H_ - -#include -#include "esp_err.h" - -/** - * @brief Initialize DS18S20 temperature sensor bus - * - */ -void temp_sensor_init(void); - -/** - * @brief Get found sensor count - * - * @return uint8_t - */ -uint8_t temp_sensor_get_count(void); - -/** - * @brief Return lowest temperature after temp_sensor_measure - * - * @return int16_t - */ -int16_t temp_sensor_get_low(void); - -/** - * @brief Return highest temperature after temp_sensor_measure - * - * @return int - */ -int temp_sensor_get_high(void); - -/** - * @brief Return temperature sensor error - * - * @return bool - */ -bool temp_sensor_is_error(void); - -#endif /* TEMP_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/temp_sensor.h === diff --git a/projeto_parte2.c b/projeto_parte2.c old mode 100755 new mode 100644 index 5dc732c..9c98ba1 --- a/projeto_parte2.c +++ b/projeto_parte2.c @@ -1,1166 +1,2714 @@ -// === Início de: components/evse/evse_limits.c === -#include "evse_state.h" -#include "evse_api.h" -#include "evse_limits.h" -#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 - -// ======================== -// Concurrency protection -// ======================== - -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; - -// ======================== -// Limit status flag -// ======================== - -bool evse_get_limit_reached(void) { - bool val; - portENTER_CRITICAL(&evse_mux); - val = limit_reached; - portEXIT_CRITICAL(&evse_mux); - return val; -} - -void evse_set_limit_reached(bool v) { - portENTER_CRITICAL(&evse_mux); - limit_reached = v; - portEXIT_CRITICAL(&evse_mux); -} - -// ======================== -// Runtime limit accessors -// ======================== - -uint32_t evse_get_consumption_limit(void) { - uint32_t val; - portENTER_CRITICAL(&evse_mux); - val = consumption_limit; - portEXIT_CRITICAL(&evse_mux); - return val; -} - -void evse_set_consumption_limit(uint32_t value) { - portENTER_CRITICAL(&evse_mux); - consumption_limit = value; - portEXIT_CRITICAL(&evse_mux); -} - -uint32_t evse_get_charging_time_limit(void) { - uint32_t val; - portENTER_CRITICAL(&evse_mux); - val = charging_time_limit; - portEXIT_CRITICAL(&evse_mux); - return val; -} - -void evse_set_charging_time_limit(uint32_t value) { - portENTER_CRITICAL(&evse_mux); - charging_time_limit = value; - portEXIT_CRITICAL(&evse_mux); -} - -uint16_t evse_get_under_power_limit(void) { - uint16_t val; - portENTER_CRITICAL(&evse_mux); - val = under_power_limit; - portEXIT_CRITICAL(&evse_mux); - return val; -} - -void evse_set_under_power_limit(uint16_t value) { - portENTER_CRITICAL(&evse_mux); - under_power_limit = value; - portEXIT_CRITICAL(&evse_mux); -} - -// ======================== -// Default (persistent) limit accessors -// These values can be stored/restored via NVS -// ======================== - -uint32_t evse_get_default_consumption_limit(void) { - return default_consumption_limit; -} - -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(); -} - - -// ======================== -// 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) { - evse_state_t state = evse_get_state(); - if (!evse_state_is_charging(state)) return; - - bool reached = false; - - uint32_t energy = evse_get_total_energy(); - uint32_t power = evse_get_instant_power(); - TickType_t now = xTaskGetTickCount(); - TickType_t start = evse_get_session_start(); - - if (consumption_limit > 0 && energy >= consumption_limit) { - ESP_LOGW("EVSE", "Energy limit reached"); - reached = true; - } - - if (charging_time_limit > 0 && - (now - start) >= pdMS_TO_TICKS(charging_time_limit * 1000)) { - ESP_LOGW("EVSE", "Charging time limit reached"); - reached = true; - } - - if (under_power_limit > 0 && power < under_power_limit) { - ESP_LOGW("EVSE", "Under power limit reached"); - reached = true; - } - - if (reached) { - evse_set_limit_reached(true); - } -} - -// === Fim de: components/evse/evse_limits.c === - - -// === Início de: components/evse/evse_config.c === -#include // For PRI macros -#include "evse_config.h" -#include "board_config.h" -#include "evse_limits.h" -#include "esp_log.h" -#include "nvs.h" - -static const char *TAG = "evse_config"; - -static nvs_handle_t nvs; - -// ======================== -// Configurable parameters -// ======================== -static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT; -static uint16_t charging_current; // Persisted (NVS) -static uint16_t charging_current_runtime = 0; // Runtime only -static bool socket_outlet; -static bool rcm; -static uint8_t temp_threshold = 60; -static bool require_auth; - -// ======================== -// Initialization -// ======================== -esp_err_t evse_config_init(void) { - ESP_LOGD(TAG, "Initializing NVS configuration..."); - return nvs_open("evse", NVS_READWRITE, &nvs); -} - -void evse_check_defaults(void) { - esp_err_t err; - uint8_t u8; - uint16_t u16; - uint32_t u32; - bool needs_commit = false; - - ESP_LOGD(TAG, "Checking default parameters..."); - - // Max charging current - err = nvs_get_u8(nvs, "max_chrg_curr", &u8); - if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) { - max_charging_current = MAX_CHARGING_CURRENT_LIMIT; - nvs_set_u8(nvs, "max_chrg_curr", max_charging_current); - needs_commit = true; - ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current); - } else { - max_charging_current = u8; - } - - // Charging current (default, persisted) - err = nvs_get_u16(nvs, "def_chrg_curr", &u16); - if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) { - charging_current = max_charging_current * 10; - nvs_set_u16(nvs, "def_chrg_curr", charging_current); - needs_commit = true; - ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current); - } else { - charging_current = u16; - } - - // Runtime charging current initialized from persisted default - charging_current_runtime = charging_current; - ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime); - - // Auth required - err = nvs_get_u8(nvs, "require_auth", &u8); - require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false; - if (err != ESP_OK) { - nvs_set_u8(nvs, "require_auth", require_auth); - needs_commit = true; - } - - // Socket outlet - err = nvs_get_u8(nvs, "socket_outlet", &u8); - socket_outlet = (err == ESP_OK && u8) && board_config.proximity; - if (err != ESP_OK) { - nvs_set_u8(nvs, "socket_outlet", socket_outlet); - needs_commit = true; - } - - // RCM - err = nvs_get_u8(nvs, "rcm", &u8); - rcm = (err == ESP_OK && u8) && board_config.rcm; - if (err != ESP_OK) { - nvs_set_u8(nvs, "rcm", rcm); - needs_commit = true; - } - - // Temp threshold - err = nvs_get_u8(nvs, "temp_threshold", &u8); - temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60; - if (err != ESP_OK) { - nvs_set_u8(nvs, "temp_threshold", temp_threshold); - needs_commit = true; - } - - // Optional limits - if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) - evse_set_consumption_limit(u32); - - if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) - evse_set_charging_time_limit(u32); - - if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) - evse_set_under_power_limit(u16); - - // Save to NVS if needed - if (needs_commit) { - err = nvs_commit(nvs); - if (err == ESP_OK) { - ESP_LOGD(TAG, "Configuration committed to NVS."); - } else { - ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err)); - } - } -} - -// ======================== -// Charging current getters/setters -// ======================== -uint8_t evse_get_max_charging_current(void) { - return max_charging_current; -} - -esp_err_t evse_set_max_charging_current(uint8_t value) { - if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT) - return ESP_ERR_INVALID_ARG; - max_charging_current = value; - nvs_set_u8(nvs, "max_chrg_curr", value); - return nvs_commit(nvs); -} - -uint16_t evse_get_charging_current(void) { - return charging_current; -} - -esp_err_t evse_set_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) - return ESP_ERR_INVALID_ARG; - charging_current = value; - nvs_set_u16(nvs, "def_chrg_curr", value); - return nvs_commit(nvs); -} - -uint16_t evse_get_default_charging_current(void) { - uint16_t value; - if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK) - return value; - return charging_current; -} - -esp_err_t evse_set_default_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) - return ESP_ERR_INVALID_ARG; - nvs_set_u16(nvs, "def_chrg_curr", value); - return nvs_commit(nvs); -} - -// ======================== -// Runtime current (not saved) -// ======================== -void evse_set_runtime_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current)) { - ESP_LOGW(TAG, "Rejected runtime charging current (out of bounds): %d", value); - return; - } - charging_current_runtime = value; - ESP_LOGD(TAG, "Runtime charging current updated: %d", charging_current_runtime); -} - -uint16_t evse_get_runtime_charging_current(void) { - return charging_current_runtime; -} - - -// ======================== -// Socket outlet -// ======================== -bool evse_get_socket_outlet(void) { - return socket_outlet; -} - -esp_err_t evse_set_socket_outlet(bool value) { - if (value && !board_config.proximity) - return ESP_ERR_INVALID_ARG; - socket_outlet = value; - nvs_set_u8(nvs, "socket_outlet", value); - return nvs_commit(nvs); -} - -// ======================== -// RCM -// ======================== -bool evse_is_rcm(void) { - return rcm; -} - -esp_err_t evse_set_rcm(bool value) { - if (value && !board_config.rcm) - return ESP_ERR_INVALID_ARG; - rcm = value; - nvs_set_u8(nvs, "rcm", value); - return nvs_commit(nvs); -} - -// ======================== -// Temperature -// ======================== -uint8_t evse_get_temp_threshold(void) { - return temp_threshold; -} - -esp_err_t evse_set_temp_threshold(uint8_t value) { - if (value < 40 || value > 80) - return ESP_ERR_INVALID_ARG; - temp_threshold = value; - nvs_set_u8(nvs, "temp_threshold", value); - return nvs_commit(nvs); -} - -// ======================== -// Authentication -// ======================== -bool evse_is_require_auth(void) { - return require_auth; -} - -void evse_set_require_auth(bool value) { - require_auth = value; - nvs_set_u8(nvs, "require_auth", value); - nvs_commit(nvs); -} - -// ======================== -// Availability -// ======================== -static bool is_available = true; - -bool evse_config_is_available(void) { - return is_available; -} - -void evse_config_set_available(bool available) { - is_available = available; -} - -// ======================== -// Enable/Disable -// ======================== -static bool is_enabled = true; - -bool evse_config_is_enabled(void) { - return is_enabled; -} - -void evse_config_set_enabled(bool enabled) { - is_enabled = enabled; -} - -// === Fim de: components/evse/evse_config.c === - - -// === Início de: components/evse/evse_manager.c === -#include "evse_manager.h" -#include "evse_state.h" -#include "evse_error.h" -#include "evse_hardware.h" -#include "evse_config.h" -#include "evse_api.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "esp_log.h" -#include - -#include "auth_events.h" -#include "loadbalancer_events.h" -#include "esp_event.h" - -static const char *TAG = "EVSE_Manager"; - -static SemaphoreHandle_t evse_mutex; -static bool auth_enabled = false; - -#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo - -// ===== Task de ciclo principal ===== -static void evse_manager_task(void *arg) { - while (true) { - evse_manager_tick(); - vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS)); - } -} - -// ===== Tratador de eventos de autenticação ===== -static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* data) { - if (base != AUTH_EVENTS || data == NULL) return; - - switch (id) { - case AUTH_EVENT_TAG_PROCESSED: { - auth_tag_event_data_t *evt = (auth_tag_event_data_t*)data; - ESP_LOGI("EVSE", "Tag: %s | Autorizada: %s", evt->tag, evt->authorized ? "SIM" : "NÃO"); - evse_state_set_authorized(evt->authorized); - break; - } - - case AUTH_EVENT_ENABLED_CHANGED: - case AUTH_EVENT_INIT: { - auth_enabled_event_data_t *evt = (auth_enabled_event_data_t*)data; - auth_enabled = evt->enabled; - - ESP_LOGI("EVSE", "Auth %s (%s)", - id == AUTH_EVENT_ENABLED_CHANGED ? "ficou" : "init", - evt->enabled ? "ATIVO" : "INATIVO"); - - if (!auth_enabled) { - evse_state_set_authorized(true); - ESP_LOGI("EVSE", "Autenticação desativada → autorização forçada."); - } else { - evse_state_set_authorized(false); - ESP_LOGI("EVSE", "Autenticação ativada → aguardando autorização por tag."); - } - break; - } - } -} - -// ===== Tratador de eventos de loadbalancer ===== -static void on_loadbalancer_event(void* handler_arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) { - 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", evt->timestamp_us); - // Ações adicionais podem ser adicionadas aqui conforme necessário - } else if (event_id == LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED) { - const loadbalancer_charging_limit_event_t* evt = (const loadbalancer_charging_limit_event_t*) event_data; - ESP_LOGD(TAG, "Novo limite de corrente: %.1f A (ts: %lld)", evt->limit, evt->timestamp_us); - evse_set_runtime_charging_current((uint16_t)(evt->limit)); - } -} - -// ===== Inicialização ===== -void evse_manager_init(void) { - evse_mutex = xSemaphoreCreateMutex(); - - evse_config_init(); - evse_error_init(); - evse_hardware_init(); - evse_state_init(); - - 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_LOGI(TAG, "EVSE Manager inicializado."); - xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL); -} - -// ===== Main Tick ===== -void evse_manager_tick(void) { - xSemaphoreTake(evse_mutex, portMAX_DELAY); - - evse_hardware_tick(); - evse_error_tick(); - evse_state_tick(); - evse_temperature_check(); - - if (auth_enabled) { - // If the car is disconnected, revoke authorization - if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A) { - ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization."); - evse_state_set_authorized(false); - } - } else { - // If authentication is disabled, ensure authorization is always granted - if (!evse_state_get_authorized()) { - evse_state_set_authorized(true); - ESP_LOGI(TAG, "Authentication disabled → forced authorization."); - } - } - - xSemaphoreGive(evse_mutex); -} - - -// ===== API pública ===== -bool evse_manager_is_available(void) { - return evse_config_is_available(); -} - -void evse_manager_set_available(bool available) { - evse_config_set_available(available); -} - -void evse_manager_set_authorized(bool authorized) { - evse_state_set_authorized(authorized); -} - -bool evse_manager_is_charging(void) { - return evse_state_is_charging(evse_get_state()); -} - -void evse_manager_set_enabled(bool enabled) { - evse_config_set_enabled(enabled); -} - -bool evse_manager_is_enabled(void) { - return evse_config_is_enabled(); -} - -// === Fim de: components/evse/evse_manager.c === - - -// === Início de: components/evse/evse_events.c === -#include "evse_events.h" - -ESP_EVENT_DEFINE_BASE(EVSE_EVENTS); - -// === Fim de: components/evse/evse_events.c === - - -// === Início de: components/evse/include/evse_pilot.h === -#ifndef PILOT_H_ -#define PILOT_H_ +// === Início de: components/peripherals/include/rcm.h === +#ifndef RCM_H_ +#define RCM_H_ + +#include + +/** + * @brief Initialize residual current monitor + * + */ +void rcm_init(void); + +/** + * @brief Test residual current monitor + * + * @return true + * @return false + */ +bool rcm_test(void); + +/** + * @brief Residual current monitor was detected leakage + * + * @return true + * @return false + */ +bool rcm_is_triggered(void); + +#endif /* RCM_H_ */ + +// === Fim de: components/peripherals/include/rcm.h === + + +// === Início de: components/peripherals/include/aux_io.h === +#ifndef AUX_IO_H_ +#define AUX_IO_H_ + +#include "esp_err.h" + +/** + * @brief Initialize aux + * + */ +void aux_init(void); + +/** + * @brief Read digital input + * + * @param name + * @param value + * @return esp_err_t + */ +esp_err_t aux_read(const char *name, bool *value); + +/** + * @brief Write digial output + * + * @param name + * @param value + * @return esp_err_t + */ +esp_err_t aux_write(const char *name, bool value); + +/** + * @brief Read analog input + * + * @param name + * @param value + * @return esp_err_t + */ +esp_err_t aux_analog_read(const char *name, int *value); + +#endif /* AUX_IO_H_ */ +// === Fim de: components/peripherals/include/aux_io.h === + + +// === Início de: components/peripherals/include/led.h === +#ifndef LED_H_ +#define LED_H_ + +#include +#include + +/** + * @brief Identificadores dos LEDs disponíveis no hardware + */ +typedef enum { + LED_ID_STOP, + LED_ID_CHARGING, + LED_ID_ERROR, + LED_ID_MAX +} led_id_t; + +/** + * @brief Padrões de comportamento possíveis para os LEDs + */ +typedef enum { + LED_PATTERN_OFF, ///< LED sempre desligado + LED_PATTERN_ON, ///< LED sempre ligado + LED_PATTERN_BLINK, ///< Pisca com ciclo padrão (500ms on / 500ms off) + LED_PATTERN_BLINK_FAST, ///< Pisca rápido (200ms / 200ms) + LED_PATTERN_BLINK_SLOW, ///< Pisca lento (300ms / 1700ms) + LED_PATTERN_CHARGING_EFFECT ///< Efeito visual para carregamento (2s on / 1s off) +} led_pattern_t; + +/** + * @brief Inicializa os LEDs com base na configuração da placa + * Deve ser chamada uma única vez na inicialização do sistema. + */ +void led_init(void); + +/** + * @brief Define diretamente o tempo ligado/desligado de um LED. + * Pode ser usado para padrões personalizados. + * + * @param led_id Identificador do LED (ver enum led_id_t) + * @param ontime Tempo ligado em milissegundos + * @param offtime Tempo desligado em milissegundos + */ +void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime); + +/** + * @brief Aplica um dos padrões de piscar definidos ao LED + * + * @param led_id Identificador do LED (ver enum led_id_t) + * @param pattern Padrão desejado (ver enum led_pattern_t) + */ +void led_apply_pattern(led_id_t led_id, led_pattern_t pattern); + +#endif /* LED_H_ */ + +// === Fim de: components/peripherals/include/led.h === + + +// === Início de: components/peripherals/include/buzzer.h === +#ifndef BUZZER_H_ +#define BUZZER_H_ + +#include + +/** + * @brief Inicializa o buzzer e inicia monitoramento automático do estado EVSE. + */ +void buzzer_init(void); + +/** + * @brief Liga e desliga o buzzer manualmente (uso interno ou testes). + */ +void buzzer_on(void); +void buzzer_off(void); + +/** + * @brief Ativa o buzzer por um período fixo (em milissegundos). + */ +void buzzer_beep_ms(uint16_t ms); + +#endif /* BUZZER_H_ */ + +// === Fim de: components/peripherals/include/buzzer.h === + + +// === Início de: components/peripherals/include/ac_relay.h === +#ifndef AC_RELAY_H_ +#define AC_RELAY_H_ + +#include + +/** + * @brief Inicializa o relé de corrente alternada. + */ +void ac_relay_init(void); + +/** + * @brief Define o estado do relé de corrente alternada. + * + * @param state true para ligar, false para desligar. + */ +void ac_relay_set_state(bool state); + +/** + * @brief Retorna o estado atual do relé de corrente alternada. + * + * @return true se estiver ligado, false se desligado. + */ +bool ac_relay_get_state(void); + +#endif /* AC_RELAY_H_ */ + +// === Fim de: components/peripherals/include/ac_relay.h === + + +// === Início de: components/peripherals/include/lm75a.h === +#ifndef LM75A_H +#define LM75A_H + +#include "esp_err.h" // Para o uso de tipos de erro do ESP-IDF, caso esteja utilizando. #ifdef __cplusplus extern "C" { #endif -#include -#include +/** + * @brief Inicializa o sensor LM75A. + * + * Configura o sensor para leitura e define os pinos de comunicação. + */ +esp_err_t lm75a_init(void); /** - * @brief Níveis categóricos de tensão no sinal CP (Control Pilot) + * @brief Desinicializa o sensor LM75A. + * + * Libera os recursos usados pelo sensor. */ -typedef enum -{ - PILOT_VOLTAGE_12, ///< Estado A: +12V - PILOT_VOLTAGE_9, ///< Estado B: +9V - PILOT_VOLTAGE_6, ///< Estado C: +6V - PILOT_VOLTAGE_3, ///< Estado D: +3V - PILOT_VOLTAGE_1 ///< Estado E/F: abaixo de 3V -} pilot_voltage_t; +esp_err_t lm75a_deinit(void); /** - * @brief Inicializa o driver do sinal Pilot + * @brief Lê a temperatura do LM75A. + * + * @param show Se for 1, a temperatura será exibida em algum tipo de log ou interface. + * Se for 0, o valor é apenas retornado sem exibição. + * @return A temperatura lida em graus Celsius. */ -void pilot_init(void); +float lm75a_read_temperature(int show); /** - * @brief Define o nível do Pilot: +12V ou -12V - * - * @param level true = +12V, false = -12V + * @brief Define o valor do limite de temperatura (T_OS) para o sensor LM75A. + * + * @param tos O limite de temperatura de sobrecarga (T_OS) em graus Celsius. + * @return ESP_OK em caso de sucesso ou código de erro se falhar. */ -void pilot_set_level(bool level); +esp_err_t lm75a_set_tos(int tos); /** - * @brief Ativa o PWM do Pilot com corrente limitada - * - * @param amps Corrente em décimos de ampère (ex: 160 = 16A) + * @brief Define o valor do limite de temperatura de histerese (T_HYS) para o sensor LM75A. + * + * @param thys O limite de histerese de temperatura (T_HYS) em graus Celsius. + * @return ESP_OK em caso de sucesso ou código de erro se falhar. */ -void pilot_set_amps(uint16_t amps); +esp_err_t lm75a_set_thys(int thys); /** - * @brief Mede o nível de tensão do Pilot e detecta -12V - * - * @param up_voltage Valor categórico da tensão positiva - * @param down_voltage_n12 true se o nível negativo atingir -12V + * @brief Obtém o limite de temperatura de sobrecarga (T_OS) do sensor LM75A. + * + * @return O valor atual de T_OS em graus Celsius. */ -void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12); +int lm75a_get_tos(void); /** - * @brief Retorna o estado lógico atual do Pilot (nível alto = +12V) - * - * @return true se nível atual for +12V, false se for -12V + * @brief Obtém o limite de temperatura de histerese (T_HYS) do sensor LM75A. + * + * @return O valor atual de T_HYS em graus Celsius. */ -bool pilot_get_state(void); +int lm75a_get_thys(void); /** - * @brief Cache interno opcional dos níveis de tensão reais do Pilot + * @brief Habilita ou desabilita a interrupção do LM75A. + * + * @param en 1 para habilitar a interrupção, 0 para desabilitar. + * @return ESP_OK em caso de sucesso ou código de erro se falhar. */ -typedef struct { - uint16_t high_mv; ///< Pico positivo medido (mV) - uint16_t low_mv; ///< Pico negativo medido (mV) -} pilot_voltage_cache_t; +esp_err_t lm75a_set_int(int en); + +/** + * @brief Obtém o estado do pino OS (Overtemperature Shutdown) do LM75A. + * + * @return 1 se o pino OS estiver ativo (indica que a temperatura de sobrecarga foi atingida), + * 0 caso contrário. + */ +int lm75a_get_osio(void); #ifdef __cplusplus } #endif -#endif /* PILOT_H_ */ +#endif /* LM75A_H */ -// === Fim de: components/evse/include/evse_pilot.h === +// === Fim de: components/peripherals/include/lm75a.h === -// === Início de: components/evse/include/evse_manager.h === -#ifndef EVSE_MANAGER_H -#define EVSE_MANAGER_H +// === Início de: components/peripherals/include/ntc_sensor.h === +#ifndef NTC_SENSOR_H_ +#define NTC_SENSOR_H_ +/** + * @brief Initialize ntc senso + * + */ +void ntc_sensor_init(void); + +/** + * @brief Return temperature after temp_sensor_measure + * + * @return float + */ +float ntc_temp_sensor(void); + +#endif /* NTC_SENSOR_H_ */ + +// === Fim de: components/peripherals/include/ntc_sensor.h === + + +// === Início de: components/peripherals/include/proximity.h === +#ifndef PROXIMITY_H_ +#define PROXIMITY_H_ + +#include + +/** + * @brief Initialize proximity check + * + */ +void proximity_init(void); + +/** + * @brief Return measured value of max current on PP + * + * @return current in A + */ +uint8_t proximity_get_max_current(void); + +#endif /* PROXIMITY_H_ */ + +// === Fim de: components/peripherals/include/proximity.h === + + +// === Início de: components/peripherals/include/socket_lock.h === +#ifndef SOCKED_LOCK_H_ +#define SOCKED_LOCK_H_ + +#include "esp_err.h" + +typedef enum +{ + SOCKED_LOCK_STATUS_IDLE, + SOCKED_LOCK_STATUS_OPERATING, + SOCKED_LOCK_STATUS_LOCKING_FAIL, + SOCKED_LOCK_STATUS_UNLOCKING_FAIL +} socket_lock_status_t; + +/** + * @brief Initialize socket lock + * + */ +void socket_lock_init(void); + +/** + * @brief Get socket lock detection on high, stored in NVS + * + * @return true when locked has zero resistance + * @return false when unlocked has zero resistance + */ +bool socket_lock_is_detection_high(void); + +/** + * @brief Set socket lock detection on high, stored in NVS + * + * @param detection_high + */ +void socket_lock_set_detection_high(bool detection_high); + +/** + * @brief Get socket lock operating time + * + * @return time in ms + */ +uint16_t socket_lock_get_operating_time(void); + +/** + * @brief Set socket lock operating time + * + * @param operating_time - time in ms + * @return esp_err_t + */ +esp_err_t socket_lock_set_operating_time(uint16_t operating_time); + +/** + * @brief Get socket lock retry count + * + * @return retry count + */ +uint8_t socket_lock_get_retry_count(void); + +/** + * @brief Set socket lock retry count + * + * @param retry_count + */ +void socket_lock_set_retry_count(uint8_t retry_count); + +/** + * @brief Get socket lock break time + * + * @return time in ms + */ +uint16_t socket_lock_get_break_time(void); + +/** + * @brief Set socket lock break time + * + * @param break_time + * @return esp_err_t + */ +esp_err_t socket_lock_set_break_time(uint16_t break_time); + +/** + * @brief Set socke lock to locked / unlocked state + * + * @param locked + */ +void socket_lock_set_locked(bool locked); + +/** + * @brief Get socket lock status + * + * @return socket_lock_status_t + */ +socket_lock_status_t socket_lock_get_status(void); + +/** + * @brief Read the current physical lock state using the detection pin. + */ +bool socket_lock_is_locked_state(void); + + +#endif /* SOCKED_LOCK_H_ */ + +// === Fim de: components/peripherals/include/socket_lock.h === + + +// === Início de: components/peripherals/include/adc.h === +#ifndef ADC_H_ +#define ADC_H_ + +#include "esp_adc/adc_oneshot.h" +#include "esp_adc/adc_cali.h" +#include "esp_adc/adc_cali_scheme.h" + +extern adc_oneshot_unit_handle_t adc_handle; + +extern adc_cali_handle_t adc_cali_handle; + +void adc_init(void); + +#endif /* ADC_H_ */ +// === Fim de: components/peripherals/include/adc.h === + + +// === Início de: components/peripherals/include/temp_sensor.h === +#ifndef TEMP_SENSOR_H_ +#define TEMP_SENSOR_H_ + +#include +#include "esp_err.h" + +/** + * @brief Initialize DS18S20 temperature sensor bus + * + */ +void temp_sensor_init(void); + +/** + * @brief Get found sensor count + * + * @return uint8_t + */ +uint8_t temp_sensor_get_count(void); + +/** + * @brief Return lowest temperature after temp_sensor_measure + * + * @return int16_t + */ +int16_t temp_sensor_get_low(void); + +/** + * @brief Return highest temperature after temp_sensor_measure + * + * @return int + */ +int temp_sensor_get_high(void); + +/** + * @brief Return temperature sensor error + * + * @return bool + */ +bool temp_sensor_is_error(void); + +#endif /* TEMP_SENSOR_H_ */ + +// === Fim de: components/peripherals/include/temp_sensor.h === + + +// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h === #pragma once #ifdef __cplusplus extern "C" { #endif -#include -#include -#include -#include - -/** - * @brief Inicializa os módulos internos do EVSE (hardware, estado, erros, etc.) - * e inicia a tarefa de supervisão periódica (tick). - */ -void evse_manager_init(void); - -/** - * @brief Executa uma iteração do ciclo de controle do EVSE. - * - * Esta função é chamada automaticamente pela task periódica, - * mas pode ser chamada manualmente em testes. - */ -void evse_manager_tick(void); - -/** - * @brief Verifica se o EVSE está disponível para uso. - * - * Isso considera falhas críticas, disponibilidade configurada, etc. - */ -bool evse_manager_is_available(void); - -/** - * @brief Define se o EVSE deve estar disponível (ex: via controle remoto). - */ -void evse_manager_set_available(bool available); - -/** - * @brief Define se o EVSE está autorizado a carregar (ex: após autenticação). - */ -void evse_manager_set_authorized(bool authorized); - -/** - * @brief Verifica se o EVSE está atualmente carregando. - */ -bool evse_manager_is_charging(void); - -/** - * @brief Ativa ou desativa logicamente o EVSE (controla habilitação geral). - */ -void evse_manager_set_enabled(bool enabled); - -/** - * @brief Verifica se o EVSE está ativado logicamente. - */ -bool evse_manager_is_enabled(void); - -#ifdef __cplusplus -} -#endif - - -#endif // EVSE_MANAGER_H - -// === Fim de: components/evse/include/evse_manager.h === - - -// === Início de: components/evse/include/evse_fsm.h === -#ifndef EVSE_FSM_H -#define EVSE_FSM_H - #include #include -#include "evse_api.h" -#include "evse_pilot.h" -#include "freertos/FreeRTOS.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Reinicia a máquina de estados do EVSE para o estado inicial (A). - */ -void evse_fsm_reset(void); - -/** - * @brief Processa uma leitura do sinal de piloto e atualiza a máquina de estados do EVSE. - * - * Esta função deve ser chamada periodicamente pelo núcleo de controle para - * avaliar mudanças no estado do conector, disponibilidade do carregador e - * autorização do usuário. - * - * @param pilot_voltage Leitura atual da tensão do sinal piloto. - * @param authorized Indica se o carregamento foi autorizado. - * @param available Indica se o carregador está disponível (ex: sem falhas). - * @param enabled Indica se o carregador está habilitado via software. - */ -void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled); - -#ifdef __cplusplus -} -#endif - -#endif // EVSE_FSM_H - -// === Fim de: components/evse/include/evse_fsm.h === - - -// === Início de: components/evse/include/evse_hardware.h === -#ifndef EVSE_HARDWARE_H -#define EVSE_HARDWARE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/** - * @brief Inicializa todos os periféricos de hardware do EVSE (pilot, relé, trava, etc.) - */ -void evse_hardware_init(void); - -/** - * @brief Executa atualizações periódicas no hardware (tick) - */ -void evse_hardware_tick(void); - -/** - * @brief Verifica se o sinal piloto está em nível alto (12V) - */ -bool evse_hardware_is_pilot_high(void); - -/** - * @brief Verifica se o veículo está fisicamente conectado via Proximity - */ -bool evse_hardware_is_vehicle_connected(void); - -/** - * @brief Verifica se há consumo de energia (corrente detectada) - */ -bool evse_hardware_is_energy_detected(void); - -/** - * @brief Liga o relé de fornecimento de energia - */ -void evse_hardware_relay_on(void); - -/** - * @brief Desliga o relé de fornecimento de energia - */ -void evse_hardware_relay_off(void); - -/** - * @brief Consulta o estado atual do relé - * @return true se ligado, false se desligado - */ -bool evse_hardware_relay_status(void); - -/** - * @brief Aciona a trava física do conector - */ -void evse_hardware_lock(void); - -/** - * @brief Libera a trava física do conector - */ -void evse_hardware_unlock(void); - -/** - * @brief Verifica se o conector está travado - */ -bool evse_hardware_is_locked(void); - -#ifdef __cplusplus -} -#endif - -#endif // EVSE_HARDWARE_H - -// === Fim de: components/evse/include/evse_hardware.h === - - -// === Início de: components/evse/include/evse_config.h === -#ifndef EVSE_CONFIG_H -#define EVSE_CONFIG_H - -#include -#include #include "esp_err.h" -#ifdef __cplusplus -extern "C" { -#endif +/** + * @brief Inicializa o driver do medidor ADE7758 (SPI, mutex, registradores). + */ +esp_err_t meter_ade7758_init(void); -// ======================== -// Limites Globais (Defines) -// ======================== +/** + * @brief Inicia a tarefa de leitura de dados do medidor ADE7758. + */ +esp_err_t meter_ade7758_start(void); -// Corrente máxima de carregamento (configurável pelo usuário) -#define MIN_CHARGING_CURRENT_LIMIT 6 // A -#define MAX_CHARGING_CURRENT_LIMIT 32 // A +/** + * @brief Para a tarefa de leitura e limpa os dados internos do medidor ADE7758. + */ +void meter_ade7758_stop(void); -// Corrente via cabo (proximity) — se configurável -#define MIN_CABLE_CURRENT_LIMIT 6 // A -#define MAX_CABLE_CURRENT_LIMIT 63 // A - -// ======================== -// Funções de Configuração -// ======================== - -// Inicialização -esp_err_t evse_config_init(void); -void evse_check_defaults(void); - -// Corrente de carregamento -uint8_t evse_get_max_charging_current(void); -esp_err_t evse_set_max_charging_current(uint8_t value); - -uint16_t evse_get_charging_current(void); -esp_err_t evse_set_charging_current(uint16_t value); - -uint16_t evse_get_default_charging_current(void); -esp_err_t evse_set_default_charging_current(uint16_t value); - -// Configuração de socket outlet -bool evse_get_socket_outlet(void); -esp_err_t evse_set_socket_outlet(bool socket_outlet); - -void evse_set_runtime_charging_current(uint16_t value); -uint16_t evse_get_runtime_charging_current(void); - - -// RCM -bool evse_is_rcm(void); -esp_err_t evse_set_rcm(bool rcm); - -// Temperatura -uint8_t evse_get_temp_threshold(void); -esp_err_t evse_set_temp_threshold(uint8_t threshold); - -// Autenticação -bool evse_is_require_auth(void); -void evse_set_require_auth(bool require); - -// Disponibilidade -bool evse_config_is_available(void); -void evse_config_set_available(bool available); - -// Ativação/desativação do EVSE -bool evse_config_is_enabled(void); -void evse_config_set_enabled(bool enabled); #ifdef __cplusplus } #endif -#endif // EVSE_CONFIG_H - -// === Fim de: components/evse/include/evse_config.h === +// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h === -// === Início de: components/evse/include/evse_state.h === -#ifndef EVSE_STATE_H -#define EVSE_STATE_H +// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.h === +#include "driver/spi_common.h" +#include "driver/spi_master.h" -#include + + +#define WRITE 0x80 // WRITE bit BT7 to write to registers +#define CLKIN 10000000 // ADE7758 frec, 10.000000MHz +#define PERIODO 50 // Actually it is frequency, it is used to calculate the amount of Cycles that it accumulates for energy. +#define PHASE_A 1 +#define PHASE_B 2 +#define PHASE_C 3 + + +//Register address + +//------Name--------Address---------Lenght +#define AWATTHR 0x01 //---------16 +#define BWATTHR 0x02 //---------16 +#define CWATTHR 0x03 //---------16 + +#define AVARHR 0x04 //---------16 +#define BVARHR 0x05 //---------16 +#define CVARHR 0x06 //---------16 + +#define AVAHR 0x07 //---------16 +#define BVAHR 0x08 //---------16 +#define CVAHR 0x09 //---------16 + +#define AIRMS 0x0A //---------24 +#define BIRMS 0x0B //---------24 +#define CIRMS 0x0C //---------24 + +#define AVRMS 0x0D //---------24 +#define BVRMS 0x0E //---------24 +#define CVRMS 0x0F //---------24 + +#define FREQ 0x10 //---------12 +#define TEMP 0x11 //---------8 +#define WFORM 0x12 //---------24 +#define OPMODE 0x13 //---------8 +#define MMODE 0x14 //---------8 +#define WAVMODE 0x15 //---------8 +#define COMPMODE 0x16 //---------8 +#define LCYCMODE 0x17 //---------8 +#define MASK 0x18 //---------24 +#define STATUS 0x19 //---------24 +#define RSTATUS 0x1A //---------24 +#define ZXTOUT 0x1B //---------16 +#define LINECYC 0x1C //---------16 +#define SAGCYC 0x1D //---------8 +#define SAGLVL 0x1E //---------8 +#define VPINTLVL 0x1F //---------8 +#define IPINTLVL 0x20 //---------8 +#define VPEAK 0x21 //---------8 +#define IPEAK 0x22 //---------8 +#define GAIN 0x23 //---------8 +#define AVRMSGAIN 0x24 //---------12 +#define BVRMSGAIN 0x25 //---------12 +#define CVRMSGAIN 0x26 //---------12 +#define AIGAIN 0x27 //---------12 +#define BIGAIN 0x28 //---------12 +#define CIGAIN 0x29 //---------12 +#define AWG 0x2A //---------12 +#define BWG 0x2B //---------12 +#define CWG 0x2C //---------12 +#define AVARG 0x2D //---------12 +#define BVARG 0x2E //---------12 +#define CVARG 0x2F //---------12 +#define AVAG 0x30 //---------12 +#define BVAG 0X31 //---------12 +#define CVAG 0x32 //---------12 +#define AVRMSOS 0x33 //---------12 +#define BVRMSOS 0X34 //---------12 +#define CVRMSOS 0X35 //---------12 +#define AIRMSOS 0X36 //---------12 +#define BIRMSOS 0X37 //---------12 +#define CIRMSOS 0X38 //---------12 +#define AWATTOS 0X39 //---------12 +#define BWATTOS 0X3A //---------12 +#define CWATTOS 0X3B //---------12 +#define AVAROS 0X3C //---------12 +#define BVAROS 0X3D //---------12 +#define CVAROS 0X3E //---------12 +#define APHCAL 0X3F //---------7 +#define BPHCAL 0X40 //---------7 +#define CPHCAL 0X41 //---------7 +#define WDIV 0X42 //---------8 +#define VARDIV 0X43 //---------8 +#define VADIV 0X44 //---------8 +#define APCFNUM 0X45 //---------16 +#define APCFDEN 0X46 //---------12 +#define VARCFNUM 0X47 //---------16 +#define VARCFDEN 0X48 //---------12 + +#define CHKSUM 0X7E //---------8 +#define VERSION 0x7f //---------8 +#define DUMMY_BYTE 0xFF + + +//bits + +/** +OPERATIONAL MODE REGISTER (0x13) +The general configuration of the ADE7758 is defined by writing to the OPMODE register. +Table 18 summarizes the functionality of each bit in the OPMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set. +1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set. +2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set. +3 to 5 DISMOD 0 By setting these bits, ADE7758�s ADCs can be turned off. In normal operation, these bits should be left at Logic 0. + DISMOD[2:0] Description + 0 0 0 Normal operation. + 1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels. + 0 0 1 Switch off only the current channel ADCs. + 1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths. + 0 1 0 Switch off only the voltage channel ADCs. + 1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths. + 0 1 1 Put the ADE7758 in sleep mode. + 1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ). +6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 �s after a software reset. +7 RESERVED 0 This should be left at 0. + +*/ + +#define DISHPF 0x01 +#define DISLPF 0x02 +#define DISCF 0x04 +#define SWRST 0x40 + +/** +MEASUREMENT MODE REGISTER (0x14) +The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register. +Table 19 summarizes the functionality of each bit in the MMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency. + FREQSEL1 FREQSEL0 Source + 0 0 Phase A + 0 1 Phase B + 1 0 Phase C + 1 1 Reserved +2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers. + Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values + of the largest current and voltage waveform (over a fixed number of half-line cycles) + from Phase A. The number of half-line cycles is determined by the content of the + LINECYC register. At the end of the LINECYC number of half-line cycles, the content + of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns + on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one + bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is, + the voltage and current peak are independently processed (see the Peak Current Detection section). +5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection. + Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A. + Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C. + Note that more than one bit can be set for detection on multiple phases. + If the absolute values of the voltage or current waveform samples in the selected phases exceeds + the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the + STATUS registers are set (see the Peak Current Detection section). + +*/ + +#define FREQSEL0 0x01 +#define FREQSEL1 0x02 + + +/** +WAVEFORM MODE REGISTER (0x15) +The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register. +Table 20 summarizes the functionality of each bit in the WAVMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample. + PHSEL[1:0] Source + 0 0 Phase A + 0 1 Phase B + 1 0 Phase C + 1 1 Reserved +2 to 4 WAVSEL 0 These bits are used to select the type of waveform. + WAVSEL[2:0] Source + 0 0 0 Current + 0 0 1 Voltage + 0 1 0 Active Power Multiplier Output + 0 1 1 Reactive Power Multiplier Output + 1 0 0 VA Multiplier Output + -Others- Reserved +5 to 6 DTRT 0 These bits are used to select the data rate. + DTRT[1:0] Update Rate + 0 0 26.04 kSPS (CLKIN/3/128) + 0 1 13.02 kSPS (CLKIN/3/256) + 1 0 6.51 kSPS (CLKIN/3/512) + 1 1 3.25 kSPS (CLKIN/3/1024) +7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output + frequency that is proportional to the total apparent power (VA). + In the default state, Logic 0, the VARCF pin outputs a frequency proportional + to the total reactive power (VAR). +*/ + + + +/** +COMPUTATIONAL MODE REGISTER (0x16) +The computational method of the ADE7758 is defined by writing to the COMPMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers. + CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by �90�, respectively. + Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10 + AWATTHR VA � IA VA � (IA � IB) VA � (IA�IB) + BWATTHR VB � IB 0 0 + CWATTHR VC � IC VC � (IC � IB) VC � IC + + AVARHR VA � IA VA � (IA � IB) VA � (IA�IB) + BVARHR VB � IB 0 0 + CVARHR VC � IC VC � (IC � IB) VC � IC + + AVAHR VARMS � IARMS VARMS � IARMS VARMS � ARMS + BVAHR VBRMS � IBRMS (VARMS + VCRMS)/2 � IBRMS VARMS � IBRMS + CVAHR VCRMS � ICRMS VCRMS � ICRMS VCRMS � ICRMS + +2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs. + Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included. + Bit 3 and Bit 4 are for Phase B and Phase C, respectively. + Setting all three bits enables the sum of all three phases to be included in the frequency outputs + (see the Active Power Frequency Output and the Reactive Power Frequency Output sections). + +5 ABS 0 Setting this bit places the APCF output pin in absolute only mode. + Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour + accumulation registers (AWATTHR, BWATTHR, and CWATTHR). + Note that this bit only affects the APCF pin and has no effect on the content of the corresponding + registers. + +6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode. + Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation + registers (AVARHR, BVARHR, and CVARHR). + The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase, + that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive, + there is no change to the sign of the VAR. + Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding + registers. + +7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758. +*/ + + +/** +LINE CYCLE ACCUMULATION MODE REGISTER (0x17) +The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register. + +Bit Location Bit Mnemonic Default Value Description + +0 LWATT 0 Setting this bit places the watt-hour accumulation registers + (AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode. +1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers) + into line-cycle accumulation mode. +2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers) + into line-cycle accumulation mode. +3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle + accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively. + More than one phase can be selected for the zero-crossing detection, + and the accumulation time is shortened accordingly. +6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three + phases, that is, a read to those registers resets the registers to 0 after the content of the registers + have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1. +7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the + line input. +*/ + + +#define LWATT 0x01 +#define LVAR 0x02 +#define LVA 0x04 +#define ZXSEL_A 0x08 +#define ZXSEL_B 0x10 +#define ZXSEL_C 0x20 +#define RSTREAD 0x40 +#define FREQSEL 0x80 + + + +/** INTERRUPT MASK REGISTER (0x18) +When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register. +The IRQ logic output is reset to its default collector open state when the RSTATUS register is read. +describes the function of each bit in the interrupt mask register. +**/ + +// The next table summarizes the function of each bit for +// the Interrupt Enable Register + +/* Bit Mask // Bit Location / Description +#define AEHF 0x0001 // bit 0 - Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers, that is, the WATTHR register is half full. +#define REHF 0x0002 // bit 1 - Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers, that is, the VARHR register is half full. +#define VAEHF 0x0004 // bit 2 - Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR registers, that is, the VAHR register is half full. +#define SAGA 0x0008 // bit 3 - Enables an interrupt when there is a SAG on the line voltage of the Phase A. +#define SAGB 0x0010 // bit 4 - Enables an interrupt when there is a SAG on the line voltage of the Phase B. +#define SAGC 0x0020 // bit 5 - Enables an interrupt when there is a SAG on the line voltage of the Phase C. +#define ZXTOA 0x0040 // bit 6 - Enables an interrupt when there is a zero-crossing timeout detection on Phase A. +#define ZXTOB 0x0080 // bit 7 - Enables an interrupt when there is a zero-crossing timeout detection on Phase B. +#define ZXTOC 0x0100 // bit 8 - Enables an interrupt when there is a zero-crossing timeout detection on Phase C. +#define ZXA 0x0200 // bit 9 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase A +#define ZXB 0x0400 // bit 10 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase B +#define ZXC 0x0800 // bit 11 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase C +#define LENERGY 0x1000 // bit 12 - Enables an interrupt when the energy accumulations over LINECYC are finished. +//RESERVED 0x2000 // bit 13 - RESERVED +#define PKV 0x4000 // bit 14 - Enables an interrupt when the voltage input selected in the MMODE register is above the value in the VPINTLVL register. +#define PKI 0x8000 // bit 15 - Enables an interrupt when the current input selected in the MMODE register is above the value in the IPINTLVL register. +#define WFSM 0x010000 // bit 16 - Enables an interrupt when data is present in the WAVEMODE register. +#define REVPAP 0x020000 // bit 17 - Enables an interrupt when there is a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. +#define REVPRP 0x040000 // bit 18 - Enables an interrupt when there is a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. +#define SEQERR 0x080000 // bit 19 - Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing of Phase C but with that of Phase B. +*/ +/** INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A) +The interrupt status register is used to determine the source of an interrupt event. +When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set. +The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set. +When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt. +All the interrupts in the interrupt status register stay at their logic high state after an event occurs. +The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read. +**/ + +// The next table summarizes the function of each bit for +// the Interrupt Status Register, the Reset Interrupt Status Register. + +// Bit Mask // Bit Location / Description +#define AEHF 0x0001 // bit 0 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three WATTHR registers, that is, the WATTHR register is half full. +#define REHF 0x0002 // bit 1 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three VARHR registers, that is, the VARHR register is half full. +#define VAEHF 0x0004 // bit 2 - Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three VAHR registers, that is, the VAHR register is half full. +#define SAGA 0x0008 // bit 3 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A. +#define SAGB 0x0010 // bit 4 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B. +#define SAGC 0x0020 // bit 5 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C. +#define ZXTOA 0x0040 // bit 6 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A. +#define ZXTOB 0x0080 // bit 7 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B. +#define ZXTOC 0x0100 // bit 8 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C +#define ZXA 0x0200 // bit 9 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A. +#define ZXB 0x0400 // bit 10 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B +#define ZXC 0x0800 // bit 11 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C +#define LENERGY 0x1000 // bit 12 - In line energy accumulation, indicates the end of an integration over an integer number of half- line cycles (LINECYC). See the Calibration section. +#define RESET 0x2000 // bit 13 - Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758 and sets the registers back to their default values. This bit in the STATUS or RSTATUS register is logic high for only one clock cycle after a reset event. +#define PKV 0x4000 // bit 14 - Indicates that an interrupt was caused when the selected voltage input is above the value in the VPINTLVL register. +#define PKI 0x8000 // bit 15 - Indicates that an interrupt was caused when the selected current input is above the value in the IPINTLVL register. +#define WFSM 0x010000 // bit 16 - Indicates that new data is present in the waveform register. +#define REVPAP 0x020000 // bit 17 - Indicates that an interrupt was caused by a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. +#define REVPRP 0x040000 // bit 18 - Indicates that an interrupt was caused by a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register. +#define SEQERR 0x080000 // bit 19 - Indicates that an interrupt was caused by a zero crossing from Phase A followed not by the zero crossing of Phase C but by that of Phase B. + + +//constants +#define GAIN_1 0x00 +#define GAIN_2 0x01 +#define GAIN_4 0x02 +#define INTEGRATOR_ON 1 +#define INTEGRATOR_OFF 0 +#define FULLSCALESELECT_0_5V 0x00 +#define FULLSCALESELECT_0_25V 0x01 +#define FULLSCALESELECT_0_125V 0x02 + +esp_err_t transferByte(const uint8_t reg_addr, const uint8_t data, const uint8_t command); +esp_err_t transferMultiplesBytes(const uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t data_length, const uint8_t command); + +esp_err_t Init(const spi_host_device_t spi_peripheral, const int pin_miso, const int pin_mosi, const int pin_sclk); +esp_err_t InitSpi(const int ss); + +esp_err_t RegisterDevice(const uint8_t mode, const int ss, const int addr_length, const int command_length, const int bus_speed); +uint8_t ReadRegister(const uint8_t reg_addr, const uint8_t command); +esp_err_t WriteRegister(const uint8_t reg_addr, const uint8_t reg_data, const uint8_t command); +esp_err_t WriteRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command); +esp_err_t ReadRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command); +spi_device_handle_t GetHandle(); + + +//---------------------------------------------------------------------------- +// Modes and configurations +//---------------------------------------------------------------------------- +void setOpMode(uint8_t m); +uint8_t getOpMode(); +void setMMode(uint8_t m); +uint8_t getMMode(); +void setWavMode(uint8_t m); +uint8_t getWavMode(); +void setCompMode(uint8_t m); +uint8_t getCompMode(); +void setLcycMode(uint8_t m); +uint8_t getLcycMode(); +void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1); +void setupDivs(uint8_t Watt_div,uint8_t VAR_div,uint8_t VA_div); +uint32_t getMaskInterrupts(); +void setMaskInterrupts(uint32_t m); +uint32_t getStatus(); +uint32_t resetStatus(); +int32_t getAIRMS(); +int32_t getBIRMS(); +int32_t getCIRMS(); +int32_t getAVRMS(); +int32_t getBVRMS(); +int32_t getCVRMS(); +uint32_t avrms(); +uint32_t bvrms(); +uint32_t cvrms(); +uint32_t airms(); +uint32_t birms(); +int32_t cirms(); +int32_t getFreq(); +void setLineCyc(uint32_t d); +int32_t getACurrentOffset(); +int32_t getBCurrentOffset(); +int32_t getCCurrentOffset(); +void setACurrentOffset(int32_t o); +void setBCurrentOffset(int32_t o); +void setCCurrentOffset(int32_t o); +int32_t getAVoltageOffset(); +int32_t getBVoltageOffset(); +int32_t getCVoltageOffset(); +void setAVoltageOffset(int32_t o); +void setBVoltageOffset(int32_t o); +void setCVoltageOffset(int32_t o); +void setAWattOffset(int32_t o); +void setBWattOffset(int32_t o); +void setCWattOffset(int32_t o); +void setZeroCrossingTimeout(int32_t d); +int32_t getZeroCrossingTimeout(); +uint8_t setPotLine(uint8_t Phase, uint32_t Ciclos); +int32_t getWatt(uint8_t Phase); +int32_t getVar(uint8_t Phase); +int32_t getVa(uint8_t Phase); +uint8_t getVersion(); +uint8_t read8(uint8_t reg); +uint32_t read16(uint8_t reg); +uint32_t read24(uint8_t reg); + +esp_err_t write24(uint8_t reg, uint32_t data); +esp_err_t write16(uint8_t reg, uint32_t data); +esp_err_t write8(uint8_t reg, uint8_t data); + +void enableADE7758Chip(); +void disableADE7758Chip(); +void setAPCFDEN(int32_t d); +int32_t getAPCFDEN(); +void setAPCFNUM(int32_t d); +int32_t getAPCFNUM(); +void setVARCFNUM(int32_t d); +int32_t getVARCFNUM(); +void setVARCFDEN(int32_t d); +int32_t getVARCFDEN(); +void setAWG(int32_t d); +int32_t getAWG(); +void setBWG(int32_t d); +void setCWG(int32_t d); +void setAVARG(int32_t d); +int32_t getAVARG(); +void setBVARG(int32_t d); +int32_t getBVARG(); +void setCVARG(int32_t d); +int32_t getCVARG(); +void setAVAG(int32_t d); +void setBVAG(int32_t d); +void setCVAG(int32_t d); +// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.h === + + +// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.c === +#include +#include +#include "driver/spi_master.h" +#include "sdkconfig.h" +#include "esp_log.h" +#include "ade7758.h" + +static const char *TAG = "ade7758"; + +// --- SPI internals --- +static spi_device_handle_t ade7758_spi_handle = NULL; +static spi_host_device_t spi_host = SPI2_HOST; // default + +static spi_transaction_t spi_transaction; + +// --- Configuração SPI do dispositivo --- +static const uint8_t MODE = 2; +static const uint8_t ADDR_BITS = 7; +static const uint8_t CMD_BITS = 1; +static const uint8_t SPI_WRITE = 1; +static const uint8_t SPI_READ = 0; +static const int BUS_SPEED_HZ = 1000000; + +// === Transações básicas === + +static esp_err_t transfer_byte(uint8_t reg_addr, uint8_t data, uint8_t command) { + spi_transaction.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA; + spi_transaction.length = 8; + spi_transaction.cmd = command; + spi_transaction.addr = reg_addr; + spi_transaction.tx_data[0] = data; + + return spi_device_transmit(ade7758_spi_handle, &spi_transaction); +} + +static esp_err_t transfer_bytes(uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t len, uint8_t command) { + if (len < 1) len = 1; + + spi_transaction_t t = { + .flags = 0, + .length = 8 * len, + .cmd = command, + .addr = reg_addr, + .tx_buffer = tx_buf, + .rx_buffer = rx_buf + }; + + return spi_device_transmit(ade7758_spi_handle, &t); +} + +// === Interface pública === + +esp_err_t Init(spi_host_device_t host, int pin_miso, int pin_mosi, int pin_sclk) { + // Essa função não inicializa o barramento SPI + // Apenas armazena os parâmetros + spi_host = host; + return ESP_OK; +} + +esp_err_t InitSpi(int cs_gpio) { + spi_device_interface_config_t devcfg = { + .command_bits = CMD_BITS, + .address_bits = ADDR_BITS, + .mode = MODE, + .clock_speed_hz = BUS_SPEED_HZ, + .spics_io_num = cs_gpio, + .queue_size = 5, + }; + + return spi_bus_add_device(spi_host, &devcfg, &ade7758_spi_handle); +} + +spi_device_handle_t GetHandle(void) { + return ade7758_spi_handle; +} + +// === Registro de acesso === + +uint8_t ReadRegister(uint8_t reg_addr, uint8_t command) { + transfer_byte(reg_addr, 0, command); + return spi_transaction.rx_data[0]; +} + +esp_err_t WriteRegister(uint8_t reg_addr, uint8_t data, uint8_t command) { + return transfer_byte(reg_addr, data, command); +} + +esp_err_t WriteRegisterMultipleBytes(uint8_t reg, uint8_t *data, uint8_t count, uint8_t command) { + return transfer_bytes(reg, data, NULL, count, command); +} + +esp_err_t ReadRegisterMultipleBytes(uint8_t reg, uint8_t *buf, uint8_t count, uint8_t command) { + return transfer_bytes(reg, NULL, buf, count, command); +} + +// === Leitura e escrita de tamanho fixo === + +esp_err_t write8(uint8_t reg, uint8_t value) { + return WriteRegister(reg, value, SPI_WRITE); +} + +esp_err_t write16(uint8_t reg, uint32_t value) { + uint8_t buf[2] = { + (value >> 8) & 0xFF, + (value >> 0) & 0xFF + }; + return WriteRegisterMultipleBytes(reg, buf, 2, SPI_WRITE); +} + +esp_err_t write24(uint8_t reg, uint32_t value) { + uint8_t buf[3] = { + (value >> 16) & 0xFF, + (value >> 8) & 0xFF, + (value >> 0) & 0xFF + }; + return WriteRegisterMultipleBytes(reg, buf, 3, SPI_WRITE); +} + +uint8_t read8(uint8_t reg) { + uint8_t buf[1]; + ReadRegisterMultipleBytes(reg, buf, 1, SPI_READ); + return buf[0]; +} + +uint32_t read16(uint8_t reg) { + uint8_t buf[2]; + ReadRegisterMultipleBytes(reg, buf, 2, SPI_READ); + return (buf[0] << 8) | buf[1]; +} + +uint32_t read24(uint8_t reg) { + uint8_t buf[3]; + ReadRegisterMultipleBytes(reg, buf, 3, SPI_READ); + return (buf[0] << 16) | (buf[1] << 8) | buf[2]; +} + +esp_err_t readBlockData(uint8_t reg, uint8_t *buf, int len) { + return ReadRegisterMultipleBytes(reg, buf, len, SPI_READ); +} + + +/***************************** + * + * public functions + * + *****************************/ + +/** + * In general: + * @params: void + * @return: register content (measure) of the proper type depending on register width + */ + +unsigned char getVersion() +{ + return read8(VERSION); +} + +/** === setOpMode / getOpMode === + OPERATIONAL MODE REGISTER (0x13) +The general configuration of the ADE7758 is defined by writing to the OPMODE register. +Table 18 summarizes the functionality of each bit in the OPMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set. +1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set. +2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set. +3 to 5 DISMOD 0 By setting these bits, ADE7758�s ADCs can be turned off. In normal operation, these bits should be left at Logic 0. + DISMOD[2:0] Description + 0 0 0 Normal operation. + 1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels. + 0 0 1 Switch off only the current channel ADCs. + 1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths. + 0 1 0 Switch off only the voltage channel ADCs. + 1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths. + 0 1 1 Put the ADE7758 in sleep mode. + 1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ). +6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 �s after a software reset. +7 RESERVED 0 This should be left at 0. + +*/ + +void setOpMode(uint8_t m) +{ + write8(OPMODE, m); +} +uint8_t getOpMode() +{ + return read8(OPMODE); +} + +/** === setMMode / getMMode === +MEASUREMENT MODE REGISTER (0x14) +The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register. +Table 19 summarizes the functionality of each bit in the MMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency. + FREQSEL1 FREQSEL0 Source + 0 0 Phase A + 0 1 Phase B + 1 0 Phase C + 1 1 Reserved +2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers. + Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values + of the largest current and voltage waveform (over a fixed number of half-line cycles) + from Phase A. The number of half-line cycles is determined by the content of the + LINECYC register. At the end of the LINECYC number of half-line cycles, the content + of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns + on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one + bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is, + the voltage and current peak are independently processed (see the Peak Current Detection section). +5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection. + Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A. + Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C. + Note that more than one bit can be set for detection on multiple phases. + If the absolute values of the voltage or current waveform samples in the selected phases exceeds + the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the + STATUS registers are set (see the Peak Current Detection section). + +*/ + +void setMMode(uint8_t m) +{ + write8(MMODE, m); +} +uint8_t getMMode(void) +{ + return read8(MMODE); +} + +/** === setWavMode / getWavMode === +WAVEFORM MODE REGISTER (0x15) +The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register. +Table 20 summarizes the functionality of each bit in the WAVMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample. + PHSEL[1:0] Source + 0 0 Phase A + 0 1 Phase B + 1 0 Phase C + 1 1 Reserved +2 to 4 WAVSEL 0 These bits are used to select the type of waveform. + WAVSEL[2:0] Source + 0 0 0 Current + 0 0 1 Voltage + 0 1 0 Active Power Multiplier Output + 0 1 1 Reactive Power Multiplier Output + 1 0 0 VA Multiplier Output + -Others- Reserved +5 to 6 DTRT 0 These bits are used to select the data rate. + DTRT[1:0] Update Rate + 0 0 26.04 kSPS (CLKIN/3/128) + 0 1 13.02 kSPS (CLKIN/3/256) + 1 0 6.51 kSPS (CLKIN/3/512) + 1 1 3.25 kSPS (CLKIN/3/1024) +7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output + frequency that is proportional to the total apparent power (VA). + In the default state, Logic 0, the VARCF pin outputs a frequency proportional + to the total reactive power (VAR). +*/ + +void setWavMode(uint8_t m) +{ + write8(WAVMODE, m); +} +uint8_t getWavMode() +{ + return read8(WAVMODE); +} + +/** === setCompMode / getCompMode === + +COMPUTATIONAL MODE REGISTER (0x16) +The computational method of the ADE7758 is defined by writing to the COMPMODE register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers. + CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by �90�, respectively. + Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10 + AWATTHR VA � IA VA � (IA � IB) VA � (IA�IB) + BWATTHR VB � IB 0 0 + CWATTHR VC � IC VC � (IC � IB) VC � IC + + AVARHR VA � IA VA � (IA � IB) VA � (IA�IB) + BVARHR VB � IB 0 0 + CVARHR VC � IC VC � (IC � IB) VC � IC + + AVAHR VARMS � IARMS VARMS � IARMS VARMS � ARMS + BVAHR VBRMS � IBRMS (VARMS + VCRMS)/2 � IBRMS VARMS � IBRMS + CVAHR VCRMS � ICRMS VCRMS � ICRMS VCRMS � ICRMS + +2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs. + Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included. + Bit 3 and Bit 4 are for Phase B and Phase C, respectively. + Setting all three bits enables the sum of all three phases to be included in the frequency outputs + (see the Active Power Frequency Output and the Reactive Power Frequency Output sections). + +5 ABS 0 Setting this bit places the APCF output pin in absolute only mode. + Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour + accumulation registers (AWATTHR, BWATTHR, and CWATTHR). + Note that this bit only affects the APCF pin and has no effect on the content of the corresponding + registers. + +6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode. + Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation + registers (AVARHR, BVARHR, and CVARHR). + The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase, + that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive, + there is no change to the sign of the VAR. + Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding + registers. + +7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758. +*/ + +void setCompMode(uint8_t m) +{ + write8(COMPMODE, m); +} +uint8_t getCompMode(void) +{ + return read8(COMPMODE); +} + +/** === setLcycMode / getLcycMode === + +LINE CYCLE ACCUMULATION MODE REGISTER (0x17) +The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register. + +Bit Location Bit Mnemonic Default Value Description + +0 LWATT 0 Setting this bit places the watt-hour accumulation registers + (AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode. +1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers) + into line-cycle accumulation mode. +2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers) + into line-cycle accumulation mode. +3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle + accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively. + More than one phase can be selected for the zero-crossing detection, + and the accumulation time is shortened accordingly. +6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three + phases, that is, a read to those registers resets the registers to 0 after the content of the registers + have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1. +7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the + line input. +*/ + +void setLcycMode(uint8_t m) +{ + write8(LCYCMODE, m); +} +uint8_t getLcycMode(void) +{ + return read8(LCYCMODE); +} + +/** === gainSetup === + GAIN REGISTER (0x23) +The PGA configuration of the ADE7758 is defined by writing to the GAIN register. +Table 18 summarizes the functionality of each bit in the GAIN register. + +Bit Location Bit Mnemonic Default Value Description +0 to 1 PGA1 0 Current GAIN + PGA1[1:0] Description + 0 0 x1 + 0 1 x2 + 1 0 x4 + 1 1 RESERVED +2 ---- RESERVED Reserved. +3 to 4 SCALE 0 Current input full-scale select + SCALE[1:0] Description + 0 0 0.5v + 0 1 0.25v + 1 0 0.125v + 1 1 Reserved +5 to 6 PGA2 0 Voltage GAIN + PGA2[1:0] Description + 0 0 x1 + 0 1 x2 + 1 0 x4 + 1 1 RESERVED +7 INTEGRATOR 0 This bit enables the integrator on the current chanel when set. +*/ + +void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1) +{ + char pgas = (integrator << 7) | (PGA2 << 5) | (scale << 3) | (PGA1); + write8(GAIN, pgas); // write GAIN register, format is |3 bits PGA2 gain|2 bits full scale|3 bits PGA1 gain +} + +void setupDivs(uint8_t Watt_div, uint8_t VAR_div, uint8_t VA_div) +{ + write8(WDIV, Watt_div); + write8(VARDIV, VAR_div); + write8(VADIV, VA_div); +} + +/** === getMaskInterrupts / setMaskInterrupts +MASK REGISTER (0x18) +When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register. +The IRQ logic output is reset to its default collector open state when the RSTATUS register is read. +describes the function of each bit in the interrupt mask register. + +Bit Location Interrupt Flag Default Value Description +0 AEHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers, + that is, the WATTHR register is half full. +1 REHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers, + that is, the VARHR register is half full. +2 VAEHF 0 Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR + registers, that is, the VAHR register is half full. +3 SAGA 0 Enables an interrupt when there is a SAG on the line voltage of the Phase A. +4 SAGB 0 Enables an interrupt when there is a SAG on the line voltage of the Phase B. +5 SAGC 0 Enables an interrupt when there is a SAG on the line voltage of the Phase C. +6 ZXTOA 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase A. +7 ZXTOB 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase B. +8 ZXTOC 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase C. +9 ZXA 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase A + (see the Zero-Crossing Detection section). +10 ZXB 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase B + (see the Zero-Crossing Detection section). +11 ZXC 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase C + (see the Zero-Crossing Detection section). +12 LENERGY 0 Enables an interrupt when the energy accumulations over LINECYC are finished. +13 RESERVED 0 Reserved. +14 PKV 0 Enables an interrupt when the voltage input selected in the MMODE register is above + the value in the VPINTLVL register. +15 PKI 0 Enables an interrupt when the current input selected in the MMODE register is above the + value in the IPINTLVL register. +16 WFSM 0 Enables an interrupt when data is present in the WAVEMODE register. +17 REVPAP 0 Enables an interrupt when there is a sign change in the watt calculation among any one of + the phases specified by the TERMSEL bits in the COMPMODE register. +18 REVPRP 0 Enables an interrupt when there is a sign change in the VAR calculation among any one of + the phases specified by the TERMSEL bits in the COMPMODE register. +19 SEQERR 0 Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing + of Phase C but with that of Phase B. +*/ +uint32_t getMaskInterrupts(void) +{ + return read24(MASK); +} +void setMaskInterrupts(uint32_t m) +{ + write24(MASK, m); +} +/** getStatus / resetStatus +INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A) +The interrupt status register is used to determine the source of an interrupt event. +When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set. +The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set. +When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt. +All the interrupts in the interrupt status register stay at their logic high state after an event occurs. +The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read. + +Bit Location Interrupt Flag Default Value Event Description +0 AEHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three + WATTHR registers, that is, the WATTHR register is half full. +1 REHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three + VARHR registers, that is, the VARHR register is half full. +2 VAEHF 0 Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three + VAHR registers, that is, the VAHR register is half full. +3 SAGA 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A. +4 SAGB 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B. +5 SAGC 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C. +6 ZXTOA 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A. +7 ZXTOB 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B. +8 ZXTOC 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C. +9 ZXA 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A. +10 ZXB 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B. +11 ZXC 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C. +12 LENERGY 0 In line energy accumulation, indicates the end of an integration over an integer number of + half- line cycles (LINECYC). See the Calibration section. +13 RESET 1 Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758 + and sets the registers back to their default values. This bit in the STATUS or RSTATUS register + is logic high for only one clock cycle after a reset event. +14 PKV 0 Indicates that an interrupt was caused when the selected voltage input is above the value in the + VPINTLVL register. +15 PKI 0 Indicates that an interrupt was caused when the selected current input is above the value + in the IPINTLVL register. +16 WFSM 0 Indicates that new data is present in the waveform register. +17 REVPAP 0 Indicates that an interrupt was caused by a sign change in the watt calculation among any + one of the phases specified by the TERMSEL bits in the COMPMODE register. +18 REVPRP 0 Indicates that an interrupt was caused by a sign change in the VAR calculation among any + one of the phases specified by the TERMSEL bits in the COMPMODE register. +19 SEQERR 0 Indicates that an interrupt was caused by a zero crossing from Phase A + followed not by the zero crossing of Phase C but by that of Phase B. +*/ + +uint32_t getStatus(void) +{ + return read24(STATUS); +} + +uint32_t resetStatus(void) +{ + return read24(RSTATUS); +} + +/** === getAIRMS === + * Phase A Current RMS Value + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ +int32_t getAIRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXA)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(AIRMS); +} + +/** === getBIRMS === + * Phase B Current RMS Value + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ + +int32_t getBIRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXB)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(BIRMS); +} +/** === getCIRMS === + * Phase C Current RMS Value + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ +int32_t getCIRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXC)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(CIRMS); +} + +/** === getAVRMS === + * Phase A RMS Value (Voltage Channel). + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ + +int32_t getAVRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXA)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(AVRMS); +} + +/** === getBVRMS === + * Phase B RMS Value (Voltage Channel). + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ + +int32_t getBVRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXB)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(BVRMS); +} + +/** === getCVRMS === + * Phase C RMS Value (Voltage Channel). + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with the data (24 bits unsigned). + */ + +int32_t getCVRMS(void) +{ + long lastupdate = 0; + resetStatus(); // Clear all interrupts + while (!(getStatus() & ZXC)) // wait Zero-Crossing + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // break; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + return read24(CVRMS); +} + +/** === vrms === + * Returns the mean of last 20 readings of RMS voltage. Also supress first reading to avoid + * corrupted data. + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with RMS voltage value + */ + +uint32_t avrms(void) +{ + uint8_t i = 0; + uint32_t v = 0; + if (getAVRMS()) + { // Ignore first reading to avoid garbage + for (i = 0; i < 10; ++i) + { + v += getAVRMS(); + } + return v / 10; + } + else + { + return 0; + } +} +uint32_t bvrms() +{ + uint8_t i = 0; + uint32_t v = 0; + if (getBVRMS()) + { // Ignore first reading to avoid garbage + for (i = 0; i < 10; ++i) + { + v += getBVRMS(); + } + return v / 10; + } + else + { + return 0; + } +} +uint32_t cvrms() +{ + uint8_t i = 0; + uint32_t v = 0; + if (getCVRMS()) + { // Ignore first reading to avoid garbage + for (i = 0; i < 10; ++i) + { + v += getCVRMS(); + } + return v / 10; + } + else + { + return 0; + } +} + +/** === irms === + * Returns the mean of last 20 readings of RMS current. Also supress first reading to avoid + * corrupted data. + * To minimize noise, synchronize the reading of the rms register with the zero crossing + * of the voltage input and take the average of a number of readings. + * @param none + * @return long with RMS current value in hundreds of [mA], ie. 6709=67[mA] + */ + +uint32_t airms() +{ + char n = 0; + long i = 0; + if (getAIRMS()) + { // Ignore first reading to avoid garbage + for (n = 0; n < 10; ++n) + { + i += getAIRMS(); + } + return i / 10; + } + else + { + return 0; + } +} +uint32_t birms() +{ + char n = 0; + long i = 0; + if (getBIRMS()) + { // Ignore first reading to avoid garbage + for (n = 0; n < 10; ++n) + { + i += getBIRMS(); + } + return i / 10; + } + else + { + return 0; + } +} +int32_t cirms() +{ + char n = 0; + int32_t i = 0; + if (getCIRMS()) + { // Ignore first reading to avoid garbage + for (n = 0; n < 10; ++n) + { + i += getCIRMS(); + } + return i / 10; + } + else + { + return 0; + } +} + +/** === getFreq === + * Period of the Phase selected in MMode. + * @param none + * @return int with the data (12 bits unsigned). + */ + +int32_t getFreq(void) +{ + return read16(FREQ); +} + +/** setLineCyc: sets the number of cycles required for the accumulations. + **/ + +void setLineCyc(uint32_t d) +{ + write16(LINECYC, d); +} + +/** === setCurrentOffset / getCurrentOffset === + * @param none + * @return int with the data (12 bits 2-complement signed). + */ + +int32_t getACurrentOffset() +{ + int32_t data; + data = ((int32_t)(read16(AIRMSOS) << 20) >> 20); + return data; +} +int32_t getBCurrentOffset() +{ + int32_t data; + data = ((int32_t)(read16(BIRMSOS) << 20) >> 20); + return data; +} +int32_t getCCurrentOffset() +{ + int32_t data; + data = ((int32_t)(read16(CIRMSOS) << 20) >> 20); + return data; +} +void setACurrentOffset(int32_t o) +{ + write16(AIRMSOS, o); +} +void setBCurrentOffset(int32_t o) +{ + write16(BIRMSOS, o); +} +void setCCurrentOffset(int32_t o) +{ + write16(CIRMSOS, o); +} + +void setAWattOffset(int32_t o) +{ + write16(AWATTOS, o); +} +void setBWattOffset(int32_t o) +{ + write16(BWATTOS, o); +} +void setCWattOffset(int32_t o) +{ + write16(CWATTOS, o); +} + +void setAVAROffset(int32_t o) +{ + write16(AVAROS, o); +} +void setBVAROffset(int32_t o) +{ + write16(BVAROS, o); +} +void setCVAROffset(int32_t o) +{ + write16(CVAROS, o); +} +/** === setVoltageOffset / getVoltageOffset === + * @param none + * @return int with the data (12 bits 2-complement signed). + */ + +int32_t getAVoltageOffset() +{ + int32_t data; + data = ((int32_t)(read16(AVRMSOS) << 20) >> 20); + return data; +} +int32_t getBVoltageOffset() +{ + int32_t data; + data = ((int32_t)(read16(BVRMSOS) << 20) >> 20); + return data; +} +int32_t getCVoltageOffset() +{ + int32_t data; + data = ((int32_t)(read16(CVRMSOS) << 20) >> 20); + return data; +} +void setAVoltageOffset(int32_t o) +{ + write16(AVRMSOS, o); +} +void setBVoltageOffset(int32_t o) +{ + write16(BVRMSOS, o); +} +void setCVoltageOffset(int32_t o) +{ + write16(CVRMSOS, o); +} + +/** === setZeroCrossingTimeout / getZeroCrossingTimeout === + * Zero-Crossing Timeout. If no zero crossings are detected + * on Channel 2 within a time period specified by this 12-bit register, + * the interrupt request line (IRQ) is activated + * @param none + * @return int with the data (12 bits unsigned). + */ + +void setZeroCrossingTimeout(int32_t d) +{ + write16(ZXTOUT, d); +} +int32_t getZeroCrossingTimeout() +{ + return read16(ZXTOUT); +} + +/** === setPotLine(Phase) === +Set the conditions for Line accumulation in the selected phase. +Then wait for the interruption and return the phase number when it occurs. +If it does not happen for more than 1.5 seconds, it returns a 0. +**/ + +uint8_t setPotLine(uint8_t Phase, uint32_t Cycles) +{ + long lastupdate = 0; + char m = 0; + switch (Phase) + { + case PHASE_A: + m = (LWATT | LVAR | LVA | ZXSEL_A); + break; + case PHASE_B: + m = (LWATT | LVAR | LVA | ZXSEL_B); + break; + case PHASE_C: + m = (LWATT | LVAR | LVA | ZXSEL_C); + break; + } + setLcycMode(0); + setLcycMode(m); + resetStatus(); + setLineCyc(Cycles); + lastupdate = 0; + while (!(getStatus() & LENERGY)) // wait to terminar de acumular + { // wait for the selected interrupt to occur + lastupdate++; + if (lastupdate > 20) + { + return 0; // breack; + } + vTaskDelay(pdMS_TO_TICKS(20)); + } + + return Phase; +} + +/** === getWatt(phase) / getVar(phase) / getVa(phase) === +Devuelve los valores de la potencia requerida de la fase seleccionada. +Utilizar antes setPotLine(phase) para generar los valores. +**/ + +int32_t getWatt(uint8_t Phase) +{ + int32_t temp = 0; + switch (Phase) + { + case PHASE_A: + temp = read16(AWATTHR); + break; + case PHASE_B: + temp = read16(BWATTHR); + break; + case PHASE_C: + temp = read16(CWATTHR); + break; + } + return temp; +} +int32_t getVar(uint8_t Phase) +{ + int32_t temp = 0; + switch (Phase) + { + case PHASE_A: + temp = read16(AVARHR); + break; + case PHASE_B: + temp = read16(BVARHR); + break; + case PHASE_C: + temp = read16(CVARHR); + break; + } + return temp; +} +int32_t getVa(uint8_t Phase) +{ + int32_t temp = 0; + switch (Phase) + { + case PHASE_A: + temp = read16(AVAHR); + break; + case PHASE_B: + temp = read16(BVAHR); + break; + case PHASE_C: + temp = read16(CVAHR); + break; + } + return temp; +} +void setAPCFDEN(int32_t d) +{ + write16(APCFDEN, d); +} +int32_t getAPCFDEN(void) +{ + int32_t data; + data = ((int32_t)(read16(APCFDEN) << 20) >> 20); + return data; +} +void setAPCFNUM(int32_t d) +{ + write16(APCFNUM, d); +} +int32_t getAPCFNUM(void) +{ + int32_t data; + data = ((int32_t)(read16(APCFNUM) << 20) >> 20); + return data; +} +void setVARCFNUM(int32_t d) +{ + write16(VARCFNUM, d); +} +int32_t getVARCFNUM(void) +{ + int32_t data; + data = ((int32_t)(read16(VARCFNUM) << 20) >> 20); + return data; +} +void setVARCFDEN(int32_t d) +{ + write16(VARCFDEN, d); +} +int32_t getVARCFDEN(void) +{ + int32_t data; + data = ((int32_t)(read16(VARCFDEN) << 20) >> 20); + return data; +} +void setAWG(int32_t d) +{ + write16(AWG, d); +} +int32_t getAWG() +{ + return ((read16(AWG)) << 4) >> 4; +} +void setBWG(int32_t d) +{ + write16(BWG, d); +} +void setCWG(int32_t d) +{ + write16(CWG, d); +} +void setAVARG(int32_t d) +{ + write16(AVARG, d); +} +int32_t getAVARG() +{ + return ((read16(AVARG)) << 4) >> 4; +} +void setBVARG(int32_t d) +{ + write16(BVARG, d); +} +int32_t getBVARG() +{ + return ((read16(BVARG)) << 4) >> 4; +} +void setCVARG(int32_t d) +{ + write16(CVARG, d); +} +int32_t getCVARG() +{ + return ((read16(CVARG)) << 4) >> 4; +} +void setAVAG(int32_t d) +{ + write16(AVAG, d); +} +void setBVAG(int32_t d) +{ + write16(BVAG, d); +} +void setCVAG(int32_t d) +{ + write16(CVAG, d); +} +// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.c === + + +// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c === +#include "meter_ade7758.h" +#include "spi_bus_manager.h" +#include "ade7758.h" +#include "meter_events.h" + +#include +#include +#include + +#include "esp_log.h" #include "freertos/FreeRTOS.h" -#include "evse_events.h" +#include "freertos/task.h" +#include "freertos/semphr.h" -// ============================ -// EVSE Pilot Signal States -// ============================ +#include "driver/gpio.h" -typedef enum { - EVSE_STATE_A, // EV Not Connected (12V) - EVSE_STATE_B1, // EV Connected (9V, Not Authorized) - EVSE_STATE_B2, // EV Connected (9V, Authorized and Ready) - EVSE_STATE_C1, // Charging Requested (6V, Relay Off) - EVSE_STATE_C2, // Charging Active (6V, Relay On) - EVSE_STATE_D1, // Ventilation Required (3V, Relay Off) - EVSE_STATE_D2, // Ventilation Active (3V, Relay On) - EVSE_STATE_E, // Error: Pilot Short to Ground (0V) - EVSE_STATE_F // Fault: No Pilot or EVSE Unavailable -} evse_state_t; +#define TAG "meter_ade7758" -// ============================ -// Initialization & Core Control -// ============================ +// === Pinos === +#define PIN_NUM_CS 15 +#define PIN_ADUM_EN 4 -/** - * @brief Initializes the EVSE state machine. +// === Constantes de calibração === +#define VRMS_CAL 4732.78f +#define IRMS_CAL 53416.0f + +#define METER_READ_INTERVAL_MS 5000 + +// === Estrutura interna === +typedef struct { + float vrms[3]; + float irms[3]; + int watt[3]; + int var[3]; // reservado + int va[3]; // reservado +} meter_ade7758_internal_data_t; + +static meter_ade7758_internal_data_t meter_data; +static TaskHandle_t meter_task = NULL; +static SemaphoreHandle_t meter_mutex = NULL; +static uint32_t meter_watchdog_counter = 0; + +// === Controle ADUM1401 === +static void adum1401_enable(bool enable) { + gpio_config_t io_conf = { + .pin_bit_mask = BIT64(PIN_ADUM_EN), + .mode = GPIO_MODE_OUTPUT, + .pull_up_en = GPIO_PULLUP_DISABLE, + .pull_down_en = GPIO_PULLDOWN_DISABLE, + .intr_type = GPIO_INTR_DISABLE + }; + gpio_config(&io_conf); + gpio_set_level(PIN_ADUM_EN, enable ? 1 : 0); + ESP_LOGI(TAG, "ADUM1401 %s", enable ? "ativado" : "desativado"); +} + +// === Post de evento === +static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) { + meter_event_data_t evt = { + .frequency = 0, + .power_factor = 0, + .total_energy = 0 + }; + + memcpy(evt.vrms, data->vrms, sizeof(evt.vrms)); + memcpy(evt.irms, data->irms, sizeof(evt.irms)); + memcpy(evt.watt, data->watt, sizeof(evt.watt)); + + esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err)); + } +} + +// === Task de leitura === +static void meter_ade7758_task_func(void *param) { + ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada"); + + meter_ade7758_internal_data_t previous = {0}; + + while (true) { + meter_ade7758_internal_data_t meterData = {0}; + + ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion()); + + meterData.vrms[0] = avrms() / VRMS_CAL; + meterData.vrms[1] = bvrms() / VRMS_CAL; + meterData.vrms[2] = cvrms() / VRMS_CAL; + + meterData.irms[0] = airms() / IRMS_CAL; + meterData.irms[1] = birms() / IRMS_CAL; + meterData.irms[2] = cirms() / IRMS_CAL; + + if (setPotLine(PHASE_A, 20)) meterData.watt[0] = getWatt(PHASE_A); + if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B); + if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C); + + if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) { + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + meter_data = meterData; + meter_watchdog_counter++; + xSemaphoreGive(meter_mutex); + + meter_ade7758_post_event(&meterData); + } + previous = meterData; + } + + vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS)); + } +} + +// === Inicialização === +esp_err_t meter_ade7758_init(void) { + ESP_LOGI(TAG, "Inicializando ADE7758..."); + + if (!meter_mutex) { + meter_mutex = xSemaphoreCreateMutex(); + if (!meter_mutex) { + ESP_LOGE(TAG, "Falha ao criar mutex"); + return ESP_ERR_NO_MEM; + } + } + + if (!spi_bus_manager_is_initialized()) { + esp_err_t err = spi_bus_manager_init(); // usa pinos padrão + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro ao inicializar SPI: %s", esp_err_to_name(err)); + return err; + } + } + + adum1401_enable(true); // Ativa o ADUM1401 antes de usar SPI + vTaskDelay(pdMS_TO_TICKS(10)); // Delay de estabilização + + esp_err_t err = Init(spi_bus_manager_get_host(), -1, -1, -1); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro Init SPI ADE7758: %s", esp_err_to_name(err)); + return err; + } + + err = InitSpi(PIN_NUM_CS); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro ao registrar dispositivo SPI: %s", esp_err_to_name(err)); + return err; + } + + gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); + setupDivs(1, 1, 1); + setLcycMode(0x00); + resetStatus(); + + ESP_LOGI(TAG, "ADE7758 inicializado com sucesso."); + return ESP_OK; +} + +// === Execução === +esp_err_t meter_ade7758_start(void) { + if (meter_task) return ESP_ERR_INVALID_STATE; + + BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task); + return result == pdPASS ? ESP_OK : ESP_FAIL; +} + +void meter_ade7758_stop(void) { + if (meter_task) { + vTaskDelete(meter_task); + meter_task = NULL; + } +} + +// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c === + + +// === Início de: components/meter_manager/driver/meter_orno/modbus_params.h === +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 */ -void evse_state_init(void); -/** - * @brief Periodic tick function for the state machine. - */ -void evse_state_tick(void); +#ifndef _DEVICE_PARAMS +#define _DEVICE_PARAMS -// ============================ -// State Access -// ============================ +#include -/** - * @brief Returns the current EVSE state. - */ -evse_state_t evse_get_state(void); +#pragma pack(push, 1) -/** - * @brief Updates the current EVSE state and triggers events. - */ -void evse_set_state(evse_state_t state); +// Discrete Inputs +typedef struct { + uint8_t discrete_input0 : 1; + uint8_t discrete_input1 : 1; + uint8_t discrete_input2 : 1; + uint8_t discrete_input3 : 1; + uint8_t discrete_input4 : 1; + uint8_t discrete_input5 : 1; + uint8_t discrete_input6 : 1; + uint8_t discrete_input7 : 1; + uint8_t discrete_input_port1; + uint8_t discrete_input_port2; +} discrete_reg_params_t; -/** - * @brief Returns the tick count when charging session started. - */ -TickType_t evse_get_session_start(void); +// Coils +typedef struct { + uint8_t coils_port0; + uint8_t coils_port1; + uint8_t coils_port2; +} coil_reg_params_t; -/** - * @brief Converts the state enum to a human-readable string. - */ -const char* evse_state_to_str(evse_state_t state); +// Input Registers (pode manter caso use em outro driver) +typedef struct { + float input_data0; + float input_data1; + float input_data2; + float input_data3; + uint16_t data[150]; + float input_data4; + float input_data5; + float input_data6; + float input_data7; + uint16_t data_block1[150]; +} input_reg_params_t; -// ============================ -// State Evaluators -// ============================ +// Holding Registers (ajustado para os campos usados no ORNO 516) +typedef struct { + float l1_current; // 0x0016 + float l2_current; // 0x0018 + float l3_current; // 0x001A -/** - * @brief Returns true if the state represents an active session (B2, C1, C2). - */ -bool evse_state_is_session(evse_state_t state); + float l1_voltage; // 0x000E + float l2_voltage; // 0x0010 + float l3_voltage; // 0x0012 -/** - * @brief Returns true if the state represents active charging (C1, C2). - */ -bool evse_state_is_charging(evse_state_t state); - -/** - * @brief Returns true if the vehicle is plugged in. - */ -bool evse_state_is_plugged(evse_state_t state); - -// ============================ -// Authorization -// ============================ - -/** - * @brief Sets the vehicle authorization state. - */ -void evse_state_set_authorized(bool authorized); - -/** - * @brief Returns the current vehicle authorization state. - */ -bool evse_state_get_authorized(void); - -#endif // EVSE_STATE_H - -// === Fim de: components/evse/include/evse_state.h === + float total_active_power; // 0x001C + float total_reactive_power; + float active_power; + float apparent_power; + float reactive_power; +} holding_reg_params_t; -// === Início de: components/evse/include/evse_error.h === -#ifndef EVSE_ERROR_H -#define EVSE_ERROR_H +#pragma pack(pop) + +// Instâncias globais das estruturas +extern holding_reg_params_t holding_reg_params; +extern input_reg_params_t input_reg_params; +extern coil_reg_params_t coil_reg_params; +extern discrete_reg_params_t discrete_reg_params; + +#endif // !_DEVICE_PARAMS + +// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.h === + + +// === Início de: components/meter_manager/driver/meter_orno/meter_orno.h === +#ifndef ORNO_MODBUS_H_ +#define ORNO_MODBUS_H_ + +#include +#include "esp_err.h" + #include #include -#include "evse_pilot.h" - - -#define EVSE_ERR_AUTO_CLEAR_BITS ( \ - EVSE_ERR_DIODE_SHORT_BIT | \ - EVSE_ERR_TEMPERATURE_HIGH_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) - -// 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); -bool evse_error_cleared_flag(void); - -#endif // EVSE_ERROR_H - -// === Fim de: components/evse/include/evse_error.h === - - -// === Início de: components/evse/include/evse_limits.h === -#ifndef EVSE_LIMITS_H -#define EVSE_LIMITS_H - -#include -#include -#include "evse_state.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// ======================== -// Limit state control -// ======================== +#include "esp_err.h" /** - * @brief Sets the 'limit reached' flag. Used internally when a session exceeds defined thresholds. + * @brief Inicializa o driver do medidor (SPI, mutex, registradores ADE7758). */ -void evse_set_limit_reached(bool value); +esp_err_t meter_init(void); /** - * @brief Returns whether any session limit has been reached (energy, time or power). + * @brief Inicia a tarefa de leitura de dados do medidor. */ -bool evse_get_limit_reached(void); - -// ======================== -// Limit checking -// ======================== +esp_err_t meter_start(void); /** - * @brief Evaluates if the session has exceeded any configured limits. - * Should be called periodically while in charging state. + * @brief Para a tarefa de leitura e limpa os dados internos. */ -void evse_limits_check(void); +void meter_stop(void); -// ======================== -// Runtime limit configuration -// ======================== +/** + * @brief Verifica se o medidor está em execução. + * + * @return true se a tarefa estiver ativa, false caso contrário. + */ +bool meter_is_running(void); -uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); // in Wh +/** + * @brief Limpa os dados armazenados no medidor (zera todos os valores). + */ +void meter_clear_data(void); -uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); // in seconds +// ----- Leituras por fase (L1, L2, L3) ----- -uint16_t evse_get_under_power_limit(void); -void evse_set_under_power_limit(uint16_t value); // in Watts +// Tensão RMS (em volts) +float meter_get_vrms_l1(void); +float meter_get_vrms_l2(void); +float meter_get_vrms_l3(void); -// ======================== -// Default (persistent) limits -// ======================== +// Corrente RMS (em amperes) +float meter_get_irms_l1(void); +float meter_get_irms_l2(void); +float meter_get_irms_l3(void); -uint32_t evse_get_default_consumption_limit(void); -void evse_set_default_consumption_limit(uint32_t value); +// Potência ativa (W) +int meter_get_watt_l1(void); +int meter_get_watt_l2(void); +int meter_get_watt_l3(void); -uint32_t evse_get_default_charging_time_limit(void); -void evse_set_default_charging_time_limit(uint32_t value); +// Potência reativa (VAR) +int meter_get_var_l1(void); +int meter_get_var_l2(void); +int meter_get_var_l3(void); -uint16_t evse_get_default_under_power_limit(void); -void evse_set_default_under_power_limit(uint16_t value); +// Potência aparente (VA) +int meter_get_va_l1(void); +int meter_get_va_l2(void); +int meter_get_va_l3(void); + +// (Opcional) contador de watchdog para diagnóstico +uint32_t meter_get_watchdog_counter(void); #ifdef __cplusplus } #endif -#endif // EVSE_LIMITS_H +#endif /* ORNO_MODBUS_H_ */ -// === Fim de: components/evse/include/evse_limits.h === +// === Fim de: components/meter_manager/driver/meter_orno/meter_orno.h === + + +// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.h === +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" + +/** + * @brief Inicializa o driver do medidor ORNO 513 (SPI, mutex, registradores). + */ +esp_err_t meter_orno513_init(void); + +/** + * @brief Inicia a tarefa de leitura de dados do medidor ORNO 513. + */ +esp_err_t meter_orno513_start(void); + +/** + * @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 513. + */ +void meter_orno513_stop(void); + + +#ifdef __cplusplus +} +#endif + +// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.h === + + +// === Início de: components/meter_manager/driver/meter_orno/meter_orno516.h === +#ifndef METER_ORNO516_H_ +#define METER_ORNO516_H_ + +#include +#include +#include "esp_err.h" + +/** + * @brief Inicializa o driver do medidor ORNO 516 (SPI, mutex, registradores). + * + * @return esp_err_t Retorna ESP_OK se a inicialização for bem-sucedida, caso contrário retorna um erro. + */ +esp_err_t meter_orno516_init(void); + +/** + * @brief Inicia a tarefa de leitura de dados do medidor ORNO 516. + * + * @return esp_err_t Retorna ESP_OK se a tarefa for iniciada com sucesso, caso contrário retorna um erro. + */ +esp_err_t meter_orno516_start(void); + +/** + * @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 516. + */ +void meter_orno516_stop(void); + + +#ifdef __cplusplus +} +#endif + +#endif /* METER_ORNO516_H_ */ + +// === Fim de: components/meter_manager/driver/meter_orno/meter_orno516.h === + + +// === Início de: components/meter_manager/driver/meter_orno/modbus_params.c === +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ +/*===================================================================================== + * Description: + * C file to define parameter storage instances + *====================================================================================*/ +#include "modbus_params.h" + +// Here are the user defined instances for device parameters packed by 1 byte +// These are keep the values that can be accessed from Modbus master +holding_reg_params_t holding_reg_params = { 0 }; + +input_reg_params_t input_reg_params = { 0 }; + +coil_reg_params_t coil_reg_params = { 0 }; + +discrete_reg_params_t discrete_reg_params = { 0 }; + +// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.c === + + +// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.c === +#include "meter_orno513.h" +#include "modbus_params.h" +#include "mbcontroller.h" +#include "meter_events.h" +#include "esp_log.h" +#include "driver/uart.h" +#include + +#define TAG "serial_mdb_orno513" + +#define MB_PORT_NUM 2 +#define MB_DEV_SPEED 9600 +#define MB_UART_TXD 17 +#define MB_UART_RXD 16 +#define MB_UART_RTS 5 +#define UPDATE_INTERVAL (3000 / portTICK_PERIOD_MS) +#define POLL_INTERVAL (100 / portTICK_PERIOD_MS) + +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) +#define STR(x) ((const char *)(x)) +#define OPTS(min, max, step) {.opt1 = min, .opt2 = max, .opt3 = step} + +// State flag +static bool is_initialized = false; +static TaskHandle_t meter_task = NULL; + +// CID enums +enum { + CID_TOTAL_ACTIVE_ENERGY = 0, + CID_TOTAL_REACTIVE_ENERGY, + CID_ACTIVE_POWER, + CID_APPARENT_POWER, + CID_REACTIVE_POWER, + CID_L1_CURRENT, + CID_L1_VOLTAGE +}; + +// Register addresses +#define TOTALFACTIVE 0x010E +#define TOTALRACTIVE 0x0118 +#define ACTIVEPOWER 0x0104 +#define APPARENTPOWER 0x0106 +#define REACTIVEPOWER 0x0108 +#define L1CURRENT 0x0102 +#define L1VOLTAGE 0x0100 + +const mb_parameter_descriptor_t device_parameters_orno513[] = { + {CID_TOTAL_ACTIVE_ENERGY, STR("Total Active Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALFACTIVE, 2, + HOLD_OFFSET(total_active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_TOTAL_REACTIVE_ENERGY, STR("Total Reactive Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALRACTIVE, 2, + HOLD_OFFSET(total_reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_ACTIVE_POWER, STR("Active Power"), STR("W"), 1, MB_PARAM_HOLDING, ACTIVEPOWER, 2, + HOLD_OFFSET(active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_APPARENT_POWER, STR("Apparent Power"), STR("VA"), 1, MB_PARAM_HOLDING, APPARENTPOWER, 2, + HOLD_OFFSET(apparent_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_REACTIVE_POWER, STR("Reactive Power"), STR("VAR"), 1, MB_PARAM_HOLDING, REACTIVEPOWER, 2, + HOLD_OFFSET(reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(l1_current), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100, 0.1), PAR_PERMS_READ}, + + {CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(l1_voltage), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ} +}; + +const uint16_t num_device_parameters_orno513 = sizeof(device_parameters_orno513) / sizeof(device_parameters_orno513[0]); + +static void *get_param_ptr(const mb_parameter_descriptor_t *param) { + if (!param || param->param_offset == 0) return NULL; + return ((uint8_t *)&holding_reg_params + param->param_offset - 1); +} + +static void serial_mdb_task(void *param) { + esp_err_t err; + const mb_parameter_descriptor_t *desc = NULL; + + float voltage[3] = {0}; + float current[3] = {0}; + int watt[3] = {0}; + float energy = 0.0f; + + while (1) { + for (uint16_t cid = 0; cid < num_device_parameters_orno513; cid++) { + err = mbc_master_get_cid_info(cid, &desc); + if (err != ESP_OK || !desc) continue; + + void *data_ptr = get_param_ptr(desc); + uint8_t type = 0; + err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type); + + if (err == ESP_OK && data_ptr) { + int32_t raw = *(int32_t *)data_ptr; + float val = raw / 10.0f; + ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units); + + switch (cid) { + case CID_L1_VOLTAGE: voltage[0] = val; break; + case CID_L1_CURRENT: current[0] = val; break; + case CID_ACTIVE_POWER: + watt[0] = (int)(val); + watt[1] = watt[0]; + watt[2] = watt[0]; + break; + case CID_TOTAL_ACTIVE_ENERGY: + energy = val / 1000.0f; + break; + default: + break; + } + } else { + ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err)); + } + + vTaskDelay(POLL_INTERVAL); + } + + meter_event_data_t evt = { + .frequency = 0.0f, + .power_factor = 0.0f, + .total_energy = energy, + .source = "GRID" + }; + + memcpy(evt.vrms, voltage, sizeof(evt.vrms)); + memcpy(evt.irms, current, sizeof(evt.irms)); + memcpy(evt.watt, watt, sizeof(evt.watt)); + + esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10)); + + + vTaskDelay(UPDATE_INTERVAL); + } +} + +esp_err_t meter_orno513_init(void) { + if (is_initialized) { + ESP_LOGW(TAG, "meter_orno513 already initialized"); + return ESP_ERR_INVALID_STATE; + } + + ESP_LOGI(TAG, "meter_orno513_init"); + + mb_communication_info_t comm = { + .port = MB_PORT_NUM, + .mode = MB_MODE_RTU, + .baudrate = MB_DEV_SPEED, + .parity = UART_PARITY_DISABLE + }; + + void *handler = NULL; + + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &handler); + if (err != ESP_OK) { + ESP_LOGE(TAG, "mbc_master_init failed"); + return err; + } + + ESP_ERROR_CHECK(mbc_master_setup(&comm)); + ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(mbc_master_start()); + ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX)); + vTaskDelay(pdMS_TO_TICKS(5)); + ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno513, num_device_parameters_orno513)); + + is_initialized = true; + return ESP_OK; +} + +esp_err_t meter_orno513_start(void) { + + ESP_LOGI(TAG, "meter_orno513_start"); + + if (!is_initialized) { + ESP_LOGE(TAG, "meter_orno513 not initialized"); + return ESP_ERR_INVALID_STATE; + } + + if (meter_task == NULL) { + xTaskCreate(serial_mdb_task, "meter_orno513_task", 4096, NULL, 3, &meter_task); + ESP_LOGI(TAG, "meter_orno513 task started"); + } + + return ESP_OK; +} + +void meter_orno513_stop(void) { + if (!is_initialized) { + ESP_LOGW(TAG, "meter_orno513 not initialized"); + return; + } + + ESP_LOGI(TAG, "Stopping meter_orno513"); + + uart_driver_delete(MB_PORT_NUM); + + esp_err_t err = mbc_master_destroy(); + if (err != ESP_OK) { + ESP_LOGW(TAG, "mbc_master_destroy() returned %s", esp_err_to_name(err)); + } + + is_initialized = false; +} + +// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.c === diff --git a/projeto_parte3.c b/projeto_parte3.c old mode 100755 new mode 100644 index f3c1ae4..d8f470a --- a/projeto_parte3.c +++ b/projeto_parte3.c @@ -1,1058 +1,824 @@ -// === Início de: components/evse/include/evse_events.h === -#ifndef EVSE_EVENTS_H -#define EVSE_EVENTS_H +// === Início de: components/meter_manager/driver/meter_orno/meter_orno516.c === +#include "meter_orno516.h" +#include "meter_events.h" +#include "modbus_params.h" +#include "mbcontroller.h" +#include "esp_log.h" +#include "driver/uart.h" +#include +#define TAG "serial_mdb_orno516" + +#define MB_PORT_NUM 2 +#define MB_DEV_SPEED 9600 +#define MB_UART_TXD 17 +#define MB_UART_RXD 16 +#define MB_UART_RTS 5 + +#define UPDATE_INTERVAL (5000 / portTICK_PERIOD_MS) +#define POLL_INTERVAL (100 / portTICK_PERIOD_MS) + +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) +#define STR(fieldname) ((const char *)(fieldname)) +#define OPTS(min_val, max_val, step_val) {.opt1 = min_val, .opt2 = max_val, .opt3 = step_val} + +// Estado do driver +static bool is_initialized = false; +static TaskHandle_t meter_task = NULL; + +#define L1VOLTAGE 0x000E +#define L2VOLTAGE 0x0010 +#define L3VOLTAGE 0x0012 +#define L1CURRENT 0x0016 +#define L2CURRENT 0x0018 +#define L3CURRENT 0x001A +#define TOTALACTIVEPOWER 0x001C + +enum { + CID_L1_CURRENT = 0, + CID_L2_CURRENT, + CID_L3_CURRENT, + CID_L1_VOLTAGE, + CID_L2_VOLTAGE, + CID_L3_VOLTAGE, + CID_TOTAL_ACTIVE_POWER +}; + +const mb_parameter_descriptor_t device_parameters_orno516[] = { + {CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(l1_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + {CID_L2_CURRENT, STR("L2 Current"), STR("A"), 1, MB_PARAM_HOLDING, L2CURRENT, 2, + HOLD_OFFSET(l2_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + {CID_L3_CURRENT, STR("L3 Current"), STR("A"), 1, MB_PARAM_HOLDING, L3CURRENT, 2, + HOLD_OFFSET(l3_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + {CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(l1_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ}, + {CID_L2_VOLTAGE, STR("L2 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L2VOLTAGE, 2, + HOLD_OFFSET(l2_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ}, + {CID_L3_VOLTAGE, STR("L3 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L3VOLTAGE, 2, + HOLD_OFFSET(l3_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ}, + {CID_TOTAL_ACTIVE_POWER, STR("Total Active Power"), STR("W"), 1, MB_PARAM_HOLDING, TOTALACTIVEPOWER, 2, + HOLD_OFFSET(total_active_power), PARAM_TYPE_FLOAT, 4, OPTS(0, 100000, 1), PAR_PERMS_READ} +}; + +const uint16_t num_device_parameters_orno516 = sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]); + +float ReverseFloat(const float inFloat) { + float retVal; + char *floatToConvert = (char *)&inFloat; + char *returnFloat = (char *)&retVal; + returnFloat[0] = floatToConvert[2]; + returnFloat[1] = floatToConvert[3]; + returnFloat[2] = floatToConvert[0]; + returnFloat[3] = floatToConvert[1]; + return retVal; +} + +static void *get_param_ptr(const mb_parameter_descriptor_t *param) { + if (!param || param->param_offset == 0) return NULL; + return ((uint8_t *)&holding_reg_params + param->param_offset - 1); +} + + +static void meter_orno516_post_event(float *voltage, float *current, int *power) { + meter_event_data_t evt = { + .source = "GRID", + .frequency = 0.0f, // ORNO-516 não fornece + .power_factor = 0.0f, // idem + .total_energy = 0.0f // idem + }; + memcpy(evt.vrms, voltage, sizeof(evt.vrms)); + memcpy(evt.irms, current, sizeof(evt.irms)); + memcpy(evt.watt, power, sizeof(evt.watt)); + + esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, + &evt, sizeof(evt), pdMS_TO_TICKS(10)); + + if (err != ESP_OK) { + ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err)); + } +} + +static void serial_mdb_task(void *param) { + esp_err_t err; + const mb_parameter_descriptor_t *desc = NULL; + float voltage[3] = {0}, current[3] = {0}; + int power[3] = {0}; + + while (1) { + for (uint16_t cid = 0; cid < num_device_parameters_orno516; cid++) { + err = mbc_master_get_cid_info(cid, &desc); + if (err != ESP_OK || !desc) continue; + + void *data_ptr = get_param_ptr(desc); + uint8_t type = 0; + err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type); + + if (err == ESP_OK && data_ptr) { + float val = ReverseFloat(*(float *)data_ptr); + ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units); + + switch (cid) { + case CID_L1_VOLTAGE: voltage[0] = val; break; + case CID_L2_VOLTAGE: voltage[1] = val; break; + case CID_L3_VOLTAGE: voltage[2] = val; break; + case CID_L1_CURRENT: current[0] = val; break; + case CID_L2_CURRENT: current[1] = val; break; + case CID_L3_CURRENT: current[2] = val; break; + case CID_TOTAL_ACTIVE_POWER: + power[0] = (int)(val / 3); + power[1] = (int)(val / 3); + power[2] = (int)(val / 3); + break; + default: + break; + } + } else { + ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err)); + } + + vTaskDelay(POLL_INTERVAL); + } + + meter_orno516_post_event(voltage, current, power); + vTaskDelay(UPDATE_INTERVAL); + } +} + + +esp_err_t meter_orno516_init(void) { + if (is_initialized) { + ESP_LOGW(TAG, "Already initialized"); + return ESP_ERR_INVALID_STATE; + } + + // Tenta apagar UART apenas se estiver inicializada + if (uart_is_driver_installed(MB_PORT_NUM)) { + uart_driver_delete(MB_PORT_NUM); + ESP_LOGI(TAG, "UART driver deleted"); + } + + mbc_master_destroy(); // OK mesmo que não esteja inicializado + + mb_communication_info_t comm = { + .port = MB_PORT_NUM, + .mode = MB_MODE_RTU, + .baudrate = MB_DEV_SPEED, + .parity = UART_PARITY_EVEN + }; + + void *handler = NULL; + ESP_ERROR_CHECK(mbc_master_init(MB_PORT_SERIAL_MASTER, &handler)); + ESP_ERROR_CHECK(mbc_master_setup(&comm)); + ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(mbc_master_start()); + ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX)); + vTaskDelay(pdMS_TO_TICKS(5)); + ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno516, num_device_parameters_orno516)); + + is_initialized = true; + return ESP_OK; +} + +esp_err_t meter_orno516_start(void) { + if (!is_initialized) { + ESP_LOGE(TAG, "Not initialized"); + return ESP_ERR_INVALID_STATE; + } + + if (meter_task == NULL) { + xTaskCreate(serial_mdb_task, "meter_orno516_task", 4096, NULL, 3, &meter_task); + ESP_LOGI(TAG, "Task started"); + } + + return ESP_OK; +} + +void meter_orno516_stop(void) { + if (!is_initialized) { + ESP_LOGW(TAG, "Not initialized, skipping stop"); + return; + } + + if (meter_task) { + vTaskDelete(meter_task); + meter_task = NULL; + ESP_LOGI(TAG, "Task stopped"); + } + + mbc_master_destroy(); + + if (uart_is_driver_installed(MB_PORT_NUM)) { + uart_driver_delete(MB_PORT_NUM); + ESP_LOGI(TAG, "UART driver deleted"); + } + + is_initialized = false; + ESP_LOGI(TAG, "Meter ORNO-516 cleaned up"); +} + +// === Fim de: components/meter_manager/driver/meter_orno/meter_orno516.c === + + +// === Início de: components/meter_manager/driver/meter_zigbee/meter_zigbee.h === #pragma once -#include "esp_event.h" -ESP_EVENT_DECLARE_BASE(EVSE_EVENTS); - -typedef enum { - EVSE_EVENT_INIT, - EVSE_EVENT_STATE_CHANGED, - // Outros eventos possíveis futuramente -} evse_event_id_t; - -typedef enum { - EVSE_STATE_EVENT_IDLE, - EVSE_STATE_EVENT_WAITING, - EVSE_STATE_EVENT_CHARGING, - EVSE_STATE_EVENT_FAULT -} evse_state_event_t; - -typedef struct { - evse_state_event_t state; -} evse_state_event_data_t; - - -#endif // EVSE_EVENTS_H - -// === Fim de: components/evse/include/evse_events.h === - - -// === Início de: components/evse/include/evse_api.h === -#ifndef EVSE_API_H -#define EVSE_API_H +#ifdef __cplusplus +extern "C" { +#endif #include #include #include "esp_err.h" -#include "evse_state.h" // Define evse_state_t -// Inicialização -void evse_init(void); -void evse_process(void); +/** + * @brief Inicializa o driver do medidor Zigbee (UART, mutex, etc.). + * + * @return ESP_OK se a inicialização for bem-sucedida, erro caso contrário. + */ +esp_err_t meter_zigbee_init(void); -// Estado -evse_state_t evse_get_state(void); -const char* evse_state_to_str(evse_state_t state); -bool evse_is_connector_plugged(evse_state_t state); -bool evse_is_limit_reached(void); +/** + * @brief Inicia a tarefa de leitura dos dados do medidor Zigbee. + * + * @return ESP_OK se a tarefa for iniciada com sucesso, erro caso contrário. + */ +esp_err_t meter_zigbee_start(void); -// Autorização e disponibilidade -bool evse_is_enabled(void); -void evse_set_enabled(bool value); -bool evse_is_available(void); -void evse_set_available(bool value); -bool evse_is_require_auth(void); -void evse_set_require_auth(bool value); - -// Corrente -uint16_t evse_get_charging_current(void); -esp_err_t evse_set_charging_current(uint16_t value); -uint16_t evse_get_default_charging_current(void); -esp_err_t evse_set_default_charging_current(uint16_t value); -uint8_t evse_get_max_charging_current(void); -esp_err_t evse_set_max_charging_current(uint8_t value); - -// Temperatura -uint8_t evse_get_temp_threshold(void); -esp_err_t evse_set_temp_threshold(uint8_t value); - -// RCM / Socket -bool evse_get_socket_outlet(void); -esp_err_t evse_set_socket_outlet(bool value); -bool evse_is_rcm(void); -esp_err_t evse_set_rcm(bool value); - -// Limites -uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); -uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); -uint16_t evse_get_under_power_limit(void); -void evse_set_under_power_limit(uint16_t value); - -void evse_set_limit_reached(bool value); - -// Energia total acumulada da sessão (em Wh) -uint32_t evse_get_total_energy(void); - -// Potência instantânea medida (em W) -uint32_t evse_get_instant_power(void); - -// Limites default -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); +/** + * @brief Interrompe a tarefa e limpa recursos (UART, mutex, etc.). + */ +void meter_zigbee_stop(void); -uint32_t evse_get_total_energy(void); -uint32_t evse_get_instant_power(void); +#ifdef __cplusplus +} +#endif + +// === Fim de: components/meter_manager/driver/meter_zigbee/meter_zigbee.h === - -#endif // EVSE_API_H - -// === Fim de: components/evse/include/evse_api.h === - - -// === Início de: components/loadbalancer/src/loadbalancer_events.c === -#include "loadbalancer_events.h" - -// Define a base de eventos para o loadbalancer -ESP_EVENT_DEFINE_BASE(LOADBALANCER_EVENTS); - -// === Fim de: components/loadbalancer/src/loadbalancer_events.c === - - -// === Início de: components/loadbalancer/src/loadbalancer.c === -#include "loadbalancer.h" -#include "loadbalancer_events.h" -#include "esp_event.h" -#include "esp_log.h" +// === Início de: components/meter_manager/driver/meter_zigbee/meter_zigbee.c === +#include "meter_zigbee.h" +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "input_filter.h" -#include "nvs_flash.h" -#include "nvs.h" -#include +#include "freertos/semphr.h" +#include "esp_log.h" +#include "esp_system.h" +#include "driver/uart.h" +#include "driver/gpio.h" #include "meter_events.h" -#include "evse_events.h" + +#define TAG "meter_zigbee" + +// UART config +#define UART_PORT UART_NUM_1 +#define TXD_PIN GPIO_NUM_17 +#define RXD_PIN GPIO_NUM_16 +#define UART_BUF_SIZE 128 +#define RX_FRAME_SIZE 14 + +// Zigbee Attribute IDs +#define ATTR_CURRENT_L1 0x0006 +#define ATTR_CURRENT_L2 0x0007 +#define ATTR_CURRENT_L3 0x0008 +#define ATTR_VOLTAGE_L1 0x0266 +#define ATTR_CURRENT_L1_ALT 0x0267 +#define ATTR_POWER_L1 0x0268 +#define ATTR_VOLTAGE_L2 0x0269 +#define ATTR_CURRENT_L2_ALT 0x026A +#define ATTR_POWER_L2 0x026B +#define ATTR_VOLTAGE_L3 0x026C +#define ATTR_CURRENT_L3_ALT 0x026D +#define ATTR_POWER_L3 0x026E +#define ATTR_FREQUENCY 0x0265 +#define ATTR_POWER_FACTOR 0x020F +#define ATTR_TOTAL_ENERGY 0x0201 + +#define PHASE_COUNT 3 +#define PHASE_L1 0 +#define PHASE_L2 1 +#define PHASE_L3 2 + +// Internal meter state +typedef struct { + float vrms[PHASE_COUNT]; + float irms[PHASE_COUNT]; + int watt[PHASE_COUNT]; + int var[PHASE_COUNT]; + int va[PHASE_COUNT]; + float frequency; + float power_factor; + float total_energy; +} meter_zigbee_data_t; + +static bool phase_updated[PHASE_COUNT] = {false, false, false}; +static meter_zigbee_data_t meter_data = {0}; +static SemaphoreHandle_t meter_mutex = NULL; +static TaskHandle_t meter_zigbee_task = NULL; -static const char *TAG = "loadbalancer"; +static void meter_zigbee_post_event(void) { + meter_event_data_t evt = { + .source = "GRID", + .frequency = meter_data.frequency, + .power_factor = meter_data.power_factor, + .total_energy = meter_data.total_energy + }; -// Limites configuráveis -#define MIN_CHARGING_CURRENT_LIMIT 6 // A -#define MAX_CHARGING_CURRENT_LIMIT 32 // A -#define MIN_GRID_CURRENT_LIMIT 6 // A -#define MAX_GRID_CURRENT_LIMIT 100 // A + memcpy(evt.vrms, meter_data.vrms, sizeof(evt.vrms)); + memcpy(evt.irms, meter_data.irms, sizeof(evt.irms)); + memcpy(evt.watt, meter_data.watt, sizeof(evt.watt)); -// Parâmetros -static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT; -static bool loadbalancer_enabled = false; + esp_err_t err = esp_event_post(METER_EVENT, + METER_EVENT_DATA_READY, + &evt, + sizeof(evt), + pdMS_TO_TICKS(10)); -static float grid_current = 0.0f; -static float evse_current = 0.0f; -static input_filter_t grid_filter; -static input_filter_t evse_filter; + if (err != ESP_OK) { + ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err)); + } +} -#define NVS_NAMESPACE "loadbalancing" -#define NVS_MAX_GRID_CURRENT "max_grid_curr" -#define NVS_LOADBALANCER_ENABLED "enabled" -static void loadbalancer_meter_event_handler(void *handler_arg, - esp_event_base_t base, - int32_t id, - void *event_data) -{ - if (id != METER_EVENT_DATA_READY || event_data == NULL) +static void handle_zigbee_frame(const uint8_t *buf, size_t len) { + ESP_LOGI(TAG, "Received UART frame (%d bytes):", len); + ESP_LOG_BUFFER_HEX(TAG, buf, len); + + if (len < RX_FRAME_SIZE) { + ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len); return; + } - const meter_event_data_t *evt = (const meter_event_data_t *)event_data; + uint16_t attr = buf[2] | (buf[3] << 8); + uint8_t size = buf[5]; - ESP_LOGI(TAG, "Received meter event from source: %s", evt->source); - ESP_LOGI(TAG, "Raw IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]); - ESP_LOGI(TAG, "Raw VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[2]); - ESP_LOGI(TAG, "Raw Power: [W1=%d, W2=%d, W3=%d]", evt->watt[0], evt->watt[1], evt->watt[2]); - ESP_LOGI(TAG, "Freq: %.2f Hz | PF: %.2f | Energy: %.3f kWh", - evt->frequency, evt->power_factor, evt->total_energy); + if (size != 8) { + ESP_LOGW(TAG, "Unsupported payload size: %d", size); + return; + } - // Calcula a corrente máxima entre as 3 fases - float max_irms = evt->irms[0]; - for (int i = 1; i < 3; ++i) - { - if (evt->irms[i] > max_irms) - { - max_irms = evt->irms[i]; + uint16_t volt_raw = (buf[6] << 8) | buf[7]; + uint32_t current_raw = (buf[8] << 16) | (buf[9] << 8) | buf[10]; + uint32_t power_raw = (buf[11] << 16) | (buf[12] << 8) | buf[13]; + + float volt = volt_raw / 10.0f; + float current = current_raw / 100.0f; + float power = power_raw / 1000.0f; + + ESP_LOGI(TAG, "Parsed Attr 0x%04X: V=%.1fV I=%.2fA P=%.1fW", attr, volt, current, power); + + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + switch (attr) { + case ATTR_CURRENT_L1: + case ATTR_CURRENT_L1_ALT: + meter_data.irms[PHASE_L1] = current; + meter_data.vrms[PHASE_L1] = volt; + meter_data.watt[PHASE_L1] = (int)power; + phase_updated[PHASE_L1] = true; + break; + case ATTR_CURRENT_L2: + case ATTR_CURRENT_L2_ALT: + meter_data.irms[PHASE_L2] = current; + meter_data.vrms[PHASE_L2] = volt; + meter_data.watt[PHASE_L2] = (int)power; + phase_updated[PHASE_L2] = true; + break; + case ATTR_CURRENT_L3: + case ATTR_CURRENT_L3_ALT: + meter_data.irms[PHASE_L3] = current; + meter_data.vrms[PHASE_L3] = volt; + meter_data.watt[PHASE_L3] = (int)power; + phase_updated[PHASE_L3] = true; + break; + case ATTR_POWER_FACTOR: + meter_data.power_factor = current; + break; + case ATTR_FREQUENCY: + meter_data.frequency = current; + break; + case ATTR_TOTAL_ENERGY: + meter_data.total_energy = current; + break; + default: + ESP_LOGW(TAG, "Unknown attr: 0x%04X", attr); + break; + } + xSemaphoreGive(meter_mutex); + } + + // Verifica se todas as 3 fases foram atualizadas + if (phase_updated[PHASE_L1] && phase_updated[PHASE_L2] && phase_updated[PHASE_L3]) { + meter_zigbee_post_event(); + memset(phase_updated, 0, sizeof(phase_updated)); + } +} + +static void meter_zigbee_task_func(void *param) { + uint8_t *buf = malloc(RX_FRAME_SIZE); + if (!buf) { + ESP_LOGE(TAG, "Failed to allocate buffer"); + vTaskDelete(NULL); + return; + } + + ESP_LOGI(TAG, "Zigbee meter task started"); + + while (1) { + int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(5000)); + if (len == RX_FRAME_SIZE) { + handle_zigbee_frame(buf, len); + } else if (len == 0) { + ESP_LOGD(TAG, "UART timeout with no data"); + } else { + ESP_LOGW(TAG, "Incomplete frame received (%d bytes)", len); } } - ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms); + free(buf); + vTaskDelete(NULL); +} - // Atualiza com filtro exponencial dependendo da origem - if (strncmp(evt->source, "GRID", 4) == 0) - { - grid_current = input_filter_update(&grid_filter, max_irms); - ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current); +esp_err_t meter_zigbee_init(void) { + ESP_LOGI(TAG, "Initializing Zigbee meter"); + + if (!meter_mutex) { + meter_mutex = xSemaphoreCreateMutex(); + if (!meter_mutex) return ESP_ERR_NO_MEM; } - else if (strncmp(evt->source, "EVSE", 4) == 0) - { - evse_current = input_filter_update(&evse_filter, max_irms); - ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current); + + uart_config_t config = { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .source_clk = UART_SCLK_DEFAULT + }; + + ESP_ERROR_CHECK(uart_param_config(UART_PORT, &config)); + ESP_ERROR_CHECK(uart_set_pin(UART_PORT, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); + ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_BUF_SIZE * 2, 0, 0, NULL, 0)); + + return ESP_OK; +} + +esp_err_t meter_zigbee_start(void) { + if (meter_zigbee_task) return ESP_ERR_INVALID_STATE; + + xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 3, &meter_zigbee_task); + return ESP_OK; +} + +void meter_zigbee_stop(void) { + if (meter_zigbee_task) { + vTaskDelete(meter_zigbee_task); + meter_zigbee_task = NULL; } - else - { - ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source); + + uart_driver_delete(UART_PORT); + + if (meter_mutex) { + vSemaphoreDelete(meter_mutex); + meter_mutex = NULL; } } -static void loadbalancer_evse_event_handler(void *handler_arg, - esp_event_base_t base, - int32_t id, - void *event_data) -{ - const evse_state_event_data_t *evt = (const evse_state_event_data_t *)event_data; - - ESP_LOGI(TAG, "EVSE state changed: %d", evt->state); - - switch (evt->state) - { - case EVSE_STATE_EVENT_IDLE: - // Vehicle is disconnected - current flow can be reduced or reset - ESP_LOGI(TAG, "EVSE is IDLE - possible to release current"); - break; - - case EVSE_STATE_EVENT_WAITING: - // EV is connected but not charging yet (e.g., waiting for authorization) - ESP_LOGI(TAG, "EVSE is waiting - connected but not charging"); - break; - - case EVSE_STATE_EVENT_CHARGING: - grid_current = 0.0f; - evse_current = 0.0f; - // Charging has started - maintain or monitor current usage - ESP_LOGI(TAG, "EVSE is charging"); - break; - - case EVSE_STATE_EVENT_FAULT: - // A fault has occurred - safety measures may be needed - ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing"); - // Optional: disable load balancing during fault condition - // loadbalancer_set_enabled(false); - break; - - default: - ESP_LOGW(TAG, "Unknown EVSE state: %d", evt->state); - break; - } +bool meter_zigbee_is_running(void) { + return meter_zigbee_task != NULL; } -// Carrega configuração do NVS -static esp_err_t loadbalancer_load_config() -{ +// === Fim de: components/meter_manager/driver/meter_zigbee/meter_zigbee.c === + + +// === Início de: components/meter_manager/src/meter_manager.c === +#include "meter_manager.h" +#include "esp_log.h" +#include "meter_ade7758.h" +#include "meter_orno513.h" +#include "meter_orno516.h" +#include "meter_zigbee.h" +#include "nvs_flash.h" +#include "nvs.h" +#include + +static const char *TAG = "meter_manager"; + +// Tipos de medidores EVSE e GRID +static meter_type_t meter_evse_type = METER_TYPE_NONE; +static meter_type_t meter_grid_type = METER_TYPE_NONE; + +#define NVS_NAMESPACE "meterconfig" +#define NVS_EVSE_MODEL "evse_model" +#define NVS_GRID_MODEL "grid_model" + +// Função unificada para ler ou inicializar um modelo de medidor +static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type) { nvs_handle_t handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open NVS handle for %s: %s", key, esp_err_to_name(err)); return err; } - bool needs_commit = false; - uint8_t temp_u8; - - // max_grid_current - err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8); - if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT) - { - max_grid_current = temp_u8; - } - else - { - max_grid_current = MAX_GRID_CURRENT_LIMIT; - nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current); - ESP_LOGW(TAG, "max_grid_current missing or invalid, setting default: %d", max_grid_current); - needs_commit = true; - } - - // loadbalancer_enabled - err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8); - if (err == ESP_OK && temp_u8 <= 1) - { - loadbalancer_enabled = (temp_u8 != 0); - } - else - { - loadbalancer_enabled = false; - nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0); - ESP_LOGW(TAG, "loadbalancer_enabled missing or invalid, setting default: 0"); - needs_commit = true; - } - - if (needs_commit) - { + uint8_t value = 0; + err = nvs_get_u8(handle, key, &value); + if (err == ESP_OK && value <= METER_TYPE_TRIF_ZIGBEE) { + *type = (meter_type_t)value; + ESP_LOGI(TAG, "Loaded meter type %d from NVS key '%s'", value, key); + } else { + *type = METER_TYPE_NONE; + nvs_set_u8(handle, key, *type); nvs_commit(handle); + ESP_LOGW(TAG, "Invalid or missing key '%s', setting default (NONE)", key); } nvs_close(handle); return ESP_OK; } -// Salva o estado habilitado no NVS -void loadbalancer_set_enabled(bool enabled) -{ - ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled); +static esp_err_t write_meter_model_to_nvs(const char *key, meter_type_t meter_type) { nvs_handle_t handle; esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); - return; - } - - err = nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, enabled ? 1 : 0); - if (err == ESP_OK) - { - nvs_commit(handle); - loadbalancer_enabled = enabled; - ESP_LOGI(TAG, "Load balancing enabled state saved"); - - loadbalancer_state_event_t evt = { - .enabled = enabled, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_STATE_CHANGED, - &evt, - sizeof(evt), - portMAX_DELAY); - } - else - { - ESP_LOGE(TAG, "Failed to save loadbalancer_enabled"); - } - - nvs_close(handle); -} - -// Define e salva o limite de corrente da rede -esp_err_t load_balancing_set_max_grid_current(uint8_t value) -{ - if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT) - { - ESP_LOGE(TAG, "Invalid grid current limit: %d", value); - return ESP_ERR_INVALID_ARG; - } - - nvs_handle_t handle; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to open NVS handle for writing"); return err; } - err = nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, value); - if (err == ESP_OK) - { - nvs_commit(handle); - max_grid_current = value; - ESP_LOGI(TAG, "max_grid_current set to: %d", value); - } - else - { - ESP_LOGE(TAG, "Failed to save max_grid_current to NVS"); + err = nvs_set_u8(handle, key, (uint8_t)meter_type); + if (err == ESP_OK) { + err = nvs_commit(handle); + ESP_LOGI(TAG, "Saved meter type %d to NVS key '%s'", meter_type, key); + } else { + ESP_LOGE(TAG, "Failed to write meter type to NVS key '%s'", key); } nvs_close(handle); return err; } -uint8_t load_balancing_get_max_grid_current(void) -{ - return max_grid_current; -} -bool loadbalancer_is_enabled(void) -{ - return loadbalancer_enabled; -} +// Função para inicializar o medidor EVSE +esp_err_t meter_manager_evse_init() { + esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type); + if (err != ESP_OK) return err; -// Tarefa principal com eventos -void loadbalancer_task(void *param) -{ - while (true) - { - if (!loadbalancer_enabled) - { - vTaskDelay(pdMS_TO_TICKS(1000)); - continue; - } + ESP_LOGI(TAG, "Initializing EVSE meter of type %s", meter_type_to_str(meter_evse_type)); - float available = max_grid_current - grid_current + evse_current; - - if (available < MIN_CHARGING_CURRENT_LIMIT) - { - available = MIN_CHARGING_CURRENT_LIMIT; - } - else if (available > max_grid_current) - { - available = max_grid_current; - } - - ESP_LOGD(TAG, "Setting EVSE current limit: %.1f A", available); - - loadbalancer_charging_limit_event_t evt = { - .limit = available, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED, - &evt, - sizeof(evt), - portMAX_DELAY); - - vTaskDelay(pdMS_TO_TICKS(1000)); + switch (meter_evse_type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: return meter_ade7758_init(); + case METER_TYPE_ORNO513: return meter_orno513_init(); + case METER_TYPE_ORNO516: return meter_orno516_init(); + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init(); + default: return ESP_ERR_INVALID_ARG; } } -void loadbalancer_init(void) -{ - ESP_LOGI(TAG, "Initializing load balancer"); +esp_err_t meter_manager_grid_init() { + esp_err_t err = load_or_init_meter_model(NVS_GRID_MODEL, &meter_grid_type); + if (err != ESP_OK) return err; - if (loadbalancer_load_config() != ESP_OK) - { - ESP_LOGW(TAG, "Failed to load/init config. Using in-memory defaults."); - } + ESP_LOGI(TAG, "Initializing GRID meter of type %s", meter_type_to_str(meter_grid_type)); - input_filter_init(&grid_filter, 0.3f); - input_filter_init(&evse_filter, 0.3f); - - if (xTaskCreate(loadbalancer_task, "loadbalancer", 4096, NULL, 4, NULL) != pdPASS) - { - ESP_LOGE(TAG, "Failed to create loadbalancer task"); - } - - loadbalancer_state_event_t evt = { - .enabled = loadbalancer_enabled, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_INIT, - &evt, - sizeof(evt), - portMAX_DELAY); - - ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY, - &loadbalancer_meter_event_handler, NULL)); - - ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, - EVSE_EVENT_STATE_CHANGED, - &loadbalancer_evse_event_handler, - NULL)); -} - -// === Fim de: components/loadbalancer/src/loadbalancer.c === - - -// === Início de: components/loadbalancer/src/input_filter.c === -#include "input_filter.h" - -void input_filter_init(input_filter_t *filter, float alpha) { - if (filter) { - filter->alpha = alpha; - filter->value = 0.0f; - filter->initialized = 0; + switch (meter_grid_type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: return meter_ade7758_init(); + case METER_TYPE_ORNO513: return meter_orno513_init(); + case METER_TYPE_ORNO516: return meter_orno516_init(); + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init(); + default: return ESP_ERR_INVALID_ARG; } } -float input_filter_update(input_filter_t *filter, float input) { - if (!filter) return input; - - if (!filter->initialized) { - filter->value = input; - filter->initialized = 1; - } else { - filter->value = filter->alpha * input + (1.0f - filter->alpha) * filter->value; +esp_err_t meter_manager_grid_start() { + meter_type_t type = meter_manager_grid_get_model(); + switch (type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: return meter_ade7758_start(); + case METER_TYPE_ORNO513: return meter_orno513_start(); + case METER_TYPE_ORNO516: return meter_orno516_start(); + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start(); + default: return ESP_ERR_INVALID_ARG; } - - return filter->value; } -// === Fim de: components/loadbalancer/src/input_filter.c === +esp_err_t meter_manager_grid_stop(void) { + meter_type_t type = meter_manager_grid_get_model(); + switch (type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: meter_ade7758_stop(); break; + case METER_TYPE_ORNO513: meter_orno513_stop(); break; + case METER_TYPE_ORNO516: meter_orno516_stop(); break; + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break; + default: return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} -// === Início de: components/loadbalancer/include/loadbalancer_events.h === -#pragma once -#include "esp_event.h" -#include -#include -#include "esp_timer.h" +esp_err_t meter_manager_evse_set_model(meter_type_t meter_type) { + meter_evse_type = meter_type; + return write_meter_model_to_nvs(NVS_EVSE_MODEL, meter_evse_type); +} -ESP_EVENT_DECLARE_BASE(LOADBALANCER_EVENTS); +esp_err_t meter_manager_grid_set_model(meter_type_t meter_type) { + meter_grid_type = meter_type; + return write_meter_model_to_nvs(NVS_GRID_MODEL, meter_grid_type); +} -typedef enum { - LOADBALANCER_EVENT_INIT, - LOADBALANCER_EVENT_STATE_CHANGED, - LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED -} loadbalancer_event_id_t; +esp_err_t meter_manager_evse_start() { + meter_type_t type = meter_manager_evse_get_model(); + switch (type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: return meter_ade7758_start(); + case METER_TYPE_ORNO513: return meter_orno513_start(); + case METER_TYPE_ORNO516: return meter_orno516_start(); + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start(); + default: return ESP_ERR_INVALID_ARG; + } +} -typedef struct { - float limit; - int64_t timestamp_us; -} loadbalancer_charging_limit_event_t; +esp_err_t meter_manager_evse_stop(void) { + meter_type_t type = meter_manager_evse_get_model(); + switch (type) { + case METER_TYPE_NONE: return ESP_OK; + case METER_TYPE_ADE7758: meter_ade7758_stop(); break; + case METER_TYPE_ORNO513: meter_orno513_stop(); break; + case METER_TYPE_ORNO516: meter_orno516_stop(); break; + case METER_TYPE_MONO_ZIGBEE: + case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break; + default: return ESP_ERR_INVALID_ARG; + } + return ESP_OK; +} -typedef struct { - bool enabled; - int64_t timestamp_us; -} loadbalancer_state_event_t; +bool meter_manager_evse_is_enabled(void) { + return meter_manager_evse_get_model() != METER_TYPE_NONE; +} + +meter_type_t meter_manager_evse_get_model(void) { + return meter_evse_type; +} + +meter_type_t meter_manager_grid_get_model(void) { + return meter_grid_type; +} + +const char* meter_type_to_str(meter_type_t type) { + switch (type) { + case METER_TYPE_NONE: return "NENHUM"; + case METER_TYPE_ADE7758: return "IC ADE"; + case METER_TYPE_ORNO513: return "ORNO-513"; + case METER_TYPE_ORNO516: return "ORNO-516"; + case METER_TYPE_MONO_ZIGBEE: return "MONO-ZIGBEE"; + case METER_TYPE_TRIF_ZIGBEE: return "TRIF-ZIGBEE"; + default: return "NENHUM"; + } +} + +meter_type_t string_to_meter_type(const char *str) { + if (!str) return METER_TYPE_NONE; + if (strcmp(str, "IC ADE") == 0) return METER_TYPE_ADE7758; + if (strcmp(str, "ORNO-513") == 0) return METER_TYPE_ORNO513; + if (strcmp(str, "ORNO-516") == 0) return METER_TYPE_ORNO516; + if (strcmp(str, "MONO-ZIGBEE") == 0) return METER_TYPE_MONO_ZIGBEE; + if (strcmp(str, "TRIF-ZIGBEE") == 0) return METER_TYPE_TRIF_ZIGBEE; + return METER_TYPE_NONE; +} + +// === Fim de: components/meter_manager/src/meter_manager.c === -// === Fim de: components/loadbalancer/include/loadbalancer_events.h === +// === Início de: components/meter_manager/src/meter_events.c === +#include "meter_events.h" + +// Define a base de eventos +ESP_EVENT_DEFINE_BASE(METER_EVENT); + +// === Fim de: components/meter_manager/src/meter_events.c === -// === Início de: components/loadbalancer/include/loadbalancer.h === -#ifndef LOADBALANCER_H_ -#define LOADBALANCER_H_ +// === Início de: components/meter_manager/include/meter_manager.h === +#ifndef METER_MANAGER_H +#define METER_MANAGER_H -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include #include "esp_err.h" +#include // Para garantir que 'bool' seja reconhecido + +// Definindo tipos de medidores possíveis para EVSE e Grid +typedef enum { + METER_TYPE_NONE, // Nenhum Medidor + METER_TYPE_ADE7758, // ADE7758 + METER_TYPE_ORNO513, // ORNO 513 + METER_TYPE_ORNO516, // ORNO 516 + METER_TYPE_MONO_ZIGBEE, // Medidor Zigbee (Mono) + METER_TYPE_TRIF_ZIGBEE // Medidor Zigbee (Trifásico) +} meter_type_t; + +/** + * @brief Funções para gerenciar o medidor EVSE (ex: ADE7758). + */ + +// Inicializa o medidor EVSE com o tipo especificado (ex: ADE7758) +esp_err_t meter_manager_evse_init(void); + +// Inicia o medidor EVSE com o tipo especificado +esp_err_t meter_manager_evse_start(void); + +// Para o medidor EVSE +esp_err_t meter_manager_evse_stop(void); + +// Verifica se o medidor EVSE está habilitado +bool meter_manager_evse_is_enabled(void); + +// Define o modelo do medidor EVSE (ADE7758, etc) +esp_err_t meter_manager_evse_set_model(meter_type_t meter_type); + +// Retorna o modelo do medidor EVSE (ADE7758, etc) +meter_type_t meter_manager_evse_get_model(void); /** - * @brief Initializes the load balancer. - * - * This function configures the load balancer and its resources, including - * any necessary persistence configurations, such as storage in NVS (Non-Volatile Storage). - * This function prepares the system to perform load balancing efficiently. + * @brief Funções para gerenciar o medidor Grid (ORNO 513, ORNO 516, Zigbee). */ -void loadbalancer_init(void); -/** - * @brief Continuous task for the load balancer. - * - * This function executes the load balancing logic continuously, typically in a FreeRTOS task. - * It performs balance calculations, checks the grid current and energy conditions, and adjusts - * the outputs as necessary to ensure efficient energy consumption. - * - * @param param Input parameter, usually used to pass additional information or relevant context - * for the task execution. - */ -void loadbalancer_task(void *param); +// Inicializa o medidor Grid com o tipo especificado (ORNO 513, ORNO 516, Zigbee) +esp_err_t meter_manager_grid_init(void); -/** - * @brief Enables or disables the load balancing system. - * - * This function allows enabling or disabling the load balancing system. When enabled, the load - * balancer starts managing the grid current based on the configured limits. If disabled, the system - * operates without balancing. - * - * The configuration is persisted in NVS, ensuring that the choice is maintained across system restarts. - * - * @param value If true, enables load balancing. If false, disables it. - */ -void loadbalancer_set_enabled(bool value); +// Inicia o medidor Grid com o tipo especificado +esp_err_t meter_manager_grid_start(void); -/** - * @brief Checks if load balancing is enabled. - * - * This function returns the current status of the load balancing system. - * - * @return Returns true if load balancing is enabled, otherwise returns false. - */ -bool loadbalancer_is_enabled(void); +// Para o medidor Grid +esp_err_t meter_manager_grid_stop(void); -/** - * @brief Sets the maximum grid current. - * - * This function configures the maximum grid current that can be supplied to the load balancing system. - * The value set ensures that the system does not overload the electrical infrastructure and respects - * the safety limits. - * - * @param max_grid_current The maximum allowed current (in amperes) for the load balancing system. - * This value should be appropriate for the grid capacity and the installation. - */ -esp_err_t load_balancing_set_max_grid_current(uint8_t max_grid_current); +// Habilita ou desabilita o medidor Grid +void meter_manager_grid_set_enabled(bool value); + +// Define o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee) +esp_err_t meter_manager_grid_set_model(meter_type_t meter_type); + +// Retorna o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee) +meter_type_t meter_manager_grid_get_model(void); + +// Função auxiliar para converter o tipo de medidor em uma string +const char* meter_type_to_str(meter_type_t type); + +meter_type_t string_to_meter_type(const char *str); + +#endif // METER_MANAGER_H + +// === Fim de: components/meter_manager/include/meter_manager.h === -/** - * @brief Gets the maximum grid current. - * - * This function retrieves the current maximum grid current limit. - * - * @return The maximum grid current (in amperes). - */ -uint8_t load_balancing_get_max_grid_current(void); +// === Início de: components/meter_manager/include/meter_events.h === +#ifndef METER_EVENTS_H +#define METER_EVENTS_H -#ifdef __cplusplus -} -#endif - -#endif /* LOADBALANCER_H_ */ - -// === Fim de: components/loadbalancer/include/loadbalancer.h === - - -// === Início de: components/loadbalancer/include/input_filter.h === -#pragma once +#include "esp_event.h" +#include "meter_manager.h" // Para meter_type_t #ifdef __cplusplus extern "C" { #endif +// Base de eventos dos medidores +ESP_EVENT_DECLARE_BASE(METER_EVENT); + +// IDs de eventos emitidos por medidores +typedef enum { + METER_EVENT_DATA_READY = 0, + METER_EVENT_ERROR, + METER_EVENT_STARTED, + METER_EVENT_STOPPED +} meter_event_id_t; + +// Estrutura de dados enviados com METER_EVENT_DATA_READY typedef struct { - float alpha; ///< Fator de suavização (0.0 a 1.0) - float value; ///< Último valor filtrado - int initialized; ///< Flag de inicialização -} input_filter_t; + const char *source; // "GRID" ou "EVSE" + float vrms[3]; // Tensão por fase + float irms[3]; // Corrente por fase + int watt[3]; // Potência ativa por fase + float frequency; // Frequência da rede (Hz) + float power_factor; // Fator de potência + float total_energy; // Energia acumulada (kWh) +} meter_event_data_t; -/** - * @brief Inicializa o filtro com o fator alpha desejado. - * @param filter Ponteiro para a estrutura do filtro - * @param alpha Valor entre 0.0 (mais lento) e 1.0 (sem filtro) - */ -void input_filter_init(input_filter_t *filter, float alpha); - -/** - * @brief Atualiza o valor filtrado com uma nova entrada. - * @param filter Ponteiro para o filtro - * @param input Valor bruto - * @return Valor suavizado - */ -float input_filter_update(input_filter_t *filter, float input); #ifdef __cplusplus } #endif -// === Fim de: components/loadbalancer/include/input_filter.h === +#endif // METER_EVENTS_H - -// === Início de: components/auth/src/auth_events.c === -#include "auth_events.h" - -ESP_EVENT_DEFINE_BASE(AUTH_EVENTS); - -// === Fim de: components/auth/src/auth_events.c === - - -// === Início de: components/auth/src/wiegand.c === -/** - * @file wiegand.c - * - * ESP-IDF Wiegand protocol receiver - */ -#include -#include -#include -#include -#include "wiegand.h" - -static const char *TAG = "wiegand"; - -#define TIMER_INTERVAL_US 50000 // 50ms - -#define CHECK(x) \ - do \ - { \ - esp_err_t __; \ - if ((__ = x) != ESP_OK) \ - return __; \ - } while (0) -#define CHECK_ARG(VAL) \ - do \ - { \ - if (!(VAL)) \ - return ESP_ERR_INVALID_ARG; \ - } while (0) - -static void isr_disable(wiegand_reader_t *reader) -{ - gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_DISABLE); - gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_DISABLE); -} - -static void isr_enable(wiegand_reader_t *reader) -{ - gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_NEGEDGE); - 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) - return; - - int d0 = gpio_get_level(reader->gpio_d0); - int d1 = gpio_get_level(reader->gpio_d1); - - // ignore equal - if (d0 == d1) - return; - // overflow - if (reader->bits >= reader->size * 8) - return; - - esp_timer_stop(reader->timer); - - uint8_t value; - if (reader->bit_order == WIEGAND_MSB_FIRST) - value = (d0 ? 0x80 : 0) >> (reader->bits % 8); - else - value = (d0 ? 1 : 0) << (reader->bits % 8); - - if (reader->byte_order == WIEGAND_MSB_FIRST) - reader->buf[reader->size - reader->bits / 8 - 1] |= value; - else - reader->buf[reader->bits / 8] |= value; - - reader->bits++; - - esp_timer_start_once(reader->timer, TIMER_INTERVAL_US); -} - -static void timer_handler(void *arg) -{ - wiegand_reader_t *reader = (wiegand_reader_t *)arg; - - ESP_LOGI(TAG, "Got %d bits of data", reader->bits); - - wiegand_reader_disable(reader); - - if (reader->callback) - reader->callback(reader); - - wiegand_reader_enable(reader); - - isr_enable(reader); -} - -//////////////////////////////////////////////////////////////////////////////// - -esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, - bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, - wiegand_order_t byte_order) -{ - CHECK_ARG(reader && buf_size && callback); - - /* - esp_err_t res = gpio_install_isr_service(0); - if (res != ESP_OK && res != ESP_ERR_INVALID_STATE) - return res; - */ - - memset(reader, 0, sizeof(wiegand_reader_t)); - reader->gpio_d0 = gpio_d0; - reader->gpio_d1 = gpio_d1; - reader->size = buf_size; - reader->buf = calloc(buf_size, 1); - reader->bit_order = bit_order; - reader->byte_order = byte_order; - reader->callback = callback; - - esp_timer_create_args_t timer_args = { - .name = TAG, - .arg = reader, - .callback = timer_handler, - .dispatch_method = ESP_TIMER_TASK}; - CHECK(esp_timer_create(&timer_args, &reader->timer)); - - CHECK(gpio_set_direction(gpio_d0, GPIO_MODE_INPUT)); - CHECK(gpio_set_direction(gpio_d1, GPIO_MODE_INPUT)); - CHECK(gpio_set_pull_mode(gpio_d0, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); - CHECK(gpio_set_pull_mode(gpio_d1, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); - isr_disable(reader); - CHECK(gpio_isr_handler_add(gpio_d0, isr_handler, reader)); - CHECK(gpio_isr_handler_add(gpio_d1, isr_handler, reader)); - isr_enable(reader); - reader->enabled = true; - - ESP_LOGI(TAG, "Reader initialized on D0=%d, D1=%d", gpio_d0, gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_disable(wiegand_reader_t *reader) -{ - CHECK_ARG(reader); - - isr_disable(reader); - esp_timer_stop(reader->timer); - reader->enabled = false; - - ESP_LOGI(TAG, "Reader on D0=%d, D1=%d disabled", reader->gpio_d0, reader->gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_enable(wiegand_reader_t *reader) -{ - CHECK_ARG(reader); - - reader->bits = 0; - memset(reader->buf, 0, reader->size); - - isr_enable(reader); - reader->enabled = true; - - ESP_LOGI(TAG, "Reader on D0=%d, D1=%d enabled", reader->gpio_d0, reader->gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_done(wiegand_reader_t *reader) -{ - CHECK_ARG(reader && reader->buf); - - isr_disable(reader); - CHECK(gpio_isr_handler_remove(reader->gpio_d0)); - CHECK(gpio_isr_handler_remove(reader->gpio_d1)); - esp_timer_stop(reader->timer); - CHECK(esp_timer_delete(reader->timer)); - free(reader->buf); - - ESP_LOGI(TAG, "Reader removed"); - - return ESP_OK; -} - -// === Fim de: components/auth/src/wiegand.c === - - -// === Início de: components/auth/src/auth.c === -/* - * auth.c - */ - -#include "auth.h" -#include "auth_events.h" -#include "esp_event.h" -#include -#include -#include -#include -#include -#include "wiegand_reader.h" -#include "nvs_flash.h" -#include "nvs.h" - -#define MAX_TAGS 50 - -static const char *TAG = "Auth"; - -static bool enabled = false; -static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; -static int tag_count = 0; - -// =========================== -// Persistência em NVS -// =========================== - -static void load_auth_config(void) { - nvs_handle_t handle; - esp_err_t err = nvs_open("auth", NVS_READONLY, &handle); - if (err == ESP_OK) { - uint8_t val; - if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) { - enabled = val; - ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled); - } - nvs_close(handle); - } else { - ESP_LOGW(TAG, "No stored auth config found. Using default."); - } -} - -static void save_auth_config(void) { - nvs_handle_t handle; - if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) { - nvs_set_u8(handle, "enabled", enabled); - nvs_commit(handle); - nvs_close(handle); - ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled); - } else { - ESP_LOGE(TAG, "Failed to save auth config."); - } -} - -// =========================== -// Internos -// =========================== - -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 true; - //TODO - //return false; -} - -// =========================== -// API pública -// =========================== - -void auth_set_enabled(bool value) { - enabled = value; - save_auth_config(); - ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED"); - - auth_enabled_event_data_t event = { .enabled = enabled }; - esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY); -} - -bool auth_is_enabled(void) { - return enabled; -} - -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; - - strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); - valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; - tag_count++; - ESP_LOGI(TAG, "Tag added: %s", 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++) { - strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); - } - tag_count--; - ESP_LOGI(TAG, "Tag removed: %s", tag); - return true; - } - } - return false; -} - -bool auth_tag_exists(const char *tag) { - return is_tag_valid(tag); -} - -void auth_list_tags(void) { - ESP_LOGI(TAG, "Registered Tags (%d):", tag_count); - for (int i = 0; i < tag_count; i++) { - ESP_LOGI(TAG, "- %s", valid_tags[i]); - } -} - -void auth_init(void) { - load_auth_config(); // carrega estado de ativação - - if (enabled) { - initWiegand(); // só inicia se estiver habilitado - ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)"); - } else { - ESP_LOGI(TAG, "Auth disabled, Wiegand reader not started"); - } - - auth_enabled_event_data_t evt = { .enabled = enabled }; - esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); - - ESP_LOGI(TAG, "Estado inicial AUTH enviado (enabled = %d)", enabled); -} - -void auth_process_tag(const char *tag) { - if (!tag || !auth_is_enabled()) { - ESP_LOGW(TAG, "Auth disabled or NULL tag received."); - return; - } - - auth_tag_event_data_t event; - strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1); - event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0'; - event.authorized = is_tag_valid(tag); - - ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED"); - - esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY); -} - -// === Fim de: components/auth/src/auth.c === - - -// === Início de: components/auth/src/wiegand_reader.c === -#include -#include -#include -#include -#include -#include -#include -#include "auth.h" - -#define CONFIG_EXAMPLE_BUF_SIZE 50 - -static const char *TAG = "WiegandReader"; - -static wiegand_reader_t reader; -static QueueHandle_t queue = NULL; - -typedef struct { - uint8_t data[CONFIG_EXAMPLE_BUF_SIZE]; - size_t bits; -} data_packet_t; - -static void reader_callback(wiegand_reader_t *r) { - data_packet_t p; - p.bits = r->bits; - memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE); - xQueueSendToBack(queue, &p, 0); -} - -static void wiegand_task(void *arg) { - queue = xQueueCreate(5, sizeof(data_packet_t)); - if (!queue) { - ESP_LOGE(TAG, "Failed to create queue"); - vTaskDelete(NULL); - return; - } - - ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18, - true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); - - data_packet_t p; - while (1) { - ESP_LOGI(TAG, "Waiting for Wiegand data..."); - if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) { - ESP_LOGI(TAG, "Bits received: %d", p.bits); - - char tag[20] = {0}; - - if (p.bits == 26) { - snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]); - } else if (p.bits == 34) { - snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]); - } else { - ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits); - continue; - } - - ESP_LOGI(TAG, "Tag read: %s", tag); - auth_process_tag(tag); // agora delega toda a lógica à auth.c - } - } -} - -void initWiegand(void) { - ESP_LOGI(TAG, "Initializing Wiegand reader"); - xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL); -} - -// === Fim de: components/auth/src/wiegand_reader.c === +// === Fim de: components/meter_manager/include/meter_events.h === diff --git a/projeto_parte4.c b/projeto_parte4.c deleted file mode 100755 index f67b378..0000000 --- a/projeto_parte4.c +++ /dev/null @@ -1,896 +0,0 @@ - - -// === Início de: components/auth/include/auth.h === -#ifndef AUTH_H -#define AUTH_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// Tamanho máximo de uma tag RFID (incluindo '\0') -#define AUTH_TAG_MAX_LEN 20 - -/// Estrutura de evento emitida após leitura de uma tag -typedef struct { - char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida - bool authorized; ///< true se a tag for reconhecida como válida -} auth_event_t; - -/** - * @brief Inicializa o sistema de autenticação. - * - * - Carrega a configuração (enabled) da NVS - * - Inicia o leitor Wiegand - * - Emite evento AUTH_EVENT_INIT com estado atual - */ -void auth_init(void); - -/** - * @brief Ativa ou desativa o uso de autenticação via RFID. - * - * Esta configuração é persistida em NVS. Se desativado, o sistema - * considerará todas as autorizações como aceitas. - * - * @param value true para ativar, false para desativar - */ -void auth_set_enabled(bool value); - -/** - * @brief Verifica se o sistema de autenticação está habilitado. - */ -bool auth_is_enabled(void); - -/** - * @brief Adiciona uma nova tag RFID à lista de autorizadas. - * - * @param tag String da tag (máx AUTH_TAG_MAX_LEN-1) - * @return true se a tag foi adicionada, false se já existia ou inválida - */ -bool auth_add_tag(const char *tag); - -/** - * @brief Remove uma tag previamente cadastrada. - * - * @param tag String da tag - * @return true se foi removida, false se não encontrada - */ -bool auth_remove_tag(const char *tag); - -/** - * @brief Verifica se uma tag já está registrada como válida. - */ -bool auth_tag_exists(const char *tag); - -/** - * @brief Lista todas as tags válidas atualmente registradas (via logs). - */ -void auth_list_tags(void); - -/** - * @brief Processa uma tag RFID lida (chamada normalmente pelo leitor). - * - * - Verifica validade - * - Emite evento AUTH_EVENT_TAG_PROCESSED - * - Inicia timer de expiração se autorizada - */ -void auth_process_tag(const char *tag); - - -#ifdef __cplusplus -} -#endif - -#endif // AUTH_H - -// === Fim de: components/auth/include/auth.h === - - -// === Início de: components/auth/include/auth_events.h === -#pragma once -#include "esp_event.h" - -#define AUTH_EVENT_TAG_MAX_LEN 32 - -ESP_EVENT_DECLARE_BASE(AUTH_EVENTS); - -typedef enum { - AUTH_EVENT_TAG_PROCESSED, - AUTH_EVENT_ENABLED_CHANGED, - AUTH_EVENT_INIT, -} auth_event_id_t; - -typedef struct { - char tag[AUTH_EVENT_TAG_MAX_LEN]; - bool authorized; -} auth_tag_event_data_t; - -typedef struct { - bool enabled; -} auth_enabled_event_data_t; - -// === Fim de: components/auth/include/auth_events.h === - - -// === Início de: components/auth/include/wiegand.h === -/* - * Copyright (c) 2021 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file wiegand.h - * @defgroup wiegand wiegand - * @{ - * - * ESP-IDF Wiegand protocol receiver - * - * Copyright (c) 2021 Ruslan V. Uss - * - * BSD Licensed as described in the file LICENSE - */ -#ifndef __WIEGAND_H__ -#define __WIEGAND_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct wiegand_reader wiegand_reader_t; - -typedef void (*wiegand_callback_t)(wiegand_reader_t *reader); - -/** - * Bit and byte order of data - */ -typedef enum { - WIEGAND_MSB_FIRST = 0, - WIEGAND_LSB_FIRST -} wiegand_order_t; - -/** - * Wiegand reader descriptor - */ -struct wiegand_reader -{ - gpio_num_t gpio_d0, gpio_d1; - wiegand_callback_t callback; - wiegand_order_t bit_order; - wiegand_order_t byte_order; - - uint8_t *buf; - size_t size; - size_t bits; - esp_timer_handle_t timer; - bool start_parity; - bool enabled; -}; - -/** - * @brief Create and initialize reader instance. - * - * @param reader Reader descriptor - * @param gpio_d0 GPIO pin for D0 - * @param gpio_d1 GPIO pin for D0 - * @param internal_pullups Enable internal pull-up resistors for D0 and D1 GPIO - * @param buf_size Reader buffer size in bytes, must be large enough to - * contain entire Wiegand key - * @param callback Callback function for processing received codes - * @param bit_order Bit order of data - * @param byte_order Byte order of data - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, - bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, - wiegand_order_t byte_order); - -/** - * @brief Disable reader - * - * While reader is disabled, it will not receive new data - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_disable(wiegand_reader_t *reader); - -/** - * @brief Enable reader - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_enable(wiegand_reader_t *reader); - -/** - * @brief Delete reader instance. - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_done(wiegand_reader_t *reader); - -#ifdef __cplusplus -} -#endif - -/**@}*/ - -#endif /* __WIEGAND_H__ */ - -// === Fim de: components/auth/include/wiegand.h === - - -// === Início de: components/auth/include/wiegand_reader.h === -#ifndef WIEGAND_READER_H -#define WIEGAND_READER_H - -#ifdef __cplusplus -extern "C" { -#endif - -void initWiegand(void); - -#ifdef __cplusplus -} -#endif - -#endif // WIEGAND_READER_H - -// === Fim de: components/auth/include/wiegand_reader.h === - - -// === Início de: components/rest_api/src/ocpp_api.c === -// ========================= -// ocpp_api.c -// ========================= -#include "ocpp_api.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "ocpp_api"; - -static struct { - char url[256]; - char chargeBoxId[128]; - char certificate[256]; - char privateKey[256]; -} ocpp_config = {"", "", "", ""}; - -static esp_err_t ocpp_get_status_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *status = cJSON_CreateObject(); - cJSON_AddStringToObject(status, "status", "connected"); - char *str = cJSON_Print(status); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(status); - return ESP_OK; -} - -static esp_err_t ocpp_get_config_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *json = cJSON_CreateObject(); - cJSON_AddStringToObject(json, "url", ocpp_config.url); - cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId); - cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate); - cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey); - char *str = cJSON_Print(json); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(json); - return ESP_OK; -} - -static esp_err_t ocpp_post_config_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - cJSON *url = cJSON_GetObjectItem(json, "url"); - if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url)); - cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId"); - if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId)); - cJSON *cert = cJSON_GetObjectItem(json, "certificate"); - if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate)); - cJSON *key = cJSON_GetObjectItem(json, "privateKey"); - if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey)); - cJSON_Delete(json); - httpd_resp_sendstr(req, "OCPP config atualizada com sucesso"); - return ESP_OK; -} - -void register_ocpp_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t status_uri = { - .uri = "/api/v1/ocpp", - .method = HTTP_GET, - .handler = ocpp_get_status_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &status_uri); - - httpd_uri_t get_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_GET, - .handler = ocpp_get_config_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - httpd_uri_t post_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_POST, - .handler = ocpp_post_config_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/ocpp_api.c === - - -// === Início de: components/rest_api/src/static_file_api.c === -#include "static_file_api.h" -#include "esp_log.h" -#include -#include -#include "esp_vfs.h" - -static const char *TAG = "static_file_api"; - -#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) -#define SCRATCH_BUFSIZE (10240) - -typedef struct rest_server_context { - char base_path[ESP_VFS_PATH_MAX + 1]; - char scratch[SCRATCH_BUFSIZE]; -} rest_server_context_t; - -#define CHECK_FILE_EXTENSION(filename, ext) \ - (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) - -static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) { - const char *type = "text/plain"; - if (CHECK_FILE_EXTENSION(filepath, ".html")) type = "text/html"; - else if (CHECK_FILE_EXTENSION(filepath, ".js")) type = "application/javascript"; - else if (CHECK_FILE_EXTENSION(filepath, ".css")) type = "text/css"; - else if (CHECK_FILE_EXTENSION(filepath, ".png")) type = "image/png"; - else if (CHECK_FILE_EXTENSION(filepath, ".ico")) type = "image/x-icon"; - else if (CHECK_FILE_EXTENSION(filepath, ".svg")) type = "image/svg+xml"; - return httpd_resp_set_type(req, type); -} - -static esp_err_t static_get_handler(httpd_req_t *req) { - char filepath[FILE_PATH_MAX]; - rest_server_context_t *ctx = (rest_server_context_t *) req->user_ctx; - - strlcpy(filepath, ctx->base_path, sizeof(filepath)); - if (req->uri[strlen(req->uri) - 1] == '/') { - strlcat(filepath, "/index.html", sizeof(filepath)); - } else { - strlcat(filepath, req->uri, sizeof(filepath)); - } - - int fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - // fallback para /index.html (SPA) - ESP_LOGW(TAG, "Arquivo não encontrado: %s. Tentando index.html", filepath); - strlcpy(filepath, ctx->base_path, sizeof(filepath)); - strlcat(filepath, "/index.html", sizeof(filepath)); - fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Arquivo não encontrado"); - return ESP_FAIL; - } - } - - set_content_type_from_file(req, filepath); - - char *chunk = ctx->scratch; - ssize_t read_bytes; - do { - read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); - if (read_bytes == -1) { - ESP_LOGE(TAG, "Erro lendo arquivo: %s", filepath); - close(fd); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao ler arquivo"); - return ESP_FAIL; - } else if (read_bytes > 0) { - if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { - close(fd); - httpd_resp_sendstr_chunk(req, NULL); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao enviar arquivo"); - return ESP_FAIL; - } - } - } while (read_bytes > 0); - - close(fd); - httpd_resp_send_chunk(req, NULL, 0); - return ESP_OK; -} - -void register_static_file_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t uri = { - .uri = "/*", - .method = HTTP_GET, - .handler = static_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &uri); -} - -// === Fim de: components/rest_api/src/static_file_api.c === - - -// === Início de: components/rest_api/src/meters_settings_api.c === -#include "meters_settings_api.h" -#include "meter_manager.h" // Atualizado para usar o novo manager -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "meters_settings_api"; - -// Função para recuperar as configurações dos contadores -static esp_err_t meters_config_get_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters"); - - httpd_resp_set_type(req, "application/json"); - - cJSON *config = cJSON_CreateObject(); - - // Recuperando as configurações dos contadores - meter_type_t gridmeterType = meter_manager_grid_get_model(); - meter_type_t evsemeterType = meter_manager_evse_get_model(); - - ESP_LOGI(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType)); - ESP_LOGI(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType)); - - // Adicionando os tipos de contadores ao objeto JSON - cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType)); - cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType)); - - // Convertendo o objeto JSON para uma string - const char *json_str = cJSON_Print(config); - ESP_LOGI(TAG, "Returning meters config: %s", json_str); - - httpd_resp_sendstr(req, json_str); - - // Liberação da memória - free((void *)json_str); - cJSON_Delete(config); - - return ESP_OK; -} - -// Função para atualizar as configurações dos contadores -static esp_err_t meters_config_post_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - - if (len <= 0) { - ESP_LOGE(TAG, "Received empty body in POST request"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - - buf[len] = '\0'; // Garantir que a string está terminada - - ESP_LOGI(TAG, "Received POST data: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Failed to parse JSON data"); - // Resposta detalhada de erro - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format"); - return ESP_FAIL; - } - - // Atualizando os contadores - cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter"); - if (gridmeter) { - meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type - ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring); - meter_manager_grid_set_model(gridType); - } - - cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter"); - if (evsemeter) { - meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type - ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring); - meter_manager_evse_set_model(evseType); - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Meters updated successfully"); - - ESP_LOGI(TAG, "Meters configuration updated successfully"); - - return ESP_OK; -} - -// Registrando os manipuladores de URI para os contadores -void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { - ESP_LOGI(TAG, "Registering URI handlers for meters settings"); - - // URI para o método GET - httpd_uri_t meters_get_uri = { - .uri = "/api/v1/config/meters", - .method = HTTP_GET, - .handler = meters_config_get_handler, - .user_ctx = ctx - }; - ESP_LOGI(TAG, "Registering GET handler for /api/v1/config/meters"); - httpd_register_uri_handler(server, &meters_get_uri); - - // URI para o método POST - httpd_uri_t meters_post_uri = { - .uri = "/api/v1/config/meters", - .method = HTTP_POST, - .handler = meters_config_post_handler, - .user_ctx = ctx - }; - ESP_LOGI(TAG, "Registering POST handler for /api/v1/config/meters"); - httpd_register_uri_handler(server, &meters_post_uri); -} - -// === Fim de: components/rest_api/src/meters_settings_api.c === - - -// === Início de: components/rest_api/src/rest_main.c === -#include "rest_main.h" -#include "evse_settings_api.h" -#include "meters_settings_api.h" -#include "loadbalancing_settings_api.h" -#include "network_api.h" -#include "ocpp_api.h" -#include "auth_api.h" -#include "dashboard_api.h" -#include "static_file_api.h" -#include "esp_log.h" - - -static const char *TAG = "rest_main"; - -esp_err_t rest_server_init(const char *base_path) { - ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path); - - rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t)); - if (!ctx) { - ESP_LOGE(TAG, "Failed to allocate memory for REST context"); - return ESP_ERR_NO_MEM; - } - - strlcpy(ctx->base_path, base_path, sizeof(ctx->base_path)); - - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.uri_match_fn = httpd_uri_match_wildcard; - config.max_uri_handlers = 32; - - httpd_handle_t server = NULL; - esp_err_t err = httpd_start(&server, &config); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err)); - free(ctx); - return err; - } - - ESP_LOGI(TAG, "HTTP server started successfully"); - - // Register endpoint groups - register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_network_handlers(server, ctx); // Apenas chamando a função sem comparação - register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação - register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação - register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação - register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação - - ESP_LOGI(TAG, "All REST API endpoint groups registered successfully"); - - return ESP_OK; -} - -// === Fim de: components/rest_api/src/rest_main.c === - - -// === Início de: components/rest_api/src/network_api.c === -// ========================= -// network_api.c -// ========================= - -#include "network_api.h" -#include "esp_log.h" -#include "cJSON.h" -#include "wifi.h" -#include "mqtt.h" - -static const char *TAG = "network_api"; - -typedef struct { - bool enabled; - char ssid[33]; - char password[65]; -} wifi_task_data_t; - - -static void wifi_apply_config_task(void *param) { - wifi_task_data_t *data = (wifi_task_data_t *)param; - ESP_LOGI("wifi_task", "Applying Wi-Fi config in background task"); - wifi_set_config(data->enabled, data->ssid, data->password); - free(data); - vTaskDelete(NULL); -} - -static esp_err_t wifi_get_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi"); - - httpd_resp_set_type(req, "application/json"); - - // Obter dados da NVS via wifi.c - bool enabled = wifi_get_enabled(); - char ssid[33] = {0}; - char password[65] = {0}; - - wifi_get_ssid(ssid); - wifi_get_password(password); - - // Criar JSON - cJSON *json = cJSON_CreateObject(); - cJSON_AddBoolToObject(json, "enabled", enabled); - cJSON_AddStringToObject(json, "ssid", ssid); - cJSON_AddStringToObject(json, "password", password); - - // Enviar resposta - char *response = cJSON_Print(json); - httpd_resp_sendstr(req, response); - - // Limpeza - free(response); - cJSON_Delete(json); - - return ESP_OK; -} - -static esp_err_t wifi_post_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Handling POST /api/v1/config/wifi"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) return ESP_FAIL; - buf[len] = '\0'; - - cJSON *json = cJSON_Parse(buf); - if (!json) return ESP_FAIL; - - // Valores padrão - bool enabled = false; - const char *ssid = NULL; - const char *password = NULL; - - cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled"); - if (cJSON_IsBool(j_enabled)) enabled = j_enabled->valueint; - - cJSON *j_ssid = cJSON_GetObjectItem(json, "ssid"); - if (cJSON_IsString(j_ssid)) ssid = j_ssid->valuestring; - - cJSON *j_password = cJSON_GetObjectItem(json, "password"); - if (cJSON_IsString(j_password)) password = j_password->valuestring; - - // Enviar resposta antes de alterar Wi-Fi - httpd_resp_sendstr(req, "Wi-Fi config atualizada com sucesso"); - - // Alocar struct para passar para a task - wifi_task_data_t *task_data = malloc(sizeof(wifi_task_data_t)); - if (!task_data) { - cJSON_Delete(json); - ESP_LOGE(TAG, "Memory allocation failed for Wi-Fi task"); - return ESP_ERR_NO_MEM; - } - - task_data->enabled = enabled; - strncpy(task_data->ssid, ssid ? ssid : "", sizeof(task_data->ssid)); - strncpy(task_data->password, password ? password : "", sizeof(task_data->password)); - - // Criar task normal com função C - xTaskCreate( - wifi_apply_config_task, - "wifi_config_task", - 4096, - task_data, - 3, - NULL - ); - - cJSON_Delete(json); - return ESP_OK; -} - - -static esp_err_t config_mqtt_get_handler(httpd_req_t *req) -{ - ESP_LOGI(TAG, "Handling GET /api/v1/config/mqtt"); - - httpd_resp_set_type(req, "application/json"); - - bool enabled = mqtt_get_enabled(); - char server[64] = {0}; - char base_topic[32] = {0}; - char username[32] = {0}; - char password[64] = {0}; - uint16_t periodicity = mqtt_get_periodicity(); - - mqtt_get_server(server); - mqtt_get_base_topic(base_topic); - mqtt_get_user(username); - mqtt_get_password(password); - - ESP_LOGI(TAG, "MQTT Config:"); - ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); - ESP_LOGI(TAG, " Server: %s", server); - ESP_LOGI(TAG, " Topic: %s", base_topic); - ESP_LOGI(TAG, " Username: %s", username); - ESP_LOGI(TAG, " Password: %s", password); - ESP_LOGI(TAG, " Periodicity: %d", periodicity); - - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "enabled", enabled); - cJSON_AddStringToObject(config, "host", server); - cJSON_AddNumberToObject(config, "port", 1883); - cJSON_AddStringToObject(config, "username", username); - cJSON_AddStringToObject(config, "password", password); - cJSON_AddStringToObject(config, "topic", base_topic); - cJSON_AddNumberToObject(config, "periodicity", periodicity); - - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - free((void *)config_str); - cJSON_Delete(config); - return ESP_OK; -} - - -static esp_err_t config_mqtt_post_handler(httpd_req_t *req) -{ - ESP_LOGI(TAG, "Handling POST /api/v1/config/mqtt"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - ESP_LOGE(TAG, "Failed to read request body"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; - ESP_LOGI(TAG, "Received JSON: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Invalid JSON format"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - bool enabled = false; - const char *host = NULL, *topic = NULL, *username = NULL, *password = NULL; - int periodicity = 30; - - if (cJSON_IsBool(cJSON_GetObjectItem(json, "enabled"))) - enabled = cJSON_GetObjectItem(json, "enabled")->valueint; - - cJSON *j_host = cJSON_GetObjectItem(json, "host"); - if (cJSON_IsString(j_host)) host = j_host->valuestring; - - cJSON *j_topic = cJSON_GetObjectItem(json, "topic"); - if (cJSON_IsString(j_topic)) topic = j_topic->valuestring; - - cJSON *j_user = cJSON_GetObjectItem(json, "username"); - if (cJSON_IsString(j_user)) username = j_user->valuestring; - - cJSON *j_pass = cJSON_GetObjectItem(json, "password"); - if (cJSON_IsString(j_pass)) password = j_pass->valuestring; - - cJSON *j_periodicity = cJSON_GetObjectItem(json, "periodicity"); - if (cJSON_IsNumber(j_periodicity)) periodicity = j_periodicity->valueint; - - ESP_LOGI(TAG, "Applying MQTT config:"); - ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); - ESP_LOGI(TAG, " Host: %s", host); - ESP_LOGI(TAG, " Topic: %s", topic); - ESP_LOGI(TAG, " Username: %s", username); - ESP_LOGI(TAG, " Password: %s", password); - ESP_LOGI(TAG, " Periodicity: %d", periodicity); - - esp_err_t err = mqtt_set_config(enabled, host, topic, username, password, periodicity); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to apply MQTT config (code %d)", err); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to apply config"); - cJSON_Delete(json); - return ESP_FAIL; - } - - httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso"); - cJSON_Delete(json); - return ESP_OK; -} - - - -void register_network_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t wifi_get = { - .uri = "/api/v1/config/wifi", - .method = HTTP_GET, - .handler = wifi_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &wifi_get); - - httpd_uri_t wifi_post = { - .uri = "/api/v1/config/wifi", - .method = HTTP_POST, - .handler = wifi_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &wifi_post); - - // URI handler for getting MQTT config - httpd_uri_t config_mqtt_get_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_GET, - .handler = config_mqtt_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &config_mqtt_get_uri); - - // URI handler for posting MQTT config - httpd_uri_t config_mqtt_post_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_POST, - .handler = config_mqtt_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &config_mqtt_post_uri); -} - -// === Fim de: components/rest_api/src/network_api.c === diff --git a/projeto_parte5.c b/projeto_parte5.c deleted file mode 100755 index af3f4a7..0000000 --- a/projeto_parte5.c +++ /dev/null @@ -1,1066 +0,0 @@ - - -// === Início de: components/rest_api/src/dashboard_api.c === -#include "dashboard_api.h" -#include "esp_log.h" -#include "cJSON.h" -#include "evse_api.h" -#include "evse_error.h" - -static const char *TAG = "dashboard_api"; - -static esp_err_t dashboard_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - - // Cria o objeto JSON principal do dashboard - cJSON *dashboard = cJSON_CreateObject(); - - // Status do sistema - evse_state_t state = evse_get_state(); - cJSON_AddStringToObject(dashboard, "status", evse_state_to_str(state)); - - // Carregador - informação do carregador 1 (adapte conforme necessário) - cJSON *chargers = cJSON_CreateArray(); - cJSON *charger1 = cJSON_CreateObject(); - cJSON_AddNumberToObject(charger1, "id", 1); - cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state)); - cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current() / 10); - cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current()); - - // Calcular a potência com base na corrente (considerando 230V) - int power = (evse_get_charging_current() / 10) * 230; - cJSON_AddNumberToObject(charger1, "power", power); - - cJSON_AddItemToArray(chargers, charger1); - cJSON_AddItemToObject(dashboard, "chargers", chargers); - - // Consumo e tempo de carregamento - cJSON_AddNumberToObject(dashboard, "energyConsumed", evse_get_consumption_limit()); - cJSON_AddNumberToObject(dashboard, "chargingTime", evse_get_charging_time_limit()); - - // Alertas - cJSON *alerts = cJSON_CreateArray(); - if (evse_is_limit_reached()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("Limite de consumo atingido.")); - } - if (!evse_is_available()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("Estação indisponível.")); - } - if (!evse_is_enabled()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("EVSE desativado.")); - } - cJSON_AddItemToObject(dashboard, "alerts", alerts); - - // Erros - uint32_t error_bits = evse_get_error(); - cJSON *errors = cJSON_CreateArray(); - if (error_bits & EVSE_ERR_DIODE_SHORT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Diodo curto-circuitado")); - if (error_bits & EVSE_ERR_LOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no travamento")); - if (error_bits & EVSE_ERR_UNLOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no destravamento")); - if (error_bits & EVSE_ERR_RCM_SELFTEST_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no autoteste do RCM")); - if (error_bits & EVSE_ERR_RCM_TRIGGERED_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("RCM disparado")); - if (error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Temperatura elevada")); - if (error_bits & EVSE_ERR_PILOT_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Erro no sinal piloto")); - if (error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no sensor de temperatura")); - cJSON_AddItemToObject(dashboard, "errors", errors); - - // Enviar resposta JSON - const char *json_str = cJSON_Print(dashboard); - httpd_resp_sendstr(req, json_str); - - // Liberar memória - free((void *)json_str); - cJSON_Delete(dashboard); - - return ESP_OK; -} - -void register_dashboard_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t uri = { - .uri = "/api/v1/dashboard", - .method = HTTP_GET, - .handler = dashboard_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &uri); -} - -// === Fim de: components/rest_api/src/dashboard_api.c === - - -// === Início de: components/rest_api/src/auth_api.c === -// ========================= -// auth_api.c -// ========================= -#include "auth_api.h" -#include "auth.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "auth_api"; - -static struct { - char username[128]; -} users[10] = { /*{"admin"}, {"user1"}*/ }; -static int num_users = 2; - -static esp_err_t auth_methods_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *json = cJSON_CreateObject(); - cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() ); - char *str = cJSON_PrintUnformatted(json); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(json); - return ESP_OK; -} - -static esp_err_t auth_methods_post_handler(httpd_req_t *req) { - char buf[256]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados"); - return ESP_FAIL; - } - - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido"); - return ESP_FAIL; - } - - cJSON *rfid = cJSON_GetObjectItem(json, "RFID"); - if (rfid && cJSON_IsBool(rfid)) { - auth_set_enabled(cJSON_IsTrue(rfid)); - } else { - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'RFID' inválido ou ausente"); - return ESP_FAIL; - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Métodos de autenticação atualizados"); - return ESP_OK; -} - - -static esp_err_t users_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *root = cJSON_CreateObject(); - cJSON *list = cJSON_CreateArray(); - for (int i = 0; i < num_users; ++i) { - cJSON *u = cJSON_CreateObject(); - cJSON_AddStringToObject(u, "username", users[i].username); - cJSON_AddItemToArray(list, u); - } - cJSON_AddItemToObject(root, "users", list); - char *str = cJSON_Print(root); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(root); - return ESP_OK; -} - -static esp_err_t users_post_handler(httpd_req_t *req) { - char buf[128]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) return ESP_FAIL; - buf[len] = '\0'; - if (num_users < 10) { - strlcpy(users[num_users].username, buf, sizeof(users[num_users].username)); - num_users++; - httpd_resp_sendstr(req, "Usuário adicionado com sucesso"); - } else { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido"); - } - return ESP_OK; -} - -static esp_err_t users_delete_handler(httpd_req_t *req) { - char query[128]; - if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) { - char username[128]; - if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) { - for (int i = 0; i < num_users; i++) { - if (strcmp(users[i].username, username) == 0) { - for (int j = i; j < num_users - 1; j++) { - users[j] = users[j + 1]; - } - num_users--; - httpd_resp_sendstr(req, "Usuário removido com sucesso"); - return ESP_OK; - } - } - } - } - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado"); - return ESP_FAIL; -} - -void register_auth_handlers(httpd_handle_t server, void *ctx) { - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/auth-methods", - .method = HTTP_GET, - .handler = auth_methods_get_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/auth-methods", - .method = HTTP_POST, - .handler = auth_methods_post_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_GET, - .handler = users_get_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_POST, - .handler = users_post_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_DELETE, - .handler = users_delete_handler, - .user_ctx = ctx - }); -} - -// === Fim de: components/rest_api/src/auth_api.c === - - -// === Início de: components/rest_api/src/loadbalancing_settings_api.c === -#include "loadbalancing_settings_api.h" -#include "loadbalancer.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "loadbalancing_settings_api"; - -// GET Handler: Retorna configurações atuais de load balancing -static esp_err_t loadbalancing_config_get_handler(httpd_req_t *req) { - bool enabled = loadbalancer_is_enabled(); - uint8_t currentLimit = load_balancing_get_max_grid_current(); - - ESP_LOGI(TAG, "Fetching load balancing settings: enabled = %d, currentLimit = %u", enabled, currentLimit); - - httpd_resp_set_type(req, "application/json"); - - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "loadBalancingEnabled", enabled); - cJSON_AddNumberToObject(config, "loadBalancingCurrentLimit", currentLimit); - - const char *json_str = cJSON_Print(config); - httpd_resp_sendstr(req, json_str); - - ESP_LOGI(TAG, "Returned config: %s", json_str); - - free((void *)json_str); - cJSON_Delete(config); - return ESP_OK; -} - -// POST Handler: Atualiza configurações de load balancing -static esp_err_t loadbalancing_config_post_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - - if (len <= 0) { - ESP_LOGE(TAG, "Received empty POST body"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - - buf[len] = '\0'; - ESP_LOGI(TAG, "Received POST data: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Invalid JSON"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizar estado habilitado - cJSON *enabled_item = cJSON_GetObjectItem(json, "loadBalancingEnabled"); - if (enabled_item && cJSON_IsBool(enabled_item)) { - bool isEnabled = cJSON_IsTrue(enabled_item); - loadbalancer_set_enabled(isEnabled); - ESP_LOGI(TAG, "Updated loadBalancingEnabled to: %d", isEnabled); - } - - // Atualizar limite de corrente - cJSON *limit_item = cJSON_GetObjectItem(json, "loadBalancingCurrentLimit"); - if (limit_item && cJSON_IsNumber(limit_item)) { - uint8_t currentLimit = (uint8_t)limit_item->valuedouble; - - // Validar intervalo - if (currentLimit < 6 || currentLimit > 100) { - ESP_LOGW(TAG, "Rejected invalid currentLimit: %d", currentLimit); - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid currentLimit (must be 6-100)"); - return ESP_FAIL; - } - - esp_err_t err = load_balancing_set_max_grid_current(currentLimit); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to save currentLimit: %s", esp_err_to_name(err)); - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save setting"); - return ESP_FAIL; - } - - ESP_LOGI(TAG, "Updated loadBalancingCurrentLimit to: %d", currentLimit); - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Load balancing settings updated successfully"); - return ESP_OK; -} - -// Registro dos handlers na API HTTP -void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx) { - // GET - httpd_uri_t get_uri = { - .uri = "/api/v1/config/loadbalancing", - .method = HTTP_GET, - .handler = loadbalancing_config_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - // POST - httpd_uri_t post_uri = { - .uri = "/api/v1/config/loadbalancing", - .method = HTTP_POST, - .handler = loadbalancing_config_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/loadbalancing_settings_api.c === - - -// === Início de: components/rest_api/src/evse_settings_api.c === -// ========================= -// evse_settings_api.c -// ========================= -#include "evse_settings_api.h" -#include "evse_api.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "evse_settings_api"; - -static esp_err_t config_settings_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *config = cJSON_CreateObject(); - cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current()); - cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold()); - const char *json_str = cJSON_Print(config); - httpd_resp_sendstr(req, json_str); - free((void *)json_str); - cJSON_Delete(config); - return ESP_OK; -} - -static esp_err_t config_settings_post_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - cJSON *current = cJSON_GetObjectItem(json, "currentLimit"); - if (current) evse_set_max_charging_current(current->valueint); - cJSON *temp = cJSON_GetObjectItem(json, "temperatureLimit"); - if (temp) evse_set_temp_threshold(temp->valueint); - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Configurações atualizadas com sucesso"); - return ESP_OK; -} - -void register_evse_settings_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t get_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_GET, - .handler = config_settings_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - httpd_uri_t post_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_POST, - .handler = config_settings_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/evse_settings_api.c === - - -// === Início de: components/rest_api/include/dashboard_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra o handler da dashboard (status geral do sistema) - */ -void register_dashboard_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/dashboard_api.h === - - -// === Início de: components/rest_api/include/static_file_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra o handler para servir arquivos estáticos da web (SPA) - */ -void register_static_file_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/static_file_api.h === - - -// === Início de: components/rest_api/include/network_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de configuração Wi-Fi e MQTT - */ -void register_network_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/network_api.h === - - -// === Início de: components/rest_api/include/auth_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de autenticação e gerenciamento de usuários - */ -void register_auth_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/auth_api.h === - - -// === Início de: components/rest_api/include/loadbalancing_settings_api.h === -// ========================= -// loadbalancing_settings_api.h -// ========================= - -#ifndef LOADBALANCING_SETTINGS_API_H -#define LOADBALANCING_SETTINGS_API_H - -#include "esp_err.h" -#include "esp_http_server.h" - -// Função para registrar os manipuladores de URI para as configurações de load balancing e solar -void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx); - -#endif // LOADBALANCING_SETTINGS_API_H - -// === Fim de: components/rest_api/include/loadbalancing_settings_api.h === - - -// === Início de: components/rest_api/include/rest_main.h === -#pragma once - -#include -#include - -#define SCRATCH_BUFSIZE (10240) - -typedef struct rest_server_context { - char base_path[ESP_VFS_PATH_MAX + 1]; - char scratch[SCRATCH_BUFSIZE]; -} rest_server_context_t; - -esp_err_t rest_server_init(const char *base_path); - -// === Fim de: components/rest_api/include/rest_main.h === - - -// === Início de: components/rest_api/include/meters_settings_api.h === -// ========================= -// meters_settings_api.h -// ========================= - -#ifndef METERS_SETTINGS_API_H -#define METERS_SETTINGS_API_H - -#include "esp_err.h" -#include "esp_http_server.h" - -// Função para registrar os manipuladores de URI para as configurações dos contadores -void register_meters_settings_handlers(httpd_handle_t server, void *ctx); - -#endif // METERS_SETTINGS_API_H - -// === Fim de: components/rest_api/include/meters_settings_api.h === - - -// === Início de: components/rest_api/include/ocpp_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers da configuração e status do OCPP - */ -void register_ocpp_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/ocpp_api.h === - - -// === Início de: components/rest_api/include/evse_settings_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de configuração elétrica e limites de carregamento - */ -void register_evse_settings_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/evse_settings_api.h === - - -// === Início de: components/network/src/wifi_2.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_mac.h" -#include "nvs.h" -#include "mdns.h" - -#include "wifi.h" - - -#include "nvs_flash.h" -#include - -#define WIFI_STORAGE_NAMESPACE "wifi_config" - - - -#define TAG "wifi" -#define AP_SSID "plx-%02x%02x%02x" -#define MDNS_HOSTNAME "plx%02x" - -#define NVS_NAMESPACE "wifi" - -static nvs_handle_t nvs; -static esp_netif_t *ap_netif; -EventGroupHandle_t wifi_event_group; - -// -// Event handler para modo AP -// -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - if (event_base == WIFI_EVENT) { - switch (event_id) { - case WIFI_EVENT_AP_STACONNECTED: { - wifi_event_ap_staconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " conectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - case WIFI_EVENT_AP_STADISCONNECTED: { - wifi_event_ap_stadisconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " desconectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - } - } -} - -// -// Iniciar o AP com SSID baseado no MAC -// -void wifi_ap_start(void) -{ - ESP_LOGI(TAG, "Iniciando AP"); - - ESP_ERROR_CHECK(esp_wifi_stop()); - - wifi_config_t ap_config = { - .ap = { - .ssid = "", - .ssid_len = 0, - .channel = 1, - .password = "", - .max_connection = 4, - .authmode = WIFI_AUTH_OPEN - } - }; - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - snprintf((char *)ap_config.ap.ssid, sizeof(ap_config.ap.ssid), AP_SSID, mac[3], mac[4], mac[5]); - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT); -} - -// -// Inicializar Wi-Fi em modo AP -// -void wifi_ini(void) -{ - ESP_LOGI(TAG, "Inicializando Wi-Fi (modo AP)"); - - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - wifi_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_netif_init()); - /* - if (!esp_event_loop_is_running()) { - ESP_ERROR_CHECK(esp_event_loop_create_default()); - }*/ - - ap_netif = esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - char hostname[16]; - snprintf(hostname, sizeof(hostname), MDNS_HOSTNAME, mac[5]); - - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(hostname)); - ESP_ERROR_CHECK(mdns_instance_name_set("EVSE Controller")); - - wifi_ap_start(); -} - -esp_netif_t *wifi_get_ap_netif(void) -{ - return ap_netif; -} - -esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) { - - return ESP_OK; -} - -void wifi_get_ssid(char *value) { - // Your implementation here -} - -void wifi_get_password(char *value) { - // Your implementation here -} - -bool wifi_get_enabled(void) -{ - return true; -} - -// === Fim de: components/network/src/wifi_2.c === - - -// === Início de: components/network/src/wifi.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_mac.h" -#include "nvs.h" -#include "mdns.h" - -#include "wifi.h" - -#define AP_SSID "plx-%02x%02x%02x" - -#define MDNS_SSID "plx%02x" - -#define NVS_NAMESPACE "wifi" -#define NVS_ENABLED "enabled" -#define NVS_SSID "ssid" -#define NVS_PASSWORD "password" - -static const char *TAG = "wifi"; - -static nvs_handle_t nvs; - -static esp_netif_t *sta_netif; - -static esp_netif_t *ap_netif; - -EventGroupHandle_t wifi_event_group; - -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - ESP_LOGI(TAG, "event_handler"); - - if (event_base == WIFI_EVENT) - { - if (event_id == WIFI_EVENT_AP_STACONNECTED) - { - ESP_LOGI(TAG, "STA connected"); - wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; - ESP_LOGI(TAG, "WiFi AP " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - } - if (event_id == WIFI_EVENT_AP_STADISCONNECTED) - { - ESP_LOGI(TAG, "AP STA disconnected"); - wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; - ESP_LOGI(TAG, "WiFi AP " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT); - } - if (event_id == WIFI_EVENT_STA_DISCONNECTED) - { - ESP_LOGI(TAG, "STA disconnected"); - xEventGroupClearBits(wifi_event_group, WIFI_STA_CONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT); - esp_wifi_connect(); - } - if (event_id == WIFI_EVENT_STA_START) - { - ESP_LOGI(TAG, "STA start"); - esp_wifi_connect(); - } - } - else if (event_base == IP_EVENT) - { - ESP_LOGI(TAG, "event_base == IP_EVENT"); - - if (event_id == IP_EVENT_STA_GOT_IP || event_id == IP_EVENT_GOT_IP6) - { - if (event_id == IP_EVENT_STA_GOT_IP) - { - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(TAG, "WiFi STA got ip: " IPSTR, IP2STR(&event->ip_info.ip)); - } - else - { - ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - ESP_LOGI(TAG, "WiFi STA got ip6: " IPV6STR, IPV62STR(event->ip6_info.ip)); - } - xEventGroupClearBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_STA_CONNECTED_BIT); - } - } -} - -static void sta_set_config(void) -{ - - ESP_LOGI(TAG, "sta_set_config"); - - if (wifi_get_enabled()) - { - wifi_config_t wifi_config = { - .sta = { - .pmf_cfg = { - .capable = true, - .required = false}}}; - wifi_get_ssid((char *)wifi_config.sta.ssid); - wifi_get_password((char *)wifi_config.sta.password); - - esp_wifi_set_mode(WIFI_MODE_STA); - esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); - } -} - -static void ap_set_config(void) -{ - - ESP_LOGI(TAG, "ap_set_config"); - - wifi_config_t wifi_ap_config = { - .ap = { - .max_connection = 1, - .authmode = WIFI_AUTH_OPEN}}; - uint8_t mac[6]; - esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - sprintf((char *)wifi_ap_config.ap.ssid, AP_SSID, mac[3], mac[4], mac[5]); - - wifi_config_t wifi_sta_config = {0}; - - esp_wifi_set_mode(WIFI_MODE_APSTA); - esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_ap_config); - esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config); -} - -static void sta_try_start(void) -{ - - ESP_LOGI(TAG, "sta_try_start"); - - sta_set_config(); - if (wifi_get_enabled()) - { - ESP_LOGI(TAG, "Starting STA"); - esp_wifi_start(); - xEventGroupSetBits(wifi_event_group, WIFI_STA_MODE_BIT); - } -} - -void wifi_ini(void) -{ - - - ESP_LOGI(TAG, "Wifi init"); - - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - wifi_event_group = xEventGroupCreate(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ap_netif = esp_netif_create_default_wifi_ap(); - sta_netif = esp_netif_create_default_wifi_sta(); - - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - char chargeid[6]; - uint8_t mac[6]; - esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - sprintf((char *)chargeid, MDNS_SSID, mac[5]); - - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(chargeid)); - ESP_ERROR_CHECK(mdns_instance_name_set("EVSE controller")); - - sta_try_start(); - -} - -esp_netif_t *wifi_get_sta_netif(void) -{ - return sta_netif; -} - -esp_netif_t *wifi_get_ap_netif(void) -{ - return ap_netif; -} - -esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) -{ - - ESP_LOGI(TAG, "Wifi set config"); - - if (enabled) - { - if (ssid == NULL || strlen(ssid) == 0) - { - size_t len = 0; - nvs_get_str(nvs, NVS_SSID, NULL, &len); - if (len <= 1) - { - ESP_LOGE(TAG, "Required SSID"); - return ESP_ERR_INVALID_ARG; - } - } - } - - if (ssid != NULL && strlen(ssid) > 32) - { - ESP_LOGE(TAG, "SSID out of range"); - return ESP_ERR_INVALID_ARG; - } - - if (password != NULL && strlen(password) > 32) - { - ESP_LOGE(TAG, "Password out of range"); - return ESP_ERR_INVALID_ARG; - } - - nvs_set_u8(nvs, NVS_ENABLED, enabled); - if (ssid != NULL) - { - nvs_set_str(nvs, NVS_SSID, ssid); - } - if (password != NULL) - { - nvs_set_str(nvs, NVS_PASSWORD, password); - } - nvs_commit(nvs); - - ESP_LOGI(TAG, "Stopping AP/STA"); - xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT); - esp_wifi_stop(); - - sta_try_start(); - - return ESP_OK; -} - -uint16_t wifi_scan(wifi_scan_ap_t *scan_aps) -{ - - ESP_LOGI(TAG, "wifi_scan"); - - uint16_t number = WIFI_SCAN_SCAN_LIST_SIZE; - wifi_ap_record_t ap_info[WIFI_SCAN_SCAN_LIST_SIZE]; - uint16_t ap_count = 0; - memset(ap_info, 0, sizeof(ap_info)); - - esp_wifi_scan_start(NULL, true); - esp_wifi_scan_get_ap_records(&number, ap_info); - esp_wifi_scan_get_ap_num(&ap_count); - - ESP_LOGI(TAG, "wifi_scan --- %d", ap_count); - - for (int i = 0; (i < WIFI_SCAN_SCAN_LIST_SIZE) && (i < ap_count); i++) - { - - ESP_LOGI(TAG, "wifi_scan ---"); - - strcpy(scan_aps[i].ssid, (const char *)ap_info[i].ssid); - scan_aps[i].rssi = ap_info[i].rssi; - scan_aps[i].auth = ap_info[i].authmode != WIFI_AUTH_OPEN; - } - - return ap_count; -} - -bool wifi_get_enabled(void) -{ - uint8_t value = false; - nvs_get_u8(nvs, NVS_ENABLED, &value); - return value; -} - -void wifi_get_ssid(char *value) -{ - size_t len = 32; - value[0] = '\0'; - nvs_get_str(nvs, NVS_SSID, value, &len); -} - -void wifi_get_password(char *value) -{ - size_t len = 64; - value[0] = '\0'; - nvs_get_str(nvs, NVS_PASSWORD, value, &len); -} - -void wifi_ap_start(void) -{ - ESP_LOGI(TAG, "Starting AP"); - - xEventGroupClearBits(wifi_event_group, WIFI_STA_MODE_BIT); - esp_wifi_stop(); - - ap_set_config(); - esp_wifi_start(); - - xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT); -} - -void wifi_ap_stop(void) -{ - ESP_LOGI(TAG, "Stopping AP"); - xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT); - esp_wifi_stop(); - - sta_try_start(); -} - -bool wifi_is_ap(void) -{ - wifi_mode_t mode; - esp_wifi_get_mode(&mode); - return mode == WIFI_MODE_APSTA; -} - -// === Fim de: components/network/src/wifi.c === diff --git a/projeto_parte6.c b/projeto_parte6.c deleted file mode 100755 index 2c303ad..0000000 --- a/projeto_parte6.c +++ /dev/null @@ -1,735 +0,0 @@ - - -// === Início de: components/network/include/wifi.h === -#ifndef WIFI_H_ -#define WIFI_H_ - -#include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "esp_err.h" -#include "esp_netif.h" - -#define WIFI_SCAN_SCAN_LIST_SIZE 10 - -#define WIFI_AP_CONNECTED_BIT BIT0 -#define WIFI_AP_DISCONNECTED_BIT BIT1 -#define WIFI_STA_CONNECTED_BIT BIT2 -#define WIFI_STA_DISCONNECTED_BIT BIT3 -#define WIFI_AP_MODE_BIT BIT4 -#define WIFI_STA_MODE_BIT BIT5 - -typedef struct -{ - char ssid[32]; - int rssi; - bool auth; -} wifi_scan_ap_t; - -/** - * @brief WiFi event group WIFI_AP_CONNECTED_BIT | WIFI_AP_DISCONNECTED_BIT | WIFI_STA_CONNECTED_BIT | WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT - * - */ -extern EventGroupHandle_t wifi_event_group; - -/** - * @brief Initialize WiFi - * - */ -void wifi_ini(void); - -/** - * @brief Return WiFi STA network interface - * - * @return esp_netif_t* - */ -esp_netif_t* wifi_get_sta_netif(void); - -/** - * @brief Return WiFi AP network interface - * - * @return esp_netif_t* - */ -esp_netif_t* wifi_get_ap_netif(void); - -/** - * @brief Set WiFi config - * - * @param enabled - * @param ssid NULL value will be skiped - * @param password NULL value will be skiped - * @return esp_err_t - */ -esp_err_t wifi_set_config(bool enabled, const char* ssid, const char* password); - -/** - * @brief Get WiFi STA enabled, stored in NVS - * - * @return true - * @return false - */ -bool wifi_get_enabled(void); - -/** - * @brief Scan for AP - * - * @param scan_aps array with length WIFI_SCAN_SCAN_LIST_SIZE - * @return uint16_t number of available AP - */ -uint16_t wifi_scan(wifi_scan_ap_t *scan_aps); - -/** - * @brief Get WiFi STA ssid, string length 32, stored in NVS - * - * @param value - */ -void wifi_get_ssid(char* value); - -/** - * @brief Get WiFi STA password, string length 32, stored in NVS - * - * @param value - */ -void wifi_get_password(char* value); - -/** - * @brief Start WiFi AP mode - * - */ -void wifi_ap_start(void); - -/** - * @brief Stop WiFi AP mode - * - */ -void wifi_ap_stop(void); - -#endif /* WIFI_H_ */ - -// === Fim de: components/network/include/wifi.h === - - -// === Início de: components/peripherals/src/ac_relay.c === -#include "esp_log.h" -#include "driver/gpio.h" - -#include "ac_relay.h" -#include "board_config.h" - -static const char* TAG = "ac_relay"; - -/** - * @brief Initialize the AC relay GPIO. - * - * Configures the specified GPIO pin as an output and sets its initial state to OFF (low). - */ -void ac_relay_init(void) -{ - gpio_config_t conf = { - .pin_bit_mask = BIT64(board_config.ac_relay_gpio), - .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, ///< Disabled unless required - .pull_up_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - - esp_err_t ret = gpio_config(&conf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to configure GPIO (error: %s)", esp_err_to_name(ret)); - return; - } - - gpio_set_level(board_config.ac_relay_gpio, false); ///< Ensure relay starts OFF - ESP_LOGI(TAG, "AC relay initialized. Pin: %d", board_config.ac_relay_gpio); -} - -/** - * @brief Set the state of the AC relay. - * - * @param state True to turn the relay ON, False to turn it OFF. - */ -void ac_relay_set_state(bool state) -{ - ESP_LOGI(TAG, "Setting AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, state); - - esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to set GPIO level (error: %s)", esp_err_to_name(ret)); - } -} - -/** - * @brief Get the current state of the AC relay. - * - * @return true if the relay is ON, false if OFF. - */ -bool ac_relay_get_state(void) -{ - int level = gpio_get_level(board_config.ac_relay_gpio); - ESP_LOGD(TAG, "Current AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, level); - return level; -} - -// === Fim de: components/peripherals/src/ac_relay.c === - - -// === Início de: components/peripherals/src/ntc_sensor.c === -#include -#include -#include "freertos/task.h" -#include "esp_log.h" -#include "ntc_sensor.h" -#include "ntc_driver.h" - -#include "adc.h" - -static const char *TAG = "temp_sensor"; - -#define MEASURE_PERIOD 15000 // 10s - -static float temp = 0.0; - -static ntc_device_handle_t ntc = NULL; - -static portMUX_TYPE temp_mux = portMUX_INITIALIZER_UNLOCKED; - -static void ntc_sensor_task_func(void *param) { - float t; - while (true) { - if (ntc_dev_get_temperature(ntc, &t) == ESP_OK) { - portENTER_CRITICAL(&temp_mux); - temp = t; - portEXIT_CRITICAL(&temp_mux); - } - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); - } -} - -float ntc_temp_sensor(void) { - float t; - portENTER_CRITICAL(&temp_mux); - t = temp; - portEXIT_CRITICAL(&temp_mux); - return t; -} - -void ntc_sensor_init(void) -{ - - ESP_LOGI(TAG, "ntc_sensor_init"); - - // Select the NTC sensor and initialize the hardware parameters - ntc_config_t ntc_config = { - .b_value = 3950, - .r25_ohm = 10000, - .fixed_ohm = 4700, - .vdd_mv = 3300, - .circuit_mode = CIRCUIT_MODE_NTC_GND, - .atten = ADC_ATTEN_DB_12, - .channel = ADC_CHANNEL_0, - .unit = ADC_UNIT_1}; - - // Create the NTC Driver and Init ADC - // ntc_device_handle_t ntc = NULL; - // adc_oneshot_unit_handle_t adc_handle = NULL; - ESP_ERROR_CHECK(ntc_dev_create(&ntc_config, &ntc, &adc_handle)); - ESP_ERROR_CHECK(ntc_dev_get_adc_handle(ntc, &adc_handle)); - - xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 3, NULL); -} - -// === Fim de: components/peripherals/src/ntc_sensor.c === - - -// === Início de: components/peripherals/src/proximity.c === -#include "esp_log.h" - -#include "proximity.h" -#include "board_config.h" -#include "adc.h" - -static const char *TAG = "proximity"; - -void proximity_init(void) -{ - if (board_config.proximity) - { - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12}; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.proximity_adc_channel, &config)); - } -} - -uint8_t proximity_get_max_current(void) -{ - int voltage; - adc_oneshot_read(adc_handle, board_config.proximity_adc_channel, &voltage); - adc_cali_raw_to_voltage(adc_cali_handle, voltage, &voltage); - - ESP_LOGI(TAG, "Measured: %dmV", voltage); - - uint8_t current; - - if (voltage >= board_config.proximity_down_threshold_8) - { - current = 8; - } - else if (voltage >= board_config.proximity_down_threshold_10) - { - current = 10; - } - - else if (voltage >= board_config.proximity_down_threshold_13) - { - current = 13; - } - else if (voltage >= board_config.proximity_down_threshold_20) - { - current = 20; - } - - else if (voltage >= board_config.proximity_down_threshold_25) - { - current = 25; - } - else if (voltage >= board_config.proximity_down_threshold_32) - { - current = 32; - } - else - { - current = 32; - } - - ESP_LOGI(TAG, "Max current: %dA", current); - - return current; -} -// === Fim de: components/peripherals/src/proximity.c === - - -// === Início de: components/peripherals/src/buzzer.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "board_config.h" -#include "buzzer.h" -#include "evse_api.h" - -static gpio_num_t buzzer_gpio = GPIO_NUM_NC; -static evse_state_t last_buzzer_state = -1; -static QueueHandle_t buzzer_queue = NULL; - -void buzzer_on(void) { - if (buzzer_gpio != GPIO_NUM_NC) - gpio_set_level(buzzer_gpio, 1); -} - -void buzzer_off(void) { - if (buzzer_gpio != GPIO_NUM_NC) - gpio_set_level(buzzer_gpio, 0); -} - -// ---------------------- -// Padrões de Buzzer -// ---------------------- - -typedef struct { - uint16_t on_ms; - uint16_t off_ms; -} buzzer_pattern_step_t; - -typedef enum { - BUZZER_PATTERN_NONE = 0, - BUZZER_PATTERN_PLUGGED, - BUZZER_PATTERN_UNPLUGGED, - BUZZER_PATTERN_CHARGING, -} buzzer_pattern_id_t; - -static const buzzer_pattern_step_t pattern_plugged[] = { - {100, 100}, {200, 0} -}; - -static const buzzer_pattern_step_t pattern_unplugged[] = { - {150, 150}, {150, 150}, {150, 0} -}; - -static const buzzer_pattern_step_t pattern_charging[] = { - {80, 150}, {100, 120}, {120, 100}, {140, 0} -}; - -// ---------------------- -// Executor de padrões -// ---------------------- - -static void buzzer_execute_pattern(buzzer_pattern_id_t pattern_id) { - const buzzer_pattern_step_t *pattern = NULL; - size_t length = 0; - - switch (pattern_id) { - case BUZZER_PATTERN_PLUGGED: - pattern = pattern_plugged; - length = sizeof(pattern_plugged) / sizeof(pattern_plugged[0]); - break; - case BUZZER_PATTERN_UNPLUGGED: - pattern = pattern_unplugged; - length = sizeof(pattern_unplugged) / sizeof(pattern_unplugged[0]); - break; - case BUZZER_PATTERN_CHARGING: - pattern = pattern_charging; - length = sizeof(pattern_charging) / sizeof(pattern_charging[0]); - break; - default: - return; - } - - for (size_t i = 0; i < length; i++) { - buzzer_on(); - vTaskDelay(pdMS_TO_TICKS(pattern[i].on_ms)); - buzzer_off(); - if (pattern[i].off_ms > 0) - vTaskDelay(pdMS_TO_TICKS(pattern[i].off_ms)); - } -} - -// ---------------------- -// Task que toca o buzzer -// ---------------------- - -static void buzzer_worker_task(void *arg) { - buzzer_pattern_id_t pattern_id; - - while (true) { - if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) { - //buzzer_execute_pattern(pattern_id); - } - } -} - -// ---------------------- -// Task de monitoramento -// ---------------------- - -static void buzzer_monitor_task(void *arg) { - while (true) { - evse_state_t current = evse_get_state(); - - if (current != last_buzzer_state) { - buzzer_pattern_id_t pattern_id = BUZZER_PATTERN_NONE; - - switch (current) { - case EVSE_STATE_A: - if (last_buzzer_state != EVSE_STATE_A) - pattern_id = BUZZER_PATTERN_UNPLUGGED; - break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - if (last_buzzer_state != EVSE_STATE_B1 && last_buzzer_state != EVSE_STATE_B2) - pattern_id = BUZZER_PATTERN_PLUGGED; - break; - case EVSE_STATE_C2: - case EVSE_STATE_D2: - if (last_buzzer_state != EVSE_STATE_C2 && last_buzzer_state != EVSE_STATE_D2) - pattern_id = BUZZER_PATTERN_CHARGING; - break; - default: - break; - } - - if (pattern_id != BUZZER_PATTERN_NONE) { - xQueueSend(buzzer_queue, &pattern_id, 0); // Não bloqueia - } - - last_buzzer_state = current; - } - - vTaskDelay(pdMS_TO_TICKS(100)); - } -} - -// ---------------------- -// Inicialização -// ---------------------- - -void buzzer_init(void) { - if (board_config.buzzer) { - buzzer_gpio = board_config.buzzer_gpio; - - gpio_config_t io_conf = { - .pin_bit_mask = BIT64(buzzer_gpio), - .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_ENABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - gpio_config(&io_conf); - gpio_set_level(buzzer_gpio, 0); - } - - buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t)); - - xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 3, NULL); - xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 3, NULL); -} - -// === Fim de: components/peripherals/src/buzzer.c === - - -// === Início de: components/peripherals/src/ds18x20.h === -/* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _DS18X20_H -#define _DS18X20_H - -#include -#include "onewire.h" - -typedef onewire_addr_t ds18x20_addr_t; - -/** An address value which can be used to indicate "any device on the bus" */ -#define DS18X20_ANY ONEWIRE_NONE - -/** Family ID (lower address byte) of DS18B20 sensors */ -#define DS18B20_FAMILY_ID 0x28 - -/** Family ID (lower address byte) of DS18S20 sensors */ -#define DS18S20_FAMILY_ID 0x10 - -/** - * @brief Find the addresses of all ds18x20 devices on the bus. - * - * Scans the bus for all devices and places their addresses in the supplied - * array. If there are more than `addr_count` devices on the bus, only the - * first `addr_count` are recorded. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A pointer to an array of ::ds18x20_addr_t values. - * This will be populated with the addresses of the found - * devices. - * @param addr_count Number of slots in the `addr_list` array. At most this - * many addresses will be returned. - * @param found The number of devices found. Note that this may be less - * than, equal to, or more than `addr_count`, depending on - * how many ds18x20 devices are attached to the bus. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found); - -/** - * @brief Tell one or more sensors to perform a temperature measurement and - * conversion (CONVERT_T) operation. - * - * This operation can take up to 750ms to complete. - * - * If `wait=true`, this routine will automatically drive the pin high for the - * necessary 750ms after issuing the command to ensure parasitically-powered - * devices have enough power to perform the conversion operation (for - * non-parasitically-powered devices, this is not necessary but does not - * hurt). If `wait=false`, this routine will drive the pin high, but will - * then return immediately. It is up to the caller to wait the requisite time - * and then depower the bus using onewire_depower() or by issuing another - * command once conversion is done. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device on the bus. This can be set - * to ::DS18X20_ANY to send the command to all devices on the bus - * at the same time. - * @param wait Whether to wait for the necessary 750ms for the ds18x20 to - * finish performing the conversion before returning to the - * caller (You will normally want to do this). - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); - -/** - * @brief Read the value from the last CONVERT_T operation. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18b20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18s20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation for multiple devices. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** Perform a ds18x20_measure() followed by ds18s20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18s20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18b20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Perform a ds18x20_measure() followed by ds18x20_read_temp_multi() - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** - * @brief Read the scratchpad data for a particular ds18x20 device. - * - * This is not generally necessary to do directly. It is done automatically - * as part of ds18x20_read_temperature(). - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 8-byte buffer to hold the read data. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Write the scratchpad data for a particular ds18x20 device. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to write. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 3-byte buffer to hold the data to write - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Issue the copy scratchpad command, copying current scratchpad to - * EEPROM. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to command. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr); - - -#endif /* _DS18X20_H */ -// === Fim de: components/peripherals/src/ds18x20.h === diff --git a/projeto_parte7.c b/projeto_parte7.c deleted file mode 100755 index e598611..0000000 --- a/projeto_parte7.c +++ /dev/null @@ -1,793 +0,0 @@ - - -// === Início de: components/peripherals/src/socket_lock.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "socket_lock.h" -#include "board_config.h" - -#define NVS_NAMESPACE "socket_lock" -#define NVS_OPERATING_TIME "op_time" -#define NVS_BREAK_TIME "break_time" -#define NVS_RETRY_COUNT "retry_count" -#define NVS_DETECTION_HIGH "detect_hi" - -#define OPERATING_TIME_MIN 100 -#define OPERATING_TIME_MAX 1000 -#define LOCK_DELAY 500 - -#define LOCK_BIT BIT0 -#define UNLOCK_BIT BIT1 -#define REPEAT_LOCK_BIT BIT2 -#define REPEAT_UNLOCK_BIT BIT3 - -static const char* TAG = "socket_lock"; - -static nvs_handle_t nvs; - -static uint16_t operating_time = 300; - -static uint16_t break_time = 1000; - -static bool detection_high; - -static uint8_t retry_count = 5; - -static socket_lock_status_t status; - -static TaskHandle_t socket_lock_task; - -static bool is_locked(void) -{ - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - - vTaskDelay(pdMS_TO_TICKS(board_config.socket_lock_detection_delay)); - - return gpio_get_level(board_config.socket_lock_detection_gpio) == detection_high; -} - -bool socket_lock_is_locked_state(void) -{ - return is_locked(); -} - -static void socket_lock_task_func(void* param) -{ - uint32_t notification; - - TickType_t previous_tick = 0; - uint8_t attempt = 0; - - while (true) { - if (xTaskNotifyWait(0x00, 0xff, ¬ification, portMAX_DELAY)) { - if (notification & (LOCK_BIT | UNLOCK_BIT)) { - attempt = retry_count; - } - - if (notification & (UNLOCK_BIT | REPEAT_UNLOCK_BIT)) { - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (!is_locked()) { - ESP_LOGI(TAG, "Unlock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not unlocked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_UNLOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not unlocked"); - status = SOCKED_LOCK_STATUS_UNLOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } else if (notification & (LOCK_BIT | REPEAT_LOCK_BIT)) { - if (notification & LOCK_BIT) { - vTaskDelay(pdMS_TO_TICKS(LOCK_DELAY)); //delay before first lock attempt - } - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (is_locked()) { - ESP_LOGI(TAG, "Lock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not locked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_LOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not locked"); - status = SOCKED_LOCK_STATUS_LOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } - - TickType_t delay_tick = xTaskGetTickCount() - previous_tick; - if (delay_tick < pdMS_TO_TICKS(break_time)) { - vTaskDelay(pdMS_TO_TICKS(break_time) - delay_tick); - } - previous_tick = xTaskGetTickCount(); - } - } -} - -void socket_lock_init(void) -{ - if (board_config.socket_lock) { - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - nvs_get_u16(nvs, NVS_OPERATING_TIME, &operating_time); - - nvs_get_u16(nvs, NVS_BREAK_TIME, &break_time); - - nvs_get_u8(nvs, NVS_RETRY_COUNT, &retry_count); - - uint8_t u8; - if (nvs_get_u8(nvs, NVS_DETECTION_HIGH, &u8) == ESP_OK) { - detection_high = u8; - } - - gpio_config_t io_conf = {}; - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_a_gpio) | BIT64(board_config.socket_lock_b_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_detection_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - xTaskCreate(socket_lock_task_func, "socket_lock_task", 2 * 1024, NULL, 10, &socket_lock_task); - } -} - -bool socket_lock_is_detection_high(void) -{ - return detection_high; -} - -void socket_lock_set_detection_high(bool _detection_high) -{ - detection_high = _detection_high; - - nvs_set_u8(nvs, NVS_DETECTION_HIGH, detection_high); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_operating_time(void) -{ - return operating_time; -} - -esp_err_t socket_lock_set_operating_time(uint16_t _operating_time) -{ - if (_operating_time < OPERATING_TIME_MIN || _operating_time > OPERATING_TIME_MAX) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - operating_time = _operating_time; - nvs_set_u16(nvs, NVS_OPERATING_TIME, operating_time); - nvs_commit(nvs); - - return ESP_OK; -} - -uint8_t socket_lock_get_retry_count(void) -{ - return retry_count; -} - -void socket_lock_set_retry_count(uint8_t _retry_count) -{ - retry_count = _retry_count; - nvs_set_u8(nvs, NVS_RETRY_COUNT, retry_count); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_break_time(void) -{ - return break_time; -} - -esp_err_t socket_lock_set_break_time(uint16_t _break_time) -{ - if (_break_time < board_config.socket_lock_min_break_time) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - break_time = _break_time; - nvs_set_u16(nvs, NVS_BREAK_TIME, break_time); - nvs_commit(nvs); - - return ESP_OK; -} - -void socket_lock_set_locked(bool locked) -{ - ESP_LOGI(TAG, "Set locked %d", locked); - - xTaskNotify(socket_lock_task, locked ? LOCK_BIT : UNLOCK_BIT, eSetBits); - status = SOCKED_LOCK_STATUS_OPERATING; -} - -socket_lock_status_t socket_lock_get_status(void) -{ - return status; -} -// === Fim de: components/peripherals/src/socket_lock.c === - - -// === Início de: components/peripherals/src/temp_sensor.c === -#include -#include -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" - -#include "temp_sensor.h" -#include "lm75a.h" - -#define MAX_SENSORS 5 -#define MEASURE_PERIOD 10000 // 10s -#define MEASURE_ERR_THRESHOLD 3 - -static const char *TAG = "temp_sensor"; - -static uint8_t sensor_count = 0; - -static int16_t low_temp = 0; - -static int high_temp = 0; - -static uint8_t measure_err_count = 0; - -static void temp_sensor_task_func(void *param) -{ - while (true) - { - high_temp = lm75a_read_temperature(0); - - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); - } -} - -void temp_sensor_init(void) -{ - - ESP_LOGW(TAG, "temp_sensor_init"); - - lm75a_init(); - - xTaskCreate(temp_sensor_task_func, "temp_sensor_task", 5 * 1024, NULL, 5, NULL); -} - -uint8_t temp_sensor_get_count(void) -{ - return sensor_count; -} - -int16_t temp_sensor_get_low(void) -{ - return low_temp; -} - -int temp_sensor_get_high(void) -{ - return high_temp; -} - -bool temp_sensor_is_error(void) -{ - return sensor_count == 0 || measure_err_count > MEASURE_ERR_THRESHOLD; -} -// === Fim de: components/peripherals/src/temp_sensor.c === - - -// === Início de: components/peripherals/src/aux_io.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "aux_io.h" -#include "board_config.h" -#include "adc.h" - -#define MAX_AUX_IN 4 -#define MAX_AUX_OUT 4 -#define MAX_AUX_AIN 4 - -//static const char* TAG = "aux"; - -static int aux_in_count = 0; -static int aux_out_count = 0; -static int aux_ain_count = 0; - -static struct aux_gpio_s -{ - gpio_num_t gpio; - const char* name; -} aux_in[MAX_AUX_IN], aux_out[MAX_AUX_OUT]; - -static struct aux_adc_s -{ - adc_channel_t adc; - const char* name; -} aux_ain[MAX_AUX_AIN]; - - -void aux_init(void) -{ - // IN - - gpio_config_t io_conf = { - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLDOWN_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - .pin_bit_mask = 0 - }; - - if (board_config.aux_in_1) { - aux_in[aux_in_count].gpio = board_config.aux_in_1_gpio; - aux_in[aux_in_count].name = board_config.aux_in_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_1_gpio); - aux_in_count++; - } - - if (board_config.aux_in_2) { - aux_in[aux_in_count].gpio = board_config.aux_in_2_gpio; - aux_in[aux_in_count].name = board_config.aux_in_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_2_gpio); - aux_in_count++; - } - - if (board_config.aux_in_3) { - aux_in[aux_in_count].gpio = board_config.aux_in_3_gpio; - aux_in[aux_in_count].name = board_config.aux_in_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_3_gpio); - aux_in_count++; - } - - if (board_config.aux_in_4) { - aux_in[aux_in_count].gpio = board_config.aux_in_4_gpio; - aux_in[aux_in_count].name = board_config.aux_in_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_4_gpio); - aux_in_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // OUT - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = 0; - - if (board_config.aux_out_1) { - aux_out[aux_out_count].gpio = board_config.aux_out_1_gpio; - aux_out[aux_out_count].name = board_config.aux_out_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_1_gpio); - aux_out_count++; - } - - if (board_config.aux_out_2) { - aux_out[aux_out_count].gpio = board_config.aux_out_2_gpio; - aux_out[aux_out_count].name = board_config.aux_out_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_2_gpio); - aux_out_count++; - } - - if (board_config.aux_out_3) { - aux_out[aux_out_count].gpio = board_config.aux_out_3_gpio; - aux_out[aux_out_count].name = board_config.aux_out_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_3_gpio); - aux_out_count++; - } - - if (board_config.aux_out_4) { - aux_out[aux_out_count].gpio = board_config.aux_out_4_gpio; - aux_out[aux_out_count].name = board_config.aux_out_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_4_gpio); - aux_out_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // AIN - - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12 - }; - - if (board_config.aux_ain_1) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_1_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_1_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_1_adc_channel, &config)); - aux_ain_count++; - } - - if (board_config.aux_ain_2) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_2_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_2_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_2_adc_channel, &config)); - aux_ain_count++; - } -} - -esp_err_t aux_read(const char* name, bool* value) -{ - for (int i = 0; i < aux_in_count; i++) { - if (strcmp(aux_in[i].name, name) == 0) { - *value = gpio_get_level(aux_in[i].gpio) == 1; - return ESP_OK; - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_write(const char* name, bool value) -{ - for (int i = 0; i < aux_out_count; i++) { - if (strcmp(aux_out[i].name, name) == 0) { - return gpio_set_level(aux_out[i].gpio, value); - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_analog_read(const char* name, int* value) -{ - for (int i = 0; i < aux_ain_count; i++) { - if (strcmp(aux_ain[i].name, name) == 0) { - int raw = 0; - esp_err_t ret = adc_oneshot_read(adc_handle, aux_ain[i].adc, &raw); - if (ret == ESP_OK) { - return adc_cali_raw_to_voltage(adc_cali_handle, raw, value); - } else { - return ret; - } - } - } - return ESP_ERR_NOT_FOUND; -} -// === Fim de: components/peripherals/src/aux_io.c === - - -// === Início de: components/peripherals/src/lm75a.c === -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "driver/i2c_master.h" - -#define I2C_MASTER_NUM I2C_NUM_1 -#define I2C_MASTER_SCL_IO GPIO_NUM_22 // CONFIG_EXAMPLE_I2C_SCL /*!< gpio number for I2C master clock */ -#define I2C_MASTER_SDA_IO GPIO_NUM_21 // CONFIG_EXAMPLE_I2C_SDA /*!< gpio number for I2C master data */ -#define I2C_MASTER_FREQ_HZ 100000 // CONFIG_I2C_TRANS_SPEED /*!< I2C master clock frequency */ -#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define LM75A_SLAVE_ADDR 0x48 // CONFIG_LM75A_SLAVE_ADDR /*!< LM75A slave address, you can set any 7bit value */ -#define ACK_VAL 0x0 /*!< I2C ack value */ -#define NACK_VAL 0x1 /*!< I2C nack value */ -#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ -#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ -#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ -#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ - -/* -#define GPIO_INPUT_IO_0 CONFIG_LM75A_OS_PIN -#define GPIO_OUTPUT_IO_0 CONFIG_LM75A_VCC_PIN -#define GPIO_OUTPUT_PIN_SEL (1ULL << GPIO_OUTPUT_IO_0) -#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO_0) -#define ESP_INTR_FLAG_DEFAULT 0 -*/ - -// static xQueueHandle gpio_evt_queue = NULL; -// static int gpio_int_task_enable = 0; -// static TaskHandle_t gpio_int_task_handle = NULL; - -/** - * @brief test code to read esp-i2c-slave - * We need to fill the buffer of esp slave device, then master can read them out. - * - * _______________________________________________________________________________________ - * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop | - * --------|--------------------------|----------------------|--------------------|------| - * - */ -static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size) -{ - if (size == 0) - { - return ESP_OK; - } - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN); - if (size > 1) - { - i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); - } - i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief Test code to write esp-i2c-slave - * Master device write data to slave(both esp32), - * the data will be stored in slave buffer. - * We can read them out from slave buffer. - * - * ___________________________________________________________________ - * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | - * --------|---------------------------|----------------------|------| - * - */ -static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t *data_wr, size_t size) -{ - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN); - i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief i2c master initialization - */ -static void i2c_master_init() -{ - int i2c_master_port = I2C_MASTER_NUM; - i2c_config_t conf; - conf.mode = I2C_MODE_MASTER; - conf.sda_io_num = I2C_MASTER_SDA_IO; - conf.sda_pullup_en = GPIO_PULLUP_DISABLE; - conf.scl_io_num = I2C_MASTER_SCL_IO; - conf.scl_pullup_en = GPIO_PULLUP_DISABLE; - conf.master.clk_speed = I2C_MASTER_FREQ_HZ; - conf.clk_flags = 0; - - i2c_param_config(i2c_master_port, &conf); - i2c_driver_install(i2c_master_port, conf.mode, - I2C_MASTER_RX_BUF_DISABLE, - I2C_MASTER_TX_BUF_DISABLE, 0); -} - -int lm75a_read_temperature(int show) -{ - uint8_t buf[2]; - float tmp; - buf[0] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - if (show) - printf("lm75a_read_temperature=%.1f\n", tmp); - return tmp; -} - -/* -static void IRAM_ATTR gpio_isr_handler(void *arg) -{ - uint32_t gpio_num = (uint32_t)arg; - xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); -} - -static void gpio_int_task(void *arg) -{ - uint32_t io_num; - gpio_int_task_enable = 1; - while (gpio_int_task_enable) - { - if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) - { - - // read temperature to clean int; - if (io_num == GPIO_INPUT_IO_0) - { - printf("GPIO[%d] intr, val: %d\n\n", io_num, gpio_get_level(io_num)); - lm75a_read_temperature(0); // read to clean interrupt. - } - } - } - printf("quit gpio_int_task\n"); - if (gpio_evt_queue) - { - vQueueDelete(gpio_evt_queue); - gpio_evt_queue = NULL; - } - gpio_int_task_handle = NULL; - vTaskDelete(NULL); -} - -void init_os_gpio() -{ - printf("init_os_gpio!\n"); - - if (gpio_evt_queue == NULL) - gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); - - if (gpio_int_task_handle == NULL) - { - xTaskCreate(gpio_int_task, "gpio_int_task", 2048, NULL, 10, &gpio_int_task_handle); - // install gpio isr service - gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); - // hook isr handler for specific gpio pin again - gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0); - } -} - -static void deinit_os_gpio() -{ - printf("deinit_os_gpio!\n"); - - if (gpio_int_task_handle) - { - gpio_isr_handler_remove(GPIO_INPUT_IO_0); - gpio_uninstall_isr_service(); - gpio_int_task_enable = 0; - int io = 0; - xQueueSend(gpio_evt_queue, &io, 0); // send a fake signal to quit task. - } -} - -static void lm75a_vcc_enable() -{ - gpio_config_t io_conf; - // enable output for vcc - io_conf.intr_type = GPIO_PIN_INTR_DISABLE; - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; - io_conf.pull_down_en = 0; - io_conf.pull_up_en = 0; - gpio_config(&io_conf); - - // enable input for interrupt - io_conf.intr_type = GPIO_PIN_INTR_NEGEDGE; // GPIO_PIN_INTR_ANYEDGE; - io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = 1; - gpio_set_pull_mode(GPIO_INPUT_IO_0, GPIO_FLOATING); - gpio_config(&io_conf); - gpio_set_level(GPIO_OUTPUT_IO_0, 1); -} - -static void lm75a_vcc_disable() -{ - gpio_set_level(GPIO_OUTPUT_IO_0, 0); -} -*/ - -void lm75a_init() -{ - // lm75a_vcc_enable(); - i2c_master_init(); -} - -void lm75a_deinit() -{ - // deinit_os_gpio(); - i2c_driver_delete(I2C_MASTER_NUM); - // lm75a_vcc_disable(); -} - -void lm75a_set_tos(int tos) -{ - uint8_t buf[4]; - printf("lm75a_set_tos: %d\n", tos); - // set Tos: - buf[0] = 0x3; - buf[1] = (tos & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_set_thys(int thys) -{ - uint8_t buf[4]; - printf("lm75a_set_thys: %d\n", thys); - // set Thyst: - buf[0] = 0x2; - buf[1] = (thys & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_get_tos() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x3; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_tos: %.1f\n", tmp); -} - -void lm75a_get_thys() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x2; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_thys: %.1f\n", tmp); -} - -void lm75a_set_int(int en) -{ - uint8_t buf[2]; - - en = !!en; - if (en) - { - printf("lm75a_set_int: %d\n", en); - buf[0] = 0x1; - buf[1] = (1 << 1); // D1 set to 1; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - // gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE); - // init_os_gpio(); - } - else - { - printf("lm75a_set_int: %d\n", en); - // deinit_os_gpio(); - buf[0] = 0x1; - buf[1] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - } -} - -void lm75a_get_osio() -{ - // printf("os_io: %d\n", gpio_get_level(GPIO_INPUT_IO_0)); -} - -// === Fim de: components/peripherals/src/lm75a.c === diff --git a/projeto_parte8.c b/projeto_parte8.c deleted file mode 100755 index 683d4f6..0000000 --- a/projeto_parte8.c +++ /dev/null @@ -1,783 +0,0 @@ - - -// === Início de: components/peripherals/src/onewire.c === -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. - */ - -#include -#include -#include -#include "rom/ets_sys.h" - -#include "onewire.h" - -#define ONEWIRE_SELECT_ROM 0x55 -#define ONEWIRE_SKIP_ROM 0xcc -#define ONEWIRE_SEARCH 0xf0 -#define ONEWIRE_CRC8_TABLE - -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - -// Waits up to `max_wait` microseconds for the specified pin to go high. -// Returns true if successful, false if the bus never comes high (likely -// shorted). -static inline bool _onewire_wait_for_bus(gpio_num_t pin, int max_wait) -{ - bool state; - for (int i = 0; i < ((max_wait + 4) / 5); i++) { - if (gpio_get_level(pin)) - break; - ets_delay_us(5); - } - state = gpio_get_level(pin); - // Wait an extra 1us to make sure the devices have an adequate recovery - // time before we drive things low again. - ets_delay_us(1); - return state; -} - -static void setup_pin(gpio_num_t pin, bool open_drain) -{ - gpio_set_direction(pin, open_drain ? GPIO_MODE_INPUT_OUTPUT_OD : GPIO_MODE_OUTPUT); - // gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY); -} - -// Perform the onewire reset function. We will wait up to 250uS for -// the bus to come high, if it doesn't then it is broken or shorted -// and we return false; -// -// Returns true if a device asserted a presence pulse, false otherwise. -// -bool onewire_reset(gpio_num_t pin) -{ - setup_pin(pin, true); - - gpio_set_level(pin, 1); - // wait until the wire is high... just in case - if (!_onewire_wait_for_bus(pin, 250)) - return false; - - gpio_set_level(pin, 0); - ets_delay_us(480); - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 1); // allow it to float - ets_delay_us(70); - bool r = !gpio_get_level(pin); - portEXIT_CRITICAL(&mux); - - // Wait for all devices to finish pulling the bus low before returning - if (!_onewire_wait_for_bus(pin, 410)) - return false; - - return r; -} - -static bool _onewire_write_bit(gpio_num_t pin, bool v) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return false; - - portENTER_CRITICAL(&mux); - if (v) { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(10); - gpio_set_level(pin, 1); // allow output high - ets_delay_us(55); - } else { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(65); - gpio_set_level(pin, 1); // allow output high - } - ets_delay_us(1); - portEXIT_CRITICAL(&mux); - - return true; -} - -static int _onewire_read_bit(gpio_num_t pin) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return -1; - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 0); - ets_delay_us(2); - gpio_set_level(pin, 1); // let pin float, pull up will raise - ets_delay_us(11); - int r = gpio_get_level(pin); // Must sample within 15us of start - ets_delay_us(48); - portEXIT_CRITICAL(&mux); - - return r; -} - -// Write a byte. The writing code uses open-drain mode and expects the pullup -// resistor to pull the line high when not driven low. If you need strong -// power after the write (e.g. DS18B20 in parasite power mode) then call -// onewire_power() after this is complete to actively drive the line high. -// -bool onewire_write(gpio_num_t pin, uint8_t v) -{ - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) - if (!_onewire_write_bit(pin, (bitMask & v))) - return false; - - return true; -} - -bool onewire_write_bytes(gpio_num_t pin, const uint8_t* buf, size_t count) -{ - for (size_t i = 0; i < count; i++) - if (!onewire_write(pin, buf[i])) - return false; - - return true; -} - -// Read a byte -// -int onewire_read(gpio_num_t pin) -{ - int r = 0; - - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { - int bit = _onewire_read_bit(pin); - if (bit < 0) - return -1; - else if (bit) - r |= bitMask; - } - return r; -} - -bool onewire_read_bytes(gpio_num_t pin, uint8_t* buf, size_t count) -{ - size_t i; - int b; - - for (i = 0; i < count; i++) { - b = onewire_read(pin); - if (b < 0) - return false; - buf[i] = b; - } - return true; -} - -bool onewire_select(gpio_num_t pin, onewire_addr_t addr) -{ - uint8_t i; - - if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) - return false; - - for (i = 0; i < 8; i++) { - if (!onewire_write(pin, addr & 0xff)) - return false; - addr >>= 8; - } - - return true; -} - -bool onewire_skip_rom(gpio_num_t pin) -{ - return onewire_write(pin, ONEWIRE_SKIP_ROM); -} - -bool onewire_power(gpio_num_t pin) -{ - // Make sure the bus is not being held low before driving it high, or we - // may end up shorting ourselves out. - if (!_onewire_wait_for_bus(pin, 10)) - return false; - - setup_pin(pin, false); - gpio_set_level(pin, 1); - - return true; -} - -void onewire_depower(gpio_num_t pin) -{ - setup_pin(pin, true); -} - -void onewire_search_start(onewire_search_t* search) -{ - // reset the search state - memset(search, 0, sizeof(*search)); -} - -void onewire_search_prefix(onewire_search_t* search, uint8_t family_code) -{ - uint8_t i; - - search->rom_no[0] = family_code; - for (i = 1; i < 8; i++) { - search->rom_no[i] = 0; - } - search->last_discrepancy = 64; - search->last_device_found = false; -} - -// Perform a search. If the next device has been successfully enumerated, its -// ROM address will be returned. If there are no devices, no further -// devices, or something horrible happens in the middle of the -// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to -// start over. -// -// --- Replaced by the one from the Dallas Semiconductor web site --- -//-------------------------------------------------------------------------- -// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing -// search state. -// Return 1 : device found, ROM number in ROM_NO buffer -// 0 : device not found, end of search -// -onewire_addr_t onewire_search_next(onewire_search_t* search, gpio_num_t pin) -{ - //TODO: add more checking for read/write errors - uint8_t id_bit_number; - uint8_t last_zero, search_result; - int rom_byte_number; - int8_t id_bit, cmp_id_bit; - onewire_addr_t addr; - unsigned char rom_byte_mask; - bool search_direction; - - // initialize for search - id_bit_number = 1; - last_zero = 0; - rom_byte_number = 0; - rom_byte_mask = 1; - search_result = 0; - - // if the last call was not the last one - if (!search->last_device_found) { - // 1-Wire reset - if (!onewire_reset(pin)) { - // reset the search - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } - - // issue the search command - onewire_write(pin, ONEWIRE_SEARCH); - - // loop to do the search - do { - // read a bit and its complement - id_bit = _onewire_read_bit(pin); - cmp_id_bit = _onewire_read_bit(pin); - - if ((id_bit == 1) && (cmp_id_bit == 1)) - break; - else { - // all devices coupled have 0 or 1 - if (id_bit != cmp_id_bit) - search_direction = id_bit; // bit write value for search - else { - // if this discrepancy if before the Last Discrepancy - // on a previous next then pick the same as last time - if (id_bit_number < search->last_discrepancy) - search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0); - else - // if equal to last pick 1, if not then pick 0 - search_direction = (id_bit_number == search->last_discrepancy); - - // if 0 was picked then record its position in LastZero - if (!search_direction) - last_zero = id_bit_number; - } - - // set or clear the bit in the ROM byte rom_byte_number - // with mask rom_byte_mask - if (search_direction) - search->rom_no[rom_byte_number] |= rom_byte_mask; - else - search->rom_no[rom_byte_number] &= ~rom_byte_mask; - - // serial number search direction write bit - _onewire_write_bit(pin, search_direction); - - // increment the byte counter id_bit_number - // and shift the mask rom_byte_mask - id_bit_number++; - rom_byte_mask <<= 1; - - // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask - if (rom_byte_mask == 0) { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } while (rom_byte_number < 8); // loop until through all ROM bytes 0-7 - - // if the search was successful then - if (!(id_bit_number < 65)) { - // search successful so set last_discrepancy,last_device_found,search_result - search->last_discrepancy = last_zero; - - // check for last device - if (search->last_discrepancy == 0) - search->last_device_found = true; - - search_result = 1; - } - } - - // if no device found then reset counters so next 'search' will be like a first - if (!search_result || !search->rom_no[0]) { - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } else { - addr = 0; - for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) { - addr = (addr << 8) | search->rom_no[rom_byte_number]; - } - //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr); - } - return addr; -} - -// The 1-Wire CRC scheme is described in Maxim Application Note 27: -// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" -// - -#ifdef ONEWIRE_CRC8_TABLE -// This table comes from Dallas sample code where it is freely reusable, -// though Copyright (c) 2000 Dallas Semiconductor Corporation -static const uint8_t dscrc_table[] = { - 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, - 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, - 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, - 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, - 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, - 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, - 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, - 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, - 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, - 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, - 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, - 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, - 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, - 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, - 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, - 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 -}; - -// -// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM -// and the registers. (note: this might better be done without to -// table, it would probably be smaller and certainly fast enough -// compared to all those delayMicrosecond() calls. But I got -// confused, so I use this table from the examples.) -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - crc = dscrc_table[crc ^ *data++]; - - return crc; -} -#else -// -// Compute a Dallas Semiconductor 8 bit CRC directly. -// this is much slower, but much smaller, than the lookup table. -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - { - uint8_t inbyte = *data++; - for (int i = 8; i; i--) - { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) - crc ^= 0x8C; - inbyte >>= 1; - } - } - return crc; -} -#endif /* ONEWIRE_CRC8_TABLE */ - -// Compute the 1-Wire CRC16 and compare it against the received CRC. -// Example usage (reading a DS2408): -// // Put everything in a buffer so we can compute the CRC easily. -// uint8_t buf[13]; -// buf[0] = 0xF0; // Read PIO Registers -// buf[1] = 0x88; // LSB address -// buf[2] = 0x00; // MSB address -// WriteBytes(net, buf, 3); // Write 3 cmd bytes -// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 -// if (!CheckCRC16(buf, 11, &buf[11])) { -// // Handle error. -// } -// -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param inverted_crc - The two CRC16 bytes in the received data. -// This should just point into the received data, -// *not* at a 16-bit integer. -// @param crc - The crc starting value (optional) -// @return 1, iff the CRC matches. -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) -{ - uint16_t crc = ~onewire_crc16(input, len, crc_iv); - return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; -} - -// Compute a Dallas Semiconductor 16 bit CRC. This is required to check -// the integrity of data received from many 1-Wire devices. Note that the -// CRC computed here is *not* what you'll get from the 1-Wire network, -// for two reasons: -// 1) The CRC is transmitted bitwise inverted. -// 2) Depending on the endian-ness of your processor, the binary -// representation of the two-byte return value may have a different -// byte order than the two bytes you get from 1-Wire. -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param crc - The crc starting value (optional) -// @return The CRC16, as defined by Dallas Semiconductor. -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) -{ - uint16_t crc = crc_iv; - static const uint8_t oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; - - uint16_t i; - for (i = 0; i < len; i++) { - // Even though we're just copying a byte from the input, - // we'll be doing 16-bit computation with it. - uint16_t cdata = input[i]; - cdata = (cdata ^ crc) & 0xff; - crc >>= 8; - - if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) - crc ^= 0xC001; - - cdata <<= 6; - crc ^= cdata; - cdata <<= 1; - crc ^= cdata; - } - return crc; -} -// === Fim de: components/peripherals/src/onewire.c === - - -// === Início de: components/peripherals/src/onewire.h === -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. - */ - -#ifndef ONEWIRE_H_ -#define ONEWIRE_H_ - -#include -#include -#include "driver/gpio.h" - -/** - * Type used to hold all 1-Wire device ROM addresses (64-bit) - */ -typedef uint64_t onewire_addr_t; - -/** - * Structure to contain the current state for onewire_search_next(), etc - */ -typedef struct -{ - uint8_t rom_no[8]; - uint8_t last_discrepancy; - bool last_device_found; -} onewire_search_t; - -/** - * ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device - * (CRC mismatch), and so can be useful as an indicator for "no-such-device", - * etc. - */ -#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) - -/** - * @brief Perform a 1-Wire reset cycle. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if at least one device responds with a presence pulse, - * `false` if no devices were detected (or the bus is shorted, etc) - */ -bool onewire_reset(gpio_num_t pin); - -/** - * @brief Issue a 1-Wire "ROM select" command to select a particular device. - * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param addr The ROM address of the device to select - * - * @return `true` if the "ROM select" command could be successfully issued, - * `false` if there was an error. - */ -bool onewire_select(gpio_num_t pin, const onewire_addr_t addr); - -/** - * @brief Issue a 1-Wire "skip ROM" command to select *all* devices on the bus. - * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if the "skip ROM" command could be successfully issued, - * `false` if there was an error. - */ -bool onewire_skip_rom(gpio_num_t pin); - -/** - * @brief Write a byte on the onewire bus. - * - * The writing code uses open-drain mode and expects the pullup resistor to - * pull the line high when not driven low. If you need strong power after the - * write (e.g. DS18B20 in parasite power mode) then call ::onewire_power() - * after this is complete to actively drive the line high. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param v The byte value to write - * - * @return `true` if successful, `false` on error. - */ -bool onewire_write(gpio_num_t pin, uint8_t v); - -/** - * @brief Write multiple bytes on the 1-Wire bus. - * - * See ::onewire_write() for more info. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param buf A pointer to the buffer of bytes to be written - * @param count Number of bytes to write - * - * @return `true` if all bytes written successfully, `false` on error. - */ -bool onewire_write_bytes(gpio_num_t pin, const uint8_t *buf, size_t count); - -/** - * @brief Read a byte from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return the read byte on success, negative value on error. - */ -int onewire_read(gpio_num_t pin); - -/** - * @brief Read multiple bytes from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param[out] buf A pointer to the buffer to contain the read bytes - * @param count Number of bytes to read - * - * @return `true` on success, `false` on error. - */ -bool onewire_read_bytes(gpio_num_t pin, uint8_t *buf, size_t count); - -/** - * @brief Actively drive the bus high to provide extra power for certain - * operations of parasitically-powered devices. - * - * For parasitically-powered devices which need more power than can be - * provided via the normal pull-up resistor, it may be necessary for some - * operations to drive the bus actively high. This function can be used to - * perform that operation. - * - * The bus can be depowered once it is no longer needed by calling - * ::onewire_depower(), or it will be depowered automatically the next time - * ::onewire_reset() is called to start another command. - * - * @note Make sure the device(s) you are powering will not pull more current - * than the ESP32/ESP8266 is able to supply via its GPIO pins (this is - * especially important when multiple devices are on the same bus and - * they are all performing a power-intensive operation at the same time - * (i.e. multiple DS18B20 sensors, which have all been given a - * "convert T" operation by using ::onewire_skip_rom())). - * - * @note This routine will check to make sure that the bus is already high - * before driving it, to make sure it doesn't attempt to drive it high - * while something else is pulling it low (which could cause a reset or - * damage the ESP32/ESP8266). - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` on success, `false` on error. - */ -bool onewire_power(gpio_num_t pin); - -/** - * @brief Stop forcing power onto the bus. - * - * You only need to do this if you previously called ::onewire_power() to drive - * the bus high and now want to allow it to float instead. Note that - * onewire_reset() will also automatically depower the bus first, so you do - * not need to call this first if you just want to start a new operation. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - */ -void onewire_depower(gpio_num_t pin); - -/** - * @brief Clear the search state so that it will start from the beginning on - * the next call to ::onewire_search_next(). - * - * @param[out] search The onewire_search_t structure to reset. - */ -void onewire_search_start(onewire_search_t *search); - -/** - * @brief Setup the search to search for devices with the specified - * "family code". - * - * @param[out] search The onewire_search_t structure to update. - * @param family_code The "family code" to search for. - */ -void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); - -/** - * @brief Search for the next device on the bus. - * - * The order of returned device addresses is deterministic. You will always - * get the same devices in the same order. - * - * @note It might be a good idea to check the CRC to make sure you didn't get - * garbage. - * - * @return the address of the next device on the bus, or ::ONEWIRE_NONE if - * there is no next address. ::ONEWIRE_NONE might also mean that - * the bus is shorted, there are no devices, or you have already - * retrieved all of them. - */ -onewire_addr_t onewire_search_next(onewire_search_t *search, gpio_num_t pin); - -/** - * @brief Compute a Dallas Semiconductor 8 bit CRC. - * - * These are used in the ROM address and scratchpad registers to verify the - * transmitted data is correct. - */ -uint8_t onewire_crc8(const uint8_t *data, uint8_t len); - -/** - * @brief Compute the 1-Wire CRC16 and compare it against the received CRC. - * - * Example usage (reading a DS2408): - * @code{.c} - * // Put everything in a buffer so we can compute the CRC easily. - * uint8_t buf[13]; - * buf[0] = 0xF0; // Read PIO Registers - * buf[1] = 0x88; // LSB address - * buf[2] = 0x00; // MSB address - * onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes - * onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 - * if (!onewire_check_crc16(buf, 11, &buf[11])) { - * // TODO: Handle error. - * } - * @endcode - * - * @param input Array of bytes to checksum. - * @param len Number of bytes in `input` - * @param inverted_crc The two CRC16 bytes in the received data. - * This should just point into the received data, - * *not* at a 16-bit integer. - * @param crc_iv The crc starting value (optional) - * - * @return `true` if the CRC matches, `false` otherwise. - */ -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); - -/** - * @brief Compute a Dallas Semiconductor 16 bit CRC. - * - * This is required to check the integrity of data received from many 1-Wire - * devices. Note that the CRC computed here is *not* what you'll get from the - * 1-Wire network, for two reasons: - * - * 1. The CRC is transmitted bitwise inverted. - * 2. Depending on the endian-ness of your processor, the binary - * representation of the two-byte return value may have a different - * byte order than the two bytes you get from 1-Wire. - * - * @param input Array of bytes to checksum. - * @param len How many bytes are in `input`. - * @param crc_iv The crc starting value (optional) - * - * @return the CRC16, as defined by Dallas Semiconductor. - */ -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); - - -#endif /* ONEWIRE_H_ */ -// === Fim de: components/peripherals/src/onewire.h === diff --git a/projeto_parte9.c b/projeto_parte9.c deleted file mode 100755 index 54217fa..0000000 --- a/projeto_parte9.c +++ /dev/null @@ -1,1232 +0,0 @@ - - -// === Início de: components/peripherals/src/ds18x20.c === -/* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#include -#include -#include -#include -#include "ds18x20.h" - -#define ds18x20_WRITE_SCRATCHPAD 0x4E -#define ds18x20_READ_SCRATCHPAD 0xBE -#define ds18x20_COPY_SCRATCHPAD 0x48 -#define ds18x20_READ_EEPROM 0xB8 -#define ds18x20_READ_PWRSUPPLY 0xB4 -#define ds18x20_SEARCHROM 0xF0 -#define ds18x20_SKIP_ROM 0xCC -#define ds18x20_READROM 0x33 -#define ds18x20_MATCHROM 0x55 -#define ds18x20_ALARMSEARCH 0xEC -#define ds18x20_CONVERT_T 0x44 - -#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) -#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) - -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - -static const char* TAG = "ds18x20"; - -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_CONVERT_T); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - if (wait){ - vTaskDelay(pdMS_TO_TICKS(750)); - onewire_depower(pin); - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - uint8_t crc; - uint8_t expected_crc; - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_READ_SCRATCHPAD); - - for (int i = 0; i < 8; i++) - buffer[i] = onewire_read(pin); - crc = onewire_read(pin); - - expected_crc = onewire_crc8(buffer, 8); - if (crc != expected_crc) - { - ESP_LOGE(TAG, "CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)", buffer[0], buffer[1], - buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); - return ESP_ERR_INVALID_CRC; - } - - return ESP_OK; -} - -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_WRITE_SCRATCHPAD); - - for (int i = 0; i < 3; i++) - onewire_write(pin, buffer[i]); - - return ESP_OK; -} - -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_COPY_SCRATCHPAD); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - // And then it needs to keep that power up for 10ms. - vTaskDelay(pdMS_TO_TICKS(10)); - onewire_depower(pin); - - return ESP_OK; -} - -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - - *temperature = ((int16_t)temp * 625.0) / 100; - - return ESP_OK; -} - -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; - - *temperature = (temp * 625) / 100 - 25; - - return ESP_OK; -} - -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - if ((uint8_t)addr == DS18B20_FAMILY_ID) { - return ds18b20_read_temperature(pin, addr, temperature); - } else { - return ds18s20_read_temperature(pin, addr, temperature); - } -} - -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18b20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18s20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18x20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list && addr_count); - - CHECK(ds18x20_measure(pin, DS18X20_ANY, true)); - - return ds18x20_read_temp_multi(pin, addr_list, addr_count, result_list); -} - -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, size_t* found) -{ - CHECK_ARG(addr_list && addr_count); - - onewire_search_t search; - onewire_addr_t addr; - - *found = 0; - onewire_search_start(&search); - while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) - { - uint8_t family_id = (uint8_t)addr; - if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) - { - if (*found < addr_count) - addr_list[*found] = addr; - *found += 1; - } - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list); - - esp_err_t res = ESP_OK; - for (size_t i = 0; i < addr_count; i++) - { - esp_err_t tmp = ds18x20_read_temperature(pin, addr_list[i], &result_list[i]); - if (tmp != ESP_OK) - res = tmp; - } - return res; -} - -// === Fim de: components/peripherals/src/ds18x20.c === - - -// === Início de: components/peripherals/src/led.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "led.h" -#include "board_config.h" -#include "evse_error.h" -#include "evse_api.h" - -#define LED_UPDATE_INTERVAL_MS 100 -#define BLOCK_TIME pdMS_TO_TICKS(10) - -static const char *TAG = "led"; - -typedef struct { - gpio_num_t gpio; - bool on : 1; - uint16_t ontime; - uint16_t offtime; - TimerHandle_t timer; - led_pattern_t pattern; - uint8_t blink_count; -} led_t; - -static led_t leds[LED_ID_MAX] = {0}; -static TimerHandle_t led_update_timer = NULL; -static evse_state_t led_state = -1; - -// ---------------------------- -// Funções Internas -// ---------------------------- - -static void led_update_timer_callback(TimerHandle_t xTimer); -static void led_update(void); -static void led_apply_by_state(evse_state_t state); - -static inline void led_gpio_write(gpio_num_t gpio, bool level) { - if (gpio != GPIO_NUM_NC) - gpio_set_level(gpio, level); -} - -static void led_timer_callback(TimerHandle_t xTimer) -{ - led_t *led = (led_t *)pvTimerGetTimerID(xTimer); - led->on = !led->on; - led_gpio_write(led->gpio, led->on); - uint32_t next_time = led->on ? led->ontime : led->offtime; - - xTimerChangePeriod(led->timer, pdMS_TO_TICKS(next_time), BLOCK_TIME); -} - -// ---------------------------- -// Inicialização -// ---------------------------- - -void led_init(void) -{ - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .intr_type = GPIO_INTR_DISABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_ENABLE, - .pin_bit_mask = 0 - }; - - for (int i = 0; i < LED_ID_MAX; i++) { - leds[i].gpio = GPIO_NUM_NC; - } - - if (board_config.led_stop) { - leds[LED_ID_STOP].gpio = board_config.led_stop_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_stop_gpio); - } - - if (board_config.led_charging) { - leds[LED_ID_CHARGING].gpio = board_config.led_charging_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_charging_gpio); - } - - if (board_config.led_error) { - leds[LED_ID_ERROR].gpio = board_config.led_error_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_error_gpio); - } - - if (io_conf.pin_bit_mask != 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - if (!led_update_timer) { - led_update_timer = xTimerCreate("led_update_timer", - pdMS_TO_TICKS(LED_UPDATE_INTERVAL_MS), - pdTRUE, NULL, - led_update_timer_callback); - if (led_update_timer) { - xTimerStart(led_update_timer, BLOCK_TIME); - } else { - ESP_LOGE(TAG, "Failed to create LED update timer"); - } - } -} - -// ---------------------------- -// API Pública -// ---------------------------- - -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime) -{ - if (led_id >= LED_ID_MAX) return; - - led_t *led = &leds[led_id]; - if (led->gpio == GPIO_NUM_NC) return; - - // Evita reconfiguração idêntica - if (led->ontime == ontime && led->offtime == offtime) - return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->ontime = ontime; - led->offtime = offtime; - - if (ontime == 0) { - led->on = false; - led_gpio_write(led->gpio, 0); - } else if (offtime == 0) { - led->on = true; - led_gpio_write(led->gpio, 1); - } else { - led->on = true; - led_gpio_write(led->gpio, 1); - - if (!led->timer) { - led->timer = xTimerCreate("led_timer", pdMS_TO_TICKS(ontime), - pdFALSE, (void *)led, led_timer_callback); - } - - if (led->timer) { - xTimerStart(led->timer, BLOCK_TIME); - } - } -} - -void led_apply_pattern(led_id_t id, led_pattern_t pattern) -{ - if (id >= LED_ID_MAX) return; - - led_t *led = &leds[id]; - if (led->gpio == GPIO_NUM_NC) return; - - if (led->pattern == pattern) return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->pattern = pattern; - led->blink_count = 0; - - switch (pattern) { - case LED_PATTERN_OFF: - led_set_state(id, 0, 0); - break; - case LED_PATTERN_ON: - led_set_state(id, 1, 0); - break; - case LED_PATTERN_BLINK: - led_set_state(id, 500, 500); - break; - case LED_PATTERN_BLINK_FAST: - led_set_state(id, 200, 200); - break; - case LED_PATTERN_BLINK_SLOW: - led_set_state(id, 300, 1700); - break; - case LED_PATTERN_CHARGING_EFFECT: - led_set_state(id, 2000, 1000); - break; - } -} - -// ---------------------------- -// Controle por Estado -// ---------------------------- - -static void led_apply_by_state(evse_state_t state) -{ - // Reset todos - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_OFF); - - switch (state) { - case EVSE_STATE_A: - led_apply_pattern(LED_ID_STOP, LED_PATTERN_ON); - break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - case EVSE_STATE_C1: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_ON); - break; - case EVSE_STATE_C2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_CHARGING_EFFECT); - break; - case EVSE_STATE_D1: - case EVSE_STATE_D2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_BLINK_FAST); - break; - case EVSE_STATE_E: - case EVSE_STATE_F: - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - break; - default: - break; - } -} - -// ---------------------------- -// Timer Update -// ---------------------------- - -static void led_update(void) -{ - if (evse_error_is_active()) { - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - return; - } - - evse_state_t current = evse_get_state(); - - if (current != led_state) { - led_state = current; - led_apply_by_state(current); - } -} - -static void led_update_timer_callback(TimerHandle_t xTimer) -{ - (void)xTimer; - led_update(); -} - -// === Fim de: components/peripherals/src/led.c === - - -// === Início de: components/peripherals/src/rcm.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "esp_log.h" - -#include "rcm.h" -#include "board_config.h" -#include "evse_api.h" - -// static bool do_test = false; - -// static bool triggered = false; - -// static bool test_triggered = false; - -// static void IRAM_ATTR rcm_isr_handler(void* arg) -// { -// if (!do_test) { -// triggered = true; -// } else { -// test_triggered = true; -// } -// } - -void rcm_init(void) -{ - if (board_config.rcm) { - gpio_config_t io_conf = {}; - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = BIT64(board_config.rcm_test_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - io_conf.mode = GPIO_MODE_INPUT; - // io_conf.intr_type = GPIO_INTR_POSEDGE; - io_conf.pin_bit_mask = BIT64(board_config.rcm_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - //ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.rcm_gpio, rcm_isr_handler, NULL)); - } -} - -bool rcm_test(void) -{ - // do_test = true; - // test_triggered = false; - - // gpio_set_level(board_config.rcm_test_gpio, 1); - // vTaskDelay(pdMS_TO_TICKS(100)); - // gpio_set_level(board_config.rcm_test_gpio, 0); - - // do_test = false; - - // return test_triggered; - - gpio_set_level(board_config.rcm_test_gpio, 1); - vTaskDelay(pdMS_TO_TICKS(100)); - bool success = gpio_get_level(board_config.rcm_gpio) == 1; - gpio_set_level(board_config.rcm_test_gpio, 0); - - return success; -} - -bool rcm_is_triggered(void) -{ - // bool _triggered = triggered; - // if (gpio_get_level(board_config.rcm_gpio) == 0) { - // triggered = false; - // } - // return _triggered; - if (gpio_get_level(board_config.rcm_gpio) == 1) { - vTaskDelay(pdMS_TO_TICKS(1)); - return gpio_get_level(board_config.rcm_gpio) == 1; - } - - return false; -} -// === Fim de: components/peripherals/src/rcm.c === - - -// === Início de: components/peripherals/src/adc.c === -#include "adc.h" -#include "esp_log.h" - -const static char* TAG = "adc"; - -adc_oneshot_unit_handle_t adc_handle; - -adc_cali_handle_t adc_cali_handle; - -void adc_init(void) -{ - adc_oneshot_unit_init_cfg_t conf = { - .unit_id = ADC_UNIT_1 - }; - ESP_ERROR_CHECK(adc_oneshot_new_unit(&conf, &adc_handle)); - - bool calibrated = false; - -#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Curve Fitting"); - adc_cali_curve_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - if (adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } -#endif - -#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Line Fitting"); - adc_cali_line_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, -#if CONFIG_IDF_TARGET_ESP32 - .default_vref = 1100 -#endif - }; - if (adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } -#endif - - if (!calibrated) { - ESP_LOGE(TAG, "No calibration scheme"); - ESP_ERROR_CHECK(ESP_FAIL); - } -} -// === Fim de: components/peripherals/src/adc.c === - - -// === Início de: components/peripherals/src/adc121s021_dma.c === -#include "driver/spi_master.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "adc121s021_dma.h" - -#define TAG "adc_dma" - -#define PIN_NUM_MOSI 23 -#define PIN_NUM_MISO 19 -#define PIN_NUM_CLK 18 -#define PIN_NUM_CS 5 - -#define SPI_HOST_USED SPI2_HOST -#define SAMPLE_SIZE_BYTES 2 -#define ADC_BITS 12 - -static spi_device_handle_t adc_spi; - -void adc121s021_dma_init(void) -{ - spi_bus_config_t buscfg = { - .mosi_io_num = PIN_NUM_MOSI, - .miso_io_num = PIN_NUM_MISO, - .sclk_io_num = PIN_NUM_CLK, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = SAMPLE_SIZE_BYTES, - }; - - spi_device_interface_config_t devcfg = { - .clock_speed_hz = 6000000, // 6 MHz - .mode = 0, - .spics_io_num = PIN_NUM_CS, - .queue_size = 2, - .flags = SPI_DEVICE_NO_DUMMY, - }; - - ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST_USED, &buscfg, SPI_DMA_CH_AUTO)); - ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST_USED, &devcfg, &adc_spi)); -} - -bool adc121s021_dma_get_sample(uint16_t *sample) -{ - uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy TX - uint8_t rx_buffer[2] = {0}; - - spi_transaction_t t = { - .length = 16, // 16 bits - .tx_buffer = tx_buffer, - .rx_buffer = rx_buffer, - .flags = 0 - }; - - esp_err_t err = spi_device_transmit(adc_spi, &t); - if (err != ESP_OK) { - ESP_LOGE(TAG, "SPI transmit error: %s", esp_err_to_name(err)); - return false; - } - - // Extrai os 12 bits significativos da resposta do ADC121S021 - *sample = ((rx_buffer[0] << 8) | rx_buffer[1]) & 0x0FFF; - - return true; -} - -// === Fim de: components/peripherals/src/adc121s021_dma.c === - - -// === Início de: components/peripherals/src/peripherals.c === -#include "peripherals.h" -#include "adc.h" -#include "led.h" -#include "buzzer.h" -#include "proximity.h" -#include "ac_relay.h" -#include "socket_lock.h" -#include "rcm.h" -#include "aux_io.h" -#include "ntc_sensor.h" - -void peripherals_init(void) -{ - ac_relay_init(); - led_init(); - buzzer_init(); - adc_init(); - proximity_init(); - // socket_lock_init(); - // rcm_init(); - //energy_meter_init(); - // aux_init(); - ntc_sensor_init(); -} -// === Fim de: components/peripherals/src/peripherals.c === - - -// === Início de: components/peripherals/include/adc121s021_dma.h === -#ifndef ADC_DMA_H_ -#define ADC_DMA_H_ - - -#include -#include - -void adc121s021_dma_init(void); -bool adc121s021_dma_get_sample(uint16_t *sample); - - -#endif /* ADC_DMA_h_ */ - -// === Fim de: components/peripherals/include/adc121s021_dma.h === - - -// === Início de: components/peripherals/include/peripherals.h === -#ifndef PERIPHERALS_H -#define PERIPHERALS_H - -void peripherals_init(void); - -#endif /* PERIPHERALS_H */ - -// === Fim de: components/peripherals/include/peripherals.h === - - -// === Início de: components/peripherals/include/rcm.h === -#ifndef RCM_H_ -#define RCM_H_ - -#include - -/** - * @brief Initialize residual current monitor - * - */ -void rcm_init(void); - -/** - * @brief Test residual current monitor - * - * @return true - * @return false - */ -bool rcm_test(void); - -/** - * @brief Residual current monitor was detected leakage - * - * @return true - * @return false - */ -bool rcm_is_triggered(void); - -#endif /* RCM_H_ */ - -// === Fim de: components/peripherals/include/rcm.h === - - -// === Início de: components/peripherals/include/aux_io.h === -#ifndef AUX_IO_H_ -#define AUX_IO_H_ - -#include "esp_err.h" - -/** - * @brief Initialize aux - * - */ -void aux_init(void); - -/** - * @brief Read digital input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_read(const char *name, bool *value); - -/** - * @brief Write digial output - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_write(const char *name, bool value); - -/** - * @brief Read analog input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_analog_read(const char *name, int *value); - -#endif /* AUX_IO_H_ */ -// === Fim de: components/peripherals/include/aux_io.h === - - -// === Início de: components/peripherals/include/led.h === -#ifndef LED_H_ -#define LED_H_ - -#include -#include - -/** - * @brief Identificadores dos LEDs disponíveis no hardware - */ -typedef enum { - LED_ID_STOP, - LED_ID_CHARGING, - LED_ID_ERROR, - LED_ID_MAX -} led_id_t; - -/** - * @brief Padrões de comportamento possíveis para os LEDs - */ -typedef enum { - LED_PATTERN_OFF, ///< LED sempre desligado - LED_PATTERN_ON, ///< LED sempre ligado - LED_PATTERN_BLINK, ///< Pisca com ciclo padrão (500ms on / 500ms off) - LED_PATTERN_BLINK_FAST, ///< Pisca rápido (200ms / 200ms) - LED_PATTERN_BLINK_SLOW, ///< Pisca lento (300ms / 1700ms) - LED_PATTERN_CHARGING_EFFECT ///< Efeito visual para carregamento (2s on / 1s off) -} led_pattern_t; - -/** - * @brief Inicializa os LEDs com base na configuração da placa - * Deve ser chamada uma única vez na inicialização do sistema. - */ -void led_init(void); - -/** - * @brief Define diretamente o tempo ligado/desligado de um LED. - * Pode ser usado para padrões personalizados. - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param ontime Tempo ligado em milissegundos - * @param offtime Tempo desligado em milissegundos - */ -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime); - -/** - * @brief Aplica um dos padrões de piscar definidos ao LED - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param pattern Padrão desejado (ver enum led_pattern_t) - */ -void led_apply_pattern(led_id_t led_id, led_pattern_t pattern); - -#endif /* LED_H_ */ - -// === Fim de: components/peripherals/include/led.h === - - -// === Início de: components/peripherals/include/buzzer.h === -#ifndef BUZZER_H_ -#define BUZZER_H_ - -#include - -/** - * @brief Inicializa o buzzer e inicia monitoramento automático do estado EVSE. - */ -void buzzer_init(void); - -/** - * @brief Liga e desliga o buzzer manualmente (uso interno ou testes). - */ -void buzzer_on(void); -void buzzer_off(void); - -/** - * @brief Ativa o buzzer por um período fixo (em milissegundos). - */ -void buzzer_beep_ms(uint16_t ms); - -#endif /* BUZZER_H_ */ - -// === Fim de: components/peripherals/include/buzzer.h === - - -// === Início de: components/peripherals/include/ac_relay.h === -#ifndef AC_RELAY_H_ -#define AC_RELAY_H_ - -#include - -/** - * @brief Inicializa o relé de corrente alternada. - */ -void ac_relay_init(void); - -/** - * @brief Define o estado do relé de corrente alternada. - * - * @param state true para ligar, false para desligar. - */ -void ac_relay_set_state(bool state); - -/** - * @brief Retorna o estado atual do relé de corrente alternada. - * - * @return true se estiver ligado, false se desligado. - */ -bool ac_relay_get_state(void); - -#endif /* AC_RELAY_H_ */ - -// === Fim de: components/peripherals/include/ac_relay.h === - - -// === Início de: components/peripherals/include/lm75a.h === -#ifndef LM75A_H -#define LM75A_H - -#include "esp_err.h" // Para o uso de tipos de erro do ESP-IDF, caso esteja utilizando. - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Inicializa o sensor LM75A. - * - * Configura o sensor para leitura e define os pinos de comunicação. - */ -esp_err_t lm75a_init(void); - -/** - * @brief Desinicializa o sensor LM75A. - * - * Libera os recursos usados pelo sensor. - */ -esp_err_t lm75a_deinit(void); - -/** - * @brief Lê a temperatura do LM75A. - * - * @param show Se for 1, a temperatura será exibida em algum tipo de log ou interface. - * Se for 0, o valor é apenas retornado sem exibição. - * @return A temperatura lida em graus Celsius. - */ -float lm75a_read_temperature(int show); - -/** - * @brief Define o valor do limite de temperatura (T_OS) para o sensor LM75A. - * - * @param tos O limite de temperatura de sobrecarga (T_OS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_tos(int tos); - -/** - * @brief Define o valor do limite de temperatura de histerese (T_HYS) para o sensor LM75A. - * - * @param thys O limite de histerese de temperatura (T_HYS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_thys(int thys); - -/** - * @brief Obtém o limite de temperatura de sobrecarga (T_OS) do sensor LM75A. - * - * @return O valor atual de T_OS em graus Celsius. - */ -int lm75a_get_tos(void); - -/** - * @brief Obtém o limite de temperatura de histerese (T_HYS) do sensor LM75A. - * - * @return O valor atual de T_HYS em graus Celsius. - */ -int lm75a_get_thys(void); - -/** - * @brief Habilita ou desabilita a interrupção do LM75A. - * - * @param en 1 para habilitar a interrupção, 0 para desabilitar. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_int(int en); - -/** - * @brief Obtém o estado do pino OS (Overtemperature Shutdown) do LM75A. - * - * @return 1 se o pino OS estiver ativo (indica que a temperatura de sobrecarga foi atingida), - * 0 caso contrário. - */ -int lm75a_get_osio(void); - -#ifdef __cplusplus -} -#endif - -#endif /* LM75A_H */ - -// === Fim de: components/peripherals/include/lm75a.h === - - -// === Início de: components/peripherals/include/ntc_sensor.h === -#ifndef NTC_SENSOR_H_ -#define NTC_SENSOR_H_ - -/** - * @brief Initialize ntc senso - * - */ -void ntc_sensor_init(void); - -/** - * @brief Return temperature after temp_sensor_measure - * - * @return float - */ -float ntc_temp_sensor(void); - -#endif /* NTC_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/ntc_sensor.h === - - -// === Início de: components/peripherals/include/proximity.h === -#ifndef PROXIMITY_H_ -#define PROXIMITY_H_ - -#include - -/** - * @brief Initialize proximity check - * - */ -void proximity_init(void); - -/** - * @brief Return measured value of max current on PP - * - * @return current in A - */ -uint8_t proximity_get_max_current(void); - -#endif /* PROXIMITY_H_ */ - -// === Fim de: components/peripherals/include/proximity.h === - - -// === Início de: components/peripherals/include/socket_lock.h === -#ifndef SOCKED_LOCK_H_ -#define SOCKED_LOCK_H_ - -#include "esp_err.h" - -typedef enum -{ - SOCKED_LOCK_STATUS_IDLE, - SOCKED_LOCK_STATUS_OPERATING, - SOCKED_LOCK_STATUS_LOCKING_FAIL, - SOCKED_LOCK_STATUS_UNLOCKING_FAIL -} socket_lock_status_t; - -/** - * @brief Initialize socket lock - * - */ -void socket_lock_init(void); - -/** - * @brief Get socket lock detection on high, stored in NVS - * - * @return true when locked has zero resistance - * @return false when unlocked has zero resistance - */ -bool socket_lock_is_detection_high(void); - -/** - * @brief Set socket lock detection on high, stored in NVS - * - * @param detection_high - */ -void socket_lock_set_detection_high(bool detection_high); - -/** - * @brief Get socket lock operating time - * - * @return time in ms - */ -uint16_t socket_lock_get_operating_time(void); - -/** - * @brief Set socket lock operating time - * - * @param operating_time - time in ms - * @return esp_err_t - */ -esp_err_t socket_lock_set_operating_time(uint16_t operating_time); - -/** - * @brief Get socket lock retry count - * - * @return retry count - */ -uint8_t socket_lock_get_retry_count(void); - -/** - * @brief Set socket lock retry count - * - * @param retry_count - */ -void socket_lock_set_retry_count(uint8_t retry_count); - -/** - * @brief Get socket lock break time - * - * @return time in ms - */ -uint16_t socket_lock_get_break_time(void); - -/** - * @brief Set socket lock break time - * - * @param break_time - * @return esp_err_t - */ -esp_err_t socket_lock_set_break_time(uint16_t break_time); - -/** - * @brief Set socke lock to locked / unlocked state - * - * @param locked - */ -void socket_lock_set_locked(bool locked); - -/** - * @brief Get socket lock status - * - * @return socket_lock_status_t - */ -socket_lock_status_t socket_lock_get_status(void); - -/** - * @brief Read the current physical lock state using the detection pin. - */ -bool socket_lock_is_locked_state(void); - - -#endif /* SOCKED_LOCK_H_ */ - -// === Fim de: components/peripherals/include/socket_lock.h === - - -// === Início de: components/peripherals/include/adc.h === -#ifndef ADC_H_ -#define ADC_H_ - -#include "esp_adc/adc_oneshot.h" -#include "esp_adc/adc_cali.h" -#include "esp_adc/adc_cali_scheme.h" - -extern adc_oneshot_unit_handle_t adc_handle; - -extern adc_cali_handle_t adc_cali_handle; - -void adc_init(void); - -#endif /* ADC_H_ */ -// === Fim de: components/peripherals/include/adc.h === diff --git a/projeto_unificado.c b/projeto_unificado.c deleted file mode 100755 index edca027..0000000 --- a/projeto_unificado.c +++ /dev/null @@ -1,8613 +0,0 @@ - - -// === Início de: main/main.c === -#include -#include -#include - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" - -#include "esp_log.h" -#include "esp_err.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_spiffs.h" -#include "esp_system.h" -#include "nvs_flash.h" -#include "driver/gpio.h" - -#include "wifi.h" -#include "board_config.h" -#include "logger.h" -#include "rest_main.h" - -#include "peripherals.h" -#include "protocols.h" -#include "evse_manager.h" -#include "evse_api.h" -#include "auth.h" -#include "loadbalancer.h" -#include "meter_manager.h" - - -#define EVSE_MANAGER_TICK_PERIOD_MS 1000 -#define AP_CONNECTION_TIMEOUT 120000 -#define RESET_HOLD_TIME 10000 -#define DEBOUNCE_TIME_MS 50 - -#define PRESS_BIT BIT0 -#define RELEASED_BIT BIT1 - -static const char *TAG = "app_main"; - -static TaskHandle_t user_input_task; -static TickType_t press_tick = 0; -static TickType_t last_interrupt_tick = 0; -static bool pressed = false; - - -// -// File system (SPIFFS) init and info -// -static void fs_info(esp_vfs_spiffs_conf_t *conf) { - size_t total = 0, used = 0; - esp_err_t ret = esp_spiffs_info(conf->partition_label, &total, &used); - if (ret == ESP_OK) - ESP_LOGI(TAG, "Partition %s: total: %d, used: %d", conf->partition_label, total, used); - else - ESP_LOGE(TAG, "Failed to get SPIFFS info: %s", esp_err_to_name(ret)); -} - -static void fs_init(void) { - esp_vfs_spiffs_conf_t cfg_conf = { - .base_path = "/cfg", - .partition_label = "cfg", - .max_files = 1, - .format_if_mount_failed = false - }; - - esp_vfs_spiffs_conf_t data_conf = { - .base_path = "/data", - .partition_label = "data", - .max_files = 5, - .format_if_mount_failed = true - }; - - ESP_ERROR_CHECK(esp_vfs_spiffs_register(&cfg_conf)); - ESP_ERROR_CHECK(esp_vfs_spiffs_register(&data_conf)); - - fs_info(&cfg_conf); - fs_info(&data_conf); -} - -// -// Wi-Fi event monitoring task -// -static void wifi_event_task_func(void *param) { - EventBits_t mode_bits; - while (1) { - mode_bits = xEventGroupWaitBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT, pdFALSE, pdFALSE, portMAX_DELAY); - - if (mode_bits & WIFI_AP_MODE_BIT) { - if (xEventGroupWaitBits(wifi_event_group, WIFI_AP_CONNECTED_BIT, pdFALSE, pdFALSE, pdMS_TO_TICKS(AP_CONNECTION_TIMEOUT)) & WIFI_AP_CONNECTED_BIT) { - xEventGroupWaitBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); - } else { - if (xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT) { - //wifi_ap_stop(); - } - } - } else if (mode_bits & WIFI_STA_MODE_BIT) { - xEventGroupWaitBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT, pdFALSE, pdFALSE, portMAX_DELAY); - } - } -} - -// -// Botão e tratamento -// -static void handle_button_press(void) { - ESP_LOGI(TAG, "Ativando modo AP"); - if (!(xEventGroupGetBits(wifi_event_group) & WIFI_AP_MODE_BIT)) { - wifi_ap_start(); - } -} - -static void user_input_task_func(void *param) { - uint32_t notification; - while (1) { - if (xTaskNotifyWait(0x00, 0xFF, ¬ification, portMAX_DELAY)) { - if (notification & PRESS_BIT) { - press_tick = xTaskGetTickCount(); - pressed = true; - ESP_LOGI(TAG, "Botão pressionado"); - } - - if (notification & RELEASED_BIT && pressed) { - pressed = false; - ESP_LOGI(TAG, "Botão liberado"); - handle_button_press(); - } - } - } -} - -static void IRAM_ATTR button_isr_handler(void *arg) { - BaseType_t higher_task_woken = pdFALSE; - TickType_t now = xTaskGetTickCountFromISR(); - - if (now - last_interrupt_tick < pdMS_TO_TICKS(DEBOUNCE_TIME_MS)) return; - last_interrupt_tick = now; - - if (!gpio_get_level(board_config.button_wifi_gpio)) { - xTaskNotifyFromISR(user_input_task, RELEASED_BIT, eSetBits, &higher_task_woken); - } else { - xTaskNotifyFromISR(user_input_task, PRESS_BIT, eSetBits, &higher_task_woken); - } - - if (higher_task_woken) { - portYIELD_FROM_ISR(); - } -} - -static void button_init(void) { - gpio_config_t conf = { - .pin_bit_mask = BIT64(board_config.button_wifi_gpio), - .mode = GPIO_MODE_INPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .pull_up_en = GPIO_PULLUP_ENABLE, - .intr_type = GPIO_INTR_ANYEDGE - }; - ESP_ERROR_CHECK(gpio_config(&conf)); - ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.button_wifi_gpio, button_isr_handler, NULL)); -} - -// -// Inicialização dos módulos do sistema -// -static void init_modules(void) { - peripherals_init(); - //api_init(); - ESP_ERROR_CHECK(rest_server_init("/data")); - protocols_init(); - evse_manager_init(); - evse_init(); // Cria a task para FSM - button_init(); - auth_init(); - loadbalancer_init(); - meter_manager_grid_init(); - meter_manager_grid_start(); - //meter_manager_evse_init(); - - // Outros módulos (descomente conforme necessário) - // meter_init(); - // ocpp_start(); - // orno_modbus_start(); - // currentshaper_start(); - // initWiegand(); - // meter_zigbee_start(); - // master_sync_start(); - // slave_sync_start(); -} - -// -// Função principal do firmware -// -void app_main(void) { - logger_init(); - esp_log_set_vprintf(logger_vprintf); - - esp_reset_reason_t reason = esp_reset_reason(); - ESP_LOGI(TAG, "Reset reason: %d", reason); - - esp_err_t ret = nvs_flash_init(); - if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { - ESP_LOGW(TAG, "Erasing NVS flash"); - ESP_ERROR_CHECK(nvs_flash_erase()); - ret = nvs_flash_init(); - } - ESP_ERROR_CHECK(ret); - - fs_init(); - ESP_ERROR_CHECK(esp_netif_init()); - ESP_ERROR_CHECK(esp_event_loop_create_default()); - ESP_ERROR_CHECK(gpio_install_isr_service(0)); - - board_config_load(); - wifi_ini(); - //wifi_ap_start(); - init_modules(); - - xTaskCreate(wifi_event_task_func, "wifi_event_task", 8 * 1024, NULL, 3, NULL); - xTaskCreate(user_input_task_func, "user_input_task", 4 * 1024, NULL, 3, &user_input_task); -} - -// === Fim de: main/main.c === - - -// === Início de: components/evse/evse_pilot.c === -#include -#include -#include -#include -#include - -#include "driver/ledc.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_rom_sys.h" - -#include "evse_pilot.h" -#include "adc.h" -#include "board_config.h" - -#define PILOT_PWM_TIMER LEDC_TIMER_0 -#define PILOT_PWM_CHANNEL LEDC_CHANNEL_0 -#define PILOT_PWM_SPEED_MODE LEDC_LOW_SPEED_MODE -#define PILOT_PWM_DUTY_RES LEDC_TIMER_10_BIT -#define PILOT_PWM_MAX_DUTY 1023 - -#define NUM_PILOT_SAMPLES 100 -#define MAX_SAMPLE_ATTEMPTS 1000 -#define PILOT_EXTREME_PERCENT 10 // 10% superior e inferior - -static const char *TAG = "evse_pilot"; - -void pilot_init(void) -{ - ledc_timer_config_t ledc_timer = { - .speed_mode = PILOT_PWM_SPEED_MODE, - .timer_num = PILOT_PWM_TIMER, - .duty_resolution = PILOT_PWM_DUTY_RES, - .freq_hz = 1000, - .clk_cfg = LEDC_AUTO_CLK - }; - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); - - ledc_channel_config_t ledc_channel = { - .speed_mode = PILOT_PWM_SPEED_MODE, - .channel = PILOT_PWM_CHANNEL, - .timer_sel = PILOT_PWM_TIMER, - .intr_type = LEDC_INTR_DISABLE, - .gpio_num = board_config.pilot_pwm_gpio, - .duty = 0, - .hpoint = 0 - }; - 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)); - - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12, - }; - - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.pilot_adc_channel, &config)); -} - -void pilot_set_level(bool level) -{ - ESP_LOGI(TAG, "Set level %d", level); - ledc_stop(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, level ? 1 : 0); -} - -void pilot_set_amps(uint16_t amps) -{ - ESP_LOGI(TAG, "Set amps %d", amps); - - if (amps < 60 || amps > 800) { - ESP_LOGE(TAG, "Invalid ampere value: %d A*10", amps); - return; - } - - uint32_t duty; - if (amps <= 510) { - duty = (PILOT_PWM_MAX_DUTY * amps) / 600; - } else { - duty = ((PILOT_PWM_MAX_DUTY * amps) / 2500) + (64 * (PILOT_PWM_MAX_DUTY / 100)); - } - - if (duty > PILOT_PWM_MAX_DUTY) - duty = PILOT_PWM_MAX_DUTY; - - ESP_LOGI(TAG, "Set amp %dA*10 -> duty %lu/%d", amps, duty, PILOT_PWM_MAX_DUTY); - ledc_set_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL, duty); - ledc_update_duty(PILOT_PWM_SPEED_MODE, PILOT_PWM_CHANNEL); -} -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)]; -} - -void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12) -{ - ESP_LOGD(TAG, "pilot_measure"); - - int samples[NUM_PILOT_SAMPLES]; - int collected = 0, attempts = 0; - int sample; - - while (collected < NUM_PILOT_SAMPLES && attempts < MAX_SAMPLE_ATTEMPTS) { - if (adc_oneshot_read(adc_handle, board_config.pilot_adc_channel, &sample) == ESP_OK) { - samples[collected++] = sample; - esp_rom_delay_us(10); - } else { - esp_rom_delay_us(100); - attempts++; - } - } - - if (collected < NUM_PILOT_SAMPLES) { - ESP_LOGW(TAG, "Timeout on sample read (%d/%d)", collected, NUM_PILOT_SAMPLES); - *up_voltage = PILOT_VOLTAGE_1; - *down_voltage_n12 = false; - 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); - - - ESP_LOGD(TAG, "Final: high_raw=%d, low_raw=%d", high_raw, low_raw); - - int high_mv = 0; - int low_mv = 0; - - if (adc_cali_raw_to_voltage(adc_cali_handle, high_raw, &high_mv) != ESP_OK || - adc_cali_raw_to_voltage(adc_cali_handle, low_raw, &low_mv) != ESP_OK) { - ESP_LOGW(TAG, "ADC calibration failed"); - *up_voltage = PILOT_VOLTAGE_1; - *down_voltage_n12 = false; - return; - } - - if (high_mv >= board_config.pilot_down_threshold_12) - *up_voltage = PILOT_VOLTAGE_12; - else if (high_mv >= board_config.pilot_down_threshold_9) - *up_voltage = PILOT_VOLTAGE_9; - else if (high_mv >= board_config.pilot_down_threshold_6) - *up_voltage = PILOT_VOLTAGE_6; - else if (high_mv >= board_config.pilot_down_threshold_3) - *up_voltage = PILOT_VOLTAGE_3; - else - *up_voltage = PILOT_VOLTAGE_1; - - *down_voltage_n12 = (low_mv <= board_config.pilot_down_threshold_n12); - - ESP_LOGD(TAG, "Final: up_voltage=%d, down_voltage_n12=%d", *up_voltage, *down_voltage_n12); -} - -// === Fim de: components/evse/evse_pilot.c === - - -// === Início de: components/evse/evse_hardware.c === -#include "evse_hardware.h" -#include "evse_pilot.h" -#include "ac_relay.h" -#include "socket_lock.h" -#include "proximity.h" - -static const char *TAG = "evse_hardware"; - -void evse_hardware_init(void) { - pilot_init(); - pilot_set_level(true); // Sinal piloto em 12V (inicial) - ac_relay_set_state(false); // Relé desligado - //socket_lock_set_locked(false); // Destrava o conector -} - -void evse_hardware_tick(void) { - // Tick para atualizações de hardware com polling, se necessário -} - -bool evse_hardware_is_pilot_high(void) { - return pilot_get_state(); // true se sinal piloto estiver em nível alto -} - -bool evse_hardware_is_vehicle_connected(void) { - // Verifica se o veículo está conectado pelo resistor do pino PP - return proximity_get_max_current() > 0; -} - -bool evse_hardware_is_energy_detected(void) { - return false; -} - -void evse_hardware_relay_on(void) { - ac_relay_set_state(true); -} - -void evse_hardware_relay_off(void) { - ac_relay_set_state(false); -} - -bool evse_hardware_relay_status(void) { - return ac_relay_get_state(); -} - -void evse_hardware_lock(void) { - socket_lock_set_locked(true); -} - -void evse_hardware_unlock(void) { - socket_lock_set_locked(false); -} - -bool evse_hardware_is_locked(void) { - return socket_lock_is_locked_state(); -} - -// === Fim de: components/evse/evse_hardware.c === - - -// === Início de: components/evse/evse_state.c === -#include "evse_state.h" -#include "evse_events.h" -#include "freertos/FreeRTOS.h" -#include "freertos/portmacro.h" -#include "esp_log.h" - -static evse_state_t current_state = EVSE_STATE_A; -static bool is_authorized = false; - -// Proteção básica para variáveis globais em sistemas concorrentes -static portMUX_TYPE state_mux = portMUX_INITIALIZER_UNLOCKED; - -static evse_state_event_t map_state_to_event(evse_state_t s) { - switch (s) { - case EVSE_STATE_A: - return EVSE_STATE_EVENT_IDLE; - case EVSE_STATE_B1: - return EVSE_STATE_EVENT_WAITING; - case EVSE_STATE_B2: - case EVSE_STATE_C1: - case EVSE_STATE_C2: - return EVSE_STATE_EVENT_CHARGING; - case EVSE_STATE_E: - case EVSE_STATE_F: - return EVSE_STATE_EVENT_FAULT; - default: - return EVSE_STATE_EVENT_IDLE; - } -} -void evse_set_state(evse_state_t state) { - bool changed = false; - evse_state_t previous_state; - - portENTER_CRITICAL(&state_mux); - previous_state = current_state; - if (state != current_state) { - current_state = state; - changed = true; - } - portEXIT_CRITICAL(&state_mux); - - if (changed) { - ESP_LOGI("EVSE_STATE", "Estado alterado de %s para %s", - evse_state_to_str(previous_state), - evse_state_to_str(state)); - - evse_state_event_data_t evt = { - .state = map_state_to_event(state) - }; - esp_event_post(EVSE_EVENTS, EVSE_EVENT_STATE_CHANGED, &evt, sizeof(evt), portMAX_DELAY); - } -} - -evse_state_t evse_get_state(void) { - portENTER_CRITICAL(&state_mux); - evse_state_t s = current_state; - portEXIT_CRITICAL(&state_mux); - return s; -} - -const char* evse_state_to_str(evse_state_t state) { - switch (state) { - case EVSE_STATE_A: return "A - EV Not Connected (12V)"; - case EVSE_STATE_B1: return "B1 - EV Connected (9V, Not Authorized)"; - case EVSE_STATE_B2: return "B2 - EV Connected (9V, Authorized and Ready)"; - case EVSE_STATE_C1: return "C1 - Charging Requested (6V, Relay Off)"; - case EVSE_STATE_C2: return "C2 - Charging Active (6V, Relay On)"; - case EVSE_STATE_D1: return "D1 - Ventilation Required (3V, Relay Off)"; - case EVSE_STATE_D2: return "D2 - Ventilation Active (3V, Relay On)"; - case EVSE_STATE_E: return "E - Error: Control Pilot Shorted to Ground (0V)"; - case EVSE_STATE_F: return "F - Fault: EVSE Unavailable or No Pilot Signal"; - default: return "Unknown State"; - } -} - -void evse_state_init(void) { - portENTER_CRITICAL(&state_mux); - current_state = EVSE_STATE_A; - is_authorized = true; - portEXIT_CRITICAL(&state_mux); - - ESP_LOGI("EVSE_STATE", "Inicializado em estado: %s", evse_state_to_str(current_state)); - - evse_state_event_data_t evt = { - .state = map_state_to_event(current_state) - }; - esp_event_post(EVSE_EVENTS, EVSE_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); -} - -void evse_state_tick(void) { - // Tick do estado (placeholder) -} - -bool evse_state_is_charging(evse_state_t state) { - return state == EVSE_STATE_C1 || state == EVSE_STATE_C2; -} - -bool evse_state_is_plugged(evse_state_t state) { - return state == EVSE_STATE_B1 || state == EVSE_STATE_B2 || - state == EVSE_STATE_C1 || state == EVSE_STATE_C2 || - state == EVSE_STATE_D1 || state == EVSE_STATE_D2; -} - -bool evse_state_is_session(evse_state_t state) { - return state == EVSE_STATE_B2 || state == EVSE_STATE_C1 || state == EVSE_STATE_C2; -} - -void evse_state_set_authorized(bool authorized) { - portENTER_CRITICAL(&state_mux); - is_authorized = authorized; - portEXIT_CRITICAL(&state_mux); -} - -bool evse_state_get_authorized(void) { - portENTER_CRITICAL(&state_mux); - bool result = is_authorized; - portEXIT_CRITICAL(&state_mux); - return result; -} - -// === Fim de: components/evse/evse_state.c === - - -// === Início de: components/evse/evse_fsm.c === -// evse_fsm.c - Máquina de Estados EVSE com controle centralizado - -#include "evse_fsm.h" -#include "evse_api.h" -#include "evse_pilot.h" -#include "evse_config.h" -#include "esp_log.h" -#include "ac_relay.h" -#include "board_config.h" -#include "socket_lock.h" -#include "proximity.h" -#include "rcm.h" -#include "evse_state.h" - -static const char *TAG = "evse_fsm"; - -#ifndef MIN -#define MIN(a, b) ((a) < (b) ? (a) : (b)) -#endif - -static bool c1_d1_waiting = false; -static TickType_t c1_d1_relay_to = 0; - -void evse_fsm_reset(void) { - evse_set_state(EVSE_STATE_A); - c1_d1_waiting = false; - c1_d1_relay_to = 0; -} - -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) { - cable_max_current = proximity_get_max_current(); - } - - 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 * 10, cable_max_current * 10)); - ac_relay_set_state(false); - break; - - case EVSE_STATE_C1: - case EVSE_STATE_D1: - pilot_set_level(true); - 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 * 10, cable_max_current * 10)); - ac_relay_set_state(true); - break; - } -} - -void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled) { - TickType_t now = xTaskGetTickCount(); - 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; - - 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; - } - } - __attribute__((fallthrough)); // Evita warning de fallthrough implícito - - 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; - } - break; - - case EVSE_STATE_E: - break; // Sem transições a partir de E - - case EVSE_STATE_F: - if (available) { - evse_set_state(EVSE_STATE_A); - } - break; - } - - evse_state_t next = evse_get_state(); - if (next != prev) { - ESP_LOGI(TAG, "State changed: %s -> %s", evse_state_to_str(prev), evse_state_to_str(next)); - update_outputs(next); - } -} - -// === Fim de: components/evse/evse_fsm.c === - - -// === Início de: components/evse/evse_error.c === -#include "evse_error.h" -#include "evse_config.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "ntc_sensor.h" - -static const char *TAG = "evse_error"; - -static uint32_t error_bits = 0; -static TickType_t auto_clear_timeout = 0; -static bool error_cleared = false; - -void evse_error_init(void) { - // Inicialização do sistema de erros -} - -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); - ESP_LOGW(TAG, "Erro: pilot abaixo de 2V (falha)"); - } - } - - // 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"); - } - } -} - -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 - - // Log informativo com os valores - 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); - ESP_LOGW(TAG, "Sensor NTC falhou ou está desconectado"); - } - return; - } - - evse_error_clear(EVSE_ERR_TEMPERATURE_FAULT_BIT); // leitura válida - - 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); - } - } else { - evse_error_clear(EVSE_ERR_TEMPERATURE_HIGH_BIT); - } -} - -uint32_t evse_get_error(void) { - return error_bits; -} - -bool evse_is_error_cleared(void) { - return error_cleared; -} - -void evse_mark_error_cleared(void) { - error_cleared = false; -} - -// Já existentes -void evse_error_set(uint32_t bitmask) { - error_bits |= bitmask; - - if (bitmask & EVSE_ERR_AUTO_CLEAR_BITS) { - auto_clear_timeout = xTaskGetTickCount() + pdMS_TO_TICKS(60000); // 60s - } -} - -void evse_error_clear(uint32_t bitmask) { - bool had_error = error_bits != 0; - error_bits &= ~bitmask; - - if (had_error && error_bits == 0) { - error_cleared = true; - } -} - -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); - auto_clear_timeout = 0; - } -} - -bool evse_error_is_active(void) { - return error_bits != 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; -} - -// === Fim de: components/evse/evse_error.c === - - -// === Início de: components/evse/evse_core.c === -// evse_core.c - Função principal de controle do EVSE - -#include "evse_fsm.h" -#include "evse_error.h" -#include "evse_limits.h" -#include "evse_config.h" -#include "evse_api.h" -#include "evse_pilot.h" -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "esp_log.h" - -static const char *TAG = "evse_core"; - -static SemaphoreHandle_t mutex; - -static evse_state_t last_state = EVSE_STATE_A; - -static void evse_core_task(void *arg); - -void evse_init(void) { - ESP_LOGI(TAG, "EVSE Init"); - - mutex = xSemaphoreCreateMutex(); - - evse_check_defaults(); - evse_fsm_reset(); - pilot_set_level(true); // Estado inicial do piloto - - xTaskCreate(evse_core_task, "evse_core_task", 4096, NULL, 5, NULL); -} - -void evse_process(void) { - xSemaphoreTake(mutex, portMAX_DELAY); - - pilot_voltage_t pilot_voltage; - bool is_n12v = false; - - pilot_measure(&pilot_voltage, &is_n12v); - ESP_LOGD(TAG, "Pilot: %d, -12V: %s", pilot_voltage, is_n12v ? "yes" : "no"); - - if (evse_get_error() == 0 && !evse_is_error_cleared()) { - - evse_error_check(pilot_voltage, is_n12v); - - evse_fsm_process( - pilot_voltage, - evse_state_get_authorized(), - evse_config_is_available(), - evse_config_is_enabled() - ); - - evse_limits_check(evse_get_state()); - - 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; - } - - evse_mark_error_cleared(); - } - - xSemaphoreGive(mutex); -} - - -// ================================ -// Interface pública -// ================================ - -bool evse_is_enabled(void) { - return evse_config_is_enabled(); -} - -void evse_set_enabled(bool value) { - ESP_LOGI(TAG, "Set enabled %d", value); - evse_config_set_enabled(value); -} - -bool evse_is_available(void) { - return evse_config_is_available(); -} - -void evse_set_available(bool value) { - ESP_LOGI(TAG, "Set available %d", value); - evse_config_set_available(value); -} - -// ================================ -// Tarefa principal -// ================================ - -static void evse_core_task(void *arg) { - while (true) { - evse_process(); - vTaskDelay(pdMS_TO_TICKS(100)); - } -} - -// === Fim de: components/evse/evse_core.c === - - -// === Início de: components/evse/evse_limits.c === -#include "evse_limits.h" -#include -#include - -// ======================== -// Estado interno -// ======================== - -static bool limit_reached = false; -static uint32_t consumption_limit = 0; -static uint32_t charging_time_limit = 0; -static uint16_t under_power_limit = 0; - -static uint32_t default_consumption_limit = 0; -static uint32_t default_charging_time_limit = 0; -static uint16_t default_under_power_limit = 0; - -// ======================== -// Estado de controle -// ======================== - -void evse_set_limit_reached(uint8_t value) { - limit_reached = (value != 0); -} - -bool evse_is_limit_reached(void) { - return limit_reached; -} - -// ======================== -// Limites em tempo de execução -// ======================== - -uint32_t evse_get_consumption_limit(void) { - return consumption_limit; -} - -void evse_set_consumption_limit(uint32_t value) { - consumption_limit = value; -} - -uint32_t evse_get_charging_time_limit(void) { - return charging_time_limit; -} - -void evse_set_charging_time_limit(uint32_t value) { - charging_time_limit = value; -} - -uint16_t evse_get_under_power_limit(void) { - return under_power_limit; -} - -void evse_set_under_power_limit(uint16_t value) { - under_power_limit = value; -} - -// ======================== -// Limites padrão (persistentes) -// ======================== - -uint32_t evse_get_default_consumption_limit(void) { - return default_consumption_limit; -} - -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; -} - -// ======================== -// Lógica de verificação de limites -// ======================== - -void evse_limits_check(evse_state_t state) { - // Se algum limite estiver ativo, verifique o estado - if ((consumption_limit > 0 || charging_time_limit > 0 || under_power_limit > 0) - && evse_state_is_charging(state)) { - // (Lógica real a ser aplicada aqui, ex: medição de consumo, tempo ou potência) - evse_set_limit_reached(1); - } -} - -// === Fim de: components/evse/evse_limits.c === - - -// === Início de: components/evse/evse_config.c === -#include // For PRI macros -#include "evse_config.h" -#include "board_config.h" -#include "evse_limits.h" -#include "esp_log.h" -#include "nvs.h" - -static const char *TAG = "evse_config"; - -static nvs_handle_t nvs; - -// ======================== -// Configurable parameters -// ======================== -static uint8_t max_charging_current = MAX_CHARGING_CURRENT_LIMIT; -static uint16_t charging_current; // Persisted (NVS) -static uint16_t charging_current_runtime = 0; // Runtime only -static bool socket_outlet; -static bool rcm; -static uint8_t temp_threshold = 60; -static bool require_auth; - -// ======================== -// Initialization -// ======================== -esp_err_t evse_config_init(void) { - ESP_LOGD(TAG, "Initializing NVS configuration..."); - return nvs_open("evse", NVS_READWRITE, &nvs); -} - -void evse_check_defaults(void) { - esp_err_t err; - uint8_t u8; - uint16_t u16; - uint32_t u32; - bool needs_commit = false; - - ESP_LOGD(TAG, "Checking default parameters..."); - - // Max charging current - err = nvs_get_u8(nvs, "max_chrg_curr", &u8); - if (err != ESP_OK || u8 < MIN_CHARGING_CURRENT_LIMIT || u8 > MAX_CHARGING_CURRENT_LIMIT) { - max_charging_current = MAX_CHARGING_CURRENT_LIMIT; - nvs_set_u8(nvs, "max_chrg_curr", max_charging_current); - needs_commit = true; - ESP_LOGW(TAG, "Invalid or missing max_chrg_curr, resetting to %d", max_charging_current); - } else { - max_charging_current = u8; - } - - // Charging current (default, persisted) - err = nvs_get_u16(nvs, "def_chrg_curr", &u16); - if (err != ESP_OK || u16 < (MIN_CHARGING_CURRENT_LIMIT * 10) || u16 > (max_charging_current * 10)) { - charging_current = max_charging_current * 10; - nvs_set_u16(nvs, "def_chrg_curr", charging_current); - needs_commit = true; - ESP_LOGW(TAG, "Invalid or missing def_chrg_curr, resetting to %d", charging_current); - } else { - charging_current = u16; - } - - // Runtime charging current initialized from persisted default - charging_current_runtime = charging_current; - ESP_LOGD(TAG, "Runtime charging current initialized to: %d", charging_current_runtime); - - // Auth required - err = nvs_get_u8(nvs, "require_auth", &u8); - require_auth = (err == ESP_OK && u8 <= 1) ? u8 : false; - if (err != ESP_OK) { - nvs_set_u8(nvs, "require_auth", require_auth); - needs_commit = true; - } - - // Socket outlet - err = nvs_get_u8(nvs, "socket_outlet", &u8); - socket_outlet = (err == ESP_OK && u8) && board_config.proximity; - if (err != ESP_OK) { - nvs_set_u8(nvs, "socket_outlet", socket_outlet); - needs_commit = true; - } - - // RCM - err = nvs_get_u8(nvs, "rcm", &u8); - rcm = (err == ESP_OK && u8) && board_config.rcm; - if (err != ESP_OK) { - nvs_set_u8(nvs, "rcm", rcm); - needs_commit = true; - } - - // Temp threshold - err = nvs_get_u8(nvs, "temp_threshold", &u8); - temp_threshold = (err == ESP_OK && u8 >= 40 && u8 <= 80) ? u8 : 60; - if (err != ESP_OK) { - nvs_set_u8(nvs, "temp_threshold", temp_threshold); - needs_commit = true; - } - - // Optional limits - if (nvs_get_u32(nvs, "def_cons_lim", &u32) == ESP_OK) - evse_set_consumption_limit(u32); - - if (nvs_get_u32(nvs, "def_ch_time_lim", &u32) == ESP_OK) - evse_set_charging_time_limit(u32); - - if (nvs_get_u16(nvs, "def_un_pwr_lim", &u16) == ESP_OK) - evse_set_under_power_limit(u16); - - // Save to NVS if needed - if (needs_commit) { - err = nvs_commit(nvs); - if (err == ESP_OK) { - ESP_LOGD(TAG, "Configuration committed to NVS."); - } else { - ESP_LOGE(TAG, "Failed to commit configuration to NVS: %s", esp_err_to_name(err)); - } - } -} - -// ======================== -// Charging current getters/setters -// ======================== -uint8_t evse_get_max_charging_current(void) { - return max_charging_current; -} - -esp_err_t evse_set_max_charging_current(uint8_t value) { - if (value < MIN_CHARGING_CURRENT_LIMIT || value > MAX_CHARGING_CURRENT_LIMIT) - return ESP_ERR_INVALID_ARG; - max_charging_current = value; - nvs_set_u8(nvs, "max_chrg_curr", value); - return nvs_commit(nvs); -} - -uint16_t evse_get_charging_current(void) { - return charging_current; -} - -esp_err_t evse_set_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) - return ESP_ERR_INVALID_ARG; - charging_current = value; - nvs_set_u16(nvs, "def_chrg_curr", value); - return nvs_commit(nvs); -} - -uint16_t evse_get_default_charging_current(void) { - uint16_t value; - if (nvs_get_u16(nvs, "def_chrg_curr", &value) == ESP_OK) - return value; - return charging_current; -} - -esp_err_t evse_set_default_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT * 10) || value > (max_charging_current * 10)) - return ESP_ERR_INVALID_ARG; - nvs_set_u16(nvs, "def_chrg_curr", value); - return nvs_commit(nvs); -} - -// ======================== -// Runtime current (not saved) -// ======================== -void evse_set_runtime_charging_current(uint16_t value) { - if (value < (MIN_CHARGING_CURRENT_LIMIT) || value > (max_charging_current)) { - ESP_LOGW(TAG, "Rejected runtime charging current (out of bounds): %d", value); - return; - } - charging_current_runtime = value; - ESP_LOGD(TAG, "Runtime charging current updated: %d", charging_current_runtime); -} - -uint16_t evse_get_runtime_charging_current(void) { - return charging_current_runtime; -} - - -// ======================== -// Socket outlet -// ======================== -bool evse_get_socket_outlet(void) { - return socket_outlet; -} - -esp_err_t evse_set_socket_outlet(bool value) { - if (value && !board_config.proximity) - return ESP_ERR_INVALID_ARG; - socket_outlet = value; - nvs_set_u8(nvs, "socket_outlet", value); - return nvs_commit(nvs); -} - -// ======================== -// RCM -// ======================== -bool evse_is_rcm(void) { - return rcm; -} - -esp_err_t evse_set_rcm(bool value) { - if (value && !board_config.rcm) - return ESP_ERR_INVALID_ARG; - rcm = value; - nvs_set_u8(nvs, "rcm", value); - return nvs_commit(nvs); -} - -// ======================== -// Temperature -// ======================== -uint8_t evse_get_temp_threshold(void) { - return temp_threshold; -} - -esp_err_t evse_set_temp_threshold(uint8_t value) { - if (value < 40 || value > 80) - return ESP_ERR_INVALID_ARG; - temp_threshold = value; - nvs_set_u8(nvs, "temp_threshold", value); - return nvs_commit(nvs); -} - -// ======================== -// Authentication -// ======================== -bool evse_is_require_auth(void) { - return require_auth; -} - -void evse_set_require_auth(bool value) { - require_auth = value; - nvs_set_u8(nvs, "require_auth", value); - nvs_commit(nvs); -} - -// ======================== -// Availability -// ======================== -static bool is_available = true; - -bool evse_config_is_available(void) { - return is_available; -} - -void evse_config_set_available(bool available) { - is_available = available; -} - -// ======================== -// Enable/Disable -// ======================== -static bool is_enabled = true; - -bool evse_config_is_enabled(void) { - return is_enabled; -} - -void evse_config_set_enabled(bool enabled) { - is_enabled = enabled; -} - -// === Fim de: components/evse/evse_config.c === - - -// === Início de: components/evse/evse_manager.c === -#include "evse_manager.h" -#include "evse_state.h" -#include "evse_error.h" -#include "evse_hardware.h" -#include "evse_config.h" -#include "evse_api.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "esp_log.h" -#include - -#include "auth_events.h" -#include "loadbalancer_events.h" -#include "esp_event.h" - -static const char *TAG = "EVSE_Manager"; - -static SemaphoreHandle_t evse_mutex; -static bool auth_enabled = false; - -#define EVSE_MANAGER_TICK_PERIOD_MS 1000 // 1 segundo - -// ===== Task de ciclo principal ===== -static void evse_manager_task(void *arg) { - while (true) { - evse_manager_tick(); - vTaskDelay(pdMS_TO_TICKS(EVSE_MANAGER_TICK_PERIOD_MS)); - } -} - -// ===== Tratador de eventos de autenticação ===== -static void on_auth_event(void* arg, esp_event_base_t base, int32_t id, void* data) { - if (base != AUTH_EVENTS || data == NULL) return; - - switch (id) { - case AUTH_EVENT_TAG_PROCESSED: { - auth_tag_event_data_t *evt = (auth_tag_event_data_t*)data; - ESP_LOGI("EVSE", "Tag: %s | Autorizada: %s", evt->tag, evt->authorized ? "SIM" : "NÃO"); - evse_state_set_authorized(evt->authorized); - break; - } - - case AUTH_EVENT_ENABLED_CHANGED: - case AUTH_EVENT_INIT: { - auth_enabled_event_data_t *evt = (auth_enabled_event_data_t*)data; - auth_enabled = evt->enabled; - - ESP_LOGI("EVSE", "Auth %s (%s)", - id == AUTH_EVENT_ENABLED_CHANGED ? "ficou" : "init", - evt->enabled ? "ATIVO" : "INATIVO"); - - if (!auth_enabled) { - evse_state_set_authorized(true); - ESP_LOGI("EVSE", "Autenticação desativada → autorização forçada."); - } else { - evse_state_set_authorized(false); - ESP_LOGI("EVSE", "Autenticação ativada → aguardando autorização por tag."); - } - break; - } - } -} - -// ===== Tratador de eventos de loadbalancer ===== -static void on_loadbalancer_event(void* handler_arg, esp_event_base_t event_base, - int32_t event_id, void* event_data) { - 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", evt->timestamp_us); - // Ações adicionais podem ser adicionadas aqui conforme necessário - } else if (event_id == LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED) { - const loadbalancer_charging_limit_event_t* evt = (const loadbalancer_charging_limit_event_t*) event_data; - ESP_LOGD(TAG, "Novo limite de corrente: %.1f A (ts: %lld)", evt->limit, evt->timestamp_us); - evse_set_runtime_charging_current((uint16_t)(evt->limit)); - } -} - -// ===== Inicialização ===== -void evse_manager_init(void) { - evse_mutex = xSemaphoreCreateMutex(); - - evse_config_init(); - evse_error_init(); - evse_hardware_init(); - evse_state_init(); - - 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_LOGI(TAG, "EVSE Manager inicializado."); - xTaskCreate(evse_manager_task, "evse_manager_task", 4096, NULL, 5, NULL); -} - -// ===== Main Tick ===== -void evse_manager_tick(void) { - xSemaphoreTake(evse_mutex, portMAX_DELAY); - - evse_hardware_tick(); - evse_error_tick(); - evse_state_tick(); - evse_temperature_check(); - - if (auth_enabled) { - // If the car is disconnected, revoke authorization - if (evse_state_get_authorized() && evse_get_state() == EVSE_STATE_A) { - ESP_LOGI(TAG, "Vehicle disconnected → revoking authorization."); - evse_state_set_authorized(false); - } - } else { - // If authentication is disabled, ensure authorization is always granted - if (!evse_state_get_authorized()) { - evse_state_set_authorized(true); - ESP_LOGI(TAG, "Authentication disabled → forced authorization."); - } - } - - xSemaphoreGive(evse_mutex); -} - - -// ===== API pública ===== -bool evse_manager_is_available(void) { - return evse_config_is_available(); -} - -void evse_manager_set_available(bool available) { - evse_config_set_available(available); -} - -void evse_manager_set_authorized(bool authorized) { - evse_state_set_authorized(authorized); -} - -bool evse_manager_is_charging(void) { - return evse_state_is_charging(evse_get_state()); -} - -void evse_manager_set_enabled(bool enabled) { - evse_config_set_enabled(enabled); -} - -bool evse_manager_is_enabled(void) { - return evse_config_is_enabled(); -} - -// === Fim de: components/evse/evse_manager.c === - - -// === Início de: components/evse/evse_events.c === -#include "evse_events.h" - -ESP_EVENT_DEFINE_BASE(EVSE_EVENTS); - -// === Fim de: components/evse/evse_events.c === - - -// === Início de: components/evse/include/evse_pilot.h === -#ifndef PILOT_H_ -#define PILOT_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/** - * @brief Níveis categóricos de tensão no sinal CP (Control Pilot) - */ -typedef enum -{ - PILOT_VOLTAGE_12, ///< Estado A: +12V - PILOT_VOLTAGE_9, ///< Estado B: +9V - PILOT_VOLTAGE_6, ///< Estado C: +6V - PILOT_VOLTAGE_3, ///< Estado D: +3V - PILOT_VOLTAGE_1 ///< Estado E/F: abaixo de 3V -} pilot_voltage_t; - -/** - * @brief Inicializa o driver do sinal Pilot - */ -void pilot_init(void); - -/** - * @brief Define o nível do Pilot: +12V ou -12V - * - * @param level true = +12V, false = -12V - */ -void pilot_set_level(bool level); - -/** - * @brief Ativa o PWM do Pilot com corrente limitada - * - * @param amps Corrente em décimos de ampère (ex: 160 = 16A) - */ -void pilot_set_amps(uint16_t amps); - -/** - * @brief Mede o nível de tensão do Pilot e detecta -12V - * - * @param up_voltage Valor categórico da tensão positiva - * @param down_voltage_n12 true se o nível negativo atingir -12V - */ -void pilot_measure(pilot_voltage_t *up_voltage, bool *down_voltage_n12); - -/** - * @brief Retorna o estado lógico atual do Pilot (nível alto = +12V) - * - * @return true se nível atual for +12V, false se for -12V - */ -bool pilot_get_state(void); - -/** - * @brief Cache interno opcional dos níveis de tensão reais do Pilot - */ -typedef struct { - uint16_t high_mv; ///< Pico positivo medido (mV) - uint16_t low_mv; ///< Pico negativo medido (mV) -} pilot_voltage_cache_t; - -#ifdef __cplusplus -} -#endif - -#endif /* PILOT_H_ */ - -// === Fim de: components/evse/include/evse_pilot.h === - - -// === Início de: components/evse/include/evse_manager.h === -#ifndef EVSE_MANAGER_H -#define EVSE_MANAGER_H - -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include -#include - -/** - * @brief Inicializa os módulos internos do EVSE (hardware, estado, erros, etc.) - * e inicia a tarefa de supervisão periódica (tick). - */ -void evse_manager_init(void); - -/** - * @brief Executa uma iteração do ciclo de controle do EVSE. - * - * Esta função é chamada automaticamente pela task periódica, - * mas pode ser chamada manualmente em testes. - */ -void evse_manager_tick(void); - -/** - * @brief Verifica se o EVSE está disponível para uso. - * - * Isso considera falhas críticas, disponibilidade configurada, etc. - */ -bool evse_manager_is_available(void); - -/** - * @brief Define se o EVSE deve estar disponível (ex: via controle remoto). - */ -void evse_manager_set_available(bool available); - -/** - * @brief Define se o EVSE está autorizado a carregar (ex: após autenticação). - */ -void evse_manager_set_authorized(bool authorized); - -/** - * @brief Verifica se o EVSE está atualmente carregando. - */ -bool evse_manager_is_charging(void); - -/** - * @brief Ativa ou desativa logicamente o EVSE (controla habilitação geral). - */ -void evse_manager_set_enabled(bool enabled); - -/** - * @brief Verifica se o EVSE está ativado logicamente. - */ -bool evse_manager_is_enabled(void); - -#ifdef __cplusplus -} -#endif - - -#endif // EVSE_MANAGER_H - -// === Fim de: components/evse/include/evse_manager.h === - - -// === Início de: components/evse/include/evse_fsm.h === -#ifndef EVSE_FSM_H -#define EVSE_FSM_H - -#include -#include -#include "evse_api.h" -#include "evse_pilot.h" -#include "freertos/FreeRTOS.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Reinicia a máquina de estados do EVSE para o estado inicial (A). - */ -void evse_fsm_reset(void); - -/** - * @brief Processa uma leitura do sinal de piloto e atualiza a máquina de estados do EVSE. - * - * Esta função deve ser chamada periodicamente pelo núcleo de controle para - * avaliar mudanças no estado do conector, disponibilidade do carregador e - * autorização do usuário. - * - * @param pilot_voltage Leitura atual da tensão do sinal piloto. - * @param authorized Indica se o carregamento foi autorizado. - * @param available Indica se o carregador está disponível (ex: sem falhas). - * @param enabled Indica se o carregador está habilitado via software. - */ -void evse_fsm_process(pilot_voltage_t pilot_voltage, bool authorized, bool available, bool enabled); - -#ifdef __cplusplus -} -#endif - -#endif // EVSE_FSM_H - -// === Fim de: components/evse/include/evse_fsm.h === - - -// === Início de: components/evse/include/evse_hardware.h === -#ifndef EVSE_HARDWARE_H -#define EVSE_HARDWARE_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include - -/** - * @brief Inicializa todos os periféricos de hardware do EVSE (pilot, relé, trava, etc.) - */ -void evse_hardware_init(void); - -/** - * @brief Executa atualizações periódicas no hardware (tick) - */ -void evse_hardware_tick(void); - -/** - * @brief Verifica se o sinal piloto está em nível alto (12V) - */ -bool evse_hardware_is_pilot_high(void); - -/** - * @brief Verifica se o veículo está fisicamente conectado via Proximity - */ -bool evse_hardware_is_vehicle_connected(void); - -/** - * @brief Verifica se há consumo de energia (corrente detectada) - */ -bool evse_hardware_is_energy_detected(void); - -/** - * @brief Liga o relé de fornecimento de energia - */ -void evse_hardware_relay_on(void); - -/** - * @brief Desliga o relé de fornecimento de energia - */ -void evse_hardware_relay_off(void); - -/** - * @brief Consulta o estado atual do relé - * @return true se ligado, false se desligado - */ -bool evse_hardware_relay_status(void); - -/** - * @brief Aciona a trava física do conector - */ -void evse_hardware_lock(void); - -/** - * @brief Libera a trava física do conector - */ -void evse_hardware_unlock(void); - -/** - * @brief Verifica se o conector está travado - */ -bool evse_hardware_is_locked(void); - -#ifdef __cplusplus -} -#endif - -#endif // EVSE_HARDWARE_H - -// === Fim de: components/evse/include/evse_hardware.h === - - -// === Início de: components/evse/include/evse_config.h === -#ifndef EVSE_CONFIG_H -#define EVSE_CONFIG_H - -#include -#include -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -// ======================== -// Limites Globais (Defines) -// ======================== - -// Corrente máxima de carregamento (configurável pelo usuário) -#define MIN_CHARGING_CURRENT_LIMIT 6 // A -#define MAX_CHARGING_CURRENT_LIMIT 32 // A - -// Corrente via cabo (proximity) — se configurável -#define MIN_CABLE_CURRENT_LIMIT 6 // A -#define MAX_CABLE_CURRENT_LIMIT 63 // A - -// ======================== -// Funções de Configuração -// ======================== - -// Inicialização -esp_err_t evse_config_init(void); -void evse_check_defaults(void); - -// Corrente de carregamento -uint8_t evse_get_max_charging_current(void); -esp_err_t evse_set_max_charging_current(uint8_t value); - -uint16_t evse_get_charging_current(void); -esp_err_t evse_set_charging_current(uint16_t value); - -uint16_t evse_get_default_charging_current(void); -esp_err_t evse_set_default_charging_current(uint16_t value); - -// Configuração de socket outlet -bool evse_get_socket_outlet(void); -esp_err_t evse_set_socket_outlet(bool socket_outlet); - -void evse_set_runtime_charging_current(uint16_t value); -uint16_t evse_get_runtime_charging_current(void); - - -// RCM -bool evse_is_rcm(void); -esp_err_t evse_set_rcm(bool rcm); - -// Temperatura -uint8_t evse_get_temp_threshold(void); -esp_err_t evse_set_temp_threshold(uint8_t threshold); - -// Autenticação -bool evse_is_require_auth(void); -void evse_set_require_auth(bool require); - -// Disponibilidade -bool evse_config_is_available(void); -void evse_config_set_available(bool available); - -// Ativação/desativação do EVSE -bool evse_config_is_enabled(void); -void evse_config_set_enabled(bool enabled); - -#ifdef __cplusplus -} -#endif - -#endif // EVSE_CONFIG_H - -// === Fim de: components/evse/include/evse_config.h === - - -// === Início de: components/evse/include/evse_state.h === -#ifndef EVSE_STATE_H -#define EVSE_STATE_H - -#include "evse_events.h" - - -#include - -// Estado do EVSE (pilot signal) -typedef enum { - EVSE_STATE_A, - EVSE_STATE_B1, - EVSE_STATE_B2, - EVSE_STATE_C1, - EVSE_STATE_C2, - EVSE_STATE_D1, - EVSE_STATE_D2, - EVSE_STATE_E, - EVSE_STATE_F -} evse_state_t; - - -// Funções públicas necessárias -void evse_state_init(void); -void evse_state_tick(void); - -void evse_state_set_authorized(bool authorized); -bool evse_state_get_authorized(void); - - -evse_state_t evse_get_state(void); - -void evse_set_state(evse_state_t state); - -// Converte o estado para string -const char* evse_state_to_str(evse_state_t state); - -// Retorna true se o estado representa sessão ativa -bool evse_state_is_session(evse_state_t state); - -// Retorna true se o estado representa carregamento ativo -bool evse_state_is_charging(evse_state_t state); - -// Retorna true se o estado representa veículo conectado -bool evse_state_is_plugged(evse_state_t state); - -//evse_state_event_t map_state_to_event(evse_state_t state); - -#endif // EVSE_STATE_H - -// === Fim de: components/evse/include/evse_state.h === - - -// === Início de: components/evse/include/evse_error.h === -#ifndef EVSE_ERROR_H -#define EVSE_ERROR_H - -#include -#include -#include "evse_pilot.h" - - -#define EVSE_ERR_AUTO_CLEAR_BITS ( \ - EVSE_ERR_DIODE_SHORT_BIT | \ - EVSE_ERR_TEMPERATURE_HIGH_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) - -// 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); -bool evse_error_cleared_flag(void); - -#endif // EVSE_ERROR_H - -// === Fim de: components/evse/include/evse_error.h === - - -// === Início de: components/evse/include/evse_limits.h === -#ifndef EVSE_LIMITS_H -#define EVSE_LIMITS_H - -#include -#include -#include "evse_state.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/// Estado dos limites -void evse_set_limit_reached(uint8_t value); -bool evse_is_limit_reached(void); - -/// Verifica e aplica lógica de limites com base no estado atual do EVSE -void evse_limits_check(evse_state_t state); - -/// Limites ativos (runtime) -uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); - -uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); - -uint16_t evse_get_under_power_limit(void); -void evse_set_under_power_limit(uint16_t value); - -/// Limites padrão (persistentes) -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 === - - -// === Início de: components/evse/include/evse_events.h === -#ifndef EVSE_EVENTS_H -#define EVSE_EVENTS_H - -#pragma once -#include "esp_event.h" - -ESP_EVENT_DECLARE_BASE(EVSE_EVENTS); - -typedef enum { - EVSE_EVENT_INIT, - EVSE_EVENT_STATE_CHANGED, - // Outros eventos possíveis futuramente -} evse_event_id_t; - -typedef enum { - EVSE_STATE_EVENT_IDLE, - EVSE_STATE_EVENT_WAITING, - EVSE_STATE_EVENT_CHARGING, - EVSE_STATE_EVENT_FAULT -} evse_state_event_t; - -typedef struct { - evse_state_event_t state; -} evse_state_event_data_t; - - -#endif // EVSE_EVENTS_H - -// === Fim de: components/evse/include/evse_events.h === - - -// === Início de: components/evse/include/evse_api.h === -#ifndef EVSE_API_H -#define EVSE_API_H - -#include -#include -#include "esp_err.h" -#include "evse_state.h" // Define evse_state_t - -// Inicialização -void evse_init(void); -void evse_process(void); - -// Estado -evse_state_t evse_get_state(void); -const char* evse_state_to_str(evse_state_t state); -bool evse_is_connector_plugged(evse_state_t state); -bool evse_is_limit_reached(void); - -// Autorização e disponibilidade -bool evse_is_enabled(void); -void evse_set_enabled(bool value); -bool evse_is_available(void); -void evse_set_available(bool value); -bool evse_is_require_auth(void); -void evse_set_require_auth(bool value); - -// Corrente -uint16_t evse_get_charging_current(void); -esp_err_t evse_set_charging_current(uint16_t value); -uint16_t evse_get_default_charging_current(void); -esp_err_t evse_set_default_charging_current(uint16_t value); -uint8_t evse_get_max_charging_current(void); -esp_err_t evse_set_max_charging_current(uint8_t value); - -// Temperatura -uint8_t evse_get_temp_threshold(void); -esp_err_t evse_set_temp_threshold(uint8_t value); - -// RCM / Socket -bool evse_get_socket_outlet(void); -esp_err_t evse_set_socket_outlet(bool value); -bool evse_is_rcm(void); -esp_err_t evse_set_rcm(bool value); - -// Limites -uint32_t evse_get_consumption_limit(void); -void evse_set_consumption_limit(uint32_t value); -uint32_t evse_get_charging_time_limit(void); -void evse_set_charging_time_limit(uint32_t value); -uint16_t evse_get_under_power_limit(void); -void evse_set_under_power_limit(uint16_t value); - -void evse_set_limit_reached(uint8_t value); - -// Limites default -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); - -#endif // EVSE_API_H - -// === Fim de: components/evse/include/evse_api.h === - - -// === Início de: components/loadbalancer/src/loadbalancer_events.c === -#include "loadbalancer_events.h" - -// Define a base de eventos para o loadbalancer -ESP_EVENT_DEFINE_BASE(LOADBALANCER_EVENTS); - -// === Fim de: components/loadbalancer/src/loadbalancer_events.c === - - -// === Início de: components/loadbalancer/src/loadbalancer.c === -#include "loadbalancer.h" -#include "loadbalancer_events.h" -#include "esp_event.h" -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "input_filter.h" -#include "nvs_flash.h" -#include "nvs.h" -#include -#include "meter_events.h" -#include "evse_events.h" - - - -static const char *TAG = "loadbalancer"; - -// Limites configuráveis -#define MIN_CHARGING_CURRENT_LIMIT 6 // A -#define MAX_CHARGING_CURRENT_LIMIT 32 // A -#define MIN_GRID_CURRENT_LIMIT 6 // A -#define MAX_GRID_CURRENT_LIMIT 100 // A - -// Parâmetros -static uint8_t max_grid_current = MAX_GRID_CURRENT_LIMIT; -static bool loadbalancer_enabled = false; - -static float grid_current = 0.0f; -static float evse_current = 0.0f; -static input_filter_t grid_filter; -static input_filter_t evse_filter; - -#define NVS_NAMESPACE "loadbalancing" -#define NVS_MAX_GRID_CURRENT "max_grid_curr" -#define NVS_LOADBALANCER_ENABLED "enabled" - -static void loadbalancer_meter_event_handler(void *handler_arg, - esp_event_base_t base, - int32_t id, - void *event_data) -{ - if (id != METER_EVENT_DATA_READY || event_data == NULL) - return; - - const meter_event_data_t *evt = (const meter_event_data_t *)event_data; - - ESP_LOGI(TAG, "Received meter event from source: %s", evt->source); - ESP_LOGI(TAG, "Raw IRMS: [%.2f, %.2f, %.2f] A", evt->irms[0], evt->irms[1], evt->irms[2]); - ESP_LOGI(TAG, "Raw VRMS: [%.1f, %.1f, %.1f] V", evt->vrms[0], evt->vrms[1], evt->vrms[2]); - ESP_LOGI(TAG, "Raw Power: [W1=%d, W2=%d, W3=%d]", evt->watt[0], evt->watt[1], evt->watt[2]); - ESP_LOGI(TAG, "Freq: %.2f Hz | PF: %.2f | Energy: %.3f kWh", - evt->frequency, evt->power_factor, evt->total_energy); - - // Calcula a corrente máxima entre as 3 fases - float max_irms = evt->irms[0]; - for (int i = 1; i < 3; ++i) - { - if (evt->irms[i] > max_irms) - { - max_irms = evt->irms[i]; - } - } - - ESP_LOGI(TAG, "Max IRMS detected: %.2f A", max_irms); - - // Atualiza com filtro exponencial dependendo da origem - if (strncmp(evt->source, "GRID", 4) == 0) - { - grid_current = input_filter_update(&grid_filter, max_irms); - ESP_LOGI(TAG, "GRID IRMS (filtered): %.2f A", grid_current); - } - else if (strncmp(evt->source, "EVSE", 4) == 0) - { - evse_current = input_filter_update(&evse_filter, max_irms); - ESP_LOGI(TAG, "EVSE IRMS (filtered): %.2f A", evse_current); - } - else - { - ESP_LOGW(TAG, "Unknown meter event source: %s", evt->source); - } -} - -static void loadbalancer_evse_event_handler(void *handler_arg, - esp_event_base_t base, - int32_t id, - void *event_data) -{ - const evse_state_event_data_t *evt = (const evse_state_event_data_t *)event_data; - - ESP_LOGI(TAG, "EVSE state changed: %d", evt->state); - - switch (evt->state) - { - case EVSE_STATE_EVENT_IDLE: - // Vehicle is disconnected - current flow can be reduced or reset - ESP_LOGI(TAG, "EVSE is IDLE - possible to release current"); - break; - - case EVSE_STATE_EVENT_WAITING: - // EV is connected but not charging yet (e.g., waiting for authorization) - ESP_LOGI(TAG, "EVSE is waiting - connected but not charging"); - break; - - case EVSE_STATE_EVENT_CHARGING: - grid_current = 0.0f; - evse_current = 0.0f; - // Charging has started - maintain or monitor current usage - ESP_LOGI(TAG, "EVSE is charging"); - break; - - case EVSE_STATE_EVENT_FAULT: - // A fault has occurred - safety measures may be needed - ESP_LOGW(TAG, "EVSE is in FAULT - temporarily disabling load balancing"); - // Optional: disable load balancing during fault condition - // loadbalancer_set_enabled(false); - break; - - default: - ESP_LOGW(TAG, "Unknown EVSE state: %d", evt->state); - break; - } -} - -// Carrega configuração do NVS -static esp_err_t loadbalancer_load_config() -{ - nvs_handle_t handle; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS for load/init: %s", esp_err_to_name(err)); - return err; - } - - bool needs_commit = false; - uint8_t temp_u8; - - // max_grid_current - err = nvs_get_u8(handle, NVS_MAX_GRID_CURRENT, &temp_u8); - if (err == ESP_OK && temp_u8 >= MIN_GRID_CURRENT_LIMIT && temp_u8 <= MAX_GRID_CURRENT_LIMIT) - { - max_grid_current = temp_u8; - } - else - { - max_grid_current = MAX_GRID_CURRENT_LIMIT; - nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, max_grid_current); - ESP_LOGW(TAG, "max_grid_current missing or invalid, setting default: %d", max_grid_current); - needs_commit = true; - } - - // loadbalancer_enabled - err = nvs_get_u8(handle, NVS_LOADBALANCER_ENABLED, &temp_u8); - if (err == ESP_OK && temp_u8 <= 1) - { - loadbalancer_enabled = (temp_u8 != 0); - } - else - { - loadbalancer_enabled = false; - nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, 0); - ESP_LOGW(TAG, "loadbalancer_enabled missing or invalid, setting default: 0"); - needs_commit = true; - } - - if (needs_commit) - { - nvs_commit(handle); - } - - nvs_close(handle); - return ESP_OK; -} - -// Salva o estado habilitado no NVS -void loadbalancer_set_enabled(bool enabled) -{ - ESP_LOGI(TAG, "Setting load balancing enabled to %d", enabled); - nvs_handle_t handle; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); - return; - } - - err = nvs_set_u8(handle, NVS_LOADBALANCER_ENABLED, enabled ? 1 : 0); - if (err == ESP_OK) - { - nvs_commit(handle); - loadbalancer_enabled = enabled; - ESP_LOGI(TAG, "Load balancing enabled state saved"); - - loadbalancer_state_event_t evt = { - .enabled = enabled, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_STATE_CHANGED, - &evt, - sizeof(evt), - portMAX_DELAY); - } - else - { - ESP_LOGE(TAG, "Failed to save loadbalancer_enabled"); - } - - nvs_close(handle); -} - -// Define e salva o limite de corrente da rede -esp_err_t load_balancing_set_max_grid_current(uint8_t value) -{ - if (value < MIN_GRID_CURRENT_LIMIT || value > MAX_GRID_CURRENT_LIMIT) - { - ESP_LOGE(TAG, "Invalid grid current limit: %d", value); - return ESP_ERR_INVALID_ARG; - } - - nvs_handle_t handle; - esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle); - if (err != ESP_OK) - { - ESP_LOGE(TAG, "Failed to open NVS: %s", esp_err_to_name(err)); - return err; - } - - err = nvs_set_u8(handle, NVS_MAX_GRID_CURRENT, value); - if (err == ESP_OK) - { - nvs_commit(handle); - max_grid_current = value; - ESP_LOGI(TAG, "max_grid_current set to: %d", value); - } - else - { - ESP_LOGE(TAG, "Failed to save max_grid_current to NVS"); - } - - nvs_close(handle); - return err; -} - -uint8_t load_balancing_get_max_grid_current(void) -{ - return max_grid_current; -} - -bool loadbalancer_is_enabled(void) -{ - return loadbalancer_enabled; -} - -// Tarefa principal com eventos -void loadbalancer_task(void *param) -{ - while (true) - { - if (!loadbalancer_enabled) - { - vTaskDelay(pdMS_TO_TICKS(1000)); - continue; - } - - float available = max_grid_current - grid_current + evse_current; - - if (available < MIN_CHARGING_CURRENT_LIMIT) - { - available = MIN_CHARGING_CURRENT_LIMIT; - } - else if (available > max_grid_current) - { - available = max_grid_current; - } - - ESP_LOGD(TAG, "Setting EVSE current limit: %.1f A", available); - - loadbalancer_charging_limit_event_t evt = { - .limit = available, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED, - &evt, - sizeof(evt), - portMAX_DELAY); - - vTaskDelay(pdMS_TO_TICKS(1000)); - } -} - -void loadbalancer_init(void) -{ - ESP_LOGI(TAG, "Initializing load balancer"); - - if (loadbalancer_load_config() != ESP_OK) - { - ESP_LOGW(TAG, "Failed to load/init config. Using in-memory defaults."); - } - - input_filter_init(&grid_filter, 0.3f); - input_filter_init(&evse_filter, 0.3f); - - if (xTaskCreate(loadbalancer_task, "loadbalancer", 4096, NULL, 4, NULL) != pdPASS) - { - ESP_LOGE(TAG, "Failed to create loadbalancer task"); - } - - loadbalancer_state_event_t evt = { - .enabled = loadbalancer_enabled, - .timestamp_us = esp_timer_get_time()}; - - esp_event_post(LOADBALANCER_EVENTS, - LOADBALANCER_EVENT_INIT, - &evt, - sizeof(evt), - portMAX_DELAY); - - ESP_ERROR_CHECK(esp_event_handler_register(METER_EVENT, METER_EVENT_DATA_READY, - &loadbalancer_meter_event_handler, NULL)); - - ESP_ERROR_CHECK(esp_event_handler_register(EVSE_EVENTS, - EVSE_EVENT_STATE_CHANGED, - &loadbalancer_evse_event_handler, - NULL)); -} - -// === Fim de: components/loadbalancer/src/loadbalancer.c === - - -// === Início de: components/loadbalancer/src/input_filter.c === -#include "input_filter.h" - -void input_filter_init(input_filter_t *filter, float alpha) { - if (filter) { - filter->alpha = alpha; - filter->value = 0.0f; - filter->initialized = 0; - } -} - -float input_filter_update(input_filter_t *filter, float input) { - if (!filter) return input; - - if (!filter->initialized) { - filter->value = input; - filter->initialized = 1; - } else { - filter->value = filter->alpha * input + (1.0f - filter->alpha) * filter->value; - } - - return filter->value; -} - -// === Fim de: components/loadbalancer/src/input_filter.c === - - -// === Início de: components/loadbalancer/include/loadbalancer_events.h === -#pragma once -#include "esp_event.h" -#include -#include -#include "esp_timer.h" - -ESP_EVENT_DECLARE_BASE(LOADBALANCER_EVENTS); - -typedef enum { - LOADBALANCER_EVENT_INIT, - LOADBALANCER_EVENT_STATE_CHANGED, - LOADBALANCER_EVENT_CHARGING_LIMIT_CHANGED -} loadbalancer_event_id_t; - -typedef struct { - float limit; - int64_t timestamp_us; -} loadbalancer_charging_limit_event_t; - -typedef struct { - bool enabled; - int64_t timestamp_us; -} loadbalancer_state_event_t; - - -// === Fim de: components/loadbalancer/include/loadbalancer_events.h === - - -// === Início de: components/loadbalancer/include/loadbalancer.h === -#ifndef LOADBALANCER_H_ -#define LOADBALANCER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "esp_err.h" - - -/** - * @brief Initializes the load balancer. - * - * This function configures the load balancer and its resources, including - * any necessary persistence configurations, such as storage in NVS (Non-Volatile Storage). - * This function prepares the system to perform load balancing efficiently. - */ -void loadbalancer_init(void); - -/** - * @brief Continuous task for the load balancer. - * - * This function executes the load balancing logic continuously, typically in a FreeRTOS task. - * It performs balance calculations, checks the grid current and energy conditions, and adjusts - * the outputs as necessary to ensure efficient energy consumption. - * - * @param param Input parameter, usually used to pass additional information or relevant context - * for the task execution. - */ -void loadbalancer_task(void *param); - -/** - * @brief Enables or disables the load balancing system. - * - * This function allows enabling or disabling the load balancing system. When enabled, the load - * balancer starts managing the grid current based on the configured limits. If disabled, the system - * operates without balancing. - * - * The configuration is persisted in NVS, ensuring that the choice is maintained across system restarts. - * - * @param value If true, enables load balancing. If false, disables it. - */ -void loadbalancer_set_enabled(bool value); - -/** - * @brief Checks if load balancing is enabled. - * - * This function returns the current status of the load balancing system. - * - * @return Returns true if load balancing is enabled, otherwise returns false. - */ -bool loadbalancer_is_enabled(void); - -/** - * @brief Sets the maximum grid current. - * - * This function configures the maximum grid current that can be supplied to the load balancing system. - * The value set ensures that the system does not overload the electrical infrastructure and respects - * the safety limits. - * - * @param max_grid_current The maximum allowed current (in amperes) for the load balancing system. - * This value should be appropriate for the grid capacity and the installation. - */ -esp_err_t load_balancing_set_max_grid_current(uint8_t max_grid_current); - - -/** - * @brief Gets the maximum grid current. - * - * This function retrieves the current maximum grid current limit. - * - * @return The maximum grid current (in amperes). - */ -uint8_t load_balancing_get_max_grid_current(void); - -#ifdef __cplusplus -} -#endif - -#endif /* LOADBALANCER_H_ */ - -// === Fim de: components/loadbalancer/include/loadbalancer.h === - - -// === Início de: components/loadbalancer/include/input_filter.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - float alpha; ///< Fator de suavização (0.0 a 1.0) - float value; ///< Último valor filtrado - int initialized; ///< Flag de inicialização -} input_filter_t; - -/** - * @brief Inicializa o filtro com o fator alpha desejado. - * @param filter Ponteiro para a estrutura do filtro - * @param alpha Valor entre 0.0 (mais lento) e 1.0 (sem filtro) - */ -void input_filter_init(input_filter_t *filter, float alpha); - -/** - * @brief Atualiza o valor filtrado com uma nova entrada. - * @param filter Ponteiro para o filtro - * @param input Valor bruto - * @return Valor suavizado - */ -float input_filter_update(input_filter_t *filter, float input); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/loadbalancer/include/input_filter.h === - - -// === Início de: components/auth/src/auth_events.c === -#include "auth_events.h" - -ESP_EVENT_DEFINE_BASE(AUTH_EVENTS); - -// === Fim de: components/auth/src/auth_events.c === - - -// === Início de: components/auth/src/wiegand.c === -/** - * @file wiegand.c - * - * ESP-IDF Wiegand protocol receiver - */ -#include -#include -#include -#include -#include "wiegand.h" - -static const char *TAG = "wiegand"; - -#define TIMER_INTERVAL_US 50000 // 50ms - -#define CHECK(x) \ - do \ - { \ - esp_err_t __; \ - if ((__ = x) != ESP_OK) \ - return __; \ - } while (0) -#define CHECK_ARG(VAL) \ - do \ - { \ - if (!(VAL)) \ - return ESP_ERR_INVALID_ARG; \ - } while (0) - -static void isr_disable(wiegand_reader_t *reader) -{ - gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_DISABLE); - gpio_set_intr_type(reader->gpio_d1, GPIO_INTR_DISABLE); -} - -static void isr_enable(wiegand_reader_t *reader) -{ - gpio_set_intr_type(reader->gpio_d0, GPIO_INTR_NEGEDGE); - 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) - return; - - int d0 = gpio_get_level(reader->gpio_d0); - int d1 = gpio_get_level(reader->gpio_d1); - - // ignore equal - if (d0 == d1) - return; - // overflow - if (reader->bits >= reader->size * 8) - return; - - esp_timer_stop(reader->timer); - - uint8_t value; - if (reader->bit_order == WIEGAND_MSB_FIRST) - value = (d0 ? 0x80 : 0) >> (reader->bits % 8); - else - value = (d0 ? 1 : 0) << (reader->bits % 8); - - if (reader->byte_order == WIEGAND_MSB_FIRST) - reader->buf[reader->size - reader->bits / 8 - 1] |= value; - else - reader->buf[reader->bits / 8] |= value; - - reader->bits++; - - esp_timer_start_once(reader->timer, TIMER_INTERVAL_US); -} - -static void timer_handler(void *arg) -{ - wiegand_reader_t *reader = (wiegand_reader_t *)arg; - - ESP_LOGI(TAG, "Got %d bits of data", reader->bits); - - wiegand_reader_disable(reader); - - if (reader->callback) - reader->callback(reader); - - wiegand_reader_enable(reader); - - isr_enable(reader); -} - -//////////////////////////////////////////////////////////////////////////////// - -esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, - bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, - wiegand_order_t byte_order) -{ - CHECK_ARG(reader && buf_size && callback); - - /* - esp_err_t res = gpio_install_isr_service(0); - if (res != ESP_OK && res != ESP_ERR_INVALID_STATE) - return res; - */ - - memset(reader, 0, sizeof(wiegand_reader_t)); - reader->gpio_d0 = gpio_d0; - reader->gpio_d1 = gpio_d1; - reader->size = buf_size; - reader->buf = calloc(buf_size, 1); - reader->bit_order = bit_order; - reader->byte_order = byte_order; - reader->callback = callback; - - esp_timer_create_args_t timer_args = { - .name = TAG, - .arg = reader, - .callback = timer_handler, - .dispatch_method = ESP_TIMER_TASK}; - CHECK(esp_timer_create(&timer_args, &reader->timer)); - - CHECK(gpio_set_direction(gpio_d0, GPIO_MODE_INPUT)); - CHECK(gpio_set_direction(gpio_d1, GPIO_MODE_INPUT)); - CHECK(gpio_set_pull_mode(gpio_d0, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); - CHECK(gpio_set_pull_mode(gpio_d1, internal_pullups ? GPIO_PULLUP_ONLY : GPIO_FLOATING)); - isr_disable(reader); - CHECK(gpio_isr_handler_add(gpio_d0, isr_handler, reader)); - CHECK(gpio_isr_handler_add(gpio_d1, isr_handler, reader)); - isr_enable(reader); - reader->enabled = true; - - ESP_LOGI(TAG, "Reader initialized on D0=%d, D1=%d", gpio_d0, gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_disable(wiegand_reader_t *reader) -{ - CHECK_ARG(reader); - - isr_disable(reader); - esp_timer_stop(reader->timer); - reader->enabled = false; - - ESP_LOGI(TAG, "Reader on D0=%d, D1=%d disabled", reader->gpio_d0, reader->gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_enable(wiegand_reader_t *reader) -{ - CHECK_ARG(reader); - - reader->bits = 0; - memset(reader->buf, 0, reader->size); - - isr_enable(reader); - reader->enabled = true; - - ESP_LOGI(TAG, "Reader on D0=%d, D1=%d enabled", reader->gpio_d0, reader->gpio_d1); - - return ESP_OK; -} - -esp_err_t wiegand_reader_done(wiegand_reader_t *reader) -{ - CHECK_ARG(reader && reader->buf); - - isr_disable(reader); - CHECK(gpio_isr_handler_remove(reader->gpio_d0)); - CHECK(gpio_isr_handler_remove(reader->gpio_d1)); - esp_timer_stop(reader->timer); - CHECK(esp_timer_delete(reader->timer)); - free(reader->buf); - - ESP_LOGI(TAG, "Reader removed"); - - return ESP_OK; -} - -// === Fim de: components/auth/src/wiegand.c === - - -// === Início de: components/auth/src/auth.c === -/* - * auth.c - */ - -#include "auth.h" -#include "auth_events.h" -#include "esp_event.h" -#include -#include -#include -#include -#include -#include "wiegand_reader.h" -#include "nvs_flash.h" -#include "nvs.h" - -#define MAX_TAGS 50 - -static const char *TAG = "Auth"; - -static bool enabled = false; -static char valid_tags[MAX_TAGS][AUTH_TAG_MAX_LEN]; -static int tag_count = 0; - -// =========================== -// Persistência em NVS -// =========================== - -static void load_auth_config(void) { - nvs_handle_t handle; - esp_err_t err = nvs_open("auth", NVS_READONLY, &handle); - if (err == ESP_OK) { - uint8_t val; - if (nvs_get_u8(handle, "enabled", &val) == ESP_OK) { - enabled = val; - ESP_LOGI(TAG, "Loaded auth enabled = %d", enabled); - } - nvs_close(handle); - } else { - ESP_LOGW(TAG, "No stored auth config found. Using default."); - } -} - -static void save_auth_config(void) { - nvs_handle_t handle; - if (nvs_open("auth", NVS_READWRITE, &handle) == ESP_OK) { - nvs_set_u8(handle, "enabled", enabled); - nvs_commit(handle); - nvs_close(handle); - ESP_LOGI(TAG, "Auth config saved: enabled = %d", enabled); - } else { - ESP_LOGE(TAG, "Failed to save auth config."); - } -} - -// =========================== -// Internos -// =========================== - -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 true; - //TODO - //return false; -} - -// =========================== -// API pública -// =========================== - -void auth_set_enabled(bool value) { - enabled = value; - save_auth_config(); - ESP_LOGI(TAG, "Auth %s", enabled ? "ENABLED" : "DISABLED"); - - auth_enabled_event_data_t event = { .enabled = enabled }; - esp_event_post(AUTH_EVENTS, AUTH_EVENT_ENABLED_CHANGED, &event, sizeof(event), portMAX_DELAY); -} - -bool auth_is_enabled(void) { - return enabled; -} - -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; - - strncpy(valid_tags[tag_count], tag, AUTH_TAG_MAX_LEN - 1); - valid_tags[tag_count][AUTH_TAG_MAX_LEN - 1] = '\0'; - tag_count++; - ESP_LOGI(TAG, "Tag added: %s", 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++) { - strncpy(valid_tags[j], valid_tags[j + 1], AUTH_TAG_MAX_LEN); - } - tag_count--; - ESP_LOGI(TAG, "Tag removed: %s", tag); - return true; - } - } - return false; -} - -bool auth_tag_exists(const char *tag) { - return is_tag_valid(tag); -} - -void auth_list_tags(void) { - ESP_LOGI(TAG, "Registered Tags (%d):", tag_count); - for (int i = 0; i < tag_count; i++) { - ESP_LOGI(TAG, "- %s", valid_tags[i]); - } -} - -void auth_init(void) { - load_auth_config(); // carrega estado de ativação - - if (enabled) { - initWiegand(); // só inicia se estiver habilitado - ESP_LOGI(TAG, "Wiegand reader initialized (Auth enabled)"); - } else { - ESP_LOGI(TAG, "Auth disabled, Wiegand reader not started"); - } - - auth_enabled_event_data_t evt = { .enabled = enabled }; - esp_event_post(AUTH_EVENTS, AUTH_EVENT_INIT, &evt, sizeof(evt), portMAX_DELAY); - - ESP_LOGI(TAG, "Estado inicial AUTH enviado (enabled = %d)", enabled); -} - -void auth_process_tag(const char *tag) { - if (!tag || !auth_is_enabled()) { - ESP_LOGW(TAG, "Auth disabled or NULL tag received."); - return; - } - - auth_tag_event_data_t event; - strncpy(event.tag, tag, AUTH_EVENT_TAG_MAX_LEN - 1); - event.tag[AUTH_EVENT_TAG_MAX_LEN - 1] = '\0'; - event.authorized = is_tag_valid(tag); - - ESP_LOGI(TAG, "Tag %s: %s", tag, event.authorized ? "AUTHORIZED" : "DENIED"); - - esp_event_post(AUTH_EVENTS, AUTH_EVENT_TAG_PROCESSED, &event, sizeof(event), portMAX_DELAY); -} - -// === Fim de: components/auth/src/auth.c === - - -// === Início de: components/auth/src/wiegand_reader.c === -#include -#include -#include -#include -#include -#include -#include -#include "auth.h" - -#define CONFIG_EXAMPLE_BUF_SIZE 50 - -static const char *TAG = "WiegandReader"; - -static wiegand_reader_t reader; -static QueueHandle_t queue = NULL; - -typedef struct { - uint8_t data[CONFIG_EXAMPLE_BUF_SIZE]; - size_t bits; -} data_packet_t; - -static void reader_callback(wiegand_reader_t *r) { - data_packet_t p; - p.bits = r->bits; - memcpy(p.data, r->buf, CONFIG_EXAMPLE_BUF_SIZE); - xQueueSendToBack(queue, &p, 0); -} - -static void wiegand_task(void *arg) { - queue = xQueueCreate(5, sizeof(data_packet_t)); - if (!queue) { - ESP_LOGE(TAG, "Failed to create queue"); - vTaskDelete(NULL); - return; - } - - ESP_ERROR_CHECK(wiegand_reader_init(&reader, 19, 18, - true, CONFIG_EXAMPLE_BUF_SIZE, reader_callback, WIEGAND_MSB_FIRST, WIEGAND_LSB_FIRST)); - - data_packet_t p; - while (1) { - ESP_LOGI(TAG, "Waiting for Wiegand data..."); - if (xQueueReceive(queue, &p, portMAX_DELAY) == pdPASS) { - ESP_LOGI(TAG, "Bits received: %d", p.bits); - - char tag[20] = {0}; - - if (p.bits == 26) { - snprintf(tag, sizeof(tag), "%03d%03d%03d", p.data[0], p.data[1], p.data[2]); - } else if (p.bits == 34) { - snprintf(tag, sizeof(tag), "%03d%03d%03d%03d", p.data[0], p.data[1], p.data[2], p.data[3]); - } else { - ESP_LOGW(TAG, "Unsupported bit length: %d", (int)p.bits); - continue; - } - - ESP_LOGI(TAG, "Tag read: %s", tag); - auth_process_tag(tag); // agora delega toda a lógica à auth.c - } - } -} - -void initWiegand(void) { - ESP_LOGI(TAG, "Initializing Wiegand reader"); - xTaskCreate(wiegand_task, TAG, configMINIMAL_STACK_SIZE * 4, NULL, 4, NULL); -} - -// === Fim de: components/auth/src/wiegand_reader.c === - - -// === Início de: components/auth/include/auth.h === -#ifndef AUTH_H -#define AUTH_H - -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -/// Tamanho máximo de uma tag RFID (incluindo '\0') -#define AUTH_TAG_MAX_LEN 20 - -/// Estrutura de evento emitida após leitura de uma tag -typedef struct { - char tag[AUTH_TAG_MAX_LEN]; ///< Tag lida - bool authorized; ///< true se a tag for reconhecida como válida -} auth_event_t; - -/** - * @brief Inicializa o sistema de autenticação. - * - * - Carrega a configuração (enabled) da NVS - * - Inicia o leitor Wiegand - * - Emite evento AUTH_EVENT_INIT com estado atual - */ -void auth_init(void); - -/** - * @brief Ativa ou desativa o uso de autenticação via RFID. - * - * Esta configuração é persistida em NVS. Se desativado, o sistema - * considerará todas as autorizações como aceitas. - * - * @param value true para ativar, false para desativar - */ -void auth_set_enabled(bool value); - -/** - * @brief Verifica se o sistema de autenticação está habilitado. - */ -bool auth_is_enabled(void); - -/** - * @brief Adiciona uma nova tag RFID à lista de autorizadas. - * - * @param tag String da tag (máx AUTH_TAG_MAX_LEN-1) - * @return true se a tag foi adicionada, false se já existia ou inválida - */ -bool auth_add_tag(const char *tag); - -/** - * @brief Remove uma tag previamente cadastrada. - * - * @param tag String da tag - * @return true se foi removida, false se não encontrada - */ -bool auth_remove_tag(const char *tag); - -/** - * @brief Verifica se uma tag já está registrada como válida. - */ -bool auth_tag_exists(const char *tag); - -/** - * @brief Lista todas as tags válidas atualmente registradas (via logs). - */ -void auth_list_tags(void); - -/** - * @brief Processa uma tag RFID lida (chamada normalmente pelo leitor). - * - * - Verifica validade - * - Emite evento AUTH_EVENT_TAG_PROCESSED - * - Inicia timer de expiração se autorizada - */ -void auth_process_tag(const char *tag); - - -#ifdef __cplusplus -} -#endif - -#endif // AUTH_H - -// === Fim de: components/auth/include/auth.h === - - -// === Início de: components/auth/include/auth_events.h === -#pragma once -#include "esp_event.h" - -#define AUTH_EVENT_TAG_MAX_LEN 32 - -ESP_EVENT_DECLARE_BASE(AUTH_EVENTS); - -typedef enum { - AUTH_EVENT_TAG_PROCESSED, - AUTH_EVENT_ENABLED_CHANGED, - AUTH_EVENT_INIT, -} auth_event_id_t; - -typedef struct { - char tag[AUTH_EVENT_TAG_MAX_LEN]; - bool authorized; -} auth_tag_event_data_t; - -typedef struct { - bool enabled; -} auth_enabled_event_data_t; - -// === Fim de: components/auth/include/auth_events.h === - - -// === Início de: components/auth/include/wiegand.h === -/* - * Copyright (c) 2021 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -/** - * @file wiegand.h - * @defgroup wiegand wiegand - * @{ - * - * ESP-IDF Wiegand protocol receiver - * - * Copyright (c) 2021 Ruslan V. Uss - * - * BSD Licensed as described in the file LICENSE - */ -#ifndef __WIEGAND_H__ -#define __WIEGAND_H__ - -#include -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct wiegand_reader wiegand_reader_t; - -typedef void (*wiegand_callback_t)(wiegand_reader_t *reader); - -/** - * Bit and byte order of data - */ -typedef enum { - WIEGAND_MSB_FIRST = 0, - WIEGAND_LSB_FIRST -} wiegand_order_t; - -/** - * Wiegand reader descriptor - */ -struct wiegand_reader -{ - gpio_num_t gpio_d0, gpio_d1; - wiegand_callback_t callback; - wiegand_order_t bit_order; - wiegand_order_t byte_order; - - uint8_t *buf; - size_t size; - size_t bits; - esp_timer_handle_t timer; - bool start_parity; - bool enabled; -}; - -/** - * @brief Create and initialize reader instance. - * - * @param reader Reader descriptor - * @param gpio_d0 GPIO pin for D0 - * @param gpio_d1 GPIO pin for D0 - * @param internal_pullups Enable internal pull-up resistors for D0 and D1 GPIO - * @param buf_size Reader buffer size in bytes, must be large enough to - * contain entire Wiegand key - * @param callback Callback function for processing received codes - * @param bit_order Bit order of data - * @param byte_order Byte order of data - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_init(wiegand_reader_t *reader, gpio_num_t gpio_d0, gpio_num_t gpio_d1, - bool internal_pullups, size_t buf_size, wiegand_callback_t callback, wiegand_order_t bit_order, - wiegand_order_t byte_order); - -/** - * @brief Disable reader - * - * While reader is disabled, it will not receive new data - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_disable(wiegand_reader_t *reader); - -/** - * @brief Enable reader - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_enable(wiegand_reader_t *reader); - -/** - * @brief Delete reader instance. - * - * @param reader Reader descriptor - * @return `ESP_OK` on success - */ -esp_err_t wiegand_reader_done(wiegand_reader_t *reader); - -#ifdef __cplusplus -} -#endif - -/**@}*/ - -#endif /* __WIEGAND_H__ */ - -// === Fim de: components/auth/include/wiegand.h === - - -// === Início de: components/auth/include/wiegand_reader.h === -#ifndef WIEGAND_READER_H -#define WIEGAND_READER_H - -#ifdef __cplusplus -extern "C" { -#endif - -void initWiegand(void); - -#ifdef __cplusplus -} -#endif - -#endif // WIEGAND_READER_H - -// === Fim de: components/auth/include/wiegand_reader.h === - - -// === Início de: components/rest_api/src/ocpp_api.c === -// ========================= -// ocpp_api.c -// ========================= -#include "ocpp_api.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "ocpp_api"; - -static struct { - char url[256]; - char chargeBoxId[128]; - char certificate[256]; - char privateKey[256]; -} ocpp_config = {"", "", "", ""}; - -static esp_err_t ocpp_get_status_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *status = cJSON_CreateObject(); - cJSON_AddStringToObject(status, "status", "connected"); - char *str = cJSON_Print(status); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(status); - return ESP_OK; -} - -static esp_err_t ocpp_get_config_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *json = cJSON_CreateObject(); - cJSON_AddStringToObject(json, "url", ocpp_config.url); - cJSON_AddStringToObject(json, "chargeBoxId", ocpp_config.chargeBoxId); - cJSON_AddStringToObject(json, "certificate", ocpp_config.certificate); - cJSON_AddStringToObject(json, "privateKey", ocpp_config.privateKey); - char *str = cJSON_Print(json); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(json); - return ESP_OK; -} - -static esp_err_t ocpp_post_config_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - cJSON *url = cJSON_GetObjectItem(json, "url"); - if (url) strlcpy(ocpp_config.url, url->valuestring, sizeof(ocpp_config.url)); - cJSON *id = cJSON_GetObjectItem(json, "chargeBoxId"); - if (id) strlcpy(ocpp_config.chargeBoxId, id->valuestring, sizeof(ocpp_config.chargeBoxId)); - cJSON *cert = cJSON_GetObjectItem(json, "certificate"); - if (cert) strlcpy(ocpp_config.certificate, cert->valuestring, sizeof(ocpp_config.certificate)); - cJSON *key = cJSON_GetObjectItem(json, "privateKey"); - if (key) strlcpy(ocpp_config.privateKey, key->valuestring, sizeof(ocpp_config.privateKey)); - cJSON_Delete(json); - httpd_resp_sendstr(req, "OCPP config atualizada com sucesso"); - return ESP_OK; -} - -void register_ocpp_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t status_uri = { - .uri = "/api/v1/ocpp", - .method = HTTP_GET, - .handler = ocpp_get_status_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &status_uri); - - httpd_uri_t get_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_GET, - .handler = ocpp_get_config_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - httpd_uri_t post_uri = { - .uri = "/api/v1/config/ocpp", - .method = HTTP_POST, - .handler = ocpp_post_config_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/ocpp_api.c === - - -// === Início de: components/rest_api/src/static_file_api.c === -#include "static_file_api.h" -#include "esp_log.h" -#include -#include -#include "esp_vfs.h" - -static const char *TAG = "static_file_api"; - -#define FILE_PATH_MAX (ESP_VFS_PATH_MAX + 128) -#define SCRATCH_BUFSIZE (10240) - -typedef struct rest_server_context { - char base_path[ESP_VFS_PATH_MAX + 1]; - char scratch[SCRATCH_BUFSIZE]; -} rest_server_context_t; - -#define CHECK_FILE_EXTENSION(filename, ext) \ - (strcasecmp(&filename[strlen(filename) - strlen(ext)], ext) == 0) - -static esp_err_t set_content_type_from_file(httpd_req_t *req, const char *filepath) { - const char *type = "text/plain"; - if (CHECK_FILE_EXTENSION(filepath, ".html")) type = "text/html"; - else if (CHECK_FILE_EXTENSION(filepath, ".js")) type = "application/javascript"; - else if (CHECK_FILE_EXTENSION(filepath, ".css")) type = "text/css"; - else if (CHECK_FILE_EXTENSION(filepath, ".png")) type = "image/png"; - else if (CHECK_FILE_EXTENSION(filepath, ".ico")) type = "image/x-icon"; - else if (CHECK_FILE_EXTENSION(filepath, ".svg")) type = "image/svg+xml"; - return httpd_resp_set_type(req, type); -} - -static esp_err_t static_get_handler(httpd_req_t *req) { - char filepath[FILE_PATH_MAX]; - rest_server_context_t *ctx = (rest_server_context_t *) req->user_ctx; - - strlcpy(filepath, ctx->base_path, sizeof(filepath)); - if (req->uri[strlen(req->uri) - 1] == '/') { - strlcat(filepath, "/index.html", sizeof(filepath)); - } else { - strlcat(filepath, req->uri, sizeof(filepath)); - } - - int fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - // fallback para /index.html (SPA) - ESP_LOGW(TAG, "Arquivo não encontrado: %s. Tentando index.html", filepath); - strlcpy(filepath, ctx->base_path, sizeof(filepath)); - strlcat(filepath, "/index.html", sizeof(filepath)); - fd = open(filepath, O_RDONLY, 0); - if (fd == -1) { - httpd_resp_send_err(req, HTTPD_404_NOT_FOUND, "Arquivo não encontrado"); - return ESP_FAIL; - } - } - - set_content_type_from_file(req, filepath); - - char *chunk = ctx->scratch; - ssize_t read_bytes; - do { - read_bytes = read(fd, chunk, SCRATCH_BUFSIZE); - if (read_bytes == -1) { - ESP_LOGE(TAG, "Erro lendo arquivo: %s", filepath); - close(fd); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao ler arquivo"); - return ESP_FAIL; - } else if (read_bytes > 0) { - if (httpd_resp_send_chunk(req, chunk, read_bytes) != ESP_OK) { - close(fd); - httpd_resp_sendstr_chunk(req, NULL); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao enviar arquivo"); - return ESP_FAIL; - } - } - } while (read_bytes > 0); - - close(fd); - httpd_resp_send_chunk(req, NULL, 0); - return ESP_OK; -} - -void register_static_file_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t uri = { - .uri = "/*", - .method = HTTP_GET, - .handler = static_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &uri); -} - -// === Fim de: components/rest_api/src/static_file_api.c === - - -// === Início de: components/rest_api/src/meters_settings_api.c === -#include "meters_settings_api.h" -#include "meter_manager.h" // Atualizado para usar o novo manager -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "meters_settings_api"; - -// Função para recuperar as configurações dos contadores -static esp_err_t meters_config_get_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Received GET request for /api/v1/config/meters"); - - httpd_resp_set_type(req, "application/json"); - - cJSON *config = cJSON_CreateObject(); - - // Recuperando as configurações dos contadores - meter_type_t gridmeterType = meter_manager_grid_get_model(); - meter_type_t evsemeterType = meter_manager_evse_get_model(); - - ESP_LOGI(TAG, "Grid meter type: %s", meter_type_to_str(gridmeterType)); - ESP_LOGI(TAG, "EVSE meter type: %s", meter_type_to_str(evsemeterType)); - - // Adicionando os tipos de contadores ao objeto JSON - cJSON_AddStringToObject(config, "gridmeter", meter_type_to_str(gridmeterType)); - cJSON_AddStringToObject(config, "evsemeter", meter_type_to_str(evsemeterType)); - - // Convertendo o objeto JSON para uma string - const char *json_str = cJSON_Print(config); - ESP_LOGI(TAG, "Returning meters config: %s", json_str); - - httpd_resp_sendstr(req, json_str); - - // Liberação da memória - free((void *)json_str); - cJSON_Delete(config); - - return ESP_OK; -} - -// Função para atualizar as configurações dos contadores -static esp_err_t meters_config_post_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Received POST request for /api/v1/config/meters"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - - if (len <= 0) { - ESP_LOGE(TAG, "Received empty body in POST request"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - - buf[len] = '\0'; // Garantir que a string está terminada - - ESP_LOGI(TAG, "Received POST data: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Failed to parse JSON data"); - // Resposta detalhada de erro - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON format"); - return ESP_FAIL; - } - - // Atualizando os contadores - cJSON *gridmeter = cJSON_GetObjectItem(json, "gridmeter"); - if (gridmeter) { - meter_type_t gridType = string_to_meter_type(gridmeter->valuestring); // Usando a função string_to_meter_type - ESP_LOGI(TAG, "Updating grid meter type to: %s", gridmeter->valuestring); - meter_manager_grid_set_model(gridType); - } - - cJSON *evsemeter = cJSON_GetObjectItem(json, "evsemeter"); - if (evsemeter) { - meter_type_t evseType = string_to_meter_type(evsemeter->valuestring); // Usando a função string_to_meter_type - ESP_LOGI(TAG, "Updating EVSE meter type to: %s", evsemeter->valuestring); - meter_manager_evse_set_model(evseType); - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Meters updated successfully"); - - ESP_LOGI(TAG, "Meters configuration updated successfully"); - - return ESP_OK; -} - -// Registrando os manipuladores de URI para os contadores -void register_meters_settings_handlers(httpd_handle_t server, void *ctx) { - ESP_LOGI(TAG, "Registering URI handlers for meters settings"); - - // URI para o método GET - httpd_uri_t meters_get_uri = { - .uri = "/api/v1/config/meters", - .method = HTTP_GET, - .handler = meters_config_get_handler, - .user_ctx = ctx - }; - ESP_LOGI(TAG, "Registering GET handler for /api/v1/config/meters"); - httpd_register_uri_handler(server, &meters_get_uri); - - // URI para o método POST - httpd_uri_t meters_post_uri = { - .uri = "/api/v1/config/meters", - .method = HTTP_POST, - .handler = meters_config_post_handler, - .user_ctx = ctx - }; - ESP_LOGI(TAG, "Registering POST handler for /api/v1/config/meters"); - httpd_register_uri_handler(server, &meters_post_uri); -} - -// === Fim de: components/rest_api/src/meters_settings_api.c === - - -// === Início de: components/rest_api/src/rest_main.c === -#include "rest_main.h" -#include "evse_settings_api.h" -#include "meters_settings_api.h" -#include "loadbalancing_settings_api.h" -#include "network_api.h" -#include "ocpp_api.h" -#include "auth_api.h" -#include "dashboard_api.h" -#include "static_file_api.h" -#include "esp_log.h" - - -static const char *TAG = "rest_main"; - -esp_err_t rest_server_init(const char *base_path) { - ESP_LOGI(TAG, "Initializing REST API with base path: %s", base_path); - - rest_server_context_t *ctx = calloc(1, sizeof(rest_server_context_t)); - if (!ctx) { - ESP_LOGE(TAG, "Failed to allocate memory for REST context"); - return ESP_ERR_NO_MEM; - } - - strlcpy(ctx->base_path, base_path, sizeof(ctx->base_path)); - - httpd_config_t config = HTTPD_DEFAULT_CONFIG(); - config.uri_match_fn = httpd_uri_match_wildcard; - config.max_uri_handlers = 32; - - httpd_handle_t server = NULL; - esp_err_t err = httpd_start(&server, &config); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to start HTTP server: %s", esp_err_to_name(err)); - free(ctx); - return err; - } - - ESP_LOGI(TAG, "HTTP server started successfully"); - - // Register endpoint groups - register_evse_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_network_handlers(server, ctx); // Apenas chamando a função sem comparação - register_ocpp_handlers(server, ctx); // Apenas chamando a função sem comparação - register_auth_handlers(server, ctx); // Apenas chamando a função sem comparação - register_dashboard_handlers(server, ctx); // Apenas chamando a função sem comparação - register_meters_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_loadbalancing_settings_handlers(server, ctx); // Apenas chamando a função sem comparação - register_static_file_handlers(server, ctx); // Apenas chamando a função sem comparação - - ESP_LOGI(TAG, "All REST API endpoint groups registered successfully"); - - return ESP_OK; -} - -// === Fim de: components/rest_api/src/rest_main.c === - - -// === Início de: components/rest_api/src/network_api.c === -// ========================= -// network_api.c -// ========================= - -#include "network_api.h" -#include "esp_log.h" -#include "cJSON.h" -#include "wifi.h" -#include "mqtt.h" - -static const char *TAG = "network_api"; - -typedef struct { - bool enabled; - char ssid[33]; - char password[65]; -} wifi_task_data_t; - - -static void wifi_apply_config_task(void *param) { - wifi_task_data_t *data = (wifi_task_data_t *)param; - ESP_LOGI("wifi_task", "Applying Wi-Fi config in background task"); - wifi_set_config(data->enabled, data->ssid, data->password); - free(data); - vTaskDelete(NULL); -} - -static esp_err_t wifi_get_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Handling GET /api/v1/config/wifi"); - - httpd_resp_set_type(req, "application/json"); - - // Obter dados da NVS via wifi.c - bool enabled = wifi_get_enabled(); - char ssid[33] = {0}; - char password[65] = {0}; - - wifi_get_ssid(ssid); - wifi_get_password(password); - - // Criar JSON - cJSON *json = cJSON_CreateObject(); - cJSON_AddBoolToObject(json, "enabled", enabled); - cJSON_AddStringToObject(json, "ssid", ssid); - cJSON_AddStringToObject(json, "password", password); - - // Enviar resposta - char *response = cJSON_Print(json); - httpd_resp_sendstr(req, response); - - // Limpeza - free(response); - cJSON_Delete(json); - - return ESP_OK; -} - -static esp_err_t wifi_post_handler(httpd_req_t *req) { - ESP_LOGI(TAG, "Handling POST /api/v1/config/wifi"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) return ESP_FAIL; - buf[len] = '\0'; - - cJSON *json = cJSON_Parse(buf); - if (!json) return ESP_FAIL; - - // Valores padrão - bool enabled = false; - const char *ssid = NULL; - const char *password = NULL; - - cJSON *j_enabled = cJSON_GetObjectItem(json, "enabled"); - if (cJSON_IsBool(j_enabled)) enabled = j_enabled->valueint; - - cJSON *j_ssid = cJSON_GetObjectItem(json, "ssid"); - if (cJSON_IsString(j_ssid)) ssid = j_ssid->valuestring; - - cJSON *j_password = cJSON_GetObjectItem(json, "password"); - if (cJSON_IsString(j_password)) password = j_password->valuestring; - - // Enviar resposta antes de alterar Wi-Fi - httpd_resp_sendstr(req, "Wi-Fi config atualizada com sucesso"); - - // Alocar struct para passar para a task - wifi_task_data_t *task_data = malloc(sizeof(wifi_task_data_t)); - if (!task_data) { - cJSON_Delete(json); - ESP_LOGE(TAG, "Memory allocation failed for Wi-Fi task"); - return ESP_ERR_NO_MEM; - } - - task_data->enabled = enabled; - strncpy(task_data->ssid, ssid ? ssid : "", sizeof(task_data->ssid)); - strncpy(task_data->password, password ? password : "", sizeof(task_data->password)); - - // Criar task normal com função C - xTaskCreate( - wifi_apply_config_task, - "wifi_config_task", - 4096, - task_data, - 3, - NULL - ); - - cJSON_Delete(json); - return ESP_OK; -} - - -static esp_err_t config_mqtt_get_handler(httpd_req_t *req) -{ - ESP_LOGI(TAG, "Handling GET /api/v1/config/mqtt"); - - httpd_resp_set_type(req, "application/json"); - - bool enabled = mqtt_get_enabled(); - char server[64] = {0}; - char base_topic[32] = {0}; - char username[32] = {0}; - char password[64] = {0}; - uint16_t periodicity = mqtt_get_periodicity(); - - mqtt_get_server(server); - mqtt_get_base_topic(base_topic); - mqtt_get_user(username); - mqtt_get_password(password); - - ESP_LOGI(TAG, "MQTT Config:"); - ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); - ESP_LOGI(TAG, " Server: %s", server); - ESP_LOGI(TAG, " Topic: %s", base_topic); - ESP_LOGI(TAG, " Username: %s", username); - ESP_LOGI(TAG, " Password: %s", password); - ESP_LOGI(TAG, " Periodicity: %d", periodicity); - - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "enabled", enabled); - cJSON_AddStringToObject(config, "host", server); - cJSON_AddNumberToObject(config, "port", 1883); - cJSON_AddStringToObject(config, "username", username); - cJSON_AddStringToObject(config, "password", password); - cJSON_AddStringToObject(config, "topic", base_topic); - cJSON_AddNumberToObject(config, "periodicity", periodicity); - - const char *config_str = cJSON_Print(config); - httpd_resp_sendstr(req, config_str); - - free((void *)config_str); - cJSON_Delete(config); - return ESP_OK; -} - - -static esp_err_t config_mqtt_post_handler(httpd_req_t *req) -{ - ESP_LOGI(TAG, "Handling POST /api/v1/config/mqtt"); - - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - ESP_LOGE(TAG, "Failed to read request body"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid request body"); - return ESP_FAIL; - } - buf[len] = '\0'; - ESP_LOGI(TAG, "Received JSON: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Invalid JSON format"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - bool enabled = false; - const char *host = NULL, *topic = NULL, *username = NULL, *password = NULL; - int periodicity = 30; - - if (cJSON_IsBool(cJSON_GetObjectItem(json, "enabled"))) - enabled = cJSON_GetObjectItem(json, "enabled")->valueint; - - cJSON *j_host = cJSON_GetObjectItem(json, "host"); - if (cJSON_IsString(j_host)) host = j_host->valuestring; - - cJSON *j_topic = cJSON_GetObjectItem(json, "topic"); - if (cJSON_IsString(j_topic)) topic = j_topic->valuestring; - - cJSON *j_user = cJSON_GetObjectItem(json, "username"); - if (cJSON_IsString(j_user)) username = j_user->valuestring; - - cJSON *j_pass = cJSON_GetObjectItem(json, "password"); - if (cJSON_IsString(j_pass)) password = j_pass->valuestring; - - cJSON *j_periodicity = cJSON_GetObjectItem(json, "periodicity"); - if (cJSON_IsNumber(j_periodicity)) periodicity = j_periodicity->valueint; - - ESP_LOGI(TAG, "Applying MQTT config:"); - ESP_LOGI(TAG, " Enabled: %s", enabled ? "true" : "false"); - ESP_LOGI(TAG, " Host: %s", host); - ESP_LOGI(TAG, " Topic: %s", topic); - ESP_LOGI(TAG, " Username: %s", username); - ESP_LOGI(TAG, " Password: %s", password); - ESP_LOGI(TAG, " Periodicity: %d", periodicity); - - esp_err_t err = mqtt_set_config(enabled, host, topic, username, password, periodicity); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to apply MQTT config (code %d)", err); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to apply config"); - cJSON_Delete(json); - return ESP_FAIL; - } - - httpd_resp_sendstr(req, "Configuração MQTT atualizada com sucesso"); - cJSON_Delete(json); - return ESP_OK; -} - - - -void register_network_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t wifi_get = { - .uri = "/api/v1/config/wifi", - .method = HTTP_GET, - .handler = wifi_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &wifi_get); - - httpd_uri_t wifi_post = { - .uri = "/api/v1/config/wifi", - .method = HTTP_POST, - .handler = wifi_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &wifi_post); - - // URI handler for getting MQTT config - httpd_uri_t config_mqtt_get_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_GET, - .handler = config_mqtt_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &config_mqtt_get_uri); - - // URI handler for posting MQTT config - httpd_uri_t config_mqtt_post_uri = { - .uri = "/api/v1/config/mqtt", - .method = HTTP_POST, - .handler = config_mqtt_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &config_mqtt_post_uri); -} - -// === Fim de: components/rest_api/src/network_api.c === - - -// === Início de: components/rest_api/src/dashboard_api.c === -#include "dashboard_api.h" -#include "esp_log.h" -#include "cJSON.h" -#include "evse_api.h" -#include "evse_error.h" - -static const char *TAG = "dashboard_api"; - -static esp_err_t dashboard_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - - // Cria o objeto JSON principal do dashboard - cJSON *dashboard = cJSON_CreateObject(); - - // Status do sistema - evse_state_t state = evse_get_state(); - cJSON_AddStringToObject(dashboard, "status", evse_state_to_str(state)); - - // Carregador - informação do carregador 1 (adapte conforme necessário) - cJSON *chargers = cJSON_CreateArray(); - cJSON *charger1 = cJSON_CreateObject(); - cJSON_AddNumberToObject(charger1, "id", 1); - cJSON_AddStringToObject(charger1, "status", evse_state_to_str(state)); - cJSON_AddNumberToObject(charger1, "current", evse_get_charging_current() / 10); - cJSON_AddNumberToObject(charger1, "maxCurrent", evse_get_max_charging_current()); - - // Calcular a potência com base na corrente (considerando 230V) - int power = (evse_get_charging_current() / 10) * 230; - cJSON_AddNumberToObject(charger1, "power", power); - - cJSON_AddItemToArray(chargers, charger1); - cJSON_AddItemToObject(dashboard, "chargers", chargers); - - // Consumo e tempo de carregamento - cJSON_AddNumberToObject(dashboard, "energyConsumed", evse_get_consumption_limit()); - cJSON_AddNumberToObject(dashboard, "chargingTime", evse_get_charging_time_limit()); - - // Alertas - cJSON *alerts = cJSON_CreateArray(); - if (evse_is_limit_reached()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("Limite de consumo atingido.")); - } - if (!evse_is_available()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("Estação indisponível.")); - } - if (!evse_is_enabled()) { - cJSON_AddItemToArray(alerts, cJSON_CreateString("EVSE desativado.")); - } - cJSON_AddItemToObject(dashboard, "alerts", alerts); - - // Erros - uint32_t error_bits = evse_get_error(); - cJSON *errors = cJSON_CreateArray(); - if (error_bits & EVSE_ERR_DIODE_SHORT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Diodo curto-circuitado")); - if (error_bits & EVSE_ERR_LOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no travamento")); - if (error_bits & EVSE_ERR_UNLOCK_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no destravamento")); - if (error_bits & EVSE_ERR_RCM_SELFTEST_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no autoteste do RCM")); - if (error_bits & EVSE_ERR_RCM_TRIGGERED_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("RCM disparado")); - if (error_bits & EVSE_ERR_TEMPERATURE_HIGH_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Temperatura elevada")); - if (error_bits & EVSE_ERR_PILOT_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Erro no sinal piloto")); - if (error_bits & EVSE_ERR_TEMPERATURE_FAULT_BIT) cJSON_AddItemToArray(errors, cJSON_CreateString("Falha no sensor de temperatura")); - cJSON_AddItemToObject(dashboard, "errors", errors); - - // Enviar resposta JSON - const char *json_str = cJSON_Print(dashboard); - httpd_resp_sendstr(req, json_str); - - // Liberar memória - free((void *)json_str); - cJSON_Delete(dashboard); - - return ESP_OK; -} - -void register_dashboard_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t uri = { - .uri = "/api/v1/dashboard", - .method = HTTP_GET, - .handler = dashboard_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &uri); -} - -// === Fim de: components/rest_api/src/dashboard_api.c === - - -// === Início de: components/rest_api/src/auth_api.c === -// ========================= -// auth_api.c -// ========================= -#include "auth_api.h" -#include "auth.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "auth_api"; - -static struct { - char username[128]; -} users[10] = { /*{"admin"}, {"user1"}*/ }; -static int num_users = 2; - -static esp_err_t auth_methods_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *json = cJSON_CreateObject(); - cJSON_AddBoolToObject(json, "RFID", auth_is_enabled() ); - char *str = cJSON_PrintUnformatted(json); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(json); - return ESP_OK; -} - -static esp_err_t auth_methods_post_handler(httpd_req_t *req) { - char buf[256]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Erro ao receber dados"); - return ESP_FAIL; - } - - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "JSON inválido"); - return ESP_FAIL; - } - - cJSON *rfid = cJSON_GetObjectItem(json, "RFID"); - if (rfid && cJSON_IsBool(rfid)) { - auth_set_enabled(cJSON_IsTrue(rfid)); - } else { - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Campo 'RFID' inválido ou ausente"); - return ESP_FAIL; - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Métodos de autenticação atualizados"); - return ESP_OK; -} - - -static esp_err_t users_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *root = cJSON_CreateObject(); - cJSON *list = cJSON_CreateArray(); - for (int i = 0; i < num_users; ++i) { - cJSON *u = cJSON_CreateObject(); - cJSON_AddStringToObject(u, "username", users[i].username); - cJSON_AddItemToArray(list, u); - } - cJSON_AddItemToObject(root, "users", list); - char *str = cJSON_Print(root); - httpd_resp_sendstr(req, str); - free(str); - cJSON_Delete(root); - return ESP_OK; -} - -static esp_err_t users_post_handler(httpd_req_t *req) { - char buf[128]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) return ESP_FAIL; - buf[len] = '\0'; - if (num_users < 10) { - strlcpy(users[num_users].username, buf, sizeof(users[num_users].username)); - num_users++; - httpd_resp_sendstr(req, "Usuário adicionado com sucesso"); - } else { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Limite de usuários atingido"); - } - return ESP_OK; -} - -static esp_err_t users_delete_handler(httpd_req_t *req) { - char query[128]; - if (httpd_req_get_url_query_str(req, query, sizeof(query)) == ESP_OK) { - char username[128]; - if (httpd_query_key_value(query, "username", username, sizeof(username)) == ESP_OK) { - for (int i = 0; i < num_users; i++) { - if (strcmp(users[i].username, username) == 0) { - for (int j = i; j < num_users - 1; j++) { - users[j] = users[j + 1]; - } - num_users--; - httpd_resp_sendstr(req, "Usuário removido com sucesso"); - return ESP_OK; - } - } - } - } - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Usuário não encontrado"); - return ESP_FAIL; -} - -void register_auth_handlers(httpd_handle_t server, void *ctx) { - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/auth-methods", - .method = HTTP_GET, - .handler = auth_methods_get_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/auth-methods", - .method = HTTP_POST, - .handler = auth_methods_post_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_GET, - .handler = users_get_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_POST, - .handler = users_post_handler, - .user_ctx = ctx - }); - httpd_register_uri_handler(server, &(httpd_uri_t){ - .uri = "/api/v1/config/users", - .method = HTTP_DELETE, - .handler = users_delete_handler, - .user_ctx = ctx - }); -} - -// === Fim de: components/rest_api/src/auth_api.c === - - -// === Início de: components/rest_api/src/loadbalancing_settings_api.c === -#include "loadbalancing_settings_api.h" -#include "loadbalancer.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "loadbalancing_settings_api"; - -// GET Handler: Retorna configurações atuais de load balancing -static esp_err_t loadbalancing_config_get_handler(httpd_req_t *req) { - bool enabled = loadbalancer_is_enabled(); - uint8_t currentLimit = load_balancing_get_max_grid_current(); - - ESP_LOGI(TAG, "Fetching load balancing settings: enabled = %d, currentLimit = %u", enabled, currentLimit); - - httpd_resp_set_type(req, "application/json"); - - cJSON *config = cJSON_CreateObject(); - cJSON_AddBoolToObject(config, "loadBalancingEnabled", enabled); - cJSON_AddNumberToObject(config, "loadBalancingCurrentLimit", currentLimit); - - const char *json_str = cJSON_Print(config); - httpd_resp_sendstr(req, json_str); - - ESP_LOGI(TAG, "Returned config: %s", json_str); - - free((void *)json_str); - cJSON_Delete(config); - return ESP_OK; -} - -// POST Handler: Atualiza configurações de load balancing -static esp_err_t loadbalancing_config_post_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - - if (len <= 0) { - ESP_LOGE(TAG, "Received empty POST body"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - - buf[len] = '\0'; - ESP_LOGI(TAG, "Received POST data: %s", buf); - - cJSON *json = cJSON_Parse(buf); - if (!json) { - ESP_LOGE(TAG, "Invalid JSON"); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - // Atualizar estado habilitado - cJSON *enabled_item = cJSON_GetObjectItem(json, "loadBalancingEnabled"); - if (enabled_item && cJSON_IsBool(enabled_item)) { - bool isEnabled = cJSON_IsTrue(enabled_item); - loadbalancer_set_enabled(isEnabled); - ESP_LOGI(TAG, "Updated loadBalancingEnabled to: %d", isEnabled); - } - - // Atualizar limite de corrente - cJSON *limit_item = cJSON_GetObjectItem(json, "loadBalancingCurrentLimit"); - if (limit_item && cJSON_IsNumber(limit_item)) { - uint8_t currentLimit = (uint8_t)limit_item->valuedouble; - - // Validar intervalo - if (currentLimit < 6 || currentLimit > 100) { - ESP_LOGW(TAG, "Rejected invalid currentLimit: %d", currentLimit); - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid currentLimit (must be 6-100)"); - return ESP_FAIL; - } - - esp_err_t err = load_balancing_set_max_grid_current(currentLimit); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Failed to save currentLimit: %s", esp_err_to_name(err)); - cJSON_Delete(json); - httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Failed to save setting"); - return ESP_FAIL; - } - - ESP_LOGI(TAG, "Updated loadBalancingCurrentLimit to: %d", currentLimit); - } - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Load balancing settings updated successfully"); - return ESP_OK; -} - -// Registro dos handlers na API HTTP -void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx) { - // GET - httpd_uri_t get_uri = { - .uri = "/api/v1/config/loadbalancing", - .method = HTTP_GET, - .handler = loadbalancing_config_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - // POST - httpd_uri_t post_uri = { - .uri = "/api/v1/config/loadbalancing", - .method = HTTP_POST, - .handler = loadbalancing_config_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/loadbalancing_settings_api.c === - - -// === Início de: components/rest_api/src/evse_settings_api.c === -// ========================= -// evse_settings_api.c -// ========================= -#include "evse_settings_api.h" -#include "evse_api.h" -#include "esp_log.h" -#include "cJSON.h" - -static const char *TAG = "evse_settings_api"; - -static esp_err_t config_settings_get_handler(httpd_req_t *req) { - httpd_resp_set_type(req, "application/json"); - cJSON *config = cJSON_CreateObject(); - cJSON_AddNumberToObject(config, "currentLimit", evse_get_max_charging_current()); - cJSON_AddNumberToObject(config, "temperatureLimit", evse_get_temp_threshold()); - const char *json_str = cJSON_Print(config); - httpd_resp_sendstr(req, json_str); - free((void *)json_str); - cJSON_Delete(config); - return ESP_OK; -} - -static esp_err_t config_settings_post_handler(httpd_req_t *req) { - char buf[512]; - int len = httpd_req_recv(req, buf, sizeof(buf) - 1); - if (len <= 0) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Empty body"); - return ESP_FAIL; - } - buf[len] = '\0'; - cJSON *json = cJSON_Parse(buf); - if (!json) { - httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Invalid JSON"); - return ESP_FAIL; - } - - cJSON *current = cJSON_GetObjectItem(json, "currentLimit"); - if (current) evse_set_max_charging_current(current->valueint); - cJSON *temp = cJSON_GetObjectItem(json, "temperatureLimit"); - if (temp) evse_set_temp_threshold(temp->valueint); - - cJSON_Delete(json); - httpd_resp_sendstr(req, "Configurações atualizadas com sucesso"); - return ESP_OK; -} - -void register_evse_settings_handlers(httpd_handle_t server, void *ctx) { - httpd_uri_t get_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_GET, - .handler = config_settings_get_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &get_uri); - - httpd_uri_t post_uri = { - .uri = "/api/v1/config/settings", - .method = HTTP_POST, - .handler = config_settings_post_handler, - .user_ctx = ctx - }; - httpd_register_uri_handler(server, &post_uri); -} - -// === Fim de: components/rest_api/src/evse_settings_api.c === - - -// === Início de: components/rest_api/include/dashboard_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra o handler da dashboard (status geral do sistema) - */ -void register_dashboard_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/dashboard_api.h === - - -// === Início de: components/rest_api/include/static_file_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra o handler para servir arquivos estáticos da web (SPA) - */ -void register_static_file_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/static_file_api.h === - - -// === Início de: components/rest_api/include/network_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de configuração Wi-Fi e MQTT - */ -void register_network_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/network_api.h === - - -// === Início de: components/rest_api/include/auth_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de autenticação e gerenciamento de usuários - */ -void register_auth_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/auth_api.h === - - -// === Início de: components/rest_api/include/loadbalancing_settings_api.h === -// ========================= -// loadbalancing_settings_api.h -// ========================= - -#ifndef LOADBALANCING_SETTINGS_API_H -#define LOADBALANCING_SETTINGS_API_H - -#include "esp_err.h" -#include "esp_http_server.h" - -// Função para registrar os manipuladores de URI para as configurações de load balancing e solar -void register_loadbalancing_settings_handlers(httpd_handle_t server, void *ctx); - -#endif // LOADBALANCING_SETTINGS_API_H - -// === Fim de: components/rest_api/include/loadbalancing_settings_api.h === - - -// === Início de: components/rest_api/include/rest_main.h === -#pragma once - -#include -#include - -#define SCRATCH_BUFSIZE (10240) - -typedef struct rest_server_context { - char base_path[ESP_VFS_PATH_MAX + 1]; - char scratch[SCRATCH_BUFSIZE]; -} rest_server_context_t; - -esp_err_t rest_server_init(const char *base_path); - -// === Fim de: components/rest_api/include/rest_main.h === - - -// === Início de: components/rest_api/include/meters_settings_api.h === -// ========================= -// meters_settings_api.h -// ========================= - -#ifndef METERS_SETTINGS_API_H -#define METERS_SETTINGS_API_H - -#include "esp_err.h" -#include "esp_http_server.h" - -// Função para registrar os manipuladores de URI para as configurações dos contadores -void register_meters_settings_handlers(httpd_handle_t server, void *ctx); - -#endif // METERS_SETTINGS_API_H - -// === Fim de: components/rest_api/include/meters_settings_api.h === - - -// === Início de: components/rest_api/include/ocpp_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers da configuração e status do OCPP - */ -void register_ocpp_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/ocpp_api.h === - - -// === Início de: components/rest_api/include/evse_settings_api.h === -#pragma once - -#ifdef __cplusplus -extern "C" { -#endif - -#include "esp_http_server.h" - -/** - * @brief Registra os handlers de configuração elétrica e limites de carregamento - */ -void register_evse_settings_handlers(httpd_handle_t server, void *ctx); - -#ifdef __cplusplus -} -#endif - -// === Fim de: components/rest_api/include/evse_settings_api.h === - - -// === Início de: components/network/src/wifi_2.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_mac.h" -#include "nvs.h" -#include "mdns.h" - -#include "wifi.h" - - -#include "nvs_flash.h" -#include - -#define WIFI_STORAGE_NAMESPACE "wifi_config" - - - -#define TAG "wifi" -#define AP_SSID "plx-%02x%02x%02x" -#define MDNS_HOSTNAME "plx%02x" - -#define NVS_NAMESPACE "wifi" - -static nvs_handle_t nvs; -static esp_netif_t *ap_netif; -EventGroupHandle_t wifi_event_group; - -// -// Event handler para modo AP -// -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - if (event_base == WIFI_EVENT) { - switch (event_id) { - case WIFI_EVENT_AP_STACONNECTED: { - wifi_event_ap_staconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " conectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - case WIFI_EVENT_AP_STADISCONNECTED: { - wifi_event_ap_stadisconnected_t *event = event_data; - ESP_LOGI(TAG, "STA " MACSTR " desconectou, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - break; - } - } - } -} - -// -// Iniciar o AP com SSID baseado no MAC -// -void wifi_ap_start(void) -{ - ESP_LOGI(TAG, "Iniciando AP"); - - ESP_ERROR_CHECK(esp_wifi_stop()); - - wifi_config_t ap_config = { - .ap = { - .ssid = "", - .ssid_len = 0, - .channel = 1, - .password = "", - .max_connection = 4, - .authmode = WIFI_AUTH_OPEN - } - }; - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - snprintf((char *)ap_config.ap.ssid, sizeof(ap_config.ap.ssid), AP_SSID, mac[3], mac[4], mac[5]); - - ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); - ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config)); - ESP_ERROR_CHECK(esp_wifi_start()); - - xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT); -} - -// -// Inicializar Wi-Fi em modo AP -// -void wifi_ini(void) -{ - ESP_LOGI(TAG, "Inicializando Wi-Fi (modo AP)"); - - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - wifi_event_group = xEventGroupCreate(); - - ESP_ERROR_CHECK(esp_netif_init()); - /* - if (!esp_event_loop_is_running()) { - ESP_ERROR_CHECK(esp_event_loop_create_default()); - }*/ - - ap_netif = esp_netif_create_default_wifi_ap(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - uint8_t mac[6]; - ESP_ERROR_CHECK(esp_read_mac(mac, ESP_MAC_WIFI_SOFTAP)); - char hostname[16]; - snprintf(hostname, sizeof(hostname), MDNS_HOSTNAME, mac[5]); - - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(hostname)); - ESP_ERROR_CHECK(mdns_instance_name_set("EVSE Controller")); - - wifi_ap_start(); -} - -esp_netif_t *wifi_get_ap_netif(void) -{ - return ap_netif; -} - -esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) { - - return ESP_OK; -} - -void wifi_get_ssid(char *value) { - // Your implementation here -} - -void wifi_get_password(char *value) { - // Your implementation here -} - -bool wifi_get_enabled(void) -{ - return true; -} - -// === Fim de: components/network/src/wifi_2.c === - - -// === Início de: components/network/src/wifi.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/event_groups.h" -#include "esp_log.h" -#include "esp_wifi.h" -#include "esp_event.h" -#include "esp_netif.h" -#include "esp_mac.h" -#include "nvs.h" -#include "mdns.h" - -#include "wifi.h" - -#define AP_SSID "plx-%02x%02x%02x" - -#define MDNS_SSID "plx%02x" - -#define NVS_NAMESPACE "wifi" -#define NVS_ENABLED "enabled" -#define NVS_SSID "ssid" -#define NVS_PASSWORD "password" - -static const char *TAG = "wifi"; - -static nvs_handle_t nvs; - -static esp_netif_t *sta_netif; - -static esp_netif_t *ap_netif; - -EventGroupHandle_t wifi_event_group; - -static void event_handler(void *arg, esp_event_base_t event_base, int32_t event_id, void *event_data) -{ - ESP_LOGI(TAG, "event_handler"); - - if (event_base == WIFI_EVENT) - { - if (event_id == WIFI_EVENT_AP_STACONNECTED) - { - ESP_LOGI(TAG, "STA connected"); - wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *)event_data; - ESP_LOGI(TAG, "WiFi AP " MACSTR " join, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - } - if (event_id == WIFI_EVENT_AP_STADISCONNECTED) - { - ESP_LOGI(TAG, "AP STA disconnected"); - wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *)event_data; - ESP_LOGI(TAG, "WiFi AP " MACSTR " leave, AID=%d", MAC2STR(event->mac), event->aid); - xEventGroupClearBits(wifi_event_group, WIFI_AP_CONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_AP_DISCONNECTED_BIT); - } - if (event_id == WIFI_EVENT_STA_DISCONNECTED) - { - ESP_LOGI(TAG, "STA disconnected"); - xEventGroupClearBits(wifi_event_group, WIFI_STA_CONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT); - esp_wifi_connect(); - } - if (event_id == WIFI_EVENT_STA_START) - { - ESP_LOGI(TAG, "STA start"); - esp_wifi_connect(); - } - } - else if (event_base == IP_EVENT) - { - ESP_LOGI(TAG, "event_base == IP_EVENT"); - - if (event_id == IP_EVENT_STA_GOT_IP || event_id == IP_EVENT_GOT_IP6) - { - if (event_id == IP_EVENT_STA_GOT_IP) - { - ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data; - ESP_LOGI(TAG, "WiFi STA got ip: " IPSTR, IP2STR(&event->ip_info.ip)); - } - else - { - ip_event_got_ip6_t *event = (ip_event_got_ip6_t *)event_data; - ESP_LOGI(TAG, "WiFi STA got ip6: " IPV6STR, IPV62STR(event->ip6_info.ip)); - } - xEventGroupClearBits(wifi_event_group, WIFI_STA_DISCONNECTED_BIT); - xEventGroupSetBits(wifi_event_group, WIFI_STA_CONNECTED_BIT); - } - } -} - -static void sta_set_config(void) -{ - - ESP_LOGI(TAG, "sta_set_config"); - - if (wifi_get_enabled()) - { - wifi_config_t wifi_config = { - .sta = { - .pmf_cfg = { - .capable = true, - .required = false}}}; - wifi_get_ssid((char *)wifi_config.sta.ssid); - wifi_get_password((char *)wifi_config.sta.password); - - esp_wifi_set_mode(WIFI_MODE_STA); - esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config); - } -} - -static void ap_set_config(void) -{ - - ESP_LOGI(TAG, "ap_set_config"); - - wifi_config_t wifi_ap_config = { - .ap = { - .max_connection = 1, - .authmode = WIFI_AUTH_OPEN}}; - uint8_t mac[6]; - esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - sprintf((char *)wifi_ap_config.ap.ssid, AP_SSID, mac[3], mac[4], mac[5]); - - wifi_config_t wifi_sta_config = {0}; - - esp_wifi_set_mode(WIFI_MODE_APSTA); - esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_ap_config); - esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_sta_config); -} - -static void sta_try_start(void) -{ - - ESP_LOGI(TAG, "sta_try_start"); - - sta_set_config(); - if (wifi_get_enabled()) - { - ESP_LOGI(TAG, "Starting STA"); - esp_wifi_start(); - xEventGroupSetBits(wifi_event_group, WIFI_STA_MODE_BIT); - } -} - -void wifi_ini(void) -{ - - - ESP_LOGI(TAG, "Wifi init"); - - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - wifi_event_group = xEventGroupCreate(); - - wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); - - ap_netif = esp_netif_create_default_wifi_ap(); - sta_netif = esp_netif_create_default_wifi_sta(); - - ESP_ERROR_CHECK(esp_wifi_init(&cfg)); - - ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL)); - - char chargeid[6]; - uint8_t mac[6]; - esp_wifi_get_mac(ESP_IF_WIFI_AP, mac); - sprintf((char *)chargeid, MDNS_SSID, mac[5]); - - ESP_ERROR_CHECK(mdns_init()); - ESP_ERROR_CHECK(mdns_hostname_set(chargeid)); - ESP_ERROR_CHECK(mdns_instance_name_set("EVSE controller")); - - sta_try_start(); - -} - -esp_netif_t *wifi_get_sta_netif(void) -{ - return sta_netif; -} - -esp_netif_t *wifi_get_ap_netif(void) -{ - return ap_netif; -} - -esp_err_t wifi_set_config(bool enabled, const char *ssid, const char *password) -{ - - ESP_LOGI(TAG, "Wifi set config"); - - if (enabled) - { - if (ssid == NULL || strlen(ssid) == 0) - { - size_t len = 0; - nvs_get_str(nvs, NVS_SSID, NULL, &len); - if (len <= 1) - { - ESP_LOGE(TAG, "Required SSID"); - return ESP_ERR_INVALID_ARG; - } - } - } - - if (ssid != NULL && strlen(ssid) > 32) - { - ESP_LOGE(TAG, "SSID out of range"); - return ESP_ERR_INVALID_ARG; - } - - if (password != NULL && strlen(password) > 32) - { - ESP_LOGE(TAG, "Password out of range"); - return ESP_ERR_INVALID_ARG; - } - - nvs_set_u8(nvs, NVS_ENABLED, enabled); - if (ssid != NULL) - { - nvs_set_str(nvs, NVS_SSID, ssid); - } - if (password != NULL) - { - nvs_set_str(nvs, NVS_PASSWORD, password); - } - nvs_commit(nvs); - - ESP_LOGI(TAG, "Stopping AP/STA"); - xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT); - esp_wifi_stop(); - - sta_try_start(); - - return ESP_OK; -} - -uint16_t wifi_scan(wifi_scan_ap_t *scan_aps) -{ - - ESP_LOGI(TAG, "wifi_scan"); - - uint16_t number = WIFI_SCAN_SCAN_LIST_SIZE; - wifi_ap_record_t ap_info[WIFI_SCAN_SCAN_LIST_SIZE]; - uint16_t ap_count = 0; - memset(ap_info, 0, sizeof(ap_info)); - - esp_wifi_scan_start(NULL, true); - esp_wifi_scan_get_ap_records(&number, ap_info); - esp_wifi_scan_get_ap_num(&ap_count); - - ESP_LOGI(TAG, "wifi_scan --- %d", ap_count); - - for (int i = 0; (i < WIFI_SCAN_SCAN_LIST_SIZE) && (i < ap_count); i++) - { - - ESP_LOGI(TAG, "wifi_scan ---"); - - strcpy(scan_aps[i].ssid, (const char *)ap_info[i].ssid); - scan_aps[i].rssi = ap_info[i].rssi; - scan_aps[i].auth = ap_info[i].authmode != WIFI_AUTH_OPEN; - } - - return ap_count; -} - -bool wifi_get_enabled(void) -{ - uint8_t value = false; - nvs_get_u8(nvs, NVS_ENABLED, &value); - return value; -} - -void wifi_get_ssid(char *value) -{ - size_t len = 32; - value[0] = '\0'; - nvs_get_str(nvs, NVS_SSID, value, &len); -} - -void wifi_get_password(char *value) -{ - size_t len = 64; - value[0] = '\0'; - nvs_get_str(nvs, NVS_PASSWORD, value, &len); -} - -void wifi_ap_start(void) -{ - ESP_LOGI(TAG, "Starting AP"); - - xEventGroupClearBits(wifi_event_group, WIFI_STA_MODE_BIT); - esp_wifi_stop(); - - ap_set_config(); - esp_wifi_start(); - - xEventGroupSetBits(wifi_event_group, WIFI_AP_MODE_BIT); -} - -void wifi_ap_stop(void) -{ - ESP_LOGI(TAG, "Stopping AP"); - xEventGroupClearBits(wifi_event_group, WIFI_AP_MODE_BIT); - esp_wifi_stop(); - - sta_try_start(); -} - -bool wifi_is_ap(void) -{ - wifi_mode_t mode; - esp_wifi_get_mode(&mode); - return mode == WIFI_MODE_APSTA; -} - -// === Fim de: components/network/src/wifi.c === - - -// === Início de: components/network/include/wifi.h === -#ifndef WIFI_H_ -#define WIFI_H_ - -#include -#include "freertos/FreeRTOS.h" -#include "freertos/event_groups.h" -#include "esp_err.h" -#include "esp_netif.h" - -#define WIFI_SCAN_SCAN_LIST_SIZE 10 - -#define WIFI_AP_CONNECTED_BIT BIT0 -#define WIFI_AP_DISCONNECTED_BIT BIT1 -#define WIFI_STA_CONNECTED_BIT BIT2 -#define WIFI_STA_DISCONNECTED_BIT BIT3 -#define WIFI_AP_MODE_BIT BIT4 -#define WIFI_STA_MODE_BIT BIT5 - -typedef struct -{ - char ssid[32]; - int rssi; - bool auth; -} wifi_scan_ap_t; - -/** - * @brief WiFi event group WIFI_AP_CONNECTED_BIT | WIFI_AP_DISCONNECTED_BIT | WIFI_STA_CONNECTED_BIT | WIFI_STA_DISCONNECTED_BIT | WIFI_AP_MODE_BIT | WIFI_STA_MODE_BIT - * - */ -extern EventGroupHandle_t wifi_event_group; - -/** - * @brief Initialize WiFi - * - */ -void wifi_ini(void); - -/** - * @brief Return WiFi STA network interface - * - * @return esp_netif_t* - */ -esp_netif_t* wifi_get_sta_netif(void); - -/** - * @brief Return WiFi AP network interface - * - * @return esp_netif_t* - */ -esp_netif_t* wifi_get_ap_netif(void); - -/** - * @brief Set WiFi config - * - * @param enabled - * @param ssid NULL value will be skiped - * @param password NULL value will be skiped - * @return esp_err_t - */ -esp_err_t wifi_set_config(bool enabled, const char* ssid, const char* password); - -/** - * @brief Get WiFi STA enabled, stored in NVS - * - * @return true - * @return false - */ -bool wifi_get_enabled(void); - -/** - * @brief Scan for AP - * - * @param scan_aps array with length WIFI_SCAN_SCAN_LIST_SIZE - * @return uint16_t number of available AP - */ -uint16_t wifi_scan(wifi_scan_ap_t *scan_aps); - -/** - * @brief Get WiFi STA ssid, string length 32, stored in NVS - * - * @param value - */ -void wifi_get_ssid(char* value); - -/** - * @brief Get WiFi STA password, string length 32, stored in NVS - * - * @param value - */ -void wifi_get_password(char* value); - -/** - * @brief Start WiFi AP mode - * - */ -void wifi_ap_start(void); - -/** - * @brief Stop WiFi AP mode - * - */ -void wifi_ap_stop(void); - -#endif /* WIFI_H_ */ - -// === Fim de: components/network/include/wifi.h === - - -// === Início de: components/peripherals/src/ac_relay.c === -#include "esp_log.h" -#include "driver/gpio.h" - -#include "ac_relay.h" -#include "board_config.h" - -static const char* TAG = "ac_relay"; - -/** - * @brief Initialize the AC relay GPIO. - * - * Configures the specified GPIO pin as an output and sets its initial state to OFF (low). - */ -void ac_relay_init(void) -{ - gpio_config_t conf = { - .pin_bit_mask = BIT64(board_config.ac_relay_gpio), - .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_DISABLE, ///< Disabled unless required - .pull_up_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - - esp_err_t ret = gpio_config(&conf); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to configure GPIO (error: %s)", esp_err_to_name(ret)); - return; - } - - gpio_set_level(board_config.ac_relay_gpio, false); ///< Ensure relay starts OFF - ESP_LOGI(TAG, "AC relay initialized. Pin: %d", board_config.ac_relay_gpio); -} - -/** - * @brief Set the state of the AC relay. - * - * @param state True to turn the relay ON, False to turn it OFF. - */ -void ac_relay_set_state(bool state) -{ - ESP_LOGI(TAG, "Setting AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, state); - - esp_err_t ret = gpio_set_level(board_config.ac_relay_gpio, state); - if (ret != ESP_OK) { - ESP_LOGE(TAG, "Failed to set GPIO level (error: %s)", esp_err_to_name(ret)); - } -} - -/** - * @brief Get the current state of the AC relay. - * - * @return true if the relay is ON, false if OFF. - */ -bool ac_relay_get_state(void) -{ - int level = gpio_get_level(board_config.ac_relay_gpio); - ESP_LOGD(TAG, "Current AC relay state: Pin: %d, State: %d", board_config.ac_relay_gpio, level); - return level; -} - -// === Fim de: components/peripherals/src/ac_relay.c === - - -// === Início de: components/peripherals/src/ntc_sensor.c === -#include -#include -#include "freertos/task.h" -#include "esp_log.h" -#include "ntc_sensor.h" -#include "ntc_driver.h" - -#include "adc.h" - -static const char *TAG = "temp_sensor"; - -#define MEASURE_PERIOD 15000 // 10s - -static float temp = 0.0; - -static ntc_device_handle_t ntc = NULL; - -static portMUX_TYPE temp_mux = portMUX_INITIALIZER_UNLOCKED; - -static void ntc_sensor_task_func(void *param) { - float t; - while (true) { - if (ntc_dev_get_temperature(ntc, &t) == ESP_OK) { - portENTER_CRITICAL(&temp_mux); - temp = t; - portEXIT_CRITICAL(&temp_mux); - } - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); - } -} - -float ntc_temp_sensor(void) { - float t; - portENTER_CRITICAL(&temp_mux); - t = temp; - portEXIT_CRITICAL(&temp_mux); - return t; -} - -void ntc_sensor_init(void) -{ - - ESP_LOGI(TAG, "ntc_sensor_init"); - - // Select the NTC sensor and initialize the hardware parameters - ntc_config_t ntc_config = { - .b_value = 3950, - .r25_ohm = 10000, - .fixed_ohm = 4700, - .vdd_mv = 3300, - .circuit_mode = CIRCUIT_MODE_NTC_GND, - .atten = ADC_ATTEN_DB_12, - .channel = ADC_CHANNEL_0, - .unit = ADC_UNIT_1}; - - // Create the NTC Driver and Init ADC - // ntc_device_handle_t ntc = NULL; - // adc_oneshot_unit_handle_t adc_handle = NULL; - ESP_ERROR_CHECK(ntc_dev_create(&ntc_config, &ntc, &adc_handle)); - ESP_ERROR_CHECK(ntc_dev_get_adc_handle(ntc, &adc_handle)); - - xTaskCreate(ntc_sensor_task_func, "ntc_sensor_task", 5 * 1024, NULL, 3, NULL); -} - -// === Fim de: components/peripherals/src/ntc_sensor.c === - - -// === Início de: components/peripherals/src/proximity.c === -#include "esp_log.h" - -#include "proximity.h" -#include "board_config.h" -#include "adc.h" - -static const char *TAG = "proximity"; - -void proximity_init(void) -{ - if (board_config.proximity) - { - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12}; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.proximity_adc_channel, &config)); - } -} - -uint8_t proximity_get_max_current(void) -{ - int voltage; - adc_oneshot_read(adc_handle, board_config.proximity_adc_channel, &voltage); - adc_cali_raw_to_voltage(adc_cali_handle, voltage, &voltage); - - ESP_LOGI(TAG, "Measured: %dmV", voltage); - - uint8_t current; - - if (voltage >= board_config.proximity_down_threshold_8) - { - current = 8; - } - else if (voltage >= board_config.proximity_down_threshold_10) - { - current = 10; - } - - else if (voltage >= board_config.proximity_down_threshold_13) - { - current = 13; - } - else if (voltage >= board_config.proximity_down_threshold_20) - { - current = 20; - } - - else if (voltage >= board_config.proximity_down_threshold_25) - { - current = 25; - } - else if (voltage >= board_config.proximity_down_threshold_32) - { - current = 32; - } - else - { - current = 32; - } - - ESP_LOGI(TAG, "Max current: %dA", current); - - return current; -} -// === Fim de: components/peripherals/src/proximity.c === - - -// === Início de: components/peripherals/src/buzzer.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "board_config.h" -#include "buzzer.h" -#include "evse_api.h" - -static gpio_num_t buzzer_gpio = GPIO_NUM_NC; -static evse_state_t last_buzzer_state = -1; -static QueueHandle_t buzzer_queue = NULL; - -void buzzer_on(void) { - if (buzzer_gpio != GPIO_NUM_NC) - gpio_set_level(buzzer_gpio, 1); -} - -void buzzer_off(void) { - if (buzzer_gpio != GPIO_NUM_NC) - gpio_set_level(buzzer_gpio, 0); -} - -// ---------------------- -// Padrões de Buzzer -// ---------------------- - -typedef struct { - uint16_t on_ms; - uint16_t off_ms; -} buzzer_pattern_step_t; - -typedef enum { - BUZZER_PATTERN_NONE = 0, - BUZZER_PATTERN_PLUGGED, - BUZZER_PATTERN_UNPLUGGED, - BUZZER_PATTERN_CHARGING, -} buzzer_pattern_id_t; - -static const buzzer_pattern_step_t pattern_plugged[] = { - {100, 100}, {200, 0} -}; - -static const buzzer_pattern_step_t pattern_unplugged[] = { - {150, 150}, {150, 150}, {150, 0} -}; - -static const buzzer_pattern_step_t pattern_charging[] = { - {80, 150}, {100, 120}, {120, 100}, {140, 0} -}; - -// ---------------------- -// Executor de padrões -// ---------------------- - -static void buzzer_execute_pattern(buzzer_pattern_id_t pattern_id) { - const buzzer_pattern_step_t *pattern = NULL; - size_t length = 0; - - switch (pattern_id) { - case BUZZER_PATTERN_PLUGGED: - pattern = pattern_plugged; - length = sizeof(pattern_plugged) / sizeof(pattern_plugged[0]); - break; - case BUZZER_PATTERN_UNPLUGGED: - pattern = pattern_unplugged; - length = sizeof(pattern_unplugged) / sizeof(pattern_unplugged[0]); - break; - case BUZZER_PATTERN_CHARGING: - pattern = pattern_charging; - length = sizeof(pattern_charging) / sizeof(pattern_charging[0]); - break; - default: - return; - } - - for (size_t i = 0; i < length; i++) { - buzzer_on(); - vTaskDelay(pdMS_TO_TICKS(pattern[i].on_ms)); - buzzer_off(); - if (pattern[i].off_ms > 0) - vTaskDelay(pdMS_TO_TICKS(pattern[i].off_ms)); - } -} - -// ---------------------- -// Task que toca o buzzer -// ---------------------- - -static void buzzer_worker_task(void *arg) { - buzzer_pattern_id_t pattern_id; - - while (true) { - if (xQueueReceive(buzzer_queue, &pattern_id, portMAX_DELAY)) { - //buzzer_execute_pattern(pattern_id); - } - } -} - -// ---------------------- -// Task de monitoramento -// ---------------------- - -static void buzzer_monitor_task(void *arg) { - while (true) { - evse_state_t current = evse_get_state(); - - if (current != last_buzzer_state) { - buzzer_pattern_id_t pattern_id = BUZZER_PATTERN_NONE; - - switch (current) { - case EVSE_STATE_A: - if (last_buzzer_state != EVSE_STATE_A) - pattern_id = BUZZER_PATTERN_UNPLUGGED; - break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - if (last_buzzer_state != EVSE_STATE_B1 && last_buzzer_state != EVSE_STATE_B2) - pattern_id = BUZZER_PATTERN_PLUGGED; - break; - case EVSE_STATE_C2: - case EVSE_STATE_D2: - if (last_buzzer_state != EVSE_STATE_C2 && last_buzzer_state != EVSE_STATE_D2) - pattern_id = BUZZER_PATTERN_CHARGING; - break; - default: - break; - } - - if (pattern_id != BUZZER_PATTERN_NONE) { - xQueueSend(buzzer_queue, &pattern_id, 0); // Não bloqueia - } - - last_buzzer_state = current; - } - - vTaskDelay(pdMS_TO_TICKS(100)); - } -} - -// ---------------------- -// Inicialização -// ---------------------- - -void buzzer_init(void) { - if (board_config.buzzer) { - buzzer_gpio = board_config.buzzer_gpio; - - gpio_config_t io_conf = { - .pin_bit_mask = BIT64(buzzer_gpio), - .mode = GPIO_MODE_OUTPUT, - .pull_down_en = GPIO_PULLDOWN_ENABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .intr_type = GPIO_INTR_DISABLE - }; - gpio_config(&io_conf); - gpio_set_level(buzzer_gpio, 0); - } - - buzzer_queue = xQueueCreate(4, sizeof(buzzer_pattern_id_t)); - - xTaskCreate(buzzer_monitor_task, "buzzer_monitor", 2048, NULL, 3, NULL); - xTaskCreate(buzzer_worker_task, "buzzer_worker", 2048, NULL, 3, NULL); -} - -// === Fim de: components/peripherals/src/buzzer.c === - - -// === Início de: components/peripherals/src/ds18x20.h === -/* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#ifndef _DS18X20_H -#define _DS18X20_H - -#include -#include "onewire.h" - -typedef onewire_addr_t ds18x20_addr_t; - -/** An address value which can be used to indicate "any device on the bus" */ -#define DS18X20_ANY ONEWIRE_NONE - -/** Family ID (lower address byte) of DS18B20 sensors */ -#define DS18B20_FAMILY_ID 0x28 - -/** Family ID (lower address byte) of DS18S20 sensors */ -#define DS18S20_FAMILY_ID 0x10 - -/** - * @brief Find the addresses of all ds18x20 devices on the bus. - * - * Scans the bus for all devices and places their addresses in the supplied - * array. If there are more than `addr_count` devices on the bus, only the - * first `addr_count` are recorded. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A pointer to an array of ::ds18x20_addr_t values. - * This will be populated with the addresses of the found - * devices. - * @param addr_count Number of slots in the `addr_list` array. At most this - * many addresses will be returned. - * @param found The number of devices found. Note that this may be less - * than, equal to, or more than `addr_count`, depending on - * how many ds18x20 devices are attached to the bus. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, size_t *found); - -/** - * @brief Tell one or more sensors to perform a temperature measurement and - * conversion (CONVERT_T) operation. - * - * This operation can take up to 750ms to complete. - * - * If `wait=true`, this routine will automatically drive the pin high for the - * necessary 750ms after issuing the command to ensure parasitically-powered - * devices have enough power to perform the conversion operation (for - * non-parasitically-powered devices, this is not necessary but does not - * hurt). If `wait=false`, this routine will drive the pin high, but will - * then return immediately. It is up to the caller to wait the requisite time - * and then depower the bus using onewire_depower() or by issuing another - * command once conversion is done. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device on the bus. This can be set - * to ::DS18X20_ANY to send the command to all devices on the bus - * at the same time. - * @param wait Whether to wait for the necessary 750ms for the ds18x20 to - * finish performing the conversion before returning to the - * caller (You will normally want to do this). - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait); - -/** - * @brief Read the value from the last CONVERT_T operation. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18b20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation (ds18s20 version). - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Read the value from the last CONVERT_T operation for multiple devices. - * - * This should be called after ds18x20_measure() to fetch the result of the - * temperature measurement. - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** Perform a ds18x20_measure() followed by ds18s20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18s20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18b20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** Perform a ds18x20_measure() followed by ds18x20_read_temperature() - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param temperature The temperature in degrees Celsius - */ -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t *temperature); - -/** - * @brief Perform a ds18x20_measure() followed by ds18x20_read_temp_multi() - * - * @param pin The GPIO pin connected to the ds18x20 bus - * @param addr_list A list of addresses for devices to read. - * @param addr_count The number of entries in `addr_list`. - * @param result_list An array of int16_ts to hold the returned temperature - * values. It should have at least `addr_count` entries. - * - * @returns `ESP_OK` if all temperatures were fetched successfully - */ -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t *addr_list, size_t addr_count, int16_t *result_list); - -/** - * @brief Read the scratchpad data for a particular ds18x20 device. - * - * This is not generally necessary to do directly. It is done automatically - * as part of ds18x20_read_temperature(). - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to read. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 8-byte buffer to hold the read data. - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Write the scratchpad data for a particular ds18x20 device. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to write. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * @param buffer An 3-byte buffer to hold the data to write - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t *buffer); - -/** - * @brief Issue the copy scratchpad command, copying current scratchpad to - * EEPROM. - * - * @param pin The GPIO pin connected to the ds18x20 device - * @param addr The 64-bit address of the device to command. This can be set - * to ::DS18X20_ANY to read any device on the bus (but note - * that this will only work if there is exactly one device - * connected, or they will corrupt each others' transmissions) - * - * @returns `ESP_OK` if the command was successfully issued - */ -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr); - - -#endif /* _DS18X20_H */ -// === Fim de: components/peripherals/src/ds18x20.h === - - -// === Início de: components/peripherals/src/socket_lock.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "socket_lock.h" -#include "board_config.h" - -#define NVS_NAMESPACE "socket_lock" -#define NVS_OPERATING_TIME "op_time" -#define NVS_BREAK_TIME "break_time" -#define NVS_RETRY_COUNT "retry_count" -#define NVS_DETECTION_HIGH "detect_hi" - -#define OPERATING_TIME_MIN 100 -#define OPERATING_TIME_MAX 1000 -#define LOCK_DELAY 500 - -#define LOCK_BIT BIT0 -#define UNLOCK_BIT BIT1 -#define REPEAT_LOCK_BIT BIT2 -#define REPEAT_UNLOCK_BIT BIT3 - -static const char* TAG = "socket_lock"; - -static nvs_handle_t nvs; - -static uint16_t operating_time = 300; - -static uint16_t break_time = 1000; - -static bool detection_high; - -static uint8_t retry_count = 5; - -static socket_lock_status_t status; - -static TaskHandle_t socket_lock_task; - -static bool is_locked(void) -{ - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - - vTaskDelay(pdMS_TO_TICKS(board_config.socket_lock_detection_delay)); - - return gpio_get_level(board_config.socket_lock_detection_gpio) == detection_high; -} - -bool socket_lock_is_locked_state(void) -{ - return is_locked(); -} - -static void socket_lock_task_func(void* param) -{ - uint32_t notification; - - TickType_t previous_tick = 0; - uint8_t attempt = 0; - - while (true) { - if (xTaskNotifyWait(0x00, 0xff, ¬ification, portMAX_DELAY)) { - if (notification & (LOCK_BIT | UNLOCK_BIT)) { - attempt = retry_count; - } - - if (notification & (UNLOCK_BIT | REPEAT_UNLOCK_BIT)) { - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 1); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (!is_locked()) { - ESP_LOGI(TAG, "Unlock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not unlocked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_UNLOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not unlocked"); - status = SOCKED_LOCK_STATUS_UNLOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } else if (notification & (LOCK_BIT | REPEAT_LOCK_BIT)) { - if (notification & LOCK_BIT) { - vTaskDelay(pdMS_TO_TICKS(LOCK_DELAY)); //delay before first lock attempt - } - gpio_set_level(board_config.socket_lock_a_gpio, 1); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - vTaskDelay(pdMS_TO_TICKS(operating_time)); - - if (is_locked()) { - ESP_LOGI(TAG, "Lock OK"); - status = SOCKED_LOCK_STATUS_IDLE; - } else { - if (attempt > 1) { - ESP_LOGW(TAG, "Not locked yet, repeating..."); - attempt--; - xTaskNotify(socket_lock_task, REPEAT_LOCK_BIT, eSetBits); - } else { - ESP_LOGE(TAG, "Not locked"); - status = SOCKED_LOCK_STATUS_LOCKING_FAIL; - } - } - - gpio_set_level(board_config.socket_lock_a_gpio, 0); - gpio_set_level(board_config.socket_lock_b_gpio, 0); - } - - TickType_t delay_tick = xTaskGetTickCount() - previous_tick; - if (delay_tick < pdMS_TO_TICKS(break_time)) { - vTaskDelay(pdMS_TO_TICKS(break_time) - delay_tick); - } - previous_tick = xTaskGetTickCount(); - } - } -} - -void socket_lock_init(void) -{ - if (board_config.socket_lock) { - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - nvs_get_u16(nvs, NVS_OPERATING_TIME, &operating_time); - - nvs_get_u16(nvs, NVS_BREAK_TIME, &break_time); - - nvs_get_u8(nvs, NVS_RETRY_COUNT, &retry_count); - - uint8_t u8; - if (nvs_get_u8(nvs, NVS_DETECTION_HIGH, &u8) == ESP_OK) { - detection_high = u8; - } - - gpio_config_t io_conf = {}; - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_a_gpio) | BIT64(board_config.socket_lock_b_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pin_bit_mask = BIT64(board_config.socket_lock_detection_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - xTaskCreate(socket_lock_task_func, "socket_lock_task", 2 * 1024, NULL, 10, &socket_lock_task); - } -} - -bool socket_lock_is_detection_high(void) -{ - return detection_high; -} - -void socket_lock_set_detection_high(bool _detection_high) -{ - detection_high = _detection_high; - - nvs_set_u8(nvs, NVS_DETECTION_HIGH, detection_high); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_operating_time(void) -{ - return operating_time; -} - -esp_err_t socket_lock_set_operating_time(uint16_t _operating_time) -{ - if (_operating_time < OPERATING_TIME_MIN || _operating_time > OPERATING_TIME_MAX) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - operating_time = _operating_time; - nvs_set_u16(nvs, NVS_OPERATING_TIME, operating_time); - nvs_commit(nvs); - - return ESP_OK; -} - -uint8_t socket_lock_get_retry_count(void) -{ - return retry_count; -} - -void socket_lock_set_retry_count(uint8_t _retry_count) -{ - retry_count = _retry_count; - nvs_set_u8(nvs, NVS_RETRY_COUNT, retry_count); - nvs_commit(nvs); -} - -uint16_t socket_lock_get_break_time(void) -{ - return break_time; -} - -esp_err_t socket_lock_set_break_time(uint16_t _break_time) -{ - if (_break_time < board_config.socket_lock_min_break_time) { - ESP_LOGE(TAG, "Operating time out of range"); - return ESP_ERR_INVALID_ARG; - } - - break_time = _break_time; - nvs_set_u16(nvs, NVS_BREAK_TIME, break_time); - nvs_commit(nvs); - - return ESP_OK; -} - -void socket_lock_set_locked(bool locked) -{ - ESP_LOGI(TAG, "Set locked %d", locked); - - xTaskNotify(socket_lock_task, locked ? LOCK_BIT : UNLOCK_BIT, eSetBits); - status = SOCKED_LOCK_STATUS_OPERATING; -} - -socket_lock_status_t socket_lock_get_status(void) -{ - return status; -} -// === Fim de: components/peripherals/src/socket_lock.c === - - -// === Início de: components/peripherals/src/temp_sensor.c === -#include -#include -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" - -#include "temp_sensor.h" -#include "lm75a.h" - -#define MAX_SENSORS 5 -#define MEASURE_PERIOD 10000 // 10s -#define MEASURE_ERR_THRESHOLD 3 - -static const char *TAG = "temp_sensor"; - -static uint8_t sensor_count = 0; - -static int16_t low_temp = 0; - -static int high_temp = 0; - -static uint8_t measure_err_count = 0; - -static void temp_sensor_task_func(void *param) -{ - while (true) - { - high_temp = lm75a_read_temperature(0); - - vTaskDelay(pdMS_TO_TICKS(MEASURE_PERIOD)); - } -} - -void temp_sensor_init(void) -{ - - ESP_LOGW(TAG, "temp_sensor_init"); - - lm75a_init(); - - xTaskCreate(temp_sensor_task_func, "temp_sensor_task", 5 * 1024, NULL, 5, NULL); -} - -uint8_t temp_sensor_get_count(void) -{ - return sensor_count; -} - -int16_t temp_sensor_get_low(void) -{ - return low_temp; -} - -int temp_sensor_get_high(void) -{ - return high_temp; -} - -bool temp_sensor_is_error(void) -{ - return sensor_count == 0 || measure_err_count > MEASURE_ERR_THRESHOLD; -} -// === Fim de: components/peripherals/src/temp_sensor.c === - - -// === Início de: components/peripherals/src/aux_io.c === -#include -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "freertos/task.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "nvs.h" - -#include "aux_io.h" -#include "board_config.h" -#include "adc.h" - -#define MAX_AUX_IN 4 -#define MAX_AUX_OUT 4 -#define MAX_AUX_AIN 4 - -//static const char* TAG = "aux"; - -static int aux_in_count = 0; -static int aux_out_count = 0; -static int aux_ain_count = 0; - -static struct aux_gpio_s -{ - gpio_num_t gpio; - const char* name; -} aux_in[MAX_AUX_IN], aux_out[MAX_AUX_OUT]; - -static struct aux_adc_s -{ - adc_channel_t adc; - const char* name; -} aux_ain[MAX_AUX_AIN]; - - -void aux_init(void) -{ - // IN - - gpio_config_t io_conf = { - .mode = GPIO_MODE_INPUT, - .pull_up_en = GPIO_PULLDOWN_DISABLE, - .pull_down_en = GPIO_PULLDOWN_DISABLE, - .intr_type = GPIO_INTR_DISABLE, - .pin_bit_mask = 0 - }; - - if (board_config.aux_in_1) { - aux_in[aux_in_count].gpio = board_config.aux_in_1_gpio; - aux_in[aux_in_count].name = board_config.aux_in_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_1_gpio); - aux_in_count++; - } - - if (board_config.aux_in_2) { - aux_in[aux_in_count].gpio = board_config.aux_in_2_gpio; - aux_in[aux_in_count].name = board_config.aux_in_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_2_gpio); - aux_in_count++; - } - - if (board_config.aux_in_3) { - aux_in[aux_in_count].gpio = board_config.aux_in_3_gpio; - aux_in[aux_in_count].name = board_config.aux_in_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_3_gpio); - aux_in_count++; - } - - if (board_config.aux_in_4) { - aux_in[aux_in_count].gpio = board_config.aux_in_4_gpio; - aux_in[aux_in_count].name = board_config.aux_in_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_in_4_gpio); - aux_in_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // OUT - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = 0; - - if (board_config.aux_out_1) { - aux_out[aux_out_count].gpio = board_config.aux_out_1_gpio; - aux_out[aux_out_count].name = board_config.aux_out_1_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_1_gpio); - aux_out_count++; - } - - if (board_config.aux_out_2) { - aux_out[aux_out_count].gpio = board_config.aux_out_2_gpio; - aux_out[aux_out_count].name = board_config.aux_out_2_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_2_gpio); - aux_out_count++; - } - - if (board_config.aux_out_3) { - aux_out[aux_out_count].gpio = board_config.aux_out_3_gpio; - aux_out[aux_out_count].name = board_config.aux_out_3_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_3_gpio); - aux_out_count++; - } - - if (board_config.aux_out_4) { - aux_out[aux_out_count].gpio = board_config.aux_out_4_gpio; - aux_out[aux_out_count].name = board_config.aux_out_4_name; - io_conf.pin_bit_mask |= BIT64(board_config.aux_out_4_gpio); - aux_out_count++; - } - - if (io_conf.pin_bit_mask > 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - // AIN - - adc_oneshot_chan_cfg_t config = { - .bitwidth = ADC_BITWIDTH_DEFAULT, - .atten = ADC_ATTEN_DB_12 - }; - - if (board_config.aux_ain_1) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_1_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_1_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_1_adc_channel, &config)); - aux_ain_count++; - } - - if (board_config.aux_ain_2) { - aux_ain[aux_ain_count].adc = board_config.aux_ain_2_adc_channel; - aux_ain[aux_ain_count].name = board_config.aux_out_2_name; - ESP_ERROR_CHECK(adc_oneshot_config_channel(adc_handle, board_config.aux_ain_2_adc_channel, &config)); - aux_ain_count++; - } -} - -esp_err_t aux_read(const char* name, bool* value) -{ - for (int i = 0; i < aux_in_count; i++) { - if (strcmp(aux_in[i].name, name) == 0) { - *value = gpio_get_level(aux_in[i].gpio) == 1; - return ESP_OK; - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_write(const char* name, bool value) -{ - for (int i = 0; i < aux_out_count; i++) { - if (strcmp(aux_out[i].name, name) == 0) { - return gpio_set_level(aux_out[i].gpio, value); - } - } - return ESP_ERR_NOT_FOUND; -} - -esp_err_t aux_analog_read(const char* name, int* value) -{ - for (int i = 0; i < aux_ain_count; i++) { - if (strcmp(aux_ain[i].name, name) == 0) { - int raw = 0; - esp_err_t ret = adc_oneshot_read(adc_handle, aux_ain[i].adc, &raw); - if (ret == ESP_OK) { - return adc_cali_raw_to_voltage(adc_cali_handle, raw, value); - } else { - return ret; - } - } - } - return ESP_ERR_NOT_FOUND; -} -// === Fim de: components/peripherals/src/aux_io.c === - - -// === Início de: components/peripherals/src/lm75a.c === -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/queue.h" -#include "driver/gpio.h" -#include "driver/i2c_master.h" - -#define I2C_MASTER_NUM I2C_NUM_1 -#define I2C_MASTER_SCL_IO GPIO_NUM_22 // CONFIG_EXAMPLE_I2C_SCL /*!< gpio number for I2C master clock */ -#define I2C_MASTER_SDA_IO GPIO_NUM_21 // CONFIG_EXAMPLE_I2C_SDA /*!< gpio number for I2C master data */ -#define I2C_MASTER_FREQ_HZ 100000 // CONFIG_I2C_TRANS_SPEED /*!< I2C master clock frequency */ -#define I2C_MASTER_TX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define I2C_MASTER_RX_BUF_DISABLE 0 /*!< I2C master do not need buffer */ -#define LM75A_SLAVE_ADDR 0x48 // CONFIG_LM75A_SLAVE_ADDR /*!< LM75A slave address, you can set any 7bit value */ -#define ACK_VAL 0x0 /*!< I2C ack value */ -#define NACK_VAL 0x1 /*!< I2C nack value */ -#define WRITE_BIT I2C_MASTER_WRITE /*!< I2C master write */ -#define READ_BIT I2C_MASTER_READ /*!< I2C master read */ -#define ACK_CHECK_EN 0x1 /*!< I2C master will check ack from slave*/ -#define ACK_CHECK_DIS 0x0 /*!< I2C master will not check ack from slave */ - -/* -#define GPIO_INPUT_IO_0 CONFIG_LM75A_OS_PIN -#define GPIO_OUTPUT_IO_0 CONFIG_LM75A_VCC_PIN -#define GPIO_OUTPUT_PIN_SEL (1ULL << GPIO_OUTPUT_IO_0) -#define GPIO_INPUT_PIN_SEL (1ULL << GPIO_INPUT_IO_0) -#define ESP_INTR_FLAG_DEFAULT 0 -*/ - -// static xQueueHandle gpio_evt_queue = NULL; -// static int gpio_int_task_enable = 0; -// static TaskHandle_t gpio_int_task_handle = NULL; - -/** - * @brief test code to read esp-i2c-slave - * We need to fill the buffer of esp slave device, then master can read them out. - * - * _______________________________________________________________________________________ - * | start | slave_addr + rd_bit +ack | read n-1 bytes + ack | read 1 byte + nack | stop | - * --------|--------------------------|----------------------|--------------------|------| - * - */ -static esp_err_t i2c_master_read_slave(i2c_port_t i2c_num, uint8_t *data_rd, size_t size) -{ - if (size == 0) - { - return ESP_OK; - } - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | READ_BIT, ACK_CHECK_EN); - if (size > 1) - { - i2c_master_read(cmd, data_rd, size - 1, ACK_VAL); - } - i2c_master_read_byte(cmd, data_rd + size - 1, NACK_VAL); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief Test code to write esp-i2c-slave - * Master device write data to slave(both esp32), - * the data will be stored in slave buffer. - * We can read them out from slave buffer. - * - * ___________________________________________________________________ - * | start | slave_addr + wr_bit + ack | write n bytes + ack | stop | - * --------|---------------------------|----------------------|------| - * - */ -static esp_err_t i2c_master_write_slave(i2c_port_t i2c_num, uint8_t *data_wr, size_t size) -{ - i2c_cmd_handle_t cmd = i2c_cmd_link_create(); - i2c_master_start(cmd); - i2c_master_write_byte(cmd, (LM75A_SLAVE_ADDR << 1) | WRITE_BIT, ACK_CHECK_EN); - i2c_master_write(cmd, data_wr, size, ACK_CHECK_EN); - i2c_master_stop(cmd); - esp_err_t ret = i2c_master_cmd_begin(i2c_num, cmd, 1000 / portTICK_PERIOD_MS); - i2c_cmd_link_delete(cmd); - return ret; -} - -/** - * @brief i2c master initialization - */ -static void i2c_master_init() -{ - int i2c_master_port = I2C_MASTER_NUM; - i2c_config_t conf; - conf.mode = I2C_MODE_MASTER; - conf.sda_io_num = I2C_MASTER_SDA_IO; - conf.sda_pullup_en = GPIO_PULLUP_DISABLE; - conf.scl_io_num = I2C_MASTER_SCL_IO; - conf.scl_pullup_en = GPIO_PULLUP_DISABLE; - conf.master.clk_speed = I2C_MASTER_FREQ_HZ; - conf.clk_flags = 0; - - i2c_param_config(i2c_master_port, &conf); - i2c_driver_install(i2c_master_port, conf.mode, - I2C_MASTER_RX_BUF_DISABLE, - I2C_MASTER_TX_BUF_DISABLE, 0); -} - -int lm75a_read_temperature(int show) -{ - uint8_t buf[2]; - float tmp; - buf[0] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - if (show) - printf("lm75a_read_temperature=%.1f\n", tmp); - return tmp; -} - -/* -static void IRAM_ATTR gpio_isr_handler(void *arg) -{ - uint32_t gpio_num = (uint32_t)arg; - xQueueSendFromISR(gpio_evt_queue, &gpio_num, NULL); -} - -static void gpio_int_task(void *arg) -{ - uint32_t io_num; - gpio_int_task_enable = 1; - while (gpio_int_task_enable) - { - if (xQueueReceive(gpio_evt_queue, &io_num, portMAX_DELAY)) - { - - // read temperature to clean int; - if (io_num == GPIO_INPUT_IO_0) - { - printf("GPIO[%d] intr, val: %d\n\n", io_num, gpio_get_level(io_num)); - lm75a_read_temperature(0); // read to clean interrupt. - } - } - } - printf("quit gpio_int_task\n"); - if (gpio_evt_queue) - { - vQueueDelete(gpio_evt_queue); - gpio_evt_queue = NULL; - } - gpio_int_task_handle = NULL; - vTaskDelete(NULL); -} - -void init_os_gpio() -{ - printf("init_os_gpio!\n"); - - if (gpio_evt_queue == NULL) - gpio_evt_queue = xQueueCreate(10, sizeof(uint32_t)); - - if (gpio_int_task_handle == NULL) - { - xTaskCreate(gpio_int_task, "gpio_int_task", 2048, NULL, 10, &gpio_int_task_handle); - // install gpio isr service - gpio_install_isr_service(ESP_INTR_FLAG_DEFAULT); - // hook isr handler for specific gpio pin again - gpio_isr_handler_add(GPIO_INPUT_IO_0, gpio_isr_handler, (void *)GPIO_INPUT_IO_0); - } -} - -static void deinit_os_gpio() -{ - printf("deinit_os_gpio!\n"); - - if (gpio_int_task_handle) - { - gpio_isr_handler_remove(GPIO_INPUT_IO_0); - gpio_uninstall_isr_service(); - gpio_int_task_enable = 0; - int io = 0; - xQueueSend(gpio_evt_queue, &io, 0); // send a fake signal to quit task. - } -} - -static void lm75a_vcc_enable() -{ - gpio_config_t io_conf; - // enable output for vcc - io_conf.intr_type = GPIO_PIN_INTR_DISABLE; - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL; - io_conf.pull_down_en = 0; - io_conf.pull_up_en = 0; - gpio_config(&io_conf); - - // enable input for interrupt - io_conf.intr_type = GPIO_PIN_INTR_NEGEDGE; // GPIO_PIN_INTR_ANYEDGE; - io_conf.pin_bit_mask = GPIO_INPUT_PIN_SEL; - io_conf.mode = GPIO_MODE_INPUT; - io_conf.pull_up_en = 1; - gpio_set_pull_mode(GPIO_INPUT_IO_0, GPIO_FLOATING); - gpio_config(&io_conf); - gpio_set_level(GPIO_OUTPUT_IO_0, 1); -} - -static void lm75a_vcc_disable() -{ - gpio_set_level(GPIO_OUTPUT_IO_0, 0); -} -*/ - -void lm75a_init() -{ - // lm75a_vcc_enable(); - i2c_master_init(); -} - -void lm75a_deinit() -{ - // deinit_os_gpio(); - i2c_driver_delete(I2C_MASTER_NUM); - // lm75a_vcc_disable(); -} - -void lm75a_set_tos(int tos) -{ - uint8_t buf[4]; - printf("lm75a_set_tos: %d\n", tos); - // set Tos: - buf[0] = 0x3; - buf[1] = (tos & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_set_thys(int thys) -{ - uint8_t buf[4]; - printf("lm75a_set_thys: %d\n", thys); - // set Thyst: - buf[0] = 0x2; - buf[1] = (thys & 0xff); - buf[2] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 3); -} - -void lm75a_get_tos() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x3; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_tos: %.1f\n", tmp); -} - -void lm75a_get_thys() -{ - uint8_t buf[4]; - float tmp; - buf[0] = 0x2; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 1); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); - tmp = buf[0]; - if (buf[1] & 128) - tmp += 0.5; - - printf("lm75a_get_thys: %.1f\n", tmp); -} - -void lm75a_set_int(int en) -{ - uint8_t buf[2]; - - en = !!en; - if (en) - { - printf("lm75a_set_int: %d\n", en); - buf[0] = 0x1; - buf[1] = (1 << 1); // D1 set to 1; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - // gpio_set_intr_type(GPIO_INPUT_IO_0, GPIO_INTR_NEGEDGE); - // init_os_gpio(); - } - else - { - printf("lm75a_set_int: %d\n", en); - // deinit_os_gpio(); - buf[0] = 0x1; - buf[1] = 0; - i2c_master_write_slave(I2C_MASTER_NUM, buf, 2); - i2c_master_read_slave(I2C_MASTER_NUM, buf, 2); // do one time read to clean interrupt before enter interrupt mode. - } -} - -void lm75a_get_osio() -{ - // printf("os_io: %d\n", gpio_get_level(GPIO_INPUT_IO_0)); -} - -// === Fim de: components/peripherals/src/lm75a.c === - - -// === Início de: components/peripherals/src/onewire.c === -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. - */ - -#include -#include -#include -#include "rom/ets_sys.h" - -#include "onewire.h" - -#define ONEWIRE_SELECT_ROM 0x55 -#define ONEWIRE_SKIP_ROM 0xcc -#define ONEWIRE_SEARCH 0xf0 -#define ONEWIRE_CRC8_TABLE - -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - -// Waits up to `max_wait` microseconds for the specified pin to go high. -// Returns true if successful, false if the bus never comes high (likely -// shorted). -static inline bool _onewire_wait_for_bus(gpio_num_t pin, int max_wait) -{ - bool state; - for (int i = 0; i < ((max_wait + 4) / 5); i++) { - if (gpio_get_level(pin)) - break; - ets_delay_us(5); - } - state = gpio_get_level(pin); - // Wait an extra 1us to make sure the devices have an adequate recovery - // time before we drive things low again. - ets_delay_us(1); - return state; -} - -static void setup_pin(gpio_num_t pin, bool open_drain) -{ - gpio_set_direction(pin, open_drain ? GPIO_MODE_INPUT_OUTPUT_OD : GPIO_MODE_OUTPUT); - // gpio_set_pull_mode(pin, GPIO_PULLUP_ONLY); -} - -// Perform the onewire reset function. We will wait up to 250uS for -// the bus to come high, if it doesn't then it is broken or shorted -// and we return false; -// -// Returns true if a device asserted a presence pulse, false otherwise. -// -bool onewire_reset(gpio_num_t pin) -{ - setup_pin(pin, true); - - gpio_set_level(pin, 1); - // wait until the wire is high... just in case - if (!_onewire_wait_for_bus(pin, 250)) - return false; - - gpio_set_level(pin, 0); - ets_delay_us(480); - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 1); // allow it to float - ets_delay_us(70); - bool r = !gpio_get_level(pin); - portEXIT_CRITICAL(&mux); - - // Wait for all devices to finish pulling the bus low before returning - if (!_onewire_wait_for_bus(pin, 410)) - return false; - - return r; -} - -static bool _onewire_write_bit(gpio_num_t pin, bool v) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return false; - - portENTER_CRITICAL(&mux); - if (v) { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(10); - gpio_set_level(pin, 1); // allow output high - ets_delay_us(55); - } else { - gpio_set_level(pin, 0); // drive output low - ets_delay_us(65); - gpio_set_level(pin, 1); // allow output high - } - ets_delay_us(1); - portEXIT_CRITICAL(&mux); - - return true; -} - -static int _onewire_read_bit(gpio_num_t pin) -{ - if (!_onewire_wait_for_bus(pin, 10)) - return -1; - - portENTER_CRITICAL(&mux); - gpio_set_level(pin, 0); - ets_delay_us(2); - gpio_set_level(pin, 1); // let pin float, pull up will raise - ets_delay_us(11); - int r = gpio_get_level(pin); // Must sample within 15us of start - ets_delay_us(48); - portEXIT_CRITICAL(&mux); - - return r; -} - -// Write a byte. The writing code uses open-drain mode and expects the pullup -// resistor to pull the line high when not driven low. If you need strong -// power after the write (e.g. DS18B20 in parasite power mode) then call -// onewire_power() after this is complete to actively drive the line high. -// -bool onewire_write(gpio_num_t pin, uint8_t v) -{ - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) - if (!_onewire_write_bit(pin, (bitMask & v))) - return false; - - return true; -} - -bool onewire_write_bytes(gpio_num_t pin, const uint8_t* buf, size_t count) -{ - for (size_t i = 0; i < count; i++) - if (!onewire_write(pin, buf[i])) - return false; - - return true; -} - -// Read a byte -// -int onewire_read(gpio_num_t pin) -{ - int r = 0; - - for (uint8_t bitMask = 0x01; bitMask; bitMask <<= 1) { - int bit = _onewire_read_bit(pin); - if (bit < 0) - return -1; - else if (bit) - r |= bitMask; - } - return r; -} - -bool onewire_read_bytes(gpio_num_t pin, uint8_t* buf, size_t count) -{ - size_t i; - int b; - - for (i = 0; i < count; i++) { - b = onewire_read(pin); - if (b < 0) - return false; - buf[i] = b; - } - return true; -} - -bool onewire_select(gpio_num_t pin, onewire_addr_t addr) -{ - uint8_t i; - - if (!onewire_write(pin, ONEWIRE_SELECT_ROM)) - return false; - - for (i = 0; i < 8; i++) { - if (!onewire_write(pin, addr & 0xff)) - return false; - addr >>= 8; - } - - return true; -} - -bool onewire_skip_rom(gpio_num_t pin) -{ - return onewire_write(pin, ONEWIRE_SKIP_ROM); -} - -bool onewire_power(gpio_num_t pin) -{ - // Make sure the bus is not being held low before driving it high, or we - // may end up shorting ourselves out. - if (!_onewire_wait_for_bus(pin, 10)) - return false; - - setup_pin(pin, false); - gpio_set_level(pin, 1); - - return true; -} - -void onewire_depower(gpio_num_t pin) -{ - setup_pin(pin, true); -} - -void onewire_search_start(onewire_search_t* search) -{ - // reset the search state - memset(search, 0, sizeof(*search)); -} - -void onewire_search_prefix(onewire_search_t* search, uint8_t family_code) -{ - uint8_t i; - - search->rom_no[0] = family_code; - for (i = 1; i < 8; i++) { - search->rom_no[i] = 0; - } - search->last_discrepancy = 64; - search->last_device_found = false; -} - -// Perform a search. If the next device has been successfully enumerated, its -// ROM address will be returned. If there are no devices, no further -// devices, or something horrible happens in the middle of the -// enumeration then ONEWIRE_NONE is returned. Use OneWire::reset_search() to -// start over. -// -// --- Replaced by the one from the Dallas Semiconductor web site --- -//-------------------------------------------------------------------------- -// Perform the 1-Wire Search Algorithm on the 1-Wire bus using the existing -// search state. -// Return 1 : device found, ROM number in ROM_NO buffer -// 0 : device not found, end of search -// -onewire_addr_t onewire_search_next(onewire_search_t* search, gpio_num_t pin) -{ - //TODO: add more checking for read/write errors - uint8_t id_bit_number; - uint8_t last_zero, search_result; - int rom_byte_number; - int8_t id_bit, cmp_id_bit; - onewire_addr_t addr; - unsigned char rom_byte_mask; - bool search_direction; - - // initialize for search - id_bit_number = 1; - last_zero = 0; - rom_byte_number = 0; - rom_byte_mask = 1; - search_result = 0; - - // if the last call was not the last one - if (!search->last_device_found) { - // 1-Wire reset - if (!onewire_reset(pin)) { - // reset the search - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } - - // issue the search command - onewire_write(pin, ONEWIRE_SEARCH); - - // loop to do the search - do { - // read a bit and its complement - id_bit = _onewire_read_bit(pin); - cmp_id_bit = _onewire_read_bit(pin); - - if ((id_bit == 1) && (cmp_id_bit == 1)) - break; - else { - // all devices coupled have 0 or 1 - if (id_bit != cmp_id_bit) - search_direction = id_bit; // bit write value for search - else { - // if this discrepancy if before the Last Discrepancy - // on a previous next then pick the same as last time - if (id_bit_number < search->last_discrepancy) - search_direction = ((search->rom_no[rom_byte_number] & rom_byte_mask) > 0); - else - // if equal to last pick 1, if not then pick 0 - search_direction = (id_bit_number == search->last_discrepancy); - - // if 0 was picked then record its position in LastZero - if (!search_direction) - last_zero = id_bit_number; - } - - // set or clear the bit in the ROM byte rom_byte_number - // with mask rom_byte_mask - if (search_direction) - search->rom_no[rom_byte_number] |= rom_byte_mask; - else - search->rom_no[rom_byte_number] &= ~rom_byte_mask; - - // serial number search direction write bit - _onewire_write_bit(pin, search_direction); - - // increment the byte counter id_bit_number - // and shift the mask rom_byte_mask - id_bit_number++; - rom_byte_mask <<= 1; - - // if the mask is 0 then go to new SerialNum byte rom_byte_number and reset mask - if (rom_byte_mask == 0) { - rom_byte_number++; - rom_byte_mask = 1; - } - } - } while (rom_byte_number < 8); // loop until through all ROM bytes 0-7 - - // if the search was successful then - if (!(id_bit_number < 65)) { - // search successful so set last_discrepancy,last_device_found,search_result - search->last_discrepancy = last_zero; - - // check for last device - if (search->last_discrepancy == 0) - search->last_device_found = true; - - search_result = 1; - } - } - - // if no device found then reset counters so next 'search' will be like a first - if (!search_result || !search->rom_no[0]) { - search->last_discrepancy = 0; - search->last_device_found = false; - return ONEWIRE_NONE; - } else { - addr = 0; - for (rom_byte_number = 7; rom_byte_number >= 0; rom_byte_number--) { - addr = (addr << 8) | search->rom_no[rom_byte_number]; - } - //printf("Ok I found something at %08x%08x...\n", (uint32_t)(addr >> 32), (uint32_t)addr); - } - return addr; -} - -// The 1-Wire CRC scheme is described in Maxim Application Note 27: -// "Understanding and Using Cyclic Redundancy Checks with Maxim iButton Products" -// - -#ifdef ONEWIRE_CRC8_TABLE -// This table comes from Dallas sample code where it is freely reusable, -// though Copyright (c) 2000 Dallas Semiconductor Corporation -static const uint8_t dscrc_table[] = { - 0, 94, 188, 226, 97, 63, 221, 131, 194, 156, 126, 32, 163, 253, 31, 65, - 157, 195, 33, 127, 252, 162, 64, 30, 95, 1, 227, 189, 62, 96, 130, 220, - 35, 125, 159, 193, 66, 28, 254, 160, 225, 191, 93, 3, 128, 222, 60, 98, - 190, 224, 2, 92, 223, 129, 99, 61, 124, 34, 192, 158, 29, 67, 161, 255, - 70, 24, 250, 164, 39, 121, 155, 197, 132, 218, 56, 102, 229, 187, 89, 7, - 219, 133, 103, 57, 186, 228, 6, 88, 25, 71, 165, 251, 120, 38, 196, 154, - 101, 59, 217, 135, 4, 90, 184, 230, 167, 249, 27, 69, 198, 152, 122, 36, - 248, 166, 68, 26, 153, 199, 37, 123, 58, 100, 134, 216, 91, 5, 231, 185, - 140, 210, 48, 110, 237, 179, 81, 15, 78, 16, 242, 172, 47, 113, 147, 205, - 17, 79, 173, 243, 112, 46, 204, 146, 211, 141, 111, 49, 178, 236, 14, 80, - 175, 241, 19, 77, 206, 144, 114, 44, 109, 51, 209, 143, 12, 82, 176, 238, - 50, 108, 142, 208, 83, 13, 239, 177, 240, 174, 76, 18, 145, 207, 45, 115, - 202, 148, 118, 40, 171, 245, 23, 73, 8, 86, 180, 234, 105, 55, 213, 139, - 87, 9, 235, 181, 54, 104, 138, 212, 149, 203, 41, 119, 244, 170, 72, 22, - 233, 183, 85, 11, 136, 214, 52, 106, 43, 117, 151, 201, 74, 20, 246, 168, - 116, 42, 200, 150, 21, 75, 169, 247, 182, 232, 10, 84, 215, 137, 107, 53 -}; - -// -// Compute a Dallas Semiconductor 8 bit CRC. These show up in the ROM -// and the registers. (note: this might better be done without to -// table, it would probably be smaller and certainly fast enough -// compared to all those delayMicrosecond() calls. But I got -// confused, so I use this table from the examples.) -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - crc = dscrc_table[crc ^ *data++]; - - return crc; -} -#else -// -// Compute a Dallas Semiconductor 8 bit CRC directly. -// this is much slower, but much smaller, than the lookup table. -// -uint8_t onewire_crc8(const uint8_t* data, uint8_t len) -{ - uint8_t crc = 0; - - while (len--) - { - uint8_t inbyte = *data++; - for (int i = 8; i; i--) - { - uint8_t mix = (crc ^ inbyte) & 0x01; - crc >>= 1; - if (mix) - crc ^= 0x8C; - inbyte >>= 1; - } - } - return crc; -} -#endif /* ONEWIRE_CRC8_TABLE */ - -// Compute the 1-Wire CRC16 and compare it against the received CRC. -// Example usage (reading a DS2408): -// // Put everything in a buffer so we can compute the CRC easily. -// uint8_t buf[13]; -// buf[0] = 0xF0; // Read PIO Registers -// buf[1] = 0x88; // LSB address -// buf[2] = 0x00; // MSB address -// WriteBytes(net, buf, 3); // Write 3 cmd bytes -// ReadBytes(net, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 -// if (!CheckCRC16(buf, 11, &buf[11])) { -// // Handle error. -// } -// -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param inverted_crc - The two CRC16 bytes in the received data. -// This should just point into the received data, -// *not* at a 16-bit integer. -// @param crc - The crc starting value (optional) -// @return 1, iff the CRC matches. -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv) -{ - uint16_t crc = ~onewire_crc16(input, len, crc_iv); - return (crc & 0xFF) == inverted_crc[0] && (crc >> 8) == inverted_crc[1]; -} - -// Compute a Dallas Semiconductor 16 bit CRC. This is required to check -// the integrity of data received from many 1-Wire devices. Note that the -// CRC computed here is *not* what you'll get from the 1-Wire network, -// for two reasons: -// 1) The CRC is transmitted bitwise inverted. -// 2) Depending on the endian-ness of your processor, the binary -// representation of the two-byte return value may have a different -// byte order than the two bytes you get from 1-Wire. -// @param input - Array of bytes to checksum. -// @param len - How many bytes to use. -// @param crc - The crc starting value (optional) -// @return The CRC16, as defined by Dallas Semiconductor. -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv) -{ - uint16_t crc = crc_iv; - static const uint8_t oddparity[16] = { 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 }; - - uint16_t i; - for (i = 0; i < len; i++) { - // Even though we're just copying a byte from the input, - // we'll be doing 16-bit computation with it. - uint16_t cdata = input[i]; - cdata = (cdata ^ crc) & 0xff; - crc >>= 8; - - if (oddparity[cdata & 0x0F] ^ oddparity[cdata >> 4]) - crc ^= 0xC001; - - cdata <<= 6; - crc ^= cdata; - cdata <<= 1; - crc ^= cdata; - } - return crc; -} -// === Fim de: components/peripherals/src/onewire.c === - - -// === Início de: components/peripherals/src/onewire.h === -/* - * The MIT License (MIT) - * - * Copyright (c) 2014 zeroday nodemcu.com - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * ------------------------------------------------------------------------------- - * Portions copyright (C) 2000 Dallas Semiconductor Corporation, under the - * following additional terms: - * - * Except as contained in this notice, the name of Dallas Semiconductor - * shall not be used except as stated in the Dallas Semiconductor - * Branding Policy. - */ - -#ifndef ONEWIRE_H_ -#define ONEWIRE_H_ - -#include -#include -#include "driver/gpio.h" - -/** - * Type used to hold all 1-Wire device ROM addresses (64-bit) - */ -typedef uint64_t onewire_addr_t; - -/** - * Structure to contain the current state for onewire_search_next(), etc - */ -typedef struct -{ - uint8_t rom_no[8]; - uint8_t last_discrepancy; - bool last_device_found; -} onewire_search_t; - -/** - * ::ONEWIRE_NONE is an invalid ROM address that will never occur in a device - * (CRC mismatch), and so can be useful as an indicator for "no-such-device", - * etc. - */ -#define ONEWIRE_NONE ((onewire_addr_t)(0xffffffffffffffffLL)) - -/** - * @brief Perform a 1-Wire reset cycle. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if at least one device responds with a presence pulse, - * `false` if no devices were detected (or the bus is shorted, etc) - */ -bool onewire_reset(gpio_num_t pin); - -/** - * @brief Issue a 1-Wire "ROM select" command to select a particular device. - * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param addr The ROM address of the device to select - * - * @return `true` if the "ROM select" command could be successfully issued, - * `false` if there was an error. - */ -bool onewire_select(gpio_num_t pin, const onewire_addr_t addr); - -/** - * @brief Issue a 1-Wire "skip ROM" command to select *all* devices on the bus. - * - * It is necessary to call ::onewire_reset() before calling this function. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` if the "skip ROM" command could be successfully issued, - * `false` if there was an error. - */ -bool onewire_skip_rom(gpio_num_t pin); - -/** - * @brief Write a byte on the onewire bus. - * - * The writing code uses open-drain mode and expects the pullup resistor to - * pull the line high when not driven low. If you need strong power after the - * write (e.g. DS18B20 in parasite power mode) then call ::onewire_power() - * after this is complete to actively drive the line high. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param v The byte value to write - * - * @return `true` if successful, `false` on error. - */ -bool onewire_write(gpio_num_t pin, uint8_t v); - -/** - * @brief Write multiple bytes on the 1-Wire bus. - * - * See ::onewire_write() for more info. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param buf A pointer to the buffer of bytes to be written - * @param count Number of bytes to write - * - * @return `true` if all bytes written successfully, `false` on error. - */ -bool onewire_write_bytes(gpio_num_t pin, const uint8_t *buf, size_t count); - -/** - * @brief Read a byte from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return the read byte on success, negative value on error. - */ -int onewire_read(gpio_num_t pin); - -/** - * @brief Read multiple bytes from a 1-Wire device. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * @param[out] buf A pointer to the buffer to contain the read bytes - * @param count Number of bytes to read - * - * @return `true` on success, `false` on error. - */ -bool onewire_read_bytes(gpio_num_t pin, uint8_t *buf, size_t count); - -/** - * @brief Actively drive the bus high to provide extra power for certain - * operations of parasitically-powered devices. - * - * For parasitically-powered devices which need more power than can be - * provided via the normal pull-up resistor, it may be necessary for some - * operations to drive the bus actively high. This function can be used to - * perform that operation. - * - * The bus can be depowered once it is no longer needed by calling - * ::onewire_depower(), or it will be depowered automatically the next time - * ::onewire_reset() is called to start another command. - * - * @note Make sure the device(s) you are powering will not pull more current - * than the ESP32/ESP8266 is able to supply via its GPIO pins (this is - * especially important when multiple devices are on the same bus and - * they are all performing a power-intensive operation at the same time - * (i.e. multiple DS18B20 sensors, which have all been given a - * "convert T" operation by using ::onewire_skip_rom())). - * - * @note This routine will check to make sure that the bus is already high - * before driving it, to make sure it doesn't attempt to drive it high - * while something else is pulling it low (which could cause a reset or - * damage the ESP32/ESP8266). - * - * @param pin The GPIO pin connected to the 1-Wire bus. - * - * @return `true` on success, `false` on error. - */ -bool onewire_power(gpio_num_t pin); - -/** - * @brief Stop forcing power onto the bus. - * - * You only need to do this if you previously called ::onewire_power() to drive - * the bus high and now want to allow it to float instead. Note that - * onewire_reset() will also automatically depower the bus first, so you do - * not need to call this first if you just want to start a new operation. - * - * @param pin The GPIO pin connected to the 1-Wire bus. - */ -void onewire_depower(gpio_num_t pin); - -/** - * @brief Clear the search state so that it will start from the beginning on - * the next call to ::onewire_search_next(). - * - * @param[out] search The onewire_search_t structure to reset. - */ -void onewire_search_start(onewire_search_t *search); - -/** - * @brief Setup the search to search for devices with the specified - * "family code". - * - * @param[out] search The onewire_search_t structure to update. - * @param family_code The "family code" to search for. - */ -void onewire_search_prefix(onewire_search_t *search, uint8_t family_code); - -/** - * @brief Search for the next device on the bus. - * - * The order of returned device addresses is deterministic. You will always - * get the same devices in the same order. - * - * @note It might be a good idea to check the CRC to make sure you didn't get - * garbage. - * - * @return the address of the next device on the bus, or ::ONEWIRE_NONE if - * there is no next address. ::ONEWIRE_NONE might also mean that - * the bus is shorted, there are no devices, or you have already - * retrieved all of them. - */ -onewire_addr_t onewire_search_next(onewire_search_t *search, gpio_num_t pin); - -/** - * @brief Compute a Dallas Semiconductor 8 bit CRC. - * - * These are used in the ROM address and scratchpad registers to verify the - * transmitted data is correct. - */ -uint8_t onewire_crc8(const uint8_t *data, uint8_t len); - -/** - * @brief Compute the 1-Wire CRC16 and compare it against the received CRC. - * - * Example usage (reading a DS2408): - * @code{.c} - * // Put everything in a buffer so we can compute the CRC easily. - * uint8_t buf[13]; - * buf[0] = 0xF0; // Read PIO Registers - * buf[1] = 0x88; // LSB address - * buf[2] = 0x00; // MSB address - * onewire_write_bytes(pin, buf, 3); // Write 3 cmd bytes - * onewire_read_bytes(pin, buf+3, 10); // Read 6 data bytes, 2 0xFF, 2 CRC16 - * if (!onewire_check_crc16(buf, 11, &buf[11])) { - * // TODO: Handle error. - * } - * @endcode - * - * @param input Array of bytes to checksum. - * @param len Number of bytes in `input` - * @param inverted_crc The two CRC16 bytes in the received data. - * This should just point into the received data, - * *not* at a 16-bit integer. - * @param crc_iv The crc starting value (optional) - * - * @return `true` if the CRC matches, `false` otherwise. - */ -bool onewire_check_crc16(const uint8_t* input, size_t len, const uint8_t* inverted_crc, uint16_t crc_iv); - -/** - * @brief Compute a Dallas Semiconductor 16 bit CRC. - * - * This is required to check the integrity of data received from many 1-Wire - * devices. Note that the CRC computed here is *not* what you'll get from the - * 1-Wire network, for two reasons: - * - * 1. The CRC is transmitted bitwise inverted. - * 2. Depending on the endian-ness of your processor, the binary - * representation of the two-byte return value may have a different - * byte order than the two bytes you get from 1-Wire. - * - * @param input Array of bytes to checksum. - * @param len How many bytes are in `input`. - * @param crc_iv The crc starting value (optional) - * - * @return the CRC16, as defined by Dallas Semiconductor. - */ -uint16_t onewire_crc16(const uint8_t* input, size_t len, uint16_t crc_iv); - - -#endif /* ONEWIRE_H_ */ -// === Fim de: components/peripherals/src/onewire.h === - - -// === Início de: components/peripherals/src/ds18x20.c === -/* - * Copyright (c) 2016 Grzegorz Hetman - * Copyright (c) 2016 Alex Stewart - * Copyright (c) 2018 Ruslan V. Uss - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holder nor the names of itscontributors - * may be used to endorse or promote products derived from this software without - * specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE - * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - - -#include -#include -#include -#include -#include "ds18x20.h" - -#define ds18x20_WRITE_SCRATCHPAD 0x4E -#define ds18x20_READ_SCRATCHPAD 0xBE -#define ds18x20_COPY_SCRATCHPAD 0x48 -#define ds18x20_READ_EEPROM 0xB8 -#define ds18x20_READ_PWRSUPPLY 0xB4 -#define ds18x20_SEARCHROM 0xF0 -#define ds18x20_SKIP_ROM 0xCC -#define ds18x20_READROM 0x33 -#define ds18x20_MATCHROM 0x55 -#define ds18x20_ALARMSEARCH 0xEC -#define ds18x20_CONVERT_T 0x44 - -#define CHECK(x) do { esp_err_t __; if ((__ = x) != ESP_OK) return __; } while (0) -#define CHECK_ARG(VAL) do { if (!(VAL)) return ESP_ERR_INVALID_ARG; } while (0) - -static portMUX_TYPE mux = portMUX_INITIALIZER_UNLOCKED; - -static const char* TAG = "ds18x20"; - -esp_err_t ds18x20_measure(gpio_num_t pin, ds18x20_addr_t addr, bool wait) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_CONVERT_T); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - if (wait){ - vTaskDelay(pdMS_TO_TICKS(750)); - onewire_depower(pin); - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - uint8_t crc; - uint8_t expected_crc; - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_READ_SCRATCHPAD); - - for (int i = 0; i < 8; i++) - buffer[i] = onewire_read(pin); - crc = onewire_read(pin); - - expected_crc = onewire_crc8(buffer, 8); - if (crc != expected_crc) - { - ESP_LOGE(TAG, "CRC check failed reading scratchpad: %02x %02x %02x %02x %02x %02x %02x %02x : %02x (expected %02x)", buffer[0], buffer[1], - buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7], crc, expected_crc); - return ESP_ERR_INVALID_CRC; - } - - return ESP_OK; -} - -esp_err_t ds18x20_write_scratchpad(gpio_num_t pin, ds18x20_addr_t addr, uint8_t* buffer) -{ - CHECK_ARG(buffer); - - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - onewire_write(pin, ds18x20_WRITE_SCRATCHPAD); - - for (int i = 0; i < 3; i++) - onewire_write(pin, buffer[i]); - - return ESP_OK; -} - -esp_err_t ds18x20_copy_scratchpad(gpio_num_t pin, ds18x20_addr_t addr) -{ - if (!onewire_reset(pin)) - return ESP_ERR_INVALID_RESPONSE; - - if (addr == DS18X20_ANY) - onewire_skip_rom(pin); - else - onewire_select(pin, addr); - - portENTER_CRITICAL(&mux); - onewire_write(pin, ds18x20_COPY_SCRATCHPAD); - // For parasitic devices, power must be applied within 10us after issuing - // the convert command. - onewire_power(pin); - portEXIT_CRITICAL(&mux); - - // And then it needs to keep that power up for 10ms. - vTaskDelay(pdMS_TO_TICKS(10)); - onewire_depower(pin); - - return ESP_OK; -} - -esp_err_t ds18b20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - - *temperature = ((int16_t)temp * 625.0) / 100; - - return ESP_OK; -} - -esp_err_t ds18s20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - uint8_t scratchpad[8]; - int16_t temp; - - CHECK(ds18x20_read_scratchpad(pin, addr, scratchpad)); - - temp = scratchpad[1] << 8 | scratchpad[0]; - temp = ((temp & 0xfffe) << 3) + (16 - scratchpad[6]) - 4; - - *temperature = (temp * 625) / 100 - 25; - - return ESP_OK; -} - -esp_err_t ds18x20_read_temperature(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - if ((uint8_t)addr == DS18B20_FAMILY_ID) { - return ds18b20_read_temperature(pin, addr, temperature); - } else { - return ds18s20_read_temperature(pin, addr, temperature); - } -} - -esp_err_t ds18b20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18b20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18s20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18s20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read(gpio_num_t pin, ds18x20_addr_t addr, int16_t* temperature) -{ - CHECK_ARG(temperature); - - CHECK(ds18x20_measure(pin, addr, true)); - return ds18x20_read_temperature(pin, addr, temperature); -} - -esp_err_t ds18x20_measure_and_read_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list && addr_count); - - CHECK(ds18x20_measure(pin, DS18X20_ANY, true)); - - return ds18x20_read_temp_multi(pin, addr_list, addr_count, result_list); -} - -esp_err_t ds18x20_scan_devices(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, size_t* found) -{ - CHECK_ARG(addr_list && addr_count); - - onewire_search_t search; - onewire_addr_t addr; - - *found = 0; - onewire_search_start(&search); - while ((addr = onewire_search_next(&search, pin)) != ONEWIRE_NONE) - { - uint8_t family_id = (uint8_t)addr; - if (family_id == DS18B20_FAMILY_ID || family_id == DS18S20_FAMILY_ID) - { - if (*found < addr_count) - addr_list[*found] = addr; - *found += 1; - } - } - - return ESP_OK; -} - -esp_err_t ds18x20_read_temp_multi(gpio_num_t pin, ds18x20_addr_t* addr_list, size_t addr_count, int16_t* result_list) -{ - CHECK_ARG(result_list); - - esp_err_t res = ESP_OK; - for (size_t i = 0; i < addr_count; i++) - { - esp_err_t tmp = ds18x20_read_temperature(pin, addr_list[i], &result_list[i]); - if (tmp != ESP_OK) - res = tmp; - } - return res; -} - -// === Fim de: components/peripherals/src/ds18x20.c === - - -// === Início de: components/peripherals/src/led.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/timers.h" -#include "esp_log.h" -#include "driver/gpio.h" -#include "led.h" -#include "board_config.h" -#include "evse_error.h" -#include "evse_api.h" - -#define LED_UPDATE_INTERVAL_MS 100 -#define BLOCK_TIME pdMS_TO_TICKS(10) - -static const char *TAG = "led"; - -typedef struct { - gpio_num_t gpio; - bool on : 1; - uint16_t ontime; - uint16_t offtime; - TimerHandle_t timer; - led_pattern_t pattern; - uint8_t blink_count; -} led_t; - -static led_t leds[LED_ID_MAX] = {0}; -static TimerHandle_t led_update_timer = NULL; -static evse_state_t led_state = -1; - -// ---------------------------- -// Funções Internas -// ---------------------------- - -static void led_update_timer_callback(TimerHandle_t xTimer); -static void led_update(void); -static void led_apply_by_state(evse_state_t state); - -static inline void led_gpio_write(gpio_num_t gpio, bool level) { - if (gpio != GPIO_NUM_NC) - gpio_set_level(gpio, level); -} - -static void led_timer_callback(TimerHandle_t xTimer) -{ - led_t *led = (led_t *)pvTimerGetTimerID(xTimer); - led->on = !led->on; - led_gpio_write(led->gpio, led->on); - uint32_t next_time = led->on ? led->ontime : led->offtime; - - xTimerChangePeriod(led->timer, pdMS_TO_TICKS(next_time), BLOCK_TIME); -} - -// ---------------------------- -// Inicialização -// ---------------------------- - -void led_init(void) -{ - gpio_config_t io_conf = { - .mode = GPIO_MODE_OUTPUT, - .intr_type = GPIO_INTR_DISABLE, - .pull_up_en = GPIO_PULLUP_DISABLE, - .pull_down_en = GPIO_PULLDOWN_ENABLE, - .pin_bit_mask = 0 - }; - - for (int i = 0; i < LED_ID_MAX; i++) { - leds[i].gpio = GPIO_NUM_NC; - } - - if (board_config.led_stop) { - leds[LED_ID_STOP].gpio = board_config.led_stop_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_stop_gpio); - } - - if (board_config.led_charging) { - leds[LED_ID_CHARGING].gpio = board_config.led_charging_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_charging_gpio); - } - - if (board_config.led_error) { - leds[LED_ID_ERROR].gpio = board_config.led_error_gpio; - io_conf.pin_bit_mask |= BIT64(board_config.led_error_gpio); - } - - if (io_conf.pin_bit_mask != 0) { - ESP_ERROR_CHECK(gpio_config(&io_conf)); - } - - if (!led_update_timer) { - led_update_timer = xTimerCreate("led_update_timer", - pdMS_TO_TICKS(LED_UPDATE_INTERVAL_MS), - pdTRUE, NULL, - led_update_timer_callback); - if (led_update_timer) { - xTimerStart(led_update_timer, BLOCK_TIME); - } else { - ESP_LOGE(TAG, "Failed to create LED update timer"); - } - } -} - -// ---------------------------- -// API Pública -// ---------------------------- - -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime) -{ - if (led_id >= LED_ID_MAX) return; - - led_t *led = &leds[led_id]; - if (led->gpio == GPIO_NUM_NC) return; - - // Evita reconfiguração idêntica - if (led->ontime == ontime && led->offtime == offtime) - return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->ontime = ontime; - led->offtime = offtime; - - if (ontime == 0) { - led->on = false; - led_gpio_write(led->gpio, 0); - } else if (offtime == 0) { - led->on = true; - led_gpio_write(led->gpio, 1); - } else { - led->on = true; - led_gpio_write(led->gpio, 1); - - if (!led->timer) { - led->timer = xTimerCreate("led_timer", pdMS_TO_TICKS(ontime), - pdFALSE, (void *)led, led_timer_callback); - } - - if (led->timer) { - xTimerStart(led->timer, BLOCK_TIME); - } - } -} - -void led_apply_pattern(led_id_t id, led_pattern_t pattern) -{ - if (id >= LED_ID_MAX) return; - - led_t *led = &leds[id]; - if (led->gpio == GPIO_NUM_NC) return; - - if (led->pattern == pattern) return; - - if (led->timer) { - xTimerStop(led->timer, BLOCK_TIME); - } - - led->pattern = pattern; - led->blink_count = 0; - - switch (pattern) { - case LED_PATTERN_OFF: - led_set_state(id, 0, 0); - break; - case LED_PATTERN_ON: - led_set_state(id, 1, 0); - break; - case LED_PATTERN_BLINK: - led_set_state(id, 500, 500); - break; - case LED_PATTERN_BLINK_FAST: - led_set_state(id, 200, 200); - break; - case LED_PATTERN_BLINK_SLOW: - led_set_state(id, 300, 1700); - break; - case LED_PATTERN_CHARGING_EFFECT: - led_set_state(id, 2000, 1000); - break; - } -} - -// ---------------------------- -// Controle por Estado -// ---------------------------- - -static void led_apply_by_state(evse_state_t state) -{ - // Reset todos - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_OFF); - - switch (state) { - case EVSE_STATE_A: - led_apply_pattern(LED_ID_STOP, LED_PATTERN_ON); - break; - case EVSE_STATE_B1: - case EVSE_STATE_B2: - case EVSE_STATE_C1: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_ON); - break; - case EVSE_STATE_C2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_CHARGING_EFFECT); - break; - case EVSE_STATE_D1: - case EVSE_STATE_D2: - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_BLINK_FAST); - break; - case EVSE_STATE_E: - case EVSE_STATE_F: - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - break; - default: - break; - } -} - -// ---------------------------- -// Timer Update -// ---------------------------- - -static void led_update(void) -{ - if (evse_error_is_active()) { - led_apply_pattern(LED_ID_ERROR, LED_PATTERN_BLINK_FAST); - led_apply_pattern(LED_ID_STOP, LED_PATTERN_OFF); - led_apply_pattern(LED_ID_CHARGING, LED_PATTERN_OFF); - return; - } - - evse_state_t current = evse_get_state(); - - if (current != led_state) { - led_state = current; - led_apply_by_state(current); - } -} - -static void led_update_timer_callback(TimerHandle_t xTimer) -{ - (void)xTimer; - led_update(); -} - -// === Fim de: components/peripherals/src/led.c === - - -// === Início de: components/peripherals/src/rcm.c === -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "driver/gpio.h" -#include "esp_log.h" - -#include "rcm.h" -#include "board_config.h" -#include "evse_api.h" - -// static bool do_test = false; - -// static bool triggered = false; - -// static bool test_triggered = false; - -// static void IRAM_ATTR rcm_isr_handler(void* arg) -// { -// if (!do_test) { -// triggered = true; -// } else { -// test_triggered = true; -// } -// } - -void rcm_init(void) -{ - if (board_config.rcm) { - gpio_config_t io_conf = {}; - - io_conf.mode = GPIO_MODE_OUTPUT; - io_conf.pin_bit_mask = BIT64(board_config.rcm_test_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - - io_conf.mode = GPIO_MODE_INPUT; - // io_conf.intr_type = GPIO_INTR_POSEDGE; - io_conf.pin_bit_mask = BIT64(board_config.rcm_gpio); - ESP_ERROR_CHECK(gpio_config(&io_conf)); - //ESP_ERROR_CHECK(gpio_isr_handler_add(board_config.rcm_gpio, rcm_isr_handler, NULL)); - } -} - -bool rcm_test(void) -{ - // do_test = true; - // test_triggered = false; - - // gpio_set_level(board_config.rcm_test_gpio, 1); - // vTaskDelay(pdMS_TO_TICKS(100)); - // gpio_set_level(board_config.rcm_test_gpio, 0); - - // do_test = false; - - // return test_triggered; - - gpio_set_level(board_config.rcm_test_gpio, 1); - vTaskDelay(pdMS_TO_TICKS(100)); - bool success = gpio_get_level(board_config.rcm_gpio) == 1; - gpio_set_level(board_config.rcm_test_gpio, 0); - - return success; -} - -bool rcm_is_triggered(void) -{ - // bool _triggered = triggered; - // if (gpio_get_level(board_config.rcm_gpio) == 0) { - // triggered = false; - // } - // return _triggered; - if (gpio_get_level(board_config.rcm_gpio) == 1) { - vTaskDelay(pdMS_TO_TICKS(1)); - return gpio_get_level(board_config.rcm_gpio) == 1; - } - - return false; -} -// === Fim de: components/peripherals/src/rcm.c === - - -// === Início de: components/peripherals/src/adc.c === -#include "adc.h" -#include "esp_log.h" - -const static char* TAG = "adc"; - -adc_oneshot_unit_handle_t adc_handle; - -adc_cali_handle_t adc_cali_handle; - -void adc_init(void) -{ - adc_oneshot_unit_init_cfg_t conf = { - .unit_id = ADC_UNIT_1 - }; - ESP_ERROR_CHECK(adc_oneshot_new_unit(&conf, &adc_handle)); - - bool calibrated = false; - -#if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Curve Fitting"); - adc_cali_curve_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, - }; - if (adc_cali_create_scheme_curve_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } -#endif - -#if ADC_CALI_SCHEME_LINE_FITTING_SUPPORTED - if (!calibrated) { - ESP_LOGI(TAG, "Calibration scheme version is %s", "Line Fitting"); - adc_cali_line_fitting_config_t cali_config = { - .unit_id = ADC_UNIT_1, - .atten = ADC_ATTEN_DB_12, - .bitwidth = ADC_BITWIDTH_DEFAULT, -#if CONFIG_IDF_TARGET_ESP32 - .default_vref = 1100 -#endif - }; - if (adc_cali_create_scheme_line_fitting(&cali_config, &adc_cali_handle) == ESP_OK) { - calibrated = true; - } - } -#endif - - if (!calibrated) { - ESP_LOGE(TAG, "No calibration scheme"); - ESP_ERROR_CHECK(ESP_FAIL); - } -} -// === Fim de: components/peripherals/src/adc.c === - - -// === Início de: components/peripherals/src/adc121s021_dma.c === -#include "driver/spi_master.h" -#include "driver/gpio.h" -#include "esp_log.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "adc121s021_dma.h" - -#define TAG "adc_dma" - -#define PIN_NUM_MOSI 23 -#define PIN_NUM_MISO 19 -#define PIN_NUM_CLK 18 -#define PIN_NUM_CS 5 - -#define SPI_HOST_USED SPI2_HOST -#define SAMPLE_SIZE_BYTES 2 -#define ADC_BITS 12 - -static spi_device_handle_t adc_spi; - -void adc121s021_dma_init(void) -{ - spi_bus_config_t buscfg = { - .mosi_io_num = PIN_NUM_MOSI, - .miso_io_num = PIN_NUM_MISO, - .sclk_io_num = PIN_NUM_CLK, - .quadwp_io_num = -1, - .quadhd_io_num = -1, - .max_transfer_sz = SAMPLE_SIZE_BYTES, - }; - - spi_device_interface_config_t devcfg = { - .clock_speed_hz = 6000000, // 6 MHz - .mode = 0, - .spics_io_num = PIN_NUM_CS, - .queue_size = 2, - .flags = SPI_DEVICE_NO_DUMMY, - }; - - ESP_ERROR_CHECK(spi_bus_initialize(SPI_HOST_USED, &buscfg, SPI_DMA_CH_AUTO)); - ESP_ERROR_CHECK(spi_bus_add_device(SPI_HOST_USED, &devcfg, &adc_spi)); -} - -bool adc121s021_dma_get_sample(uint16_t *sample) -{ - uint8_t tx_buffer[2] = {0x00, 0x00}; // Dummy TX - uint8_t rx_buffer[2] = {0}; - - spi_transaction_t t = { - .length = 16, // 16 bits - .tx_buffer = tx_buffer, - .rx_buffer = rx_buffer, - .flags = 0 - }; - - esp_err_t err = spi_device_transmit(adc_spi, &t); - if (err != ESP_OK) { - ESP_LOGE(TAG, "SPI transmit error: %s", esp_err_to_name(err)); - return false; - } - - // Extrai os 12 bits significativos da resposta do ADC121S021 - *sample = ((rx_buffer[0] << 8) | rx_buffer[1]) & 0x0FFF; - - return true; -} - -// === Fim de: components/peripherals/src/adc121s021_dma.c === - - -// === Início de: components/peripherals/src/peripherals.c === -#include "peripherals.h" -#include "adc.h" -#include "led.h" -#include "buzzer.h" -#include "proximity.h" -#include "ac_relay.h" -#include "socket_lock.h" -#include "rcm.h" -#include "aux_io.h" -#include "ntc_sensor.h" - -void peripherals_init(void) -{ - ac_relay_init(); - led_init(); - buzzer_init(); - adc_init(); - proximity_init(); - // socket_lock_init(); - // rcm_init(); - //energy_meter_init(); - // aux_init(); - ntc_sensor_init(); -} -// === Fim de: components/peripherals/src/peripherals.c === - - -// === Início de: components/peripherals/include/adc121s021_dma.h === -#ifndef ADC_DMA_H_ -#define ADC_DMA_H_ - - -#include -#include - -void adc121s021_dma_init(void); -bool adc121s021_dma_get_sample(uint16_t *sample); - - -#endif /* ADC_DMA_h_ */ - -// === Fim de: components/peripherals/include/adc121s021_dma.h === - - -// === Início de: components/peripherals/include/peripherals.h === -#ifndef PERIPHERALS_H -#define PERIPHERALS_H - -void peripherals_init(void); - -#endif /* PERIPHERALS_H */ - -// === Fim de: components/peripherals/include/peripherals.h === - - -// === Início de: components/peripherals/include/rcm.h === -#ifndef RCM_H_ -#define RCM_H_ - -#include - -/** - * @brief Initialize residual current monitor - * - */ -void rcm_init(void); - -/** - * @brief Test residual current monitor - * - * @return true - * @return false - */ -bool rcm_test(void); - -/** - * @brief Residual current monitor was detected leakage - * - * @return true - * @return false - */ -bool rcm_is_triggered(void); - -#endif /* RCM_H_ */ - -// === Fim de: components/peripherals/include/rcm.h === - - -// === Início de: components/peripherals/include/aux_io.h === -#ifndef AUX_IO_H_ -#define AUX_IO_H_ - -#include "esp_err.h" - -/** - * @brief Initialize aux - * - */ -void aux_init(void); - -/** - * @brief Read digital input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_read(const char *name, bool *value); - -/** - * @brief Write digial output - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_write(const char *name, bool value); - -/** - * @brief Read analog input - * - * @param name - * @param value - * @return esp_err_t - */ -esp_err_t aux_analog_read(const char *name, int *value); - -#endif /* AUX_IO_H_ */ -// === Fim de: components/peripherals/include/aux_io.h === - - -// === Início de: components/peripherals/include/led.h === -#ifndef LED_H_ -#define LED_H_ - -#include -#include - -/** - * @brief Identificadores dos LEDs disponíveis no hardware - */ -typedef enum { - LED_ID_STOP, - LED_ID_CHARGING, - LED_ID_ERROR, - LED_ID_MAX -} led_id_t; - -/** - * @brief Padrões de comportamento possíveis para os LEDs - */ -typedef enum { - LED_PATTERN_OFF, ///< LED sempre desligado - LED_PATTERN_ON, ///< LED sempre ligado - LED_PATTERN_BLINK, ///< Pisca com ciclo padrão (500ms on / 500ms off) - LED_PATTERN_BLINK_FAST, ///< Pisca rápido (200ms / 200ms) - LED_PATTERN_BLINK_SLOW, ///< Pisca lento (300ms / 1700ms) - LED_PATTERN_CHARGING_EFFECT ///< Efeito visual para carregamento (2s on / 1s off) -} led_pattern_t; - -/** - * @brief Inicializa os LEDs com base na configuração da placa - * Deve ser chamada uma única vez na inicialização do sistema. - */ -void led_init(void); - -/** - * @brief Define diretamente o tempo ligado/desligado de um LED. - * Pode ser usado para padrões personalizados. - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param ontime Tempo ligado em milissegundos - * @param offtime Tempo desligado em milissegundos - */ -void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime); - -/** - * @brief Aplica um dos padrões de piscar definidos ao LED - * - * @param led_id Identificador do LED (ver enum led_id_t) - * @param pattern Padrão desejado (ver enum led_pattern_t) - */ -void led_apply_pattern(led_id_t led_id, led_pattern_t pattern); - -#endif /* LED_H_ */ - -// === Fim de: components/peripherals/include/led.h === - - -// === Início de: components/peripherals/include/buzzer.h === -#ifndef BUZZER_H_ -#define BUZZER_H_ - -#include - -/** - * @brief Inicializa o buzzer e inicia monitoramento automático do estado EVSE. - */ -void buzzer_init(void); - -/** - * @brief Liga e desliga o buzzer manualmente (uso interno ou testes). - */ -void buzzer_on(void); -void buzzer_off(void); - -/** - * @brief Ativa o buzzer por um período fixo (em milissegundos). - */ -void buzzer_beep_ms(uint16_t ms); - -#endif /* BUZZER_H_ */ - -// === Fim de: components/peripherals/include/buzzer.h === - - -// === Início de: components/peripherals/include/ac_relay.h === -#ifndef AC_RELAY_H_ -#define AC_RELAY_H_ - -#include - -/** - * @brief Inicializa o relé de corrente alternada. - */ -void ac_relay_init(void); - -/** - * @brief Define o estado do relé de corrente alternada. - * - * @param state true para ligar, false para desligar. - */ -void ac_relay_set_state(bool state); - -/** - * @brief Retorna o estado atual do relé de corrente alternada. - * - * @return true se estiver ligado, false se desligado. - */ -bool ac_relay_get_state(void); - -#endif /* AC_RELAY_H_ */ - -// === Fim de: components/peripherals/include/ac_relay.h === - - -// === Início de: components/peripherals/include/lm75a.h === -#ifndef LM75A_H -#define LM75A_H - -#include "esp_err.h" // Para o uso de tipos de erro do ESP-IDF, caso esteja utilizando. - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Inicializa o sensor LM75A. - * - * Configura o sensor para leitura e define os pinos de comunicação. - */ -esp_err_t lm75a_init(void); - -/** - * @brief Desinicializa o sensor LM75A. - * - * Libera os recursos usados pelo sensor. - */ -esp_err_t lm75a_deinit(void); - -/** - * @brief Lê a temperatura do LM75A. - * - * @param show Se for 1, a temperatura será exibida em algum tipo de log ou interface. - * Se for 0, o valor é apenas retornado sem exibição. - * @return A temperatura lida em graus Celsius. - */ -float lm75a_read_temperature(int show); - -/** - * @brief Define o valor do limite de temperatura (T_OS) para o sensor LM75A. - * - * @param tos O limite de temperatura de sobrecarga (T_OS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_tos(int tos); - -/** - * @brief Define o valor do limite de temperatura de histerese (T_HYS) para o sensor LM75A. - * - * @param thys O limite de histerese de temperatura (T_HYS) em graus Celsius. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_thys(int thys); - -/** - * @brief Obtém o limite de temperatura de sobrecarga (T_OS) do sensor LM75A. - * - * @return O valor atual de T_OS em graus Celsius. - */ -int lm75a_get_tos(void); - -/** - * @brief Obtém o limite de temperatura de histerese (T_HYS) do sensor LM75A. - * - * @return O valor atual de T_HYS em graus Celsius. - */ -int lm75a_get_thys(void); - -/** - * @brief Habilita ou desabilita a interrupção do LM75A. - * - * @param en 1 para habilitar a interrupção, 0 para desabilitar. - * @return ESP_OK em caso de sucesso ou código de erro se falhar. - */ -esp_err_t lm75a_set_int(int en); - -/** - * @brief Obtém o estado do pino OS (Overtemperature Shutdown) do LM75A. - * - * @return 1 se o pino OS estiver ativo (indica que a temperatura de sobrecarga foi atingida), - * 0 caso contrário. - */ -int lm75a_get_osio(void); - -#ifdef __cplusplus -} -#endif - -#endif /* LM75A_H */ - -// === Fim de: components/peripherals/include/lm75a.h === - - -// === Início de: components/peripherals/include/ntc_sensor.h === -#ifndef NTC_SENSOR_H_ -#define NTC_SENSOR_H_ - -/** - * @brief Initialize ntc senso - * - */ -void ntc_sensor_init(void); - -/** - * @brief Return temperature after temp_sensor_measure - * - * @return float - */ -float ntc_temp_sensor(void); - -#endif /* NTC_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/ntc_sensor.h === - - -// === Início de: components/peripherals/include/proximity.h === -#ifndef PROXIMITY_H_ -#define PROXIMITY_H_ - -#include - -/** - * @brief Initialize proximity check - * - */ -void proximity_init(void); - -/** - * @brief Return measured value of max current on PP - * - * @return current in A - */ -uint8_t proximity_get_max_current(void); - -#endif /* PROXIMITY_H_ */ - -// === Fim de: components/peripherals/include/proximity.h === - - -// === Início de: components/peripherals/include/socket_lock.h === -#ifndef SOCKED_LOCK_H_ -#define SOCKED_LOCK_H_ - -#include "esp_err.h" - -typedef enum -{ - SOCKED_LOCK_STATUS_IDLE, - SOCKED_LOCK_STATUS_OPERATING, - SOCKED_LOCK_STATUS_LOCKING_FAIL, - SOCKED_LOCK_STATUS_UNLOCKING_FAIL -} socket_lock_status_t; - -/** - * @brief Initialize socket lock - * - */ -void socket_lock_init(void); - -/** - * @brief Get socket lock detection on high, stored in NVS - * - * @return true when locked has zero resistance - * @return false when unlocked has zero resistance - */ -bool socket_lock_is_detection_high(void); - -/** - * @brief Set socket lock detection on high, stored in NVS - * - * @param detection_high - */ -void socket_lock_set_detection_high(bool detection_high); - -/** - * @brief Get socket lock operating time - * - * @return time in ms - */ -uint16_t socket_lock_get_operating_time(void); - -/** - * @brief Set socket lock operating time - * - * @param operating_time - time in ms - * @return esp_err_t - */ -esp_err_t socket_lock_set_operating_time(uint16_t operating_time); - -/** - * @brief Get socket lock retry count - * - * @return retry count - */ -uint8_t socket_lock_get_retry_count(void); - -/** - * @brief Set socket lock retry count - * - * @param retry_count - */ -void socket_lock_set_retry_count(uint8_t retry_count); - -/** - * @brief Get socket lock break time - * - * @return time in ms - */ -uint16_t socket_lock_get_break_time(void); - -/** - * @brief Set socket lock break time - * - * @param break_time - * @return esp_err_t - */ -esp_err_t socket_lock_set_break_time(uint16_t break_time); - -/** - * @brief Set socke lock to locked / unlocked state - * - * @param locked - */ -void socket_lock_set_locked(bool locked); - -/** - * @brief Get socket lock status - * - * @return socket_lock_status_t - */ -socket_lock_status_t socket_lock_get_status(void); - -/** - * @brief Read the current physical lock state using the detection pin. - */ -bool socket_lock_is_locked_state(void); - - -#endif /* SOCKED_LOCK_H_ */ - -// === Fim de: components/peripherals/include/socket_lock.h === - - -// === Início de: components/peripherals/include/adc.h === -#ifndef ADC_H_ -#define ADC_H_ - -#include "esp_adc/adc_oneshot.h" -#include "esp_adc/adc_cali.h" -#include "esp_adc/adc_cali_scheme.h" - -extern adc_oneshot_unit_handle_t adc_handle; - -extern adc_cali_handle_t adc_cali_handle; - -void adc_init(void); - -#endif /* ADC_H_ */ -// === Fim de: components/peripherals/include/adc.h === - - -// === Início de: components/peripherals/include/temp_sensor.h === -#ifndef TEMP_SENSOR_H_ -#define TEMP_SENSOR_H_ - -#include -#include "esp_err.h" - -/** - * @brief Initialize DS18S20 temperature sensor bus - * - */ -void temp_sensor_init(void); - -/** - * @brief Get found sensor count - * - * @return uint8_t - */ -uint8_t temp_sensor_get_count(void); - -/** - * @brief Return lowest temperature after temp_sensor_measure - * - * @return int16_t - */ -int16_t temp_sensor_get_low(void); - -/** - * @brief Return highest temperature after temp_sensor_measure - * - * @return int - */ -int temp_sensor_get_high(void); - -/** - * @brief Return temperature sensor error - * - * @return bool - */ -bool temp_sensor_is_error(void); - -#endif /* TEMP_SENSOR_H_ */ - -// === Fim de: components/peripherals/include/temp_sensor.h === diff --git a/projeto_unificado.zip b/projeto_unificado.zip deleted file mode 100644 index 3e6a8a57709495b380f4095e2b5fbb9849930c0d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 53746 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�ykP5c)@?Dn0xy<2m9~9JdJ&^8 zf>tPbPZ(DI9T3+$G*ZT0-nTK&W{ii2gUdIFX=Y zgUGAY;ph1v=wFaF^9BBkc+pkf;Hv<%e-sU0AxHkgNKJQYlt3ec*T!Rh2+yYcFk7Uv z;1>ifbI|?bPffStmG`lD_zTnIvC5&YR4~1IiU1DSp%OmD%UhxVtT)D`cwA?~`A9`K z!GBDTANwY8(fe5$!ZwDJk-l6BKY**`<8d}j;hOjKGSdvXbl%2w>08XDA`f)oJg@T=+R*}m?+3mJTdyS|701Tob zbZih71=5*809U^@N+LDS(@SeWeE zA}1#~%w$zVb{N__z=}7*$ z?SM4qpX|}PdcW)G2UX=tiHGr=|5W7Gdu(}58F;cUksQIP#TAGo^vd2|8EDD_ef*+4 z2h?d4Rl~z5u4@#?Mp4MF4mNb&Dmex5D1SxzQM2@L><3F*KBwU3V8w=WIQ}WjW~jQn z^z62b@>uX<99?GSAsfyqbV-vIaEHpmT4?Y9?|5N&VBE&ze0fD_JO8TjDcommQp4Wk z@vSJ~npNN?hp)1$^xC|xjv@o3>ksw!+_8&sim_nOTMK{UtLl4gH7XEDHveJvm{o!`?!KWPP%mYP*&I50QF`Df%gaCQ z)-L|r^g#t7*^_u7oYr6mW6sTXuW^Eh6Fs4TXLBJ?=SCiPHza^`9fE$9s4)h9aOo^x|O*^CFMG z#NG)*pg{b)eTxsaJgg2Fu_rM=(l`c1A#sBXh$Ane@5}to%`BR?6-xfVYCrsG^d7;_ z$=>)p_|xdWO^G=q`i^~_AD11oBz}s0RdwHt{=^g@X?Fu-r9}5{%f#6N0-}bJY>$9D zVjpO<4te={~T5E4QH3j&R4tW;EmS8V3tom}YdesTMfdJM-n2+_8e5;fNFD zVyvx1(|F%XWSUkJF$X=@C9@4pMbov{s#ggfSiha*(RAm_*N{Pb;1aR6u$>X)OpwKM zj=W{|RFon?Bnp(gHVPZ~Rhs@pK!xxk3#LkbOHE>+v*La#W%4@@sB}juy?Pps2 z`{X9A?KonT9xMtjFi&2VTuv#?VL)<#toVLfvLN~}913(wR@@-h=aZ3w#su!L6PqnL zSr~v|8VlcooKGg2&`iHf+q>y5E>Iq`o>Sg-BdvPgN*%KL!tJ%0&EDyDGL^knvU$V4 zcUrGohp$^SVD_6v=N-LS+hNNfxrT_womg+LYeui}_>*2Dl>Drv2S>+Ux(k4h2>sXAVMRq&Pf&ABGu<$rj11tVqrl(nX_dZ)PfAb= zlxd6=Ro1WSchvUx{Jp!n72Sg`TlNoX=ni6ayNB>(k?UhU*GDM!IXihNhHzEi3iniU zNPg$LD$f92Dh9MSPafhR2i%Q1^?zvUF#E0|4{|wFqxlg}i+1H~#zIm4<(KhPV}>>G zyVhy)a|nK`DFo%U!eLOkW7-pDV`g{R3hXP9}+HgyP{NJt(IJfL>-XhwQE(t0WirZ%qB0EzieEc2x;Lrvm8b%LP2l@F9`8 zg1vc++t`>>R|62tpCRIGOB^eK$gMm>;w}WaC|Abyl}I?J`ok&jFEEdBCKA2_>t`vo z`)~1ZAp?MO;t`56o=!&zb`6y<#SYOqY%r-?Jop&dZWxPv*f1h9J<{S;b`|jnn((rs zGqlL=*&NXT6?=-O31nQucHJJUOcZZgLBD%6c-!c;f_6VR?RCFvAGQvI2aP`bhI#e2 zJ$QrnG@(SV(HXo8x<>&<7yq{1IjjV&?@xQJen05;5L)}>^tj!E$L&t@`0NmN4Z$m@ z+UWw{w}IgS?x0I;3TW+CAK{&}dd)ZRv+=5Z+#bB6!^5NYpo5T)y1k$goHlxccJu7G z(F;z`dZ*of3y?Sj$eniQs0R(TPFkJ8E;J0!g4TENBj~?r93NwA2(EDk-RL2rL9=`M zuGfD3W)R@5;1)c5)dKv;pNU$7-ZhUKZIEw=jg!Xf7S-wkAnGKRWHf`fZ(8^iTW!F9 z6we0x)9k`>0Y59y-QGaee%tQ1DnX;y?jvQ6dfgN1DN+|IbqNks?QmcmqP#soPy~OU z^;-&NaM)@b1Gqj`7JZfFcg^F-PxK)XB$-O{ZmEfat6B{XfgPhP=*N=;xqdlZ07o{{ zIWVFKJG-2YILSLrBQ-~&5l<1cVF2T+nbNu$#;`<0*xD4u7jr$$5S|tl&RUKx8?3X zCy(Pr7G}5Zt!%lxnEsUAPU~&E*FyUMa8C%V1**aRe*XQp?NfRi!299QQ@;UIku(6< zB2{zmo4w{iIKZi*_VHxDKl?r$ygO|LljRNQAM#PV(;l>eJ^u>&0!J}v9loH|6k`)- z!E%PO^2bSZ6Ji$i9|NHHJk4SxyUsf=Opq`@XnuqWmnj7i*hN4+ksSadMW7n2GXcP2 z7>T%}GUve*RD(IL+#o0^VpVCJ{1lIG$)D;9kd8;?U412dj6LW0SAyn*W(bId04|q1 z0QGj+7pbpstS;m)vAgm3cb7`CR`6Buq#QgB_RC&3Z%4<0M$7hD21UHlN*wYJ#}fo> z_bug=P|9BqK#*!%je`Ws5locDJPK+{S`a5>Y^2i{m8l{V$)M1KJO0}eAf`H5+XlFB@9P%Xdq`? z;v7pLc1l!Smkj>dwW&$#Q}-}-cERt?OpeV*zA9MX6(o=W_9Y@WJXCLMExK6SdXDdhhkaLMT z>5P~hAU&c)JcM>Mm~QR1?cylZ4SL9Rk&9VV;C4xxW2y9Y~o_TUXZNkMU<0<`lB z?pp5D$-^6(IRrRl_=1H%tpx66nfv4VbbkX}3sL=8EdN(?s`SqQF<~+YuCt*QH_^z$ z9`|93o!Xt}vsXgY*9ZVQMu-V*p8XY?fw&;h>HrIukZLv!l@$~(eHASF1pEZ0V-`&? zcRFs&p`F_Pl6H^e?dKC}+YHcLE_$3eE+Q|=UD^%f^yR(si1hP}7ULaWjOmmM*QfXf zltIj5c}45YcscumoF}pbol0p7zZ5R&q`i@rcZ~}lYW~E-6uJtQ)b<749U<3tP~B6I zRA^jEuR%rRl<E~LCcQ1DX%=`4JUd81DJ;O45!{rgbGB2>0m=OUrF5hsnrAFO+X%50h7>5dHm??0IgpHVp(%JAXqh;<5rc{f9x4P0!FZPlG zSn}(j>h0~0VNOp)AkR4z zum;6LlR}D!@mH{jRq$ZQ({VqOXYm+hR<7UjwV7$oIr2#iCl(*y=6C(8RTr0B2i{$# zuO;$@7ssZXkcrBF^UcfPK$2+#I{lQ)!g)F=m@_hn^9nKu<3>3To29THFs^&nrWQ3> zk)8KE#x?0G9*<1j76mmvPZnaM1$4cRC9p|!8=zwjtje^z!jkE74s7rNSGA>Z>btKV z>THqd)oT60KL)gemS~LPRh07Y%3K&(9L>iQj=Q zIAaZr&S358PUE*Q_RpUahY1z{Rcw)p3zV2P`vcx1Vk^ha&JL{SvvD-!Z1KV(!xxW= zEW37t9XsVYnjPSd3*zkal)#t zIlBzoX;2HEOMW0us?cbQTkJ3v@Czs`?p$F#@u}*1C@Mu=lzg~dpvI7}&N}BzNaV&9 zle9=;54wll=g5(udPAq_t}*r*Cm}BJkHuX!j3k@cbDGqmkh}N7dPQ>zPhKmc{gAW3 z8=EJXxv0fh)h)nyXJ{7kcYf_z=S8zq^w|Vfqxj7V^h7CDY&&L5l%Z1FX4H+Lm_2ow z;)IdeNA2^c`lscN(;?3b;fGi=#gCwip}-rXE!=Pls!H2f~i67_K13v{}VA%AeESa!hfNqZX_@au-QY}}7u99%`U%oUtl}7_V z?sUZ~hAa%IafHI6iK9h06L*6ih+ZVZZNubRcqSB79Qb3La)4;pB4nAMOm(Y1I1>3~F=;$G`7~}-5~|pKJYrn%oNdl?c9v#zf{tr8 z4*p+3(iaZZmoApYV`45Fp4HR*coM=1_uO<&&R}d*!&xAdJXAF(+JZBxJ~(KR3}`*F z#@jn%H|yRy&idu0Z(TM&g$%WpXoc+yazWGTnZ_?o%EY zpf(_nL+9!8@`@HfIqiT%JI`!=Ap`5lY0*hDoaT&xbm-( z)37R*&bg`nri7NmCx8nFCc>4MBs{m3(?g13zjQ5yc~oo#kxeZg%(Wg}Gu1=>Yv|Q# zN6nioDfy9pVp5|2Ca>LpX3&3D+jMks!AoM8(biSJB?70-5}yoHur zs3GSLCCQX%HA9w`{Lbasi`;zvhD@E#)14icK}8cK`uA&`JQ&eqwE%Yqj7LlQCU<`d z+G4P@p(9!Zf2tnb;Qzb3yX3X3Z=9Bd$uz*|Ddv@Mv9P?Avw{-6WHsF`JEL`s7XuGO+&aIl_2whJJbII7&WlC<)KVjL^ItLWQ6g zkDxa+=1rVH8862SG6jqTaw4(axeItpFn+*)Sx`et56K7FR35 zU!GKg+QC63sMnvuuVy*hlM68rE63q0NDI1aV_XIOe4 zp6+4cXIO?x!=I-BWDi?|*EIx>We@%WFW}{05IDZA*Q*R`4~y0D&;DN!C;)s4h3bgr zKH~F~N@Ib)062V4ZBjR|Ic(}c;Pn?O3$?JRI)10J0QfJ60QM2z?jc-!U)x78_zIE4 z_w{|z$7cxY3AxYUE3Ae15wZ3O8?1sfs@DJrQLRc%VzC2wiG6;?!0I)8NW`kbA0poY z_7b}R|I(lU&}Rr8s0=>{S7>_g0NX<7Kn?C?72YAS>NP?EiUW4o_1Xa9 zd5)#=TMfZJ!9V!z36;m5>|s}+)In83Lv*PijtezJMGz7eq%T!?horCLmm0MRO&$=Y z*xH`pG-8S>_5^!T$8QH%7%?S2p$3o~wPyeq>(;0=B2k5R_@!18J*^|-)R<=QhpQnL zR2*O;1+WPMLsTVX;VrUaooENm5-?;v9Cw;nNOYP-L@?x~dX3t`63B{7Is8TaN3MTH zTr4mnp2e;m5W)zAs1AjRmym`$G&qP%I{1T~uG0)cl%LA^iQ~=$!!GU4IxdFVHExf_hXN{ZANkUZB(gKzu_BQ?^@1`pFeT?zCXtI4C)83mB_PpEZs1 z*ZQE6VMi7EJd^`1Md=k>#$1^2$$#hAUSECn-agrbcc>Wr6Kzem8)mVy9N+Hp%F50v zlO?L|V>}o{Ux|fyz@viR_Yrdg>@|Ku-&DkK_wPm^t;#7A|CBa&FC~2(%~5(^;{hG~ zXA;_03sq*r1fAMTOmhZaro-^6vsc3scvf z*C-8B5z4-Sbzy>64OQ7C`+RGlAIMW2g#eVtEz9CM6)eb=#G;~@4#}}=A!&VR2LTHb zE*Iq1Jxgh7PsRN^3r?jN&Ldc<9^vLJf@O4vZIg@mlFx-D`WkEeMrX&!)ojHtr8B;| zRHhnYZj#mbLb3KFS^)X7UE48*D2gQk~bY_d|;*#bJCi%_hkLTcYGQ z=vpTzC0M*(4i4j4%-e5qrjNg(>CP10E>iMWoug)iHnenJ5w`ScG{05cJ&*YwhPX;A zu6+V}J8baeQDwG8MucbI}= z1W<2|&>C_<*9XsUy-c{P6SLcMWc|F{Z0hk#aggpaJ*1-%bQSpehuTvzJ)qg4M*mgi zU-L=Tc!gh7MkLj6;`k;B$3H+i=bePBJ9M6Zs`W{UI?7Z1PQszx;P3WLbD4zOa;J;c;l0s5Mc! zIw@{P?*WYa24vWi_vM^MM;cnQS~ZUxxB?zTJhd@^!t~C?UYuDJ7xk%in};i~Lk`?E zV8PWN0So>JSnx-{fY}m^CryQ@`p!uGAZ1$cl2}%9t+W z0yFK2KEvf4Ra|5PHgn7I~=s2pApro=HaA&oVC@SxUwi<|{mm zk3<_IJQ++m^6P_Xx|1!3S33;r0Zo0N5VMiXGK6ZWWRFd`gesc-IX*6z-7xstSKajX z42HMEu@d}Z6M$DAX)(h4^z;ixdJ2mevzP4%t?CN3>PI|@|B)`Rqv9@nQY8;UqcaLT zK{{&gLc5WkBKVO`vwyVC=Am9p4834%N+NuPxJ@E1+7pv8W3RsqpG(@)CTCGarU%Fk ze7)#_^c$sg9P|jgfP19javaIL9g-q#I!*<;0UyEkk!B3e%ID8*gv!#R5Z|&>IUd8x zzyzML*=lfPO*;FgnkIh>6+jG+>Q~{Ql9J~m$&hSBc#>dVb0E*#QPH^aeTHkE8Nzs$ z8?}O~UA(LFUGQ2vqId}kiQpU0M=%DD6t@Yz(_r)8$)K(noP6&--_R7?Cd-Y0c@XY|a5OmmE$}GN`uwlc}M0spMOQN9u5H1cu{6*iK|XkVG9ACgflLSV5Z z#knlp5R3&Xor$Z7q_+s+o1mz@-JyDDni6Lbf&fPVXk|e>?H*pS{3q$b2#`IV6?j@r z7{^iYA&Gcr3A!|zQeZCb7>f^iSD?-RJT{bs3vm~dOl!IiXe6>Iq80#>_~MX8>I}wq zVkFl%b#oDPZ3Wm|O%bZ7C$1gGc#C%^o&7+w9>bCc9YzAM-Ck%cyiIa1EtlzHX|hnR zuNrK$T%?mokXc`Y<=ZTeJQ@-%f9T)9`WIaljiwaLN~$hu(xIb^bAz16uauQ({f^|w zvH4K!>PZRU+l#zE)y$+0Tw49pz4~MLdr*J+ly-vXj)E91XEW#sjyPWZkkgzjowRss zU@^tdqkbn7G_OT4HA&r2l0~u`A}SU(y7WWij#tq%V(~4N!UaH#Fk5G-e>*5IrWpka zk|Q2O3k^`xn?I#wH{R7)NZkcCU0FH@DZt#%CsZ_`=*mypQKW%90AYmZXu;rt(I~JA zUxL*ms}#a=xonRevGN>5Uowr&e7H`uJJAubpNqyQfo?_cB8g2dKtRQYVuWLh7MKQT zp=VmMaDcI|i}oMpfE<~Mx|L8Mrl7=hVoiD&q3=>8cF=4G;?F3WpwC8*t;(NvZM>T_ z_eLGa3F|&4Sp)(P6Qv2KQ?)uNRNldyOVLG4rmdx`kT@+^pO%;1#|qqBeGGF9$*dg#F>PvBVmZqfH0{eoP^L)t&-zD`K2N5BNDDE!?xVw z@eU44%1+YBokUUyFuDShlf`!*_s1IrkH`&UQ(XizucTuSX_83+&Pc1%MoP^%<->7f z-Bq%j>5!T;-n8!7y=6s5G*5kY?Qz2RC(Ma7Xr3;wjCB#nC>41|#y}b-1!$8`-*7!v z%Rd^T7G?-kqvH=a0tIbGAFS$~+ElIX;&L{kzyO*fj1U(Bg4L#tbTM%XB5_FS_#<(6 zNT$f6hFn;N@|F2p5OT*iZ*9qxm_AZr?7%F_rnL zLTZs~RlYEdD+}rUG8MOo1l(P>8uyB%)+8n+C0aOuat6V$ObbHgQbqV(WlAl#xV7d-^f$5KHIshK|k)Zx)HDmacZsAgDbkX{3 z=}}q)W`#qkVIm=CFx~PxueMcob;G^X(6$#U@^Vhy{)}Fy+e%c!^HYh`0{72teLoRc zf&1rOd_NJKyVoG zzS*eX@*r&(WxOe;dB26#T(Yjg(K@T?L(9Czc z!5Ogn*URZ}9M6}TT@mZ{V&ykSwj~U+NOK7khvi&2qw$=;xj8{A5^ka}P8<8}Ar$h- z6PKVXY@Q=OI-H)$nyN&F&n=EBP2z=|@G#wN)Hr4`<3t;%39cu=h0<>Bccznz3hn2| z37#TfSj~!?b{Z#iIT=UEM653LJR;DbK@o|_jIL_Op9vSa@LJB80-o@2WMe8UTNIKb z(}Y*MUb7q2xxL+>6DL&9^q?XiAB8-67@^HL)rDvt)7vK{a>_J}PJOY@Q(Fi6b)Y9;uxf+kL+`&(^v_=P2kpVxpcTCCb`PavX}{I`uH9_)Uj)b9 zK2h!rT`3P6g9bGZP=IXk8os|e>$i!3ZHyq_J3B>Z$}*7PEzpOcH5*X-kci#wP@jQX z-5$BN`n@}~LuVhT*Bltd5Fq+xqNAtoY|v@FK5oBmb($^9{j;pxKik~$+r~TT04iKN z3E+{<%oQ5jpnViH4!>(7@}fL2M87SVhsf7_BSD9bxy|vebOBh>Dt2E!!Z&M(=g$yT-8q%<+xzx2lfNV2XzA4m@rjVg(!C-IHQj z7lQCM1S}7Y*HBM#=>sUF`zlE_fsMyw?+xcOkqE`iPE`~~(+$((_8XR$Qxu>^ zd~%MV^O8>SzQ>X7ywhNEXK*@Nq!mET@vBpz$2ZHH-b)MR!WaV=4vMCt=#11UZl*);VIQj`Wt-_QCm0Qq%@@eE3{ z(MdlXbVI23ZNKykGZej$#d5Tir!u-}I~Y695W1(CXD`nEs;rlrv6&gsAGVc}+4LL} z&yCA!;-bIpQq7KZY-sRB!vB3U>>7@ARt`xFUw*g)CyfP@kK>0Ii0XUwwf0`$M>I%P*CqD!*C+bxzW(}De|`3zE=5=Dp67y?xy%Ng z-+H2nq8pTV5w77475tp&XUOD(GDAk?@J7&eF z@5}JuNx|soK@md&W$XJ}2;5IUF0@+|^e>x|^c6rkYMiu>--Yc%GfINMvv4XJ17Hs| z=>zP-7X*HQUfx>`&@UjwrZ5CC=yNpx)C_F1q}-93h-0E4JFd+31oTeTe~_MTH?m&l z{|D)*cb?v_ry+R=uj%h2f*;~Bg*t_!C*=2U5P~BfQ^7nTjF%YMNCVh_EQSK6sk4$i zqGO6W0(%o}6WaoEWy5GHZWrgTLTpDQe)Na~7w4&?D*I`ZcQ2$bE3)A5G9e>GS6}$R zuxsT*lMA^fP3_<@U~CBR10pzK%0;l;OFb$^?AsB9JrF?bogR5;_i zf)t>-U1JBRDXt+dNY7a>zL@06`%3W8z>v3%<`^P8ZF{!HgppsMI;TfN=FN;V87^%@ zX8`*+e_LmOaXb<=^^u6@Fy(>oH6Nzul$^br_zV9ySK8kj~m*Qyrf4Q5981UZFuAHf<-ZAh(0_80mvY z!blG&qk-Oz93hy^m@)X(hzyZVgXeN`8h$27LHFUsg?>6f3bRjM$d{PJ%m*yO8D1=z zPjPzkH`}OsK2D}TiCOW*N2;;V#}D)q|7JfRvD$~>N#lD8MOOXzLKd={$13bQ9ug){ zxN~-VtV+{JkcYo0%-;SlIeRvz-CLOh?`S#w_5b2s>vdVW+7DjFb11;V=j@ExKxc6LJXI%#85}O}Q^I{4V_}&puG0 z*YSe>Z6B5t5k;k)ZzuuMm*T5tQ={wjP9jUi%9>{}2?F_~6SkPyLje6}`Hh}G7r*2* zq56zW^D3HN#-||s(h;{s{jN9@T(wcI7*e1tCGt8hZBLUSW#fy0S^qO#&!TD+FpnQI z*B6GoU}1*~8h=-r2=KE@!3G2uoOBOcVfSosdN$C;?_D4rENvKiM-&5z*9#BZeRQ8N z%Hd@);P5b>%7Ujbd(TdbijLCjsZ;o{`?eFdI&SG?N||9qS{u%n^>KxrRtub5Y(f%9 z$@^X6evA1nLpx=1HkS$(H|Z<~OSIJ=bWasjZ_Q9kY-)iA!|vQKFN0U@!P6)Hwh2Oc zJ)RT+FPUBz;o5uyno+y+x)@&x;eI$$y&t+7L>^Z!s4tzA zO~S_B?S;I#GiG1uq4-+P!~W7;RvZ$zYod@vpj?8)d*Bv$U>{qp`q)y-uhNva0XOKK zwJNk`Z(gf#O?5QkRYbxMQ>9OPJh!YPhGmspf*|}j$B##R=!o9NL~xWX;({BiOAkzd z+LX3|oNt`AgVSIC4+dQf-eG^ZIEI$e)&>#1}z2V z!c1>uujBw&O)5}1ev!PA&a72D$0Q#W88kkYQf(s?*g6A+j8(m;IG_-p409VWbN+5WckeQ9RZ)QfmX^qT z&grI_v-h6#&e(Vw`8=}gWHG!7N(wuTz%a^q%YNDzz{=JMyGKXQT~0IvE7HGO$)$dE zk^|?VcGdx{Z4dO-al7*Y$PhR0?K-&on8pYG+y!=c)aVb^H9&cS>UV6Qf82e$u7N#D zMD%Ry7Njm4wvLWk%}x4238PlEv+4TX#5(}#7$dUGgBG+g+F-A?{hnWCstzJ|?ok>p;%uQeS8jpDJh{B2U~a`l z06@6991V86*Ek7*b$bCI50h^mUeWl@X*?eV(fpFq$f!(Ov?CH2+6lHSw0O5AUaDQ0 zS=dF;u7O|VMuRYOEyFnj9F#IAMWN;Rqq)5kRCHd7$QI2F@!mN*Djr^KcYsR_Hk(@r_EODbgC_9x9R30`G zZ$COC6gn+$)ojfaZN+0$&Z!<-kB;pQBf7Lr{w2KvZ@YF!qhW6C#%F~qFFPMdPXzO# zAXGyi1o$U}#UX&g4gvrc;u)|l{2e^*?Z(5LY=wNQG9a4W$>-3zsJ5XU3x=W|Moq6Nl30mosR z&Bk#%>;v=@*rJ{FzJu*YdoaL_7G9+7_6Ds(#h_nmNbW(=arbpwUU3qIFb+jFyox6= zhVznnuKtugz%tAEhZy*0fw4)P{&NMKfAliGr{gzHJ{4-h_v(qsG~>ahm+mKGo6KlifGu?%REV;nIB&qh>G{ z%|3ZK*Y2M+j+!oCS|nYw+c|2#hHc$ZIDly%vrpQEu%JySHqXppFY$_w6@#pJEf~U}<{c;cA8N@rCjDwB4b@ zjFWD^9n|;o&)fZOQ2R^ndGq+&AgCSWpZ8%<_BH(eX*+z=?GHkVrGX!KQm03{OylJA zxE1!>e{F@Y-VIv)pswDDY1G%OSgR`=0+L%~c(RsLcy27C21W8KDuOs)W?E3d4_HrK zzn?4#*-;E$m?SeiZzf5WdI|ykj6&IP{8PRV5Ys60w`DZCp6OCMHRt8ksNm%!x(O-h z*#+!%vY+8~j8_!AH~jz{4Yw(zt{p9<-pPlJ;; zKbv%;{bbc7z$_VN9t!lWKE_LsU&+r{*$}b27vty>w*kmhhb;{C5_Y=b;n~T_JG)=5 z(2%2y!%yyFamiv#3dV@zUSZfL4X7A4&IVnn<`zMO9i^aP4kD9Jh*>CKF6+&N! zqp+SO<%aNxAC#R$Xp5UrU9Tn&(HkT_{{6@yDi4>F$!#$B-l{csYndECEE7>m0c^k? z@Nqo7qy-J5w;_BE9hm773L{(^YYwybsQ| zc`GBRcXiu#i8Oxa)<^T5yJD^LLSO#;v%8+o=IIS4g1vQs4Z}I^Rqdx4NG#lx!aw$l zoDDTv-h}Q7JzW5gQEmiUPH9mwdgIxn2_*Q9aUf{^=1y6U)P_%9mX>|Ao}x`v_s4yu z%$Mp>JdNjZo8$ayT&8kRrPoq?6#v>Ie4+Pl(xf6N~EqR%;bhNTuFhukkXoT79CGpz4qywRIQl8NbQ6tj&~o1XX>kIR z_zo${mbH_rpm0KU-Utt4mBC`6-Rlw2kY7zv6| zE2_&tx)ejx_|5TiP2y|yhg#J?v1z)AY+i>#c;fELBZ8I3i9??wQh-ysTzqn**t@X+ zs{*7rf!s8LE%4`{6GJW{7hZ(W)G~y(lNqJ;%f`tix~Ao7cC3oIng!L!JEY=NJ591-gq;e83GAk!7tq!JOoP&XwK|~` zmyZMS_2Q!Jhe;kM6&L3G*Z-I$u&F53sruu9{w)H5o`yLPW1?|sPXItCLmvdkUy32L zF5(2UYa|&iKr}eR2v(s>F>U9*w;8nDda*b7~`?xEaYe4P3Vi658)K;B#7rS!!ks=nVbP2CNd1F4k*H8-E}0S zxS0^x6C1mM>{-GD0lS{!>9S3U z0v~os4Zk0|wK%c-zk`aUSC+f1OL7x#Elsb^{)*vLjnzp%@h(s+=C{}p2eFuuQgIcs zgOauV`X7sANK-T>4`Om_G^VM!srJv*V_?@n33}b?B$|TE;;CC&W+~;nn8PZVF3H^? z&dT|A(y464dU|nD(0F5!d@$NXPiSBlp!To-JB7|%BsaiD?o9HudmhKLaFXdgC`f3F zN7N|XflaJ|zLPSpY%sPu-VA%KnXk%P^9<)j!>7AA$xsj4~Ps5d6V%ksv!xk!aX7wj74T z*?5`Z|6jy6v|c=DJ^+1Go{+9!YBs+yvTCVsN<~K(U{(Wt3!la!r3z|Evt*h|t$B%x z4yiS}&gO^+wt7lr$y&UQlk=E0@OeC3f|io80)slsSl013J&IRJ>0kd>5E(J6$Wm@M zEhpbqdL8s*jt5zBDuF`v>wg6CO>&-q>IZ@~8ef9I#eqcv;hq!3yE^yU4GfKpKLT>K z0u+FaB7B9(7R};mgk7dNiTWadrdAZG)tqZs2#p0Em-A?u1=M+b@d^lYcM;MHeu7-2y16q^9RE*3tY@!SwY<^`FmR zunbKa1hAw$N=W(PumA5TxlGj@0}%wW=qf5;85EU(9Z>`Ry*n}B=M|-+!yJOpi#P8! zB+b>WO&bj*Pi_MG_oWS>$HajzdFph}7r*{bP~o>3+fflgL>Ib()M7b`1FTlS;fNU{ zdnpFeKl#_=nIF76;0g6MLlmj%$Mj8f4#IyC;dsH?DSt&QQs&E<+EIGhDV_#3X1gm< zb?(-!1XZO%$TFXmhoE(0Az%JVH85IB!o?4e`++ zq*g-5q!^@Rgqys-En{~ju=w;MO|ZuF5+rN6VhQjnlZ28F{rVsCB=wDMnkA`LX!6Uq z;q#_9j`eWE zEwLOH)ewjDw+|qk!{L>KBgj7z*7NiNw!1g#fGC{?r>E}ZlrIxhVZJbv;Od5P{-DE= zDC;{IG0Z4tV$S1&lOog5EROxnig?rVtRJ=yWoRbP z%kF8b*BB^6xu*nvM;j}8I~Eq=TsE@U7auL}0Nk)GGrwh3vKf~Tu)NJ;IX448lf8z@ z1`Kh?NNF&quvA}ORbW?}&N)kS=exekYP2oA#+dI|9n*q>a{Y5Wr|j>r(nr&wed5FW zRjRR^7T3{xSgRvhs1OVhyMkWz@7gO~+wC?Ah>8oYgACuvv!1P|cuJxd(|n-8G)G@F z*aDK1OiyI88kwljS}4KLRm=_PQqZZlgH#7$5!~>)m{Ix;x5i$$K52LIX;9?53-Jw& z-Wh~J>*Un4YjGa0c68XxeMdwBw{|JQUT{wHP)~1j+pb4{<%!906QL2Yu@cAFsy zrDc^hk{L(*@XQLE-R>Zx@(lj}15ir`2xjD4C>qfK02;~z08mQ-0u%!j00;mG02Nfu zT4v;1C>qfK02;~z022Tp00000000000RMpi0001Ra&Ky7bZ=jEZfRy|V_{@(E@M?x z4FCtaK}~3SLrrLHLrrLPcnbgl1ONa400aO4002-+1qJ{B000310RUkD004W@00000 DKO53^ diff --git a/readproject.py b/readproject.py index a047a1e..9a65afc 100644 --- a/readproject.py +++ b/readproject.py @@ -1,6 +1,6 @@ import os -TAMANHO_MAX = 31000 # Limite por arquivo +TAMANHO_MAX = 100000 # Limite por arquivo def coletar_arquivos(diretorios, extensoes=(".c", ".h")): arquivos = [] @@ -15,7 +15,7 @@ def coletar_arquivos(diretorios, extensoes=(".c", ".h")): def unir_em_partes(arquivos, prefixo="projeto_parte", limite=TAMANHO_MAX): parte = 1 - conteudo_atual = "" + conteudo_atual = "." total_arquivos = 0 for arquivo in arquivos: @@ -53,8 +53,7 @@ def unir_em_partes(arquivos, prefixo="projeto_parte", limite=TAMANHO_MAX): def main(): diretorio_main = "main" componentes_escolhidos = [ - "evse", "loadbalancer", "auth", "manager_meter", - "rest_api", "network", "peripherals" + "peripherals" , "meter_manager" ] diretorios_componentes = [os.path.join("components", nome) for nome in componentes_escolhidos]