Exemplo n.º 1
0
    async def do_delay(self, pa):
        # show # of failures and implement the delay, which could be 
        # very long.
        from main import numpad

        dis.clear()
        dis.text(None, 0, "Checking...", FontLarge)
        dis.text(None, 24, 'Wait '+pretty_delay(pa.delay_required * pa.seconds_per_tick))
        dis.text(None, 40, "(%d failures)" % pa.num_fails)

        # save a little bit of interrupt load/overhead
        numpad.stop()

        while pa.is_delay_needed():
            dis.progress_bar(pa.delay_achieved / pa.delay_required)
            dis.show()

            pa.delay()

        numpad.start()
Exemplo n.º 2
0
def set_genuine():
    # PIN must be blank for this to work
    # - or logged in already as main
    from main import pa

    if pa.is_secondary:
        return

    if not pa.is_successful():
        # assume blank pin during factory selftest
        pa.setup(b'')
        assert not pa.is_delay_needed()     # "PIN failures?"

        if not pa.is_successful():
            pa.login()
            assert pa.is_successful()       # "PIN not blank?"

    # do verify step
    pa.greenlight_firmware()

    dis.show()
Exemplo n.º 3
0
    def _show_words(self, has_secondary=False):

        dis.clear()
        dis.text(None, 0, "Recognize these?" if (not self.is_setting) or self.is_repeat \
                            else "Write these down:")

        dis.show()
        dis.busy_bar(True)
        words = pincodes.PinAttempt.prefix_words(self.pin.encode())

        y = 15
        x = 18
        dis.text(x, y,    words[0], FontLarge)
        dis.text(x, y+18, words[1], FontLarge)

        if self.offer_second:
            dis.text(None, -1, "Press (2) for secondary wallet", FontTiny)
        else:
            dis.text(None, -1, "X to CANCEL, or OK to CONTINUE", FontTiny)

        dis.busy_bar(False)     # includes a dis.show()
Exemplo n.º 4
0
def show_fatal_error(msg):
    # show a multi-line error message, over some kinda "fatal" banner
    from main import dis
    from display import FontTiny

    dis.clear()
    lines = msg.split('\n')[-6:]
    dis.text(None, 1, '>>>> Yikes!! <<<<')

    y = 13 + 2
    for num, ln in enumerate(lines):
        ln = ln.strip()

        if ln[0:6] == 'File "':
            # convert: File "main.py", line 63, in interact
            #    into: main.py:63  interact
            ln = ln[6:].replace('", line ', ':').replace(', in ', '  ')

        dis.text(0, y + (num * 8), ln, FontTiny)

    dis.show()
Exemplo n.º 5
0
    def draw_background(self):
        # Render and capture static parts of screen one-time.
        from main import dis
        from display import FontTiny

        dis.clear()
        dis.text(6, 0, "HSM MODE")
        dis.show() # cover the 300ms or so it takes to draw the rest below

        dis.hline(15)

        x, y = 0, 28
        for lab, xoff, val in [ 
            ('APPROVED', 0, '0'),
            ('REFUSED', 0, '0'),
            ('PERIOD LEFT', 5, 'xx'),
        ]:
            nx = dis.text(x+xoff, y-7, lab, FontTiny)
            hw = nx - x
            if lab == 'REFUSED':
                dis.dis.line(nx+2, 0, nx+2, y+16, 1)
            else:
                if not xoff:
                    dis.dis.line(nx+2, y-12, nx+2, y+16, 1)

            # keep this:
            #print('%s @ x=%d' % (lab, x+(hw//2)-2))

            # was:
            #tw = 7*len(val)     # = dis.width(val, FontSmall)
            #dis.text(x+((hw-tw)//2)-1, y+1, val)
            x = nx + 7

        dis.hline(y+17)

        # no local confirmation code entered, typically
        dis.text(80, 0, '######')

        # save this static background
        self.screen_buf = dis.dis.buffer[:]
