Program lifecycle

Two entry points exist for user code:

  1. Bare scripts run under main.py / REPL. You own the main loop. LPF2 objects (ports, remote hubs, virtual devices) are pumped by the C firmware automatically (see Automatic polling); but Python-side helpers such as buttons.poll() still need calling from your loop. Generally you’re not able to run scripts like this.

  2. Menu-launched scripts run under the on-device program runner in ports/esp32/fs/main.py. The runner injects hub.on() and drives setup / loop at ~10 ms per tick.

The @hub.on decorator

@hub.on("setup")
def setup():
    # runs once
    ...

@hub.on("loop")
def loop():
    # runs each tick
    ...

The decorator only exists inside the runner. import hub from boot.py or the REPL will not see it.

Powering off

  • Hold the centre / power button ~2 s — hardware kill.

  • Call hub.powerOff() — immediate power-off from Python. Does not return.

Log level

hub.log (hub._log_module) controls the firmware log verbosity:

hub.log.setLevel(2)   # 0=none, 1=err, 2=warn, 3=info, 4=debug, 5=verbose