Interfacing with an
INA219
Current Sensor Device

Measuring current used to require an Op-Amp feeding a microcontroller's ADC via a low Ohmage resistor, but the INA219 device removes the need for the Op-Amp and the ADC and can be accomplished via an I2C interface. Making it much easier to use. There are several boards available that contain an INA219 device and the rest of the components required, and one such board is shown below:

The ready made boards usually come with a 0.1 Ohm resistor attached to them, so they will measure medium currents. However, if smalller currents are required to be measured. i.e. In the microAmp region, the resistor can be replaced with a 1.0 Ohm type, and the INA219_SetCal_16V_40mA() procedure called to set it up, and the INA219_GetCurrent_uA() procedure called to get the low current reading. Note that the resolution for the very low currents is in 10uA steps, so a measurement of 23uA will show as 20uA, and a measurement of 26uA will sometimes show as 30uA etc... The devices are not really designed for such low current measurements, so I had to "think out of the box" and create a pair of procedures that could cater for them with enough accuracy for a project I was creating. See the code listing below for a demonstration of the Positron8 INA219 library reading quite a high current using the 0.1 Ohm resistor that is the default on most of the ready made boards. It doesn't really get much simpler does it? when using the Positron compilers. :-)

INA219 Demonstration Program Listing (Expand to View the code listing by Clicking Here)

'
' /\\\\\\\\\
' /\\\///////\\\
' \/\\\ \/\\\ /\\\ /\\\
' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\
' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\
' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\
' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\
' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\
' \/// \/// \///// \////////// \////////// \///// \///// \////////\//
' Let's find out together what makes a PIC Tick!
'
' Read an INA219 current sensor device and display the current value on a serial terminal
' Written for the Positron8 BASIC compiler by Les Johnson
'

Device = 18F26K22 ' Tell the compiler what device to compile for
Declare Xtal = 16 ' Tell the compiler what frequency the device is operating at (in MHz)
Declare Float_Display_Type = Fast ' Alter the floating point display routine to faster and more accurate
'
' Setup USART1
'

Declare Hserial_Baud = 9600
Declare HRSOut1_Pin = PORTC.6
'
' Setup the pins to use for the I2C interface for the INA219 device
'

$define INA219_SDA_Pin PORTC.4 ' The INA219 I2C SDA (Data) pin
$define INA219_SCL_Pin PORTC.3 ' The INA219 I2C SCL (Clock) pin

Include "INA219.inc" ' Load the INA219 library routines into the program
'
' Create some variables for the demo
'

Dim fCurrent_A As Float ' Holds the Current result in Amps from the procedure INA219_GetCurrent_A
Dim fCurrent_mA As Float ' Holds the Current result in milliAmps from the procedure INA219_GetCurrent_mA
Dim fCurrent_uA As Float ' Holds the Current result in microAmps from the procedure fCurrent_uA

'------------------------------------------------------------------------------------------------------
' The main program starts here
' Read the INA219 device and display the measured current value on a serial terminal
'

Main:
'INA219_SetCal_16V_40mA() ' Set the INA219 to the required configuration to read low currents with a 1 Ohm resistor
'INA219_SetCal_16V_400mA() ' Set the INA219 to the required configuration to read currents with a 0.1 Ohm resistor
INA219_SetCal_32V_1A() ' Set the INA219 to the required configuration to read higher currents with a 0.1 Ohm resistor

Do ' Create a loop
fCurrent_A =
INA219_GetCurrent_A() ' Read the Current in Amps
fCurrent_mA =
INA219_GetCurrent_mA() ' Read the Current in milliAmps
'fCurrent_uA = INA219_GetCurrent_uA() ' Read the Current in microAmps

HRSOutLn "----------------------"
HRSOutLn "Current: ", Dec2 fCurrent_A, " A" ' Display the Amps on a serial terminal
HRSOutLn "Current: ", Dec2 fCurrent_mA, " mA" ' Display the milliAmps on a serial terminal
'HRSOutLn "Current: ", Dec0 fCurrent_uA, " uA" ' Display the microAmps on a serial terminal
DelayMS 1000 ' A small delay between readings so we can see things change
Loop ' Do it forever

Below is a screenshot of the above program listing, operating within the Proteus simulator:

