OLED display flashing

I have a problem with my code that the battery icon and percentage are flashing in sync with prints to the serial monitor. Fan icon and speed bar are working correctly. I added a video also but the black line is only visible in the video and is not a problem . Im not good with programming and i used chatgpt to make individual functions of the code but had to combine them myself because chatgpt got overwhelmed with the length of the code.
Thanks for the help.

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <EEPROM.h>

// OLED display constants
#define OLED_ADDR 0x3C
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

// Buzzer pin definition
#define BUZZER_PIN 3

// Pin for the analog input (battery)
const int batteryPin = A0;

// ** Updated divider ratio based on your real measurements **
const float dividerRatio = 5.88;  // Updated ratio from your test

// Battery voltage range for percentage calculation
const float maxVoltage = 27.4;  // 100% battery voltage
const float minVoltage = 23.0;  // 0% battery voltage

// Hysteresis for charging detection
bool isCharging = false;  // Keep track of charging state

// Voltage thresholds for charging detection
const float startChargingVoltage = 28.0;  // Voltage at which charging is detected
const float stopChargingVoltage = 27.0;   // Voltage below which charging stops

// Animation state variables
int animationPhase = 0;  // For charging animation
unsigned long bootTime;
unsigned long lastUpdateTime = 0;   // Tracks last update time
unsigned long updateInterval = 200; // Initial update interval of 200 ms (0.2 seconds)

const int numAverages = 10;
float voltageReadings[numAverages] = {0};
int currentReadingIndex = 0;

// Bitmap data for new lightning icon (14x14px)
const unsigned char epd_bitmap_lightning_charge_fill_svgrepo_com [] PROGMEM = {
	0x00, 0x40, 0x00, 0xc0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x80, 0x0f, 0x80, 0x1f, 0xf0, 0x3f, 0xe0, 
	0x07, 0xc0, 0x07, 0x80, 0x07, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x08, 0x00
};

// Fan icon bitmaps (25x25px)
const unsigned char epd_bitmap_fan1[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00, 
	0x01, 0x78, 0x40, 0x00, 0x02, 0x78, 0x20, 0x00, 0x04, 0xf1, 0xf0, 0x00, 0x08, 0xf3, 0xf8, 0x00, 
	0x04, 0x73, 0xf0, 0x00, 0x1e, 0x77, 0xc4, 0x00, 0x1f, 0x3f, 0x04, 0x00, 0x1f, 0xfe, 0x00, 0x00, 
	0x0f, 0xfd, 0xf0, 0x00, 0x07, 0xfd, 0xf8, 0x00, 0x00, 0x3e, 0xfc, 0x00, 0x10, 0xfe, 0x7c, 0x00, 
	0x07, 0xf7, 0x18, 0x00, 0x07, 0xe7, 0x08, 0x00, 0x0f, 0xcf, 0x80, 0x00, 0x07, 0x8f, 0x00, 0x00, 
	0x02, 0x0f, 0x20, 0x00, 0x00, 0x85, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00
};

const unsigned char epd_bitmap_fan2[] PROGMEM = {
    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00, 
	0x02, 0x0f, 0x40, 0x00, 0x07, 0x9f, 0xa0, 0x00, 0x0f, 0x9f, 0x10, 0x00, 0x07, 0x9e, 0x08, 0x00, 
	0x17, 0xdc, 0x60, 0x00, 0x03, 0xdd, 0xfc, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xfc, 0x00, 
	0x0e, 0x7e, 0x1c, 0x00, 0x1f, 0xff, 0x04, 0x00, 0x0f, 0xe7, 0xc4, 0x00, 0x1f, 0x9b, 0xe0, 0x00, 
	0x12, 0x39, 0xe0, 0x00, 0x00, 0x79, 0xe8, 0x00, 0x08, 0x78, 0xf0, 0x00, 0x00, 0xf8, 0xe0, 0x00, 
	0x01, 0xf0, 0x40, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	0x00, 0x00, 0x00, 0x00
};

