Catch INA219 Raw data

Hello everyone,

I installed sensor INA219, completing voltage and current both measurement.

And load, for start, a library from Adafruit.

It work so fine, but with at a little bit thing, were I would like to transmit the raw data of the sensor, and not the floating data.

I reduced the exemple of "get current" from Adafruit INA219, and tryed some things without any success.

I now that the sensor have 12bit of resolution, and normaly must send raw data 0 to 4095, where are values interesting me, not values like 1.25 or 5.22.

Apparently this raw to floating conversion, is done in the library, and I am too newbie to understand of modify it.

But there is a sintax or function to catch directly theses raw data, without any conversion ?

Thanks so much, for any help

Here is the code I used :

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

 

void setup(void) 
{
  Serial.begin(1200);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);

  }
    
  Serial.println("Start");
  
  // Initialize the INA219.
  // By default the initialization will use the largest range (32V, 2A).  However
  // you can call a setCalibration function to change this range (see comments).
  if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }
  }
  // To use a slightly lower 32V, 1A range (higher precision on amps):
  //ina219.setCalibration_32V_1A();
  // Or to use a lower 16V, 400mA range (higher precision on volts and amps):
  //ina219.setCalibration_16V_400mA();


}

void loop(void) 
{



 // pinMode(2, INPUT_PULLUP);
  
 // float current_mA = 0;
  int busvoltage = 0;

 // int contrl = digitalRead(2);
 // int inputv = analogRead (A6);
 // int temp = analogRead (A2);
  

  busvoltage = ina219.getBusVoltage_V();
 // current_mA = ina219.getCurrent_mA();
  
  
 // Serial.print("$");Serial.print(contrl);
 // Serial.print(";");Serial.print(inputv);
 // Serial.print(";");Serial.print(temp);
  Serial.print(";");Serial.print(busvoltage);
 // Serial.print(";");Serial.print(current_mA);
  
  Serial.println("");

  delay(1000);
}

The file Adafruit_INA219/Adafruit_INA219.h at master · adafruit/Adafruit_INA219 · GitHub contains:

  int16_t getBusVoltage_raw();
  int16_t getShuntVoltage_raw();
  int16_t getCurrent_raw();
  int16_t getPower_raw();

They are in the private section of the .h file. You could perhaps try moving them into the public section and try calling them from your code

Alternative and imho safest solution is to undo the toFloat

int16_t raw = busVoltage() * constant; // constant to be determined.

The advantage is that some INA2xy have raw values that can be interpreted in different ways depending on a gain factor etc. Don't know from my head if the INA219 has this dependency.

Thanks for replies both,

I will try theses solutions monday when come back to my house.
And will report, if it work.

Thanks a lot, for help !

I'm not a C expert but as Dave_Lowther said, there are four private functions into the Adafruit_INA219 library, and according to this solution C++ : How do I call private methods through public ones? you can do the following:

In the Adafruit_INA219.h file, you have this class:

class Adafruit_INA219 {
public:
  Adafruit_INA219(uint8_t addr = INA219_ADDRESS);
  ~Adafruit_INA219();
  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);
  bool success();

private:
  Adafruit_I2CDevice *i2c_dev = NULL;

  bool _success;

  uint8_t ina219_i2caddr = -1;
  uint32_t ina219_calValue;
  // The following multipliers are used to convert raw current and power
  // values to mA and mW, taking into account the current config settings
  uint32_t ina219_currentDivider_mA;
  float ina219_powerMultiplier_mW;

  void init();
  int16_t getBusVoltage_raw();
  int16_t getShuntVoltage_raw();
  int16_t getCurrent_raw();
  int16_t getPower_raw();
};

You can define four new methos into the "public" section:

class Adafruit_INA219 {
public:
  Adafruit_INA219(uint8_t addr = INA219_ADDRESS);
  ~Adafruit_INA219();
  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);
  bool success();
  int16_t publicGetBusVoltageRaw();
  int16_t publicGetShuntVoltageRaw();
  int16_t publicGetCurrentRaw();
  int16_t publicGetPowerRaw();


