Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 89-apps-should-be-able-to-specify-if-they-want-wifi-to-be-disabled-when-entering-them
  • 9Rmain
  • allow-reloading-sunmenu
  • always-have-a-wifi-instance
  • anon/gpndemo
  • anon/update-sim
  • anon/webflasher
  • app_text_viewer
  • audio_input
  • audio_io
  • blm_dev_chan
  • ch3/bl00mbox_docs
  • ci-1690580595
  • dev_p4
  • dev_p4-iggy
  • dev_p4-iggy-rebased
  • dx/dldldld
  • dx/fb-save-restore
  • dx/hint-hint
  • dx/jacksense-headset-mic-only
  • events
  • fil3s-limit-filesize
  • fil3s-media
  • fpletz/flake
  • gr33nhouse-improvements
  • history-rewrite
  • icon-flower
  • iggy/stemming
  • iggy/stemming_merge
  • led_fix_fix
  • main
  • main+schneider
  • media_has_video_has_audio
  • micropython_api
  • mixer2
  • moon2_demo_temp
  • moon2_migrate_apps
  • more-accurate-battery
  • pippin/ctx_sprite_sheet_support
  • pippin/display-python-errors-on-display
  • pippin/make_empty_drawlists_skip_render_and_blit
  • pippin/more-accurate-battery
  • pippin/tcp_redirect_hack
  • pippin/tune_ctx_config_update_from_upstream
  • pippin/uhm_flash_access_bust
  • pressable_bugfix
  • py_only_update_fps_overlay_when_changing
  • q3k/doom-poc
  • q3k/render-to-texture
  • rahix/flow3rseeds
  • raw_captouch_new
  • raw_captouch_old
  • release/1.0.0
  • release/1.1.0
  • release/1.1.1
  • release/1.2.0
  • release/1.3.0
  • release/1.4.0
  • restore_blit
  • return_of_melodic_demo
  • rev4_micropython
  • schneider/application-remove-name
  • schneider/bhi581
  • schneider/factory_test
  • schneider/recovery
  • scope_hack
  • sdkconfig-spiram-tinyusb
  • sec/auto-nick
  • sec/blinky
  • sector_size_512
  • shoegaze-fps
  • smaller_gradient_lut
  • store_delta_ms_and_ins_as_class_members
  • task_cleanup
  • uctx-wip
  • w1f1-in-sim
  • widgets_draw
  • wifi-json-error-handling
  • wip-docs
  • wip-tinyusb
  • v1.0.0
  • v1.0.0+rc1
  • v1.0.0+rc2
  • v1.0.0+rc3
  • v1.0.0+rc4
  • v1.0.0+rc5
  • v1.0.0+rc6
  • v1.1.0
  • v1.1.0+rc1
  • v1.1.1
  • v1.2.0
  • v1.2.0+rc1
  • v1.3.0
  • v1.4.0
94 results

Target

Select target project
  • flow3r/flow3r-firmware
  • Vespasian/flow3r-firmware
  • alxndr42/flow3r-firmware
  • pl/flow3r-firmware
  • Kari/flow3r-firmware
  • raimue/flow3r-firmware
  • grandchild/flow3r-firmware
  • mu5tach3/flow3r-firmware
  • Nervengift/flow3r-firmware
  • arachnist/flow3r-firmware
  • TheNewCivilian/flow3r-firmware
  • alibi/flow3r-firmware
  • manuel_v/flow3r-firmware
  • xeniter/flow3r-firmware
  • maxbachmann/flow3r-firmware
  • yGifoom/flow3r-firmware
  • istobic/flow3r-firmware
  • EiNSTeiN_/flow3r-firmware
  • gnudalf/flow3r-firmware
  • 999eagle/flow3r-firmware
  • toerb/flow3r-firmware
  • pandark/flow3r-firmware
  • teal/flow3r-firmware
  • x42/flow3r-firmware
  • alufers/flow3r-firmware
  • dos/flow3r-firmware
  • yrlf/flow3r-firmware
  • LuKaRo/flow3r-firmware
  • ThomasElRubio/flow3r-firmware
  • ai/flow3r-firmware
  • T_X/flow3r-firmware
  • highTower/flow3r-firmware
  • beanieboi/flow3r-firmware
  • Woazboat/flow3r-firmware
  • gooniesbro/flow3r-firmware
  • marvino/flow3r-firmware
  • kressnerd/flow3r-firmware
  • quazgar/flow3r-firmware
  • aoid/flow3r-firmware
  • jkj/flow3r-firmware
  • naomi/flow3r-firmware
