INA226 low power mode

This commit is contained in:
Anton Viktorov 2024-03-18 17:11:17 +01:00
parent e753ac3a97
commit aa506f665d
3 changed files with 115 additions and 12 deletions

View File

@ -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_CURRENT = 0x04;
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_AVG_SAMPLES[] = {1, 4, 16, 64, 128, 256, 512, 1024};
static const uint32_t INA226_TIMEOUT_MS = 10 * 1000;
void INA226Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up INA226...");
ConfigurationRegister config;
config.reset = 1;
if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) {
this->config_reg_.reset = 1;
if (!this->write_byte_16(INA226_REGISTER_CONFIG, this->config_reg_.raw)) {
this->mark_failed();
return;
}
delay(1);
config.raw = 0;
config.reserved = 0b100; // as per datasheet
this->config_reg_.raw = 0;
this->config_reg_.reserved = 0b100; // as per datasheet
// 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)
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)
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)
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();
return;
}
@ -106,6 +110,44 @@ void INA226Component::dump_config() {
float INA226Component::get_setup_priority() const { return setup_priority::DATA; }
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) {
uint16_t 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;
}
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 esphome

View File

@ -29,10 +29,21 @@ enum AdcAvgSamples : uint16_t {
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 {
uint16_t raw;
struct {
uint16_t mode : 3;
AdcMode mode : 3;
AdcTime shunt_voltage_conversion_time : 3;
AdcTime bus_voltage_conversion_time : 3;
AdcAvgSamples avg_samples : 3;
@ -47,12 +58,14 @@ class INA226Component : public PollingComponent, public i2c::I2CDevice {
void dump_config() override;
float get_setup_priority() const override;
void update() override;
void loop() override;
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_adc_time_voltage(AdcTime time) { adc_time_voltage_ = 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_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_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; }
protected:
enum State { NOT_INITIALIZED, IDLE, WAITING_FOR_DATA, READY_TO_PUBLISH } state_{NOT_INITIALIZED};
float shunt_resistance_ohm_;
float max_current_a_;
AdcTime adc_time_voltage_{AdcTime::ADC_TIME_1100US};
AdcTime adc_time_current_{AdcTime::ADC_TIME_1100US};
AdcAvgSamples adc_avg_samples_{AdcAvgSamples::ADC_AVG_SAMPLES_4};
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 *shunt_voltage_sensor_{nullptr};
sensor::Sensor *current_sensor_{nullptr};
sensor::Sensor *power_sensor_{nullptr};
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

View File

@ -23,6 +23,12 @@ DEPENDENCIES = ["i2c"]
CONF_ADC_AVERAGING = "adc_averaging"
CONF_ADC_TIME = "adc_time"
CONF_OPERATING_MODE = "operating_mode"
OPERATING_MODES = {
"continuous": False,
"low_power": True,
}
ina226_ns = cg.esphome_ns.namespace("ina226")
INA226Component = ina226_ns.class_(
@ -105,6 +111,9 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_ADC_AVERAGING, default=4): cv.enum(
ADC_AVG_SAMPLES, int=True
),
cv.Optional(CONF_OPERATING_MODE, default="continuous"): cv.enum(
OPERATING_MODES, lower=True
),
}
)
.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_avg_samples(config[CONF_ADC_AVERAGING]))
cg.add(var.set_enable_low_power_mode(config[CONF_OPERATING_MODE]))
if CONF_BUS_VOLTAGE in config:
sens = await sensor.new_sensor(config[CONF_BUS_VOLTAGE])