This project demonstrates how to monitor the voltage of lead-acid batteries (12V and 24V) using an Arduino Uno. The voltage input is scaled using a resistor divider to ensure that the battery voltage, which can reach up to 32V, is within the 0-5V range of the Arduino's ADC.
We use a simple voltage divider circuit with two resistors (R1 and R2) to scale the input voltage (up to 32V) down to a voltage the Arduino can measure (0-5V). The voltage divider equation is:
Vout = Vin × (R2 / (R1 + R2))
For this project, we use the following resistor values:
To calculate the maximum voltage that the Arduino will read, we rearrange the formula:
The following LEDs indicate the battery charge level:
/*
* Arduino Uno: Lead-Acid Battery Monitor with LEDs and Resistor Divider
* Voltage divider setup: 56kΩ (R1) and 10kΩ (R2) to measure up to 32V
* © 2024 Copyright Peter I. Dunne, all rights reserved
* Prepared for educational use
* Released under the Mozilla Public License
*/
const int analogPin = A0; // Pin connected to battery voltage divider
const int greenLED = 9; // Green LED for full charge
const int yellowLED = 10; // Yellow LED for mid-range charge
const int redLED = 11; // Red LED for low or overcharge indication
// Voltage divider resistor values
const float R1 = 56000.0; // Resistor connected to battery (56kΩ)
const float R2 = 10000.0; // Resistor connected to ground (10kΩ)
// Battery voltage thresholds (in Volts)
const float lowThreshold12V = 11.4;
const float highThreshold12V = 14.4;
const float lowThreshold24V = 22.8;
const float highThreshold24V = 28.8;
// Timing variables for non-blocking pulsing
unsigned long previousMillis = 0;
unsigned long ledInterval = 500; // Default to 2Hz (500ms on/off)
// Maximum voltage the Arduino can read
const float maxVoltage = 32.0;
void setup() {
pinMode(greenLED, OUTPUT);
pinMode(yellowLED, OUTPUT);
pinMode(redLED, OUTPUT);
Serial.begin(115200);
Serial.println("Code by Peter Ivan Dunne, ©2024, all rights reserved");
Serial.println("Released under the Mozilla Public License");
Serial.println("https://jazenga.com/educational");
Serial.println("Monitor Lead Acid batteries with auto detection of 12v/24v systems");
}
void loop() {
// Read analog value from ADC
int sensorValue = analogRead(analogPin);
// Convert the analog reading to battery voltage
float voltage = sensorValue * (5.0 / 1023.0) * ((R1 + R2) / R2);
// Determine battery type (12V or 24V)
bool is24V = voltage >= 16;
// Calculate charge level
float chargeLevel = calculateChargeLevel(voltage, is24V);
if (chargeLevel<0){
chargeLevel=0;
}
if (chargeLevel>100){
chargeLevel=100;
}
// Handle LED behavior based on voltage thresholds
handleLEDs(voltage, is24V);
// Print the voltage and charge level to the Serial Monitor
Serial.print(is24V ? "24v system" : "12V system");
Serial.print(" Battery Voltage: ");
Serial.print(voltage);
Serial.print(" V, Charge Level: ");
Serial.print(chargeLevel);
Serial.println("%");
delay(1000); // Wait for 1 second before the next reading
}
// Function to calculate charge level percentage
float calculateChargeLevel(float voltage, bool is24V) {
float maxVoltage = is24V ? highThreshold24V : highThreshold12V;
float minVoltage = is24V ? lowThreshold24V : lowThreshold12V;
return((voltage - minVoltage) / (maxVoltage - minVoltage)) * 100.0;
}
// Function to handle LED indicators based on battery status
void handleLEDs(float voltage, bool is24V) {
float lowThreshold = is24V ? lowThreshold24V : lowThreshold12V;
float highThreshold = is24V ? highThreshold24V : highThreshold12V;
unsigned long currentMillis = millis();
// Green LED for fully charged battery
if (voltage >= highThreshold) {
digitalWrite(greenLED, HIGH); // Full charge: solid green
digitalWrite(yellowLED, LOW);
digitalWrite(redLED, LOW);
}
// Yellow LED for mid-range charge (pulsing at 2Hz)
else if (voltage > lowThreshold && voltage < highThreshold) {
digitalWrite(greenLED, LOW);
if (currentMillis - previousMillis >= ledInterval) {
previousMillis = currentMillis;
digitalWrite(yellowLED, !digitalRead(yellowLED)); // Toggle yellow LED
}
// Pulse the yellow LED based on charge level
ledInterval = 1000 - (calculateChargeLevel(voltage, is24V) * 10); // Proportional duty cycle
digitalWrite(redLED, LOW);
}
// Red LED for low or over-voltage conditions
else {
digitalWrite(greenLED, LOW);
digitalWrite(yellowLED, LOW);
if (currentMillis - previousMillis >= ledInterval) {
previousMillis = currentMillis;
digitalWrite(redLED, !digitalRead(redLED)); // Toggle red LED
}
// Pulse the red LED at 1Hz for under-voltage, 3Hz for over-voltage
ledInterval = (voltage < lowThreshold) ? 1000 : 333;
}
}