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

@@ -1,11 +1,13 @@
# List the source files to be compiled
set(srcs
"driver/meter_ade7758/meter_ade7758.c"
"driver/meter_ade7758/ade7758.c"
"driver/meter_orno/meter_orno513.c"
"driver/meter_orno/meter_orno516.c"
"driver/meter_orno/modbus_params.c"
"driver/meter_zigbee/meter_zigbee.c"
"src/meter_manager.c"
"src/meter_events.c"
)
# List the include directories
@@ -19,4 +21,5 @@ set(includes
# Register the component with the ESP-IDF build system
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "${includes}"
PRIV_REQUIRES nvs_flash
REQUIRES esp_event esp-modbus)

View File

@@ -1,5 +1,6 @@
#include "meter_ade7758.h"
#include "ade7758.h"
#include "meter_events.h"
#include <stdio.h>
#include <string.h>
@@ -42,22 +43,23 @@ static SemaphoreHandle_t meter_mutex = NULL;
static uint32_t meter_watchdog_counter = 0;
// === Utilitários internos ===
static void meter_ade7758_post_event(const meter_ade7758_internal_data_t *data) {
meter_event_data_t evt = {
.frequency = 0,
.power_factor = 0,
.total_energy = 0
};
static void meter_ade7758_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);
}
}
memcpy(evt.vrms, data->vrms, sizeof(evt.vrms));
memcpy(evt.irms, data->irms, sizeof(evt.irms));
memcpy(evt.watt, data->watt, sizeof(evt.watt));
static bool meter_ade7758_read_internal(meter_ade7758_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;
esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY,
&evt, sizeof(evt), pdMS_TO_TICKS(10));
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
return false;
}
static void meter_ade7758_task_func(void *param) {
@@ -66,27 +68,29 @@ static void meter_ade7758_task_func(void *param) {
meter_ade7758_internal_data_t previous = {0};
while (true) {
meter_ade7758_internal_data_t current = {0};
meter_ade7758_internal_data_t meterData = {0};
current.vrms[0] = avrms() / VRMS_CAL;
current.vrms[1] = bvrms() / VRMS_CAL;
current.vrms[2] = cvrms() / VRMS_CAL;
meterData.vrms[0] = avrms() / VRMS_CAL;
meterData.vrms[1] = bvrms() / VRMS_CAL;
meterData.vrms[2] = cvrms() / VRMS_CAL;
current.irms[0] = airms() / IRMS_CAL;
current.irms[1] = birms() / IRMS_CAL;
current.irms[2] = cirms() / IRMS_CAL;
meterData.irms[0] = airms() / IRMS_CAL;
meterData.irms[1] = birms() / IRMS_CAL;
meterData.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 (setPotLine(PHASE_A, 20)) meterData.watt[0] = getWatt(PHASE_A);
if (setPotLine(PHASE_B, 20)) meterData.watt[1] = getWatt(PHASE_B);
if (setPotLine(PHASE_C, 20)) meterData.watt[2] = getWatt(PHASE_C);
if (memcmp(&previous, &current, sizeof(current)) != 0) {
if (memcmp(&previous, &meterData, sizeof(meterData)) != 0) {
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
meter_data = current;
meter_data = meterData;
meter_watchdog_counter++;
xSemaphoreGive(meter_mutex);
meter_ade7758_post_event(&meterData);
}
previous = current;
previous = meterData;
}
vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
@@ -106,8 +110,6 @@ esp_err_t meter_ade7758_init(void) {
}
}
meter_ade7758_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);
@@ -126,8 +128,7 @@ esp_err_t meter_ade7758_init(void) {
esp_err_t meter_ade7758_start(void) {
if (meter_task) return ESP_ERR_INVALID_STATE;
meter_ade7758_clear_internal_data();
BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 5, &meter_task);
BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task);
return result == pdPASS ? ESP_OK : ESP_FAIL;
}
@@ -136,41 +137,4 @@ void meter_ade7758_stop(void) {
vTaskDelete(meter_task);
meter_task = NULL;
}
meter_ade7758_clear_internal_data();
}
bool meter_ade7758_is_running(void) {
return meter_task != NULL;
}
void meter_ade7758_clear_data(void) {
meter_ade7758_clear_internal_data();
}
// === Interface pública: acesso aos dados ===
float meter_ade7758_get_vrms_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[0] : 0; }
float meter_ade7758_get_vrms_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[1] : 0; }
float meter_ade7758_get_vrms_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.vrms[2] : 0; }
float meter_ade7758_get_irms_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[0] : 0; }
float meter_ade7758_get_irms_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[1] : 0; }
float meter_ade7758_get_irms_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.irms[2] : 0; }
int meter_ade7758_get_watt_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[0] : 0; }
int meter_ade7758_get_watt_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[1] : 0; }
int meter_ade7758_get_watt_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.watt[2] : 0; }
int meter_ade7758_get_var_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[0] : 0; }
int meter_ade7758_get_var_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[1] : 0; }
int meter_ade7758_get_var_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.var[2] : 0; }
int meter_ade7758_get_va_l1(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[0] : 0; }
int meter_ade7758_get_va_l2(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[1] : 0; }
int meter_ade7758_get_va_l3(void) { meter_ade7758_internal_data_t d; return meter_ade7758_read_internal(&d) ? d.va[2] : 0; }
// === Diagnóstico ===
uint32_t meter_ade7758_get_watchdog_counter(void) {
return meter_watchdog_counter;
}

View File

