From 7936cbf3a76716fa8912cab772a38325a7837959 Mon Sep 17 00:00:00 2001 From: Marek Ventur Date: Sun, 5 Aug 2018 22:14:31 +0100 Subject: [PATCH] Launcher and minimal home screen --- .development/pyboard.py | 2 +- .development/pyboard_util.py | 24 ++++++++++++++---------- .development/sync.py | 10 ++++++++++ .development/tilda_tools.py | 19 +++++++++++++++---- badge_store/main.py | 2 +- boot.py | 13 ++++++++----- home_default/main.py | 36 +++++++++++++++++++++++++++++++----- launcher/main.py | 22 ++++++++++------------ lib/app.py | 15 +++++++++++++++ lib/buttons.py | 1 - lib/homescreen.py | 33 ++++++++++++++++++++++----------- lib/random.py | 9 +++++++++ lib/test_random.py | 20 ++++++++++++++++++++ settings/main.py | 10 +++++++++- 14 files changed, 165 insertions(+), 51 deletions(-) create mode 100644 lib/random.py create mode 100644 lib/test_random.py diff --git a/.development/pyboard.py b/.development/pyboard.py index 16ee41f..d8b2697 100644 --- a/.development/pyboard.py +++ b/.development/pyboard.py @@ -309,7 +309,7 @@ class Pyboard: print(data) raise PyboardError('could not enter raw repl') - def exit_raw_repl(self): + def exit_raw_repl(self):\ self.serial.write(b'\r\x02') # ctrl-B: enter friendly REPL def follow(self, timeout, data_consumer=None): diff --git a/.development/pyboard_util.py b/.development/pyboard_util.py index 783e062..585789a 100644 --- a/.development/pyboard_util.py +++ b/.development/pyboard_util.py @@ -49,9 +49,7 @@ def soft_reset(args, verbose = True): if verbose: print("Soft reboot:", end="") write_command(pyb, b'\x04') # ctrl-D: soft reset - #print("1") data = pyb.read_until(1, b'soft reboot\r\n') - #print("2") if data.endswith(b'soft reboot\r\n'): if verbose: print(" DONE") @@ -109,12 +107,18 @@ def run(args, paths, verbose=True): pyboard.stdout_write_bytes(ret_err) sys.exit(1) - # run any files - for filename in paths: - with open(filename, 'rb') as f: - print("-------- %s --------" % filename) - pyfile = f.read() - execbuffer(pyfile) + try: + # run any files + for filename in paths: + with open(filename, 'rb') as f: + print("-------- %s --------" % filename) + pyfile = f.read() + execbuffer(pyfile) - # exiting raw-REPL just drops to friendly-REPL mode - pyb.exit_raw_repl() + # exiting raw-REPL just drops to friendly-REPL mode + pyb.exit_raw_repl() + except OSError as e: + if "Device not configured" in str(e): + print("Connection to badge lost") # This can happen on a hard rest + else: + raise e diff --git a/.development/sync.py b/.development/sync.py index 9d9dfdf..1754a6f 100644 --- a/.development/sync.py +++ b/.development/sync.py @@ -70,6 +70,16 @@ def set_boot_app(storage, app_to_boot): if app_to_boot: print("setting next boot to %s" % app_to_boot) +def set_no_boot(storage): + path = os.path.join(storage, 'no_boot') + try: + os.remove(path) + except OSError: + pass + with open(path, 'w') as f: + f.write("\n") + + def get_root(): root = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')) if not os.path.isfile(os.path.join(root, "boot.py")): diff --git a/.development/tilda_tools.py b/.development/tilda_tools.py index b59aba4..0119b74 100755 --- a/.development/tilda_tools.py +++ b/.development/tilda_tools.py @@ -19,10 +19,13 @@ $ tilda_tools sync my_game shared $ tilda_tools sync ... Sync (as above), but execute my_app after reboot -$ tilda_toold.py sync --boot my_app [] +$ tilda_tools sync --boot my_app [] Sync (as above), but execute a single file afterwards without copying it to the badge -$ tilda_toold.py sync --run some_other_file.py +$ tilda_tools sync --run some_other_file.py + +Sync a given app and execute it +$ tilda_tools app home_default Executes a single file on the badge without copying anything (Using pyboard.py) $ tilda_tools run my_app/main.py @@ -54,7 +57,7 @@ from resources import * def main(): import argparse cmd_parser = argparse.ArgumentParser(description='Toolchain for working with the TiLDA Mk4') - cmd_parser.add_argument('command', nargs=1, help='command [test|reset|sync|run|validate|wifi|firmware-update]', choices=['test', 'reset', 'sync', 'validate', 'run', 'wifi', 'firmware-update']) + cmd_parser.add_argument('command', nargs=1, help='command [test|reset|sync|run|validate|wifi|firmware-update|app]', choices=['test', 'reset', 'sync', 'validate', 'run', 'wifi', 'firmware-update', 'app']) cmd_parser.add_argument('-d', '--device', help='the serial device of the badge') cmd_parser.add_argument('-s', '--storage', help='the usb mass storage path of the badge') cmd_parser.add_argument('-b', '--baudrate', default=115200, help='the baud rate of the serial device') @@ -76,6 +79,11 @@ def main(): if command == "wifi": wifi.select_wifi() + if command == "app": + command = "sync" + args.run = "%s/main.py" % args.paths[0] + #args.boot = args.paths[0] + if command in ["test", "validate", "sync"]: resources = get_resources(path) add_metadata(path, resources) @@ -107,10 +115,12 @@ def main(): if command in ["reset", "sync"]: sync.set_boot_app(get_storage(args), args.boot or "") - pyboard_util.soft_reset(args) if args.run: command = "run" args.paths = [args.run] + sync.set_no_boot(get_storage(args)) + pyboard_util.soft_reset(args) + if command == "run": pyboard_util.check_run(args.paths) @@ -119,6 +129,7 @@ def main(): if run_tests: for resource in synced_resources: pyboard_util.check_run([resource]) + sync.set_no_boot(get_storage(args)) pyboard_util.run(args, [resource], False) pyboard_util.soft_reset(args, False) diff --git a/badge_store/main.py b/badge_store/main.py index 98fa085..13ecdd0 100644 --- a/badge_store/main.py +++ b/badge_store/main.py @@ -87,7 +87,7 @@ def main_menu(): if option: option["function"]() else: - return + app.restart_to_default() main_menu() #show_app("launcher") diff --git a/boot.py b/boot.py index bafc524..700ad79 100644 --- a/boot.py +++ b/boot.py @@ -26,9 +26,12 @@ def file(file, remove): def any_home(): return app(next(a for a in root if a.startswith("home"))) -start = None -if "main.py" in root: - start = "main.py" -start = file("once.txt", True) or file("default_app.txt", False) or any_home() or "bootstrap.py" +if "no_boot" in root: + os.remove("no_boot") +else: + start = None + if "main.py" in root: + start = "main.py" + start = file("once.txt", True) or file("default_app.txt", False) or any_home() or "bootstrap.py" -pyb.main(start) + pyb.main(start) diff --git a/home_default/main.py b/home_default/main.py index 6c04be5..ae4be23 100644 --- a/home_default/main.py +++ b/home_default/main.py @@ -8,13 +8,39 @@ newly activated or reset. ___name___ = "Homescreen (Default)" ___license___ = "GPL" ___categories___ = ["homescreen"] +___dependencies___ = ["homescreen"] ___launchable___ = False ___bootstrapped___ = True -print("there") -import ugfx, homescreen +import ugfx +from homescreen import * +import time + +init() + +# title +ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) +ugfx.Label(0, 20, ugfx.width(), 40, "TiLDA Mk4", justification=ugfx.Label.CENTERTOP) + +# name +if name(): + ugfx.set_default_font(ugfx.FONT_NAME) + ugfx.Label(0, 60, ugfx.width(), 40, name(), justification=ugfx.Label.CENTERTOP) +else: + ugfx.set_default_font(ugfx.FONT_MEDIUM) + ugfx.Label(0, 60, ugfx.width(), 40, "Set your name in the settings app", justification=ugfx.Label.CENTERTOP) + +# info +ugfx.Label(0, 200, ugfx.width(), 40, "Press MENU", justification=ugfx.Label.CENTERTOP) + +ugfx.set_default_font(ugfx.FONT_MEDIUM) +status = ugfx.Label(0, 130, ugfx.width(), 40, "", justification=ugfx.Label.CENTERTOP) + +# update loop +def tick(): + status.text("wifi: %s%%\nbattery: %s%%" % (int(wifi_strength() * 100), int(battery() * 100))) + time.sleep_ms(500) + +loop(tick) -homescreen.init(color = 0xe4ffdb) -ugfx.display_image(0, 0, "home_default/bg.gif") -ugfx.text(20, 20, homescreen.name(), ugfx.BLACK) diff --git a/launcher/main.py b/launcher/main.py index 3fa84e1..c80dd30 100644 --- a/launcher/main.py +++ b/launcher/main.py @@ -3,23 +3,21 @@ ___name___ = "Launcher" ___license___ = "MIT" ___categories___ = ["System"] +___dependencies___ = ["dialogs", "app"] ___launchable___ = False ___bootstrapped___ = True import ugfx +from app import * +from dialogs import * +ugfx.init() ugfx.clear() -apps = range(1, 10) -cols = 4 +options = [{"title": a.title, "app": a} for a in get_apps()] +option = prompt_option(options, none_text="Homescreen", text="Select App to start") - -for i, p in enumerate(apps): - logical_x = i % cols - logical_y = i // cols - x = logical_x * width - - - - -print("launcher") +if not option: + restart_to_default() +else: + option["app"].boot() diff --git a/lib/app.py b/lib/app.py index 9274f40..ebe141e 100644 --- a/lib/app.py +++ b/lib/app.py @@ -7,6 +7,7 @@ ___license___ = "MIT" ___dependencies___ = ["metadata_reader", "ospath"] from ospath import * +import os, machine from metadata_reader import read_metadata class App: @@ -63,6 +64,10 @@ class App: except Exception as e: pass + def boot(self): + write_launch_file(self.name) + machine.reset() # used to be pyb.hard_reset() + def __str__(self): return self.title @@ -103,3 +108,13 @@ def get_categories(): _categories.update(app.categories) return _categories +def write_launch_file(app, file = "once.txt"): + with open(file, "wt") as file: + file.write(app) + file.flush() + os.sync() + +def restart_to_default(): + write_launch_file("") + machine.reset() # used to be pyb.hard_reset() + diff --git a/lib/buttons.py b/lib/buttons.py index 8b3643c..82a7a0d 100644 --- a/lib/buttons.py +++ b/lib/buttons.py @@ -42,7 +42,6 @@ def rotate(button): """remaps names of buttons to rotated values""" return ROTATION_MAP[button] - def is_pressed(button): pin = _get_pin(button) if pin.pull() == pyb.Pin.PULL_DOWN: diff --git a/lib/homescreen.py b/lib/homescreen.py index de8c7fa..d60077d 100644 --- a/lib/homescreen.py +++ b/lib/homescreen.py @@ -16,29 +16,40 @@ They also *may*: * Display remaining battery "homescreen.battery()" (0-1) """ -__license___ = "MIT" -__dependencies___ = ["database", "buttons"] +___license___ = "MIT" +___dependencies___ = ["database", "buttons", "random", "app"] -import database, ugfx, buttons +import database, ugfx, random, buttons, time +from app import App def init(color = 0xFFFFFF): ugfx.init() + ugfx.orientation(90) ugfx.clear(ugfx.html_color(color)) + +# A special loop that exits on menu being pressed +def loop(func, interval = 500): buttons.init() - #buttons.enable_interrupt() + state = {"pressed": False} # This is a terrible hack + def irp(t): + state["pressed"] = True + buttons.enable_interrupt("BTN_MENU", irp, on_release = True) + while not state["pressed"]: + func() + time.sleep_ms(interval) + buttons.disable_interrupt("BTN_MENU") + App("launcher").boot() + def menu(): ugfx.clear() -def name(): - return database.get("homescreen.name", "bar") - -def mobile_strength(): - return 0.75 +def name(default = None): + return database.get("homescreen.name", default) def wifi_strength(): - return 0.65 + return random.rand() / 256 def battery(): - return 0.65 + return random.rand() / 256 diff --git a/lib/random.py b/lib/random.py new file mode 100644 index 0000000..6cf1493 --- /dev/null +++ b/lib/random.py @@ -0,0 +1,9 @@ +"""Library to generate random numbers""" + +___license___ = "MIT" + +import os + +# todo: write an actual useful function +def rand(): + return int(os.urandom(1)[0]) diff --git a/lib/test_random.py b/lib/test_random.py new file mode 100644 index 0000000..2492a37 --- /dev/null +++ b/lib/test_random.py @@ -0,0 +1,20 @@ +"""Tests for random lib""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "random"] + +import unittest +from random import * + +class TestRandom(unittest.TestCase): + + def test_rand(self): + for i in range(1, 100): + r = rand() + self.assertTrue(r>0) + self.assertTrue(r<256) + + + +if __name__ == '__main__': + unittest.main() diff --git a/settings/main.py b/settings/main.py index c663d03..9edb20e 100644 --- a/settings/main.py +++ b/settings/main.py @@ -5,5 +5,13 @@ ___license___ = "GPL" ___categories___ = ["System"] ___launchable___ = True ___bootstrapped___ = True +___dependencies___ = ["app", "dialogs"] -print("settings") +import app +from dialogs import * + +ugfx.init() + +option = prompt_option(["tbd"], none_text="Exit", title="Settings") + +app.restart_to_default()