Battery Measure Device With Low Voltage Alarm
by DIMITROPOYLOSS in Circuits > Arduino
45 Views, 0 Favorites, 0 Comments
Battery Measure Device With Low Voltage Alarm
A Note to the Community / Friendly Request:
"This is one of my first detailed project guides! I've put a lot of effort and testing into making this tester as accurate, safe, and useful as possible. However, English is not my native language and there might still be minor typos, wiring oversights, or code bugs.
If you notice anything that needs fixing or have ideas for improvements, please be gentle in the comments! Constructive feedback and suggestions are always welcome.
Hello, this device measures battery voltage. It has 3 terminals. 3 buttons (menu, up, down), an RGB LED, and a buzzer.
- Main terminal. Here u power up the device using a 2s or a 12v battery; the code measures the battery voltage automatically
- 1-5volt terminal (use down button to see the measurment). Here u can measure batteries from 1 to 4.2 volts. (The device must have power from the main terminal.
- Current terminal (250watt max): here u can measure the current of a device, 1- 15 volts only
BUTTONS
- MENU
(8.4 & 12v) Main screen > set warning> set alarm >fine tunning voltage> fine tunning current.
- UP
Shows the warning & alert values
- DOWN
Change to 1-5v screen (use the Menu button for fine tuning)
The buzzer beeps when the alarm is triggered.
Supplies
1 x box to put everything inside (nnnnnn.stl)
1× Arduino Nano
1× LCD 16x2 with i2c
1 x mp1584
1x ACS712
3 x push buttons
1 x RGB LED with 3x 220 ohm resistors, 1 for each color
1 X Buzzer
1 x 10k ohm resistor
1 x 4.6k ohm resistor
1 x 1k ohm resistor
1 x zener 5.1v diode
1 x diode more (i use one sb3100)
1 x breadboard
2 x alligator clips
a bunch of cables
MAIN CABLE INFO
First of all set the mp1584 to 5volts .
Connect the in (+) to a 12V battery + and the in(-) to the battery - then use a multimeter and a small screwdriver to set the output voltage to 5 volt
main terminal cable (see the sketch)
Use 2 wires 1 red(+) and 1 black(-) put 1 alligator clip each (match the colors) ,connect a diode (i use a sb3100) to the red wire (the silver line goes to the mp1584 side)
then connect to red(+) wire the 10k ohm R and the 4.6 k ohm R to black(-) then connect together the other sides of the 10 and 4.6 R to A0.
connect the main cable to mp1584 + to in(+)and - to in(-) then connect the mp1584 out(+) to arduino nano 5v and the mp1584 out(-) to aruino nano gnd
so we have
Main cable red wire to mp1584 in+
Main cable black wire to mp1584 in-
the free resistor sides of the (10 & 4.6) together to A0
mp1584 out+ to nano 5v
mp1584 out- to nano gnd
CONNECTIONS
lcd 16x2 with i2c
(If the screen shows nothing use the potentiometer in the back of the chip to adjust the light or change the address in the code from 0x27 to 0x3F.)
5v to nano 5v
gnd to nano gnd
scl to nano A5
sda to nano A4
Buzzer + to nano D9
Buzzer - to nano GND
RGB led 4 legs R,G,B,COMMON
Connect the common to Nano GND
connect 1x 220 resistor to each leg
R goes to nano D3
G goes to nano D5
B goes to nano D6
Buttons
connect the three button
menu button to nano D2
up button to nano D4
down button to nano D7
all gnds to nano gnd
ACS712
VCC to Nano 5V
OUT to Nano A1
GND to Nano GND
secondary cable for 1 to 5 volt batteries (see sketch)
Connect the 1k resistor in series with the red cable to A3. Connect the Zener diode between A3 and GND (Cathode/Line side to A3, Anode side to GND).
the secondary cables goes like
red cable (with 1 k resistor) goes to terminal the zener side goes to A3
black cable goes from terminal to nano gnd
THE CODE
Here is the code (the code is made with the help of Gemini)
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int VOLT_PIN = A0;
const int CURR_PIN = A1;
const int LOW_V_PIN = A3;
const int RED_PIN = 3;
const int GREEN_PIN = 5;
const int BLUE_PIN = 6;
const int BUZZER_PIN = 9;
const int BTN_MENU = 2;
const int BTN_UP = 4;
const int BTN_DOWN = 7; // Κουμπί εναλλαγής εισόδου A0 <-> A3
const float R1 = 10040.0;
const float R2 = 4608.0;
const float ACS_SENSITIVITY = 0.066;
float warnVoltage12V = 12.00;
float alarmVoltage12V = 11.50;
float vCal12V = 1.0364;
float warnVoltage2S = 7.20;
float alarmVoltage2S = 6.60;
float vCal2S = 1.0364;
float vCalA3 = 1.0000;
float vCal1V1 = 1.5000;
float maxWatts = 250.0;
float cCal = 1.4964;
float smoothCurrent = 0.0;
float smoothVoltage = 0.0;
float smoothLowV = 0.0;
bool is2SMode = false;
bool manualA3Mode = false;
bool isHighPrecision1V1 = false;
enum Mode { MAIN_VIEW, SHOW_LIMITS, SET_WARN, SET_ALARM, CALIB_VOLT, CALIB_CURR };
Mode currentMode = MAIN_VIEW;
unsigned long lastDebounce = 0;
void setup() {
pinMode(RED_PIN, OUTPUT);
pinMode(GREEN_PIN, OUTPUT);
pinMode(BLUE_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BTN_MENU, INPUT_PULLUP);
pinMode(BTN_UP, INPUT_PULLUP);
pinMode(BTN_DOWN, INPUT_PULLUP);
lcd.init();
lcd.backlight();
float savedWarn12, savedAlarm12, savedVCal12, savedCCal;
float savedWarn2S, savedAlarm2S, savedVCal2S, savedVCalA3, savedVCal1V1;
EEPROM.get(0, savedWarn12);
EEPROM.get(4, savedAlarm12);
EEPROM.get(8, savedVCal12);
EEPROM.get(12, savedCCal);
EEPROM.get(16, savedWarn2S);
EEPROM.get(20, savedAlarm2S);
EEPROM.get(24, savedVCal2S);
EEPROM.get(28, savedVCalA3);
EEPROM.get(32, savedVCal1V1);
if (!isnan(savedWarn12) && savedWarn12 > 5.0 && savedWarn12 < 20.0) warnVoltage12V = savedWarn12;
if (!isnan(savedAlarm12) && savedAlarm12 > 5.0 && savedAlarm12 < 20.0) alarmVoltage12V = savedAlarm12;
if (!isnan(savedVCal12) && savedVCal12 > 0.5 && savedVCal12 < 2.0) vCal12V = savedVCal12;
if (!isnan(savedCCal) && savedCCal > 0.5 && savedCCal < 3.0) cCal = savedCCal;
if (!isnan(savedWarn2S) && savedWarn2S > 4.0 && savedWarn2S < 10.0) warnVoltage2S = savedWarn2S;
if (!isnan(savedAlarm2S) && savedAlarm2S > 4.0 && savedAlarm2S < 10.0) alarmVoltage2S = savedAlarm2S;
if (!isnan(savedVCal2S) && savedVCal2S > 0.5 && savedVCal2S < 2.0) vCal2S = savedVCal2S;
if (!isnan(savedVCalA3) && savedVCalA3 > 0.5 && savedVCalA3 < 2.0) vCalA3 = savedVCalA3;
if (!isnan(savedVCal1V1) && savedVCal1V1 >= 0.8000 && savedVCal1V1 <= 2.0000) {
vCal1V1 = savedVCal1V1;
} else {
vCal1V1 = 1.5000;
}
lcd.setCursor(0, 0);
lcd.print(" Multi-Bat Tester");
lcd.setCursor(0, 1);
lcd.print(" Press DOWN for A3");
delay(1500);
lcd.clear();
}
void setColor(int red, int green, int blue) {
analogWrite(RED_PIN, red);
analogWrite(GREEN_PIN, green);
analogWrite(BLUE_PIN, blue);
}
void handleButtons() {
if (millis() - lastDebounce < 250) return;
// Menu Button
if (digitalRead(BTN_MENU) == LOW) {
lastDebounce = millis();
if (currentMode == MAIN_VIEW || currentMode == SHOW_LIMITS) {
currentMode = SET_WARN;
} else if (currentMode == SET_WARN) {
currentMode = SET_ALARM;
} else if (currentMode == SET_ALARM) {
currentMode = CALIB_VOLT;
} else if (currentMode == CALIB_VOLT) {
currentMode = CALIB_CURR;
} else if (currentMode == CALIB_CURR) {
EEPROM.put(0, warnVoltage12V);
EEPROM.put(4, alarmVoltage12V);
EEPROM.put(8, vCal12V);
EEPROM.put(12, cCal);
EEPROM.put(16, warnVoltage2S);
EEPROM.put(20, alarmVoltage2S);
EEPROM.put(24, vCal2S);
EEPROM.put(28, vCalA3);
EEPROM.put(32, vCal1V1);
lcd.clear();
lcd.setCursor(0, 0); lcd.print(" ALL SETTINGS ");
lcd.setCursor(0, 1); lcd.print(" SAVED TO ROM ");
delay(1200);
lcd.clear();
currentMode = MAIN_VIEW;
}
}
if (currentMode == MAIN_VIEW) {
if (digitalRead(BTN_DOWN) == LOW) {
lastDebounce = millis();
manualA3Mode = !manualA3Mode; // Toggle A3 Mode
lcd.clear();
}
else if (digitalRead(BTN_UP) == LOW) {
lastDebounce = millis();
currentMode = SHOW_LIMITS;
lcd.clear();
}
}
else if (currentMode == SHOW_LIMITS) {
if (digitalRead(BTN_UP) == LOW || digitalRead(BTN_DOWN) == LOW) {
lastDebounce = millis();
currentMode = MAIN_VIEW;
lcd.clear();
}
}
else if (currentMode == SET_WARN) {
if (!manualA3Mode) {
if (is2SMode) {
if (digitalRead(BTN_UP) == LOW) { warnVoltage2S += 0.05; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { warnVoltage2S -= 0.05; lastDebounce = millis(); }
} else {
if (digitalRead(BTN_UP) == LOW) { warnVoltage12V += 0.10; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { warnVoltage12V -= 0.10; lastDebounce = millis(); }
}
}
}
else if (currentMode == SET_ALARM) {
if (!manualA3Mode) {
if (is2SMode) {
if (digitalRead(BTN_UP) == LOW) { alarmVoltage2S += 0.05; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { alarmVoltage2S -= 0.05; lastDebounce = millis(); }
} else {
if (digitalRead(BTN_UP) == LOW) { alarmVoltage12V += 0.10; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { alarmVoltage12V -= 0.10; lastDebounce = millis(); }
}
}
}
else if (currentMode == CALIB_VOLT) {
if (manualA3Mode) {
if (isHighPrecision1V1) {
if (digitalRead(BTN_UP) == LOW) { vCal1V1 += 0.005; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { vCal1V1 -= 0.005; lastDebounce = millis(); }
} else {
if (digitalRead(BTN_UP) == LOW) { vCalA3 += 0.001; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { vCalA3 -= 0.001; lastDebounce = millis(); }
}
} else if (is2SMode) {
if (digitalRead(BTN_UP) == LOW) { vCal2S += 0.001; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { vCal2S -= 0.001; lastDebounce = millis(); }
} else {
if (digitalRead(BTN_UP) == LOW) { vCal12V += 0.001; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { vCal12V -= 0.001; lastDebounce = millis(); }
}
}
else if (currentMode == CALIB_CURR) {
if (digitalRead(BTN_UP) == LOW) { cCal += 0.005; lastDebounce = millis(); }
if (digitalRead(BTN_DOWN) == LOW) { cCal -= 0.005; lastDebounce = millis(); }
}
}
void loop() {
handleButtons();
analogReference(DEFAULT);
delay(2);
long vSum = 0;
for (int i = 0; i < 10; i++) { vSum += analogRead(VOLT_PIN); delay(1); }
float vBase = (((vSum / 10.0) * 5.0) / 1023.0) * ((R1 + R2) / R2);
is2SMode = (vBase < 9.0);
float vRaw = vBase * (is2SMode ? vCal2S : vCal12V);
float vA3 = 0.0;
if (manualA3Mode) {
long lowVSum = 0;
for (int i = 0; i < 10; i++) { lowVSum += analogRead(LOW_V_PIN); delay(1); }
float vA3Test = ((lowVSum / 10.0) * 5.0) / 1023.0;
if (vA3Test < 2.00) {
analogReference(INTERNAL);
delay(5);
long internalSum = 0;
for (int i = 0; i < 15; i++) { internalSum += analogRead(LOW_V_PIN); delay(1); }
vA3 = (((internalSum / 15.0) * 1.10) / 1023.0) * vCal1V1;
isHighPrecision1V1 = true;
analogReference(DEFAULT);
delay(2);
} else {
vA3 = vA3Test * vCalA3;
isHighPrecision1V1 = false;
}
}
long cSum = 0;
for (int i = 0; i < 20; i++) { cSum += analogRead(CURR_PIN); delay(1); }
float cOut = ((cSum / 20.0) * 5.0) / 1023.0;
float cRaw = ((cOut - 2.50) / ACS_SENSITIVITY) * cCal;
if (cRaw < 0.15 && cRaw > -0.15) cRaw = 0.0;
if (cRaw < 0) cRaw = abs(cRaw);
if (smoothVoltage == 0.0) smoothVoltage = vRaw;
if (smoothCurrent == 0.0) smoothCurrent = cRaw;
if (smoothLowV == 0.0) smoothLowV = vA3;
smoothVoltage = (smoothVoltage * 0.85) + (vRaw * 0.15);
smoothCurrent = (smoothCurrent * 0.90) + (cRaw * 0.10);
smoothLowV = (smoothLowV * 0.85) + (vA3 * 0.15);
float watts = smoothVoltage * smoothCurrent;
float currentWarn = is2SMode ? warnVoltage2S : warnVoltage12V;
float currentAlarm = is2SMode ? alarmVoltage2S : alarmVoltage12V;
if (currentMode == MAIN_VIEW) {
if (manualA3Mode) {
lcd.setCursor(0, 0);
lcd.print("A3:"); lcd.print(smoothLowV, 3); lcd.print("V ");
if (isHighPrecision1V1) lcd.print("(1.1V)");
else lcd.print("(5.0V)");
lcd.setCursor(0, 1);
if (smoothLowV >= 0.8 && smoothLowV <= 1.8) {
lcd.print("TYPE: 1.5V/NiMH ");
setColor(0, 255, 0);
} else if (smoothLowV >= 2.5 && smoothLowV <= 4.5) {
lcd.print("TYPE: 1S Li-Ion ");
setColor(0, 255, 0);
} else {
lcd.print("TYPE: LOW/NONE ");
setColor(0, 0, 255);
}
noTone(BUZZER_PIN);
}
else {
lcd.setCursor(0, 0);
lcd.print("V:"); lcd.print(smoothVoltage, 2); lcd.print("V ");
lcd.setCursor(8, 0);
lcd.print("A:"); lcd.print(smoothCurrent, 2); lcd.print("A ");
lcd.setCursor(0, 1);
lcd.print("W:");
if (watts < 100.0) lcd.print(" ");
lcd.print(watts, 1); lcd.print("W");
if (watts > maxWatts) {
setColor(255, 0, 0);
lcd.setCursor(11, 1); lcd.print("[OVER]");
tone(BUZZER_PIN, 1800); delay(80); noTone(BUZZER_PIN); delay(80);
}
else if (smoothVoltage >= currentWarn) {
setColor(0, 255, 0);
noTone(BUZZER_PIN);
lcd.setCursor(11, 1);
lcd.print(is2SMode ? "[ 2S]" : " [OK]");
}
else if (smoothVoltage < currentWarn && smoothVoltage > currentAlarm) {
setColor(255, 35, 0);
noTone(BUZZER_PIN);
lcd.setCursor(11, 1); lcd.print("[WRN]");
}
else {
setColor(255, 0, 0);
lcd.setCursor(11, 1); lcd.print("[LOW]");
tone(BUZZER_PIN, 1000); delay(100); noTone(BUZZER_PIN); delay(100);
}
}
}
else if (currentMode == SHOW_LIMITS) {
lcd.setCursor(0, 0);
lcd.print(manualA3Mode ? "-MODE: A3 MANU-" : (is2SMode ? "-MODE: 2S 8.4V-" : "-MODE: 12V BAT-"));
lcd.setCursor(0, 1);
if (manualA3Mode) {
lcd.print(isHighPrecision1V1 ? "Ref: 1.1V (High)" : "Ref: 5.0V Std ");
} else {
lcd.print("WRN:"); lcd.print(currentWarn, 1); lcd.print("V");
lcd.setCursor(8, 1);
lcd.print("ALM:"); lcd.print(currentAlarm, 1); lcd.print("V ");
}
}
else if (currentMode == SET_WARN) {
lcd.setCursor(0, 0);
lcd.print(manualA3Mode ? ">N/A FOR A3 IN " : (is2SMode ? ">SET WARN (2S) " : ">SET WARN (12V) "));
lcd.setCursor(0, 1);
lcd.print(manualA3Mode ? "Direct Meter " : "Limit: ");
if (!manualA3Mode) { lcd.print(currentWarn, 2); lcd.print("V "); }
}
else if (currentMode == SET_ALARM) {
lcd.setCursor(0, 0);
lcd.print(manualA3Mode ? ">N/A FOR A3 IN " : (is2SMode ? ">SET ALARM (2S) " : ">SET ALARM(12V) "));
lcd.setCursor(0, 1);
lcd.print(manualA3Mode ? "Direct Meter " : "Limit: ");
if (!manualA3Mode) { lcd.print(currentAlarm, 2); lcd.print("V "); }
}
else if (currentMode == CALIB_VOLT) {
lcd.setCursor(0, 0);
if (manualA3Mode) {
if (isHighPrecision1V1) lcd.print(">CALIB 1.1V Ref ");
else lcd.print(">CALIB A3 (5V) ");
}
else if (is2SMode) lcd.print("> CALIB V (2S) ");
else lcd.print("> CALIB V (12V) ");
lcd.setCursor(0, 1);
float activeVCal = manualA3Mode ? (isHighPrecision1V1 ? vCal1V1 : vCalA3) : (is2SMode ? vCal2S : vCal12V);
lcd.print("vCal: "); lcd.print(activeVCal, 4); lcd.print(" ");
}
else if (currentMode == CALIB_CURR) {
lcd.setCursor(0, 0);
lcd.print("> CALIB CURRENT ");
lcd.setCursor(0, 1);
lcd.print("cCal: "); lcd.print(cCal, 4); lcd.print(" ");
}
delay(150);
}
BOXSTLS
the stls for the box
https://www.thingiverse.com/thing:7393092
u have to open some holes manually
for ventilation and for the nano data cable if u like
MENU INFO
1. Setting Alarm & Warning Voltages (8.4V & 12V Batteries)
You can set custom Warning and Alarm thresholds for both 8.4V (2S LiPo/Li-ion) and 12V (Lead-Acid / 3S) batteries.
- For 12V Mode: (u must have a 12v battery in main cable)
- Press MENU 1 time to set the 12V Warning Voltage using the UP/DOWN buttons.
- Press MENU 2 times to set the 12V Alarm Voltage.
- For 8.4V (2S) Mode: (u must have a 8.4v battery in main cable)
- Press MENU 1 times to set the 2S Warning Voltage.
- Press MENU2 times to set the 2S Alarm Voltage.
2. Voltage Fine-Tuning (Calibration)
Before using the tester, calibrate the voltage readings using a reliable Digital Multimeter (DMM) connected in parallel with the battery.
- To Calibrate 12V Mode ($A0$ Input):
- Connect a 12V battery (or power supply) to the main test leads along with your multimeter.
- Press MENU 3 times to enter the 12V Voltage Calibration screen (VCal 12V).
- Use UP/DOWN until the LCD voltage matches your multimeter reading.
- Press MENU until you exit the calibration mode to save the new settings to EEPROM.
- To Calibrate 8.4V (2S) Mode ($A0$ Input):
- Connect an 8.4V battery to the main test leads along with your multimeter.
- Press MENU 3 times to enter the 2S Voltage Calibration screen (VCal 2S).
- Adjust using UP/DOWN until the display matches your multimeter.
- Press MENU until you exit the calibration mode to save the new settings to EEPROM.
- To Calibrate Low Voltage Mode ($A3$ Input — 1.5V / 3.7V):
- Press the DOWN button on the home screen to switch the display to the A3 / 1.1V Internal Reference Mode.
- Connect a small battery (e.g., 1.5V AA or 3.7V Li-ion) to the A3 pin along with your multimeter.
- Press MENU 2 times to enter the A3 Calibration screen (VCal A3)
- Adjust with UP/DOWN to match your multimeter.
- Press MENU until you exit the calibration mode to save the new settings to EEPROM.
alibration Step: Current Sensor (ACS712)
⚠️ WARNING: Measuring current requires connecting your multimeter in SERIES with the load, NOT in parallel. Connecting a multimeter directly across a battery in current mode will cause a short circuit!
To calibrate the current sensor:
- Set your digital multimeter (DMM) to 10A DC Current mode and move the red probe to the 10A jack.
- Connect the multimeter in series with your test load (for example: Battery (+) $\rightarrow$ Multimeter Red $\rightarrow$ Multimeter Black $\rightarrow$ ACS712 Sensor $\rightarrow$ Load $\rightarrow$ Battery (-)).
- Press the MENU button 4 times on the tester to navigate to the Current Calibration screen (CCal).
- Compare the reading on the LCD display with the reading on your multimeter. Use the UP and DOWN buttons to adjust the calibration multiplier until the LCD matches your multimeter exactly.
- Press MENU until you exit the calibration mode to save the new settings to EEPROM.