INA226 I2C Current/Power Monitor

The Ina226 class implements support for the Texas Instruments INA226 current and power monitor. It exposes helpers to configure averaging and conversion times, program calibration based on shunt resistance and desired current LSB, and read shunt voltage, bus voltage, current, and power.

API Reference

Header File

Classes

class Ina226 : public espp::BasePeripheral<uint8_t, true>

INA226 Current/Power Monitor (I2C)

Provides helpers to configure and read bus voltage, shunt voltage, current, and power. Uses BasePeripheral for I2C access, 8-bit register addresses.

Section: Example

extern "C" void app_main(void) {
  espp::Logger logger({.tag = "INA226 Example", .level = espp::Logger::Verbosity::INFO});

  espp::I2c i2c({
      .port = I2C_NUM_1,
      .sda_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SDA_GPIO,
      .scl_io_num = (gpio_num_t)CONFIG_EXAMPLE_I2C_SCL_GPIO,
      .clk_speed = CONFIG_EXAMPLE_I2C_CLOCK_SPEED_HZ,
  });

  // since the ina226 can have addresses ranging from 0x40 to 0x4F, we probe for
  // it
#if defined(CONFIG_EXAMPLE_USE_STATIC_ADDRESS)
  uint8_t address = CONFIG_EXAMPLE_I2C_ADDRESS;
#else
  uint8_t address = espp::Ina226::DEFAULT_ADDRESS;
  static constexpr uint8_t max_address = 0x4F;
  while (!i2c.probe_device(address) && address <= max_address) {
    address++;
  }
  if (address > max_address) {
    logger.info("INA226 not found on I2C bus!");
    return;
  }
#endif

  logger.info("Using INA226 address 0x{:02X}", address);

  // Configure INA226 with reasonable defaults and known shunt resistor
  espp::Ina226 ina({
      .device_address = address,
      .averaging = espp::Ina226::Avg::AVG_16,
      .bus_conv_time = espp::Ina226::ConvTime::MS_1_1,
      .shunt_conv_time = espp::Ina226::ConvTime::MS_1_1,
      .mode = espp::Ina226::Mode::SHUNT_BUS_CONT,
      .current_lsb = 0.001f,         // 1mA / LSB
      .shunt_resistance_ohms = 0.1f, // 0.1 ohm
      .probe = std::bind(&espp::I2c::probe_device, &i2c, std::placeholders::_1),
      .write = std::bind(&espp::I2c::write, &i2c, std::placeholders::_1, std::placeholders::_2,
                         std::placeholders::_3),
      .read_register =
          std::bind(&espp::I2c::read_at_register, &i2c, std::placeholders::_1,
                    std::placeholders::_2, std::placeholders::_3, std::placeholders::_4),
      .write_then_read =
          std::bind(&espp::I2c::write_read, &i2c, std::placeholders::_1, std::placeholders::_2,
                    std::placeholders::_3, std::placeholders::_4, std::placeholders::_5),
      .log_level = espp::Logger::Verbosity::WARN,
  });

  std::error_code ec;
  // Optional explicit initialize
  ina.initialize(ec);
  if (ec) {
    logger.info("INA226 init failed: {}", ec.message());
    return;
  }

  auto task_fn = [&](std::mutex &m, std::condition_variable &cv) {
    auto now = std::chrono::high_resolution_clock::now();

    float vbus = ina.bus_voltage_volts(ec);
    float vshunt = ina.shunt_voltage_volts(ec);
    float current = ina.current_amps(ec);
    float power = ina.power_watts(ec);
    if (ec) {
      logger.info("INA226 read failed: {}", ec.message());
      return true; // stop
    }
    logger.info("Vbus={:.3f}V, Vshunt={:.6f}V, I={:.3f}A, P={:.3f}W", vbus, vshunt, current, power);

    {
      std::unique_lock<std::mutex> lk(m);
      cv.wait_until(lk, now + 250ms);
    }
    return false;
  };

  espp::Task task({.callback = task_fn,
                   .task_config = {.name = "INA226 Task", .stack_size_bytes = 4 * 1024},
                   .log_level = espp::Logger::Verbosity::WARN});
  task.start();

  while (true) {
    std::this_thread::sleep_for(1s);
  }
}

Public Types

enum class Avg : uint16_t

Averaging (AVG) field values (bits 14..12 of CONFIG)

Values:

enumerator AVG_1
enumerator AVG_4
enumerator AVG_16
enumerator AVG_64
enumerator AVG_128
enumerator AVG_256
enumerator AVG_512
enumerator AVG_1024
enum class ConvTime : uint16_t

Bus/Shunt conversion time (VBUSCT/VSHCT) field values (bits 11..9, 8..6)