private:
  Adafruit_I2CDevice *i2c_dev = NULL;

  bool _success;

  uint8_t ina219_i2caddr = -1;
  uint32_t ina219_calValue;
  // The following multipliers are used to convert raw current and power
  // values to mA and mW, taking into account the current config settings
  uint32_t ina219_currentDivider_mA;
  float ina219_powerMultiplier_mW;

  void init();
  int16_t getBusVoltage_raw();
  int16_t getShuntVoltage_raw();
  int16_t getCurrent_raw();
  int16_t getPower_raw();
};

Now, go to the Adafruit_INA219.cpp file and at the end add these methods:

int16_t Adafruit_INA219::publicGetShuntVoltageRaw(){
  return getShuntVoltage_raw(); 
}

int16_t Adafruit_INA219::publicGetBusVoltageRaw(){
  return getBusVoltage_raw();
}

int16_t Adafruit_INA219::publicGetCurrentRaw(){
  return getCurrent_raw();
}

int16_t Adafruit_INA219::publicGetPowerRaw(){
  return getPower_raw();
}

save the files and you can implement this code:

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

  uint16_t busVoltageRaw;
  uint16_t shuntVoltageRaw;
  uint16_t currentRaw;
  uint16_t powerRaw;

  char s1[10];
  char s2[10];
  char s3[10];
  char s4[10];

void setup(void) 
{
  Serial.begin(115200);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);
  }
    
  Serial.println("Hello!");
  
  // Initialize the INA219.
  // By default the initialization will use the largest range (32V, 2A).  However
  // you can call a setCalibration function to change this range (see comments).
  if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }
  }
  // To use a slightly lower 32V, 1A range (higher precision on amps):
  //ina219.setCalibration_32V_1A();
  // Or to use a lower 16V, 400mA range (higher precision on volts and amps):
  //ina219.setCalibration_16V_400mA();

  Serial.println("Measuring voltage and current with INA219 ...");
}

void loop(void) 
{
  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;
  float power_mW = 0;
  
  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage - (shuntvoltage / 1000);


  busVoltageRaw = ina219.publicGetBusVoltageRaw();
  shuntVoltageRaw = ina219.publicGetShuntVoltageRaw();
  currentRaw = ina219.publicGetCurrentRaw();
  powerRaw = ina219.publicGetPowerRaw();
  
  sprintf(s1, "%04X", busVoltageRaw);
  sprintf(s2, "%04X", shuntVoltageRaw);
  sprintf(s3, "%04X", currentRaw);
  sprintf(s4, "%04X", powerRaw);

  Serial.print("Bus Voltage register: 0x"); Serial.println(s1); 
  Serial.print("Shunt Voltage register: 0x"); Serial.println(s2);
  Serial.print("Current register: 0x"); Serial.println(s3);
  Serial.print("Power register: 0x"); Serial.println(s4);
  Serial.println("");
  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println("");

  delay(2000);
}

Good evening everybody's !

In first, please take my apologies for my late reply 3 days after your messages.

belmont1591,

Tanks so much for your precious and detailed help !

I applied the modification in the .cpp and .h files of Adafruit ina219 library, like you described.

And Applied with a little bit of modification the final code of the get current sketch.

And it work !

Exactly what I searched !

I just have to understood why the current register Hex value won't correspond to analog value.

I have to check in the ina 219 documentation.

But it is a most than a good startment for my power monitor.

Thanks a lot for the help !

I just have to understood why the current register Hex value won't correspond to analog value.

Because the value of the Current_Register is (according to INA219 Datasheet, page 12)

Current_Register = (Shunt_Voltage_Register x Calibration_Register)/4096

In my case, the ShuntVoltage_Register is 0x714 = 1812 in decimal, and the value of my Calibration_Register is 6710 (in decimal), so

Current_Register = (1812*6710)/4096 = 2968 = 0x0B98

This is because I modified the value of the Calibration_Register, but In your case, to know the value of the Calibration_Register, you need to see the Adafruit_INA219.cpp file, inside the setCalibration_32V_2A() method, specifically the line 225

