Files
chargeflow/projeto_parte2.c
2025-06-25 06:34:03 +01:00

2715 lines
93 KiB
C
Raw Blame History

// === Início de: components/peripherals/include/rcm.h ===
#ifndef RCM_H_
#define RCM_H_
#include <stdbool.h>
/**
* @brief Initialize residual current monitor
*
*/
void rcm_init(void);
/**
* @brief Test residual current monitor
*
* @return true
* @return false
*/
bool rcm_test(void);
/**
* @brief Residual current monitor was detected leakage
*
* @return true
* @return false
*/
bool rcm_is_triggered(void);
#endif /* RCM_H_ */
// === Fim de: components/peripherals/include/rcm.h ===
// === Início de: components/peripherals/include/aux_io.h ===
#ifndef AUX_IO_H_
#define AUX_IO_H_
#include "esp_err.h"
/**
* @brief Initialize aux
*
*/
void aux_init(void);
/**
* @brief Read digital input
*
* @param name
* @param value
* @return esp_err_t
*/
esp_err_t aux_read(const char *name, bool *value);
/**
* @brief Write digial output
*
* @param name
* @param value
* @return esp_err_t
*/
esp_err_t aux_write(const char *name, bool value);
/**
* @brief Read analog input
*
* @param name
* @param value
* @return esp_err_t
*/
esp_err_t aux_analog_read(const char *name, int *value);
#endif /* AUX_IO_H_ */
// === Fim de: components/peripherals/include/aux_io.h ===
// === Início de: components/peripherals/include/led.h ===
#ifndef LED_H_
#define LED_H_
#include <stdint.h>
#include <stdbool.h>
/**
* @brief Identificadores dos LEDs disponíveis no hardware
*/
typedef enum {
LED_ID_STOP,
LED_ID_CHARGING,
LED_ID_ERROR,
LED_ID_MAX
} led_id_t;
/**
* @brief Padrões de comportamento possíveis para os LEDs
*/
typedef enum {
LED_PATTERN_OFF, ///< LED sempre desligado
LED_PATTERN_ON, ///< LED sempre ligado
LED_PATTERN_BLINK, ///< Pisca com ciclo padrão (500ms on / 500ms off)
LED_PATTERN_BLINK_FAST, ///< Pisca rápido (200ms / 200ms)
LED_PATTERN_BLINK_SLOW, ///< Pisca lento (300ms / 1700ms)
LED_PATTERN_CHARGING_EFFECT ///< Efeito visual para carregamento (2s on / 1s off)
} led_pattern_t;
/**
* @brief Inicializa os LEDs com base na configuração da placa
* Deve ser chamada uma única vez na inicialização do sistema.
*/
void led_init(void);
/**
* @brief Define diretamente o tempo ligado/desligado de um LED.
* Pode ser usado para padrões personalizados.
*
* @param led_id Identificador do LED (ver enum led_id_t)
* @param ontime Tempo ligado em milissegundos
* @param offtime Tempo desligado em milissegundos
*/
void led_set_state(led_id_t led_id, uint16_t ontime, uint16_t offtime);
/**
* @brief Aplica um dos padrões de piscar definidos ao LED
*
* @param led_id Identificador do LED (ver enum led_id_t)
* @param pattern Padrão desejado (ver enum led_pattern_t)
*/
void led_apply_pattern(led_id_t led_id, led_pattern_t pattern);
#endif /* LED_H_ */
// === Fim de: components/peripherals/include/led.h ===
// === Início de: components/peripherals/include/buzzer.h ===
#ifndef BUZZER_H_
#define BUZZER_H_
#include <stdint.h>
/**
* @brief Inicializa o buzzer e inicia monitoramento automático do estado EVSE.
*/
void buzzer_init(void);
/**
* @brief Liga e desliga o buzzer manualmente (uso interno ou testes).
*/
void buzzer_on(void);
void buzzer_off(void);
/**
* @brief Ativa o buzzer por um período fixo (em milissegundos).
*/
void buzzer_beep_ms(uint16_t ms);
#endif /* BUZZER_H_ */
// === Fim de: components/peripherals/include/buzzer.h ===
// === Início de: components/peripherals/include/ac_relay.h ===
#ifndef AC_RELAY_H_
#define AC_RELAY_H_
#include <stdbool.h>
/**
* @brief Inicializa o relé de corrente alternada.
*/
void ac_relay_init(void);
/**
* @brief Define o estado do relé de corrente alternada.
*
* @param state true para ligar, false para desligar.
*/
void ac_relay_set_state(bool state);
/**
* @brief Retorna o estado atual do relé de corrente alternada.
*
* @return true se estiver ligado, false se desligado.
*/
bool ac_relay_get_state(void);
#endif /* AC_RELAY_H_ */
// === Fim de: components/peripherals/include/ac_relay.h ===
// === Início de: components/peripherals/include/lm75a.h ===
#ifndef LM75A_H
#define LM75A_H
#include "esp_err.h" // Para o uso de tipos de erro do ESP-IDF, caso esteja utilizando.
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Inicializa o sensor LM75A.
*
* Configura o sensor para leitura e define os pinos de comunicação.
*/
esp_err_t lm75a_init(void);
/**
* @brief Desinicializa o sensor LM75A.
*
* Libera os recursos usados pelo sensor.
*/
esp_err_t lm75a_deinit(void);
/**
* @brief Lê a temperatura do LM75A.
*
* @param show Se for 1, a temperatura será exibida em algum tipo de log ou interface.
* Se for 0, o valor é apenas retornado sem exibição.
* @return A temperatura lida em graus Celsius.
*/
float lm75a_read_temperature(int show);
/**
* @brief Define o valor do limite de temperatura (T_OS) para o sensor LM75A.
*
* @param tos O limite de temperatura de sobrecarga (T_OS) em graus Celsius.
* @return ESP_OK em caso de sucesso ou código de erro se falhar.
*/
esp_err_t lm75a_set_tos(int tos);
/**
* @brief Define o valor do limite de temperatura de histerese (T_HYS) para o sensor LM75A.
*
* @param thys O limite de histerese de temperatura (T_HYS) em graus Celsius.
* @return ESP_OK em caso de sucesso ou código de erro se falhar.
*/
esp_err_t lm75a_set_thys(int thys);
/**
* @brief Obtém o limite de temperatura de sobrecarga (T_OS) do sensor LM75A.
*
* @return O valor atual de T_OS em graus Celsius.
*/
int lm75a_get_tos(void);
/**
* @brief Obtém o limite de temperatura de histerese (T_HYS) do sensor LM75A.
*
* @return O valor atual de T_HYS em graus Celsius.
*/
int lm75a_get_thys(void);
/**
* @brief Habilita ou desabilita a interrupção do LM75A.
*
* @param en 1 para habilitar a interrupção, 0 para desabilitar.
* @return ESP_OK em caso de sucesso ou código de erro se falhar.
*/
esp_err_t lm75a_set_int(int en);
/**
* @brief Obtém o estado do pino OS (Overtemperature Shutdown) do LM75A.
*
* @return 1 se o pino OS estiver ativo (indica que a temperatura de sobrecarga foi atingida),
* 0 caso contrário.
*/
int lm75a_get_osio(void);
#ifdef __cplusplus
}
#endif
#endif /* LM75A_H */
// === Fim de: components/peripherals/include/lm75a.h ===
// === Início de: components/peripherals/include/ntc_sensor.h ===
#ifndef NTC_SENSOR_H_
#define NTC_SENSOR_H_
/**
* @brief Initialize ntc senso
*
*/
void ntc_sensor_init(void);
/**
* @brief Return temperature after temp_sensor_measure
*
* @return float
*/
float ntc_temp_sensor(void);
#endif /* NTC_SENSOR_H_ */
// === Fim de: components/peripherals/include/ntc_sensor.h ===
// === Início de: components/peripherals/include/proximity.h ===
#ifndef PROXIMITY_H_
#define PROXIMITY_H_
#include <stdint.h>
/**
* @brief Initialize proximity check
*
*/
void proximity_init(void);
/**
* @brief Return measured value of max current on PP
*
* @return current in A
*/
uint8_t proximity_get_max_current(void);
#endif /* PROXIMITY_H_ */
// === Fim de: components/peripherals/include/proximity.h ===
// === Início de: components/peripherals/include/socket_lock.h ===
#ifndef SOCKED_LOCK_H_
#define SOCKED_LOCK_H_
#include "esp_err.h"
typedef enum
{
SOCKED_LOCK_STATUS_IDLE,
SOCKED_LOCK_STATUS_OPERATING,
SOCKED_LOCK_STATUS_LOCKING_FAIL,
SOCKED_LOCK_STATUS_UNLOCKING_FAIL
} socket_lock_status_t;
/**
* @brief Initialize socket lock
*
*/
void socket_lock_init(void);
/**
* @brief Get socket lock detection on high, stored in NVS
*
* @return true when locked has zero resistance
* @return false when unlocked has zero resistance
*/
bool socket_lock_is_detection_high(void);
/**
* @brief Set socket lock detection on high, stored in NVS
*
* @param detection_high
*/
void socket_lock_set_detection_high(bool detection_high);
/**
* @brief Get socket lock operating time
*
* @return time in ms
*/
uint16_t socket_lock_get_operating_time(void);
/**
* @brief Set socket lock operating time
*
* @param operating_time - time in ms
* @return esp_err_t
*/
esp_err_t socket_lock_set_operating_time(uint16_t operating_time);
/**
* @brief Get socket lock retry count
*
* @return retry count
*/
uint8_t socket_lock_get_retry_count(void);
/**
* @brief Set socket lock retry count
*
* @param retry_count
*/
void socket_lock_set_retry_count(uint8_t retry_count);
/**
* @brief Get socket lock break time
*
* @return time in ms
*/
uint16_t socket_lock_get_break_time(void);
/**
* @brief Set socket lock break time
*
* @param break_time
* @return esp_err_t
*/
esp_err_t socket_lock_set_break_time(uint16_t break_time);
/**
* @brief Set socke lock to locked / unlocked state
*
* @param locked
*/
void socket_lock_set_locked(bool locked);
/**
* @brief Get socket lock status
*
* @return socket_lock_status_t
*/
socket_lock_status_t socket_lock_get_status(void);
/**
* @brief Read the current physical lock state using the detection pin.
*/
bool socket_lock_is_locked_state(void);
#endif /* SOCKED_LOCK_H_ */
// === Fim de: components/peripherals/include/socket_lock.h ===
// === Início de: components/peripherals/include/adc.h ===
#ifndef ADC_H_
#define ADC_H_
#include "esp_adc/adc_oneshot.h"
#include "esp_adc/adc_cali.h"
#include "esp_adc/adc_cali_scheme.h"
extern adc_oneshot_unit_handle_t adc_handle;
extern adc_cali_handle_t adc_cali_handle;
void adc_init(void);
#endif /* ADC_H_ */
// === Fim de: components/peripherals/include/adc.h ===
// === Início de: components/peripherals/include/temp_sensor.h ===
#ifndef TEMP_SENSOR_H_
#define TEMP_SENSOR_H_
#include <stdint.h>
#include "esp_err.h"
/**
* @brief Initialize DS18S20 temperature sensor bus
*
*/
void temp_sensor_init(void);
/**
* @brief Get found sensor count
*
* @return uint8_t
*/
uint8_t temp_sensor_get_count(void);
/**
* @brief Return lowest temperature after temp_sensor_measure
*
* @return int16_t
*/
int16_t temp_sensor_get_low(void);
/**
* @brief Return highest temperature after temp_sensor_measure
*
* @return int
*/
int temp_sensor_get_high(void);
/**
* @brief Return temperature sensor error
*
* @return bool
*/
bool temp_sensor_is_error(void);
#endif /* TEMP_SENSOR_H_ */
// === Fim de: components/peripherals/include/temp_sensor.h ===
// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h ===
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Inicializa o driver do medidor ADE7758 (SPI, mutex, registradores).
*/
esp_err_t meter_ade7758_init(void);
/**
* @brief Inicia a tarefa de leitura de dados do medidor ADE7758.
*/
esp_err_t meter_ade7758_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos do medidor ADE7758.
*/
void meter_ade7758_stop(void);
#ifdef __cplusplus
}
#endif
// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.h ===
// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.h ===
#include "driver/spi_common.h"
#include "driver/spi_master.h"
#define WRITE 0x80 // WRITE bit BT7 to write to registers
#define CLKIN 10000000 // ADE7758 frec, 10.000000MHz
#define PERIODO 50 // Actually it is frequency, it is used to calculate the amount of Cycles that it accumulates for energy.
#define PHASE_A 1
#define PHASE_B 2
#define PHASE_C 3
//Register address
//------Name--------Address---------Lenght
#define AWATTHR 0x01 //---------16
#define BWATTHR 0x02 //---------16
#define CWATTHR 0x03 //---------16
#define AVARHR 0x04 //---------16
#define BVARHR 0x05 //---------16
#define CVARHR 0x06 //---------16
#define AVAHR 0x07 //---------16
#define BVAHR 0x08 //---------16
#define CVAHR 0x09 //---------16
#define AIRMS 0x0A //---------24
#define BIRMS 0x0B //---------24
#define CIRMS 0x0C //---------24
#define AVRMS 0x0D //---------24
#define BVRMS 0x0E //---------24
#define CVRMS 0x0F //---------24
#define FREQ 0x10 //---------12
#define TEMP 0x11 //---------8
#define WFORM 0x12 //---------24
#define OPMODE 0x13 //---------8
#define MMODE 0x14 //---------8
#define WAVMODE 0x15 //---------8
#define COMPMODE 0x16 //---------8
#define LCYCMODE 0x17 //---------8
#define MASK 0x18 //---------24
#define STATUS 0x19 //---------24
#define RSTATUS 0x1A //---------24
#define ZXTOUT 0x1B //---------16
#define LINECYC 0x1C //---------16
#define SAGCYC 0x1D //---------8
#define SAGLVL 0x1E //---------8
#define VPINTLVL 0x1F //---------8
#define IPINTLVL 0x20 //---------8
#define VPEAK 0x21 //---------8
#define IPEAK 0x22 //---------8
#define GAIN 0x23 //---------8
#define AVRMSGAIN 0x24 //---------12
#define BVRMSGAIN 0x25 //---------12
#define CVRMSGAIN 0x26 //---------12
#define AIGAIN 0x27 //---------12
#define BIGAIN 0x28 //---------12
#define CIGAIN 0x29 //---------12
#define AWG 0x2A //---------12
#define BWG 0x2B //---------12
#define CWG 0x2C //---------12
#define AVARG 0x2D //---------12
#define BVARG 0x2E //---------12
#define CVARG 0x2F //---------12
#define AVAG 0x30 //---------12
#define BVAG 0X31 //---------12
#define CVAG 0x32 //---------12
#define AVRMSOS 0x33 //---------12
#define BVRMSOS 0X34 //---------12
#define CVRMSOS 0X35 //---------12
#define AIRMSOS 0X36 //---------12
#define BIRMSOS 0X37 //---------12
#define CIRMSOS 0X38 //---------12
#define AWATTOS 0X39 //---------12
#define BWATTOS 0X3A //---------12
#define CWATTOS 0X3B //---------12
#define AVAROS 0X3C //---------12
#define BVAROS 0X3D //---------12
#define CVAROS 0X3E //---------12
#define APHCAL 0X3F //---------7
#define BPHCAL 0X40 //---------7
#define CPHCAL 0X41 //---------7
#define WDIV 0X42 //---------8
#define VARDIV 0X43 //---------8
#define VADIV 0X44 //---------8
#define APCFNUM 0X45 //---------16
#define APCFDEN 0X46 //---------12
#define VARCFNUM 0X47 //---------16
#define VARCFDEN 0X48 //---------12
#define CHKSUM 0X7E //---------8
#define VERSION 0x7f //---------8
#define DUMMY_BYTE 0xFF
//bits
/**
OPERATIONAL MODE REGISTER (0x13)
The general configuration of the ADE7758 is defined by writing to the OPMODE register.
Table 18 summarizes the functionality of each bit in the OPMODE register.
Bit Location Bit Mnemonic Default Value Description
0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set.
1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set.
2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set.
3 to 5 DISMOD 0 By setting these bits, ADE7758<35>s ADCs can be turned off. In normal operation, these bits should be left at Logic 0.
DISMOD[2:0] Description
0 0 0 Normal operation.
1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels.
0 0 1 Switch off only the current channel ADCs.
1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths.
0 1 0 Switch off only the voltage channel ADCs.
1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths.
0 1 1 Put the ADE7758 in sleep mode.
1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ).
6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 <20>s after a software reset.
7 RESERVED 0 This should be left at 0.
*/
#define DISHPF 0x01
#define DISLPF 0x02
#define DISCF 0x04
#define SWRST 0x40
/**
MEASUREMENT MODE REGISTER (0x14)
The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register.
Table 19 summarizes the functionality of each bit in the MMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency.
FREQSEL1 FREQSEL0 Source
0 0 Phase A
0 1 Phase B
1 0 Phase C
1 1 Reserved
2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers.
Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values
of the largest current and voltage waveform (over a fixed number of half-line cycles)
from Phase A. The number of half-line cycles is determined by the content of the
LINECYC register. At the end of the LINECYC number of half-line cycles, the content
of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns
on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one
bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is,
the voltage and current peak are independently processed (see the Peak Current Detection section).
5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection.
Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A.
Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C.
Note that more than one bit can be set for detection on multiple phases.
If the absolute values of the voltage or current waveform samples in the selected phases exceeds
the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the
STATUS registers are set (see the Peak Current Detection section).
*/
#define FREQSEL0 0x01
#define FREQSEL1 0x02
/**
WAVEFORM MODE REGISTER (0x15)
The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register.
Table 20 summarizes the functionality of each bit in the WAVMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample.
PHSEL[1:0] Source
0 0 Phase A
0 1 Phase B
1 0 Phase C
1 1 Reserved
2 to 4 WAVSEL 0 These bits are used to select the type of waveform.
WAVSEL[2:0] Source
0 0 0 Current
0 0 1 Voltage
0 1 0 Active Power Multiplier Output
0 1 1 Reactive Power Multiplier Output
1 0 0 VA Multiplier Output
-Others- Reserved
5 to 6 DTRT 0 These bits are used to select the data rate.
DTRT[1:0] Update Rate
0 0 26.04 kSPS (CLKIN/3/128)
0 1 13.02 kSPS (CLKIN/3/256)
1 0 6.51 kSPS (CLKIN/3/512)
1 1 3.25 kSPS (CLKIN/3/1024)
7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output
frequency that is proportional to the total apparent power (VA).
In the default state, Logic 0, the VARCF pin outputs a frequency proportional
to the total reactive power (VAR).
*/
/**
COMPUTATIONAL MODE REGISTER (0x16)
The computational method of the ADE7758 is defined by writing to the COMPMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers.
CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by <20>90<39>, respectively.
Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10
AWATTHR VA <20> IA VA <20> (IA <20> IB) VA <20> (IA<49>IB)
BWATTHR VB <20> IB 0 0
CWATTHR VC <20> IC VC <20> (IC <20> IB) VC <20> IC
AVARHR VA <20> IA VA <20> (IA <20> IB) VA <20> (IA<49>IB)
BVARHR VB <20> IB 0 0
CVARHR VC <20> IC VC <20> (IC <20> IB) VC <20> IC
AVAHR VARMS <20> IARMS VARMS <20> IARMS VARMS <20> ARMS
BVAHR VBRMS <20> IBRMS (VARMS + VCRMS)/2 <20> IBRMS VARMS <20> IBRMS
CVAHR VCRMS <20> ICRMS VCRMS <20> ICRMS VCRMS <20> ICRMS
2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs.
Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included.
Bit 3 and Bit 4 are for Phase B and Phase C, respectively.
Setting all three bits enables the sum of all three phases to be included in the frequency outputs
(see the Active Power Frequency Output and the Reactive Power Frequency Output sections).
5 ABS 0 Setting this bit places the APCF output pin in absolute only mode.
Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour
accumulation registers (AWATTHR, BWATTHR, and CWATTHR).
Note that this bit only affects the APCF pin and has no effect on the content of the corresponding
registers.
6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode.
Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation
registers (AVARHR, BVARHR, and CVARHR).
The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase,
that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive,
there is no change to the sign of the VAR.
Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding
registers.
7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758.
*/
/**
LINE CYCLE ACCUMULATION MODE REGISTER (0x17)
The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register.
Bit Location Bit Mnemonic Default Value Description
0 LWATT 0 Setting this bit places the watt-hour accumulation registers
(AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode.
1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers)
into line-cycle accumulation mode.
2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers)
into line-cycle accumulation mode.
3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle
accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively.
More than one phase can be selected for the zero-crossing detection,
and the accumulation time is shortened accordingly.
6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three
phases, that is, a read to those registers resets the registers to 0 after the content of the registers
have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1.
7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the
line input.
*/
#define LWATT 0x01
#define LVAR 0x02
#define LVA 0x04
#define ZXSEL_A 0x08
#define ZXSEL_B 0x10
#define ZXSEL_C 0x20
#define RSTREAD 0x40
#define FREQSEL 0x80
/** INTERRUPT MASK REGISTER (0x18)
When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register.
The IRQ logic output is reset to its default collector open state when the RSTATUS register is read.
describes the function of each bit in the interrupt mask register.
**/
// The next table summarizes the function of each bit for
// the Interrupt Enable Register
/* Bit Mask // Bit Location / Description
#define AEHF 0x0001 // bit 0 - Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers, that is, the WATTHR register is half full.
#define REHF 0x0002 // bit 1 - Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers, that is, the VARHR register is half full.
#define VAEHF 0x0004 // bit 2 - Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR registers, that is, the VAHR register is half full.
#define SAGA 0x0008 // bit 3 - Enables an interrupt when there is a SAG on the line voltage of the Phase A.
#define SAGB 0x0010 // bit 4 - Enables an interrupt when there is a SAG on the line voltage of the Phase B.
#define SAGC 0x0020 // bit 5 - Enables an interrupt when there is a SAG on the line voltage of the Phase C.
#define ZXTOA 0x0040 // bit 6 - Enables an interrupt when there is a zero-crossing timeout detection on Phase A.
#define ZXTOB 0x0080 // bit 7 - Enables an interrupt when there is a zero-crossing timeout detection on Phase B.
#define ZXTOC 0x0100 // bit 8 - Enables an interrupt when there is a zero-crossing timeout detection on Phase C.
#define ZXA 0x0200 // bit 9 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase A
#define ZXB 0x0400 // bit 10 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase B
#define ZXC 0x0800 // bit 11 - Enables an interrupt when there is a zero crossing in the voltage channel of Phase C
#define LENERGY 0x1000 // bit 12 - Enables an interrupt when the energy accumulations over LINECYC are finished.
//RESERVED 0x2000 // bit 13 - RESERVED
#define PKV 0x4000 // bit 14 - Enables an interrupt when the voltage input selected in the MMODE register is above the value in the VPINTLVL register.
#define PKI 0x8000 // bit 15 - Enables an interrupt when the current input selected in the MMODE register is above the value in the IPINTLVL register.
#define WFSM 0x010000 // bit 16 - Enables an interrupt when data is present in the WAVEMODE register.
#define REVPAP 0x020000 // bit 17 - Enables an interrupt when there is a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register.
#define REVPRP 0x040000 // bit 18 - Enables an interrupt when there is a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register.
#define SEQERR 0x080000 // bit 19 - Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing of Phase C but with that of Phase B.
*/
/** INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A)
The interrupt status register is used to determine the source of an interrupt event.
When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set.
The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set.
When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt.
All the interrupts in the interrupt status register stay at their logic high state after an event occurs.
The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read.
**/
// The next table summarizes the function of each bit for
// the Interrupt Status Register, the Reset Interrupt Status Register.
// Bit Mask // Bit Location / Description
#define AEHF 0x0001 // bit 0 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three WATTHR registers, that is, the WATTHR register is half full.
#define REHF 0x0002 // bit 1 - Indicates that an interrupt was caused by a change in Bit 14 among any one of the three VARHR registers, that is, the VARHR register is half full.
#define VAEHF 0x0004 // bit 2 - Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three VAHR registers, that is, the VAHR register is half full.
#define SAGA 0x0008 // bit 3 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A.
#define SAGB 0x0010 // bit 4 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B.
#define SAGC 0x0020 // bit 5 - Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C.
#define ZXTOA 0x0040 // bit 6 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A.
#define ZXTOB 0x0080 // bit 7 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B.
#define ZXTOC 0x0100 // bit 8 - Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C
#define ZXA 0x0200 // bit 9 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A.
#define ZXB 0x0400 // bit 10 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B
#define ZXC 0x0800 // bit 11 - Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C
#define LENERGY 0x1000 // bit 12 - In line energy accumulation, indicates the end of an integration over an integer number of half- line cycles (LINECYC). See the Calibration section.
#define RESET 0x2000 // bit 13 - Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758 and sets the registers back to their default values. This bit in the STATUS or RSTATUS register is logic high for only one clock cycle after a reset event.
#define PKV 0x4000 // bit 14 - Indicates that an interrupt was caused when the selected voltage input is above the value in the VPINTLVL register.
#define PKI 0x8000 // bit 15 - Indicates that an interrupt was caused when the selected current input is above the value in the IPINTLVL register.
#define WFSM 0x010000 // bit 16 - Indicates that new data is present in the waveform register.
#define REVPAP 0x020000 // bit 17 - Indicates that an interrupt was caused by a sign change in the watt calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register.
#define REVPRP 0x040000 // bit 18 - Indicates that an interrupt was caused by a sign change in the VAR calculation among any one of the phases specified by the TERMSEL bits in the COMPMODE register.
#define SEQERR 0x080000 // bit 19 - Indicates that an interrupt was caused by a zero crossing from Phase A followed not by the zero crossing of Phase C but by that of Phase B.
//constants
#define GAIN_1 0x00
#define GAIN_2 0x01
#define GAIN_4 0x02
#define INTEGRATOR_ON 1
#define INTEGRATOR_OFF 0
#define FULLSCALESELECT_0_5V 0x00
#define FULLSCALESELECT_0_25V 0x01
#define FULLSCALESELECT_0_125V 0x02
esp_err_t transferByte(const uint8_t reg_addr, const uint8_t data, const uint8_t command);
esp_err_t transferMultiplesBytes(const uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t data_length, const uint8_t command);
esp_err_t Init(const spi_host_device_t spi_peripheral, const int pin_miso, const int pin_mosi, const int pin_sclk);
esp_err_t InitSpi(const int ss);
esp_err_t RegisterDevice(const uint8_t mode, const int ss, const int addr_length, const int command_length, const int bus_speed);
uint8_t ReadRegister(const uint8_t reg_addr, const uint8_t command);
esp_err_t WriteRegister(const uint8_t reg_addr, const uint8_t reg_data, const uint8_t command);
esp_err_t WriteRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command);
esp_err_t ReadRegisterMultipleBytes(const uint8_t reg_addr, uint8_t *reg_data_buffer, const uint8_t byte_count, const uint8_t command);
spi_device_handle_t GetHandle();
//----------------------------------------------------------------------------
// Modes and configurations
//----------------------------------------------------------------------------
void setOpMode(uint8_t m);
uint8_t getOpMode();
void setMMode(uint8_t m);
uint8_t getMMode();
void setWavMode(uint8_t m);
uint8_t getWavMode();
void setCompMode(uint8_t m);
uint8_t getCompMode();
void setLcycMode(uint8_t m);
uint8_t getLcycMode();
void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1);
void setupDivs(uint8_t Watt_div,uint8_t VAR_div,uint8_t VA_div);
uint32_t getMaskInterrupts();
void setMaskInterrupts(uint32_t m);
uint32_t getStatus();
uint32_t resetStatus();
int32_t getAIRMS();
int32_t getBIRMS();
int32_t getCIRMS();
int32_t getAVRMS();
int32_t getBVRMS();
int32_t getCVRMS();
uint32_t avrms();
uint32_t bvrms();
uint32_t cvrms();
uint32_t airms();
uint32_t birms();
int32_t cirms();
int32_t getFreq();
void setLineCyc(uint32_t d);
int32_t getACurrentOffset();
int32_t getBCurrentOffset();
int32_t getCCurrentOffset();
void setACurrentOffset(int32_t o);
void setBCurrentOffset(int32_t o);
void setCCurrentOffset(int32_t o);
int32_t getAVoltageOffset();
int32_t getBVoltageOffset();
int32_t getCVoltageOffset();
void setAVoltageOffset(int32_t o);
void setBVoltageOffset(int32_t o);
void setCVoltageOffset(int32_t o);
void setAWattOffset(int32_t o);
void setBWattOffset(int32_t o);
void setCWattOffset(int32_t o);
void setZeroCrossingTimeout(int32_t d);
int32_t getZeroCrossingTimeout();
uint8_t setPotLine(uint8_t Phase, uint32_t Ciclos);
int32_t getWatt(uint8_t Phase);
int32_t getVar(uint8_t Phase);
int32_t getVa(uint8_t Phase);
uint8_t getVersion();
uint8_t read8(uint8_t reg);
uint32_t read16(uint8_t reg);
uint32_t read24(uint8_t reg);
esp_err_t write24(uint8_t reg, uint32_t data);
esp_err_t write16(uint8_t reg, uint32_t data);
esp_err_t write8(uint8_t reg, uint8_t data);
void enableADE7758Chip();
void disableADE7758Chip();
void setAPCFDEN(int32_t d);
int32_t getAPCFDEN();
void setAPCFNUM(int32_t d);
int32_t getAPCFNUM();
void setVARCFNUM(int32_t d);
int32_t getVARCFNUM();
void setVARCFDEN(int32_t d);
int32_t getVARCFDEN();
void setAWG(int32_t d);
int32_t getAWG();
void setBWG(int32_t d);
void setCWG(int32_t d);
void setAVARG(int32_t d);
int32_t getAVARG();
void setBVARG(int32_t d);
int32_t getBVARG();
void setCVARG(int32_t d);
int32_t getCVARG();
void setAVAG(int32_t d);
void setBVAG(int32_t d);
void setCVAG(int32_t d);
// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.h ===
// === Início de: components/meter_manager/driver/meter_ade7758/ade7758.c ===
#include <stdio.h>
#include <stdlib.h>
#include "driver/spi_master.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "ade7758.h"
static const char *TAG = "ade7758";
// --- SPI internals ---
static spi_device_handle_t ade7758_spi_handle = NULL;
static spi_host_device_t spi_host = SPI2_HOST; // default
static spi_transaction_t spi_transaction;
// --- Configuração SPI do dispositivo ---
static const uint8_t MODE = 2;
static const uint8_t ADDR_BITS = 7;
static const uint8_t CMD_BITS = 1;
static const uint8_t SPI_WRITE = 1;
static const uint8_t SPI_READ = 0;
static const int BUS_SPEED_HZ = 1000000;
// === Transações básicas ===
static esp_err_t transfer_byte(uint8_t reg_addr, uint8_t data, uint8_t command) {
spi_transaction.flags = SPI_TRANS_USE_RXDATA | SPI_TRANS_USE_TXDATA;
spi_transaction.length = 8;
spi_transaction.cmd = command;
spi_transaction.addr = reg_addr;
spi_transaction.tx_data[0] = data;
return spi_device_transmit(ade7758_spi_handle, &spi_transaction);
}
static esp_err_t transfer_bytes(uint8_t reg_addr, uint8_t *tx_buf, uint8_t *rx_buf, size_t len, uint8_t command) {
if (len < 1) len = 1;
spi_transaction_t t = {
.flags = 0,
.length = 8 * len,
.cmd = command,
.addr = reg_addr,
.tx_buffer = tx_buf,
.rx_buffer = rx_buf
};
return spi_device_transmit(ade7758_spi_handle, &t);
}
// === Interface pública ===
esp_err_t Init(spi_host_device_t host, int pin_miso, int pin_mosi, int pin_sclk) {
// Essa função não inicializa o barramento SPI
// Apenas armazena os parâmetros
spi_host = host;
return ESP_OK;
}
esp_err_t InitSpi(int cs_gpio) {
spi_device_interface_config_t devcfg = {
.command_bits = CMD_BITS,
.address_bits = ADDR_BITS,
.mode = MODE,
.clock_speed_hz = BUS_SPEED_HZ,
.spics_io_num = cs_gpio,
.queue_size = 5,
};
return spi_bus_add_device(spi_host, &devcfg, &ade7758_spi_handle);
}
spi_device_handle_t GetHandle(void) {
return ade7758_spi_handle;
}
// === Registro de acesso ===
uint8_t ReadRegister(uint8_t reg_addr, uint8_t command) {
transfer_byte(reg_addr, 0, command);
return spi_transaction.rx_data[0];
}
esp_err_t WriteRegister(uint8_t reg_addr, uint8_t data, uint8_t command) {
return transfer_byte(reg_addr, data, command);
}
esp_err_t WriteRegisterMultipleBytes(uint8_t reg, uint8_t *data, uint8_t count, uint8_t command) {
return transfer_bytes(reg, data, NULL, count, command);
}
esp_err_t ReadRegisterMultipleBytes(uint8_t reg, uint8_t *buf, uint8_t count, uint8_t command) {
return transfer_bytes(reg, NULL, buf, count, command);
}
// === Leitura e escrita de tamanho fixo ===
esp_err_t write8(uint8_t reg, uint8_t value) {
return WriteRegister(reg, value, SPI_WRITE);
}
esp_err_t write16(uint8_t reg, uint32_t value) {
uint8_t buf[2] = {
(value >> 8) & 0xFF,
(value >> 0) & 0xFF
};
return WriteRegisterMultipleBytes(reg, buf, 2, SPI_WRITE);
}
esp_err_t write24(uint8_t reg, uint32_t value) {
uint8_t buf[3] = {
(value >> 16) & 0xFF,
(value >> 8) & 0xFF,
(value >> 0) & 0xFF
};
return WriteRegisterMultipleBytes(reg, buf, 3, SPI_WRITE);
}
uint8_t read8(uint8_t reg) {
uint8_t buf[1];
ReadRegisterMultipleBytes(reg, buf, 1, SPI_READ);
return buf[0];
}
uint32_t read16(uint8_t reg) {
uint8_t buf[2];
ReadRegisterMultipleBytes(reg, buf, 2, SPI_READ);
return (buf[0] << 8) | buf[1];
}
uint32_t read24(uint8_t reg) {
uint8_t buf[3];
ReadRegisterMultipleBytes(reg, buf, 3, SPI_READ);
return (buf[0] << 16) | (buf[1] << 8) | buf[2];
}
esp_err_t readBlockData(uint8_t reg, uint8_t *buf, int len) {
return ReadRegisterMultipleBytes(reg, buf, len, SPI_READ);
}
/*****************************
*
* public functions
*
*****************************/
/**
* In general:
* @params: void
* @return: register content (measure) of the proper type depending on register width
*/
unsigned char getVersion()
{
return read8(VERSION);
}
/** === setOpMode / getOpMode ===
OPERATIONAL MODE REGISTER (0x13)
The general configuration of the ADE7758 is defined by writing to the OPMODE register.
Table 18 summarizes the functionality of each bit in the OPMODE register.
Bit Location Bit Mnemonic Default Value Description
0 DISHPF 0 The HPFs in all current channel inputs are disabled when this bit is set.
1 DISLPF 0 The LPFs after the watt and VAR multipliers are disabled when this bit is set.
2 DISCF 1 The frequency outputs APCF and VARCF are disabled when this bit is set.
3 to 5 DISMOD 0 By setting these bits, ADE7758<35>s ADCs can be turned off. In normal operation, these bits should be left at Logic 0.
DISMOD[2:0] Description
0 0 0 Normal operation.
1 0 0 Redirect the voltage inputs to the signal paths for the current channels and the current inputs to the signal paths for the voltage channels.
0 0 1 Switch off only the current channel ADCs.
1 0 1 Switch off current channel ADCs and redirect the current input signals to the voltage channel signal paths.
0 1 0 Switch off only the voltage channel ADCs.
1 1 0 Switch off voltage channel ADCs and redirect the voltage input signals to the current channel signal paths.
0 1 1 Put the ADE7758 in sleep mode.
1 1 1 Put the ADE7758 in power-down mode (reduces AIDD to 1 mA typ).
6 SWRST 0 Software Chip Reset. A data transfer to the ADE7758 should not take place for at least 18 <20>s after a software reset.
7 RESERVED 0 This should be left at 0.
*/
void setOpMode(uint8_t m)
{
write8(OPMODE, m);
}
uint8_t getOpMode()
{
return read8(OPMODE);
}
/** === setMMode / getMMode ===
MEASUREMENT MODE REGISTER (0x14)
The configuration of the PERIOD and peak measurements made by the ADE7758 is defined by writing to the MMODE register.
Table 19 summarizes the functionality of each bit in the MMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 FREQSEL 0 These bits are used to select the source of the measurement of the voltage line frequency.
FREQSEL1 FREQSEL0 Source
0 0 Phase A
0 1 Phase B
1 0 Phase C
1 1 Reserved
2 to 4 PEAKSEL 7 These bits select the phases used for the voltage and current peak registers.
Setting Bit 2 switches the IPEAK and VPEAK registers to hold the absolute values
of the largest current and voltage waveform (over a fixed number of half-line cycles)
from Phase A. The number of half-line cycles is determined by the content of the
LINECYC register. At the end of the LINECYC number of half-line cycles, the content
of the registers is replaced with the new peak values. Similarly, setting Bit 3 turns
on the peak detection for Phase B, and Bit 4 for Phase C. Note that if more than one
bit is set, the VPEAK and IPEAK registers can hold values from two different phases, that is,
the voltage and current peak are independently processed (see the Peak Current Detection section).
5 to 7 PKIRQSEL 7 These bits select the phases used for the peak interrupt detection.
Setting Bit 5 switches on the monitoring of the absolute current and voltage waveform to Phase A.
Similarly, setting Bit 6 turns on the waveform detection for Phase B, and Bit 7 for Phase C.
Note that more than one bit can be set for detection on multiple phases.
If the absolute values of the voltage or current waveform samples in the selected phases exceeds
the preset level specified in the VPINTLVL or IPINTLVL registers the corresponding bit(s) in the
STATUS registers are set (see the Peak Current Detection section).
*/
void setMMode(uint8_t m)
{
write8(MMODE, m);
}
uint8_t getMMode(void)
{
return read8(MMODE);
}
/** === setWavMode / getWavMode ===
WAVEFORM MODE REGISTER (0x15)
The waveform sampling mode of the ADE7758 is defined by writing to the WAVMODE register.
Table 20 summarizes the functionality of each bit in the WAVMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 PHSEL 0 These bits are used to select the phase of the waveform sample.
PHSEL[1:0] Source
0 0 Phase A
0 1 Phase B
1 0 Phase C
1 1 Reserved
2 to 4 WAVSEL 0 These bits are used to select the type of waveform.
WAVSEL[2:0] Source
0 0 0 Current
0 0 1 Voltage
0 1 0 Active Power Multiplier Output
0 1 1 Reactive Power Multiplier Output
1 0 0 VA Multiplier Output
-Others- Reserved
5 to 6 DTRT 0 These bits are used to select the data rate.
DTRT[1:0] Update Rate
0 0 26.04 kSPS (CLKIN/3/128)
0 1 13.02 kSPS (CLKIN/3/256)
1 0 6.51 kSPS (CLKIN/3/512)
1 1 3.25 kSPS (CLKIN/3/1024)
7 VACF 0 Setting this bit to Logic 1 switches the VARCF output pin to an output
frequency that is proportional to the total apparent power (VA).
In the default state, Logic 0, the VARCF pin outputs a frequency proportional
to the total reactive power (VAR).
*/
void setWavMode(uint8_t m)
{
write8(WAVMODE, m);
}
uint8_t getWavMode()
{
return read8(WAVMODE);
}
/** === setCompMode / getCompMode ===
COMPUTATIONAL MODE REGISTER (0x16)
The computational method of the ADE7758 is defined by writing to the COMPMODE register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 CONSEL 0 These bits are used to select the input to the energy accumulation registers.
CONSEL[1:0] = 11 is reserved. IA, IB, and IC are IA, IB, and IC phase shifted by <20>90<39>, respectively.
Registers CONSEL[1, 0] = 00 CONSEL[1, 0] = 01 CONSEL[1, 0] = 10
AWATTHR VA <20> IA VA <20> (IA <20> IB) VA <20> (IA<49>IB)
BWATTHR VB <20> IB 0 0
CWATTHR VC <20> IC VC <20> (IC <20> IB) VC <20> IC
AVARHR VA <20> IA VA <20> (IA <20> IB) VA <20> (IA<49>IB)
BVARHR VB <20> IB 0 0
CVARHR VC <20> IC VC <20> (IC <20> IB) VC <20> IC
AVAHR VARMS <20> IARMS VARMS <20> IARMS VARMS <20> ARMS
BVAHR VBRMS <20> IBRMS (VARMS + VCRMS)/2 <20> IBRMS VARMS <20> IBRMS
CVAHR VCRMS <20> ICRMS VCRMS <20> ICRMS VCRMS <20> ICRMS
2 to 4 TERMSEL 7 These bits are used to select the phases to be included in the APCF and VARCF pulse outputs.
Setting Bit 2 selects Phase A (the inputs to AWATTHR and AVARHR registers) to be included.
Bit 3 and Bit 4 are for Phase B and Phase C, respectively.
Setting all three bits enables the sum of all three phases to be included in the frequency outputs
(see the Active Power Frequency Output and the Reactive Power Frequency Output sections).
5 ABS 0 Setting this bit places the APCF output pin in absolute only mode.
Namely, the APCF output frequency is proportional to the sum of the absolute values of the watt-hour
accumulation registers (AWATTHR, BWATTHR, and CWATTHR).
Note that this bit only affects the APCF pin and has no effect on the content of the corresponding
registers.
6 SAVAR 0 Setting this bit places the VARCF output pin in the signed adjusted mode.
Namely, the VARCF output frequency is proportional to the sign-adjusted sum of the VAR-hour accumulation
registers (AVARHR, BVARHR, and CVARHR).
The sign of the VAR is determined from the sign of the watt calculation from the corresponding phase,
that is, the sign of the VAR is flipped if the sign of the watt is negative, and if the watt is positive,
there is no change to the sign of the VAR.
Note that this bit only affects the VARCF pin and has no effect on the content of the corresponding
registers.
7 NOLOAD 0 Setting this bit activates the no-load threshold in the ADE7758.
*/
void setCompMode(uint8_t m)
{
write8(COMPMODE, m);
}
uint8_t getCompMode(void)
{
return read8(COMPMODE);
}
/** === setLcycMode / getLcycMode ===
LINE CYCLE ACCUMULATION MODE REGISTER (0x17)
The functionalities involved the line-cycle accumulation mode in the ADE7758 are defined by writing to the LCYCMODE register.
Bit Location Bit Mnemonic Default Value Description
0 LWATT 0 Setting this bit places the watt-hour accumulation registers
(AWATTHR, BWATTHR, and CWATTHR registers) into line-cycle accumulation mode.
1 LVAR 0 Setting this bit places the VAR-hour accumulation registers (AVARHR, BVARHR, and CVARHR registers)
into line-cycle accumulation mode.
2 LVA 0 Setting this bit places the VA-hour accumulation registers (AVAHR, BVAHR, and CVAHR registers)
into line-cycle accumulation mode.
3 to 5 ZXSEL 7 These bits select the phases used for counting the number of zero crossings in the line-cycle
accumulation mode. Bit 3, Bit 4, and Bit 5 select Phase A, Phase B, and Phase C, respectively.
More than one phase can be selected for the zero-crossing detection,
and the accumulation time is shortened accordingly.
6 RSTREAD 1 Setting this bit enables the read-with-reset for all the WATTHR, VARHR, and VAHR registers for all three
phases, that is, a read to those registers resets the registers to 0 after the content of the registers
have been read. This bit should be set to Logic 0 when the LWATT, LVAR, or LVA bits are set to Logic 1.
7 FREQSEL 0 Setting this bit causes the FREQ (0x10) register to display the period, instead of the frequency of the
line input.
*/
void setLcycMode(uint8_t m)
{
write8(LCYCMODE, m);
}
uint8_t getLcycMode(void)
{
return read8(LCYCMODE);
}
/** === gainSetup ===
GAIN REGISTER (0x23)
The PGA configuration of the ADE7758 is defined by writing to the GAIN register.
Table 18 summarizes the functionality of each bit in the GAIN register.
Bit Location Bit Mnemonic Default Value Description
0 to 1 PGA1 0 Current GAIN
PGA1[1:0] Description
0 0 x1
0 1 x2
1 0 x4
1 1 RESERVED
2 ---- RESERVED Reserved.
3 to 4 SCALE 0 Current input full-scale select
SCALE[1:0] Description
0 0 0.5v
0 1 0.25v
1 0 0.125v
1 1 Reserved
5 to 6 PGA2 0 Voltage GAIN
PGA2[1:0] Description
0 0 x1
0 1 x2
1 0 x4
1 1 RESERVED
7 INTEGRATOR 0 This bit enables the integrator on the current chanel when set.
*/
void gainSetup(uint8_t integrator, uint8_t scale, uint8_t PGA2, uint8_t PGA1)
{
char pgas = (integrator << 7) | (PGA2 << 5) | (scale << 3) | (PGA1);
write8(GAIN, pgas); // write GAIN register, format is |3 bits PGA2 gain|2 bits full scale|3 bits PGA1 gain
}
void setupDivs(uint8_t Watt_div, uint8_t VAR_div, uint8_t VA_div)
{
write8(WDIV, Watt_div);
write8(VARDIV, VAR_div);
write8(VADIV, VA_div);
}
/** === getMaskInterrupts / setMaskInterrupts
MASK REGISTER (0x18)
When an interrupt event occurs in the ADE7758, the IRQ logic output goes active low if the mask bit for this event is Logic 1 in the MASK register.
The IRQ logic output is reset to its default collector open state when the RSTATUS register is read.
describes the function of each bit in the interrupt mask register.
Bit Location Interrupt Flag Default Value Description
0 AEHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three WATTHR registers,
that is, the WATTHR register is half full.
1 REHF 0 Enables an interrupt when there is a change in Bit 14 of any one of the three VARHR registers,
that is, the VARHR register is half full.
2 VAEHF 0 Enables an interrupt when there is a 0 to 1 transition in the MSB of any one of the three VAHR
registers, that is, the VAHR register is half full.
3 SAGA 0 Enables an interrupt when there is a SAG on the line voltage of the Phase A.
4 SAGB 0 Enables an interrupt when there is a SAG on the line voltage of the Phase B.
5 SAGC 0 Enables an interrupt when there is a SAG on the line voltage of the Phase C.
6 ZXTOA 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase A.
7 ZXTOB 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase B.
8 ZXTOC 0 Enables an interrupt when there is a zero-crossing timeout detection on Phase C.
9 ZXA 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase A
(see the Zero-Crossing Detection section).
10 ZXB 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase B
(see the Zero-Crossing Detection section).
11 ZXC 0 Enables an interrupt when there is a zero crossing in the voltage channel of Phase C
(see the Zero-Crossing Detection section).
12 LENERGY 0 Enables an interrupt when the energy accumulations over LINECYC are finished.
13 RESERVED 0 Reserved.
14 PKV 0 Enables an interrupt when the voltage input selected in the MMODE register is above
the value in the VPINTLVL register.
15 PKI 0 Enables an interrupt when the current input selected in the MMODE register is above the
value in the IPINTLVL register.
16 WFSM 0 Enables an interrupt when data is present in the WAVEMODE register.
17 REVPAP 0 Enables an interrupt when there is a sign change in the watt calculation among any one of
the phases specified by the TERMSEL bits in the COMPMODE register.
18 REVPRP 0 Enables an interrupt when there is a sign change in the VAR calculation among any one of
the phases specified by the TERMSEL bits in the COMPMODE register.
19 SEQERR 0 Enables an interrupt when the zero crossing from Phase A is followed not by the zero crossing
of Phase C but with that of Phase B.
*/
uint32_t getMaskInterrupts(void)
{
return read24(MASK);
}
void setMaskInterrupts(uint32_t m)
{
write24(MASK, m);
}
/** getStatus / resetStatus
INTERRUPT STATUS REGISTER (0x19)/RESET INTERRUPT STATUS REGISTER (0x1A)
The interrupt status register is used to determine the source of an interrupt event.
When an interrupt event occurs in the ADE7758, the corresponding flag in the interrupt status register is set.
The IRQ pin goes active low if the corresponding bit in the interrupt mask register is set.
When the MCU services the interrupt, it must first carry out a read from the interrupt status register to determine the source of the interrupt.
All the interrupts in the interrupt status register stay at their logic high state after an event occurs.
The state of the interrupt bit in the interrupt status register is reset to its default value once the reset interrupt status register is read.
Bit Location Interrupt Flag Default Value Event Description
0 AEHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three
WATTHR registers, that is, the WATTHR register is half full.
1 REHF 0 Indicates that an interrupt was caused by a change in Bit 14 among any one of the three
VARHR registers, that is, the VARHR register is half full.
2 VAEHF 0 Indicates that an interrupt was caused by a 0 to 1 transition in Bit 15 among any one of the three
VAHR registers, that is, the VAHR register is half full.
3 SAGA 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase A.
4 SAGB 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase B.
5 SAGC 0 Indicates that an interrupt was caused by a SAG on the line voltage of the Phase C.
6 ZXTOA 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase A.
7 ZXTOB 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase B.
8 ZXTOC 0 Indicates that an interrupt was caused by a missing zero crossing on the line voltage of the Phase C.
9 ZXA 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase A.
10 ZXB 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase B.
11 ZXC 0 Indicates a detection of a rising edge zero crossing in the voltage channel of Phase C.
12 LENERGY 0 In line energy accumulation, indicates the end of an integration over an integer number of
half- line cycles (LINECYC). See the Calibration section.
13 RESET 1 Indicates that the 5 V power supply is below 4 V. Enables a software reset of the ADE7758
and sets the registers back to their default values. This bit in the STATUS or RSTATUS register
is logic high for only one clock cycle after a reset event.
14 PKV 0 Indicates that an interrupt was caused when the selected voltage input is above the value in the
VPINTLVL register.
15 PKI 0 Indicates that an interrupt was caused when the selected current input is above the value
in the IPINTLVL register.
16 WFSM 0 Indicates that new data is present in the waveform register.
17 REVPAP 0 Indicates that an interrupt was caused by a sign change in the watt calculation among any
one of the phases specified by the TERMSEL bits in the COMPMODE register.
18 REVPRP 0 Indicates that an interrupt was caused by a sign change in the VAR calculation among any
one of the phases specified by the TERMSEL bits in the COMPMODE register.
19 SEQERR 0 Indicates that an interrupt was caused by a zero crossing from Phase A
followed not by the zero crossing of Phase C but by that of Phase B.
*/
uint32_t getStatus(void)
{
return read24(STATUS);
}
uint32_t resetStatus(void)
{
return read24(RSTATUS);
}
/** === getAIRMS ===
* Phase A Current RMS Value
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getAIRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXA)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(AIRMS);
}
/** === getBIRMS ===
* Phase B Current RMS Value
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getBIRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXB)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(BIRMS);
}
/** === getCIRMS ===
* Phase C Current RMS Value
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getCIRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXC)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(CIRMS);
}
/** === getAVRMS ===
* Phase A RMS Value (Voltage Channel).
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getAVRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXA)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(AVRMS);
}
/** === getBVRMS ===
* Phase B RMS Value (Voltage Channel).
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getBVRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXB)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(BVRMS);
}
/** === getCVRMS ===
* Phase C RMS Value (Voltage Channel).
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with the data (24 bits unsigned).
*/
int32_t getCVRMS(void)
{
long lastupdate = 0;
resetStatus(); // Clear all interrupts
while (!(getStatus() & ZXC)) // wait Zero-Crossing
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // break;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return read24(CVRMS);
}
/** === vrms ===
* Returns the mean of last 20 readings of RMS voltage. Also supress first reading to avoid
* corrupted data.
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with RMS voltage value
*/
uint32_t avrms(void)
{
uint8_t i = 0;
uint32_t v = 0;
if (getAVRMS())
{ // Ignore first reading to avoid garbage
for (i = 0; i < 10; ++i)
{
v += getAVRMS();
}
return v / 10;
}
else
{
return 0;
}
}
uint32_t bvrms()
{
uint8_t i = 0;
uint32_t v = 0;
if (getBVRMS())
{ // Ignore first reading to avoid garbage
for (i = 0; i < 10; ++i)
{
v += getBVRMS();
}
return v / 10;
}
else
{
return 0;
}
}
uint32_t cvrms()
{
uint8_t i = 0;
uint32_t v = 0;
if (getCVRMS())
{ // Ignore first reading to avoid garbage
for (i = 0; i < 10; ++i)
{
v += getCVRMS();
}
return v / 10;
}
else
{
return 0;
}
}
/** === irms ===
* Returns the mean of last 20 readings of RMS current. Also supress first reading to avoid
* corrupted data.
* To minimize noise, synchronize the reading of the rms register with the zero crossing
* of the voltage input and take the average of a number of readings.
* @param none
* @return long with RMS current value in hundreds of [mA], ie. 6709=67[mA]
*/
uint32_t airms()
{
char n = 0;
long i = 0;
if (getAIRMS())
{ // Ignore first reading to avoid garbage
for (n = 0; n < 10; ++n)
{
i += getAIRMS();
}
return i / 10;
}
else
{
return 0;
}
}
uint32_t birms()
{
char n = 0;
long i = 0;
if (getBIRMS())
{ // Ignore first reading to avoid garbage
for (n = 0; n < 10; ++n)
{
i += getBIRMS();
}
return i / 10;
}
else
{
return 0;
}
}
int32_t cirms()
{
char n = 0;
int32_t i = 0;
if (getCIRMS())
{ // Ignore first reading to avoid garbage
for (n = 0; n < 10; ++n)
{
i += getCIRMS();
}
return i / 10;
}
else
{
return 0;
}
}
/** === getFreq ===
* Period of the Phase selected in MMode.
* @param none
* @return int with the data (12 bits unsigned).
*/
int32_t getFreq(void)
{
return read16(FREQ);
}
/** setLineCyc: sets the number of cycles required for the accumulations.
**/
void setLineCyc(uint32_t d)
{
write16(LINECYC, d);
}
/** === setCurrentOffset / getCurrentOffset ===
* @param none
* @return int with the data (12 bits 2-complement signed).
*/
int32_t getACurrentOffset()
{
int32_t data;
data = ((int32_t)(read16(AIRMSOS) << 20) >> 20);
return data;
}
int32_t getBCurrentOffset()
{
int32_t data;
data = ((int32_t)(read16(BIRMSOS) << 20) >> 20);
return data;
}
int32_t getCCurrentOffset()
{
int32_t data;
data = ((int32_t)(read16(CIRMSOS) << 20) >> 20);
return data;
}
void setACurrentOffset(int32_t o)
{
write16(AIRMSOS, o);
}
void setBCurrentOffset(int32_t o)
{
write16(BIRMSOS, o);
}
void setCCurrentOffset(int32_t o)
{
write16(CIRMSOS, o);
}
void setAWattOffset(int32_t o)
{
write16(AWATTOS, o);
}
void setBWattOffset(int32_t o)
{
write16(BWATTOS, o);
}
void setCWattOffset(int32_t o)
{
write16(CWATTOS, o);
}
void setAVAROffset(int32_t o)
{
write16(AVAROS, o);
}
void setBVAROffset(int32_t o)
{
write16(BVAROS, o);
}
void setCVAROffset(int32_t o)
{
write16(CVAROS, o);
}
/** === setVoltageOffset / getVoltageOffset ===
* @param none
* @return int with the data (12 bits 2-complement signed).
*/
int32_t getAVoltageOffset()
{
int32_t data;
data = ((int32_t)(read16(AVRMSOS) << 20) >> 20);
return data;
}
int32_t getBVoltageOffset()
{
int32_t data;
data = ((int32_t)(read16(BVRMSOS) << 20) >> 20);
return data;
}
int32_t getCVoltageOffset()
{
int32_t data;
data = ((int32_t)(read16(CVRMSOS) << 20) >> 20);
return data;
}
void setAVoltageOffset(int32_t o)
{
write16(AVRMSOS, o);
}
void setBVoltageOffset(int32_t o)
{
write16(BVRMSOS, o);
}
void setCVoltageOffset(int32_t o)
{
write16(CVRMSOS, o);
}
/** === setZeroCrossingTimeout / getZeroCrossingTimeout ===
* Zero-Crossing Timeout. If no zero crossings are detected
* on Channel 2 within a time period specified by this 12-bit register,
* the interrupt request line (IRQ) is activated
* @param none
* @return int with the data (12 bits unsigned).
*/
void setZeroCrossingTimeout(int32_t d)
{
write16(ZXTOUT, d);
}
int32_t getZeroCrossingTimeout()
{
return read16(ZXTOUT);
}
/** === setPotLine(Phase) ===
Set the conditions for Line accumulation in the selected phase.
Then wait for the interruption and return the phase number when it occurs.
If it does not happen for more than 1.5 seconds, it returns a 0.
**/
uint8_t setPotLine(uint8_t Phase, uint32_t Cycles)
{
long lastupdate = 0;
char m = 0;
switch (Phase)
{
case PHASE_A:
m = (LWATT | LVAR | LVA | ZXSEL_A);
break;
case PHASE_B:
m = (LWATT | LVAR | LVA | ZXSEL_B);
break;
case PHASE_C:
m = (LWATT | LVAR | LVA | ZXSEL_C);
break;
}
setLcycMode(0);
setLcycMode(m);
resetStatus();
setLineCyc(Cycles);
lastupdate = 0;
while (!(getStatus() & LENERGY)) // wait to terminar de acumular
{ // wait for the selected interrupt to occur
lastupdate++;
if (lastupdate > 20)
{
return 0; // breack;
}
vTaskDelay(pdMS_TO_TICKS(20));
}
return Phase;
}
/** === getWatt(phase) / getVar(phase) / getVa(phase) ===
Devuelve los valores de la potencia requerida de la fase seleccionada.
Utilizar antes setPotLine(phase) para generar los valores.
**/
int32_t getWatt(uint8_t Phase)
{
int32_t temp = 0;
switch (Phase)
{
case PHASE_A:
temp = read16(AWATTHR);
break;
case PHASE_B:
temp = read16(BWATTHR);
break;
case PHASE_C:
temp = read16(CWATTHR);
break;
}
return temp;
}
int32_t getVar(uint8_t Phase)
{
int32_t temp = 0;
switch (Phase)
{
case PHASE_A:
temp = read16(AVARHR);
break;
case PHASE_B:
temp = read16(BVARHR);
break;
case PHASE_C:
temp = read16(CVARHR);
break;
}
return temp;
}
int32_t getVa(uint8_t Phase)
{
int32_t temp = 0;
switch (Phase)
{
case PHASE_A:
temp = read16(AVAHR);
break;
case PHASE_B:
temp = read16(BVAHR);
break;
case PHASE_C:
temp = read16(CVAHR);
break;
}
return temp;
}
void setAPCFDEN(int32_t d)
{
write16(APCFDEN, d);
}
int32_t getAPCFDEN(void)
{
int32_t data;
data = ((int32_t)(read16(APCFDEN) << 20) >> 20);
return data;
}
void setAPCFNUM(int32_t d)
{
write16(APCFNUM, d);
}
int32_t getAPCFNUM(void)
{
int32_t data;
data = ((int32_t)(read16(APCFNUM) << 20) >> 20);
return data;
}
void setVARCFNUM(int32_t d)
{
write16(VARCFNUM, d);
}
int32_t getVARCFNUM(void)
{
int32_t data;
data = ((int32_t)(read16(VARCFNUM) << 20) >> 20);
return data;
}
void setVARCFDEN(int32_t d)
{
write16(VARCFDEN, d);
}
int32_t getVARCFDEN(void)
{
int32_t data;
data = ((int32_t)(read16(VARCFDEN) << 20) >> 20);
return data;
}
void setAWG(int32_t d)
{
write16(AWG, d);
}
int32_t getAWG()
{
return ((read16(AWG)) << 4) >> 4;
}
void setBWG(int32_t d)
{
write16(BWG, d);
}
void setCWG(int32_t d)
{
write16(CWG, d);
}
void setAVARG(int32_t d)
{
write16(AVARG, d);
}
int32_t getAVARG()
{
return ((read16(AVARG)) << 4) >> 4;
}
void setBVARG(int32_t d)
{
write16(BVARG, d);
}
int32_t getBVARG()
{
return ((read16(BVARG)) << 4) >> 4;
}
void setCVARG(int32_t d)
{
write16(CVARG, d);
}
int32_t getCVARG()
{
return ((read16(CVARG)) << 4) >> 4;
}
void setAVAG(int32_t d)
{
write16(AVAG, d);
}
void setBVAG(int32_t d)
{
write16(BVAG, d);
}
void setCVAG(int32_t d)
{
write16(CVAG, d);
}
// === Fim de: components/meter_manager/driver/meter_ade7758/ade7758.c ===
// === Início de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c ===
#include "meter_ade7758.h"
#include "spi_bus_manager.h"
#include "ade7758.h"
#include "meter_events.h"
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "driver/gpio.h"
#define TAG "meter_ade7758"
// === Pinos ===
#define PIN_NUM_CS 15
#define PIN_ADUM_EN 4
// === Constantes de calibração ===
#define VRMS_CAL 4732.78f
#define IRMS_CAL 53416.0f
#define METER_READ_INTERVAL_MS 5000
// === Estrutura interna ===
typedef struct {
float vrms[3];
float irms[3];
int watt[3];
int var[3]; // reservado
int va[3]; // reservado
} meter_ade7758_internal_data_t;
static meter_ade7758_internal_data_t meter_data;
static TaskHandle_t meter_task = NULL;
static SemaphoreHandle_t meter_mutex = NULL;
static uint32_t meter_watchdog_counter = 0;
// === Controle ADUM1401 ===
static void adum1401_enable(bool enable) {
gpio_config_t io_conf = {
.pin_bit_mask = BIT64(PIN_ADUM_EN),
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = GPIO_PULLUP_DISABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = GPIO_INTR_DISABLE
};
gpio_config(&io_conf);
gpio_set_level(PIN_ADUM_EN, enable ? 1 : 0);
ESP_LOGI(TAG, "ADUM1401 %s", enable ? "ativado" : "desativado");
}
// === Post de evento ===
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
};
memcpy(evt.vrms, data->vrms, sizeof(evt.vrms));
memcpy(evt.irms, data->irms, sizeof(evt.irms));
memcpy(evt.watt, data->watt, 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));
}
}
// === Task de leitura ===
static void meter_ade7758_task_func(void *param) {
ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada");
meter_ade7758_internal_data_t previous = {0};
while (true) {
meter_ade7758_internal_data_t meterData = {0};
ESP_LOGI(TAG, "Tarefa de medição ADE7758 iniciada %d",getVersion());
meterData.vrms[0] = avrms() / VRMS_CAL;
meterData.vrms[1] = bvrms() / VRMS_CAL;
meterData.vrms[2] = cvrms() / VRMS_CAL;
meterData.irms[0] = airms() / IRMS_CAL;
meterData.irms[1] = birms() / IRMS_CAL;
meterData.irms[2] = cirms() / IRMS_CAL;
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, &meterData, sizeof(meterData)) != 0) {
if (xSemaphoreTake(meter_mutex, pdMS_TO_TICKS(10)) == pdTRUE) {
meter_data = meterData;
meter_watchdog_counter++;
xSemaphoreGive(meter_mutex);
meter_ade7758_post_event(&meterData);
}
previous = meterData;
}
vTaskDelay(pdMS_TO_TICKS(METER_READ_INTERVAL_MS));
}
}
// === Inicialização ===
esp_err_t meter_ade7758_init(void) {
ESP_LOGI(TAG, "Inicializando ADE7758...");
if (!meter_mutex) {
meter_mutex = xSemaphoreCreateMutex();
if (!meter_mutex) {
ESP_LOGE(TAG, "Falha ao criar mutex");
return ESP_ERR_NO_MEM;
}
}
if (!spi_bus_manager_is_initialized()) {
esp_err_t err = spi_bus_manager_init(); // usa pinos padrão
if (err != ESP_OK) {
ESP_LOGE(TAG, "Erro ao inicializar SPI: %s", esp_err_to_name(err));
return err;
}
}
adum1401_enable(true); // Ativa o ADUM1401 antes de usar SPI
vTaskDelay(pdMS_TO_TICKS(10)); // Delay de estabilização
esp_err_t err = Init(spi_bus_manager_get_host(), -1, -1, -1);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Erro Init SPI ADE7758: %s", esp_err_to_name(err));
return err;
}
err = InitSpi(PIN_NUM_CS);
if (err != ESP_OK) {
ESP_LOGE(TAG, "Erro ao registrar dispositivo SPI: %s", esp_err_to_name(err));
return err;
}
gainSetup(INTEGRATOR_OFF, FULLSCALESELECT_0_5V, GAIN_1, GAIN_1);
setupDivs(1, 1, 1);
setLcycMode(0x00);
resetStatus();
ESP_LOGI(TAG, "ADE7758 inicializado com sucesso.");
return ESP_OK;
}
// === Execução ===
esp_err_t meter_ade7758_start(void) {
if (meter_task) return ESP_ERR_INVALID_STATE;
BaseType_t result = xTaskCreate(meter_ade7758_task_func, "meter_ade7758_task", 4096, NULL, 3, &meter_task);
return result == pdPASS ? ESP_OK : ESP_FAIL;
}
void meter_ade7758_stop(void) {
if (meter_task) {
vTaskDelete(meter_task);
meter_task = NULL;
}
}
// === Fim de: components/meter_manager/driver/meter_ade7758/meter_ade7758.c ===
// === Início de: components/meter_manager/driver/meter_orno/modbus_params.h ===
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef _DEVICE_PARAMS
#define _DEVICE_PARAMS
#include <stdint.h>
#pragma pack(push, 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;
// Coils
typedef struct {
uint8_t coils_port0;
uint8_t coils_port1;
uint8_t coils_port2;
} coil_reg_params_t;
// 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;
// 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 // !_DEVICE_PARAMS
// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.h ===
// === Início de: components/meter_manager/driver/meter_orno/meter_orno.h ===
#ifndef ORNO_MODBUS_H_
#define ORNO_MODBUS_H_
#include <stdbool.h>
#include "esp_err.h"
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Inicializa o driver do medidor (SPI, mutex, registradores ADE7758).
*/
esp_err_t meter_init(void);
/**
* @brief Inicia a tarefa de leitura de dados do medidor.
*/
esp_err_t meter_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos.
*/
void meter_stop(void);
/**
* @brief Verifica se o medidor está em execução.
*
* @return true se a tarefa estiver ativa, false caso contrário.
*/
bool meter_is_running(void);
/**
* @brief Limpa os dados armazenados no medidor (zera todos os valores).
*/
void meter_clear_data(void);
// ----- Leituras por fase (L1, L2, L3) -----
// Tensão RMS (em volts)
float meter_get_vrms_l1(void);
float meter_get_vrms_l2(void);
float meter_get_vrms_l3(void);
// Corrente RMS (em amperes)
float meter_get_irms_l1(void);
float meter_get_irms_l2(void);
float meter_get_irms_l3(void);
// Potência ativa (W)
int meter_get_watt_l1(void);
int meter_get_watt_l2(void);
int meter_get_watt_l3(void);
// Potência reativa (VAR)
int meter_get_var_l1(void);
int meter_get_var_l2(void);
int meter_get_var_l3(void);
// Potência aparente (VA)
int meter_get_va_l1(void);
int meter_get_va_l2(void);
int meter_get_va_l3(void);
// (Opcional) contador de watchdog para diagnóstico
uint32_t meter_get_watchdog_counter(void);
#ifdef __cplusplus
}
#endif
#endif /* ORNO_MODBUS_H_ */
// === Fim de: components/meter_manager/driver/meter_orno/meter_orno.h ===
// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.h ===
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Inicializa o driver do medidor ORNO 513 (SPI, mutex, registradores).
*/
esp_err_t meter_orno513_init(void);
/**
* @brief Inicia a tarefa de leitura de dados do medidor ORNO 513.
*/
esp_err_t meter_orno513_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 513.
*/
void meter_orno513_stop(void);
#ifdef __cplusplus
}
#endif
// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.h ===
// === Início de: components/meter_manager/driver/meter_orno/meter_orno516.h ===
#ifndef METER_ORNO516_H_
#define METER_ORNO516_H_
#include <stdint.h>
#include <stdbool.h>
#include "esp_err.h"
/**
* @brief Inicializa o driver do medidor ORNO 516 (SPI, mutex, registradores).
*
* @return esp_err_t Retorna ESP_OK se a inicialização for bem-sucedida, caso contrário retorna um erro.
*/
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_orno516_start(void);
/**
* @brief Para a tarefa de leitura e limpa os dados internos do medidor ORNO 516.
*/
void meter_orno516_stop(void);
#ifdef __cplusplus
}
#endif
#endif /* METER_ORNO516_H_ */
// === Fim de: components/meter_manager/driver/meter_orno/meter_orno516.h ===
// === Início de: components/meter_manager/driver/meter_orno/modbus_params.c ===
/*
* SPDX-FileCopyrightText: 2016-2021 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
/*=====================================================================================
* Description:
* C file to define parameter storage instances
*====================================================================================*/
#include "modbus_params.h"
// Here are the user defined instances for device parameters packed by 1 byte
// These are keep the values that can be accessed from Modbus master
holding_reg_params_t holding_reg_params = { 0 };
input_reg_params_t input_reg_params = { 0 };
coil_reg_params_t coil_reg_params = { 0 };
discrete_reg_params_t discrete_reg_params = { 0 };
// === Fim de: components/meter_manager/driver/meter_orno/modbus_params.c ===
// === Início de: components/meter_manager/driver/meter_orno/meter_orno513.c ===
#include "meter_orno513.h"
#include "modbus_params.h"
#include "mbcontroller.h"
#include "meter_events.h"
#include "esp_log.h"
#include "driver/uart.h"
#include <stddef.h>
#define TAG "serial_mdb_orno513"
#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)
#define HOLD_OFFSET(field) ((uint16_t)(offsetof(holding_reg_params_t, field) + 1))
#define STR(x) ((const char *)(x))
#define OPTS(min, max, step) {.opt1 = min, .opt2 = max, .opt3 = step}
// State flag
static bool is_initialized = false;
static TaskHandle_t meter_task = NULL;
// 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
};
// 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}
};
const uint16_t num_device_parameters_orno513 = sizeof(device_parameters_orno513) / sizeof(device_parameters_orno513[0]);
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);
}
static void serial_mdb_task(void *param) {
esp_err_t err;
const mb_parameter_descriptor_t *desc = NULL;
float voltage[3] = {0};
float current[3] = {0};
int watt[3] = {0};
float energy = 0.0f;
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;
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) {
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);
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 {
ESP_LOGE(TAG, "CID %u (%s) read failed: %s", cid, desc->param_key, esp_err_to_name(err));
}
vTaskDelay(POLL_INTERVAL);
}
meter_event_data_t evt = {
.frequency = 0.0f,
.power_factor = 0.0f,
.total_energy = energy,
.source = "GRID"
};
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);
}
}
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 *handler = 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;
}
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;
}
// === Fim de: components/meter_manager/driver/meter_orno/meter_orno513.c ===