// Define motor and button pins
const int enaPin = 9;
const int in1Pin = 8;
const int in2Pin = 7;
const int buttonUp = 10;
const int buttonDown = 11;

// Fan speed and button debounce settings
int fanSpeed = 0;               // Speed in percent (0-100)
int speedStep = 10;             // Increment step (10%)
const int maxSpeed = 100;       // Max speed (100%)
const int minSpeed = 0;         // Min speed (0%)
const int minPWM = 38;          // Minimum PWM value
const int maxPWM = 117;         // Maximum PWM value

unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
const unsigned long debounceDelay = 50;

bool lastButtonUpState = HIGH;
bool lastButtonDownState = HIGH;

// Animation settings for fan icon
unsigned long lastUpdate = 0;
const int animationInterval = 300; // Animation interval in ms
bool displayFan1 = true;           // Toggle between fan images

void setup() {
    // Initialize motor driver pins
    pinMode(enaPin, OUTPUT);
    pinMode(in1Pin, OUTPUT);
    pinMode(in2Pin, OUTPUT);

    // Set button pins
    pinMode(buttonUp, INPUT_PULLUP);
    pinMode(buttonDown, INPUT_PULLUP);

    // Set buzzer pin
    pinMode(BUZZER_PIN, OUTPUT);

    // Set initial motor direction
    digitalWrite(in1Pin, LOW);
    digitalWrite(in2Pin, HIGH);

    // Load last saved speed from EEPROM
    fanSpeed = EEPROM.read(0);
    fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);

    // Set initial fan speed
    analogWrite(enaPin, map(fanSpeed, minSpeed, maxSpeed, minPWM, maxPWM));

    Serial.begin(9600);
    Serial.print("Initial Fan Speed: ");
    Serial.println(fanSpeed);

      // Record boot time
  bootTime = millis();

    // Initialize display without splash screen
    display.begin(OLED_ADDR, true);
    display.clearDisplay();
    display.display();               // Refresh screen to clear any residuals
}

// Function to read and average analog input voltage
float readBatteryVoltage() {
  int total = 0;
  const int numReadings = 10;
  for (int i = 0; i < numReadings; i++) {
    total += analogRead(batteryPin);
    delay(5);
  }
  int analogValue = total / numReadings;
  float voltage = analogValue * (5.0 / 1023.0) * dividerRatio;
  return voltage;
}

// Function to calculate the average of the last 10 readings
float calculateAverageVoltage() {
  float sum = 0;
  for (int i = 0; i < numAverages; i++) {
    sum += voltageReadings[i];
  }
  return sum / numAverages;
  display.display();
}

// Function to draw battery icon with charging animation if charging
void drawBatteryIcon(float batteryPercentage, bool charging) {
  display.drawRect(0, 0, 39, 16, SH110X_WHITE);  // Draw battery outline
  display.fillRect(40, 5, 3, 6, SH110X_WHITE);   // Battery notch

  int fillWidth;
  if (charging) {
    // Animate the charging fill by cycling fill levels
    fillWidth = (39 - 2) * ((animationPhase % 10) / 10.0);
  } else {
    // Fill based on battery percentage
    fillWidth = (int)((batteryPercentage / 100.0) * (39 - 2));
  }
  display.fillRect(1, 1, fillWidth, 14, SH110X_WHITE);  // Fill battery level
  display.display();
}

// Function to draw the 14x14 lightning icon to the right of the battery percentage, 7 pixels closer
void drawLightningIcon() {
  display.drawBitmap(82, 0, epd_bitmap_lightning_charge_fill_svgrepo_com, 14, 14, SH110X_WHITE);
}

