inital upload

This commit is contained in:
DitroniXDev
2025-12-17 14:23:02 +00:00
committed by GitHub
parent ac4dad4eb1
commit 796d7e31e9
8 changed files with 1451 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
import esphome.codegen as cg
CODEOWNERS = ["@DitroniXDev"]
atm90e36_ns = cg.esphome_ns.namespace("atm90e36")
ATM90E36Component = atm90e36_ns.class_("ATM90E36Component", cg.Component)
CONF_ATM90E36_ID = "atm90e36_id"
+516
View File
@@ -0,0 +1,516 @@
#include "atm90e36.h"
#include <cinttypes>
#include <cmath>
#include <numbers>
#include "esphome/core/log.h"
namespace esphome {
namespace atm90e36 {
static const char *const TAG = "atm90e36";
//Loop called by normal ESP32 Loop()
void ATM90E36Component::loop() {
if (this->get_publish_interval_flag_()) {
this->set_publish_interval_flag_(false);
for (uint8_t phase = 0; phase < 3; phase++) {
if (this->phase_[phase].voltage_sensor_ != nullptr)
this->phase_[phase].voltage_ = this->get_phase_voltage_(phase);
if (this->phase_[phase].current_sensor_ != nullptr)
this->phase_[phase].current_ = this->get_phase_current_(phase);
if (this->phase_[phase].power_sensor_ != nullptr)
this->phase_[phase].active_power_ = this->get_phase_active_power_(phase);
if (this->phase_[phase].power_factor_sensor_ != nullptr)
this->phase_[phase].power_factor_ = this->get_phase_power_factor_(phase);
if (this->phase_[phase].reactive_power_sensor_ != nullptr)
this->phase_[phase].reactive_power_ = this->get_phase_reactive_power_(phase);
if (this->phase_[phase].apparent_power_sensor_ != nullptr)
this->phase_[phase].apparent_power_ = this->get_phase_apparent_power_(phase);
if (this->phase_[phase].forward_active_energy_sensor_ != nullptr)
this->phase_[phase].forward_active_energy_ = this->get_phase_forward_active_energy_(phase);
if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr)
this->phase_[phase].reverse_active_energy_ = this->get_phase_reverse_active_energy_(phase);
if (this->phase_[phase].phase_angle_sensor_ != nullptr)
this->phase_[phase].phase_angle_ = this->get_phase_angle_(phase);
if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr)
this->phase_[phase].harmonic_active_power_ = this->get_phase_harmonic_active_power_(phase);
if (this->phase_[phase].peak_current_sensor_ != nullptr)
this->phase_[phase].peak_current_ = this->get_phase_peak_current_(phase);
// After the local store is collected we can publish them trusting they are within +-1 hardware sampling
if (this->phase_[phase].voltage_sensor_ != nullptr)
this->phase_[phase].voltage_sensor_->publish_state(this->get_local_phase_voltage_(phase));
if (this->phase_[phase].current_sensor_ != nullptr)
this->phase_[phase].current_sensor_->publish_state(this->get_local_phase_current_(phase));
if (this->phase_[phase].power_sensor_ != nullptr)
this->phase_[phase].power_sensor_->publish_state(this->get_local_phase_active_power_(phase));
if (this->phase_[phase].power_factor_sensor_ != nullptr)
this->phase_[phase].power_factor_sensor_->publish_state(this->get_local_phase_power_factor_(phase));
if (this->phase_[phase].reactive_power_sensor_ != nullptr)
this->phase_[phase].reactive_power_sensor_->publish_state(this->get_local_phase_reactive_power_(phase));
if (this->phase_[phase].apparent_power_sensor_ != nullptr)
this->phase_[phase].apparent_power_sensor_->publish_state(this->get_local_phase_apparent_power_(phase));
if (this->phase_[phase].forward_active_energy_sensor_ != nullptr) {
this->phase_[phase].forward_active_energy_sensor_->publish_state(
this->get_local_phase_forward_active_energy_(phase));
}
if (this->phase_[phase].reverse_active_energy_sensor_ != nullptr) {
this->phase_[phase].reverse_active_energy_sensor_->publish_state(
this->get_local_phase_reverse_active_energy_(phase));
}
if (this->phase_[phase].phase_angle_sensor_ != nullptr)
this->phase_[phase].phase_angle_sensor_->publish_state(this->get_local_phase_angle_(phase));
if (this->phase_[phase].harmonic_active_power_sensor_ != nullptr) {
this->phase_[phase].harmonic_active_power_sensor_->publish_state(
this->get_local_phase_harmonic_active_power_(phase));
}
if (this->phase_[phase].peak_current_sensor_ != nullptr)
this->phase_[phase].peak_current_sensor_->publish_state(this->get_local_phase_peak_current_(phase));
}
if (this->freq_sensor_ != nullptr)
this->freq_sensor_->publish_state(this->get_frequency_());
if (this->chip_temperature_sensor_ != nullptr)
this->chip_temperature_sensor_->publish_state(this->get_chip_temperature_());
}
}
void ATM90E36Component::update() {
this->set_publish_interval_flag_(true);
this->status_clear_warning();
#ifdef USE_TEXT_SENSOR
this->check_phase_status();
this->check_freq_status();
#endif
}
//Setup called by ESP32 setup()
void ATM90E36Component::setup() {
this->spi_setup();
this->cs_summary_ = this->cs_->dump_summary();
const char *cs = this->cs_summary_.c_str();
uint16_t mmode0 = 0x87; // 3P4W 50Hz
uint16_t high_thresh = 0;
uint16_t low_thresh = 0;
if (line_freq_ == 60) {
mmode0 |= 1 << 12; // sets 12th bit to 1, 60Hz
// for freq threshold registers
high_thresh = 6300; // 63.00 Hz
low_thresh = 5700; // 57.00 Hz
} else {
high_thresh = 5300; // 53.00 Hz
low_thresh = 4700; // 47.00 Hz
}
if (current_phases_ == 2) {
mmode0 |= 1 << 8; // sets 8th bit to 1, 3P3W
mmode0 |= 0 << 1; // sets 1st bit to 0, phase b is not counted into the all-phase sum energy/power (P/Q/S)
}
//Calulate MMMode1 taking current_gain gain_dpga and gain_voltage
pga_cal = pga_current_;
pga_cal |= pga_voltage_ << 8;
pga_cal |= dpga_gain_ << 14;
this->write16_(ATM90E36_REGISTER_SOFTRESET, 0x789A, false); // Perform soft reset
delay(6); // Wait for the minimum 5ms + 1ms
this->write16_(ATM90E36_REGISTER_FUNCEN0, 0x0000); // Voltage sag
this->write16_(ATM90E36_REGISTER_FUNCEN1, 0x0000); // Voltage sag
this->write16_(ATM90E36_REGISTER_SAGTH, calculate_voltage_threshold(50.0, this->phase_[0].voltage_gain_, 1)); // Voltage sag
this->write16_(ATM90E36_REGISTER_CONFIGSTART, 0x5678); // Start Config of chip
this->write16_(ATM90E36_REGISTER_PLCONSTH, 0x0861); // PL Constant MSB (default) = 140625000
this->write16_(ATM90E36_REGISTER_PLCONSTL, 0xC468); // PL Constant LSB (default)
this->write16_(ATM90E36_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program) mmode0
this->write16_(ATM90E36_REGISTER_MMODE1, pga_cal); // PGA Gain Configuration for Current Channels 0x5555
this->write16_(ATM90E36_REGISTER_PSTARTTH, 0x0000); // All Active Startup Power Threshold - 0.02A/0.00032 = 7500
this->write16_(ATM90E36_REGISTER_QSTARTTH, 0x0000); // All Reactive Startup Power Threshold - 50%
this->write16_(ATM90E36_REGISTER_SSTARTTH, 0x0000); // All Reactive Startup Power Threshold - 50%
this->write16_(ATM90E36_REGISTER_PPHASETH, 0x0000); // Each Phase Active Phase Threshold - 0.002A/0.00032 = 750
this->write16_(ATM90E36_REGISTER_QPHASETH, 0x0000); // Each phase Reactive Phase Threshold - 10%
this->write16_(ATM90E36_REGISTER_SPHASETH, 0x0000); // Apparent Phase Threshold
this->write16_(ATM90E36_REGISTER_CSO, 0x4741); // Checksum 0
this->write16_(ATM90E36_REGISTER_CALSTART, 0x5678); // Metering calibration startup
this->write16_(ATM90E36_REGISTER_PQGAINA, 0x0000); // CT1 line active power offset
this->write16_(ATM90E36_REGISTER_PHIA, 0x0000); // CT1 line reactive power offset
this->write16_(ATM90E36_REGISTER_PQGAINB, 0x0000); // CT2 line active power offset
this->write16_(ATM90E36_REGISTER_PHIB, 0x0000); // CT2 line reactive power offset
this->write16_(ATM90E36_REGISTER_PQGAINC, 0x0000); // CT3 line active power offset
this->write16_(ATM90E36_REGISTER_PHIC, 0x0000); // CT3 line reactive power offset
this->write16_(ATM90E36_REGISTER_POFFSETA, 0x0000); // Line calibration gain
this->write16_(ATM90E36_REGISTER_QOFFSETA, 0x0000); // Line calibration angle
this->write16_(ATM90E36_REGISTER_POFFSETB, 0x0000); // Line calibration gain
this->write16_(ATM90E36_REGISTER_QOFFSETB, 0x0000); // Line calibration angle
this->write16_(ATM90E36_REGISTER_POFFSETC, 0x0000); // Line calibration gain
this->write16_(ATM90E36_REGISTER_QOFFSETC, 0x0000); // Line calibration angle
this->write16_(ATM90E36_REGISTER_CS1, 0x0000); // Checksum 1
// **************** FUNDAMENTAL/HARMONIC & ENERGY CALIBRATION REGISTERS ****************
this->write16_(ATM90E36_REGISTER_HARMSTART, 0x5678); // Metering calibration startup
this->write16_(ATM90E36_REGISTER_POFFSETAF, 0x0000); // CT1 Fund. active power offset
this->write16_(ATM90E36_REGISTER_POFFSETBF, 0x0000); // CT2 Fund. active power offset
this->write16_(ATM90E36_REGISTER_POFFSETCF, 0x0000); // CT3 Fund. active power offset
this->write16_(ATM90E36_REGISTER_PGAINAF, 0x0000); // CT1 Fund. active power gain
this->write16_(ATM90E36_REGISTER_PGAINBF, 0x0000); // CT2 Fund. active power gain
this->write16_(ATM90E36_REGISTER_PGAINCF, 0x0000); // CT3 Fund. active power gain
this->write16_(ATM90E36_REGISTER_CS2, 0x0000); // Checksum 2
// **************** MEASUREMENT CALIBRATION REGISTERS ****************
this->write16_(ATM90E36_REGISTER_ADJSTART, 0x5678); // Measurement calibration
this->write16_(ATM90E36_REGISTER_UGAINA, this->phase_[0].voltage_gain_); // CT1 SVoltage RMS gain
this->write16_(ATM90E36_REGISTER_IGAINA, this->phase_[0].ct_gain_); // CT1 line current gain.
this->write16_(ATM90E36_REGISTER_UOFFSETA, 0x0000); // CT1 Voltage offset
this->write16_(ATM90E36_REGISTER_IOFFSETA, 0x0000); // CT1 line current offset
this->write16_(ATM90E36_REGISTER_UGAINB, this->phase_[1].voltage_gain_); // CT2 SVoltage RMS gain
this->write16_(ATM90E36_REGISTER_IGAINB, this->phase_[1].ct_gain_); // CT2 line current gain.
this->write16_(ATM90E36_REGISTER_UOFFSETB, 0x0000); // CT2 Voltage offset
this->write16_(ATM90E36_REGISTER_IOFFSETB, 0x0000); // CT2 line current offset
this->write16_(ATM90E36_REGISTER_UGAINC, this->phase_[2].voltage_gain_); // CT3 SVoltage RMS gain
this->write16_(ATM90E36_REGISTER_IGAINC, this->phase_[2].ct_gain_); // CT3 line current gain.
this->write16_(ATM90E36_REGISTER_UOFFSETA, 0x0000); // CT3 Voltage offset
this->write16_(ATM90E36_REGISTER_IOFFSETA, 0x0000); // CT3 line current offset
// N
this->write16_(ATM90E36_REGISTER_IGAINN, 0xFD7F); // D line current gain
this->write16_(ATM90E36_REGISTER_CS3, 0x02F6); // Checksum 3
}
void ATM90E36Component::dump_config() {
ESP_LOGCONFIG("", "ATM90E36:");
LOG_PIN(" CS Pin: ", this->cs_);
if (this->is_failed()) {
ESP_LOGE(TAG, ESP_LOG_MSG_COMM_FAIL);
}
LOG_UPDATE_INTERVAL(this);
LOG_SENSOR(" ", "Voltage A", this->phase_[PHASEA].voltage_sensor_);
LOG_SENSOR(" ", "Current A", this->phase_[PHASEA].current_sensor_);
LOG_SENSOR(" ", "Power A", this->phase_[PHASEA].power_sensor_);
LOG_SENSOR(" ", "Reactive Power A", this->phase_[PHASEA].reactive_power_sensor_);
LOG_SENSOR(" ", "Apparent Power A", this->phase_[PHASEA].apparent_power_sensor_);
LOG_SENSOR(" ", "PF A", this->phase_[PHASEA].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy A", this->phase_[PHASEA].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy A", this->phase_[PHASEA].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Harmonic Power A", this->phase_[PHASEA].harmonic_active_power_sensor_);
LOG_SENSOR(" ", "Phase Angle A", this->phase_[PHASEA].phase_angle_sensor_);
LOG_SENSOR(" ", "Peak Current A", this->phase_[PHASEA].peak_current_sensor_);
LOG_SENSOR(" ", "Voltage B", this->phase_[PHASEB].voltage_sensor_);
LOG_SENSOR(" ", "Current B", this->phase_[PHASEB].current_sensor_);
LOG_SENSOR(" ", "Power B", this->phase_[PHASEB].power_sensor_);
LOG_SENSOR(" ", "Reactive Power B", this->phase_[PHASEB].reactive_power_sensor_);
LOG_SENSOR(" ", "Apparent Power B", this->phase_[PHASEB].apparent_power_sensor_);
LOG_SENSOR(" ", "PF B", this->phase_[PHASEB].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy B", this->phase_[PHASEB].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy B", this->phase_[PHASEB].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Harmonic Power B", this->phase_[PHASEB].harmonic_active_power_sensor_);
LOG_SENSOR(" ", "Phase Angle B", this->phase_[PHASEB].phase_angle_sensor_);
LOG_SENSOR(" ", "Peak Current B", this->phase_[PHASEB].peak_current_sensor_);
LOG_SENSOR(" ", "Voltage C", this->phase_[PHASEC].voltage_sensor_);
LOG_SENSOR(" ", "Current C", this->phase_[PHASEC].current_sensor_);
LOG_SENSOR(" ", "Power C", this->phase_[PHASEC].power_sensor_);
LOG_SENSOR(" ", "Reactive Power C", this->phase_[PHASEC].reactive_power_sensor_);
LOG_SENSOR(" ", "Apparent Power C", this->phase_[PHASEC].apparent_power_sensor_);
LOG_SENSOR(" ", "PF C", this->phase_[PHASEC].power_factor_sensor_);
LOG_SENSOR(" ", "Active Forward Energy C", this->phase_[PHASEC].forward_active_energy_sensor_);
LOG_SENSOR(" ", "Active Reverse Energy C", this->phase_[PHASEC].reverse_active_energy_sensor_);
LOG_SENSOR(" ", "Harmonic Power C", this->phase_[PHASEC].harmonic_active_power_sensor_);
LOG_SENSOR(" ", "Phase Angle C", this->phase_[PHASEC].phase_angle_sensor_);
LOG_SENSOR(" ", "Peak Current C", this->phase_[PHASEC].peak_current_sensor_);
LOG_SENSOR(" ", "Frequency", this->freq_sensor_);
LOG_SENSOR(" ", "Chip Temp", this->chip_temperature_sensor_);
}
float ATM90E36Component::get_setup_priority() const { return setup_priority::IO; }
// R/C registers can conly be cleared after the LastSPIData register is updated (register 78H)
// Peakdetect period: 05H. Bit 15:8 are PeakDet_period in ms. 7:0 are Sag_period
// Default is 143FH (20ms, 63ms)
uint16_t ATM90E36Component::read16_(uint16_t a_register) {
this->enable();
delay_microseconds_safe(10); // min delay between CS low and first SCK is 200ns - 1us is plenty
uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03);
uint8_t addrl = (a_register & 0xFF);
uint8_t data[4] = {addrh, addrl, 0x00, 0x00};
this->transfer_array(data, 4);
uint16_t output = encode_uint16(data[2], data[3]);
ESP_LOGVV(TAG, "read16_ 0x%04" PRIX16 " output 0x%04" PRIX16, a_register, output);
delay_microseconds_safe(10); // allow the last clock to propagate before releasing CS
this->disable();
delay_microseconds_safe(1); // meet minimum CS high time before next transaction
return output;
}
void ATM90E36Component::write16_(uint16_t a_register, uint16_t val, bool validate) {
ESP_LOGVV(TAG, "write16_ 0x%04" PRIX16 " val 0x%04" PRIX16, a_register, val);
uint8_t addrh = ((a_register >> 8) & 0x03);
uint8_t addrl = (a_register & 0xFF);
uint8_t data[4] = {addrh, addrl, uint8_t((val >> 8) & 0xFF), uint8_t(val & 0xFF)};
this->enable();
delay_microseconds_safe(1); // ensure CS setup time
this->write_array(data, 4);
delay_microseconds_safe(1); // allow clock to settle before raising CS
this->disable();
delay_microseconds_safe(1); // ensure minimum CS high time
if (validate)
this->validate_spi_read_(val, "write16()");
}
float ATM90E36Component::get_local_phase_voltage_(uint8_t phase) { return this->phase_[phase].voltage_; }
float ATM90E36Component::get_local_phase_current_(uint8_t phase) { return this->phase_[phase].current_; }
float ATM90E36Component::get_local_phase_active_power_(uint8_t phase) { return this->phase_[phase].active_power_; }
float ATM90E36Component::get_local_phase_reactive_power_(uint8_t phase) { return this->phase_[phase].reactive_power_; }
float ATM90E36Component::get_local_phase_apparent_power_(uint8_t phase) { return this->phase_[phase].apparent_power_; }
float ATM90E36Component::get_local_phase_power_factor_(uint8_t phase) { return this->phase_[phase].power_factor_; }
float ATM90E36Component::get_local_phase_forward_active_energy_(uint8_t phase) {
return this->phase_[phase].forward_active_energy_;
}
float ATM90E36Component::get_local_phase_reverse_active_energy_(uint8_t phase) {
return this->phase_[phase].reverse_active_energy_;
}
float ATM90E36Component::get_local_phase_angle_(uint8_t phase) { return this->phase_[phase].phase_angle_; }
float ATM90E36Component::get_local_phase_harmonic_active_power_(uint8_t phase) {
return this->phase_[phase].harmonic_active_power_;
}
float ATM90E36Component::get_local_phase_peak_current_(uint8_t phase) { return this->phase_[phase].peak_current_; }
float ATM90E36Component::get_phase_voltage_(uint8_t phase) {
uint16_t voltage = this->read16_(ATM90E36_REGISTER_URMS + phase);
this->validate_spi_read_(voltage, "get_phase_voltage()");
if (voltage < 50) {
voltage = 0;
}
return (float) voltage / 100;
}
float ATM90E36Component::get_phase_voltage_avg_(uint8_t phase) {
const uint8_t reads = 10;
uint32_t accumulation = 0;
uint16_t voltage = 0;
for (uint8_t i = 0; i < reads; i++) {
voltage = this->read16_(ATM90E36_REGISTER_URMS + phase);
this->validate_spi_read_(voltage, "get_phase_voltage_avg_()");
accumulation += voltage;
}
voltage = accumulation / reads;
this->phase_[phase].voltage_ = (float) voltage / 100;
return this->phase_[phase].voltage_;
}
float ATM90E36Component::get_phase_current_avg_(uint8_t phase) {
const uint8_t reads = 10;
uint32_t accumulation = 0;
uint16_t current = 0;
for (uint8_t i = 0; i < reads; i++) {
current = this->read16_(ATM90E36_REGISTER_IRMS + phase);
this->validate_spi_read_(current, "get_phase_current_avg_()");
accumulation += current;
}
current = accumulation / reads;
this->phase_[phase].current_ = (float) current / 1000;
return this->phase_[phase].current_;
}
float ATM90E36Component::get_phase_current_(uint8_t phase) {
const uint16_t current = this->read16_(ATM90E36_REGISTER_IRMS + phase);
this->validate_spi_read_(current, "get_phase_current_()");
return (float) current / 1000;
}
float ATM90E36Component::get_phase_active_power_(uint8_t phase) {
float val = (float)this->read16_(ATM90E36_REGISTER_PMEAN + phase);
if (val >= 0xFFFF) {
val = 0;
}
return val;
}
float ATM90E36Component::get_phase_reactive_power_(uint8_t phase) {
int16_t val = (int16_t)this->read16_(ATM90E36_REGISTER_QMEAN + phase);
return (float)val / 1000;
}
float ATM90E36Component::get_phase_apparent_power_(uint8_t phase) {
const int val = this->read16_(ATM90E36_REGISTER_SMEANT + phase);
return val;
}
float ATM90E36Component::get_phase_power_factor_(uint8_t phase) {
uint16_t powerfactor = this->read16_(ATM90E36_REGISTER_PFMEAN + phase); // unsigned to compare to lastspidata
this->validate_spi_read_(powerfactor, "get_phase_power_factor_()");
return (float) ((int16_t) powerfactor) / 1000; // make it signed again
}
float ATM90E36Component::get_phase_forward_active_energy_(uint8_t phase) {
const uint16_t val = this->read16_(ATM90E36_REGISTER_APENERGY + phase);
if ((UINT32_MAX - this->phase_[phase].cumulative_forward_active_energy_) > val) {
this->phase_[phase].cumulative_forward_active_energy_ += val;
} else {
this->phase_[phase].cumulative_forward_active_energy_ = val;
}
// 0.01CF resolution = 0.003125 Wh per count
return ((float) this->phase_[phase].cumulative_forward_active_energy_ * (10.0f / 3200.0f));
}
float ATM90E36Component::get_phase_reverse_active_energy_(uint8_t phase) {
const uint16_t val = this->read16_(ATM90E36_REGISTER_ANENERGY + phase);
if (UINT32_MAX - this->phase_[phase].cumulative_reverse_active_energy_ > val) {
this->phase_[phase].cumulative_reverse_active_energy_ += val;
} else {
this->phase_[phase].cumulative_reverse_active_energy_ = val;
}
// 0.01CF resolution = 0.003125 Wh per count
return ((float) this->phase_[phase].cumulative_reverse_active_energy_ * (10.0f / 3200.0f));
}
float ATM90E36Component::get_phase_harmonic_active_power_(uint8_t phase) {
int16_t val = (int16_t)this->read16_(ATM90E36_REGISTER_PMEANH + phase);
return (float)val;
}
float ATM90E36Component::get_phase_angle_(uint8_t phase) {
int16_t val = (int16_t)this->read16_(ATM90E36_REGISTER_PANGLE + phase) / 10.0;
return (val > 180) ? (float) (val - 360.0f) : (float) val;
}
float ATM90E36Component::get_phase_peak_current_(uint8_t phase) {
int16_t val = (float) this->read16_(ATM90E36_REGISTER_IPEAK + phase);
if (!this->peak_current_signed_)
val = std::abs(val);
// phase register * phase current gain value / 1000 * 2^13
return (val * this->phase_[phase].ct_gain_ / 8192000.0);
}
float ATM90E36Component::get_frequency_() {
const uint16_t freq = this->read16_(ATM90E36_REGISTER_FREQ);
return (float) freq / 100;
}
float ATM90E36Component::get_chip_temperature_() {
const uint16_t ctemp = this->read16_(ATM90E36_REGISTER_TEMP);
return (float) ctemp;
}
#ifdef USE_TEXT_SENSOR
void ATM90E36Component::check_phase_status() {
SysStatus0 state0;
SysStatus1 state1;
state0.data = this->read16_(ATM90E36_REGISTER_SYSSTATUS0);
state1.data = this->read16_(ATM90E36_REGISTER_SYSSTATUS1);
std::string status;
if (state0.bits.SagWarn) {
status += "Voltage Sag; ";
}
if (state1.bits.THDUOv) {
status += "Over Voltage; ";
}
auto *sensor = this->phase_status_text_sensor_;
if (sensor == nullptr)
return;
if (!status.empty()) {
status.pop_back(); // remove space
status.pop_back(); // remove semicolon
ESP_LOGW(TAG, "%s: %s", sensor->get_name().c_str(), status.c_str());
sensor->publish_state(status);
} else {
sensor->publish_state("Okay");
}
}
void ATM90E36Component::check_freq_status() {
SysStatus0 state0;
state0.data = this->read16_(ATM90E36_REGISTER_SYSSTATUS0);
std::string freq_status;
if (state0.bits.SagWarn) {
freq_status = "LOW";
} else {
freq_status = "Normal";
}
if (this->freq_status_text_sensor_ != nullptr) {
if (freq_status == "Normal") {
ESP_LOGD(TAG, "Frequency status: %s", freq_status.c_str());
} else {
ESP_LOGW(TAG, "Frequency status: %s", freq_status.c_str());
}
this->freq_status_text_sensor_->publish_state(freq_status);
}
}
#endif
uint16_t ATM90E36Component::calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier) {
// this assumes that 60Hz electrical systems use 120V mains,
// which is usually, but not always the case
float nominal_voltage = (line_freq == 60) ? 120.0f : 220.0f;
float target_voltage = nominal_voltage * multiplier;
float peak_01v = target_voltage * 100.0f * std::numbers::sqrt2_v<float>; // convert RMS → peak, scale to 0.01V
float divider = (2.0f * ugain) / 32768.0f;
float threshold = peak_01v / divider;
return static_cast<uint16_t>(threshold);
}
bool ATM90E36Component::validate_spi_read_(uint16_t expected, const char *context) {
uint16_t last = this->read16_(ATM90E36_REGISTER_LASTSPIDATA);
if (last != expected) {
if (context != nullptr) {
ESP_LOGW(TAG, "[%s] SPI read mismatch: expected 0x%04X, got 0x%04X", context, expected, last);
} else {
ESP_LOGW(TAG, "SPI read mismatch: expected 0x%04X, got 0x%04X", expected, last);
}
return false;
}
return true;
}
} // namespace atm90e36
} // namespace esphome
+239
View File
@@ -0,0 +1,239 @@
#pragma once
#include <unordered_map>
#include "atm90e36_reg.h"
#include "esphome/components/sensor/sensor.h"
#include "esphome/components/spi/spi.h"
#include "esphome/core/application.h"
#include "esphome/core/component.h"
#include "esphome/core/helpers.h"
#include "esphome/core/preferences.h"
namespace esphome {
namespace atm90e36 {
class ATM90E36Component : public PollingComponent,
public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH,
spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_2MHZ> {
public:
static const uint8_t PHASEA = 0;
static const uint8_t PHASEB = 1;
static const uint8_t PHASEC = 2;
const char *phase_labels[3] = {"A", "B", "C"};
void loop() override;
void setup() override;
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void set_voltage_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].voltage_sensor_ = obj; }
void set_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].current_sensor_ = obj; }
void set_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_sensor_ = obj; }
void set_reactive_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].reactive_power_sensor_ = obj; }
void set_apparent_power_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].apparent_power_sensor_ = obj; }
void set_forward_active_energy_sensor(int phase, sensor::Sensor *obj) {
this->phase_[phase].forward_active_energy_sensor_ = obj;
}
void set_reverse_active_energy_sensor(int phase, sensor::Sensor *obj) {
this->phase_[phase].reverse_active_energy_sensor_ = obj;
}
void set_power_factor_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].power_factor_sensor_ = obj; }
void set_phase_angle_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].phase_angle_sensor_ = obj; }
void set_harmonic_active_power_sensor(int phase, sensor::Sensor *obj) {
this->phase_[phase].harmonic_active_power_sensor_ = obj;
}
void set_peak_current_sensor(int phase, sensor::Sensor *obj) { this->phase_[phase].peak_current_sensor_ = obj; }
void set_volt_gain(int phase, uint16_t gain) {
this->phase_[phase].voltage_gain_ = gain;
}
void set_ct_gain(int phase, uint16_t gain) {
this->phase_[phase].ct_gain_ = gain;
}
void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; }
void set_peak_current_signed(bool flag) { peak_current_signed_ = flag; }
void set_chip_temperature_sensor(sensor::Sensor *chip_temperature_sensor) {
chip_temperature_sensor_ = chip_temperature_sensor;
}
void set_line_freq(int freq) { line_freq_ = freq; }
void set_current_phases(int phases) { current_phases_ = phases; }
void set_pga_current(uint16_t gain) { pga_current_ = gain; }
void set_pga_voltage(uint16_t gain) { pga_voltage_ = gain; }
void set_dpga_gain(uint16_t gain) { dpga_gain_ = gain; }
#ifdef USE_NUMBER
void set_reference_voltage(uint8_t phase, number::Number *ref_voltage) { ref_voltages_[phase] = ref_voltage; }
void set_reference_current(uint8_t phase, number::Number *ref_current) { ref_currents_[phase] = ref_current; }
#endif
float get_reference_voltage(uint8_t phase) {
#ifdef USE_NUMBER
return (phase >= 0 && phase < 3 && ref_voltages_[phase]) ? ref_voltages_[phase]->state : 120.0; // Default voltage
#else
return 120.0; // Default voltage
#endif
}
float get_reference_current(uint8_t phase) {
#ifdef USE_NUMBER
return (phase >= 0 && phase < 3 && ref_currents_[phase]) ? ref_currents_[phase]->state : 5.0f; // Default current
#else
return 5.0f; // Default current
#endif
}
bool using_saved_calibrations_ = false; // Track if stored calibrations are being used
#ifdef USE_TEXT_SENSOR
void check_phase_status();
void check_freq_status();
void check_over_current();
void set_phase_status_text_sensor(text_sensor::TextSensor *sensor) {
this->phase_status_text_sensor_ = sensor;
}
void set_freq_status_text_sensor(text_sensor::TextSensor *sensor) { this->freq_status_text_sensor_ = sensor; }
#endif
uint16_t calculate_voltage_threshold(int line_freq, uint16_t ugain, float multiplier);
int32_t last_periodic_millis = millis();
protected:
#ifdef USE_NUMBER
number::Number *ref_voltages_[3]{nullptr, nullptr, nullptr};
number::Number *ref_currents_[3]{nullptr, nullptr, nullptr};
#endif
uint16_t read16_(uint16_t a_register);
void write16_(uint16_t a_register, uint16_t val, bool validate = true);
float get_local_phase_voltage_(uint8_t phase);
float get_local_phase_current_(uint8_t phase);
float get_local_phase_active_power_(uint8_t phase);
float get_local_phase_reactive_power_(uint8_t phase);
float get_local_phase_apparent_power_(uint8_t phase);
float get_local_phase_power_factor_(uint8_t phase);
float get_local_phase_forward_active_energy_(uint8_t phase);
float get_local_phase_reverse_active_energy_(uint8_t phase);
float get_local_phase_angle_(uint8_t phase);
float get_local_phase_harmonic_active_power_(uint8_t phase);
float get_local_phase_peak_current_(uint8_t phase);
float get_phase_voltage_(uint8_t phase);
float get_phase_voltage_avg_(uint8_t phase);
float get_phase_current_(uint8_t phase);
float get_phase_current_avg_(uint8_t phase);
float get_phase_active_power_(uint8_t phase);
float get_phase_reactive_power_(uint8_t phase);
float get_phase_apparent_power_(uint8_t phase);
float get_phase_power_factor_(uint8_t phase);
float get_phase_forward_active_energy_(uint8_t phase);
float get_phase_reverse_active_energy_(uint8_t phase);
float get_phase_angle_(uint8_t phase);
float get_phase_harmonic_active_power_(uint8_t phase);
float get_phase_peak_current_(uint8_t phase);
float get_frequency_();
float get_chip_temperature_();
bool get_publish_interval_flag_() { return publish_interval_flag_; };
void set_publish_interval_flag_(bool flag) { publish_interval_flag_ = flag; };
void restore_offset_calibrations_();
void restore_power_offset_calibrations_();
void restore_gain_calibrations_();
void save_offset_calibration_to_memory_();
void save_gain_calibration_to_memory_();
void save_power_offset_calibration_to_memory_();
void write_offsets_to_registers_(uint8_t phase, int16_t voltage_offset, int16_t current_offset);
void write_power_offsets_to_registers_(uint8_t phase, int16_t p_offset, int16_t q_offset);
void write_gains_to_registers_();
bool verify_gain_writes_();
bool validate_spi_read_(uint16_t expected, const char *context = nullptr);
void log_calibration_status_();
struct ATM90E32Phase {
uint16_t voltage_gain_{0};
uint16_t ct_gain_{0};
int16_t voltage_offset_{0};
int16_t current_offset_{0};
int16_t active_power_offset_{0};
int16_t reactive_power_offset_{0};
float voltage_{0};
float current_{0};
float active_power_{0};
float reactive_power_{0};
float apparent_power_{0};
float power_factor_{0};
float forward_active_energy_{0};
float reverse_active_energy_{0};
float phase_angle_{0};
float harmonic_active_power_{0};
float peak_current_{0};
sensor::Sensor *voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
sensor::Sensor *reactive_power_sensor_{nullptr};
sensor::Sensor *apparent_power_sensor_{nullptr};
sensor::Sensor *power_factor_sensor_{nullptr};
sensor::Sensor *forward_active_energy_sensor_{nullptr};
sensor::Sensor *reverse_active_energy_sensor_{nullptr};
sensor::Sensor *phase_angle_sensor_{nullptr};
sensor::Sensor *harmonic_active_power_sensor_{nullptr};
sensor::Sensor *peak_current_sensor_{nullptr};
uint32_t cumulative_forward_active_energy_{0};
uint32_t cumulative_reverse_active_energy_{0};
} phase_[3];
union SysStatus0 {
uint16_t data;
struct {
bool b0 : 1;
bool b1 : 1;
bool PhaseLoseWn : 1;
bool SagWarn : 1;
bool b4 : 1;
bool b5 : 1;
bool IRevWn : 1;
bool URevWn : 1;
bool CS3Err : 1;
bool b9 : 1;
bool CS2Err : 1;
bool b11 : 1;
bool CS1Err : 1;
bool b13 : 1;
bool CS0Err : 1;
bool b15 : 1;
} bits;
};
union SysStatus1 {
uint16_t data;
struct {
bool RevPchgC : 1;
bool RevPchgB : 1;
bool RevPchgA : 1;
bool RevPchgT : 1;
bool RevQchgC : 1;
bool RevQchgB : 1;
bool RevQchgA : 1;
bool RevQchgT : 1;
bool b8 : 1;
bool DFTDone : 1;
bool THDIOv : 1;
bool THDUOv : 1;
bool b12 : 1;
bool b13 : 1;
bool INOv0 : 1;
bool INOv1 : 1;
} bits;
};
ESPPreferenceObject offset_pref_;
ESPPreferenceObject power_offset_pref_;
ESPPreferenceObject gain_calibration_pref_;
std::string cs_summary_;
sensor::Sensor *freq_sensor_{nullptr};
#ifdef USE_TEXT_SENSOR
text_sensor::TextSensor *phase_status_text_sensor_{nullptr};
text_sensor::TextSensor *freq_status_text_sensor_{nullptr};
#endif
sensor::Sensor *chip_temperature_sensor_{nullptr};
uint16_t pga_current_{0x15};
uint16_t pga_voltage_{0x15};
uint16_t dpga_gain_{0x2};
int line_freq_{60};
int current_phases_{3};
bool publish_interval_flag_{false};
bool peak_current_signed_{false};
uint16_t pga_cal{0x0};
};
} // namespace atm90e32
} // namespace esphome
+244
View File
@@ -0,0 +1,244 @@
#pragma once
#include <cinttypes>
namespace esphome {
namespace atm90e36 {
/* STATUS REGISTERS */
static const uint16_t ATM90E36_REGISTER_SOFTRESET = 0x00; // Software Reset
static const uint16_t ATM90E36_REGISTER_SYSSTATUS0 = 0x01; // System Status 0
static const uint16_t ATM90E36_REGISTER_SYSSTATUS1 = 0x02; // System Status 1
static const uint16_t ATM90E36_REGISTER_FUNCEN0 = 0x03; // Function Enable 0
static const uint16_t ATM90E36_REGISTER_FUNCEN1 = 0x04; // Function Enable 1
static const uint16_t ATM90E36_REGISTER_ZXCONFIG = 0x07; // Zero-Crossing Config
static const uint16_t ATM90E36_REGISTER_SAGTH = 0x08; // Voltage Sag Th
static const uint16_t ATM90E36_REGISTER_PHASELOSSTH = 0x09; // Voltage Phase Losing Th
static const uint16_t ATM90E36_REGISTER_INWARNTH0 = 0x0A; // Threshold for sampled (from ADC) N line rms current
static const uint16_t ATM90E36_REGISTER_INWARNTH1 = 0x0B; // Threshold for sampled (from ADC) N line rms current
static const uint16_t ATM90E36_REGISTER_THDNUTH = 0x0C; // Voltage THD Warning Threshold
static const uint16_t ATM90E36_REGISTER_THDNITH = 0x0D; // Current THD Warning Threshold
static const uint16_t ATM90E36_REGISTER_DMACTRL = 0x0E; // DMA Mode Interface Control
static const uint16_t ATM90E36_REGISTER_LASTSPIDATA = 0x0F; // Last Read/ Write SPI Value
/* LOW POWER MODE REGISTERS - NOT USED */
static const uint16_t ATM90E36_REGISTER_DETECTCTRL = 0x10;
static const uint16_t ATM90E36_REGISTER_DETECTTH1 = 0x11;
static const uint16_t ATM90E36_REGISTER_DETECTTH2 = 0x12;
static const uint16_t ATM90E36_REGISTER_DETECTTH3 = 0x13;
static const uint16_t ATM90E36_REGISTER_PMOFFSETA = 0x14;
static const uint16_t ATM90E36_REGISTER_PMOFFSETB = 0x15;
static const uint16_t ATM90E36_REGISTER_PMOFFSETC = 0x16;
static const uint16_t ATM90E36_REGISTER_PMPGA = 0x17;
static const uint16_t ATM90E36_REGISTER_PMIRMSA = 0x18;
static const uint16_t ATM90E36_REGISTER_PMIRMSB = 0x19;
static const uint16_t ATM90E36_REGISTER_PMIRMSC = 0x1A;
static const uint16_t ATM90E36_REGISTER_PMCONFIG = 0x10B;
static const uint16_t ATM90E36_REGISTER_PMAVGSAMPLES = 0x1C;
static const uint16_t ATM90E36_REGISTER_PMIRMSLSB = 0x1D;
/* CONFIGURATION REGISTERS */
static const uint16_t ATM90E36_REGISTER_CONFIGSTART = 0x30; // Calibration Start Command
static const uint16_t ATM90E36_REGISTER_PLCONSTH = 0x31; // High Word of PL_Constant
static const uint16_t ATM90E36_REGISTER_PLCONSTL = 0x32; // Low Word of PL_Constant
static const uint16_t ATM90E36_REGISTER_MMODE0 = 0x33; // Metering Mode Config
static const uint16_t ATM90E36_REGISTER_MMODE1 = 0x34; // PGA Gain Configuration for Current Channels
static const uint16_t ATM90E36_REGISTER_PSTARTTH = 0x35; // Startup Power Th (P)
static const uint16_t ATM90E36_REGISTER_QSTARTTH = 0x36; // Startup Power Th (Q)
static const uint16_t ATM90E36_REGISTER_SSTARTTH = 0x37; // Startup Power Th (S)
static const uint16_t ATM90E36_REGISTER_PPHASETH = 0x38; // Startup Power Accum Th (P)
static const uint16_t ATM90E36_REGISTER_QPHASETH = 0x39; // Startup Power Accum Th (Q)
static const uint16_t ATM90E36_REGISTER_SPHASETH = 0x3A; // Startup Power Accum Th (S)
static const uint16_t ATM90E36_REGISTER_CSO = 0x3B; // Checksum 0
/* CALIBRATION REGISTERS */
static const uint16_t ATM90E36_REGISTER_CALSTART = 0X40; // Calibration Start Command
static const uint16_t ATM90E36_REGISTER_POFFSETA = 0x41; // A Line Power Offset (P)
static const uint16_t ATM90E36_REGISTER_QOFFSETA = 0x42; // A Line Power Offset (Q)
static const uint16_t ATM90E36_REGISTER_POFFSETB = 0x43; // B Line Power Offset (P)
static const uint16_t ATM90E36_REGISTER_QOFFSETB = 0x44; // B Line Power Offset (Q)
static const uint16_t ATM90E36_REGISTER_POFFSETC = 0x45; // C Line Power Offset (P)
static const uint16_t ATM90E36_REGISTER_QOFFSETC = 0x46; // C Line Power Offset (Q)
static const uint16_t ATM90E36_REGISTER_PQGAINA = 0x47; // A Line Calibration Gain
static const uint16_t ATM90E36_REGISTER_PHIA = 0x48; // A Line Calibration Angle
static const uint16_t ATM90E36_REGISTER_PQGAINB = 0x49; // B Line Calibration Gain
static const uint16_t ATM90E36_REGISTER_PHIB = 0x4A; // B Line Calibration Angle
static const uint16_t ATM90E36_REGISTER_PQGAINC = 0x4B; // C Line Calibration Gain
static const uint16_t ATM90E36_REGISTER_PHIC = 0x4C; // C Line Calibration Angle
static const uint16_t ATM90E36_REGISTER_CS1 = 0X4D; // Checksum 1
/* FUNDAMENTAL/HARMONIC ENERGY CALIBRATION REGISTERS */
static const uint16_t ATM90E36_REGISTER_HARMSTART = 0X50; // Harmonic Calibration Startup Command
static const uint16_t ATM90E36_REGISTER_POFFSETAF = 0x51; // A Fund Power Offset (P)
static const uint16_t ATM90E36_REGISTER_POFFSETBF = 0x52; // B Fund Power Offset (P)
static const uint16_t ATM90E36_REGISTER_POFFSETCF = 0x53; // C Fund Power Offset (P)
static const uint16_t ATM90E36_REGISTER_PGAINAF = 0x54; // A Fund Power Gain (P)
static const uint16_t ATM90E36_REGISTER_PGAINBF = 0x55; // B Fund Power Gain (P)
static const uint16_t ATM90E36_REGISTER_PGAINCF = 0x56; // C Fund Power Gain (P)
static const uint16_t ATM90E36_REGISTER_CS2 = 0X57; // Checksum 2
/* MEASUREMENT CALIBRATION REGISTERS */
static const uint16_t ATM90E36_REGISTER_ADJSTART = 0X60; // Measurement Calibration Startup Command
static const uint16_t ATM90E36_REGISTER_UGAINA = 0x61; // A Voltage RMS Gain
static const uint16_t ATM90E36_REGISTER_IGAINA = 0x62; // A Current RMS Gain
static const uint16_t ATM90E36_REGISTER_UOFFSETA = 0x63; // A Voltage Offset
static const uint16_t ATM90E36_REGISTER_IOFFSETA = 0x64; // A Current Offset
static const uint16_t ATM90E36_REGISTER_UGAINB = 0x65; // B Voltage RMS Gain
static const uint16_t ATM90E36_REGISTER_IGAINB = 0x66; // B Current RMS Gain
static const uint16_t ATM90E36_REGISTER_UOFFSETB = 0x67; // B Voltage Offset
static const uint16_t ATM90E36_REGISTER_IOFFSETB = 0x68; // B Current Offset
static const uint16_t ATM90E36_REGISTER_UGAINC = 0x69; // C Voltage RMS Gain
static const uint16_t ATM90E36_REGISTER_IGAINC = 0x6A; // C Current RMS Gain
static const uint16_t ATM90E36_REGISTER_UOFFSETC = 0x6B; // C Voltage Offset
static const uint16_t ATM90E36_REGISTER_IOFFSETC = 0x6C; // C Current Offset
static const uint16_t ATM90E36_REGISTER_IGAINN = 0x6D; // N Current Gain
static const uint16_t ATM90E36_REGISTER_IOFFSETN = 0x6E; // N Current Offset
static const uint16_t ATM90E36_REGISTER_CS3 = 0X6F; // Checksum 3
/* ENERGY REGISTERS */
static const uint16_t ATM90E36_REGISTER_APENERGYT = 0x80; // Total Forward Active
static const uint16_t ATM90E36_REGISTER_APENERGY = 0x81; // Forward Active Reg Base
static const uint16_t ATM90E36_REGISTER_APENERGYA = 0x81; // A Forward Active
static const uint16_t ATM90E36_REGISTER_APENERGYB = 0x82; // B Forward Active
static const uint16_t ATM90E36_REGISTER_APENERGYC = 0x83; // C Forward Active
static const uint16_t ATM90E36_REGISTER_ANENERGYT = 0x84; // Total Reverse Active
static const uint16_t ATM90E36_REGISTER_ANENERGY = 0x85; // Reverse Active Reg Base
static const uint16_t ATM90E36_REGISTER_ANENERGYA = 0x85; // A Reverse Active
static const uint16_t ATM90E36_REGISTER_ANENERGYB = 0x86; // B Reverse Active
static const uint16_t ATM90E36_REGISTER_ANENERGYC = 0x87; // C Reverse Active
static const uint16_t ATM90E36_REGISTER_RPENERGYT = 0x88; // Total Forward Reactive
static const uint16_t ATM90E36_REGISTER_RPENERGYA = 0x89; // A Forward Reactive
static const uint16_t ATM90E36_REGISTER_RPENERGYB = 0x8A; // B Forward Reactive
static const uint16_t ATM90E36_REGISTER_RPENERGYC = 0x8B; // C Forward Reactive
static const uint16_t ATM90E36_REGISTER_RNENERGYT = 0x8C; // Total Reverse Reactive
static const uint16_t ATM90E36_REGISTER_RNENERGYA = 0x8D; // A Reverse Reactive
static const uint16_t ATM90E36_REGISTER_RNENERGYB = 0x8E; // B Reverse Reactive
static const uint16_t ATM90E36_REGISTER_RNENERGYC = 0x8F; // C Reverse Reactive
static const uint16_t ATM90E36_REGISTER_SAENERGYT = 0x90; // Total Apparent Energy
static const uint16_t ATM90E36_REGISTER_SENERGYA = 0x91; // A Apparent Energy
static const uint16_t ATM90E36_REGISTER_SENERGYB = 0x92; // B Apparent Energy
static const uint16_t ATM90E36_REGISTER_SENERGYC = 0x93; // C Apparent Energy
static const uint16_t ATM90E36_REGISTER_SVENERGYT = 0X94; // (Vector Sum) Total Apparent Energy
static const uint16_t ATM90E36_REGISTER_ENSTATUS0 = 0X95; // Metering Status 0
static const uint16_t ATM90E36_REGISTER_ENSTATUS1 = 0X96; // Metering Status 1
///////////////// 0x97 // Reserved Register
static const uint16_t ATM90E36_REGISTER_SVMEANT = 0X98; // (Vector Sum) Total Apparent Power
static const uint16_t ATM90E36_REGISTER_SVMEANTLSB = 0X99; // lSB of (Vector Sum) Total Apparent Power
/* FUNDAMENTAL / HARMONIC ENERGY REGISTERS */
static const uint16_t ATM90E36_REGISTER_APENERGYTF = 0xA0; // Total Forward Fund. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYAF = 0xA1; // A Forward Fund. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYBF = 0xA2; // B Forward Fund. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYCF = 0xA3; // C Forward Fund. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYTF = 0xA4; // Total Reverse Fund Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYAF = 0xA5; // A Reverse Fund. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYBF = 0xA6; // B Reverse Fund. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYCF = 0xA7; // C Reverse Fund. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYTH = 0xA8; // Total Forward Harm. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYAH = 0xA9; // A Forward Harm. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYBH = 0xAA; // B Forward Harm. Energy
static const uint16_t ATM90E36_REGISTER_APENERGYCH = 0xAB; // C Forward Harm. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYTH = 0xAC; // Total Reverse Harm. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYAH = 0xAD; // A Reverse Harm. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYBH = 0xAE; // B Reverse Harm. Energy
static const uint16_t ATM90E36_REGISTER_ANENERGYCH = 0xAF; // C Reverse Harm. Energy
/* POWER & P.F. REGISTERS */
static const uint16_t ATM90E36_REGISTER_PMEANT = 0xB0; // Phase Active Total (P)
static const uint16_t ATM90E36_REGISTER_PMEAN = 0xB1; // Phase A Active Power start for loop (P)
static const uint16_t ATM90E36_REGISTER_PMEANA = 0xB1; // Phase A Active Power (P)
static const uint16_t ATM90E36_REGISTER_PMEANB = 0xB2; // Phase B Active Power (P)
static const uint16_t ATM90E36_REGISTER_PMEANC = 0xB3; // Phase C Active Power (P)
static const uint16_t ATM90E36_REGISTER_QMEANT = 0xB4; // Potencia Media Total (Q)
static const uint16_t ATM90E36_REGISTER_QMEAN = 0xB5; // Reactive Power all Phases (Q)
static const uint16_t ATM90E36_REGISTER_QMEANA = 0xB5; // Reactive Power A (Q)
static const uint16_t ATM90E36_REGISTER_QMEANB = 0xB6; // Reactive Power B (Q)
static const uint16_t ATM90E36_REGISTER_QMEANC = 0xB7; // Reactive Power C (Q)
static const uint16_t ATM90E36_REGISTER_SMEANT = 0xB8; // Apparent Power All Phases (S)
static const uint16_t ATM90E36_REGISTER_SMEANA = 0xB9; // Apparent Power A (S)
static const uint16_t ATM90E36_REGISTER_SMEANB = 0xBA; // Apparent Power B (S)
static const uint16_t ATM90E36_REGISTER_SMEANC = 0xBB; // Apparent Power C (S)
static const uint16_t ATM90E36_REGISTER_PFMEANT = 0xBC; // Total Power Factor all Phases
static const uint16_t ATM90E36_REGISTER_PFMEAN = 0xBD; // Power Factor Start for loop
static const uint16_t ATM90E36_REGISTER_PFMEANA = 0xBD; // Power Factor A
static const uint16_t ATM90E36_REGISTER_PFMEANB = 0xBE; // Power Factor B
static const uint16_t ATM90E36_REGISTER_PFMEANC = 0xBF; // Power Factor C
static const uint16_t ATM90E36_REGISTER_PMEANTLSB = 0xC0; // Lower Word (Tot. Act. Power)
static const uint16_t ATM90E36_REGISTER_PMEANLSB = 0xC1; // Lower Word Reg Base (Active Power)
static const uint16_t ATM90E36_REGISTER_PMEANALSB = 0xC1; // Lower Word (A Act. Power)
static const uint16_t ATM90E36_REGISTER_PMEANBLSB = 0xC2; // Lower Word (B Act. Power)
static const uint16_t ATM90E36_REGISTER_PMEANCLSB = 0xC3; // Lower Word (C Act. Power)
static const uint16_t ATM90E36_REGISTER_QMEANTLSB = 0xC4; // Lower Word (Tot. React. Power)
static const uint16_t ATM90E36_REGISTER_QMEANLSB = 0xC5; // Lower Word Reg Base (Reactive Power)
static const uint16_t ATM90E36_REGISTER_QMEANALSB = 0xC5; // Lower Word (A React. Power)
static const uint16_t ATM90E36_REGISTER_QMEANBLSB = 0xC6; // Lower Word (B React. Power)
static const uint16_t ATM90E36_REGISTER_QMEANCLSB = 0xC7; // Lower Word (C React. Power)
static const uint16_t ATM90E36_REGISTER_SAMEANTLSB = 0xC8; // Lower Word (Tot. App. Power)
static const uint16_t ATM90E36_REGISTER_SMEANALSB = 0xC9; // Lower Word (A App. Power)
static const uint16_t ATM90E36_REGISTER_SMEANBLSB = 0xCA; // Lower Word (B App. Power)
static const uint16_t ATM90E36_REGISTER_SMEANCLSB = 0xCB; // Lower Word (C App. Power)
/* FUND/HARM POWER & V/I RMS REGISTERS */
static const uint16_t ATM90E36_REGISTER_PMEANTF = 0xD0; // Total Active Fund. Power
static const uint16_t ATM90E36_REGISTER_PMEANAF = 0xD1; // A Active Fund. Power
static const uint16_t ATM90E36_REGISTER_PMEANBF = 0xD2; // B Active Fund. Power
static const uint16_t ATM90E36_REGISTER_PMEANCF = 0xD3; // C Active Fund. Power
static const uint16_t ATM90E36_REGISTER_PMEANTH = 0xD4; // Total Active Harm. Power
static const uint16_t ATM90E36_REGISTER_PMEANH = 0xD5; // Active Harm. Power Reg Base
static const uint16_t ATM90E36_REGISTER_PMEANAH = 0xD5; // A Active Harm. Power
static const uint16_t ATM90E36_REGISTER_PMEANBH = 0xD6; // B Active Harm. Power
static const uint16_t ATM90E36_REGISTER_PMEANCH = 0xD7; // C Active Harm. Power
static const uint16_t ATM90E36_REGISTER_URMS = 0xD9; // RMS Voltage Reg Base
static const uint16_t ATM90E36_REGISTER_URMSA = 0xD9; // A RMS Voltage
static const uint16_t ATM90E36_REGISTER_URMSB = 0xDA; // B RMS Voltage
static const uint16_t ATM90E36_REGISTER_URMSC = 0xDB; // C RMS Voltage
static const uint16_t ATM90E36_REGISTER_IRMS = 0xDD; // RMS Current Reg Base
static const uint16_t ATM90E36_REGISTER_IRMSA = 0xDD; // A RMS Current
static const uint16_t ATM90E36_REGISTER_IRMSB = 0xDE; // B RMS Current
static const uint16_t ATM90E36_REGISTER_IRMSC = 0xDF; // C RMS Current
static const uint16_t ATM90E36_REGISTER_IRMSN = 0xD8; // Calculated N RMS Current
static const uint16_t ATM90E36_REGISTER_PMEANTFLSB = 0xE0; // Lower Word (Tot. Act. Fund. Power)
static const uint16_t ATM90E36_REGISTER_PMEANAFLSB = 0xE1; // Lower Word (A Act. Fund. Power)
static const uint16_t ATM90E36_REGISTER_PMEANBFLSB = 0xE2; // Lower Word (B Act. Fund. Power)
static const uint16_t ATM90E36_REGISTER_PMEANCFLSB = 0xE3; // Lower Word (C Act. Fund. Power)
static const uint16_t ATM90E36_REGISTER_PMEANTHLSB = 0xE4; // Lower Word (Tot. Act. Harm. Power)
static const uint16_t ATM90E36_REGISTER_PMEANHLSB = 0xE5; // Lower Word (A Act. Harm. Power) Reg Base
static const uint16_t ATM90E36_REGISTER_PMEANAHLSB = 0xE5; // Lower Word (A Act. Harm. Power)
static const uint16_t ATM90E36_REGISTER_PMEANBHLSB = 0xE6; // Lower Word (B Act. Harm. Power)
static const uint16_t ATM90E36_REGISTER_PMEANCHLSB = 0xE7; // Lower Word (C Act. Harm. Power)
///////////////// 0xE8 // Reserved Register
static const uint16_t ATM90E36_REGISTER_URMSLSB = 0xE9; // Lower Word RMS Voltage Reg Base
static const uint16_t ATM90E36_REGISTER_URMSALSB = 0xE9; // Lower Word (A RMS Voltage)
static const uint16_t ATM90E36_REGISTER_URMSBLSB = 0xEA; // Lower Word (B RMS Voltage)
static const uint16_t ATM90E36_REGISTER_URMSCLSB = 0xEB; // Lower Word (C RMS Voltage)
///////////////// 0xEC // Reserved Register
static const uint16_t ATM90E36_REGISTER_IRMSLSB = 0xED; // Lower Word RMS Current Reg Base
static const uint16_t ATM90E36_REGISTER_IRMSALSB = 0xED; // Lower Word (A RMS Current)
static const uint16_t ATM90E36_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS Current)
static const uint16_t ATM90E36_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current)
/* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/
static const uint16_t ATM90E36_REGISTER_UPEAKA = 0xF1; // A Voltage Peak
static const uint16_t ATM90E36_REGISTER_UPEAKB = 0xF2; // B Voltage Peak
static const uint16_t ATM90E36_REGISTER_UPEAKC = 0xF3; // C Voltage Peak
//////////////// 0xF4 // Reserved Register
static const uint16_t ATM90E36_REGISTER_IPEAK = 0xF5; // Peak Current Reg Base
static const uint16_t ATM90E36_REGISTER_IPEAKA = 0xF5; // A Current Peak
static const uint16_t ATM90E36_REGISTER_IPEAKB = 0xF6; // B Current Peak
static const uint16_t ATM90E36_REGISTER_IPEAKC = 0xF7; // C Current Peak
static const uint16_t ATM90E36_REGISTER_FREQ = 0xF8; // Frequency
static const uint16_t ATM90E36_REGISTER_PANGLE = 0xF9; // Mean Phase Angle Reg Base
static const uint16_t ATM90E36_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle
static const uint16_t ATM90E36_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle
static const uint16_t ATM90E36_REGISTER_PANGLEC = 0xFB; // C Mean Phase Angle
static const uint16_t ATM90E36_REGISTER_TEMP = 0xFC; // Measured Temperature
static const uint16_t ATM90E36_REGISTER_UANGLEA = 0xFD; // A Voltage Phase Angle
static const uint16_t ATM90E36_REGISTER_UANGLEB = 0xFE; // B Voltage Phase Angle
static const uint16_t ATM90E36_REGISTER_UANGLEC = 0xFF; // C Voltage Phase Angle
} // namespace atm90e32
} // namespace esphome
+130
View File
@@ -0,0 +1,130 @@
import esphome.codegen as cg
from esphome.components import number
import esphome.config_validation as cv
from esphome.const import (
CONF_ID,
CONF_MAX_VALUE,
CONF_MIN_VALUE,
CONF_MODE,
CONF_PHASE_A,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_REFERENCE_VOLTAGE,
CONF_STEP,
ENTITY_CATEGORY_CONFIG,
UNIT_AMPERE,
UNIT_VOLT,
)
from .. import atm90e36_ns
from ..sensor import ATM90E36Component
ATM90E36Number = atm90e36_ns.class_(
"ATM90E36Number", number.Number, cg.Parented.template(ATM90E36Component)
)
CONF_REFERENCE_CURRENT = "reference_current"
PHASE_KEYS = [CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]
REFERENCE_VOLTAGE_PHASE_SCHEMA = cv.All(
cv.Schema(
{
cv.Optional(CONF_MODE, default="box"): cv.string,
cv.Optional(CONF_MIN_VALUE, default=100.0): cv.float_,
cv.Optional(CONF_MAX_VALUE, default=260.0): cv.float_,
cv.Optional(CONF_STEP, default=0.1): cv.float_,
}
).extend(
number.number_schema(
class_=ATM90E36Number,
unit_of_measurement=UNIT_VOLT,
entity_category=ENTITY_CATEGORY_CONFIG,
icon="mdi:power-plug",
)
)
)
REFERENCE_CURRENT_PHASE_SCHEMA = cv.All(
cv.Schema(
{
cv.Optional(CONF_MODE, default="box"): cv.string,
cv.Optional(CONF_MIN_VALUE, default=1.0): cv.float_,
cv.Optional(CONF_MAX_VALUE, default=200.0): cv.float_,
cv.Optional(CONF_STEP, default=0.1): cv.float_,
}
).extend(
number.number_schema(
class_=ATM90E36Number,
unit_of_measurement=UNIT_AMPERE,
entity_category=ENTITY_CATEGORY_CONFIG,
icon="mdi:home-lightning-bolt",
)
)
)
REFERENCE_VOLTAGE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_PHASE_A): REFERENCE_VOLTAGE_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): REFERENCE_VOLTAGE_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): REFERENCE_VOLTAGE_PHASE_SCHEMA,
}
)
REFERENCE_CURRENT_SCHEMA = cv.Schema(
{
cv.Optional(CONF_PHASE_A): REFERENCE_CURRENT_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): REFERENCE_CURRENT_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): REFERENCE_CURRENT_PHASE_SCHEMA,
}
)
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(CONF_ID): cv.use_id(ATM90E36Component),
cv.Optional(CONF_REFERENCE_VOLTAGE): REFERENCE_VOLTAGE_SCHEMA,
cv.Optional(CONF_REFERENCE_CURRENT): REFERENCE_CURRENT_SCHEMA,
}
)
async def to_code(config):
parent = await cg.get_variable(config[CONF_ID])
if voltage_cfg := config.get(CONF_REFERENCE_VOLTAGE):
voltage_objs = [None, None, None]
for i, key in enumerate(PHASE_KEYS):
if validated := voltage_cfg.get(key):
obj = await number.new_number(
validated,
min_value=validated["min_value"],
max_value=validated["max_value"],
step=validated["step"],
)
await cg.register_parented(obj, parent)
voltage_objs[i] = obj
# Inherit from A → B/C if only A defined
if voltage_objs[0] is not None:
for i in range(3):
if voltage_objs[i] is None:
voltage_objs[i] = voltage_objs[0]
for i, obj in enumerate(voltage_objs):
if obj is not None:
cg.add(parent.set_reference_voltage(i, obj))
if current_cfg := config.get(CONF_REFERENCE_CURRENT):
for i, key in enumerate(PHASE_KEYS):
if validated := current_cfg.get(key):
obj = await number.new_number(
validated,
min_value=validated["min_value"],
max_value=validated["max_value"],
step=validated["step"],
)
await cg.register_parented(obj, parent)
cg.add(parent.set_reference_current(i, obj))
@@ -0,0 +1,16 @@
#pragma once
#include "esphome/core/component.h"
#include "esphome/components/atm90e36/atm90e36.h"
#include "esphome/components/number/number.h"
namespace esphome {
namespace atm90e36 {
class ATM90E36Number : public number::Number, public Parented<ATM90E36Component> {
public:
void control(float value) override { this->publish_state(value); }
};
} // namespace atm90e32
} // namespace esphome
+265
View File
@@ -0,0 +1,265 @@
import esphome.codegen as cg
from esphome.components import sensor, spi
import esphome.config_validation as cv
from esphome.const import (
CONF_APPARENT_POWER,
CONF_CURRENT,
CONF_FORWARD_ACTIVE_ENERGY,
CONF_FREQUENCY,
CONF_ID,
CONF_LINE_FREQUENCY,
CONF_PHASE_A,
CONF_PHASE_ANGLE,
CONF_PHASE_B,
CONF_PHASE_C,
CONF_POWER,
CONF_POWER_FACTOR,
CONF_REACTIVE_POWER,
CONF_REVERSE_ACTIVE_ENERGY,
CONF_VOLTAGE,
DEVICE_CLASS_APPARENT_POWER,
DEVICE_CLASS_CURRENT,
DEVICE_CLASS_ENERGY,
DEVICE_CLASS_POWER,
DEVICE_CLASS_POWER_FACTOR,
DEVICE_CLASS_REACTIVE_POWER,
DEVICE_CLASS_TEMPERATURE,
DEVICE_CLASS_VOLTAGE,
ENTITY_CATEGORY_DIAGNOSTIC,
ICON_CURRENT_AC,
ICON_LIGHTBULB,
STATE_CLASS_MEASUREMENT,
STATE_CLASS_TOTAL_INCREASING,
UNIT_AMPERE,
UNIT_CELSIUS,
UNIT_DEGREES,
UNIT_HERTZ,
UNIT_VOLT,
UNIT_VOLT_AMPS,
UNIT_VOLT_AMPS_REACTIVE,
UNIT_WATT,
UNIT_WATT_HOURS,
)
from . import atm90e36_ns
CONF_CHIP_TEMPERATURE = "chip_temperature"
CONF_GAIN_CURRNT = "gain_current"
CONF_GAIN_DPGA = "gain_dpga"
CONF_GAIN_CURRENT = "gain_current"
CONF_CURRENT_PHASES = "current_phases"
CONF_GAIN_VOLTAGE = "gain_voltage"
CONF_GAIN_CT = "gain_ct"
CONF_OFFSET_VOLTAGE = "offset_voltage"
CONF_OFFSET_CURRENT = "offset_current"
CONF_OFFSET_ACTIVE_POWER = "offset_active_power"
CONF_OFFSET_REACTIVE_POWER = "offset_reactive_power"
CONF_HARMONIC_POWER = "harmonic_power"
CONF_PEAK_CURRENT = "peak_current"
CONF_PEAK_CURRENT_SIGNED = "peak_current_signed"
CONF_ENABLE_OFFSET_CALIBRATION = "enable_offset_calibration"
CONF_ENABLE_GAIN_CALIBRATION = "enable_gain_calibration"
CONF_PHASE_STATUS = "phase_status"
CONF_FREQUENCY_STATUS = "frequency_status"
UNIT_DEG = "degrees"
LINE_FREQS = {
"50HZ": 50,
"60HZ": 60,
}
CURRENT_PHASES = {
"2": 2,
"3": 3,
}
CURRENT_GAINS = {
"1X": 0x0,
"2X": 0x15,
"4X": 0x2A,
}
VOLTAGE_GAINS = {
"1X": 0x0,
"2X": 0x15,
"4X": 0x2A,
}
DPGA_GAINS = {
"1X": 0x0,
"2X": 0x1,
"4X": 0x2,
"8X": 0x3
}
ATM90E36Component = atm90e36_ns.class_(
"ATM90E36Component", cg.PollingComponent, spi.SPIDevice
)
ATM90E36_PHASE_SCHEMA = cv.Schema(
{
cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_VOLTAGE,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CURRENT): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=0,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_REACTIVE_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS_REACTIVE,
icon=ICON_LIGHTBULB,
accuracy_decimals=2,
device_class=DEVICE_CLASS_REACTIVE_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_APPARENT_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_VOLT_AMPS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_APPARENT_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_POWER_FACTOR): sensor.sensor_schema(
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER_FACTOR,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_FORWARD_ACTIVE_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_REVERSE_ACTIVE_ENERGY): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT_HOURS,
accuracy_decimals=2,
device_class=DEVICE_CLASS_ENERGY,
state_class=STATE_CLASS_TOTAL_INCREASING,
),
cv.Optional(CONF_PHASE_ANGLE): sensor.sensor_schema(
unit_of_measurement=UNIT_DEGREES,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_HARMONIC_POWER): sensor.sensor_schema(
unit_of_measurement=UNIT_WATT,
accuracy_decimals=2,
device_class=DEVICE_CLASS_POWER,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_PEAK_CURRENT): sensor.sensor_schema(
unit_of_measurement=UNIT_AMPERE,
accuracy_decimals=2,
device_class=DEVICE_CLASS_CURRENT,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_GAIN_VOLTAGE, default=7305): cv.uint16_t,
cv.Optional(CONF_GAIN_CT, default=27961): cv.uint16_t,
cv.Optional(CONF_OFFSET_VOLTAGE, default=0): cv.int_,
cv.Optional(CONF_OFFSET_CURRENT, default=0): cv.int_,
cv.Optional(CONF_OFFSET_ACTIVE_POWER, default=0): cv.int_,
cv.Optional(CONF_OFFSET_REACTIVE_POWER, default=0): cv.int_,
}
)
CONFIG_SCHEMA = (
cv.Schema(
{
cv.GenerateID(): cv.declare_id(ATM90E36Component),
cv.Optional(CONF_PHASE_A): ATM90E36_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_B): ATM90E36_PHASE_SCHEMA,
cv.Optional(CONF_PHASE_C): ATM90E36_PHASE_SCHEMA,
cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(
unit_of_measurement=UNIT_HERTZ,
icon=ICON_CURRENT_AC,
accuracy_decimals=1,
state_class=STATE_CLASS_MEASUREMENT,
),
cv.Optional(CONF_CHIP_TEMPERATURE): sensor.sensor_schema(
unit_of_measurement=UNIT_CELSIUS,
accuracy_decimals=1,
device_class=DEVICE_CLASS_TEMPERATURE,
state_class=STATE_CLASS_MEASUREMENT,
entity_category=ENTITY_CATEGORY_DIAGNOSTIC,
),
cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True),
cv.Optional(CONF_CURRENT_PHASES, default="3"): cv.enum(
CURRENT_PHASES, upper=True
),
cv.Optional(CONF_GAIN_CURRENT, default="1X"): cv.enum(CURRENT_GAINS, upper=True),
cv.Optional(CONF_GAIN_VOLTAGE, default="1X"): cv.enum(VOLTAGE_GAINS, upper=True),
cv.Optional(CONF_GAIN_DPGA, default="1X"): cv.enum(DPGA_GAINS, upper=True),
cv.Optional(CONF_PEAK_CURRENT_SIGNED, default=False): cv.boolean,
cv.Optional(CONF_ENABLE_OFFSET_CALIBRATION, default=False): cv.boolean,
cv.Optional(CONF_ENABLE_GAIN_CALIBRATION, default=False): cv.boolean,
}
)
.extend(cv.polling_component_schema("60s"))
.extend(spi.spi_device_schema())
)
async def to_code(config):
var = cg.new_Pvariable(config[CONF_ID])
await cg.register_component(var, config)
await spi.register_spi_device(var, config)
for i, phase in enumerate([CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C]):
if phase not in config:
continue
conf = config[phase]
cg.add(var.set_volt_gain(i, conf[CONF_GAIN_VOLTAGE]))
cg.add(var.set_ct_gain(i, conf[CONF_GAIN_CT]))
if voltage_config := conf.get(CONF_VOLTAGE):
sens = await sensor.new_sensor(voltage_config)
cg.add(var.set_voltage_sensor(i, sens))
if current_config := conf.get(CONF_CURRENT):
sens = await sensor.new_sensor(current_config)
cg.add(var.set_current_sensor(i, sens))
if power_config := conf.get(CONF_POWER):
sens = await sensor.new_sensor(power_config)
cg.add(var.set_power_sensor(i, sens))
if reactive_power_config := conf.get(CONF_REACTIVE_POWER):
sens = await sensor.new_sensor(reactive_power_config)
cg.add(var.set_reactive_power_sensor(i, sens))
if apparent_power_config := conf.get(CONF_APPARENT_POWER):
sens = await sensor.new_sensor(apparent_power_config)
cg.add(var.set_apparent_power_sensor(i, sens))
if power_factor_config := conf.get(CONF_POWER_FACTOR):
sens = await sensor.new_sensor(power_factor_config)
cg.add(var.set_power_factor_sensor(i, sens))
if forward_active_energy_config := conf.get(CONF_FORWARD_ACTIVE_ENERGY):
sens = await sensor.new_sensor(forward_active_energy_config)
cg.add(var.set_forward_active_energy_sensor(i, sens))
if reverse_active_energy_config := conf.get(CONF_REVERSE_ACTIVE_ENERGY):
sens = await sensor.new_sensor(reverse_active_energy_config)
cg.add(var.set_reverse_active_energy_sensor(i, sens))
if phase_angle_config := conf.get(CONF_PHASE_ANGLE):
sens = await sensor.new_sensor(phase_angle_config)
cg.add(var.set_phase_angle_sensor(i, sens))
if harmonic_active_power_config := conf.get(CONF_HARMONIC_POWER):
sens = await sensor.new_sensor(harmonic_active_power_config)
cg.add(var.set_harmonic_active_power_sensor(i, sens))
if peak_current_config := conf.get(CONF_PEAK_CURRENT):
sens = await sensor.new_sensor(peak_current_config)
cg.add(var.set_peak_current_sensor(i, sens))
if frequency_config := config.get(CONF_FREQUENCY):
sens = await sensor.new_sensor(frequency_config)
cg.add(var.set_freq_sensor(sens))
if chip_temperature_config := config.get(CONF_CHIP_TEMPERATURE):
sens = await sensor.new_sensor(chip_temperature_config)
cg.add(var.set_chip_temperature_sensor(sens))
cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY]))
cg.add(var.set_current_phases(config[CONF_CURRENT_PHASES]))
cg.add(var.set_pga_current(config[CONF_GAIN_CURRENT]))
cg.add(var.set_pga_voltage(config[CONF_GAIN_VOLTAGE]))
cg.add(var.set_dpga_gain(config[CONF_GAIN_DPGA]))
cg.add(var.set_peak_current_signed(config[CONF_PEAK_CURRENT_SIGNED]))
@@ -0,0 +1,33 @@
import esphome.codegen as cg
from esphome.components import text_sensor
import esphome.config_validation as cv
from esphome.const import CONF_ID, CONF_PHASE_A, CONF_PHASE_B, CONF_PHASE_C
from ..sensor import ATM90E36Component
CONF_PHASE_STATUS = "phase_status"
CONF_FREQUENCY_STATUS = "frequency_status"
CONFIG_SCHEMA = cv.Schema(
{
cv.GenerateID(): cv.use_id(ATM90E36Component),
cv.Optional(CONF_PHASE_STATUS): text_sensor.text_sensor_schema(
icon="mdi:flash-alert"
),
cv.Optional(CONF_FREQUENCY_STATUS): text_sensor.text_sensor_schema(
icon="mdi:lightbulb-alert"
),
}
)
async def to_code(config):
parent = await cg.get_variable(config[CONF_ID])
if phase_cfg := config.get(CONF_PHASE_STATUS):
sens = await text_sensor.new_text_sensor(phase_cfg)
cg.add(parent.set_phase_status_text_sensor(sens))
if freq_status_config := config.get(CONF_FREQUENCY_STATUS):
sens = await text_sensor.new_text_sensor(freq_status_config)
cg.add(parent.set_freq_status_text_sensor(sens))