From ef4e2ab0dfbc0887ee911d7a6604b0ae0302d06f Mon Sep 17 00:00:00 2001 From: moon2 <moon2protonmail@protonmail.com> Date: Fri, 22 Sep 2023 02:04:40 +0000 Subject: [PATCH] leds: added random menu mode --- docs/api/led_patterns.rst | 26 ++++++ docs/index.rst | 1 + python_payload/apps/appearance/__init__.py | 65 ++++++++------ python_payload/st3m/application.py | 2 +- python_payload/st3m/led_patterns.py | 32 ------- python_payload/st3m/run.py | 2 +- python_payload/st3m/settings.py | 12 ++- python_payload/st3m/ui/led_patterns.py | 99 ++++++++++++++++++++++ sim/fakes/leds.py | 4 + 9 files changed, 180 insertions(+), 63 deletions(-) create mode 100644 docs/api/led_patterns.rst delete mode 100644 python_payload/st3m/led_patterns.py create mode 100644 python_payload/st3m/ui/led_patterns.py diff --git a/docs/api/led_patterns.rst b/docs/api/led_patterns.rst new file mode 100644 index 0000000000..46173d5013 --- /dev/null +++ b/docs/api/led_patterns.rst @@ -0,0 +1,26 @@ +.. py:module:: led_patterns + +``st3m.ui.led_patterns`` module +=============================== + +None of these functions call ``leds.update()``, to actually see the changes you have to do that yourself! + +.. py:function:: highlight_petal_rgb(num : int, r : float, g : float, b : float, num_leds : int = 5) -> None + + Sets the LED closest to the petal and num_leds-1 around it to the given rgb color. + If num_leds is uneven the appearance will be symmetric. + +.. py:function:: shift_all_hsv(h : float = 0, s : float = 0, v : float = 0) -> None + + Shifts all LEDs by the given values. Clips effective ``s`` and ``v``. + +.. py:function:: pretty_pattern() -> None + + Generates a random pretty pattern and loads it into the LED buffer. + +.. py:function:: set_menu_colors() -> None + + If not disabled in settings: Tries to load LED colors from /flash/menu_leds.json. Else, or in + case of missing file, call ``pretty_pattern()``. Note: There is no caching, it tries to attempt + to read the file every time, if you need something faster use ``leds.get_rgb`` to cache it in the + application. diff --git a/docs/index.rst b/docs/index.rst index 3fdc936c6d..20ce30f793 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -50,6 +50,7 @@ User manual api/ctx.rst api/leds.rst api/colours.rst + api/led_patterns.rst api/uos.rst api/sys_buttons.rst api/sys_display.rst diff --git a/python_payload/apps/appearance/__init__.py b/python_payload/apps/appearance/__init__.py index 61514471c1..e9ce5824ef 100644 --- a/python_payload/apps/appearance/__init__.py +++ b/python_payload/apps/appearance/__init__.py @@ -3,7 +3,7 @@ import math, random, sys_display from st3m import settings import leds import sys_display -from st3m.ui import colours +from st3m.ui import colours, led_patterns class App(Application): @@ -22,12 +22,12 @@ class App(Application): self.angle = 0 self.focused_widget = 1 self.active = False - self.num_widgets = 4 + self.num_widgets = 5 self.overhang = -20 self.line_height = 24 self.input.buttons.app.left.repeat_enable(800, 300) self.input.buttons.app.right.repeat_enable(800, 300) - self.mid_x = 50 + self.mid_x = 42 self.led_accumulator_ms = 0 self.blueish = False self.half_time = 600 @@ -107,6 +107,19 @@ class App(Application): ctx.text(choices[a] + " ") return no + def draw_boolean(self, label, value, on_str="on", off_str="off"): + ctx = self.ctx + self.draw_widget(label) + if self.widget_no == self.focused_widget and self.active: + value = not value + self.active = False + + if value: + ctx.text(on_str) + else: + ctx.text(off_str) + return value + def draw_number(self, label, step_size, no, unit=""): ctx = self.ctx self.draw_widget(label) @@ -171,7 +184,21 @@ class App(Application): self.draw_bg() tmp = self.draw_number( - "led brightness", 5, int(settings.num_leds_brightness.value) + "display brightness", + 5, + int(settings.num_display_brightness.value), + unit="%", + ) + if tmp < 5: + tmp = 5 + if tmp > 100: + tmp = 100 + if tmp != settings.num_display_brightness.value: + settings.num_display_brightness.set_value(tmp) + sys_display.set_backlight(settings.num_display_brightness.value) + + tmp = self.draw_number( + "LED brightness", 5, int(settings.num_leds_brightness.value) ) if tmp < 5: tmp = 5 @@ -181,7 +208,7 @@ class App(Application): settings.num_leds_brightness.set_value(tmp) leds.set_brightness(settings.num_leds_brightness.value) - tmp = self.draw_number("led speed", 5, int(settings.num_leds_speed.value)) + tmp = self.draw_number("LED speed", 5, int(settings.num_leds_speed.value)) if tmp < 0: tmp = 0 elif tmp > 255: @@ -194,19 +221,13 @@ class App(Application): settings.num_leds_speed.set_value(tmp) leds.set_slew_rate(settings.num_leds_speed.value) - tmp = self.draw_number( - "display brightness", - 5, - int(settings.num_display_brightness.value), - unit="%", + tmp = self.draw_boolean( + "menu LEDs", settings.onoff_leds_random_menu.value, "random", "user" ) - if tmp < 5: - tmp = 5 - if tmp > 100: - tmp = 100 - if tmp != settings.num_display_brightness.value: - settings.num_display_brightness.set_value(tmp) - sys_display.set_backlight(settings.num_display_brightness.value) + if tmp != settings.onoff_leds_random_menu.value: + settings.onoff_leds_random_menu.set_value(tmp) + led_patterns.pretty_pattern() + leds.update() self.delta_ms = 0 self.select_pressed = False @@ -230,15 +251,7 @@ class App(Application): while self.led_accumulator_ms > self.half_time: self.led_accumulator_ms = self.led_accumulator_ms % self.half_time - self.leds_shift_hue(0.8) - - def leds_shift_hue(self, val): - for i in range(40): - rgb = leds.get_rgb(i) - h, s, v = colours.rgb_to_hsv(*rgb) - h += val - leds.set_rgb(i, *colours.hsv_to_rgb(h, s, v)) - leds.update() + led_patterns.shift_all_hsv(h=0.8) def on_enter(self, vm): super().on_enter(vm) diff --git a/python_payload/st3m/application.py b/python_payload/st3m/application.py index da27f383be..6f38e9dabe 100644 --- a/python_payload/st3m/application.py +++ b/python_payload/st3m/application.py @@ -10,7 +10,7 @@ from st3m.goose import Optional, List, Dict from st3m.logging import Log from st3m import settings from ctx import Context -from st3m import led_patterns +from st3m.ui import led_patterns import leds import toml diff --git a/python_payload/st3m/led_patterns.py b/python_payload/st3m/led_patterns.py deleted file mode 100644 index 72adec3473..0000000000 --- a/python_payload/st3m/led_patterns.py +++ /dev/null @@ -1,32 +0,0 @@ -import leds -import json - - -def set_menu_colors(): - """ - set all LEDs to the configured menu colors if provided in - /flash/menu_leds.json. leds.update() must be called externally. - """ - path = "/flash/menu_leds.json" - try: - with open(path, "r") as f: - settings = json.load(f) - except OSError: - leds.set_all_rgb(0, 0, 0) - return - for i in range(40): - col = settings["leds"][i] - leds.set_rgb(i, col[0], col[1], col[2]) - - -def highlight_petal(num, r, g, b, num_leds=5): - """ - Sets the LED closest to the petal and num_leds-1 around it to - the color. If num_leds is uneven the appearance will be symmetric. - leds.update() must be called externally. - """ - num = num % 10 - if num_leds < 0: - num_leds = 0 - for i in range(num_leds): - leds.set_rgb((num * 4 + i - num_leds // 2) % 40, r, g, b) diff --git a/python_payload/st3m/run.py b/python_payload/st3m/run.py index 45bb77b743..c98f213116 100644 --- a/python_payload/st3m/run.py +++ b/python_payload/st3m/run.py @@ -20,7 +20,7 @@ from st3m.application import ( ) from st3m.about import About from st3m import settings_menu as settings, logging, processors, wifi -from st3m import led_patterns +from st3m.ui import led_patterns import st3m.wifi import captouch, audio, leds, gc, sys_buttons, sys_display, sys_mode, media diff --git a/python_payload/st3m/settings.py b/python_payload/st3m/settings.py index d814843dfe..4cda284985 100644 --- a/python_payload/st3m/settings.py +++ b/python_payload/st3m/settings.py @@ -241,11 +241,16 @@ num_speaker_max_db = StringTunable( ) num_display_brightness = StringTunable( - "Display Brightness", "system.brightness.display", 100 + "Display Brightness", "system.appearance.display_brightness", 100 +) +num_leds_brightness = StringTunable( + "LED Brightness", "system.appearance.leds_brightness", 70 ) -num_leds_brightness = StringTunable("LED Brightness", "system.brightness.leds", 70) -num_leds_speed = StringTunable("LED speed", "system.brightness.leds_speed", 235) +num_leds_speed = StringTunable("LED Speed", "system.appearance.leds_speed", 235) +onoff_leds_random_menu = OnOffTunable( + "Random Menu LEDs", "system.appearance.leds_random_menu", False +) # List of all settings to be loaded/saved load_save_settings: List[UnaryTunable] = [ @@ -273,6 +278,7 @@ load_save_settings: List[UnaryTunable] = [ num_display_brightness, num_leds_brightness, num_leds_speed, + onoff_leds_random_menu, ] diff --git a/python_payload/st3m/ui/led_patterns.py b/python_payload/st3m/ui/led_patterns.py new file mode 100644 index 0000000000..a5aa9ccdb1 --- /dev/null +++ b/python_payload/st3m/ui/led_patterns.py @@ -0,0 +1,99 @@ +import leds +import json +import math +import random + +from st3m.ui import colours +from st3m.settings import onoff_leds_random_menu + + +def _clip(val): + if val > 1.0: + return 1.0 + if val < 0.0: + return 1.0 + return val + + +def set_menu_colors(): + """ + set all LEDs to the configured menu colors if provided in + /flash/menu_leds.json and settings.onoff_leds_random_menu + is false, else calls pretty_pattern. + leds.update() must be called externally. + """ + if onoff_leds_random_menu.value: + pretty_pattern() + return + + path = "/flash/menu_leds.json" + try: + with open(path, "r") as f: + settings = json.load(f) + except OSError: + pretty_pattern() + return + for i in range(40): + col = settings["leds"][i] + leds.set_rgb(i, col[0], col[1], col[2]) + + +def pretty_pattern(): + """ + generates a pretty random pattern. + leds.update() must be called externally. + """ + hsv = [0.0, 0.0, 0.0] + hsv[0] = random.random() * math.tau + hsv[1] = random.random() * 0.3 + 0.7 + hsv[2] = random.random() * 0.3 + 0.7 + start = int(random.random() * 40) + for i in range(48): + hsv[0] += (random.random() - 0.5) * 2 + for j in range(1, 3): + hsv[j] += (random.random() - 0.5) / 2 + # asymmetric clipping: draw it to bright colors + if hsv[j] < 0.7: + hsv[j] = 0.7 + (0.7 - hsv[j]) / 2 + if hsv[j] > 1: + hsv[j] = 1 + + g = (i + start) % 40 + if i < 40: + leds.set_rgb(g, *colours.hsv_to_rgb(*hsv)) + else: + hsv_old = colours.rgb_to_hsv(*leds.get_rgb(g)) + hsv_mixed = [0.0, 0.0, 0.0] + k = (i - 39) / 8 + if abs(hsv[0] - hsv_old[0]) < math.tau / 2: + hsv_mixed[0] = hsv_old[0] * k + hsv[0] * (1 - k) + elif hsv[0] > hsv_old[0]: + hsv_mixed[0] = (hsv_old[0] + math.tau) * k + hsv[0] * (1 - k) + else: + hsv_mixed[0] = hsv_old[0] * k + (hsv[0] + math.tau) * (1 - k) + + for h in range(1, 3): + hsv_mixed[h] = hsv_old[h] * k + hsv[h] * (1 - k) + leds.set_rgb(j, *colours.hsv_to_rgb(*hsv_mixed)) + + +def shift_all_hsv(h=0, s=0, v=0): + for i in range(40): + hue, sat, val = colours.rgb_to_hsv(*leds.get_rgb(i)) + hue += h + sat = _clip(sat + s) + val = _clip(val + v) + leds.set_rgb(i, *colours.hsv_to_rgb(hue, sat, val)) + + +def highlight_petal_rgb(num, r, g, b, num_leds=5): + """ + Sets the LED closest to the petal and num_leds-1 around it to + the color. If num_leds is uneven the appearance will be symmetric. + leds.update() must be called externally. + """ + num = num % 10 + if num_leds < 0: + num_leds = 0 + for i in range(num_leds): + leds.set_rgb((num * 4 + i - num_leds // 2) % 40, r, g, b) diff --git a/sim/fakes/leds.py b/sim/fakes/leds.py index c7144899bc..6b268f4620 100644 --- a/sim/fakes/leds.py +++ b/sim/fakes/leds.py @@ -13,6 +13,10 @@ def set_rgb(ix, r, g, b): _sim.set_led_rgb(ix, r, g, b) +def get_rgb(ix): + return 0, 0, 0 + + def set_all_rgb(r, g, b): for i in range(40): set_rgb(i, r, g, b) -- GitLab