mirror of
				https://github.com/esphome/esphome.git
				synced 2025-10-31 07:03:55 +00:00 
			
		
		
		
	Add support for ATM90E26 (#4366)
Co-authored-by: Jesse Hills <3060199+jesserockz@users.noreply.github.com>
This commit is contained in:
		| @@ -32,6 +32,7 @@ esphome/components/api/* @OttoWinter | |||||||
| esphome/components/as7341/* @mrgnr | esphome/components/as7341/* @mrgnr | ||||||
| esphome/components/async_tcp/* @OttoWinter | esphome/components/async_tcp/* @OttoWinter | ||||||
| esphome/components/atc_mithermometer/* @ahpohl | esphome/components/atc_mithermometer/* @ahpohl | ||||||
|  | esphome/components/atm90e26/* @danieltwagner | ||||||
| esphome/components/b_parasite/* @rbaron | esphome/components/b_parasite/* @rbaron | ||||||
| esphome/components/ballu/* @bazuchan | esphome/components/ballu/* @bazuchan | ||||||
| esphome/components/bang_bang/* @OttoWinter | esphome/components/bang_bang/* @OttoWinter | ||||||
|   | |||||||
							
								
								
									
										1
									
								
								esphome/components/atm90e26/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								esphome/components/atm90e26/__init__.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | |||||||
|  | CODEOWNERS = ["@danieltwagner"] | ||||||
							
								
								
									
										235
									
								
								esphome/components/atm90e26/atm90e26.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								esphome/components/atm90e26/atm90e26.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,235 @@ | |||||||
|  | #include "atm90e26.h" | ||||||
|  | #include "atm90e26_reg.h" | ||||||
|  | #include "esphome/core/log.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace atm90e26 { | ||||||
|  |  | ||||||
|  | static const char *const TAG = "atm90e26"; | ||||||
|  |  | ||||||
|  | void ATM90E26Component::update() { | ||||||
|  |   if (this->read16_(ATM90E26_REGISTER_FUNCEN) != 0x0030) { | ||||||
|  |     this->status_set_warning(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   if (this->voltage_sensor_ != nullptr) { | ||||||
|  |     this->voltage_sensor_->publish_state(this->get_line_voltage_()); | ||||||
|  |   } | ||||||
|  |   if (this->current_sensor_ != nullptr) { | ||||||
|  |     this->current_sensor_->publish_state(this->get_line_current_()); | ||||||
|  |   } | ||||||
|  |   if (this->power_sensor_ != nullptr) { | ||||||
|  |     this->power_sensor_->publish_state(this->get_active_power_()); | ||||||
|  |   } | ||||||
|  |   if (this->reactive_power_sensor_ != nullptr) { | ||||||
|  |     this->reactive_power_sensor_->publish_state(this->get_reactive_power_()); | ||||||
|  |   } | ||||||
|  |   if (this->power_factor_sensor_ != nullptr) { | ||||||
|  |     this->power_factor_sensor_->publish_state(this->get_power_factor_()); | ||||||
|  |   } | ||||||
|  |   if (this->forward_active_energy_sensor_ != nullptr) { | ||||||
|  |     this->forward_active_energy_sensor_->publish_state(this->get_forward_active_energy_()); | ||||||
|  |   } | ||||||
|  |   if (this->reverse_active_energy_sensor_ != nullptr) { | ||||||
|  |     this->reverse_active_energy_sensor_->publish_state(this->get_reverse_active_energy_()); | ||||||
|  |   } | ||||||
|  |   if (this->freq_sensor_ != nullptr) { | ||||||
|  |     this->freq_sensor_->publish_state(this->get_frequency_()); | ||||||
|  |   } | ||||||
|  |   this->status_clear_warning(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ATM90E26Component::setup() { | ||||||
|  |   ESP_LOGCONFIG(TAG, "Setting up ATM90E26 Component..."); | ||||||
|  |   this->spi_setup(); | ||||||
|  |  | ||||||
|  |   uint16_t mmode = 0x422;  // default values for everything but L/N line current gains | ||||||
|  |   mmode |= (gain_pga_ & 0x7) << 13; | ||||||
|  |   mmode |= (n_line_gain_ & 0x3) << 11; | ||||||
|  |  | ||||||
|  |   this->write16_(ATM90E26_REGISTER_SOFTRESET, 0x789A);  // Perform soft reset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_FUNCEN, | ||||||
|  |                  0x0030);  // Voltage sag irq=1, report on warnout pin=1, energy dir change irq=0 | ||||||
|  |   uint16_t read = this->read16_(ATM90E26_REGISTER_LASTDATA); | ||||||
|  |   if (read != 0x0030) { | ||||||
|  |     ESP_LOGW(TAG, "Could not initialize ATM90E26 IC, check SPI settings: %d", read); | ||||||
|  |     this->mark_failed(); | ||||||
|  |     return; | ||||||
|  |   } | ||||||
|  |   // TODO: 100 * <nominal voltage, e.g. 230> * sqrt(2) * <fraction of nominal, e.g. 0.9> / (4 * gain_voltage/32768) | ||||||
|  |   this->write16_(ATM90E26_REGISTER_SAGTH, 0x17DD);  // Voltage sag threshhold 0x1F2F | ||||||
|  |  | ||||||
|  |   // Set metering calibration values | ||||||
|  |   this->write16_(ATM90E26_REGISTER_CALSTART, 0x5678);  // CAL Metering calibration startup command | ||||||
|  |  | ||||||
|  |   // Configure | ||||||
|  |   this->write16_(ATM90E26_REGISTER_MMODE, mmode);  // Metering Mode Configuration (see above) | ||||||
|  |  | ||||||
|  |   this->write16_(ATM90E26_REGISTER_PLCONSTH, (pl_const_ >> 16));   // PL Constant MSB | ||||||
|  |   this->write16_(ATM90E26_REGISTER_PLCONSTL, pl_const_ & 0xFFFF);  // PL Constant LSB | ||||||
|  |  | ||||||
|  |   // Calibrate this to be 1 pulse per Wh | ||||||
|  |   this->write16_(ATM90E26_REGISTER_LGAIN, gain_metering_);  // L Line Calibration Gain (active power metering) | ||||||
|  |   this->write16_(ATM90E26_REGISTER_LPHI, 0x0000);           // L Line Calibration Angle | ||||||
|  |   this->write16_(ATM90E26_REGISTER_NGAIN, 0x0000);          // N Line Calibration Gain | ||||||
|  |   this->write16_(ATM90E26_REGISTER_NPHI, 0x0000);           // N Line Calibration Angle | ||||||
|  |   this->write16_(ATM90E26_REGISTER_PSTARTTH, 0x08BD);       // Active Startup Power Threshold (default) = 2237 | ||||||
|  |   this->write16_(ATM90E26_REGISTER_PNOLTH, 0x0000);         // Active No-Load Power Threshold | ||||||
|  |   this->write16_(ATM90E26_REGISTER_QSTARTTH, 0x0AEC);       // Reactive Startup Power Threshold (default) = 2796 | ||||||
|  |   this->write16_(ATM90E26_REGISTER_QNOLTH, 0x0000);         // Reactive No-Load Power Threshold | ||||||
|  |  | ||||||
|  |   // Compute Checksum for the registers we set above | ||||||
|  |   // low byte = sum of all bytes | ||||||
|  |   uint16_t cs = | ||||||
|  |       ((mmode >> 8) + (mmode & 0xFF) + (pl_const_ >> 24) + ((pl_const_ >> 16) & 0xFF) + ((pl_const_ >> 8) & 0xFF) + | ||||||
|  |        (pl_const_ & 0xFF) + (gain_metering_ >> 8) + (gain_metering_ & 0xFF) + 0x08 + 0xBD + 0x0A + 0xEC) & | ||||||
|  |       0xFF; | ||||||
|  |   // high byte = XOR of all bytes | ||||||
|  |   cs |= ((mmode >> 8) ^ (mmode & 0xFF) ^ (pl_const_ >> 24) ^ ((pl_const_ >> 16) & 0xFF) ^ ((pl_const_ >> 8) & 0xFF) ^ | ||||||
|  |          (pl_const_ & 0xFF) ^ (gain_metering_ >> 8) ^ (gain_metering_ & 0xFF) ^ 0x08 ^ 0xBD ^ 0x0A ^ 0xEC) | ||||||
|  |         << 8; | ||||||
|  |  | ||||||
|  |   this->write16_(ATM90E26_REGISTER_CS1, cs); | ||||||
|  |   ESP_LOGVV(TAG, "Set CS1 to: 0x%04X", cs); | ||||||
|  |  | ||||||
|  |   // Set measurement calibration values | ||||||
|  |   this->write16_(ATM90E26_REGISTER_ADJSTART, 0x5678);      // Measurement calibration startup command, registers 31-3A | ||||||
|  |   this->write16_(ATM90E26_REGISTER_UGAIN, gain_voltage_);  // Voltage RMS gain | ||||||
|  |   this->write16_(ATM90E26_REGISTER_IGAINL, gain_ct_);      // L line current RMS gain | ||||||
|  |   this->write16_(ATM90E26_REGISTER_IGAINN, 0x7530);        // N Line Current RMS Gain | ||||||
|  |   this->write16_(ATM90E26_REGISTER_UOFFSET, 0x0000);       // Voltage Offset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_IOFFSETL, 0x0000);      // L Line Current Offset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_IOFFSETN, 0x0000);      // N Line Current Offse | ||||||
|  |   this->write16_(ATM90E26_REGISTER_POFFSETL, 0x0000);      // L Line Active Power Offset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_QOFFSETL, 0x0000);      // L Line Reactive Power Offset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_POFFSETN, 0x0000);      // N Line Active Power Offset | ||||||
|  |   this->write16_(ATM90E26_REGISTER_QOFFSETN, 0x0000);      // N Line Reactive Power Offset | ||||||
|  |  | ||||||
|  |   // Compute Checksum for the registers we set above | ||||||
|  |   cs = ((gain_voltage_ >> 8) + (gain_voltage_ & 0xFF) + (gain_ct_ >> 8) + (gain_ct_ & 0xFF) + 0x75 + 0x30) & 0xFF; | ||||||
|  |   cs |= ((gain_voltage_ >> 8) ^ (gain_voltage_ & 0xFF) ^ (gain_ct_ >> 8) ^ (gain_ct_ & 0xFF) ^ 0x75 ^ 0x30) << 8; | ||||||
|  |   this->write16_(ATM90E26_REGISTER_CS2, cs); | ||||||
|  |   ESP_LOGVV(TAG, "Set CS2 to: 0x%04X", cs); | ||||||
|  |  | ||||||
|  |   this->write16_(ATM90E26_REGISTER_CALSTART, | ||||||
|  |                  0x8765);  // Checks correctness of 21-2B registers and starts normal metering if ok | ||||||
|  |   this->write16_(ATM90E26_REGISTER_ADJSTART, | ||||||
|  |                  0x8765);  // Checks correctness of 31-3A registers and starts normal measurement  if ok | ||||||
|  |  | ||||||
|  |   uint16_t sys_status = this->read16_(ATM90E26_REGISTER_SYSSTATUS); | ||||||
|  |   if (sys_status & 0xC000) {  // Checksum 1 Error | ||||||
|  |  | ||||||
|  |     ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS1 was incorrect, expected: 0x%04X", | ||||||
|  |              this->read16_(ATM90E26_REGISTER_CS1)); | ||||||
|  |     this->mark_failed(); | ||||||
|  |   } | ||||||
|  |   if (sys_status & 0x3000) {  // Checksum 2 Error | ||||||
|  |     ESP_LOGW(TAG, "Could not initialize ATM90E26 IC: CS2 was incorrect, expected: 0x%04X", | ||||||
|  |              this->read16_(ATM90E26_REGISTER_CS2)); | ||||||
|  |     this->mark_failed(); | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ATM90E26Component::dump_config() { | ||||||
|  |   ESP_LOGCONFIG("", "ATM90E26:"); | ||||||
|  |   LOG_PIN("  CS Pin: ", this->cs_); | ||||||
|  |   if (this->is_failed()) { | ||||||
|  |     ESP_LOGE(TAG, "Communication with ATM90E26 failed!"); | ||||||
|  |   } | ||||||
|  |   LOG_UPDATE_INTERVAL(this); | ||||||
|  |   LOG_SENSOR("  ", "Voltage A", this->voltage_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Current A", this->current_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Power A", this->power_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Reactive Power A", this->reactive_power_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "PF A", this->power_factor_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Active Forward Energy A", this->forward_active_energy_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Active Reverse Energy A", this->reverse_active_energy_sensor_); | ||||||
|  |   LOG_SENSOR("  ", "Frequency", this->freq_sensor_); | ||||||
|  | } | ||||||
|  | float ATM90E26Component::get_setup_priority() const { return setup_priority::DATA; } | ||||||
|  |  | ||||||
|  | uint16_t ATM90E26Component::read16_(uint8_t a_register) { | ||||||
|  |   uint8_t data[2]; | ||||||
|  |   uint16_t output; | ||||||
|  |  | ||||||
|  |   this->enable(); | ||||||
|  |   delayMicroseconds(4); | ||||||
|  |   this->write_byte(a_register | 0x80); | ||||||
|  |   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; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void ATM90E26Component::write16_(uint8_t a_register, uint16_t val) { | ||||||
|  |   ESP_LOGVV(TAG, "write16_ 0x%04X val 0x%04X", a_register, val); | ||||||
|  |   this->enable(); | ||||||
|  |   delayMicroseconds(4); | ||||||
|  |   this->write_byte(a_register & 0x7F); | ||||||
|  |   delayMicroseconds(4); | ||||||
|  |   this->write_byte((val >> 8) & 0xFF); | ||||||
|  |   this->write_byte(val & 0xFF); | ||||||
|  |   this->disable(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_line_current_() { | ||||||
|  |   uint16_t current = this->read16_(ATM90E26_REGISTER_IRMS); | ||||||
|  |   return current / 1000.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_line_voltage_() { | ||||||
|  |   uint16_t voltage = this->read16_(ATM90E26_REGISTER_URMS); | ||||||
|  |   return voltage / 100.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_active_power_() { | ||||||
|  |   int16_t val = this->read16_(ATM90E26_REGISTER_PMEAN);  // two's complement | ||||||
|  |   return (float) val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_reactive_power_() { | ||||||
|  |   int16_t val = this->read16_(ATM90E26_REGISTER_QMEAN);  // two's complement | ||||||
|  |   return (float) val; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_power_factor_() { | ||||||
|  |   uint16_t val = this->read16_(ATM90E26_REGISTER_POWERF);  // signed | ||||||
|  |   if (val & 0x8000) { | ||||||
|  |     return -(val & 0x7FF) / 1000.0f; | ||||||
|  |   } else { | ||||||
|  |     return val / 1000.0f; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_forward_active_energy_() { | ||||||
|  |   uint16_t val = this->read16_(ATM90E26_REGISTER_APENERGY); | ||||||
|  |   if ((UINT32_MAX - this->cumulative_forward_active_energy_) > val) { | ||||||
|  |     this->cumulative_forward_active_energy_ += val; | ||||||
|  |   } else { | ||||||
|  |     this->cumulative_forward_active_energy_ = val; | ||||||
|  |   } | ||||||
|  |   // The register holds thenths of pulses, we want to output Wh | ||||||
|  |   return (this->cumulative_forward_active_energy_ * 100.0f / meter_constant_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_reverse_active_energy_() { | ||||||
|  |   uint16_t val = this->read16_(ATM90E26_REGISTER_ANENERGY); | ||||||
|  |   if (UINT32_MAX - this->cumulative_reverse_active_energy_ > val) { | ||||||
|  |     this->cumulative_reverse_active_energy_ += val; | ||||||
|  |   } else { | ||||||
|  |     this->cumulative_reverse_active_energy_ = val; | ||||||
|  |   } | ||||||
|  |   return (this->cumulative_reverse_active_energy_ * 100.0f / meter_constant_); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | float ATM90E26Component::get_frequency_() { | ||||||
|  |   uint16_t freq = this->read16_(ATM90E26_REGISTER_FREQ); | ||||||
|  |   return freq / 100.0f; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | }  // namespace atm90e26 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										72
									
								
								esphome/components/atm90e26/atm90e26.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								esphome/components/atm90e26/atm90e26.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,72 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | #include "esphome/core/component.h" | ||||||
|  | #include "esphome/components/sensor/sensor.h" | ||||||
|  | #include "esphome/components/spi/spi.h" | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace atm90e26 { | ||||||
|  |  | ||||||
|  | class ATM90E26Component : public PollingComponent, | ||||||
|  |                           public spi::SPIDevice<spi::BIT_ORDER_MSB_FIRST, spi::CLOCK_POLARITY_HIGH, | ||||||
|  |                                                 spi::CLOCK_PHASE_TRAILING, spi::DATA_RATE_200KHZ> { | ||||||
|  |  public: | ||||||
|  |   void setup() override; | ||||||
|  |   void dump_config() override; | ||||||
|  |   float get_setup_priority() const override; | ||||||
|  |   void update() override; | ||||||
|  |  | ||||||
|  |   void set_voltage_sensor(sensor::Sensor *obj) { this->voltage_sensor_ = obj; } | ||||||
|  |   void set_current_sensor(sensor::Sensor *obj) { this->current_sensor_ = obj; } | ||||||
|  |   void set_power_sensor(sensor::Sensor *obj) { this->power_sensor_ = obj; } | ||||||
|  |   void set_reactive_power_sensor(sensor::Sensor *obj) { this->reactive_power_sensor_ = obj; } | ||||||
|  |   void set_forward_active_energy_sensor(sensor::Sensor *obj) { this->forward_active_energy_sensor_ = obj; } | ||||||
|  |   void set_reverse_active_energy_sensor(sensor::Sensor *obj) { this->reverse_active_energy_sensor_ = obj; } | ||||||
|  |   void set_power_factor_sensor(sensor::Sensor *obj) { this->power_factor_sensor_ = obj; } | ||||||
|  |   void set_freq_sensor(sensor::Sensor *freq_sensor) { freq_sensor_ = freq_sensor; } | ||||||
|  |   void set_line_freq(int freq) { line_freq_ = freq; } | ||||||
|  |   void set_meter_constant(float val) { meter_constant_ = val; } | ||||||
|  |   void set_pl_const(uint32_t pl_const) { pl_const_ = pl_const; } | ||||||
|  |   void set_gain_metering(uint16_t gain) { this->gain_metering_ = gain; } | ||||||
|  |   void set_gain_voltage(uint16_t gain) { this->gain_voltage_ = gain; } | ||||||
|  |   void set_gain_ct(uint16_t gain) { this->gain_ct_ = gain; } | ||||||
|  |   void set_gain_pga(uint16_t gain) { gain_pga_ = gain; } | ||||||
|  |   void set_n_line_gain(uint16_t gain) { n_line_gain_ = gain; } | ||||||
|  |  | ||||||
|  |  protected: | ||||||
|  |   uint16_t read16_(uint8_t a_register); | ||||||
|  |   int read32_(uint8_t addr_h, uint8_t addr_l); | ||||||
|  |   void write16_(uint8_t a_register, uint16_t val); | ||||||
|  |  | ||||||
|  |   float get_line_voltage_(); | ||||||
|  |   float get_line_current_(); | ||||||
|  |   float get_active_power_(); | ||||||
|  |   float get_reactive_power_(); | ||||||
|  |   float get_power_factor_(); | ||||||
|  |   float get_forward_active_energy_(); | ||||||
|  |   float get_reverse_active_energy_(); | ||||||
|  |   float get_frequency_(); | ||||||
|  |   float get_chip_temperature_(); | ||||||
|  |  | ||||||
|  |   sensor::Sensor *freq_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *voltage_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *current_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *power_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *reactive_power_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *power_factor_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *forward_active_energy_sensor_{nullptr}; | ||||||
|  |   sensor::Sensor *reverse_active_energy_sensor_{nullptr}; | ||||||
|  |   uint32_t cumulative_forward_active_energy_{0}; | ||||||
|  |   uint32_t cumulative_reverse_active_energy_{0}; | ||||||
|  |   uint16_t gain_metering_{7481}; | ||||||
|  |   uint16_t gain_voltage_{26400}; | ||||||
|  |   uint16_t gain_ct_{31251}; | ||||||
|  |   uint16_t gain_pga_{0x4}; | ||||||
|  |   uint16_t n_line_gain_{0x2}; | ||||||
|  |   int line_freq_{60}; | ||||||
|  |   float meter_constant_{3200.0f}; | ||||||
|  |   uint32_t pl_const_{1429876}; | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | }  // namespace atm90e26 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										70
									
								
								esphome/components/atm90e26/atm90e26_reg.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								esphome/components/atm90e26/atm90e26_reg.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,70 @@ | |||||||
|  | #pragma once | ||||||
|  |  | ||||||
|  | namespace esphome { | ||||||
|  | namespace atm90e26 { | ||||||
|  |  | ||||||
|  | /* Status and Special Register */ | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SOFTRESET = 0x00;  // Software Reset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SYSSTATUS = 0x01;  // System Status | ||||||
|  | static const uint8_t ATM90E26_REGISTER_FUNCEN = 0x02;     // Function Enable | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SAGTH = 0x03;      // Voltage Sag Threshold | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SMALLPMOD = 0x04;  // Small-Power Mode | ||||||
|  | static const uint8_t ATM90E26_REGISTER_LASTDATA = 0x06;   // Last Read/Write SPI/UART Value | ||||||
|  |  | ||||||
|  | /* Metering Calibration and Configuration Register */ | ||||||
|  | static const uint8_t ATM90E26_REGISTER_LSB = 0x08;       // RMS/Power 16-bit LSB | ||||||
|  | static const uint8_t ATM90E26_REGISTER_CALSTART = 0x20;  // Calibration Start Command | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PLCONSTH = 0x21;  // High Word of PL_Constant | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PLCONSTL = 0x22;  // Low Word of PL_Constant | ||||||
|  | static const uint8_t ATM90E26_REGISTER_LGAIN = 0x23;     // L Line Calibration Gain | ||||||
|  | static const uint8_t ATM90E26_REGISTER_LPHI = 0x24;      // L Line Calibration Angle | ||||||
|  | static const uint8_t ATM90E26_REGISTER_NGAIN = 0x25;     // N Line Calibration Gain | ||||||
|  | static const uint8_t ATM90E26_REGISTER_NPHI = 0x26;      // N Line Calibration Angle | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PSTARTTH = 0x27;  // Active Startup Power Threshold | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PNOLTH = 0x28;    // Active No-Load Power Threshold | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QSTARTTH = 0x29;  // Reactive Startup Power Threshold | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QNOLTH = 0x2A;    // Reactive No-Load Power Threshold | ||||||
|  | static const uint8_t ATM90E26_REGISTER_MMODE = 0x2B;     // Metering Mode Configuration | ||||||
|  | static const uint8_t ATM90E26_REGISTER_CS1 = 0x2C;       // Checksum 1 | ||||||
|  |  | ||||||
|  | /* Measurement Calibration Register */ | ||||||
|  | static const uint8_t ATM90E26_REGISTER_ADJSTART = 0x30;  // Measurement Calibration Start Command | ||||||
|  | static const uint8_t ATM90E26_REGISTER_UGAIN = 0x31;     // Voltage RMS Gain | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IGAINL = 0x32;    // L Line Current RMS Gain | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IGAINN = 0x33;    // N Line Current RMS Gain | ||||||
|  | static const uint8_t ATM90E26_REGISTER_UOFFSET = 0x34;   // Voltage Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IOFFSETL = 0x35;  // L Line Current Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IOFFSETN = 0x36;  // N Line Current Offse | ||||||
|  | static const uint8_t ATM90E26_REGISTER_POFFSETL = 0x37;  // L Line Active Power Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QOFFSETL = 0x38;  // L Line Reactive Power Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_POFFSETN = 0x39;  // N Line Active Power Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QOFFSETN = 0x3A;  // N Line Reactive Power Offset | ||||||
|  | static const uint8_t ATM90E26_REGISTER_CS2 = 0x3B;       // Checksum 2 | ||||||
|  |  | ||||||
|  | /* Energy Register */ | ||||||
|  | static const uint8_t ATM90E26_REGISTER_APENERGY = 0x40;  // Forward Active Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_ANENERGY = 0x41;  // Reverse Active Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_ATENERGY = 0x42;  // Absolute Active Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_RPENERGY = 0x43;  // Forward (Inductive) Reactive Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_RNENERG = 0x44;   // Reverse (Capacitive) Reactive Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_RTENERGY = 0x45;  // Absolute Reactive Energy | ||||||
|  | static const uint8_t ATM90E26_REGISTER_ENSTATUS = 0x46;  // Metering Status | ||||||
|  |  | ||||||
|  | /* Measurement Register */ | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IRMS = 0x48;     // L Line Current RMS | ||||||
|  | static const uint8_t ATM90E26_REGISTER_URMS = 0x49;     // Voltage RMS | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PMEAN = 0x4A;    // L Line Mean Active Power | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QMEAN = 0x4B;    // L Line Mean Reactive Power | ||||||
|  | static const uint8_t ATM90E26_REGISTER_FREQ = 0x4C;     // Voltage Frequency | ||||||
|  | static const uint8_t ATM90E26_REGISTER_POWERF = 0x4D;   // L Line Power Factor | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PANGLE = 0x4E;   // Phase Angle between Voltage and L Line Current | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SMEAN = 0x4F;    // L Line Mean Apparent Power | ||||||
|  | static const uint8_t ATM90E26_REGISTER_IRMS2 = 0x68;    // N Line Current rms | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PMEAN2 = 0x6A;   // N Line Mean Active Power | ||||||
|  | static const uint8_t ATM90E26_REGISTER_QMEAN2 = 0x6B;   // N Line Mean Reactive Power | ||||||
|  | static const uint8_t ATM90E26_REGISTER_POWERF2 = 0x6D;  // N Line Power Factor | ||||||
|  | static const uint8_t ATM90E26_REGISTER_PANGLE2 = 0x6E;  // Phase Angle between Voltage and N Line Current | ||||||
|  | static const uint8_t ATM90E26_REGISTER_SMEAN2 = 0x6F;   // N Line Mean Apparent Power | ||||||
|  |  | ||||||
|  | }  // namespace atm90e26 | ||||||
|  | }  // namespace esphome | ||||||
							
								
								
									
										157
									
								
								esphome/components/atm90e26/sensor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										157
									
								
								esphome/components/atm90e26/sensor.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,157 @@ | |||||||