void loop() {
    unsigned long currentMillis = millis();

    // Handle fan icon animation
    if (currentMillis - lastUpdate >= animationInterval) {
        lastUpdate = currentMillis;
        display.clearDisplay();

        // Toggle between fan images
        if (displayFan1) {
            display.drawBitmap(103, 39, epd_bitmap_fan1, 25, 25, 1);
        } else {
            display.drawBitmap(103, 39, epd_bitmap_fan2, 25, 25, 1);
        }
        displayFan1 = !displayFan1;

        // Display fan speed indicator
        displayFanSpeedIndicator();
        display.display();
    }

    // Read button states and adjust fan speed
    bool buttonUpState = digitalRead(buttonUp);
    bool buttonDownState = digitalRead(buttonDown);

    if (buttonUpState == LOW && lastButtonUpState == HIGH && (millis() - lastDebounceTimeUp) > debounceDelay) {
        lastDebounceTimeUp = millis();
        increaseSpeed();
        buzz(); // Buzz on button press
    }
    
    lastButtonUpState = buttonUpState;

    if (buttonDownState == LOW && lastButtonDownState == HIGH && (millis() - lastDebounceTimeDown) > debounceDelay) {
        lastDebounceTimeDown = millis();
        decreaseSpeed();
        buzz(); // Buzz on button press
    }
    
    lastButtonDownState = buttonDownState;
}

// Display the fan speed indicator bars on the OLED
void displayFanSpeedIndicator() {
    int barWidth = 8;   // Width of each bar
    int barHeight = 3;  // Height of each bar
    int spacing = 1;     // Space between bars
    int xOffset = 3;     // Starting X position for the bars
    int yOffset = SCREEN_HEIGHT - barHeight - spacing; // Starting Y position for the bars

    for (int i = 0; i < maxSpeed / speedStep; i++) {
        int x = xOffset + i * (barWidth + spacing);

        if (i < fanSpeed / speedStep) {
            // Filled bar for active fan speed
            display.fillRect(x, yOffset - (i * barHeight), barWidth, barHeight + (i * barHeight), SH110X_WHITE);
        } else {
            // Outline bar for inactive fan speed
            display.drawRect(x, yOffset - (i * barHeight), barWidth, barHeight + (i * barHeight), SH110X_WHITE);
        }
    }
    unsigned long currentTime = millis();

  // Check if it's time for the next update based on the interval
  if (currentTime - lastUpdateTime >= updateInterval) {
    lastUpdateTime = currentTime;

    // Adjust the update interval to 5 seconds after 5 seconds from boot
    if (currentTime - bootTime >= 5000) {
      updateInterval = 5000; // Update every 5 seconds
    }

    // Read battery voltage and update the readings array
    voltageReadings[currentReadingIndex] = readBatteryVoltage();
    currentReadingIndex = (currentReadingIndex + 1) % numAverages;

    // Calculate the average voltage from the last 10 readings
    float averageVoltage = calculateAverageVoltage();

    // Calculate battery percentage based on voltage range
    float batteryPercentage = (averageVoltage - minVoltage) / (maxVoltage - minVoltage) * 100.0;
    if (batteryPercentage > 100) batteryPercentage = 100;
    if (batteryPercentage < 0) batteryPercentage = 0;

    // Charging detection with hysteresis logic
    if (!isCharging && averageVoltage >= startChargingVoltage) {
      isCharging = true;  // Start charging if voltage >= 28.0V
    } else if (isCharging && averageVoltage < stopChargingVoltage) {
      isCharging = false;  // Stop charging if voltage < 27.0V
    }

    // Print data to Serial Monitor
    Serial.print("Battery Percentage: ");
    Serial.print((int)batteryPercentage);
    Serial.println("%");

    Serial.print("Battery Voltage: ");
    Serial.print(averageVoltage, 2);
    Serial.println(" V");

    Serial.println(isCharging ? "Status: Charging" : "Status: Not Charging");
   
   // Draw the battery icon with charging animation if charging
    drawBatteryIcon(batteryPercentage, isCharging);

    // Display battery percentage next to battery icon
    display.setTextSize(1); 
    display.setTextColor(SH110X_WHITE);
    display.setCursor(55, 4);
    display.print((int)batteryPercentage);
    display.println("%");

    // Draw lightning icon if charging, positioned 7 pixels closer to the percentage
    if (isCharging) {
      drawLightningIcon();
      animationPhase++;  // Increment animation phase for charging fill
    }

    // Update OLED display
    display.display();
  }
}




