// Software for small hand held counter using ESP32C3 Super mini and SSD 1306 OLED Display
// By Simon N. Carter
// July 30, 2026

#include <Wire.h>             // Include these libraries
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include<EEPROM.h>

#define EEPROM_SIZE 512       // Set EEPROM Size

// Define screen dimensions (128x64)
#define SCREEN_WIDTH 128 
#define SCREEN_HEIGHT 64 

// Create display object sharing the Arduino hardware I2C pins
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

long int i = 0;               // Variable to count number of button presses
long int n;                   // Variable for counting
int v;                        // Variable for counting number of battery measurments
int Count_Button = 1;         // Set Count button as pin 1
int VBat = 2;                 // Set Battery voltage sensing to pin 2
int SW_State = 0;             // Variable indicating if Switch state is high or low
int StartTime = 0;            // Variable for setting start time for time elapsed
int elapsed = 0;              // Variable for count of time elapsed in milliseconds
int RFlag = 0;                // EEPROM Record Flag; if 0, record, if 1 do not record
float Batt_Volts = 0;         // Variable for measuring battery voltage

void setup() {

pinMode(Count_Button, INPUT); // Set Count Button pin as input
pinMode(VBat, INPUT);         // Set Battery voltage sense pin as input 

Serial.begin(9600);           // Begin serial connection to Serial Monitor
Wire.begin(8, 9);             // Set ESP32C3 pins to SDA and SCL for I2C communication

// Initialize EEPROM simulation in flash memory
  if (!EEPROM.begin(EEPROM_SIZE)) {
    //Serial.println("Failed to initialize EEPROM");
    return;
  }

EEPROM.get(0, n);              // Get the last count recorded in EEPROM Memory
i = n;                         // Set Count to last know number from memory
//Serial.println(n);
  // Initialize display with I2C address 0x3C (common for these modules)
  
  if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { //Set up SSD1306 I2C connection and address
    for(;;); // Stop execution if display connection fails
  }
  display.clearDisplay();      // Clear the internal buffer
  display.setTextSize(2);      // Set text size (1 is small, 2 is medium)
  display.setTextColor(SSD1306_WHITE); // Set text color to white
  display.setCursor(5, 15);    // Position cursor roughly in the center
  display.print("Gratitude");  // Write text to buffer
  display.setCursor(5, 35);    // Position cursor roughly in the center
  display.print(" Counter");   // Write text to buffer
  display.display();           // Display text
  delay(1500);
  
  for (v = 0; v < 10; v++) {   // Measure the battery voltage 10 times
  Batt_Volts += analogReadMilliVolts(VBat);
  delay(10);
  }
  
  Batt_Volts = Batt_Volts/10;  // Find average battery voltage
  Batt_Volts = Batt_Volts*4.1/2400; // Calculate actual battery voltage in volts (Note this accounts for resistor divider)
  delay(100);
  
  display.clearDisplay();      // Clear the internal buffer
  display.setCursor(5, 5);     // Position cursor roughly in the center
  display.print("Batt Level"); // Write text to buffer
  display.display();           // Push buffer to physical hardware screen
  display.setCursor(10, 35);   // Position cursor roughly in the center
  display.print(Batt_Volts);   // Write text to buffer
  display.display();           // Push buffer to physical hardware screen
  display.setCursor(65, 35);   // Position cursor roughly in the center
  display.print("Volts");      // Write text to buffer
  display.display();           // Push buffer to physical hardware screen
  delay(3500);                 // Pause to display
  
  display.clearDisplay();      // Clear the internal buffer
  display.setCursor(55, 5);    // Position cursor roughly in the center
  display.print(i);            // Write text to buffer
  display.setCursor(15, 35);   // Position cursor roughly in the center
  display.print("Thank you");  // Write text to buffer
  display.display();           // Push buffer to physical hardware screen


}

