← All projects
Sensors Beginner

The 0–25 V Voltage Sensor: a 12 V Battery Monitor on an ESP32

The "25 V" module is just two resistors, 30 kΩ and 7.5 kΩ. We see why on an ESP32 it measures only 16.5 V rather than 25, how to calibrate the reading against a multimeter, and how to turn a lead-acid battery voltage into a charge percentage.

Difficulty
Beginner
Time
30 min
Category
Sensors
Updated

About the project

To measure a voltage above 3.3 V you have to scale it down with a divider. The "Voltage Sensor 0–25 V" module is a ready-made 30 kΩ / 7.5 kΩ divider with a screw terminal and header pins. It divides by 5: 25 V in gives 5 V out — exactly the Arduino Uno range. With it you can easily watch the battery of an alarm system, a solar setup or a car.

What you need

  • ESP32 DevKit (or an Arduino Uno)
  • A 0–25 V voltage sensor module
  • A 12 V source: a battery or a power supply
  • A multimeter for calibration

Wiring

Module Connect to
VCC terminal battery +
GND terminal battery −
S GPIO34 (ADC1)
+ nothing: on most modules this pin is not connected at all
− ESP32 GND

The battery minus and the board GND must be common, otherwise there is nothing to measure against.

The main trap: 3.3 V

The module is designed for 5 V Arduinos. An ESP32 input takes up to 3.3 V, so with a 5:1 divider the maximum is 16.5 V. That is enough for a 12 V battery (up to 14.4 V while charging), but not for a 24 V system: 25 V would put 5 V on the pin and damage it. For higher voltages make your own divider, for example 100 kΩ / 10 kΩ (11:1).

On top of that the ESP32 ADC is non-linear near the top, so we read analogReadMilliVolts() with its factory calibration and average 32 readings.

Code

// 12 V battery monitor: 0–25 V voltage sensor (30k / 7.5k divider)
const int SENSE_PIN = 34;
const float DIVIDER = (30000.0 + 7500.0) / 7500.0;   // = 5
const float CALIBRATION = 1.00;   // adjust by comparing with a multimeter

float batteryVolts() {
  uint32_t mv = 0;
  for (int i = 0; i < 32; i++) mv += analogReadMilliVolts(SENSE_PIN);
  return mv / 32.0 / 1000.0 * DIVIDER * CALIBRATION;
}

int percentLeadAcid(float v) {   // 12 V lead-acid at rest: 11.8 V ≈ 0 %, 12.7 V ≈ 100 %
  return constrain((int)((v - 11.8) / (12.7 - 11.8) * 100), 0, 100);
}

void setup() {
  Serial.begin(115200);
}

void loop() {
  float v = batteryVolts();
  Serial.printf("Battery %.2f V  ~%d %%%s\n", v, percentLeadAcid(v), v < 11.9 ? "  LOW!" : "");
  delay(1000);
}

In the simulator change the power supply voltage. Set 20 V and the simulator reminds you that this is too much for an ESP32.

Calibration

The resistors have a 1–5 % tolerance and the ADC reference drifts too, so an error of 0.1–0.3 V is normal. Measure the voltage with a multimeter, compare it with the Serial Monitor, and set CALIBRATION = multimeter / reading. For example, the multimeter says 12.46 V and the sketch 12.31 V: CALIBRATION = 1.012.

Charge percentage

A lead-acid battery voltage shows the charge accurately only at rest, an hour after charging or a load. Under load it sags, and while charging it reads high. Lithium batteries need a different table: a 1S Li-ion goes from 3.0 V (0 %) to 4.2 V (100 %), and the curve is far from linear.

What next

  • A Telegram message when the voltage drops below 11.9 V.
  • A 24-hour chart on an ESP32 web page: you can see the solar panel charge the battery during the day.
  • Current and power — add an INA219, which measures both.