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/async_tcp/* @OttoWinter | ||||
| esphome/components/atc_mithermometer/* @ahpohl | ||||
| esphome/components/atm90e26/* @danieltwagner | ||||
| esphome/components/b_parasite/* @rbaron | ||||
| esphome/components/ballu/* @bazuchan | ||||
| 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: | ||||
|       name: NIR | ||||
|     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 | ||||
|     cs_pin: 5 | ||||
|     phase_a: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user