@@ -23,47 +23,6 @@ esp_err_t meter_ade7758_start(void);
*/
void meter_ade7758_stop(void);
/**
* @brief Verifica se o medidor ADE7758 está em execução.
*
* @return true se a tarefa estiver ativa, false caso contrário.
*/
bool meter_ade7758_is_running(void);
/**
* @brief Limpa os dados armazenados no medidor ADE7758 (zera todos os valores).
*/
void meter_ade7758_clear_data(void);
// ----- Leituras por fase (L1, L2, L3) -----
// Tensão RMS (em volts)
float meter_ade7758_get_vrms_l1(void);
float meter_ade7758_get_vrms_l2(void);
float meter_ade7758_get_vrms_l3(void);
// Corrente RMS (em amperes)
float meter_ade7758_get_irms_l1(void);
float meter_ade7758_get_irms_l2(void);
float meter_ade7758_get_irms_l3(void);
// Potência ativa (W)
int meter_ade7758_get_watt_l1(void);
int meter_ade7758_get_watt_l2(void);
int meter_ade7758_get_watt_l3(void);
// Potência reativa (VAR)
int meter_ade7758_get_var_l1(void);
int meter_ade7758_get_var_l2(void);
int meter_ade7758_get_var_l3(void);
// Potência aparente (VA)
int meter_ade7758_get_va_l1(void);
int meter_ade7758_get_va_l2(void);
int meter_ade7758_get_va_l3(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_ade7758_get_watchdog_counter(void);
#ifdef __cplusplus
}

View File

