From 6113ba1f32b1bfe53589534ca2cc114c43604198 Mon Sep 17 00:00:00 2001 From: luxflow Date: Wed, 25 Sep 2024 02:10:57 +0900 Subject: [PATCH] display: Add display sleep Add display sleep to prevent burn-in Currently supports UC1701, SSD1306 displays. Tested on SSD1306-based device (BTT MINI12864 V2.0) If multiple displays are connected, all displays will wake when a key_event occurs on the main display This is because only the main display can have menu Signed-off-by: YounJae Rho --- docs/Config_Reference.md | 3 +++ docs/G-Codes.md | 6 ++++++ klippy/extras/display/display.py | 32 ++++++++++++++++++++++++++++++++ klippy/extras/display/menu.py | 6 ++++++ klippy/extras/display/uc1701.py | 20 ++++++++++++++++++++ 5 files changed, 67 insertions(+) diff --git a/docs/Config_Reference.md b/docs/Config_Reference.md index ca21bcf21..69125c9ff 100644 --- a/docs/Config_Reference.md +++ b/docs/Config_Reference.md @@ -4441,6 +4441,9 @@ lcd_type: # controls the content of the screen (see the "display_data" section # for more information). The default is _default_20x4 for hd44780 or # aip31068_spi displays and _default_16x4 for other displays. +#sleep_timeout: +# Timeout for sleep in seconds. +# The default is 0 seconds (disabled) #menu_timeout: # Timeout for menu. Being inactive this amount of seconds will # trigger menu exit or return to root menu when having autorun diff --git a/docs/G-Codes.md b/docs/G-Codes.md index 7a60aa8a0..c29e2a7e8 100644 --- a/docs/G-Codes.md +++ b/docs/G-Codes.md @@ -323,6 +323,12 @@ display data groups in the config, e.g. `[display_data command. If DISPLAY is not specified it defaults to "display" (the primary display). +#### SET_DISPLAY_SLEEP +`SET_DISPLAY_SLEEP [DISPLAY=] SLEEP=[0|1]`: +Set the display to sleep or wake +If DISPLAY is not specified it defaults to "display" (the +primary display). + ### [display_status] The display_status module is automatically loaded if a diff --git a/klippy/extras/display/display.py b/klippy/extras/display/display.py index cc33bc154..07a8e99a0 100644 --- a/klippy/extras/display/display.py +++ b/klippy/extras/display/display.py @@ -179,6 +179,9 @@ class PrinterLCD: self.reactor = self.printer.get_reactor() # Load low-level lcd handler self.lcd_chip = config.getchoice('lcd_type', LCD_chips)(config) + self.sleep_timeout = config.getint('sleep_timeout', 0) + self.next_sleep_time = 0 + self.is_sleep = 0 # Load menu and display_status self.menu = None name = config.get_name() @@ -209,9 +212,15 @@ class PrinterLCD: gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', name, self.cmd_SET_DISPLAY_GROUP, desc=self.cmd_SET_DISPLAY_GROUP_help) + gcode.register_mux_command('SET_DISPLAY_SLEEP', 'DISPLAY', name, + self.cmd_SET_DISPLAY_SLEEP, + desc=self.cmd_SET_DISPLAY_SLEEP_help) if name == 'display': gcode.register_mux_command('SET_DISPLAY_GROUP', 'DISPLAY', None, self.cmd_SET_DISPLAY_GROUP) + gcode.register_mux_command('SET_DISPLAY_SLEEP', 'DISPLAY', None, + self.cmd_SET_DISPLAY_SLEEP, + desc=self.cmd_SET_DISPLAY_SLEEP_help) def get_dimensions(self): return self.lcd_chip.get_dimensions() def handle_ready(self): @@ -220,6 +229,10 @@ class PrinterLCD: self.reactor.update_timer(self.screen_update_timer, self.reactor.NOW) # Screen updating def screen_update_event(self, eventtime): + self.sleep_if_timeout(eventtime) + if self.is_sleep: + return eventtime + REDRAW_MIN_TIME + if self.redraw_request_pending: self.redraw_request_pending = False self.redraw_time = eventtime + REDRAW_MIN_TIME @@ -262,6 +275,21 @@ class PrinterLCD: data = [0xff] + [(pixels >> (i * 8)) & 0xff] * 14 + [0xff] self.lcd_chip.write_graphics(col + width - 1 - i, row, data) return "" + def update_next_sleep_time(self, eventtime): + if self.sleep_timeout != 0: + self.next_sleep_time = eventtime + self.sleep_timeout + def sleep_if_timeout(self, eventtime): + if self.is_sleep == 0 and self.sleep_timeout != 0: + if self.next_sleep_time == 0: + self.update_next_sleep_time(eventtime) + elif self.is_sleep == 0 and self.next_sleep_time < eventtime: + self.sleep(1) + def sleep(self, sleep): + if self.lcd_chip.sleep: + self.lcd_chip.sleep(sleep) + self.is_sleep = sleep + if not sleep: + self.next_sleep_time = 0 cmd_SET_DISPLAY_GROUP_help = "Set the active display group" def cmd_SET_DISPLAY_GROUP(self, gcmd): group = gcmd.get('GROUP') @@ -269,6 +297,10 @@ class PrinterLCD: if new_dg is None: raise gcmd.error("Unknown display_data group '%s'" % (group,)) self.show_data_group = new_dg + cmd_SET_DISPLAY_SLEEP_help = "Set the display to sleep" + def cmd_SET_DISPLAY_SLEEP(self, gcmd): + sleep = gcmd.get_int("SLEEP", 1) + self.sleep(sleep) def load_config(config): return PrinterLCD(config) diff --git a/klippy/extras/display/menu.py b/klippy/extras/display/menu.py index e7723a7e3..a3a69967a 100644 --- a/klippy/extras/display/menu.py +++ b/klippy/extras/display/menu.py @@ -722,6 +722,7 @@ class MenuManager: self.send_event('init', self) def handle_ready(self): + self.display_list = self.printer.lookup_objects('display') # start timer reactor = self.printer.get_reactor() reactor.register_timer(self.timer_event, reactor.NOW) @@ -1026,6 +1027,11 @@ class MenuManager: self.begin(eventtime) def key_event(self, key, eventtime): + for _, display in self.display_list: + if display.is_sleep: + display.sleep(0) + display.update_next_sleep_time(eventtime) + if key == 'click': self._click_callback(eventtime, key) elif key == 'long_click': diff --git a/klippy/extras/display/uc1701.py b/klippy/extras/display/uc1701.py index 85b74decd..86d2118f2 100644 --- a/klippy/extras/display/uc1701.py +++ b/klippy/extras/display/uc1701.py @@ -193,6 +193,17 @@ class UC1701(DisplayBase): self.send([0xA5]) # display all self.send([0xA4]) # normal display self.flush() + def sleep(self, on): + sleep_cmds = [ + 0xAE, # Display Off + 0xA5 # All pixel On + ] + wake_cmds = [ + 0xA4, # All pixel Off + 0xAF # Display On + ] + self.send(sleep_cmds if on else wake_cmds) + self.flush() # The SSD1306 supports both i2c and "4-wire" spi class SSD1306(DisplayBase): @@ -232,6 +243,15 @@ class SSD1306(DisplayBase): ] self.send(init_cmds) self.flush() + def sleep(self, on): + sleep_cmds = [ + 0xAE, # Display Off + ] + wake_cmds = [ + 0xAF # Display On + ] + self.send(sleep_cmds if on else wake_cmds) + self.flush() # the SH1106 is SSD1306 compatible with up to 132 columns class SH1106(SSD1306):