Exemplo n.º 6
0
    def show(self):
        #
        # Redraw the menu.
        #
        dis.clear()

        #print('cur=%d ypos=%d' % (self.cursor, self.ypos))

        # subclass hook
        self.early_draw(dis)

        x, y = (10, 2)
        h = 14
        for n in range(self.ypos + PER_M + 1):
            if n + self.ypos >= self.count: break
            msg = self.items[n + self.ypos].label
            is_sel = (self.cursor == n + self.ypos)
            if is_sel:
                dis.dis.fill_rect(0, y, 128, h - 1, 1)
                dis.icon(2, y, 'wedge', invert=1)
                dis.text(x, y, msg, invert=1)
            else:
                dis.text(x, y, msg)

            if msg[0] == ' ' and self.space_indicators:
                dis.icon(x - 2, y + 11, 'space', invert=is_sel)

            if self.chosen is not None and (n + self.ypos) == self.chosen:
                dis.icon(108, y, 'selected', invert=is_sel)

            y += h
            if y > 128: break

        # subclass hook
        self.late_draw(dis)

        if self.count > PER_M:
            dis.scroll_bar(self.ypos / (self.count - PER_M))

        dis.show()
Exemplo n.º 7
0
async def test_microsd():
    if ckcc.is_simulator(): return

    async def wait_til_state(want):
        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Remove' if sd.present() else 'Insert', font=FontLarge)
        dis.show()

        while 1:
            if want == sd.present(): return
            await sleep_ms(100)
            if ux_poll_once():
                raise RuntimeError("MicroSD test aborted")

    try:
        import pyb
        sd = pyb.SDCard()
        sd.power(0)

        # test presence switch
        for ph in range(7):
            await wait_til_state(not sd.present())

            if ph >= 2 and sd.present():
                # debounce
                await sleep_ms(100)
                if sd.present(): break
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Testing', font=FontLarge)
        dis.show()

        # card inserted
        assert sd.present()     #, "SD not present?"

        # power up?
        sd.power(1)
        await sleep_ms(100)

        try:
            blks, bsize, *unused = sd.info()
            assert bsize == 512
        except:
            assert 0        # , "card info"

        # just read it a bit, writing would prove little
        buf = bytearray(512)
        msize = 256*1024
        for addr in range(0, msize, 1024):
            sd.readblocks(addr, buf)
            dis.progress_bar_show(addr/msize)

            if addr == 0:
                assert buf[-2:] == b'\x55\xaa'      # "Bad read"

        # force removal, so cards don't get stuck in finished units
        await wait_til_state(False)

    finally:
        # CRTICAL: power it back down
        sd.power(0)