void MemRec() {                // Sub to record count in EEPROM memory
SW_State = digitalRead(Count_Button); // Read the Count Button current state
  //delay(10);
 
if(SW_State == HIGH) {         //If button is pressed...
//delay(10);
StartTime = millis();          //Then note the current UProcessor time


while (SW_State == HIGH) {              // While the button is pressed
SW_State = digitalRead(Count_Button);   // Keep reading the button state and wait here
elapsed = millis() - StartTime;         // Calculate time elapsed since button pressed
if (elapsed > 3000 && elapsed <7000) {  // If elapsed time greater than 3 seconds and less than 7 sec
  display.setTextSize(2);               // Set text size (1 is small, 2 is medium)
  display.setCursor(110, 0);            // Position cursor roughly in the center
  display.print("+");                   // Write text to buffer - dislay a "+"
  display.display();                    // Push buffer to physical hardware screen
}

if (elapsed > 5000 && elapsed <7000) {  // If elapsed time greater than 5 seconds and less than 7 sec
  display.setTextSize(2);               // Set text size (1 is small, 2 is medium)
  display.setCursor(110, 12);           // Position cursor roughly in the center
  display.print("+");                   // Write text to buffer - dislay a second "+"
  display.display();                    // Push buffer to physical hardware screen
}

if (elapsed > 6000 && elapsed <7000) {  // If elapsed time greater than 6 seconds and less than 7 sec
  display.setTextSize(2);               // Set text size (1 is small, 2 is medium)
  display.setCursor(110, 24);           // Position cursor roughly in the center
  display.print("+");                   // Write text to buffer - dislay a third "+"
  display.display();                    // Push buffer to physical hardware screen
}

if (elapsed > 7000) {                   // If elapsed time greater than 7 seconds 
  display.clearDisplay();               // Clear the internal buffer
  display.setTextSize(3);               // Set text size (1 is small, 2 is medium)
  display.setCursor(55, 15);            // Position cursor roughly in the center
  display.print("R");                   // Write text to buffer - display "R" indicating reset
  display.display();                    // Push buffer to physical hardware screen
}
//Serial.print("Time: ");Serial.println(elapsed);
}                                       // WHILE LOOP END Here

if (millis() - StartTime < 3000){       //If the button was pressed for less than 3 seconds do nothing
__asm__ __volatile__ ("nop");

}

if (elapsed >= 3000 && elapsed <= 7000) { //If the button was pressed for more than 3 seconds

//Serial.println(i);

EEPROM.put(0, i);                        // Put the current count in to EEPROM memory location 0

if (EEPROM.commit()) {                   // Saves all buffered changes from your microcontroller's RAM directly into its permanent flash memory 
    //ln("Integer successfully saved to flash!");
  } else {
    //Serial.println("EEPROM commit failed");
  }

  display.clearDisplay();               // Clear the internal buffer
  display.setTextSize(2);               // Set text size (1 is small, 2 is medium)
  display.setCursor(55, 10);            // Position cursor roughly in the center
  display.print(i);                     // Write text to buffer - show count
  display.setCursor(5, 40);             // Position cursor roughly in the center
  display.print("Count Rec");           // Write text to buffer
  display.display();                    // Push buffer to physical hardware screen
  
}

else if (elapsed >= 7000){              //If the button was pressed for more than 7 seconds
//Serial.println(i);
EEPROM.put(0, 0);                       //Reset EEPROM memory location 0 to zero
i = 0;                                  //Reset count to zero

 display.clearDisplay();                // Clear the internal buffer
  display.setTextSize(3);               // Set text size (1 is small, 2 is medium)
  display.setCursor(55, 10);            // Position cursor roughly in the center
  display.print(i);                     // Write text to buffer - display current count
  display.setCursor(15, 40);            // Position cursor roughly in the center
  display.print("Reset");               // Write text to buffer
  display.display();                    // Push buffer to physical hardware screen
  delay(100);
  
}
 }
  }

