INA226 Current and Power Sensor

About this post

After reporting on the INA219 in my last post, I would like to introduce the INA226 and my associated library INA226_WE in this article.

In a first approximation, the INA226 is an INA219 with alarm function, which is particularly well suited for monitoring currents. In addition, the INA226 can be used on both the high-side and the low-side. I will come back to further differences to the INA219 during the article.

First of all, I will deal with the measuring principle and the technical data. Then I present the library with its numerous example sketches. Finally, the last part is for those who want to go deeper. It deals with the inner details of the INA226 and the library.

The measuring principle

An INA226 module
An INA226 module, front and back

Basically, the INA226 works the same way as the INA219. You conduct the current to be measured via the terminals IN+ and IN- through a shunt (current resistor). An A/D converter measures the voltage drop across the shunt and the INA226 calculates the current from this.

If you use the bare INA226 (the ten-pin IC on the module), then you are free to choose the size of the shunt. The modules have a shunt of 0.1 ohms. In any case, this applies to all models that I have dealt with.

In addition, the INA226 measures the bus voltage, i.e. the voltage drop across consumer. This happens between VBUS and GND. The INA219, on the other hand, measures the bus voltage between IN and GND. That’s why you have to place the INA219 before the consumer (high-side). With the INA226, you are more flexible, you can use it on both the high side and the low-side.

The INA226 calculates the power from the current and the voltage drop across the consumer. It saves the measured values in its data registers, where you can read them by I2C.

INA226 in high-side configuration
INA226 in high-side configuration

Typical circuit

Typical INA226 circuit (used for examples)
Typical INA226 circuit (used for examples)

I used the above (high-side) circuit for all example sketches. It is important that the INA226 and the consumer have a common GND, otherwise the measurement of the bus voltage will not work. If you swap IN+ and IN- you will get negative values for the shunt voltage and current.

Some technical data of the INA226 module

  • Bus voltage: 0 – 36 volts
  • Maximum bus current: 800 milliampere
  • Supply voltage: 3 – 5.5 volts
  • Power consumption (self-determined):
    • Continuous mode: 0.35 mA
    • Power-Down Mode: 2.3.µA
  • Measurement modes: continuous or on-demand (“triggered”);
  • Averaging of 1, 4, 64, 128, 256, 512 or 1024 individual measurements
  • A/D conversion time adjustable in eight levels: 0.14 to 8.2 ms
  • Data registers:
    • Shunt voltage register
    • Bus voltage register
    • Current register
    • Power register
  • Communication via I2C, 4 addresses adjustable (module back):
    • 0x40: A0, A1 open
    • 0x41: A0 closed, A1 open
    • 0x44: A0 open, A1 closed
    • 0x45: A0, A1 closed
  • Programmable alarm pin for limit violations and available data

Further technical data can be found in the manufacturer’s data sheet.

It is a great pity that the INA226 module does not have a smaller shunt because with 800 mA the current upper limit is not very lush. It’s like driving with the handbrake on. I will explain later why this current limit is only a quarter of the INA219 limit with the same shunt. If you should use the bare INA226 IC you are more flexible. I have implemented a function that enables you to change the resistance value.

Use of the INA226 library

You can download the library INA226_WE here from GitHub or install it directly with the Library Manager of the Arduino IDE.

I’ve created a total of seven example sketches that I use to present the functions of the library. I will focus most on the example of continuous mode. Many of the functions are used in all sketches and therefore only need to be explained once.

Example 1: Continuous Mode

After you have installed the library and wired your INA226, upload the sketch “Continuous.ino”.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

/* There are several ways to create your INA226 object:
 * INA226_WE ina226 = INA226_WE()              -> uses Wire / I2C Address = 0x40
 * INA226_WE ina226 = INA226_WE(ICM20948_ADDR) -> uses Wire / I2C_ADDRESS
 * INA226_WE ina226 = INA226_WE(&wire2)        -> uses the TwoWire object wire2 / I2C_ADDRESS
 * INA226_WE ina226 = INA226_WE(&wire2, I2C_ADDRESS) -> all together
 * Successfully tested with two I2C busses on an ESP32
 */
INA226_WE ina226 = INA226_WE(I2C_ADDRESS);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  //ina226.setAverage(AVERAGE_16); // choose mode and uncomment for change of default

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  //ina226.setConversionTime(CONV_TIME_1100); //choose conversion time and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA226 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Continuous");
  
  ina226.waitUntilConversionCompleted(); //if you comment this line the first data might be zero
}

void loop() {
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 

  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
  
  delay(3000);
}

Parameter setting using the example of continuous mode

INA226_WE ina226 = INA226_WE() creates the IN226 object. You can pass the I2C address and/or a Wire object. The latter allows you to use e.g. both I2C busses of an ESP32.

The function init() activates the INA226 with the default values. To change these basic settings, you can modify parameters in the setup:

  1. Set the number of individual measurements for the shunt and bus voltage conversion with setAverage()
    • 1, 4, 16, 64, 128, 256, 512 or 1024 individual measurements are averaged
  2. Set the A/D conversion time for the shunt and bus voltage with setConvTime()
    • 8 levels adjustable between 140 µs and 8.244 ms
    • Note: obtaining a data set of shunt and bus voltage takes twice the time
  3. Set the measurement mode with setMeasureMode()
    • CONTINUOUS – continuous measurement
    • TRIGGERED – “on request”: I explain in the next example.
    • POWER_DOWN – turns off the INA226. But better use the more comfortable powerDown() function, which is explained below.
    • The INA226 actually allows determining shunt or bus voltages – but I did not implement that. Using my library, the measurements are only available in a double pack.
  4. Set the current range with setCurrentRange()
    • You can set 400 or 800 mA as the maximum current. The smaller the current range, the higher the resolution for the current and the power.

With setCorrectionFactor() you can introduce a correction factor if the current values determined with the INA226 should differ from those determined by you, for example, with calibrated measuring instruments. The factor is the quotient of the exact and INA226 value.

Other functions used in the example

You can query the data registers of the INA226 at any time. They contain the last measured value. Before the first measurement is completed, all values are zero. With waitUntilConversionCompleted() you can wait until the current measurement is complete.

Using readAndClearFlags() the overflow and alarm flags are read. In this example sketch, we need this call only to update the state of the variable overflow, which – if true – signals the overflow of a register.

The functions for reading the data registers, such as getShuntVoltage_mV(), should be self-explanatory.

Calculation of the measurement time

The duration of a measurement is as follows:

Duration = Number_{measurements} (Averages) \cdot Conversion\ Time\cdot 2

Output

And this is what the output of the sketch looks like on the serial monitor:

Output of the continuous sketch
Output of the continuous sketch

Example 2: On-Demand (Triggered) Mode

You set the triggered mode with setMeasureMode(TRIGGERED) (line 47 in Triggered.ino).   Each measurement is started manually with startSingleMeasurement() (line 74). I have programmed the function to automatically wait until the current readings are available. So you don’t need to call waitUntilConversionCompleted() in triggered mode.

Otherwise, the sketch is identical to Continuous.ino.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16           8
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  //ina226.setAverage(AVERAGE_1); // choose mode and uncomment for change of default

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  //ina226.setConversionTime(CONV_TIME_1100); //choose conversion time and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  ina226.setMeasureMode(TRIGGERED); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Triggered");
   
  // ina226.waitUntilConversionCompleted(); //makes no sense - in triggered mode we wait anyway for completed conversion
}

