/*
This project is about charging system, due to i was not able to get
TP4056 in the Wokwi database, I have used slide switches the first one
is for start charging and sensond is for battery full indications.

You have all component you make an actual project and just use this
source code to program your ESP32.

Major componens list:
1- ESP32 WROOM 32 (Dev Board)
2- SSD1306 OLED DISPLAY
3- TP4056 CHARGING MODULE WITH BATTERY PROTECTION CIRCUIT
4- 10K RESISTORS = 3
5- 4.7L RESISTOR = 1

*/

#include <Arduino.h>
#include <Adafruit_SSD1306.h>

// SSD1306 OLED Display
#define OLED_RESET -1
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define SSD1306_I2C_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// PIN CONFIGURATIONS
#define CHRG_PIN 5  // Connect to TP4056 CHRG pin
#define STDBY_PIN 18 // Connect to TP4056 STDBY pin
#define BATT_LEVEL 35 // Connect to B+ via divider resistors

// Position variables for battery icon
const int batteryX = 44; // X position
const int batteryY = 32; // Y position

// FUNCTION PROTOTYPES
void displayBatteryStatus();
void drawBattery(int level, bool charging);

void setup() {
  Serial.begin(115200);
  pinMode(CHRG_PIN, INPUT);
  pinMode(STDBY_PIN, INPUT);
  pinMode(BATT_LEVEL, INPUT);

  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  } else {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(10, 25);
    display.print("LOADING SYSTEM");
    display.display();

    // Display animation dots
    for (int i = 0; i < 3; i++) {
      delay(600);  // 0.6 second delay
      display.print(".");
      display.display();
    }
  
    display.clearDisplay();
  }
}

void loop() {
  displayBatteryStatus();
  delay(500);
}

void displayBatteryStatus() {
  int chrg_status = digitalRead(CHRG_PIN);
  int stdby_status = digitalRead(STDBY_PIN);

  // Read battery level from ADC pin
  int adcValue = analogRead(BATT_LEVEL);

  // Convert ADC value to battery voltage
  float batteryVoltage = adcValue * (3.3 / 4095.0); // Adjust according to the voltage divider ratio
  batteryVoltage = batteryVoltage * (4700.0 + 10000.0) / 4700.0;  // Adjust for the voltage divider ratio (4.7k from A0 to GND, 10k from battery to A0)

  // Constrain battery voltage to realistic values
  batteryVoltage = constrain(batteryVoltage, 3.0, 4.2);

  // Map battery voltage to percentage level
  int batteryLevel = map(batteryVoltage * 100, 300, 420, 0, 100); // Adjust the mapping based on your battery voltage range (3.0V to 4.2V)

  // Display battery voltage, fine-tune voltage, and raw ADC value
  display.clearDisplay();
  display.setTextSize(1);
  display.setTextColor(SSD1306_WHITE);
  display.setCursor(0, 0);

  // Display raw ADC value for debugging
  display.print("RAW ADC: ");
  display.println(adcValue);
  
  // Display battery voltage
  display.setCursor(0, 10);
  display.print("VOLTAGE: ");
  display.print(batteryVoltage, 2); // Display voltage with two decimal places
  display.println("V");

  // Draw battery status and level
  display.setCursor(0, 20);
  if (chrg_status == LOW) {
    display.println("CHARGING");
    drawBattery(batteryLevel, true);
  } else if (stdby_status == LOW) {
    display.println("BATTERY FULL");
    drawBattery(batteryLevel, false);
  } else {
    display.println("NOT IN CHARGING");
    drawBattery(batteryLevel, false);
  }

  display.display();
}

void drawBattery(int level, bool charging) {
  // Draw battery outline
  display.drawRect(batteryX, batteryY, 40, 20, SSD1306_WHITE);
  display.drawRect(batteryX + 40, batteryY + 5, 5, 10, SSD1306_WHITE);
  
  // Draw battery level
  int fillWidth = map(level, 0, 100, 0, 38);
  display.fillRect(batteryX + 1, batteryY + 1, fillWidth, 18, SSD1306_WHITE);

  if (charging) {
    // Calculate charging animation fill area
    static unsigned long lastUpdateTime = 0;
    static int chargingLevel = 0;
    int animationSpeed = 20; // Milliseconds per frame

    // Update charging level periodically
    if (millis() - lastUpdateTime > animationSpeed) {
      chargingLevel++; // Increment charging animation level
      if (chargingLevel > fillWidth + 18) { // Reset animation to start from the left
        chargingLevel = -18;
      }
      lastUpdateTime = millis();
    }

    // Calculate the start and end positions for animation fill
    int startChargeX = batteryX + 1 + chargingLevel;
    int startChargeY = batteryY + 1;
    int endChargeX = startChargeX + 18;
    int endChargeY = startChargeY + 18;

    // Clip animation fill to stay within battery level bounds
    if (startChargeX < batteryX + 1) {
      startChargeX = batteryX + 1;
    }
    if (endChargeX > batteryX + 1 + fillWidth) {
      endChargeX = batteryX + 1 + fillWidth;
    }

    // Draw charging animation fill
    if (chargingLevel >= 0 && chargingLevel <= fillWidth) {
      display.fillRect(startChargeX, startChargeY, endChargeX - startChargeX, endChargeY - startChargeY, SSD1306_WHITE);
    }
  }
}