diff --git a/components/evsemeter/src/evsemeter_ade7758.c b/components/evsemeter/src/evsemeter_ade7758.c index 76b1e4f..ed8b8fd 100755 --- a/components/evsemeter/src/evsemeter_ade7758.c +++ b/components/evsemeter/src/evsemeter_ade7758.c @@ -1,30 +1,13 @@ #include "evsemeter.h" #include "esp_event.h" #include "esp_log.h" -#include "ade7758.h" - -#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 - -#define IRMS_CAL 53416.0f +#include "meter_ade7758.h" static const char *TAG = "evsemeter_ade7758"; esp_err_t evsemeter_init(void) { ESP_LOGI(TAG, "Initializing EVSE meter (ADE7758)"); - - ESP_ERROR_CHECK(Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK)); - ESP_ERROR_CHECK(InitSpi(PIN_NUM_CS)); - - gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); - setupDivs(1, 1, 1); - setLcycMode(0x00); - resetStatus(); - return ESP_OK; } @@ -33,8 +16,7 @@ esp_err_t evsemeter_read_current(float *current) if (!current) { return ESP_ERR_INVALID_ARG; } - - *current = airms() / IRMS_CAL; + esp_event_post(EVSEMETER_EVENT, EVSEMETER_EVENT_UPDATE, current, sizeof(float), portMAX_DELAY); return ESP_OK; } diff --git a/components/meter_ade7758/CMakeLists.txt b/components/meter_ade7758/CMakeLists.txt index 311eb95..602527d 100755 --- a/components/meter_ade7758/CMakeLists.txt +++ b/components/meter_ade7758/CMakeLists.txt @@ -1,8 +1,7 @@ idf_component_register( SRCS "src/ade7758.c" - "src/meter.c" - "src/energy_meter.c" + "src/meter_ade7758.c" INCLUDE_DIRS "include" REQUIRES diff --git a/components/meter_ade7758/include/meter.h b/components/meter_ade7758/include/meter.h deleted file mode 100755 index eb60c6d..0000000 --- a/components/meter_ade7758/include/meter.h +++ /dev/null @@ -1,108 +0,0 @@ -#ifndef METER_H_ -#define METER_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include -#include -#include "esp_err.h" - -/** - * @brief Grid energy meter model - */ -typedef enum { - ENERGY_METER_NONE, - ENERGY_METER_ORNO_515, - ENERGY_METER_ORNO_517, -} meter_model_t; - -/** - * @brief Estrutura com os dados de medição trifásica. - */ -typedef struct MeterData -{ - float vrmsA; - float vrmsB; - float vrmsC; - float irmsA; - float irmsB; - float irmsC; - int32_t wattA; - int32_t varA; - int32_t vaA; - int32_t wattB; - int32_t varB; - int32_t vaB; - int32_t wattC; - int32_t varC; - int32_t vaC; -} MeterData; - -/** - * @brief Inicializa o hardware do medidor e recursos internos (SPI, mutex). - */ -void meter_init(void); - -/** - * @brief Inicia a task de medição. - */ -void meter_start(void); - -/** - * @brief Para a task de medição e reseta dados. - */ -void meter_stop(void); - -/** - * @brief Zera todos os campos da estrutura de dados do medidor. - */ -void meter_initData(void); - -/** - * @brief Retorna uma cópia segura dos dados atuais do medidor. - * - * @return MeterData Cópia da última leitura válida. - */ -MeterData meter_getData(void); - - -/** - * @brief Check if meter task is currently running. - * - * @return true if running, false otherwise. - */ -bool meter_is_running(void); - -// High level energy meter API -void energy_meter_init(void); -bool meter_get_state(void); -esp_err_t meter_set_state(bool _state); -meter_model_t meter_get_model(void); -esp_err_t meter_set_model(meter_model_t mode); -void energy_meter_start_session(void); -void energy_meter_stop_session(void); -void energy_meter_process(bool charging, uint16_t charging_current); -uint32_t energy_meter_get_power(void); -uint32_t energy_meter_get_session_time(void); -uint32_t energy_meter_get_charging_time(void); -uint32_t energy_meter_get_consumption(void); -void energy_meter_get_voltage(float *voltage); -float energy_meter_get_l1_voltage(void); -float energy_meter_get_l2_voltage(void); -float energy_meter_get_l3_voltage(void); -void energy_meter_get_current(float *current); -float energy_meter_get_l1_current(void); -float energy_meter_get_l2_current(void); -float energy_meter_get_l3_current(void); -const char *meter_model_to_str(meter_model_t mode); -meter_model_t meter_str_to_model(const char *str); -const char *meter_state_to_str(bool state); -bool meter_str_to_state(const char *str); - -#ifdef __cplusplus -} -#endif - -#endif /* METER_H_ */ \ No newline at end of file diff --git a/components/meter_ade7758/include/meter_ade7758.h b/components/meter_ade7758/include/meter_ade7758.h new file mode 100755 index 0000000..198d17c --- /dev/null +++ b/components/meter_ade7758/include/meter_ade7758.h @@ -0,0 +1,70 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" + +/** + * @brief Inicializa o driver do medidor (SPI, mutex, registradores ADE7758). + */ +esp_err_t meter_init(void); + +/** + * @brief Inicia a tarefa de leitura de dados do medidor. + */ +esp_err_t meter_start(void); + +/** + * @brief Para a tarefa de leitura e limpa os dados internos. + */ +void meter_stop(void); + +/** + * @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); + +/** + * @brief Limpa os dados armazenados no medidor (zera todos os valores). + */ +void meter_clear_data(void); + +// ----- Leituras por fase (L1, L2, L3) ----- + +// Tensão RMS (em volts) +float meter_get_vrms_l1(void); +float meter_get_vrms_l2(void); +float meter_get_vrms_l3(void); + +// Corrente RMS (em amperes) +float meter_get_irms_l1(void); +float meter_get_irms_l2(void); +float meter_get_irms_l3(void); + +// Potência ativa (W) +int meter_get_watt_l1(void); +int meter_get_watt_l2(void); +int meter_get_watt_l3(void); + +// Potência reativa (VAR) +int meter_get_var_l1(void); +int meter_get_var_l2(void); +int meter_get_var_l3(void); + +// 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 diff --git a/components/meter_ade7758/src/energy_meter.c b/components/meter_ade7758/src/energy_meter.c deleted file mode 100755 index 070f39c..0000000 --- a/components/meter_ade7758/src/energy_meter.c +++ /dev/null @@ -1,237 +0,0 @@ -#include -#include -#include // <- Necessário para bool -#include "freertos/FreeRTOS.h" -#include "freertos/semphr.h" -#include "esp_log.h" -#include "esp_timer.h" -#include "nvs.h" - -#include "meter.h" -#include "orno_modbus.h" - -#define NVS_NAMESPACE "evse_emeter" -#define NVS_MODEL "model" -#define NVS_STATE "state" - -static const char *TAG = "energy_meter"; - -static nvs_handle nvs; - -static bool state = false; -static meter_model_t model = ENERGY_METER_NONE; - -static uint16_t power = 0; -static bool has_session = false; -static int64_t start_time = 0; -static uint32_t charging_time = 0; // ms -static uint32_t consumption = 0; // Ws -static float cur[3] = {0, 0, 0}; -static float vlt[3] = {0, 0, 0}; -static int64_t prev_time = 0; - -static void set_calc_power(float p, uint32_t delta_ms) -{ - consumption += roundf((p * delta_ms) / 1000.0f); - power = roundf(p); -} - -void energy_meter_init(void) -{ - ESP_LOGI(TAG, "energy_meter_init"); - ESP_ERROR_CHECK(nvs_open(NVS_NAMESPACE, NVS_READWRITE, &nvs)); - - uint8_t u8 = ENERGY_METER_NONE; - nvs_get_u8(nvs, NVS_MODEL, &u8); - model = u8; -} - -bool meter_get_state(void) -{ - return orno_modbus_get_meter_state(); -} - -esp_err_t meter_set_state(bool _state) -{ - state = _state; - nvs_set_u8(nvs, NVS_STATE, state); - nvs_commit(nvs); - return ESP_OK; -} - -meter_model_t meter_get_model(void) -{ - return model; -} - -esp_err_t meter_set_model(meter_model_t _model) -{ - ESP_LOGI(TAG, "meter_set_model"); - - if (_model < 0 || _model > ENERGY_METER_ORNO_517) { - ESP_LOGE(TAG, "Model out of range"); - return ESP_ERR_INVALID_ARG; - } - - model = _model; - nvs_set_u8(nvs, NVS_MODEL, model); - nvs_commit(nvs); - orno_modbus_set_model(model != ENERGY_METER_NONE); - return ESP_OK; -} - -void energy_meter_start_session(void) -{ - if (!has_session) { - ESP_LOGI(TAG, "Start session"); - start_time = esp_timer_get_time(); - has_session = true; - //meter_start(); - } -} - -void energy_meter_stop_session(void) -{ - if (has_session) { - ESP_LOGI(TAG, "Stop session"); - start_time = 0; - consumption = 0; - charging_time = 0; - has_session = false; - //meter_stop(); - } -} - -void energy_meter_process(bool charging, uint16_t charging_current) -{ - int64_t now = esp_timer_get_time(); - uint32_t delta_ms = (now - prev_time) / 1000; - - if (charging && meter_is_running()) { - MeterData data = meter_getData(); - - vlt[0] = data.vrmsA; - vlt[1] = data.vrmsB; - vlt[2] = data.vrmsC; - - cur[0] = data.irmsA; - cur[1] = data.irmsB; - cur[2] = data.irmsC; - - uint32_t total_power = data.wattA + data.wattB + data.wattC; - - set_calc_power((float)total_power, delta_ms); - charging_time += delta_ms; - } else { - vlt[0] = vlt[1] = vlt[2] = 0; - cur[0] = cur[1] = cur[2] = 0; - power = 0; - } - - prev_time = now; -} - -uint32_t energy_meter_get_power(void) -{ - return power; -} - -uint32_t energy_meter_get_session_time(void) -{ - return has_session ? (esp_timer_get_time() - start_time) / 1000000 : 0; -} - -uint32_t energy_meter_get_charging_time(void) -{ - return charging_time / 1000; -} - -uint32_t energy_meter_get_consumption(void) -{ - return consumption / 3600; -} - -void energy_meter_get_voltage(float *voltage) -{ - memcpy(voltage, vlt, sizeof(vlt)); -} - -float energy_meter_get_l1_voltage(void) -{ - return vlt[0]; -} - -float energy_meter_get_l2_voltage(void) -{ - return vlt[1]; -} - -float energy_meter_get_l3_voltage(void) -{ - return vlt[2]; -} - -void energy_meter_get_current(float *current) -{ - memcpy(current, cur, sizeof(cur)); -} - -float energy_meter_get_l1_current(void) -{ - return cur[0]; -} - -float energy_meter_get_l2_current(void) -{ - return cur[1]; -} - -float energy_meter_get_l3_current(void) -{ - return cur[2]; -} - - -const char *meter_state_to_str(bool state) -{ - return state == true ? "CONNECTED" : "NOT CONNECTED"; -} - -const char *meter_model_to_str(meter_model_t mode) -{ - - switch (mode) - { - case ENERGY_METER_NONE: - return "NONE"; - case ENERGY_METER_ORNO_515: - return "OR-WE-515"; - case ENERGY_METER_ORNO_517: - return "OR-WE-517"; - default: - return "NONE"; - } -} - -meter_model_t meter_str_to_model(const char *str) -{ - if (!strcmp(str, "NONE")) - { - return ENERGY_METER_NONE; - } - if (!strcmp(str, "OR-WE-515")) - { - return ENERGY_METER_ORNO_515; - } - if (!strcmp(str, "OR-WE-517")) - { - return ENERGY_METER_ORNO_517; - } - - return ENERGY_METER_NONE; -} - -bool meter_str_to_state(const char *str) -{ - return strcmp(str, "CONNECTED") == 0; -} diff --git a/components/meter_ade7758/src/meter.c b/components/meter_ade7758/src/meter.c deleted file mode 100755 index fc1edd5..0000000 --- a/components/meter_ade7758/src/meter.c +++ /dev/null @@ -1,174 +0,0 @@ -#include "meter.h" - -#include -#include -#include -#include - -#include "esp_log.h" -#include "esp_system.h" - -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" - -#include "driver/spi_master.h" -#include "driver/gpio.h" - -#include "ade7758.h" -#include "evse_api.h" - -#define TAG "meter" - -// SPI Config -#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 - -// Calibration constants -#define VRMS_CAL 4732.78f -#define IRMS_CAL 53416.0f - -#define METER_READ_INTERVAL_MS 5000 - -static TaskHandle_t meter_task = NULL; -static MeterData metervalue; -static SemaphoreHandle_t meter_mutex = NULL; - -static uint32_t meter_watchdog_counter = 0; - -void meter_initData(void) { - if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { - memset(&metervalue, 0, sizeof(metervalue)); - xSemaphoreGive(meter_mutex); - } else { - ESP_LOGE(TAG, "Falha ao adquirir semáforo para zerar dados."); - } -} - -MeterData meter_getData(void) { - MeterData copy; - if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { - copy = metervalue; - xSemaphoreGive(meter_mutex); - } else { - ESP_LOGE(TAG, "Falha ao adquirir semáforo para leitura de dados."); - memset(©, 0, sizeof(copy)); - } - return copy; -} - -bool meter_is_running(void) { - return meter_task != NULL; -} - -uint32_t meter_get_watchdog_counter(void) { - return meter_watchdog_counter; -} - -static void meter_task_func(void *param) { - ESP_LOGI(TAG, "Meter task started"); - - MeterData previousData = {0}; - bool dataChanged = false; - - while (true) { - if (evse_state_is_charging(evse_get_state())) { - MeterData local = {0}; - - local.vrmsA = avrms() / VRMS_CAL; - local.vrmsB = bvrms() / VRMS_CAL; - local.vrmsC = cvrms() / VRMS_CAL; - - local.irmsA = airms() / IRMS_CAL; - local.irmsB = birms() / IRMS_CAL; - local.irmsC = cirms() / IRMS_CAL; - - ESP_LOGD(TAG, "VRMS: A=%.2f, B=%.2f, C=%.2f", local.vrmsA, local.vrmsB, local.vrmsC); - ESP_LOGD(TAG, "IRMS: A=%.2f, B=%.2f, C=%.2f", local.irmsA, local.irmsB, local.irmsC); - - if (setPotLine(PHASE_A, 20)) { - local.wattA = getWatt(PHASE_A); - ESP_LOGD(TAG, "Watt A: %" PRIi32, local.wattA); - } - - if (setPotLine(PHASE_B, 20)) { - local.wattB = getWatt(PHASE_B); - ESP_LOGD(TAG, "Watt B: %" PRIi32, local.wattB); - } - - if (setPotLine(PHASE_C, 20)) { - local.wattC = getWatt(PHASE_C); - ESP_LOGI(TAG, "Watt C: %" PRIi32, local.wattC); - } - - // Verifique se os dados mudaram antes de atualizar - if (memcmp(&local, &previousData, sizeof(MeterData)) != 0) { - dataChanged = true; - previousData = local; - } else { - dataChanged = false; - } - - if (dataChanged && xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { - metervalue = local; - meter_watchdog_counter++; - xSemaphoreGive(meter_mutex); - } - } - - vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS)); - } -} - -void Calibrate_ADE7758(void) { - gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); - setupDivs(1, 1, 1); - setLcycMode(0x00); - resetStatus(); -} - -void meter_init(void) { - ESP_LOGI(TAG, "Initializing meter"); - - if (!meter_mutex) { - meter_mutex = xSemaphoreCreateMutex(); - if (!meter_mutex) { - ESP_LOGE(TAG, "Erro ao criar semáforo de mutex"); - return; // Pode parar a inicialização caso não consiga criar o mutex - } - } - - meter_initData(); - esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK); - if (err != ESP_OK) { - ESP_LOGE(TAG, "Erro na inicialização do hardware SPI: %d", err); - return; - } - - InitSpi(PIN_NUM_CS); -} - -void meter_start(void) { - ESP_LOGI(TAG, "Starting meter"); - - Calibrate_ADE7758(); - meter_initData(); - - if (!meter_task) { - xTaskCreate(meter_task_func, "meter_task", 5 * 1024, NULL, 5, &meter_task); - } -} - -void meter_stop(void) { - ESP_LOGI(TAG, "Stopping meter"); - - if (meter_task) { - vTaskDelete(meter_task); - meter_task = NULL; - } - - meter_initData(); -} diff --git a/components/meter_ade7758/src/meter_ade7758.c b/components/meter_ade7758/src/meter_ade7758.c new file mode 100755 index 0000000..77ac468 --- /dev/null +++ b/components/meter_ade7758/src/meter_ade7758.c @@ -0,0 +1,177 @@ +#include "meter_ade7758.h" +#include "ade7758.h" + +#include +#include +#include + +#include "esp_log.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/semphr.h" + +#include "driver/spi_master.h" + + +#define TAG "meter" + +// === 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 + +// === Constantes de calibração === +#define VRMS_CAL 4732.78f +#define IRMS_CAL 53416.0f + +#define METER_READ_INTERVAL_MS 5000 + +// === Dados internos === +typedef struct { + float vrms[3]; + float irms[3]; + int watt[3]; + int var[3]; // reservados + int va[3]; // reservados +} meter_internal_data_t; + +static meter_internal_data_t meter_data; +static TaskHandle_t meter_task = NULL; +static SemaphoreHandle_t meter_mutex = NULL; +static uint32_t meter_watchdog_counter = 0; + +// === Utilitários internos === + +static void meter_clear_internal_data(void) { + if (meter_mutex && xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + memset(&meter_data, 0, sizeof(meter_data)); + xSemaphoreGive(meter_mutex); + } +} + +static bool meter_read_internal(meter_internal_data_t *out) { + if (!out) return false; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + *out = meter_data; + xSemaphoreGive(meter_mutex); + return true; + } + return false; +} + +static void meter_task_func(void *param) { + ESP_LOGI(TAG, "Meter task started"); + + meter_internal_data_t previous = {0}; + + while (true) { + meter_internal_data_t current = {0}; + + current.vrms[0] = avrms() / VRMS_CAL; + current.vrms[1] = bvrms() / VRMS_CAL; + current.vrms[2] = cvrms() / VRMS_CAL; + + current.irms[0] = airms() / IRMS_CAL; + current.irms[1] = birms() / IRMS_CAL; + current.irms[2] = cirms() / IRMS_CAL; + + if (setPotLine(PHASE_A, 20)) current.watt[0] = getWatt(PHASE_A); + if (setPotLine(PHASE_B, 20)) current.watt[1] = getWatt(PHASE_B); + if (setPotLine(PHASE_C, 20)) current.watt[2] = getWatt(PHASE_C); + + if (memcmp(&previous, ¤t, sizeof(current)) != 0) { + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + meter_data = current; + meter_watchdog_counter++; + xSemaphoreGive(meter_mutex); + } + previous = current; + } + + vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS)); + } +} + +// === Interface pública: controle === + +esp_err_t meter_init(void) { + ESP_LOGI(TAG, "Inicializando medidor..."); + + if (!meter_mutex) { + meter_mutex = xSemaphoreCreateMutex(); + if (!meter_mutex) { + ESP_LOGE(TAG, "Falha ao criar mutex"); + return ESP_ERR_NO_MEM; + } + } + + meter_clear_internal_data(); + + esp_err_t err = Init(EEPROM_HOST, PIN_NUM_MISO, PIN_NUM_MOSI, PIN_NUM_CLK); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Erro ao inicializar SPI (%d)", err); + return err; + } + + InitSpi(PIN_NUM_CS); + gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1); + setupDivs(1, 1, 1); + setLcycMode(0x00); + resetStatus(); + + return ESP_OK; +} + +esp_err_t meter_start(void) { + if (meter_task) return ESP_ERR_INVALID_STATE; + + meter_clear_internal_data(); + BaseType_t result = xTaskCreate(meter_task_func, "meter_task", 4096, NULL, 5, &meter_task); + return result == pdPASS ? ESP_OK : ESP_FAIL; +} + +void meter_stop(void) { + if (meter_task) { + vTaskDelete(meter_task); + meter_task = NULL; + } + meter_clear_internal_data(); +} + +bool meter_is_running(void) { + return meter_task != NULL; +} + +void meter_clear_data(void) { + meter_clear_internal_data(); +} + +// === Interface pública: acesso aos dados === + +float meter_get_vrms_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[0] : 0; } +float meter_get_vrms_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[1] : 0; } +float meter_get_vrms_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.vrms[2] : 0; } + +float meter_get_irms_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[0] : 0; } +float meter_get_irms_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[1] : 0; } +float meter_get_irms_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.irms[2] : 0; } + +int meter_get_watt_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[0] : 0; } +int meter_get_watt_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[1] : 0; } +int meter_get_watt_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.watt[2] : 0; } + +int meter_get_var_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[0] : 0; } +int meter_get_var_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[1] : 0; } +int meter_get_var_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.var[2] : 0; } + +int meter_get_va_l1(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[0] : 0; } +int meter_get_va_l2(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[1] : 0; } +int meter_get_va_l3(void) { meter_internal_data_t d; return meter_read_internal(&d) ? d.va[2] : 0; } + +// === Diagnóstico === + +uint32_t meter_get_watchdog_counter(void) { + return meter_watchdog_counter; +} diff --git a/components/meter_orno/CMakeLists.txt b/components/meter_orno/CMakeLists.txt index c584d9e..a344f4e 100755 --- a/components/meter_orno/CMakeLists.txt +++ b/components/meter_orno/CMakeLists.txt @@ -1,5 +1,5 @@ set(srcs - "src/orno_modbus.c" + "src/meter_orno.c" "src/modbus_params.c" "src/orno513.c" "src/orno516.c" ) idf_component_register(SRCS "${srcs}" diff --git a/components/meter_orno/include/meter_orno.h b/components/meter_orno/include/meter_orno.h new file mode 100755 index 0000000..198d17c --- /dev/null +++ b/components/meter_orno/include/meter_orno.h @@ -0,0 +1,70 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" + +/** + * @brief Inicializa o driver do medidor (SPI, mutex, registradores ADE7758). + */ +esp_err_t meter_init(void); + +/** + * @brief Inicia a tarefa de leitura de dados do medidor. + */ +esp_err_t meter_start(void); + +/** + * @brief Para a tarefa de leitura e limpa os dados internos. + */ +void meter_stop(void); + +/** + * @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); + +/** + * @brief Limpa os dados armazenados no medidor (zera todos os valores). + */ +void meter_clear_data(void); + +// ----- Leituras por fase (L1, L2, L3) ----- + +// Tensão RMS (em volts) +float meter_get_vrms_l1(void); +float meter_get_vrms_l2(void); +float meter_get_vrms_l3(void); + +// Corrente RMS (em amperes) +float meter_get_irms_l1(void); +float meter_get_irms_l2(void); +float meter_get_irms_l3(void); + +// Potência ativa (W) +int meter_get_watt_l1(void); +int meter_get_watt_l2(void); +int meter_get_watt_l3(void); + +// Potência reativa (VAR) +int meter_get_var_l1(void); +int meter_get_var_l2(void); +int meter_get_var_l3(void); + +// 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 diff --git a/components/meter_orno/include/modbus_params.h b/components/meter_orno/include/modbus_params.h new file mode 100644 index 0000000..ce9e6e6 --- /dev/null +++ b/components/meter_orno/include/modbus_params.h @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/*===================================================================================== + * Description: + * The Modbus parameter structures used to define Modbus instances that + * can be addressed by Modbus protocol. Define these structures per your needs in + * your application. Below is just an example of possible parameters. + *====================================================================================*/ +#ifndef _DEVICE_PARAMS +#define _DEVICE_PARAMS + +#include + +// This file defines structure of modbus parameters which reflect correspond modbus address space +// for each modbus register type (coils, discreet inputs, holding registers, input registers) +#pragma pack(push, 1) +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; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + uint8_t coils_port0; + uint8_t coils_port1; + uint8_t coils_port2; +} coil_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + float input_data0; // 0 + float input_data1; // 2 + float input_data2; // 4 + float input_data3; // 6 + uint16_t data[150]; // 8 + 150 = 158 + float input_data4; // 158 + float input_data5; + float input_data6; + float input_data7; + uint16_t data_block1[150]; +} input_reg_params_t; +#pragma pack(pop) + +#pragma pack(push, 1) +typedef struct +{ + uint32_t holding_data0; + uint32_t holding_data1; + uint32_t holding_data2; + uint32_t holding_data3; + uint32_t holding_data4; + uint32_t holding_data5; + uint32_t holding_data6; + uint32_t holding_data7; +} holding_reg_params_t; +#pragma pack(pop) + +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 // !defined(_DEVICE_PARAMS) diff --git a/components/meter_orno/include/orno_modbus.h b/components/meter_orno/include/orno_modbus.h deleted file mode 100755 index 1f016a3..0000000 --- a/components/meter_orno/include/orno_modbus.h +++ /dev/null @@ -1,62 +0,0 @@ -#ifndef ORNO_MODBUS_H_ -#define ORNO_MODBUS_H_ - -#include -#include "esp_err.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - * @brief Tipo do medidor ORNO usado na aplicação. - */ -typedef enum { - ORNO_METER_GRID, ///< Medidor na entrada da rede elétrica - ORNO_METER_EVSE ///< Medidor na saída da EVSE -} orno_meter_type_t; - -/** - * @brief Inicializa o driver ORNO Modbus. - */ -esp_err_t orno_modbus_init(void); - -/** - * @brief Lê a corrente RMS do medidor especificado. - * - * @param type Tipo do medidor (GRID ou EVSE) - * @param current Ponteiro para armazenar o valor da corrente (em amperes) - * @return esp_err_t ESP_OK em caso de sucesso, erro caso contrário - */ -esp_err_t orno_modbus_read_current(orno_meter_type_t type, float *current); - -/** - * @brief Ativa ou desativa o modo de teste (simulação). - */ -void orno_modbus_set_meter_test(bool state); - -/** - * @brief Define o modelo usado do medidor (caso afete registros). - */ -void orno_modbus_set_model(bool enabled); - -/** - * @brief Retorna o estado atual do medidor (ligado/desligado). - */ -bool orno_modbus_get_meter_state(void); - -/** - * @brief Inicia a task interna de comunicação (se usada). - */ -void orno_modbus_start(void); - -/** - * @brief Para a task de comunicação (se usada). - */ -void orno_modbus_stop(void); - -#ifdef __cplusplus -} -#endif - -#endif /* ORNO_MODBUS_H_ */ diff --git a/components/meter_orno/src/orno_modbus.c b/components/meter_orno/src/meter_orno.c similarity index 97% rename from components/meter_orno/src/orno_modbus.c rename to components/meter_orno/src/meter_orno.c index 29b34aa..bbfa9de 100755 --- a/components/meter_orno/src/orno_modbus.c +++ b/components/meter_orno/src/meter_orno.c @@ -1,4 +1,4 @@ -#include "orno_modbus.h" +#include "meter_orno.h" #include #include "esp_log.h" diff --git a/components/meter_orno/src/modbus_params.c b/components/meter_orno/src/modbus_params.c new file mode 100644 index 0000000..fb5a0e4 --- /dev/null +++ b/components/meter_orno/src/modbus_params.c @@ -0,0 +1,20 @@ +/* + * 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 }; diff --git a/components/meter_orno/src/orno513.c b/components/meter_orno/src/orno513.c new file mode 100755 index 0000000..d6e7270 --- /dev/null +++ b/components/meter_orno/src/orno513.c @@ -0,0 +1,530 @@ +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" + +#include "modbus_params.h" // for modbus parameters structures +#include "mbcontroller.h" +#include "sdkconfig.h" +#include "meter_orno.h" + +#define TXD_PIN (GPIO_NUM_17) +#define RXD_PIN (GPIO_NUM_16) + +static const char *TAG = "serial_mdb"; + +static bool enabled = false; +static bool meterState = false; +static bool meterTest = false; + +static TaskHandle_t serial_mdb_task = NULL; + +#define MB_PORT_NUM 2 //(CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection +#define MB_DEV_SPEED 9600 //(CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART +// #define MB_PARITY_EVEN + +#define MB_UART_TXD 17 +#define MB_UART_RXD 16 +#define MB_UART_RTS 5 + +// Note: Some pins on target chip cannot be assigned for UART communication. +// See UART documentation for selected board and target to configure pins using Kconfig. + +// The number of parameters that intended to be used in the particular control process +#define MASTER_MAX_CIDS num_device_parameters + +// Number of reading of parameters from slave +#define MASTER_MAX_RETRY 30 + +// Timeout to update cid over Modbus +#define UPDATE_CIDS_TIMEOUT_MS (3000) +#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS) + +// Timeout between polls +#define POLL_TIMEOUT_MS (500) +#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS) + +// Timeout between erros +#define ERROR_TIMEOUT_MS (1000) +#define ERROR_TIMEOUT_TICS (ERROR_TIMEOUT_MS / portTICK_PERIOD_MS) + +// The macro to get offset for parameter in the appropriate structure +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) +#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1)) +#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1)) +// Discrete offset macro +#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1)) + +#define STR(fieldname) ((const char *)(fieldname)) +// Options can be used as bit masks or parameter limits +#define OPTS(min_val, max_val, step_val) \ + { \ + .opt1 = min_val, .opt2 = max_val, .opt3 = step_val} + +// Enumeration of modbus device addresses accessed by master device +enum +{ + MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here) +}; + +// Enumeration of all supported CIDs for device (used in parameter definition table) +enum +{ + CID_HOLD_DATA_0 = 0, + CID_HOLD_DATA_1 = 1, + CID_HOLD_DATA_2 = 2, + CID_HOLD_DATA_3 = 3, + CID_HOLD_DATA_4 = 4, + CID_HOLD_DATA_5 = 5, + CID_HOLD_DATA_6 = 6 +}; + +#define SN 0x1000 +#define METERID 0x1003 +#define FW 0x1004 + +#define L1VOLTAGE 0x0100 +// #define L2VOLTAGE 0x0010 +// #define L3VOLTAGE 0x0012 + +#define L1CURRENT 0x0102 +// #define L2CURRENT 0x0018 +// #define L3CURRENT 0x001A + +#define ACTIVEPOWER 0x0104 +#define APPARENTPOWER 0x0106 +#define REACTIVEPOWER 0x0108 + +#define TOTALFACTIVE 0x010E +#define TOTALRACTIVE 0x0118 + +// Example Data (Object) Dictionary for Modbus parameters: +// The CID field in the table must be unique. +// Modbus Slave Addr field defines slave address of the device with correspond parameter. +// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such). +// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly. +// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value. +// Data Type, Data Size specify type of the characteristic and its data size. +// Parameter Options field specifies the options that can be used to process parameter value (limits or masks). +// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc). +const mb_parameter_descriptor_t device_parameters[] = { + // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode} + + {CID_HOLD_DATA_0, STR("TOTALFACTIVE"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TOTALFACTIVE, 2, + HOLD_OFFSET(holding_data0), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_1, STR("TOTALRACTIVE"), STR("kWh"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, TOTALRACTIVE, 2, + HOLD_OFFSET(holding_data1), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_2, STR("ACTIVEPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, ACTIVEPOWER, 2, + HOLD_OFFSET(holding_data2), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_3, STR("APPARENTPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, APPARENTPOWER, 2, + HOLD_OFFSET(holding_data3), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_4, STR("REACTIVEPOWER"), STR("W"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, REACTIVEPOWER, 2, + HOLD_OFFSET(holding_data4), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_5, STR("L1CURRENT"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(holding_data5), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_6, STR("L1VOLTAGE"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data6), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ} + /* + + {CID_HOLD_DATA_3, STR("L1VOLTAGE 4"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data3), PARAM_TYPE_I32_DCBA, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_4, STR("L1VOLTAGE 5"), STR("V"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data4), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_5, STR("L1CURRENT 2"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(holding_data5), PARAM_TYPE_FLOAT, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ} + + + {CID_HOLD_DATA_2, STR("ID 2"), STR("ID 2"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, METERID, 2, + HOLD_OFFSET(holding_data4), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_3, STR("ID 3"), STR("FW 2"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, FW, 2, + HOLD_OFFSET(holding_data5), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ} + + + {CID_HOLD_DATA_2, STR("ID 2"), STR("ID 2"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, METERID, 1, + HOLD_OFFSET(holding_data2), PARAM_TYPE_U16, 2, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_3, STR("ID 3"), STR("FW 2"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, FW, 1, + HOLD_OFFSET(holding_data3), PARAM_TYPE_U16, 2, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_4, STR("ID 4"), STR("ID 4"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data4), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_5, STR("ID 5"), STR("ID 5"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data5), PARAM_TYPE_U16, 2, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_6, STR("ID 6"), STR("ID 6"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1VOLTAGE, 2, + HOLD_OFFSET(holding_data6), PARAM_TYPE_U32, 4, OPTS(0, 100000, 1), PAR_PERMS_READ} + + + + + {CID_HOLD_DATA_4, STR("ID 4"), STR("ID 3"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, METERID, 2, + HOLD_OFFSET(holding_data4), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_5, STR("ID 5"), STR("FW 3"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, FW, 2, + HOLD_OFFSET(holding_data5), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_6, STR("ID 6"), STR("ID 4"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, METERID, 2, + HOLD_OFFSET(holding_data6), PARAM_TYPE_U8, 2, OPTS(0, 100000, 1), PAR_PERMS_READ} + + + {CID_HOLD_DATA_0, STR("L1"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + + + {CID_HOLD_DATA_1, STR("L2"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L2CURRENT, 2, + HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_2, STR("L3"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L3CURRENT, 2, + HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ} + */ + +}; + +// Calculate number of parameters in the table +const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); + +// The function to get pointer to parameter storage (instance) according to parameter description table +static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor) +{ + assert(param_descriptor != NULL); + void *instance_ptr = NULL; + if (param_descriptor->param_offset != 0) + { + switch (param_descriptor->mb_param_type) + { + case MB_PARAM_HOLDING: + instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_INPUT: + instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_COIL: + instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_DISCRETE: + instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1); + break; + default: + instance_ptr = NULL; + break; + } + } + else + { + ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid); + assert(instance_ptr != NULL); + } + return instance_ptr; +} + +// Float - Mid-Little Endian (CDAB) +float ReverseFloat(const float inFloat) +{ + float retVal; + char *floatToConvert = (char *)&inFloat; + char *returnFloat = (char *)&retVal; + + // swap the bytes into a temporary buffer + returnFloat[0] = floatToConvert[2]; + returnFloat[1] = floatToConvert[3]; + returnFloat[2] = floatToConvert[0]; + returnFloat[3] = floatToConvert[1]; + + return retVal; +} + +// Int - Mid-Little Endian (CDAB) +int ReverseInt(const int inFloat) +{ + int retVal; + char *floatToConvert = (char *)&inFloat; + char *returnFloat = (char *)&retVal; + + // swap the bytes into a temporary buffer + returnFloat[0] = floatToConvert[2]; + returnFloat[1] = floatToConvert[3]; + returnFloat[2] = floatToConvert[0]; + returnFloat[3] = floatToConvert[1]; + + return retVal; +} + +static void serial_mdb_task_func(void *param) +{ + ESP_LOGI(TAG, "serial_mdb_task_func"); + esp_err_t err = ESP_OK; + + float maxcurrent = 0; + float l1current = 0; + float l2current = 0; + float l3current = 0; + int error_count = 0; + + bool alarm_state = false; + const mb_parameter_descriptor_t *param_descriptor = NULL; + + ESP_LOGI(TAG, "Start modbus..."); + + while (true) + { + + // if ((evse_state_is_charging(evse_get_state()) && enabled) || (meterTest && enabled)) + { + // Read all found characteristics from slave(s) + for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) + { + // Get data from parameters description table + // and use this information to fill the characteristics description table + // and having all required fields in just one table + err = mbc_master_get_cid_info(cid, ¶m_descriptor); + if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) + { + void *temp_data_ptr = master_get_param_data(param_descriptor); + uint8_t type = 0; + + err = mbc_master_get_parameter(cid, (char *)param_descriptor->param_key, + (uint8_t *)temp_data_ptr, &type); + + if (err == ESP_OK) + { + + ESP_LOGI(TAG, "err == ESP_OK..."); + + error_count = 0; + meterState = true; + + if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) || + (param_descriptor->mb_param_type == MB_PARAM_INPUT)) + { + int value = *(int *)temp_data_ptr; + // value = ReverseInt(value); + + /* + switch (cid) + { + case 0: + // setMaxGridCurrent(grid_get_max_current() * 10); + maxcurrent = 0; + l1current = 0; + l2current = 0; + l3current = 0; + + l1current = value; + break; + case 1: + l2current = value; + break; + case 2: + l3current = value; + + maxcurrent = (l1current > l2current) ? l1current : l2current; + maxcurrent = (maxcurrent > l3current) ? maxcurrent : l3current; + + // maxcurrent = (maxcurrent * 5) + 25; + + // setLiveGridCurrent((int)maxcurrent * 10); + break; + default: + // code block + }*/ + + ESP_LOGI(TAG, "Characteristic #%u %s (%s) value = %d (0x%" PRIx32 ") read successful.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + value, + *(uint32_t *)temp_data_ptr); + + if (((value > param_descriptor->param_opts.max) || + (value < param_descriptor->param_opts.min))) + { + alarm_state = true; + break; + } + } + else + { + uint8_t state = *(uint8_t *)temp_data_ptr; + const char *rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF"; + if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) + { + ESP_LOGI(TAG, "Characteristic 6 #%u %s (%s) value = %s (0x%" PRIx8 ") read successful.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + (const char *)rw_str, + *(uint8_t *)temp_data_ptr); + } + else + { + ESP_LOGE(TAG, "Characteristic 7 #%u %s (%s) value = %s (0x%" PRIx8 "), unexpected value.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + (const char *)rw_str, + *(uint8_t *)temp_data_ptr); + alarm_state = true; + break; + } + if (state & param_descriptor->param_opts.opt1) + { + alarm_state = true; + break; + } + } + } + else + { + + if (error_count > 3 && !meterTest) + { + meterState = false; + vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls + } + else + { + error_count++; + } + + ESP_LOGE(TAG, "Characteristic 8 #%u (%s) read fail, err = 0x%x (%s).", + param_descriptor->cid, + param_descriptor->param_key, + (int)err, + (char *)esp_err_to_name(err)); + } + + vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls + } + } + } + vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); + } + + if (alarm_state) + { + ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid); + } + else + { + ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY); + } + ESP_LOGI(TAG, "Destroy master..."); + ESP_ERROR_CHECK(mbc_master_destroy()); + + /* + while (true) + { + + + vTaskDelay(pdMS_TO_TICKS(1000)); + } + */ +} + +// Modbus master initialization +static esp_err_t master_init(void) +{ + // Initialize and start Modbus controller + mb_communication_info_t comm = { + //.slave_addr = 1, + .port = MB_PORT_NUM, + .mode = MB_MODE_RTU, + .baudrate = MB_DEV_SPEED, + .parity = UART_PARITY_DISABLE}; + void *master_handler = NULL; + + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); + MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail."); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail, returns(0x%x).", (int)err); + err = mbc_master_setup((void *)&comm); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller setup fail, returns(0x%x).", (int)err); + + // Set UART pin numbers + err = uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, + MB_UART_RTS, UART_PIN_NO_CHANGE); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err); + + err = mbc_master_start(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller start fail, returned (0x%x).", (int)err); + + // Set driver mode to Half Duplex + err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err); + + vTaskDelay(5); + err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller set descriptor fail, returns(0x%x).", (int)err); + ESP_LOGI(TAG, "Modbus master stack initialized..."); + return err; +} + +/** + * @brief Set meter model + * + */ +void serial_mdb_set_model(bool _enabled) +{ + enabled = _enabled; +} + +/** + * @brief Set meter state + * + */ +bool serial_mdb_get_meter_state() +{ + return meterState; +} + +/** + * @brief Set meter state + * + */ +void serial_mdb_set_meter_test(bool _meterTest) +{ + meterTest = _meterTest; +} + +void serial_mdb_start() +{ + + ESP_LOGI(TAG, "Starting MDB Serial"); + + enabled = meter_get_model() != ENERGY_METER_NONE; + + ESP_ERROR_CHECK(master_init()); + + xTaskCreate(serial_mdb_task_func, "serial_mdb_task", 4 * 1024, NULL, 5, &serial_mdb_task); +} + +void serial_mdb_stop(void) +{ + ESP_LOGI(TAG, "Stopping"); + + if (serial_mdb_task) + { + vTaskDelete(serial_mdb_task); + serial_mdb_task = NULL; + } + + // if (port != -1) + //{ + uart_driver_delete(MB_PORT_NUM); + // port = -1; + //} +} \ No newline at end of file diff --git a/components/meter_orno/src/orno516.c b/components/meter_orno/src/orno516.c new file mode 100755 index 0000000..77528fd --- /dev/null +++ b/components/meter_orno/src/orno516.c @@ -0,0 +1,438 @@ +#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE +#include "esp_log.h" + +#include "meter_orno.h" +#include "modbus_params.h" // for modbus parameters structures +#include "mbcontroller.h" +#include "sdkconfig.h" + +#define TXD_PIN (GPIO_NUM_17) +#define RXD_PIN (GPIO_NUM_16) + +static const char *TAG = "serial_mdb"; + +static bool enabled = false; +static bool meterState = false; +static bool meterTest = false; + +static TaskHandle_t serial_mdb_task = NULL; + +#define MB_PORT_NUM 2 //(CONFIG_MB_UART_PORT_NUM) // Number of UART port used for Modbus connection +#define MB_DEV_SPEED 9600 //(CONFIG_MB_UART_BAUD_RATE) // The communication speed of the UART +// #define MB_PARITY_EVEN + +#define MB_UART_TXD 17 +#define MB_UART_RXD 16 +#define MB_UART_RTS 5 + +// Note: Some pins on target chip cannot be assigned for UART communication. +// See UART documentation for selected board and target to configure pins using Kconfig. + +// The number of parameters that intended to be used in the particular control process +#define MASTER_MAX_CIDS num_device_parameters + +// Number of reading of parameters from slave +#define MASTER_MAX_RETRY 30 + +// Timeout to update cid over Modbus +#define UPDATE_CIDS_TIMEOUT_MS (5000) +#define UPDATE_CIDS_TIMEOUT_TICS (UPDATE_CIDS_TIMEOUT_MS / portTICK_PERIOD_MS) + +// Timeout between polls +#define POLL_TIMEOUT_MS (1) +#define POLL_TIMEOUT_TICS (POLL_TIMEOUT_MS / portTICK_PERIOD_MS) + +// Timeout between erros +#define ERROR_TIMEOUT_MS (30000) +#define ERROR_TIMEOUT_TICS (ERROR_TIMEOUT_MS / portTICK_PERIOD_MS) + +// The macro to get offset for parameter in the appropriate structure +#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1)) +#define INPUT_OFFSET(field) ((uint16_t)(offsetof(input_reg_params_t, field) + 1)) +#define COIL_OFFSET(field) ((uint16_t)(offsetof(coil_reg_params_t, field) + 1)) +// Discrete offset macro +#define DISCR_OFFSET(field) ((uint16_t)(offsetof(discrete_reg_params_t, field) + 1)) + +#define STR(fieldname) ((const char *)(fieldname)) +// Options can be used as bit masks or parameter limits +#define OPTS(min_val, max_val, step_val) \ + { \ + .opt1 = min_val, .opt2 = max_val, .opt3 = step_val} + +// Enumeration of modbus device addresses accessed by master device +enum +{ + MB_DEVICE_ADDR1 = 1 // Only one slave device used for the test (add other slave addresses here) +}; + +// Enumeration of all supported CIDs for device (used in parameter definition table) +enum +{ + CID_HOLD_DATA_0 = 0, + CID_HOLD_DATA_1 = 1, + CID_HOLD_DATA_2 = 2, + CID_HOLD_DATA_3 = 3, + CID_HOLD_DATA_4 = 4, + CID_HOLD_DATA_5 = 5, + CID_HOLD_DATA_6 = 6 +}; + +#define SN 0x01 +#define METERID 0x02 + +#define L1VOLTAGE 0x000E +#define L2VOLTAGE 0x0010 +#define L3VOLTAGE 0x0012 + +#define L1CURRENT 0x0016 +#define L2CURRENT 0x0018 +#define L3CURRENT 0x001A + +#define TOTALACTIVEPOWER 0x001C + +// Example Data (Object) Dictionary for Modbus parameters: +// The CID field in the table must be unique. +// Modbus Slave Addr field defines slave address of the device with correspond parameter. +// Modbus Reg Type - Type of Modbus register area (Holding register, Input Register and such). +// Reg Start field defines the start Modbus register number and Reg Size defines the number of registers for the characteristic accordingly. +// The Instance Offset defines offset in the appropriate parameter structure that will be used as instance to save parameter value. +// Data Type, Data Size specify type of the characteristic and its data size. +// Parameter Options field specifies the options that can be used to process parameter value (limits or masks). +// Access Mode - can be used to implement custom options for processing of characteristic (Read/Write restrictions, factory mode values and etc). +const mb_parameter_descriptor_t device_parameters[] = { + // { CID, Param Name, Units, Modbus Slave Addr, Modbus Reg Type, Reg Start, Reg Size, Instance Offset, Data Type, Data Size, Parameter Options, Access Mode} + + //{CID_HOLD_DATA_0, STR("ID 1"), STR("ID"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, METERID, 2, + // HOLD_OFFSET(holding_data0), PARAM_TYPE_U8, 1, OPTS(0, 100000, 1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_0, STR("L1"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L1CURRENT, 2, + HOLD_OFFSET(holding_data0), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_1, STR("L2"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L2CURRENT, 2, + HOLD_OFFSET(holding_data1), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ}, + + {CID_HOLD_DATA_2, STR("L3"), STR("A"), MB_DEVICE_ADDR1, MB_PARAM_HOLDING, L3CURRENT, 2, + HOLD_OFFSET(holding_data2), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ} + +}; + +// Calculate number of parameters in the table +const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0])); + +// The function to get pointer to parameter storage (instance) according to parameter description table +static void *master_get_param_data(const mb_parameter_descriptor_t *param_descriptor) +{ + assert(param_descriptor != NULL); + void *instance_ptr = NULL; + if (param_descriptor->param_offset != 0) + { + switch (param_descriptor->mb_param_type) + { + case MB_PARAM_HOLDING: + instance_ptr = ((void *)&holding_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_INPUT: + instance_ptr = ((void *)&input_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_COIL: + instance_ptr = ((void *)&coil_reg_params + param_descriptor->param_offset - 1); + break; + case MB_PARAM_DISCRETE: + instance_ptr = ((void *)&discrete_reg_params + param_descriptor->param_offset - 1); + break; + default: + instance_ptr = NULL; + break; + } + } + else + { + ESP_LOGE(TAG, "Wrong parameter offset for CID #%u", (unsigned)param_descriptor->cid); + assert(instance_ptr != NULL); + } + return instance_ptr; +} + +// Float - Mid-Little Endian (CDAB) +float ReverseFloat(const float inFloat) +{ + float retVal; + char *floatToConvert = (char *)&inFloat; + char *returnFloat = (char *)&retVal; + + // swap the bytes into a temporary buffer + returnFloat[0] = floatToConvert[2]; + returnFloat[1] = floatToConvert[3]; + returnFloat[2] = floatToConvert[0]; + returnFloat[3] = floatToConvert[1]; + + return retVal; +} + +static void serial_mdb_task_func(void *param) +{ + ESP_LOGI(TAG, "serial_mdb_task_func"); + esp_err_t err = ESP_OK; + + float maxcurrent = 0; + float l1current = 0; + float l2current = 0; + float l3current = 0; + int error_count = 0; + + bool alarm_state = false; + const mb_parameter_descriptor_t *param_descriptor = NULL; + + ESP_LOGI(TAG, "Start modbus..."); + + while (true) + { + + if ((evse_state_is_charging(evse_get_state()) && enabled) || (meterTest && enabled)) + { + // Read all found characteristics from slave(s) + for (uint16_t cid = 0; (err != ESP_ERR_NOT_FOUND) && cid < MASTER_MAX_CIDS; cid++) + { + // Get data from parameters description table + // and use this information to fill the characteristics description table + // and having all required fields in just one table + err = mbc_master_get_cid_info(cid, ¶m_descriptor); + if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL)) + { + void *temp_data_ptr = master_get_param_data(param_descriptor); + uint8_t type = 0; + + err = mbc_master_get_parameter(cid, (char *)param_descriptor->param_key, + (uint8_t *)temp_data_ptr, &type); + + if (err == ESP_OK) + { + error_count = 0; + meterState = true; + + if ((param_descriptor->mb_param_type == MB_PARAM_HOLDING) || + (param_descriptor->mb_param_type == MB_PARAM_INPUT)) + { + float value = *(float *)temp_data_ptr; + value = ReverseFloat(value); + + switch (cid) + { + case 0: + setMaxGridCurrent(grid_get_max_current() * 10); + maxcurrent = 0; + l1current = 0; + l2current = 0; + l3current = 0; + + l1current = value; + break; + case 1: + l2current = value; + break; + case 2: + l3current = value; + + maxcurrent = (l1current > l2current) ? l1current : l2current; + maxcurrent = (maxcurrent > l3current) ? maxcurrent : l3current; + + //maxcurrent = (maxcurrent * 5) + 25; + + setLiveGridCurrent((int)maxcurrent * 10); + break; + default: + // code block + } + + ESP_LOGD(TAG, "Characteristic #%u %s (%s) value = %f (0x%" PRIx32 ") read successful.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + value, + *(uint32_t *)temp_data_ptr); + + if (((value > param_descriptor->param_opts.max) || + (value < param_descriptor->param_opts.min))) + { + alarm_state = true; + break; + } + } + else + { + uint8_t state = *(uint8_t *)temp_data_ptr; + const char *rw_str = (state & param_descriptor->param_opts.opt1) ? "ON" : "OFF"; + if ((state & param_descriptor->param_opts.opt2) == param_descriptor->param_opts.opt2) + { + ESP_LOGI(TAG, "Characteristic 6 #%u %s (%s) value = %s (0x%" PRIx8 ") read successful.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + (const char *)rw_str, + *(uint8_t *)temp_data_ptr); + } + else + { + ESP_LOGE(TAG, "Characteristic 7 #%u %s (%s) value = %s (0x%" PRIx8 "), unexpected value.", + param_descriptor->cid, + param_descriptor->param_key, + param_descriptor->param_units, + (const char *)rw_str, + *(uint8_t *)temp_data_ptr); + alarm_state = true; + break; + } + if (state & param_descriptor->param_opts.opt1) + { + alarm_state = true; + break; + } + } + } + else + { + + if (error_count > 3 && !meterTest) + { + meterState = false; + vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls + } + else + { + error_count++; + } + + ESP_LOGE(TAG, "Characteristic 8 #%u (%s) read fail, err = 0x%x (%s).", + param_descriptor->cid, + param_descriptor->param_key, + (int)err, + (char *)esp_err_to_name(err)); + } + + vTaskDelay(POLL_TIMEOUT_TICS); // timeout between polls + } + } + } + vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS); + } + + if (alarm_state) + { + ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid); + } + else + { + ESP_LOGE(TAG, "Alarm is not triggered after %u retries.", MASTER_MAX_RETRY); + } + ESP_LOGI(TAG, "Destroy master..."); + ESP_ERROR_CHECK(mbc_master_destroy()); + + /* + while (true) + { + + + vTaskDelay(pdMS_TO_TICKS(1000)); + } + */ +} + +// Modbus master initialization +static esp_err_t master_init(void) +{ + // Initialize and start Modbus controller + mb_communication_info_t comm = { + //.slave_addr = 1, + .port = MB_PORT_NUM, + .mode = MB_MODE_RTU, + .baudrate = MB_DEV_SPEED, + .parity = UART_PARITY_EVEN}; + void *master_handler = NULL; + + esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler); + MB_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail."); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller initialization fail, returns(0x%x).", (int)err); + err = mbc_master_setup((void *)&comm); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller setup fail, returns(0x%x).", (int)err); + + // Set UART pin numbers + err = uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, + MB_UART_RTS, UART_PIN_NO_CHANGE); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set pin failure, uart_set_pin() returned (0x%x).", (int)err); + + err = mbc_master_start(); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller start fail, returned (0x%x).", (int)err); + + // Set driver mode to Half Duplex + err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb serial set mode failure, uart_set_mode() returned (0x%x).", (int)err); + + vTaskDelay(5); + err = mbc_master_set_descriptor(&device_parameters[0], num_device_parameters); + MB_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG, + "mb controller set descriptor fail, returns(0x%x).", (int)err); + ESP_LOGI(TAG, "Modbus master stack initialized..."); + return err; +} + +/** + * @brief Set meter model + * + */ +void serial_mdb_set_model(bool _enabled) +{ + enabled = _enabled; +} + +/** + * @brief Set meter state + * + */ +bool serial_mdb_get_meter_state() +{ + return meterState; +} + +/** + * @brief Set meter state + * + */ +void serial_mdb_set_meter_test(bool _meterTest) +{ + meterTest = _meterTest; +} + +void serial_mdb_start() +{ + + ESP_LOGI(TAG, "Starting MDB Serial"); + + enabled = meter_get_model() != ENERGY_METER_NONE; + + ESP_ERROR_CHECK(master_init()); + + xTaskCreate(serial_mdb_task_func, "serial_mdb_task", 4 * 1024, NULL, 5, &serial_mdb_task); +} + +void serial_mdb_stop(void) +{ + ESP_LOGI(TAG, "Stopping"); + + if (serial_mdb_task) + { + vTaskDelete(serial_mdb_task); + serial_mdb_task = NULL; + } + + // if (port != -1) + //{ + uart_driver_delete(MB_PORT_NUM); + // port = -1; + //} +} \ No newline at end of file diff --git a/components/meter_zigbee/include/meter_zigbee.h b/components/meter_zigbee/include/meter_zigbee.h index 5150212..d22518b 100755 --- a/components/meter_zigbee/include/meter_zigbee.h +++ b/components/meter_zigbee/include/meter_zigbee.h @@ -1,26 +1,88 @@ -#ifndef METER_ZIGBEE_H_ -#define METER_ZIGBEE_H_ +#pragma once -#include "driver/uart.h" +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include "esp_err.h" /** - * @brief Send Data - * + * @brief Inicializa o driver do medidor Zigbee (UART, mutex, etc.) */ -int meter_zigbee_send_data(const char *data); - +esp_err_t meter_init(void); /** - * @brief Start serial MT - * + * @brief Inicia a tarefa de leitura dos dados do medidor Zigbee. */ -void meter_zigbee_start(); - +esp_err_t meter_start(void); /** - * @brief Stop serial MT - * + * @brief Interrompe a tarefa e limpa recursos (UART, mutex). */ -void meter_zigbee_stop(void); +void meter_stop(void); -#endif /* METER_ZIGBEE_H_ */ +/** + * @brief Verifica se o medidor Zigbee está em execução. + * + * @return true se a tarefa está ativa, false se não. + */ +bool meter_is_running(void); + +/** + * @brief Limpa todos os dados armazenados em memória. + */ +void meter_clear_data(void); + +// ---------------------- +// Leituras por fase (L1, L2, L3) +// ---------------------- + +// Corrente RMS (em amperes) +float meter_get_irms_l1(void); +float meter_get_irms_l2(void); +float meter_get_irms_l3(void); + +// Tensão RMS (em volts) +float meter_get_vrms_l1(void); +float meter_get_vrms_l2(void); +float meter_get_vrms_l3(void); + +// Potência ativa (W) +int meter_get_watt_l1(void); +int meter_get_watt_l2(void); +int meter_get_watt_l3(void); + +// Potência reativa (VAR) +int meter_get_var_l1(void); +int meter_get_var_l2(void); +int meter_get_var_l3(void); + +// Potência aparente (VA) +int meter_get_va_l1(void); +int meter_get_va_l2(void); +int meter_get_va_l3(void); + +// ---------------------- +// Dados adicionais +// ---------------------- + +/** + * @brief Retorna a frequência da rede em Hz. + */ +float meter_get_frequency(void); + +/** + * @brief Retorna o fator de potência médio. + */ +float meter_get_power_factor(void); + +/** + * @brief Retorna a energia total acumulada (kWh ou Wh, dependendo do dispositivo). + */ +float meter_get_total_energy(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/meter_zigbee/src/meter_zigbee.c b/components/meter_zigbee/src/meter_zigbee.c index 27bc846..8834bbd 100755 --- a/components/meter_zigbee/src/meter_zigbee.c +++ b/components/meter_zigbee/src/meter_zigbee.c @@ -1,33 +1,102 @@ +#include "meter_zigbee.h" + +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "esp_system.h" +#include "freertos/semphr.h" #include "esp_log.h" +#include "esp_system.h" #include "driver/uart.h" -#include "string.h" #include "driver/gpio.h" -#include "meter_zigbee.h" -#include #define TAG "meter_zigbee" -#define TXD_PIN (GPIO_NUM_17) -#define RXD_PIN (GPIO_NUM_16) -#define BUF_SIZE 128 -#define RX_BUF_SIZE 14 +// 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 -#define VOLTAGE_CURRENT1_ATTR 0x0006 -#define VOLTAGE_CURRENT2_ATTR 0x0007 -#define VOLTAGE_CURRENT3_ATTR 0x0008 +// Zigbee Attribute IDs +#define ATTR_CURRENT_L1 0x0006 +#define ATTR_CURRENT_L2 0x0007 +#define ATTR_CURRENT_L3 0x0008 -static TaskHandle_t meter_zigbee_task = NULL; -static float l1_current = 0, l2_current = 0, l3_current = 0; +#define ATTR_VOLTAGE_L1 0x0266 +#define ATTR_CURRENT_L1_ALT 0x0267 +#define ATTR_POWER_L1 0x0268 -static float decode_current(const uint8_t *buf) { +#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 meter_zigbee_data_t meter_data = {0}; +static SemaphoreHandle_t meter_mutex = NULL; +static TaskHandle_t meter_task = NULL; + +// ---------- Utils ---------- + +static inline float decode_float(const uint8_t *buf) { return (buf[9] + (buf[8] << 8) + (buf[7] << 16)) / 100.0f; } -static void decode_frame(const uint8_t *buf) { - uint16_t attr_code = buf[1] | (buf[2] << 8); +static float meter_data_get_float(const float *arr, uint8_t phase) { + float val = 0.0f; + if (phase >= PHASE_COUNT) return 0; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + val = arr[phase]; + xSemaphoreGive(meter_mutex); + } + return val; +} + +static int meter_data_get_int(const int *arr, uint8_t phase) { + int val = 0; + if (phase >= PHASE_COUNT) return 0; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + val = arr[phase]; + xSemaphoreGive(meter_mutex); + } + return val; +} + +static void meter_data_clear(void) { + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + memset(&meter_data, 0, sizeof(meter_data)); + xSemaphoreGive(meter_mutex); + } +} + +// ---------- Frame Handler ---------- + +static void handle_zigbee_frame(const uint8_t *buf) { + uint16_t attr = buf[1] | (buf[2] << 8); uint8_t size = buf[4]; if (size != 8) { @@ -35,35 +104,62 @@ static void decode_frame(const uint8_t *buf) { return; } - float current = decode_current(buf); - ESP_LOGI(TAG, "Attr 0x%04X - Current: %.2f A", attr_code, current); + float value = decode_float(buf); + ESP_LOGI(TAG, "Attr 0x%04X = %.2f", attr, value); - switch (attr_code) { - case VOLTAGE_CURRENT1_ATTR: l1_current = current; break; - case VOLTAGE_CURRENT2_ATTR: l2_current = current; break; - case VOLTAGE_CURRENT3_ATTR: l3_current = current; break; - default: - ESP_LOGW(TAG, "Unknown attribute code: 0x%04X", attr_code); - return; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + switch (attr) { + case ATTR_CURRENT_L1: + case ATTR_CURRENT_L1_ALT: + meter_data.irms[0] = value; + break; + + case ATTR_CURRENT_L2: + case ATTR_CURRENT_L2_ALT: + meter_data.irms[1] = value; + break; + + case ATTR_CURRENT_L3: + case ATTR_CURRENT_L3_ALT: + meter_data.irms[2] = value; + break; + + case ATTR_VOLTAGE_L1: meter_data.vrms[0] = value; break; + case ATTR_VOLTAGE_L2: meter_data.vrms[1] = value; break; + case ATTR_VOLTAGE_L3: meter_data.vrms[2] = value; break; + + case ATTR_POWER_L1: meter_data.watt[0] = (int)value; break; + case ATTR_POWER_L2: meter_data.watt[1] = (int)value; break; + case ATTR_POWER_L3: meter_data.watt[2] = (int)value; break; + + case ATTR_POWER_FACTOR: meter_data.power_factor = value; break; + case ATTR_FREQUENCY: meter_data.frequency = value; break; + case ATTR_TOTAL_ENERGY: meter_data.total_energy = value; break; + + default: + ESP_LOGW(TAG, "Unknown attr: 0x%04X", attr); + break; + } + xSemaphoreGive(meter_mutex); } - - float max_current = fmaxf(fmaxf(l1_current, l2_current), l3_current); - ESP_LOGI(TAG, "Max current: %.2f A", max_current); } -static void meter_zigbee_task_func(void *param) { - ESP_LOGI(TAG, "Zigbee meter task started"); - uint8_t *buf = malloc(RX_BUF_SIZE); +// ---------- Task ---------- + +static void meter_task_func(void *param) { + uint8_t *buf = malloc(RX_FRAME_SIZE); if (!buf) { ESP_LOGE(TAG, "Memory allocation failed"); vTaskDelete(NULL); return; } + ESP_LOGI(TAG, "Zigbee meter task started"); + while (1) { - int len = uart_read_bytes(UART_NUM_1, buf, RX_BUF_SIZE, pdMS_TO_TICKS(1000)); + int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(1000)); if (len >= 10) { - decode_frame(buf); + handle_zigbee_frame(buf); } } @@ -71,39 +167,112 @@ static void meter_zigbee_task_func(void *param) { vTaskDelete(NULL); } -int meter_zigbee_send_data(const char *data) { - int len = strlen(data); - int sent = uart_write_bytes(UART_NUM_1, data, len); - ESP_LOGI(TAG, "Sent %d bytes", sent); - return sent; -} +// ---------- Public API (meter.h) ---------- -void meter_zigbee_start(void) { - ESP_LOGI(TAG, "Starting Zigbee UART"); +esp_err_t meter_init(void) { + ESP_LOGI(TAG, "Initializing Zigbee meter"); - uart_config_t uart_config = { + if (!meter_mutex) { + meter_mutex = xSemaphoreCreateMutex(); + if (!meter_mutex) return ESP_ERR_NO_MEM; + } + + meter_data_clear(); + + uart_config_t config = { .baud_rate = 115200, .data_bits = UART_DATA_8_BITS, - .parity = UART_PARITY_DISABLE, + .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_NUM_1, &uart_config)); - ESP_ERROR_CHECK(uart_set_pin(UART_NUM_1, TXD_PIN, RXD_PIN, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)); - ESP_ERROR_CHECK(uart_driver_install(UART_NUM_1, BUF_SIZE * 2, 0, 0, NULL,0)); + 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)); - xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 5, &meter_zigbee_task); + return ESP_OK; } -void meter_zigbee_stop(void) { - ESP_LOGI(TAG, "Stopping Zigbee UART"); +esp_err_t meter_start(void) { + if (meter_task) return ESP_ERR_INVALID_STATE; - if (meter_zigbee_task) { - vTaskDelete(meter_zigbee_task); - meter_zigbee_task = NULL; + xTaskCreate(meter_task_func, "meter_zigbee_task", 4096, NULL, 5, &meter_task); + return ESP_OK; +} + +void meter_stop(void) { + if (meter_task) { + vTaskDelete(meter_task); + meter_task = NULL; } - uart_driver_delete(UART_NUM_1); + uart_driver_delete(UART_PORT); + + if (meter_mutex) { + vSemaphoreDelete(meter_mutex); + meter_mutex = NULL; + } +} + +bool meter_is_running(void) { + return meter_task != NULL; +} + +void meter_clear_data(void) { + meter_data_clear(); +} + +// ---------- RMS Current ---------- +float meter_get_irms_l1(void) { return meter_data_get_float(meter_data.irms, PHASE_L1); } +float meter_get_irms_l2(void) { return meter_data_get_float(meter_data.irms, PHASE_L2); } +float meter_get_irms_l3(void) { return meter_data_get_float(meter_data.irms, PHASE_L3); } + +// ---------- RMS Voltage ---------- +float meter_get_vrms_l1(void) { return meter_data_get_float(meter_data.vrms, PHASE_L1); } +float meter_get_vrms_l2(void) { return meter_data_get_float(meter_data.vrms, PHASE_L2); } +float meter_get_vrms_l3(void) { return meter_data_get_float(meter_data.vrms, PHASE_L3); } + +// ---------- Active Power ---------- +int meter_get_watt_l1(void) { return meter_data_get_int(meter_data.watt, PHASE_L1); } +int meter_get_watt_l2(void) { return meter_data_get_int(meter_data.watt, PHASE_L2); } +int meter_get_watt_l3(void) { return meter_data_get_int(meter_data.watt, PHASE_L3); } + +// ---------- Reactive Power ---------- +int meter_get_var_l1(void) { return meter_data_get_int(meter_data.var, PHASE_L1); } +int meter_get_var_l2(void) { return meter_data_get_int(meter_data.var, PHASE_L2); } +int meter_get_var_l3(void) { return meter_data_get_int(meter_data.var, PHASE_L3); } + +// ---------- Apparent Power ---------- +int meter_get_va_l1(void) { return meter_data_get_int(meter_data.va, PHASE_L1); } +int meter_get_va_l2(void) { return meter_data_get_int(meter_data.va, PHASE_L2); } +int meter_get_va_l3(void) { return meter_data_get_int(meter_data.va, PHASE_L3); } + +// ---------- Extra Data ---------- +float meter_get_frequency(void) { + float v = 0; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + v = meter_data.frequency; + xSemaphoreGive(meter_mutex); + } + return v; +} + +float meter_get_power_factor(void) { + float v = 0; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + v = meter_data.power_factor; + xSemaphoreGive(meter_mutex); + } + return v; +} + +float meter_get_total_energy(void) { + float v = 0; + if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) { + v = meter_data.total_energy; + xSemaphoreGive(meter_mutex); + } + return v; }