#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_INA219.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ================= PINS =================
#define SDA_PIN 4
#define SCL_PIN 5
// ================= OLED =================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_ADDRESS 0x3C
Adafruit_SSD1306 display(
SCREEN_WIDTH,
SCREEN_HEIGHT,
&Wire,
OLED_RESET
);
// ================= INA219 =================
Adafruit_INA219 ina219;
// ================= SETTINGS =================
// Charging detection threshold
const float CHARGE_THRESHOLD = 30.0;
// No battery detection threshold
const float NO_BATTERY_THRESHOLD = 1.0;
// Current direction
const float CURRENT_SIGN = 1.0;
// Measurement intervals
const unsigned long MEASURE_INTERVAL = 500;
const unsigned long DISPLAY_INTERVAL = 500;
// ================= VARIABLES =================
float batteryVoltage = 0.0;
float chargeCurrent = 0.0;
float addedmAh = 0.0;
int batteryPercent = 0;
bool charging = false;
bool previousCharging = false;
bool voltageValid = false;
bool batteryPresent = false;
unsigned long lastMeasurement = 0;
unsigned long lastDisplay = 0;
// ================= BATTERY PERCENT =================
int calculatePercent(float voltage) {
if (voltage <= 3.0) return 0;
if (voltage >= 4.2) return 100;
float percent =
(voltage - 3.0) / 1.2 * 100.0;
return constrain((int)percent, 0, 100);
}
// ================= READ INA219 =================
void updateMeasurements() {
unsigned long now = millis();
float elapsedHours =
(now - lastMeasurement) / 3600000.0;
lastMeasurement = now;
// Read charging current
chargeCurrent =
ina219.getCurrent_mA() * CURRENT_SIGN;
if (chargeCurrent < 0) {
chargeCurrent = 0;
}
// Detect charging
charging = chargeCurrent > CHARGE_THRESHOLD;
if (charging) {
batteryPresent = true;
// Reset capacity at start of a new charge cycle
if (!previousCharging) {
addedmAh = 0.0;
}
// Do not read voltage during charging
voltageValid = false;
// Calculate charged capacity
addedmAh += chargeCurrent * elapsedHours;
} else {
// Read voltage only when not charging
batteryVoltage = ina219.getBusVoltage_V();
voltageValid = true;
// Detect battery
if (batteryVoltage < NO_BATTERY_THRESHOLD) {
batteryPresent = false;
batteryPercent = 0;
} else {
batteryPresent = true;
batteryPercent =
calculatePercent(batteryVoltage);
}
}
previousCharging = charging;
}
// ================= OLED DISPLAY =================
void updateOLED() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
// Header
display.setCursor(0, 0);
display.println("BATTERY CHARGER");
display.drawLine(
0, 12,
127, 12,
SSD1306_WHITE
);
// ========== NO BATTERY ==========
if (!charging && !batteryPresent) {
display.setCursor(0, 22);
display.println("Status: NO BATTERY");
display.setCursor(0, 42);
display.println("Insert Battery");
}
// ========== CHARGING ==========
else if (charging) {
display.setCursor(0, 18);
display.println("Status: CHARGING");
display.setCursor(0, 32);
display.print("Current: ");
display.print(chargeCurrent, 0);
display.println(" mA");
display.setCursor(0, 46);
display.print("Added: ");
display.print(addedmAh, 1);
display.println(" mAh");
}
// ========== BATTERY IDLE ==========
else {
display.setCursor(0, 17);
display.println("Status: IDLE");
display.setCursor(0, 29);
display.print("Voltage: ");
display.print(batteryVoltage, 2);
display.println(" V");
display.setCursor(0, 41);
display.print("Battery: ");
display.print(batteryPercent);
display.println(" %");
display.setCursor(0, 53);
display.print("Added: ");
display.print(addedmAh, 1);
display.println(" mAh");
}
display.display();
}
// ================= SETUP =================
void setup() {
Serial.begin(115200);
Wire.begin(SDA_PIN, SCL_PIN);
// Initialize OLED
if (!display.begin(
SSD1306_SWITCHCAPVCC,
OLED_ADDRESS
)) {
while (true) {
delay(100);
}
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(15, 20);
display.println("STARTING...");
display.display();
// Initialize INA219
if (!ina219.begin()) {
display.clearDisplay();
display.setCursor(0, 20);
display.println("INA219 NOT FOUND!");
display.display();
while (true) {
delay(100);
}
}
ina219.setCalibration_32V_2A();
lastMeasurement = millis();
display.clearDisplay();
display.setCursor(20, 20);
display.println("SYSTEM READY");
display.display();
delay(1000);
}
// ================= LOOP =================
void loop() {
unsigned long now = millis();
if (now - lastMeasurement >= MEASURE_INTERVAL) {
updateMeasurements();
}
if (now - lastDisplay >= DISPLAY_INTERVAL) {
lastDisplay = now;
updateOLED();
}
}