Exemplo n.º 8
0
async def spinner_edit(pw):
    # Allow them to pick each digit using "D-pad"
    from main import dis
    from display import FontTiny, FontSmall

    # Should allow full unicode, NKDN
    # - but limited to what we can show in FontSmall
    # - so really just ascii; not even latin-1
    # - 8-bit codepoints only
    my_rng = range(32, 127)          # FontSmall.code_range
    symbols = b' !"#$%&\'()*+,-./:;<=>?@[\\]^_`{|}~'
    letters = b'abcdefghijklmnopqrstuvwxyz'
    Letters = b'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
    numbers = b'1234567890'
    #assert len(set(symbols+letters+Letters+numbers)) == len(my_rng)
    
    footer1 = "1=Letters  2=Numbers  3=Symbols"
    footer2 = "4=SwapCase  0=HELP"
    y = 20
    pw = bytearray(pw or 'A')

    pos = len(pw)-1       # which part being changed
    n_visible = const(9)
    scroll_x = max(pos - n_visible, 0)

    def cycle_set(which, direction=1):
        # pick next item in set of choices
        for n, s in enumerate(which):
            if pw[pos] == s:
                try:
                    pw[pos] = which[n+direction]
                except IndexError:
                    pw[pos] = which[0 if direction==1 else -1]
                return
        pw[pos] = which[0]

    def change(dx):
        # next/prev within the same subset of related chars
        ch = pw[pos]
        for subset in [symbols, letters, Letters, numbers]:
            if ch in subset:
                return cycle_set(subset, dx)

        # probably unreachable code: numeric up/down
        ch = pw[pos] + dx
        if ch not in my_rng:
            ch = (my_rng.stop-1) if dx < 0 else my_rng.start
            assert ch in my_rng
        pw[pos] = ch

    # no key-repeat on certain keys
    press = PressRelease('4xy')
    while 1:
        dis.clear()

        lr = pos - scroll_x     # left/right distance of cursor
        if lr < 4 and scroll_x:
            scroll_x -= 1
        elif lr < 0:
            scroll_x = pos
        elif lr >= (n_visible-1):
            # past right edge
            scroll_x += 1

        for i in range(n_visible):
            # calc abs position in string
            ax = scroll_x + i
            x = 4 + (13*i)
            try:
                ch = pw[ax]
            except IndexError:
                continue

            if ax == pos:
                # draw cursor
                if len(pw) < 2*n_visible:
                    dis.text(x-4, y-19, '0x%02X' % ch, FontTiny)
                dis.icon(x-2, y-10, 'spin')

            if ch == 0x20:
                dis.icon(x, y+11, 'space')
            else:
                dis.text(x, y, chr(ch) if ch in my_rng else chr(215), FontSmall)

        if scroll_x > 0:
            dis.text(2, y-14, str(pw, 'ascii')[0:scroll_x].replace(' ', '_'), FontTiny)
        if scroll_x + n_visible < len(pw):
            dis.text(-1, 1, "MORE>", FontTiny)

        if 0:
            wy = 6
            count = len(pw)
            dis.text(-8, wy-4, "%d" % count)


        dis.text(None, -10, footer1, FontTiny)
        dis.text(None, -1, footer2, FontTiny)
        dis.show()

        ch = await press.wait()
        if ch == 'y':
            return str(pw, 'ascii')
        elif ch == 'x':
            if len(pw) > 1:
                # delete current char
                pw = pw[0:pos] + pw[pos+1:]
                if pos >= len(pw):
                    pos = len(pw)-1
            else:
                pp = await ux_show_story("OK to leave without any changes? Or X to cancel leaving.")
                if pp == 'x': continue
                return None

        elif ch == '7':      # left
            pos -= 1
            if pos < 0: pos = 0
        elif ch == '9':      # right
            pos += 1
            if pos >= len(pw):
                if len(pw) < 100 and pw[-3:] != b'   ':
                    pw += ' '       # expand with spaces
                else:
                    pos -= 1        # abort addition

        elif ch == '5':     # up
            change(1)
        elif ch == '8':     # down
            change(-1)
        elif ch == '1':     # alpha
            cycle_set(b'Aa')
        elif ch == '4':     # toggle case
            if (pw[pos] & ~0x20) in range(65, 91):
                pw[pos] ^= 0x20
        elif ch == '2':     # numbers
            cycle_set(numbers)
        elif ch == '3':     # symbols (all of them)
            cycle_set(symbols)
        elif ch == '0':     # help
            await ux_show_story('''\
Use arrow keys (5789) to select letter and move around. 

1=Letters (Aa..)
2=Numbers (12..)
3=Symbols (!@#&*)
4=Swap Case (q/Q)
X=Delete char

To quit without changes, delete everything. \
Add more characters by moving past end (right side).
''')
Exemplo n.º 9
0
async def ux_show_story(msg,
                        title=None,
                        escape=None,
                        sensitive=False,
                        strict_escape=False):
    # show a big long string, and wait for XY to continue
    # - returns character used to get out (X or Y)
    # - can accept other chars to 'escape' as well.
    # - accepts a stream or string
    from main import dis, numpad
    from display import FontLarge

    assert not numpad.disabled  # probably inside a CardSlot context

    lines = []
    if title:
        # kinda weak rendering but it works.
        lines.append('\x01' + title)

    if hasattr(msg, 'readline'):
        msg.seek(0)
        for ln in msg:
            if ln[-1] == '\n':
                ln = ln[:-1]

            if len(ln) > CH_PER_W:
                lines.extend(word_wrap(ln, CH_PER_W))
            else:
                # ok if empty string, just a blank line
                lines.append(ln)

        # no longer needed & rude to our caller, but let's save the memory
        msg.close()
        del msg
        gc.collect()
    else:
        for ln in msg.split('\n'):
            if len(ln) > CH_PER_W:
                lines.extend(word_wrap(ln, CH_PER_W))
            else:
                # ok if empty string, just a blank line
                lines.append(ln)

    # trim blank lines at end, add our own marker
    while not lines[-1]:
        lines = lines[:-1]

    lines.append('EOT')

    #print("story:\n\n\"" + '"\n"'.join(lines))
    #lines[0] = '111111111121234567893'

    top = 0
    H = 5
    ch = None
    pr = PressRelease()
    while 1:
        # redraw
        dis.clear()

        y = 0
        for ln in lines[top:top + H]:
            if ln == 'EOT':
                dis.hline(y + 3)
            elif ln and ln[0] == '\x01':
                dis.text(0, y, ln[1:], FontLarge)
                y += 21
            else:
                dis.text(0, y, ln)

                if sensitive and len(ln) > 3 and ln[2] == ':':
                    dis.mark_sensitive(y, y + 13)

                y += 13

        dis.scroll_bar(top / len(lines))
        dis.show()

        # wait to do something
        ch = await pr.wait()
        if escape and (ch == escape or ch in escape):
            # allow another way out for some usages
            return ch
        elif ch in 'xy':
            if not strict_escape:
                return ch
        elif ch == '0':
            top = 0
        elif ch == '7':  # page up
            top = max(0, top - H)
        elif ch == '9':  # page dn
            top = min(len(lines) - 2, top + H)
        elif ch == '5':  # scroll up
            top = max(0, top - 1)
        elif ch == '8':  # scroll dn
            top = min(len(lines) - 2, top + 1)
