new meter

This commit is contained in:
2025-06-14 10:27:29 +01:00
parent 4892718736
commit 6f95c7ba59
228 changed files with 3178 additions and 3115 deletions

View File

@@ -7,6 +7,7 @@
#include "esp_system.h"
#include "driver/uart.h"
#include "driver/gpio.h"
#include "meter_events.h"
#define TAG "meter_zigbee"
@@ -21,19 +22,15 @@
#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
@@ -55,100 +52,114 @@ typedef struct {
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_task = NULL;
static TaskHandle_t meter_zigbee_task = NULL;
// ---------- Utils ----------
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
};
static inline float decode_float(const uint8_t *buf) {
return (buf[9] + (buf[8] << 8) + (buf[7] << 16)) / 100.0f;
}
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));
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;
}
esp_err_t err = esp_event_post(METER_EVENT,
METER_EVENT_DATA_READY,
&evt,
sizeof(evt),
pdMS_TO_TICKS(10));
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);
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
}
// ---------- Frame Handler ----------
static void handle_zigbee_frame(const uint8_t *buf) {
uint16_t attr = buf[1] | (buf[2] << 8);
uint8_t size = buf[4];
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 (size != 8) {
ESP_LOGW(TAG, "Unexpected data size: %d", size);
if (len < RX_FRAME_SIZE) {
ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len);
return;
}
float value = decode_float(buf);
ESP_LOGI(TAG, "Attr 0x%04X = %.2f", attr, value);
uint16_t attr = buf[2] | (buf[3] << 8);
uint8_t size = buf[5];
if (size != 8) {
ESP_LOGW(TAG, "Unsupported payload size: %d", size);
return;
}
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[0] = value;
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[1] = value;
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[2] = value;
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;
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);
}
// 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));
}
}
// ---------- Task ----------
static void meter_task_func(void *param) {
static void meter_zigbee_task_func(void *param) {
uint8_t *buf = malloc(RX_FRAME_SIZE);
if (!buf) {
ESP_LOGE(TAG, "Memory allocation failed");
ESP_LOGE(TAG, "Failed to allocate buffer");
vTaskDelete(NULL);
return;
}
@@ -156,9 +167,13 @@ static void meter_task_func(void *param) {
ESP_LOGI(TAG, "Zigbee meter task started");
while (1) {
int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(1000));
if (len >= 10) {
handle_zigbee_frame(buf);
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);
}
}
@@ -166,9 +181,7 @@ static void meter_task_func(void *param) {
vTaskDelete(NULL);
}
// ---------- Public API (meter.h) ----------
esp_err_t meter_init(void) {
esp_err_t meter_zigbee_init(void) {
ESP_LOGI(TAG, "Initializing Zigbee meter");
if (!meter_mutex) {
@@ -176,8 +189,6 @@ esp_err_t meter_init(void) {
if (!meter_mutex) return ESP_ERR_NO_MEM;
}
meter_data_clear();
uart_config_t config = {
.baud_rate = 115200,
.data_bits = UART_DATA_8_BITS,
@@ -194,17 +205,17 @@ esp_err_t meter_init(void) {
return ESP_OK;
}
esp_err_t meter_start(void) {
if (meter_task) return ESP_ERR_INVALID_STATE;
esp_err_t meter_zigbee_start(void) {
if (meter_zigbee_task) return ESP_ERR_INVALID_STATE;
xTaskCreate(meter_task_func, "meter_zigbee_task", 4096, NULL, 5, &meter_task);
xTaskCreate(meter_zigbee_task_func, "meter_zigbee_task", 4096, NULL, 3, &meter_zigbee_task);
return ESP_OK;
}
void meter_stop(void) {
if (meter_task) {
vTaskDelete(meter_task);
meter_task = NULL;
void meter_zigbee_stop(void) {
if (meter_zigbee_task) {
vTaskDelete(meter_zigbee_task);
meter_zigbee_task = NULL;
}
uart_driver_delete(UART_PORT);
@@ -215,63 +226,6 @@ void meter_stop(void) {
}
}
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;
bool meter_zigbee_is_running(void) {
return meter_zigbee_task != NULL;
}

View File

@@ -13,85 +13,20 @@ extern "C" {
*
* @return ESP_OK se a inicialização for bem-sucedida, erro caso contrário.
*/
esp_err_t meter_init_zigbee(void);
esp_err_t meter_zigbee_init(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_start_zigbee(void);
esp_err_t meter_zigbee_start(void);
/**
* @brief Interrompe a tarefa e limpa recursos (UART, mutex, etc.).
*/
void meter_stop_zigbee(void);
void meter_zigbee_stop(void);
/**
* @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_zigbee(void);
/**
* @brief Limpa todos os dados armazenados em memória.
*/
void meter_clear_data_zigbee(void);
// ----------------------
// Leituras por fase (L1, L2, L3)
// ----------------------
// Corrente RMS (em amperes)
float meter_get_irms_l1_zigbee(void);
float meter_get_irms_l2_zigbee(void);
float meter_get_irms_l3_zigbee(void);
// Tensão RMS (em volts)
float meter_get_vrms_l1_zigbee(void);
float meter_get_vrms_l2_zigbee(void);
float meter_get_vrms_l3_zigbee(void);
// Potência ativa (W)
int meter_get_watt_l1_zigbee(void);
int meter_get_watt_l2_zigbee(void);
int meter_get_watt_l3_zigbee(void);
// Potência reativa (VAR)
int meter_get_var_l1_zigbee(void);
int meter_get_var_l2_zigbee(void);
int meter_get_var_l3_zigbee(void);
// Potência aparente (VA)
int meter_get_va_l1_zigbee(void);
int meter_get_va_l2_zigbee(void);
int meter_get_va_l3_zigbee(void);
// ----------------------
// Dados adicionais
// ----------------------
/**
* @brief Retorna a frequência da rede em Hz.
*
* @return Valor da frequência da rede em Hz.
*/
float meter_get_frequency_zigbee(void);
/**
* @brief Retorna o fator de potência médio.
*
* @return Valor do fator de potência médio.
*/
float meter_get_power_factor_zigbee(void);
/**
* @brief Retorna a energia total acumulada (kWh ou Wh, dependendo do dispositivo).
*
* @return Valor da energia total acumulada.
*/
float meter_get_total_energy_zigbee(void);
#ifdef __cplusplus
}