// simple project using Arduino UNO and 128x64 SSD1306 IIC OLED Display to show battery charging indicator

// created by upir, 2024
// youtube channel: https://www.youtube.com/upir_upir

// YOUTUBE VIDEO: https://youtu.be/icD-hER8YQ8
// SOURCE files: https://github.com/upiir/esp32s3_tiny_oled

// Links from the video:
// Do you like this video? You can buy me a coffee ☕: https://www.buymeacoffee.com/upir
// ESP32S3 OLED board: https://s.click.aliexpress.com/e/_DBqaco3
// YouTube video describing the settings: https://www.youtube.com/watch?v=6KOtqACpGTY
// GitHub with default sketch: https://github.com/01Space/ESP32-S3-0.42OLED
// 72x40 SSD1306 OLED 0.42" Display (alone): https://s.click.aliexpress.com/e/_Ddq0EwJ

// Related videos with Arduino UNO and OLED screen:
// Arduino + tiny OLED: https://youtu.be/caHcaUoQ2kg
// Arduino + OLED displays: https://www.youtube.com/playlist?list=PLjQRaMdk7pBZ1UV3IL5ol8Qc7R9k-kwXA


#define SDA_PIN 41 // SDA pin, I2C serial data
#define SCL_PIN 40 // SCL pin, I2C serial clock

#include <Arduino.h> 
#include <U8g2lib.h> // u8g2 library used for drawing on the OLED display
#include <Wire.h> // library requires for IIC communication

// display initialization
U8G2_SSD1306_72X40_ER_F_HW_I2C u8g2(U8G2_R0, /* reset=*/ U8X8_PIN_NONE , /*clock*/ SCL_PIN, /*data*/ SDA_PIN);//  [full framebuffer, size = 360 bytes]


// image below is generated using the image2cpp website


// 'battery_outline', 70x40px
const unsigned char epd_bitmap_battery_outline [] PROGMEM = {
	0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 
	0xff, 0x03, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x03, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 
	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x3e, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x03, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 
	0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x06, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x07, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x03, 0xfc, 
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x01
};


// x position, y position and size of metaballs
int metaball_xpos[3];
int metaball_ypos[3];
int metaball_size[3];
float pixel_value; // calculated pixel value

float time_anim = 0.0; // time variable controls all the movements


void setup(void) {
  u8g2.begin(); // start the u8g2 library
}

void loop(void) {

  time_anim = time_anim + 0.1; // increase the time animation

  // set a new position for metaballs
  for (int i = 0; i < 3; i++) {
    metaball_xpos[i] = sin(time_anim * (i + 1.4)) * 26 + 36;
    metaball_ypos[i] = cos(time_anim * (i + 2.6) + 0.9) * 10 + 20;
    metaball_size[i] = 30; // constant size
  }

  u8g2.clearBuffer();	// clear the internal memory
	u8g2.setBitmapMode(1); // draw transparent images

  u8g2.drawXBMP(0, 0, 70, 40, epd_bitmap_battery_outline); // draw the image of the outline of the battery icon
  u8g2.setDrawColor(1); // set color to white color

  // calculate pixel value for metaballs
  for (int xpos = 3; xpos < 64; xpos++) {
    for (int ypos = 3; ypos < 37; ypos++) {

      pixel_value = metaball_size[0] / sqrt( pow(xpos - metaball_xpos[0], 2) + pow(ypos - metaball_ypos[0], 2) ) + 
                    metaball_size[1] / sqrt( pow(xpos - metaball_xpos[1], 2) + pow(ypos - metaball_ypos[1], 2) ) + 
                    metaball_size[2] / sqrt( pow(xpos - metaball_xpos[2], 2) + pow(ypos - metaball_ypos[2], 2) );

      // draw white pixel based on some threshold value
      if (pixel_value > 6) {
        u8g2.drawPixel(xpos, ypos);
      }
    } 
  }

  u8g2.sendBuffer();					// transfer internal memory to the display
}