INA219 Current and Power Sensor

About this post

In this article I would like to introduce the INA219 current and power sensor module and my associated library INA219_WE.

In addition to the INA219, I tried out various other current sensor modules such as the ACS712 or the MAXIM471. However, I liked the INA219 best because it reliably detects currents of a few milliamperes. In addition to the current, the module also determines the power and voltage drop across the system to be measured. I will deal with the INA226, so to speak the brother of INA219, in another article.

Libraries for the INA219 already existed, but I wasn’t pleased with any. Either I lacked certain options such as the trigger mode or I found the functions a bit unwieldy to use. But the tastes are different. If you want to try alternative libraries, you can find some of them in the Arduino Library Manager or at GitHub (under search term INA219).

Measuring principle of the INA219

INA219 modules from different sources
Various INA219 modules

The measuring principle of the INA219 is simply based on Ohm’s law. The current to be measured is passed through a resistor and the voltage dropping across it is determined. Since one wants to interfere as little as possible with the system to be measured, the resistance is tiny. Such a current measuring resistor is called a shunt. In the case of the INA219 module, the shunt has a size of 0.1 ohms (R100).

The INA219’s shunt is integrated into the consumer’s circuit as shown below. To calculate the size of the current, you basically just have to multiply the voltage drop across the shunt by 10. Why do you need something as complex as the INA219? Well, on the one hand, the voltage to be measured is small. So, you need an amplifier and – that’s anyway – an A/D converter. On the other hand, the INA219 can measure a little more than just the current.

In addition to the shunt voltage, the INA219 also determines the voltage drop across the consumer (“bus”) between VIN and GND. From this and from the size of the current, the INA219 calculates the power consumption of the consumer.

The circuit of shunt and consumer is separated from the supply current of the INA219. It is important, however, that the two have a common GND.

How to integrate the INA219 into the circuit to be measured
How to integrate the INA219 into the circuit to be measured

Typical circuit

Typical circuit for the INA219
Typical circuit for the INA219

The INA219, more precisely the shunt, must be inserted into the circuit before the consumer, otherwise the measurement of the bus voltage will not work. This setup is called high-side.

The connection is simple. It doesn’t matter if you use the terminals or the pins for VIN+ and VIN-.

By the way, if you swap VIN- and VIN+ in the circuit shown above, you get negative values for the current and the shunt voltage.

Some technical data of the INA219 module

Here are the most important data of the INA219 module:

  • Bus voltage: 0 – 26 volts
  • Maximum bus current: 3.2 amperes (when using a 0.1 ohms shunt)
  • Supply voltage: 3 – 5.5 volts
  • Power consumption (self-determined):
    • Continuous mode: 0.7 mA
    • Power-Down Mode: 9.5 µA
  • 4 gain levels (1-, 2-, 4-, 8-fold)
  • Measurement modes: continuous or on-demand (“triggered”);
  • Data registers:
    • Shunt voltage register
    • Bus Voltage (bus voltage register)
    • Current register
    • Power register
  • Communication via I2C, 4 addresses adjustable:
    • 0x40: A0, A1 open
    • 0x41: A0 closed, A1 open
    • 0x44: A0 open, A1 closed
    • 0x45: A0, A1 closed

If you want to check the I2C address, you can use my I2C scanner.

Further technical information can be found in the data sheet of the INA219.

You get the INA219 for a few euros in most online electronics stores, e.g. here at Amazon. More expensive are modules from Adafruit. I cannot judge whether the price difference is reflected in better quality.

Use of the INA219_WE library

You either install the library using the Arduino Library Manager or you can download it from GitHub here.

Continuous mode

Build your circuit as shown above. Then upload the sample sketch “Continuous.ino”:

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

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

