Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Cedric Lemle
- //
- // 1-16 - channel Lithium Battery capacity tester at: https://www.instructables.com/id/16-Channel-18650-Lithium-Capacity-Tester/
- //
- // schematic: battery"+" --- resistor pin1 --- resistor pin2 --- mosfet drain --- mosfet source --- battery"-"
- // battery"+" to resistor pin1 = measure point "bat(c"
- //
- // Take care if you use the USB input to power the Arduino. If you do so, you won´t have a constant 5V for
- // the internal reference. This will cause wrong measurements. Use the Vin pin instead with an external power
- // supply. The onboard 7805 voltage regulator has a constant voltage output. Measure if there are actually 5V!
- // If there are, for example, 4.8V on the "5V Pin", and your Arduino measures a Cell with 3.7V, then your
- // Arduino will measure only 3.552V ! [ (3.7/5)*4.8 ]. You can adjust the "offsetvoltage" to the value you
- // need. For example: if you measure 4.92 V on the "5V Pin", then you have to adjust the offset value to "4920".
- //
- // I used the internal pull-up resistors to have defined levels. Because of this, you will see "5000mV" in the
- // Display if no battery is connected. You can use external pull-down resistors (20-50Kohm) if you want to get
- // "0mV" displayed if no battery is connected. In this case you have to rewrite the code.
- // MJK, Michael Kennedy, May 26, 2017, few notes:
- // - Replaced some long-winded constructs with much shorter code, to reduce the source.
- // - Now uses #if/#else/#elif/#endif, to reduce the size of the executable.
- // - To do: re-check all "time" calcs, to avoid overflows/midnights, etc.
- // - To do: re-check all "float" calcs, to avoid inaccurate, internal, integer calcs.
- // - To do: see query near the end re BREAKs
- #define disp 0 // 0 = 16x2 Display 1 = 20x4 Display (I2C)
- #if (disp == 0)
- #include <LiquidCrystal.h> // library for LCD Display
- LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Set the LCD address
- #endif
- #if (disp == 1)
- #include <Wire.h> // library for I2C Display
- #include <LiquidCrystal_I2C.h> // library for I2C Display
- LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
- #endif
- // Values to adjust
- float batnumber = 16; // adjust between 1-16 to set your number of batteries
- float offset = 5013; // offset value for 5V onboard --- type in the voltage you measured at the "5v" pin of your arduino
- float cutoffmin = 3000; // Lithium Voltage to stop discharging --- 3000 = 3,000V
- float cutoffmax = 4300; // Lithium Voltage to recognize there is no Battery inserted
- int intervaldisp = 1000; // refresh rate in ms for Display - 1000 is standard
- int interval = 1000; // refresh rate in ms for Batteries - 1000 is standard
- int intervalserial = 1500; // refresh rate in ms for Serial output - 2500 is standard
- int fetoffset[17] = {16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16}; // voltage measured at "SOURCE - DRAIN" --> first number = Fet1, second number = Fet2
- int fet[17] = {22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52}; // output pins for your mosfets, 22 = Bat1, 24 = Bat2, etc.
- int bat[17] = {A0,A1,A2,A3,A4,A5,A6,A7,A8,A9,A10,A11,A12,A13,A14,A15}; // input pins for Measuring Bat voltage, A0 = Bat1, A1 = Bat2, etc.
- float load[17] = {1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6,1.6,1,1,1,1,1,1,}; // Values of your Load in "ohm" --- use "." instead of "," for a point number --> first number = Bat1, second number = Bat2, etc.
- // Values not to change
- int c = 0; // value for setting Battery Number
- int x[17] = {0}; // value for correct calculating
- int dispchange = 0; // data for changing displays
- boolean done[17] = {false}; // Memory if Battery is done with discharging or not
- float totalcurrent[17] = {0.00}; // contents the discharged mAh
- float valbat[17] = {0}; // contents the bat voltage
- float valshunt[17] = {0}; // contents the shunt voltage
- unsigned long premillis[17] = {0}; // for calculating mAh
- unsigned long millispassed[17] = {0}; // for calculating mAh
- unsigned long currentmillis[18] = {0}; // for refreshing display/batterys
- unsigned long previousmillis[18] = {0}; // for refreshing display/batterys
- unsigned long start[17] = {0}; // for passed time since discharge began
- unsigned long duration[17] = {0}; // for passed time since discharge began
- String stat[17] = "wait"; // for saving actual status
- void setup() {
- Serial.begin(9600);
- for (int p = 0; p < 15; p++) { // declare the inputs/outputs, and set all inputs "HIGH"
- pinMode(fet[p], OUTPUT), pinMode(bat[p], INPUT), digitalWrite(bat[p], HIGH);
- }
- #if (disp == 0) // Display = 1602
- lcd.begin(16, 2); // set up the LCD's number of rows and columns:
- lcd.print(" 1-16 Cell "); // Print a message to the LCD.
- lcd.setCursor(0, 1); // Second line first char
- lcd.print(" Capacity Tester"); // Print a message to the LCD.
- #elif (disp == 1) // Display = 2004
- lcd.begin(24, 4); // set up the LCD's number of rows and columns:
- lcd.print("********************"); // Print a message to the LCD.
- lcd.setCursor(0, 1); // Second line first char
- lcd.print(" 1-16 Cell"); // Print a message to the LCD.
- lcd.setCursor(0, 2); // Next line first char
- lcd.print(" Capacity Tester"); // Print a message to the LCD.
- lcd.setCursor(0, 3); // Next line first char
- lcd.print("********************");
- #endif
- Serial.print("16 Channel 18650 Discharger");
- delay(2000);
- lcd.clear(); // clear the Display
- }
- void loop() {
- for (c = 0; c < 16; c++) { // BAT1-16!
- valbat[c] = analogRead(bat[c]); // read the value from the sensor:
- valbat[c] = offset / 1024.0 * valbat[c]; // convert 0-1023 to 0-5000 mv
- currentmillis[c] = millis(); // save actual ms time in "currentmillis[n]"
- if (currentmillis[c] - previousmillis[c] >= interval) { // check what time has passed and compare it with "interval"
- previousmillis[c] = currentmillis[c]; // save "currentmillis[n]" in "previousmillis[n]"
- if ((valbat[c] > cutoffmax) && (valbat[c] < 6000)) { // check if a battery is connected. 5000mV will be displayed if nothing is connected
- done[c] = false; // reset "done"
- totalcurrent[c] = 0; // reset the counted mAh
- digitalWrite(fet[c],LOW); // turn off the mosfet
- x[c] = 1; // data storage for correct calculating - otherwise there will be problems with the counted mAh.
- stat[c] = "wait"; // status which will be displayed
- start[c] = 0; // data storage for time counting. Reset Data for next Battery
- duration[c] = 0; // time counting which will be displayed. Reset Data for next battery
- } else if (valbat[c] > cutoffmin && !done[c]) { // check if battery voltage is higher than the cutoff voltage, and if it's not done
- digitalWrite(fet[c], HIGH); // enable FET[n]
- valbat[c] = analogRead(bat[c]); // read voltage of the Battery
- valbat[c] = offset / 1024.0 * valbat[c]; // convert 0-1023 to 0-5000 mv
- premillis[c] = millis(); // save the past time
- if (x[c] == 1) { // save premillis in millispassed in the first round. Otherwise a wrong calculation would be the effect
- millispassed[c] = premillis[c]; // save premillis in millispassed
- }
- // calculate the current per passed time
- totalcurrent[c] = totalcurrent[c] + (((valbat[c] - fetoffset[c]) * 5000 / offset) / load[c] / 3600 * ((premillis[c] - millispassed[c]) / 1000.0));
- millispassed[c] = millis(); // save the past time
- x[c] = 0; // x = 0 --> next round "millispassed[c] = premillis[c]; " will be ignored
- if (start[c] == 0) { // make sure to subtract passed time till the discharging begins
- start[c]= millis(); // save actual time in "start[n]"
- }
- duration[c] = millis() - start[c]; // save passed time since discharging began
- stat[c] = "run "; // write actual status
- } else {
- done[c] = true; // tell the software above the battery is empty
- digitalWrite(fet[c],LOW); // turn off the mosfet
- stat[c] = "done"; // write actual status
- }
- }
- }
- // Display output /////////////////////////////////////////////////////////////////////
- currentmillis[16] = millis(); // save actual time in currentmillis[n]
- if (currentmillis[16] - previousmillis[16] >= intervaldisp) { // check what time has passed and compare it with "intervaldisp"
- previousmillis[16] = currentmillis[16]; // save "currentmillis[n]" in "previousmillis[n]"
- #if (disp == 0) //0 = 1602 display //1 = 2004 Display
- if (dispchange >= 0) && (dispchange <= 15) { // each site (1-15) of the display
- lcd.clear(); // print Bat Data to the Display
- lcd.setCursor(0, 0), lcd.print("%02d=",dispchange), lcd.print(totalcurrent[dispchange], 0), lcd.setCursor(7, 0), lcd.print("mAh"), lcd.setCursor(11, 0), lcd.print(duration[dispchange]/1000), lcd.setCursor(dispchange, 0), lcd.print("s");
- lcd.setCursor(0, 1), lcd.print("%02d=",dispchange), lcd.print(valbat[dispchange], 0), lcd.setCursor(7, 1), lcd.print("mV"), lcd.setCursor(11, 1), lcd.print(stat[dispchange]);
- }
- #elif (disp == 1) //0 = 1602 display //1 = 2004 Display
- if (dispchange >= 0) && (dispchange <= 7) { // each site (1-7) of the Display
- lcd.clear(); // print Bat Data to the Display
- lcd.setCursor(0, 0), lcd.print("%02d=",dispchange+dispchange+1), lcd.print(totalcurrent[dispchange+dispchange+0], 0), lcd.setCursor(7, 0), lcd.print("mAh"), lcd.setCursor(13, 0), lcd.print(duration[dispchange+dispchange+0]/1000), lcd.setCursor(17, 0), lcd.print("sec");
- lcd.setCursor(0, 1), lcd.print("%02d=",dispchange+dispchange+1), lcd.print(valbat[dispchange+dispchange+0], 0), lcd.setCursor(7, 1), lcd.print("mV"), lcd.setCursor(13, 1), lcd.print(stat[dispchange+dispchange+0]);
- lcd.setCursor(0, 2), lcd.print("%02d=",dispchange+dispchange+2), lcd.print(totalcurrent[dispchange+dispchange+1], 0), lcd.setCursor(7, 2), lcd.print("mAh"), lcd.setCursor(13, 2), lcd.print(duration[dispchange+dispchange+1]/1000), lcd.setCursor(17, 2), lcd.print("sec");
- lcd.setCursor(0, 3), lcd.print("%02d=",dispchange+dispchange+2), lcd.print(valbat[dispchange+dispchange+1], 0), lcd.setCursor(7, 3), lcd.print("mV"), lcd.setCursor(13, 3), lcd.print(stat[dispchange+dispchange+1]);
- }
- #endif
- // MJK - In the next two FOR-loops, should the BREAK be inside the previous IF condition????????????????????????????????
- #if (disp == 0) // this is for display changing and set maximum number of Display. If "batnumber = 5 --> bat 1 - bat 5 will be shown
- if (batnumber >= 2) {
- for (dispchange < 17; dispchange++;) {
- if (dispchange >= batnumber) {
- dispchange = 0;
- }
- break;
- }
- }
- #elif (disp == 1) // this is for display changing and set maximum number of Display. If "batnumber = 5 --> bat 1 - bat 6 will be shown
- for (dispchange < 9; dispchange++;) {
- if (dispchange >= (batnumber/2)) {
- dispchange = 0;
- }
- break;
- }
- if (batnumber <= 1) {
- dispchange = 0;
- }
- #endif
- }
- // Serial output /////////////////////////////////////////////////////////////////////
- currentmillis[17] = millis(); // save actual time in currentmillis[n]
- if (currentmillis[17] - previousmillis[17] >= intervalserial) { // check what time has passed and compare it with "intervaldisp"
- previousmillis[17] = currentmillis[17]; // save "currentmillis[n]" in "previousmillis[n]"
- for (int a = 0; a < batnumber; a++) {
- Serial.print("Bat"), Serial.println(a+1);
- Serial.print(valbat[a]), Serial.println("mV");
- Serial.print(totalcurrent[a]), Serial.println("mAh");
- Serial.println("");
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement