# INA226 for ESP-IDF (ESP32)
- [INA226 for ESP-IDF (ESP32)](#ina226-for-esp-idf-esp32)
- [Introduction](#introduction)
- [Feature overview](#feature-overview)
- [Register map](#register-map)
- [Operating modes (`MODE` field, bits `2:0` of register `00h`)](#operating-modes-mode-field-bits-20-of-register-00h)
- [`ALERT` pin](#alert-pin)
- [Sensor configuration](#sensor-configuration)
- [`ina226_config_t` parameters](#ina226_config_t-parameters)
- [Allowed values](#allowed-values)
- [Can I skip manual config?](#can-i-skip-manual-config)
- [Single-shot measurement mode](#single-shot-measurement-mode)
- [Continuous measurement mode](#continuous-measurement-mode)
- [`ALERT` interrupt on threshold crossing](#alert-interrupt-on-threshold-crossing)
- [`ALERT` interrupts on conversion ready](#alert-interrupts-on-conversion-ready)
- [Power-Down mode](#power-down-mode)
- [API](#api)
- [Creation and lifecycle](#creation-and-lifecycle)
- [Measurements and operating modes](#measurements-and-operating-modes)
- [Reading measured values](#reading-measured-values)
- [ALERT and threshold events](#alert-and-threshold-events)
- [Default configuration](#default-configuration)
- [License](#license)
## Introduction
`INA226` is a digital power monitor with an I2C interface. It measures bus voltage (`VBUS`) and shunt voltage drop (`VSHUNT`), and also calculates current and power (after calibration).
### Feature overview
- Measurement of:
- bus voltage (`Bus Voltage`, register `02h`),
- shunt voltage (`Shunt Voltage`, register `01h`),
- current (`Current`, register `04h`, after writing calibration),
- power (`Power`, register `03h`, after writing calibration).
- Flexible ADC setup via configuration register `00h`:
- averaging (`AVG`),
- conversion time for `VBUS` and `VSHUNT` (`VBUSCT`, `VSHCT`),
- operating mode (`MODE`).
- Software reset support (`RST`, bit 15 of register `00h`).
- Calibration for a specific shunt via register `05h` (`Calibration`).
- Support for hardware `ALERT` output:
- threshold events (over/under limit),
- data-ready signal (`Conversion Ready`).
- Chip communication check via `Die ID` (`FFh`, default value `2260h`).
### Register map
| Address | Access | Register | Default | Purpose |
| :---: | :---: | :--- | :---: | :--- |
| `00h` | R/W | Configuration | `4127h` | ADC setup, averaging, conversion time, mode, reset |
| `01h` | R | Shunt Voltage | `0000h` | Measured shunt voltage drop |
| `02h` | R | Bus Voltage | `0000h` | Measured bus voltage |
| `03h` | R | Power | `0000h` | Calculated power (after calibration) |
| `04h` | R | Current | `0000h` | Calculated current (after calibration) |
| `05h` | R/W | Calibration | `0000h` | Calibration coefficient |
| `06h` | R/W | Mask/Enable | `0000h` | Masks, flags, and `ALERT` polarity/latch |
| `07h` | R/W | Alert Limit | `0000h` | Threshold for `ALERT` triggering |
| `FFh` | R | Die ID | `2260h` | Chip identifier |
### Operating modes (`MODE` field, bits `2:0` of register `00h`)
| MODE | Mode |
| :--: | :--- |
| `000` | Power-Down (disabled) |
| `001` | Shunt Voltage, trigger (single shunt measurement) |
| `010` | Bus Voltage, trigger (single bus measurement) |
| `011` | Shunt + Bus, trigger (single shunt and bus measurement) |
| `100` | Power-Down (disabled) |
| `101` | Shunt Voltage, continuous |
| `110` | Bus Voltage, continuous |
| `111` | Shunt + Bus, continuous (default mode) |
### `ALERT` pin
The `ALERT` output is configured through the `Mask/Enable` (`06h`) and `Alert Limit` (`07h`) registers.
- Threshold events:
- `SOL`/`SUL` — shunt voltage out-of-range,
- `BOL`/`BUL` — bus voltage out-of-range,
- `POL` — power threshold exceeded.
- Data-ready event:
- `CNVR` (source mask),
- `CVRF` (conversion-ready flag).
- Service bits:
- `AFF` — alert function flag,
- `OVF` — math overflow,
- `APOL` — `ALERT` polarity (`0` — active-low, `1` — active-high),
- `LEN` — output mode (`0` — transparent, `1` — latched).
## Sensor configuration
After `ina226_create()`, configure the device using `ina226_init(sensor, bus, cfg)`.
- If `cfg != NULL`, your values from `ina226_config_t` are used.
- If `cfg == NULL`, the library uses `INA226_DEFAULT_CONFIG()`.
Important: after `ina226_init()`, the driver leaves INA226 in `Power-Down` mode; starting measurements is done separately (`ina226_measure()`, `ina226_start_single()`, or `ina226_start_continuous()`).
### `ina226_config_t` parameters
```c
typedef struct {
float r_shunt_ohms;
float max_expected_amps;
ina226_avg_t avg;
ina226_conv_t bus_ct;
ina226_conv_t shunt_ct;
uint8_t address;
uint8_t measurements;
} ina226_config_t;
```
| Field | Meaning | Default value |
| :--- | :--- | :--- |
| `r_shunt_ohms` | Shunt resistance, ohms. Used for calibration and current/power conversion. | `0.1f` |
| `max_expected_amps` | Expected maximum current, amps. Affects `Current_LSB` and the calibration coefficient. | `1.0f` |
| `avg` | ADC averaging (`AVG`, register `00h`). | `INA226_AVG_1` |
| `bus_ct` | `VBUS` channel conversion time (`VBUSCT`, register `00h`). | `INA226_CONV_TIME_1_1MS` |
| `shunt_ct` | `VSHUNT` channel conversion time (`VSHCT`, register `00h`). | `INA226_CONV_TIME_1_1MS` |
| `address` | 7-bit INA226 I2C address. | `INA226_I2C_DEFAULT_ADDR` (`0x40`) |
| `measurements` | Bitmask of measured/read values (`ina226_measurements_t`). | `INA226_MEAS_ALL` |
### Allowed values
- `avg`: `INA226_AVG_1`, `INA226_AVG_4`, `INA226_AVG_16`, `INA226_AVG_64`, `INA226_AVG_128`, `INA226_AVG_256`, `INA226_AVG_512`, `INA226_AVG_1024`.
- `bus_ct` / `shunt_ct`: `INA226_CONV_TIME_140US`, `INA226_CONV_TIME_204US`, `INA226_CONV_TIME_332US`, `INA226_CONV_TIME_588US`, `INA226_CONV_TIME_1_1MS`, `INA226_CONV_TIME_2_116MS`, `INA226_CONV_TIME_4_156MS`, `INA226_CONV_TIME_8_244MS`.
- `address`: from `0x40` to `0x4F` inclusive.
- `measurements`: combination of flags:
- `INA226_MEAS_BUS_VOLTAGE`
- `INA226_MEAS_SHUNT_VOLTAGE`
- `INA226_MEAS_CURRENT`
- `INA226_MEAS_POWER`
- or simply `INA226_MEAS_ALL`
> You must not pass an empty mask (`0`). At least one channel must be selected.
### Can I skip manual config?
Yes. You can skip configuration entirely and pass `NULL`:
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL));
```
This is a valid and recommended quick-start option (and it is what the `examples/*` use).
If you need precise tuning for your hardware (different shunt, different address, different timings/averaging), create a config from defaults and change only required fields:
```c
ina226_config_t cfg = INA226_DEFAULT_CONFIG();
cfg.r_shunt_ohms = 0.02f;
cfg.max_expected_amps = 10.0f;
cfg.avg = INA226_AVG_16;
cfg.bus_ct = INA226_CONV_TIME_2_116MS;
cfg.shunt_ct = INA226_CONV_TIME_2_116MS;
cfg.address = 0x41;
cfg.measurements = INA226_MEAS_ALL;
ESP_ERROR_CHECK(ina226_init(sensor, bus, &cfg));
```
## Single-shot measurement mode
Single-shot (triggered) mode is convenient when measurements are needed on demand: for example once per second, on an event, or on command from another module.
This library provides two workflows:
1. `ina226_measure(sensor, &measure)` — the simplest way:
- starts one conversion in trigger mode,
- waits computed conversion time (taking `AVG`, `VBUSCT`, and `VSHCT` into account),
- checks readiness,
- reads selected channels into `ina226_measure_t`.
2. `ina226_start_single(sensor)` + `ina226_read_measure(sensor, &measure)` — manual way:
- conversion start is separate,
- waiting/synchronization is fully controlled by you (for example via `ALERT`/`CNVR`),
- reading is done in a separate call.
`MODE` is chosen automatically from `cfg.measurements`:
- only `INA226_MEAS_BUS_VOLTAGE` → `Bus trigger` (`010`),
- only shunt/current (without power) → `Shunt trigger` (`001`),
- `INA226_MEAS_POWER` (even without other flags), mixed sets, or `INA226_MEAS_ALL` → `Shunt + Bus trigger` (`011`).
Example (as in `examples/triggered_mode`):
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL));
while (true) {
ina226_measure_t measure = {0};
esp_err_t err = ina226_measure(sensor, &measure);
if (err == ESP_OK) {
// Use measure.bus_voltage_v, measure.shunt_voltage_mv,
// measure.current_a, measure.power_w
}
vTaskDelay(pdMS_TO_TICKS(1000));
}
```
> In triggered mode, each new measurement cycle requires a new start (via `ina226_measure()` or `ina226_start_single()`).
## Continuous measurement mode
Continuous mode is suitable for nonstop monitoring: INA226 continuously performs conversions by itself, and the MCU only reads fresh values.
Basic flow:
1. `ina226_start_continuous(sensor)` — switches the sensor into continuous mode.
2. Periodically check data readiness with `ina226_get_ready(sensor, &ready)`.
3. When `ready == true`, read values via `ina226_read_measure(sensor, &measure)`.
4. Stop measurements when needed: `ina226_stop_continuous(sensor)` (returns to `Power-Down`).
`MODE` is also selected automatically from the `measurements` mask:
- only `INA226_MEAS_BUS_VOLTAGE` → `Bus continuous` (`110`),
- only shunt/current (without power) → `Shunt continuous` (`101`),
- `INA226_MEAS_POWER` (even without other flags), mixed sets, or `INA226_MEAS_ALL` → `Shunt + Bus continuous` (`111`).
Example (as in `examples/continuous_mode`):
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL));
ESP_ERROR_CHECK(ina226_start_continuous(sensor));
while (true) {
bool ready = false;
while (!ready) {
ESP_ERROR_CHECK(ina226_get_ready(sensor, &ready));
if (!ready) {
vTaskDelay(pdMS_TO_TICKS(10));
}
}
ina226_measure_t measure = {0};
ESP_ERROR_CHECK(ina226_read_measure(sensor, &measure));
// Use measure.*
}
```
> `ina226_read_measure()` does not change INA226 mode; it only reads selected channel registers.
## `ALERT` interrupt on threshold crossing
This mode is used when you need a hardware signal as soon as a measured value crosses a threshold (for example, `VBUS` drops below an acceptable level).
Basic flow:
1. Initialize sensor: `ina226_init(sensor, bus, cfg_or_null)`.
2. Configure source/condition/threshold: `ina226_set_alert(...)`.
3. Start continuous measurements: `ina226_start_continuous(sensor)`.
4. In ISR/task, read status via `ina226_get_alert_status(sensor, &status)`.
Example (as in `examples/threshold_alert`, trigger when `VBUS < 2.6V`):
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL));
ESP_ERROR_CHECK(ina226_set_alert(sensor,
INA226_ALERT_BUS_VOLTAGE,
INA226_ALERT_MODE_LATCHED,
INA226_ALERT_BELOW,
2.6f));
ESP_ERROR_CHECK(ina226_start_continuous(sensor));
// ... after ALERT IRQ
ina226_alert_status_t status = INA226_ALERT_STATUS_NONE;
ESP_ERROR_CHECK(ina226_get_alert_status(sensor, &status));
if (status == INA226_ALERT_STATUS_BUS_UNDER_LIMIT) {
float vbus = 0.0f;
ESP_ERROR_CHECK(ina226_read_bus_voltage(sensor, &vbus));
// fault handling
}
```
Important:
- `INA226_ALERT_MODE_TRANSPARENT` (`LEN=0`) — `ALERT` is released automatically when the condition disappears.
- `INA226_ALERT_MODE_LATCHED` (`LEN=1`) — `ALERT` stays latched until register `06h` is read.
- In this library, reading `ina226_get_alert_status()` / `ina226_get_alert_flags()` also acknowledges the event (ACK): it clears `ALERT` latch (when `LEN=1`) and clears corresponding status flags.
## `ALERT` interrupts on conversion ready
This mode is useful to synchronize reading of fresh data without constant polling.
To enable it, use:
- `ina226_set_conversion_ready_alert(sensor, true)` — enables `CNVR`, sets `ALERT` to transparent mode, and disables threshold sources so `ALERT` is used only for conversion-ready events.
Typical flow (as in `examples/conversion_ready`):
1. `ina226_set_conversion_ready_alert(sensor, true)`.
2. Start measurement (`ina226_start_single(sensor)` for single-shot or continuous mode).
3. Wait for `ALERT` IRQ.
4. In task, read `ina226_get_alert_status(sensor, &status)` and ensure `status == INA226_ALERT_STATUS_CONVERSION_READY`.
5. Read data using `ina226_read_measure(sensor, &measure)`.
Single-shot example:
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL));
ESP_ERROR_CHECK(ina226_set_conversion_ready_alert(sensor, true));
ESP_ERROR_CHECK(ina226_start_single(sensor));
// wait IRQ ...
ina226_alert_status_t status = INA226_ALERT_STATUS_NONE;
ESP_ERROR_CHECK(ina226_get_alert_status(sensor, &status));
if (status == INA226_ALERT_STATUS_CONVERSION_READY) {
ina226_measure_t measure = {0};
ESP_ERROR_CHECK(ina226_read_measure(sensor, &measure));
// handle measure.*
}
```
> Note: `ina226_get_ready(sensor, &ready)` also checks readiness via `CVRF` (by reading `06h`), but this is a polling approach. For IRQ-based flow, `ALERT + ina226_get_alert_status()` is usually more convenient.
## Power-Down mode
In `Power-Down`, INA226 does not perform conversions, but registers are still accessible via I2C.
How this library uses it:
- After `ina226_init()`, the device is already in `Power-Down`.
- `ina226_stop_continuous(sensor)` puts the sensor back into `Power-Down`.
- Exit from `Power-Down` by starting measurements:
- `ina226_start_single(sensor)`,
- `ina226_start_continuous(sensor)`,
- or `ina226_measure(sensor, &measure)` (internally starts a trigger cycle).
Power control example:
```c
ESP_ERROR_CHECK(ina226_init(sensor, bus, NULL)); // already in Power-Down
// ... when measurement is needed
ESP_ERROR_CHECK(ina226_start_continuous(sensor));
// ... when measurement is no longer needed
ESP_ERROR_CHECK(ina226_stop_continuous(sensor)); // back to Power-Down
```
In practice, this helps reduce power consumption during pauses between measurements. When designing timings, keep in mind that exit from `Power-Down` takes time (per INA226 datasheet — up to tens of milliseconds, typically up to ~40 ms is used as guidance).
## API
### Creation and lifecycle
- `esp_err_t ina226_create(ina226_handle_t *out_dev)` - Create a driver instance (memory allocation, no I2C operations)
- `esp_err_t ina226_init(ina226_handle_t dev, i2c_master_bus_handle_t bus, ina226_config_t *cfg)` - Initialize sensor on I2C bus, verify chip, reset, calibrate, and apply configuration (after init the device is in `Power-Down`)
- `esp_err_t ina226_destroy(ina226_handle_t dev)` - Remove device from I2C bus and release resources
### Measurements and operating modes
- `esp_err_t ina226_measure(ina226_handle_t dev, ina226_measure_t *measure)` - Perform a full single trigger cycle with ready wait and channel read
- `esp_err_t ina226_start_single(ina226_handle_t dev)` - Start a single trigger measurement without waiting for completion
- `esp_err_t ina226_start_continuous(ina226_handle_t dev)` - Start continuous autonomous measurements
- `esp_err_t ina226_get_ready(ina226_handle_t dev, bool *ready)` - Check new-measurement ready flag (`CVRF`) for polling in continuous mode
- `esp_err_t ina226_stop_continuous(ina226_handle_t dev)` - Stop continuous measurements and switch to `Power-Down`
### Reading measured values
- `esp_err_t ina226_read_measure(ina226_handle_t dev, ina226_measure_t *measure)` - Read all active channels without changing current operating mode
- `esp_err_t ina226_read_bus_voltage(ina226_handle_t dev, float *voltage_v)` - Read bus voltage (V)
- `esp_err_t ina226_read_shunt_voltage(ina226_handle_t dev, float *voltage_mv)` - Read shunt voltage (mV)
- `esp_err_t ina226_read_current(ina226_handle_t dev, float *current_a)` - Read current (A)
- `esp_err_t ina226_read_power(ina226_handle_t dev, float *power_w)` - Read power (W)
- `esp_err_t ina226_set_measurements(ina226_handle_t dev, uint8_t measurements)` - Configure active measurement mask (`ina226_measurements_t`) for `ina226_read_measure()` and `ina226_measure()`
### ALERT and threshold events
- `esp_err_t ina226_set_alert(ina226_handle_t dev, ina226_alert_source_t source, ina226_alert_mode_t mode, ina226_alert_condition_t condition, float threshold)` - Configure ALERT source, mode (transparent/latched), condition (above/below threshold), and threshold
- `esp_err_t ina226_disable_alert(ina226_handle_t dev)` - Disable all ALERT event sources (keeping `APOL` polarity)
- `esp_err_t ina226_set_conversion_ready_alert(ina226_handle_t dev, bool enable)` - Enable/disable “conversion ready” signaling (`CNVR`) on ALERT pin
- `esp_err_t ina226_get_alert_flags(ina226_handle_t dev, uint16_t *flags)` - Read raw flags from Mask/Enable register (`0x06`)
- `esp_err_t ina226_get_alert_status(ina226_handle_t dev, ina226_alert_status_t *status)` - Read and decode reason of last ALERT event
- `const char *ina226_alert_status_to_str(ina226_alert_status_t status)` - Convert ALERT status to human-readable string
### Default configuration
- `#define INA226_DEFAULT_CONFIG()` - Macro that builds `ina226_config_t` with default values (`Rshunt=0.1 Ω`, `Imax=1.0 A`, `AVG=1`, `CT=1.1 ms`, address `INA226_I2C_DEFAULT_ADDR`, measurements `INA226_MEAS_ALL`)
> Note: `ina226_get_alert_flags()` and `ina226_get_alert_status()` read register `0x06` (Mask/Enable), which acknowledges/clears part of INA226 status flags (`CVRF`, and ALERT latch when `LEN=1`).
## License
[MIT](./LICENSE)
idf.py add-dependency "pkolt/ina226^1.0.0"