Project 3: Arduino based Data Logger with INA219 and micro SD Card

Lately I am experimenting with solar panels. I was interested in the current, voltage and power I can generate with a panel I bought in China. So I decided to build myself a data logger with some of the components I had laying around.

Most important components are a Arduino Nano and a INA219 current and voltage sensor. As I planed to measure over greater time spans I added a micro SD Card Board with a 4 GB micro SD card.

Hence I do not own a 3D printer I went for a wooden housing.

Require Hardware

1 x Arduino

1 x INA 219 Development Board

1 x LCD Display

1 x Micro SD Card Development Board

1 x 220 Ohm Resistor

1 x Potentiometer

1 x On/Off Switch

1 x Battery Holder for 9V Blocks

1 x Banana Connector red

1 x Banana Connector black

Circuit

Connecting the LCD

Please see Arduino’s Hello World tutorial for the pinning of the LCD: https://www.arduino.cc/en/Tutorial/HelloWorld

Connecting the INA219 Board

There are different boards available. Basically they are all very similar, but the main difference is the bus they support. Mine is I2C (or IIC).

INA219 PINArduino PINOther
VCC5V
GNDGND
SDAA4
SCLA5

I connected VIN+ to the red banana connector and the VIN- to the black banana connector. So I can use banana plugs with different endings for measuring.

Please note: If you also like to measure voltage, you must connect the ground of the device under test with the ground of the test device. In my current setup I only have a wire going out of the box until I habe another banana connector.

Connecting the micro SD Card Board

The micro SD Card Board board uses SPI, but there are also I2C boards available. please note that the SPI pins depend on the Arduino you use. If you are using any other Arduino than a Nano please see the pinning of you version.

SC Card PINArduino PINOther
VCC5V
GNDGND
CSD10
CLKD13
DID11 (MOSI)
DOD12 (MISO)

Please note that both the build in SD library of the Arduino IDE as well as the SdFat library work with a FAT16 or FAT32 file system. The maximum card size supported by the libraries is 4 GB.

Code

I had every problems with the SD card library available in standard installation of the Arduino IDE. As an alternative I used the SdFat library you can download from here: https://github.com/greiman/SdFat

The sketch uses more than 65% of Nano’s memory. Therefore I decided to not use Serial prints. Instead I print the most critical errors to the LCD.

The sketch reads bus voltage in Volt, shunt voltage in mV, load voltage in V, current in mA and power in mW from INA219 and calculates the energy in mAh. The values of bus voltage, current, power and energy are printed to the LCD. All values are printed to a *.csv file on the SD card. The sketch tries to use file „measure01.csv“. If the file exists it increments the number in the file name by one until there is not file with that name.

The file is created in the setup function using the flags O_WRONLY | O_CREAT | O_EXCL for opening the file.

Please not that SdFat library will return an error if you use these flags on an existing file. Therefore I use the flags O_WRITE | O_APPEND in the loop function for re-opening the file.

The sketch measures every 1000 ms. If you need a different sample rate please adopt the value of SAMPLE_INTERVAL_MS.

#include <splash.h>
#include <SPI.h>
#include <Wire.h>
#include "SdFat.h"

#include <Adafruit_INA219.h>
#include <LiquidCrystal.h>

#define LCD_DATA_11 6
#define LCD_DATA_12 5
#define LCD_DATA_13 4
#define LCD_DATA_14 3
#define LCD_RS 7
#define LCD_E 2

//==============================================================================
// Error messages stored in flash.
#define error(msg) sd.errorHalt(F(msg))

LiquidCrystal lcd(LCD_RS, LCD_E, LCD_DATA_14, LCD_DATA_13, LCD_DATA_12, LCD_DATA_11);

Adafruit_INA219 ina219;

const String fileprefix = "meas";
const String filesuffix = ".csv";
String filename;
int filecount = 0;

// File system object.
SdFat sd;

// Log file.
SdFile myFile;

float energy = 0.0;
int loopCount = 0;
float shuntvoltage = 0;
float busvoltage = 0;
float current_mA = 0;
float loadvoltage = 0;
float power_mW = 0;

// Time in micros for next data record.
uint32_t logTime;
uint32_t diff;

