コード例 #1
0
def animate():
    teeth = False
    fps = False
    shadow = True
    pressed = False
    ms1 = utime.time_ms()
    with display.open() as disp:
        while True:
            t = utime.localtime()
            ms0 = ms1
            ms1 = utime.time_ms() % 1000
            disp.clear()
            volt = os.read_battery()
            charge = (volt - 3.4) / 0.8
            draw_spring(disp, charge, shadow)
            draw_gears(disp, t, ms1, teeth, shadow)
            draw_hands(disp, t, shadow)
            if fps:
                disp.print("{:.1f} fps".format(1000.0 / (ms1 - ms0)),
                           posx=100,
                           posy=60)
            disp.update()
            butt = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
                                | buttons.TOP_RIGHT)
            if butt == 0:
                pressed = False
            if not pressed and butt & buttons.BOTTOM_LEFT != 0:
                teeth = not teeth
                pressed = True
            if not pressed and butt & buttons.BOTTOM_RIGHT != 0:
                fps = not fps
                pressed = True
            if not pressed and butt & buttons.TOP_RIGHT != 0:
                shadow = not shadow
                pressed = True
コード例 #2
0
def button_events(timeout=None):
    yield 0

    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)
    button_pressed = True if v != 0 else False

    if timeout is not None:
        timeout = int(timeout * 1000)
        next_tick = utime.time_ms() + timeout

    while True:
        if timeout is not None:
            current_time = utime.time_ms()
            if current_time > next_tick:
                next_tick = current_time + timeout
                yield TIMEOUT

            v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT | buttons.TOP_RIGHT)

            if v == 0:
                button_pressed = False

            elif not button_pressed:
                for mask in (buttons.BOTTOM_LEFT, buttons.BOTTOM_RIGHT, buttons.TOP_RIGHT):
                    if v & mask != 0:
                        yield mask
コード例 #3
0
ファイル: monotime.py プロジェクト: dos1/card10-firmware
def set_unix_time(t):
    global _offset_ms

    cur_t = _utime.time_ms()
    _utime.set_unix_time(t)
    new_t = _utime.time_ms()

    diff = cur_t - new_t
    _offset_ms += diff
コード例 #4
0
ファイル: simple_menu.py プロジェクト: dos1/card10-firmware
    def run(self):
        """Start the event-loop."""
        try:
            timeout = self.scroll_speed
            if self.timeout is not None and self.timeout < self.scroll_speed:
                timeout = self.timeout

            for ev in button_events(timeout):
                if ev == buttons.BOTTOM_RIGHT:
                    self.select_time = utime.time_ms()
                    self.draw_menu(-8)
                    self.idx = (self.idx + 1) % len(self.entries)
                    try:
                        self.on_scroll(self.entries[self.idx], self.idx)
                    except _ExitMenuException:
                        raise
                    except Exception as e:
                        print("Exception during menu.on_scroll():")
                        sys.print_exception(e)
                elif ev == self.button_scroll_up:
                    self.select_time = utime.time_ms()
                    self.draw_menu(8)
                    self.idx = (self.idx + len(self.entries) - 1) % len(
                        self.entries)
                    try:
                        self.on_scroll(self.entries[self.idx], self.idx)
                    except _ExitMenuException:
                        raise
                    except Exception as e:
                        print("Exception during menu.on_scroll():")
                        sys.print_exception(e)
                elif ev == self.button_select:
                    try:
                        self.on_select(self.entries[self.idx], self.idx)
                        self.select_time = utime.time_ms()
                    except _ExitMenuException:
                        raise
                    except Exception as e:
                        print("Menu crashed!")
                        sys.print_exception(e)
                        self.error("Menu", "crashed")
                        utime.sleep(1.0)

                self.draw_menu()

                if self.timeout is not None and (utime.time_ms() -
                                                 self.select_time) > int(
                                                     self.timeout * 1000):
                    self.on_timeout()
        except _ExitMenuException:
            pass
