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.
}