INA219: Simple Arduino current measurement up to 3.2A. How your Arduino can measure 3.2A at 100uA resolution, measuring the voltage and power consumption at the same time, using an I2C chip.

The INA219 :

  • Measures current using a high-side shunt resistor.

  • Uses a simple I2C interface.

  • Has a resolution of 100uA (with a 0.1R shunt resistor).

  • Measures voltage and current and therefore power.
INA219 Breakout board

This chip measures voltage across a known resistance (usually 0.1R - see above image). You program the value of the resistance into the chip and it returns a value in Amps.

The breakout board version below can measure ~3.2A but the chip can measure 10~15A using a different sense resistor (See the datasheet for example calculations).

arduino INA219 - How to measure current using an Arduino

Interesting features:

1. Current (~3.2A) and voltage (~26V) measurement; these are independent of 5V (Vcc).

2. You can change the maximum current measured or the resolution measurement (change sense resistor + software).

3. The polarity of the measured input does not matter (but no reading for -ve).

4. Uses an internal opamp - no external amplification needed - it also has a defined accuracy so it does not need calibrating.

5.Can report power used in the load (as it measures I and V).

6. You make a measurement by connecting to high supply Voltage (usually 5V ~ but can be up to 26V) and then to your circuit, to the supply of the chip/unit you want to observe the current.

INA219 Specification

  Parameter
INA219
Voltage Supply (Vs)
 3V0 ~ 5V5
Abs. Max VDD (except analogue inputs)
-0V3 ~ 6V0
Analogue inputs (max differential)[1][4]
±26V
Analogue inputs (Common mode range)[4] -0.3 ~ 26V
Current resolution (depends on RSHUNT here 0R1)
100uA
Interface
I2C
I2C rate
100,400,2560kHz
Resolution (ADC)[5] 12 bit (15 bit)
Quiescent current (typ,max)
0.7mA, 1mA
Power down mode current (typ,max)
6, 15uA
Current error % A(typ,max) B(typ,max) [2]
(±0.2,±0.5) (±0.2,±0.3)
Current error over full temperature (A,B) [2] 1%,0.5%
Voltage error % A(typ,max) B(typ,max) (±0.2,±0.5) (±0.2,±0.5)
Voltage error over full temperature (A,B) 1%,1%
DNL
0.1LSB
Offset voltage (PGA =1,2,4,8)[2] (typ)
10,20,30,40uV
Offset voltage (PGA =1,2,4,8)[2] (max)[3] 100,125,150,200uV
Offset voltage drift
0.1uV/°C
Gain settings [programmable]
40mV, 80mV, 180mV and 320mV
Gain error
40m%
Conversion time (9bit res. ~ 12bit res., typ)
84~532us
I2C Addresses (h/w selected = 16off)
0x40 ~ 0x4F
VESD (Human body model)
±4000V
Operating temperature
-40°C ~125°C
    [1] The total voltage between +Ain and  -Ain must not exceed 0.3~26V.
    [2] The device comes in two accuracy standards A, B with B more accurate.
    [3] Offset voltage is lower for the B part - see datasheet.
    [4] Inputs are reversible
but you can only measure if +Ain > -Ain.
    [5] 15 bit resolution is achieved by averaging for ±320mV range.

How to Wire INA219

How to connect INA219: It's simple just connect Vin+ to your supply voltage (this is the high side voltage), and connect Vin- to your circuit - this is the power supply to your circuit.

The bus voltage (VIN-) is the voltage measured at your circuit.  So it is the Supply voltage minus the voltage developed across the shunt resistor - which is small because the resistor is 0.1Ohms. As your circuit draws more current more voltage is dropped across the shunt resistor and the bus voltage drops. That's the price of any measuring circuit.

Resolution