Values:

enumerator US_140
enumerator US_204
enumerator US_332
enumerator US_588
enumerator MS_1_1
enumerator MS_2_116
enumerator MS_4_156
enumerator MS_8_244
enum class Mode : uint16_t

Operating mode (MODE bits 2..0)

Values:

enumerator POWER_DOWN
enumerator SHUNT_TRIG
enumerator BUS_TRIG
enumerator SHUNT_BUS_TRIG
enumerator ADC_OFF
enumerator SHUNT_CONT
enumerator BUS_CONT
enumerator SHUNT_BUS_CONT
typedef std::function<bool(uint8_t)> probe_fn

Function to probe the peripheral

Param address:

The address to probe

Return:

True if the peripheral is found at the given address

Public Functions

inline explicit Ina226(const Config &config)

Constructor with configuration

Parameters:

config – Configuration structure with device address, averaging, conversion times, mode, current LSB, shunt resistance, and I/O hooks

inline bool initialize(std::error_code &ec)

Initialize the INA226

Parameters:

ec – Error code to capture any initialization errors

Returns:

true if initialization succeeded, false if it failed

inline uint16_t manufacturer_id(std::error_code &ec)

Read manufacturer ID (0x5449 for Texas Instruments)

Parameters:

ec – Error code to capture any read errors

Returns:

Manufacturer ID (0x5449) or 0 on error

inline uint16_t die_id(std::error_code &ec)

Read die ID (0x2260 for INA226)

Parameters:

ec – Error code to capture any read errors

Returns:

Die ID (0x2260) or 0 on error

inline float shunt_voltage_volts(std::error_code &ec)

Read shunt voltage in volts

Note

The shunt voltage is signed, so it can be negative if the current flows in the reverse direction. The LSB is 2.5 uV

Parameters:

ec – Error code to capture any read errors

Returns:

Shunt voltage in volts, or 0.0f on error

inline float bus_voltage_volts(std::error_code &ec)

Read bus voltage in volts

Note

The bus voltage is unsigned, so it cannot be negative. The LSB is 1.25 mV

Parameters:

ec – Error code to capture any read errors

Returns:

Bus voltage in volts, or 0.0f on error

inline float current_amps(std::error_code &ec)

Read current in amps

Note

The current is signed, so it can be negative if the current flows in the reverse direction. The LSB is set via the calibrate() method and is in Amps/LSB.

Parameters:

ec – Error code to capture any read errors

Returns:

Current in amps, or 0.0f on error

inline float power_watts(std::error_code &ec)

Read power in watts

Note

The power is unsigned, so it cannot be negative. The LSB is 25 * current_lsb, where current_lsb is set via the calibrate() method.

Parameters:

ec – Error code to capture any read errors

Returns:

Power in watts, or 0.0f on error

inline bool configure(Avg avg, ConvTime vbus, ConvTime vshunt, Mode mode, std::error_code &ec)

Configure the INA226 with averaging, conversion times, and mode

Parameters:
  • avg – Averaging mode

  • vbus – Bus voltage conversion time

  • vshunt – Shunt voltage conversion time

  • mode – Operating mode

  • ec – Error code to capture any write errors

Returns:

true if configuration succeeded, false if it failed

inline bool calibrate(float current_lsb, float shunt_res_ohms, std::error_code &ec)

Calibrate the INA226 with current LSB and shunt resistance

Note

This function programs the CALIBRATION register based on the provided current_lsb and shunt resistance. The current_lsb is used to determine the scaling of the CURRENT and POWER registers. The shunt resistance is used to calculate the calibration value.

Note

The current_lsb should be chosen based on the expected maximum current and the resolution required. A smaller current_lsb provides higher resolution but reduces the maximum measurable current.

Note

Per the datasheet, the calibration value is calculated as:

\[ Cal = floor(0.00512 / (current_lsb * shunt_res_ohms)) \]
This ensures that the calibration value is non-zero and avoids division by zero. If the calculated value is zero, it is set to 1 to avoid issues. This function should be called after configuring the INA226.

Parameters:
  • current_lsb – Current LSB in Amps/LSB

  • shunt_res_ohms – Shunt resistance in Ohms

  • ec – Error code to capture any write errors

Returns:

true if calibration succeeded, false if it failed

inline bool probe(std::error_code &ec)

Probe the peripheral

Note

This function is thread safe

Note

If the probe function is not set, this function will return false and set the error code to operation_not_supported

Note

This function is only available if UseAddress is true

Parameters:

ec – The error code to set if there is an error

Returns:

True if the peripheral is found

inline void set_address(uint8_t address)

Set the address of the peripheral