@@ -1,325 +1,206 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
#include "meter_orno513.h"
#include "modbus_params.h" // for modbus parameters structures
#include "modbus_params.h"
#include "mbcontroller.h"
#include "sdkconfig.h"
#include "meter_events.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)
#define TAG "serial_mdb_orno513"
static const char *TAG = "serial_mdb";
#define MB_PORT_NUM 2
#define MB_DEV_SPEED 9600
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
#define UPDATE_INTERVAL (3000 / portTICK_PERIOD_MS)
#define POLL_INTERVAL (100 / portTICK_PERIOD_MS)
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_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
// 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 errors
#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(x) ((const char *)(x))
#define OPTS(min, max, step) {.opt1 = min, .opt2 = max, .opt3 = step}
#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}
// State flag
static bool is_initialized = false;
static TaskHandle_t meter_task = NULL;
// 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)
// CID enums
enum {
CID_TOTAL_ACTIVE_ENERGY = 0,
CID_TOTAL_REACTIVE_ENERGY,
CID_ACTIVE_POWER,
CID_APPARENT_POWER,
CID_REACTIVE_POWER,
CID_L1_CURRENT,
CID_L1_VOLTAGE
};
// 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
// Register addresses
#define TOTALFACTIVE 0x010E
#define TOTALRACTIVE 0x0118
#define ACTIVEPOWER 0x0104
#define APPARENTPOWER 0x0106
#define REACTIVEPOWER 0x0108
#define L1CURRENT 0x0102
#define L1VOLTAGE 0x0100
const mb_parameter_descriptor_t device_parameters_orno513[] = {
{CID_TOTAL_ACTIVE_ENERGY, STR("Total Active Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALFACTIVE, 2,
HOLD_OFFSET(total_active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_TOTAL_REACTIVE_ENERGY, STR("Total Reactive Energy"), STR("kWh"), 1, MB_PARAM_HOLDING, TOTALRACTIVE, 2,
HOLD_OFFSET(total_reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_ACTIVE_POWER, STR("Active Power"), STR("W"), 1, MB_PARAM_HOLDING, ACTIVEPOWER, 2,
HOLD_OFFSET(active_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_APPARENT_POWER, STR("Apparent Power"), STR("VA"), 1, MB_PARAM_HOLDING, APPARENTPOWER, 2,
HOLD_OFFSET(apparent_power), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100000, 1), PAR_PERMS_READ},
{CID_REACTIVE_POWER, STR("Reactive Power"), STR("VAR"), 1, MB_PARAM_HOLDING, REACTIVEPOWER, 2,
HOLD_OFFSET(reactive_power), PARAM_TYPE_I32_CDAB, 4, OPTS(-100000, 100000, 1), PAR_PERMS_READ},
{CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(l1_current), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 100, 0.1), PAR_PERMS_READ},
{CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2,
HOLD_OFFSET(l1_voltage), PARAM_TYPE_I32_CDAB, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ}
};
#define SN 0x1000
#define METERID 0x1003
#define FW 0x1004
const uint16_t num_device_parameters_orno513 = sizeof(device_parameters_orno513) / sizeof(device_parameters_orno513[0]);
#define L1VOLTAGE 0x0100
#define L1CURRENT 0x0102
#define ACTIVEPOWER 0x0104
#define APPARENTPOWER 0x0106
#define REACTIVEPOWER 0x0108
#define TOTALFACTIVE 0x010E
#define TOTALRACTIVE 0x0118
// Example Data (Object) Dictionary for Modbus parameters:
const mb_parameter_descriptor_t device_parameters[] = {
{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}
};
// Calculate number of parameters in the table
const uint16_t num_device_parameters = (sizeof(device_parameters) / sizeof(device_parameters[0]));
// 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;
static void *get_param_ptr(const mb_parameter_descriptor_t *param) {
if (!param || param->param_offset == 0) return NULL;
return ((uint8_t *)&holding_reg_params + param->param_offset - 1);
}
// Float - Mid-Little Endian (CDAB)
float ReverseFloat(const float inFloat)
{
float retVal;
char *floatToConvert = (char *)&inFloat;
char *returnFloat = (char *)&retVal;
static void serial_mdb_task(void *param) {
esp_err_t err;
const mb_parameter_descriptor_t *desc = NULL;
// swap the bytes into a temporary buffer
returnFloat[0] = floatToConvert[2];
returnFloat[1] = floatToConvert[3];
returnFloat[2] = floatToConvert[0];
returnFloat[3] = floatToConvert[1];
float voltage[3] = {0};
float current[3] = {0};
int watt[3] = {0};
float energy = 0.0f;
return retVal;
}
while (1) {
for (uint16_t cid = 0; cid < num_device_parameters_orno513; cid++) {
err = mbc_master_get_cid_info(cid, &desc);
if (err != ESP_OK || !desc) continue;
static void serial_mdb_task_func(void *param)
{
ESP_LOGI(TAG, "serial_mdb_task_func");
esp_err_t err = ESP_OK;
void *data_ptr = get_param_ptr(desc);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type);
float l1current = 0;
int error_count = 0;
if (err == ESP_OK && data_ptr) {
int32_t raw = *(int32_t *)data_ptr;
float val = raw / 10.0f;
ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units);
bool alarm_state = false;
const mb_parameter_descriptor_t *param_descriptor = NULL;
ESP_LOGI(TAG, "Start modbus...");
while (true)
{
// 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
err = mbc_master_get_cid_info(cid, &param_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))
{
int value = *(int *)temp_data_ptr;
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;
}
}
switch (cid) {
case CID_L1_VOLTAGE: voltage[0] = val; break;
case CID_L1_CURRENT: current[0] = val; break;
case CID_ACTIVE_POWER:
watt[0] = (int)(val);
watt[1] = watt[0];
watt[2] = watt[0];
break;
case CID_TOTAL_ACTIVE_ENERGY:
energy = val / 1000.0f;
break;
default:
break;
}
else
{
if (error_count > 3 && !meterTest)
{
meterState = false;
vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls
}
else
{
error_count++;
}
ESP_LOGE(TAG, "Characteristic #%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
} else {
ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err));
}
vTaskDelay(POLL_INTERVAL);
}
vTaskDelay(UPDATE_CIDS_TIMEOUT_TICS);
}
meter_event_data_t evt = {
.frequency = 0.0f,
.power_factor = 0.0f,
.total_energy = energy,
.source = "GRID"
};
if (alarm_state)
{
ESP_LOGI(TAG, "Alarm triggered by cid #%u.", param_descriptor->cid);
memcpy(evt.vrms, voltage, sizeof(evt.vrms));
memcpy(evt.irms, current, sizeof(evt.irms));
memcpy(evt.watt, watt, sizeof(evt.watt));
esp_event_post(METER_EVENT, METER_EVENT_DATA_READY, &evt, sizeof(evt), pdMS_TO_TICKS(10));
vTaskDelay(UPDATE_INTERVAL);
}
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());
}
// Modbus master initialization
static esp_err_t master_init(void)
{
esp_err_t meter_orno513_init(void) {
if (is_initialized) {
ESP_LOGW(TAG, "meter_orno513 already initialized");
return ESP_ERR_INVALID_STATE;
}
ESP_LOGI(TAG, "meter_orno513_init");
mb_communication_info_t comm = {
.port = MB_PORT_NUM,
.mode = MB_MODE_RTU,
.baudrate = MB_DEV_SPEED,
.parity = UART_PARITY_DISABLE};
void *master_handler = NULL;
.parity = UART_PARITY_DISABLE
};
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &master_handler);
ESP_RETURN_ON_FALSE((master_handler != NULL), ESP_ERR_INVALID_STATE, TAG,
"mb controller initialization fail.");
ESP_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);
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller setup fail, returns(0x%x).", (int)err);
void *handler = NULL;
err = uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD,
MB_UART_RTS, UART_PIN_NO_CHANGE);
ESP_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();
ESP_RETURN_ON_FALSE((err == ESP_OK), ESP_ERR_INVALID_STATE, TAG,
"mb controller start fail, returned (0x%x).", (int)err);
err = uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX);
ESP_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);
ESP_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;
}
// Function to start the meter
esp_err_t meter_orno513_start(void)
{
ESP_LOGI(TAG, "Starting MDB Serial");
// Call the initialization function directly
esp_err_t err = master_init(); // Don't wrap this in ESP_ERROR_CHECK
ESP_ERROR_CHECK(err); // Check if there was an error during initialization
// Create the task for reading Modbus data
xTaskCreate(serial_mdb_task_func, "serial_mdb_task", 4 * 1024, NULL, 5, &serial_mdb_task);
return err;
}
// Function to stop the meter
void meter_orno513_stop(void)
{
ESP_LOGI(TAG, "Stopping");
if (serial_mdb_task)
{
vTaskDelete(serial_mdb_task);
serial_mdb_task = NULL;
esp_err_t err = mbc_master_init(MB_PORT_SERIAL_MASTER, &handler);
if (err != ESP_OK) {
ESP_LOGE(TAG, "mbc_master_init failed");
return err;
}
uart_driver_delete(MB_PORT_NUM);
ESP_ERROR_CHECK(mbc_master_setup(&comm));
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(mbc_master_start());
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
vTaskDelay(pdMS_TO_TICKS(5));
ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno513, num_device_parameters_orno513));
is_initialized = true;
return ESP_OK;
}
esp_err_t meter_orno513_start(void) {
ESP_LOGI(TAG, "meter_orno513_start");
if (!is_initialized) {
ESP_LOGE(TAG, "meter_orno513 not initialized");
return ESP_ERR_INVALID_STATE;
}
if (meter_task == NULL) {
xTaskCreate(serial_mdb_task, "meter_orno513_task", 4096, NULL, 3, &meter_task);
ESP_LOGI(TAG, "meter_orno513 task started");
}
return ESP_OK;
}
void meter_orno513_stop(void) {
if (!is_initialized) {
ESP_LOGW(TAG, "meter_orno513 not initialized");
return;
}
ESP_LOGI(TAG, "Stopping meter_orno513");
uart_driver_delete(MB_PORT_NUM);
esp_err_t err = mbc_master_destroy();
if (err != ESP_OK) {
ESP_LOGW(TAG, "mbc_master_destroy() returned %s", esp_err_to_name(err));
}
is_initialized = false;
}

View File

@@ -23,37 +23,6 @@ esp_err_t meter_orno513_start(void);
*/
void meter_orno513_stop(void);
/**
* @brief Verifica se o medidor ORNO 513 está em execução.
*
* @return true se a tarefa estiver ativa, false caso contrário.
*/
bool meter_orno513_is_running(void);
/**
* @brief Limpa os dados armazenados no medidor ORNO 513 (zera todos os valores).
*/
void meter_orno513_clear_data(void);
// ----- Leituras por fase (L1) -----
// Tensão RMS (em volts)
float meter_orno513_get_vrms_l1(void);
// Corrente RMS (em amperes)
float meter_orno513_get_irms_l1(void);
// Potência ativa (W)
int meter_orno513_get_watt_l1(void);
// Potência reativa (VAR)
int meter_orno513_get_var_l1(void);
// Potência aparente (VA)
int meter_orno513_get_va_l1(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_orno513_get_watchdog_counter(void);
#ifdef __cplusplus
}