Current resolution is set by the minimum detectable voltage (across RSHUNT); 1 LSB of the ADC; fixed at 10uV. This is an internally set value controlled in the chip but you can change the current resolution by changing the value of RSHUNT.

    Therefore minimum detectable current = 10uV/ Shunt resistor.

    Typically RSHUNT = 0R1 so...

    Minimum detectable current (12 bit res.) = 10uV/0.1R = 100uA.

Normally the shunt resistor is 0.1Ohms, as these are commonly fitted to breakout boards. However, you can select any shunt resistor e.g. 2mOhm and the current resolution will change; changing the calculation above.

You can find an example design that uses a different shunt resistance value in the data sheet.

Note The bus voltage resolution, different to the current resolution and is set at 4mV per LSB.

Resolution with Averaging

The device resolution is odd as the physical ADC is 12-bit but the data sheet mentions 15-bits.

Oversampling is used to increase the effective ADC bits from the physical 12-bit  ADC, to a 15bit oversampled ADC output. This is why equations below (and in the datasheet) reference 215. Very confusing as the data sheet does not really mention oversampling!

INA219 Datasheet

Download the INA219 datasheet here.

INA219 Pinout

INA219 pinout
                                            [Source: Datasheet]

How the INA219 works

Inside the INA219 there is an ADC which can only measure voltage; Specifically the differential input VIN+ - VIN-, and using internal switches it also measures VIN- referenced to ground (using the same amplifier).

ina219 front end
[Source: datasheet]

The INA219 cannot measure current unless it knows the value of the external shunt resistance. This why there is are programmable registers for configuration and calibration (See Table 2 in the datasheet "Summary of Register Set").
INA219 back end block diagram
[Source: datasheet]

These registers and their operation are shown in the diagram below:

ina219 functional block diagram

[Source: datasheet]

Resolution Bits and Sampling Trade Off

By averaging any ADC reading you can increase the number of bits available. This chip can do that for you using up to 128 samples. The default start-up register state is set to 12bit conversion with a conversion time of 532us.

You can select a lower resolution result to give a faster read rate or you can allow the chip to oversample which results in a more accurate reading - but the read rate is slower. The table below shows the rate for the lower bit numbers:

  Bits/Samples
Conversion Time (typ)
Conversion Time (max)
  9 bit
84us
93us
  10 bit
148us
163us
  11 bit
276us304us
  12 bit [default]
532us
586us
  2 samples
1.06ms
1.17ms
  128 samples
68.10ms
75.01ms

    [Source: Datasheet Table 5 ADC Settings, and main specs.]

You can also use sample rates 4, 8, 16, 32, and 64. To find the conversion time simply multiply the sample rate chosen by the 12bit sample time e.g. 128 samples gives 128 * 532e-6 = 0.068096 (this is a typical value - the max is 75ms).

Bit Range and PGA Gain

The total number of bits collected depends on the PGA gain selected:

PGA Gain
ADC Bits (n)
FSR (mV)
LSB calc mV/pow(2,n)
  8 [default]
15
320
9.765uV
  4
14
160
9.765uV
  2
1380
9.765uV
  1
12
40
9.765uV

    [Source: Datasheet 8.6.3.1 Shunt Voltage Register]

FSR is the Full Scale Range and is measured across the shunt resistor.

As the gain is increased so the number of ADC bits increases keeping the LSB value the same. Increasing the number of bits by oversampling means the time to gather the result increases.

Notice how the LSB calculation is kept the same with changing gain - this keeps the resolution consistent across different gain settings.

Software

Arduino IDE Version used: 2.1.0

Arduino Libraries

Instructions on installing libraries are here.

INA219_WE