Exemplo n.º 10
0
    def calc(self):
        # average history, apply threshold to know which are "down"
        if self.debug == 1:
            print('\x1b[H\x1b[2J\n')
            LABELS = [('col%d' % n) for n in range(3)] + [('row%d' % n)
                                                          for n in range(4)]
        if self.debug == 2:
            from main import dis
            dis.clear()

        pressed = set()
        now = []
        diffs = []
        for idx in range(NUM_PINS):
            avg = self.levels[idx]  # not an average anymore
            now.append(avg)

            if self.baseline:
                diff = self.baseline[idx] - avg

                # the critical "threshold" .. remember, values below this are
                # might be "light" touches or proximity.
                if diff > THRESHOLD:
                    pressed.add(idx)

                if self.debug == 1:
                    print('%s: %5d   %4d   %d' %
                          (LABELS[idx], avg, diff, idx in pressed))
                    diffs.append(diff)

                if self.debug == 2:
                    from main import dis
                    y = (idx * 6) + 3

                    if 0:
                        x = int((avg * 128) / 16384.)
                        bx = int((self.baseline[idx] * 128) / 16384.)

                        for j in range(4):
                            dis.dis.line(0, y + j, 128, y + j, 0)

                        dis.dis.pixel(x, y, 1)
                        dis.dis.pixel(bx, y + 1, 1)

                    dx = 64 + int(diff / 8)
                    dx = min(max(0, dx), 127)
                    dis.dis.pixel(dx, y + 2, 1)
                    dis.dis.pixel(dx, y + 3, 1)

                    if idx == 0:
                        dx = 64 + int(THRESHOLD / 8)
                        dis.dis.vline(dx, 60, 64, 1)

                    dis.show()

        if self.debug == 1:
            print('\n')
            if diffs:
                print('min_diff = %d' % min(diffs))
                print('avg_diff = %d' % (sum(diffs) / len(diffs)))

        # should we remember this as a reference point (of no keys pressed)
        if self.trigger_baseline:
            self.baseline = now.copy()
            self.trigger_baseline = False
            pressed.clear()

        if self.debug == 2: return

        # Consider only single-pressed here; we can detect
        # many 2-key combo's but no plan to support that so they
        # are probably noise from that PoV.
        col_down = [i for i in range(3) if i in pressed]
        row_down = [i - 3 for i in range(3, 7) if i in pressed]

        if len(col_down) == 1 and len(row_down) == 1:
            # determine what key
            key = self.DECODER[(row_down[0], col_down[0])]
        else:
            # not sure, or all up
            key = ''

        if key != self.key_pressed:
            # annouce change
            self.key_pressed = key

            if self._changes.full():
                # no space, but do a "all up" and the new event
                print('numpad Q overflow')
                self._changes.get_nowait()
                self._changes.get_nowait()
                if key != '':
                    self._changes.put_nowait('')

            self._changes.put_nowait(key)

            self.last_event_time = utime.ticks_ms()