// Increase fan speed by increments of speedStep
void increaseSpeed() {
    if (fanSpeed < maxSpeed) {
        fanSpeed += speedStep;
        fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);
        updateFanSpeed();
    }
}

// Decrease fan speed by increments of speedStep
void decreaseSpeed() {
    if (fanSpeed > minSpeed) {
        fanSpeed -= speedStep;
        fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);
        updateFanSpeed();
    }
}

// Update PWM output and save speed to EEPROM
void updateFanSpeed() {
    int pwmValue = map(fanSpeed, minSpeed, maxSpeed, minPWM, maxPWM);
    analogWrite(enaPin, pwmValue);
    
    EEPROM.write(0, fanSpeed); // Save new speed to EEPROM

    Serial.print("Fan Speed (%): ");
    Serial.print(fanSpeed);
    Serial.print(" | PWM Value: ");
    Serial.println(pwmValue);
}

// Buzz function to sound the buzzer briefly when a button is pressed
void buzz() {
   tone(BUZZER_PIN, 8000);   // Send an 8KHz sound signal to the buzzer.
   delay(40);                // Sound duration.
   noTone(BUZZER_PIN);       // Stop sound.
   delay(200);              // Delay before next possible buzz.
}

animationInterval = 300
slightly more than three times per second
so don't update so often
or update strategically - for instance update the bar slope area only when it has changed, you don't have to re-do the entire display

Why is this function calling display.display() ? It does not change the display in any way. Calling display.display() takes quite some time and should be avoided wherever possible.

EDIT: actually, that call to display.update() will never be reached. The return command above it prevents that.

I see several other places in the code where display.update() is called. I suggest removing all of them except the one at the end of loop(). For that call, use millis() to ensure it is not called too often. Maybe 10 times per second, for example.

Removing other display.display commands caused the fan icon to stop spinning after few seconds and after adjusting the fan speed it takes a long time for the speed bar to be updated. How to i add the millis() into the display.display command?

Post the latest update of the code.

The buzz() function will need to be re-written. It is blocking code and will prevent smooth updating of the OLED.

This may be a mistake. EEPROM has limited write cycles, approximately 100,000 before it wears out and stops working. You need to ensure you are only writing to the EEPROM when the value has changed. You can use EEPROM.update() for this, or simply put a check in your code that the value has changed before you call EEPROM.write().

#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SH110X.h>
#include <EEPROM.h>

// OLED display constants
#define OLED_ADDR 0x3C
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
Adafruit_SH1106G display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire);

// Buzzer pin definition
#define BUZZER_PIN 3

// Pin for the analog input (battery)
const int batteryPin = A0;

// ** Updated divider ratio based on your real measurements **
const float dividerRatio = 5.88;  // Updated ratio from your test

// Battery voltage range for percentage calculation
const float maxVoltage = 27.4;  // 100% battery voltage
const float minVoltage = 23.0;  // 0% battery voltage

// Hysteresis for charging detection
bool isCharging = false;  // Keep track of charging state

// Voltage thresholds for charging detection
const float startChargingVoltage = 28.0;  // Voltage at which charging is detected
const float stopChargingVoltage = 27.0;   // Voltage below which charging stops

// Animation state variables
int animationPhase = 0;  // For charging animation
unsigned long bootTime;
unsigned long lastUpdateTime = 0;    // Tracks last update time
unsigned long updateInterval = 200;  // Initial update interval of 200 ms (0.2 seconds)

const int numAverages = 10;
float voltageReadings[numAverages] = { 0 };
int currentReadingIndex = 0;

// Bitmap data for new lightning icon (14x14px)
const unsigned char epd_bitmap_lightning_charge_fill_svgrepo_com[] PROGMEM = {
  0x00, 0x40, 0x00, 0xc0, 0x01, 0xc0, 0x03, 0x80, 0x07, 0x80, 0x0f, 0x80, 0x1f, 0xf0, 0x3f, 0xe0,
  0x07, 0xc0, 0x07, 0x80, 0x07, 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x08, 0x00
};