View File

@@ -1,383 +1,217 @@
#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
#include "esp_log.h"
#include "meter_orno516.h"
#include "modbus_params.h" // for modbus parameters structures
#include "meter_events.h"
#include "modbus_params.h"
#include "mbcontroller.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#define TXD_PIN (GPIO_NUM_17)
#define RXD_PIN (GPIO_NUM_16)
static const char *TAG = "serial_mdb_orno516";
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 TAG "serial_mdb_orno516"
#define MB_PORT_NUM 2
#define MB_DEV_SPEED 9600
#define MB_UART_TXD 17
#define MB_UART_RXD 16
#define MB_UART_RTS 5
// 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.
#define UPDATE_INTERVAL (5000 / portTICK_PERIOD_MS)
#define POLL_INTERVAL (100 / portTICK_PERIOD_MS)
// The number of parameters that intended to be used in the particular control process
#define MASTER_MAX_CIDS num_device_parameters_orno516
// 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}
#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)
// Estado do driver
static bool is_initialized = false;
static TaskHandle_t meter_task = NULL;
#define L1VOLTAGE 0x000E
#define L2VOLTAGE 0x0010
#define L3VOLTAGE 0x0012
#define L1CURRENT 0x0016
#define L2CURRENT 0x0018
#define L3CURRENT 0x001A
#define TOTALACTIVEPOWER 0x001C
enum {
CID_L1_CURRENT = 0,
CID_L2_CURRENT,
CID_L3_CURRENT,
CID_L1_VOLTAGE,
CID_L2_VOLTAGE,
CID_L3_VOLTAGE,
CID_TOTAL_ACTIVE_POWER
};
// 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_orno516[] = {
// { 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("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}
{CID_L1_CURRENT, STR("L1 Current"), STR("A"), 1, MB_PARAM_HOLDING, L1CURRENT, 2,
HOLD_OFFSET(l1_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L2_CURRENT, STR("L2 Current"), STR("A"), 1, MB_PARAM_HOLDING, L2CURRENT, 2,
HOLD_OFFSET(l2_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L3_CURRENT, STR("L3 Current"), STR("A"), 1, MB_PARAM_HOLDING, L3CURRENT, 2,
HOLD_OFFSET(l3_current), PARAM_TYPE_FLOAT, 4, OPTS(-1000, 1000, 0.1), PAR_PERMS_READ},
{CID_L1_VOLTAGE, STR("L1 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L1VOLTAGE, 2,
HOLD_OFFSET(l1_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_L2_VOLTAGE, STR("L2 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L2VOLTAGE, 2,
HOLD_OFFSET(l2_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_L3_VOLTAGE, STR("L3 Voltage"), STR("V"), 1, MB_PARAM_HOLDING, L3VOLTAGE, 2,
HOLD_OFFSET(l3_voltage), PARAM_TYPE_FLOAT, 4, OPTS(0, 300, 0.1), PAR_PERMS_READ},
{CID_TOTAL_ACTIVE_POWER, STR("Total Active Power"), STR("W"), 1, MB_PARAM_HOLDING, TOTALACTIVEPOWER, 2,
HOLD_OFFSET(total_active_power), PARAM_TYPE_FLOAT, 4, OPTS(0, 100000, 1), PAR_PERMS_READ}
};
// Calculate number of parameters in the table
const uint16_t num_device_parameters_orno516 = (sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]));
const uint16_t num_device_parameters_orno516 = sizeof(device_parameters_orno516) / sizeof(device_parameters_orno516[0]);
// The function to get pointer to parameter storage (instance) according to parameter description table
static void *master_get_param_data_orno516(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_orno516(const float inFloat)
{
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_orno516(void *param)
{
ESP_LOGI(TAG, "serial_mdb_task_func_orno516");
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)
{
// 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, &param_descriptor);
if ((err != ESP_ERR_NOT_FOUND) && (param_descriptor != NULL))
{
void *temp_data_ptr = master_get_param_data_orno516(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_orno516(value);
switch (cid)
{
case 0:
maxcurrent = 0;
l1current = 0;
l2current = 0;
l3current = 0;
l1current = value;
break;
case 1:
l2current = value;
break;
case 2:
l3current = value;
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
{
if (error_count > 3 && !meterTest)
{
meterState = false;
vTaskDelay(ERROR_TIMEOUT_MS * error_count); // timeout between polls
}
else
{
error_count++;
}
ESP_LOGE(TAG, "Characteristic #%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());
static void *get_param_ptr(const mb_parameter_descriptor_t *param) {
if (!param || param->param_offset == 0) return NULL;
return ((uint8_t *)&holding_reg_params + param->param_offset - 1);
}
// Modbus master initialization
static esp_err_t master_init_orno516(void)
{
// Initialize and start Modbus controller
static void meter_orno516_post_event(float *voltage, float *current, int *power) {
meter_event_data_t evt = {
.source = "GRID",
.frequency = 0.0f, // ORNO-516 não fornece
.power_factor = 0.0f, // idem
.total_energy = 0.0f // idem
};
memcpy(evt.vrms, voltage, sizeof(evt.vrms));
memcpy(evt.irms, current, sizeof(evt.irms));
memcpy(evt.watt, power, sizeof(evt.watt));
esp_err_t err = esp_event_post(METER_EVENT, METER_EVENT_DATA_READY,
&evt, sizeof(evt), pdMS_TO_TICKS(10));
if (err != ESP_OK) {
ESP_LOGW(TAG, "Falha ao emitir evento: %s", esp_err_to_name(err));
}
}
static void serial_mdb_task(void *param) {
esp_err_t err;
const mb_parameter_descriptor_t *desc = NULL;
float voltage[3] = {0}, current[3] = {0};
int power[3] = {0};
while (1) {
for (uint16_t cid = 0; cid < num_device_parameters_orno516; cid++) {
err = mbc_master_get_cid_info(cid, &desc);
if (err != ESP_OK || !desc) continue;
void *data_ptr = get_param_ptr(desc);
uint8_t type = 0;
err = mbc_master_get_parameter(cid, (char *)desc->param_key, (uint8_t *)data_ptr, &type);
if (err == ESP_OK && data_ptr) {
float val = ReverseFloat(*(float *)data_ptr);
ESP_LOGI(TAG, "%s: %.2f %s", desc->param_key, val, desc->param_units);
switch (cid) {
case CID_L1_VOLTAGE: voltage[0] = val; break;
case CID_L2_VOLTAGE: voltage[1] = val; break;
case CID_L3_VOLTAGE: voltage[2] = val; break;
case CID_L1_CURRENT: current[0] = val; break;
case CID_L2_CURRENT: current[1] = val; break;
case CID_L3_CURRENT: current[2] = val; break;
case CID_TOTAL_ACTIVE_POWER:
power[0] = (int)(val / 3);
power[1] = (int)(val / 3);
power[2] = (int)(val / 3);
break;
default:
break;
}
} else {
ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err));
}
vTaskDelay(POLL_INTERVAL);
}
meter_orno516_post_event(voltage, current, power);
vTaskDelay(UPDATE_INTERVAL);
}
}
esp_err_t meter_orno516_init(void) {
if (is_initialized) {
ESP_LOGW(TAG, "Already initialized");
return ESP_ERR_INVALID_STATE;
}
// Tenta apagar UART apenas se estiver inicializada
if (uart_is_driver_installed(MB_PORT_NUM)) {
uart_driver_delete(MB_PORT_NUM);
ESP_LOGI(TAG, "UART driver deleted");
}
mbc_master_destroy(); // OK mesmo que não esteja inicializado
mb_communication_info_t comm = {
//.slave_addr = 1,
.port = MB_PORT_NUM,
.mode = MB_MODE_RTU,
.baudrate = MB_DEV_SPEED,
.parity = UART_PARITY_EVEN};
void *master_handler = NULL;
.parity = UART_PARITY_EVEN
};
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);
void *handler = NULL;
ESP_ERROR_CHECK(mbc_master_init(MB_PORT_SERIAL_MASTER, &handler));
ESP_ERROR_CHECK(mbc_master_setup(&comm));
ESP_ERROR_CHECK(uart_set_pin(MB_PORT_NUM, MB_UART_TXD, MB_UART_RXD, MB_UART_RTS, UART_PIN_NO_CHANGE));
ESP_ERROR_CHECK(mbc_master_start());
ESP_ERROR_CHECK(uart_set_mode(MB_PORT_NUM, UART_MODE_RS485_HALF_DUPLEX));
vTaskDelay(pdMS_TO_TICKS(5));
ESP_ERROR_CHECK(mbc_master_set_descriptor(device_parameters_orno516, num_device_parameters_orno516));
// 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_orno516[0], num_device_parameters_orno516);
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;
is_initialized = true;
return ESP_OK;
}
/**
* @brief Set meter model
*
*/
void serial_mdb_set_model_orno516(bool _enabled)
{
enabled = _enabled;
}
/**
* @brief Set meter state
*
*/
bool serial_mdb_get_meter_state_orno516()
{
return meterState;
}
/**
* @brief Set meter test state
*
*/
void serial_mdb_set_meter_test_orno516(bool _meterTest)
{
meterTest = _meterTest;
}
esp_err_t serial_mdb_start_orno516()
{
ESP_LOGI(TAG, "Starting MDB Serial");
// Call the initialization function and check for errors
esp_err_t err = master_init_orno516();
ESP_ERROR_CHECK(err); // Check if there was an error during initialization
// Create the task to handle the MDB serial communication
xTaskCreate(serial_mdb_task_func_orno516, "serial_mdb_task_orno516", 4 * 1024, NULL, 5, &serial_mdb_task);
return err;
}
void serial_mdb_stop_orno516(void)
{
ESP_LOGI(TAG, "Stopping");
if (serial_mdb_task)
{
vTaskDelete(serial_mdb_task);
serial_mdb_task = NULL;
esp_err_t meter_orno516_start(void) {
if (!is_initialized) {
ESP_LOGE(TAG, "Not initialized");
return ESP_ERR_INVALID_STATE;
}
uart_driver_delete(MB_PORT_NUM);
if (meter_task == NULL) {
xTaskCreate(serial_mdb_task, "meter_orno516_task", 4096, NULL, 3, &meter_task);
ESP_LOGI(TAG, "Task started");
}
return ESP_OK;
}
void meter_orno516_stop(void) {
if (!is_initialized) {
ESP_LOGW(TAG, "Not initialized, skipping stop");
return;
}
if (meter_task) {
vTaskDelete(meter_task);
meter_task = NULL;
ESP_LOGI(TAG, "Task stopped");
}
mbc_master_destroy();
if (uart_is_driver_installed(MB_PORT_NUM)) {
uart_driver_delete(MB_PORT_NUM);
ESP_LOGI(TAG, "UART driver deleted");
}
is_initialized = false;
ESP_LOGI(TAG, "Meter ORNO-516 cleaned up");
}

View File

@@ -10,61 +10,20 @@
*
* @return esp_err_t Retorna ESP_OK se a inicialização for bem-sucedida, caso contrário retorna um erro.
*/
esp_err_t meter_init_orno516(void);
esp_err_t meter_orno516_init(void);
/**
* @brief Inicia a tarefa de leitura de dados do medidor ORNO 516.
*
* @return esp_err_t Retorna ESP_OK se a tarefa for iniciada com sucesso, caso contrário retorna um erro.
*/
esp_err_t meter_start_orno516(void);
esp_err_t meter_orno516_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 516.
*/
void meter_stop_orno516(void);
void meter_orno516_stop(void);
/**
* @brief Verifica se o medidor ORNO 516 está em execução.
*
* @return true Se a tarefa estiver ativa, false caso contrário.
*/
bool meter_is_running_orno516(void);
/**
* @brief Limpa os dados armazenados no medidor ORNO 516 (zera todos os valores).
*/
void meter_clear_data_orno516(void);
// ----- Leituras por fase (L1, L2, L3) -----
// Tensão RMS (em volts)
float meter_get_vrms_l1_orno516(void);
float meter_get_vrms_l2_orno516(void);
float meter_get_vrms_l3_orno516(void);
// Corrente RMS (em amperes)
float meter_get_irms_l1_orno516(void);
float meter_get_irms_l2_orno516(void);
float meter_get_irms_l3_orno516(void);
// Potência ativa (W)
int meter_get_watt_l1_orno516(void);
int meter_get_watt_l2_orno516(void);
int meter_get_watt_l3_orno516(void);
// Potência reativa (VAR)
int meter_get_var_l1_orno516(void);
int meter_get_var_l2_orno516(void);
int meter_get_var_l3_orno516(void);
// Potência aparente (VA)
int meter_get_va_l1_orno516(void);
int meter_get_va_l2_orno516(void);
int meter_get_va_l3_orno516(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_get_watchdog_counter_orno516(void);
#ifdef __cplusplus
}

View File

@@ -4,83 +4,72 @@
* 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 <stdint.h>
// 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;
// Discrete Inputs
typedef struct {
uint8_t discrete_input0 : 1;
uint8_t discrete_input1 : 1;
uint8_t discrete_input2 : 1;
uint8_t discrete_input3 : 1;
uint8_t discrete_input4 : 1;
uint8_t discrete_input5 : 1;
uint8_t discrete_input6 : 1;
uint8_t discrete_input7 : 1;
uint8_t discrete_input_port1;
uint8_t discrete_input_port2;
} discrete_reg_params_t;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct
{
// Coils
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
// Input Registers (pode manter caso use em outro driver)
typedef struct {
float input_data0;
float input_data1;
float input_data2;
float input_data3;
uint16_t data[150];
float input_data4;
float input_data5;
float input_data6;
float input_data7;
uint16_t data_block1[150];
} input_reg_params_t;
#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;
uint32_t holding_data8;
uint32_t holding_data9;
uint32_t holding_data10;
uint32_t holding_data11;
uint32_t holding_data12;
uint32_t holding_data13;
// Holding Registers (ajustado para os campos usados no ORNO 516)
typedef struct {
float l1_current; // 0x0016
float l2_current; // 0x0018
float l3_current; // 0x001A
float l1_voltage; // 0x000E
float l2_voltage; // 0x0010
float l3_voltage; // 0x0012
float total_active_power; // 0x001C
float total_reactive_power;
float active_power;
float apparent_power;
float reactive_power;
} holding_reg_params_t;
#pragma pack(pop)
// Instâncias globais das estruturas
extern holding_reg_params_t holding_reg_params;
extern input_reg_params_t input_reg_params;
extern coil_reg_params_t coil_reg_params;
extern discrete_reg_params_t discrete_reg_params;
#endif // !defined(_DEVICE_PARAMS)
#endif // !_DEVICE_PARAMS

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
}

View File

@@ -0,0 +1,38 @@
#ifndef METER_EVENTS_H
#define METER_EVENTS_H
#include "esp_event.h"
#include "meter_manager.h" // Para meter_type_t
#ifdef __cplusplus
extern "C" {
#endif
// Base de eventos dos medidores
ESP_EVENT_DECLARE_BASE(METER_EVENT);
// IDs de eventos emitidos por medidores
typedef enum {
METER_EVENT_DATA_READY = 0,
METER_EVENT_ERROR,
METER_EVENT_STARTED,
METER_EVENT_STOPPED
} meter_event_id_t;
// Estrutura de dados enviados com METER_EVENT_DATA_READY
typedef struct {
const char *source; // "GRID" ou "EVSE"
float vrms[3]; // Tensão por fase
float irms[3]; // Corrente por fase
int watt[3]; // Potência ativa por fase
float frequency; // Frequência da rede (Hz)
float power_factor; // Fator de potência
float total_energy; // Energia acumulada (kWh)
} meter_event_data_t;
#ifdef __cplusplus
}
#endif
#endif // METER_EVENTS_H

View File

@@ -2,28 +2,66 @@
#define METER_MANAGER_H
#include "esp_err.h"
#include <stdbool.h> // Para garantir que 'bool' seja reconhecido
// Definindo tipos de medidores possíveis para EVSE e Grid
typedef enum {
METER_TYPE_NONE, // Nenhum medidor
METER_TYPE_EVSE_ADE7758, // EVSE com ADE7758
METER_TYPE_GRID_ORNO513, // Grid com ORNO 513
METER_TYPE_GRID_ORNO516, // Grid com ORNO 516
METER_TYPE_GRID_ZIGBEE // Grid com Zigbee
METER_TYPE_NONE, // Nenhum Medidor
METER_TYPE_ADE7758, // ADE7758
METER_TYPE_ORNO513, // ORNO 513
METER_TYPE_ORNO516, // ORNO 516
METER_TYPE_MONO_ZIGBEE, // Medidor Zigbee (Mono)
METER_TYPE_TRIF_ZIGBEE // Medidor Zigbee (Trifásico)
} meter_type_t;
// Funções para inicializar e gerenciar o medidor EVSE (pode ser ADE7758)
esp_err_t meter_manager_init_evse(meter_type_t evse_type); // Inicializa o medidor EVSE (ex: ADE7758)
esp_err_t meter_manager_start_evse(meter_type_t evse_type); // Inicia o EVSE com o tipo especificado
esp_err_t meter_manager_stop_evse(void); // Para o EVSE
/**
* @brief Funções para gerenciar o medidor EVSE (ex: ADE7758).
*/
// Funções para inicializar e gerenciar o medidor Grid (pode ser ORNO 513, ORNO 516 ou Zigbee)
esp_err_t meter_manager_init_grid(meter_type_t grid_type); // Inicializa o medidor Grid (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_start_grid(meter_type_t grid_type); // Inicia o medidor Grid com o tipo especificado
esp_err_t meter_manager_stop_grid(void); // Para o medidor Grid
// Inicializa o medidor EVSE com o tipo especificado (ex: ADE7758)
esp_err_t meter_manager_evse_init(void);
// Funções para ler dados dos medidores
esp_err_t meter_manager_read_current(meter_type_t meter_type, float *current); // Lê a corrente do medidor
esp_err_t meter_manager_read_voltage(meter_type_t meter_type, float *voltage); // Lê a tensão do medidor
// Inicia o medidor EVSE com o tipo especificado
esp_err_t meter_manager_evse_start(void);
// Para o medidor EVSE
esp_err_t meter_manager_evse_stop(void);
// Verifica se o medidor EVSE está habilitado
bool meter_manager_evse_is_enabled(void);
// Define o modelo do medidor EVSE (ADE7758, etc)
esp_err_t meter_manager_evse_set_model(meter_type_t meter_type);
// Retorna o modelo do medidor EVSE (ADE7758, etc)
meter_type_t meter_manager_evse_get_model(void);
/**
* @brief Funções para gerenciar o medidor Grid (ORNO 513, ORNO 516, Zigbee).
*/
// Inicializa o medidor Grid com o tipo especificado (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_grid_init(void);
// Inicia o medidor Grid com o tipo especificado
esp_err_t meter_manager_grid_start(void);
// Para o medidor Grid
esp_err_t meter_manager_grid_stop(void);
// Habilita ou desabilita o medidor Grid
void meter_manager_grid_set_enabled(bool value);
// Define o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee)
esp_err_t meter_manager_grid_set_model(meter_type_t meter_type);
// Retorna o modelo do medidor Grid (ORNO 513, ORNO 516, Zigbee)
meter_type_t meter_manager_grid_get_model(void);
// Função auxiliar para converter o tipo de medidor em uma string
const char* meter_type_to_str(meter_type_t type);
meter_type_t string_to_meter_type(const char *str);
#endif // METER_MANAGER_H

View File

@@ -0,0 +1,4 @@
#include "meter_events.h"
// Define a base de eventos
ESP_EVENT_DEFINE_BASE(METER_EVENT);

View File

@@ -4,135 +4,196 @@
#include "meter_orno513.h"
#include "meter_orno516.h"
#include "meter_zigbee.h"
#include "nvs_flash.h"
#include "nvs.h"
#include <string.h>
static const char *TAG = "meter_manager";
// Variáveis para armazenar o tipo de medidor atual
static meter_type_t current_meter_type = METER_TYPE_NONE;
// Tipos de medidores EVSE e GRID
static meter_type_t meter_evse_type = METER_TYPE_NONE;
static meter_type_t meter_grid_type = METER_TYPE_NONE;
esp_err_t meter_init(meter_type_t meter_type) {
current_meter_type = meter_type;
ESP_LOGI(TAG, "Initializing meter of type: %d", meter_type);
#define NVS_NAMESPACE "meterconfig"
#define NVS_EVSE_MODEL "evse_model"
#define NVS_GRID_MODEL "grid_model"
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_init(); // Inicializa o medidor ADE7758 (EVSE)
case METER_TYPE_GRID_ORNO513:
return meter_orno513_init(); // Inicializa o medidor ORNO 513 ou 516 (Grid)
case METER_TYPE_GRID_ORNO516:
return meter_init_orno516(); // Inicializa o medidor ORNO 513 ou 516 (Grid)
case METER_TYPE_GRID_ZIGBEE:
return meter_init_zigbee(); // Inicializa o medidor Zigbee (Grid)
default:
ESP_LOGE(TAG, "Unsupported meter type");
return ESP_ERR_INVALID_ARG;
// Função unificada para ler ou inicializar um modelo de medidor
static esp_err_t load_or_init_meter_model(const char *key, meter_type_t *type) {
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS handle for %s: %s", key, esp_err_to_name(err));
return err;
}
uint8_t value = 0;
err = nvs_get_u8(handle, key, &value);
if (err == ESP_OK && value <= METER_TYPE_TRIF_ZIGBEE) {
*type = (meter_type_t)value;
ESP_LOGI(TAG, "Loaded meter type %d from NVS key '%s'", value, key);
} else {
*type = METER_TYPE_NONE;
nvs_set_u8(handle, key, *type);
nvs_commit(handle);
ESP_LOGW(TAG, "Invalid or missing key '%s', setting default (NONE)", key);
}
nvs_close(handle);
return ESP_OK;
}
static esp_err_t write_meter_model_to_nvs(const char *key, meter_type_t meter_type) {
nvs_handle_t handle;
esp_err_t err = nvs_open(NVS_NAMESPACE, NVS_READWRITE, &handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Failed to open NVS handle for writing");
return err;
}
err = nvs_set_u8(handle, key, (uint8_t)meter_type);
if (err == ESP_OK) {
err = nvs_commit(handle);
ESP_LOGI(TAG, "Saved meter type %d to NVS key '%s'", meter_type, key);
} else {
ESP_LOGE(TAG, "Failed to write meter type to NVS key '%s'", key);
}
nvs_close(handle);
return err;
}
// Função para inicializar o medidor EVSE
esp_err_t meter_manager_evse_init() {
esp_err_t err = load_or_init_meter_model(NVS_EVSE_MODEL, &meter_evse_type);
if (err != ESP_OK) return err;
ESP_LOGI(TAG, "Initializing EVSE meter of type %s", meter_type_to_str(meter_evse_type));
switch (meter_evse_type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_init();
case METER_TYPE_ORNO513: return meter_orno513_init();
case METER_TYPE_ORNO516: return meter_orno516_init();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init();
default: return ESP_ERR_INVALID_ARG;
}
}
esp_err_t meter_start(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter type is not initialized");
return ESP_ERR_INVALID_STATE;
}
esp_err_t meter_manager_grid_init() {
esp_err_t err = load_or_init_meter_model(NVS_GRID_MODEL, &meter_grid_type);
if (err != ESP_OK) return err;
ESP_LOGI(TAG, "Starting meter");
ESP_LOGI(TAG, "Initializing GRID meter of type %s", meter_type_to_str(meter_grid_type));
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_start();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_start();
case METER_TYPE_GRID_ORNO516:
return meter_start_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_start_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type");
return ESP_ERR_INVALID_ARG;
switch (meter_grid_type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_init();
case METER_TYPE_ORNO513: return meter_orno513_init();
case METER_TYPE_ORNO516: return meter_orno516_init();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_init();
default: return ESP_ERR_INVALID_ARG;
}
}
void meter_stop(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter is not initialized");
return;
}
ESP_LOGI(TAG, "Stopping meter");
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
meter_ade7758_stop();
break;
case METER_TYPE_GRID_ORNO513:
meter_orno513_stop();
break;
case METER_TYPE_GRID_ORNO516:
meter_stop_orno516();
break;
case METER_TYPE_GRID_ZIGBEE:
meter_stop_zigbee();
break;
default:
ESP_LOGE(TAG, "Unsupported meter type");
break;
esp_err_t meter_manager_grid_start() {
meter_type_t type = meter_manager_grid_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_start();
case METER_TYPE_ORNO513: return meter_orno513_start();
case METER_TYPE_ORNO516: return meter_orno516_start();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start();
default: return ESP_ERR_INVALID_ARG;
}
}
bool meter_is_running(void) {
if (current_meter_type == METER_TYPE_NONE) {
ESP_LOGE(TAG, "Meter is not initialized");
return false;
esp_err_t meter_manager_grid_stop(void) {
meter_type_t type = meter_manager_grid_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: meter_ade7758_stop(); break;
case METER_TYPE_ORNO513: meter_orno513_stop(); break;
case METER_TYPE_ORNO516: meter_orno516_stop(); break;
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break;
default: return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_is_running();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_is_running();
case METER_TYPE_GRID_ORNO516:
return meter_is_running_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_is_running_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type");
return false;
esp_err_t meter_manager_evse_set_model(meter_type_t meter_type) {
meter_evse_type = meter_type;
return write_meter_model_to_nvs(NVS_EVSE_MODEL, meter_evse_type);
}
esp_err_t meter_manager_grid_set_model(meter_type_t meter_type) {
meter_grid_type = meter_type;
return write_meter_model_to_nvs(NVS_GRID_MODEL, meter_grid_type);
}
esp_err_t meter_manager_evse_start() {
meter_type_t type = meter_manager_evse_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: return meter_ade7758_start();
case METER_TYPE_ORNO513: return meter_orno513_start();
case METER_TYPE_ORNO516: return meter_orno516_start();
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: return meter_zigbee_start();
default: return ESP_ERR_INVALID_ARG;
}
}
float meter_get_vrms_l1(void) {
if (current_meter_type == METER_TYPE_NONE) return 0;
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_get_vrms_l1();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_get_vrms_l1();
case METER_TYPE_GRID_ORNO516:
return meter_get_vrms_l1_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_get_vrms_l1_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type for reading vrms_l1");
return 0;
esp_err_t meter_manager_evse_stop(void) {
meter_type_t type = meter_manager_evse_get_model();
switch (type) {
case METER_TYPE_NONE: return ESP_OK;
case METER_TYPE_ADE7758: meter_ade7758_stop(); break;
case METER_TYPE_ORNO513: meter_orno513_stop(); break;
case METER_TYPE_ORNO516: meter_orno516_stop(); break;
case METER_TYPE_MONO_ZIGBEE:
case METER_TYPE_TRIF_ZIGBEE: meter_zigbee_stop(); break;
default: return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
bool meter_manager_evse_is_enabled(void) {
return meter_manager_evse_get_model() != METER_TYPE_NONE;
}
meter_type_t meter_manager_evse_get_model(void) {
return meter_evse_type;
}
meter_type_t meter_manager_grid_get_model(void) {
return meter_grid_type;
}
const char* meter_type_to_str(meter_type_t type) {
switch (type) {
case METER_TYPE_NONE: return "NENHUM";
case METER_TYPE_ADE7758: return "IC ADE";
case METER_TYPE_ORNO513: return "ORNO-513";
case METER_TYPE_ORNO516: return "ORNO-516";
case METER_TYPE_MONO_ZIGBEE: return "MONO-ZIGBEE";
case METER_TYPE_TRIF_ZIGBEE: return "TRIF-ZIGBEE";
default: return "NENHUM";
}
}
// Continue as funções `meter_get_*` para cada tipo de dado (corrente, potência, etc.)
float meter_get_irms_l1(void) {
if (current_meter_type == METER_TYPE_NONE) return 0;
switch (current_meter_type) {
case METER_TYPE_EVSE_ADE7758:
return meter_ade7758_get_irms_l1();
case METER_TYPE_GRID_ORNO513:
return meter_orno513_get_irms_l1();
case METER_TYPE_GRID_ORNO516:
return meter_get_irms_l1_orno516();
case METER_TYPE_GRID_ZIGBEE:
return meter_get_irms_l1_zigbee();
default:
ESP_LOGE(TAG, "Unsupported meter type for reading irms_l1");
return 0;
}
meter_type_t string_to_meter_type(const char *str) {
if (!str) return METER_TYPE_NONE;
if (strcmp(str, "IC ADE") == 0) return METER_TYPE_ADE7758;
if (strcmp(str, "ORNO-513") == 0) return METER_TYPE_ORNO513;
if (strcmp(str, "ORNO-516") == 0) return METER_TYPE_ORNO516;
if (strcmp(str, "MONO-ZIGBEE") == 0) return METER_TYPE_MONO_ZIGBEE;
if (strcmp(str, "TRIF-ZIGBEE") == 0) return METER_TYPE_TRIF_ZIGBEE;
return METER_TYPE_NONE;
}
// You should add the rest of the functions similarly as you progress