Smart 18650 Battery Charger with OLED - Bambu3Design – 3D Printing & Design
Smart 18650 Battery Charger with OLED

Smart 18650 Battery Charger with OLED

#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();
  }
}
Scroll to Top