Click Here to view the complete INA219.inc Library code listing

$ifndef _INA219_INC_
$define _INA219_INC_
'
' /\\\\\\\\\
' /\\\///////\\\
' \/\\\ \/\\\ /\\\ /\\\
' \/\\\\\\\\\\\/ /\\\\\ /\\\\\\\\\\ /\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\\\ /\\\\\\\\\
' \/\\\//////\\\ /\\\///\\\ \/\\\////// /\\\/////\\\ \////\\\//// \////\\\//// \////////\\\
' \/\\\ \//\\\ /\\\ \//\\\ \/\\\\\\\\\\ /\\\\\\\\\\\ \/\\\ \/\\\ /\\\\\\\\\\
' \/\\\ \//\\\ \//\\\ /\\\ \////////\\\ \//\\/////// \/\\\ /\\ \/\\\ /\\ /\\\/////\\\
' \/\\\ \//\\\ \///\\\\\/ /\\\\\\\\\\ \//\\\\\\\\\\ \//\\\\\ \//\\\\\ \//\\\\\\\\/\\
' \/// \/// \///// \////////// \////////// \///// \///// \////////\//
' Let's find out together what makes a PIC Tick!
'
' A library to read from an INA219 current sensor device using an I2C interface
' Written for the Positron8 BASIC compiler by Les Johnson
'

Symbol INA219_cI2C_Address = ($40 << 1) ' Once shifted left, will be %1000000 (pins A0 and A1 = GND)
'
' Defines for use with the INA219 device
'

$define cINA219_Conf_Reset $8000 ' Reset bit

$define cINA219_Conf_BusVoltRange_16V $0000 ' 0-16V Range
$define cINA219_Conf_BusVoltRange_32V $2000 ' 0-32V Range

$define cINA219_Conf_Gain_1_40mV $0000 ' Gain 1, 40mV Range
$define cINA219_Conf_Gain_2_80mV $0800 ' Gain 2, 80mV Range
$define cINA219_Conf_Gain_4_160mV $1000 ' Gain 4, 160mV Range
$define cINA219_Conf_Gain_8_320mV $1800 ' Gain 8, 320mV Range

$define cINA219_Conf_BusADCres_9bit $0080 ' 9-bit bus res = 0..511
$define cINA219_Conf_BusADCres_10bit $0100 ' 10-bit bus res = 0..1023
$define cINA219_Conf_BusADCres_11bit $0200 ' 11-bit bus res = 0..2047
$define cINA219_Conf_BusADCres_12bit $0400 ' 12-bit bus res = 0..4097

$define cINA219_Conf_SADCres_9bit_1S_84uS $0000 ' 1 x 9-bit shunt sample
$define cINA219_Conf_SADCres_10bit_1S_148uS $0008 ' 1 x 10-bit shunt sample
$define cINA219_Conf_SADCres_11bit_1S_276uS $0010 ' 1 x 11-bit shunt sample
$define cINA219_Conf_SADCres_12bit_1S_532uS $0018 ' 1 x 12-bit shunt sample
$define cINA219_Conf_SADCres_12bit_2S_1060uS $0048 ' 2 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_4S_2130uS $0050 ' 4 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_8S_4260uS $0058 ' 8 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_16S_8510uS $0060 ' 16 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_32S_17MS $0068 ' 32 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_64S_34MS $0070 ' 64 x 12-bit shunt samples averaged together
$define cINA219_Conf_SADCres_12bit_128S_69MS $0078 ' 128 x 12-bit shunt samples averaged together

$define cINA219_Conf_Mode_PowerDown $0000
$define cINA219_Conf_Mode_SVolt_Triggered $0001
$define cINA219_Conf_Mode_BVolt_Triggered $0002
$define cINA219_Conf_Mode_SandBVolt_Triggered $0003
$define cINA219_Conf_Mode_ADCOff $0004
$define cINA219_Conf_Mode_SVolt_Cont $0005
$define cINA219_Conf_Mode_BVolt_Cont $0006
$define cINA219_Conf_Mode_SandBVolt_Cont $0007
'
' INA219 Registers
'

