diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f637456..67eb951 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -68,6 +68,8 @@ jobs: micropython/ports/esp32/build-tildagon/partition_table/partition-table.bin micropython/ports/esp32/build-tildagon/ota_data_initial.bin micropython/ports/esp32/build-tildagon/tildagon.txt + micropython/ports/esp32/build-tildagon/frozen_mpy/frontboards/TwentyFour/app.mpy + micropython/ports/esp32/build-tildagon/frozen_mpy/frontboards/TwentyFour/tokens.mpy - name: Create latest release for tags uses: "marvinpinto/action-automatic-releases@latest" if: github.event_name == 'push' diff --git a/modules/app_components/tokens.py b/modules/app_components/tokens.py index df440a8..93a5b7f 100644 --- a/modules/app_components/tokens.py +++ b/modules/app_components/tokens.py @@ -52,3 +52,9 @@ def clear_background(ctx): def set_color(ctx, color): ctx.rgb(*ui_colors.get(color, colors.get(color, color))) + + +try: + from frontboard.tokens import * # noqa: F403 +except ImportError: + pass diff --git a/modules/frontboards/__init__.py b/modules/frontboards/__init__.py index 69513cd..b8c6210 100644 --- a/modules/frontboards/__init__.py +++ b/modules/frontboards/__init__.py @@ -1,5 +1,27 @@ from app import App +from system.hexpansion.util import read_hexpansion_header, get_hexpansion_block_devices +import vfs class FrontBoard(App): year: int + + +def mount_frontboard(i2c, readonly=True): + header = read_hexpansion_header(i2c, eeprom_addr=0x57) + if header is None: + return False + + try: + eep, partition = get_hexpansion_block_devices(i2c, header, addr=0x57) + except Exception: + return False + + mountpoint = "/frontboard" + + try: + vfs.mount(partition, mountpoint, readonly=readonly) + except OSError: + return False + + return True diff --git a/modules/frontboards/twentyfour.py b/modules/frontboards/twentyfour.py deleted file mode 100644 index 0285c28..0000000 --- a/modules/frontboards/twentyfour.py +++ /dev/null @@ -1,43 +0,0 @@ -import asyncio - -import display -from events.input import Button, BUTTON_TYPES, ButtonDownEvent, ButtonUpEvent -from system.eventbus import eventbus -from tildagonos import tildagonos -from . import FrontBoard - - -BUTTONS = { - "A": Button("A", "TwentyTwentyFour", BUTTON_TYPES["UP"]), - "B": Button("B", "TwentyTwentyFour", BUTTON_TYPES["RIGHT"]), - "C": Button("C", "TwentyTwentyFour", BUTTON_TYPES["CONFIRM"]), - "D": Button("D", "TwentyTwentyFour", BUTTON_TYPES["DOWN"]), - "E": Button("E", "TwentyTwentyFour", BUTTON_TYPES["LEFT"]), - "F": Button("F", "TwentyTwentyFour", BUTTON_TYPES["CANCEL"]), -} - - -class TwentyTwentyFour(FrontBoard): - BUTTON_PINS = { - BUTTONS["A"]: (0x5A, 0, (1 << 6)), - BUTTONS["B"]: (0x5A, 0, (1 << 7)), - BUTTONS["C"]: (0x59, 0, (1 << 0)), - BUTTONS["D"]: (0x59, 0, (1 << 1)), - BUTTONS["E"]: (0x59, 0, (1 << 2)), - BUTTONS["F"]: (0x59, 0, (1 << 3)), - } - - async def background_task(self): - display.gfx_init() - - button_states = {button: False for button in self.BUTTON_PINS.keys()} - while True: - tildagonos.read_egpios() - for button, pin in self.BUTTON_PINS.items(): - button_down = not tildagonos.check_egpio_state(pin, readgpios=False) - if button_down and not button_states[button]: - await eventbus.emit_async(ButtonDownEvent(button=button)) - if not button_down and button_states[button]: - await eventbus.emit_async(ButtonUpEvent(button=button)) - button_states[button] = button_down - await asyncio.sleep(0.01) diff --git a/modules/lib/requests/__init__.mpy b/modules/lib/requests/__init__.mpy deleted file mode 100644 index ee445f5..0000000 Binary files a/modules/lib/requests/__init__.mpy and /dev/null differ diff --git a/modules/lib/urequests.mpy b/modules/lib/urequests.mpy deleted file mode 100644 index 8677c9b..0000000 Binary files a/modules/lib/urequests.mpy and /dev/null differ diff --git a/modules/main.py b/modules/main.py index d0c9958..660a25f 100644 --- a/modules/main.py +++ b/modules/main.py @@ -1,18 +1,41 @@ # main.py -- put your code here! from esp32 import Partition +import machine +import os -from system.scheduler import scheduler -from system.hexpansion.app import HexpansionManagerApp -from system.patterndisplay.app import PatternDisplay -from system.notification.app import NotificationService -from system.launcher.app import Launcher from system.power.handler import PowerEventHandler +import display +import frontboards +import tildagonos -from frontboards.twentyfour import TwentyTwentyFour + +tildagonos.tildagonos.init_gpio() +display.gfx_init() # Start front-board interface -scheduler.start_app(TwentyTwentyFour()) + +fb_i2c = machine.I2C(0) +frontboard = 0x57 in fb_i2c.scan() +if frontboard: + # We have a frontboard, try to mount it + mounted = frontboards.mount_frontboard(fb_i2c) + print(f"Frontboard mounted {mounted}") + if not mounted or "app.mpy" not in os.listdir("/frontboard"): + # Provision the board if not mountable + import provision_fb + + provision_fb.populate_fb() + + +# Do main imports after mounting the frontboard so they can +# import the year's design tokens +from system.scheduler import scheduler # noqa: E402 +from system.hexpansion.app import HexpansionManagerApp # noqa: E402 +from system.patterndisplay.app import PatternDisplay # noqa: E402 +from system.notification.app import NotificationService # noqa: E402 +from system.launcher.app import Launcher # noqa: E402 + # Start expansion interface scheduler.start_app(HexpansionManagerApp()) @@ -28,6 +51,13 @@ PowerEventHandler.RegisterDefaultCallbacks(PowerEventHandler) +if frontboard: + # Import the interface and start the app + import frontboard.app + + scheduler.start_app(frontboard.app.__app_export__()) + + Partition.mark_app_valid_cancel_rollback() scheduler.run_forever() diff --git a/modules/provision_fb.py b/modules/provision_fb.py new file mode 100644 index 0000000..b8686af --- /dev/null +++ b/modules/provision_fb.py @@ -0,0 +1,91 @@ +from machine import I2C +from system.hexpansion.util import get_hexpansion_block_devices, HexpansionHeader +import vfs +import wifi +import display + +base = "/template" + +""" +import os, wifi, requests +os.mkdir("/template") +wifi.connect("emf2024", "badge", "badge") +wifi.wait() +for filename in ["tokens.mpy", "app.mpy"]: + with open(f"/template/{filename}", "wb") as fb_file: + data = requests.get(f"https://0d3d512069d3.ngrok.app/{filename}") + print(data.status_code) + fb_file.write(data.content) + print(fb_file.tell()) +""" + + +def status(msg=""): + try: + ctx = display.get_ctx() + ctx.rgb(0, 0, 0).rectangle(-120, -120, 240, 240).fill() + ctx.text_align = ctx.CENTER + ctx.font_size = 18.0 + ctx.rgb(1, 1, 1).move_to(0, 0).text("Provisioning ...") + ctx.rgb(1, 0, 0).move_to(0, 20).text(msg) + display.end_frame(ctx) + except Exception: + pass + + +def populate_fb(): + status() + # Ensure the board isn't mounted + mountpoint = "/frontboard" + + try: + vfs.umount(mountpoint) + except OSError: + pass + + port = 0 + addr = 0x57 + i2c = I2C(port) + wifi.connect() + + status("Header") + + h = HexpansionHeader( + manifest_version="2024", + fs_offset=32, + eeprom_page_size=32, + eeprom_total_size=1024 * 8, + vid=0xBAD3, + pid=0x2400, + unique_id=0x0, + friendly_name="TwentyTwentyFour", + ) + + status("Writing EEPROM") + # Write header to 0x00 of the eeprom + i2c.writeto(addr, bytes([0, 0]) + h.to_bytes()) + + _, partition = get_hexpansion_block_devices(i2c, h, addr) + + success = True + + status("Creating filesystem") + + vfs.VfsLfs2.mkfs(partition) + vfs.mount(partition, mountpoint, readonly=False) + + for filename in ["tokens.mpy", "app.mpy"]: + with open(f"{mountpoint}/{filename}", "wb") as fb_file: + with open(f"{base}/{filename}", "rb") as template_file: + fb_file.write(template_file.read()) + if fb_file.tell() != template_file.tell(): + status("Failed write") + success = False + + if not success: + raise ValueError("Retry") + + status("Remounting") + vfs.umount(mountpoint) + vfs.mount(partition, mountpoint, readonly=True) + status("Done")