Project overview

Test Setup - Current Monitor

Bill of Materials

Item Quantity Description Notes
Raspberry Pi Pico 1 Microcontroller Board Main controller
INA226-Board 1 INA226 Current monitor module Measuring the current
LCD Display 1 1602 LCD module display 2x16 Display that shows the measured current
Breadboard 1 Prototyping board For wiring
10k potentiometer 1 Adjustable 10k potentiometer To adjust the brightness with the VO-Pin on the LCD
Wires - Solid copper wires Connecting the boards

Wiring​

wiring-pico-ina226-lcd
Figure 1: Wiring between Pi PICO,INA226 and LCD in KICAD
layout-ina226-board
Figure 2: Layout of the INA226 Board
wiring-pico-ina226-lcd
Figure 3: Wiring on the breadboard

For the wiring of the LCD I have used the 4-bit mode, like it is explained on the LCD Arduino Library page.

I have configured the I2C0 Pins GP20(SDA) and GP21(SCL) on the RPI Pico to communicate with the INA226. Just to mention, the INA226 can also measure a voltage on the VBUS pin, which I don´t use. However it can be changed, if needed, later on. The 10k potentiometer adjusts the brightness/contrast of the display. I just looked for the resistance where the text was the clearest.

Programming​

I have programmed the RPI Pico through the Arduino IDE. The only thing to adjust, is to set the board setting on "Raspberry Pi Pico".

#include <Wire.h>
#include <LiquidCrystal.h>

#define INA_ADDR_1 0x40

// rs, enable, d4, d5, d6, d7
LiquidCrystal lcd(16, 19, 28, 27, 26, 22);

// I2C-Pins anpassen
const int PIN_SDA = 20;   // <- deine Pins
const int PIN_SCL = 21;


void write16(uint8_t ina_addr, uint8_t reg, uint16_t val){
  Wire.beginTransmission(ina_addr);
  Wire.write(reg);
  Wire.write(val >> 8);
  Wire.write(val & 0xFF);
  Wire.endTransmission();
}

int16_t read16s(uint8_t ina_addr,uint8_t reg){
  Wire.beginTransmission(ina_addr);
  Wire.write(reg);
  Wire.endTransmission(false);
  Wire.requestFrom(ina_addr, (uint8_t)2);
  return (int16_t)((Wire.read()<<8)|Wire.read());
}


void setup(){

  Wire.setSDA(PIN_SDA); Wire.setSCL(PIN_SCL);
  Wire.begin();

  // --- AVG=128, t=1.1ms, MODE=continuous ---  
  write16(INA_ADDR_1,0x00, 0x0927);


  //--- Calibration for Rshunt=0.1 Ω (Current_LSB=10 µA/bit) max current 0.33 A---
  write16(INA_ADDR_1,0x05, 0x1400);   // CAL=5120

  lcd.begin(16,2);
  
}



double I_acc_mAs = 0.0f;
double I_acc_mAh = 0.0f;

unsigned long lastSecondTime = 0;
unsigned long lastUpdateTime = 0;
unsigned long seconds = 0;
unsigned long minutes = 0;
unsigned long hours = 0;



void loop(){
  unsigned long now = millis();

  if (now - lastSecondTime >= 1000) {
    lastSecondTime = now;
    seconds++;
    if(seconds > 59) 
    {
      seconds = 0;
      minutes++;
    }
    if(minutes > 59){
      minutes = 0;
      hours++;
    }
  }



  //Offset quick fix
  int8_t shunt_off_raw_1 = 0;

  // raw value
  int16_t shunt_raw_1 = read16s(INA_ADDR_1,0x01) - shunt_off_raw_1;// SHUNT VOLTAGE

  float Ushunt_mV_1 = shunt_raw_1 * 2.5e-3;       // 2,5 µV/LSB -> mV
  float I_mA_UR_1   = Ushunt_mV_1 / 0.1f;         // Rshunt=0.1 Ω

  int16_t cur_raw_1 = read16s(INA_ADDR_1,0x04) - shunt_off_raw_1 ; // CURRENT (needs CAL)
  float I_mA_reg_1  = cur_raw_1 * 0.01f;         // 10 µA/Bit

  
  I_acc_mAs = I_acc_mAs + I_mA_reg_1 * 0.5f;  // accumulate the current every 0.5 seconds  


  lcd.clear();              
  lcd.setCursor(0, 0);
  
  lcd.print("I: ");
  lcd.print(I_mA_reg_1,3);
  lcd.print("mA");

  // update only every 10 seconds ---
  if (now - lastUpdateTime >= 10000) {  // 10 000 ms = 10 s
    lastUpdateTime = now;
    I_acc_mAh = I_acc_mAs / 3600.0;

  }


  lcd.setCursor(0, 1);
  if(I_acc_mAh < 100) lcd.print(I_acc_mAh ,3);
  else lcd.print(I_acc_mAh ,2);
  lcd.print("mAh ");


  if(hours < 10)lcd.print("0");
  lcd.print(hours);
  lcd.print(":");

  if(minutes < 10)lcd.print("0");
  lcd.print(minutes);

  delay(500);
}



    

INA226 Functionality

The maximum voltage, which the INA226 can measure is 81.92 mV. The LSB is 2.5 uV(25 uA with 0.1 Ohm). With a resistor of 0.1 Ohm it means that the maximum measurable current is 0.8192 Amps. Currents above that will saturate and result in clipping internally.

However, there is a Current Register (0x04h), which can be calibrated. We can adjust the LSB on that by writing in the Calibration Register (0x05h). I chose to configure an LSB of 10 uA. The calibration value for that is calculated as follows:

CAL = 0.00512 / (Current_LSB x R_Shunt) = 0.00512 / (10 uA x 0.1 Ohm) = 5120

Using the LSB of 10 uA has the downside that the maximum current the Current Register can show is

Current_LSB = Maximum Current/2^15 --> Maximum Current = 0.327 A

To be honest, it doesnt really make the difference if it is 10 uA or 25 uA, I just left it at 10 uA at the moment.

Precision and Noise

TI says in the datasheet, that for noisy circuits a filter on the input pins can be used. Since I have bought an already finished layout and didn´t want to make my life too hard, I decided to configure an averaging of 128 with a conversion time of 1.1 ms (continuous) at least to filter out a little bit of noise. The configuration register address is 0x00h. You can see that in my setup() function.

Reading and Calculation

To read out the raw value/s, we take a look at the registers 0x01(Shunt Voltage) and 0x04 (Current), starting off with the first one.

We read out the raw value, multiply it to the LSB of 2.5 uV and divide it by 0.1 Ohm to get the current.

Current_mA(0x01h) = raw * 2.5 mV / 0.1 Ohm

Since the current register already stores in amps, we just read the raw value from the 0x04(Current Register) and multiply it to the value we calibrated. In our case it was 10uA or 0.01 mA

Current_mA(0x04h) = raw * 0.01 mA

Lastly I have decided to update the LCD every 500 ms and the accumulated charge every 10 seconds. Thats generally it.

Testing​

Leave a comment

But be careful and mindful. Only I can delete the messages!