Last active
March 21, 2019 16:54
-
-
Save pinski1/a8209fc344c81d1bfd27 to your computer and use it in GitHub Desktop.
eBike Battery Capacity Tester with LTC4151 & LCD Shield
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** Battery Characteriser | |
Arduino ??? | |
LTC4151 power monitor | |
Load, 2x paralleled 10Ω 200Watt power resistors | |
DS18B20, 4x temperature sensors | |
Fans, 2x 120mm PWM-controlled PC FANs | |
SD Card, for logged data | |
LCD Display and keypad http://www.hobbytronics.co.uk/arduino-lcd-keypad-shield?keyword=LCD%20Shield | |
*/ | |
#include <SD.h> | |
#include <Wire.h> | |
#include <OneWire.h> | |
#include <DallasTemperature.h> | |
#include <LiquidCrystal.h> | |
// LTC4151 library? | |
/** Settings */ | |
#define BAUD_RATE (115200) | |
#define CELL_VOLTAGE_MAX (4200) // in milliVolts | |
#define CELL_VOLTAGE_MIN (2800) // in milliVolts | |
#define RATE_READ (200) // ms between I2C reads | |
#define RATE_SCREEN (100) // ms between screen updates | |
#define LTC_ADDR (0xD4) | |
/** Masks */ | |
#define BUTTON_NONE (0x00) | |
#define BUTTON_UP (0x01) | |
#define BUTTON_DOWN (0x02) | |
#define BUTTON_LEFT (0x04) | |
#define BUTTON_RIGHT (0x08) | |
#define BUTTON_SEL (0x10) | |
/** Pin Map */ | |
#define PIN_BUTTONS A0 | |
#define PIN_LCD_DB4 4 | |
#define PIN_LCD_DB5 5 | |
#define PIN_LCD_DB6 6 | |
#define PIN_LCD_DB7 7 | |
#define PIN_LCD_RS 8 | |
#define PIN_LCD_EN 9 | |
#define PIN_BACKLIGHT -1 | |
#define PIN_TEMP_SEN -1 | |
#define PIN_FAN_PWM -1 | |
#define PIN_FAN1_TAC -1 | |
#define PIN_FAN2_TAC -1 | |
#define PIN_LOAD_ENB -1 | |
#define PIN_SD_SEL -1 | |
/** Class instantiations */ | |
File logFile; | |
OneWire oneWire(PIN_TEMP_SEN); | |
DallasTemperature temp_sensors(&oneWire); | |
LiquidCrystal lcd(PIN_LCD_RS, PIN_LCD_EN, PIN_LCD_DB4, PIN_LCD_DB5, PIN_LCD_DB6, PIN_LCD_DB7); | |
char fileName[] = "LOGGER00.CSV"; | |
byte get_button(void); | |
// get power | |
// calc temp | |
// enum for LCD menu | |
struct _bat_status { | |
unsigned long timestamp; | |
unsigned long voltage; | |
unsigned long current; | |
bool load_en; | |
int temp[5]; | |
}; | |
void setup(void) { | |
pinMode(PIN_BUTTONS, INPUT); | |
pinMode(PIN_BACKLIGHT, OUTPUT); | |
pinMode(PIN_FAN_PWM, OUTPUT); | |
pinMode(PIN_FAN1_TAC, INPUT); | |
pinMode(PIN_FAN2_TAC, INPUT); | |
pinMode(PIN_LOAD_ENB, OUTPUT); | |
pinMode(PIN_SD_SEL, OUTPUT); | |
analogWrite(PIN_BACKLIGHT, 128); | |
analogWrite(PIN_FAN_PWM, 0); | |
digitalWrite(PIN_LOAD_ENB, LOW); | |
digitalWrite(PIN_SD_SEL, HIGH); | |
Serial.begin(BAUD_RATE); | |
Wire.begin(); | |
lcd.begin(16, 2); | |
lcd.setCursor(0, 0); | |
temp_sensors.begin(); | |
} | |
void loop(void) { | |
// put your main code here, to run repeatedly: | |
} | |
/** Reads keypad */ | |
byte get_button(void) { | |
byte button_pressed = BUTTON_NONE; | |
unsigned int ana_value = analogRead(PIN_BUTTONS); | |
if (ana_value < 60) button_pressed = BUTTON_RIGHT; | |
else if (ana_value < 200) button_pressed = BUTTON_UP; | |
else if (ana_value < 400) button_pressed = BUTTON_DOWN; | |
else if (ana_value < 600) button_pressed = BUTTON_LEFT; | |
else if (ana_value < 800) button_pressed = BUTTON_SEL; | |
return button_pressed; | |
} | |
/** Calculate temperature | |
Maths taken from here: https://learn.adafruit.com/thermistor/using-a-thermistor | |
@param voltage The read voltage in microvolts | |
@returns The temperature in °C as a float | |
*/ | |
float calc_temp(unsigned int voltage) { | |
const float beta = 4390.0; // https://uk.farnell.com/panasonic-electronic-components/ertj1vs104fa/thermistor-ntc-0603-100k/dp/1892612 | |
const float nom = 100000.0; // 100kΩ at 25°C | |
const float rtc = (25.0 + 273.15); | |
float steinhart = 0.0; | |
steinhart = (voltage / 10000000.0) / nom; | |
steinhart = log(steinhart); | |
steinhart /= beta; | |
steinhart += 1.0 / rtc; | |
steinhart = 1.0 / steinhart; | |
steinhart -= 273.15; // convert to °C | |
return steinhart; | |
} | |
/** read LTC4151 */ | |
struct _bat_status get_power(void) { | |
const int a_lsb_to_uv = (10000); // 20uV per LSB & 2mΩ | |
const int v_lsb_to_uv = (25000); // 25mV per LSB | |
const int t_lsb_to_uv = (500); // 500uV per LSB | |
byte data_buffer[6]; | |
byte com_status; | |
struct _bat_status battery_status; | |
battery_status.timestamp = millis(); | |
battery_status.load_en = (digitalRead(PIN_LOAD_ENB) == HIGH) ? true : false; | |
// read registers | |
Wire.beginTransmission(LTC_ADDR); | |
Wire.write(0x00); | |
com_status = Wire.endTransmission(false); | |
Wire.requestFrom(LTC_ADDR, 6, true); | |
for (int i = 0; i < 6 && Wire.available(); i++) | |
{ | |
data_buffer[i++] = Wire.read(); | |
} | |
battery_status.current = (data_buffer[1] << 8) | data_buffer[0] & 0x0FFF; | |
battery_status.current *= a_lsb_to_uv; | |
battery_status.voltage = (data_buffer[3] << 8) | data_buffer[2] & 0x0FFF; | |
battery_status.voltage *= v_lsb_to_uv; | |
battery_status.temp[0] = (data_buffer[5] << 8) | data_buffer[4] & 0x0FFF; | |
battery_status.temp[0] *= t_lsb_to_uv; | |
battery_status.temp[0] = int(calc_temp(battery_status.temp[0])); | |
// read DS18B20 sensors | |
temp_sensors.requestTemperatures(); | |
byte num_sensors = temp_sensors.getDeviceCount(); | |
for (int i = 0; i < 4; i++) | |
{ | |
if(i < num_sensors) battery_status.temp[1 + i] = temp_sensors.getTempCByIndex(i); | |
else battery_status.temp[1 + i] = -300; | |
} | |
return battery_status; | |
} |
Further To do:
- Add DS18B20 code to allow for load temperature monitoring
- Add code for over temperature shutdown
- Add 2x16 LCD display for detailed headless/PCless feedback
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
To do:
Run capacity calculations during programNotes: