fix evse_link
This commit is contained in:
@@ -12,36 +12,37 @@
|
||||
#define TAG "meter_zigbee"
|
||||
|
||||
// UART config
|
||||
#define UART_PORT UART_NUM_2
|
||||
#define TXD_PIN GPIO_NUM_17
|
||||
#define RXD_PIN GPIO_NUM_16
|
||||
#define UART_BUF_SIZE 128
|
||||
#define RX_FRAME_SIZE 14
|
||||
#define UART_PORT UART_NUM_2
|
||||
#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 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
|
||||
#define PHASE_COUNT 3
|
||||
#define PHASE_L1 0
|
||||
#define PHASE_L2 1
|
||||
#define PHASE_L3 2
|
||||
|
||||
// Internal meter state
|
||||
typedef struct {
|
||||
typedef struct
|
||||
{
|
||||
float vrms[PHASE_COUNT];
|
||||
float irms[PHASE_COUNT];
|
||||
int watt[PHASE_COUNT];
|
||||
@@ -58,24 +59,28 @@ static meter_zigbee_data_t meter_data = {0};
|
||||
static SemaphoreHandle_t meter_mutex = NULL;
|
||||
static TaskHandle_t meter_zigbee_task = NULL;
|
||||
|
||||
|
||||
bool meter_zigbee_is_running(void) {
|
||||
bool meter_zigbee_is_running(void)
|
||||
{
|
||||
return meter_zigbee_task != NULL;
|
||||
}
|
||||
|
||||
void send_stop_command(void) {
|
||||
//const char *cmd = "stop\n"; // Comando enviado para o outro lado interpretar e dormir
|
||||
//uart_write_bytes(UART_PORT, cmd, strlen(cmd));
|
||||
//uart_wait_tx_done(UART_PORT, pdMS_TO_TICKS(100)); // Aguarda envio terminar
|
||||
static inline int32_t tuya_power16_to_signed(uint16_t p)
|
||||
{
|
||||
// Igual ao quirk multi_dp_to_power()
|
||||
if (p > 0x7FFF)
|
||||
{
|
||||
return (int32_t)((0x999A - p) * -1);
|
||||
}
|
||||
return (int32_t)p;
|
||||
}
|
||||
|
||||
static void meter_zigbee_post_event(void) {
|
||||
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
|
||||
};
|
||||
.total_energy = meter_data.total_energy};
|
||||
|
||||
memcpy(evt.vrms, meter_data.vrms, sizeof(evt.vrms));
|
||||
memcpy(evt.irms, meter_data.irms, sizeof(evt.irms));
|
||||
@@ -87,17 +92,19 @@ static void meter_zigbee_post_event(void) {
|
||||
sizeof(evt),
|
||||
portMAX_DELAY);
|
||||
|
||||
if (err != ESP_OK) {
|
||||
if (err != ESP_OK)
|
||||
{
|
||||
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void handle_zigbee_frame(const uint8_t *buf, size_t len) {
|
||||
static void handle_zigbee_frame(const uint8_t *buf, size_t len)
|
||||
{
|
||||
ESP_LOGD(TAG, "Received UART frame (%d bytes):", len);
|
||||
//ESP_LOG_BUFFER_HEX(TAG, buf, len);
|
||||
// ESP_LOG_BUFFER_HEX(TAG, buf, len);
|
||||
|
||||
if (len < RX_FRAME_SIZE) {
|
||||
if (len < RX_FRAME_SIZE)
|
||||
{
|
||||
ESP_LOGW(TAG, "Invalid frame: too short (len = %d)", len);
|
||||
return;
|
||||
}
|
||||
@@ -105,70 +112,85 @@ static void handle_zigbee_frame(const uint8_t *buf, size_t len) {
|
||||
uint16_t attr = buf[2] | (buf[3] << 8);
|
||||
uint8_t size = buf[5];
|
||||
|
||||
if (size != 8) {
|
||||
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];
|
||||
// payload 8 bytes começa em buf[6]
|
||||
const uint8_t *p = &buf[6];
|
||||
|
||||
float volt = volt_raw / 10.0f;
|
||||
float current = current_raw / 1000.0f;
|
||||
float power = power_raw;
|
||||
uint16_t volt_raw = ((uint16_t)p[0] << 8) | p[1];
|
||||
|
||||
ESP_LOGD(TAG, "Parsed Attr 0x%04X: V=%.1fV I=%.2fA P=%.1fW", attr, volt, current, power);
|
||||
uint16_t curr_raw_u16 = ((uint16_t)p[3] << 8) | p[4]; // 2 bytes
|
||||
uint16_t pow_raw_u16 = ((uint16_t)p[6] << 8) | p[7]; // 2 bytes
|
||||
|
||||
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 = 0;
|
||||
break;
|
||||
case ATTR_FREQUENCY:
|
||||
meter_data.frequency = 0;
|
||||
break;
|
||||
case ATTR_TOTAL_ENERGY:
|
||||
meter_data.total_energy = 0;
|
||||
break;
|
||||
default:
|
||||
ESP_LOGW(TAG, "Unknown attr: 0x%04X", attr);
|
||||
break;
|
||||
int32_t power = tuya_power16_to_signed(pow_raw_u16);
|
||||
|
||||
float volt = volt_raw / 10.0f;
|
||||
float curr = curr_raw_u16 / 1000.0f;
|
||||
|
||||
// Se queres “corrente com sinal”, deriva pelo sinal da potência:
|
||||
float current = (power < 0) ? -curr : curr;
|
||||
|
||||
ESP_LOGD(TAG, "Attr 0x%04X: V=%.1fV I=%.3fA (signed=%+.3fA) P=%+ldW",
|
||||
attr, volt, curr, current, (long)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 = 0;
|
||||
break;
|
||||
case ATTR_FREQUENCY:
|
||||
meter_data.frequency = 0;
|
||||
break;
|
||||
case ATTR_TOTAL_ENERGY:
|
||||
meter_data.total_energy = 0;
|
||||
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]) {
|
||||
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) {
|
||||
static void meter_zigbee_task_func(void *param)
|
||||
{
|
||||
uint8_t *buf = malloc(RX_FRAME_SIZE);
|
||||
if (!buf) {
|
||||
if (!buf)
|
||||
{
|
||||
ESP_LOGE(TAG, "Failed to allocate buffer");
|
||||
vTaskDelete(NULL);
|
||||
return;
|
||||
@@ -176,13 +198,19 @@ static void meter_zigbee_task_func(void *param) {
|
||||
|
||||
ESP_LOGI(TAG, "Zigbee meter task started");
|
||||
|
||||
while (1) {
|
||||
while (1)
|
||||
{
|
||||
int len = uart_read_bytes(UART_PORT, buf, RX_FRAME_SIZE, pdMS_TO_TICKS(5000));
|
||||
if (len == RX_FRAME_SIZE) {
|
||||
if (len == RX_FRAME_SIZE)
|
||||
{
|
||||
handle_zigbee_frame(buf, len);
|
||||
} else if (len == 0) {
|
||||
}
|
||||
else if (len == 0)
|
||||
{
|
||||
ESP_LOGD(TAG, "UART timeout with no data");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
ESP_LOGW(TAG, "Incomplete frame received (%d bytes)", len);
|
||||
}
|
||||
}
|
||||
@@ -191,22 +219,24 @@ static void meter_zigbee_task_func(void *param) {
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
esp_err_t meter_zigbee_init(void) {
|
||||
esp_err_t meter_zigbee_init(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Initializing Zigbee meter");
|
||||
|
||||
if (!meter_mutex) {
|
||||
if (!meter_mutex)
|
||||
{
|
||||
meter_mutex = xSemaphoreCreateMutex();
|
||||
if (!meter_mutex) return ESP_ERR_NO_MEM;
|
||||
if (!meter_mutex)
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
|
||||
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
|
||||
};
|
||||
.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));
|
||||
@@ -215,25 +245,28 @@ esp_err_t meter_zigbee_init(void) {
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t meter_zigbee_start(void) {
|
||||
if (meter_zigbee_task) return ESP_ERR_INVALID_STATE;
|
||||
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)
|
||||
{
|
||||
|
||||
|
||||
void meter_zigbee_stop(void) {
|
||||
|
||||
if (meter_zigbee_task) {
|
||||
if (meter_zigbee_task)
|
||||
{
|
||||
vTaskDelete(meter_zigbee_task);
|
||||
meter_zigbee_task = NULL;
|
||||
}
|
||||
|
||||
uart_driver_delete(UART_PORT);
|
||||
|
||||
if (meter_mutex) {
|
||||
if (meter_mutex)
|
||||
{
|
||||
vSemaphoreDelete(meter_mutex);
|
||||
meter_mutex = NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user