void Adafruit_INA219::setCalibration_32V_2A() {
  // By default we use a pretty huge range for the input voltage,
  // which probably isn't the most appropriate choice for system
  // that don't use a lot of power.  But all of the calculations
  // are shown below if you want to change the settings.  You will
  // also need to change any relevant register settings, such as
  // setting the VBUS_MAX to 16V instead of 32V, etc.

  // VBUS_MAX = 32V             (Assumes 32V, can also be set to 16V)
  // VSHUNT_MAX = 0.32          (Assumes Gain 8, 320mV, can also be 0.16, 0.08,
  // 0.04) RSHUNT = 0.1               (Resistor value in ohms)

  // 1. Determine max possible current
  // MaxPossible_I = VSHUNT_MAX / RSHUNT
  // MaxPossible_I = 3.2A

  // 2. Determine max expected current
  // MaxExpected_I = 2.0A

  // 3. Calculate possible range of LSBs (Min = 15-bit, Max = 12-bit)
  // MinimumLSB = MaxExpected_I/32767
  // MinimumLSB = 0.000061              (61uA per bit)
  // MaximumLSB = MaxExpected_I/4096
  // MaximumLSB = 0,000488              (488uA per bit)

  // 4. Choose an LSB between the min and max values
  //    (Preferrably a roundish number close to MinLSB)
  // CurrentLSB = 0.0001 (100uA per bit)

  // 5. Compute the calibration register
  // Cal = trunc (0.04096 / (Current_LSB * RSHUNT))
  // Cal = 4096 (0x1000)

  ina219_calValue = 4096; //THIS IS THE CALIBRATION_REGISTER VALUE

  // 6. Calculate the power LSB
  // PowerLSB = 20 * CurrentLSB
  // PowerLSB = 0.002 (2mW per bit)

  // 7. Compute the maximum current and shunt voltage values before overflow
  //
  // Max_Current = Current_LSB * 32767
  // Max_Current = 3.2767A before overflow
  //
  // If Max_Current > Max_Possible_I then
  //    Max_Current_Before_Overflow = MaxPossible_I
  // Else
  //    Max_Current_Before_Overflow = Max_Current
  // End If
  //
  // Max_ShuntVoltage = Max_Current_Before_Overflow * RSHUNT
  // Max_ShuntVoltage = 0.32V
  //
  // If Max_ShuntVoltage >= VSHUNT_MAX
  //    Max_ShuntVoltage_Before_Overflow = VSHUNT_MAX
  // Else
  //    Max_ShuntVoltage_Before_Overflow = Max_ShuntVoltage
  // End If

  // 8. Compute the Maximum Power
  // MaximumPower = Max_Current_Before_Overflow * VBUS_MAX
  // MaximumPower = 3.2 * 32V
  // MaximumPower = 102.4W

  // Set multipliers to convert raw current/power values
  ina219_currentDivider_mA = 10; // Current LSB = 100uA per bit (1000/100 = 10)
  ina219_powerMultiplier_mW = 2; // Power LSB = 1mW per bit (2/1)

  // Set Calibration register to 'Cal' calculated above
  Adafruit_BusIO_Register calibration_reg =
      Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
  calibration_reg.write(ina219_calValue, 2);

  // Set Config register to take into account the settings above
  uint16_t config = INA219_CONFIG_BVOLTAGERANGE_32V |
                    INA219_CONFIG_GAIN_8_320MV | INA219_CONFIG_BADCRES_12BIT |
                    INA219_CONFIG_SADCRES_12BIT_1S_532US |
                    INA219_CONFIG_MODE_SANDBVOLT_CONTINUOUS;
  Adafruit_BusIO_Register config_reg =
      Adafruit_BusIO_Register(i2c_dev, INA219_REG_CONFIG, 2, MSBFIRST);
  _success = config_reg.write(config, 2);
}

You will see that

 ina219_calValue = 4096;

So basically, your Current_Register value will be

Current_Register = (Shunt_Voltage_Register x Calibration_Register)/4096 = (Shunt_Voltage_Register x 4096) / 4096 = Shunt_Voltage_Register

NOTE: Current_Register IS DIFFERENT TO current (in ampers)

To obtain the value of the current (in ampers) then you need to use the following equation

Current(A) = Current_Register x Current_LSB

Again, in my case, the value of my Current_LSB is 61.035uA per bit (because I made some modifications), so:
Current(A) = 2968 x 61.035uA = 0.18115 A = 181.15 mA

