Advertisement
KennSoft

1-16 - channel Lithium Battery capacity tester

Jun 6th, 2017
385
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.29 KB | None | 0 0
  1. // Cedric Lemle
  2. //
  3. // 1-16 - channel Lithium Battery capacity tester at: https://www.instructables.com/id/16-Channel-18650-Lithium-Capacity-Tester/
  4. //
  5. // schematic: battery"+" --- resistor pin1 --- resistor pin2 --- mosfet drain --- mosfet source --- battery"-"
  6. // battery"+" to resistor pin1 = measure point "bat(c"
  7. //
  8. // Take care if you use the USB input to power the Arduino. If you do so, you won´t have a constant 5V for
  9. // the internal reference. This will cause wrong measurements. Use the Vin pin instead with an external power
  10. // supply. The onboard 7805 voltage regulator has a constant voltage output. Measure if there are actually 5V!
  11. // If there are, for example, 4.8V on the "5V Pin", and your Arduino measures a Cell with 3.7V, then your
  12. // Arduino will measure only 3.552V ! [ (3.7/5)*4.8 ]. You can adjust the "offsetvoltage" to the value you
  13. // need. For example: if you measure 4.92 V on the "5V Pin", then you have to adjust the offset value to "4920".
  14. //
  15. // I used the internal pull-up resistors to have defined levels. Because of this, you will see "5000mV" in the
  16. // Display if no battery is connected. You can use external pull-down resistors (20-50Kohm) if you want to get
  17. // "0mV" displayed if no battery is connected. In this case you have to rewrite the code.
  18.  
  19. // MJK, Michael Kennedy, May 26, 2017, few notes:
  20. // - Replaced some long-winded constructs with much shorter code, to reduce the source.
  21. // - Now uses #if/#else/#elif/#endif, to reduce the size of the executable.
  22. // - To do: re-check all "time" calcs, to avoid overflows/midnights, etc.
  23. // - To do: re-check all "float" calcs, to avoid inaccurate, internal, integer calcs.
  24. // - To do: see query near the end re BREAKs
  25.  
  26. #define disp 0 // 0 = 16x2 Display 1 = 20x4 Display (I2C)
  27.  
  28. #if (disp == 0)
  29. #include <LiquidCrystal.h> // library for LCD Display
  30. LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Set the LCD address
  31. #endif
  32.  
  33. #if (disp == 1)
  34. #include <Wire.h> // library for I2C Display
  35. #include <LiquidCrystal_I2C.h> // library for I2C Display
  36. LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Set the LCD I2C address
  37. #endif
  38.  
  39. // Values to adjust
  40.  
  41. float batnumber = 16; // adjust between 1-16 to set your number of batteries
  42. float offset = 5013; // offset value for 5V onboard --- type in the voltage you measured at the "5v" pin of your arduino
  43. float cutoffmin = 3000; // Lithium Voltage to stop discharging --- 3000 = 3,000V
  44. float cutoffmax = 4300; // Lithium Voltage to recognize there is no Battery inserted
  45.  
  46. int intervaldisp = 1000; // refresh rate in ms for Display - 1000 is standard
  47. int interval = 1000; // refresh rate in ms for Batteries - 1000 is standard
  48. int intervalserial = 1500; // refresh rate in ms for Serial output - 2500 is standard
  49.  
  50. 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
  51. 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.
  52. 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.
  53. 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.
  54.  
  55. // Values not to change
  56.  
  57. int c = 0; // value for setting Battery Number
  58. int x[17] = {0}; // value for correct calculating
  59. int dispchange = 0; // data for changing displays
  60. boolean done[17] = {false}; // Memory if Battery is done with discharging or not
  61. float totalcurrent[17] = {0.00}; // contents the discharged mAh
  62. float valbat[17] = {0}; // contents the bat voltage
  63. float valshunt[17] = {0}; // contents the shunt voltage
  64. unsigned long premillis[17] = {0}; // for calculating mAh
  65. unsigned long millispassed[17] = {0}; // for calculating mAh
  66. unsigned long currentmillis[18] = {0}; // for refreshing display/batterys
  67. unsigned long previousmillis[18] = {0}; // for refreshing display/batterys
  68. unsigned long start[17] = {0}; // for passed time since discharge began
  69. unsigned long duration[17] = {0}; // for passed time since discharge began
  70. String stat[17] = "wait"; // for saving actual status
  71.  
  72. void setup() {
  73. Serial.begin(9600);
  74.  
  75. for (int p = 0; p < 15; p++) { // declare the inputs/outputs, and set all inputs "HIGH"
  76. pinMode(fet[p], OUTPUT), pinMode(bat[p], INPUT), digitalWrite(bat[p], HIGH);
  77. }
  78.  
  79. #if (disp == 0) // Display = 1602
  80. lcd.begin(16, 2); // set up the LCD's number of rows and columns:
  81. lcd.print(" 1-16 Cell "); // Print a message to the LCD.
  82. lcd.setCursor(0, 1); // Second line first char
  83. lcd.print(" Capacity Tester"); // Print a message to the LCD.
  84. #elif (disp == 1) // Display = 2004
  85. lcd.begin(24, 4); // set up the LCD's number of rows and columns:
  86. lcd.print("********************"); // Print a message to the LCD.
  87. lcd.setCursor(0, 1); // Second line first char
  88. lcd.print(" 1-16 Cell"); // Print a message to the LCD.
  89. lcd.setCursor(0, 2); // Next line first char
  90. lcd.print(" Capacity Tester"); // Print a message to the LCD.
  91. lcd.setCursor(0, 3); // Next line first char
  92. lcd.print("********************");
  93. #endif
  94.  
  95. Serial.print("16 Channel 18650 Discharger");
  96. delay(2000);
  97. lcd.clear(); // clear the Display
  98. }
  99.  
  100. void loop() {
  101.  
  102. for (c = 0; c < 16; c++) { // BAT1-16!
  103. valbat[c] = analogRead(bat[c]); // read the value from the sensor:
  104. valbat[c] = offset / 1024.0 * valbat[c]; // convert 0-1023 to 0-5000 mv
  105. currentmillis[c] = millis(); // save actual ms time in "currentmillis[n]"
  106. if (currentmillis[c] - previousmillis[c] >= interval) { // check what time has passed and compare it with "interval"
  107. previousmillis[c] = currentmillis[c]; // save "currentmillis[n]" in "previousmillis[n]"
  108.  
  109. if ((valbat[c] > cutoffmax) && (valbat[c] < 6000)) { // check if a battery is connected. 5000mV will be displayed if nothing is connected
  110. done[c] = false; // reset "done"
  111. totalcurrent[c] = 0; // reset the counted mAh
  112. digitalWrite(fet[c],LOW); // turn off the mosfet
  113. x[c] = 1; // data storage for correct calculating - otherwise there will be problems with the counted mAh.
  114. stat[c] = "wait"; // status which will be displayed
  115. start[c] = 0; // data storage for time counting. Reset Data for next Battery
  116. duration[c] = 0; // time counting which will be displayed. Reset Data for next battery
  117.  
  118. } else if (valbat[c] > cutoffmin && !done[c]) { // check if battery voltage is higher than the cutoff voltage, and if it's not done
  119. digitalWrite(fet[c], HIGH); // enable FET[n]
  120. valbat[c] = analogRead(bat[c]); // read voltage of the Battery
  121. valbat[c] = offset / 1024.0 * valbat[c]; // convert 0-1023 to 0-5000 mv
  122. premillis[c] = millis(); // save the past time
  123. if (x[c] == 1) { // save premillis in millispassed in the first round. Otherwise a wrong calculation would be the effect
  124. millispassed[c] = premillis[c]; // save premillis in millispassed
  125. }
  126. // calculate the current per passed time
  127. totalcurrent[c] = totalcurrent[c] + (((valbat[c] - fetoffset[c]) * 5000 / offset) / load[c] / 3600 * ((premillis[c] - millispassed[c]) / 1000.0));
  128. millispassed[c] = millis(); // save the past time
  129. x[c] = 0; // x = 0 --> next round "millispassed[c] = premillis[c]; " will be ignored
  130. if (start[c] == 0) { // make sure to subtract passed time till the discharging begins
  131. start[c]= millis(); // save actual time in "start[n]"
  132. }
  133. duration[c] = millis() - start[c]; // save passed time since discharging began
  134. stat[c] = "run "; // write actual status
  135.  
  136. } else {
  137. done[c] = true; // tell the software above the battery is empty
  138. digitalWrite(fet[c],LOW); // turn off the mosfet
  139. stat[c] = "done"; // write actual status
  140. }
  141. }
  142. }
  143.  
  144. // Display output /////////////////////////////////////////////////////////////////////
  145.  
  146. currentmillis[16] = millis(); // save actual time in currentmillis[n]
  147. if (currentmillis[16] - previousmillis[16] >= intervaldisp) { // check what time has passed and compare it with "intervaldisp"
  148. previousmillis[16] = currentmillis[16]; // save "currentmillis[n]" in "previousmillis[n]"
  149.  
  150. #if (disp == 0) //0 = 1602 display //1 = 2004 Display
  151. if (dispchange >= 0) && (dispchange <= 15) { // each site (1-15) of the display
  152. lcd.clear(); // print Bat Data to the Display
  153. 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");
  154. 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]);
  155. }
  156. #elif (disp == 1) //0 = 1602 display //1 = 2004 Display
  157. if (dispchange >= 0) && (dispchange <= 7) { // each site (1-7) of the Display
  158. lcd.clear(); // print Bat Data to the Display
  159. 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");
  160. 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]);
  161. 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");
  162. 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]);
  163. }
  164. #endif
  165.  
  166. // MJK - In the next two FOR-loops, should the BREAK be inside the previous IF condition????????????????????????????????
  167.  
  168. #if (disp == 0) // this is for display changing and set maximum number of Display. If "batnumber = 5 --> bat 1 - bat 5 will be shown
  169. if (batnumber >= 2) {
  170. for (dispchange < 17; dispchange++;) {
  171. if (dispchange >= batnumber) {
  172. dispchange = 0;
  173. }
  174. break;
  175. }
  176. }
  177. #elif (disp == 1) // this is for display changing and set maximum number of Display. If "batnumber = 5 --> bat 1 - bat 6 will be shown
  178. for (dispchange < 9; dispchange++;) {
  179. if (dispchange >= (batnumber/2)) {
  180. dispchange = 0;
  181. }
  182. break;
  183. }
  184. if (batnumber <= 1) {
  185. dispchange = 0;
  186. }
  187. #endif
  188.  
  189. }
  190.  
  191. // Serial output /////////////////////////////////////////////////////////////////////
  192.  
  193. currentmillis[17] = millis(); // save actual time in currentmillis[n]
  194. if (currentmillis[17] - previousmillis[17] >= intervalserial) { // check what time has passed and compare it with "intervaldisp"
  195. previousmillis[17] = currentmillis[17]; // save "currentmillis[n]" in "previousmillis[n]"
  196. for (int a = 0; a < batnumber; a++) {
  197. Serial.print("Bat"), Serial.println(a+1);
  198. Serial.print(valbat[a]), Serial.println("mV");
  199. Serial.print(totalcurrent[a]), Serial.println("mAh");
  200. Serial.println("");
  201. }
  202. }
  203. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement