Smart battery life tester
Calculates alkaline batteries remaining life
Components and supplies
1
Resistor 220 ohm
1
16x2 LCD display with I²C interface
1
AA battery holder
1
Resistor 100 ohm (0.5 W min!)
1
Perf board
4
Transistor NPN (BC547 or similar)
1
Resistor 100k ohm
1
Resistor 10k
1
Resistor 47 ohms 1 W
1
5v ac/dc convertor
1
Resistor 1k ohm
4
push buttons
1
resistor 6.6k ohm
4
spst relay
1
Arduino Nano
1
project box
4
diode
1
power jack connector
4
Resistor 470 ohm
1
9V Battery Clip
Tools and machines
1
Hot glue gun (generic)
1
Soldering kit
1
drill
1
Utility Knife
Apps and platforms
1
Arduino IDE 2.0 (beta)
Project description
Code
batteryReader
cpp
1//Here is the code i wrote, i made it pretty basic, nothing too fancy but it does work. 2 3 4#include <Wire.h> 5#include <hd44780.h> 6#include <hd44780ioClass/hd44780_I2Cexp.h> 7 8 9 10hd44780_I2Cexp lcd; // Create lcd object (MUST be named "lcd") 11 12float rFactor; 13int load220 = 7; 14int load100 = 5; 15int load47 = 2; 16int vPin = A0; 17int life; 18int load9v = 6; 19int vPin9 = A2; 20int bPin = 9; 21int bVal; 22 23float voltageScore; 24float voltage; 25float rint; 26float vint; 27String rating; 28int pos; 29const int NUM_SAMPLES = 50; 30const int TRIM = 5; // how many values to trim on each end 31 32int bPin220 = 3; 33int bPin100 = 4; 34int bPin47 = 8; 35int lightVal; 36int medVal; 37int heavyVal; 38 39bool pressed = 0; 40bool go; 41int battery; 42int mode; 43 44void setup() { 45 Serial.begin(9600); 46 Wire.begin(); 47 lcd.begin(16,2); 48 pinMode(load220, OUTPUT); 49 pinMode(load100, OUTPUT); 50 pinMode(load47, OUTPUT); 51 pinMode(bPin,INPUT); 52 pinMode(bPin220,INPUT); 53 pinMode(bPin100,INPUT); 54 pinMode(bPin47,INPUT); 55 56 pinMode(load9v,OUTPUT); 57 digitalWrite(bPin,HIGH); 58 digitalWrite(bPin220,HIGH); 59 digitalWrite(bPin100,HIGH); 60 digitalWrite(bPin47,HIGH); 61 pinMode(vPin, INPUT); 62 pinMode(vPin9,INPUT); 63} 64 65void loop() { 66 mode = 0; 67 pressed = 0; 68 go = 0; 69 70 lcd.clear(); 71 lcd.setCursor(0, 0); 72 lcd.print("select battery"); 73 Serial.print("check1"); 74 //theres no difference between AA and AAA, i just wanted to use all 3 buttons. But the users doesnt know that 75 //checks which battery type 76 while(pressed == 0){ 77 lightVal = digitalRead(bPin220); 78 medVal = digitalRead(bPin100); 79 heavyVal = digitalRead(bPin47); 80 81 if(lightVal == 0){ 82 battery = 9; 83 84 Serial.println(mode); 85 lcd.clear(); 86 lcd.setCursor(0,0); 87 lcd.print("9v selected"); 88 lcd.setCursor(0,1); 89 lcd.print("press go "); 90 delay(1000); 91 lcd.setCursor(0,1); 92 lcd.print("to start test "); 93 go = true; 94 } 95 if(medVal == 0){ 96 battery = 1.5; 97 98 lcd.clear(); 99 lcd.setCursor(0,0); 100 lcd.print("AA selected"); 101 lcd.setCursor(0,1); 102 lcd.print("press go "); 103 delay(1000); 104 lcd.setCursor(0,1); 105 lcd.print("to continue "); 106 Serial.println(mode); 107 go = true; 108 } 109 if(heavyVal == 0){ 110 battery = 1.5; 111 112 Serial.println(mode); 113 lcd.clear(); 114 lcd.setCursor(0,0); 115 lcd.print("AAA selected"); 116 lcd.setCursor(0,1); 117 lcd.print("press go "); 118 delay(1000); 119 lcd.setCursor(0,1); 120 lcd.print("to continue "); 121 go = true; 122 } 123 //escapes while loop 124 if(go == true){ 125 bVal = digitalRead(bPin); 126 if(bVal == 0){ 127 pressed = true; 128 } 129 } 130 } 131 132 133 go = 0; 134 pressed = 0; 135 //special code for 9v battery 136 if(battery == 9){ 137 pressed = 1; 138 calculate(load9v,vPin9,1000); 139 voltage = voltage * (10000.0 + 6800.0)/6800.0; 140 vint = vint * (10000.0 + 6800.0)/6800.0; 141 rint = calcR(vint, 1000, voltage); 142 Serial.println(voltage); 143 Serial.println(vint); 144 Serial.println(rint); 145 mode = 3; 146 voltageScore = constrain((voltage - 7.0) / (9.4 - 7.0), 0.0, 1.0) * 100.0; 147 rFactor = getRFactor(rint); 148 life = (voltageScore * rFactor) / 100.0; 149 pos = 8; 150 } 151 if(pressed == 0){ 152 lcd.clear(); 153 lcd.setCursor(0, 0); 154 lcd.print("Select load"); 155 } 156 // chooses the load value 157 while(pressed == 0){ 158 lightVal = digitalRead(bPin220); 159 medVal = digitalRead(bPin100); 160 heavyVal = digitalRead(bPin47); 161 if(lightVal == 0){ 162 mode = 0; 163 Serial.println(mode); 164 lcd.clear(); 165 lcd.setCursor(0,0); 166 lcd.print("light load"); 167 lcd.setCursor(0,1); 168 lcd.print("press go "); 169 delay(1000); 170 lcd.setCursor(0,1); 171 lcd.print("to start test"); 172 go = true; 173 } 174 if(medVal == 0){ 175 mode = 1; 176 lcd.clear(); 177 lcd.setCursor(0,0); 178 lcd.print("medium load"); 179 lcd.setCursor(0,1); 180 lcd.print("press go "); 181 delay(1000); 182 lcd.setCursor(0,1); 183 lcd.print("to start test "); 184 Serial.println(mode); 185 go = true; 186 } 187 if(heavyVal == 0){ 188 mode = 2; 189 Serial.println(mode); 190 lcd.clear(); 191 lcd.setCursor(0,0); 192 lcd.print("heavy load"); 193 lcd.setCursor(0,1); 194 lcd.print("press go "); 195 delay(1000); 196 lcd.setCursor(0,1); 197 lcd.print("to start test"); 198 go = true; 199 } 200 if(go == true){ 201 bVal = digitalRead(bPin); 202 if(bVal == 0){ 203 pressed = true; 204 } 205 } 206 207 } 208 if(mode == 0){ 209 calculate(load220,vPin,220); 210 if(voltage - vint <0.005){ 211 rint = -1; 212 life = -1; 213 rating = "pls read again"; 214 } 215 216 } 217 if(mode == 1){ 218 calculate(load100,vPin,100); 219 } 220 if(mode == 2){ 221 calculate(load47,vPin,47); 222 } 223 lcd.clear(); 224 225 226 //ratings 227 if (rint < 0) { 228 rating = "error try again"; 229 life = 0; 230 } else if (life >= 85) { 231 rating = "Excellent"; 232 } else if (life >= 60) { 233 rating = "Good"; 234 } else if (life >= 35) { 235 rating = "Weak"; 236 } else if (life >= 15) { 237 rating = "Poor"; 238 } else { 239 rating = "Throw it out"; 240 } 241 242 243 244 // Final average 245 246 Serial.print("Average Internal Resistance: "); 247 248 Serial.println(" Ω"); 249 lcd.clear(); 250 lcd.setCursor(0,0); 251 lcd.print("voltage:"); 252 lcd.setCursor(8,0); 253 lcd.print(voltage); 254 lcd.setCursor(0,1); 255 lcd.print("Rint:"); 256 lcd.setCursor(7,1); 257 lcd.print(rint); 258 delay(5000); 259 lcd.clear(); 260 lcd.setCursor(0,0); 261 lcd.print("life:"); 262 lcd.setCursor(6,0); 263 lcd.print(life); 264 lcd.setCursor(pos,0); 265 lcd.print("%"); 266 lcd.setCursor(0,1); 267 lcd.print(rating); 268 269 270 delay(5000); 271 lcd.setCursor(0,0); 272 lcd.print("let battery rest"); 273 lcd.setCursor(0,1); 274 lcd.print("before reTesting"); 275 delay(2000); 276} 277 278 279// read voltage 280int readVoltage(int pin) { 281 int readings[NUM_SAMPLES]; 282 283 // Collect readings 284 for (int i = 0; i < NUM_SAMPLES; i++) { 285 readings[i] = analogRead(pin); 286 delayMicroseconds(500); // short delay to stabilize readings 287 } 288 289 for (int i = 0; i < NUM_SAMPLES - 1; i++) { 290 for (int j = i + 1; j < NUM_SAMPLES; j++) { 291 if (readings[j] < readings[i]) { 292 int temp = readings[i]; 293 readings[i] = readings[j]; 294 readings[j] = temp; 295 } 296 } 297 } 298 // Step 3: Average the middle values 299 long sum = 0; 300 for (int i = TRIM; i < NUM_SAMPLES - TRIM; i++) { 301 sum += readings[i]; 302 } 303 304 int trimmedAverage = sum / (NUM_SAMPLES - 2 * TRIM); 305 return trimmedAverage; 306} 307//calculate internal resistance 308float calcR(float vload, int rload, float vOpen) { 309 float rInt = rload * ((vOpen - vload) / vload); 310 Serial.print("→ rInt: "); 311 Serial.print(rInt, 3); 312 Serial.println(" Ω"); 313 return rInt; 314} 315//gets arduino reference voltage 316long readVcc() { 317 // Measures Vcc using the 1.1V internal reference 318 ADMUX = _BV(REFS0) | _BV(MUX3) | _BV(MUX2) | _BV(MUX1); 319 delay(2); // Wait for Vref to settle 320 ADCSRA |= _BV(ADSC); 321 while (bit_is_set(ADCSRA, ADSC)); 322 uint16_t result = ADC; 323 long vcc = 1125300L / result; // 1.1V * 1023 * 1000 324 return vcc; // in millivolts 325} 326//turns analog reading into actual voltage 327void calculate(int load,int pin,int rload){ 328 float Vref = readVcc() / 1000.0; 329 voltage = readVoltage(pin) * (Vref / 1023.0); 330 Serial.print("Open : "); 331 Serial.println(voltage, 3); 332 lcd.clear(); 333 lcd.setCursor(0,0); 334 lcd.print("getting voltage"); 335 336 delay(200); 337 338 digitalWrite(load, HIGH); 339 delay(500); 340 vint = readVoltage(pin)* (Vref / 1023.0); 341 Serial.print("Load Voltage: "); 342 Serial.println(vint, 3); 343 rint = calcR(vint, rload, voltage); 344 digitalWrite(load, LOW); 345 delay(500); 346 347 348 349 if(pin == vPin){ 350 if (voltage >= 1.6) voltageScore = 100; 351 else if (voltage >= 1.5) voltageScore = 95; 352 else if (voltage >= 1.4) voltageScore = 80; 353 else if (voltage >= 1.3) voltageScore = 60; 354 else if (voltage >= 1.2) voltageScore = 35; 355 else if (voltage >= 1.1) voltageScore = 15; 356 else voltageScore = 5; 357 358 359 if (rint <= 0.3) rFactor = 100; 360 else if (rint <= 0.4) rFactor = 90; 361 else if (rint <= 0.6) rFactor = 75; 362 else if (rint <= 1.0) rFactor = 50; 363 else if (rint <= 1.5) rFactor = 30; 364 else rFactor = 10; 365 life = (voltageScore * rFactor) / 100.0; 366 if(life == 100){ 367 pos = 9; 368 }else{ 369 pos = 8; 370 } 371 } 372 373} 374// gets the rFactor for life expectanty 375float getRFactor(float rint) { 376 if (rint < 5.0) return 100.0; 377 else if (rint < 10.0) return 90.0; 378 else if (rint < 15.0) return 70.0; 379 else if (rint < 25.0) return 50.0; 380 else if (rint < 35.0) return 30.0; 381 else return 15.0; 382}
Documentation
Battery Tester vid
Here's a video I made of me assembling the device
Battery teter Vid.mp4
Schematic
schematic does not include lcd
Screenshot 2025-08-02 115344.png

Comments
Only logged in users can leave comments