Measure LiPo Voltage with ESP32 S2 Mini and Voltage Divider

I recently bought an S2 Mini and a Arudino Voltmeter 10 stücke/1pc intelligente elektronik dc 0 25v standard spannung sensor modul test elektronische steine smart roboter für arduino diy kit| | - AliExpress but can't get it to work.

Goal
I have an 3,7V Lipo Battery that I want to measure the voltage from. The actual voltage currently is 4.1V.

Wiring
Battery + and - output to the Voltmeter + and - input
Voltmeter output - to S2 Mini ground, + unconnected and Signal to pin 2.

How it should work
Basically the voltmeter is just a voltage divider (using R1 = 30000 and R2 = 7500) because the voltage would be too high if I understand correctly. So I have to measure the reduced voltages that goes into the ADC (Step 1) and then convert that voltage with a factor to get back the actual voltage (Step 2).

Step 1.
To measure the actual input of the reduced voltage, I have to first measure the ADC value by
adc_value = analogRead(ANALOG_IN_PIN);
This can give me a number between 0 and 4095 which stands for a voltage of 0 to 3.3v.
In my case I get an analogread of 2653
Now this needs to be converted to a voltage value. So I have to multiply the ADCValue by the voltage the device is working with (3.3V) and divide that by the maximum ADC Value of 4095 by using:
adc_voltage = (adc_value * 3.3) / 4095.0;
That gives me a value of 777.44.

Now I should have the resulting voltage after the voltage divider reduced the actual voltage which brings me to step 2.

Step 2.
This should be easy by just applying the formular:

in_voltage = adc_voltage / (R2/(R1+R2)) ;

Calculating (R2/(R1+R2)) equates to 0.2
Which leads to: measured voltage of 777mV divided by 0.2.
But that gives me a totally wrong value of 3885 mV, while my multimeter gives me a value of the lipo battery of 4100mV.

Whole code

// Define analog input
#define ANALOG_IN_PIN 2
 
// Floats for ADC voltage & Input voltage
float adc_voltage = 0.0;
float in_voltage = 0.0;
 
// Floats for resistor values in divider (in ohms)
float R1 = 30000.0;
float R2 = 7500.0; 
 
// Float for Reference Voltage
float ref_voltage = 3.3;
 
// Integer for ADC value
int adc_value = 0;
 
void setup()
{
   // Setup Serial Monitor
   Serial.begin(9600);
   Serial.println("DC Voltage Test");
}
 
void loop(){
   // Read the Analog Input
   adc_value = analogRead(ANALOG_IN_PIN);
   Serial.println(adc_value);
   
   // Determine voltage at ADC input
   adc_voltage  = (adc_value * ref_voltage) / 4095.0; 
   Serial.println( adc_voltage, 2);
   
   // Calculate voltage at divider input
   in_voltage = adc_voltage / (R2/(R1+R2)) ; 
   
   // Print results to Serial Monitor to 2 decimal places
  Serial.print("Input Voltage = ");
  Serial.println(in_voltage, 2);
  
  // Short delay
  delay(500);
}

Question
Since I think Step 2 is correct, there seems to be a problem with my logic in Step 1.
Is there anyone who knows what I did wrong?

You could use analogReadMilliVolts() function to get the voltage value in mVolts.

adc_value = analogReadMilliVolts(ANALOG_IN_PIN);

ADC on ESP32 have internal adjustable attenuator and the default is -11dB. The attenuator is very nonlinear, so if you want more precision, better don`t use it.

To bypass the internal ADC attenuator you have to add this to setup() loop:

analogSetPinAttenuation(ANALOG_IN_PIN, ADC_0db);

Btw , with 0dB attenuation max input voltage of adc is about 1V max!

Wow, that helped me a lot!!!

Actually I did not know of that function, so my whole "Step 1" is obsolete. Still wondering what I did wrong but doesn't really matter now.

Unfortunately though, I'm not fully there yet.
I just measured the voltage again with my multimeter and it measures at 4113 mV
My initial measurement with the code above gave me 3885 mV
With your advice for using adc_value = analogReadMilliVolts(ANALOG_IN_PIN); I get 4225mV (much closer to the real value)
With also adding analogSetPinAttenuation(ANALOG_IN_PIN, ADC_0db); I get 4045mV (even closer!)

However I tried commenting out analogSetPinAttenuation(ANALOG_IN_PIN, ADC_0db); and instead calibrating manually by reducing the analogReadMilliVolts by 20 with:

      adc_value = analogReadMilliVolts(ANALOG_IN_PIN);
      adc_calibrated = adc_value - 20;
      in_voltage = adc_calibrated / (R2/(R1+R2)) ;

Which now gives me 4122 mV and is spot on.

Does it make sense to just reduce the value by 20 or am I running into other problems that way?

It should be all right, that`s a just a small offset value for recalibrating ADC.