void Batt_Check(){                      // Sub to check battery voltage

for (v = 0; v < 10; v++) {              // Read battery voltage 10 times
  Batt_Volts += analogReadMilliVolts(VBat);
  //delay(10);
  }
  
  Batt_Volts = Batt_Volts/10;           // Determine average battery voltage
  Batt_Volts = Batt_Volts*4.1/2400;     // Calculate actual battery voltage in volts (Note this accounts for resistor divider)
  //delay(10);

if (Batt_Volts > 3.5){                  // When battery voltage is above 3.5V set record flag to 0, i.e normal battery range
RFlag = 0;
}

if (Batt_Volts < 3.2 && RFlag == 0){   // When battery voltage falls below 20% and record flag = 0, go ahead and record into EEPROM
if(RFlag == 0) {                       // Only write to EEPROM if flag is zero
EEPROM.put(0, i);                      // Write count to memory
}

if (EEPROM.commit()) {                 // Saves all buffered changes from your microcontroller's RAM directly into its permanent flash memory 
    //Serial.println("Integer successfully saved to flash!");
  } else {
    //Serial.println("EEPROM commit failed");
  }
  
  display.clearDisplay();             // Clear the internal buffer
  display.setTextSize(2);             // Set text size (1 is small, 2 is medium)
  display.setCursor(20, 10);          // Position cursor roughly in the center
  display.print("Battery");           // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  display.setCursor(45, 40);          // Position cursor roughly in the center
  display.print("LOW");               // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  delay(2000);                        // Pause to display

if(RFlag == 0) {                      // If cleared to record...

  display.clearDisplay();             // Clear the internal buffer
  display.setCursor(5, 40);           // Position cursor roughly in the center
  display.print("Count Rec");         // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  delay(1000);                        // Pause to display
  display.clearDisplay();             // Clear the internal buffer
  display.setCursor(10, 20);          // Position cursor roughly in the center
  display.print("Recharge");          // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  delay(2000);                        // Pause to display
  display.clearDisplay();             // Clear the internal buffer
  display.setTextSize(2);             // Set text size (1 is small, 2 is medium)
  display.setCursor(5, 10);           // Position cursor roughly in the center
  display.print("Check Batt");        // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  display.setCursor(5, 30);           // Position cursor roughly in the center
  display.print("or Press");          // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  display.setCursor(5, 50);           // Position cursor roughly in the center
  display.print("to Cont...");        // Write text to buffer
  display.display();                  // Push buffer to physical hardware screen
  
RFlag = 1;                            // Set the Flag, No longer record to memory on low battery, you have already done it once

}
 }
  }

void loop() {
 
 Batt_Check();                                 // Go check battery voltage

 SW_State = digitalRead(Count_Button);         // Check push button state
 delay(10);
  
  if(SW_State == HIGH) {                       // If button pressed...
    i++;                                       // Increment count

  display.clearDisplay();                      // Clear the internal buffer
  display.setTextSize(3);                      // Set text size (1 is small, 2 is medium)
  
  if(i<10){display.setCursor(55, 10);}         // Keep moving the cursor location as count increasesto remain centred in the screen
  if(i>9 && i<100){display.setCursor(45, 10);}
  if(i>99 && i<1000){display.setCursor(35, 10);}
  if(i>999 && i<10000){display.setCursor(25, 10);}
  if(i>9999 && i<100000){display.setCursor(15, 10);}
  if(i>99999 && i<1000000){display.setCursor(5, 10);}
  if(i>999999){display.setCursor(55, 10);      // If you reach 1 million set counter to zero (You probably never will :))
  i=0;}
  
  display.clearDisplay();                      // Clear the internal buffer
  display.print(i);                            // Write text to buffer - display current count
  display.display();                           // Push buffer to physical hardware screen
  display.setCursor(5, 40);                    // Position cursor roughly in the center
  display.setTextSize(2);                      // Set text size (1 is small, 2 is medium)
  display.print("Thank you!");                 // Write text to buffer
  display.display();                           // Push buffer to physical hardware screen
  delay(10);
  
  MemRec();                                    // Go to Memory Record sub to see if count should be recorded
  }

}
  