void setup() {
  Serial.begin(9600);
  Wire.begin();
  if(!ina219.init()){
    Serial.println("INA219 not connected!");
  }

  /* Set ADC Mode for Bus and ShuntVoltage
  * Mode *            * Res / Samples *       * Conversion Time *
  BIT_MODE_9        9 Bit Resolution             84 µs
  BIT_MODE_10       10 Bit Resolution            148 µs  
  BIT_MODE_11       11 Bit Resolution            276 µs
  BIT_MODE_12       12 Bit Resolution            532 µs  (DEFAULT)
  SAMPLE_MODE_2     Mean Value 2 samples         1.06 ms
  SAMPLE_MODE_4     Mean Value 4 samples         2.13 ms
  SAMPLE_MODE_8     Mean Value 8 samples         4.26 ms
  SAMPLE_MODE_16    Mean Value 16 samples        8.51 ms     
  SAMPLE_MODE_32    Mean Value 32 samples        17.02 ms
  SAMPLE_MODE_64    Mean Value 64 samples        34.05 ms
  SAMPLE_MODE_128   Mean Value 128 samples       68.10 ms
  */
  //ina219.setADCMode(SAMPLE_MODE_128); // choose mode and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  ADC_OFF    - Analog/Digital Converter switched off
  CONTINUOUS  - Continuous measurements (DEFAULT)
  */
  // ina219.setMeasureMode(CONTINUOUS); // choose mode and uncomment for change of default
  
  /* Set PGain
  * Gain *  * Shunt Voltage Range *   * Max Current (if shunt is 0.1 ohms) *
   PG_40       40 mV                    0.4 A
   PG_80       80 mV                    0.8 A
   PG_160      160 mV                   1.6 A
   PG_320      320 mV                   3.2 A (DEFAULT)
  */
  // ina219.setPGain(PG_320); // choose gain and uncomment for change of default
  
  /* Set Bus Voltage Range
   BRNG_16   -> 16 V
   BRNG_32   -> 32 V (DEFAULT)
  */
  // ina219.setBusRange(BRNG_32); // choose range and uncomment for change of default

  Serial.println("INA219 Current Sensor Example Sketch - Continuous");

  /* If the current values delivered by the INA219 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 INA219
  */
  // ina219.setCorrectionFactor(0.98); // insert your correction factor if necessary
  
  /* If you experience a shunt voltage offset, that means you detect a shunt voltage which is not 
     zero, although the current should be zero, you can apply a correction. For this, uncomment the 
     following function and apply the offset you have detected.   
  */
  // ina219.setShuntVoltOffset_mV(0.5); // insert the shunt voltage (millivolts) you detect at zero current 
}

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; 
  bool ina219_overflow = false;
  
  shuntVoltage_mV = ina219.getShuntVoltage_mV();
  busVoltage_V = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  ina219_overflow = ina219.getOverflow();
  
  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(!ina219_overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher PGAIN");
  }
  Serial.println();
  
  delay(3000);
}

Parameter setting using the continuous.ino Sketch example

In this example, I would like to explain not only the continuous mode itself, but also the general setting parameters.

INA219_WE ina219 = INA219_WE() creates your INA219 object. I have implemented several options. You could pass a Wire object to the constructor. With this you are able to use both I2C busses of an ESP32. 

The function init() ensures that the INA219 is activated with the default values. To change these basic settings, you can change four parameters in the setup:

  1. ADC mode for shunt and bus voltage conversion ( setADCMode() ):
    • BIT_MODE_X: Single measurements with X bit resolution.
    • SAMPLE_MODE_X: Mean value from X measurements
    • Default: BIT_MODE_12.
    • The times for the A/D conversion are specified in the sketch. Note that two conversions take place per measurement cycle, one for the shunt and one for the bus voltage.
  2. Measuring mode ( setMeasureMode() )
    • CONTINUOUS – continuous: this is the default that is used here.
    • TRIGGERED – “on request”: is explained in the next example.
    • ADC_OFF – switching off the A/D converter
    • POWER_DOWN – turns off the INA219. But better use the more comfortable powerDown() function, which is explained below.
    • The INA219 actually allows determining shunt or bus voltages – but I did not implement that.
  3. Gain factor ( setPGain() )
    • PG_X: The “X” specifies the limit for the shunt voltage. Z.B.: PG_40 — > 40 mV — > corresponds to a maximum of 400 mA of electricity
    • The higher X, the higher the maximum current, but the lower the resolution for the current and the power
  4. Bus voltage range ( setBusRange() )
    • 16 or 32 volts

You can also change the settings at any time later, i.e. outside the setup function.

With setCorrectionFactor(factor) you can introduce a correction factor if the current values determined with the INA219 should differ from those determined with calibrated measuring instruments. The factor is the quotient of the “correct” and the INA219 value. For me, all INA219 modules matched my multimeter well.