$define cINA219_Reg_Config 0 ' Config register (R/W)
$define cINA219_Reg_ShuntVoltage 1 ' Shunt Voltage Register (R)
$define cINA219_Reg_BusVoltage 2 ' Bus Voltage Register (R)
$define cINA219_Reg_Power 3 ' Power Register (R)
$define cINA219_Reg_Current 4 ' Current Register (R)
$define cINA219_Reg_Calibration 5 ' Calibration Register (R/W)
'
' Setups for the calibration
'

$define cConfig_32V_2A cINA219_Conf_BusVoltRange_32V | cINA219_Conf_Gain_8_320mV | cINA219_Conf_BusADCres_12bit | cINA219_Conf_SADCres_12bit_1S_532uS | cINA219_Conf_Mode_SandBVolt_Cont

$define cConfig_32V_1A cINA219_Conf_BusVoltRange_32V | cINA219_Conf_Gain_8_320mV | cINA219_Conf_BusADCres_12bit | cINA219_Conf_SADCres_12bit_1S_532uS | cINA219_Conf_Mode_SandBVolt_Cont

$define cConfig_16V_400mA cINA219_Conf_BusVoltRange_16V | cINA219_Conf_Gain_1_40mV | cINA219_Conf_BusADCres_12bit | cINA219_Conf_SADCres_12bit_1S_532uS | cINA219_Conf_Mode_SandBVolt_Cont
'
' Create some variables for use with the library routines
'

Dim INA219_wCalValue As Word ' Holds the calibration value for the INA219
Dim INA219_fCurrentDiv_mA As Float ' Holds the division required for calibration
Dim INA219_fPowerMult_mW As Float ' Holds the multiplier required for calibration
Dim INA219_fRes As Float ' Used as an alias for the results of the procedures
Dim INA219_wReg As SWord

'------------------------------------------------------------------------------------------------------
' Writes to a register in the INA219 device
' Input : pReg holds the register address
' pValue holds the value to write
' Output : None
' Notes : None
'

Proc INA219_WriteRegister(pReg As Byte, pValue As Word)
I2COut INA219_SDA_Pin, INA219_SCL_Pin, INA219_cI2C_Address, pReg, [pValue]
EndProc

'------------------------------------------------------------------------------------------------------
' Reads a register's value from the INA219 device
' Input : pReg holds the register address
' Output : Returns the 16-bit value read (Result is aliased to global variable; INA219_wReg
' Notes : None
'

Proc INA219_ReadRegister(pReg As Byte), INA219_wReg
I2CIn INA219_SDA_Pin, INA219_SCL_Pin, INA219_cI2C_Address, pReg, [Result]
EndProc

'---------------------------------------------------------------------------------------------------------
' Configures the INA219 to be able to measure up to 32V and 2A of current.
' Each unit of current corresponds to 100uA, and each unit of power corresponds to 2mW.
' Counter overflow occurs at 3.2A.
' Input : None
' Output : INA219_fCurrentDiv_mA holds divisor required for some calculations in other procedures
' : INA219_fPowerMult_mW holds multiplier required for some calculations in other procedures
' Notes : These settings are for a 0.1 Ohm resistor on the INA219 PCB
'

Proc INA219_SetCal_32V_2A()
INA219_wCalValue = 4096
INA219_fCurrentDiv_mA = 10.0 ' Current LSB = 100uA per bit (1000/100 = 10)
INA219_fPowerMult_mW = 2.0 ' Power LSB = 1mW per bit (2/1)
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Load the Calibration register
INA219_WriteRegister(cINA219_Reg_Config, cConfig_32V_2A) ' Load the Config register for the settings
EndProc

'---------------------------------------------------------------------------------------------------------
' Configures the INA219 to be able to measure up to 32V and 1A of current.
' Each unit of current corresponds to 40uA, and each unit of power corresponds to 800mW.
' Counter overflow occurs at 1.3A.
' Input : None
' Output : INA219_fCurrentDiv_mA holds divisor required for some calculations in other procedures
' : INA219_fPowerMult_mW holds multiplier required for some calculations in other procedures
' Notes : These settings are for a 0.1 Ohm resistor on the INA219 PCB
'