41 results
Select Git revision
  • 89-apps-should-be-able-to-specify-if-they-want-wifi-to-be-disabled-when-entering-them
  • 9Rmain
  • allow-reloading-sunmenu
  • always-have-a-wifi-instance
  • anon/gpndemo
  • anon/update-sim
  • anon/webflasher
  • app_text_viewer
  • audio_input
  • audio_io
  • blm_dev_chan
  • ch3/bl00mbox_docs
  • ci-1690580595
  • dev_p4
  • dev_p4-iggy
  • dev_p4-iggy-rebased
  • dx/dldldld
  • dx/fb-save-restore
  • dx/hint-hint
  • dx/jacksense-headset-mic-only
  • events
  • fil3s-limit-filesize
  • fil3s-media
  • fpletz/flake
  • gr33nhouse-improvements
  • history-rewrite
  • icon-flower
  • iggy/stemming
  • iggy/stemming_merge
  • led_fix_fix
  • main
  • main+schneider
  • media_has_video_has_audio
  • micropython_api
  • mixer2
  • moon2_demo_temp
  • moon2_migrate_apps
  • more-accurate-battery
  • pippin/ctx_sprite_sheet_support
  • pippin/display-python-errors-on-display
  • pippin/make_empty_drawlists_skip_render_and_blit
  • pippin/more-accurate-battery
  • pippin/tcp_redirect_hack
  • pippin/tune_ctx_config_update_from_upstream
  • pippin/uhm_flash_access_bust
  • pressable_bugfix
  • py_only_update_fps_overlay_when_changing
  • q3k/doom-poc
  • q3k/render-to-texture
  • rahix/flow3rseeds
  • raw_captouch_new
  • raw_captouch_old
  • release/1.0.0
  • release/1.1.0
  • release/1.1.1
  • release/1.2.0
  • release/1.3.0
  • release/1.4.0
  • restore_blit
  • return_of_melodic_demo
  • rev4_micropython
  • schneider/application-remove-name
  • schneider/bhi581
  • schneider/factory_test
  • schneider/recovery
  • scope_hack
  • sdkconfig-spiram-tinyusb
  • sec/auto-nick
  • sec/blinky
  • sector_size_512
  • shoegaze-fps
  • smaller_gradient_lut
  • store_delta_ms_and_ins_as_class_members
  • task_cleanup
  • uctx-wip
  • w1f1-in-sim
  • widgets_draw
  • wifi-json-error-handling
  • wip-docs
  • wip-tinyusb
  • v1.0.0
  • v1.0.0+rc1
  • v1.0.0+rc2
  • v1.0.0+rc3
  • v1.0.0+rc4
  • v1.0.0+rc5
  • v1.0.0+rc6
  • v1.1.0
  • v1.1.0+rc1
  • v1.1.1
  • v1.2.0
  • v1.2.0+rc1
  • v1.3.0
  • v1.4.0