It seems that some INA219 modules tend to show shunt voltage offsets. Anscheinend zeigen einige INA219 Module Shuntspannung-Offsets. This means that although no current is flowing (consumer/load switched off), a shunt voltage is displayed and a resulting current. With setShuntVoltOffset() you can correct this. Pass the shunt voltage in millivolts, which you measure without current, as parameter to the function.

The function getOverflow() checks whether one of the data registers is overflowed. If this is the case, select a different gain factor.

The functions for reading the measurement results, such as getShuntVoltage_mV(), are self-explanatory.

Output

Output of the continuous sketch for the INA219
Output of the continuous sketch for the INA219

Attentive readers may ask why “Current” in this output example does not exactly match ten times the shunt voltage (RShunt was 0.1 Ohm). This is because the registers can be read at any time. The current conversion is not being waited for. The contents of the shunt and the current register can therefore originate from different measurement cycles. Another consequence is that the values can be “old” up to 68.1 ms (this would be the upper limit in SAMPLE_MODE_128). In continuous mode, however, this should not be a problem. In triggered mode, on the other hand, the system waits for the current conversion to complete after starting the measurement.

Triggered (“on request”) mode

The example sketch for triggered mode differs from the continuous sketch in only two lines. In line 35, the mode is activated. In line 63, the function startSingleMeasurement() starts the measurement. Only when the measurement is completed, the output takes place.

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

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

void setup() {
  Serial.begin(9600);
  Wire.begin();
  if(!ina219.init()){
    Serial.println("INA219 not connected!");
}

  /* Set ADC Mode for Bus and ShuntVoltage
  * Mode *            * Res / Samples *       * Conversion Time *
  BIT_MODE_9        9 Bit Resolution             84 µs
  BIT_MODE_10       10 Bit Resolution            148 µs  
  BIT_MODE_11       11 Bit Resolution            276 µs
  BIT_MODE_12       12 Bit Resolution            532 µs  (DEFAULT)
  SAMPLE_MODE_2     Mean Value 2 samples         1.06 ms
  SAMPLE_MODE_4     Mean Value 4 samples         2.13 ms
  SAMPLE_MODE_8     Mean Value 8 samples         4.26 ms
  SAMPLE_MODE_16    Mean Value 16 samples        8.51 ms     
  SAMPLE_MODE_32    Mean Value 32 samples        17.02 ms
  SAMPLE_MODE_64    Mean Value 64 samples        34.05 ms
  SAMPLE_MODE_128   Mean Value 128 samples       68.10 ms
  */
  // ina219.setADCMode(BIT_MODE_12); // choose mode and uncomment for change of default
  
  /* Set measure mode
  POWER_DOWN - INA219 switched off
  TRIGGERED  - measurement on demand
  ADC_OFF    - Analog/Digital Converter switched off
  CONTINUOUS  - Continuous measurements (DEFAULT)
  */
  ina219.setMeasureMode(TRIGGERED); // Triggered measurements for this example
  
  /* Set PGain
  * Gain *  * Shunt Voltage Range *   * Max Current *
   PG_40       40 mV                    0.4 A
   PG_80       80 mV                    0.8 A
   PG_160      160 mV                   1.6 A
   PG_320      320 mV                   3.2 A (DEFAULT)
  */
  // ina219.setPGain(PG_320); // choose gain and uncomment for change of default
  
  /* Set Bus Voltage Range
   BRNG_16   -> 16 V
   BRNG_32   -> 32 V (DEFAULT)
  */
  // ina219.setBusRange(BRNG_32); // choose range and uncomment for change of default

  /* If the current values delivered by the INA219 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 INA219
  */
  // ina219.setCorrectionFactor(0.98); // insert your correction factor if necessary

  /* If you experience a shunt voltage offset, that means you detect a shunt voltage which is not 
     zero, although the current should be zero, you can apply a correction. For this, uncomment the 
     following function and apply the offset you have detected.   
  */
  // ina219.setShuntVoltOffset_mV(0.0); // insert the shunt voltage (millivolts) you detect at zero current
  
  Serial.println("INA219 Current Sensor Example Sketch - Triggered Mode");

}

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; 
  bool ina219_overflow = false;
  
  ina219.startSingleMeasurement(); // triggers single-shot measurement and waits until completed
  shuntVoltage_mV = ina219.getShuntVoltage_mV();
  busVoltage_V = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  ina219_overflow = ina219.getOverflow();
  
  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(!ina219_overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher PGAIN");
  }
  Serial.println();
  
  delay(3000);
} 

 

