Launcher and minimal home screen
parent
1c9e95db23
commit
7936cbf3a7
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")):
|
||||
|
|
|
@ -19,10 +19,13 @@ $ tilda_tools sync my_game shared
|
|||
$ tilda_tools sync <pattern1> <pattern2> ...
|
||||
|
||||
Sync (as above), but execute my_app after reboot
|
||||
$ tilda_toold.py sync --boot my_app [<other sync parameter>]
|
||||
$ tilda_tools sync --boot my_app [<other sync parameter>]
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ def main_menu():
|
|||
if option:
|
||||
option["function"]()
|
||||
else:
|
||||
return
|
||||
app.restart_to_default()
|
||||
|
||||
main_menu()
|
||||
#show_app("launcher")
|
||||
|
|
13
boot.py
13
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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
|
|
15
lib/app.py
15
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()
|
||||
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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])
|
|
@ -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()
|
|
@ -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()
|
||||
|
|
Loading…
Reference in New Issue