void loop() {
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  ina226.startSingleMeasurement();
  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
  
  delay(3000);
}
 

Example 3: Power-Down Mode

In power-down mode, you bring down the power consumption of the INA226 from approx. 0.35 mA to approx. 2.3 µA (own measurements).

The example sketch PowerDown.ino shows the power-down mode in action. The sketch initializes the INA226 with the default parameters. Five sets of measurements are output every three seconds. The function powerDown() then saves the contents of the configuration register and disables the INA226. The function powerUp() writes back the copy of the configuration register. On the one hand, this writing process awakens the INA226, on the other hand it ensures that the INA226 returns to the previously selected mode (here: continuous).

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  // default parameters are set - for change check the other examples
  ina226.init();
  Serial.println("INA226 Current Sensor Example Sketch - PowerDown");
  Serial.println("Continuous Sampling starts");
  Serial.println();
}

void loop() {
  for(int i=0; i<5; i++){
    continuousSampling();
    delay(3000);
  }
  
  Serial.println("Power down for 10s");
  ina226.powerDown();
  for(int i=0; i<10; i++){
    Serial.print(".");
    delay(1000);
  }
  
  Serial.println("Power up!");
  Serial.println("");
  ina226.powerUp();
}

void continuousSampling(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 

  ina226.readAndClearFlags();
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}
 

Example 4: Conversion Ready Alarm

Now upload the sketch “Continuous_Alert_Controlled.ino”. With this example, you get to know the alert pin. With 1024 the maximum number of measurements to be averaged is used. Then we go to the upper limit of the conversion time, namely 8.244 milliseconds. This means that the combination of shunt and bus voltage measurement takes approximately 16.9 seconds. As measurement mode we select CONTINUOUS. The function enableConvReadyAlert() activates the alert pin, which is active-low by default. The alert pin is connected to the Arduino Pin 2, for which we set up an interrupt.

When a measurement is finished, the alert pin goes to LOW and an interrupt is triggered. The variable “event” becomes true and the if block is processed in the main loop. First, readAndClearFlags() is executed. This will delete the conversion ready flag and also read the overflow flag. The measurement data is read and displayed. The interrupt on pin 2 was deactivated after triggering and is switched on again after the values have been output.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);
// INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  ina226.setAverage(AVERAGE_1024); 

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  ina226.setConversionTime(CONV_TIME_8244); // Conversion ready after conversion time x number of averages x 2
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Continuous_Alert_Controlled");
  
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);

  ina226.enableConvReadyAlert(); // an interrupt will occur on interrupt pin when conversion is ready
}

void loop() {
  if(event){
    ina226.readAndClearFlags(); // reads interrupt and overflow flags and deletes them 
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    event = false;  
  }
  
  delay(100);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}
 

Practical application of the Conversion Ready Alert

During the long 17 seconds between measurements, the Arduino or any other microcontroller you use in your project has nothing to do. This consumes valuable electricity in battery-powered projects. So just send your microcontroller to sleep and let it wake up through the interrupt. If you don’t know how to do it, look here at my post on this subject.

Example 5: Limit Alert

I would like to introduce you to the Limit Alarm using the sketch Limit_Alert.ino. The INA226 is in continuous mode. On the Arduino side, an interrupt on pin 2 is set up again.

The function enableAlertLatch() sets the alarm pin so that it is active when an alarm is raised until it is manually readAndClearFlags() set inactive again. Without this setup, the pin would be automatically reset if the next measured value is within the limits.

With setAlertType() you determine the limit and which of the measured values is observed. You can specify a min or max limit for the shunt voltage, bus voltage, or current. For the power you can only set a max limit. Actually, the INA226 has no alarm function for the current, I implemented this via a detour.

And that’s it. When the set limit is exceeded, the alarm pin becomes active, the interrupt is triggered and the measured values are read. Again, you will read the current values. At fast measuring frequency, therefore, you do not necessarily read exactly the measured value that triggered the alarm. If you wanted to, you could set the INA226 into power-down mode when the alarm is triggered. Then you can take your time to read the measured data.

You have to be a bit careful with the readAndClearFlags() function. When you read the flags to evaluate them, you delete them. If the alarm condition still exists, the alarm pin becomes active again immediately. If this happens before the interrupt is reactivated, everything gets confused. The alarm pin would already be low while the interrupt pin was waiting for a falling edge. It could wait for a very long time! Therefore, readAndClearFlags() is called again after the interrupt has been reactivated. Separating the reading and deletion of the flags would be a little easier to control, but this is not implemented in INA226.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  /* Set Number of measurements for shunt and bus voltage which shall be averaged
  * Mode *     * Number of samples *
  AVERAGE_1            1 (default)
  AVERAGE_4            4
  AVERAGE_16          16
  AVERAGE_64          64
  AVERAGE_128        128
  AVERAGE_256        256
  AVERAGE_512        512
  AVERAGE_1024      1024
  */
  // ina226.setAverage(AVERAGE_1024); 

  /* Set conversion time in microseconds
     One set of shunt and bus voltage conversion will take: 
     number of samples to be averaged x conversion time x 2
     
     * Mode *         * conversion time *
     CONV_TIME_140          140 µs
     CONV_TIME_204          204 µs
     CONV_TIME_332          332 µs
     CONV_TIME_588          588 µs
     CONV_TIME_1100         1.1 ms (default)
     CONV_TIME_2116       2.116 ms
     CONV_TIME_4156       4.156 ms
     CONV_TIME_8244       8.244 ms  
  */
  // ina226.setConversionTime(CONV_TIME_8244); 
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  CONTINUOUS  - Continuous measurements (default)
  */
  //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set Current Range
    * Mode *   * Max Current *
     MA_400          400 mA
     MA_800          800 mA (default)
  */
  //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default
  
  /* If the current values delivered by the INA226 differ by a constant factor
     from values obtained with calibrated equipment you can define a correction factor.
     Correction factor = current delivered from calibrated equipment / current delivered by INA226
  */
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Limit_Alert");
  
  /* In the default mode the limit interrupt flag will be deleted after the next measurement within limits. 
     With enableAltertLatch(), the flag will have to be deleted with readAndClearFlags(). 
  */
    ina226.enableAlertLatch();
  
  /* Set the alert type and the limit
      * Mode *        * Description *           * limit unit *
    SHUNT_OVER     Shunt Voltage over limit          mV
    SHUNT_UNDER    Shunt Voltage under limit         mV
    CURRENT_OVER   Current over limit                mA
    CURRENT_UNDER  Current under limit               mA
    BUS_OVER       Bus Voltage over limit            V
    BUS_UNDER      Bus Voltage under limit           V
    POWER_OVER     Power over limit                  mW
  */
  ina226.setAlertType(POWER_OVER, 230.0);
 
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);
}

void loop() {
  if(event){
    ina226.readAndClearFlags(); // reads interrupt and overflow flags and deletes them 
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    event = false;
    ina226.readAndClearFlags();
  }  
  delay(1000);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
  
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}
 

Example 6: Limit and Conversion Alert

I hope you are still motivated – that is the last example. With the sketch Limit_And_Conversion_Alert.ino I want to show how you can use the limit and the conversion ready alarm side by side. Both alarms are activated as in the previous sketches.