In triggered mode, all output values belong to one measurement cycle:

Output of the triggered sketch for the INA219
Output of the triggered sketch for the INA219

Power-Down Mode

With the power-down mode, you can reduce the current of the INA219 from approx. 0.7 mA to less than 10 µA (own measurements). 0.7 mA does not sound much, but in one year it is more than 6000 mAh. This is relevant for battery-powered projects.

The following example sketch shows the power-down mode in action. The sketch initializes the INA219 with the default parameters. Five measurement packets are output every three seconds. The function powerDown() then backs up the content of the configuration register and switches off the INA219. The function powerUp() writes back the copy of the configuration register. On the one hand, this writing process awakens the INA219, on the other hand it ensures that the INA219 returns to the previously selected mode (here: continuous).

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

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

void setup() {
  Serial.begin(9600);
  Wire.begin();
  // default parameters are set - for change check the other examples
  if(!ina219.init()){
    Serial.println("INA219 not connected!");
  }
  Serial.println("INA219 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");
  ina219.powerDown();
  for(int i=0; i<10; i++){
    Serial.print(".");
    delay(1000);
  }
  
  Serial.println("Power up!");
  Serial.println("");
  ina219.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; 
  bool ina219_overflow = false;
  
  shuntVoltage_mV = ina219.getShuntVoltage_mV();
  busVoltage_V = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getBusPower();
  loadVoltage_V  = busVoltage_V + (shuntVoltage_mV/1000);
  ina219_overflow = ina219.getOverflow();
  
  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(!ina219_overflow){
    Serial.println("Values OK - no overflow");
  }
  else{
    Serial.println("Overflow! Choose higher PGAIN");
  }
  Serial.println();
}

 

Changing the shunt

Despite an intensive search, I only found modules with a 0.1 Ohm shunt. So you are limited to a current of 3.2 amps. If you want to measure larger currents, then you have three options:

  1. Use the bare INA219 IC instead of the module.
  2. Desolder the R100 shunt and replace it. However, with only one soldering iron, this is not trivial.
  3. The pragmatic solution: you simply solder a second shunt on top of the first shunt (sandwich-like). 

I tried way 3 (see right). Two parallel shunts of 0.1 and 0.05 ohms give a total shunt of 0.0333 ohms. This worked very well. 

Use setShuntSizeInOhms() to set the shunt. Also setPGain() still works with the changed shunt. When using PG_Value and a shunt with a resistance R, the maximum current is I:

I_{\text{max}}\;\text{[A]}= \frac{Value}{R\cdot 1000}

You find an example sketch as part of the library.

Overview of all functions of the library

And here again the overview of all functions. It is part of my documentation on GitHub.

Liste der Funktionen der INA219_WE Bibliothek
List of functions of the INA219_WE library

Details of the library and the registers

This part is only for those who want to go into the details of the INA219_WE library or to better understand the internal processes in INA219.

The registers of INA219

The INA219 has six 16-bit registers. You can only write to the configuration and calibration registers. The other registers provide the measured or calculated data and are read-only accordingly.

Register of the INA219
Register of the INA219

The basic settings are made in the configuration register:

The configuration register

Configuration register of the INA219
Configuration register of the INA219
  • RST: Setting the RST bit triggers a reset. The register contents are reset to default.
  • BRNG: Bus Range determines the range of the bus voltage, i.e. 16 or 32 volts.
  • PGX: The two bits determine PGAIN
  • BADCX / SADCX: These bits determine the resolution or the number of measurements per measurement cycle for the bus and shunt voltage (see table). In my library, no different values can be written in BADC and SADC. I had decided not to make the operation too complex.
  • MODEX: The three mode bits set the operating mode (see table). Again, I haven’t implemented everything that’s possible. Only the modes highlighted in green are available.
ADC Settings - apply to shunt and bus voltage conversion
ADC Settings – apply to shunt and bus voltage conversion
Adjustable modes of the INA219 - only the green modes are implemented
Adjustable modes of the INA219 – only the green modes are implemented

The other registers

The shunt and the current register are not particularly exciting. It should be noted, however, that both have sign bits, i.e. they can take values between +32767 and -32767. The Bus Voltage and the Power Register are not signed.

