//>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> // ARDUINO BATTERY CAPACITY TESTER //Version-2.1 //by deba168,INDIA // The code is taken from Hesam Moshiri ( https://www.pcbway.com/blog/technology/Battery_capacity_measurement_using_Arduino.html ) //Dated : 02/02/2022 // //Dated : 29/02/2020 //Added code to read a band gap reference connected to analog pin A1, // thus allowing measurement of Vcc and avoiding the need to measure it using a multimeter //Replaced 1 ohm 5% resistor with 1 ohm 1% power resistor //Modified code to compute actual current draw based on desired values. Measurements // using ammeter show computed values within 2 milliamps of measured //>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> #include #include #define DEBUG 0 // Binary Treatment 0=Nothing, 1=Early, 2=Running Voltages, 4=Finish #define SCREEN_WIDTH 128 // OLED display width, in pixels #define SCREEN_HEIGHT 64 // OLED display height, in pixels // Declaration for an SSD1306 display connected to I2C (SDA, SCL pins) #define OLED_RESET 4 // Reset pin # (or -1 if sharing Arduino reset pin) Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); void timerInterrupt(); void Display_UP_DOWN();//Debugging support void Print_DEBUG_4();//Debugging support const float Low_BAT_level = 2.80;// Threshold for stopping test //Desired Current steps with a 3R load (R7) int Current [] = {0,50,100,200,300,400,500,600,700,800,900,1000}; int PWM [] = {0, 2, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50}; int Array_Size;// calculated during setup const byte PWM_Pin = 10; const byte Buzzer = 9; const int BAT_Pin = A0; const int Vref_Pin = A1;//Pin to which Band Gap Reference is attached int Current_Value = 0; // Selected Current value for test int PWM_Value = 0;// Value of PWM during test int PWM_Index = 0;// Index into PWM array during test unsigned long Capacity; float Capacity_f; int ADC_Value = 0; float Vref_Voltage = 1.215; // LM385BLP-1.2 Band Gap Reference voltage float Vcc = 5.32; // Voltage of Arduino 5V pin ( Measured during setup using Band Gap Reference ) float BAT_Voltage = 0; float Resistance = 1.0; // Value of R3 Power Resistor float sample = 0; byte Hour = 0, Minute = 0, Second = 0; bool calc = false, Done = false, Report_Info = true; Button UP_Button(2, 25, false, true); Button Down_Button(3, 25, false, true); // string values for reporting intermediate information const int VAL_MAX = 10; char val_0[VAL_MAX]={""}; char val_2[VAL_MAX]={""}; void setup () {//Setup Serial.begin(38400); pinMode(PWM_Pin, OUTPUT); pinMode(Buzzer, OUTPUT); analogWrite(PWM_Pin, PWM_Value); UP_Button.begin(); Down_Button.begin(); display.begin(SSD1306_SWITCHCAPVCC, 0x3C); display.clearDisplay(); display.setTextColor(WHITE); display.setTextSize(1); display.setCursor(12,25); display.print("Open Green Energy"); display.display(); delay(3000); display.clearDisplay(); display.setTextSize(2); display.setCursor(2,15); display.print("Adj Curr:"); display.setCursor(2,40); display.print("UP/Down:"); display.print("0"); display.setCursor(2,55); display.setTextSize(1); #if (DEBUG == 1 || DEBUG == 5) Serial.println("\nStart of calculations"); #endif Array_Size = sizeof(Current)/2; //Read voltages of Band Gap Reference and use it to calculate actual Vcc float fTemp=0.0; for(int i=0;i< 100;i++) { fTemp=fTemp+analogRead(Vref_Pin); //read the Band Gap Reference voltage delay (2); } fTemp=fTemp/100.0; Vcc = Vref_Voltage * 1024.0 / fTemp; // Convert desired current levels to PWM using actual Vcc // Convert PWM values back to actual current levels // While measuring actual current I discovered that the actual draw is // (PWM + 1)/256 rather than PWM/255 as indicated in Arduino documentation int iTemp; for (int i=0;i 0 && calc == false) { PWM_Value = PWM[--PWM_Index]; analogWrite(PWM_Pin,PWM_Value); Display_UP_DOWN(); } if (UP_Button.pressedFor (1000) && calc == false) { digitalWrite(Buzzer, HIGH); delay(100); digitalWrite(Buzzer, LOW); display.clearDisplay(); timerInterrupt(); } } //************************* End of Loop function ******************************* void timerInterrupt(){ calc = true; while (Done == false) {//(Done == false) Second ++; if (Second == 60) { Second = 0; Minute ++; } if (Minute == 60) { Minute = 0; Hour ++; } //************ Measuring Battery Voltage *********** for(int i=0;i< 100;i++) { sample=sample+analogRead(BAT_Pin); //read the Battery voltage delay (2); } sample=sample/100; BAT_Voltage = sample * Vcc / 1024.0; //********************************************* display.clearDisplay(); display.setTextSize(2); display.setCursor(20,5); // display.print(String(Hour) + ":" + String(Minute) + ":" + String(Second)); display.print(Hour); display.print(":"); display.print(Minute); display.print(":"); display.print(Second); display.setTextSize(1); display.setCursor(0,25); display.print("Disch Curr: "); // display.print(String(Current[PWM_Index])+"mA"); display.print(Current_Value); display.print("mA"); display.setCursor(2,40); // display.print("Bat Volt:" + String(BAT_Voltage)+"V" ); display.print("Bat Volt:"); display.print(BAT_Voltage,3); display.print("V"); // When total seconds is greater than 32767 the statement below did not work until the byte values // Hour, Minute and Second were cast to an unsigned long. Apparently the compiler cast the byte // values to an "int" first which cannot represent 32768 correctly Capacity = ((unsigned long)Hour * 3600) + ((unsigned long)Minute * 60) + (unsigned long)Second; Capacity_f = ((float)Capacity * Current_Value) / 3600.0; display.setCursor(2, 55); // display.print("Capacity:" + String(Capacity) + "mAh"); display.print("Capacity:"); display.print(Capacity_f,1); display.print("mAh"); display.display(); #if (DEBUG == 4 || DEBUG == 2) Print_DEBUG_4(); #endif if (BAT_Voltage < Low_BAT_level) {//BAT_Voltage < Low_BAT_level #if (DEBUG == 4 || DEBUG == 5) Serial.println("\nCurrent_Value PWM_Value"); Serial.print(Current_Value); Serial.print(" "); Serial.println(PWM_Value); Serial.println("\nHour Minute Second PWM_Index"); Serial.print(Hour); Serial.print(" "); Serial.print(Minute); Serial.print(" "); Serial.print(Second); Serial.print(" "); Serial.println(PWM_Index); #endif // When total seconds is greater than 32767 the statement below did not work until the byte values // Hour, Minute and Second were cast to an unsigned long. Apparently the compiler cast the byte // values to an "int" first which cannot represent 32768 correctly Capacity = ((unsigned long)Hour * 3600) + ((unsigned long)Minute * 60) + (unsigned long)Second; #if (DEBUG == 4 || DEBUG == 5) Serial.println("Capacity HMS"); Serial.println(Capacity); #endif Capacity_f = ((float)Capacity * Current_Value) / 3600.0; #if (DEBUG == 4 || DEBUG == 5) Serial.println("Capacity HMS*PWM"); Serial.println(Capacity_f,1); #endif display.clearDisplay(); display.setTextSize(2); display.setCursor(2,15); display.print("Capacity:"); display.setCursor(2,40); // display.print(String(Capacity) + "mAh"); display.print(Capacity_f,1); display.print("mAh"); display.display(); Done = true; PWM_Value = 0; analogWrite(PWM_Pin, PWM_Value); digitalWrite(Buzzer, HIGH); delay(100); digitalWrite(Buzzer, LOW); delay(100); digitalWrite(Buzzer, HIGH); delay(100); digitalWrite(Buzzer, LOW); delay(100); }//BAT_Voltage < Low_BAT_level delay(1000); }//(Done == false) }// timerInterrupt void Display_UP_DOWN() {//Display_UP_DOWN() Current_Value = Current[PWM_Index]; display.clearDisplay(); display.setCursor(2,25); display.print("Curr:"); display.print(Current_Value); display.print("mA "); display.setCursor(2,40); display.print("PWM="); display.print(PWM_Value); display.display(); }//Display_UP_DOWN() void Print_DEBUG_4() {//Print_DEBUG_4() Serial.print(Hour); Serial.print(":"); Serial.print(Minute); Serial.print(":"); Serial.print(Second); Serial.print(" "); Serial.print(BAT_Voltage,3); Serial.print("v "); Serial.print(Capacity_f,1); Serial.println("mAh"); }//Print_DEBUG_4()