Exemplo n.º 11
0
async def start_login_sequence():
    # Boot up login sequence here.
    #
    from main import pa, settings, dis, loop, numpad
    import version

    if pa.is_blank():
        # Blank devices, with no PIN set all, can continue w/o login

        # Do green-light set immediately after firmware upgrade
        if version.is_fresh_version():
            pa.greenlight_firmware()
            dis.show()

        goto_top_menu()
        return

    # Allow impatient devs and crazy people to skip the PIN
    guess = settings.get('_skip_pin', None)
    if guess is not None:
        try:
            dis.fullscreen("(Skip PIN)")
            pa.setup(guess)
            pa.login()
        except:
            pass

    # if that didn't work, or no skip defined, force
    # them to login succefully.
    while not pa.is_successful():
        # always get a PIN and login first
        await block_until_login()

    # Must read settings after login
    settings.set_key()
    settings.load()

    # Restore a login preference or two
    numpad.sensitivity = settings.get('sens', numpad.sensitivity)

    # Do green-light set immediately after firmware upgrade
    if not pa.is_secondary:
        if version.is_fresh_version():
            pa.greenlight_firmware()
            dis.show()

    # Populate xfp/xpub values, if missing.
    # - can happen for first-time login of duress wallet
    # - may indicate lost settings, which we can easily recover from
    # - these values are important to USB protocol
    if not (settings.get('xfp', 0)
            and settings.get('xpub', 0)) and not pa.is_secret_blank():
        try:
            import stash

            # Recalculate xfp/xpub values (depends both on secret and chain)
            with stash.SensitiveValues() as sv:
                sv.capture_xpub()
        except Exception as exc:
            # just in case, keep going; we're not useless and this
            # is early in boot process
            print("XFP save failed: %s" % exc)

    # Allow USB protocol, now that we are auth'ed
    from usb import enable_usb
    enable_usb(loop, False)

    goto_top_menu()
Exemplo n.º 12
0
async def ux_show_story(msg, title=None, escape=None):
    # show a big long string, and wait for XY to continue
    # - returns character used to get out (X or Y)
    # - can accept other chars to 'escape' as well.
    from main import dis, numpad
    from display import FontLarge

    assert not numpad.disabled  # probably inside a CardSlot context

    lines = []
    if title:
        # kinda weak rendering but it works.
        lines.append('\x01' + title)
        #lines.append('')

    for ln in msg.split('\n'):
        if len(ln) > CH_PER_W:
            lines.extend(word_wrap(ln, CH_PER_W))
        else:
            # ok if empty string, just a blank line
            lines.append(ln)

    # trim blank lines at end, add our own marker
    while not lines[-1]:
        lines = lines[:-1]

    lines.append('EOT')

    #print("story:\n\n\"" + '"\n"'.join(lines))
    #lines[0] = '111111111121234567893'

    top = 0
    H = 5
    ch = None
    pr = PressRelease()
    while 1:
        # redraw
        dis.clear()

        y = 0
        for ln in lines[top:top + H]:
            if ln == 'EOT':
                dis.hline(y + 3)
            elif ln and ln[0] == '\x01':
                dis.text(0, y, ln[1:], FontLarge)
                y += 21
            else:
                dis.text(0, y, ln)
                y += 13

        dis.scroll_bar(top / len(lines))
        dis.show()

        # wait to do something
        ch = await pr.wait()
        if escape and (ch == escape or ch in escape):
            # allow another way out for some usages
            return ch
        elif ch in 'xy':
            return ch
        elif ch == '0':
            top = 0
        elif ch == '5':
            top = max(0, top - 1)
        elif ch == '8':
            top = min(len(lines) - 2, top + 1)