Proc INA219_SetCal_32V_1A()
INA219_wCalValue = 10240
INA219_fCurrentDiv_mA = 25.0 ' Current LSB = 40uA per bit (1000/40 = 25)
INA219_fPowerMult_mW = 0.8 ' Power LSB = 800uW per bit
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Load the Calibration register
INA219_WriteRegister(cINA219_Reg_Config, cConfig_32V_1A) ' Load the Config register for the settings
EndProc

'---------------------------------------------------------------------------------------------------------
' Configure the INA219 to be able to measure up to 16V and 400mA max, with a resolution of 0.1mA
' Input : None
' Output : INA219_fCurrentDiv_mA holds divisor required for some calculations in other procedures
' : INA219_fPowerMult_mW holds multiplier required for some calculations in other procedures
' Notes : These settings are for a 0.1 Ohm resistor on the INA219 PCB
'

Proc INA219_SetCal_16V_400mA()
'
' Calibration that uses the highest precision for current measurement (0.1mA), and supporting 16V at 400mA max.
'
' V_Bus_Max = 16V
' V_Shunt_Max = 0.04 (Assumes Gain 1, 40mV)
' R_Shunt = 0.1 (Resistor value in ohms)
'
' Determine max possible current
' MaxPossible_I = VShunt_Max / R_Shunt
' MaxPossible_I = 0.4A
'
' Determine max expected current
' MaxExpected_I = 0.4A
'
' Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
' MinimumLSB = MaxExpected_I / 32767
' MinimumLSB = 0.0000122 (12uA per bit)
' MaximumLSB = MaxExpected_I / 4096
' MaximumLSB = 0.0000977 (98uA per bit)
'
' Choose an LSB between the min and max values (Preferrably a roundish number close to MinLSB)
' CurrentLSB = 0.00005 (50uA per bit)
'
' Calculate the calibration register
' CalValue = trunc (0.04096 / (Current_LSB * R_Shunt))
' CalValue = 8192


INA219_wCalValue = 8192
'
' Calculate the power LSB
' PowerLSB = 20 * CurrentLSB
' PowerLSB = 0.001 (1mW per bit)
'
' Calculate the maximum current and shunt voltage values before overflow
' Max_Current = Current_LSB * 32767
' Max_Current = 1.63835A before overflow
'
' If Max_Current > Max_Possible_I Then
' Max_Current_Before_Overflow = MaxPossible_I
' Else
' Max_Current_Before_Overflow = Max_Current
' EndIf
'
' Max_Current_Before_Overflow = MaxPossible_I
' Max_Current_Before_Overflow = 0.4
'
' Max_ShuntVoltage = Max_Current_Before_Overflow * R_Shunt
' Max_ShuntVoltage = 0.04V
'
' If Max_ShuntVoltage >= VShunt_Max Then
' Max_ShuntVoltage_Before_Overflow = VShunt_Max
' Else
' Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
' EndIf
'

' Max_ShuntVoltage_Before_Overflow = VShunt_Max
' Max_ShuntVoltage_Before_Overflow = 0.04V
'
' Calculate the Maximum Power
' MaximumPower = Max_Current_Before_Overflow * V_Bus_Max
' MaximumPower = 0.4 * 16V
' MaximumPower = 6.4W
'

INA219_fCurrentDiv_mA = 20.0 ' Current LSB = 50uA per bit (1000/50 = 20)
INA219_fPowerMult_mW = 1.0 ' Power LSB = 1mW per bit
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Load the Calibration register
INA219_WriteRegister(cINA219_Reg_Config, cConfig_16V_400mA) ' Load the Config register for the settings
EndProc

'---------------------------------------------------------------------------------------------------------
' Configure the INA219 to be able to measure up to 16V and 40mA max, with a resolution of 0.01mA
' Input : None
' Output : INA219_fCurrentDiv_mA holds divisor required for some calculations in other procedures
' : INA219_fPowerMult_mW holds multiplier required for some calculations in other procedures
' Notes : The default 0.1 Ohm resistor on the PCB must be changed to a 1.0 Ohm resistor
'

Proc INA219_SetCal_16V_40mA()
INA219_wCalValue = 8192 ' Calibration value
INA219_fCurrentDiv_mA = 200 ' Current LSB = 5uA per bit (1000/5 = 200)
INA219_fPowerMult_mW = 1.0 ' Power LSB = 100mW per bit
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Load the Calibration register
INA219_WriteRegister(cINA219_Reg_Config, cConfig_16V_400mA) ' Load the Config register for the settings
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the shunt voltage in mV (+-327mV) from the INA219 device
' Input : None
' Output : Returns the Shunt Voltage value in milliVolts
' Notes : None
'