In your case, the value of the Current_LSB will be 100uA per bit (see line 257 of the .cpp file)

  // Set multipliers to convert raw current/power values
  ina219_currentDivider_mA = 10; // Current LSB = 100uA per bit (1000/100 = 10)
  ina219_powerMultiplier_mW = 2; // Power LSB = 1mW per bit (2/1)

Hello Belmont 1591,

Thanks for this return about the parameters detail of the sensor.

In my personal case, I use a shunt of 10 mOhms, for a maximum current of 3.5 A, into 5,2 v.

Max shunt voltage, will be 35 mV.

I started the calcul in the Adafruit .cpp file, which caused a calibration number very high (5120000), and a LSB current of 8µA / bit.

I think, I need to play with the bit setting gain.

I have to prepar my calcul, in accordance with the sensor calcul detail before loading everything.

Just a question,

What is difference between max possible current and max expected current ?

Does it Max current and nominal current ?

Are you sure that the value in decimal of your calibration_register is equal to 5,120,000?

Could you please add one more public method in the ina219 .h and .cpp files?

  1. Added calibrationRegisterValue() Method in .h file:
class Adafruit_INA219 {
public:
  Adafruit_INA219(uint8_t addr = INA219_ADDRESS);
  ~Adafruit_INA219();
  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);
  bool success();
  int16_t publicGetBusVoltageRaw();
  int16_t publicGetShuntVoltageRaw();
  int16_t publicGetCurrentRaw();
  int16_t publicGetPowerRaw();
  uint16_t calibrationRegisterValue();


private:
  Adafruit_I2CDevice *i2c_dev = NULL;

  bool _success;

  uint8_t ina219_i2caddr = -1;
  uint32_t ina219_calValue;
  // The following multipliers are used to convert raw current and power
  // values to mA and mW, taking into account the current config settings
  uint32_t ina219_currentDivider_mA;
  float ina219_powerMultiplier_mW;

  void init();
  void customInit();
  int16_t getBusVoltage_raw();
  int16_t getShuntVoltage_raw();
  int16_t getCurrent_raw();
  int16_t getPower_raw();
};
  1. At the very end of your .cpp file add this
uint16_t Adafruit_INA219::calibrationRegisterValue(){
uint16_t value;

  Adafruit_BusIO_Register calibrationRegister = Adafruit_BusIO_Register(i2c_dev, INA219_REG_CALIBRATION, 2, MSBFIRST);
  _success = calibrationRegister.read(&value);
  return value;
}

And you can try with this code to see the calibration_register value:

#include <Wire.h>
#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

  uint16_t busVoltageRaw;
  uint16_t shuntVoltageRaw;
  uint16_t currentRaw;
  uint16_t powerRaw;
  uint16_t calibrationRegister;

  char s1[10];
  char s2[10];
  char s3[10];
  char s4[10];
  char s5[20];

void setup(void) 
{
  Serial.begin(115200);
  while (!Serial) {
      // will pause Zero, Leonardo, etc until serial console opens
      delay(1);
  }
    
  Serial.println("Hello!");
  
  // Initialize the INA219.
  // By default the initialization will use the largest range (32V, 2A).  However
  // you can call a setCalibration function to change this range (see comments).
  if (! ina219.begin()) {
    Serial.println("Failed to find INA219 chip");
    while (1) { delay(10); }
  }
  // To use a slightly lower 32V, 1A range (higher precision on amps):
  //ina219.setCalibration_32V_1A();
  // Or to use a lower 16V, 400mA range (higher precision on volts and amps):
  //ina219.setCalibration_16V_400mA();

  Serial.println("Measuring voltage and current with INA219 ...");
}

