From aba592722146ffc6d961e3f6f182d1687855d620 Mon Sep 17 00:00:00 2001 From: BlueFox Date: Sat, 16 Nov 2024 16:31:33 +0100 Subject: [PATCH] Did a huge refactoring and outsouring for modularity and a small performance improvement --- input_tests.py => demos/input_tests.py | 0 lcd_big_hello.py => demos/lcd_big_hello.py | 0 .../lcd_driver_demo.py | 0 main.py | 352 ++---------------- programs/demos.py | 41 ++ programs/manual.py | 17 + programs/settings.py | 243 ++++++++++++ programs/timer.py | 62 +++ 8 files changed, 389 insertions(+), 326 deletions(-) rename input_tests.py => demos/input_tests.py (100%) rename lcd_big_hello.py => demos/lcd_big_hello.py (100%) rename lcd_driver_demo.py => demos/lcd_driver_demo.py (100%) create mode 100644 programs/demos.py create mode 100644 programs/manual.py create mode 100644 programs/settings.py create mode 100644 programs/timer.py diff --git a/input_tests.py b/demos/input_tests.py similarity index 100% rename from input_tests.py rename to demos/input_tests.py diff --git a/lcd_big_hello.py b/demos/lcd_big_hello.py similarity index 100% rename from lcd_big_hello.py rename to demos/lcd_big_hello.py diff --git a/lcd_driver_demo.py b/demos/lcd_driver_demo.py similarity index 100% rename from lcd_driver_demo.py rename to demos/lcd_driver_demo.py diff --git a/main.py b/main.py index 2fa4953..e7bf3a7 100644 --- a/main.py +++ b/main.py @@ -13,336 +13,11 @@ You should have received a copy of the GNU General Public License along with thi import utils from lcdMenu import lcdMenu from WelcomeScreen import WelcomeScreen -from time import sleep, time_ns from gc import collect # garbage collector for better memory performance config = utils.Config() btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus -# extra functions to access the garbage collector -def manual(): - config.LCD.clear() - set_value = config.PIN_OUT_RELAIS.value() - config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ") - while True: - if set_value != config.PIN_IN_SWITCH.value(): - config.PIN_OUT_RELAIS.value(config.PIN_IN_SWITCH.value()) - set_value = config.PIN_OUT_RELAIS.value() - config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ") - if config.PIN_IN_BTN_1.value() == 1 or config.PIN_IN_BTN_2.value() == 1: - return True # exit on press of these buttons; True to disable the Quitting message from lcdMenu -def timer(): - timer_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="TIMERS") - timer_values_original = [config.TIMER_1_DURATION, config.TIMER_2_DURATION, config.TIMER_3_DURATION] - timer_values = timer_values_original.copy() # here, the current timers time is stored when interrupting via the interrupt_pin (see below) - timer_splits = [lambda: divmod(round(timer_values[0]), 60), lambda: divmod(round(timer_values[1]), 60), lambda: divmod(round(timer_values[2]), 60)] - interrupt_pin = config.PIN_IN_BTN_1 # the interrupt btn stops the timer, saves the current time and goes back to the menu - reset_pin = config.PIN_IN_BTN_2 # the reset btn restores the default value - start_stop_pin = config.PIN_IN_SWITCH # the start_stop switch starts/stops the timer - - # timer_number is the number of the timer that will be run, starting at 1 - # the _timer variable is needed because otherwise python will throw crazy errors regarding "variable accessed before assignment"... - # ...just see it as a copy of the timer_number-1'th elemnt of the timer_values list (see above) - def run_timer(timer_number: int, _timer: int): - config.LCD.clear() - config.LCD.putstr(f"Timer {timer_number}".center(16)) - last_start_stop_value = start_stop_pin.value() - config.PIN_OUT_RELAIS.value(last_start_stop_value) - last_time_ns = time_ns() - while True: # now run the timer (if the switch is high) - config.LCD.move_to(0,1) - _timer_div = divmod(round(_timer), 60) - config.LCD.putstr(f"{_timer_div[0]:02d}:{_timer_div[1]:02d}".center(16)) - if interrupt_pin.value() == 1: - timer_values[timer_number-1] = _timer # save the current state - last_start_stop_value = 0 # turn the LEDs off! - config.PIN_OUT_RELAIS.value(last_start_stop_value) - return None - if reset_pin.value() == 1: - _timer = timer_values_original[timer_number-1] # reset the timers - if _timer <= 0: - config.PIN_OUT_RELAIS.off() - return True - sleep(0.05) - if last_start_stop_value != (new_value := start_stop_pin.value()): - last_start_stop_value = new_value - config.PIN_OUT_RELAIS.value(new_value) - last_time_ns = time_ns() - if start_stop_pin.value() == 1: - _timer -= (time_ns() - last_time_ns) / 1000**3 - last_time_ns = time_ns() - - timer_programs = [(f"T1 - {timer_splits[0]()[0]:02d}:{timer_splits[0]()[1]:02d}", lambda: run_timer(1, timer_values[0])), - (f"T2 - {timer_splits[1]()[0]:02d}:{timer_splits[1]()[1]:02d}", lambda: run_timer(2, timer_values[1])), - (f"T3 - {timer_splits[2]()[0]:02d}:{timer_splits[2]()[1]:02d}", lambda: run_timer(3, timer_values[2])), - ("Exit", timer_menu.stop)] - timer_menu.setup(timer_programs) # give it the callback list - timer_menu.run() - del timer_menu, timer_programs - collect() - return True # disable the "Quitting" message from lcdMenu -def settings(): - settings_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="SETTINGS") - def toggle_show_welcome(): - current_value = config.STARTUP_WELCOME_SHOW - config.LCD.clear() - config.LCD.putstr(f"Currently {'on' if current_value else 'off'}".center(16)) - config.LCD.putstr("< keep change >") - keep_btn = config.PIN_IN_BTN_1 - change_btn = config.PIN_IN_BTN_2 - while True: - if keep_btn.value() == 1: - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Stay {'on' if current_value else 'off'}!".center(16)) - sleep(0.5) - return True - if change_btn.value() == 1: - config.STARTUP_WELCOME_SHOW = not current_value - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Turned {'on' if not current_value else 'off'}!".center(16)) - sleep(0.5) - return True - def welcome_cycles(): - config.LCD.clear() - current_cycles = config.STARTUP_WELCOME_CYCLES - - option_down = [" ", "v"][current_cycles>1] - config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") - btn_left = config.PIN_IN_BTN_1 - btn_right = config.PIN_IN_BTN_2 - while True: - if btn_left.value() == 1: - sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed - if btn_right.value() == 1: - config.STARTUP_WELCOME_CYCLES = current_cycles - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - - current_cycles -= 1 - if current_cycles < 1: current_cycles = 1 - option_down = [" ", "v"][current_cycles>1] - config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") - if btn_right.value() == 1: - sleep(0.1) - if btn_left.value() == 1: - config.STARTUP_WELCOME_CYCLES = current_cycles - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - current_cycles += 1 - option_down = [" ", "v"][current_cycles>1] - config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") - - # with n being the number of the timer (starting at 0) - def set_n_timer(n: int): - # a small helper function to save the value of the n'th timer... - # as you can't programatically access TIMER_n_DURATION - def set_timer_helper(n, value): - if n == 0: - config.TIMER_1_DURATION = value - elif n == 1: - config.TIMER_2_DURATION = value - elif n == 2: - config.TIMER_3_DURATION = value - else: - utils.log(0, "There are only 3 timers at all. Trying to set the timer number {n} failed.") - - config.LCD.clear() - timer_values = [config.TIMER_1_DURATION, config.TIMER_2_DURATION, config.TIMER_3_DURATION] - current_timer = timer_values[n] # get the n'th timer - current_timer_div = lambda: divmod(current_timer, 60) - - - config.LCD.putstr(f"Timer {n+1}".center(16)) - config.LCD.putstr(f"{'v' if current_timer > 1 else ' '} " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") - btn_left = config.PIN_IN_BTN_1 - btn_right = config.PIN_IN_BTN_2 - left_was_released = True - right_was_released = True - while True: - if btn_left.value() == 1: - if left_was_released: - time_press_start = time_ns() - left_was_released = False - sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed - if btn_right.value() == 1: # exit if both btns are pressed - set_timer_helper(n, current_timer) - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - # define the step width - time_now = time_ns() - if (time_now - time_press_start) <= 1*(10**9): # max. 1 seconds pressed - current_timer -= 1 - elif (time_now - time_press_start) <= 2*(10**9): # max. 2 seconds pressed - current_timer -= 5 - elif (time_now - time_press_start) <= 3*(10**9): # max. 3 seconds pressed - current_timer -= 10 - elif (time_now - time_press_start) <= 4*(10**9): # max. 4 seconds pressed - current_timer -= 30 - elif (time_now - time_press_start) <= 5*(10**9): # max. 5 seconds pressed - current_timer -= 60 - else: # longer than 5s pressed - current_timer -= 300 - if current_timer < 1: current_timer = 5999 - config.LCD.move_to(0,1) - config.LCD.putstr("v " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") - else: - left_was_released = True - if btn_right.value() == 1: - if right_was_released: - time_press_start = time_ns() - right_was_released = False - sleep(0.1) - if btn_left.value() == 1: # exit if both btns are pressed - set_timer_helper(n, current_timer) - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - # define the step width - time_now = time_ns() - if (time_now - time_press_start) <= 1*(10**9): # max. 1 seconds pressed - current_timer += 1 - elif (time_now - time_press_start) <= 2*(10**9): # max. 2 seconds pressed - current_timer += 5 - elif (time_now - time_press_start) <= 3*(10**9): # max. 3 seconds pressed - current_timer += 10 - elif (time_now - time_press_start) <= 4*(10**9): # max. 4 seconds pressed - current_timer += 30 - elif (time_now - time_press_start) <= 5*(10**9): # max. 5 seconds pressed - current_timer += 60 - else: # longer than 5s pressed - current_timer += 300 - if current_timer > 5999: current_timer = 1 - config.LCD.move_to(0,1) - config.LCD.putstr("v " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") - else: - right_was_released = True - def set_log_level(): - config.LCD.clear() - current_level = config.LOG_LEVEL - log_levels = ["WARN", "INFO", "DEBUG"] - - config.LCD.putstr(f"Log level".center(16)) - config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row - btn_left = config.PIN_IN_BTN_1 - btn_right = config.PIN_IN_BTN_2 - while True: - if btn_left.value() == 1: - sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed - if btn_right.value() == 1: - config.LOG_LEVEL = current_level - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - - current_level -= 1 - if current_level < 0: current_level = 2 - config.LCD.move_to(0,0) - config.LCD.putstr(f"Log level".center(16)) - config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row - if btn_right.value() == 1: - sleep(0.1) - if btn_left.value() == 1: - config.LOG_LEVEL = current_level - config.LCD.move_to(0,1) # move to the second row - config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set - sleep(0.5) - while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released - pass - return True - current_level += 1 - if current_level > 2: current_level = 0 - config.LCD.move_to(0,0) - config.LCD.putstr(f"Log level".center(16)) - config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row - - def reset(): # reset all user-settable configuration to the default values - config.LCD.clear() - config.LCD.putstr("Sure about that?") - config.LCD.putstr("< no yes >") - no_btn = config.PIN_IN_BTN_1 - yes_btn = config.PIN_IN_BTN_2 - while True: - if no_btn.value() == 1: - return None - if yes_btn.value() == 1: - config.LCD.putstr("Resetting...".center(16)) - config.LCD.putstr("Welcome Screen".center(16)) - config.STARTUP_WELCOME_SHOW = True - config.STARTUP_WELCOME_CYCLES = 1 - sleep(0.5) - config.LCD.move_to(0,1) - config.LCD.putstr("Timers".center(16)) - config.TIMER_1_DURATION = 60 - config.TIMER_2_DURATION = 2400 - config.TIMER_3_DURATION = 2700 - sleep(0.5) - config.LCD.move_to(0,1) - config.LCD.putstr("Logging".center(16)) - config.LOG_LEVEL = 1 - sleep(0.5) - return True - - settings_programs = [("Show welcome", toggle_show_welcome), - ("Welcome cycles", welcome_cycles), - ("Timer 1", lambda: set_n_timer(0)), - ("Timer 2", lambda: set_n_timer(1)), - ("Timer 3", lambda: set_n_timer(2)), - ("Log level", set_log_level), - ("Reset", reset), - ("Exit", settings_menu.stop)] - settings_menu.setup(settings_programs) # give it the callback list - settings_menu.run() # run the menu until it's closed - del settings_menu, settings_programs - collect() - return True -def run_demo_menu(): - def lcd_driver_demo(): - import lcd_driver_demo as ldd - ldd.run(config.LCD) - del ldd - collect() - return True - def lcd_big_hello(): - import lcd_big_hello as lbh - lbh.run(config.LCD) - del lbh - collect() - return True - def input_tests(): - import input_tests as input_tests - input_tests.run(serial_output=False) - del input_tests - collect() - return True - demo_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="DEMOS") - demo_programs = [("LCD Demo", lcd_driver_demo), - ("Hello world", lcd_big_hello), - ("Input tests", input_tests), - ("Exit", demo_menu.stop)] - demo_menu.setup(demo_programs) # give it the callback list - demo_menu.run() - del demo_menu, demo_programs - collect() - return True - if config.STARTUP_WELCOME_SHOW: ws = WelcomeScreen(config.LCD, interrupt_pins=[config.PIN_IN_BTN_1, config.PIN_IN_BTN_2, config.PIN_IN_SWITCH], @@ -353,12 +28,37 @@ if config.STARTUP_WELCOME_SHOW: del ws collect() +# extra functions to access the garbage collector +def timer(): + import programs.timer as t + t.run(config, btn_mapping, utils.log, lcdMenu) + del t + collect() + return True +def manual(): + import programs.manual as m + m.run(config) + del m + collect() + return True +def demos(): + import programs.demos as d + d.run(config, btn_mapping, utils.log, lcdMenu) + del d + collect() + return True +def settings(): + import programs.settings as s + s.run(config, btn_mapping, utils.log, lcdMenu) + del s + collect() + return True # create the menus main_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="PROGRAMS") main_programs = [("Timer", timer), ("Manual", manual), - ("Demos", run_demo_menu), + ("Demos", demos), ("Settings", settings)] main_menu.setup(main_programs) # give it the callback list diff --git a/programs/demos.py b/programs/demos.py new file mode 100644 index 0000000..46f9da8 --- /dev/null +++ b/programs/demos.py @@ -0,0 +1,41 @@ +from gc import collect + +# All the arguments this method takes are there for one reason: reduce the amount of libraries loaded into the picos memory and thus improving performance +# config: utils.Config object +# btn_mapping: a dict containing the btn mapping for the menu operation (given to the lcdMenu object) +# log: the utils.log function +# lcdMenu: the lcdMenu class (not an object of that class!) +def run(config, btn_mapping, log, lcdMenu): + def lcd_driver_demo(): + import demos.lcd_driver_demo as ldd + ldd.run(config.LCD) + del ldd + collect() + return True + def lcd_big_hello(): + import demos.lcd_big_hello as lbh + lbh.run(config.LCD) + del lbh + collect() + return True + def input_tests(): + import demos.input_tests as it + it.run(serial_output=False) + del it + collect() + return True + demo_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="DEMOS") + demo_programs = [("LCD Demo", lcd_driver_demo), + ("Hello world", lcd_big_hello), + ("Input tests", input_tests), + ("Exit", demo_menu.stop)] + demo_menu.setup(demo_programs) # give it the callback list + demo_menu.run() + return True + +if __name__ == "__main__": + from utils import Config, log + from lcdMenu import lcdMenu + config = Config() + btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus + run(Config(), btn_mapping, log, lcdMenu) \ No newline at end of file diff --git a/programs/manual.py b/programs/manual.py new file mode 100644 index 0000000..1fb76c6 --- /dev/null +++ b/programs/manual.py @@ -0,0 +1,17 @@ +def run(config): + config.LCD.clear() + set_value = config.PIN_OUT_RELAIS.value() + config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ") + while True: + if set_value != config.PIN_IN_SWITCH.value(): + config.PIN_OUT_RELAIS.value(config.PIN_IN_SWITCH.value()) + set_value = config.PIN_OUT_RELAIS.value() + config.LCD.putstr(f"---- MANUAL ---- State: {set_value} ") + if config.PIN_IN_BTN_1.value() == 1 or config.PIN_IN_BTN_2.value() == 1: + return True # exit on press of these buttons; True to disable the Quitting message from lcdMenu + +if __name__ == "__main__": + from utils import Config, log + config = Config() + btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus + run(Config(), btn_mapping, log) \ No newline at end of file diff --git a/programs/settings.py b/programs/settings.py new file mode 100644 index 0000000..5a45a68 --- /dev/null +++ b/programs/settings.py @@ -0,0 +1,243 @@ +from time import sleep, time_ns + +# All the arguments this method takes are there for one reason: reduce the amount of libraries loaded into the picos memory and thus improving performance +# config: utils.Config object +# btn_mapping: a dict containing the btn mapping for the menu operation (given to the lcdMenu object) +# log: the utils.log function +# lcdMenu: the lcdMenu class (not an object of that class!) +def run(config, btn_mapping, log, lcdMenu): + settings_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="SETTINGS") + def toggle_show_welcome(): + current_value = config.STARTUP_WELCOME_SHOW + config.LCD.clear() + config.LCD.putstr(f"Currently {'on' if current_value else 'off'}".center(16)) + config.LCD.putstr("< keep change >") + keep_btn = config.PIN_IN_BTN_1 + change_btn = config.PIN_IN_BTN_2 + while True: + if keep_btn.value() == 1: + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Stay {'on' if current_value else 'off'}!".center(16)) + sleep(0.5) + return True + if change_btn.value() == 1: + config.STARTUP_WELCOME_SHOW = not current_value + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Turned {'on' if not current_value else 'off'}!".center(16)) + sleep(0.5) + return True + def welcome_cycles(): + config.LCD.clear() + current_cycles = config.STARTUP_WELCOME_CYCLES + + option_down = [" ", "v"][current_cycles>1] + config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") + btn_left = config.PIN_IN_BTN_1 + btn_right = config.PIN_IN_BTN_2 + while True: + if btn_left.value() == 1: + sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed + if btn_right.value() == 1: + config.STARTUP_WELCOME_CYCLES = current_cycles + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + + current_cycles -= 1 + if current_cycles < 1: current_cycles = 1 + option_down = [" ", "v"][current_cycles>1] + config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") + if btn_right.value() == 1: + sleep(0.1) + if btn_left.value() == 1: + config.STARTUP_WELCOME_CYCLES = current_cycles + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + current_cycles += 1 + option_down = [" ", "v"][current_cycles>1] + config.LCD.putstr(f" Cycles \n{option_down} {str(current_cycles).center(12)} ^") + + # with n being the number of the timer (starting at 0) + def set_n_timer(n: int): + # a small helper function to save the value of the n'th timer... + # as you can't programatically access TIMER_n_DURATION + def set_timer_helper(n, value): + if n == 0: + config.TIMER_1_DURATION = value + elif n == 1: + config.TIMER_2_DURATION = value + elif n == 2: + config.TIMER_3_DURATION = value + else: + log(0, "There are only 3 timers at all. Trying to set the timer number {n} failed.") + + config.LCD.clear() + timer_values = [config.TIMER_1_DURATION, config.TIMER_2_DURATION, config.TIMER_3_DURATION] + current_timer = timer_values[n] # get the n'th timer + current_timer_div = lambda: divmod(current_timer, 60) + + + config.LCD.putstr(f"Timer {n+1}".center(16)) + config.LCD.putstr(f"{'v' if current_timer > 1 else ' '} " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") + btn_left = config.PIN_IN_BTN_1 + btn_right = config.PIN_IN_BTN_2 + left_was_released = True + right_was_released = True + while True: + if btn_left.value() == 1: + if left_was_released: + time_press_start = time_ns() + left_was_released = False + sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed + if btn_right.value() == 1: # exit if both btns are pressed + set_timer_helper(n, current_timer) + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + # define the step width + time_now = time_ns() + if (time_now - time_press_start) <= 1*(10**9): # max. 1 seconds pressed + current_timer -= 1 + elif (time_now - time_press_start) <= 2*(10**9): # max. 2 seconds pressed + current_timer -= 5 + elif (time_now - time_press_start) <= 3*(10**9): # max. 3 seconds pressed + current_timer -= 10 + elif (time_now - time_press_start) <= 4*(10**9): # max. 4 seconds pressed + current_timer -= 30 + elif (time_now - time_press_start) <= 5*(10**9): # max. 5 seconds pressed + current_timer -= 60 + else: # longer than 5s pressed + current_timer -= 300 + if current_timer < 1: current_timer = 5999 + config.LCD.move_to(0,1) + config.LCD.putstr("v " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") + else: + left_was_released = True + if btn_right.value() == 1: + if right_was_released: + time_press_start = time_ns() + right_was_released = False + sleep(0.1) + if btn_left.value() == 1: # exit if both btns are pressed + set_timer_helper(n, current_timer) + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + # define the step width + time_now = time_ns() + if (time_now - time_press_start) <= 1*(10**9): # max. 1 seconds pressed + current_timer += 1 + elif (time_now - time_press_start) <= 2*(10**9): # max. 2 seconds pressed + current_timer += 5 + elif (time_now - time_press_start) <= 3*(10**9): # max. 3 seconds pressed + current_timer += 10 + elif (time_now - time_press_start) <= 4*(10**9): # max. 4 seconds pressed + current_timer += 30 + elif (time_now - time_press_start) <= 5*(10**9): # max. 5 seconds pressed + current_timer += 60 + else: # longer than 5s pressed + current_timer += 300 + if current_timer > 5999: current_timer = 1 + config.LCD.move_to(0,1) + config.LCD.putstr("v " + f"{current_timer_div()[0]:02d}:{current_timer_div()[1]:02d}".center(12) + " ^") + else: + right_was_released = True + def set_log_level(): + config.LCD.clear() + current_level = config.LOG_LEVEL + log_levels = ["WARN", "INFO", "DEBUG"] + + config.LCD.putstr(f"Log level".center(16)) + config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row + btn_left = config.PIN_IN_BTN_1 + btn_right = config.PIN_IN_BTN_2 + while True: + if btn_left.value() == 1: + sleep(0.1) # this value is a good compromise between being able to press both buttons and a fast up/down speed + if btn_right.value() == 1: + config.LOG_LEVEL = current_level + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + + current_level -= 1 + if current_level < 0: current_level = 2 + config.LCD.move_to(0,0) + config.LCD.putstr(f"Log level".center(16)) + config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row + if btn_right.value() == 1: + sleep(0.1) + if btn_left.value() == 1: + config.LOG_LEVEL = current_level + config.LCD.move_to(0,1) # move to the second row + config.LCD.putstr(f"Saved!".center(16)) # show a little info that it is now set + sleep(0.5) + while btn_right.value() == 1 or btn_left.value() == 1: # wait till both btns are released + pass + return True + current_level += 1 + if current_level > 2: current_level = 0 + config.LCD.move_to(0,0) + config.LCD.putstr(f"Log level".center(16)) + config.LCD.putstr("v " + f"{log_levels[current_level]} ({current_level})".center(12) + " ^") # show the log level and it's name in the second row + + def reset(): # reset all user-settable configuration to the default values + config.LCD.clear() + config.LCD.putstr("Sure about that?") + config.LCD.putstr("< no yes >") + no_btn = config.PIN_IN_BTN_1 + yes_btn = config.PIN_IN_BTN_2 + while True: + if no_btn.value() == 1: + return None + if yes_btn.value() == 1: + config.LCD.putstr("Resetting...".center(16)) + config.LCD.putstr("Welcome Screen".center(16)) + config.STARTUP_WELCOME_SHOW = True + config.STARTUP_WELCOME_CYCLES = 1 + sleep(0.5) + config.LCD.move_to(0,1) + config.LCD.putstr("Timers".center(16)) + config.TIMER_1_DURATION = 60 + config.TIMER_2_DURATION = 2400 + config.TIMER_3_DURATION = 2700 + sleep(0.5) + config.LCD.move_to(0,1) + config.LCD.putstr("Logging".center(16)) + config.LOG_LEVEL = 1 + sleep(0.5) + return True + + settings_programs = [("Show welcome", toggle_show_welcome), + ("Welcome cycles", welcome_cycles), + ("Timer 1", lambda: set_n_timer(0)), + ("Timer 2", lambda: set_n_timer(1)), + ("Timer 3", lambda: set_n_timer(2)), + ("Log level", set_log_level), + ("Reset", reset), + ("Exit", settings_menu.stop)] + settings_menu.setup(settings_programs) # give it the callback list + settings_menu.run() # run the menu until it's closed + +if __name__ == "__main__": + from utils import Config, log + from lcdMenu import lcdMenu + config = Config() + btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus + run(Config(), btn_mapping, log, lcdMenu) \ No newline at end of file diff --git a/programs/timer.py b/programs/timer.py new file mode 100644 index 0000000..85b59ce --- /dev/null +++ b/programs/timer.py @@ -0,0 +1,62 @@ +from time import sleep, time_ns + +# All the arguments this method takes are there for one reason: reduce the amount of libraries loaded into the picos memory and thus improving performance +# config: utils.Config object +# btn_mapping: a dict containing the btn mapping for the menu operation (given to the lcdMenu object) +# log: the utils.log function +# lcdMenu: the lcdMenu class (not an object of that class!) +def run(config, btn_mapping, log, lcdMenu): + timer_menu = lcdMenu(config.LCD, btn_mapping, scroll_direction=True, cycle=True, hide_menu_name=False, name="TIMERS") + timer_values_original = [config.TIMER_1_DURATION, config.TIMER_2_DURATION, config.TIMER_3_DURATION] + timer_values = timer_values_original.copy() # here, the current timers time is stored when interrupting via the interrupt_pin (see below) + timer_splits = [lambda: divmod(round(timer_values[0]), 60), lambda: divmod(round(timer_values[1]), 60), lambda: divmod(round(timer_values[2]), 60)] + interrupt_pin = config.PIN_IN_BTN_1 # the interrupt btn stops the timer, saves the current time and goes back to the menu + reset_pin = config.PIN_IN_BTN_2 # the reset btn restores the default value + start_stop_pin = config.PIN_IN_SWITCH # the start_stop switch starts/stops the timer + + # timer_number is the number of the timer that will be run, starting at 1 + # the _timer variable is needed because otherwise python will throw crazy errors regarding "variable accessed before assignment"... + # ...just see it as a copy of the timer_number-1'th elemnt of the timer_values list (see above) + def run_timer(timer_number: int, _timer: int): + config.LCD.clear() + config.LCD.putstr(f"Timer {timer_number}".center(16)) + last_start_stop_value = start_stop_pin.value() + config.PIN_OUT_RELAIS.value(last_start_stop_value) + last_time_ns = time_ns() + while True: # now run the timer (if the switch is high) + config.LCD.move_to(0,1) + _timer_div = divmod(round(_timer), 60) + config.LCD.putstr(f"{_timer_div[0]:02d}:{_timer_div[1]:02d}".center(16)) + if interrupt_pin.value() == 1: + timer_values[timer_number-1] = _timer # save the current state + last_start_stop_value = 0 # turn the LEDs off! + config.PIN_OUT_RELAIS.value(last_start_stop_value) + return None + if reset_pin.value() == 1: + _timer = timer_values_original[timer_number-1] # reset the timers + if _timer <= 0: + config.PIN_OUT_RELAIS.off() + return True + sleep(0.05) + if last_start_stop_value != (new_value := start_stop_pin.value()): + last_start_stop_value = new_value + config.PIN_OUT_RELAIS.value(new_value) + last_time_ns = time_ns() + if start_stop_pin.value() == 1: + _timer -= (time_ns() - last_time_ns) / 1000**3 + last_time_ns = time_ns() + + timer_programs = [(f"T1 - {timer_splits[0]()[0]:02d}:{timer_splits[0]()[1]:02d}", lambda: run_timer(1, timer_values[0])), + (f"T2 - {timer_splits[1]()[0]:02d}:{timer_splits[1]()[1]:02d}", lambda: run_timer(2, timer_values[1])), + (f"T3 - {timer_splits[2]()[0]:02d}:{timer_splits[2]()[1]:02d}", lambda: run_timer(3, timer_values[2])), + ("Exit", timer_menu.stop)] + timer_menu.setup(timer_programs) # give it the callback list + timer_menu.run() + return True # disable the "Quitting" message from lcdMenu + +if __name__ == "__main__": + from utils import Config, log + from lcdMenu import lcdMenu + config = Config() + btn_mapping = {"ok_btn": config.PIN_IN_BTN_1, "next_btn": config.PIN_IN_BTN_2} # the btn mapping for all lcdMenus + run(Config(), btn_mapping, log, lcdMenu) \ No newline at end of file