|  | import esphome.codegen as cg | ||||||
|  | import esphome.config_validation as cv | ||||||
|  | from esphome.components import sensor, spi | ||||||
|  | from esphome.const import ( | ||||||
|  |     CONF_ID, | ||||||
|  |     CONF_REACTIVE_POWER, | ||||||
|  |     CONF_VOLTAGE, | ||||||
|  |     CONF_CURRENT, | ||||||
|  |     CONF_POWER, | ||||||
|  |     CONF_POWER_FACTOR, | ||||||
|  |     CONF_FREQUENCY, | ||||||
|  |     CONF_FORWARD_ACTIVE_ENERGY, | ||||||
|  |     CONF_REVERSE_ACTIVE_ENERGY, | ||||||
|  |     DEVICE_CLASS_CURRENT, | ||||||
|  |     DEVICE_CLASS_ENERGY, | ||||||
|  |     DEVICE_CLASS_POWER, | ||||||
|  |     DEVICE_CLASS_POWER_FACTOR, | ||||||
|  |     DEVICE_CLASS_VOLTAGE, | ||||||
|  |     ICON_LIGHTBULB, | ||||||
|  |     ICON_CURRENT_AC, | ||||||
|  |     STATE_CLASS_MEASUREMENT, | ||||||
|  |     STATE_CLASS_TOTAL_INCREASING, | ||||||
|  |     UNIT_HERTZ, | ||||||
|  |     UNIT_VOLT, | ||||||
|  |     UNIT_AMPERE, | ||||||
|  |     UNIT_WATT, | ||||||
|  |     UNIT_VOLT_AMPS_REACTIVE, | ||||||
|  |     UNIT_WATT_HOURS, | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONF_LINE_FREQUENCY = "line_frequency" | ||||||
|  | CONF_METER_CONSTANT = "meter_constant" | ||||||
|  | CONF_PL_CONST = "pl_const" | ||||||
|  | CONF_GAIN_PGA = "gain_pga" | ||||||
|  | CONF_GAIN_METERING = "gain_metering" | ||||||
|  | CONF_GAIN_VOLTAGE = "gain_voltage" | ||||||
|  | CONF_GAIN_CT = "gain_ct" | ||||||
|  | LINE_FREQS = { | ||||||
|  |     "50HZ": 50, | ||||||
|  |     "60HZ": 60, | ||||||
|  | } | ||||||
|  | PGA_GAINS = { | ||||||
|  |     "1X": 0x4, | ||||||
|  |     "4X": 0x0, | ||||||
|  |     "8X": 0x1, | ||||||
|  |     "16X": 0x2, | ||||||
|  |     "24X": 0x3, | ||||||
|  | } | ||||||
|  |  | ||||||
|  | atm90e26_ns = cg.esphome_ns.namespace("atm90e26") | ||||||
|  | ATM90E26Component = atm90e26_ns.class_( | ||||||
|  |     "ATM90E26Component", cg.PollingComponent, spi.SPIDevice | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | CONFIG_SCHEMA = ( | ||||||
|  |     cv.Schema( | ||||||
|  |         { | ||||||
|  |             cv.GenerateID(): cv.declare_id(ATM90E26Component), | ||||||
|  |             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=2, | ||||||
|  |                 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, | ||||||
|  |                 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_FREQUENCY): sensor.sensor_schema( | ||||||
|  |                 unit_of_measurement=UNIT_HERTZ, | ||||||
|  |                 icon=ICON_CURRENT_AC, | ||||||
|  |                 accuracy_decimals=1, | ||||||
|  |                 state_class=STATE_CLASS_MEASUREMENT, | ||||||
|  |             ), | ||||||
|  |             cv.Required(CONF_LINE_FREQUENCY): cv.enum(LINE_FREQS, upper=True), | ||||||
|  |             cv.Required(CONF_METER_CONSTANT): cv.positive_float, | ||||||
|  |             cv.Optional(CONF_PL_CONST, default=1429876): cv.uint32_t, | ||||||
|  |             cv.Optional(CONF_GAIN_METERING, default=7481): cv.uint16_t, | ||||||
|  |             cv.Optional(CONF_GAIN_VOLTAGE, default=26400): cv.int_range( | ||||||
|  |                 min=0, max=32767 | ||||||
|  |             ), | ||||||
|  |             cv.Optional(CONF_GAIN_CT, default=31251): cv.uint16_t, | ||||||
|  |             cv.Optional(CONF_GAIN_PGA, default="1X"): cv.enum(PGA_GAINS, upper=True), | ||||||
|  |         } | ||||||
|  |     ) | ||||||
|  |     .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) | ||||||
|  |  | ||||||
|  |     if CONF_VOLTAGE in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_VOLTAGE]) | ||||||
|  |         cg.add(var.set_voltage_sensor(sens)) | ||||||
|  |     if CONF_CURRENT in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_CURRENT]) | ||||||
|  |         cg.add(var.set_current_sensor(sens)) | ||||||
|  |     if CONF_POWER in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_POWER]) | ||||||
|  |         cg.add(var.set_power_sensor(sens)) | ||||||
|  |     if CONF_REACTIVE_POWER in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_REACTIVE_POWER]) | ||||||
|  |         cg.add(var.set_reactive_power_sensor(sens)) | ||||||
|  |     if CONF_POWER_FACTOR in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_POWER_FACTOR]) | ||||||
|  |         cg.add(var.set_power_factor_sensor(sens)) | ||||||
|  |     if CONF_FORWARD_ACTIVE_ENERGY in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_FORWARD_ACTIVE_ENERGY]) | ||||||
|  |         cg.add(var.set_forward_active_energy_sensor(sens)) | ||||||
|  |     if CONF_REVERSE_ACTIVE_ENERGY in config: | ||||||
|  |         sens = await sensor.new_sensor(config[CONF_REVERSE_ACTIVE_ENERGY]) | ||||||
|  |         cg.add(var.set_reverse_active_energy_sensor(sens)) | ||||||
|  |     if CONF_FREQUENCY in config: | ||||||
|  |         sens = await 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_meter_constant(config[CONF_METER_CONSTANT])) | ||||||
|  |     cg.add(var.set_pl_const(config[CONF_PL_CONST])) | ||||||
|  |     cg.add(var.set_gain_metering(config[CONF_GAIN_METERING])) | ||||||
|  |     cg.add(var.set_gain_voltage(config[CONF_GAIN_VOLTAGE])) | ||||||
|  |     cg.add(var.set_gain_ct(config[CONF_GAIN_CT])) | ||||||
|  |     cg.add(var.set_gain_pga(config[CONF_GAIN_PGA])) | ||||||
| @@ -483,6 +483,25 @@ sensor: | |||||||
|     nir: |     nir: | ||||||
|       name: NIR |       name: NIR | ||||||
|     i2c_id: i2c_bus |     i2c_id: i2c_bus | ||||||
|  |   - platform: atm90e26 | ||||||
|  |     cs_pin: 5 | ||||||
|  |     voltage: | ||||||
|  |       name: Line Voltage | ||||||
|  |     current: | ||||||
|  |       name: CT Amps | ||||||
|  |     power: | ||||||
|  |       name: Active Watts | ||||||
|  |     power_factor: | ||||||
|  |       name: Power Factor | ||||||
|  |     frequency: | ||||||
|  |       name: Line Frequency | ||||||
|  |     line_frequency: 50Hz | ||||||
|  |     meter_constant: 1000 | ||||||
|  |     pl_const: 1429876 | ||||||
|  |     gain_pga: 1X | ||||||
|  |     gain_metering: 7481 | ||||||
|  |     gain_voltage: 26400 | ||||||
|  |     gain_ct: 31251 | ||||||
|   - platform: atm90e32 |   - platform: atm90e32 | ||||||
|     cs_pin: 5 |     cs_pin: 5 | ||||||
|     phase_a: |     phase_a: | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user