Exemplo n.º 13
0
async def start_login_sequence():
    # Boot up login sequence here.
    #
    from main import pa, settings, dis, loop, numpad
    from ux import idle_logout

    if pa.is_blank():
        # Blank devices, with no PIN set all, can continue w/o login

        # Do green-light set immediately after firmware upgrade
        if version.is_fresh_version():
            pa.greenlight_firmware()
            dis.show()

        goto_top_menu()
        return

    # maybe show a nickname before we do anything
    nickname = settings.get('nick', None)
    if nickname:
        try:
            await show_nickname(nickname)
        except: pass

    # Allow impatient devs and crazy people to skip the PIN
    guess = settings.get('_skip_pin', None)
    if guess is not None:
        try:
            dis.fullscreen("(Skip PIN)")
            pa.setup(guess)
            pa.login()
        except: pass

    # if that didn't work, or no skip defined, force
    # them to login succefully.
    while not pa.is_successful():
        # always get a PIN and login first
        await block_until_login()

    # Must re-read settings after login
    settings.set_key()
    settings.load()

    # implement "login countdown" feature
    delay = settings.get('lgto', 0)
    if delay:
        pa.reset()
        await login_countdown(delay)
        await block_until_login()

    # implement idle timeout now that we are logged-in
    loop.create_task(idle_logout())

    # Do green-light set immediately after firmware upgrade
    if not pa.is_secondary:
        if version.is_fresh_version():
            pa.greenlight_firmware()
            dis.show()

    # Populate xfp/xpub values, if missing.
    # - can happen for first-time login of duress wallet
    # - may indicate lost settings, which we can easily recover from
    # - these values are important to USB protocol
    if not (settings.get('xfp', 0) and settings.get('xpub', 0)) and not pa.is_secret_blank():
        try:
            import stash

            # Recalculate xfp/xpub values (depends both on secret and chain)
            with stash.SensitiveValues() as sv:
                sv.capture_xpub()
        except Exception as exc:
            # just in case, keep going; we're not useless and this
            # is early in boot process
            print("XFP save failed: %s" % exc)

    # If HSM policy file is available, offer to start that,
    # **before** the USB is even enabled.
    if version.has_fatram:
        try:
            import hsm, hsm_ux

            if hsm.hsm_policy_available():
                ar = await hsm_ux.start_hsm_approval(usb_mode=False, startup_mode=True)
                if ar:
                    await ar.interact()
        except: pass

    # Allow USB protocol, now that we are auth'ed
    from usb import enable_usb
    enable_usb(loop, False)

    goto_top_menu()