const uint32_t SAMPLE_INTERVAL_MS = 1000;

void setup(void)
{
Serial.begin(115200);

lcd.begin(16, 2);
lcd.setCursor(0,0);
lcd.print("Power Meter v0.2");

uint32_t currentFrequency;


// 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()) {
lcd.setCursor(0,0);
lcd.print("ERR: No INA219!!!");
while(1) { delay(1000); } // catch the programm on error
}


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

// setup SD card
pinMode(10, OUTPUT); // chip select
// Initialize at the highest speed supported by the board that is
// not over 50 MHz. Try a lower speed if SPI errors occur.
if (!sd.begin(10, SD_SCK_MHZ(50))) {
lcd.setCursor(0,0);
lcd.print("ERR: init SD card!");
sd.initErrorHalt();
}

if(filecount < 10) {
filename = fileprefix+"0"+filecount+filesuffix;
} else {
filename = fileprefix+filecount+filesuffix;
}
while(sd.exists(filename.c_str())) {
filecount++;

if(filecount < 10) {
filename = fileprefix+"0"+filecount+filesuffix;
} else {
filename = fileprefix+filecount+filesuffix;
}
}

if (!myFile.open(filename.c_str(), O_WRONLY | O_CREAT | O_EXCL)) {
lcd.setCursor(0,0);
lcd.print("ERR: Open file!");
error("file.open");
}

// write column header
myFile.println(F("Bus Voltage [V];Shunt Voltage [mV];Load Voltage;Current [mA];Power [mW]; Energy [mAh]"));
myFile.close();

delay(1000);

// Start on a multiple of the sample interval.
logTime = micros()/(1000UL*SAMPLE_INTERVAL_MS) + 1;
logTime *= 1000UL*SAMPLE_INTERVAL_MS;

lcd.setCursor(0,0);
lcd.print(" ");
}

void loop(void)
{
// Time for next record.
logTime += 1000UL*SAMPLE_INTERVAL_MS;

// Wait for log time.
int32_t diff;
do {
diff = micros() - logTime;
} while (diff < 0);

// Check for data rate too high.
if (diff > 10) {
error("ERR: Missed rec");
}


shuntvoltage = ina219.getShuntVoltage_mV();
busvoltage = ina219.getBusVoltage_V();
current_mA = ina219.getCurrent_mA();
power_mW = ina219.getPower_mW();
loadvoltage = busvoltage + (shuntvoltage / 1000);
energy = energy + (current_mA * SAMPLE_INTERVAL_MS / 3600000);

// 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.print("Energy: "); Serial.print(energy); Serial.println(" mAh");
// Serial.println("");

if (!myFile.open(filename.c_str(), O_WRITE | O_APPEND)) {
lcd.setCursor(0,0);
lcd.print("ERROR: Open file!");
error("file.open 2");
}

myFile.print(busvoltage); myFile.print(";");
myFile.print(shuntvoltage); myFile.print(";");
myFile.print(loadvoltage); myFile.print(";");
myFile.print(current_mA); myFile.print(";");
myFile.print(power_mW); myFile.print(";");
myFile.print(energy); myFile.println("");
myFile.close();

lcd.setCursor(0,0);
lcd.print("V= ");
lcd.setCursor(3,0);
lcd.print(busvoltage);
lcd.setCursor(6,0);
lcd.print("V ");

lcd.setCursor(8,0);
lcd.print("I= ");
lcd.setCursor(10,0);
lcd.print(current_mA);
lcd.setCursor(14,0);
lcd.print("mA");

lcd.setCursor(0,1);
lcd.print("P= ");
lcd.setCursor(2,1);
lcd.print(power_mW);
lcd.setCursor(6,1);
lcd.print("w "); // w means mW

lcd.setCursor(8,1);
lcd.print("E= ");
lcd.setCursor(10,1);
lcd.print(energy);
lcd.setCursor(14,1);
lcd.print("ah"); // ah means mAh
}

Measuring

For measuring the test device must be in row with the circuit to test. Therefore plug the VCC line of the device under test into the red banana connector and reconnect the VCC line with the black banana connector.

Please do not forget to connect the ground of the circuit to test with the ground of the test device if you’d like to measure voltage as well.