In the event of an alarm, you now want to be able to distinguish which condition triggered the alarm. In case of alarm, read the flags via readAndClearFlags(). This will update the variables limitAlert and convAltert and allow you to query them.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

int interruptPin = 2;
volatile bool event = false;

INA226_WE ina226(I2C_ADDRESS);

void setup() {
  Serial.begin(9600);
  Wire.begin();
  ina226.init();

  // Conversion will be ready after conversion time x number of averages x 2
  ina226.setAverage(AVERAGE_512); 
  ina226.setConversionTime(CONV_TIME_8244); 
  // ina226.setCorrectionFactor(0.95);
  
  Serial.println("INA226 Current Sensor Example Sketch - Limit_And_Conversion_Alert");
  
  /* In the default mode the limit interrupt flag will be deleted after the next measurement within limits. 
     With enableAltertLatch(), the flag will have to be deleted with readAndClearFlags(). 
  */
  ina226.enableAlertLatch();
  
  /* Set the alert type and the limit
      * Mode *        * Description *           * limit unit *
    SHUNT_OVER     Shunt Voltage over limit          mV
    SHUNT_UNDER    Shunt Voltage under limit         mV
    CURRENT_OVER   Current over limit                mA
    CURRENT_UNDER  Current under limit               mA
    BUS_OVER       Bus Voltage over limit            V
    BUS_UNDER      Bus Voltage under limit           V
    POWER_OVER     Power over limit                  mW
  */
  ina226.setAlertType(CURRENT_UNDER, 45.0);
  ina226.enableConvReadyAlert(); // In this example we also enable the conversion ready alert interrupt
 
  attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING);
}

void loop() {
  if(event){
    ina226.readAndClearFlags();
    displayResults();
    attachInterrupt(digitalPinToInterrupt(interruptPin), alert, FALLING); 
    ina226.readAndClearFlags(); 
    event = false;
  }  
  delay(1000);
}

void displayResults(){
  float shuntVoltage_mV = 0.0;
  float loadVoltage_V = 0.0;
  float busVoltage_V = 0.0;
  float current_mA = 0.0;
  float power_mW = 0.0; 
   
  shuntVoltage_mV = ina226.getShuntVoltage_mV();
  busVoltage_V = ina226.getBusVoltage_V();
  current_mA = ina226.getCurrent_mA();
  power_mW = ina226.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
    
  if(ina226.limitAlert){
    Serial.println("Limit Alert !!!!");
  }
  if(ina226.convAlert){
    Serial.println("Conversion Alert!!!!");
  }
  Serial.print("Shunt Voltage [mV]: "); Serial.println(shuntVoltage_mV);
  Serial.print("Bus Voltage [V]: "); Serial.println(busVoltage_V);
  Serial.print("Load Voltage [V]: "); Serial.println(loadVoltage_V);
  Serial.print("Current[mA]: "); Serial.println(current_mA);
  Serial.print("Bus Power [mW]: "); Serial.println(power_mW);
  if(!ina226.overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher current range");
  }
  Serial.println();
}

void alert(){
  event = true;
  detachInterrupt(2);
}
 

Output of Limit_And_Conversion_Alert.ino

For the following output, I have permanently exceeded the set limit. Accordingly, the INA226 reports limit violations every second. With the conditions set above, the INA226 also issues a conversion ready alert approximately every 8 seconds:

Output of the Limit_And_Conversion_Alert.ino sketch
Output of the Limit_And_Conversion_Alert.ino sketch

Beispiel 7: Continuous mit alternative resistor

A kind contributor has added a function to my library that allows you to change the shunt:

  • setResistorRange(0.005, 10.0) sets the resistor in ohms and the range in amperes.
setCurrentRange() when using different resistors.

All functions at a glance

Here you can see all the functions at a glance. I also use the table for documentation on GitHub.

List of (public) functions of the INA226_WE library
List of (public) functions of the INA226_WE library

Details of the library and the registers of the INA226

If you still want to know more, you can now go a little deeper into the INA226 and the library.

The registers of the INA226

The INA226 has 10 registers, which is significantly more than the INA219 with its 6 registers. All registers are 16 bits.

Register of INA226
Register of INA226

Configuration Register

Configuration register of the INA226

In the configuration register you can make basic settings:

  • RST – Reset Bit: if it is set, a reset is triggered.
  • AVGX – Average Bits: determine the number of individual measurements to be averaged.
  • VBUSCTX – Bus Voltage Conversion Time Bits: see table.
  • VSHCTX – Shunt Voltage Conversion Time Bits: see table.
    • I have made the simplification that only one conversion time can be selected, it then applies equally to the conversion of the bus and the shunt voltage.
  • MODEX – MODE Bits: set the mode, see table.
Average Bits of the INA226
Average Bits
Shunt and Bus Voltage Conversion Time Bits of the INA226
Shunt and Bus Voltage Conversion Time Bits
Mode Bits - only the green highlighted modes I have implemented
Mode Bits – only the green highlighted modes I have implemented

Mask/Enable Register

I jump to the Mask/Enable Register because this is the other register for settings.

Mask/Enable Register of INA226

Bits 10 to 15 are used to activate the alarms:

  • SOL / SUL: Shunt Voltage Over / Under Limit Alert
  • BOL / BUL: Bus Voltage Over / Under Limit Alert
  • POL: Power Over Limit Alert
  • CNVR: Conversion Ready Alert

You can only activate one of the limit alerts. If you set several bits, then the highest applies. You can only activate the Conversion Ready Alarm in parallel.

In my library I have implemented an additional Current Over and Current Under Alarm. Internally, the SOL or SUL bit is set and the current limit is converted into a shunt voltage limit.

Further settings can be made with the APOL and the LEN bit:

  • APOL: Alert Pin Polarity Bit – polarity of the alarm pin. Default is active-high (APOL = 0).
  • LEN: Latch Enable Bit. By default, Latch is disabled (LEN = 0). This means that the alarm pin is deactivated and the alarm flag bit is deleted as soon as an OK measurement occurs (no limit exceeded). If LEN is set, the alarm pin remains activated and the alarm flag bit is set until the Mask/Enable register is read ( readAndClearFlags() ).

The INA226 sets three flags, depending on the setting and measurement results:

  • AFF: Alarm Function Flag – a limit has been exceeded.
  • CVRF: Conversion Ready Flag – measurement results are available.
  • OVF: Overflow Flag – a data register has overflowed.

The CVRF bit is deleted when the Mask/Enable Register is read or Configuration Register is described.

I programmed the function readAndClearFlags() to store the state of AFF, CVRF and OVF in the limitAlert, convAlert, and overflow variables.

Shunt Voltage Register

The Shunt Voltage Register contains the shunt voltage with an LSB (least significant bit) of 2.5 µV. In other words, the resolution of the shunt register is 2.5 µV per unit. Because the register has a sign bit, the value range of the shunt voltage is limited to:

ShuntVoltageRange = \pm (2^{15}-1)\cdot 2.5 \text{\ µ\!V}=81.9175 \text{\ m\!V}