Exemplo n.º 14
0
    def calc(self):
        # average history, apply threshold to know which are "down"
        if self.debug == 1:
            print('\x1b[H\x1b[2J\n')
            LABELS = [('col%d' % n) for n in range(3)] + [('row%d' % n)
                                                          for n in range(4)]
        if self.debug == 2:
            from main import dis
            dis.clear()

        # should we remember this as a reference point (of no keys pressed)
        if self.trigger_baseline:
            self.baseline = array.array('I', self.prev_levels)
            self.trigger_baseline = False

            if 0:
                LABELS = [('col%d' % n)
                          for n in range(3)] + [('row%d' % n)
                                                for n in range(4)]
                print("Baselines:")
                for idx in range(NUM_PINS):
                    print('%s: %5d' % (LABELS[idx], self.baseline[idx]))

            return

        pressed = set()
        diffs = array.array('I')

        for idx in range(NUM_PINS):
            # track a running average, using different weights depending on sensitivity mode
            if self.sensitivity == 0:
                avg = self.levels[idx]
            elif self.sensitivity == 1:
                avg = (self.prev_levels[idx] + self.levels[idx]) // 2
            else:
                avg = ((self.prev_levels[idx] * 3) + self.levels[idx]) // 4

            self.prev_levels[idx] = avg

            if self.baseline:
                diff = self.baseline[idx] - avg
                diffs.append(diff)

                # the critical "threshold" .. remember, values below this are
                # might be "light" touches or proximity.
                if diff > THRESHOLD:
                    pressed.add(idx)

                # handle baseline drift, in one direction at least
                if diff < 0:
                    self.baseline[idx] = avg

                if self.debug == 1:
                    print('%s: %5d   %4d   %d' %
                          (LABELS[idx], avg, diff, idx in pressed))

                if self.debug == 2:
                    from main import dis
                    y = (idx * 6) + 3

                    dx = 64 + int(diff / 8)
                    dx = min(max(0, dx), 127)
                    dis.dis.pixel(dx, y + 1, 1)
                    dis.dis.pixel(dx, y + 2, 1)

                    dx = 64 + int(THRESHOLD / 8)
                    dis.dis.pixel(dx, y, 1)
                    dis.dis.pixel(dx, y + 3, 1)

                    dis.show()

        if max(diffs, default=0) < -10 or (len(pressed) > 4):
            print("auto recal")
            self.baseline = array.array('I', self.prev_levels)

        if self.debug == 1:
            print('\n')
            if diffs:
                print('min_diff = %5d / %5d / %5d' %
                      (min(diffs), (sum(diffs) / len(diffs)), max(diffs)))

        if self.debug == 2: return

        # Consider only single-pressed here; we can detect
        # many 2-key combo's but no plan to support that so they
        # are probably noise from that PoV.
        col_down = [i for i in range(3) if i in pressed]
        row_down = [i - 3 for i in range(3, 7) if i in pressed]

        if len(col_down) == 1 and len(row_down) == 1:
            # determine what key
            key = self.DECODER[(row_down[0], col_down[0])]
        else:
            # not sure, or all up
            key = ''

        if key != self.key_pressed:
            # annouce change
            self.key_pressed = key

            if self._changes.full():
                # no space, but do a "all up" and the new event
                print('numpad Q overflow')
                self._changes.get_nowait()
                self._changes.get_nowait()
                if key != '':
                    self._changes.put_nowait('')

            self._changes.put_nowait(key)

            self.last_event_time = utime.ticks_ms()
Exemplo n.º 15
0
async def test_microsd():
    if ckcc.is_simulator(): return

    from main import numpad
    numpad.stop()

    try:
        import pyb
        sd = pyb.SDCard()
        sd.power(0)

        # test presence switch
        for ph in range(7):
            want = not sd.present()

            dis.clear()
            dis.text(None, 10, 'MicroSD Card:')
            dis.text(None,
                     34,
                     'Remove' if sd.present() else 'Insert',
                     font=FontLarge)
            dis.show()

            while 1:
                if want == sd.present(): break
                await sleep_ms(100)
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

            if ph >= 2 and sd.present():
                # debounce
                await sleep_ms(100)
                if sd.present(): break
                if ux_poll_once():
                    raise RuntimeError("MicroSD test aborted")

        dis.clear()
        dis.text(None, 10, 'MicroSD Card:')
        dis.text(None, 34, 'Testing', font=FontLarge)
        dis.show()

        # card inserted
        assert sd.present(), "SD not present?"

        # power up?
        sd.power(1)
        await sleep_ms(100)

        try:
            blks, bsize, ctype = sd.info()
            assert bsize == 512, "wrong block size"
        except:
            assert 0, "unable to get card info"

        # just read it a bit, writing would prove little
        buf = bytearray(512)
        msize = 1024 * 1024
        for addr in range(0, msize, 1024):
            sd.readblocks(addr, buf)
            dis.progress_bar_show(addr / msize)

            if addr == 0:
                assert buf[-2:] == b'\x55\xaa', "Bad read"

    finally:
        # CRTICAL: power it back down
        sd.power(0)
        numpad.start()