# 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 :

• Is a shunt resistor current measuring chip.

• Uses a simple I2C interface.

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

• Mearures voltage and current and therefore power.

This chip is a current measuring chip which can also measure voltage - actually it can only measure voltage, but cheats by measuring voltage across a known resistance.

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

Interesting features:

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

2. You can change the current measured (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.

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

6.Measurement is made by connecting from high supply V to your circuit.

## 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.

Vin- also measures the voltage appearing at the attached 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. You can change the current resolution by changing RSHUNT.

The bus voltage resolution, different to the current detection LSB above, is set at 4mV.

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 i.e. you can calculate it as above.

An example below shows you how to use a different RSHUNT.

## 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 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).

[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").

[Source: datasheet]

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

[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 and you can have an average of up to 128 samples.

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 far 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
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.

## Bit Range and PGA Gain

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

PGA Gain
FSR (mV)
LSB calc mV/pow(2,n)
8
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.

# Software

Arduino IDE Version used: 1.8.8

## Arduino Libraries

Instructions on installing libraries are here.

### Implicit assumption

The assumption built into the libraries is that a 0.1R resistor is used as the shunt resistor. If you want a different value you will need to re-write the library code.

This is not a bad assumption as it is the resistor most commonly used but you can get lower ones if you want less voltage drop e.g. 10mOhms etc.

This library has useful comments that go through the design process in setting up the INA219.

Install the library named Adafruit_INA219. Version used 1.0.9.
Also install the library named Adafruit_BUSIO. Version used 1.3.2.

Use the library manager, searching for "ina219", "BUSIO".

### INA219_WE

Install the library named INA219_WE, Version used 1.1.1.

Use the library manager, searching for "ina219".

(On github https://github.com/wollewald/INA219_WE)

### INA219_WE Library warning

#### IDE output error message

The Arduino IDE complains that Sensor is the wrong category - you can ignore this or change the text "Sensor" in the file library.properties (within the INA219_WE directory) to "Sensors" to fix it.

### INA219_WE Library Functions

Available INA219_WE functions are:
```	INA219_WE(int addr);
INA219_WE();			//sets default I2C Address 0x40
void init();
void reset_INA219();
void setCorrectionFactor(float corr);
void setMeasureMode(INA219_MEASURE_MODE mode);
void setPGain(INA219_PGAIN gain);
void setBusRange(INA219_BUS_RANGE range);
float getShuntVoltage_mV();
float getBusVoltage_V();
float getCurrent_mA();
float getBusPower();
bool getOverflow();
void startSingleMeasurement();
void powerDown();
void powerUp();

```

So you can calibrate the INA219 and get the readings for current, shunt voltage, bus voltage and power quite easily.

Available

```  Adafruit_INA219(uint8_t addr = INA219_ADDRESS);
bool begin(TwoWire *theWire = &Wire);
void setCalibration_32V_2A();
void setCalibration_32V_1A();
void setCalibration_16V_400mA();
float getBusVoltage_V();
float getShuntVoltage_mV();
float getCurrent_mA();
float getPower_mW();
void powerSave(bool on);
```

The Adafruit_INA219 library takes a different approach - pre-supplied calibrations.

It has three convenient calibrations already worked out for you:

1.     setCalibration_32V_2A();
2.     setCalibration_32V_1A();
3.     setCalibration_16V_400mA();

If you don't want to mess around figuring out stuff then these functions provide quick and easy setup:

For 1. above, the maximum expected current is 2A,
and a range of 32V (actually 26V maximum input).

For 2. above, the maximum expected current is 1A,
and a range of 32V (actually 26V maximum input).

For 3. above, the maximum expected current is 400mA,
and a range of 16V (this in actually is 16V maximum input although it                 withstands 26V).

The expected current is just that - it is the maximum current you expect to see and only changes the resolution of the resultant reading.

For quick use you can probably see a range setting you want to use.

# 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.

For this action use the simplest code (using library INA219_WE) to read the registers (see the code and explanation to do this here ).

Note: The code in the previous code link 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.

### Library Code Warning

Neither libraries do allow you to read or write registers directly so you need to do some simple modifications described below.

Warning: Neither library allows you to read or write registers directly.

For INA219_WE its quite easy - just make the read and write I2C routines public (or if feeling squeamish re-write the two read/write functions in c) and use the #defines to access the register values - use at least some of the hard work in the library!).

#### Library INA219_WE.h modification

In file INA219_WE.h, edit as follows for methods writeRegister and readRegister:

``````public:
void writeRegister(uint8_t reg, uint16_t val);

private:
INA219_MEASURE_MODE deviceMeasureMode;
INA219_PGAIN devicePGain;
INA219_BUS_RANGE deviceBusRange;
uint16_t calVal;
uint16_t calValCorrected;
uint16_t confRegCopy;
float currentDivider_mA;
float pwrMultiplier_mW;
bool calc_overflow;
//	void writeRegister(uint8_t reg, uint16_t val);

``````

To get the current measurement in fixed point form (See above) we only need:

`	ina219.readRegister(INA219_SHUNT_REG);`

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();
}

void loop(void) {

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

delay(1000);
}
``````

[ shetch: ina219.ino ]

Code size:

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

For comparison the blink sketch uses

Flash: 930 Bytes (3%)  , SRAM 9 Bytes (0%).

The wire library will take up a lot but that is common to all INA219 code.

### Sketch Using Full Equations

Just to illustrate the readings you get using fixed point the following sketch shows "normal" use of the library compared to, "raw registu7er 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();
}

// 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--------");
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```

# Library comparison Summary

You can find the original library and code for comparison (pdf) in the link.

These are for an Arduino Nano/Uno.

Fixed Point Results :         Flash: 4840 Bytes, SRAM 479 Bytes, Time: 2596us

Library INA219_WE :        Flash: 6108 Bytes, SRAM 489 Bytes, Time: 2256us.

Adafruit_INA219 Results : Flash: 8304 Bytes, SRAM 496 Bytes, Time: 3180us.

You can see that:

1. The amount of SRAM used does not change much.
2. The time taken is not vastly different but is fastest for library INA219_WE.
3. You can save 3464 Bytes with Fixed point c.f. library Adafruit_INA219.
4. You can save 1268 Bytes with Fixed point c.f. library INA219_WE.
5. The difference between INA219_WE and Adafruit_INA219 is 2196 Bytes.

TIP: Use the fixed point method if you want to save Flash memory.

The Flash size difference between using Adafruit_INA219 and INA219_WE is due to the library code size [5] (since in both cases floating point operations were used to calculate the results).

The Flash size difference between fixed point and INA219_WE is purely down to not using floating point [4] (since they both use the same library code - INA219_WE).

# INA219 Conclusions

The INA219 is a very easy chip to use 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.

Do all the libraries tested here work - giving the same results? : Yes.
Do all the libraries work at the same speed? : More-or-less (~within 1ms).
Do all the libraries use the same SRAM? : Yes.
Do all the libraries use the same Flash : No.

You can save significant memory by using specific libraries.

You can also save over 3k Bytes of flash if you don't use floating point operations.and instead, use fixed point here.

Of the two libraries studied here Adafruit_INA219 offers the easiest use, providing you with pre-calculated calibration options. However, it does not give you control so you can't set the number of samples etc. INA219_WE does offer this control.

TIP: Use a low shunt resistance to measure higher current.

If you change 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.