diff --git a/.development/sync.py b/.development/sync.py index 1754a6f..a3add41 100644 --- a/.development/sync.py +++ b/.development/sync.py @@ -1,7 +1,7 @@ import os, shutil, sys, fnmatch def sync(storage, patterns, resources, verbose): - root = get_root() + root = get_root(verbose) # Add all paths that are already files paths = set([p for p in (patterns or []) if os.path.isfile(os.path.join(root, p))]) @@ -30,7 +30,6 @@ def sync(storage, patterns, resources, verbose): paths.add(path) if not found: print("WARN: No resources to copy found for pattern %s" % patterns) - if not verbose: print("Copying %s files: " % len(paths), end="") for path in paths: @@ -46,9 +45,7 @@ def sync(storage, patterns, resources, verbose): target = os.path.join(storage, rel_path) target_dir = os.path.dirname(target) - if os.path.isfile(target_dir): - # micropython has the tendency to sometimes corrupt directories into files - os.remove(target_dir) + ensure_dir(target_dir, storage) if not os.path.exists(target_dir): os.makedirs(target_dir) shutil.copy2(path, target) @@ -59,6 +56,14 @@ def sync(storage, patterns, resources, verbose): print(" DONE") return synced_resources +def ensure_dir(path, storage): + # micropython has a tendecy + if not path or path == storage: + return + if os.path.isfile(path): + os.remove(path) + ensure_dir(os.path.dirname(path), storage) + def set_boot_app(storage, app_to_boot): path = os.path.join(storage, 'once.txt') try: @@ -79,8 +84,7 @@ def set_no_boot(storage): with open(path, 'w') as f: f.write("\n") - -def get_root(): +def get_root(verbose=False): root = os.path.abspath(os.path.join(os.path.dirname( __file__ ), '..')) if not os.path.isfile(os.path.join(root, "boot.py")): print("Path %s doesn't contain a boot.py, aborting. Something is probably wrong with your setup.") diff --git a/.development/tilda_tools.py b/.development/tilda_tools.py index 8ed9431..6792427 100755 --- a/.development/tilda_tools.py +++ b/.development/tilda_tools.py @@ -138,7 +138,7 @@ def main(): def find_storage(): # todo: find solution for windows and linux - for pattern in ['/Volumes/PYBFLASH', '/Volumes/NO NAME']: + for pattern in ['/Volumes/TILDAMK4', '/Volumes/PYBFLASH', '/Volumes/NO NAME']: for path in glob.glob(pattern): return path print("Couldn't find badge storage - Please make it's plugged in and reset it if necessary") diff --git a/.gitignore b/.gitignore index ce8cba1..4ef4d60 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .DS_Store __pycache__ wifi.json +wifi*.json diff --git a/boot.py b/boot.py index 700ad79..850b05e 100644 --- a/boot.py +++ b/boot.py @@ -1,8 +1,6 @@ -import pyb, os, micropython, sys +import os, micropython, sys -micropython.alloc_emergency_exception_buf(100) - -sys.path.append('/flash/upip') +# micropython.alloc_emergency_exception_buf(100) # doesn't exist in TiLDA Mk4 yet os.sync() root = os.listdir() @@ -34,4 +32,4 @@ else: start = "main.py" start = file("once.txt", True) or file("default_app.txt", False) or any_home() or "bootstrap.py" - pyb.main(start) + #todo: something like tilda.main(start) diff --git a/lib/app.py b/lib/app.py index ebe141e..35a82b2 100644 --- a/lib/app.py +++ b/lib/app.py @@ -66,7 +66,7 @@ class App: def boot(self): write_launch_file(self.name) - machine.reset() # used to be pyb.hard_reset() + machine.reset() def __str__(self): return self.title @@ -116,5 +116,5 @@ def write_launch_file(app, file = "once.txt"): def restart_to_default(): write_launch_file("") - machine.reset() # used to be pyb.hard_reset() + machine.reset() diff --git a/lib/buttons.py b/lib/buttons.py index 82a7a0d..6f4265f 100644 --- a/lib/buttons.py +++ b/lib/buttons.py @@ -2,25 +2,17 @@ ___license___ = "MIT" -import pyb +import machine, time CONFIG = { - "JOY_UP": pyb.Pin.PULL_DOWN, - "JOY_DOWN": pyb.Pin.PULL_DOWN, - "JOY_RIGHT": pyb.Pin.PULL_DOWN, - "JOY_LEFT": pyb.Pin.PULL_DOWN, - "JOY_CENTER": pyb.Pin.PULL_DOWN, - "BTN_MENU": pyb.Pin.PULL_UP, - "BTN_A": pyb.Pin.PULL_UP, - "BTN_B": pyb.Pin.PULL_UP -} - -ROTATION_MAP = { - "JOY_UP": "JOY_LEFT", - "JOY_LEFT": "JOY_DOWN", - "JOY_DOWN": "JOY_RIGHT", - "JOY_RIGHT": "JOY_UP", + "JOY_UP": [1, machine.Pin.PULL_DOWN], + "JOY_DOWN": [2, machine.Pin.PULL_DOWN], + "JOY_RIGHT": [4, machine.Pin.PULL_DOWN], + "JOY_LEFT": [3, machine.Pin.PULL_DOWN], + "JOY_CENTER": [0, machine.Pin.PULL_DOWN], + "BTN_MENU": [5, machine.Pin.PULL_UP] } +# todo: port expander _tilda_pins = {} _tilda_interrupts = {} @@ -35,16 +27,12 @@ def init(buttons = CONFIG.keys()): """Inits all pins used by the TiLDA badge""" global _tilda_pins for button in buttons: - _tilda_pins[button] = pyb.Pin(button, pyb.Pin.IN) - _tilda_pins[button].init(pyb.Pin.IN, CONFIG[button]) - -def rotate(button): - """remaps names of buttons to rotated values""" - return ROTATION_MAP[button] + _tilda_pins[button] = machine.Pin(CONFIG[button][0], machine.Pin.IN) + _tilda_pins[button].init(machine.Pin.IN, CONFIG[button][1]) def is_pressed(button): pin = _get_pin(button) - if pin.pull() == pyb.Pin.PULL_DOWN: + if pin.pull() == machine.Pin.PULL_DOWN: return pin.value() > 0 else: return pin.value() == 0 @@ -58,19 +46,19 @@ def is_triggered(button, interval = 30): global _tilda_bounce if is_pressed(button): if button in _tilda_bounce: - if pyb.millis() > _tilda_bounce[button]: + if time.ticks_ms() > _tilda_bounce[button]: del _tilda_bounce[button] else: return False # The button might have bounced back to high # Wait for a while to avoid bounces to low - pyb.delay(interval) + machine.sleep_ms(interval) # Wait until button is released again while is_pressed(button): - pyb.wfi() + machine.sleep_ms(1) - _tilda_bounce[button] = pyb.millis() + interval + _tilda_bounce[button] = time.ticks_ms() + interval return True def has_interrupt(button): @@ -83,6 +71,7 @@ def has_interrupt(button): def enable_interrupt(button, interrupt, on_press = True, on_release = False): + raise Exception("interrupts don't work yet") """Attaches an interrupt to a button on_press defines whether it should be called when the button is pressed @@ -104,35 +93,30 @@ def enable_interrupt(button, interrupt, on_press = True, on_release = False): mode = None; if on_press and on_release: - mode = pyb.ExtInt.IRQ_RISING_FALLING + mode = machine.ExtInt.IRQ_RISING_FALLING else: - if pin.pull() == pyb.Pin.PULL_DOWN: - mode = pyb.ExtInt.IRQ_RISING if on_press else pyb.ExtInt.IRQ_FALLING + if pin.pull() == machine.Pin.PULL_DOWN: + mode = machine.ExtInt.IRQ_RISING if on_press else machine.ExtInt.IRQ_FALLING else: - mode = pyb.ExtInt.IRQ_FALLING if on_press else pyb.ExtInt.IRQ_RISING + mode = machine.ExtInt.IRQ_FALLING if on_press else machine.ExtInt.IRQ_RISING _tilda_interrupts[button] = { - "interrupt": pyb.ExtInt(pin, mode, pin.pull(), interrupt), + "interrupt": machine.ExtInt(pin, mode, pin.pull(), interrupt), "mode": mode, "pin": pin } def disable_interrupt(button): + raise Exception("interrupts don't work yet") global _tilda_interrupts if button in _tilda_interrupts: interrupt = _tilda_interrupts[button] - pyb.ExtInt(interrupt["pin"], interrupt["mode"], interrupt["pin"].pull(), None) + machine.ExtInt(interrupt["pin"], interrupt["mode"], interrupt["pin"].pull(), None) del _tilda_interrupts[button] init([button]) def disable_all_interrupt(): + raise Exception("interrupts don't work yet") for interrupt in _tilda_interrupts: disable_interrupt(interrupt) -def enable_menu_reset(): - import onboard - enable_interrupt("BTN_MENU", lambda t:onboard.semihard_reset(), on_release = True) - -def disable_menu_reset(): - disable_interrupt("BTN_MENU") - diff --git a/lib/dialogs.py b/lib/dialogs.py index c2bde11..d5d343e 100644 --- a/lib/dialogs.py +++ b/lib/dialogs.py @@ -1,11 +1,9 @@ """Some basic UGFX powered dialogs""" ___license___ = "MIT" -___dependencies___ = ["buttons"] +___dependencies___ = ["buttons", "sleep"] -import ugfx -import buttons -import pyb +import ugfx, buttons, sleep default_style_badge = ugfx.Style() default_style_badge.set_focus(ugfx.RED) @@ -18,11 +16,13 @@ default_style_dialog.set_background(ugfx.html_color(0xFFFFFF)) TILDA_COLOR = ugfx.html_color(0x7c1143); +FONT_SMALL = 0 #todo: find correct values +FONT_MEDIUM_BOLD = 0 -def notice(text, title="TiLDA", close_text="Close", width = 260, height = 180, font=ugfx.FONT_SMALL, style=None): +def notice(text, title="TiLDA", close_text="Close", width = 260, height = 180, font=FONT_SMALL, style=None): prompt_boolean(text, title = title, true_text = close_text, false_text = None, width = width, height = height, font=font, style=style) -def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width = 260, height = 180, font=ugfx.FONT_SMALL, style=None): +def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width = 260, height = 180, font=FONT_SMALL, style=None): """A simple one and two-options dialog if 'false_text' is set to None only one button is displayed. @@ -31,7 +31,7 @@ def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width global default_style_dialog if style == None: style = default_style_dialog - ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) + ugfx.set_default_font(FONT_MEDIUM_BOLD) window = ugfx.Container((ugfx.width() - width) // 2, (ugfx.height() - height) // 2, width, height, style=style) window.show() ugfx.set_default_font(font) @@ -44,7 +44,7 @@ def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width ugfx.set_default_font(font) label = ugfx.Label(5, 30, width - 10, height - 80, text = text, parent=window) - ugfx.set_default_font(ugfx.FONT_MEDIUM_BOLD) + ugfx.set_default_font(FONT_MEDIUM_BOLD) button_yes = ugfx.Button(5, height - 40, width // 2 - 15 if false_text else width - 15, 30 , true_text, parent=window) button_no = ugfx.Button(width // 2 + 5, height - 40, width // 2 - 15, 30 , false_text, parent=window) if false_text else None @@ -57,7 +57,7 @@ def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width window.show() while True: - pyb.wfi() + sleep.wfi() if buttons.is_triggered("BTN_A"): return True if buttons.is_triggered("BTN_B"): return False @@ -68,7 +68,7 @@ def prompt_boolean(text, title="TiLDA", true_text="Yes", false_text="No", width if button_no: button_no.destroy() label.destroy() -def prompt_text(description, init_text = "", true_text="OK", false_text="Back", width = 300, height = 200, font=ugfx.FONT_MEDIUM_BOLD, style=default_style_badge): +def prompt_text(description, init_text = "", true_text="OK", false_text="Back", width = 300, height = 200, font=FONT_MEDIUM_BOLD, style=default_style_badge): """Shows a dialog and keyboard that allows the user to input/change a string Returns None if user aborts with button B @@ -86,7 +86,7 @@ def prompt_text(description, init_text = "", true_text="OK", false_text="Back", ugfx.set_default_font(ugfx.FONT_MEDIUM) kb = ugfx.Keyboard(0, int(height/2), width, int(height/2), parent=window) edit = ugfx.Textbox(5, int(height/2)-30, int(width*4/5)-10, 25, text = init_text, parent=window) - ugfx.set_default_font(ugfx.FONT_SMALL) + ugfx.set_default_font(FONT_SMALL) button_yes = ugfx.Button(int(width*4/5), int(height/2)-30, int(width*1/5)-3, 25 , true_text, parent=window) button_no = ugfx.Button(int(width*4/5), int(height/2)-30-30, int(width/5)-3, 25 , false_text, parent=window) if false_text else None ugfx.set_default_font(font) @@ -101,7 +101,7 @@ def prompt_text(description, init_text = "", true_text="OK", false_text="Back", window.show() edit.set_focus() while True: - pyb.wfi() + sleep.wfi() ugfx.poll() #if buttons.is_triggered("BTN_A"): return edit.text() if buttons.is_triggered("BTN_B"): return None @@ -123,7 +123,7 @@ def prompt_option(options, index=0, text = "Please select one of the following:" If none_text is specified the user can use the B or Menu button to skip the selection if title is specified a blue title will be displayed about the text """ - ugfx.set_default_font(ugfx.FONT_SMALL) + ugfx.set_default_font(FONT_SMALL) window = ugfx.Container(5, 5, ugfx.width() - 10, ugfx.height() - 10) window.show() @@ -156,7 +156,7 @@ def prompt_option(options, index=0, text = "Please select one of the following:" buttons.init() while True: - pyb.wfi() + sleep.wfi() ugfx.poll() if buttons.is_triggered("BTN_A"): return options[options_list.selected_index()] if button_none and buttons.is_triggered("BTN_B"): return None @@ -180,15 +180,16 @@ class WaitingMessage: self.label = ugfx.Label(5, 40, self.window.width() - 10, ugfx.height() - 40, text = text, parent=self.window) # Indicator to show something is going on - self.indicator = ugfx.Label(ugfx.width() - 100, 0, 20, 20, text = "...", parent=self.window) - self.timer = pyb.Timer(3) - self.timer.init(freq=3) - self.timer.callback(lambda t: self.indicator.visible(not self.indicator.visible())) + #self.indicator = ugfx.Label(ugfx.width() - 100, 0, 20, 20, text = "...", parent=self.window) + #self.timer = machine.Timer(3) + #self.timer.init(freq=3) + #self.timer.callback(lambda t: self.indicator.visible(not self.indicator.visible())) + # todo: enable this once we have a timer somewhere def destroy(self): - self.timer.deinit() + #self.timer.deinit() self.label.destroy() - self.indicator.destroy() + #self.indicator.destroy() self.window.destroy() @property diff --git a/lib/hall_effect.py b/lib/hall_effect.py new file mode 100644 index 0000000..943f5a7 --- /dev/null +++ b/lib/hall_effect.py @@ -0,0 +1,16 @@ +"""Library for sleep related functions""" + +import machine + + +_adc = None +def hall_effect_adc(): + global _adc + if not _adc: + _adc = machine.ADC(machine.ADC.ADC_HALLEFFECT) + return _adc + +def get_flux(): + return hall_effect_adc().convert() # todo: convert this into something meaningful + + diff --git a/lib/homescreen.py b/lib/homescreen.py index a8ab84c..d635844 100644 --- a/lib/homescreen.py +++ b/lib/homescreen.py @@ -6,7 +6,7 @@ In particular, they *should*: * Call "homescreen.init()" at the beginning. This will initiate ugfx, clear the screen and initiate button handline. -* Use "pyb.wfi()" as much as possible to avoid draining the battery. +* Use "sleep.wfi()" as much as possible to avoid draining the battery. * Not use They also *may*: @@ -17,9 +17,9 @@ They also *may*: """ ___license___ = "MIT" -___dependencies___ = ["database", "buttons", "random", "app"] +___dependencies___ = ["database", "buttons", "random", "app", "sleep"] -import database, ugfx, random, buttons, time, select +import database, ugfx, random, select, buttons from app import App _state = None @@ -27,13 +27,10 @@ def init(enable_menu_button = True): global _state _state = {"menu": False} ugfx.init() - ugfx.orientation(90) - - if enable_menu_button: buttons.init() - buttons.enable_interrupt("BTN_MENU", lambda t: set_state("menu"), on_release = True) + #buttons.enable_interrupt("BTN_MENU", lambda t: set_state("menu"), on_release = True) def set_state(key, value = True): # we can't allocate memory in interrupts, so make sure all keys are set beforehand and @@ -42,18 +39,13 @@ def set_state(key, value = True): _state[key] = value def clean_up(): - buttons.disable_all_interrupt() + pass -def check(): - global _state - if _state["menu"]: +def sleep(interval = 500): + if button.is_triggered("BTN_MENU", interval=interval): clean_up() App("launcher").boot() -def sleep(interval = 500): - check() - time.sleep_ms(interval) # todo: deep sleep - check() def name(default = None): return database.get("homescreen.name", default) diff --git a/lib/http.py b/lib/http.py index 87844ee..c5f2298 100644 --- a/lib/http.py +++ b/lib/http.py @@ -8,7 +8,7 @@ Current known issues: """ ___license___ = "MIT" -___dependencies___ = ["urlencode"] +___dependencies___ = ["urlencode", "wifi"] import usocket, ujson, os, time, gc, wifi from urlencode import urlencode @@ -144,6 +144,7 @@ def open_http_socket(method, url, json=None, timeout=None, headers=None, data=No if proto == 'http:': port = 80 elif proto == 'https:': + #todo make this work raise OSError("HTTPS is currently not supported") port = 443 else: diff --git a/lib/ospath.py b/lib/ospath.py index 47b366a..962b6ee 100644 --- a/lib/ospath.py +++ b/lib/ospath.py @@ -10,10 +10,10 @@ import os sep = "/" -R_OK = const(4) -W_OK = const(2) -X_OK = const(1) -F_OK = const(0) +R_OK = 4 +W_OK = 2 +X_OK = 1 +F_OK = 0 def join(*args): # TODO: this is non-compliant diff --git a/lib/random.py b/lib/random.py index 23c57d5..d072333 100644 --- a/lib/random.py +++ b/lib/random.py @@ -5,6 +5,7 @@ Warning! Don't use this for anything important, it's probably biased ___license___ = "MIT" +# todo: simplify this by using "urandom" import os _bigrand_bytes = 10 diff --git a/lib/sleep.py b/lib/sleep.py new file mode 100644 index 0000000..8756216 --- /dev/null +++ b/lib/sleep.py @@ -0,0 +1,11 @@ +"""Library for sleep related functions""" + +import time + +def sleep_ms(duration): + # todo: deepsleep? + time.sleep_ms(duration) + +def wfi(): + # todo: this is fake + sleep_ms(1) diff --git a/lib/test_dialogs.py b/lib/test_dialogs.py new file mode 100644 index 0000000..571b41b --- /dev/null +++ b/lib/test_dialogs.py @@ -0,0 +1,34 @@ +"""Tests for app lib + +Very limited at the moment since we can't test the main input dialogs""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "dialogs", "sleep"] + +import unittest, ugfx +from machine import Pin +from dialogs import * +from sleep import * + +class TestDialogs(unittest.TestCase): + + def setUpClass(self): + ugfx.init() + Pin(Pin.PWM_LCD_BLIGHT).on() + + def tearDownClass(self): + Pin(Pin.PWM_LCD_BLIGHT).off() + + def test_app_object(self): + count_max = 10 + with WaitingMessage("Testing...", "Foo") as c: + for i in range(1, count_max): + sleep_ms(100) + c.text = "%d/%d" % (i, count_max) + + print("done") + + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/test_hall_effect.py b/lib/test_hall_effect.py new file mode 100644 index 0000000..f786186 --- /dev/null +++ b/lib/test_hall_effect.py @@ -0,0 +1,17 @@ +"""Tests for hall effect sensor""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "hall_effect"] + +import unittest, hall_effect + +class TestHallEffect(unittest.TestCase): + + def test_hall(self): + flux = hall_effect.get_flux() + self.assertTrue(flux > 0) + self.assertTrue(flux < 4000) + + +if __name__ == '__main__': + unittest.main() diff --git a/lib/test_icons.py b/lib/test_icons.py index f7076c2..4c5a8c2 100644 --- a/lib/test_icons.py +++ b/lib/test_icons.py @@ -9,29 +9,29 @@ from icons import * class TestIcons(unittest.TestCase): # incomplete! + # todo: fix me def setUp(self): ugfx.init() - ugfx.orientation(90) ugfx.clear() - def test_icon(self): - icon = Icon(44, 40, "Badge Store with", "badge_store/icon.gif") - icon.show() - - for s in [True, False, True]: - icon.selected = s - time.sleep(0.1) - - icon.__del__() - - def test_icon_grid(self): - items = [] - for i in range(50): - items.append({ - "title": "App %s" % i - }) - icon_grid = IconGrid(5, 5, items, None) +# def test_icon(self): +# icon = Icon(44, 40, "Badge Store with", "badge_store/icon.gif") +# icon.show() +# +# for s in [True, False, True]: +# icon.selected = s +# time.sleep(0.1) +# +# icon.__del__() +# +# def test_icon_grid(self): +# items = [] +# for i in range(50): +# items.append({ +# "title": "App %s" % i +# }) +# icon_grid = IconGrid(5, 5, items, None) diff --git a/lib/test_sleep.py b/lib/test_sleep.py new file mode 100644 index 0000000..977b667 --- /dev/null +++ b/lib/test_sleep.py @@ -0,0 +1,14 @@ +"""Tests for http""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "sleep"] + +import unittest, sleep + +class TestSleep(unittest.TestCase): + + def test_sleep(self): + sleep.sleep_ms(100) + +if __name__ == '__main__': + unittest.main() diff --git a/lib/test_wifi.py b/lib/test_wifi.py new file mode 100644 index 0000000..673bd5c --- /dev/null +++ b/lib/test_wifi.py @@ -0,0 +1,15 @@ +"""Tests for wifi""" + +___license___ = "MIT" +___dependencies___ = ["upip:unittest", "wifi"] + +import unittest, wifi + +class TestWifi(unittest.TestCase): + + def test_connect(self): + wifi.connect() + self.assertTrue(wifi.is_connected()) + +if __name__ == '__main__': + unittest.main() diff --git a/lib/wifi.py b/lib/wifi.py index 703c641..c833862 100644 --- a/lib/wifi.py +++ b/lib/wifi.py @@ -1,20 +1,17 @@ """Handles connecting to a wifi access point based on a valid wifi.json file""" ___license___ = "MIT" -___dependencies___ = ["dialogs"] +___dependencies___ = ["dialogs", "sleep"] -import network -import os -import json -import pyb -import dialogs +import network, os, json, dialogs, sleep, time _nic = None def nic(): global _nic if not _nic: - _nic = network.CC3100() + _nic = network.WLAN() + _nic.active(True) return _nic def connection_details(): @@ -37,7 +34,7 @@ def connect(wait=True, timeout=10, show_wait_message=False, prompt_on_fail=True, retry_connect = True while retry_connect: - if nic().is_connected(): + if nic().isconnected(): return details = connection_details() @@ -68,25 +65,27 @@ def connect(wait=True, timeout=10, show_wait_message=False, prompt_on_fail=True, if not retry_connect: os.remove('wifi.json') os.sync() - # We would rather let you choose a new network here, but - # scanning doesn't work after a connect at the moment - pyb.hard_reset() + else: raise def connect_wifi(details, timeout, wait=False): if 'pw' in details: - nic().connect(details['ssid'], details['pw'], timeout=timeout) + nic().connect(details['ssid'], details['pw']) else: - nic().connect(details['ssid'], timeout=timeout) + nic().connect(details['ssid']) if wait: - while not nic().is_connected(): - nic().update() - pyb.delay(100) + wait_until = time.ticks_ms() + 2000 + while not nic().isconnected(): + #nic().update() # todo: do we need this? + if (time.ticks_ms() > wait_until): + raise Exception("Timeout while trying to connect to wifi") + sleep.sleep_ms(100) + def is_connected(): - return nic().is_connected() + return nic().isconnected() def get_security_level(ap): n = nic() @@ -149,5 +148,4 @@ def choose_wifi(dialog_title='TiLDA'): file.write(json.dumps(conn_details)) os.sync() - # We can't connect after scanning for some bizarre reason, so we reset instead - pyb.hard_reset() + # todo: last time we had to hard reset here, is that still the case?