mirror of
https://github.com/esphome/esphome.git
synced 2024-09-30 04:28:42 +02:00
INA226 low power mode
This commit is contained in:
parent
e753ac3a97
commit
aa506f665d
@ -32,38 +32,42 @@ static const uint8_t INA226_REGISTER_BUS_VOLTAGE = 0x02;
|
|||||||
static const uint8_t INA226_REGISTER_POWER = 0x03;
|
static const uint8_t INA226_REGISTER_POWER = 0x03;
|
||||||
static const uint8_t INA226_REGISTER_CURRENT = 0x04;
|
static const uint8_t INA226_REGISTER_CURRENT = 0x04;
|
||||||
static const uint8_t INA226_REGISTER_CALIBRATION = 0x05;
|
static const uint8_t INA226_REGISTER_CALIBRATION = 0x05;
|
||||||
|
static const uint8_t INA226_REGISTER_MASK_ENABLE = 0x06;
|
||||||
|
|
||||||
|
static const uint16_t INA226_MASK_ENABLE_CONVERSION_READY = 0b1000;
|
||||||
|
|
||||||
static const uint16_t INA226_ADC_TIMES[] = {140, 204, 332, 588, 1100, 2116, 4156, 8244};
|
static const uint16_t INA226_ADC_TIMES[] = {140, 204, 332, 588, 1100, 2116, 4156, 8244};
|
||||||
static const uint16_t INA226_ADC_AVG_SAMPLES[] = {1, 4, 16, 64, 128, 256, 512, 1024};
|
static const uint16_t INA226_ADC_AVG_SAMPLES[] = {1, 4, 16, 64, 128, 256, 512, 1024};
|
||||||
|
|
||||||
|
static const uint32_t INA226_TIMEOUT_MS = 10 * 1000;
|
||||||
|
|
||||||
void INA226Component::setup() {
|
void INA226Component::setup() {
|
||||||
ESP_LOGCONFIG(TAG, "Setting up INA226...");
|
ESP_LOGCONFIG(TAG, "Setting up INA226...");
|
||||||
|
|
||||||
ConfigurationRegister config;
|
this->config_reg_.reset = 1;
|
||||||
|
if (!this->write_byte_16(INA226_REGISTER_CONFIG, this->config_reg_.raw)) {
|
||||||
config.reset = 1;
|
|
||||||
if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) {
|
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
delay(1);
|
delay(1);
|
||||||
|
|
||||||
config.raw = 0;
|
this->config_reg_.raw = 0;
|
||||||
config.reserved = 0b100; // as per datasheet
|
this->config_reg_.reserved = 0b100; // as per datasheet
|
||||||
|
|
||||||
// Averaging Mode AVG Bit Settings[11:9] (000 -> 1 sample, 001 -> 4 sample, 111 -> 1024 samples)
|
// Averaging Mode AVG Bit Settings[11:9] (000 -> 1 sample, 001 -> 4 sample, 111 -> 1024 samples)
|
||||||
config.avg_samples = this->adc_avg_samples_;
|
this->config_reg_.avg_samples = this->adc_avg_samples_;
|
||||||
|
|
||||||
// Bus Voltage Conversion Time VBUSCT Bit Settings [8:6] (100 -> 1.1ms, 111 -> 8.244 ms)
|
// Bus Voltage Conversion Time VBUSCT Bit Settings [8:6] (100 -> 1.1ms, 111 -> 8.244 ms)
|
||||||
config.bus_voltage_conversion_time = this->adc_time_voltage_;
|
this->config_reg_.bus_voltage_conversion_time = this->adc_time_voltage_;
|
||||||
|
|
||||||
// Shunt Voltage Conversion Time VSHCT Bit Settings [5:3] (100 -> 1.1ms, 111 -> 8.244 ms)
|
// Shunt Voltage Conversion Time VSHCT Bit Settings [5:3] (100 -> 1.1ms, 111 -> 8.244 ms)
|
||||||
config.shunt_voltage_conversion_time = this->adc_time_current_;
|
this->config_reg_.shunt_voltage_conversion_time = this->adc_time_current_;
|
||||||
|
|
||||||
// Mode Settings [2:0] Combinations (111 -> Shunt and Bus, Continuous)
|
// Mode Settings [2:0] Combinations (111 -> Shunt and Bus, Continuous)
|
||||||
config.mode = 0b111;
|
this->config_reg_.mode =
|
||||||
|
this->low_power_mode_ ? AdcMode::ADC_MODE_POWER_DOWN : AdcMode::ADC_MODE_SHUNT_AND_BUS_CONTINUOUS;
|
||||||
|
|
||||||
if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) {
|
if (!this->write_byte_16(INA226_REGISTER_CONFIG, this->config_reg_.raw)) {
|
||||||
this->mark_failed();
|
this->mark_failed();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -106,6 +110,44 @@ void INA226Component::dump_config() {
|
|||||||
float INA226Component::get_setup_priority() const { return setup_priority::DATA; }
|
float INA226Component::get_setup_priority() const { return setup_priority::DATA; }
|
||||||
|
|
||||||
void INA226Component::update() {
|
void INA226Component::update() {
|
||||||
|
if (this->is_ready() && this->state_ == State::IDLE) {
|
||||||
|
this->start_measurements_();
|
||||||
|
this->last_start_time_ = millis();
|
||||||
|
this->state_ = State::WAITING_FOR_DATA;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void INA226Component::loop() {
|
||||||
|
if (!this->is_ready())
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (this->state_) {
|
||||||
|
case State::NOT_INITIALIZED:
|
||||||
|
case State::IDLE:
|
||||||
|
break;
|
||||||
|
case State::WAITING_FOR_DATA:
|
||||||
|
if (this->is_conversion_ready_()) {
|
||||||
|
this->state_ = State::READY_TO_PUBLISH;
|
||||||
|
} else if (millis() - this->last_start_time_ > INA226_TIMEOUT_MS) {
|
||||||
|
ESP_LOGW(TAG, "INA226 Data collection timeout");
|
||||||
|
this->status_set_warning();
|
||||||
|
this->state_ = State::IDLE;
|
||||||
|
} else {
|
||||||
|
ESP_LOGD(TAG, "INA226 Data not ready yet, waiting...");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case State::READY_TO_PUBLISH:
|
||||||
|
this->stop_measurements_();
|
||||||
|
this->get_and_publish_data_();
|
||||||
|
this->state_ = State::IDLE;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// shall never be here
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void INA226Component::get_and_publish_data_() {
|
||||||
if (this->bus_voltage_sensor_ != nullptr) {
|
if (this->bus_voltage_sensor_ != nullptr) {
|
||||||
uint16_t raw_bus_voltage;
|
uint16_t raw_bus_voltage;
|
||||||
if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) {
|
if (!this->read_byte_16(INA226_REGISTER_BUS_VOLTAGE, &raw_bus_voltage)) {
|
||||||
@ -162,5 +204,33 @@ int32_t INA226Component::twos_complement_(int32_t val, uint8_t bits) {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void INA226Component::stop_measurements_() {
|
||||||
|
if (!this->low_power_mode_)
|
||||||
|
return;
|
||||||
|
|
||||||
|
this->config_reg_.mode = AdcMode::ADC_MODE_POWER_DOWN;
|
||||||
|
if (!this->write_byte_16(INA226_REGISTER_CONFIG, this->config_reg_.raw)) {
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void INA226Component::start_measurements_() {
|
||||||
|
if (!this->low_power_mode_)
|
||||||
|
return;
|
||||||
|
this->config_reg_.mode = AdcMode::ADC_MODE_SHUNT_AND_BUS_TRIGGERED;
|
||||||
|
if (!this->write_byte_16(INA226_REGISTER_CONFIG, this->config_reg_.raw)) {
|
||||||
|
this->mark_failed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool INA226Component::is_conversion_ready_() {
|
||||||
|
uint16_t me_reg;
|
||||||
|
if (!this->read_byte_16(INA226_REGISTER_MASK_ENABLE, &me_reg)) {
|
||||||
|
this->mark_failed();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return (me_reg & INA226_MASK_ENABLE_CONVERSION_READY);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace ina226
|
} // namespace ina226
|
||||||
} // namespace esphome
|
} // namespace esphome
|
||||||
|
@ -29,10 +29,21 @@ enum AdcAvgSamples : uint16_t {
|
|||||||
ADC_AVG_SAMPLES_1024 = 7
|
ADC_AVG_SAMPLES_1024 = 7
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum AdcMode : uint16_t {
|
||||||
|
ADC_MODE_POWER_DOWN = 0,
|
||||||
|
ADC_MODE_SHUNT_VOLTAGE_TRIGGERED = 1,
|
||||||
|
ADC_MODE_BUS_VOLTAGE_TRIGGERED = 2,
|
||||||
|
ADC_MODE_SHUNT_AND_BUS_TRIGGERED = 3,
|
||||||
|
ADC_MODE_POWER_DOWN2 = 4,
|
||||||
|
ADC_MODE_SHUNT_VOLTAGE_CONTINUOUS = 5,
|
||||||
|
ADC_MODE_BUS_VOLTAGE_CONTINUOUS = 6,
|
||||||
|
ADC_MODE_SHUNT_AND_BUS_CONTINUOUS = 7
|
||||||
|
};
|
||||||
|
|
||||||
union ConfigurationRegister {
|
union ConfigurationRegister {
|
||||||
uint16_t raw;
|
uint16_t raw;
|
||||||
struct {
|
struct {
|
||||||
uint16_t mode : 3;
|
AdcMode mode : 3;
|
||||||
AdcTime shunt_voltage_conversion_time : 3;
|
AdcTime shunt_voltage_conversion_time : 3;
|
||||||
AdcTime bus_voltage_conversion_time : 3;
|
AdcTime bus_voltage_conversion_time : 3;
|
||||||
AdcAvgSamples avg_samples : 3;
|
AdcAvgSamples avg_samples : 3;
|
||||||
@ -47,12 +58,14 @@ class INA226Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void dump_config() override;
|
void dump_config() override;
|
||||||
float get_setup_priority() const override;
|
float get_setup_priority() const override;
|
||||||
void update() override;
|
void update() override;
|
||||||
|
void loop() override;
|
||||||
|
|
||||||
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
|
void set_shunt_resistance_ohm(float shunt_resistance_ohm) { shunt_resistance_ohm_ = shunt_resistance_ohm; }
|
||||||
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }
|
void set_max_current_a(float max_current_a) { max_current_a_ = max_current_a; }
|
||||||
void set_adc_time_voltage(AdcTime time) { adc_time_voltage_ = time; }
|
void set_adc_time_voltage(AdcTime time) { adc_time_voltage_ = time; }
|
||||||
void set_adc_time_current(AdcTime time) { adc_time_current_ = time; }
|
void set_adc_time_current(AdcTime time) { adc_time_current_ = time; }
|
||||||
void set_adc_avg_samples(AdcAvgSamples samples) { adc_avg_samples_ = samples; }
|
void set_adc_avg_samples(AdcAvgSamples samples) { adc_avg_samples_ = samples; }
|
||||||
|
void set_enable_low_power_mode(bool enabled) { low_power_mode_ = enabled; }
|
||||||
|
|
||||||
void set_bus_voltage_sensor(sensor::Sensor *bus_voltage_sensor) { bus_voltage_sensor_ = bus_voltage_sensor; }
|
void set_bus_voltage_sensor(sensor::Sensor *bus_voltage_sensor) { bus_voltage_sensor_ = bus_voltage_sensor; }
|
||||||
void set_shunt_voltage_sensor(sensor::Sensor *shunt_voltage_sensor) { shunt_voltage_sensor_ = shunt_voltage_sensor; }
|
void set_shunt_voltage_sensor(sensor::Sensor *shunt_voltage_sensor) { shunt_voltage_sensor_ = shunt_voltage_sensor; }
|
||||||
@ -60,18 +73,28 @@ class INA226Component : public PollingComponent, public i2c::I2CDevice {
|
|||||||
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
void set_power_sensor(sensor::Sensor *power_sensor) { power_sensor_ = power_sensor; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
enum State { NOT_INITIALIZED, IDLE, WAITING_FOR_DATA, READY_TO_PUBLISH } state_{NOT_INITIALIZED};
|
||||||
float shunt_resistance_ohm_;
|
float shunt_resistance_ohm_;
|
||||||
float max_current_a_;
|
float max_current_a_;
|
||||||
AdcTime adc_time_voltage_{AdcTime::ADC_TIME_1100US};
|
AdcTime adc_time_voltage_{AdcTime::ADC_TIME_1100US};
|
||||||
AdcTime adc_time_current_{AdcTime::ADC_TIME_1100US};
|
AdcTime adc_time_current_{AdcTime::ADC_TIME_1100US};
|
||||||
AdcAvgSamples adc_avg_samples_{AdcAvgSamples::ADC_AVG_SAMPLES_4};
|
AdcAvgSamples adc_avg_samples_{AdcAvgSamples::ADC_AVG_SAMPLES_4};
|
||||||
uint32_t calibration_lsb_;
|
uint32_t calibration_lsb_;
|
||||||
|
|
||||||
|
uint32_t last_start_time_{0};
|
||||||
|
bool low_power_mode_{false};
|
||||||
|
ConfigurationRegister config_reg_;
|
||||||
|
|
||||||
sensor::Sensor *bus_voltage_sensor_{nullptr};
|
sensor::Sensor *bus_voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *shunt_voltage_sensor_{nullptr};
|
sensor::Sensor *shunt_voltage_sensor_{nullptr};
|
||||||
sensor::Sensor *current_sensor_{nullptr};
|
sensor::Sensor *current_sensor_{nullptr};
|
||||||
sensor::Sensor *power_sensor_{nullptr};
|
sensor::Sensor *power_sensor_{nullptr};
|
||||||
|
|
||||||
int32_t twos_complement_(int32_t val, uint8_t bits);
|
int32_t twos_complement_(int32_t val, uint8_t bits);
|
||||||
|
void stop_measurements_();
|
||||||
|
void start_measurements_();
|
||||||
|
bool is_conversion_ready_();
|
||||||
|
void get_and_publish_data_();
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ina226
|
} // namespace ina226
|
||||||
|
@ -23,6 +23,12 @@ DEPENDENCIES = ["i2c"]
|
|||||||
|
|
||||||
CONF_ADC_AVERAGING = "adc_averaging"
|
CONF_ADC_AVERAGING = "adc_averaging"
|
||||||
CONF_ADC_TIME = "adc_time"
|
CONF_ADC_TIME = "adc_time"
|
||||||
|
CONF_OPERATING_MODE = "operating_mode"
|
||||||
|
|
||||||
|
OPERATING_MODES = {
|
||||||
|
"continuous": False,
|
||||||
|
"low_power": True,
|
||||||
|
}
|
||||||
|
|
||||||
ina226_ns = cg.esphome_ns.namespace("ina226")
|
ina226_ns = cg.esphome_ns.namespace("ina226")
|
||||||
INA226Component = ina226_ns.class_(
|
INA226Component = ina226_ns.class_(
|
||||||
@ -105,6 +111,9 @@ CONFIG_SCHEMA = (
|
|||||||
cv.Optional(CONF_ADC_AVERAGING, default=4): cv.enum(
|
cv.Optional(CONF_ADC_AVERAGING, default=4): cv.enum(
|
||||||
ADC_AVG_SAMPLES, int=True
|
ADC_AVG_SAMPLES, int=True
|
||||||
),
|
),
|
||||||
|
cv.Optional(CONF_OPERATING_MODE, default="continuous"): cv.enum(
|
||||||
|
OPERATING_MODES, lower=True
|
||||||
|
),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
.extend(cv.polling_component_schema("60s"))
|
.extend(cv.polling_component_schema("60s"))
|
||||||
@ -129,6 +138,7 @@ async def to_code(config):
|
|||||||
cg.add(var.set_adc_time_current(adc_time_config))
|
cg.add(var.set_adc_time_current(adc_time_config))
|
||||||
|
|
||||||
cg.add(var.set_adc_avg_samples(config[CONF_ADC_AVERAGING]))
|
cg.add(var.set_adc_avg_samples(config[CONF_ADC_AVERAGING]))
|
||||||
|
cg.add(var.set_enable_low_power_mode(config[CONF_OPERATING_MODE]))
|
||||||
|
|
||||||
if CONF_BUS_VOLTAGE in config:
|
if CONF_BUS_VOLTAGE in config:
|
||||||
sens = await sensor.new_sensor(config[CONF_BUS_VOLTAGE])
|
sens = await sensor.new_sensor(config[CONF_BUS_VOLTAGE])
|
||||||
|
Loading…
Reference in New Issue
Block a user