|
// Arduino Lithium-ion load / capacity tester. |
|
// Copyright 2022 Jason Pepas. |
|
// Released under the terms of the MIT License, see https://opensource.org/licenses/MIT |
|
|
|
#include <stdint.h> |
|
#include "types.h" |
|
|
|
#define _READ_ADC_H_IMPLEMENTATION_ |
|
#include "read_adc.h" |
|
|
|
volts_t g_fullscale_v = 4.65f; |
|
volts_t g_cutoff_v = 2.5f; // Terminate the test at this voltage. |
|
|
|
// Pin assignments: |
|
pin_t g_pwm_pin = 5; |
|
pin_t g_rsense_pin = A0; |
|
pin_t g_collector_pin = A10; |
|
pin_t g_vcc_pin = A3; |
|
|
|
pwm_t milliamps_to_pwm(milliamps_t milliamps) { |
|
// PWM table: |
|
// 10: 58 mA |
|
// 20: 119 mA |
|
// 30: 180 mA |
|
// 40: 241 mA |
|
if (milliamps <= 58) { |
|
return milliamps / 58.0 * 10.0; |
|
} else if (milliamps <= 119) { |
|
return milliamps / 119.0 * 20.0; |
|
} else if (milliamps <= 180) { |
|
return milliamps / 180.0 * 30.0; |
|
} else if (milliamps <= 241) { |
|
return milliamps / 241.0 * 40.0; |
|
// 50: 302 mA |
|
// 60: 363 mA |
|
// 70: 424 mA |
|
// 80: 485 mA |
|
// 90: 546 mA |
|
} else if (milliamps <= 302) { |
|
return milliamps / 302.0 * 50.0; |
|
} else if (milliamps <= 363) { |
|
return milliamps / 363.0 * 60.0; |
|
} else if (milliamps <= 424) { |
|
return milliamps / 424.0 * 70.0; |
|
} else if (milliamps <= 485) { |
|
return milliamps / 485.0 * 80.0; |
|
} else if (milliamps <= 546) { |
|
return milliamps / 546.0 * 90.0; |
|
// 100: 607 mA |
|
// 110: 668 mA |
|
// 120: 728 mA |
|
// 130: 789 mA |
|
// 140: 849 mA |
|
} else if (milliamps <= 607) { |
|
return milliamps / 607.0 * 100.0; |
|
} else if (milliamps <= 668) { |
|
return milliamps / 668.0 * 110.0; |
|
} else if (milliamps <= 728) { |
|
return milliamps / 728.0 * 120.0; |
|
} else if (milliamps <= 789) { |
|
return milliamps / 789.0 * 130.0; |
|
} else if (milliamps <= 849) { |
|
return milliamps / 849.0 * 140.0; |
|
// 150: 910 mA |
|
// 160: 970 mA |
|
// 170: 1030 mA |
|
// 180: 1090 mA |
|
// 190: 1150 mA |
|
} else if (milliamps <= 910) { |
|
return milliamps / 910.0 * 150.0; |
|
} else if (milliamps <= 970) { |
|
return milliamps / 970.0 * 160.0; |
|
} else if (milliamps <= 1030) { |
|
return milliamps / 1030.0 * 170.0; |
|
} else if (milliamps <= 1090) { |
|
return milliamps / 1090.0 * 180.0; |
|
} else if (milliamps <= 1150) { |
|
return milliamps / 1150.0 * 190.0; |
|
// 200: 1211 mA |
|
// 210: 1271 mA |
|
// 220: 1329 mA |
|
// 230: 1389 mA (oscillation) |
|
// 240: 1445 mA (oscillation) |
|
// 250: 1496 mA (oscillation) |
|
} else if (milliamps <= 1211) { |
|
return milliamps / 1211.0 * 200.0; |
|
} else if (milliamps <= 1271) { |
|
return milliamps / 1271.0 * 210.0; |
|
} else if (milliamps <= 1329) { |
|
return milliamps / 1329.0 * 220.0; |
|
} else { |
|
return 0; |
|
} |
|
} |
|
|
|
void send_end_of_output() { |
|
// send an empty line to signal "end-of-output". |
|
Serial.print("\n"); |
|
} |
|
|
|
void log_battery_capacity(milliamps_t milliamps) { |
|
uint32_t second = 0; |
|
|
|
Serial.print("seconds,v_batt,v_rsense\n"); |
|
analogWrite(g_pwm_pin, milliamps_to_pwm(milliamps)); |
|
delay(100); |
|
|
|
while (true) { |
|
while (millis() % 1000 > 3) { |
|
delay(1); |
|
} |
|
second += 1; |
|
|
|
volts_t v_batt = read_adc_f(g_collector_pin, OVERSAMPLE_64x) / 1023.0f * g_fullscale_v; |
|
volts_t v_rsense = read_adc_f(g_rsense_pin, OVERSAMPLE_64x) / 1023.0f * g_fullscale_v; |
|
|
|
if (Serial.available() && Serial.read() == '.') { |
|
analogWrite(g_pwm_pin, 0); |
|
send_end_of_output(); |
|
break; |
|
} |
|
|
|
Serial.print(second); |
|
Serial.print(","); |
|
Serial.print(v_batt, 3); |
|
Serial.print(","); |
|
Serial.print(v_rsense, 3); |
|
Serial.print("\n"); |
|
|
|
if (v_batt < g_cutoff_v) { |
|
analogWrite(g_pwm_pin, 0); |
|
send_end_of_output(); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
void setup() { |
|
pinMode(g_pwm_pin, OUTPUT); |
|
pinMode(g_rsense_pin, INPUT); |
|
pinMode(g_collector_pin, INPUT); |
|
pinMode(g_vcc_pin, INPUT); |
|
|
|
Serial.begin(115200); |
|
while (!Serial) { |
|
; // Wait for serial port to connect. Needed for native USB port only. |
|
} |
|
Serial.print("\n"); |
|
Serial.print("Li-ion capacity logger.\n"); |
|
Serial.print("Single-character commands:\n"); |
|
Serial.print("'0': start a test at 1000mA.\n"); |
|
Serial.print("'6': start a test at 667mA.\n"); |
|
Serial.print("'5': start a test at 500mA.\n"); |
|
Serial.print("'3': start a test at 333mA.\n"); |
|
Serial.print("'2': start a test at 250mA.\n"); |
|
Serial.print("'1': start a test at 100mA.\n"); |
|
Serial.print("'.': stop the test.\n"); |
|
Serial.print("Empty line signals end-of-output.\n"); |
|
} |
|
|
|
void loop() { |
|
if (Serial.available()) { |
|
char ch = Serial.read(); |
|
if (ch == '0') { |
|
log_battery_capacity(1000); |
|
} else if (ch == '6') { |
|
log_battery_capacity(667); |
|
} else if (ch == '5') { |
|
log_battery_capacity(500); |
|
} else if (ch == '3') { |
|
log_battery_capacity(333); |
|
} else if (ch == '2') { |
|
log_battery_capacity(250); |
|
} else if (ch == '1') { |
|
log_battery_capacity(100); |
|
} |
|
} |
|
} |
|
|
|
void calibration_wip() { |
|
// int ms = 8000; |
|
// for (int i=0; i < 100; i += 10) { |
|
// analogWrite(g_pwm_pin, i); delay(ms); |
|
// } |
|
// analogWrite(g_pwm_pin, 0); delay(ms); |
|
// for (int i=100; i < 200; i += 10) { |
|
// analogWrite(g_pwm_pin, i); delay(ms); |
|
// } |
|
// analogWrite(g_pwm_pin, 0); delay(ms); |
|
// for (int i=200; i <= 255; i += 10) { |
|
// analogWrite(g_pwm_pin, i); delay(ms); |
|
// } |
|
|
|
// int ms = 3000; |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(0)); |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(100)); // 95 mA |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(250)); // 247 mA |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(500)); // 496 mA |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(750)); // 746 mA |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(1000)); // 1001 mA |
|
// delay(ms); |
|
// analogWrite(g_pwm_pin, milliamps_to_pwm(1250)); // 1249 mA |
|
// delay(ms); |
|
} |
|
|
|
|
|
// TODO: |
|
// - use voltage divider to measure Vcc. |
|
// - figure out why serial print causes pwm voltage to slightly dip |