You've already forked MicroPythonOS
mirror of
https://github.com/m5stack/MicroPythonOS.git
synced 2026-05-20 11:51:27 -07:00
Fri3d Camp 2024 Badge: improve battery monitor calibration
This commit is contained in:
@@ -4,7 +4,7 @@ MIN_VOLTAGE = 3.15
|
||||
MAX_VOLTAGE = 4.15
|
||||
|
||||
adc = None
|
||||
scale_factor = 0
|
||||
conversion_func = None # Conversion function: ADC value -> voltage
|
||||
adc_pin = None
|
||||
|
||||
# Cache to reduce WiFi interruptions (ADC2 requires WiFi to be disabled)
|
||||
@@ -19,7 +19,7 @@ def _is_adc2_pin(pin):
|
||||
return 11 <= pin <= 20
|
||||
|
||||
|
||||
def init_adc(pinnr, sf):
|
||||
def init_adc(pinnr, adc_to_voltage_func):
|
||||
"""
|
||||
Initialize ADC for battery voltage monitoring.
|
||||
|
||||
@@ -29,13 +29,16 @@ def init_adc(pinnr, sf):
|
||||
|
||||
Args:
|
||||
pinnr: GPIO pin number
|
||||
sf: Scale factor to convert raw ADC (0-4095) to battery voltage
|
||||
adc_to_voltage_func: Conversion function that takes raw ADC value (0-4095)
|
||||
and returns battery voltage in volts
|
||||
"""
|
||||
global adc, scale_factor, adc_pin
|
||||
scale_factor = sf
|
||||
global adc, conversion_func, adc_pin
|
||||
|
||||
conversion_func = adc_to_voltage_func
|
||||
adc_pin = pinnr
|
||||
|
||||
try:
|
||||
print(f"Initializing ADC pin {pinnr} with scale_factor {sf}")
|
||||
print(f"Initializing ADC pin {pinnr} with conversion function")
|
||||
if _is_adc2_pin(pinnr):
|
||||
print(f" WARNING: GPIO{pinnr} is on ADC2 - WiFi will be disabled during readings")
|
||||
from machine import ADC, Pin
|
||||
@@ -44,6 +47,9 @@ def init_adc(pinnr, sf):
|
||||
except Exception as e:
|
||||
print(f"Info: this platform has no ADC for measuring battery voltage: {e}")
|
||||
|
||||
initial_adc_value = read_raw_adc()
|
||||
print("Reading ADC at init to fill cache: {initial_adc_value} => {read_battery_voltage(raw_adc_value=initial_adc_value)}V => {get_battery_percentage(raw_adc_value=initial_adc_value)}%")
|
||||
|
||||
|
||||
def read_raw_adc(force_refresh=False):
|
||||
"""
|
||||
@@ -63,12 +69,10 @@ def read_raw_adc(force_refresh=False):
|
||||
"""
|
||||
global _cached_raw_adc, _last_read_time
|
||||
|
||||
# Desktop mode - return random value
|
||||
# Desktop mode - return random value in typical ADC range
|
||||
if not adc:
|
||||
import random
|
||||
return random.randint(1900, 2600) if scale_factor == 0 else random.randint(
|
||||
int(MIN_VOLTAGE / scale_factor), int(MAX_VOLTAGE / scale_factor)
|
||||
)
|
||||
return random.randint(1900, 2600)
|
||||
|
||||
# Check if this is an ADC2 pin (requires WiFi disable)
|
||||
needs_wifi_disable = adc_pin is not None and _is_adc2_pin(adc_pin)
|
||||
@@ -115,7 +119,7 @@ def read_raw_adc(force_refresh=False):
|
||||
WifiService.temporarily_enable(was_connected)
|
||||
|
||||
|
||||
def read_battery_voltage(force_refresh=False):
|
||||
def read_battery_voltage(force_refresh=False, raw_adc_value=None):
|
||||
"""
|
||||
Read battery voltage in volts.
|
||||
|
||||
@@ -125,19 +129,19 @@ def read_battery_voltage(force_refresh=False):
|
||||
Returns:
|
||||
float: Battery voltage in volts (clamped to 0-MAX_VOLTAGE)
|
||||
"""
|
||||
raw = read_raw_adc(force_refresh)
|
||||
voltage = raw * scale_factor
|
||||
raw = raw_adc_value if raw_adc_value else read_raw_adc(force_refresh)
|
||||
voltage = conversion_func(raw) if conversion_func else 0.0
|
||||
return max(0.0, min(voltage, MAX_VOLTAGE))
|
||||
|
||||
|
||||
def get_battery_percentage():
|
||||
def get_battery_percentage(raw_adc_value=None):
|
||||
"""
|
||||
Get battery charge percentage.
|
||||
|
||||
Returns:
|
||||
float: Battery percentage (0-100)
|
||||
"""
|
||||
voltage = read_battery_voltage()
|
||||
voltage = read_battery_voltage(raw_adc_value=raw_adc_value)
|
||||
percentage = (voltage - MIN_VOLTAGE) * 100.0 / (MAX_VOLTAGE - MIN_VOLTAGE)
|
||||
return max(0.0, min(100.0, percentage))
|
||||
|
||||
|
||||
@@ -275,9 +275,14 @@ best fit on battery power:
|
||||
2269 is 3.831
|
||||
"""
|
||||
def adc_to_voltage(adc_value):
|
||||
"""
|
||||
Convert raw ADC value to battery voltage using calibrated linear function.
|
||||
Calibration data shows linear relationship: voltage = -0.0016237 * adc + 8.2035
|
||||
This is ~10x more accurate than simple scaling (error ~0.01V vs ~0.1V).
|
||||
"""
|
||||
return (-0.0016237 * adc_value + 8.2035)
|
||||
#mpos.battery_voltage.init_adc(13, adc_to_voltage)
|
||||
mpos.battery_voltage.init_adc(13, 1/616) # simple scaling has an error of ~0.01V vs the adc_to_voltage() method
|
||||
|
||||
mpos.battery_voltage.init_adc(13, adc_to_voltage)
|
||||
|
||||
import mpos.sdcard
|
||||
mpos.sdcard.init(spi_bus, cs_pin=14)
|
||||
|
||||
@@ -88,7 +88,12 @@ except Exception as e:
|
||||
|
||||
# Simulated battery voltage ADC measuring
|
||||
import mpos.battery_voltage
|
||||
mpos.battery_voltage.init_adc(999, (3.3 / 4095) * 2)
|
||||
|
||||
def adc_to_voltage(adc_value):
|
||||
"""Convert simulated ADC value to voltage."""
|
||||
return adc_value * (3.3 / 4095) * 2
|
||||
|
||||
mpos.battery_voltage.init_adc(999, adc_to_voltage)
|
||||
|
||||
print("linux.py finished")
|
||||
|
||||
|
||||
@@ -81,7 +81,19 @@ mpos.ui.main_display.set_rotation(lv.DISPLAY_ROTATION._90) # must be done after
|
||||
|
||||
# Battery voltage ADC measuring
|
||||
import mpos.battery_voltage
|
||||
mpos.battery_voltage.init_adc(5, 262 / 100000)
|
||||
|
||||
def adc_to_voltage(adc_value):
|
||||
"""
|
||||
Convert raw ADC value to battery voltage.
|
||||
Currently uses simple linear scaling: voltage = adc * 0.00262
|
||||
|
||||
This could be improved with calibration data similar to Fri3d board.
|
||||
To calibrate: measure actual battery voltages and corresponding ADC readings,
|
||||
then fit a linear or polynomial function.
|
||||
"""
|
||||
return adc_value * 0.00262
|
||||
|
||||
mpos.battery_voltage.init_adc(5, adc_to_voltage)
|
||||
|
||||
# On the Waveshare ESP32-S3-Touch-LCD-2, the camera is hard-wired to power on,
|
||||
# so it needs a software power off to prevent it from staying hot all the time and quickly draining the battery.
|
||||
|
||||
@@ -154,6 +154,7 @@ def create_notification_bar():
|
||||
# Percentage is not shown for now:
|
||||
#battery_label.set_text(f"{round(percent)}%")
|
||||
#battery_label.remove_flag(lv.obj.FLAG.HIDDEN)
|
||||
update_battery_icon() # run it immediately instead of waiting for the timer
|
||||
|
||||
def update_wifi_icon(timer):
|
||||
from mpos.net.wifi_service import WifiService
|
||||
|
||||
Reference in New Issue
Block a user