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

20260805_092208.jpg
20260805_092241.jpg
20260805_092743.jpg
20260805_092755.jpg
20260805_092811.jpg
main cable.png

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.

  1. Main terminal. Here u power up the device using a 2s or a 12v battery; the code measures the battery voltage automatically
  2. 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.
  3. Current terminal (250watt max): here u can measure the current of a device, 1- 15 volts only


BUTTONS

  1. MENU

(8.4 & 12v) Main screen > set warning> set alarm >fine tunning voltage> fine tunning current.

  1. UP

Shows the warning & alert values

  1. 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

FHU3WMCMSGDQA1V.png

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

2nd cable.png
F630Q2MMSGDQA15.jpg

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

20260808_113320.jpg
20260808_100519 - &Alpha;&nu;&tau;&iota;&gamma;&rho;&alpha;&phi;ή.jpg
20260808_100519.jpg
20260808_100526.jpg
20260808_100610 - &Alpha;&nu;&tau;&iota;&gamma;&rho;&alpha;&phi;ή.jpg
20260808_100610.jpg
20260808_100617.jpg
20260808_100621.jpg
20260808_100725.jpg
20260808_100813.jpg
20260808_111602.jpg
20260808_111656.jpg
20260808_111706.jpg
20260808_111715.jpg
20260808_111731.jpg
20260808_111904.jpg
20260808_114237.jpg

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.

  1. For 12V Mode: (u must have a 12v battery in main cable)
  2. Press MENU 1 time to set the 12V Warning Voltage using the UP/DOWN buttons.
  3. Press MENU 2 times to set the 12V Alarm Voltage.
  4. For 8.4V (2S) Mode: (u must have a 8.4v battery in main cable)
  5. Press MENU 1 times to set the 2S Warning Voltage.
  6. 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.

  1. To Calibrate 12V Mode ($A0$ Input):
  2. Connect a 12V battery (or power supply) to the main test leads along with your multimeter.
  3. Press MENU 3 times to enter the 12V Voltage Calibration screen (VCal 12V).
  4. Use UP/DOWN until the LCD voltage matches your multimeter reading.
  5. Press MENU until you exit the calibration mode to save the new settings to EEPROM.
  6. To Calibrate 8.4V (2S) Mode ($A0$ Input):
  7. Connect an 8.4V battery to the main test leads along with your multimeter.
  8. Press MENU 3 times to enter the 2S Voltage Calibration screen (VCal 2S).
  9. Adjust using UP/DOWN until the display matches your multimeter.
  10. Press MENU until you exit the calibration mode to save the new settings to EEPROM.
  11. To Calibrate Low Voltage Mode ($A3$ Input — 1.5V / 3.7V):
  12. Press the DOWN button on the home screen to switch the display to the A3 / 1.1V Internal Reference Mode.
  13. Connect a small battery (e.g., 1.5V AA or 3.7V Li-ion) to the A3 pin along with your multimeter.
  14. Press MENU 2 times to enter the A3 Calibration screen (VCal A3)
  15. Adjust with UP/DOWN to match your multimeter.
  16. 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:

  1. Set your digital multimeter (DMM) to 10A DC Current mode and move the red probe to the 10A jack.
  2. 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 (-)).
  3. Press the MENU button 4 times on the tester to navigate to the Current Calibration screen (CCal).
  4. 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.
  5. Press MENU until you exit the calibration mode to save the new settings to EEPROM.