#include "meter_zigbee.h" #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include "freertos/semphr.h" #include "esp_log.h" #include "esp_system.h" #include "driver/uart.h" #include "driver/gpio.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 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 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) { ESP_LOGW(TAG, "Unexpected data size: %d", size); return; } float value = decode_float(buf); ESP_LOGI(TAG, "Attr 0x%04X = %.2f", attr, value); 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); } } // ---------- 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_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(1000)); if (len >= 10) { handle_zigbee_frame(buf); } } free(buf); vTaskDelete(NULL); } // ---------- Public API (meter.h) ---------- esp_err_t meter_init(void) { ESP_LOGI(TAG, "Initializing Zigbee meter"); 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, .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_start(void) { if (meter_task) return ESP_ERR_INVALID_STATE; 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_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; }