Project showcase
Sensing Current with an INA219 and Storing Data on SD Card

Sensing Current with an INA219 and Storing Data on SD Card © GPL3+

This is my version of a data logger using a INA219.

  • 2,989 views
  • 10 comments
  • 9 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)
09507 01
Soldering iron (generic)
CNC shapeoko

About this project

The Goal

The majority of my electronic projects are connected on an external power source but, I intend to make some of them work on battery or solar panel.

To achieve that goal, I need to estimate the power consumption, in mAh, over a certain period of time.

Let's take some exemples:

  • Let's say that my circuit consume 50mA for 1 hour, then I can say that a 50mAh battery will support the circuit for 1 hour.
  • Now, let's presume that my circuit triggers certain external devices and that the current consumption varies over time. For example: In the first half of the hour, my device consumes 50mA but for the second half, it consumes 100mA, then, after one hour, I could calculate that the circuit will need a 75mAh battery to support the load for one hour.
  • But what if my circuit consumes 50mA for 15 minutes then 55mA for 6 minutes then 150 for 12 minutes and so on... the only way to correctly estimate the mAh needed for a 1 hour period is by capturing the power consumption every second or more precisely, many times per second!

This is exactly the goal of that project: Continuously estimating the current consumption, overtime, and store all the data on an micro SD card so I can loadthe data into Excel and, not only will I be able to see the final power consumption but also a graph showing the variation of the power over the hole period of time.

Video explanation

I posted a video on YouTube so if you want to see the complete build, please visit the video.

Technical details

My version of a data logger based on the one made by "GreatScott"; This guy is hot and the video explanations that he makes are very detailed so, I decided to let you visit his video for the technical part and for the base code and use my video for the build and the modification that I made on the base code.

At the heart of this project resides the INA219

It is a I2C breakout board that is really easy to use but it has a small problem: the current returned, when there are no load, is in the negative range. So to hide this problem, I decided to add a condition to return 0 when this condition occurs

power_mw = (loadvoltage * current_mA);
if (power_mw < 0)
{
current_mA = 0;
loadvoltage = 0;
power_mw = 0;
}

I also added a push button to start & stop the recording so I control what to monitor. For that purpose, I added a function tu Debounce the pushbutton

I tried to make an "Easy to use" module and to make it more friendly so I prepared 2 ways to "Input the voltage".

One of the method is via a DC Power Supply Jack Socket Female Connector

The other method is via a Stereo Speaker Plate Terminal Strip Push Connector Block

Here are some 3D Rendered images of the inside


As shown in the next image, I split the front section in 2 sections: the left side are the inputs and the right side are the outputs section with the ground clip.

Once the data are imported into Excel, a deeper analysis can be made as you can see in the next image

My goal is reached now: I can monitor any electronic project and analyze the captured data to better understand the current consumption overtime and, using this data, I can start thinking about creating outdoors IoT devices powered by solar panels like this IoT Weather Station that I made: I will certainly revisit this project to estimate the current and, eventually, convert it to use solar panels.

Thanks

Code

My version of the code for that projectArduino
The main part of that code comes from GreatScott but I modified it to suit my needs.
//ref: https://www.youtube.com/watch?v=lrugreN2K4w
#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_SSD1306.h>
// inclure aussi la librarie Adafruit_BusIO-master meme si il n'y a pas d'include ici.
#include <SPI.h>
#include "SdFat.h"
SdFat SD;

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);
Adafruit_INA219 ina219;

unsigned long previousMillis = 0;
unsigned long dataAquisitionInterval_ms = 250;
const int chipSelect = 10;
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float power_mw = 0;
float current_mAh = 0;
float power_mWh = 0;
float loadvoltage = 0;
File DataLogFile;
File CurFile;

int boot = 1;

int days;     //number of days
int hours;   //the remainder from days division (in milliseconds) divided by hours, this gives the full hours
int minutes; //and so on...
int seconds;


// Moving Average VAR1
const int VAR1_numVAR1_readings = 5;
float VAR1_readings[VAR1_numVAR1_readings];      // the readings from the analog input
int VAR1_readIndex = 0;              // the index of the current reading
float VAR1_total = 0;                  // the running total

// DEBOUNCE B1
const int B1_buttonPin = 3;    // the number of the pushbutton pin
int B1_State = LOW;         // the current state of the output pin
int B1_ActualState;             // the current reading from the input pin
int B1_LastState = LOW;   // the previous reading from the input pin
unsigned long B1_lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long B1_debounceDelay = 50;    // the debounce time; increase if the output flickers
const int ledPin = 4;      // the number of the LED pin


void setup()
{
  SD.begin(chipSelect);
  display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
  ina219.begin();

  // DEBOUNCE B1
  pinMode(B1_buttonPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);

}