コード例 #5
0
    def draw_entry(self, value, index, offset):
        """
        Draw a single entry.

        This is an internal function; you can override it for customized behavior.

        :param value: The value for this entry.  Use this to identify
            different entries.
        :param int index: A unique index per entry. Stable for a certain entry,
            but **not** an index into ``entries``.
        :param int offset: Y-offset for this entry.
        """
        string = self.entry2name(value)

        if offset != 20 or len(string) < 10:
            string = " " + string + " " * 9
        else:
            # Slowly scroll entry to the side
            time_offset = (utime.time_ms() - self.select_time) // int(
                self.scroll_speed * 1000)
            time_offset = time_offset % (len(string) - 7) - 1
            time_offset = min(len(string) - 10, max(0, time_offset))
            string = " " + string[time_offset:]

        self.disp.print(
            string,
            posy=offset,
            fg=self.color_text,
            bg=self.color_1 if index % 2 == 0 else self.color_2,
        )
コード例 #6
0
    def __init__(self, entries):
        if len(entries) == 0:
            raise ValueError("at least one entry is required")

        self.entries = entries
        self.idx = 0
        self.select_time = utime.time_ms()
        self.disp = display.open()
コード例 #7
0
def toggle_leds():
    global led_mode, disp, pause_screen, leds, modes
    led_mode, display_args = next(modes)

    pause_screen = utime.time_ms() + 250
    disp.clear(COLOR_BACKGROUND)
    disp.print("LEDs", posx=50, posy=20, fg=COLOR_TEXT)
    disp.print(**display_args, posy=40, fg=COLOR_TEXT)
    disp.update()
    leds.clear()
コード例 #8
0
def toggle_bias():
    global bias, disp, pause_screen
    if write > 0:
        pause_screen = utime.time_ms() + 500
        disp.clear(COLOR_BACKGROUND)
        disp.print("Locked", posx=30, posy=30, fg=COLOR_TEXT)
        disp.update()
        return

    close_sensor()
    bias = not bias
    open_sensor()
コード例 #9
0
def toggle_mode():
    global current_mode, disp, pause_screen
    if write > 0:
        pause_screen = utime.time_ms() + 500
        disp.clear(COLOR_BACKGROUND)
        disp.print("Locked", posx=30, posy=30, fg=COLOR_TEXT)
        disp.update()
        return

    close_sensor()
    current_mode = MODE_USB if current_mode == MODE_FINGER else MODE_FINGER
    open_sensor()
コード例 #10
0
ファイル: simple_menu.py プロジェクト: dos1/card10-firmware
    def __init__(self, entries):
        if len(entries) == 0:
            raise ValueError("at least one entry is required")

        self.entries = entries
        self.idx = 0
        self.select_time = utime.time_ms()
        self.disp = display.open()
        self.button_scroll_up = (buttons.TOP_RIGHT if self.right_buttons_scroll
                                 else buttons.BOTTOM_LEFT)
        self.button_select = (buttons.BOTTOM_LEFT if self.right_buttons_scroll
                              else buttons.TOP_RIGHT)
コード例 #11
0
 async def render_loop(self, root_view, screen):
     # type: (MGuiView, MGuiScreen) -> Coroutine[Any, Any, NoReturn]
     self.__context[C.CONTEXT_ROOT] = self
     self.__context[C.CONTEXT_ROOT_VIEW] = root_view
     try:
         self.__lasttime = time_ms()
         while self.__running:
             await self.render_once(root_view, screen)
     except KeyboardInterrupt:
         # print("Abort.")
         self.stop()
         pass