By modifying the shunt you could theoretically change the maximum measurable current. However, you have no choice with the module, and you have to live with the 0.1 ohms. This results in a measuring range of +/- 819.175 mA for the current. In the case of INA219, on the other hand, the current LSB is 10 V. This means that the maximum measurable current is four times larger, but with a lower resolution.

Bus Voltage Register

The INA226 saves the result of the bus voltage conversion to the Bus Voltage Register. Only 15 bits are used by the Bus Voltage Register – the sign is always positive. The LSB is 1.25 mV, resulting in a maximum bus voltage of 40.96 V.

Current Register

The INA226 stores the current calculated from the shunt voltage in the Current Register. Here it gets a bit complicated now because a calibration factor CAL comes into play, which depends on the maximum expected current and the size of the shunt:

C\!AL = \frac{0.00512}{Current\_LSB \cdot R_{Shunt}}\\
Current\_LSB = \frac{Maximum\ expected\ current }{2^{15}}

When calculating the calibration value, you start with the maximum expected current. For my library I chose 0.8 and 0.4 A. 0.8 A because this value, as explained above, is the maximum current of the module and 0.4 A to have a gradation and thus be able to use the higher resolution. The formula for CAL results in a “crooked” value, which is rounded up to have more convenient numbers. The content of the Current Register is as follows:

Current\ Reg = \frac{Shunt\ Voltage\ Reg \cdot C\!AL}{2048}</div

In the end, you need to multiply the content of the Current Register by the Current_LSB to get the current.

Power Register

The Power Register contains the calculated power according to the following formula:

Power\ Reg = \frac{Current\ Reg \cdot Bus\ Voltage\ Reg}{20000}

Finally, the power is the content of the Power Register, multiplied by the Power_LSB. The Power_LSB is (internally defined):

Power\_LSB = Current\_LSB\cdot 25

Calibration Register

The Calibration Register contains the above-mentioned calibration value.

Alert Limit Register

You enter the alarm limit in the Alert Limit Register. I have implemented this in such a way that you can pass the limits directly via the function setAlertType() in the respective designated units.

Calculation of calibration factors and LSBs

The library converts the content of the data registers into the desired units with the LSBs and other factors:

Conversion of data register content
Conversion of data register content

To get handy convenient factors, I introduced a Current Divider and a Power Multiplier:

Current\ \text{[mA]}=\frac{Current\ Register}{Current\ Divider}\ \ \\
\text{mit}\ \ Current\ Divider = \frac{1}{Current\_LSB\cdot 1000}

and

Power\ \text{[mW]} = Power\ Register \cdot Power\ Multiplier
Power\ Multiplier = Power\_LSB \cdot 1000

I implemented the following values in the library:

Values for the implemented power ranges
Values for the implemented power ranges

Acknowledgement

The picture of the current meter is from the Free-Photos of Pixabay. I also found the alarm light from Alexey Hulsov and the display from the OpenClipart vectors on Pixabay.