Regarding the Bus Voltage Register, only 13 bits are available for the bus voltage (BD0 – BD12). Bit No. 2 is unused, bit No. 1 is CNVR (Conversion Ready). It is set when (fresh) data are available in the registers, i.e. all measurements and calculations for a cycle are complete. Reading the Power Register deletes the bit. Bit 0 is OVF (overflow) and indicates whether one of the data registers has overflowed. To calculate the bus voltage, the content of the bus voltage register must be shifted 3 bits to the right.

Bus voltage register of the INA219

The Calibration Register contains the 15 bit calibration factor “Cal”. The register is 16 bits, but bit 0 is reserved. We’ll get to what the calibration factor is all about. But, to avoid misunderstandings, I should point out that the calibration factor has nothing to do with the correction factor (setCorrectionFactor()).

INA219 – internal calculations

I found the explanations of the calculations in the data sheet confusing. I had to read it several times before I understood it. But actually it’s not that bad.

To determine the calibration factor, first consider how big the largest expected current (Max Expected Current) is. This must fit in the Current Register. Since the Current Register has a value range of +/- 215, the Current_LSB (ampere per bit) therefore is:

Current\_LSB = \frac{Max\ Expected\ Current}{2^{15}}

The equation usually results in a “crooked” value. You round it up so that in the next step you get a straight value for the calibration factor Cal. You calculate Cal according to the following formula:

Cal = trunc\left(\frac{0.04096}{Current\_LSB\cdot R_{Shunt}}\right)

0.04096 is an internally defined value, which ensures a reasonable scaling, i.e. that the values make good use of the register width.

The Shunt Voltage Register contains the shunt voltage in 10 µV / bit. Since it is a signed register, the maximum is 215-1. This results in a maximum shunt voltage of 0.32767 volts. More does not fit in the register. Since the shunt resistance is 0.1 ohms, the maximum current is approx. 3.2 amperes.

The Current Register is calculated according to the following formula (but we don’t have to worry about that):

Current\ Register=\frac{Shunt\ Voltage\ Register \cdot Cal}{4096}

Power_LSB, i.e. the power per bit (watt/bit), is 20 times the Current_LSB. This is a fixed internal setting.

Power\_LSB = 20 \cdot Current\_LSB

The content of the Power Register can be calculated from the content of the Current Register and the Bus Voltage Register. But we don’t have to worry about that either because the INA219 calculates that internally.

Power\ Register = \frac{Current\ Register \cdot Bus\ Voltage\ Register}{5000}

The Bus Voltage Register has a fixed LSB of 4 mV/bit.

Calculations by the library

Basically, you could just read the Shunt and Bus Voltage Register and calculate the rest. But we let the INA219 work for us.

Nevertheless, the values read from the registers still need to be converted:

Conversion of register contents
Conversion of register contents

To get more convenient values, 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\ M\!ultiplier
\text{mit}\ \ \ Power\ M\!ultiplier = Power\_LSB \cdot 1000

Measuring ranges implemented in the library

So, as we have seen, the resolution for the current and the power depends on the size of Current_LSB. Therefore, it makes sense to define several areas with different Current_LSBs. It seemed to me the simplest and most obvious solution to take the PGAIN ranges for this:

Measuring ranges, resolution and conversion factors for the INA219
Measuring ranges, resolution and conversion factors

So if you want to use a high resolution, you have to choose a small PGAIN.

Acknowledgement

I took the INA219 Fritzing component from the Adafruit Fritzing Part collection on GitHub.

The post image is from the Free-Photos on Pixabay and was slightly modified by me.

