Skip to content

Instantly share code, notes, and snippets.

@pinski1
Last active March 21, 2019 16:54
Show Gist options
  • Save pinski1/a8209fc344c81d1bfd27 to your computer and use it in GitHub Desktop.
Save pinski1/a8209fc344c81d1bfd27 to your computer and use it in GitHub Desktop.
eBike Battery Capacity Tester with LTC4151 & LCD Shield
/** 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;
}
@pinski1
Copy link
Author

pinski1 commented Dec 11, 2017

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