INA226 - fixed improper work with signed values, added configurable ADC parameters (#6172)

This commit is contained in:
Anton Viktorov 2024-02-19 02:24:59 +01:00 committed by GitHub
parent 062db622f3
commit e1345ae7e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 116 additions and 13 deletions

View file

@ -155,6 +155,7 @@ esphome/components/iaqcore/* @yozik04
esphome/components/ili9xxx/* @clydebarrow @nielsnl68 esphome/components/ili9xxx/* @clydebarrow @nielsnl68
esphome/components/improv_base/* @esphome/core esphome/components/improv_base/* @esphome/core
esphome/components/improv_serial/* @esphome/core esphome/components/improv_serial/* @esphome/core
esphome/components/ina226/* @Sergio303 @latonita
esphome/components/ina260/* @mreditor97 esphome/components/ina260/* @mreditor97
esphome/components/inkbird_ibsth1_mini/* @fkirill esphome/components/inkbird_ibsth1_mini/* @fkirill
esphome/components/inkplate6/* @jesserockz esphome/components/inkplate6/* @jesserockz

View file

@ -0,0 +1 @@
CODEOWNERS = ["@Sergio303", "@latonita"]

View file

@ -33,31 +33,37 @@ 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 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};
void INA226Component::setup() { void INA226Component::setup() {
ESP_LOGCONFIG(TAG, "Setting up INA226..."); ESP_LOGCONFIG(TAG, "Setting up INA226...");
// Config Register
// 0bx000000000000000 << 15 RESET Bit (1 -> trigger reset) ConfigurationRegister config;
if (!this->write_byte_16(INA226_REGISTER_CONFIG, 0x8000)) {
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);
uint16_t config = 0x0000; config.raw = 0;
config.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 |= 0b0000001000000000; config.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 |= 0b0000000100000000; config.bus_voltage_conversion_time = this->adc_time_;
// 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 |= 0b0000000000100000; config.shunt_voltage_conversion_time = this->adc_time_;
// Mode Settings [2:0] Combinations (111 -> Shunt and Bus, Continuous) // Mode Settings [2:0] Combinations (111 -> Shunt and Bus, Continuous)
config |= 0b0000000000000111; config.mode = 0b111;
if (!this->write_byte_16(INA226_REGISTER_CONFIG, config)) { if (!this->write_byte_16(INA226_REGISTER_CONFIG, config.raw)) {
this->mark_failed(); this->mark_failed();
return; return;
} }
@ -87,6 +93,9 @@ void INA226Component::dump_config() {
} }
LOG_UPDATE_INTERVAL(this); LOG_UPDATE_INTERVAL(this);
ESP_LOGCONFIG(TAG, " ADC Conversion Time: %d", INA226_ADC_TIMES[this->adc_time_ & 0b111]);
ESP_LOGCONFIG(TAG, " ADC Averaging Samples: %d", INA226_ADC_AVG_SAMPLES[this->adc_avg_samples_ & 0b111]);
LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_); LOG_SENSOR(" ", "Bus Voltage", this->bus_voltage_sensor_);
LOG_SENSOR(" ", "Shunt Voltage", this->shunt_voltage_sensor_); LOG_SENSOR(" ", "Shunt Voltage", this->shunt_voltage_sensor_);
LOG_SENSOR(" ", "Current", this->current_sensor_); LOG_SENSOR(" ", "Current", this->current_sensor_);
@ -102,7 +111,9 @@ void INA226Component::update() {
this->status_set_warning(); this->status_set_warning();
return; return;
} }
float bus_voltage_v = int16_t(raw_bus_voltage) * 0.00125f; // Convert for 2's compliment and signed value (though always positive)
float bus_voltage_v = this->twos_complement_(raw_bus_voltage, 16);
bus_voltage_v *= 0.00125f;
this->bus_voltage_sensor_->publish_state(bus_voltage_v); this->bus_voltage_sensor_->publish_state(bus_voltage_v);
} }
@ -112,7 +123,9 @@ void INA226Component::update() {
this->status_set_warning(); this->status_set_warning();
return; return;
} }
float shunt_voltage_v = int16_t(raw_shunt_voltage) * 0.0000025f; // Convert for 2's compliment and signed value
float shunt_voltage_v = this->twos_complement_(raw_shunt_voltage, 16);
shunt_voltage_v *= 0.0000025f;
this->shunt_voltage_sensor_->publish_state(shunt_voltage_v); this->shunt_voltage_sensor_->publish_state(shunt_voltage_v);
} }
@ -122,7 +135,9 @@ void INA226Component::update() {
this->status_set_warning(); this->status_set_warning();
return; return;
} }
float current_ma = int16_t(raw_current) * (this->calibration_lsb_ / 1000.0f); // Convert for 2's compliment and signed value
float current_ma = this->twos_complement_(raw_current, 16);
current_ma *= (this->calibration_lsb_ / 1000.0f);
this->current_sensor_->publish_state(current_ma / 1000.0f); this->current_sensor_->publish_state(current_ma / 1000.0f);
} }
@ -139,5 +154,12 @@ void INA226Component::update() {
this->status_clear_warning(); this->status_clear_warning();
} }
int32_t INA226Component::twos_complement_(int32_t val, uint8_t bits) {
if (val & ((uint32_t) 1 << (bits - 1))) {
val -= (uint32_t) 1 << bits;
}
return val;
}
} // namespace ina226 } // namespace ina226
} // namespace esphome } // namespace esphome

View file

@ -7,6 +7,40 @@
namespace esphome { namespace esphome {
namespace ina226 { namespace ina226 {
enum AdcTime : uint16_t {
ADC_TIME_140US = 0,
ADC_TIME_204US = 1,
ADC_TIME_332US = 2,
ADC_TIME_588US = 3,
ADC_TIME_1100US = 4,
ADC_TIME_2116US = 5,
ADC_TIME_4156US = 6,
ADC_TIME_8244US = 7
};
enum AdcAvgSamples : uint16_t {
ADC_AVG_SAMPLES_1 = 0,
ADC_AVG_SAMPLES_4 = 1,
ADC_AVG_SAMPLES_16 = 2,
ADC_AVG_SAMPLES_64 = 3,
ADC_AVG_SAMPLES_128 = 4,
ADC_AVG_SAMPLES_256 = 5,
ADC_AVG_SAMPLES_512 = 6,
ADC_AVG_SAMPLES_1024 = 7
};
union ConfigurationRegister {
uint16_t raw;
struct {
uint16_t mode : 3;
AdcTime shunt_voltage_conversion_time : 3;
AdcTime bus_voltage_conversion_time : 3;
AdcAvgSamples avg_samples : 3;
uint16_t reserved : 3;
uint16_t reset : 1;
} __attribute__((packed));
};
class INA226Component : public PollingComponent, public i2c::I2CDevice { class INA226Component : public PollingComponent, public i2c::I2CDevice {
public: public:
void setup() override; void setup() override;
@ -16,6 +50,9 @@ class INA226Component : public PollingComponent, public i2c::I2CDevice {
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(AdcTime time) { adc_time_ = time; }
void set_adc_avg_samples(AdcAvgSamples samples) { adc_avg_samples_ = samples; }
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; }
void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; } void set_current_sensor(sensor::Sensor *current_sensor) { current_sensor_ = current_sensor; }
@ -24,11 +61,15 @@ class INA226Component : public PollingComponent, public i2c::I2CDevice {
protected: protected:
float shunt_resistance_ohm_; float shunt_resistance_ohm_;
float max_current_a_; float max_current_a_;
AdcTime adc_time_{AdcTime::ADC_TIME_1100US};
AdcAvgSamples adc_avg_samples_{AdcAvgSamples::ADC_AVG_SAMPLES_4};
uint32_t calibration_lsb_; uint32_t calibration_lsb_;
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);
}; };
} // namespace ina226 } // namespace ina226

View file

@ -20,11 +20,44 @@ from esphome.const import (
DEPENDENCIES = ["i2c"] DEPENDENCIES = ["i2c"]
CONF_ADC_AVERAGING = "adc_averaging"
CONF_ADC_TIME = "adc_time"
ina226_ns = cg.esphome_ns.namespace("ina226") ina226_ns = cg.esphome_ns.namespace("ina226")
INA226Component = ina226_ns.class_( INA226Component = ina226_ns.class_(
"INA226Component", cg.PollingComponent, i2c.I2CDevice "INA226Component", cg.PollingComponent, i2c.I2CDevice
) )
AdcTime = ina226_ns.enum("AdcTime")
ADC_TIMES = {
140: AdcTime.ADC_TIME_140US,
204: AdcTime.ADC_TIME_204US,
332: AdcTime.ADC_TIME_332US,
588: AdcTime.ADC_TIME_588US,
1100: AdcTime.ADC_TIME_1100US,
2116: AdcTime.ADC_TIME_2116US,
4156: AdcTime.ADC_TIME_4156US,
8244: AdcTime.ADC_TIME_8244US,
}
AdcAvgSamples = ina226_ns.enum("AdcAvgSamples")
ADC_AVG_SAMPLES = {
1: AdcAvgSamples.ADC_AVG_SAMPLES_1,
4: AdcAvgSamples.ADC_AVG_SAMPLES_4,
16: AdcAvgSamples.ADC_AVG_SAMPLES_16,
64: AdcAvgSamples.ADC_AVG_SAMPLES_64,
128: AdcAvgSamples.ADC_AVG_SAMPLES_128,
256: AdcAvgSamples.ADC_AVG_SAMPLES_256,
512: AdcAvgSamples.ADC_AVG_SAMPLES_512,
1024: AdcAvgSamples.ADC_AVG_SAMPLES_1024,
}
def validate_adc_time(value):
value = cv.positive_time_period_microseconds(value).total_microseconds
return cv.enum(ADC_TIMES, int=True)(value)
CONFIG_SCHEMA = ( CONFIG_SCHEMA = (
cv.Schema( cv.Schema(
{ {
@ -59,6 +92,10 @@ CONFIG_SCHEMA = (
cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All( cv.Optional(CONF_MAX_CURRENT, default=3.2): cv.All(
cv.current, cv.Range(min=0.0) cv.current, cv.Range(min=0.0)
), ),
cv.Optional(CONF_ADC_TIME, default="1100 us"): validate_adc_time,
cv.Optional(CONF_ADC_AVERAGING, default=4): cv.enum(
ADC_AVG_SAMPLES, int=True
),
} }
) )
.extend(cv.polling_component_schema("60s")) .extend(cv.polling_component_schema("60s"))
@ -72,8 +109,9 @@ async def to_code(config):
await i2c.register_i2c_device(var, config) await i2c.register_i2c_device(var, config)
cg.add(var.set_shunt_resistance_ohm(config[CONF_SHUNT_RESISTANCE])) cg.add(var.set_shunt_resistance_ohm(config[CONF_SHUNT_RESISTANCE]))
cg.add(var.set_max_current_a(config[CONF_MAX_CURRENT])) cg.add(var.set_max_current_a(config[CONF_MAX_CURRENT]))
cg.add(var.set_adc_time(config[CONF_ADC_TIME]))
cg.add(var.set_adc_avg_samples(config[CONF_ADC_AVERAGING]))
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])