void loop()
{

  if (boot == 1)
  {
    CurFile = SD.open("header.txt", FILE_WRITE);
    if (CurFile)
    {
      CurFile.println("day,hrs,voltage,current_mA,power_mWh,current_mAh");
      CurFile.close();
      boot = 0;
    }
    //    B1_State = LOW;
    digitalWrite(ledPin, LOW);
  }

  // DEBOUNCE B1
  B1_StartStop_SD();
  // set the LED:
  digitalWrite(ledPin, B1_State);

  unsigned long currentMillis = millis();

  //Calculation for the Up Time
  long day = 86400000; // 86400000 milliseconds in a day
  long hour = 3600000; // 3600000 milliseconds in an hour
  long minute = 60000; // 60000 milliseconds in a minute
  long second =  1000; // 1000 milliseconds in a second

  days = currentMillis / day ;                                //number of days
  hours = (currentMillis % day) / hour;                       //the remainder from days division (in milliseconds) divided by hours, this gives the full hours
  minutes = ((currentMillis % day) % hour) / minute ;         //and so on...
  seconds = (((currentMillis % day) % hour) % minute) / second;
  //Calculation for the Up Time

  if (currentMillis - previousMillis >= dataAquisitionInterval_ms)
  {
    previousMillis = currentMillis;
    ina219values();

    //>>> writing to the card
    if (B1_State == HIGH)
    {
      DataLogFile = SD.open("DataLog.txt", FILE_WRITE);
      if (DataLogFile)
      {
        DataLogFile.print(days);
        DataLogFile.print(",");

        if (hours < 10) DataLogFile.print('0');
        DataLogFile.print(hours);
        DataLogFile.print(":");
        if (minutes < 10) DataLogFile.print('0');
        DataLogFile.print(minutes);
        DataLogFile.print(":");
        if (seconds < 10) DataLogFile.print('0');
        DataLogFile.print(seconds);
        DataLogFile.print(",");

        DataLogFile.print(loadvoltage);
        DataLogFile.print(",");
        DataLogFile.print(current_mA);
        DataLogFile.print(",");
        DataLogFile.print(power_mWh);
        DataLogFile.print(",");
        DataLogFile.println(current_mAh);
        DataLogFile.close();
      }  //>>> writing to the card
    }

    displaydata();
  }

}

void displaydata()
{
  display.clearDisplay();
  display.setTextColor(WHITE);
  display.setTextSize(1);

  //first line
  display.setCursor(0, 0);

  display.print(days);
  display.print("d ");

  if (hours < 10) display.print('0');
  display.print(hours);
  display.print(":");
  if (minutes < 10) display.print('0');
  display.print(minutes);
  display.print(":");
  if (seconds < 10) display.print('0');
  display.print(seconds);
  display.print(" -> ");

  display.print(loadvoltage);
  display.print("v ");


  //second ling
  display.setCursor(0, 10);
  display.print(power_mw);
  display.print("mW ");
  display.print(abs(current_mA));
  display.print("mA");

  //third ling
  display.setCursor(0, 20);
  display.print(power_mWh);
  display.print("mWh ");
  display.print(current_mAh);
  display.print("mAh");
  display.display();
}

void ina219values()
{
  shuntvoltage = (ina219.getShuntVoltage_mV());
  busvoltage = (ina219.getBusVoltage_V());
  current_mA = MovingAVG_VAR1(ina219.getCurrent_mA());
  loadvoltage = (busvoltage + (shuntvoltage / 1000));

  // to avoid inconsistant readings, set to zero when values are below zero
  power_mw = (loadvoltage * current_mA);
  if (power_mw < 0)
  {
    current_mA = 0;
    loadvoltage = 0;
    power_mw = 0;
  }

  power_mWh = power_mWh + (power_mw       / (60 * 60) / (1000 / dataAquisitionInterval_ms));
  current_mAh = current_mAh + (current_mA / (60 * 60) / (1000 / dataAquisitionInterval_ms));

}




float MovingAVG_VAR1(float ValueToAVG)
{
  float VAR1_average = 0;                // the average
  // subtract the last reading:
  VAR1_total = VAR1_total - VAR1_readings[VAR1_readIndex];
  // read from the sensor:
  VAR1_readings[VAR1_readIndex] = ValueToAVG;
  // add the reading to the VAR1_total:
  VAR1_total = VAR1_total + VAR1_readings[VAR1_readIndex];
  // advance to the next position in the array:
  VAR1_readIndex = VAR1_readIndex + 1;
  // if we're at the end of the array...
  if (VAR1_readIndex >= VAR1_numVAR1_readings)
    VAR1_readIndex = 0;
  // calculate the VAR1_average:
  VAR1_average = VAR1_total / VAR1_numVAR1_readings;
  // send it to the computer as ASCII digits
  return VAR1_average;
}


void B1_StartStop_SD()
{ // DEBOUNCE B1
  int reading = digitalRead(B1_buttonPin);
  if (reading != B1_LastState)
    B1_lastDebounceTime = millis();
  if ((millis() - B1_lastDebounceTime) > B1_debounceDelay)
  {
    if (reading != B1_ActualState)
    {
      B1_ActualState = reading;
      if (B1_ActualState == HIGH)
        B1_State = !B1_State;
    }
  }
  B1_LastState = reading;
}

Custom parts and enclosures

The Casing
The top

Comments

Similar projects you might like

SD Card Temperature Data Logger

Project tutorial by 3 developers

  • 12,601 views
  • 0 comments
  • 10 respects

Motion Sensing Under Bed Lights

Project tutorial by T3ch Flicks

  • 7,615 views
  • 6 comments
  • 35 respects

Reading Text Files From an SD Card (Arduino)

Project tutorial by millerman4487

  • 1,241 views
  • 0 comments
  • 3 respects

Interactive LED Table for 50€

Project showcase by Antoine Rochebois

  • 43,544 views
  • 12 comments
  • 134 respects

Shy Mask That Shuts Up When It Sees People

Project showcase by ChenTheDesignMaker

  • 4,603 views
  • 1 comment
  • 18 respects
Add projectSign up / Login