From db968bc6b0c4bdc3b857ef4116caecf7c95c3410 Mon Sep 17 00:00:00 2001 From: Andrew Thompson Date: Sun, 16 Jun 2019 22:00:15 +1200 Subject: [PATCH] Atmel M90E32AS Energy Metering IC. Found in CircuitSetup 2chan and 6chan energy meterss (#629) * Atmel M90E32AS Energy Metering IC. Found in CircuitSetup 2can and 6chan energy meters * fix style bugs * Update esphome/components/atm90e32/atm90e32.cpp Co-Authored-By: Otto Winter * Properly put atm90e32_reg in namespace * Use phase grouped config like ina3221 * Log why the component is marked failed, did not read back our register value * 32bit register reads are 2s compliment * Fix atm90e32 option name in test * clang-format changes from travis-ci * use new protected method names * Whitespace changes to please Travis * Update esphome/components/atm90e32/atm90e32.cpp Co-Authored-By: Otto Winter * Fix up type change for val_h/val_l * Remove conditions around values with defaults * Rename constants to match their value * Remove 2's complement check Co-authored-by: Andrew Thompson Co-authored-by: Otto Winter --- esphome/components/atm90e32/__init__.py | 0 esphome/components/atm90e32/atm90e32.cpp | 188 +++++++++++++++ esphome/components/atm90e32/atm90e32.h | 58 +++++ esphome/components/atm90e32/atm90e32_reg.h | 253 +++++++++++++++++++++ esphome/components/atm90e32/sensor.py | 72 ++++++ esphome/const.py | 1 + tests/test1.yaml | 29 +++ 7 files changed, 601 insertions(+) create mode 100644 esphome/components/atm90e32/__init__.py create mode 100644 esphome/components/atm90e32/atm90e32.cpp create mode 100644 esphome/components/atm90e32/atm90e32.h create mode 100644 esphome/components/atm90e32/atm90e32_reg.h create mode 100644 esphome/components/atm90e32/sensor.py diff --git a/esphome/components/atm90e32/__init__.py b/esphome/components/atm90e32/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/esphome/components/atm90e32/atm90e32.cpp b/esphome/components/atm90e32/atm90e32.cpp new file mode 100644 index 0000000000..283319994e --- /dev/null +++ b/esphome/components/atm90e32/atm90e32.cpp @@ -0,0 +1,188 @@ +#include "atm90e32.h" +#include "atm90e32_reg.h" +#include "esphome/core/log.h" + +namespace esphome { +namespace atm90e32 { + +static const char *TAG = "atm90e32"; + +void ATM90E32Component::update() { + if (this->read16_(ATM90E32_REGISTER_METEREN) != 1) { + this->status_set_warning(); + return; + } + + if (this->phase_[0].voltage_sensor_ != nullptr) { + this->phase_[0].voltage_sensor_->publish_state(this->get_line_voltage_a_()); + } + if (this->phase_[1].voltage_sensor_ != nullptr) { + this->phase_[1].voltage_sensor_->publish_state(this->get_line_voltage_b_()); + } + if (this->phase_[2].voltage_sensor_ != nullptr) { + this->phase_[2].voltage_sensor_->publish_state(this->get_line_voltage_c_()); + } + if (this->phase_[0].current_sensor_ != nullptr) { + this->phase_[0].current_sensor_->publish_state(this->get_line_current_a_()); + } + if (this->phase_[1].current_sensor_ != nullptr) { + this->phase_[1].current_sensor_->publish_state(this->get_line_current_b_()); + } + if (this->phase_[2].current_sensor_ != nullptr) { + this->phase_[2].current_sensor_->publish_state(this->get_line_current_c_()); + } + if (this->phase_[0].power_sensor_ != nullptr) { + this->phase_[0].power_sensor_->publish_state(this->get_active_power_a_()); + } + if (this->phase_[1].power_sensor_ != nullptr) { + this->phase_[1].power_sensor_->publish_state(this->get_active_power_b_()); + } + if (this->phase_[2].power_sensor_ != nullptr) { + this->phase_[2].power_sensor_->publish_state(this->get_active_power_c_()); + } + if (this->freq_sensor_ != nullptr) { + this->freq_sensor_->publish_state(this->get_frequency_()); + } + this->status_clear_warning(); +} + +void ATM90E32Component::setup() { + ESP_LOGCONFIG(TAG, "Setting up ATM90E32Component..."); + this->spi_setup(); + + uint16_t mmode0 = 0x185; + if (line_freq_ == 60) { + mmode0 |= 1 << 12; + } + + this->write16_(ATM90E32_REGISTER_SOFTRESET, 0x789A); // Perform soft reset + this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x55AA); // enable register config access + this->write16_(ATM90E32_REGISTER_METEREN, 0x0001); // Enable Metering + if (this->read16_(ATM90E32_REGISTER_LASTSPIDATA) != 0x0001) { + ESP_LOGW(TAG, "Could not initialize ATM90E32 IC, check SPI settings"); + this->mark_failed(); + return; + } + + this->write16_(ATM90E32_REGISTER_ZXCONFIG, 0x0A55); // ZX2, ZX1, ZX0 pin config + this->write16_(ATM90E32_REGISTER_MMODE0, mmode0); // Mode Config (frequency set in main program) + this->write16_(ATM90E32_REGISTER_MMODE1, pga_gain_); // PGA Gain Configuration for Current Channels + this->write16_(ATM90E32_REGISTER_PSTARTTH, 0x0AFC); // Active Startup Power Threshold = 50% + this->write16_(ATM90E32_REGISTER_QSTARTTH, 0x0AEC); // Reactive Startup Power Threshold = 50% + this->write16_(ATM90E32_REGISTER_PPHASETH, 0x00BC); // Active Phase Threshold = 10% + this->write16_(ATM90E32_REGISTER_UGAINA, this->phase_[0].volt_gain_); // A Voltage rms gain + this->write16_(ATM90E32_REGISTER_IGAINA, this->phase_[0].ct_gain_); // A line current gain + this->write16_(ATM90E32_REGISTER_UGAINB, this->phase_[1].volt_gain_); // B Voltage rms gain + this->write16_(ATM90E32_REGISTER_IGAINB, this->phase_[1].ct_gain_); // B line current gain + this->write16_(ATM90E32_REGISTER_UGAINC, this->phase_[2].volt_gain_); // C Voltage rms gain + this->write16_(ATM90E32_REGISTER_IGAINC, this->phase_[2].ct_gain_); // C line current gain + this->write16_(ATM90E32_REGISTER_CFGREGACCEN, 0x0000); // end configuration +} + +void ATM90E32Component::dump_config() { + ESP_LOGCONFIG("", "ATM90E32:"); + LOG_PIN(" CS Pin: ", this->cs_); + if (this->is_failed()) { + ESP_LOGE(TAG, "Communication with ATM90E32 failed!"); + } + LOG_UPDATE_INTERVAL(this); + LOG_SENSOR(" ", "Voltage A", this->phase_[0].voltage_sensor_); + LOG_SENSOR(" ", "Current A", this->phase_[0].current_sensor_); + LOG_SENSOR(" ", "Power A", this->phase_[0].power_sensor_); + LOG_SENSOR(" ", "Voltage B", this->phase_[1].voltage_sensor_); + LOG_SENSOR(" ", "Current B", this->phase_[1].current_sensor_); + LOG_SENSOR(" ", "Power B", this->phase_[1].power_sensor_); + LOG_SENSOR(" ", "Voltage C", this->phase_[2].voltage_sensor_); + LOG_SENSOR(" ", "Current C", this->phase_[2].current_sensor_); + LOG_SENSOR(" ", "Power C", this->phase_[2].power_sensor_); + LOG_SENSOR(" ", "Frequency", this->freq_sensor_) +} +float ATM90E32Component::get_setup_priority() const { return setup_priority::DATA; } + +uint16_t ATM90E32Component::read16_(uint16_t a_register) { + uint8_t addrh = (1 << 7) | ((a_register >> 8) & 0x03); + uint8_t addrl = (a_register & 0xFF); + uint8_t data[2]; + uint16_t output; + + this->enable(); + delayMicroseconds(10); + this->write_byte(addrh); + this->write_byte(addrl); + delayMicroseconds(4); + this->read_array(data, 2); + this->disable(); + + output = (uint16_t(data[0] & 0xFF) << 8) | (data[1] & 0xFF); + ESP_LOGVV(TAG, "read16_ 0x%04X output 0x%04X", a_register, output); + return output; +} + +int ATM90E32Component::read32_(uint16_t addr_h, uint16_t addr_l) { + uint16_t val_h = this->read16_(addr_h); + uint16_t val_l = this->read16_(addr_l); + int32_t val = (val_h << 16) | val_l; + + ESP_LOGVV(TAG, "read32_ addr_h 0x%04X val_h 0x%04X addr_l 0x%04X val_l 0x%04X = %d", addr_h, val_h, addr_l, val_l, + val); + + return val; +} + +void ATM90E32Component::write16_(uint16_t a_register, uint16_t val) { + uint8_t addrh = (a_register >> 8) & 0x03; + uint8_t addrl = (a_register & 0xFF); + + ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val); + this->enable(); + delayMicroseconds(10); + this->write_byte(addrh); + this->write_byte(addrl); + delayMicroseconds(4); + this->write_byte((val >> 8) & 0xff); + this->write_byte(val & 0xFF); + this->disable(); +} + +float ATM90E32Component::get_line_voltage_a_() { + uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSA); + return (float) voltage / 100; +} +float ATM90E32Component::get_line_voltage_b_() { + uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSB); + return (float) voltage / 100; +} +float ATM90E32Component::get_line_voltage_c_() { + uint16_t voltage = this->read16_(ATM90E32_REGISTER_URMSC); + return (float) voltage / 100; +} +float ATM90E32Component::get_line_current_a_() { + uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSA); + return (float) current / 1000; +} +float ATM90E32Component::get_line_current_b_() { + uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSB); + return (float) current / 1000; +} +float ATM90E32Component::get_line_current_c_() { + uint16_t current = this->read16_(ATM90E32_REGISTER_IRMSC); + return (float) current / 1000; +} +float ATM90E32Component::get_active_power_a_() { + int val = this->read32_(ATM90E32_REGISTER_PMEANA, ATM90E32_REGISTER_PMEANALSB); + return val * 0.00032f; +} +float ATM90E32Component::get_active_power_b_() { + int val = this->read32_(ATM90E32_REGISTER_PMEANB, ATM90E32_REGISTER_PMEANBLSB); + return val * 0.00032f; +} +float ATM90E32Component::get_active_power_c_() { + int val = this->read32_(ATM90E32_REGISTER_PMEANC, ATM90E32_REGISTER_PMEANCLSB); + return val * 0.00032f; +} +float ATM90E32Component::get_frequency_() { + uint16_t freq = this->read16_(ATM90E32_REGISTER_FREQ); + return (float) freq / 100; +} +} // namespace atm90e32 +} // namespace esphome diff --git a/esphome/components/atm90e32/atm90e32.h b/esphome/components/atm90e32/atm90e32.h new file mode 100644 index 0000000000..4dd2bd5784 --- /dev/null +++ b/esphome/components/atm90e32/atm90e32.h @@ -0,0 +1,58 @@ +#pragma once + +#include "esphome/core/component.h" +#include "esphome/components/sensor/sensor.h" +#include "esphome/components/spi/spi.h" + +namespace esphome { +namespace atm90e32 { + +class ATM90E32Component : public PollingComponent, + public spi::SPIDevice { + public: + 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_volt_gain(int phase, uint16_t gain) { this->phase_[phase].volt_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_line_freq(int freq) { line_freq_ = freq; } + void set_pga_gain(uint16_t gain) { pga_gain_ = gain; } + + protected: + uint16_t read16_(uint16_t a_register); + int read32_(uint16_t addr_h, uint16_t addr_l); + void write16_(uint16_t a_register, uint16_t val); + + float get_line_voltage_a_(); + float get_line_voltage_b_(); + float get_line_voltage_c_(); + float get_line_current_a_(); + float get_line_current_b_(); + float get_line_current_c_(); + float get_active_power_a_(); + float get_active_power_b_(); + float get_active_power_c_(); + float get_frequency_(); + + struct ATM90E32Phase { + uint16_t volt_gain_{41820}; + uint16_t ct_gain_{25498}; + sensor::Sensor *voltage_sensor_{nullptr}; + sensor::Sensor *current_sensor_{nullptr}; + sensor::Sensor *power_sensor_{nullptr}; + } phase_[3]; + sensor::Sensor *freq_sensor_{nullptr}; + uint16_t pga_gain_{0x15}; + int line_freq_{60}; +}; + +} // namespace atm90e32 +} // namespace esphome diff --git a/esphome/components/atm90e32/atm90e32_reg.h b/esphome/components/atm90e32/atm90e32_reg.h new file mode 100644 index 0000000000..ca3715a2a8 --- /dev/null +++ b/esphome/components/atm90e32/atm90e32_reg.h @@ -0,0 +1,253 @@ +#pragma once + +namespace esphome { +namespace atm90e32 { + +/* STATUS REGISTERS */ +static const uint16_t ATM90E32_REGISTER_METEREN = 0x00; // Metering Enable +static const uint16_t ATM90E32_REGISTER_CHANNELMAPI = 0x01; // Current Channel Mapping Configuration +static const uint16_t ATM90E32_REGISTER_CHANNELMAPU = 0x02; // Voltage Channel Mapping Configuration +static const uint16_t ATM90E32_REGISTER_SAGPEAKDETCFG = 0x05; // Sag and Peak Detector Period Configuration +static const uint16_t ATM90E32_REGISTER_OVTH = 0x06; // Over Voltage Threshold +static const uint16_t ATM90E32_REGISTER_ZXCONFIG = 0x07; // Zero-Crossing Config +static const uint16_t ATM90E32_REGISTER_SAGTH = 0x08; // Voltage Sag Th +static const uint16_t ATM90E32_REGISTER_PHASELOSSTH = 0x09; // Voltage Phase Losing Th +static const uint16_t ATM90E32_REGISTER_INWARNTH = 0x0A; // Neutral Current (Calculated) Warning Threshold +static const uint16_t ATM90E32_REGISTER_OITH = 0x0B; // Over Current Threshold +static const uint16_t ATM90E32_REGISTER_FREQLOTH = 0x0C; // Low Threshold for Frequency Detection +static const uint16_t ATM90E32_REGISTER_FREQHITH = 0x0D; // High Threshold for Frequency Detection +static const uint16_t ATM90E32_REGISTER_PMPWRCTRL = 0x0E; // Partial Measurement Mode Power Control +static const uint16_t ATM90E32_REGISTER_IRQ0MERGECFG = 0x0F; // IRQ0 Merge Configuration + +/* EMM STATUS REGISTERS */ +static const uint16_t ATM90E32_REGISTER_SOFTRESET = 0x70; // Software Reset +static const uint16_t ATM90E32_REGISTER_EMMSTATE0 = 0x71; // EMM State 0 +static const uint16_t ATM90E32_REGISTER_EMMSTATE1 = 0x72; // EMM State 1 +static const uint16_t ATM90E32_REGISTER_EMMINTSTATE0 = 0x73; // EMM Interrupt Status 0 +static const uint16_t ATM90E32_REGISTER_EMMINTSTATE1 = 0x74; // EMM Interrupt Status 1 +static const uint16_t ATM90E32_REGISTER_EMMINTEN0 = 0x75; // EMM Interrupt Enable 0 +static const uint16_t ATM90E32_REGISTER_EMMINTEN1 = 0x76; // EMM Interrupt Enable 1 +static const uint16_t ATM90E32_REGISTER_LASTSPIDATA = 0x78; // Last Read/Write SPI Value +static const uint16_t ATM90E32_REGISTER_CRCERRSTATUS = 0x79; // CRC Error Status +static const uint16_t ATM90E32_REGISTER_CRCDIGEST = 0x7A; // CRC Digest +static const uint16_t ATM90E32_REGISTER_CFGREGACCEN = 0x7F; // Configure Register Access Enable +static const uint16_t ATM90E32_STATUS_S0_OIPHASEAST = 1 << 15; // Over current on phase A +static const uint16_t ATM90E32_STATUS_S0_OIPHASEBST = 1 << 14; // Over current on phase B +static const uint16_t ATM90E32_STATUS_S0_OIPHASECST = 1 << 13; // Over current on phase C +static const uint16_t ATM90E32_STATUS_S0_OVPHASEAST = 1 << 12; // Over voltage on phase A +static const uint16_t ATM90E32_STATUS_S0_OVPHASEBST = 1 << 11; // Over voltage on phase B +static const uint16_t ATM90E32_STATUS_S0_OVPHASECST = 1 << 10; // Over voltage on phase C +static const uint16_t ATM90E32_STATUS_S0_UREVWNST = 1 << 9; // Voltage Phase Sequence Error status +static const uint16_t ATM90E32_STATUS_S0_IREVWNST = 1 << 8; // Current Phase Sequence Error status +static const uint16_t ATM90E32_STATUS_S0_INOV0ST = 1 << 7; // Calculated N line current greater tha INWarnTh reg +static const uint16_t ATM90E32_STATUS_S0_TQNOLOADST = 1 << 6; // All phase sum reactive power no-load condition status +static const uint16_t ATM90E32_STATUS_S0_TPNOLOADST = 1 << 5; // All phase sum active power no-load condition status +static const uint16_t ATM90E32_STATUS_S0_TASNOLOADST = 1 << 4; // All phase sum apparent power no-load status +static const uint16_t ATM90E32_STATUS_S0_CF1REVST = 1 << 3; // Energy for CF1 Forward/Reverse status +static const uint16_t ATM90E32_STATUS_S0_CF2REVST = 1 << 2; // Energy for CF2 Forward/Reverse status +static const uint16_t ATM90E32_STATUS_S0_CF3REVST = 1 << 1; // Energy for CF3 Forward/Reverse status +static const uint16_t ATM90E32_STATUS_S0_CF4REVST = 1 << 0; // Energy for CF4 Forward/Reverse status +static const uint16_t ATM90E32_STATUS_S1_FREQHIST = 1 << 15; // Frequency is greater than the high threshold +static const uint16_t ATM90E32_STATUS_S1_SAGPHASEAST = 1 << 14; // Voltage sag on phase A +static const uint16_t ATM90E32_STATUS_S1_SAGPHASEBST = 1 << 13; // Voltage sag on phase B +static const uint16_t ATM90E32_STATUS_S1_SAGPHASECST = 1 << 12; // Voltage sag on phase C +static const uint16_t ATM90E32_STATUS_S1_FREQLOST = 1 << 11; // Frequency is lesser than the low threshold +static const uint16_t ATM90E32_STATUS_S1_PHASELOSSAST = 1 << 10; // Phase loss in Phase A +static const uint16_t ATM90E32_STATUS_S1_PHASELOSSBST = 1 << 9; // Phase loss in Phase B +static const uint16_t ATM90E32_STATUS_S1_PHASELOSSCST = 1 << 8; // Phase loss in Phase C +static const uint16_t ATM90E32_STATUS_S1_QEREGTPST = 1 << 7; // ReActive Energy register of sum (T) Positive Status +static const uint16_t ATM90E32_STATUS_S1_QEREGAPST = 1 << 6; // ReActive Energy register of Channel A Positive Status +static const uint16_t ATM90E32_STATUS_S1_QEREGBPST = 1 << 5; // ReActive Energy register of Channel B Positive Status +static const uint16_t ATM90E32_STATUS_S1_QEREGCPST = 1 << 4; // ReActive Energy register of Channel C Positive Status +static const uint16_t ATM90E32_STATUS_S1_PEREGTPST = 1 << 3; // Active Energy register of sum (T) Positive Status +static const uint16_t ATM90E32_STATUS_S1_PEREGAPST = 1 << 2; // Active Energy register of Channel A Positive Status +static const uint16_t ATM90E32_STATUS_S1_PEREGBPST = 1 << 1; // Active Energy register of Channel B Positive Status +static const uint16_t ATM90E32_STATUS_S1_PEREGCPST = 1 << 0; // Active Energy register of Channel C Positive Status + +/* LOW POWER MODE REGISTERS - NOT USED */ +static const uint16_t ATM90E32_REGISTER_DETECTCTRL = 0x10; +static const uint16_t ATM90E32_REGISTER_DETECTTH1 = 0x11; +static const uint16_t ATM90E32_REGISTER_DETECTTH2 = 0x12; +static const uint16_t ATM90E32_REGISTER_DETECTTH3 = 0x13; +static const uint16_t ATM90E32_REGISTER_PMOFFSETA = 0x14; +static const uint16_t ATM90E32_REGISTER_PMOFFSETB = 0x15; +static const uint16_t ATM90E32_REGISTER_PMOFFSETC = 0x16; +static const uint16_t ATM90E32_REGISTER_PMPGA = 0x17; +static const uint16_t ATM90E32_REGISTER_PMIRMSA = 0x18; +static const uint16_t ATM90E32_REGISTER_PMIRMSB = 0x19; +static const uint16_t ATM90E32_REGISTER_PMIRMSC = 0x1A; +static const uint16_t ATM90E32_REGISTER_PMCONFIG = 0x10B; +static const uint16_t ATM90E32_REGISTER_PMAVGSAMPLES = 0x1C; +static const uint16_t ATM90E32_REGISTER_PMIRMSLSB = 0x1D; + +/* CONFIGURATION REGISTERS */ +static const uint16_t ATM90E32_REGISTER_PLCONSTH = 0x31; // High Word of PL_Constant +static const uint16_t ATM90E32_REGISTER_PLCONSTL = 0x32; // Low Word of PL_Constant +static const uint16_t ATM90E32_REGISTER_MMODE0 = 0x33; // Metering Mode Config +static const uint16_t ATM90E32_REGISTER_MMODE1 = 0x34; // PGA Gain Configuration for Current Channels +static const uint16_t ATM90E32_REGISTER_PSTARTTH = 0x35; // Startup Power Th (P) +static const uint16_t ATM90E32_REGISTER_QSTARTTH = 0x36; // Startup Power Th (Q) +static const uint16_t ATM90E32_REGISTER_SSTARTTH = 0x37; // Startup Power Th (S) +static const uint16_t ATM90E32_REGISTER_PPHASETH = 0x38; // Startup Power Accum Th (P) +static const uint16_t ATM90E32_REGISTER_QPHASETH = 0x39; // Startup Power Accum Th (Q) +static const uint16_t ATM90E32_REGISTER_SPHASETH = 0x3A; // Startup Power Accum Th (S) + +/* CALIBRATION REGISTERS */ +static const uint16_t ATM90E32_REGISTER_POFFSETA = 0x41; // A Line Power Offset (P) +static const uint16_t ATM90E32_REGISTER_QOFFSETA = 0x42; // A Line Power Offset (Q) +static const uint16_t ATM90E32_REGISTER_POFFSETB = 0x43; // B Line Power Offset (P) +static const uint16_t ATM90E32_REGISTER_QOFFSETB = 0x44; // B Line Power Offset (Q) +static const uint16_t ATM90E32_REGISTER_POFFSETC = 0x45; // C Line Power Offset (P) +static const uint16_t ATM90E32_REGISTER_QOFFSETC = 0x46; // C Line Power Offset (Q) +static const uint16_t ATM90E32_REGISTER_PQGAINA = 0x47; // A Line Calibration Gain +static const uint16_t ATM90E32_REGISTER_PHIA = 0x48; // A Line Calibration Angle +static const uint16_t ATM90E32_REGISTER_PQGAINB = 0x49; // B Line Calibration Gain +static const uint16_t ATM90E32_REGISTER_PHIB = 0x4A; // B Line Calibration Angle +static const uint16_t ATM90E32_REGISTER_PQGAINC = 0x4B; // C Line Calibration Gain +static const uint16_t ATM90E32_REGISTER_PHIC = 0x4C; // C Line Calibration Angle + +/* FUNDAMENTAL/HARMONIC ENERGY CALIBRATION REGISTERS */ +static const uint16_t ATM90E32_REGISTER_POFFSETAF = 0x51; // A Fund Power Offset (P) +static const uint16_t ATM90E32_REGISTER_POFFSETBF = 0x52; // B Fund Power Offset (P) +static const uint16_t ATM90E32_REGISTER_POFFSETCF = 0x53; // C Fund Power Offset (P) +static const uint16_t ATM90E32_REGISTER_PGAINAF = 0x54; // A Fund Power Gain (P) +static const uint16_t ATM90E32_REGISTER_PGAINBF = 0x55; // B Fund Power Gain (P) +static const uint16_t ATM90E32_REGISTER_PGAINCF = 0x56; // C Fund Power Gain (P) + +/* MEASUREMENT CALIBRATION REGISTERS */ +static const uint16_t ATM90E32_REGISTER_UGAINA = 0x61; // A Voltage RMS Gain +static const uint16_t ATM90E32_REGISTER_IGAINA = 0x62; // A Current RMS Gain +static const uint16_t ATM90E32_REGISTER_UOFFSETA = 0x63; // A Voltage Offset +static const uint16_t ATM90E32_REGISTER_IOFFSETA = 0x64; // A Current Offset +static const uint16_t ATM90E32_REGISTER_UGAINB = 0x65; // B Voltage RMS Gain +static const uint16_t ATM90E32_REGISTER_IGAINB = 0x66; // B Current RMS Gain +static const uint16_t ATM90E32_REGISTER_UOFFSETB = 0x67; // B Voltage Offset +static const uint16_t ATM90E32_REGISTER_IOFFSETB = 0x68; // B Current Offset +static const uint16_t ATM90E32_REGISTER_UGAINC = 0x69; // C Voltage RMS Gain +static const uint16_t ATM90E32_REGISTER_IGAINC = 0x6A; // C Current RMS Gain +static const uint16_t ATM90E32_REGISTER_UOFFSETC = 0x6B; // C Voltage Offset +static const uint16_t ATM90E32_REGISTER_IOFFSETC = 0x6C; // C Current Offset +static const uint16_t ATM90E32_REGISTER_IOFFSETN = 0x6E; // N Current Offset + +/* ENERGY REGISTERS */ +static const uint16_t ATM90E32_REGISTER_APENERGYT = 0x80; // Total Forward Active +static const uint16_t ATM90E32_REGISTER_APENERGYA = 0x81; // A Forward Active +static const uint16_t ATM90E32_REGISTER_APENERGYB = 0x82; // B Forward Active +static const uint16_t ATM90E32_REGISTER_APENERGYC = 0x83; // C Forward Active +static const uint16_t ATM90E32_REGISTER_ANENERGYT = 0x84; // Total Reverse Active +static const uint16_t ATM90E32_REGISTER_ANENERGYA = 0x85; // A Reverse Active +static const uint16_t ATM90E32_REGISTER_ANENERGYB = 0x86; // B Reverse Active +static const uint16_t ATM90E32_REGISTER_ANENERGYC = 0x87; // C Reverse Active +static const uint16_t ATM90E32_REGISTER_RPENERGYT = 0x88; // Total Forward Reactive +static const uint16_t ATM90E32_REGISTER_RPENERGYA = 0x89; // A Forward Reactive +static const uint16_t ATM90E32_REGISTER_RPENERGYB = 0x8A; // B Forward Reactive +static const uint16_t ATM90E32_REGISTER_RPENERGYC = 0x8B; // C Forward Reactive +static const uint16_t ATM90E32_REGISTER_RNENERGYT = 0x8C; // Total Reverse Reactive +static const uint16_t ATM90E32_REGISTER_RNENERGYA = 0x8D; // A Reverse Reactive +static const uint16_t ATM90E32_REGISTER_RNENERGYB = 0x8E; // B Reverse Reactive +static const uint16_t ATM90E32_REGISTER_RNENERGYC = 0x8F; // C Reverse Reactive + +static const uint16_t ATM90E32_REGISTER_SAENERGYT = 0x90; // Total Apparent Energy +static const uint16_t ATM90E32_REGISTER_SENERGYA = 0x91; // A Apparent Energy +static const uint16_t ATM90E32_REGISTER_SENERGYB = 0x92; // B Apparent Energy +static const uint16_t ATM90E32_REGISTER_SENERGYC = 0x93; // C Apparent Energy + +/* FUNDAMENTAL / HARMONIC ENERGY REGISTERS */ +static const uint16_t ATM90E32_REGISTER_APENERGYTF = 0xA0; // Total Forward Fund. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYAF = 0xA1; // A Forward Fund. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYBF = 0xA2; // B Forward Fund. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYCF = 0xA3; // C Forward Fund. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYTF = 0xA4; // Total Reverse Fund Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYAF = 0xA5; // A Reverse Fund. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYBF = 0xA6; // B Reverse Fund. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYCF = 0xA7; // C Reverse Fund. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYTH = 0xA8; // Total Forward Harm. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYAH = 0xA9; // A Forward Harm. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYBH = 0xAA; // B Forward Harm. Energy +static const uint16_t ATM90E32_REGISTER_APENERGYCH = 0xAB; // C Forward Harm. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYTH = 0xAC; // Total Reverse Harm. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYAH = 0xAD; // A Reverse Harm. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYBH = 0xAE; // B Reverse Harm. Energy +static const uint16_t ATM90E32_REGISTER_ANENERGYCH = 0xAF; // C Reverse Harm. Energy + +/* POWER & P.F. REGISTERS */ +static const uint16_t ATM90E32_REGISTER_PMEANT = 0xB0; // Total Mean Power (P) +static const uint16_t ATM90E32_REGISTER_PMEANA = 0xB1; // A Mean Power (P) +static const uint16_t ATM90E32_REGISTER_PMEANB = 0xB2; // B Mean Power (P) +static const uint16_t ATM90E32_REGISTER_PMEANC = 0xB3; // C Mean Power (P) +static const uint16_t ATM90E32_REGISTER_QMEANT = 0xB4; // Total Mean Power (Q) +static const uint16_t ATM90E32_REGISTER_QMEANA = 0xB5; // A Mean Power (Q) +static const uint16_t ATM90E32_REGISTER_QMEANB = 0xB6; // B Mean Power (Q) +static const uint16_t ATM90E32_REGISTER_QMEANC = 0xB7; // C Mean Power (Q) +static const uint16_t ATM90E32_REGISTER_SMEANT = 0xB8; // Total Mean Power (S) +static const uint16_t ATM90E32_REGISTER_SMEANA = 0xB9; // A Mean Power (S) +static const uint16_t ATM90E32_REGISTER_SMEANB = 0xBA; // B Mean Power (S) +static const uint16_t ATM90E32_REGISTER_SMEANC = 0xBB; // C Mean Power (S) +static const uint16_t ATM90E32_REGISTER_PFMEANT = 0xBC; // Mean Power Factor +static const uint16_t ATM90E32_REGISTER_PFMEANA = 0xBD; // A Power Factor +static const uint16_t ATM90E32_REGISTER_PFMEANB = 0xBE; // B Power Factor +static const uint16_t ATM90E32_REGISTER_PFMEANC = 0xBF; // C Power Factor + +static const uint16_t ATM90E32_REGISTER_PMEANTLSB = 0xC0; // Lower Word (Tot. Act. Power) +static const uint16_t ATM90E32_REGISTER_PMEANALSB = 0xC1; // Lower Word (A Act. Power) +static const uint16_t ATM90E32_REGISTER_PMEANBLSB = 0xC2; // Lower Word (B Act. Power) +static const uint16_t ATM90E32_REGISTER_PMEANCLSB = 0xC3; // Lower Word (C Act. Power) +static const uint16_t ATM90E32_REGISTER_QMEANTLSB = 0xC4; // Lower Word (Tot. React. Power) +static const uint16_t ATM90E32_REGISTER_QMEANALSB = 0xC5; // Lower Word (A React. Power) +static const uint16_t ATM90E32_REGISTER_QMEANBLSB = 0xC6; // Lower Word (B React. Power) +static const uint16_t ATM90E32_REGISTER_QMEANCLSB = 0xC7; // Lower Word (C React. Power) +static const uint16_t ATM90E32_REGISTER_SAMEANTLSB = 0xC8; // Lower Word (Tot. App. Power) +static const uint16_t ATM90E32_REGISTER_SMEANALSB = 0xC9; // Lower Word (A App. Power) +static const uint16_t ATM90E32_REGISTER_SMEANBLSB = 0xCA; // Lower Word (B App. Power) +static const uint16_t ATM90E32_REGISTER_SMEANCLSB = 0xCB; // Lower Word (C App. Power) + +/* FUND/HARM POWER & V/I RMS REGISTERS */ +static const uint16_t ATM90E32_REGISTER_PMEANTF = 0xD0; // Total Active Fund. Power +static const uint16_t ATM90E32_REGISTER_PMEANAF = 0xD1; // A Active Fund. Power +static const uint16_t ATM90E32_REGISTER_PMEANBF = 0xD2; // B Active Fund. Power +static const uint16_t ATM90E32_REGISTER_PMEANCF = 0xD3; // C Active Fund. Power +static const uint16_t ATM90E32_REGISTER_PMEANTH = 0xD4; // Total Active Harm. Power +static const uint16_t ATM90E32_REGISTER_PMEANAH = 0xD5; // A Active Harm. Power +static const uint16_t ATM90E32_REGISTER_PMEANBH = 0xD6; // B Active Harm. Power +static const uint16_t ATM90E32_REGISTER_PMEANCH = 0xD7; // C Active Harm. Power +static const uint16_t ATM90E32_REGISTER_URMSA = 0xD9; // A RMS Voltage +static const uint16_t ATM90E32_REGISTER_URMSB = 0xDA; // B RMS Voltage +static const uint16_t ATM90E32_REGISTER_URMSC = 0xDB; // C RMS Voltage +static const uint16_t ATM90E32_REGISTER_IRMSA = 0xDD; // A RMS Current +static const uint16_t ATM90E32_REGISTER_IRMSB = 0xDE; // B RMS Current +static const uint16_t ATM90E32_REGISTER_IRMSC = 0xDF; // C RMS Current +static const uint16_t ATM90E32_REGISTER_IRMSN = 0xD8; // Calculated N RMS Current + +static const uint16_t ATM90E32_REGISTER_PMEANTFLSB = 0xE0; // Lower Word (Tot. Act. Fund. Power) +static const uint16_t ATM90E32_REGISTER_PMEANAFLSB = 0xE1; // Lower Word (A Act. Fund. Power) +static const uint16_t ATM90E32_REGISTER_PMEANBFLSB = 0xE2; // Lower Word (B Act. Fund. Power) +static const uint16_t ATM90E32_REGISTER_PMEANCFLSB = 0xE3; // Lower Word (C Act. Fund. Power) +static const uint16_t ATM90E32_REGISTER_PMEANTHLSB = 0xE4; // Lower Word (Tot. Act. Harm. Power) +static const uint16_t ATM90E32_REGISTER_PMEANAHLSB = 0xE5; // Lower Word (A Act. Harm. Power) +static const uint16_t ATM90E32_REGISTER_PMEANBHLSB = 0xE6; // Lower Word (B Act. Harm. Power) +static const uint16_t ATM90E32_REGISTER_PMEANCHLSB = 0xE7; // Lower Word (C Act. Harm. Power) +static const uint16_t ATM90E32_REGISTER_URMSALSB = 0xE9; // Lower Word (A RMS Voltage) +static const uint16_t ATM90E32_REGISTER_URMSBLSB = 0xEA; // Lower Word (B RMS Voltage) +static const uint16_t ATM90E32_REGISTER_URMSCLSB = 0xEB; // Lower Word (C RMS Voltage) +static const uint16_t ATM90E32_REGISTER_IRMSALSB = 0xED; // Lower Word (A RMS Current) +static const uint16_t ATM90E32_REGISTER_IRMSBLSB = 0xEE; // Lower Word (B RMS Current) +static const uint16_t ATM90E32_REGISTER_IRMSCLSB = 0xEF; // Lower Word (C RMS Current) + +/* THD, FREQUENCY, ANGLE & TEMPTEMP REGISTERS*/ +static const uint16_t ATM90E32_REGISTER_THDNUA = 0xF1; // A Voltage THD+N +static const uint16_t ATM90E32_REGISTER_THDNUB = 0xF2; // B Voltage THD+N +static const uint16_t ATM90E32_REGISTER_THDNUC = 0xF3; // C Voltage THD+N +static const uint16_t ATM90E32_REGISTER_THDNIA = 0xF5; // A Current THD+N +static const uint16_t ATM90E32_REGISTER_THDNIB = 0xF6; // B Current THD+N +static const uint16_t ATM90E32_REGISTER_THDNIC = 0xF7; // C Current THD+N +static const uint16_t ATM90E32_REGISTER_FREQ = 0xF8; // Frequency +static const uint16_t ATM90E32_REGISTER_PANGLEA = 0xF9; // A Mean Phase Angle +static const uint16_t ATM90E32_REGISTER_PANGLEB = 0xFA; // B Mean Phase Angle +static const uint16_t ATM90E32_REGISTER_PANGLEC = 0xFB; // C Mean Phase Angle +static const uint16_t ATM90E32_REGISTER_TEMP = 0xFC; // Measured Temperature +static const uint16_t ATM90E32_REGISTER_UANGLEA = 0xFD; // A Voltage Phase Angle +static const uint16_t ATM90E32_REGISTER_UANGLEB = 0xFE; // B Voltage Phase Angle +static const uint16_t ATM90E32_REGISTER_UANGLEC = 0xFF; // C Voltage Phase Angle + +} // namespace atm90e32 +} // namespace esphome diff --git a/esphome/components/atm90e32/sensor.py b/esphome/components/atm90e32/sensor.py new file mode 100644 index 0000000000..7b62740f8e --- /dev/null +++ b/esphome/components/atm90e32/sensor.py @@ -0,0 +1,72 @@ +import esphome.codegen as cg +import esphome.config_validation as cv +from esphome.components import sensor, spi +from esphome.const import \ + CONF_ID, CONF_VOLTAGE, CONF_CURRENT, CONF_POWER, CONF_FREQUENCY, \ + ICON_FLASH, UNIT_HZ, UNIT_VOLT, UNIT_AMPERE, UNIT_WATT + +CONF_PHASE_A = 'phase_a' +CONF_PHASE_B = 'phase_b' +CONF_PHASE_C = 'phase_c' + +CONF_LINE_FREQUENCY = 'line_frequency' +CONF_GAIN_PGA = 'gain_pga' +CONF_GAIN_VOLTAGE = 'gain_voltage' +CONF_GAIN_CT = 'gain_ct' +LINE_FREQS = { + '50HZ': 50, + '60HZ': 60, +} +PGA_GAINS = { + '1X': 0x0, + '2X': 0x15, + '4X': 0x2A, +} + +atm90e32_ns = cg.esphome_ns.namespace('atm90e32') +ATM90E32Component = atm90e32_ns.class_('ATM90E32Component', cg.PollingComponent, spi.SPIDevice) + +ATM90E32_PHASE_SCHEMA = cv.Schema({ + cv.Optional(CONF_VOLTAGE): sensor.sensor_schema(UNIT_VOLT, ICON_FLASH, 2), + cv.Optional(CONF_CURRENT): sensor.sensor_schema(UNIT_AMPERE, ICON_FLASH, 2), + cv.Optional(CONF_POWER): sensor.sensor_schema(UNIT_WATT, ICON_FLASH, 2), + cv.Optional(CONF_GAIN_VOLTAGE, default=41820): cv.uint16_t, + cv.Optional(CONF_GAIN_CT, default=25498): cv.uint16_t, +}) + +CONFIG_SCHEMA = cv.Schema({ + cv.GenerateID(): cv.declare_id(ATM90E32Component), + cv.Optional(CONF_PHASE_A): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_PHASE_B): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_PHASE_C): ATM90E32_PHASE_SCHEMA, + cv.Optional(CONF_FREQUENCY): sensor.sensor_schema(UNIT_HZ, ICON_FLASH, 1), + cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), + cv.Optional(CONF_GAIN_PGA, default='2X'): cv.enum(PGA_GAINS, upper=True), +}).extend(cv.polling_component_schema('60s')).extend(spi.SPI_DEVICE_SCHEMA) + + +def to_code(config): + var = cg.new_Pvariable(config[CONF_ID]) + yield cg.register_component(var, config) + yield 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 CONF_VOLTAGE in conf: + sens = yield sensor.new_sensor(conf[CONF_VOLTAGE]) + cg.add(var.set_voltage_sensor(i, sens)) + if CONF_CURRENT in conf: + sens = yield sensor.new_sensor(conf[CONF_CURRENT]) + cg.add(var.set_current_sensor(i, sens)) + if CONF_POWER in conf: + sens = yield sensor.new_sensor(conf[CONF_POWER]) + cg.add(var.set_power_sensor(i, sens)) + if CONF_FREQUENCY in config: + sens = yield sensor.new_sensor(config[CONF_FREQUENCY]) + cg.add(var.set_freq_sensor(sens)) + cg.add(var.set_line_freq(config[CONF_LINE_FREQUENCY])) + cg.add(var.set_pga_gain(config[CONF_GAIN_PGA])) diff --git a/esphome/const.py b/esphome/const.py index a53dbfbf78..cfddd3ae7f 100644 --- a/esphome/const.py +++ b/esphome/const.py @@ -492,6 +492,7 @@ UNIT_DECIBEL = 'dB' UNIT_DEGREES = u'°' UNIT_DEGREE_PER_SECOND = u'°/s' UNIT_EMPTY = '' +UNIT_HZ = 'hz' UNIT_HECTOPASCAL = 'hPa' UNIT_KELVIN = 'K' UNIT_LUX = 'lx' diff --git a/tests/test1.yaml b/tests/test1.yaml index 4ab4a707ff..37fc8fe0e7 100644 --- a/tests/test1.yaml +++ b/tests/test1.yaml @@ -244,6 +244,35 @@ sensor: state_topic: hi/me retain: false availability: + - platform: atm90e32 + cs_pin: 5 + phase_a: + voltage: + name: "EMON Line Voltage A" + current: + name: "EMON CT1 Current" + power: + name: "EMON Active Power CT1" + gain_voltage: 47660 + gain_ct: 12577 + phase_b: + current: + name: "EMON CT2 Current" + power: + name: "EMON Active Power CT2" + gain_voltage: 47660 + gain_ct: 12577 + phase_c: + current: + name: "EMON CT3 Current" + power: + name: "EMON Active Power CT3" + gain_voltage: 47660 + gain_ct: 12577 + frequency: + name: "EMON Line Frequency" + line_frequency: 50Hz + gain_pga: 2X - platform: bh1750 name: "Living Room Brightness 3" internal: true