Proc INA219_GetShuntVolts(), INA219_fRes
Result = INA219_ReadRegister(cINA219_Reg_ShuntVoltage) ' Read the Shunt Voltage register
Result = Result / 100
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the Bus voltage in Volts from the INA219 Device
' Input : None
' Output : Returns the Bus Voltage value
' : Returns 0 if an invalid reading is taken
' Notes : None
'

Proc INA219_GetBusVolts(), Byte
Dim fBsVolts As Float
Result = 1
INA219_wReg =
INA219_ReadRegister(cINA219_Reg_BusVoltage) ' Read the Bus Voltage register
'
' Shift to the right x3 to strip CNVR and OVF bits and then multiply by LSB(4mV)
'

INA219_wReg = INA219_wReg / 8
INA219_wReg = INA219_wReg * 4
fBsVolts = INA219_wReg
fBsVolts = fBsVolts * 0.001 ' Convert mV to V
'
' Check for a valid reading
'

If INA219_wReg.0 = 1 Then ' If bit 0 is set then an overflow has occured
Result = 0 ' So current and INA219_fPower readings could be invalid.
EndIf
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the Power reading from the INA219 Device
' Input : None
' Output : Returns the power value
' Notes : None
'

Proc INA219_GetPower(), INA219_fRes
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Reset the calibration value before reading register
Result = INA219_ReadRegister(cINA219_Reg_Power) ' Read the Power register
Result = Result * INA219_fPowerMult_mW
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the raw current value (16-bit signed integer, so +-32767) from the INA219 Device
' Input : None
' Output : Returns the raw current reading
' Notes : None
'

Proc INA219_GetCurrent_Raw(), SWord
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Reset the calibration value before reading register
Result = INA219_ReadRegister(cINA219_Reg_Current) ' Read the Current register
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the current value in mA from the INA219 device
' Input : None
' Output : Returns the current value in milliAmps
' Notes : None
'

Proc INA219_GetCurrent_mA(), INA219_fRes
Dim fShuntV As Float
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Reset the calibration value before reading register
fShuntV =
INA219_GetShuntVolts() ' Read the Shunt Voltage register

Result = INA219_ReadRegister(cINA219_Reg_Current) ' Read the Current register
Result = Result / INA219_fCurrentDiv_mA
If fShuntV < 0.1 Then
Result = fShuntV
EndIf
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the current value in Amps from the INA219 device
' Input : None
' Output : Returns the current value in Amps
' Notes : None
'

Proc INA219_GetCurrent_A(), INA219_fRes
Result = INA219_GetCurrent_mA() / 1000
EndProc

'---------------------------------------------------------------------------------------------------------
' Get the current value in uA from the INA219 device
' Input : None
' Output : Returns the current value in microAmps
' Notes : The default 0.1 Ohm resistor on the PCB must be changed to a 1.0 Ohm resistor
'

Proc INA219_GetCurrent_uA(), INA219_fRes
INA219_WriteRegister(cINA219_Reg_Calibration, INA219_wCalValue) ' Reset the calibration value before reading register
Result = INA219_ReadRegister(cINA219_Reg_Current) ' Read the Current register
Result = Result / 0.2
EndProc

'---------------------------------------------------------------------------------------------------------
' Check the prescence of the INA219 by sending a reset command and reading the config register back
' Input : None
' Output : 1 if the INA219 is present, 0 if not present
' Notes : None
'

Proc INA219_Check(), Bit
Result = 0
INA219_WriteRegister(cINA219_Reg_Config, $8000) ' Send a reset command to INA219
INA219_wReg =
INA219_ReadRegister(cINA219_Reg_Config) ' Read the Configuration register

If INA219_wReg = $399F Then ' $399F is the default reset state
Result = 1
EndIf
EndProc

$endif ' _INA219_INC_

The Positron8 INA219 library and demonstration source code, and the Proteus simulation project, as shown in the screenshot above, can be downloaded from here: Positron8 - INA219 - Current Sensor Source Code