diff --git a/lib/WelcomeScreen.py b/lib/WelcomeScreen.py new file mode 100644 index 0000000..8f20c63 --- /dev/null +++ b/lib/WelcomeScreen.py @@ -0,0 +1,126 @@ +""" +WelcomeScreen: A simple library providing a customizable welcome screen fading over an LCD +Copyright (C) 2024 Benjamin Burkhardt + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +""" + +from time import sleep + + +class WelcomeScreen: + # __init__() - the constructor + # lcd: an object of I2C_LCD (from the PCF8574T library - https://git.privacynerd.de/BlueFox/micropython-libraries/src/branch/main/PCF8574T) + # --- + # interrupt_pins: a list containing machine.Pin objects exclusively (but if you give these you can still turn interrupting off + # via a parameter in the show method (see below!) + # --- + # subtitle: the text shown below the cycling text (e.g. the device's name, ...) (Lorem ipsum. by default) + # --- + # starting_msg: the text shown while cycling (default: Starting...) + # --- + # started_msg: the text shown while cycling (default: Started!) + def __init__(self, lcd, interrupt_pins=None, subtitle="Lorem ipsum.", starting_msg="Starting...", started_msg="Started!"): + self.lcd = lcd + self.columns = self.lcd.num_columns + self.lines = self.lcd.num_lines + + self.interrupt_pins = interrupt_pins + + self.subtitle = subtitle + self.starting_msg = starting_msg + self.started_msg = started_msg + + + # show() - Display the actual message + # --- + # cycles says how often the Starting text goes through + # --- + # wait_after_cycles is a number in seconds (can be float) defining how long to wait before returning OR + # if fading out is activated, the time to wait between end of the cycling animation and the fade out animation + # --- + # fade_down is a dict with following keys: + # - "enabled" - REQUIRED - wether fading is enabled (default: true) + # - "wait_between" - OPTIONAL - the time in seconds to wait between each line fade + # - "wait_after" - OPTIONAL - the time in seconds to wait after the fade out + # --- + # interruptable influences wether the program can be interrupted by pins in the self.interrupt_pins list + # if this list is empty, even when interruptable is set to true, nothing will be able to interrupt! + def show(self, cycles=1, wait_after_cycles=1, fade_down={"enabled": True}, interruptable=True): + if cycles < 1: cycles = 1 + padding = " " * self.columns # as much spaces as padding as one display line is long + padding_hyphen = "-" * self.columns # as much hyphens as padding as one display line is long + + # mechanism for centering on a 4*20 display + y_offset = 0 + if self.lines == 4: + y_offset = 1 + # also clear the top and bottom with ---- + self.lcd.move_to(0,0) + self.lcd.putstr(padding_hyphen) + self.lcd.move_to(0,4) + self.lcd.putstr(padding_hyphen) + + # get the current pin values (only if there are pins specified) (when something changes, the interrupt happens and the cycle stops) + break_flag = False + if self.interrupt_pins: + pin_values = [] + for p in self.interrupt_pins: + pin_values.append(p.value()) + # cycle the text 'cycles' times and listen for changes on interrupt pins (if any given) + for i in range(cycles): + line1 = padding + self.starting_msg + padding + line2 = self.subtitle.center(self.columns) + for i in range(self.columns + len(self.starting_msg)): + self.lcd.move_to(0,y_offset) + self.lcd.putstr(line1[0:self.columns]) + self.lcd.move_to(0,y_offset+1) + self.lcd.putstr(line2[0:self.columns]) + line1 = line1[1:] + if self.interrupt_pins: + for i, p in enumerate(self.interrupt_pins): + if pin_values[i] != p.value(): + break_flag = True + if break_flag: + break + if break_flag: + break + + self.lcd.move_to(0,y_offset) + self.lcd.putstr(self.started_msg.center(16)) + + sleep(wait_after_cycles) + + # now fade down if enabled via the params + if fade_down["enabled"]: + # get all the waiting times + if "wait_between" in fade_down.keys(): + wait_between = fade_down["wait_between"] + else: + wait_between = 0.1 + if "wait_after" in fade_down.keys(): + wait_after = fade_down["wait_after"] + else: + wait_after = 0.3 + if self.lines == 4: + old_display = padding_hyphen + self.started_msg.center(16) + self.subtitle.center(self.columns) + padding_hyphen + else: + old_display = self.started_msg.center(16) + self.subtitle.center(self.columns) + self.lcd.move_to(0,0) # move to the start of the lcd + while old_display != "": + old_display = old_display[:-self.columns] + lines_before = " " * (self.columns*self.lines-len(old_display)) + self.lcd.putstr(lines_before + old_display) + sleep(wait_between) + sleep(wait_after) \ No newline at end of file diff --git a/main.py b/main.py index 2c865a3..7f78a35 100644 --- a/main.py +++ b/main.py @@ -12,6 +12,7 @@ You should have received a copy of the GNU General Public License along with thi import config, utils from lib.ProgramChooserAdapted import ProgramChooser +from lib.WelcomeScreen import WelcomeScreen from time import sleep import gc # garbage collector for better memory performance @@ -61,10 +62,17 @@ programs = { "UV on": uv_on, "Timer": timer, "Manual": manual, - } +} - -if config.STARTUP_WELCOME_SHOW: utils.show_welcome() +if config.STARTUP_WELCOME_SHOW: + ws = WelcomeScreen(config.LCD, + interrupt_pins=[config.BTN_1, config.BTN_2, config.SWITCH], + subtitle=config.STARTUP_PROJECT_NAME, + starting_msg=config.STARTUP_MESSAGE_STARTING, + started_msg=config.STARTUP_MESSAGE_FINISHED) + ws.show(cycles=config.STARTUP_WELCOME_CYCLES) + del ws + gc.collect() pc = ProgramChooser(programs) # initialize the ProgramChooser pc.run() # and run it (will be an endless loop) diff --git a/utils.py b/utils.py index d61fa75..2ff585d 100644 --- a/utils.py +++ b/utils.py @@ -27,37 +27,3 @@ def log(log_level: int, message: str): elif cfg.LOG_LEVEL >= log_level: # if log level is valid print(f"[{log_mapping[log_level]}] {message}") - -""" -Simple function that displays a startup "welcome" screen -Configurable in config.py -""" -def show_welcome(): # cycles says how often the startup text goes through - cycles = cfg.STARTUP_WELCOME_CYCLES - if cycles < 1: - cycles = 1 - - padding = " "*cfg.LCD_I2C_NUM_COLS - started_str = cfg.STARTUP_MESSAGE_FINISHED - starting_str = cfg.STARTUP_MESSAGE_STARTING - - - # slide the first line over the display (animated from right to left) - for i in range(cycles): - line1 = padding + starting_str + padding - line2 = cfg.STARTUP_PROJECT_NAME - for i in range(cfg.LCD_I2C_NUM_COLS + len(starting_str)): - cfg.LCD.putstr(line1[0:16]) - cfg.LCD.move_to(0,1) - cfg.LCD.putstr(line2[0:16]) - line1 = line1[1:] - - cfg.LCD.move_to(0,0) - cfg.LCD.putstr(started_str) - - # now fade down - sleep(2) - cfg.LCD.move_to(0,0) - cfg.LCD.putstr(padding + started_str) - sleep(0.1) - cfg.LCD.clear()