Install the library named INA219_WE, Version used 1.3.8.

    Use the library manager, searching for "ina219".

    (The library souce code is here https://github.com/wollewald/INA219_WE)

INA219_WE Library Functions

	INA219_WE(int addr);
	INA219_WE();			//sets default I2C Address 0x40
	
init()
reset_INA219()

setCorrectionFactor(float corr)
setShuntVoltOffset_mV(float offs)

setADCMode(INA219_ADC_MODE mode)
setMeasureMode(INA219_MEASURE_MODE mode)
setPGain(INA219_PGAIN gain)
setBusRange(INA219_BUS_RANGE range)
setShuntSizeInOhms(float shuntSize)

getShuntVoltage_mV()
getBusVoltage_V()
getCurrent_mA()
getBusPower()
getOverflow()
getConversionReady()

startSingleMeasurement()
startSingleMeasurement(unsigned long timeout_us)

powerDown()
powerUp()

writeRegister(uint8_t reg, uint16_t val)
readRegister(uint8_t reg)

 [pspad]
INA219 Calibration Functions

As well as the functions that retrieve data there are two calibration functions:

	setCorrectionFactor
setShuntVoltOffset_mV

The function setShuntVoltOffset_mV is used to compensate the offset voltage ±100uV (o error) for the internal ADC. Note that this offset changes with the programmed gain (see the specifications above).

You can use this function to zero the output for zero current - otherwise the offset makes it appear to indicate current flow when there is none!

You can use the other calibration function setCorrectionFactor to calibrate the INA219 if you have an accurate current meter you can compensate for the gain error of the ADC i.e. the slope ADC.

Default settings (Simple use)

The following explanation does not follow the process outlined in the datasheet because it is the simplest way to use the chip. It does however rely on the shunt resistor being a power of 10. That allows the fixed point method used here to work.

 This is a statement from that datasheet that allows simple use:

"The Calibration Register can also be selected to provide values in the Current Register (04h) and Power Register (03h) that either provide direct decimal equivalents of the values being measured, or yield a round LSB value for each corresponding register."

The idea is to use the minimal (I2C) read and write, and obtain an output that can be processed using fixed point maths - and this will save Flash memory.

If you don't program the chip then these are the defaults:

    PG Programmable Gain (8)   Shunt voltage ±320mV.
    Resolution                           12 bit.
    Bus Range                           32V
    Mode                                   Shunt and Bus continuous.

The datasheet indicates that without programming you can read the shunt voltage as an indication of current. So the voltage will be proportional to the current used. In fact the shunt voltage will be the shunt register value multiplied by 10uV (Datasheet: 8.5.1 Programming the Calibration Register).

Since you know the shunt resistance value then the current is simply:

    I  = VSHUNT / Resistance

You can just make this calculation in the microcontroller and save messing around. For the power you just multiply the bus voltage reading (VBUS) by the current value calculated.

Note: The code also sets the sample number to 128 for increased accuracy and resolution.

Sketch using simple equations

This section shows how to use the equations to derive voltage and current with minimal programming and without using floating point. This can save a lot of memory - for instance if you use a ATTiny85 saving memory is paramount.

Simple Current

The easiest way to get the current reading is to use the shunt voltage register directly. The equation is:

   RealShuntVoltage = ShuntVoltageRegister * 10e-6

Each LSB of the shunt voltage register is worth 10uV . What you need is the current so (when Rshunt = 0.1Ohm):

    Current = V/R = (ShuntVoltageRegister * 10e-6 ) / Rshunt
    Current = (ShuntVoltageRegister * 1e-4)

The 1e-4 value is saying the decimal point is four places to the left (see below for examples).

If the full maximum value of the shunt voltage is dropped across the shunt resistor (320mV) then it means that 3.2Amps is flowing (0.32V/0.1Ohms=3.2A). The value in the current register will be 32000 (Datasheet: Table 7).

Taking this value as a fixed point value means that the decimal point is 4 places to the left so:

For a current of 3.2A, the shunt register will contain 32000:

    32000 -> 3.2000A

Some more examples:

For a current of 100mA, the shunt register will contain 1000:

    01000 -> 0.1000A

 For a current of 10mA, the shunt register will contain 100:

    00100 -> 0.0100A

For a current of 1mA, the shunt register will contain 10:

    00010 -> 0.0010A

Warning: This technique only works easily for Shunt resistances with a power of 10. e.g. 0.1Ohm, 10mOhm, 1.0 Ohm etc.

These register values can easily be processed without floating point. For instance you could make a limit detect at 300mA just by detecting if the value is greater than 3000.

You can also display these values by converting them to an ASCII representation e.g. using itoa, and then displaying leading zeros as necessary. One example is shown here: ina219-compare.ino.

Bus Voltage

The bus voltage conversion has an LSB value of 4mV and we want a reading in millivolts. The bus register is shifted left by 3 bits in the hardware of the INA219.

To get the reading you could shift it right by 3 bits, then multiply by four, but since these are simple powers of two, a shift right by 1 bit results in the correct millivolt reading.

Fixed Point Reading Example

To output a useful value for display requires a little bit of formatting (and knowing where to place the fixed point). See ina219-compare.ino for how to do this.

Total (simple) code for this operation is :

#include <Wire.h>
#include <INA219_WE.h>

INA219_WE ina219(0x45);

void setup(void) {
   Wire.begin();
   Serial.begin(119200);

   ina219.init();
   ina219.setADCMode(SAMPLE_MODE_128); // Maximum averaging, ~78ms.
}

void loop(void) {

   int num = ina219.readRegister(INA219_SHUNT_REG);

   Serial.print("raw VS "); Serial.println(num);

   delay(1000);
}

    [ shetch: ina219.ino ]

Code size:

    Flash: 4182 Bytes (13%) , SRAM 431 Bytes (20%).

Sketch Using Full Equations, raw and fixed point.

This link lets you download a pdf that explains the INA219 equations (they were previously on this page).

The following sketch examines three use cases:

  1. Raw register output
  2. Library use with floating point calculations.
  3. Fixed point output.

You can see from the results that they all give the same "output". This works because the shunt resistor is a power of 10 - 0.1Ohms. You would have to use the "library" flocating point functions if you change the shunt resistor to a non power-of -ten value.

TIP: Using Fixed point (with no floating point used elsewhere) saves 2k Flash.

Just to illustrate the readings you get using fixed point the following sketch shows "normal" use of the library compared to, "raw register values", and "fixed point" formatting:

#include <Wire.h>
#include <INA219_WE.h>

INA219_WE ina219(0x45);

void setup(void) {
   Wire.begin();
   Serial.begin(119200);

   ina219.init();
   ina219.setADCMode(SAMPLE_MODE_128);
}

// Print fixed point with dp decimal places to left.
void printFixedPointdp(long v, int dp) {

   long dpdiv = 1;
   for(int i=0;i<dp;i++,dpdiv*=10);

   long left = v / dpdiv;
   long rght = v % dpdiv;
   if (left==0) Serial.print('0');  else Serial.print(left);
   Serial.print('.');
   if (rght==0) Serial.print("000");  else Serial.print(rght);
}

void loop(void) {

   // "Normal usage"
   Serial.println("float--------");
   float f = ina219.getCurrent_mA();
   Serial.print("I  "); Serial.print(f,4); Serial.println("mA");
   f = ina219.getShuntVoltage_mV();
   Serial.print("VS "); Serial.print(f,4); Serial.println("mV");
   f = ina219.getBusVoltage_V();
   Serial.print("VB "); Serial.print(f,4); Serial.println("V");
   f = ina219.getBusPower();
   Serial.print("P  "); Serial.print(f,4); Serial.println("mW");

   Serial.println("RAW--------");
   int vshuntreg = ina219.readRegister(INA219_SHUNT_REG);
   Serial.print("raw VS "); Serial.println(vshuntreg);

   long current = vshuntreg*10;
   Serial.print("raw  I "); Serial.println(current);

   int busvoltagereg = (ina219.readRegister(INA219_BUS_REG)&0xfff8)>>1; // BV is shifted left 3 bits.
   Serial.print("    BV "); Serial.println(busvoltagereg);

   long buspower = current * (long)busvoltagereg;
   Serial.print("     P "); Serial.println(buspower);

   // Print formatted fixed point values.
   Serial.println("Fixed------");
   Serial.print(" I ");  printFixedPointdp(current,2);       Serial.println("mA");
   Serial.print("VS ");  printFixedPointdp(vshuntreg,2);     Serial.println("mV");
   Serial.print("BV ");  printFixedPointdp(busvoltagereg,3); Serial.println("V");
   Serial.print(" P ");  printFixedPointdp(buspower,5);      Serial.println("mW");

   delay(1000);
}

    [ ina219-compare.ino ]

This is the typical output from the sketch above:

float--------
I  15.5000mA
VS 1.5500mV
VB 4.7880V
P  76.0000mW
RAW--------
raw VS 155
raw  I 1550
    BV 4788
     P 7421400
Fixed------
 I 15.50mA
VS 1.55mV
BV 4.788V
 P 74.21400mW

INA219 Observations

There are a lot of complicated equations that used to be on this page stored here. I removed them as the fundamental operation of the chip is simply to return the ADC value of the voltage developed across the shunt resistance (see fixed point code above). From that you can calculate everything else within your program.

The datasheet makes a great play of how the device has registers for

  • Power
  • Current
  • Voltage

However until you calculate the calibration register value and program it in, the device returns power and current as zero (because the calibration register defaults to zero).

The calibration register is set to use the shunt resistance value to allow power and current to be reported.

INA219 back end block diagram

Consider using the INA219 as it was intended

  1. You program the calibration register using your shunt resistance value.
  2. The chip calculates power and current using the calibration register.
  3. You get the "integer" values of power and current.

The data sheet says:

Shunt voltage is calculated by multiplying the Shunt Voltage Register contents with the Shunt Voltage LSB of 10µV.
    (Note: this equation allows fixed point operation as it uses a power of 10)

The data sheet also says:
The Bus Voltage register bits are not right-aligned. In order to compute the value of the Bus Voltage, Bus Voltage Register contents must be shifted right by three bits. This shift puts the BD0 bit in the LSB position so that the contents can be multiplied by the Bus Voltage LSB of 4-mV to compute the bus voltage measured by the device.

Your program must now use floating point calculations to obtain the final readings:
To get the shunt voltage calculate shunt_register * 10e-6

To get the current divide the shunt voltage by RSHUNT.

To get the bus voltage multiply bus_voltage_register by 4e-3.

The value expected in the Power register (03h) can be calculated by multiplying the Current register value by the Bus Voltage register value and then dividing by 5000 as shown in Equation 5. [datasheet]

The key issue is that these registers always contain an integer value. So when you want to get the "real" value from them you you have to do some calculations - probably floating point.

My point is why bother? - just get Vshunt out and calculate everything else within your program (or just use a library - or just use Fixed point as explained in this page). Even if the chip does an "integer" calculation you still have to do calculations anyway.

However the datasheet also says:

The Calibration Register can also be selected to provide values in the Current Register (04h) and Power Register (03h) that either provide direct decimal equivalents of the values being measured, or yield a round LSB value for each corresponding register

So you can probably get round having to do calculations in your program if you do some mental gymnastics in using the chip!

INA219 Conclusions

The INA219 is a very easy chip to use (if you use a library and don't look at the complicated equations!) and it is really plug in and go - as long as your measurement voltage is within 26V. You can use the standard libraries to easily obtain readings for current, bus voltage and power.

TIP: Using fixzed point calculations saves 2k Flash.

It can measure with a resolution of 100uA using the default sense resistor: 0.1Ohms.

TIP: Change the shunt resistance -lower- to measure higher current.

For instance, if you changed the shunt resistor to 0.02 Ohms you can measure 10~15A. (See the datasheet for an example). You have to re-calculate the calibration register settings for this resistance.


Written by John Main, who has a degree in Electronic Engineering.


Comments

Have your say about what you just read! Leave me a comment in the box below.

Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.




Privacy Policy | Contact | About Me

Site Map | Terms of Use