From 5bd13a5cd1cbafda75737312c02cf062a2275289 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 24 Mar 2022 23:17:25 +0000 Subject: [PATCH] Badger2040: Create badger_os utils module. --- micropython/examples/badger2040/badger_os.py | 104 ++++++++++++++++++ micropython/examples/badger2040/image.py | 60 +++++----- micropython/examples/badger2040/launcher.py | 59 ++-------- .../badger2040/micropython-builtins.cmake | 1 + micropython/modules/badger2040/badger2040.cpp | 2 +- 5 files changed, 146 insertions(+), 80 deletions(-) create mode 100644 micropython/examples/badger2040/badger_os.py diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py new file mode 100644 index 00000000..f9ededb8 --- /dev/null +++ b/micropython/examples/badger2040/badger_os.py @@ -0,0 +1,104 @@ +"""Keep track of app state in persistent flash storage.""" + +import os +import gc +import machine +import badger2040 + +STATE_FILE = "appstate.txt" + + +def get_battery_level(): + # Battery measurement + vbat_adc = machine.ADC(badger2040.PIN_BATTERY) + vref_adc = machine.ADC(badger2040.PIN_1V2_REF) + vref_en = machine.Pin(badger2040.PIN_VREF_POWER) + vref_en.init(machine.Pin.OUT) + vref_en.value(0) + + # Enable the onboard voltage reference + vref_en.value(1) + + # Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries + vdd = 1.24 * (65535 / vref_adc.read_u16()) + vbat = ( + (vbat_adc.read_u16() / 65535) * 3 * vdd + ) # 3 in this is a gain, not rounding of 3.3V + + # Disable the onboard voltage reference + vref_en.value(0) + + # Convert the voltage to a level to display onscreen + return vbat + + +def get_disk_usage(): + # f_bfree and f_bavail should be the same? + # f_files, f_ffree, f_favail and f_flag are unsupported. + f_bsize, f_frsize, f_blocks, f_bfree, _, _, _, _, _, f_namemax = os.statvfs("/") + + f_total_size = f_frsize * f_blocks + f_total_free = f_bsize * f_bfree + f_total_used = f_total_size - f_total_free + + f_used = 100 / f_total_size * f_total_used + f_free = 100 / f_total_size * f_total_free + + return f_total_size, f_used, f_free + + +def state_app(): + try: + with open(STATE_FILE, "r") as f: + return f.readline().strip() + except OSError: + return None + + +def state_launch(): + app = state_app() + if app is not None: + launch("_" + app) + + +def state_delete(): + try: + os.remove(STATE_FILE) + except OSError: + pass + + +def state_save(title, *args): + with open(STATE_FILE, "w") as f: + f.write("{}\n".format(title)) + for arg in args: + f.write("{}\n".format(arg)) + + +def state_load(title, *defaults): + data = [] + try: + with open(STATE_FILE, "r") as f: + if f.readline().strip() != title: + return defaults + for default in defaults: + t = type(default) + if t is bool: + data.append(f.readline().strip() == "True") + else: + data.append(t(f.readline().strip())) + return data + except OSError: + return defaults + + +def launch(file): + for k in locals().keys(): + if k not in ("gc", "file", "machine"): + del locals()[k] + gc.collect() + try: + __import__(file[1:]) # Try to import _[file] (drop underscore prefix) + except ImportError: + __import__(file) # Failover to importing [_file] + machine.reset() # Exit back to launcher diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index 7f81b4cd..d0f48432 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -3,6 +3,7 @@ import sys import time import badger2040 from badger2040 import WIDTH, HEIGHT +import badger_os REAMDE = """ @@ -52,14 +53,6 @@ image = bytearray(int(296 * 128 / 8)) current_image = 0 show_info = True -try: - with open("appstate.txt", "r") as f: - f.readline() - current_image = int(f.readline().strip('\n')) - show_info = f.readline().strip('\n') == "True" -except OSError: - pass - # Draw an overlay box with a given message within it def draw_overlay(message, width, height, line_spacing, text_size): @@ -127,28 +120,37 @@ if TOTAL_IMAGES == 0: display.update() sys.exit() -if display.pressed_to_wake(badger2040.BUTTON_UP): - if current_image > 0: - current_image -= 1 -if display.pressed_to_wake(badger2040.BUTTON_DOWN): - if current_image < TOTAL_IMAGES - 1: - current_image += 1 -if display.pressed_to_wake(badger2040.BUTTON_A): - show_info = not show_info -if display.pressed_to_wake(badger2040.BUTTON_B) or display.pressed_to_wake(badger2040.BUTTON_C): - display.pen(15) - display.clear() - draw_overlay("To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, 0.5) - display.update() - time.sleep(4) -show_image(current_image) +current_image, show_info = badger_os.state_load("image", 0, True) + +changed = not display.woken() -# Tell launcher to relaunch this app on wake and record state -with open("appstate.txt", "w") as f: - f.write("image\n") - f.write("{}\n{}\n".format(current_image, show_info)) +while True: + if display.pressed(badger2040.BUTTON_UP): + if current_image > 0: + current_image -= 1 + changed = True + if display.pressed(badger2040.BUTTON_DOWN): + if current_image < TOTAL_IMAGES - 1: + current_image += 1 + changed = True + if display.pressed(badger2040.BUTTON_A): + show_info = not show_info + changed = True + if display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C): + display.pen(15) + display.clear() + draw_overlay("To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, 0.5) + display.update() + print(current_image) + time.sleep(4) + changed = True -# Halt the Badger to save power, it will wake up if any of the front buttons are pressed -display.halt() + if changed: + badger_os.state_save("image", current_image, show_info) + show_image(current_image) + changed = False + + # Halt the Badger to save power, it will wake up if any of the front buttons are pressed + display.halt() diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 740fa99e..348fd2e1 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -1,11 +1,10 @@ -import gc -import os import time import math import machine import badger2040 from badger2040 import WIDTH import launchericons +import badger_os # for e.g. 2xAAA batteries, try max 3.4 min 3.0 MAX_BATTERY_VOLTAGE = 4.0 @@ -15,33 +14,18 @@ MIN_BATTERY_VOLTAGE = 3.2 machine.freq(48000000) -def launch(file): - for k in locals().keys(): - if k not in ("gc", "file", "machine"): - del locals()[k] - gc.collect() - try: - __import__(file[1:]) # Try to import _[file] (drop underscore prefix) - except ImportError: - __import__(file) # Failover to importing [_file] - machine.reset() # Exit back to launcher - - # Restore previously running app try: # Pressing A and C together at start quits app if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C): - os.remove("appstate.txt") + badger_os.state_delete() else: - with open("appstate.txt", "r") as f: - # Try to launch app - launch("_" + f.readline().strip('\n')) + badger_os.state_launch() except OSError: pass except ImportError: # Happens if appstate names an unknown app. Delete appstate and reset - import os - os.remove("appstate.txt") + badger_os.state_delete() machine.reset() badger2040.clear_pressed_to_wake() @@ -96,23 +80,6 @@ def map_value(input, in_min, in_max, out_min, out_max): return (((input - in_min) * (out_max - out_min)) / (in_max - in_min)) + out_min -def get_battery_level(): - # Enable the onboard voltage reference - vref_en.value(1) - - # Calculate the logic supply voltage, as will be lower that the usual 3.3V when running off low batteries - vdd = 1.24 * (65535 / vref_adc.read_u16()) - vbat = ( - (vbat_adc.read_u16() / 65535) * 3 * vdd - ) # 3 in this is a gain, not rounding of 3.3V - - # Disable the onboard voltage reference - vref_en.value(0) - - # Convert the voltage to a level to display onscreen - return int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4)) - - def draw_battery(level, x, y): # Outline display.thickness(1) @@ -138,17 +105,7 @@ def draw_battery(level, x, y): def draw_disk_usage(x): - # f_bfree and f_bavail should be the same? - # f_files, f_ffree, f_favail and f_flag are unsupported. - f_bsize, f_frsize, f_blocks, f_bfree, _, _, _, _, _, f_namemax = os.statvfs( - "/") - - f_total_size = f_frsize * f_blocks - f_total_free = f_bsize * f_bfree - f_total_used = f_total_size - f_total_free - - f_used = 100 / f_total_size * f_total_used - # f_free = 100 / f_total_size * f_total_free + _, f_used, _ = badger_os.get_disk_usage() display.image( bytearray( @@ -207,7 +164,9 @@ def render(): display.rectangle(0, 0, WIDTH, 16) display.thickness(1) draw_disk_usage(90) - draw_battery(get_battery_level(), WIDTH - 22 - 3, 3) + vbat = badger_os.get_battery_level() + bat = int(map_value(vbat, MIN_BATTERY_VOLTAGE, MAX_BATTERY_VOLTAGE, 0, 4)) + draw_battery(bat, WIDTH - 22 - 3, 3) display.pen(15) display.text("badgerOS", 3, 8, 0.4) @@ -218,7 +177,7 @@ def launch_example(index): while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value(): time.sleep(0.01) try: - launch(examples[(page * 3) + index][0]) + badger_os.launch(examples[(page * 3) + index][0]) return True except IndexError: return False diff --git a/micropython/examples/badger2040/micropython-builtins.cmake b/micropython/examples/badger2040/micropython-builtins.cmake index 1e748026..6b5ec56f 100644 --- a/micropython/examples/badger2040/micropython-builtins.cmake +++ b/micropython/examples/badger2040/micropython-builtins.cmake @@ -52,3 +52,4 @@ copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badge.py _badge) copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/help.py _help) copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/info.py _info) copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/qrgen.py _qrgen) +copy_module(usermod_badger2040 ${CMAKE_CURRENT_LIST_DIR}/badger_os.py badger_os) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 43cefcd9..a08b3223 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -7,7 +7,7 @@ namespace { struct Badger2040_WakeUpInit { Badger2040_WakeUpInit() - : state(gpio_get_all() & (0x1f << 11)) // Record state of all the front buttons + : state(gpio_get_all() & (0x1f << pimoroni::Badger2040::DOWN)) // Record state of front buttons { gpio_set_function(pimoroni::Badger2040::ENABLE_3V3, GPIO_FUNC_SIO); gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT);