Buttons

hub.buttons (hub._buttons_module) covers the 5-way directional pad (center = power button).

Level readers

Return the current held state:

if hub.buttons.center():
    ...
if hub.buttons.left():
    ...

Names: center, up, down, left, right.

Callbacks

Callbacks fire on rising edge (release → press). They are dispatched automatically — the C firmware detects rising edges in the hardware loop and schedules a dispatcher on the MicroPython task via mp_sched_schedule. No poll call is needed in user code.

Each fired callback runs in its own new thread (_thread), so a slow callback never blocks the main program or other callbacks. Pressing a button again while its previous callback is still running starts a second, concurrent copy. Keep in mind:

  • Callbacks share globals with the main program; guard shared state with _thread.allocate_lock() if needed.

  • Each LVGL call holds the GIL, so single widget calls from a callback are safe; a multi-step update may interleave with lv.timer_handler() in the main loop and render half-updated for one frame.

  • Each thread uses internal RAM for its stack; very rapid presses with long-running callbacks can run out (“can’t create thread” is printed and that press is skipped).

  • Exceptions in a callback are printed; SystemExit ends the callback silently.

Stopping callbacks

hub.stop_callbacks() delivers SystemExit to every callback thread that is still running and returns how many were signalled. It does not wait: each thread exits at its next bytecode or inside its current time.sleep_ms. Called from inside a callback, it stops all the other callback threads. Registrations are kept.

@hub.buttons.on("up")
def ticker():
    n = 0
    while True:
        print("tick", n)
        n += 1
        time.sleep_ms(500)

@hub.buttons.on("down")
def stop_all():
    hub.stop_callbacks()

When the program ends the runner calls the same stop, waits up to 100 ms for the threads to exit, then deletes any that are left.

Decorator form:

@hub.buttons.on("left")
def left_pressed():
    print("<--")

@hub.buttons.on("center")
def center_pressed():
    print("center")

Manual form:

hub.buttons.on("up", my_cb)   # register
hub.buttons.on("up", None)    # unregister
hub.buttons.off("up")         # alt: unregister

Exiting a program

Holding the centre button for >= 2 s causes the C firmware to schedule a KeyboardInterrupt on the MicroPython task. The runner catches this and shows the “Done” screen. User code cannot suppress it — treat it as a physical stop signal.

To exit from Python: hub.exit() (raises SystemExit, does not return).

Power-off

Holding the centre button for >= 5 s triggers a hardware power-off in the C firmware loop. User code cannot suppress this.

To power off from Python: hub.powerOff() (does not return).