// Fan icon bitmaps (25x25px)
const unsigned char epd_bitmap_fan1[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7e, 0x00, 0x00,
  0x01, 0x78, 0x40, 0x00, 0x02, 0x78, 0x20, 0x00, 0x04, 0xf1, 0xf0, 0x00, 0x08, 0xf3, 0xf8, 0x00,
  0x04, 0x73, 0xf0, 0x00, 0x1e, 0x77, 0xc4, 0x00, 0x1f, 0x3f, 0x04, 0x00, 0x1f, 0xfe, 0x00, 0x00,
  0x0f, 0xfd, 0xf0, 0x00, 0x07, 0xfd, 0xf8, 0x00, 0x00, 0x3e, 0xfc, 0x00, 0x10, 0xfe, 0x7c, 0x00,
  0x07, 0xf7, 0x18, 0x00, 0x07, 0xe7, 0x08, 0x00, 0x0f, 0xcf, 0x80, 0x00, 0x07, 0x8f, 0x00, 0x00,
  0x02, 0x0f, 0x20, 0x00, 0x00, 0x85, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};

const unsigned char epd_bitmap_fan2[] PROGMEM = {
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x47, 0x00, 0x00,
  0x02, 0x0f, 0x40, 0x00, 0x07, 0x9f, 0xa0, 0x00, 0x0f, 0x9f, 0x10, 0x00, 0x07, 0x9e, 0x08, 0x00,
  0x17, 0xdc, 0x60, 0x00, 0x03, 0xdd, 0xfc, 0x00, 0x01, 0xff, 0xfc, 0x00, 0x00, 0xff, 0xfc, 0x00,
  0x0e, 0x7e, 0x1c, 0x00, 0x1f, 0xff, 0x04, 0x00, 0x0f, 0xe7, 0xc4, 0x00, 0x1f, 0x9b, 0xe0, 0x00,
  0x12, 0x39, 0xe0, 0x00, 0x00, 0x79, 0xe8, 0x00, 0x08, 0x78, 0xf0, 0x00, 0x00, 0xf8, 0xe0, 0x00,
  0x01, 0xf0, 0x40, 0x00, 0x00, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00
};

// Define motor and button pins
const int enaPin = 9;
const int in1Pin = 8;
const int in2Pin = 7;
const int buttonUp = 10;
const int buttonDown = 11;

// Fan speed and button debounce settings
int fanSpeed = 0;          // Speed in percent (0-100)
int speedStep = 10;        // Increment step (10%)
const int maxSpeed = 100;  // Max speed (100%)
const int minSpeed = 0;    // Min speed (0%)
const int minPWM = 38;     // Minimum PWM value
const int maxPWM = 117;    // Maximum PWM value

unsigned long lastDebounceTimeUp = 0;
unsigned long lastDebounceTimeDown = 0;
const unsigned long debounceDelay = 50;

bool lastButtonUpState = HIGH;
bool lastButtonDownState = HIGH;

// Animation settings for fan icon
unsigned long lastUpdate = 0;
const int animationInterval = 300;  // Animation interval in ms
bool displayFan1 = true;            // Toggle between fan images

void setup() {
  // Initialize motor driver pins
  pinMode(enaPin, OUTPUT);
  pinMode(in1Pin, OUTPUT);
  pinMode(in2Pin, OUTPUT);

  // Set button pins
  pinMode(buttonUp, INPUT_PULLUP);
  pinMode(buttonDown, INPUT_PULLUP);

  // Set buzzer pin
  pinMode(BUZZER_PIN, OUTPUT);

  // Set initial motor direction
  digitalWrite(in1Pin, LOW);
  digitalWrite(in2Pin, HIGH);

  // Load last saved speed from EEPROM
  fanSpeed = EEPROM.read(0);
  fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);

  // Set initial fan speed
  analogWrite(enaPin, map(fanSpeed, minSpeed, maxSpeed, minPWM, maxPWM));

  Serial.begin(9600);
  Serial.print("Initial Fan Speed: ");
  Serial.println(fanSpeed);

  // Record boot time
  bootTime = millis();

  // Initialize display without splash screen
  display.begin(OLED_ADDR, true);
  display.clearDisplay();
  // Refresh screen to clear any residuals
}