void loop(void) 
{
  float shuntvoltage = 0;
  float busvoltage = 0;
  float current_mA = 0;
  float loadvoltage = 0;
  float power_mW = 0;
  
  calibrationRegister = ina219.calibrationRegisterValue();

  shuntvoltage = ina219.getShuntVoltage_mV();
  busvoltage = ina219.getBusVoltage_V();
  current_mA = ina219.getCurrent_mA();
  power_mW = ina219.getPower_mW();
  loadvoltage = busvoltage - (shuntvoltage / 1000);


  busVoltageRaw = ina219.publicGetBusVoltageRaw();
  shuntVoltageRaw = ina219.publicGetShuntVoltageRaw();
  currentRaw = ina219.publicGetCurrentRaw();
  powerRaw = ina219.publicGetPowerRaw();
  
  sprintf(s1, "%04X", busVoltageRaw);
  sprintf(s2, "%04X", shuntVoltageRaw);
  sprintf(s3, "%04X", currentRaw);
  sprintf(s4, "%04X", powerRaw);
  sprintf(s5, "%04X", calibrationRegister);

  Serial.println("********************************************************************");
  Serial.print("Calibration Register Value: 0x"); Serial.println(s5);
  Serial.print("Bus Voltage register: 0x"); Serial.println(s1); 
  Serial.print("Shunt Voltage register: 0x"); Serial.println(s2);
  Serial.print("Current register: 0x"); Serial.println(s3);
  Serial.print("Power register: 0x"); Serial.println(s4);
  Serial.println("");
  Serial.print("Bus Voltage:   "); Serial.print(busvoltage); Serial.println(" V");
  Serial.print("Shunt Voltage: "); Serial.print(shuntvoltage); Serial.println(" mV");
  Serial.print("Load Voltage:  "); Serial.print(loadvoltage); Serial.println(" V");
  Serial.print("Current:       "); Serial.print(current_mA); Serial.println(" mA");
  Serial.print("Power:         "); Serial.print(power_mW); Serial.println(" mW");
  Serial.println("");

  delay(2000);
}

My output is (with Vbus = 2.76, RL = 39 Ohms and Rshunt= 100mOhms):
You can se the value of the cal_register which is 0x1A36 = 6710 in decimal as I mentioned in #post7

I was asking about the value stored in your calibration_register because the INA219's registers are 16-bit length and the maximum value you can store in the register is maxValue = 2^16 = 65536 (0-65535).

If you want to measure a maxCurrent of 3.5A with a Rshunt of 10mOhms then:

Current_LSB = maximum_expected_current/2^15
= 3.5A/2^15
= 106.8115234uA (per bit)

The value of the cal_register must be:

Cal = trunc(0.04096/(Current_LSB * Rshunt))
= trunc( 0.04096/(106.8115234uA * 10mOhm)
= 38347 (and NOT 5,120,000)

What is difference between max possible current and max expected current ?

I honestly don't know how they came to that conclusion that MaxExpected_I = 2.0A, sorry

Oh, and since you are modifying the library, it is recommended that you modify the getCurrent_mA() method to (yes, it is a hardcoded way but...):

float Adafruit_INA219::getCurrent_mA() {
  int16_t valueDec = getCurrent_raw();
  float Current_LSB = 3.5/pow(2,15);
  float current = valueDec * Current_LSB * 1000.0;
  return current;
}

//In this case, 3.5/pow(2,15) is your Current_LSB Value. here I'm assuming
// you didn't round that value.
// 3.5 is the value of your max current you want to measure

and the getPower_mW() method:

float Adafruit_INA219::getPower_mW() {
  uint16_t powerRaw = getPower_raw();
  float Current_LSB = 3.5/pow(2,15);
  float powerLSB = 20.0 * Current_LSB;
  float power = powerRaw * powerLSB * 1000.0;
  return power;
}

Hello Belmont 1591,

Effectively,

Congratulation and shame on me, what saying more, excepted relearn how to read on my my calculator....
Less concentraction with speedy work, equal to big bulshit...

Effectively, I obtain now a vallue of calibration between 40960 (with 100 µA/bits) and 37236 (with 110 µA /bits), with rounded values.

I totally forgot one pretty interessting thing of the sensor, which is it minimum function too, that it must have, the direct shunt voltage value, with 12 bit resolution ...

Which replace all theses calculations by a direct value of the shunt, but adding a supplementary conversion in the sketch.

However, applying the calculation and this function, it permit to gain some coding lines...

And I will apply your recommendations on the library.

I ask myself what it is, if you said, not a C expert ...

Because, you helped me a lot, in solving lot of my issues in quicky time,

And I thanks you a lot Belmont 1591.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.