diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2908417..6f11fa2 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -65,6 +65,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/menu.py b/modules/app_components/menu.py index ced6654..b2a35e2 100644 --- a/modules/app_components/menu.py +++ b/modules/app_components/menu.py @@ -4,7 +4,7 @@ from events.input import BUTTON_TYPES, ButtonDownEvent from system.eventbus import eventbus -from .tokens import heading_font_size, label_font_size, line_height, set_color +from app_components import tokens def ease_out_quart(x): @@ -21,9 +21,9 @@ def __init__( change_handler: Union[Callable[[str], Any], None] = None, back_handler: Union[Callable, None] = None, speed_ms=300, - item_font_size=label_font_size, - item_line_height=label_font_size * line_height, - focused_item_font_size=heading_font_size, + item_font_size=tokens.label_font_size, + item_line_height=tokens.label_font_size * tokens.line_height, + focused_item_font_size=tokens.heading_font_size, focused_item_margin=20, ): self.app = app @@ -91,7 +91,7 @@ def draw(self, ctx): ctx.text_align = ctx.CENTER ctx.text_baseline = ctx.MIDDLE - set_color(ctx, "label") + tokens.set_color(ctx, "label") ctx.move_to( 0, animation_direction * -30 + animation_progress * animation_direction * 30 ).text(self.menu_items[self.position % len(self.menu_items)]) diff --git a/modules/app_components/tokens.py b/modules/app_components/tokens.py index 717f0c9..5c7b6b2 100644 --- a/modules/app_components/tokens.py +++ b/modules/app_components/tokens.py @@ -41,3 +41,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/firmware_apps/poweroff.py b/modules/firmware_apps/poweroff.py new file mode 100644 index 0000000..97f3ad4 --- /dev/null +++ b/modules/firmware_apps/poweroff.py @@ -0,0 +1,34 @@ +import app +from app_components.tokens import clear_background + +from app_components.dialog import YesNoDialog + + +class PowerOff(app.App): + async def run(self, render_update): + # Render initial state + await render_update() + + # Create a yes/no dialogue, add it to the overlays + dialog = YesNoDialog("Power off?", self) + self.overlays = [dialog] + + # Wait for an answer from the dialogue, and if it was yes, randomise colour + if await dialog.run(render_update): + import machine + import bq25895 + + bq25895.bq25895(machine.I2C(7)).disconnect_battery() + + # Remove the dialogue and re-render + self.overlays = [] + + while True: + await render_update() + + def draw(self, ctx): + ctx.save() + clear_background() + ctx.restore() + + self.draw_overlays(ctx) diff --git a/modules/frontboards/TwentyFour/EMFTwentyFour.bin b/modules/frontboards/TwentyFour/EMFTwentyFour.bin new file mode 100644 index 0000000..7dbe6a1 Binary files /dev/null and b/modules/frontboards/TwentyFour/EMFTwentyFour.bin differ diff --git a/modules/frontboards/TwentyFour/FONTLOG.txt b/modules/frontboards/TwentyFour/FONTLOG.txt new file mode 100644 index 0000000..facfe4c --- /dev/null +++ b/modules/frontboards/TwentyFour/FONTLOG.txt @@ -0,0 +1,139 @@ +FONTLOG for the Raleway fonts + +This file provides detailed information on the Raleway Font Software. + +This information should be distributed along with the Raleway fonts and any +derivative works. + +Basic Font Information + +Raleway is an elegant sans-serif typeface family. Initially designed by +Matt McInerney as a single thin weight, it was expanded into a 9 weight family +by Pablo Impallari and Rodrigo Fuenzalida in 2012 and iKerned by Igino Marini. +In 2013 the Italics where added. + +It is a display face and the download features both old style and lining +numerals, standard and discretionary ligatures, a pretty complete set of +diacritics, as well as a stylistic alternate inspired by more geometric +sans-serif typefaces than its neo-grotesque inspired default character +set. + +It also has a sister display family, Raleway Dots. + +Also, the characters set has been expanded to cover 104 Latin languages: +Afar, Afrikaans, Albanian, Azerbaijani, Basque, Belarusian, Bislama, Bosnian, Breton, Catalan, Chamorro, Chichewa, Comorian, Croatian, Czech, Danish, Dutch, English, Esperanto, Estonian, Faroese, Fijian, Filipino/Tagalog, Finnish, Flemish, French, Gaelic (Irish / Manx / Scottish), Gagauz, German, Gikuyu, Gilbertese/Kiribati, Greenlandic, Guarani, Haitian_Creole, Hawaiian, Hungarian, Icelandic, Igo/Igbo, Indonesian, Irish, Italian, Javanese, Kashubian, Kinyarwanda, Kirundi, Latin, Latvian, Lithuanian, Luba/Ciluba/Kasai, Luxembourgish, Malagasy, Malay, Maltese, Maori, Marquesan, Marshallese, Moldovan/Moldovian/Romanian, Montenegrin, Nauruan, Ndebele, Norwegian, Oromo, Palauan/Belauan, Polish, Portuguese, Quechua, Romanian, Romansh, Sami, Samoan, Sango, Serbian, Sesotho, Setswana/Sitswana/Tswana, Seychellois_Creole, SiSwati/Swati/Swazi, Silesian, Slovak, Slovenian, Somali, Sorbian, Sotho, Spanish, Swahili, Swedish, Tahitian, Tetum, Tok_Pisin, Tongan, Tsonga, Tswana, Tuareg/Berber, Turkish, Turkmen, Tuvaluan, Uzbek/Usbek, Wallisian, Walloon, Welsh, Xhosa, Yoruba, Zulu. + +The Roman Styles also include support for the following 17 Cyrillic languages: +Balkar, Belarusian, Bosnian, Chukchi, Crimean_Tartar, Erzya, Karachay, Kumyk, Lak, +Macedonian, Moksha, Montenegrin, Nanai, Nogai, Rusyn, Serbian, Ukranian + +Documentation can be found at https://www.theleagueofmoveabletype.com + +To report issues or contribute to the project see the source repository: +https://github.com/theleagueof/raleway + +ChangeLog + +6 May 2024 (Tildagon Authors) +- Convert into ctx format for use in Tildagon badge +- Rename to comply with license terms + +29 August 2020 (Caleb Maclennan) v4.101 +- Repackage so license files get distributed properly + +27 August 2020 (Caleb Maclennan) v4.100 +- Re-release of the entire font family in more output formats +- Collect all the developments from various forks back to the original project repository +- Build and release using Fontship + +10 Jul 2020 () v4.026 +- Release on Google Fonts +- Add VTT hints +- Fixup Italic +- Fixup compose/decompose for several glyphs +- Fix diacritic placements + +17 December 2016 (Alexei Vanyashin, Cyreal) v4.020 +— Expanded glyph set to GF Cyrillic Pro + +15 October 2016 (Alexei Vanyashin, Ivan Petrov, Cyreal) v.4.010 +— Merged in Cyrillic in the GF Cyrillic Plus + localised variants range +— minor fixed: adjusted win ascender +— changed italic angle to 12 in Italic masters +— added Cyrillic kerning + +24 Sept 2013 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v3.0 +- Added Cyrillic to the 9 Roman Weights + +26 Jun 2013 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.5 +- Charset Extension, now covering 104 Latin Languages. +- Italics Added +- Re-mastered + +27 May 2013 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.4 (Beta) +- Italic Masters, ready for iKern + +1 May 2013 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.3 (Beta) +- More diactritics added, now supporting all 104 Latin languages +- Added /Delta /Omega /estimated /infinity /integral /lozenge /partialdiff /pi /product /radical /summation /uni0394 /uni03A9 /uni2113 +- Lots of small bugs fixed + +11 Nov 2012 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.2 +- Fixed Font Info and other small bugs. +- 'Heavy' style renamed as 'Black' +- Hinted using the latest version of TTFAutohint, currently v0.9.3. +- Removed the KERN table, now we are using GPOS based Kerning. +- Smaller file size for faster loading on the web. +- Al '-OT' sources files renamed as '-OTF' + +7 Sept 2012 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.1 +- Fixed vertical metrics bug + +11 May 2012 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family v2.0 +- iKerned +- Remastered + +30 March 2012 (Matt McInerney, Pablo Impallari, Rodrigo Fuenzalida) Raleway Family Beta v1.06 +- Initial Beta release of the family expansion - Not yet spaced or Kerned. +- 9 Weights: Thin, ExtraLight, Light, Regular, Medium, SemiBold, Bold, ExtraBold and Heavy weights. +- Characters set expanded to cover 95 languages +- For detailed inspection, please refer to the the FL source files (The OTF files where quickly generated using Ben Kiel's Font Generator macro, for testing purposes only). + +20 Feb 2010 (Matt McInerney) Raleway Light v1.01 +- Initial Release + +Acknowledgements + +If you make modifications be sure to add your name (N), +email (E), web-address (if you have one) (W) and +description (D). This list is in alphabetical order. + +N: Caleb Maclennan +E: caleb@alerque.com +W: https://alerque.com +D: Fontship build system + +N: Cyreal +E: contact@cyreal.org +W: http://cyreal.org +D: Cyrillic expansion + +N: Matt McInerney +E: matt@pixelspread.com +W: http://pixelspread.com/ +D: Designer + +N: Pablo Impallari +E: impallari@gmail.com +W: http://www.impallari.com +D: Designer + +N: Rodrigo Fuenzalida +E: hello@rfuenzalida.com +W: http://www.rfuenzalida.com +D: Designer + +N: Tildagon Badge Authors +E: badge@emfcamp.org +W: https://badge.emfcamp.org +D: Embedders diff --git a/modules/frontboards/TwentyFour/__init__.py b/modules/frontboards/TwentyFour/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/modules/frontboards/twentyfour.py b/modules/frontboards/TwentyFour/app.py similarity index 95% rename from modules/frontboards/twentyfour.py rename to modules/frontboards/TwentyFour/app.py index 0285c28..8df6989 100644 --- a/modules/frontboards/twentyfour.py +++ b/modules/frontboards/TwentyFour/app.py @@ -1,10 +1,9 @@ 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 +from frontboards import FrontBoard BUTTONS = { @@ -28,8 +27,6 @@ class TwentyTwentyFour(FrontBoard): } async def background_task(self): - display.gfx_init() - button_states = {button: False for button in self.BUTTON_PINS.keys()} while True: tildagonos.read_egpios() @@ -41,3 +38,6 @@ async def background_task(self): await eventbus.emit_async(ButtonUpEvent(button=button)) button_states[button] = button_down await asyncio.sleep(0.01) + + +__app_export__ = TwentyTwentyFour diff --git a/modules/frontboards/TwentyFour/tokens.py b/modules/frontboards/TwentyFour/tokens.py new file mode 100644 index 0000000..44ecc12 --- /dev/null +++ b/modules/frontboards/TwentyFour/tokens.py @@ -0,0 +1,42 @@ +# From https://www.emfcamp.org/about/branding + +# Display +display_x = 240 +display_y = 240 +display_height_inches = 1.28 +ppi = display_x / display_height_inches + +# Font size +one_pt = ppi / 72 +ten_pt = 10 * one_pt +twelve_pt = 12 * one_pt +eighteen_pt = 18 * one_pt +twentyfour_pt = 24 * one_pt +label_font_size = ten_pt +heading_font_size = eighteen_pt + +line_height = 1.5 + +# Colors +colors = { + "pale_green": (175, 201, 68), + "mid_green": (82, 131, 41), + "dark_green": (33, 48, 24), + "yellow": (294, 226, 0), + "orange": (246, 127, 2), + "pink": (245, 80, 137), + "blue": (46, 173, 217), + "white": (232, 230, 227), +} + +font_name = "Arimo Regular" + +colors = { + name: (c[0] / 256.0, c[1] / 256.0, c[2] / 256.0) for (name, c) in colors.items() +} + +ui_colors = {"background": colors["dark_green"], "label": colors["white"]} + + +def clear_background(ctx): + ctx.rgb(*colors["dark_green"]).rectangle(-120, -120, display_x, display_y).fill() 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/main.py b/modules/main.py index 8ac719a..3489474 100644 --- a/modules/main.py +++ b/modules/main.py @@ -1,16 +1,39 @@ # 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 +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()) @@ -24,7 +47,13 @@ # Start notification handler scheduler.start_app(NotificationService(), always_on_top=True) +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..2f533f8 --- /dev/null +++ b/modules/provision_fb.py @@ -0,0 +1,80 @@ +from machine import I2C +from system.hexpansion.util import get_hexpansion_block_devices, HexpansionHeader +import vfs +import requests +import wifi +import display + +base = "https://github.com/emfcamp/badge-2024-software/releases/download/latest/" + + +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", + ) + + # Write header to 0x00 of the eeprom + i2c.writeto(addr, bytes([0, 0]) + h.to_bytes()) + + _, partition = get_hexpansion_block_devices(i2c, h, addr) + + status("Filesystem") + + vfs.VfsLfs2.mkfs(partition) + vfs.mount(partition, mountpoint, readonly=False) + + status("Wifi") + wifi.wait() + + for filename in ["tokens.mpy", "app.mpy"]: + with open(f"{mountpoint}/{filename}", "wb") as fb_file: + item = requests.get(f"{base}/{filename}") + if item.status_code == 200: + fb_file.write(item.content) + fb_file.flush() + print(f"Wrote {filename}") + else: + status("Failed") + break + + status("Remounting") + vfs.umount(mountpoint) + vfs.mount(partition, "mountpoint", readonly=True) + + status("Done")