// Function to read and average analog input voltage
float readBatteryVoltage() {
  int total = 0;
  const int numReadings = 10;
  for (int i = 0; i < numReadings; i++) {
    total += analogRead(batteryPin);
    delay(5);
  }
  int analogValue = total / numReadings;
  float voltage = analogValue * (5.0 / 1023.0) * dividerRatio;
  return voltage;
}

// Function to calculate the average of the last 10 readings
float calculateAverageVoltage() {
  float sum = 0;
  for (int i = 0; i < numAverages; i++) {
    sum += voltageReadings[i];
  }
  return sum / numAverages;
}

// Function to draw battery icon with charging animation if charging
void drawBatteryIcon(float batteryPercentage, bool charging) {
  display.drawRect(0, 0, 39, 16, SH110X_WHITE);  // Draw battery outline
  display.fillRect(40, 5, 3, 6, SH110X_WHITE);   // Battery notch

  int fillWidth;
  if (charging) {
    // Animate the charging fill by cycling fill levels
    fillWidth = (39 - 2) * ((animationPhase % 10) / 10.0);
  } else {
    // Fill based on battery percentage
    fillWidth = (int)((batteryPercentage / 100.0) * (39 - 2));
  }
  display.fillRect(1, 1, fillWidth, 14, SH110X_WHITE);  // Fill battery level
}

// Function to draw the 14x14 lightning icon to the right of the battery percentage, 7 pixels closer
void drawLightningIcon() {
  display.drawBitmap(82, 0, epd_bitmap_lightning_charge_fill_svgrepo_com, 14, 14, SH110X_WHITE);
}

void loop() {
  unsigned long currentMillis = millis();

  // Handle fan icon animation
  if (currentMillis - lastUpdate >= animationInterval) {
    lastUpdate = currentMillis;
    display.clearDisplay();

    // Toggle between fan images
    if (displayFan1) {
      display.drawBitmap(103, 39, epd_bitmap_fan1, 25, 25, 1);
    } else {
      display.drawBitmap(103, 39, epd_bitmap_fan2, 25, 25, 1);
    }
    displayFan1 = !displayFan1;

    // Display fan speed indicator
    displayFanSpeedIndicator();
  }

  // Read button states and adjust fan speed
  bool buttonUpState = digitalRead(buttonUp);
  bool buttonDownState = digitalRead(buttonDown);

  if (buttonUpState == LOW && lastButtonUpState == HIGH && (millis() - lastDebounceTimeUp) > debounceDelay) {
    lastDebounceTimeUp = millis();
    increaseSpeed();
    buzz();  // Buzz on button press
  }

  lastButtonUpState = buttonUpState;

  if (buttonDownState == LOW && lastButtonDownState == HIGH && (millis() - lastDebounceTimeDown) > debounceDelay) {
    lastDebounceTimeDown = millis();
    decreaseSpeed();
    buzz();  // Buzz on button press
  }

  lastButtonDownState = buttonDownState;
}