94 results
Show changes
Showing
with 1217 additions and 30 deletions
from st3m.input import InputState
from st3m.goose import Optional, List
from st3m.ui import colours
from st3m.utils import sd_card_plugged
from st3m.ui.view import BaseView
from ctx import Context
import os
from st3m import application_settings
class DeleteView(BaseView):
def __init__(self, app) -> None:
super().__init__()
self._app = app
self._delete = 0
def draw(self, ctx: Context) -> None:
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
ctx.gray(1)
ctx.move_to(0, 0)
ctx.font = "Camp Font 3"
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.font_size = 24
ctx.text(self._app.name)
ctx.move_to(0, -40)
ctx.font_size = 20
ctx.text("Delete app?")
for x in range(2):
x = 1 - x
ctx.translate(0, 35)
ctx.move_to(0, 0)
ctx.gray(1)
if x == self._delete:
ctx.rectangle(-120, -15, 240, 30).fill()
ctx.gray(0)
ctx.text("yes" if x else "no")
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms) # Let BaseView do its thing
self._delete += self.input.buttons.app.right.pressed
self._delete -= self.input.buttons.app.left.pressed
self._delete %= 2
if self.input.buttons.app.middle.pressed:
if self._delete:
application_settings.delete_app(self._app.installed_path)
self._app.update_installed_version(None)
self.vm.pop()
from st3m.input import InputState
from st3m.goose import Optional, List
from st3m.ui import colours
from st3m.utils import sd_card_plugged
import urequests
import gzip
from utarfile import TarFile, DIRTYPE
import io
import os
import gc
import math
from st3m.ui.view import BaseView
from ctx import Context
from st3m import application_settings
class DownloadView(BaseView):
response: Optional[bytes] = b""
error_message: str = ""
_download_instance: Optional["download_file"]
"""
View state
1 = Init
2 = Fetching
3 = Extracting
4 = Extracting
5 = Done
6 = Error
"""
_state: int
def __init__(self, app, version) -> None:
super().__init__()
self._state = 1
self._try = 1
self._app = app
self._version = version
self._url = version.tar_url
self.response = b""
self._download_instance = None
self.download_percentage = 0
def _get_app_folder(self, tar_size: int) -> Optional[str]:
if sd_card_plugged():
sd_statvfs = os.statvfs("/sd")
if tar_size < sd_statvfs[1] * sd_statvfs[3]:
return "/sd/apps/"
flash_statvfs = os.statvfs("/flash")
if tar_size < flash_statvfs[1] * flash_statvfs[3]:
return "/flash/apps/"
return None
def draw(self, ctx: Context) -> None:
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
ctx.rgb(*colours.WHITE)
# Arc strokes should be drawn before move_to for some reason to look nice
if self.download_percentage and self._state == 2:
ctx.save()
ctx.line_width = 3
ctx.rotate(270 * math.pi / 180)
ctx.arc(
0, 0, 117, math.tau * (1 - self.download_percentage), math.tau, 0
).stroke()
ctx.restore()
ctx.save()
ctx.move_to(0, 0)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
text_to_draw = ""
if self._state == 1 or self._state == 2:
# Fetching
text_to_draw = "Downloading..."
if self._download_instance:
text_to_draw += f"\n{len(self.response)}b"
self._state = 2
elif self._state == 3 or self._state == 4:
# Extracting
text_to_draw = "Extracting..."
self._state = 4
elif self._state == 5:
# Done
ctx.move_to(0, -30)
ctx.text("All done...")
ctx.gray(0.75)
ctx.font_size = 22
text_to_draw = "The app will be\navailable after reboot"
elif self._state == 6:
# Errored
ctx.move_to(0, -30)
ctx.text("Oops...")
text_to_draw = self.error_message
ctx.font_size = 12
y_offset = 0
for line in text_to_draw.split("\n"):
ctx.move_to(0, y_offset)
ctx.text(line)
y_offset += int(ctx.font_size * 1.25)
ctx.restore()
def download_file(self, url: str, block_size=10240) -> List[bytes]:
gc.collect()
req = urequests.get(url)
total_size = int(req.headers["Content-Length"])
try:
while True:
new_data = req.raw.read(block_size)
yield new_data, total_size
if len(new_data) < block_size:
break
finally:
req.close()
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms) # Let BaseView do its thing
if self.vm.transitioning:
return
if self.input.buttons.app.middle.pressed:
if self.vm is None:
raise RuntimeError("vm is None")
self.vm.pop()
if self._state == 2:
fail_reason = ""
try:
if not self._download_instance:
print("Getting it")
self.response = b""
self._download_instance = self.download_file(self._url)
return
else:
try:
new_data, total_size = next(self._download_instance)
self.download_percentage = len(self.response) / total_size
if new_data:
self.response += new_data
return
except StopIteration:
self._download_instance = None
if self.response is not None:
print("Got something")
self._state = 3
return
fail_reason = "No content"
except MemoryError:
gc.collect()
self.response = None
self.error_message = "Out of Memory\n(app too big?)"
self._state = 6
return
except Exception as e:
fail_reason = f"Exception:\n{str(e)}"
print(fail_reason)
self._try += 1
if self._try >= 3:
self.response = None
self.error_message = fail_reason
self._state = 6
elif self._state == 4:
if self.response is None:
raise RuntimeError("response is None")
try:
gc.collect()
tar = gzip.decompress(self.response)
self.response = None
gc.collect()
t = TarFile(fileobj=io.BytesIO(tar))
except MemoryError:
gc.collect()
self.response = None
self.error_message = "Out of Memory\n(app too big?)"
self._state = 6
return
app_folder = self._get_app_folder(len(tar))
if not app_folder:
gc.collect()
self.response = None
self.error_message = f"Not Enough Space\nSD/flash lack:\n{len(tar)}b"
self._state = 6
return
if not os.path.exists(app_folder):
print(f"making {app_folder}")
os.mkdir(app_folder)
installed_path = app_folder + self._app.slug
if os.path.exists(installed_path):
print("removing old files at", installed_path)
application_settings.delete_app(installed_path)
for i in t:
print(i.name)
if i.type == DIRTYPE:
print("dirtype")
dirname = app_folder + i.name
if not os.path.exists(dirname):
print("making", dirname)
os.mkdir(dirname)
else:
print("dir", dirname, "exists")
else:
filename = app_folder + i.name
print("writing to", filename)
f = t.extractfile(i)
with open(filename, "wb") as of:
while data := f.read():
of.write(data)
self._app.installed_path = installed_path
self._app.update_installed_version(self._version)
print("installed at", installed_path)
self._state = 5
[app]
name = "Get Apps"
category = "Hidden"
wifi_preference = true
[entry]
class = "Gr33nhouseApp"
[metadata]
author = "Flow3r Badge Authors"
license = "LGPL-3.0-only"
url = "https://git.flow3r.garden/flow3r/flow3r-firmware"
from st3m.goose import Optional, Enum
from st3m.input import InputState
from st3m.ui import colours
from st3m.ui.view import BaseView, ViewManager
from ctx import Context
from .confirmation import ConfirmationView
from .background import Flow3rView
from .background import seed_cols as PETAL_COLORS
import math
import urequests
import time
import gc
PETAL_MAP = [0, 2, 4, 6, 8]
ONE_FIFTH = math.pi * 2 / 5
ONE_TENTH = math.pi * 2 / 10
class ViewState(Enum):
ENTER_SEED = 1
LOADING = 2
SEED_NOT_FOUND = 3
class ManualInputView(BaseView):
current_petal: Optional[int]
wait_timer: Optional[int]
def __init__(self, colors=None) -> None:
super().__init__()
self.background = Flow3rView(colors)
self.flow3r_seed = ""
self.current_petal = None
self.wait_timer = None
self.state = ViewState.ENTER_SEED
def on_enter(self, vm: ViewManager | None) -> None:
super().on_enter(vm)
self.flow3r_seed = ""
self.state = ViewState.ENTER_SEED
def on_exit(self) -> bool:
# request thinks after on_exit
return True
def draw(self, ctx: Context) -> None:
self.background.draw(ctx)
if self.state == ViewState.ENTER_SEED:
ctx.save()
for i in range(5):
ctx.rgb(*PETAL_COLORS[i])
ctx.move_to(0, 0)
ctx.arc(0, 0, 140, -ONE_TENTH - math.pi / 2, ONE_TENTH - math.pi / 2, 0)
ctx.arc(0, 0, 80, ONE_TENTH - math.pi / 2, -ONE_TENTH - math.pi / 2, 1)
ctx.fill()
ctx.rgb(0, 0, 0)
ctx.arc(0, -100, 18, 0, math.pi * 2, 9).fill()
ctx.rgb(1, 1, 1)
ctx.move_to(0, -100)
ctx.font = "Camp Font 1"
ctx.font_size = 32
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.text(str(i))
ctx.rotate(ONE_FIFTH)
# ctx.rgb(0, 0, 0)
# ctx.arc(0, 0, 80, 0, math.pi * 2, 0).fill()
ctx.restore()
ctx.rgb(1, 1, 1)
ctx.move_to(0, 0)
ctx.font = "Camp Font 1"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.text(self.flow3r_seed)
elif self.state == ViewState.LOADING:
ctx.rgb(1, 1, 1)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.move_to(0, -12)
ctx.text(f"Loading")
ctx.move_to(0, 12)
ctx.font = "Camp Font 1"
ctx.text(self.flow3r_seed)
elif self.state == ViewState.SEED_NOT_FOUND:
ctx.rgb(1, 0, 0)
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.move_to(0, -12)
ctx.font = "Camp Font 1"
ctx.text(self.flow3r_seed)
ctx.move_to(0, 12)
ctx.font = "Camp Font 3"
ctx.text(f"not found!")
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
self.background.think(ins, delta_ms)
if not self.is_active():
return
if self.state == ViewState.ENTER_SEED:
if self.current_petal is not None:
if not ins.captouch.petals[self.current_petal].pressed:
self.current_petal = None
if self.current_petal is None:
for i, petal in enumerate(PETAL_MAP):
if ins.captouch.petals[petal].pressed:
self.flow3r_seed += str(i)
self.current_petal = petal
if len(self.flow3r_seed) == 8:
self.state = ViewState.LOADING
elif self.state == ViewState.LOADING:
if self.wait_timer is None:
self.wait_timer = time.ticks_ms()
if (time.ticks_ms() - self.wait_timer) < 100:
return
self.wait_timer = None
print(f"Loading app info for seed {self.flow3r_seed}...")
res = urequests.get(
f"https://flow3r.garden/api/apps/{self.flow3r_seed}.json"
)
if res.status_code != 200:
# We are hitting RAM limits in this place. Free up
# everything we can to keep the following code from
# hitting allocation errors.
del res
gc.collect()
print(f"No app found for seed {self.flow3r_seed}!")
self.state = ViewState.SEED_NOT_FOUND
else:
if self.vm is None:
raise RuntimeError("vm is None")
app = res.json()
self.vm.push(
ConfirmationView(
url=app["tarDownloadUrl"],
name=app["name"],
author=app["author"],
)
)
elif self.state == ViewState.SEED_NOT_FOUND:
if self.wait_timer is None:
self.wait_timer = time.ticks_ms()
if (time.ticks_ms() - self.wait_timer) > 2000:
print("Please enter a new seed!")
self.flow3r_seed = ""
self.state = ViewState.ENTER_SEED
self.wait_timer = None
from st3m.input import InputState
from st3m.ui import colours
from st3m.ui.view import BaseView, ViewManager
from ctx import Context
from .background import Flow3rView
class RecordView(BaseView):
def __init__(self) -> None:
super().__init__()
self.background = Flow3rView()
def draw(self, ctx: Context) -> None:
ctx.move_to(0, 0)
ctx.save()
ctx.rgb(*colours.BLACK)
ctx.rectangle(
-120.0,
-120.0,
240.0,
240.0,
).fill()
ctx.rgb(*colours.WHITE)
ctx.font = "Camp Font 3"
ctx.font_size = 24
ctx.text_align = ctx.CENTER
ctx.text_baseline = ctx.MIDDLE
ctx.text("Coming soon")
ctx.restore()
from st3m.application import Application
import math, random, sys_display
from ctx import Context
class App(Application):
def __init__(self, app_ctx):
super().__init__(app_ctx)
self.x = 23
self.x_vel = 40 / 1000.0
self.y = -53
self.font_size = 16
self.delta_ms = 0
self.right_pressed = False
self.left_pressed = False
self.select_pressed = False
self.angle = 0
self.focused_widget = 1
self.active = False
def draw_widget(self, label):
ctx = self.ctx
self.widget_no += 1
if not self.active:
if self.select_pressed and self.focused_widget > 0:
self.active = True
self.select_pressed = False
elif self.left_pressed:
self.focused_widget -= 1
if self.focused_widget < 1:
self.focused_widget = 1
self.left_pressed = False
elif self.right_pressed:
self.focused_widget += 1
if self.focused_widget > 9:
self.focused_widget = 9
self.right_pressed = False
if self.widget_no == self.focused_widget and not self.active:
ctx.rectangle(-130, int(self.y - self.font_size * 0.8), 260, self.font_size)
ctx.line_width = 2.0
ctx.rgba(0.8, 0.6, 0.1, 1.0)
ctx.stroke()
ctx.gray(1)
ctx.move_to(-95, self.y)
self.y += self.font_size
ctx.text(label + ": ")
def draw_choice(self, label, choices, no):
ctx = self.ctx
self.draw_widget(label)
if self.widget_no == self.focused_widget and self.active:
if self.left_pressed:
no -= 1
if no < 0:
no = 0
elif self.right_pressed:
no += 1
if no >= len(choices):
no = len(choices) - 1
elif self.select_pressed:
self.active = False
self.select_pressed = False
for a in range(len(choices)):
if a == no and self.active and self.widget_no == self.focused_widget:
ctx.save()
ctx.rgba(0.8, 0.6, 0.1, 1.0)
ctx.line_width = 2.0
ctx.rectangle(
ctx.x - 1,
ctx.y - self.font_size * 0.8,
ctx.text_width(choices[a]) + 2,
self.font_size,
).stroke()
ctx.restore()
ctx.text(choices[a] + " ")
elif a == no:
ctx.save()
ctx.gray(1)
ctx.rectangle(
ctx.x - 1,
ctx.y - self.font_size * 0.8,
ctx.text_width(choices[a]) + 2,
self.font_size,
).fill()
ctx.gray(0)
ctx.text(choices[a] + " ")
ctx.restore()
else:
ctx.text(choices[a] + " ")
return no
def draw_boolean(self, label, value):
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")
else:
ctx.text(" off")
return value
def draw_bg(self):
ctx = self.ctx
ctx.gray(1.0)
ctx.font_size = self.font_size
ctx.move_to(-100, -50)
self.y = -50
self.widget_no = 0
ctx.rectangle(-120, -120, 240, 240)
ctx.gray(0)
ctx.fill()
ctx.save()
ctx.translate(self.x, -80)
ctx.logo(0, 0, 40)
ctx.restore()
self.x += self.delta_ms * self.x_vel
if self.x < -50 or self.x > 50:
self.x_vel *= -1
self.x += self.delta_ms * self.x_vel
def draw(self, ctx: Context):
curmode = sys_display.get_mode()
low_latency = (curmode & sys_display.low_latency) != 0
direct_ctx = (curmode & sys_display.direct_ctx) != 0
lock = (curmode & sys_display.lock) != 0
osd = (curmode & sys_display.osd) != 0
think_per_draw = (curmode & sys_display.EXPERIMENTAL_think_per_draw) != 0
smart_redraw = (curmode & sys_display.smart_redraw) != 0
scale = 0
if (curmode & sys_display.x4) == sys_display.x2:
scale = 1
elif (curmode & sys_display.x4) == sys_display.x3:
scale = 2
elif (curmode & sys_display.x4) == sys_display.x4:
scale = 3
bpp = curmode & 63
palette = 0
if bpp == 9:
palette = 0
bpp = 0
elif bpp == 8:
palette = 1
bpp = 0
elif bpp == 10:
palette = 2
bpp = 0
elif bpp == 11:
palette = 3
bpp = 0
elif bpp == 16:
bpp = 1
elif bpp == 24:
bpp = 2
elif bpp == 32:
bpp = 3
elif bpp == 1:
bpp = 4
elif bpp == 2:
bpp = 5
elif bpp == 4:
bpp = 6
else:
bpp = 0
self.ctx = ctx
self.draw_bg()
bpp = self.draw_choice("bpp", ["8", "16", "24", "32", "1", "2", "4"], bpp)
if bpp > 0:
palette = 0
palette = self.draw_choice("palette", ["RGB", "gray", "sepia", "cool"], palette)
scale = self.draw_choice("scale", ["1x", "2x", "3x", "4x"], scale)
low_latency = self.draw_boolean("low latency", low_latency)
direct_ctx = self.draw_boolean("direct ctx", direct_ctx)
think_per_draw = self.draw_boolean("think per draw", think_per_draw)
smart_redraw = self.draw_boolean("smart redraw", smart_redraw)
osd = self.draw_boolean("osd", osd)
lock = self.draw_boolean("lock", lock)
if direct_ctx:
low_latency = True
if palette != 0:
bpp = 0
if bpp == 0:
if palette == 0:
mode = 9
elif palette == 1:
mode = 8
elif palette == 2:
mode = 10
elif palette == 3:
mode = 11
elif bpp == 1:
mode = 16
elif bpp == 2:
mode = 24
elif bpp == 3:
mode = 32
elif bpp == 4:
mode = 1
elif bpp == 5:
mode = 2
elif bpp == 6:
mode = 4
mode += osd * sys_display.osd
mode += low_latency * sys_display.low_latency
mode += direct_ctx * sys_display.direct_ctx
mode += lock * sys_display.lock
mode += think_per_draw * sys_display.EXPERIMENTAL_think_per_draw
mode += smart_redraw * sys_display.smart_redraw
if scale == 1:
mode += sys_display.x2
elif scale == 2:
mode += sys_display.x3
elif scale == 3:
mode += sys_display.x4
if mode != curmode:
sys_display.set_default_mode(mode)
################################################################
self.delta_ms = 0
self.select_pressed = False
self.left_pressed = False
self.right_pressed = False
def think(self, ins, delta_ms):
super().think(ins, delta_ms)
self.delta_ms += delta_ms
if (
self.input.buttons.app.right.pressed
or self.input.buttons.app.right.repeated
):
self.right_pressed = True
if self.input.buttons.app.left.pressed or self.input.buttons.app.left.repeated:
self.left_pressed = True
if self.input.buttons.app.middle.pressed:
self.select_pressed = True
if __name__ == "__main__":
from st3m.run import run_app
run_app(App)
[app]
name = "Graphics Mode"
category = "Hidden"
[metadata]
author = "Flow3r Badge Authors"
license = "LGPL-3.0-only"
url = "https://git.flow3r.garden/flow3r/flow3r-firmware"
# micropython imports
from machine import I2C, Pin
# flow3r imports
from st3m.application import Application, ApplicationContext
from ctx import Context
from st3m.input import InputController, InputState
class I2CScanner(Application):
def __init__(self, app_ctx: ApplicationContext) -> None:
super().__init__(app_ctx)
self.input = InputController()
self.qwiic = I2C(1, freq=400000)
self._pending_scan = False
def scan(self):
self._devices = self.qwiic.scan()
print("Found Devices:")
print(self._devices)
def on_enter(self, vm):
super().on_enter(vm)
self._devices = None
def draw(self, ctx: Context) -> None:
# Get the default font
ctx.font = ctx.get_font_name(1)
# Draw a black background
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
ctx.text_align = ctx.MIDDLE
ctx.font_size = 16
ctx.rgb(1, 1, 1)
ctx.move_to(0, -60).text("I2C Device Scanner")
ctx.rgb(0.7, 0.7, 0.7)
ctx.font_size = 14
yOffset = 18
yStart = -30
ctx.move_to(0, yStart).text("Attach a device to the Qwiic port")
ctx.font_size = 16
ctx.move_to(0, yStart + yOffset * 1).text("Press OK to Re-Scan")
if self._devices is None:
ctx.move_to(0, yStart + (3 * yOffset)).text("Scanning...")
self._pending_scan = True
elif len(self._devices) == 0:
ctx.rgb(0.7, 0.0, 0.0)
ctx.move_to(0, yStart + (3 * yOffset)).text("No Devices Found")
else:
ctx.rgb(0.0, 0.7, 0.0)
ctx.move_to(0, yStart + (3 * yOffset)).text(
"Found %d Devices: " % len(self._devices)
)
devices_str = ""
for d in self._devices:
devices_str += hex(d) + " "
ctx.move_to(0, yStart + (4 * yOffset)).text(devices_str)
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
if self.input.buttons.app.middle.pressed:
self._devices = None
if self._pending_scan and not self.vm.transitioning:
self.scan()
self._pending_scan = False
# For running with `mpremote run`:
if __name__ == "__main__":
import st3m.run
st3m.run.run_app(I2CScanner)
[app]
name = "I2C/Qwiic Scanner"
category = "Demos"
[entry]
class = "I2CScanner"
[metadata]
author = "Flow3r Badge Authors"
license = "LGPL-3.0-only"
url = "https://git.flow3r.garden/flow3r/flow3r-firmware"
......@@ -2,6 +2,8 @@
import random
import time
import math
import json
import errno
# flow3r imports
from st3m.application import Application, ApplicationContext
......@@ -24,12 +26,21 @@ log.info("hello led painter")
class LEDPainter(Application):
def get_help(self):
help_text = (
"use petals 0 to 5 to set rgb values or petals "
"6 or 7 for shortcuts to black and white.\n\n"
"use the app button to move cw/ccw through "
"the LEDs by pressing left/right and enable "
"or disable drawing to LEDs by pressing down.\n\n"
"your pattern is saved when you exit and can be "
'set as a "LED wallpaper" in settings->appearance.'
)
return help_text
def __init__(self, app_ctx: ApplicationContext) -> None:
super().__init__(app_ctx)
self.input = InputController()
# self.scroll_R = CapScrollController()
# self.scroll_G = CapScrollController()
# self.scroll_B = CapScrollController()
self._cursor = 0
self._draw = True
self.STEPS = 30
......@@ -58,65 +69,152 @@ class LEDPainter(Application):
(39, 94),
(70, 51),
]
# self.PETAL_POS.reverse()
self.PETAL_POS = [
tuple([(k - 120) * 1.1 + 120 for k in g]) for g in self.PETAL_POS
]
def _try_load_settings(self, path):
try:
with open(path, "r") as f:
return json.load(f)
except OSError as e:
if e.errno != errno.ENOENT:
raise # ignore file not found
def _try_save_settings(self, path, settings):
try:
with open(path, "w+") as f:
f.write(json.dumps(settings))
f.close()
except OSError as e:
if e.errno != errno.ENOENT:
raise # ignore file not found
def _load_settings(self):
settings_path = "/flash/menu_leds.json"
settings = self._try_load_settings(settings_path)
if settings is None:
return
self.LEDS = settings["leds"]
for i in range(40):
col = settings["leds"][i]
leds.set_rgb(i, col[0], col[1], col[2])
def _save_settings(self):
settings_path = "/flash/menu_leds.json"
old_settings = self._try_load_settings(settings_path)
file_is_different = False
if old_settings is None:
file_is_different = True
else:
try:
ref_LEDS = old_settings["leds"]
for l in range(40):
if file_is_different:
break
for c in range(3):
if self.LEDS[l][c] != ref_LEDS[l][c]:
file_is_different = True
except:
file_is_different = True
if file_is_different:
settings = {}
settings["leds"] = self.LEDS
self._try_save_settings(settings_path, settings)
def on_enter(self, vm):
super().on_enter(vm)
self._load_settings()
leds.set_slew_rate(max(leds.get_slew_rate(), 220))
def on_exit(self):
self._save_settings()
def draw(self, ctx: Context) -> None:
ctx.font = ctx.get_font_name(1)
ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill()
ctx.rgb(1, 1, 1).rectangle(-31, -31, 62, 62).fill()
ctx.rgb(self.r / 255, self.g / 255, self.b / 255).rectangle(
-30, -30, 60, 60
).fill()
if (self.r == 0) and (self.g == 0) and (self.b == 0):
ctx.font_size = 20
ctx.move_to(-30, 0).rgb(255, 255, 255).text("BLACK")
# if (self.r == 0) and (self.g == 0) and (self.b == 0):
# ctx.move_to(0, 0).rgb(255, 255, 255).text("BLACK")
ctx.font_size = 16
ctx.text_align = ctx.LEFT
ctx.move_to(39, 5 - 17)
ctx.rgb(1, 0, 0).text(str(self.r))
ctx.move_to(39, 5)
ctx.rgb(0, 1, 0).text(str(self.g))
ctx.move_to(39, 5 + 17)
ctx.rgb(0, 0, 1).text(str(self.b))
ctx.rgb(0.7, 0.7, 0.7)
text_shift = -20
ctx.text_align = ctx.RIGHT
ctx.move_to(text_shift - 5, -62).text("brush:")
ctx.move_to(text_shift - 5, -42).text("<-/->:")
ctx.text_align = ctx.LEFT
if self._draw:
self.LEDS[self._cursor] = [self.r, self.g, self.b]
ctx.font_size = 14
ctx.move_to(-80, -40).rgb(255, 255, 255).text("(Center L) Brush Down")
ctx.move_to(text_shift, -60).text("down")
ctx.move_to(text_shift, -44).text("draw")
for i in range(len(self.LEDS)):
leds.set_rgb(i, self.LEDS[i][0], self.LEDS[i][1], self.LEDS[i][2])
else:
ctx.font_size = 14
ctx.move_to(-80, -40).rgb(255, 255, 255).text("(Center L) Brush Up")
ctx.move_to(text_shift, -60).text("up")
ctx.move_to(text_shift, -44).text("move")
for i in range(len(self.LEDS)):
leds.set_rgb(i, self.LEDS[i][0], self.LEDS[i][1], self.LEDS[i][2])
leds.set_rgb(self._cursor, 255, 255, 255)
if (self.r == 255) and (self.g == 255) and (self.b == 255):
leds.set_rgb(self._cursor, 0, 0, 255)
leds.update()
off_x = 130
off_y = 110
ctx.text_align = ctx.CENTER
ctx.font_size = 20
leds.update()
off_x = 120
off_y = 120
ctx.move_to(self.PETAL_POS[0][0] - off_x, self.PETAL_POS[0][1] - off_y).rgb(
255, 255, 255
1, 0, 0
).text("R+")
ctx.move_to(self.PETAL_POS[1][0] - off_x, self.PETAL_POS[1][1] - off_y).rgb(
255, 255, 255
1, 0, 0
).text("R-")
ctx.move_to(self.PETAL_POS[2][0] - off_x, self.PETAL_POS[2][1] - off_y).rgb(
255, 255, 255
0, 1, 0
).text("G+")
ctx.move_to(self.PETAL_POS[3][0] - off_x, self.PETAL_POS[3][1] - off_y).rgb(
255, 255, 255
0, 1, 0
).text("G-")
ctx.move_to(self.PETAL_POS[4][0] - off_x, self.PETAL_POS[4][1] - off_y).rgb(
255, 255, 255
0, 0, 1
).text("B+")
ctx.move_to(self.PETAL_POS[5][0] - off_x, self.PETAL_POS[5][1] - off_y).rgb(
255, 255, 255
0, 0, 1
).text("B-")
ctx.move_to(self.PETAL_POS[6][0] - off_x, self.PETAL_POS[6][1] - off_y).rgb(
255, 255, 255
).text("B")
ctx.move_to(self.PETAL_POS[7][0] - off_x, self.PETAL_POS[7][1] - off_y).rgb(
255, 255, 255
).text("W")
ctx.font_size = 16
pos_x = self.PETAL_POS[6][0] - off_x
pos_y = self.PETAL_POS[6][1] - off_y
ctx.move_to(pos_x, pos_y).rgb(255, 255, 255)
ctx.rectangle(pos_x - 19, pos_y - 18, 38, 26).stroke()
ctx.text("BLK")
pos_x = self.PETAL_POS[7][0] - off_x
pos_y = self.PETAL_POS[7][1] - off_y
ctx.move_to(pos_x, pos_y).rgb(255, 255, 255)
ctx.rectangle(pos_x - 19, pos_y - 18, 38, 26).fill()
ctx.rgb(0, 0, 0)
ctx.text("WHT")
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
......@@ -193,7 +291,19 @@ class LEDPainter(Application):
self.g = 255
self.b = 255
def get_help(self):
ret = (
"Use the petals to set a color and the application button to move and "
"lift the brush to paint on the LEDs! On exit the LED data is stored "
"at /flash/menu_leds.json. This file sets the default LED color "
"pattern for the main menus and is not specific to LED Painter, other "
"applications may read and write to it."
)
return ret
# For running with `mpremote run`:
if __name__ == "__main__":
import st3m.run
# if __name__ == '__main__':
# # Continue to make runnable via mpremote run.
# st3m.run.run_view(LEDPainter(ApplicationContext()))
st3m.run.run_app(LEDPainter)
[app]
name = "LED Painter"
menu = "Apps"
category = "Apps"
[entry]
class = "LEDPainter"
......
from st3m.application import Application
import sys_display, math, random
class App(Application):
def __init__(self, app_ctx):
super().__init__(app_ctx)
def on_enter(self, vm):
super().on_enter(vm)
sys_display.set_mode(1)
pal = bytearray(256 * 3)
modr = random.getrandbits(7) + 1
modg = random.getrandbits(7) + 1
modb = random.getrandbits(7) + 1
pal[0 * 3] = 0
pal[0 * 3 + 1] = 0
pal[0 * 3 + 2] = 0
pal[1 * 3] = 255
pal[1 * 3 + 1] = 255
pal[1 * 3 + 2] = 255
sys_display.set_palette(pal)
self.y = 0
self.xa = -1.5
self.xb = 1.5
self.ya = -2.0
self.yb = 1.0
def draw(self, ctx: Context):
fb_info = sys_display.fb(0)
fb = fb_info[0]
max_iterations = 30
width = fb_info[1]
height = fb_info[2]
stride = fb_info[3]
for chunk in range(3): # do 3 scanlines at a time
y = self.y
if y < height:
zy = y * (self.yb - self.ya) / (height - 1) + self.ya
inners = 0
for x in range(width):
zx = x * (self.xb - self.xa) / (width - 1) + self.xa
z = zy + zx * 1j
c = z
reached = 0
for i in range(max_iterations):
if abs(z) > 2.0:
break
z = z * z + c
reached = i
val = reached * 255 / max_iterations
val = int(math.sqrt(val / 255) * 16) & 1
fb[int((y * stride * 8 + x) / 8)] |= int(val) << (x & 7)
self.y += 1
if __name__ == "__main__":
import st3m.run
st3m.run.run_app(App)
from st3m.application import Application
import sys_display, math, random
class App(Application):
def __init__(self, app_ctx):
super().__init__(app_ctx)
def on_enter(self, vm):
super().on_enter(vm)
sys_display.set_mode(4)
self.y = 0
self.xa = -1.5
self.xb = 1.5
self.ya = -2.0
self.yb = 1.0
def draw(self, ctx: Context):
fb_info = sys_display.fb(0)
fb = fb_info[0]
max_iterations = 30
width = fb_info[1]
height = fb_info[2]
stride = fb_info[3]
for chunk in range(3): # do 3 scanlines at a time
y = self.y
if y < height:
zy = y * (self.yb - self.ya) / (height - 1) + self.ya
inners = 0
for x in range(width):
zx = x * (self.xb - self.xa) / (width - 1) + self.xa
z = zy + zx * 1j
c = z
reached = 0
for i in range(max_iterations):
if abs(z) > 2.0:
break
z = z * z + c
reached = i
val = reached * 255 / max_iterations
val = math.sqrt(val / 255) * 16
if val > 15:
val = 15
fb[int((y * stride * 2 + x) / 2)] |= int(val) << ((x & 1) * 4)
self.y += 1
def think(self, ins, delta_ms):
super().think(ins, delta_ms)
if self.input.buttons.app.right.pressed:
pal = bytearray(256 * 3)
modr = random.getrandbits(7) + 1
modg = random.getrandbits(7) + 1
modb = random.getrandbits(7) + 1
for i in range(256):
pal[i * 3] = int((i % modr) * (255 / modr))
pal[i * 3 + 1] = int((i % modg) * (255 / modg))
pal[i * 3 + 2] = int((i % modb) * (255 / modb))
sys_display.set_palette(pal)
if self.input.buttons.app.left.pressed:
pal = bytearray(256 * 3)
for i in range(256):
pal[i * 3] = i
pal[i * 3 + 1] = i
pal[i * 3 + 2] = i
sys_display.set_palette(pal)
if __name__ == "__main__":
import st3m.run
st3m.run.run_app(App)
from st3m.application import Application
import sys_display, math, random
from ctx import Context
class App(Application):
def __init__(self, app_ctx):
super().__init__(app_ctx)
def on_enter(self, vm):
super().on_enter(vm)
self.y = 0
self.xa = -1.5
self.xb = 1.5
self.ya = -2.0
self.yb = 1.0
def on_enter_done(self):
sys_display.set_mode(sys_display.cool | sys_display.x2)
def draw(self, ctx: Context):
if self.vm.transitioning:
ctx.gray(0).rectangle(-120, -120, 240, 240).fill()
return
fb_info = sys_display.fb(sys_display.cool)
fb = fb_info[0]
max_iterations = 30
width = fb_info[1]
height = fb_info[2]
stride = fb_info[3]
for chunk in range(3): # do 3 scanlines at a time
y = self.y
if y < height:
zy = y * (self.yb - self.ya) / (height - 1) + self.ya
inners = 0
for x in range(width // 2):
zx = x * (self.xb - self.xa) / (width - 1) + self.xa
z = zy + zx * 1j
c = z
reached = 0
if inners > 10 and y < height * 0.7:
reached = max_iterations - 1
else:
for i in range(max_iterations):
if abs(z) > 2.0:
break
z = z * z + c
reached = i
if reached == max_iterations - 1:
inners += 1
val = reached * 255 / max_iterations
val = math.sqrt(val / 255) * 255
fb[y * stride + x] = int(val)
fb[y * stride + width - 1 - x] = int(val)
self.y += 1
def think(self, ins, delta_ms):
super().think(ins, delta_ms)
if self.input.buttons.app.right.pressed:
pal = bytearray(256 * 3)
modr = random.getrandbits(7) + 1
modg = random.getrandbits(7) + 1
modb = random.getrandbits(7) + 1
for i in range(256):
pal[i * 3] = int((i % modr) * (255 / modr))
pal[i * 3 + 1] = int((i % modg) * (255 / modg))
pal[i * 3 + 2] = int((i % modb) * (255 / modb))
sys_display.set_palette(pal)
if self.input.buttons.app.left.pressed:
pal = bytearray(256 * 3)
for i in range(256):
pal[i * 3] = i
pal[i * 3 + 1] = i
pal[i * 3 + 2] = i
sys_display.set_palette(pal)
if __name__ == "__main__":
import st3m.run
st3m.run.run_app(App)
[app]
name = "Mandelbrot"
category = "Apps"
[metadata]
author = "Flow3r Badge Authors"
license = "LGPL-3.0-only"
url = "https://git.flow3r.garden/flow3r/flow3r-firmware"