Did a huge refactoring and outsouring for modularity and a small performance improvement

This commit is contained in:
BlueFox 2024-11-16 16:31:33 +01:00
parent a6b531c394
commit aba5927221
Signed by: BlueFox
GPG Key ID: 327233DA85435270
8 changed files with 389 additions and 326 deletions

352
main.py
View File

@ -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

41
programs/demos.py Normal file
View File

@ -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)

17
programs/manual.py Normal file
View File

@ -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)

243
programs/settings.py Normal file
View File

@ -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)

62
programs/timer.py Normal file
View File

@ -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)