UnexpectedMaker / esp32s3

Assorted files for my ESP32-S3 development boards
193 stars 29 forks source link

Micropython Helper Libraries - get_battery_voltage() results not usable #12

Open Hamsanger opened 1 year ago

Hamsanger commented 1 year ago

Using Micropython V1.19.1+, the implementation of the battery monitor on GPIO2 and get_battery_voltage() produces invalid results. The default range of the ADC with attenuation of 0dB is only 850mV (refer to the ADC Characteristics in the ESP32-S3 datasheet), so the voltage presented to the ADC from the voltage divider on GPIO2 saturates the count for any usable LiPo voltages unless some input attenuation is used.

I have obtained good results using ATTN_2_5DB and the newer ADC.read_uv() function while scaling the voltage reading using the voltage divider resistor values. For a FeatherS3 board, the code looks similar to:

# Battery voltage divider.  Voltage seen by the ADC is VBAT * R1 / (R1 + R2)
R1 = 160 # voltage divider R1 in kOhms
R2 = 442 # voltage divider R2 in kOhms

vbat_scale_factor = (R1 + R2) / R1 / 1_000_000 # to convert the microvolt reading to volts

def get_battery_voltage():
    """
    Returns the current battery voltage. If no battery is connected, returns around 4.2V which is the charge voltage
    This is an approximation only, but useful to detect if the charge state of the battery is getting low.
    """
    adc = ADC(Pin(VBAT_SENSE), atten=ADC.ATTN_2_5DB) # Assign the ADC pin to read with nominal 0..1.1V range
    # We are going to read the ADC 10 times and discard the results as we can't guarantee the ADC is calibrated or stable yet after boot. Not sure why we have to do this :(
    for _ in range(10):
        adc.read()
    measuredvbat = adc.read_uv() * vbat_scale_factor
    return round(measuredvbat, 2)

As the voltage scaling factor is derived from first principles, the values of R1 and R2 shown can be adjusted to suit other designs.

ShayBox commented 1 year ago

How does this compare to the current version, it seems to have changed to not use ADC()

anglerfish27 commented 1 year ago

FYI. I've used the built in examples with firmware 1.20 (current supported release not the nightly build). It works fine for me. The voltage of the battery when polled is exactly the voltage reading I get from a benchtop multi meter. So if the board says my Lipo battery is at 3.1V I disconnect and check it indeed its right on at 3.1xxxxxx Volts. I'm not doing anything special except running 1.20 official release of MP from their website. Good luck.

I found this to be a very handy feature so I can shutdown the MCU workload when the battery reaches what I decide is too low for that particular set of Lipo 18650's based on manufacturer specs. Best of luck. Seems to work fine for me. ::shrug::