From 51716d31a8fec79dfaee560488ba1e33c82f5d1a Mon Sep 17 00:00:00 2001 From: Thomas Farstrike Date: Fri, 20 Jun 2025 00:26:13 +0200 Subject: [PATCH] update battery voltage icon --- internal_filesystem/builtin/system/button.py | 2 - .../lib/mpos/battery_voltage.py | 35 ++++++++++++++++ internal_filesystem/lib/mpos/ui/__init__.py | 42 +++++++++++++++---- 3 files changed, 68 insertions(+), 11 deletions(-) create mode 100644 internal_filesystem/lib/mpos/battery_voltage.py diff --git a/internal_filesystem/builtin/system/button.py b/internal_filesystem/builtin/system/button.py index aee002ab..8e18f674 100644 --- a/internal_filesystem/builtin/system/button.py +++ b/internal_filesystem/builtin/system/button.py @@ -34,7 +34,6 @@ def on_long_press(t): # Callback for when long press duration is reached. else: is_pressed = False - def button_handler(pin): """Interrupt handler for button press and release.""" global press_start_time, is_pressed, timer @@ -49,7 +48,6 @@ def button_handler(pin): timer.deinit() # Cancel timer if button is released early is_pressed = False - # Set up interrupt for both falling (press) and rising (release) edges button.irq(trigger=Pin.IRQ_FALLING | Pin.IRQ_RISING, handler=button_handler) diff --git a/internal_filesystem/lib/mpos/battery_voltage.py b/internal_filesystem/lib/mpos/battery_voltage.py new file mode 100644 index 00000000..fa8aedc7 --- /dev/null +++ b/internal_filesystem/lib/mpos/battery_voltage.py @@ -0,0 +1,35 @@ +have_adc=True +try: + from machine import ADC, Pin + # Configure ADC on pin 5 (IO5 / BAT_ADC) + adc = ADC(Pin(5)) + # Set ADC to 11dB attenuation for 0–3.3V range (common for ESP32) + adc.atten(ADC.ATTN_11DB) +except Exception as e: + print("Info: this platform has no ADC for measuring battery voltage") + have_adc=False + +import time + +# ADC parameters +VREF = 3.3 # Reference voltage (3.3V for most boards, adjust if different) +ADC_MAX = 4095 # 12-bit ADC resolution +VOLTAGE_DIVIDER = 3 # (R1 + R2) / R2 = (200k + 100k) / 100k = 3 + +def read_battery_voltage(): + if not have_adc: + import random + return random.randint(370,420) / 100 + # Read raw ADC value + raw_value = adc.read() + # Convert to voltage, accounting for divider and reference + voltage = (raw_value / ADC_MAX) * VREF * VOLTAGE_DIVIDER + # Clamp to 0–4.2V range for LiPo battery + voltage = max(0, min(voltage, 4.2)) + return voltage + +# Main loop to read and print battery voltage +while False: # for testing + battery_voltage = read_battery_voltage() + print("Battery Voltage: {:.2f} V".format(battery_voltage)) + time.sleep(1) # Wait 1 second diff --git a/internal_filesystem/lib/mpos/ui/__init__.py b/internal_filesystem/lib/mpos/ui/__init__.py index 46fef816..6e4a5e00 100644 --- a/internal_filesystem/lib/mpos/ui/__init__.py +++ b/internal_filesystem/lib/mpos/ui/__init__.py @@ -2,6 +2,7 @@ import utime # for timing calls import lvgl as lv import mpos.apps +import mpos.battery_voltage import mpos.wifi from mpos.ui.anim import WidgetAnimator @@ -15,6 +16,7 @@ NOTIFICATION_BAR_HEIGHT=24 CLOCK_UPDATE_INTERVAL = 1000 # 10 or even 1 ms doesn't seem to change the framerate but 100ms is enough WIFI_ICON_UPDATE_INTERVAL = 1500 +BATTERY_ICON_UPDATE_INTERVAL = 30000 TEMPERATURE_UPDATE_INTERVAL = 2000 MEMFREE_UPDATE_INTERVAL = 5000 # not too frequent because there's a forced gc.collect() to give it a reliable value @@ -157,10 +159,10 @@ def create_notification_bar(): time_label.align(lv.ALIGN.LEFT_MID, 0, 0) temp_label = lv.label(notification_bar) temp_label.set_text("00°C") - temp_label.align_to(time_label, lv.ALIGN.OUT_RIGHT_MID, NOTIFICATION_BAR_HEIGHT , 0) + temp_label.align_to(time_label, lv.ALIGN.OUT_RIGHT_MID, mpos.ui.pct_of_display_width(7) , 0) memfree_label = lv.label(notification_bar) memfree_label.set_text("") - memfree_label.align_to(temp_label, lv.ALIGN.OUT_RIGHT_MID, NOTIFICATION_BAR_HEIGHT, 0) + memfree_label.align_to(temp_label, lv.ALIGN.OUT_RIGHT_MID, mpos.ui.pct_of_display_width(7), 0) #style = lv.style_t() #style.init() #style.set_text_font(lv.font_montserrat_8) # tiny font @@ -169,19 +171,21 @@ def create_notification_bar(): #notif_icon = lv.label(notification_bar) #notif_icon.set_text(lv.SYMBOL.BELL) #notif_icon.align_to(time_label, lv.ALIGN.OUT_RIGHT_MID, PADDING_TINY, 0) + # Battery percentage + battery_label = lv.label(notification_bar) + battery_label.set_text("100%") + battery_label.align(lv.ALIGN.RIGHT_MID, 0, 0) + battery_label.add_flag(lv.obj.FLAG.HIDDEN) # Battery icon battery_icon = lv.label(notification_bar) battery_icon.set_text(lv.SYMBOL.BATTERY_FULL) - battery_icon.align(lv.ALIGN.RIGHT_MID, 0, 0) + battery_icon.align_to(battery_label, lv.ALIGN.OUT_LEFT_MID, 0, 0) + battery_icon.add_flag(lv.obj.FLAG.HIDDEN) # WiFi icon wifi_icon = lv.label(notification_bar) wifi_icon.set_text(lv.SYMBOL.WIFI) - wifi_icon.align_to(battery_icon, lv.ALIGN.OUT_LEFT_MID, -NOTIFICATION_BAR_HEIGHT, 0) + wifi_icon.align_to(battery_icon, lv.ALIGN.OUT_LEFT_MID, -mpos.ui.pct_of_display_width(7), 0) wifi_icon.add_flag(lv.obj.FLAG.HIDDEN) - # Battery percentage - not shown to conserve space - #battery_label = lv.label(notification_bar) - #battery_label.set_text("100%") - #battery_label.align(lv.ALIGN.RIGHT_MID, 0, 0) # Update time import time def update_time(timer): @@ -197,6 +201,25 @@ def create_notification_bar(): except Exception as e: print("Warning: could not check WLAN status:", str(e)) + def update_battery_icon(timer=None): + volt = mpos.battery_voltage.read_battery_voltage() + percent = (volt - 3.7) * 100 / (4.2 - 3.7) + battery_label.set_text(f"{round(percent)}%") + battery_label.remove_flag(lv.obj.FLAG.HIDDEN) + # 3.7 - 4.15 => 0.5V diff / 3 = 0.015 + if volt > 4.15: + battery_icon.set_text(lv.SYMBOL.BATTERY_FULL) + elif volt > 4: + battery_icon.set_text(lv.SYMBOL.BATTERY_3) + elif volt > 3.85: + battery_icon.set_text(lv.SYMBOL.BATTERY_2) + elif volt > 3.75: + battery_icon.set_text(lv.SYMBOL.BATTERY_1) + else: + battery_icon.set_text(lv.SYMBOL.BATTERY_EMPTY) + battery_icon.remove_flag(lv.obj.FLAG.HIDDEN) + update_battery_icon() # run it immediately instead of waiting for the timer + def update_wifi_icon(timer): if mpos.wifi.WifiService.is_connected(): wifi_icon.remove_flag(lv.obj.FLAG.HIDDEN) @@ -229,6 +252,7 @@ def create_notification_bar(): timer2 = lv.timer_create(update_temperature, TEMPERATURE_UPDATE_INTERVAL, None) timer3 = lv.timer_create(update_memfree, MEMFREE_UPDATE_INTERVAL, None) timer4 = lv.timer_create(update_wifi_icon, WIFI_ICON_UPDATE_INTERVAL, None) + timer5 = lv.timer_create(update_battery_icon, BATTERY_ICON_UPDATE_INTERVAL, None) # hide bar animation global hide_bar_animation @@ -371,7 +395,7 @@ def create_drawer(display=None): import machine # DON'T configure BOOT button (Pin 0) as wake-up source because it wakes up immediately. # Luckily, the RESET button can be used to wake it up. - #wake_pin = Pin(0, machine.Pin.IN, machine.Pin.PULL_UP) # Pull-up enabled, active low + #wake_pin = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP) # Pull-up enabled, active low #import esp32 #esp32.wake_on_ext0(pin=wake_pin, level=esp32.WAKEUP_ALL_LOW) print("Entering deep sleep. Press BOOT button to wake up.")