コード例 #12
0
def draw_histogram():
    global disp, history, current_mode, bias, write, pause_screen

    # skip rendering due to message beeing shown
    if pause_screen == -1:
        return
    elif pause_screen > 0:
        t = utime.time_ms()
        if t > pause_screen:
            pause_screen = 0
        else:
            return

    disp.clear(COLOR_BACKGROUND)

    # get max value and calc scale
    value_max = 0
    for value in history:
        if abs(value) > value_max:
            value_max = abs(value)
    scale = SCALE_FACTOR / (value_max if value_max > 0 else 1)

    # draw histogram
    old = False
    x = 0
    samples = len(history)
    for i, value in enumerate(history):
        if old == False:
            old = (x, int(value * scale) + OFFSET)
            x += 1
            continue
        elif i > samples - WIDTH:
            disp.line(old[0], old[1], x, int(value * scale) + OFFSET, col=COLOR_LINE)
            old = (x, int(value * scale) + OFFSET)
            x += 1

    # draw text: mode/bias/write
    disp.print(
        current_mode + ("+Bias" if bias else ""),
        posx=0,
        posy=0,
        fg=(COLOR_MODE_FINGER if current_mode == MODE_FINGER else COLOR_MODE_USB),
    )

    # announce writing ecg log
    if write > 0:
        t = utime.time()
        if write > 0 and t % 2 == 0:
            disp.print("LOG", posx=0, posy=60, fg=COLOR_WRITE_FG, bg=COLOR_WRITE_BG)

    disp.update()
コード例 #13
0
def toggle_write():
    global write, disp, pause_screen
    pause_screen = utime.time_ms() + 1000
    disp.clear(COLOR_BACKGROUND)
    if write > 0:
        write_filebuffer()
        write = 0
        disp.print("Stop", posx=50, posy=20, fg=COLOR_TEXT)
        disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT)
    else:
        filebuffer = bytearray()
        write = utime.time()
        disp.print("Start", posx=45, posy=20, fg=COLOR_TEXT)
        disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT)

    disp.update()
コード例 #14
0
 async def render_once(self, root_view, screen):
     s_w, s_h = screen.get_size()
     frame = screen.get_framebuffer()
     target_duration = 1000 // self.__context[C.CONTEXT_FRAME_RATE]
     now = time_ms()
     if now - self.__lasttime < target_duration:
         await asyncio.sleep(0.0)
         return
     self.__context[C.CONTEXT_FRAME_DURATION] = now - self.__lasttime
     self.__lasttime = now
     # print(self.__context[CONTEXT_FRAME_DURATION])
     try:
         if root_view.need_render(self.__context):
             effect_area = await root_view.render(self.__context, frame,
                                                  (0, 0, s_w, s_h))
             await screen.refresh(self.__context, effect_area)
     except Exception as e:
         if is_debug:
             print_exception(e)
             self.stop()
             return
コード例 #15
0
ファイル: __init__.py プロジェクト: dos1/card10-firmware
def toggle_write():
    global write, disp, pause_screen, filebuffer, samples_since_start_of_write, write_time_string
    pause_screen = utime.time_ms() + 1000
    disp.clear(COLOR_BACKGROUND)
    if write > 0:
        write_filebuffer()
        write = 0
        disp.print("Stop", posx=50, posy=20, fg=COLOR_TEXT)
        disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT)
    else:
        filebuffer = bytearray()
        write = utime.time()
        lt = utime.localtime(write)
        write_time_string = "{:04d}-{:02d}-{:02d}_{:02d}{:02d}{:02d}".format(*lt)
        samples_since_start_of_write = 0
        try:
            os.mkdir("ecg_logs")
        except:
            pass
        disp.print("Start", posx=45, posy=20, fg=COLOR_TEXT)
        disp.print("logging", posx=30, posy=40, fg=COLOR_TEXT)

    disp.update()