Note

This function is thread safe

Note

This function is only available if UseAddress is true

Parameters:

address – The address of the peripheral

inline void set_probe(const probe_fn &probe)

Set the probe function

Note

This function is thread safe

Note

This should rarely be used, as the probe function is usually set in the constructor. If you need to change the probe function, consider using the set_config function instead.

Note

This function is only available if UseAddress is true

Parameters:

probe – The probe function

inline void set_write(const write_fn &write)

Set the write function

Note

This function is thread safe

Note

This should rarely be used, as the write function is usually set in the constructor. If you need to change the write function, consider using the set_config function instead.

Parameters:

write – The write function

inline void set_read(const read_fn &read)

Set the read function

Note

This function is thread safe

Note

This should rarely be used, as the read function is usually set in the constructor. If you need to change the read function, consider using the set_config function instead.

Parameters:

read – The read function

inline void set_read_register(const read_register_fn &read_register)

Set the read register function

Note

This function is thread safe

Note

This should rarely be used, as the read register function is usually set in the constructor. If you need to change the read register function, consider using the set_config function instead.

Parameters:

read_register – The read register function

inline void set_write_then_read(const write_then_read_fn &write_then_read)

Set the write then read function

Note

This function is thread safe

Note

This should rarely be used, as the write then read function is usually set in the constructor. If you need to change the write then

Parameters:

write_then_read – The write then read function

inline void set_separate_write_then_read_delay(const std::chrono::milliseconds &delay)

Set the delay between the write and read operations in write_then_read

Note

This function is thread safe

Note

This should rarely be used, as the delay is usually set in the constructor. If you need to change the delay, consider using the set_config function instead.

Note

This delay is only used if the write_then_read function is not set to a custom function and the write and read functions are separate functions.

Parameters:

delay – The delay between the write and read operations in write_then_read

inline void set_config(const Config &config)

Set the configuration for the peripheral

Note

This function is thread safe

Note

The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.

Parameters:

config – The configuration for the peripheral

inline void set_config(Config &&config)

Set the configuration for the peripheral

Note

This function is thread safe

Note

The configuration should normally be set in the constructor, but this function can be used to change the configuration after the peripheral has been created - for instance if the peripheral could be found on different communications buses.

Parameters:

config – The configuration for the peripheral

inline const std::string &get_name() const

Get the name of the component

Note

This is the tag of the logger

Returns:

A const reference to the name of the component

inline void set_log_tag(const std::string_view &tag)

Set the tag for the logger

Parameters:

tag – The tag to use for the logger

inline espp::Logger::Verbosity get_log_level() const

Get the log level for the logger

Returns:

The verbosity level of the logger

inline void set_log_level(espp::Logger::Verbosity level)

Set the log level for the logger

Parameters:

level – The verbosity level to use for the logger

inline void set_log_verbosity(espp::Logger::Verbosity level)

Set the log verbosity for the logger

See also

set_log_level

Note

This is a convenience method that calls set_log_level

Parameters:

level – The verbosity level to use for the logger

inline espp::Logger::Verbosity get_log_verbosity() const

Get the log verbosity for the logger

See also

get_log_level

Note

This is a convenience method that calls get_log_level

Returns:

The verbosity level of the logger

inline void set_log_rate_limit(std::chrono::duration<float> rate_limit)

Set the rate limit for the logger

Note

Only calls to the logger that have _rate_limit suffix will be rate limited

Parameters:

rate_limit – The rate limit to use for the logger

Public Static Attributes

static constexpr uint8_t DEFAULT_ADDRESS = 0x40

Default I2C address if A0/A1=GND.

Address is configured based on whether A0/A1 are connected to GND, VCC, SDA, or SCL, address can be 0x40..0x4F

struct Config

Configuration structure for INA226.

Public Members

uint8_t device_address = DEFAULT_ADDRESS

I2C address, 0x40..0x4F.

ConvTime bus_conv_time = ConvTime::MS_1_1

Bus conversion time.

ConvTime shunt_conv_time = ConvTime::MS_1_1

Shunt conversion time.

Mode mode = Mode::SHUNT_BUS_CONT

Operating mode.

BasePeripheral::probe_fn probe = {nullptr}

Probe function to check I2C device presence.

BasePeripheral::write_fn write = {nullptr}

Write function to send data to I2C device.

BasePeripheral::read_register_fn read_register{nullptr}

Read function to read a register from I2C device.

BasePeripheral::write_then_read_fn write_then_read{nullptr}

Write then read function for I2C device.

bool auto_init = true

Automatically initialize on construction.

Logger::Verbosity log_level = {Logger::Verbosity::WARN}

Log level for INA226 messages.