From 51b0e472e13a5cfe9b10fbdc53701b3a9e67dd5c Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Wed, 23 Mar 2022 00:12:10 +0000 Subject: [PATCH 01/32] Add ability to halt the Badger from Micropython and get wake button presses. Change badge app to halt when image displayed --- micropython/examples/badger2040/badge.py | 75 ++++++------------- micropython/examples/badger2040/launcher.py | 49 ++++++++---- micropython/modules/badger2040/badger2040.c | 14 +++- micropython/modules/badger2040/badger2040.cpp | 31 +++++++- micropython/modules/badger2040/badger2040.h | 7 +- 5 files changed, 106 insertions(+), 70 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index fdeae159b..5f930fe97 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -205,63 +205,34 @@ def draw_badge(): detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE, TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) -# Set up the buttons -button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN) - - -# Button handling function -def button(pin): - global show_overlay - - if pin == button_a: - show_overlay = True - return - - if pin == button_b: - show_overlay = True - return - - if pin == button_c: - show_overlay = True - return - - if pin == button_up: - show_overlay = True - return - - if pin == button_down: - show_overlay = True - return - - -# Register the button handling function with the buttons -button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button) - +# Show overlay if any of the buttons were pressed to wake up the Badger +if (badger2040.pressed_to_wake(badger2040.BUTTON_A) or + badger2040.pressed_to_wake(badger2040.BUTTON_B) or + badger2040.pressed_to_wake(badger2040.BUTTON_C) or + badger2040.pressed_to_wake(badger2040.BUTTON_UP) or + badger2040.pressed_to_wake(badger2040.BUTTON_DOWN)): + show_overlay = True # ------------------------------ -# Main program loop +# Main program # ------------------------------ draw_badge() -display.update() -while True: - if show_overlay: - draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", - WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) - display.update() - time.sleep(4) +if show_overlay: + draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", + WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) + display.update() + time.sleep(4) + + draw_badge() + display.update() +else: + display.update() - draw_badge() - display.update() - show_overlay = False +# Tell launcher to relaunch this app on wake +with open("appstate.txt", "w") as f: + f.write("badge\n") - time.sleep(0.1) +# 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 0f0c10823..f27098c88 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -11,6 +11,39 @@ MAX_BATTERY_VOLTAGE = 4.0 MIN_BATTERY_VOLTAGE = 3.2 +# Reduce clock speed to 48MHz, that's fast enough! +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") + else: + with open("appstate.txt", "r") as f: + # Try to launch app + launch("_" + f.readline().strip('\n')) +except OSError: + pass +except ImportError: + # Happens if appstate names an unknown app. Delete appstate and reset + import os + os.remove("appstate.txt") + machine.reset() + +badger2040.clear_pressed_to_wake() + page = 0 font_size = 1 @@ -179,19 +212,9 @@ def render(): display.update() -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 - - 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]) return True @@ -241,7 +264,7 @@ def button(pin): # Wait for wakeup button to be released while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value(): - pass + time.sleep(0.01) while True: diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 3a09276a8..0693f88d6 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -9,6 +9,8 @@ MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_is_busy_obj, Badger2040_is_busy); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_update_obj, Badger2040_update); MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_partial_update_obj, 4, Badger2040_partial_update); +MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt); + MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_invert_obj, Badger2040_invert); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_led_obj, Badger2040_led); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_font_obj, Badger2040_font); @@ -32,6 +34,9 @@ MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_measure_glyph_obj, 2, Badger2040_measure_g MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); +MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake); +MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_clear_pressed_to_wake_obj, Badger2040_clear_pressed_to_wake); + /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&Badger2040___del___obj) }, @@ -40,6 +45,8 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_update), MP_ROM_PTR(&Badger2040_update_obj) }, { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Badger2040_partial_update_obj) }, + { MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&Badger2040_halt_obj) }, + { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&Badger2040_invert_obj) }, { MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&Badger2040_led_obj) }, { MP_ROM_QSTR(MP_QSTR_font), MP_ROM_PTR(&Badger2040_font_obj) }, @@ -78,10 +85,13 @@ const mp_obj_type_t Badger2040_type = { /***** Globals Table *****/ -STATIC const mp_map_elem_t badger2040_globals_table[] = { +STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_badger2040) }, { MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type }, + { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_pressed_to_wake), MP_ROM_PTR(&Badger2040_clear_pressed_to_wake_obj) }, + { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_FAST), MP_ROM_INT(2) }, @@ -121,4 +131,4 @@ const mp_obj_module_t badger2040_user_cmodule = { .base = { &mp_type_module }, .globals = (mp_obj_dict_t*)&mp_module_badger2040_globals, }; -MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule, MODULE_BADGER2040_ENABLED); \ No newline at end of file +MP_REGISTER_MODULE(MP_QSTR_badger2040, badger2040_user_cmodule, MODULE_BADGER2040_ENABLED); diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 657b04000..79e9232e8 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -3,6 +3,19 @@ #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) +namespace { + struct Badger2040_ButtonStateOnWake { + Badger2040_ButtonStateOnWake() + : state(gpio_get_all()) + {} + + uint32_t get() const { return state; } + void clear() { state = 0; } + + private: + uint32_t state; + } button_wake_state __attribute__ ((init_priority (101))); +}; extern "C" { #include "badger2040.h" @@ -170,7 +183,11 @@ MICROPY_EVENT_POLL_HOOK return mp_const_none; } -// halt +mp_obj_t Badger2040_halt(mp_obj_t self_in) { + _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t); + self->badger2040->halt(); + return mp_const_none; +} // sleep mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert) { @@ -210,6 +227,16 @@ mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button) { return state ? mp_const_true : mp_const_false; } +mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button) { + bool state = (button_wake_state.get() >> mp_obj_get_int(button)) & 1; + return state ? mp_const_true : mp_const_false; +} + +mp_obj_t Badger2040_clear_pressed_to_wake() { + button_wake_state.clear(); + return mp_const_none; +} + // pressed // pressed_to_wake // wait_for_press - implement in terms of MicroPython! @@ -467,4 +494,4 @@ mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_ma return mp_obj_new_int(self->badger2040->measure_glyph(c, scale)); } -} \ No newline at end of file +} diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index b60a31541..62c55f75a 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -15,6 +15,8 @@ extern mp_obj_t Badger2040_update_speed(mp_obj_t self_in, mp_obj_t speed); extern mp_obj_t Badger2040_update(mp_obj_t self_in); extern mp_obj_t Badger2040_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +extern mp_obj_t Badger2040_halt(mp_obj_t self_in); + extern mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert); extern mp_obj_t Badger2040_led(mp_obj_t self_in, mp_obj_t brightness); extern mp_obj_t Badger2040_font(mp_obj_t self_in, mp_obj_t font); @@ -37,4 +39,7 @@ extern mp_obj_t Badger2040_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map extern mp_obj_t Badger2040_measure_text(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); -extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data); \ No newline at end of file +extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data); + +extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button); +extern mp_obj_t Badger2040_clear_pressed_to_wake(); From 78c11d220d28a2b3ceb822122d4869fa4920c1b4 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Wed, 23 Mar 2022 21:41:19 +0000 Subject: [PATCH 02/32] Enable battery power on wake, expose pressed_to_wake on Badger object --- micropython/examples/badger2040/badge.py | 10 +++---- micropython/modules/badger2040/badger2040.c | 2 ++ micropython/modules/badger2040/badger2040.cpp | 28 ++++++++++++++++--- micropython/modules/badger2040/badger2040.h | 1 + 4 files changed, 32 insertions(+), 9 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index 5f930fe97..de0d2ca9b 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -206,11 +206,11 @@ def draw_badge(): TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) # Show overlay if any of the buttons were pressed to wake up the Badger -if (badger2040.pressed_to_wake(badger2040.BUTTON_A) or - badger2040.pressed_to_wake(badger2040.BUTTON_B) or - badger2040.pressed_to_wake(badger2040.BUTTON_C) or - badger2040.pressed_to_wake(badger2040.BUTTON_UP) or - badger2040.pressed_to_wake(badger2040.BUTTON_DOWN)): +if (display.pressed_to_wake(badger2040.BUTTON_A) or + display.pressed_to_wake(badger2040.BUTTON_B) or + display.pressed_to_wake(badger2040.BUTTON_C) or + display.pressed_to_wake(badger2040.BUTTON_UP) or + display.pressed_to_wake(badger2040.BUTTON_DOWN)): show_overlay = True # ------------------------------ diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 0693f88d6..18d63b410 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -18,6 +18,7 @@ MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pen_obj, Badger2040_pen); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_thickness_obj, Badger2040_thickness); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pressed_obj, Badger2040_pressed); +MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_pressed_to_wake2_obj, Badger2040_pressed_to_wake2); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_clear_obj, Badger2040_clear); MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_pixel_obj, Badger2040_pixel); MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_line_obj, 4, Badger2040_line); @@ -54,6 +55,7 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_thickness), MP_ROM_PTR(&Badger2040_thickness_obj) }, { MP_ROM_QSTR(MP_QSTR_pressed), MP_ROM_PTR(&Badger2040_pressed_obj) }, + { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake2_obj) }, { MP_ROM_QSTR(MP_QSTR_clear), MP_ROM_PTR(&Badger2040_clear_obj) }, { MP_ROM_QSTR(MP_QSTR_pixel), MP_ROM_PTR(&Badger2040_pixel_obj) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 79e9232e8..72d92ed9d 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -1,13 +1,17 @@ #include +#include "hardware/watchdog.h" #include "badger2040.hpp" #define MP_OBJ_TO_PTR2(o, t) ((t *)(uintptr_t)(o)) namespace { - struct Badger2040_ButtonStateOnWake { - Badger2040_ButtonStateOnWake() + struct Badger2040_WakeUpInit { + Badger2040_WakeUpInit() : state(gpio_get_all()) - {} + { + gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT); + gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); + } uint32_t get() const { return state; } void clear() { state = 0; } @@ -185,7 +189,19 @@ MICROPY_EVENT_POLL_HOOK mp_obj_t Badger2040_halt(mp_obj_t self_in) { _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t); - self->badger2040->halt(); + + // Don't use the Badger halt so we can allow Micropython to be interrupted. + gpio_put(pimoroni::Badger2040::ENABLE_3V3, 0); + + self->badger2040->update_button_states(); + while (self->badger2040->button_states() == 0) { +#ifdef MICROPY_EVENT_POLL_HOOK +MICROPY_EVENT_POLL_HOOK +#endif + self->badger2040->update_button_states(); + } + watchdog_reboot(0, SRAM_END, 0); + return mp_const_none; } // sleep @@ -232,6 +248,10 @@ mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button) { return state ? mp_const_true : mp_const_false; } +mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) { + return Badger2040_pressed_to_wake(button); +} + mp_obj_t Badger2040_clear_pressed_to_wake() { button_wake_state.clear(); return mp_const_none; diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index 62c55f75a..ea486550e 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -24,6 +24,7 @@ extern mp_obj_t Badger2040_pen(mp_obj_t self_in, mp_obj_t color); extern mp_obj_t Badger2040_thickness(mp_obj_t self_in, mp_obj_t thickness); extern mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button); +extern mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button); extern mp_obj_t Badger2040_clear(mp_obj_t self_in); extern mp_obj_t Badger2040_pixel(mp_obj_t self_in, mp_obj_t x, mp_obj_t y); From a901064259dc64d791f42b7ca273f5692eaea521 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Wed, 23 Mar 2022 21:42:18 +0000 Subject: [PATCH 03/32] Use halt in image app --- micropython/examples/badger2040/image.py | 53 ++++++++++++------------ 1 file changed, 26 insertions(+), 27 deletions(-) diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index d8956a273..b5030752b 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -49,17 +49,17 @@ display = badger2040.Badger2040() -button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) - -button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN) - 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 +127,27 @@ def show_image(n): 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) -while True: - if button_up.value(): - if current_image > 0: - current_image -= 1 - show_image(current_image) - if button_down.value(): - if current_image < TOTAL_IMAGES - 1: - current_image += 1 - show_image(current_image) - if button_a.value(): - show_info = not show_info - show_image(current_image) - if button_b.value() or button_c.value(): - 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) +with open("appstate.txt", "w") as f: + f.write("image\n") + f.write("{}\n{}\n".format(current_image,show_info)) + +display.halt() - time.sleep(0.01) From 40d22a02ff50c5ab31cb51b5e942c728c0a0cd92 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Wed, 23 Mar 2022 22:42:46 +0000 Subject: [PATCH 04/32] Appease the linter --- micropython/examples/badger2040/badge.py | 13 ++++++------- micropython/examples/badger2040/image.py | 7 ++++--- micropython/examples/badger2040/launcher.py | 2 ++ 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index de0d2ca9b..af6987116 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -1,5 +1,4 @@ import badger2040 -import machine import time # Global Constants @@ -206,12 +205,12 @@ def draw_badge(): TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) # Show overlay if any of the buttons were pressed to wake up the Badger -if (display.pressed_to_wake(badger2040.BUTTON_A) or - display.pressed_to_wake(badger2040.BUTTON_B) or - display.pressed_to_wake(badger2040.BUTTON_C) or - display.pressed_to_wake(badger2040.BUTTON_UP) or - display.pressed_to_wake(badger2040.BUTTON_DOWN)): - show_overlay = True +if (display.pressed_to_wake(badger2040.BUTTON_A) + or display.pressed_to_wake(badger2040.BUTTON_B) + or display.pressed_to_wake(badger2040.BUTTON_C) + or display.pressed_to_wake(badger2040.BUTTON_UP) + or display.pressed_to_wake(badger2040.BUTTON_DOWN)): + show_overlay = True # ------------------------------ # Main program diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index b5030752b..7f81b4cdc 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -1,7 +1,6 @@ import os import sys import time -import machine import badger2040 from badger2040 import WIDTH, HEIGHT @@ -61,6 +60,7 @@ except OSError: pass + # Draw an overlay box with a given message within it def draw_overlay(message, width, height, line_spacing, text_size): @@ -145,9 +145,10 @@ def show_image(n): show_image(current_image) +# 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)) + f.write("{}\n{}\n".format(current_image, show_info)) +# 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 f27098c88..740fa99eb 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -14,6 +14,7 @@ # Reduce clock speed to 48MHz, that's fast enough! machine.freq(48000000) + def launch(file): for k in locals().keys(): if k not in ("gc", "file", "machine"): @@ -25,6 +26,7 @@ def launch(file): __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 From a0baae33a62b965b3da288337edf9e61448d4129 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Wed, 23 Mar 2022 22:48:47 +0000 Subject: [PATCH 05/32] Once more with a very long line --- micropython/examples/badger2040/badge.py | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index af6987116..d8b052e11 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -205,12 +205,8 @@ def draw_badge(): TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) # Show overlay if any of the buttons were pressed to wake up the Badger -if (display.pressed_to_wake(badger2040.BUTTON_A) - or display.pressed_to_wake(badger2040.BUTTON_B) - or display.pressed_to_wake(badger2040.BUTTON_C) - or display.pressed_to_wake(badger2040.BUTTON_UP) - or display.pressed_to_wake(badger2040.BUTTON_DOWN)): - show_overlay = True +if (display.pressed_to_wake(badger2040.BUTTON_A) or display.pressed_to_wake(badger2040.BUTTON_B) or display.pressed_to_wake(badger2040.BUTTON_C) or display.pressed_to_wake(badger2040.BUTTON_UP) or display.pressed_to_wake(badger2040.BUTTON_DOWN)): + show_overlay = True # ------------------------------ # Main program From c41714c4c98b015262eca7fc9d3b8d6738aeafdc Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 24 Mar 2022 00:24:26 +0000 Subject: [PATCH 06/32] Actually enable 3v3 at startup --- micropython/modules/badger2040/badger2040.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 72d92ed9d..f15468da8 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -9,6 +9,7 @@ namespace { Badger2040_WakeUpInit() : state(gpio_get_all()) { + gpio_set_function(pimoroni::Badger2040::ENABLE_3V3, GPIO_FUNC_SIO); gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT); gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); } From 946de6ffa26d95ad07e3c3272cfdb4e959d55c3f Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 24 Mar 2022 20:49:51 +0000 Subject: [PATCH 07/32] Take Gadgetoid's proposed change, reduce magic --- micropython/modules/badger2040/badger2040.cpp | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index f15468da8..1736daaaa 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -14,12 +14,23 @@ namespace { gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); } - uint32_t get() const { return state; } + bool get(uint32_t pin) const { + return state & (0b1 << pin); + } + + bool get_once(uint32_t pin) { + uint32_t mask = 0b1 << pin; + bool value = state & mask; + state &= ~mask; + return value; + } void clear() { state = 0; } private: uint32_t state; - } button_wake_state __attribute__ ((init_priority (101))); + }; + + Badger2040_WakeUpInit button_wake_state __attribute__ ((init_priority (101))); }; extern "C" { @@ -201,7 +212,7 @@ MICROPY_EVENT_POLL_HOOK #endif self->badger2040->update_button_states(); } - watchdog_reboot(0, SRAM_END, 0); + //watchdog_reboot(0, SRAM_END, 0); return mp_const_none; } @@ -240,12 +251,13 @@ mp_obj_t Badger2040_thickness(mp_obj_t self_in, mp_obj_t thickness) { mp_obj_t Badger2040_pressed(mp_obj_t self_in, mp_obj_t button) { _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t); self->badger2040->update_button_states(); + bool wake_state = button_wake_state.get_once(mp_obj_get_int(button)); bool state = self->badger2040->pressed(mp_obj_get_int(button)); - return state ? mp_const_true : mp_const_false; + return (state || wake_state) ? mp_const_true : mp_const_false; } mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button) { - bool state = (button_wake_state.get() >> mp_obj_get_int(button)) & 1; + bool state = button_wake_state.get(mp_obj_get_int(button)); return state ? mp_const_true : mp_const_false; } From a7f2014309b8f745a2fdcb3ca00f313d7c42b4c1 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 24 Mar 2022 21:21:20 +0000 Subject: [PATCH 08/32] Add woken method --- micropython/modules/badger2040/badger2040.c | 2 ++ micropython/modules/badger2040/badger2040.cpp | 9 +++++++++ micropython/modules/badger2040/badger2040.h | 1 + 3 files changed, 12 insertions(+) diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 18d63b410..536811d06 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -10,6 +10,7 @@ MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_update_obj, Badger2040_update); MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_partial_update_obj, 4, Badger2040_partial_update); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt); +MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_woken_obj, Badger2040_woken); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_invert_obj, Badger2040_invert); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_led_obj, Badger2040_led); @@ -47,6 +48,7 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Badger2040_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&Badger2040_halt_obj) }, + { MP_ROM_QSTR(MP_QSTR_woken), MP_ROM_PTR(&Badger2040_woken_obj) }, { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&Badger2040_invert_obj) }, { MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&Badger2040_led_obj) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 1736daaaa..c3c0c6c23 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -14,6 +14,10 @@ namespace { gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); } + bool any() const { + return state > 0; + } + bool get(uint32_t pin) const { return state & (0b1 << pin); } @@ -199,6 +203,11 @@ MICROPY_EVENT_POLL_HOOK return mp_const_none; } +mp_obj_t Badger2040_woken(mp_obj_t self_in) { + (void)self_in; + return button_wake_state.any() ? mp_const_true : mp_const_false; +} + mp_obj_t Badger2040_halt(mp_obj_t self_in) { _Badger2040_obj_t *self = MP_OBJ_TO_PTR2(self_in, _Badger2040_obj_t); diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index ea486550e..0586b3e19 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -16,6 +16,7 @@ extern mp_obj_t Badger2040_update(mp_obj_t self_in); extern mp_obj_t Badger2040_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_halt(mp_obj_t self_in); +extern mp_obj_t Badger2040_woken(mp_obj_t self_in); extern mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert); extern mp_obj_t Badger2040_led(mp_obj_t self_in, mp_obj_t brightness); From 0ab44ef10a13d717b203ef6a39111ff0695f6353 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 24 Mar 2022 22:01:10 +0000 Subject: [PATCH 09/32] Only record front button state at init time --- micropython/modules/badger2040/badger2040.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index c3c0c6c23..43cefcd96 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()) + : state(gpio_get_all() & (0x1f << 11)) // Record state of all the front buttons { gpio_set_function(pimoroni::Badger2040::ENABLE_3V3, GPIO_FUNC_SIO); gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT); From 5bd13a5cd1cbafda75737312c02cf062a2275289 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Thu, 24 Mar 2022 23:17:25 +0000 Subject: [PATCH 10/32] 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 000000000..f9ededb8a --- /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 7f81b4cdc..d0f484323 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -3,6 +3,7 @@ import time import badger2040 from badger2040 import WIDTH, HEIGHT +import badger_os REAMDE = """ @@ -52,14 +53,6 @@ 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 @@ def show_image(n): 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)) -# Halt the Badger to save power, it will wake up if any of the front buttons are pressed -display.halt() +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 + + 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 740fa99eb..348fd2e16 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 @@ 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 1e748026f..6b5ec56fd 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 43cefcd96..a08b32233 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); From fe943a05f7fcaf5fef06b09a58430987fbe950d2 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Thu, 24 Mar 2022 23:18:09 +0000 Subject: [PATCH 11/32] Include USER button in button state --- libraries/badger2040/badger2040.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/badger2040/badger2040.cpp b/libraries/badger2040/badger2040.cpp index 451fb0973..36b220feb 100644 --- a/libraries/badger2040/badger2040.cpp +++ b/libraries/badger2040/badger2040.cpp @@ -40,7 +40,7 @@ namespace pimoroni { gpio_set_function(USER, GPIO_FUNC_SIO); gpio_set_dir(USER, GPIO_IN); - gpio_set_pulls(USER, false, true); + gpio_set_pulls(USER, true, false); gpio_set_function(VBUS_DETECT, GPIO_FUNC_SIO); gpio_set_dir(VBUS_DETECT, GPIO_IN); @@ -195,8 +195,9 @@ namespace pimoroni { } void Badger2040::update_button_states() { - uint32_t mask = (1UL << A) | (1UL << B) | (1UL << C) | (1UL << D) | (1UL << E); + uint32_t mask = (1UL << A) | (1UL << B) | (1UL << C) | (1UL << D) | (1UL << E) | (1UL << USER); _button_states = gpio_get_all() & mask; + _button_states ^= (1UL << USER); // USER button state is inverted } uint32_t Badger2040::button_states() { From 994ddba0e11fa474215652dea62c09cc321fd389 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 00:26:48 +0000 Subject: [PATCH 12/32] Badge, image and launcher all now sleep when on battery --- micropython/examples/badger2040/badge.py | 46 ++++----- micropython/examples/badger2040/image.py | 5 + micropython/examples/badger2040/launcher.py | 100 +++++++++----------- 3 files changed, 71 insertions(+), 80 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index d8b052e11..a599e51df 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -1,5 +1,6 @@ -import badger2040 import time +import badger2040 +import badger_os # Global Constants WIDTH = badger2040.WIDTH @@ -169,9 +170,6 @@ def draw_badge(): # Program setup # ------------------------------ -# Global variables -show_overlay = False - # Create a new Badger and set it to update NORMAL display = badger2040.Badger2040() display.update_speed(badger2040.UPDATE_NORMAL) @@ -204,9 +202,10 @@ def draw_badge(): detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE, TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) -# Show overlay if any of the buttons were pressed to wake up the Badger -if (display.pressed_to_wake(badger2040.BUTTON_A) or display.pressed_to_wake(badger2040.BUTTON_B) or display.pressed_to_wake(badger2040.BUTTON_C) or display.pressed_to_wake(badger2040.BUTTON_UP) or display.pressed_to_wake(badger2040.BUTTON_DOWN)): - show_overlay = True +# Tell launcher to relaunch this app on wake +if not display.woken(): + badger_os.state_save("badge") + # ------------------------------ # Main program @@ -214,20 +213,21 @@ def draw_badge(): draw_badge() -if show_overlay: - draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", - WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) +while True: + # Pressing A and C together quits the app + if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): + break + + if display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): + draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", + WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) + display.update() + time.sleep(4) + + draw_badge() + display.update() - time.sleep(4) - - draw_badge() - display.update() -else: - display.update() - -# Tell launcher to relaunch this app on wake -with open("appstate.txt", "w") as f: - f.write("badge\n") - -# Halt the Badger to save power, it will wake up if any of the front buttons are pressed -display.halt() + + # If on battery, halt the Badger to save power, it will wake up if any of the front buttons are pressed + display.halt() + time.sleep(0.1) diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index d0f484323..05f1a0b75 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -127,6 +127,10 @@ def show_image(n): while True: + # Pressing A and C together quits the app + if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): + break + if display.pressed(badger2040.BUTTON_UP): if current_image > 0: current_image -= 1 @@ -154,3 +158,4 @@ def show_image(n): # Halt the Badger to save power, it will wake up if any of the front buttons are pressed display.halt() + time.sleep(0.1) diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 348fd2e16..66fc49770 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -20,7 +20,8 @@ if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C): badger_os.state_delete() else: - badger_os.state_launch() + if badger_os.state_app() != "launcher": + badger_os.state_launch() except OSError: pass except ImportError: @@ -28,12 +29,11 @@ badger_os.state_delete() machine.reset() -badger2040.clear_pressed_to_wake() +display = badger2040.Badger2040() -page = 0 -font_size = 1 -inverted = False +page, font_size, inverted = badger_os.state_load("launcher", 0, 1, False) +changed = badger_os.state_app() != "launcher" icons = bytearray(launchericons.data()) icons_width = 576 @@ -57,24 +57,6 @@ MAX_PAGE = math.ceil(len(examples) / 3) -button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN) -# Inverted. For reasons. -button_user = machine.Pin(badger2040.BUTTON_USER, machine.Pin.IN, machine.Pin.PULL_UP) - -# 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) - - -display = badger2040.Badger2040() - 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 @@ -174,7 +156,7 @@ def render(): def launch_example(index): - while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value(): + while display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): time.sleep(0.01) try: badger_os.launch(examples[(page * 3) + index][0]) @@ -184,61 +166,65 @@ def launch_example(index): def button(pin): - global page, font_size, inverted + global page, font_size, inverted, changed + changed = True - if button_user.value(): # User button is NOT held down - if pin == button_a: + if not display.pressed(badger2040.BUTTON_USER): # User button is NOT held down + if pin == badger2040.BUTTON_A: launch_example(0) - if pin == button_b: + if pin == badger2040.BUTTON_B: launch_example(1) - if pin == button_c: + if pin == badger2040.BUTTON_C: launch_example(2) - if pin == button_up: + if pin == badger2040.BUTTON_UP: if page > 0: page -= 1 - render() - if pin == button_down: + render() + if pin == badger2040.BUTTON_DOWN: if page < MAX_PAGE - 1: page += 1 - render() + render() else: # User button IS held down - if pin == button_up: + if pin == badger2040.BUTTON_UP: font_size += 1 if font_size == len(font_sizes): font_size = 0 render() - if pin == button_down: + if pin == badger2040.BUTTON_DOWN: font_size -= 1 if font_size < 0: font_size = 0 render() - if pin == button_a: + if pin == badger2040.BUTTON_A: inverted = not inverted display.invert(inverted) render() -display.update_speed(badger2040.UPDATE_MEDIUM) -render() -display.update_speed(badger2040.UPDATE_FAST) - - -# Wait for wakeup button to be released -while button_a.value() or button_b.value() or button_c.value() or button_up.value() or button_down.value(): - time.sleep(0.01) +if changed: + # Wait for any wakeup button to be released + while display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): + time.sleep(0.01) + display.update_speed(badger2040.UPDATE_MEDIUM) + render() +display.update_speed(badger2040.UPDATE_FAST) while True: - if button_a.value(): - button(button_a) - if button_b.value(): - button(button_b) - if button_c.value(): - button(button_c) - - if button_up.value(): - button(button_up) - if button_down.value(): - button(button_down) - - time.sleep(0.01) + if display.pressed(badger2040.BUTTON_A): + button(badger2040.BUTTON_A) + if display.pressed(badger2040.BUTTON_B): + button(badger2040.BUTTON_B) + if display.pressed(badger2040.BUTTON_C): + button(badger2040.BUTTON_C) + + if display.pressed(badger2040.BUTTON_UP): + button(badger2040.BUTTON_UP) + if display.pressed(badger2040.BUTTON_DOWN): + button(badger2040.BUTTON_DOWN) + + if changed: + badger_os.state_save("launcher", page, font_size, inverted) + changed = False + + display.halt() From e19fe9cb7300b7b4e5e441aa6c6d028a25dd648d Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 00:29:09 +0000 Subject: [PATCH 13/32] Apparently Thonny doesn't clear blank lines on save --- micropython/examples/badger2040/badge.py | 6 +++--- micropython/examples/badger2040/image.py | 2 +- micropython/examples/badger2040/launcher.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index a599e51df..dff8a120a 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -217,7 +217,7 @@ def draw_badge(): # Pressing A and C together quits the app if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): break - + if display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) @@ -225,9 +225,9 @@ def draw_badge(): time.sleep(4) draw_badge() - + display.update() - + # If on battery, halt the Badger to save power, it will wake up if any of the front buttons are pressed display.halt() time.sleep(0.1) diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index 05f1a0b75..22d61e07c 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -130,7 +130,7 @@ def show_image(n): # Pressing A and C together quits the app if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): break - + if display.pressed(badger2040.BUTTON_UP): if current_image > 0: current_image -= 1 diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 66fc49770..d07794fbd 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -226,5 +226,5 @@ def button(pin): if changed: badger_os.state_save("launcher", page, font_size, inverted) changed = False - + display.halt() From d313a9d8715b37fe2c6c517b860baa1e2866365f Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 01:04:13 +0000 Subject: [PATCH 14/32] Quit app on A+C pressed using an interrupt handler. Note if apps set up their own interrupt handlers then they take precedence, but in that case the app should be responsible for quitting. --- micropython/examples/badger2040/badge.py | 5 ----- micropython/examples/badger2040/badger_os.py | 16 ++++++++++++++++ micropython/examples/badger2040/image.py | 5 ----- micropython/examples/badger2040/launcher.py | 2 ++ 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index dff8a120a..ab0652e6c 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -214,10 +214,6 @@ def draw_badge(): draw_badge() while True: - # Pressing A and C together quits the app - if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): - break - if display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) @@ -230,4 +226,3 @@ def draw_badge(): # If on battery, halt the Badger to save power, it will wake up if any of the front buttons are pressed display.halt() - time.sleep(0.1) diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py index f9ededb8a..0aeb83d2d 100644 --- a/micropython/examples/badger2040/badger_os.py +++ b/micropython/examples/badger2040/badger_os.py @@ -97,6 +97,22 @@ def launch(file): if k not in ("gc", "file", "machine"): del locals()[k] gc.collect() + + button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) + button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) + + def quit_to_launcher(pin): + if button_a.value() and button_c.value(): + import os + try: + os.remove(STATE_FILE) + except OSError: + pass + machine.reset() + + button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher) + button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher) + try: __import__(file[1:]) # Try to import _[file] (drop underscore prefix) except ImportError: diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index 22d61e07c..d0f484323 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -127,10 +127,6 @@ def show_image(n): while True: - # Pressing A and C together quits the app - if display.pressed(badger2040.BUTTON_A) and display.pressed(badger2040.BUTTON_C): - break - if display.pressed(badger2040.BUTTON_UP): if current_image > 0: current_image -= 1 @@ -158,4 +154,3 @@ def show_image(n): # Halt the Badger to save power, it will wake up if any of the front buttons are pressed display.halt() - time.sleep(0.1) diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index d07794fbd..0e2f48664 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -209,6 +209,8 @@ def button(pin): render() display.update_speed(badger2040.UPDATE_FAST) +if not changed and not display.woken(): + render() while True: if display.pressed(badger2040.BUTTON_A): From f332dcdf2efbf70e4840656b63f5646f3af97077 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 01:23:17 +0000 Subject: [PATCH 15/32] Remove unused clear_pressed_to_wake method --- micropython/modules/badger2040/badger2040.c | 2 -- micropython/modules/badger2040/badger2040.cpp | 5 ----- micropython/modules/badger2040/badger2040.h | 1 - 3 files changed, 8 deletions(-) diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 536811d06..94896e7f6 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -37,7 +37,6 @@ MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_measure_glyph_obj, 2, Badger2040_measure_g MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake); -MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_clear_pressed_to_wake_obj, Badger2040_clear_pressed_to_wake); /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { @@ -94,7 +93,6 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type }, { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) }, - { MP_ROM_QSTR(MP_QSTR_clear_pressed_to_wake), MP_ROM_PTR(&Badger2040_clear_pressed_to_wake_obj) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index a08b32233..837bedfcc 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -274,11 +274,6 @@ mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) { return Badger2040_pressed_to_wake(button); } -mp_obj_t Badger2040_clear_pressed_to_wake() { - button_wake_state.clear(); - return mp_const_none; -} - // pressed // pressed_to_wake // wait_for_press - implement in terms of MicroPython! diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index 0586b3e19..34efed47c 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -44,4 +44,3 @@ extern mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data); extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button); -extern mp_obj_t Badger2040_clear_pressed_to_wake(); From b1fd8936cf9bee61d1a5b132ec2200c03a4f0d1f Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 10:57:30 +0000 Subject: [PATCH 16/32] Badger2040: Enforce minimum update blocking time. --- drivers/uc8151/uc8151.cpp | 21 +++++++++++++++++++ drivers/uc8151/uc8151.hpp | 4 ++++ libraries/badger2040/badger2040.cpp | 4 ++++ libraries/badger2040/badger2040.hpp | 1 + micropython/modules/badger2040/badger2040.cpp | 10 +++++++-- 5 files changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/uc8151/uc8151.cpp b/drivers/uc8151/uc8151.cpp index 249ca3d95..5b7fd0993 100644 --- a/drivers/uc8151/uc8151.cpp +++ b/drivers/uc8151/uc8151.cpp @@ -323,6 +323,8 @@ namespace pimoroni { void UC8151::setup(uint8_t speed) { reset(); + _update_speed = speed; + if(speed == 0) { command(PSR, { RES_128x296 | LUT_OTP | FORMAT_BW | SHIFT_RIGHT | BOOSTER_ON | RESET_NONE @@ -468,6 +470,25 @@ namespace pimoroni { setup(speed); } + uint8_t UC8151::update_speed() { + return _update_speed; + } + + uint32_t UC8151::update_time() { + switch(_update_speed) { + case 0: + return 5500; + case 1: + return 2600; + case 2: + return 1000; + case 3: + return 300; + default: + return 5500; + } + } + void UC8151::partial_update(int x, int y, int w, int h, bool blocking) { // y is given in columns ("banks"), which are groups of 8 horiontal pixels // x is given in pixels diff --git a/drivers/uc8151/uc8151.hpp b/drivers/uc8151/uc8151.hpp index fb60c5a4b..051c33f34 100644 --- a/drivers/uc8151/uc8151.hpp +++ b/drivers/uc8151/uc8151.hpp @@ -146,6 +146,8 @@ namespace pimoroni { bool inverted = false; + uint8_t _update_speed = 0; + public: UC8151(uint16_t width, uint16_t height) : width(width), height(height), frame_buffer(new uint8_t[width * height / 8]) { @@ -199,6 +201,8 @@ namespace pimoroni { void invert(bool invert); void update_speed(uint8_t speed); + uint8_t update_speed(); + uint32_t update_time(); void update(bool blocking = true); void partial_update(int x, int y, int w, int h, bool blocking = true); void off(); diff --git a/libraries/badger2040/badger2040.cpp b/libraries/badger2040/badger2040.cpp index 36b220feb..695a56d7e 100644 --- a/libraries/badger2040/badger2040.cpp +++ b/libraries/badger2040/badger2040.cpp @@ -220,6 +220,10 @@ namespace pimoroni { uc8151.update_speed(speed); } + uint32_t Badger2040::update_time() { + return uc8151.update_time(); + } + void Badger2040::partial_update(int x, int y, int w, int h, bool blocking) { uc8151.partial_update(x, y, w, h, blocking); } diff --git a/libraries/badger2040/badger2040.hpp b/libraries/badger2040/badger2040.hpp index c640714c1..f9622d6f1 100644 --- a/libraries/badger2040/badger2040.hpp +++ b/libraries/badger2040/badger2040.hpp @@ -30,6 +30,7 @@ namespace pimoroni { void update(bool blocking=false); void partial_update(int x, int y, int w, int h, bool blocking=false); void update_speed(uint8_t speed); + uint32_t update_time(); void halt(); void sleep(); bool is_busy(); diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 837bedfcc..e40efc11c 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -149,9 +149,12 @@ MICROPY_EVENT_POLL_HOOK #endif } + absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time()); self->badger2040->update(false); - while(self->badger2040->is_busy()) { + // Ensure blocking for the minimum amount of time + // in cases where "is_busy" is unreliable. + while(self->badger2040->is_busy() || absolute_time_diff_us(t_end, get_absolute_time()) > 0) { #ifdef MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK #endif @@ -190,9 +193,12 @@ MICROPY_EVENT_POLL_HOOK #endif } + absolute_time_t t_end = make_timeout_time_ms(self->badger2040->update_time()); self->badger2040->partial_update(x, y, w, h); - while(self->badger2040->is_busy()) { + // Ensure blocking for the minimum amount of time + // in cases where "is_busy" is unreliable. + while(self->badger2040->is_busy() || absolute_time_diff_us(t_end, get_absolute_time()) > 0) { #ifdef MICROPY_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK #endif From a23cc59e3c11f248fd7698f99db25fe9a75f7f41 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 11:09:27 +0000 Subject: [PATCH 17/32] Badger2040: Use Act LED. Avoid recreating files on every load. --- micropython/examples/badger2040/image.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index d0f484323..402f5070f 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -22,13 +22,14 @@ TOTAL_IMAGES = 0 + +# Turn the act LED on as soon as possible +display = badger2040.Badger2040() +display.led(128) + # Try to preload BadgerPunk image try: os.mkdir("images") -except OSError: - pass - -try: import badgerpunk with open("images/badgerpunk.bin", "wb") as f: f.write(badgerpunk.data()) @@ -40,6 +41,7 @@ except (OSError, ImportError): pass +# Load images try: IMAGES = [f for f in os.listdir("/images") if f.endswith(".bin")] TOTAL_IMAGES = len(IMAGES) @@ -47,8 +49,6 @@ pass -display = badger2040.Badger2040() - image = bytearray(int(296 * 128 / 8)) current_image = 0 show_info = True From a862a6d310078f2e1e42184c5d35143ce9fc4e07 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 11:09:44 +0000 Subject: [PATCH 18/32] Badger2040: Use Act LED in Launcher --- micropython/examples/badger2040/launcher.py | 1 + 1 file changed, 1 insertion(+) diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 0e2f48664..90815767d 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -31,6 +31,7 @@ display = badger2040.Badger2040() +display.led(128) page, font_size, inverted = badger_os.state_load("launcher", 0, 1, False) changed = badger_os.state_app() != "launcher" From d3c88116569bfe365e19169fe1479e485350f6b6 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 11:35:52 +0000 Subject: [PATCH 19/32] Badger2040: Fixup examples for Act LED and A+C to exit. --- micropython/examples/badger2040/badge.py | 9 ++-- micropython/examples/badger2040/clock.py | 58 +++++++++++++----------- micropython/examples/badger2040/ebook.py | 12 ++++- micropython/examples/badger2040/fonts.py | 1 + micropython/examples/badger2040/help.py | 8 ++-- micropython/examples/badger2040/info.py | 7 ++- micropython/examples/badger2040/list.py | 8 ++++ micropython/examples/badger2040/qrgen.py | 9 ++-- 8 files changed, 67 insertions(+), 45 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index ab0652e6c..352c35dfd 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -172,16 +172,17 @@ def draw_badge(): # Create a new Badger and set it to update NORMAL display = badger2040.Badger2040() +display.led(128) display.update_speed(badger2040.UPDATE_NORMAL) # Open the badge file try: badge = open("badge.txt", "r") except OSError: - badge = open("badge.txt", "w") - badge.write(DEFAULT_TEXT) - badge.flush() - badge.seek(0) + with open("badge.txt", "w") as f: + f.write(DEFAULT_TEXT) + f.flush() + badge = open("badge.txt", "r") # Read in the next 6 lines company = badge.readline() # "mustelid inc" diff --git a/micropython/examples/badger2040/clock.py b/micropython/examples/badger2040/clock.py index 834ab5ed1..e1852ad77 100644 --- a/micropython/examples/badger2040/clock.py +++ b/micropython/examples/badger2040/clock.py @@ -4,9 +4,10 @@ rtc = machine.RTC() -screen = badger2040.Badger2040() -screen.update_speed(badger2040.UPDATE_TURBO) -screen.font("gothic") +display = badger2040.Badger2040() +display.led(128) +display.update_speed(badger2040.UPDATE_TURBO) +display.font("gothic") cursors = ["year", "month", "day", "hour", "minute"] set_clock = False @@ -36,6 +37,9 @@ def button(pin): if not pin.value(): return + if button_a.value() and button_c.value(): + machine.reset() + adjust = 0 changed = False @@ -94,42 +98,42 @@ def draw_clock(): hms = "{:02}:{:02}:{:02}".format(hour, minute, second) ymd = "{:04}/{:02}/{:02}".format(year, month, day) - hms_width = screen.measure_text(hms, 1.8) + hms_width = display.measure_text(hms, 1.8) hms_offset = int((badger2040.WIDTH / 2) - (hms_width / 2)) - h_width = screen.measure_text(hms[0:2], 1.8) - mi_width = screen.measure_text(hms[3:5], 1.8) - mi_offset = screen.measure_text(hms[0:3], 1.8) + h_width = display.measure_text(hms[0:2], 1.8) + mi_width = display.measure_text(hms[3:5], 1.8) + mi_offset = display.measure_text(hms[0:3], 1.8) - ymd_width = screen.measure_text(ymd, 1.0) + ymd_width = display.measure_text(ymd, 1.0) ymd_offset = int((badger2040.WIDTH / 2) - (ymd_width / 2)) - y_width = screen.measure_text(ymd[0:4], 1.0) - m_width = screen.measure_text(ymd[5:7], 1.0) - m_offset = screen.measure_text(ymd[0:5], 1.0) - d_width = screen.measure_text(ymd[8:10], 1.0) - d_offset = screen.measure_text(ymd[0:8], 1.0) - - screen.pen(15) - screen.clear() - screen.pen(0) - screen.thickness(5) - screen.text(hms, hms_offset, 40, 1.8) - screen.thickness(3) - screen.text(ymd, ymd_offset, 100, 1.0) + y_width = display.measure_text(ymd[0:4], 1.0) + m_width = display.measure_text(ymd[5:7], 1.0) + m_offset = display.measure_text(ymd[0:5], 1.0) + d_width = display.measure_text(ymd[8:10], 1.0) + d_offset = display.measure_text(ymd[0:8], 1.0) + + display.pen(15) + display.clear() + display.pen(0) + display.thickness(5) + display.text(hms, hms_offset, 40, 1.8) + display.thickness(3) + display.text(ymd, ymd_offset, 100, 1.0) if set_clock: if cursors[cursor] == "year": - screen.line(ymd_offset, 120, ymd_offset + y_width, 120) + display.line(ymd_offset, 120, ymd_offset + y_width, 120) if cursors[cursor] == "month": - screen.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120) + display.line(ymd_offset + m_offset, 120, ymd_offset + m_offset + m_width, 120) if cursors[cursor] == "day": - screen.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120) + display.line(ymd_offset + d_offset, 120, ymd_offset + d_offset + d_width, 120) if cursors[cursor] == "hour": - screen.line(hms_offset, 70, hms_offset + h_width, 70) + display.line(hms_offset, 70, hms_offset + h_width, 70) if cursors[cursor] == "minute": - screen.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70) + display.line(hms_offset + mi_offset, 70, hms_offset + mi_offset + mi_width, 70) - screen.update() + display.update() year, month, day, wd, hour, minute, second, _ = rtc.datetime() diff --git a/micropython/examples/badger2040/ebook.py b/micropython/examples/badger2040/ebook.py index c2bb2898a..ae67e0207 100644 --- a/micropython/examples/badger2040/ebook.py +++ b/micropython/examples/badger2040/ebook.py @@ -92,6 +92,7 @@ def draw_frame(): # Create a new Badger and set it to update FAST display = badger2040.Badger2040() +display.led(128) display.update_speed(badger2040.UPDATE_FAST) # Set up the buttons @@ -100,9 +101,8 @@ def draw_frame(): button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN) +button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) -# Set up the activity LED -led = machine.Pin(badger2040.PIN_LED, machine.Pin.OUT) offsets = [] @@ -111,6 +111,13 @@ def draw_frame(): def button(pin): global next_page, prev_page, change_font_size, change_font + time.sleep(0.05) + if not pin.value(): + return + + if button_a.value() and button_c.value(): + machine.reset() + if pin == button_down: next_page = True @@ -129,6 +136,7 @@ def button(pin): button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button) button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button) button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button) +button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button) # ------------------------------ diff --git a/micropython/examples/badger2040/fonts.py b/micropython/examples/badger2040/fonts.py index 499bda47f..b135d0141 100644 --- a/micropython/examples/badger2040/fonts.py +++ b/micropython/examples/badger2040/fonts.py @@ -96,6 +96,7 @@ def draw_fonts(): # Create a new Badger and set it to update FAST display = badger2040.Badger2040() +display.led(128) display.update_speed(badger2040.UPDATE_FAST) # Set up the buttons diff --git a/micropython/examples/badger2040/help.py b/micropython/examples/badger2040/help.py index ff6102be5..4f000f715 100644 --- a/micropython/examples/badger2040/help.py +++ b/micropython/examples/badger2040/help.py @@ -1,11 +1,12 @@ import badger2040 -import time from badger2040 import WIDTH TEXT_SIZE = 0.45 LINE_HEIGHT = 16 display = badger2040.Badger2040() +display.led(128) + display.pen(0) display.rectangle(0, 0, WIDTH, 16) display.thickness(1) @@ -27,7 +28,7 @@ y += LINE_HEIGHT display.thickness(2) -display.text("Holding USER button:", 0, y, TEXT_SIZE) +display.text("Hold USER after:", 0, y, TEXT_SIZE) display.thickness(1) y += LINE_HEIGHT display.text("Up / Down - Change font size", 0, y, TEXT_SIZE) @@ -36,5 +37,4 @@ y += LINE_HEIGHT display.update() -while True: - time.sleep(1) +display.halt() diff --git a/micropython/examples/badger2040/info.py b/micropython/examples/badger2040/info.py index 94c3b1af8..f3b056833 100644 --- a/micropython/examples/badger2040/info.py +++ b/micropython/examples/badger2040/info.py @@ -1,11 +1,12 @@ import badger2040 -import time from badger2040 import WIDTH TEXT_SIZE = 0.45 LINE_HEIGHT = 16 display = badger2040.Badger2040() +display.led(128) + display.pen(0) display.rectangle(0, 0, WIDTH, 16) display.thickness(1) @@ -33,6 +34,4 @@ display.text("https://pimoroni.com/badger2040", 0, y, TEXT_SIZE) display.update() - -while True: - time.sleep(1) +display.halt() diff --git a/micropython/examples/badger2040/list.py b/micropython/examples/badger2040/list.py index a2846ca7c..d71bc0395 100644 --- a/micropython/examples/badger2040/list.py +++ b/micropython/examples/badger2040/list.py @@ -143,6 +143,7 @@ def draw_checkbox(x, y, size, background, foreground, thickness, tick, padding): # Create a new Badger and set it to update FAST display = badger2040.Badger2040() +display.led(128) display.update_speed(badger2040.UPDATE_FAST) # Set up the buttons @@ -177,6 +178,13 @@ def draw_checkbox(x, y, size, background, foreground, thickness, tick, padding): def button(pin): global update, current_item, needs_save + time.sleep(0.05) + if not pin.value(): + return + + if button_a.value() and button_c.value(): + machine.reset() + if len(list_content) > 0 and not update: if pin == button_a: if current_item > 0: diff --git a/micropython/examples/badger2040/qrgen.py b/micropython/examples/badger2040/qrgen.py index 1c980c1ea..3f0c4021c 100644 --- a/micropython/examples/badger2040/qrgen.py +++ b/micropython/examples/badger2040/qrgen.py @@ -7,8 +7,8 @@ try: text = open("qrcode.txt", "r") except OSError: - text = open("qrcode.txt", "w") - text.write("""https://pimoroni.com/badger2040 + with open("qrcode.txt", "w") as text: + text.write("""https://pimoroni.com/badger2040 Badger 2040 * 296x128 1-bit e-ink * six user buttons @@ -18,8 +18,8 @@ Scan this code to learn more about Badger 2040. """) - text.flush() - text.seek(0) + text.flush() + text = open("qrcode.txt", "r") lines = text.read().strip().split("\n") @@ -28,6 +28,7 @@ detail_text = lines display = badger2040.Badger2040() +display.led(128) code = qrcode.QRCode() From b8d5a3db7555f3aaf1a3ba7c9e5362589d81a3fa Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 14:25:04 +0000 Subject: [PATCH 20/32] Badger2040: Bring back clear pressed to wake. --- micropython/modules/badger2040/badger2040.c | 2 ++ micropython/modules/badger2040/badger2040.cpp | 5 +++++ micropython/modules/badger2040/badger2040.h | 1 + 3 files changed, 8 insertions(+) diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 94896e7f6..536811d06 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -37,6 +37,7 @@ MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_measure_glyph_obj, 2, Badger2040_measure_g MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake); +MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_clear_pressed_to_wake_obj, Badger2040_clear_pressed_to_wake); /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { @@ -93,6 +94,7 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_OBJ_NEW_QSTR(MP_QSTR_Badger2040), (mp_obj_t)&Badger2040_type }, { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) }, + { MP_ROM_QSTR(MP_QSTR_clear_pressed_to_wake), MP_ROM_PTR(&Badger2040_clear_pressed_to_wake_obj) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index e40efc11c..5b6d6f623 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -280,6 +280,11 @@ mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) { return Badger2040_pressed_to_wake(button); } +mp_obj_t Badger2040_clear_pressed_to_wake() { + button_wake_state.clear(); + return mp_const_none; +} + // pressed // pressed_to_wake // wait_for_press - implement in terms of MicroPython! diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index 34efed47c..0586b3e19 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -44,3 +44,4 @@ extern mp_obj_t Badger2040_measure_glyph(size_t n_args, const mp_obj_t *pos_args extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data); extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button); +extern mp_obj_t Badger2040_clear_pressed_to_wake(); From b85792f2544f45411ee21ccdfd8c81af0a8ca8bb Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 14:25:27 +0000 Subject: [PATCH 21/32] Badger2040: JSON app state. --- micropython/examples/badger2040/badge.py | 48 +------ micropython/examples/badger2040/badger_os.py | 137 +++++++++++++------ micropython/examples/badger2040/image.py | 67 +++------ micropython/examples/badger2040/launcher.py | 92 +++++++------ 4 files changed, 164 insertions(+), 180 deletions(-) diff --git a/micropython/examples/badger2040/badge.py b/micropython/examples/badger2040/badge.py index 352c35dfd..a72a674bc 100644 --- a/micropython/examples/badger2040/badge.py +++ b/micropython/examples/badger2040/badge.py @@ -20,10 +20,6 @@ NAME_PADDING = 20 DETAIL_SPACING = 10 -OVERLAY_BORDER = 40 -OVERLAY_SPACING = 20 -OVERLAY_TEXT_SIZE = 0.6 - DEFAULT_TEXT = """mustelid inc H. Badger RP2040 @@ -63,42 +59,6 @@ def truncatestring(text, text_size, width): # Drawing functions # ------------------------------ -# Draw an overlay box with a given message within it -def draw_overlay(message, width, height, line_spacing, text_size): - - # Draw a light grey background - display.pen(12) - display.rectangle((WIDTH - width) // 2, (HEIGHT - height) // 2, width, height) - - # Take the provided message and split it up into - # lines that fit within the specified width - words = message.split(" ") - lines = [] - line = "" - appended_line = "" - for word in words: - if len(word) > 0: - appended_line += " " - appended_line += word - if display.measure_text(appended_line, text_size) >= width: - lines.append(line) - appended_line = word - else: - line = appended_line - if len(line) != 0: - lines.append(line) - - display.pen(0) - display.thickness(2) - - # Display each line of text from the message, centre-aligned - num_lines = len(lines) - for i in range(num_lines): - length = display.measure_text(lines[i], text_size) - current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2 - display.text(lines[i], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size) - - # Draw the badge, including user text def draw_badge(): display.pen(0) @@ -203,10 +163,6 @@ def draw_badge(): detail2_text = truncatestring(detail2_text, DETAILS_TEXT_SIZE, TEXT_WIDTH - DETAIL_SPACING - display.measure_text(detail2_title, DETAILS_TEXT_SIZE)) -# Tell launcher to relaunch this app on wake -if not display.woken(): - badger_os.state_save("badge") - # ------------------------------ # Main program @@ -216,9 +172,7 @@ def draw_badge(): while True: if display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): - draw_overlay("To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt", - WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) - display.update() + badger_os.warning(display, "To change the text, connect Badger2040 to a PC, load up Thonny, and modify badge.txt") time.sleep(4) draw_badge() diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py index 0aeb83d2d..ef2a1e207 100644 --- a/micropython/examples/badger2040/badger_os.py +++ b/micropython/examples/badger2040/badger_os.py @@ -2,11 +2,11 @@ import os import gc +import time +import json import machine import badger2040 -STATE_FILE = "appstate.txt" - def get_battery_level(): # Battery measurement @@ -47,55 +47,62 @@ def get_disk_usage(): 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_running(): + state = {"running": "launcher"} + state_load("launcher", state) + return state["running"] + + +def state_clear_running(): + state_modify("launcher", {"running": "launcher"}) + + +def state_set_running(app): + state_modify("launcher", {"running": app}) def state_launch(): - app = state_app() - if app is not None: + app = state_running() + if app is not None and app != "launcher": launch("_" + app) -def state_delete(): +def state_delete(app): try: - os.remove(STATE_FILE) + os.remove("{}_state.txt".format(app)) 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_save(app, data): + with open("{}_state.txt".format(app), "w") as f: + f.write(json.dumps(data)) + f.flush() + +def state_modify(app, data): + state = {} + state_load(app, state) + state.update(data) + state_save(app, state) -def state_load(title, *defaults): - data = [] + +def state_load(app, defaults): 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 + data = json.loads(open("{}_state.txt".format(app), "r").read()) + if type(data) is dict: + defaults.update(data) + return True + except (OSError, ValueError): + pass + + state_save(app, defaults) + return False def launch(file): - for k in locals().keys(): - if k not in ("gc", "file", "machine"): - del locals()[k] + state_set_running(file[1:]) + gc.collect() button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) @@ -103,18 +110,66 @@ def launch(file): def quit_to_launcher(pin): if button_a.value() and button_c.value(): - import os - try: - os.remove(STATE_FILE) - except OSError: - pass + state_clear_running() + time.sleep(0.1) # Needed to stop write fail machine.reset() button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher) button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher) try: - __import__(file[1:]) # Try to import _[file] (drop underscore prefix) + try: + __import__(file[1:]) # Try to import _[file] (drop underscore prefix) + except ImportError: + __import__(file) # Failover to importing [_file] + except ImportError: - __import__(file) # Failover to importing [_file] + # If the app doesn't exist, notify the user + warning(None, "Could not launch: " + file[1:]) + time.sleep(4.0) + except Exception as e: + # If the app throws an error, catch it and display! + print(e) + warning(None, str(e)) + time.sleep(4.0) + + # If the app exits or errors, do not relaunch! + state_clear_running() machine.reset() # Exit back to launcher + + +# Draw an overlay box with a given message within it +def warning(display, message, width=badger2040.WIDTH - 40, height=badger2040.HEIGHT - 40, line_spacing=20, text_size=0.6): + if display is None: + display = badger2040.Badger2040() + display.led(128) + + # Draw a light grey background + display.pen(12) + display.rectangle((badger2040.WIDTH - width) // 2, (badger2040.HEIGHT - height) // 2, width, height) + + # Take the provided message and split it up into + # lines that fit within the specified width + words = message.split(" ") + + lines = [] + current_line = "" + for word in words: + if display.measure_text(current_line + word + " ", text_size) < width: + current_line += word + " " + else: + lines.append(current_line.strip()) + current_line = word + " " + lines.append(current_line.strip()) + + display.pen(0) + display.thickness(2) + + # Display each line of text from the message, centre-aligned + num_lines = len(lines) + for i in range(num_lines): + length = display.measure_text(lines[i], text_size) + current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2 + display.text(lines[i], (badger2040.WIDTH - length) // 2, (badger2040.HEIGHT // 2) + current_line, text_size) + + display.update() diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index 402f5070f..ccaf01e56 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -2,7 +2,7 @@ import sys import time import badger2040 -from badger2040 import WIDTH, HEIGHT +from badger2040 import HEIGHT import badger_os @@ -50,40 +50,11 @@ image = bytearray(int(296 * 128 / 8)) -current_image = 0 -show_info = True - -# Draw an overlay box with a given message within it -def draw_overlay(message, width, height, line_spacing, text_size): - - # Draw a light grey background - display.pen(12) - display.rectangle((WIDTH - width) // 2, (HEIGHT - height) // 2, width, height) - - # Take the provided message and split it up into - # lines that fit within the specified width - words = message.split(" ") - - lines = [] - current_line = "" - for word in words: - if display.measure_text(current_line + word + " ", text_size) < width: - current_line += word + " " - else: - lines.append(current_line.strip()) - current_line = word + " " - lines.append(current_line.strip()) - - display.pen(0) - display.thickness(2) - - # Display each line of text from the message, centre-aligned - num_lines = len(lines) - for i in range(num_lines): - length = display.measure_text(lines[i], text_size) - current_line = (i * line_spacing) - ((num_lines - 1) * line_spacing) // 2 - display.text(lines[i], (WIDTH - length) // 2, (HEIGHT // 2) + current_line, text_size) +state = { + "current_image": 0, + "show_info": True +} def show_image(n): @@ -92,7 +63,7 @@ def show_image(n): open("images/{}".format(file), "r").readinto(image) display.image(image) - if show_info: + if state["show_info"]: name_length = display.measure_text(name, 0.5) display.pen(0) display.rectangle(0, HEIGHT - 21, name_length + 11, 21) @@ -106,7 +77,7 @@ def show_image(n): y = int((128 / 2) - (TOTAL_IMAGES * 10 / 2) + (i * 10)) display.pen(0) display.rectangle(x, y, 8, 8) - if current_image != i: + if state["current_image"] != i: display.pen(15) display.rectangle(x + 1, y + 1, 6, 6) @@ -116,40 +87,40 @@ def show_image(n): if TOTAL_IMAGES == 0: display.pen(15) display.clear() - draw_overlay("To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.", WIDTH - OVERLAY_BORDER, HEIGHT - OVERLAY_BORDER, OVERLAY_SPACING, OVERLAY_TEXT_SIZE) - display.update() + badger_os.warning(display, "To run this demo, create an /images directory on your device and upload some 1bit 296x128 pixel images.") + time.sleep(4.0) sys.exit() -current_image, show_info = badger_os.state_load("image", 0, True) +badger_os.state_load("image", state) changed = not display.woken() while True: if display.pressed(badger2040.BUTTON_UP): - if current_image > 0: - current_image -= 1 + if state["current_image"] > 0: + state["current_image"] -= 1 changed = True if display.pressed(badger2040.BUTTON_DOWN): - if current_image < TOTAL_IMAGES - 1: - current_image += 1 + if state["current_image"] < TOTAL_IMAGES - 1: + state["current_image"] += 1 changed = True if display.pressed(badger2040.BUTTON_A): - show_info = not show_info + state["show_info"] = not state["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) + badger_os.warning(display, "To add images connect Badger2040 to a PC, load up Thonny, and see readme.txt in images/") display.update() - print(current_image) + print(state["current_image"]) time.sleep(4) changed = True if changed: - badger_os.state_save("image", current_image, show_info) - show_image(current_image) + badger_os.state_save("image", state) + show_image(state["current_image"]) changed = False # Halt the Badger to save power, it will wake up if any of the front buttons are pressed diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 90815767d..5c33b8925 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -1,3 +1,4 @@ +import gc import time import math import machine @@ -6,35 +7,33 @@ import launchericons import badger_os -# for e.g. 2xAAA batteries, try max 3.4 min 3.0 -MAX_BATTERY_VOLTAGE = 4.0 -MIN_BATTERY_VOLTAGE = 3.2 - # Reduce clock speed to 48MHz, that's fast enough! machine.freq(48000000) - -# Restore previously running app -try: +if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C): # 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): - badger_os.state_delete() - else: - if badger_os.state_app() != "launcher": - badger_os.state_launch() -except OSError: - pass -except ImportError: - # Happens if appstate names an unknown app. Delete appstate and reset - badger_os.state_delete() - machine.reset() + badger_os.state_clear_running() + badger2040.clear_pressed_to_wake() +else: + # Otherwise restore previously running app + badger_os.state_launch() +# for e.g. 2xAAA batteries, try max 3.4 min 3.0 +MAX_BATTERY_VOLTAGE = 4.0 +MIN_BATTERY_VOLTAGE = 3.2 display = badger2040.Badger2040() display.led(128) -page, font_size, inverted = badger_os.state_load("launcher", 0, 1, False) -changed = badger_os.state_app() != "launcher" +state = { + "page": 0, + "font_size": 1, + "inverted": False, + "running": "launcher" +} + +badger_os.state_load("launcher", state) +changed = state["running"] != "launcher" icons = bytearray(launchericons.data()) icons_width = 576 @@ -123,23 +122,23 @@ def render(): display.pen(0) display.thickness(2) - max_icons = min(3, len(examples[(page * 3):])) + max_icons = min(3, len(examples[(state["page"] * 3):])) for i in range(max_icons): x = centers[i] - label, icon = examples[i + (page * 3)] + label, icon = examples[i + (state["page"] * 3)] label = label[1:].replace("_", " ") display.pen(0) display.icon(icons, icon, icons_width, 64, x - 32, 24) - w = display.measure_text(label, font_sizes[font_size]) - display.text(label, x - int(w / 2), 16 + 80, font_sizes[font_size]) + w = display.measure_text(label, font_sizes[state["font_size"]]) + display.text(label, x - int(w / 2), 16 + 80, font_sizes[state["font_size"]]) for i in range(MAX_PAGE): x = 286 y = int((128 / 2) - (MAX_PAGE * 10 / 2) + (i * 10)) display.pen(0) display.rectangle(x, y, 8, 8) - if page != i: + if state["page"] != i: display.pen(15) display.rectangle(x + 1, y + 1, 6, 6) @@ -159,15 +158,20 @@ def render(): def launch_example(index): while display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): time.sleep(0.01) - try: - badger_os.launch(examples[(page * 3) + index][0]) - return True - except IndexError: - return False + + file = examples[(state["page"] * 3) + index][0] + + for k in locals().keys(): + if k not in ("gc", "file", "badger_os"): + del locals()[k] + + gc.collect() + + badger_os.launch(file) def button(pin): - global page, font_size, inverted, changed + global changed changed = True if not display.pressed(badger2040.BUTTON_USER): # User button is NOT held down @@ -178,27 +182,27 @@ def button(pin): if pin == badger2040.BUTTON_C: launch_example(2) if pin == badger2040.BUTTON_UP: - if page > 0: - page -= 1 + if state["page"] > 0: + state["page"] -= 1 render() if pin == badger2040.BUTTON_DOWN: - if page < MAX_PAGE - 1: - page += 1 + if state["page"] < MAX_PAGE - 1: + state["page"] += 1 render() else: # User button IS held down if pin == badger2040.BUTTON_UP: - font_size += 1 - if font_size == len(font_sizes): - font_size = 0 + state["font_size"] += 1 + if state["font_size"] == len(font_sizes): + state["font_size"] = 0 render() if pin == badger2040.BUTTON_DOWN: - font_size -= 1 - if font_size < 0: - font_size = 0 + state["font_size"] -= 1 + if state["font_size"] < 0: + state["font_size"] = 0 render() if pin == badger2040.BUTTON_A: - inverted = not inverted - display.invert(inverted) + state["inverted"] = not state["inverted"] + display.invert(state["inverted"]) render() @@ -227,7 +231,7 @@ def button(pin): button(badger2040.BUTTON_DOWN) if changed: - badger_os.state_save("launcher", page, font_size, inverted) + badger_os.state_save("launcher", state) changed = False display.halt() From 6dee15a6fcab9bc88ef117b5568c89f38197595d Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 14:44:55 +0000 Subject: [PATCH 22/32] Badger2040: ebook now uses badger_os --- micropython/examples/badger2040/ebook.py | 164 ++++++++++------------- 1 file changed, 71 insertions(+), 93 deletions(-) diff --git a/micropython/examples/badger2040/ebook.py b/micropython/examples/badger2040/ebook.py index ae67e0207..fea6ca54d 100644 --- a/micropython/examples/badger2040/ebook.py +++ b/micropython/examples/badger2040/ebook.py @@ -2,7 +2,7 @@ import machine import time import gc - +import badger_os # **** Put the name of your text file here ***** text_file = "book.txt" # File must be on the MicroPython device @@ -33,9 +33,6 @@ ARROW_PADDING = 2 TEXT_PADDING = 4 - -TEXT_SIZE = 0.5 -TEXT_SPACING = int(34 * TEXT_SIZE) TEXT_WIDTH = WIDTH - TEXT_PADDING - TEXT_PADDING - ARROW_WIDTH FONTS = ["sans", "gothic", "cursive", "serif"] @@ -71,7 +68,7 @@ def draw_frame(): display.rectangle(WIDTH - ARROW_WIDTH, 0, ARROW_WIDTH, HEIGHT) display.pen(0) display.thickness(ARROW_THICKNESS) - if current_page > 1: + if state["current_page"] > 0: draw_up(WIDTH - ARROW_WIDTH, (HEIGHT // 4) - (ARROW_HEIGHT // 2), ARROW_WIDTH, ARROW_HEIGHT, ARROW_THICKNESS, ARROW_PADDING) draw_down(WIDTH - ARROW_WIDTH, ((HEIGHT * 3) // 4) - (ARROW_HEIGHT // 2), @@ -83,61 +80,23 @@ def draw_frame(): # ------------------------------ # Global variables -next_page = True -prev_page = False -change_font_size = False -change_font = False -last_offset = 0 -current_page = 0 +state = { + "last_offset": 0, + "current_page": 0, + "font_idx": 0, + "text_size": 0.5, + "offsets": [] +} +badger_os.state_load("ebook", state) + +text_spacing = int(34 * state["text_size"]) + # Create a new Badger and set it to update FAST display = badger2040.Badger2040() display.led(128) display.update_speed(badger2040.UPDATE_FAST) -# Set up the buttons -button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN) - -button_a = machine.Pin(badger2040.BUTTON_A, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_b = machine.Pin(badger2040.BUTTON_B, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_c = machine.Pin(badger2040.BUTTON_C, machine.Pin.IN, machine.Pin.PULL_DOWN) - - -offsets = [] - - -# Button handling function -def button(pin): - global next_page, prev_page, change_font_size, change_font - - time.sleep(0.05) - if not pin.value(): - return - - if button_a.value() and button_c.value(): - machine.reset() - - if pin == button_down: - next_page = True - - if pin == button_up: - prev_page = True - - if pin == button_a: - change_font_size = True - - if pin == button_b: - change_font = True - - -# Register the button handling function with the buttons -button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_b.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_c.irq(trigger=machine.Pin.IRQ_RISING, handler=button) - # ------------------------------ # Render page @@ -149,7 +108,7 @@ def render_page(): pos = ebook.tell() next_pos = pos add_newline = False - display.font(FONTS[0]) + display.font(FONTS[state["font_idx"]]) while True: # Read a full line and split it into words @@ -188,7 +147,7 @@ def render_page(): if len(line) > 0 and len(next_word) > 0: appended_line += " " appended_line += next_word - appended_length = display.measure_text(appended_line, TEXT_SIZE) + appended_length = display.measure_text(appended_line, state["text_size"]) # Would this appended line be longer than the text display area, or was a blank line spotted? if appended_length >= TEXT_WIDTH or add_newline: @@ -197,14 +156,14 @@ def render_page(): print(line) display.pen(0) display.thickness(FONT_THICKNESSES[0]) - display.text(line, TEXT_PADDING, (row * TEXT_SPACING) + (TEXT_SPACING // 2) + TEXT_PADDING, TEXT_SIZE) + display.text(line, TEXT_PADDING, (row * text_spacing) + (text_spacing // 2) + TEXT_PADDING, state["text_size"]) # Clear the line and move on to the next row line = "" row += 1 # Have we reached the end of the page? - if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT: + if (row * text_spacing) + text_spacing >= HEIGHT: print("+++++") display.update() @@ -220,7 +179,7 @@ def render_page(): if add_newline: print("") row += 1 - if (row * TEXT_SPACING) + TEXT_SPACING >= HEIGHT: + if (row * text_spacing) + text_spacing >= HEIGHT: print("+++++") display.update() return @@ -235,50 +194,69 @@ def render_page(): # Main program loop # ------------------------------ +launch = True +changed = False + # Open the book file ebook = open(text_file, "r") +if len(state["offsets"]) > state["current_page"]: + ebook.seek(state["offsets"][state["current_page"]]) +else: + state["current_page"] = 0 + state["offsets"] = [] while True: # Was the next page button pressed? - if next_page: - current_page += 1 + if display.pressed(badger2040.BUTTON_DOWN): + state["current_page"] += 1 - # Is the next page one we've not displayed before? - if current_page > len(offsets): - offsets.append(ebook.tell()) # Add its start position to the offsets list - draw_frame() - render_page() - next_page = False # Clear the next page button flag + changed = True # Was the previous page button pressed? - if prev_page: - if current_page > 1: - current_page -= 1 - ebook.seek(offsets[current_page - 1]) # Retrieve the start position of the last page - draw_frame() - render_page() - prev_page = False # Clear the prev page button flag - - if change_font_size: - TEXT_SIZE += 0.1 - if TEXT_SIZE > 0.8: - TEXT_SIZE = 0.5 - TEXT_SPACING = int(34 * TEXT_SIZE) - offsets = [0] + if display.pressed(badger2040.BUTTON_UP): + if state["current_page"] > 0: + state["current_page"] -= 1 + if state["current_page"] == 0: + ebook.seek(0) + else: + ebook.seek(state["offsets"][state["current_page"] - 1]) # Retrieve the start position of the last page + changed = True + + if display.pressed(badger2040.BUTTON_A): + state["text_size"] += 0.1 + if state["text_size"] > 0.8: + state["text_size"] = 0.5 + text_spacing = int(34 * state["text_size"]) + state["offsets"] = [] ebook.seek(0) - current_page = 1 - draw_frame() - render_page() - change_font_size = False - - if change_font: - FONTS.append(FONTS.pop(0)) - FONT_THICKNESSES.append(FONT_THICKNESSES.pop(0)) - offsets = [0] + state["current_page"] = 0 + changed = True + + if display.pressed(badger2040.BUTTON_B): + state["font_idx"] += 1 + if (state["font_idx"] >= len(FONTS)): + state["font_idx"] = 0 + state["offsets"] = [] ebook.seek(0) - current_page = 1 + state["current_page"] = 0 + changed = True + + if launch and not changed: + if state["current_page"] > 0 and len(state["offsets"]) > state["current_page"] - 1: + ebook.seek(state["offsets"][state["current_page"] - 1]) + changed = True + launch = False + + if changed: draw_frame() render_page() - change_font = False - time.sleep(0.1) + # Is the next page one we've not displayed before? + if state["current_page"] >= len(state["offsets"]): + state["offsets"].append(ebook.tell()) # Add its start position to the state["offsets"] list + badger_os.state_save("ebook", state) + + changed = False + + + display.halt() From eb2e55b27c9295c660609de9c743143d2bf4ed87 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 14:50:51 +0000 Subject: [PATCH 23/32] I learn how to run the linter locally --- micropython/examples/badger2040/ebook.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/micropython/examples/badger2040/ebook.py b/micropython/examples/badger2040/ebook.py index fea6ca54d..1fe76650d 100644 --- a/micropython/examples/badger2040/ebook.py +++ b/micropython/examples/badger2040/ebook.py @@ -1,6 +1,4 @@ import badger2040 -import machine -import time import gc import badger_os @@ -258,5 +256,4 @@ def render_page(): changed = False - display.halt() From 1f1f9b00df90ec0d817ce02f1f358dc198445e69 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 14:52:54 +0000 Subject: [PATCH 24/32] Badger2040: Light the act LED immediately on wake --- micropython/modules/badger2040/badger2040.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 5b6d6f623..f85a28321 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -12,6 +12,10 @@ namespace { gpio_set_function(pimoroni::Badger2040::ENABLE_3V3, GPIO_FUNC_SIO); gpio_set_dir(pimoroni::Badger2040::ENABLE_3V3, GPIO_OUT); gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); + + gpio_set_function(pimoroni::Badger2040::LED, GPIO_FUNC_SIO); + gpio_set_dir(pimoroni::Badger2040::LED, GPIO_OUT); + gpio_put(pimoroni::Badger2040::LED, 1); } bool any() const { From 75d011242103ee464168496a0d534885e324d780 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 14:55:08 +0000 Subject: [PATCH 25/32] Badger2040: Wait for buttons to be released. Rename woken. --- micropython/modules/badger2040/badger2040.c | 7 +++---- micropython/modules/badger2040/badger2040.cpp | 12 ++++++++++-- micropython/modules/badger2040/badger2040.h | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/micropython/modules/badger2040/badger2040.c b/micropython/modules/badger2040/badger2040.c index 536811d06..2b42a96a7 100644 --- a/micropython/modules/badger2040/badger2040.c +++ b/micropython/modules/badger2040/badger2040.c @@ -9,9 +9,6 @@ MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_is_busy_obj, Badger2040_is_busy); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_update_obj, Badger2040_update); MP_DEFINE_CONST_FUN_OBJ_KW(Badger2040_partial_update_obj, 4, Badger2040_partial_update); -MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt); -MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_woken_obj, Badger2040_woken); - MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_invert_obj, Badger2040_invert); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_led_obj, Badger2040_led); MP_DEFINE_CONST_FUN_OBJ_2(Badger2040_font_obj, Badger2040_font); @@ -38,6 +35,8 @@ MP_DEFINE_CONST_FUN_OBJ_3(Badger2040_command_obj, Badger2040_command); MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_pressed_to_wake_obj, Badger2040_pressed_to_wake); MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_clear_pressed_to_wake_obj, Badger2040_clear_pressed_to_wake); +MP_DEFINE_CONST_FUN_OBJ_1(Badger2040_halt_obj, Badger2040_halt); +MP_DEFINE_CONST_FUN_OBJ_0(Badger2040_woken_by_button_obj, Badger2040_woken_by_button); /***** Binding of Methods *****/ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { @@ -48,7 +47,6 @@ STATIC const mp_rom_map_elem_t Badger2040_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_partial_update), MP_ROM_PTR(&Badger2040_partial_update_obj) }, { MP_ROM_QSTR(MP_QSTR_halt), MP_ROM_PTR(&Badger2040_halt_obj) }, - { MP_ROM_QSTR(MP_QSTR_woken), MP_ROM_PTR(&Badger2040_woken_obj) }, { MP_ROM_QSTR(MP_QSTR_invert), MP_ROM_PTR(&Badger2040_invert_obj) }, { MP_ROM_QSTR(MP_QSTR_led), MP_ROM_PTR(&Badger2040_led_obj) }, @@ -95,6 +93,7 @@ STATIC const mp_rom_map_elem_t badger2040_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_pressed_to_wake), MP_ROM_PTR(&Badger2040_pressed_to_wake_obj) }, { MP_ROM_QSTR(MP_QSTR_clear_pressed_to_wake), MP_ROM_PTR(&Badger2040_clear_pressed_to_wake_obj) }, + { MP_ROM_QSTR(MP_QSTR_woken_by_button), MP_ROM_PTR(&Badger2040_woken_by_button_obj) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_NORMAL), MP_ROM_INT(0) }, { MP_ROM_QSTR(MP_QSTR_UPDATE_MEDIUM), MP_ROM_INT(1) }, diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 5b6d6f623..464655b02 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -14,6 +14,10 @@ namespace { gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); } + bool get_current() { + return gpio_get_all() & (0x1f << pimoroni::Badger2040::DOWN); + } + bool any() const { return state > 0; } @@ -209,8 +213,7 @@ MICROPY_EVENT_POLL_HOOK return mp_const_none; } -mp_obj_t Badger2040_woken(mp_obj_t self_in) { - (void)self_in; +mp_obj_t Badger2040_woken_by_button() { return button_wake_state.any() ? mp_const_true : mp_const_false; } @@ -282,6 +285,11 @@ mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) { mp_obj_t Badger2040_clear_pressed_to_wake() { button_wake_state.clear(); + while(button_wake_state.get_current()) { +#ifdef MICROPY_EVENT_POLL_HOOK +MICROPY_EVENT_POLL_HOOK +#endif + } return mp_const_none; } diff --git a/micropython/modules/badger2040/badger2040.h b/micropython/modules/badger2040/badger2040.h index 0586b3e19..ae08294a4 100644 --- a/micropython/modules/badger2040/badger2040.h +++ b/micropython/modules/badger2040/badger2040.h @@ -16,7 +16,6 @@ extern mp_obj_t Badger2040_update(mp_obj_t self_in); extern mp_obj_t Badger2040_partial_update(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); extern mp_obj_t Badger2040_halt(mp_obj_t self_in); -extern mp_obj_t Badger2040_woken(mp_obj_t self_in); extern mp_obj_t Badger2040_invert(mp_obj_t self_in, mp_obj_t invert); extern mp_obj_t Badger2040_led(mp_obj_t self_in, mp_obj_t brightness); @@ -45,3 +44,4 @@ extern mp_obj_t Badger2040_command(mp_obj_t self_in, mp_obj_t reg, mp_obj_t data extern mp_obj_t Badger2040_pressed_to_wake(mp_obj_t button); extern mp_obj_t Badger2040_clear_pressed_to_wake(); +extern mp_obj_t Badger2040_woken_by_button(); From 5c2cc7ebf8caaefb362f23564e110af8f49bc79a Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 14:55:56 +0000 Subject: [PATCH 26/32] Badger2040: Improve launcher. --- micropython/examples/badger2040/badger_os.py | 2 ++ micropython/examples/badger2040/image.py | 2 +- micropython/examples/badger2040/launcher.py | 16 ++++++++-------- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py index ef2a1e207..928cfa8e4 100644 --- a/micropython/examples/badger2040/badger_os.py +++ b/micropython/examples/badger2040/badger_os.py @@ -54,7 +54,9 @@ def state_running(): def state_clear_running(): + running = state_running() state_modify("launcher", {"running": "launcher"}) + return running != "launcher" def state_set_running(app): diff --git a/micropython/examples/badger2040/image.py b/micropython/examples/badger2040/image.py index ccaf01e56..db31fc156 100644 --- a/micropython/examples/badger2040/image.py +++ b/micropython/examples/badger2040/image.py @@ -94,7 +94,7 @@ def show_image(n): badger_os.state_load("image", state) -changed = not display.woken() +changed = not badger2040.woken_by_button() while True: diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 5c33b8925..14b94593c 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -10,9 +10,13 @@ # Reduce clock speed to 48MHz, that's fast enough! machine.freq(48000000) +changed = False +exited_to_launcher = False +woken_by_button = badger2040.woken_by_button() # Must be done before we clear_pressed_to_wake + if badger2040.pressed_to_wake(badger2040.BUTTON_A) and badger2040.pressed_to_wake(badger2040.BUTTON_C): # Pressing A and C together at start quits app - badger_os.state_clear_running() + exited_to_launcher = badger_os.state_clear_running() badger2040.clear_pressed_to_wake() else: # Otherwise restore previously running app @@ -33,7 +37,8 @@ } badger_os.state_load("launcher", state) -changed = state["running"] != "launcher" + +display.invert(state["inverted"]) icons = bytearray(launchericons.data()) icons_width = 576 @@ -206,16 +211,11 @@ def button(pin): render() -if changed: - # Wait for any wakeup button to be released - while display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): - time.sleep(0.01) +if exited_to_launcher or not woken_by_button: display.update_speed(badger2040.UPDATE_MEDIUM) render() display.update_speed(badger2040.UPDATE_FAST) -if not changed and not display.woken(): - render() while True: if display.pressed(badger2040.BUTTON_A): From b497422bdbe01130533c74f08b83ee8a1dff7698 Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 15:13:03 +0000 Subject: [PATCH 27/32] Badger2040: Remove button release wait from clear. --- micropython/modules/badger2040/badger2040.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/micropython/modules/badger2040/badger2040.cpp b/micropython/modules/badger2040/badger2040.cpp index 464655b02..024074ee6 100644 --- a/micropython/modules/badger2040/badger2040.cpp +++ b/micropython/modules/badger2040/badger2040.cpp @@ -14,10 +14,6 @@ namespace { gpio_put(pimoroni::Badger2040::ENABLE_3V3, 1); } - bool get_current() { - return gpio_get_all() & (0x1f << pimoroni::Badger2040::DOWN); - } - bool any() const { return state > 0; } @@ -285,11 +281,6 @@ mp_obj_t Badger2040_pressed_to_wake2(mp_obj_t self_in, mp_obj_t button) { mp_obj_t Badger2040_clear_pressed_to_wake() { button_wake_state.clear(); - while(button_wake_state.get_current()) { -#ifdef MICROPY_EVENT_POLL_HOOK -MICROPY_EVENT_POLL_HOOK -#endif - } return mp_const_none; } From 3042191c7ee129377a8eadfacede3e341281f8be Mon Sep 17 00:00:00 2001 From: Phil Howard Date: Fri, 25 Mar 2022 15:14:34 +0000 Subject: [PATCH 28/32] Badger2040: Fix exit to launcher. --- micropython/examples/badger2040/badger_os.py | 2 -- micropython/examples/badger2040/launcher.py | 10 ++++++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py index 928cfa8e4..7fb321ea0 100644 --- a/micropython/examples/badger2040/badger_os.py +++ b/micropython/examples/badger2040/badger_os.py @@ -112,8 +112,6 @@ def launch(file): def quit_to_launcher(pin): if button_a.value() and button_c.value(): - state_clear_running() - time.sleep(0.1) # Needed to stop write fail machine.reset() button_a.irq(trigger=machine.Pin.IRQ_RISING, handler=quit_to_launcher) diff --git a/micropython/examples/badger2040/launcher.py b/micropython/examples/badger2040/launcher.py index 14b94593c..f003a9407 100644 --- a/micropython/examples/badger2040/launcher.py +++ b/micropython/examples/badger2040/launcher.py @@ -160,10 +160,15 @@ def render(): display.update() -def launch_example(index): - while display.pressed(badger2040.BUTTON_A) or display.pressed(badger2040.BUTTON_B) or display.pressed(badger2040.BUTTON_C) or display.pressed(badger2040.BUTTON_UP) or display.pressed(badger2040.BUTTON_DOWN): +def wait_for_user_to_release_buttons(): + pr = display.pressed + while pr(badger2040.BUTTON_A) or pr(badger2040.BUTTON_B) or pr(badger2040.BUTTON_C) or pr(badger2040.BUTTON_UP) or pr(badger2040.BUTTON_DOWN): time.sleep(0.01) + +def launch_example(index): + wait_for_user_to_release_buttons() + file = examples[(state["page"] * 3) + index][0] for k in locals().keys(): @@ -212,6 +217,7 @@ def button(pin): if exited_to_launcher or not woken_by_button: + wait_for_user_to_release_buttons() display.update_speed(badger2040.UPDATE_MEDIUM) render() From 88dd6e6e86c5c79e1c299031dfca4b80b323a3cc Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 15:27:16 +0000 Subject: [PATCH 29/32] Badger2040: qrgen now halts --- micropython/examples/badger2040/qrgen.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/micropython/examples/badger2040/qrgen.py b/micropython/examples/badger2040/qrgen.py index 3f0c4021c..f4ea40c8b 100644 --- a/micropython/examples/badger2040/qrgen.py +++ b/micropython/examples/badger2040/qrgen.py @@ -1,6 +1,5 @@ import badger2040 import qrcode -import time # Open the qrcode file @@ -66,6 +65,4 @@ def draw_qr_code(ox, oy, size, code): top += 10 display.update() - -while True: - time.sleep(1.0) +display.halt() From 47122d93519c2b45e4e193e6b8aabca0ac4cba97 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 15:58:32 +0000 Subject: [PATCH 30/32] Badger2040: fonts uses new framwork, clock more reactive to buttons --- micropython/examples/badger2040/clock.py | 4 +- micropython/examples/badger2040/fonts.py | 65 +++++++++--------------- 2 files changed, 26 insertions(+), 43 deletions(-) diff --git a/micropython/examples/badger2040/clock.py b/micropython/examples/badger2040/clock.py index e1852ad77..973e1d48c 100644 --- a/micropython/examples/badger2040/clock.py +++ b/micropython/examples/badger2040/clock.py @@ -33,7 +33,7 @@ def days_in_month(month, year): def button(pin): global last, set_clock, cursor, year, month, day, hour, minute - time.sleep(0.05) + time.sleep(0.01) if not pin.value(): return @@ -149,4 +149,4 @@ def draw_clock(): if second != last_second: draw_clock() last_second = second - time.sleep(0.1) + time.sleep(0.01) diff --git a/micropython/examples/badger2040/fonts.py b/micropython/examples/badger2040/fonts.py index b135d0141..9cf3d3663 100644 --- a/micropython/examples/badger2040/fonts.py +++ b/micropython/examples/badger2040/fonts.py @@ -1,6 +1,5 @@ import badger2040 -import machine -import time +import badger_os # Global Constants FONT_NAMES = ("sans", "gothic", "cursive", "serif", "serif_italic") @@ -65,13 +64,13 @@ def draw_fonts(): for i in range(len(FONT_NAMES)): name = FONT_NAMES[i] display.pen(0) - if i == selected_font: + if i == state["selected_font"]: display.rectangle(0, i * MENU_SPACING, MENU_WIDTH, MENU_SPACING) display.pen(15) display.text(name, MENU_PADDING, (i * MENU_SPACING) + (MENU_SPACING // 2), MENU_TEXT_SIZE) - display.font(FONT_NAMES[selected_font]) + display.font(FONT_NAMES[state["selected_font"]]) display.thickness(2) display.pen(0) @@ -91,52 +90,36 @@ def draw_fonts(): # ------------------------------ # Global variables -selected_font = 0 -pressed = False +state = {"selected_font": 0} +badger_os.state_load("fonts", state) # Create a new Badger and set it to update FAST display = badger2040.Badger2040() display.led(128) display.update_speed(badger2040.UPDATE_FAST) -# Set up the buttons -button_up = machine.Pin(badger2040.BUTTON_UP, machine.Pin.IN, machine.Pin.PULL_DOWN) -button_down = machine.Pin(badger2040.BUTTON_DOWN, machine.Pin.IN, machine.Pin.PULL_DOWN) - - -# Button handling function -def button(pin): - global pressed - global selected_font - - if not pressed: - if pin == button_up: - selected_font -= 1 - if selected_font < 0: - selected_font = len(FONT_NAMES) - 1 - pressed = True - return - if pin == button_down: - selected_font += 1 - if selected_font >= len(FONT_NAMES): - selected_font = 0 - pressed = True - return - - -# Register the button handling function with the buttons -button_up.irq(trigger=machine.Pin.IRQ_RISING, handler=button) -button_down.irq(trigger=machine.Pin.IRQ_RISING, handler=button) - +changed = not badger2040.woken_by_button() # ------------------------------ # Main program loop # ------------------------------ while True: - draw_frame() - draw_fonts() - - pressed = False - while not pressed: - time.sleep(0.1) + if display.pressed(badger2040.BUTTON_UP): + state["selected_font"] -= 1 + if state["selected_font"] < 0: + state["selected_font"] = len(FONT_NAMES) - 1 + changed = True + if display.pressed(badger2040.BUTTON_DOWN): + state["selected_font"] += 1 + if state["selected_font"] >= len(FONT_NAMES): + state["selected_font"] = 0 + changed = True + + if changed: + draw_frame() + draw_fonts() + badger_os.state_save("fonts", state) + changed = False + + display.halt() From e0e34fef428e30e81b3df1240f5658d5c4fa9d53 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 16:29:40 +0000 Subject: [PATCH 31/32] Badger2040: Put state files in their own directory to keep things tidy --- micropython/examples/badger2040/badger_os.py | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/micropython/examples/badger2040/badger_os.py b/micropython/examples/badger2040/badger_os.py index 7fb321ea0..6eb70972b 100644 --- a/micropython/examples/badger2040/badger_os.py +++ b/micropython/examples/badger2040/badger_os.py @@ -71,15 +71,23 @@ def state_launch(): def state_delete(app): try: - os.remove("{}_state.txt".format(app)) + os.remove("/state/{}.json".format(app)) except OSError: pass def state_save(app, data): - with open("{}_state.txt".format(app), "w") as f: - f.write(json.dumps(data)) - f.flush() + try: + with open("/state/{}.json".format(app), "w") as f: + f.write(json.dumps(data)) + f.flush() + except OSError: + import os + try: + os.stat("/state") + except OSError: + os.mkdir("/state") + state_save(app, data) def state_modify(app, data): @@ -91,7 +99,7 @@ def state_modify(app, data): def state_load(app, defaults): try: - data = json.loads(open("{}_state.txt".format(app), "r").read()) + data = json.loads(open("/state/{}.json".format(app), "r").read()) if type(data) is dict: defaults.update(data) return True From 72ff77aafab6d706502e789ac041e7905442c131 Mon Sep 17 00:00:00 2001 From: Mike Bell Date: Fri, 25 Mar 2022 16:52:52 +0000 Subject: [PATCH 32/32] Badger2040: Sleep after writing the book, to fix possible write delay when on battery --- micropython/examples/badger2040/ebook.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/micropython/examples/badger2040/ebook.py b/micropython/examples/badger2040/ebook.py index 1fe76650d..7f21239e3 100644 --- a/micropython/examples/badger2040/ebook.py +++ b/micropython/examples/badger2040/ebook.py @@ -1,4 +1,5 @@ import badger2040 +import time import gc import badger_os @@ -13,7 +14,10 @@ # If the specified file doesn't exist, # pre-populate with Wind In The Willows import witw - open(text_file, "wb").write(witw.data()) + with open(text_file, "wb") as f: + f.write(witw.data()) + f.flush() + time.sleep(0.1) del witw except ImportError: pass