コード例 #16
0
def main():
    global pause_histogram, histogram_offset

    # show button layout
    disp.clear(COLOR_BACKGROUND)
    disp.print("  BUTTONS  ", posx=0, posy=0, fg=COLOR_TEXT)
    disp.print("Finger/USB>", posx=0, posy=20, fg=COLOR_MODE_FINGER)
    disp.print("     Bias >", posx=0, posy=40, fg=COLOR_MODE_USB)
    disp.print("< Write Log", posx=0, posy=60, fg=COLOR_WRITE_BG)
    disp.update()
    utime.sleep(3)

    # start ecg
    open_sensor()
    while True:
        button_pressed = {"BOTTOM_LEFT": 0, "BOTTOM_RIGHT": 0, "TOP_RIGHT": 0}
        while True:
            v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
                             | buttons.TOP_RIGHT)

            # BOTTOM LEFT

            if button_pressed[
                    "BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0:
                button_pressed["BOTTOM_LEFT"] = utime.time_ms()
                if not pause_histogram:
                    toggle_write()
                else:
                    l = len(history)
                    histogram_offset += ECG_RATE / 2
                    if l - histogram_offset < WIDTH:
                        histogram_offset = l - WIDTH

            if button_pressed[
                    "BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0:
                duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"]
                button_pressed["BOTTOM_LEFT"] = 0

            # BOTTOM RIGHT

            if button_pressed[
                    "BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0:
                button_pressed["BOTTOM_RIGHT"] = utime.time_ms()
                if not pause_histogram:
                    toggle_bias()
                else:
                    histogram_offset -= ECG_RATE / 2
                    histogram_offset -= histogram_offset % (ECG_RATE / 2)
                    if histogram_offset < 0:
                        histogram_offset = 0

            if button_pressed[
                    "BOTTOM_RIGHT"] > 0 and v & buttons.BOTTOM_RIGHT == 0:
                duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"]
                button_pressed["BOTTOM_RIGHT"] = 0

            # TOP RIGHT

            # down, and still pressed
            if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT != 0:
                duration = utime.time_ms() - button_pressed["TOP_RIGHT"]
                if duration > 1000:
                    button_pressed["TOP_RIGHT"] = -1
                    toggle_pause()

            # register down event
            elif button_pressed[
                    "TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0:
                button_pressed["TOP_RIGHT"] = utime.time_ms()

            # register up event but event already called
            if button_pressed["TOP_RIGHT"] == -1 and v & buttons.TOP_RIGHT == 0:
                button_pressed["TOP_RIGHT"] = 0

            # register normal up event
            elif button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0:
                duration = utime.time_ms() - button_pressed["TOP_RIGHT"]
                button_pressed["TOP_RIGHT"] = 0
                if pause_histogram:
                    toggle_pause()
                else:
                    toggle_mode()
コード例 #17
0
ファイル: __init__.py プロジェクト: m0ppers/badge-scroller
def scroller(t):
    char_width = 15  # 180/11
    offset = ft / 20.0

    full = int(offset / float(char_width))
    rest = char_width - (int(offset) % char_width)

    full = full % len(SCROLLER_TEXT)

    d.print(SCROLLER_TEXT[full:], posx=rest, posy=62)
    d.rect(0, 62, 15, 80, col=(0, 0, 0))
    d.rect(145, 62, 160, 80, col=(0, 0, 0))


global_start = utime.time_ms()
while True:
    t = utime.time_ms() - global_start
    ft = float(t)
    with display.open() as d:
        d.clear()
        # d.print("peng1", posy=0, fg=[100, 255, 255])
        # d.update()
        # d.print("peng2", posy=0, fg=[100, 255, 255])
        # d.update()
        # for y in range(HEIGHT):
        #     for x in range(WIDTH):
        #         d.pixel(x, y, col = (255, 255, 0))
        offset = int(5.0 * math.sin(ft / 300.0))
        start = utime.time_ms()
        akronyme_analogiker(d, offset)
コード例 #18
0
def button_events(timeout=None):
    """
    Iterate over button presses (event-loop).

    This is just a helper function used internally by the menu.  But you can of
    course use it for your own scripts as well.  It works like this:

    .. code-block:: python

        import simple_menu, buttons

        for ev in simple_menu.button_events():
            if ev == buttons.BOTTOM_LEFT:
                # Left
                pass
            elif ev == buttons.BOTTOM_RIGHT:
                # Right
                pass
            elif ev == buttons.TOP_RIGHT:
                # Select
                pass

    .. versionadded:: 1.4

    :param float,optional timeout:
       Timeout after which the generator should yield in any case.  If a
       timeout is defined, the generator will periodically yield
       :py:data:`simple_menu.TIMEOUT`.

       .. versionadded:: 1.9
    """
    yield 0

    v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
                     | buttons.TOP_RIGHT)
    button_pressed = True if v != 0 else False

    if timeout is not None:
        timeout = int(timeout * 1000)
        next_tick = utime.time_ms() + timeout

    while True:
        if timeout is not None:
            current_time = utime.time_ms()
            if current_time >= next_tick:
                next_tick += timeout
                yield TIMEOUT

        v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
                         | buttons.TOP_RIGHT)

        if v == 0:
            button_pressed = False

        if not button_pressed and v & buttons.BOTTOM_LEFT != 0:
            button_pressed = True
            yield buttons.BOTTOM_LEFT

        if not button_pressed and v & buttons.BOTTOM_RIGHT != 0:
            button_pressed = True
            yield buttons.BOTTOM_RIGHT

        if not button_pressed and v & buttons.TOP_RIGHT != 0:
            button_pressed = True
            yield buttons.TOP_RIGHT
コード例 #19
0
ファイル: monotime.py プロジェクト: dos1/card10-firmware
def time_monotonic_ms():
    return _utime.time_ms() + _offset_ms
コード例 #20
0
ファイル: __init__.py プロジェクト: derf/card10-datalogger
def run_loop():
    bat = [1, [0, 230, 00], [255, 215, 0], [255, 0, 0]]
    anim = 0
    write_timer = 0
    with display.open() as disp:
        disp.clear()
        disp.backlight(5)
        disp.print("@derfnull", posy=0)
        disp.update()
        disp.close()
    while True:
        pressed = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
        if pressed & buttons.BOTTOM_LEFT:
            anim += 1
        if pressed & buttons.BOTTOM_RIGHT:
            anim += 2
        if pressed:
            if anim > 4:
                anim = 0
            if anim == 0:
                leds.clear()
                leds.set_rocket(0, 0)
                leds.set_rocket(1, 0)
                leds.set_rocket(2, 0)
            if anim == 1:
                leds.set_rocket(0, 0)
                leds.set_rocket(1, 0)
                leds.set_rocket(2, 0)
                leds.gay(0.2)
            if anim == 2:
                leds.clear()
                leds.set_rocket(0, 2)
                leds.set_rocket(1, 15)
                leds.set_rocket(2, 15)
            if anim == 3:
                leds.clear()
                leds.set_rocket(0, 15)
                leds.set_rocket(1, 15)
                leds.set_rocket(2, 15)
            if anim == 4:
                leds.clear()
                leds.set_rocket(0, 0)
                leds.set_rocket(1, 0)
                leds.set_rocket(2, 0)
                leds.set(11, [127, 127, 127])
                leds.set(12, [127, 127, 127])
                leds.set(13, [127, 127, 127])
                leds.set(14, [127, 127, 127])
        sensor_data = bme680.get_data()
        ambient_light = light_sensor.get_reading()
        battery_voltage = os.read_battery()
        with display.open() as disp:
            disp.clear()
            render_battery(disp, bat, battery_voltage)
            disp.print("@derfnull", posy=0)
            disp.print("{:2.1f} C {:2.0f} %".format(sensor_data[0],
                                                    sensor_data[1]),
                       posy=20)
            disp.print("{:5.1f} hPa ".format(sensor_data[2]), posy=40)
            disp.print("{:4.1f} kOhm ".format(sensor_data[3] / 1000), posy=60)
            disp.update()
            disp.close()
        write_timer += 1
        if write_timer == 5:
            with open('sensorlog.txt', 'a') as f:
                f.write('{} {} {} {} {} {} {}\n'.format(
                    utime.time_ms(), sensor_data[0], sensor_data[1],
                    sensor_data[2], sensor_data[3], ambient_light,
                    battery_voltage))
            write_timer = 0
        utime.sleep(2)
コード例 #21
0
ファイル: monotime.py プロジェクト: dos1/card10-firmware
def time_ms():
    return _utime.time_ms()
コード例 #22
0
#   shading=255: stars at z_max are black (no pop-up)
WIDTH = 160  # screen width [px]
HEIGHT = 80  # screen height [px]
buttonState = 0  # button state
toremove = []  # list of star indices marked for removal
if __name__ == "__main__":
    # initialise starfield with stars at random positions, sorted back to front
    starfield = [[
        urandom.randint(-x_max, x_max),
        urandom.randint(-y_max, y_max), z, -1, -1
    ] for z in sorted([urandom.randint(0, z_max) for i in range(0, n_stars)],
                      reverse=True)]
    # indefinite loop
    while True:
        # take cycle start time
        t0 = utime.time_ms()
        # read button states, process changes, save state for next cycle
        btns = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT)
        if btns & buttons.BOTTOM_LEFT == 0 and buttonState & buttons.BOTTOM_LEFT != 0:
            # reduce warp speed (bottom left button, falling edge)
            warp_target = max(int(warp) - 1, 0)
            if warp_target < int(warp):
                d_warp = -warp_step
            else:
                d_warp = 0
        elif btns & buttons.BOTTOM_RIGHT == 0 and buttonState & buttons.BOTTOM_RIGHT != 0:
            # increase warp speed (bottom right button, falling edge)
            warp_target = min(int(warp) + 1, 9)
            if warp_target > int(warp):
                d_warp = +warp_step
            else:
コード例 #23
0
def draw_histogram():
    global disp, history, current_mode, bias, write, pause_screen, update_screen

    # skip rendering due to message beeing shown
    if pause_screen == -1:
        return
    elif pause_screen > 0:
        t = utime.time_ms()
        if t > pause_screen:
            pause_screen = 0
        else:
            return

    disp.clear(COLOR_BACKGROUND)

    # offset in pause_histogram mode
    window_end = int(len(history) - histogram_offset)
    s_start = max(0, window_end - (ECG_RATE * 2))
    s_end = max(0, window_end)
    s_draw = max(0, s_end - WIDTH)

    # get max value and calc scale
    value_max = max(abs(x) for x in history[s_start:s_end])
    scale = SCALE_FACTOR / (value_max if value_max > 0 else 1)

    # draw histogram
    # values need to be inverted so high values are drawn with low pixel coordinates (at the top of the screen)
    draw_points = (int(-x * scale + OFFSET_Y) for x in history[s_draw:s_end])

    prev = next(draw_points)
    for x, value in enumerate(draw_points):
        disp.line(x, prev, x + 1, value, col=COLOR_LINE)
        prev = value

    # draw text: mode/bias/write
    if pause_histogram:
        disp.print(
            "Pause" +
            (" -{:0.1f}s".format(histogram_offset /
                                 ECG_RATE) if histogram_offset > 0 else ""),
            posx=0,
            posy=0,
            fg=COLOR_TEXT,
        )
    else:
        led_range = last_sample_count if last_sample_count > 5 else 5
        draw_leds(
            min(history[-led_range:]) / value_max,
            max(history[-led_range:]) / value_max)
        if pulse < 0:
            disp.print(
                current_mode + ("+Bias" if bias else ""),
                posx=0,
                posy=0,
                fg=(COLOR_MODE_FINGER
                    if current_mode == MODE_FINGER else COLOR_MODE_USB),
            )
        else:
            disp.print(
                "BPM: {}".format(pulse),
                posx=0,
                posy=0,
                fg=(COLOR_MODE_FINGER
                    if current_mode == MODE_FINGER else COLOR_MODE_USB),
            )

    # announce writing ecg log
    if write > 0:
        t = utime.time()
        if write > 0 and t % 2 == 0:
            disp.print("LOG",
                       posx=0,
                       posy=60,
                       fg=COLOR_WRITE_FG,
                       bg=COLOR_WRITE_BG)

    disp.update()
    update_screen = 0
コード例 #24
0
def main():
    global pause_histogram, histogram_offset

    # show button layout
    disp.clear(COLOR_BACKGROUND)
    disp.print("  BUTTONS ",
               posx=0,
               posy=0,
               fg=COLOR_TEXT,
               font=display.FONT20)
    disp.line(0, 20, 159, 20, col=COLOR_LINE)
    disp.print("       Pause >",
               posx=0,
               posy=28,
               fg=COLOR_MODE_FINGER,
               font=display.FONT16)
    disp.print("   Mode/Bias >",
               posx=0,
               posy=44,
               fg=COLOR_MODE_USB,
               font=display.FONT16)
    disp.print("< LED/WriteLog",
               posx=0,
               posy=64,
               fg=COLOR_WRITE_BG,
               font=display.FONT16)
    disp.update()
    utime.sleep(3)

    # start ecg
    open_sensor()
    while True:
        button_pressed = {"BOTTOM_LEFT": 0, "BOTTOM_RIGHT": 0, "TOP_RIGHT": 0}
        while True:
            v = buttons.read(buttons.BOTTOM_LEFT | buttons.BOTTOM_RIGHT
                             | buttons.TOP_RIGHT)

            # TOP RIGHT

            # down
            if button_pressed["TOP_RIGHT"] == 0 and v & buttons.TOP_RIGHT != 0:
                button_pressed["TOP_RIGHT"] = utime.time_ms()
                toggle_pause()

            # up
            if button_pressed["TOP_RIGHT"] > 0 and v & buttons.TOP_RIGHT == 0:
                duration = utime.time_ms() - button_pressed["TOP_RIGHT"]
                button_pressed["TOP_RIGHT"] = 0

            # BOTTOM LEFT
            #
            # on pause = shift view left
            # long = toggle write
            # short = toggle leds

            # down, and still pressed
            if (button_pressed["BOTTOM_LEFT"] > 0
                    and v & buttons.BOTTOM_LEFT != 0 and not pause_histogram):
                duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"]
                if duration > 1000:
                    button_pressed["BOTTOM_LEFT"] = -1
                    toggle_write()

            # register down event
            elif button_pressed[
                    "BOTTOM_LEFT"] == 0 and v & buttons.BOTTOM_LEFT != 0:
                button_pressed["BOTTOM_LEFT"] = utime.time_ms()

            # register up event but event already called
            if button_pressed[
                    "BOTTOM_LEFT"] == -1 and v & buttons.BOTTOM_LEFT == 0:
                button_pressed["BOTTOM_LEFT"] = 0

            # register normal up event
            elif button_pressed[
                    "BOTTOM_LEFT"] > 0 and v & buttons.BOTTOM_LEFT == 0:
                duration = utime.time_ms() - button_pressed["BOTTOM_LEFT"]
                button_pressed["BOTTOM_LEFT"] = 0
                if not pause_histogram:
                    toggle_leds()
                else:
                    l = len(history)
                    histogram_offset += ECG_RATE / 2
                    if l - histogram_offset < WIDTH:
                        histogram_offset = l - WIDTH

            # BOTTOM RIGHT
            #
            # on pause = shift view right
            # long = toggle bias
            # short = toggle mode (finger/usb)

            # down, and still pressed
            if (button_pressed["BOTTOM_RIGHT"] > 0
                    and v & buttons.BOTTOM_RIGHT != 0 and not pause_histogram):
                duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"]
                if duration > 1000:
                    button_pressed["BOTTOM_RIGHT"] = -1
                    toggle_bias()

            # register down event
            elif button_pressed[
                    "BOTTOM_RIGHT"] == 0 and v & buttons.BOTTOM_RIGHT != 0:
                button_pressed["BOTTOM_RIGHT"] = utime.time_ms()

            # register up event but event already called
            if button_pressed[
                    "BOTTOM_RIGHT"] == -1 and v & buttons.BOTTOM_RIGHT == 0:
                button_pressed["BOTTOM_RIGHT"] = 0

            # register normal up event
            elif button_pressed[
                    "BOTTOM_RIGHT"] > 0 and v & buttons.BOTTOM_RIGHT == 0:
                duration = utime.time_ms() - button_pressed["BOTTOM_RIGHT"]
                button_pressed["BOTTOM_RIGHT"] = 0
                if pause_histogram:
                    histogram_offset -= ECG_RATE / 2
                    histogram_offset -= histogram_offset % (ECG_RATE / 2)
                    if histogram_offset < 0:
                        histogram_offset = 0
                else:
                    toggle_mode()