22 thoughts on “INA219 Current and Power Sensor

  1. A much better definition of the workings of the INA219.
    The arduino sketch also works better and I think more accurate than the Adafruit one.
    I think your sketch adds bus voltage to shunt voltage to give the input voltage.
    I think the adafruit one just gives bus voltage.
    I was searching for a setup that allowed me to access the 80mV shunt range and your setup does this.
    Thank you

  2. this is absoloutly better designed and documented than adafruit library for the sensor, Thank you !

  3. Wolfgang,
    Thank you for your quick reply. I scanned the port and it turned out the address was 45 not the 40 I had been using. I was mislead somewhat by a) inexperience and B0 that it I was obtaining data form other sketches but I think they used a universal address (sse DRRobot code for example).
    Anyway it is up and running at 20V and 5A output with correct values. May I ask one more thing. I have changed the current and buspower labels to read Amps and Watts instead of mA and mW, but I need to divide the values and I cannot find which line to modify. Could you point me in the right direction please. I really appreciate your help and my apologies for my basic understanding, hopefully I will improve on it.
    BTW, I continue to be impressed by the depth of your website.

    Regards,

    Roger

    1. Hi Roger, great that it works! I have only implemented the output in Milliwatts and Milliamps, so you have to divide by 1000. For example: Serial.println(power_mW/1000);. I don’t think it’s worth to implement a separate function. Best wishes, Wolfgang

  4. Hi Wolle,
    First off thank you for such a comprehensive website on the INA219. I have been using a DFRobot INA219 brd and have been running it successfully using several basic Arduino sketches. For my application however I wanted more flexibility in setting it up which brought me to your code. However when I run the code after setting up the appropriate modes all I get on the serial monitor is 0 values as follows:
    Shunt Voltage [mv]: 0.00
    Bus Voltage [V]: 0.00
    Load Voltage [V]: 0.00
    Current [mA] : nan and so on.

    I am a mechanical designer and have only a basic understanding of Ardunino code with only a few projects under my belt, so I am sorry to bother you if I have made a simple omission.
    I would apprecaite any help ypu can give me.
    Regards
    Roger

    1. Hi Roger,
      I have not tested this module from DFRobot, but in principle it should also work with my library. One difference is the shunt size. I seems to be 10 milliohms for this module. The default for my library is 100 milliohms. That means you should take the SetShuntSize.ino sketch as a basis and apply ina219.setShuntSizeInOhms(0.01); . However this does not explain why you get 0 for all values. Is the module detected at all? In the setup() I added::
      if(!ina219.init()){
      Serial.println(“INA219 not connected!”);
      }
      Therefore you should see “INA219 is not connected” if it’s not detected. And if it’s not detected, can you check if you chosen the right I2C address?

      Regards, Wolfgang

  5. Hello Wolfang

    great job with the library and this webpage. I am using your library on a custom circuit with an ATSAM3x (arduino due) processor. For some reason after power on with your examples in continuous mode I receive only a few correct measurements after which all measurements are 0.

    Any suggestions to debug?

    Best regards
    Joseph

    1. Hi Joseph,
      strange. I just dug out my Arduino Due, let the unchanged Continuous.ino sketch run for 15 minutes and still it works fine. Have you changed anything in the sketch? But I would guess the issue is on the hardware side. Maybe you need pull-ups for the I2C connection? Or an unstable power supply: If the power supply for the load is the same you use for the INA219, then it must be powerful enough, or just a capacitor might help. And do the Due, the INA219 and the load have a common GND? If you have circuit diagram, you could send it to me (wolfgang.ewald@wolles-elektronikkiste.de).
      Kind regards, Wolfgang

  6. Is it possible to use your library with an ATtiny85A that uses TinyWireM ?
    If so, how would I declare the object… INA219_WE ina219 = INA219_WE(I2C_ADDRESS);

    1. It needs some changes in the library. Would be an interesting extension. I think it is not a very big deal to adjust it, but I would have to try. The devil is always in the details. What would you do with the measured values? Display them? Or just calculate something? I ask this because memory space could also be a limiting factor. I am interested in looking deeper into that. But not before next weekend. Can you wait a bit?

      1. Hi Wolfgang – thanks for your swift response.
        Yes I can wait no problem at all (in fact I’m waiting for some PCBs from China for this project).
        I’m working on building a solar-powered battery charger for my weather stations.
        I’m developing the code on a Wemos D1 Mini but would like to use an ATtiny85A finally (as the current consumption of the Wemos is too high).
        The values are displayed on SSD1306 OLED panel.
        I’m going to use a pin on the ATtiny85A to turn a ZVP4424A MOSFET on/off which acts as a switch to power power to the OLED panel and the INA219.
        I have a touch-button that is connected to the Wemos (ATtiny). When the button is touched the Arduino code turns on the MOSFET, takes some readings and displays them, then after a few seconds turns things off. This is to save as much energy as possible.

        Kind regards from David (Retired microelectronics computer science lecturer).

        1. Hi David,
          good news: I have implemented the option to use TinyWireM. I tested it with an ATtiny85 and a 128×64 SSD1306 Display using Tiny4kOLED. So, exactly what you want.
          The new version is available on GitHub and should also be updated by the Arduino Library manager soon. I have added an example sketch.

          It was basically not difficult but I spent hours on one detail: In order to use TinyWireM, “USE_TINY_WIRE_M_” needs to be defined. And I learned that it does not work reliably if you just add
          #define USE_TINY_WIRE_M_
          to the sketch. For reasons I don’t the compiler does not include all parts in the order you use it in the sketch. It only worked by adding it into a separate header file which I called ina219_config.h. There you need to uncomment this line.
          I hope it will also work on your side. Have fun!
          Regards, Wolfgang

  7. Hi!
    what if we want to measure current changes of less than 100uA? (I work with very small currents)
    Could I just replace the R100 resistor for a bigger value?
    Greetings from Germany

    1. Yes, it should work this way. Take the alternative shunt and apply the value in setShuntSizeInOhm(). I have only tried smaller shunts so far, but I see no reason, why it shouldn’t work bigger ones.

  8. Hello,
    I’m using your CONTINUOUS MODE code, which works good with resistive load, but when I hook up my IB metal detector the current reading jumps around a bit. I only need/use the dc current reading.

    Is there a way to smooth or average the current reading?
    current_mA = ina219.getCurrent_mA();

    Thanks, Mac VE7DEP

    1. Hi Mac, I guess the reading jumps around because the current jumps around. It should be more stable if you use the setADCMode() function and choose SAMPLE_MODE_128, for example. In this case 128 readings are averaged. The readings are taken within 68 ms. Therefore this will only help if the fluctuation of the current is faster. Another technique to smoothen readings is to include former readings in current readings.

      currentReading = 0.1 * currentReading + 0.9 * lastReading;
      lastReading = currentReading;

      You could play with the factors.

      Hope this helps and you will find nice things with your metal detector.

  9. Hello Wolfgang,
    very fine job and intresting.
    How can we proced whith 3 x INA219 (3 addr different), scanning each other alternatively ?
    No problem with one, but with 3, i don’t find.

    Your code present a solution, but how make it in the LOOP ???

    INA219_WE ina219(I2C_ADDRESS);
    // INA219_WE ina219 = INA219_WE(); // Alternative: sets default address 0x40

    MAny thanks.
    Paul (S-O of France)

    1. Hello Paul,
      great to have a request from France! I think it’s the first one. To your question:
      It’s just a lot of typing, but shouldn’t be a problem. First you define three objects, e.g.
      INA219_WE ina219_1(0x40);
      INA219_WE ina219_2(0x41);
      INA219_WE ina219_3(0x44);
      You have to do all settings three times:
      ina219_1.init();
      ina219_2.init();
      ina219_3.init();
      ina219_1.setMeasureMode();
      ina219_2.setMeasureMode();
      ….and so on.
      And in the loop it’s also no problem:
      shuntVoltage_mV = ina219_1.getShuntVoltage_mV();
      busVoltage_V = ina219_1.getBusVoltage_V();
      …and so on, then you print the results and move to the second ina219:
      shuntVoltage_mV = ina219_2.getShuntVoltage_mV();
      busVoltage_V = ina219_2.getBusVoltage_V();
      …..and the same procedure for the third.

      An alternative, but a bit of waste of program space is to do everything in parallel:
      You define all variables three times:
      shuntVoltage_mV_1 = 0.0; shuntVoltage_mV_2 = 0.0; shuntVoltage_mV_3 = 0.0;

      shuntVoltage_mV_1 = ina219_1.getShuntVoltage_mV();
      shuntVoltage_mV_2 = ina219_2.getShuntVoltage_mV();
      …and so on.

      Hope this helps! Have fun and stay healthy, Wolfgang

      1. Hello Wolfgang,
        very very fine !
        It works well now, thank for your very quick reply.

        Then in paralell i have detect a very light offset between sensors, but no problem of linearity.
        It’s really plaisant to try this to obtain the same value (for the same tension ;o).

        Greating from Pyrenees, near “Lourdes”.
        Many thaks.
        Paul

        1. Great that it works! If we are allowed to travel this year I will spend my holidays in France – but in the Bretagne. Pyrenees sounds also good….

Leave a Reply

Your email address will not be published. Required fields are marked *