// Display the fan speed indicator bars on the OLED
void displayFanSpeedIndicator() {
  int barWidth = 8;                                   // Width of each bar
  int barHeight = 3;                                  // Height of each bar
  int spacing = 1;                                    // Space between bars
  int xOffset = 3;                                    // Starting X position for the bars
  int yOffset = SCREEN_HEIGHT - barHeight - spacing;  // Starting Y position for the bars

  for (int i = 0; i < maxSpeed / speedStep; i++) {
    int x = xOffset + i * (barWidth + spacing);

    if (i < fanSpeed / speedStep) {
      // Filled bar for active fan speed
      display.fillRect(x, yOffset - (i * barHeight), barWidth, barHeight + (i * barHeight), SH110X_WHITE);
    } else {
      // Outline bar for inactive fan speed
      display.drawRect(x, yOffset - (i * barHeight), barWidth, barHeight + (i * barHeight), SH110X_WHITE);
    }
  }

  unsigned long currentTime = millis();
  // Check if it's time for the next update based on the interval
  if (currentTime - lastUpdateTime >= updateInterval) {
    lastUpdateTime = currentTime;

    // Adjust the update interval to 10 seconds after 5 seconds from boot
    if (currentTime - bootTime >= 5000) {
      updateInterval = 10000;  // Update every 10 seconds
    }

    // Read battery voltage and update the readings array
    voltageReadings[currentReadingIndex] = readBatteryVoltage();
    currentReadingIndex = (currentReadingIndex + 1) % numAverages;

    // Calculate the average voltage from the last 10 readings
    float averageVoltage = calculateAverageVoltage();

    // Calculate battery percentage based on voltage range
    float batteryPercentage = (averageVoltage - minVoltage) / (maxVoltage - minVoltage) * 100.0;
    if (batteryPercentage > 100) batteryPercentage = 100;
    if (batteryPercentage < 0) batteryPercentage = 0;

    // Charging detection with hysteresis logic
    if (!isCharging && averageVoltage >= startChargingVoltage) {
      isCharging = true;  // Start charging if voltage >= 28.0V
    } else if (isCharging && averageVoltage < stopChargingVoltage) {
      isCharging = false;  // Stop charging if voltage < 27.0V
    }

    // Print data to Serial Monitor
    Serial.print("Battery Percentage: ");
    Serial.print((int)batteryPercentage);
    Serial.println("%");

    Serial.print("Battery Voltage: ");
    Serial.print(averageVoltage, 2);
    Serial.println(" V");

    Serial.println(isCharging ? "Status: Charging" : "Status: Not Charging");


    // Draw the battery icon with charging animation if charging
    drawBatteryIcon(batteryPercentage, isCharging);

    // Display battery percentage next to battery icon
    display.setTextSize(1);
    display.setTextColor(SH110X_WHITE);
    display.setCursor(55, 4);
    display.print((int)batteryPercentage);
    display.println("%");

    // Draw lightning icon if charging, positioned 7 pixels closer to the percentage
    if (isCharging) {
      drawLightningIcon();
      animationPhase++;  // Increment animation phase for charging fill
    }

    // Update OLED display
    display.display();
  }
}


// Increase fan speed by increments of speedStep
void increaseSpeed() {
  if (fanSpeed < maxSpeed) {
    fanSpeed += speedStep;
    fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);
    updateFanSpeed();
  }
}

// Decrease fan speed by increments of speedStep
void decreaseSpeed() {
  if (fanSpeed > minSpeed) {
    fanSpeed -= speedStep;
    fanSpeed = constrain(fanSpeed, minSpeed, maxSpeed);
    updateFanSpeed();
  }
}

// Update PWM output and save speed to EEPROM
void updateFanSpeed() {
  int pwmValue = map(fanSpeed, minSpeed, maxSpeed, minPWM, maxPWM);
  analogWrite(enaPin, pwmValue);

  EEPROM.write(0, fanSpeed);  // Save new speed to EEPROM

  Serial.print("Fan Speed (%): ");
  Serial.print(fanSpeed);
  Serial.print(" | PWM Value: ");
  Serial.println(pwmValue);
}

// Buzz function to sound the buzzer briefly when a button is pressed
void buzz() {
  tone(BUZZER_PIN, 8000);  // Send an 8KHz sound signal to the buzzer.
  delay(40);               // Sound duration.
  noTone(BUZZER_PIN);      // Stop sound.
  delay(20);               // Delay before next possible buzz.
}

well i can then just reupload the code when this happens? i dont think i will change the speed that often. i am making a fresh air respirator for my welding mask.

Maybe, yes. In theory it is around 100,000 writes for each location, and there are hundreds of locations. But it's pretty easy to change to using .update().