52 thoughts on “INA226 Current and Power Sensor

  1. Hi Ewald,

    I’m doing some tests with an INA226 board that has a shunt of 0.01 ohm (R010), using an Heltec Esp32 LoRa. The commands related to your library are as below. Vbus measures the voltage across the load which is a 22.2 ohm resistor, so the expected measured current should be:
    cina1 = 4.90/(0.01 + 22.2) = 220.6 mA
    But the data I get is:

    vina1 = 4.81
    cina1 = 656,98 mA
    pina1 = 3161,62 mW

    vina1 is acceptable, but cina1 and pina1 are not. What is the mistake I’m making?

    4.90V Vbus
    | |
    o——-oR010o——o22R2o——->GND

    #include “heltec.h”
    #include
    #include
    .
    .
    .
    #define endINA1 0x45
    elapsedMillis timeElapsed1;
    INA226_WE ina1(endINA1);
    .
    .
    .
    float vina1,cina1,pina1;
    timeElapsed1 = 60000;

    void setup()
    {
    .
    .
    .
    // Starts INA1 address 0x45
    ina1.init();
    // Settings
    ina1.setMeasureMode(TRIGGERED);
    ina1.setAverage(AVERAGE_16);
    ina1.setConversionTime(CONV_TIME_4156);
    // Calibrate INA226 – shunt = 0.01 ohm, maximum current 8A
    ina1.setResistorRange(0.01,8.0);
    .
    .
    .
    }

    void loop()
    {
    // Runs every minute
    if (timeElapsed1 >= 60000)
    {
    ina1.startSingleMeasurement();
    ina1.readAndClearFlags();
    vina1 = ina1.getBusVoltage_V();
    cina1 = ina1.getCurrent_mA();
    pina1 = ina1.getBusPower();

    mostra_mensagem(); // display the values in the OLED
    timeElapsed1 = 0;
    }
    }

    1. Hi Filippo,
      what kind of resistor do you use as load? Just a “normal” 0.25 Watt resistor? 220.6 mA x 4.9 V = 1.08 Watt. If it’s a 0.25 Watt resistor it will become hot and not work anymore according to Ohm’s law. Maybe you can check that first before I have a detailed look into the code.
      Regards, Wolfgang

      1. Good day Ewald,

        the shunt is 1W and I use a 5W load resistor, so we have Pshunt = 0.01 x 0.2206² = 0.486 mW and Pload = 22.2 x 0.2206² = 1,08 W and everything is within limits.

        1. Hi Filippo, I can’t see anything wrong in your code. Not sure what the issue could be. I don’t think it’s a bug in the lib since no one else reported a similar problem yet (or no one has told me yet!?). However I will test as close as possible to the conditions that you have chosen. But this will take few days.
          Maybe you can try another library in the meantime and check if it works better, e.g.:
          https://github.com/RobTillaart/INA226

          1. Hi Ewald, I’m wondering if it could be a problem with the INA226 brekout. I don’t have any other breakout with this shunt to clear the doubt. I have other 3 INAs with external shunt that are working very well (june 13, 2021 post).

  2. Hi Wolfgang Ewald.

    Thank you for your sharing. I use your library to measure the current drawn by linear motors. If the current drawn by motors is above a certain value in the case of stalling, I want it to stop immediately but the problem is that the start-up current produces a false positive. Changing conversion time or the number of samples to be averaged didn’t work. I don’t want to use a delay in my code as it decreases response time. Is there any to overcome this issue?

    Thank you for your answer in advance.

    1. Hi, motors can be tricky beacause of the induction effects. You say you want to stop the motors above a certain value. But the problem is related with the start of the motors. You say it’s a false positive. When you start the motor you should measure a positive current. Or do you mean false positve = a positive value that’s to high? Not sure I have fully understood yet (maybe it’s just too late at night 🙂 ).

      1. Thanks for your quick response.
        I want to stop the motor when there is an obstacle that prevents it to move. I aim to achieve this by sensing higher current values than the idle state. I wrote the code by using your library. It stops when there is an obstacle but it also senses an obstacle at the time of start up as the current is too high. Is it possible to make the measurement after a short period later to pass the start-up phase? Thanks for your concern.

        1. Understand. You could indeed ignore the measured values after a motor has restarted. The most simplest way would be to just add a delay after a motor starts. Of course this does not really work if you control several motors. During the day time you would be blind. In this case you could define a variable “startingTime” for each motor. When motor1 starts then you set:
          startingTime1 = millis();
          And you define a period in which the motor shall not be stopped, e.g. in the first second.
          ignoreTime = 1000;
          And then, when the current of motor one is currentMotor1 and the shut off threshold is shutOffThreshold you write your code like this:

          if(((millis() – startingTime1) > ignoreTime) && (currentMotor1 > shutOffThreshold)){
          insert code for stopping the motor;
          }
          Not very sophisticated but should work. Or am I thinking to simple?

          1. I am grateful for your suggestion. I will try it as soon as possible. As it would not delay the program, It may be the best fit for me.

          2. Thank you, Wolfgang. I used your suggestion as a framework. It works awesome. Take care.

  3. Hallo Wolfgang, guter Artikel! Gibt nicht so viele, die sich mit dem 226 beschäftigen.
    Dein Arduino Anschlußbild und das Erklärbild darüber unterscheiden sich vom TI Datenblatt. Bei dir ist Vbus und Vload verwechselt und Vbus gehört an + der Stromquelle (an IN+ statt an IN-) (vgl. Datenblatt, erste Seite). Ich glaube, TI liegt richtig.
    Die Schaltung mißt als Vbus die Spannung über dem Verbraucher statt über der Stromquelle. Bei kleinen Strömen ist das fast dasselbe, bei maximalem Strom ist der Unterschied ca. 0.8V. Merkt man wahrscheinlich beim Versuchsaufbau kaum, aber bei kleiner Spannung der Stromquelle schon.
    Wenn du das änderst, könntest du gleich noch die Verschaltung für einen Low-Side Shunt mit aufnehmen, wenn du willst (ich benutze bei meinem Projekt einen Low-Side-Shunt).

    Alles Gute, mach weiter so!
    Dieter

    1. Hallo Dieter,
      erstmal danke für’s Feedback. Du bist hier im englischen Teil gelandet. Es gibt alles auch auf deutsch mit deutschen Kommentaren. Ob V_Bus an IN+ oder IN- kommt, ist aus meiner Sicht Geschmackssache. Es kommt darauf an: Will ich die Gesamtspannung und die Leistung einschließlich des Spannungsabfalls und Leistungsverlustes am INA226 messen oder nur meine Last? Der Unterschied ist übrigens maximal 0.8 Ampere x 0.1 Ohm = 0.08 Volt.
      VG, Wolfgang

  4. Thanks so much for your work. This documentation for your library is fantastic. Very much appreciated!

  5. Hi Ewald,

    I have got a module with a 0.01 ohm shunt. So show should I modify your library to get the correct values? I looked up the datasheet but couldn’t find info on how to calc. the current for a specific shunt value. Also is it just the shunt value that needs to be modified or some other variables also need to modified?

    1. Hi Mood,
      my first answer was wrong. I mixed up my two libraries for the INA219 and INA 226. The INA226 does support different shunts. The example sketch you can build on is Continuous_With_Resistor_Value.ino. If your shunt is 10 mOhm and the expected maximum current is 20 A then you would set it up like this:
      ina226.setResistorRange(0.01,20.0);
      Hope this helps!

  6. Hi Ewald, you are right! I made the inversion that you said and all worked fine. I choose a 50A shunt because I have, for now, a 560W/220V (standard values) of led floodlights for the garden, which with the 92% efficiency of the inversor gives a drain of the battery pack of 608.7W/24V/25.4A. You are right with your comment, what I must do is to set the alarm limit to about 30A or 40A. In the future, if I increase the load I will change the shunt. Here is a short display of the Serial:

    12:14:04.160 -> ets Jun 8 2016 00:22:57
    12:14:04.160 ->
    12:14:04.160 -> rst:0x10 (RTCWDT_RTC_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
    12:14:04.160 -> configsip: 0, SPIWP:0xee
    12:14:04.160 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
    12:14:04.160 -> mode:DIO, clock div:1
    12:14:04.160 -> load:0x3fff0018,len:4
    12:14:04.160 -> load:0x3fff001c,len:1044
    12:14:04.207 -> load:0x40078000,len:10124
    12:14:04.207 -> load:0x40080400,len:5856
    12:14:04.207 -> entry 0x400806a8
    12:14:04.450 -> [25] Connecting to Pardini
    12:14:07.063 -> [2648] Connected to WiFi
    12:14:07.063 -> [2649] IP: 192.168.0.161
    12:14:07.063 -> [2649]
    12:14:07.063 -> ___ __ __
    12:14:07.063 -> / _ )/ /_ _____ / /__
    12:14:07.063 -> / _ / / // / _ \/ ‘_/
    12:14:07.063 -> /____/_/\_, /_//_/_/\_\
    12:14:07.063 -> /___/ v1.0.0 on ESP32
    12:14:07.063 ->
    12:14:07.063 -> [2655] Connecting to blynk-cloud.com:80
    12:14:07.741 -> [3317] Ready (ping: 206ms).
    12:14:07.793 ->
    12:14:07.793 -> Varredura I2C
    12:14:07.793 ->
    12:14:07.793 -> Procurando…
    12:14:07.793 ->
    12:14:07.793 -> Encontrado Nanoshield_ADC no endereço 0x4B
    12:14:07.840 -> Encontrada eeprom At24c256 no endereço 0x50
    12:14:07.840 -> Encontrado LCD03 no endereço 0xC6
    12:14:07.840 -> Endereço desconhecido 0x80
    12:14:07.840 -> Todos os equipamentos I2C presentes
    12:14:07.840 ->
    12:14:07.840 -> Serial para Xbee inicializada
    12:14:07.840 -> LCD03 versao 8
    12:14:07.840 -> FIFO buffer = 64 Bytes
    12:14:07.840 ->
    12:14:07.840 -> LCD03 inicializado
    12:14:07.840 ->
    12:14:07.840 -> CoMoSisFog V20.1 parte 2
    12:14:07.840 -> I2C ESP32 – SDA 21 SCL 22 inicializado
    12:14:07.840 -> Nanoshield_ADC inicializado
    12:14:07.934 -> eeprom valida
    12:14:07.934 -> Bytes na eeprom – 8
    12:14:07.934 -> valido = 2863311530
    12:14:07.934 -> inter1 = 60000
    12:14:07.934 -> Iniciando…
    12:15:33.518 -> Digitado: *4*1#
    12:15:33.611 -> vina1 = 26.54
    12:15:33.611 -> cina1 = 9.74
    12:15:33.611 -> pina1 = 258.48
    12:15:39.160 -> vina2 = 26.56
    12:15:39.160 -> cina2 = 10.37
    12:15:39.160 -> pina2 = 275.44
    12:15:44.751 -> vina3 = 26.14
    12:15:44.751 -> cina3 = 19.19
    12:15:44.751 -> pina3 = 501.40
    12:15:50.289 -> etotalin = 13072.30
    12:15:50.289 -> etotalout = 9886.49
    12:17:18.387 -> 13/6/2021 12:16:56[196956] Connecting to blynk-cloud.com:80
    12:17:21.830 -> [197387] Ready (ping: 247ms).

    I like very much robotics and I am fascinated by inertial sensors. I am starting to play with a 3D MOTION click, a 9-axis Motion coprocessor from MikroElektronika (https://www.mikroe.com/3d-motion-click). I will post in the future in my blog http://blog.robotica.eng.br/

    Thank you for your help!

  7. Hi. I am using your great library to monitor my off-grid solar installation whose capacity is: inverter 24Vdc/220Vac/2000W and battery power bank 24Vdc/1440Ah. There are two sets of four solar panels (2 x 1080W) that feed energy to the battery power bank through two MPPT solar controllers in crossed link to the bank. I need to remotely monitor voltage, current, power and energy relative to the energy-in to the battery power bank and the energy-out from the inverter. For that I use an ESP32 with three INA226, one for each controller and one for the inverter. The maximum current for the controllers is 30A and 50A for the inverter, so I desolder the 0,1 resistors and used two shunt 30A, 75mV and one 50A, 75mV

    The basic lines (the program is very large) of the sketch relative to your library are (using triggered mode and limit alert):

    #include

    #define endINA1 0x40 // Controller 1
    #define endINA2 0x41 // Controller 2
    #define endINA3 0x44 // Inverter

    INA226_WE ina1(endINA1);
    INA226_WE ina2(endINA2);
    INA226_WE ina3(endINA3);

    float vina1,cina1,pina1,vina2,cina2,pina2,vina3,cina3,pina3;

    _________________________________________________________________________________________
    setup()

    // Inicia INA1 endereco 0x40 – Tracer 1 – shunt 30A – 75mV
    ina1.init();
    // Configura
    ina1.setAverage(AVERAGE_1);
    ina1.setConversionTime(CONV_TIME_1100);
    ina1.setMeasureMode(TRIGGERED);
    // Calibra INA226 – shunt = 0.0025 ohm, corrente maxima esperada = 30A
    ina1.setResistorRange(0.0025,30.0);

    // Inicia INA2 endereco 0x41 – Tracer 2 – shunt 30A – 75mV
    ina2.init();
    // Configura
    ina2.setAverage(AVERAGE_1);
    ina2.setConversionTime(CONV_TIME_1100);
    ina2.setMeasureMode(TRIGGERED);
    // Calibra INA226 – shunt = 0.0025 ohm, corrente maxima esperada = 30A
    ina2.setResistorRange(0.0025,30.0);

    // Inicia INA3 endereco 0x44 – Inversor – shunt 50A – 75mV
    ina3.init();
    // Configura
    ina3.setAverage(AVERAGE_1);
    ina3.setConversionTime(CONV_TIME_1100);
    ina3.setMeasureMode(TRIGGERED);
    ina3.setAlertType(CURRENT_OVER,50000.0);
    // Calibra INA226 – shunt = 0.0015 ohm, corrente maxima esperada = 50A
    ina3.setResistorRange(0.0015,50.0);

    I have only one alert that is for the inverter max current

    _________________________________________________________________________________________
    loop()

    every minute

    ina1.startSingleMeasurement();
    ina1.readAndClearFlags();
    vina1 = ina1.getBusVoltage_V();
    cina1 = ina1.getCurrent_mA() / 1000.0;
    pina1 = ina1.getBusPower() / 1000.0;
    if(ina1.overflow)
    {
    Serial.print(“\nINA1-overflow! Use uma corrente maior”);
    }

    ina2.startSingleMeasurement();
    ina2.readAndClearFlags();
    vina2 = ina2.getBusVoltage_V();
    cina2 = ina2.getCurrent_mA() / 1000.0;
    pina2 = ina2.getBusPower() / 1000.0;
    if(ina2.overflow)
    {
    Serial.print(“\nINA2-overflow! Use uma corrente maior”);
    }

    ina3.startSingleMeasurement();
    ina3.readAndClearFlags();
    vina3 = ina3.getBusVoltage_V();
    cina3 = ina3.getCurrent_mA() / 1000.0;
    pina3 = ina3.getBusPower() / 1000.0;
    if(ina3.overflow)
    {
    Serial.print(“\nINA3-overflow! Use uma corrente maior”);
    }
    if (ina3.limitAlert)
    {
    Serial.print(“\nALERTA!!! Corrente máxima no inversor – “);
    Serial.print(cina3, 2);
    Serial.print(“A”);
    }

    _________________________________________________________________________________________

    All work fine, all the values read are correct, but there is a problem that I can’t understand. Ina3 that has the limitAlert for 50A overcurrent ina3.setAlertType(CURRENT_OVER,50000.0 ) (50000.0 because is in miliampers) gives alert all the time at each execution even with cina3 < 50A. What is wrong, can you help me?

    1. Hi Filippo, that’s an interesting one. But I think I found the problem. You just need to first set the restistor and range:
      ina3.setResistorRange(0.0015,50.0);
      and THEN set the alert:
      ina3.setAlertType(CURRENT_OVER, 50000.0);

      The reason for the issue is that range and resistor are used to calculate some internal parameters which are then used to calculate the value which is written to the alert limit register (it’s not just the 50000 as is).

      One additional comment: you are working quite borderline with your setup. With the shunt you have chosen for the ina3 you will not be able to detect much more than 50A. If there suddenly should be let’s say 55A then the shunt register will overflow. The current register will then contain wrong data and the alert will not work. The overflow bit, on the other hand, is set if internal arithmetic operations lead to an overflow (see data sheet). I am not sure if it works with an overlowing shunt.

      Hope it works now!

  8. I suppose it would be possible to desolder the SMD shunt resistor and use the IN+ and IN- to sense the voltage drop across a separate shunt. Most shunts seem to create between 50 and 100mV for their peak rated current so should be suitable.

    I have so far tried with ADS1015, ADS1115. I found these to be quite noisy.
    Also with a LTC2487 which is great but only available in a miniscule SMD package so that presents problems.

  9. Hi. That is a great library. Thank you and congratulations for your work

    I was wondering if you know if there is a method to syncronize the INA226 reading with PWM. I need to sense a current passing trough a Mosfet controlled by a PWM signal, but when I use the INA226 the data is erratic. I think if I measure current only when the mosfet is On I can sense the peak current and that is I need

    Thanks and regards

    1. Thanks for the feedback! I think basically there are two ways. The first would be to measure only during the high phase. For this you could connect the pin which provides the PWM signal to an interrupt pin. With the interrupt you could trigger a single shot measurement. If you take the shortest conversion time it’ll take 280 µs to get a result plus the time needed for the I2C communication. The I2C communication could be shortened a bit if you you use the conversion ready alert. But the you need to manage a second interrupt. If this is quick enough depends on the PWM frequency you apply. If it’s faster than the fastest conversion time you don’t have chance anyway.

      Then there is maybe a second route. The PWM signal which controls the current will finally produce a PWM signal across the shunt. You could try to turn this PWM signal into an averaged, flat signal using a low pass filter. You would have to desolder the shunt, but still let the current flow through it, apply the low pass filter and connect the outputs of the low pass filter with the IN- / IN+. You can find some more information here:

      https://www.instructables.com/Arduino-RC-Circuit-PWM-to-analog-DC/

      Sorry if you expected something easier!

      1. I thought in the same 2 solutions

        Frequecy is 244Hz, so I think is not an issue and Im working with IC out the board

        The first solution is better regarding components count, but I need understand the way that interrupts work. I think that is the way

        Thank you again

      2. Hi again!

        I was trying use your library with hardware interrupts and FreeRTOS but get the following error:

        Guru Meditation Error: Core 0 panic’ed (IntegerDivideByZero). Exception was unhandled.

        Do you have any idea about what this can be for?

        1. Hi Diego,

          I don’t have experience in FreeRTOS, so difficult for me to give you a meaningful advice. I googled a bit but it seems to be something I would need to invest quite some time. Sorry, no idea.

  10. Hi, if I wanted to include 4 INA226, with the same Vcc (5V), but diferent loads what whould be different on the code (TRIGGRED Mode) ?

    1. Hi, first you have to set the I2C addresses with the jumpers on the module. The addresses are as follows:

      0x40: A0, A1 open
      0x41: A0 closed, A1 open
      0x44: A0 open, A1 closed
      0x45: A0, A1 closed

      Then you create four instances of INA226_WE:

      INA226_WE ina226_1(I2C_ADDRESS_1);
      INA226_WE ina226_2(I2C_ADDRESS_2);
      and so on.

      And then you apply the same code for every module, starting with the init function, but you exchange ina226 by ina226_1, ina 226_2, and so on.

      Using interrupt functions could be an issue, depending on the number of interrupt pins that your MCU has. You may need to query the alert pin status with non-interrupt I/O pins.

      1. So, right now, I have something like this:

        #include
        #include
        #define I2C_ADDRESS_1 0x40
        #define I2C_ADDRESS_2 0x41

        INA226_WE ina226_1(I2C_ADDRESS_1);
        INA226_WE ina226_2(I2C_ADDRESS_2);

        // INA226_WE ina226 = INA226_WE(); // Alternative: sets default address 0x40

        void setup() {

        Serial.begin(9600);
        Wire.begin();

        ina226_1.init();
        ina226_2.init();

        /* Set Number of measurements for shunt and bus voltage which shall be averaged
        * Mode * * Number of samples *
        AVERAGE_1 1 (default)
        AVERAGE_4 4
        AVERAGE_16 16
        AVERAGE_64 64
        AVERAGE_128 128
        AVERAGE_256 256
        AVERAGE_512 512
        AVERAGE_1024 1024
        */
        //ina226.setAverage(AVERAGE_16); // choose mode and uncomment for change of default

        /* Set conversion time in microseconds
        One set of shunt and bus voltage conversion will take:
        number of samples to be averaged x conversion time x 2

        * Mode * * conversion time *
        CONV_TIME_140 140 µs
        CONV_TIME_204 204 µs
        CONV_TIME_332 332 µs
        CONV_TIME_588 588 µs
        CONV_TIME_1100 1.1 ms (default)
        CONV_TIME_2116 2.116 ms
        CONV_TIME_4156 4.156 ms
        CONV_TIME_8244 8.244 ms
        */
        //ina226.setConversionTime(CONV_TIME_1100); //choose conversion time and uncomment for change of default

        /* Set measure mode
        POWER_DOWN – INA219 switched off
        TRIGGERED – measurement on demand
        CONTINUOUS – Continuous measurements (default)
        */
        //ina226.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default

        /* Set Current Range
        * Mode * * Max Current *
        MA_400 400 mA
        MA_800 800 mA (default)
        */
        //ina226.setCurrentRange(MA_800); // choose gain and uncomment for change of default

        /* If the current values delivered by the INA226 differ by a constant factor
        from values obtained with calibrated equipment you can define a correction factor.
        Correction factor = current delivered from calibrated equipment / current delivered by INA226
        */
        // ina226.setCorrectionFactor(0.95);

        Serial.println(“INA226 Current Sensor Example Sketch – Continuous”);

        ina226_1.waitUntilConversionCompleted(); //if you comment this line the first data might be zero
        ina226_2.waitUntilConversionCompleted();
        }

        void loop() {

        float shuntVoltage_mV1 = 0.0;
        float loadVoltage_V1 = 0.0;
        float busVoltage_V1 = 0.0;
        float current_mA1 = 0.0;
        float power_mW1 = 0.0;

        ina226_1.readAndClearFlags();
        shuntVoltage_mV1 = ina226_1.getShuntVoltage_mV();
        busVoltage_V1 = ina226_1.getBusVoltage_V();
        current_mA1 = ina226_1.getCurrent_mA();
        power_mW1 = ina226_1.getBusPower();
        loadVoltage_V1 = busVoltage_V1 + (shuntVoltage_mV1/1000);

        Serial.print(“Shunt Voltage [mV]: “); Serial.println(shuntVoltage_mV1);
        Serial.print(“Bus Voltage [V]: “); Serial.println(busVoltage_V1);
        Serial.print(“Load Voltage [V]: “); Serial.println(loadVoltage_V1);
        Serial.print(“Current[mA]: “); Serial.println(current_mA1);
        Serial.print(“Bus Power [mW]: “); Serial.println(power_mW1);
        if(!ina226_1.overflow){
        Serial.println(“Values OK – no overflow”);
        }
        else{
        Serial.println(“Overflow! Choose higher current range”);
        }
        Serial.println();

        /*—————— second one ——————-*/

        float shuntVoltage_mV2 = 0.0;
        float loadVoltage_V2 = 0.0;
        float busVoltage_V2 = 0.0;
        float current_mA2 = 0.0;
        float power_mW2 = 0.0;

        ina226_2.readAndClearFlags();
        shuntVoltage_mV2 = ina226_2.getShuntVoltage_mV();
        busVoltage_V2 = ina226_2.getBusVoltage_V();
        current_mA2 = ina226_2.getCurrent_mA();
        power_mW2 = ina226_2.getBusPower();
        loadVoltage_V2 = busVoltage_V2 + (shuntVoltage_mV2/1000);

        Serial.print(“Shunt Voltage [mV]: “); Serial.println(shuntVoltage_mV2);
        Serial.print(“Bus Voltage [V]: “); Serial.println(busVoltage_V2);
        Serial.print(“Load Voltage [V]: “); Serial.println(loadVoltage_V2);
        Serial.print(“Current[mA]: “); Serial.println(current_mA2);
        Serial.print(“Bus Power [mW]: “); Serial.println(power_mW2);
        if(!ina226_2.overflow){
        Serial.println(“Values OK – no overflow”);
        }
        else{
        Serial.println(“Overflow! Choose higher current range”);
        }
        Serial.println();

        delay(3000);
        }

        But, not even 1 INA226 is mensuring

        1. I added the two includes:

          #include
          #include

          But I assume that’s not the issue and that was just not copied into your comment, right?

          After that I can’t see any errors in your code, and basically I would have written the code similar. Interestingly I had the same request about how to use multiple INA226 modules on GitHub:

          https://github.com/wollewald/INA226_WE/issues/3

          In this case it worked. Maybe you join the discussion and ask the initiator of the issue how he did it.

          Maybe the problem is on the wiring side? Have you modified the address jumpers on the module? Do the two Ina226 modules respond at all? To check you can use an I2C scanner:

          https://wolles-elektronikkiste.de/en/i2c-scanner

          Can you check this first?

          1. Well, I’ve checked it (the I2C scanner) and its only printing: “0x40”. I assume that is only one of them connected, right?

            So, I actually have the same connections as you, but for 2 INA226. Do I have to connect A0, A1 and Vcc pins (on the backside INA226) somewhere?

            1. Yes, connect for example A0 to VCC with a dop of solder tin on the backside of the module and the address of the module will change from 0x40 to 0x41. It’s as simple as that!

                1. You let the first module unchanged (0x40). On the backside of the second module you just connect the A0 jumper with the VCC jumper. Just these two pads. Directly on the module. You can test it: let the i2c scanner run and touch the A0 and VCC jumper with the two ends of a cable. You will see that suddenly a device will appear with the address 0x41.

                  1. Okay, I will check it.

                    And so, you said:
                    -> 0x40 (No connections);
                    -> 0x41 (A0 and Vcc);
                    -> 0x44;
                    -> 0x45;
                    And for 0x44 and ox45, what jumpers do I have to connect? Does it really needs to be 0x44 and 0x45, or do I can go for 0x43 and so on?

                    By the way, I’ve touched A0 and VCC, leted the I2C scanner run, and it keeps saying “0x40”. Maybe bad contact no? I really should try to resolve it with solder, right?

                    1. Maybe some varnish on the contacts. So yes, solder it. I have just tried it works.
                      0x40: A0, A1 open
                      0x41: A0 closed (A0 connected to VCC), A1 open
                      0x44: A0 open, A1 closed (A1 connected to VCC)
                      0x45: A0, A1 closed (A0 and A1 connected to VCC)

  11. Gerglish! I finally worked out why this is so confusing, the equations are in German were ‘,’ is a ‘decimal separator’ rather than ‘thousands separator. No understanding of significant figures either.

    “It is a great pity that the INA226 module does not have a smaller shunt because with 800 mA the current upper limit is not very lush.”

    So how do I calculate what size current shunt to use for a given voltage and current? What is VShunt and VBus?

    1. Sorry if it is confusing and not that understandable in English. That’s of course disappointing for me but at least good to know. At least I have changed “,” to “.” now. Good point, this helps me. I will also have a look if I can improve the English, but that’s more longterm.

      VShunt is the voltage that drops across the shunt. VBus is the voltage that drops across the power consumer.

      If you use the module, the shunt is fixed (0.1 ohms) because it’s hardwired. The LSB of the shunt register is 2.5 microvolt. The shunt register is +/-(2^15-1) bit. So the maximum voltage that the shunt register can store is +/-81.9175 millivolts which corresponds to a maximum of +/-819.175 milliamperes.

      If you use the bare INA226 IC you are free to use the shunt you want or need. The limitation of the shunt register remains. +/-81.9175 millivolts is the maximum. With Ohm’s law you can calculate the resistor value of the shunt (Rshunt) that you need from the maximum expected current (Imax):

      RShunt [ohms] = 0.0819175 [volts] / Imax [amperes]

      Hope this helps. If not, please look into the data sheet or simply use a different library if they are better to use for you. There are several on GitHub.

      1. Thank you. The article defines VShunt and VBus but doesn’t clearly reference them to the technical data descriptions. It is clear now. I understand “Power consumer” == “Load device” too.

        For R100 and R010 Imax is 0.8 and 8.2 respectively. If this is not enough the current shunt can be removed and an external shunt used. What size current shunt can be soldered directly to the board?

        1. You can use any shunt you like, you only have to ensure that the voltage drop across the shunt does not exceed ~81.9175 millivolts. If you use a different shunt, you should take Continuous_With_Resistor_Value.ino as a basis. The other examples work with the 0.1 ohms resistor.
          The voltage you apply does not matter as long as it does not exceed 36 volts. The maximum power P that is dissipated at the shunt is only depending from the voltage drop U across the shunt and the current I: P=U x I. The maximum U is 81.9175 millivolts and therefore the power at the shunt is low.

          1. I’m just concerned about the hardware at the moment. So I understand 20A x 81.9175mV = 1.6W. I assume standard current shunts are 1W which would only support 12A (or 10A using R008) so a 2W version would be required for 20A (R004).

            Do you know if there would be any other issues with running 36V (Vmax) across the module at these amperages?

            1. So, yes, 2 W should work. If available I would take 3 W to have some additional safety buffer. For the INA226 Chip the current does not matter since it does not flow through it. It only measures the voltage. Where I am not sure is how much the conductor paths from the pins to the shunt can take. If you change the shunt, you could solder wires directly to it.

              36 V is the recommended maximum voltage according to the data sheet of the INA226. The absolute maximum is 40 V. So the voltage is not an issue.

  12. Danke für de ausführliche Beschreibung.
    Ich möchte das INA226 mit einer Autobatterie verknüpfen und bei Unterschreitung von einem bestimmten Wert eine SMS auf mein Händy.
    Da das Ganze an der Autobatterie hängt soll der Stromverbrauch auf ein Minimum reduziert werden.
    Die GSM Steuerung steht soweit, mir fehlt nur noch die Implementierung des INA226 und wenn möglich über ein Interrupt die Nachricht zu versenden und ansonsten das System schlafend zu setzen.
    Oder über 4 Messungen am Tag zu Steuern?!
    Was ist einfacher zu realisieren.

    1. Hallo,

      beides ist nicht besonders schwer zu implementieren. DIe Interruptmethode hat den Vorteil, dass der Microcontroller nur geweckt wird, wenn es sein muss. Die andere Methode hat den Vorteil, dass man weiß, dass das System noch arbeitet.

      Die programmierbaren Schlafphasen aus denen sich der Microcontroller selbst weckt, liegen im Sekundenbereich. D.h. man muss die maximale Schlafperiode wählen und dann mit Zählern arbeiten. Das spricht wieder für die Interruptmethode. Mein Beitrag über Sleepmodes hilft dir vielleicht:

      https://wolles-elektronikkiste.de/sleep-modes-und-power-management

      Um den Microcontroller im Bereich von Stunden regelmäßig zu wecken, ließe sich auch der LTC6995 einsetzen:

      https://wolles-elektronikkiste.de/ltc6995-long-timer-low-frequency-oscillator

      Hoffe, das hilft erstmal weiter.

    2. Es empfielt sich jedoch, eine Autobatterie nie lange stehen lassen und sie regelmäßig (mindestens 1mal pro Monat) über 13.8V zu bringen.
      Nur so kann Sulfatierung entgegen gewirkt werden.
      Spannungmonitoring und liegenlassen killt die Batterie ziemlich sicher…

      1. “Es empfielt sich jedoch, eine Autobatterie nie lange stehen lassen und sie regelmäßig (mindestens 1mal pro Monat) über 13.8V zu bringen.”

        Urban Legends. Richtig wäre “auf maximal 13.8 V zu laden*, alles darüber ist bereits eine Überladung, die allerdings so gering ist, dass man bei stark entladenen Akkus die Schädigung toleriert, damit die Batterie schneller geladen wird. Außerdem können Blei-Akkus sehr lang ohne nachladen gelagert werden. Sonnenschein (heute GNB/Exide) hat für die Dryfit Blei-Gel-Akkus 2 Jahre als maximale Lagerzeit ohne Nachladen angebeben

        Geschädigt werden Blei-Akkus nicht durch das Lagern sondern durch Tiefentladen und Überladen. Ich kenne Beispiele von Dryfit-Akkus in Geräten, die nur selten in Betrieb waren und mehrere Jahrzehnte gehalten haben. Diese 6-V-Blei-Gel-Akkus wurden aber nie tiefentladen und ausschließlich mit IU-Kennlinie bis 6,9 V geladen (entspricht 13,8 V bei 12-V-Batterien.

Leave a Reply

Your email address will not be published.