Components and supplies
Arduino Nano
2 relay module
IRF510N MOSFET
20x4 LCD
Tools and machines
Multimeter
Soldering iron (generic)
Apps and platforms
Arduino IDE
Project description
Code
Here is the Code for the charger.
cpp
You will need the LiquidCrystal-1.0.6 library
1#include <LiquidCrystal.h> 2#include <EEPROM.h> 3LiquidCrystal lcd(7,6,4,5,3,2); // (RS,E,D4,D5,D6,D7) 4 5// Constants 6///////////////////////////////////////// 7int maxPower = 30; // Maximal charging power. 30 = 300mA, 1 = 10mA 8int discharging_mVolt = 950; // Volt (milli volt) value where the discharging will stop. 900 = 900mV 9float correctionCharging1 = -0.15; // Correction (Volt). 0.15 = 0.15V 10float correctionCharging2 = -0.15; // 11float correctionDischarging1 = 0.05; // 12float correctionDischarging2 = 0.05; // 13///////////////////////////////////////// 14 15boolean mainMenuIsDisplayed = true; 16boolean chargingIsDisplayed = false; 17boolean disChargingIsDisplayed = false; 18boolean powerIsDisplayed = false; 19boolean timeIsDisplayed = false; 20boolean modeIsDisplayed = false; 21boolean changeVoltIsDisplayed = false; 22 23int upButton = 12; 24int downButton = 19; 25int enterButton = 8; 26int resetPin = 11; 27int relay1Pin = 13; 28int relay2Pin = 18; 29int PWM1Pin = 10; 30int PWM2Pin = 9; 31int volt1_1Pin = 14; 32int volt1_2Pin = 21; 33int volt2_1Pin = 15; 34int volt2_2Pin = 20; 35 36int hours = EEPROM.read(0); 37int power = EEPROM.read(1); 38float maxVolt = EEPROM.read(3); 39 40int cursorPosition = 1; 41 42byte selectArrow[8] = { 43 0b11000, 44 0b11100, 45 0b11110, 46 0b11111, 47 0b11111, 48 0b11110, 49 0b11100, 50 0b11000 51}; 52 53void setup(){ 54 pinMode(downButton, INPUT_PULLUP); 55 pinMode(upButton, INPUT_PULLUP); 56 pinMode(enterButton, INPUT_PULLUP); 57 pinMode(relay1Pin, OUTPUT); 58 pinMode(relay2Pin, OUTPUT); 59 pinMode(PWM1Pin, OUTPUT); 60 pinMode(PWM2Pin, OUTPUT); 61 62 // 7.8kHz 10 bit 63 TCCR1A = 0b00000011; 64 TCCR1B = 0b00000001; 65 //Serial.begin(115200); 66 lcd.begin(20, 4); // Initialize the display, (columns, rows) 67 68 lcd.createChar(0, selectArrow); 69 drawMenu(); 70} 71 72void loop(){ 73 if (digitalRead(upButton) == 0){ 74 upPressed(); 75 delay(300); 76 } 77 if (digitalRead(downButton) == 0){ 78 downPressed(); 79 delay(300); 80 } 81 if (digitalRead(enterButton) == 0){ 82 enterPressed(); 83 delay(300); 84 } 85} 86 87void charging(){ 88 lcd.clear(); 89 lcd.setCursor(6, 0); 90 lcd.print("Accu 1"); 91 lcd.setCursor(14, 0); 92 lcd.print("Accu 2"); 93 lcd.setCursor(0, 1); 94 lcd.print("V:"); 95 lcd.setCursor(0, 2); 96 lcd.print("mA:"); 97 98 uint32_t sec; 99 int timeHours = 0; 100 int timeMins; 101 String minsZero; 102 103 boolean RUN = true; 104 boolean accu1Finished = false; 105 boolean accu2Finished = false; 106 boolean accu1IsPluggedIn = false; 107 boolean accu2IsPluggedIn = false; 108 109 int PWM1 = 300; 110 int PWM2 = 300; 111 int power = EEPROM.read(1); 112 power *= 10; 113 analogWrite(PWM1Pin, PWM1); 114 analogWrite(PWM2Pin, PWM2); 115 116 while (RUN){ 117 sec = millis() / 1000ul; 118 timeHours = (sec / 3600ul); 119 timeMins = (sec % 3600ul) / 60ul; 120 121 float mV1_1 = analogRead(volt1_1Pin); 122 float mV1_2 = analogRead(volt1_2Pin); 123 124 float mV2_1 = analogRead(volt2_1Pin); 125 float mV2_2 = analogRead(volt2_2Pin); 126 127 mV1_1 = mV1_1 * 5000 / 1023; 128 mV1_2 = mV1_2 * 5000 / 1023; 129 130 mV2_1 = mV2_1 * 5000 / 1023; 131 mV2_2 = mV2_2 * 5000 / 1023; 132 133 float voltAccu1 = ((5000 - mV1_1) / 1000) + correctionCharging1; 134 float voltAccu2 = ((5000 - mV2_1) / 1000) + correctionCharging2; 135 136 int mAmpere1 = (mV1_1 - mV1_2) / 5; 137 int mAmpere2 = (mV2_1 - mV2_2) / 5; 138 139 if (mV1_1 < 50){ 140 accu1IsPluggedIn = false; 141 PWM1 = 0; 142 showNoAccu(6); 143 } else { 144 accu1IsPluggedIn = true; 145 } 146 147 if (mV2_1 < 50){ 148 accu2IsPluggedIn = false; 149 PWM2 = 0; 150 showNoAccu(14); 151 } else { 152 accu2IsPluggedIn = true; 153 } 154 155 if (!accu1Finished && accu1IsPluggedIn){ 156 if (power != mAmpere1){ 157 if (mAmpere1 < 5){ 158 PWM1 += 100; 159 } else if (power - mAmpere1 >= 40){ 160 PWM1 += 20; 161 } 162 if (mAmpere1 < power){ 163 PWM1++; 164 } else { 165 PWM1--; 166 } 167 } 168 } 169 170 if (!accu2Finished && accu2IsPluggedIn){ 171 if (power != mAmpere2){ 172 if (mAmpere2 < 5){ 173 PWM2 += 100; 174 } else if (power - mAmpere2 >= 40){ 175 PWM2 += 20; 176 } 177 if (mAmpere2 < power){ 178 PWM2++; 179 } else { 180 PWM2--; 181 } 182 } 183 } 184 185 analogWrite(PWM1Pin, PWM1); 186 analogWrite(PWM2Pin, PWM2); 187 188 if (!accu1Finished && accu1IsPluggedIn){ 189 showChargingData(6, voltAccu1, mAmpere1); 190 } 191 192 if (!accu2Finished && accu2IsPluggedIn){ 193 showChargingData(14, voltAccu2, mAmpere2); 194 } 195 196 // auto 197 if (EEPROM.read(2) == 0){ 198 if (voltAccu1 >= maxVolt / 100 && accu1IsPluggedIn){ 199 accu1Finished = true; 200 PWM1 = 0; 201 analogWrite(PWM1Pin, PWM1); 202 lcd.setCursor(6, 2); 203 lcd.print("Done"); 204 } 205 if (voltAccu2 >= maxVolt / 100 && accu2IsPluggedIn){ 206 accu2Finished = true; 207 PWM2 = 0; 208 analogWrite(PWM2Pin, PWM2); 209 lcd.setCursor(14, 2); 210 lcd.print("Done"); 211 } 212 if ((accu1Finished || !accu1IsPluggedIn) && (accu2Finished || !accu2IsPluggedIn)){ 213 RUN = false; 214 lcd.setCursor(0, 3); 215 lcd.print("Press enter to exit"); 216 } 217 } else { // manually 218 lcd.setCursor(0, 0); 219 lcd.print(" "); 220 if (timeMins < 10){ 221 minsZero = "0"; 222 } else { 223 minsZero = ""; 224 } 225 lcd.setCursor(0, 0); 226 lcd.print(String(timeHours) + ":" + minsZero + String(timeMins)); 227 if (timeHours >= hours){ 228 accu1Finished = true; 229 accu2Finished = true; 230 lcd.setCursor(0, 2); 231 lcd.print(" "); 232 lcd.setCursor(6, 2); 233 lcd.print("Done"); 234 lcd.setCursor(14, 2); 235 lcd.print("Done"); 236 lcd.setCursor(0, 3); 237 lcd.print("Press enter to exit"); 238 PWM1 = 0; 239 PWM2 = 0; 240 analogWrite(PWM1Pin, PWM1); 241 analogWrite(PWM2Pin, PWM2); 242 RUN = false; 243 } else if (!accu1IsPluggedIn && !accu2IsPluggedIn){ 244 showNoAccu(6); 245 showNoAccu(14); 246 lcd.setCursor(0, 3); 247 lcd.print("Press enter to exit"); 248 PWM1 = 0; 249 PWM2 = 0; 250 analogWrite(PWM1Pin, PWM1); 251 analogWrite(PWM2Pin, PWM2); 252 RUN = false; 253 } 254 } 255 delay(2000); 256 } 257 for(;;){ 258 if (digitalRead(enterButton) == 0){ 259 pinMode(resetPin, OUTPUT); 260 digitalWrite(resetPin, LOW); 261 } 262 delay(50); 263 } 264} 265 266void discharge(){ 267 boolean discharging1Finished = false; 268 boolean discharging2Finished = false; 269 boolean accu1IsPluggedIn = false; 270 boolean accu2IsPluggedIn = false; 271 lcd.clear(); 272 lcd.setCursor(6, 0); 273 lcd.print("Accu 1"); 274 lcd.setCursor(14, 0); 275 lcd.print("Accu 2"); 276 lcd.setCursor(0, 1); 277 lcd.print("mAh:"); 278 lcd.setCursor(0, 2); 279 lcd.print("V:"); 280 lcd.setCursor(0, 3); 281 lcd.print("mA:"); 282 283 digitalWrite(relay1Pin, HIGH); 284 digitalWrite(relay2Pin, HIGH); 285 delay(500); 286 float mAh1 = 0; 287 float mAh2 = 0; 288 for (;;){ 289 // first accu 290 float mV1_2 = analogRead(volt1_2Pin); 291 mV1_2 = (mV1_2 * 5000) / 1023; 292 mV1_2 += correctionDischarging1 * 1000; 293 294 if (mV1_2 <= 100 && !discharging1Finished){ 295 accu1IsPluggedIn = false; 296 showNoAccu(6); 297 } else { 298 accu1IsPluggedIn = true; 299 } 300 301 if (!discharging1Finished && accu1IsPluggedIn){ 302 float mAmpere1 = mV1_2 / 5; // 5Ω 303 mAh1 += mAmpere1 * 0.0027777778; 304 305 float V1_2 = mV1_2 / 1000; 306 307 showDischargingData(6, int(mAh1), V1_2, int(mAmpere1)); 308 309 if (mV1_2 < discharging_mVolt && mV1_2 > 100){ 310 discharging1Finished = true; 311 digitalWrite(relay1Pin, LOW); 312 lcd.setCursor(6, 2); 313 lcd.print("Done"); 314 lcd.setCursor(6, 3); 315 lcd.print(" "); 316 } 317 } 318 319 // second accu 320 float mV2_2 = analogRead(volt2_2Pin); 321 mV2_2 = (mV2_2 * 5000) / 1023; 322 mV2_2 += correctionDischarging2 * 1000; 323 324 if (mV2_2 <= 100 && !discharging2Finished){ 325 accu2IsPluggedIn = false; 326 showNoAccu(14); 327 } else { 328 accu2IsPluggedIn = true; 329 } 330 331 if (!discharging2Finished && accu2IsPluggedIn){ 332 float mAmpere2 = mV2_2 / 5; // 5Ω 333 mAh2 += mAmpere2 * 0.0027777778; 334 float V2_2 = mV2_2 / 1000; 335 336 showDischargingData(14, int(mAh2), V2_2, int(mAmpere2)); 337 338 if (mV2_2 < discharging_mVolt && mV2_2 > 100){ 339 discharging2Finished = true; 340 digitalWrite(relay2Pin, LOW); 341 lcd.setCursor(14, 2); 342 lcd.print("Done"); 343 lcd.setCursor(14, 3); 344 lcd.print(" "); 345 } 346 } 347 348 if ((discharging1Finished || !accu1IsPluggedIn) && (discharging2Finished || !accu2IsPluggedIn)){ 349 lcd.setCursor(0, 1); 350 lcd.print(" "); 351 lcd.setCursor(0, 2); 352 lcd.print(" "); 353 lcd.setCursor(0, 3); 354 lcd.print(" "); 355 lcd.setCursor(0, 3); 356 lcd.print("Press enter to exit"); 357 digitalWrite(relay1Pin, LOW); 358 digitalWrite(relay2Pin, LOW); 359 for (;;){ 360 if (digitalRead(enterButton) == 0){ 361 pinMode(resetPin, OUTPUT); 362 digitalWrite(resetPin, LOW); 363 } 364 delay(50); 365 } 366 } 367 delay(10000); 368 } 369} 370 371void drawMenu(){ 372 mainMenuIsDisplayed = true; 373 lcd.clear(); 374 375 drawCursor(); 376 377 lcd.setCursor(1, 0); 378 lcd.print("Charge"); 379 lcd.setCursor(1, 1); 380 lcd.print("Discharge"); 381 lcd.setCursor(1, 2); 382 lcd.print("Power"); 383 lcd.setCursor(7, 2); 384 lcd.print(String(power) + "0" + "mA"); 385 lcd.setCursor(1, 3); 386 lcd.print("Time"); 387 lcd.setCursor(7, 3); 388 lcd.print(String(hours) + "h"); 389 String showMode; 390 if (EEPROM.read(2) == 0){ 391 showMode = "Auto"; 392 } else { 393 showMode = "Manu"; 394 } 395 lcd.setCursor(11, 0); 396 lcd.print("Mode:" + showMode); 397} 398 399void drawCursor(){ 400 lcd.setCursor(0, 0); 401 lcd.print(" "); 402 lcd.setCursor(0, 1); 403 lcd.print(" "); 404 lcd.setCursor(0, 2); 405 lcd.print(" "); 406 lcd.setCursor(0, 3); 407 lcd.print(" "); 408 lcd.setCursor(10, 0); 409 lcd.print(" "); 410 411 if (cursorPosition == 1){ 412 lcd.setCursor(0, 0); 413 lcd.write(byte(0)); 414 } else if (cursorPosition == 2){ 415 lcd.setCursor(0, 1); 416 lcd.write(byte(0)); 417 } else if (cursorPosition == 3){ 418 lcd.setCursor(0, 2); 419 lcd.write(byte(0)); 420 } else if (cursorPosition == 4){ 421 lcd.setCursor(0, 3); 422 lcd.write(byte(0)); 423 } else if (cursorPosition == 5){ 424 lcd.setCursor(10, 0); 425 lcd.write(byte(0)); 426 } 427} 428 429void upPressed(){ 430 if (mainMenuIsDisplayed){ 431 if (cursorPosition == 1){ 432 cursorPosition = 5; 433 } else { 434 cursorPosition--; 435 } 436 drawCursor(); 437 } 438 439 if (timeIsDisplayed){ 440 if (hours < 99){ 441 hours++; 442 } 443 changeTime(); 444 } 445 446 if (powerIsDisplayed){ 447 if (power < maxPower){ 448 power++; 449 } 450 changePower(); 451 } 452 453 if (modeIsDisplayed){ 454 if(cursorPosition == 1){ 455 cursorPosition++; 456 lcd.setCursor(0, 1); 457 lcd.print(" "); 458 lcd.setCursor(0, 2); 459 lcd.write(byte(0)); 460 } else { 461 cursorPosition--; 462 lcd.setCursor(0, 2); 463 lcd.print(" "); 464 lcd.setCursor(0, 1); 465 lcd.write(byte(0)); 466 } 467 } 468 469 if (changeVoltIsDisplayed){ 470 if (maxVolt < 200){ 471 maxVolt++; 472 lcd.setCursor(10, 0); 473 maxVolt /= 100; 474 lcd.print(String(maxVolt) + "V"); 475 maxVolt *= 100; 476 } 477 } 478} 479 480void downPressed(){ 481 if (mainMenuIsDisplayed){ 482 if (cursorPosition == 5){ 483 cursorPosition = 1; 484 } else { 485 cursorPosition++; 486 } 487 drawCursor(); 488 } 489 490 if (timeIsDisplayed){ 491 if (hours > 1){ 492 hours--; 493 } 494 changeTime(); 495 } 496 497 if (powerIsDisplayed){ 498 if (power > 1){ 499 power--; 500 } 501 changePower(); 502 } 503 504 if (modeIsDisplayed){ 505 if(cursorPosition == 1){ 506 cursorPosition++; 507 lcd.setCursor(0, 1); 508 lcd.print(" "); 509 lcd.setCursor(0, 2); 510 lcd.write(byte(0)); 511 } else { 512 cursorPosition--; 513 lcd.setCursor(0, 2); 514 lcd.print(" "); 515 lcd.setCursor(0, 1); 516 lcd.write(byte(0)); 517 } 518 } 519 520 if (changeVoltIsDisplayed){ 521 if (maxVolt > 100){ 522 //Serial.print("1:"); 523 //Serial.println(maxVolt); 524 maxVolt--; 525 //Serial.println("2:"); 526 //Serial.println(maxVolt); 527 lcd.setCursor(10, 0); 528 maxVolt /= 100; 529 lcd.print(String(maxVolt) + "V"); 530 maxVolt *= 100; 531 } 532 } 533} 534 535 536void enterPressed(){ 537 if (powerIsDisplayed){ 538 powerIsDisplayed = false; 539 drawMenu(); 540 EEPROM.write(1, power); 541 } else if (changeVoltIsDisplayed){ 542 changeVoltIsDisplayed = false; 543 EEPROM.write(3, maxVolt); 544 drawMenu(); 545 } else if (timeIsDisplayed){ 546 timeIsDisplayed = false; 547 drawMenu(); 548 EEPROM.write(0, hours); 549 } else if (modeIsDisplayed){ 550 if(cursorPosition == 1){ 551 modeIsDisplayed = false; 552 changeVoltIsDisplayed = true; 553 EEPROM.write(2, 0); 554 lcd.clear(); 555 showInfo(); 556 lcd.setCursor(0,0); 557 float maxVolt = EEPROM.read(3); 558 maxVolt /= 100; 559 lcd.print("charge to " + String(maxVolt) + "V"); 560 maxVolt *= 100; 561 } else if (cursorPosition == 2){ 562 EEPROM.write(2, 1); 563 modeIsDisplayed = false; 564 cursorPosition = 1; 565 drawMenu(); 566 } 567 } else if (mainMenuIsDisplayed){ 568 if (cursorPosition == 1){ 569 charging(); 570 chargingIsDisplayed = true; 571 } else if(cursorPosition == 2){ 572 discharge(); 573 disChargingIsDisplayed = true; 574 } else if(cursorPosition == 3){ 575 changePower(); 576 powerIsDisplayed = true; 577 } else if(cursorPosition == 4){ 578 changeTime(); 579 timeIsDisplayed = true; 580 } else if(cursorPosition == 5){ 581 changeMode(); 582 modeIsDisplayed = true; 583 } 584 mainMenuIsDisplayed = false; 585 } 586 delay(300); 587} 588 589void changeTime(){ 590 lcd.clear(); 591 lcd.setCursor(0, 0); 592 lcd.print("Charging " + String(hours) + " hours"); 593 showInfo(); 594} 595 596void changePower(){ 597 lcd.clear(); 598 lcd.setCursor(0, 0); 599 lcd.print("Charging " + String(power) + "0" " mA"); 600 showInfo(); 601} 602 603void changeMode(){ 604 lcd.clear(); 605 lcd.setCursor(0, 0); 606 lcd.print("Charging mode:"); 607 lcd.setCursor(0, 1); 608 lcd.write(byte(0)); 609 cursorPosition = 1; 610 maxVolt /= 100; 611 String autoText = "Auto stop at " + String(maxVolt) + "V"; 612 maxVolt *= 100; 613 lcd.setCursor(1, 1); 614 lcd.print(autoText); 615 lcd.setCursor(1, 2); 616 lcd.print("Manually"); 617} 618 619void showInfo(){ 620 lcd.setCursor(0, 1); 621 lcd.print("Up = add"); 622 lcd.setCursor(0, 2); 623 lcd.print("Down = reduce"); 624 lcd.setCursor(0, 3); 625 lcd.print("Enter = save & exit"); 626} 627 628void showNoAccu(int col){ 629 lcd.setCursor(col, 1); 630 lcd.print(" "); 631 lcd.setCursor(col, 1); 632 lcd.print("No"); 633 lcd.setCursor(col, 2); 634 lcd.print("accu"); 635 lcd.setCursor(col, 3); 636 lcd.print(" "); 637} 638 639void showChargingData(int col, float v, int mA){ 640 lcd.setCursor(col, 1); 641 lcd.print(" "); 642 lcd.setCursor(col, 1); 643 lcd.print(String(v)); 644 lcd.setCursor(col, 2); 645 lcd.print(" "); 646 lcd.setCursor(col, 2); 647 lcd.print(String(mA)); 648} 649 650void showDischargingData(int col, int mAh, float v, int mA){ 651 lcd.setCursor(col, 1); 652 lcd.print(" "); 653 lcd.setCursor(col, 1); 654 lcd.print(String(mAh)); 655 lcd.setCursor(col, 2); 656 lcd.print(" "); 657 lcd.setCursor(col, 2); 658 lcd.print(String(v)); 659 lcd.setCursor(col, 3); 660 lcd.print(" "); 661 lcd.setCursor(col, 3); 662 lcd.print(String(mA)); 663}
Downloadable files
Circuit
Here is the circuit for the charger.
circuit.png
Documentation
User manual:
User_manual.pdf
Comments
Only logged in users can leave comments
frenchy22
a year ago
Very good project, based on an original idea. The code is easy to understand and the user manual is very clear. Just a suggestion: all of the ...IsDisplayed booleans and the logic to test them could be replaced with a finite state machine (FSM). The code would be simpler and easier to maintain. For more information on FSMs and Arduino, you can take a look at my project "a user-friendly interface in a simple timer".