PUPRemote lets microcontrollers present themselves as LEGO Powered Up sensors. It includes a low-level LPF2 emulator and a higher-level remote-procedure-style API for quick sensor prototypes.
- Emulate LEGO LPF2 sensors from MicroPython boards
- Higher-level RPC-style API (
PUPRemoteSensor,PUPRemoteHub) - Supports both synchronous (
process()) and asynchronous (process_async()) modes - Works with Pybricks, SPIKE 2, and Robot Inventor hubs
- Example scripts for LMS-ESP32, OpenMV, and Pybricks
- Optional lightweight
pupremote_hub.pyfor Pybricks-only deployments
- LMS-ESP32 firmware (bundles
pupremote.pyandlpf2.py) - OpenMV (copy
pupremote.pyandlpf2.pyonto the board) - Pybricks hubs (use the smaller
pupremote_hub.pywhen space matters) - Other MicroPython-capable boards with LPF2 physical connection
- The firmware already contains
pupremote.pyandlpf2.py. - Copy an example (for instance
examples/esp32_dummy_data/pup_demo_esp32.py) to the board asmain.py. - Connect the LMS-ESP32 to your hub (e.g., SPIKE Prime port A) and reboot.
- Copy
src/pupremote.pyandsrc/lpf2.pyto the OpenMV board. - Deploy an example such as
examples/openmv_simple/main.py. - Run from the OpenMV IDE or reboot to start.
- Copy
src/pupremote_hub.pyto the hub (use this smaller file instead of the fullpupremote.py). - Upload an example such as
examples/openmv_simple/pybricks_spike.pyand adjust imports tofrom pupremote_hub import PUPRemoteHubif needed. - Run the program from the Pybricks app.
Copy to the LMS-ESP32 as main.py and reboot:
# examples/esp32_dummy_data/pup_demo_esp32.py
from pupremote import PUPRemoteSensor, SPIKE_ULTRASONIC
def msg(*argv):
return "demo"
p = PUPRemoteSensor(sensor_id=SPIKE_ULTRASONIC, power=True)
p.add_command('msg', "repr", "repr")
while True:
p.process()Copy src/pupremote.py and src/lpf2.py to the board, then run:
# examples/openmv_simple/main.py
from pupremote import PUPRemoteSensor, ESP32, SPIKE_ULTRASONIC
from time import sleep_ms
p = PUPRemoteSensor(sensor_id=SPIKE_ULTRASONIC, platform=ESP32)
p.add_channel('cntr', to_hub_fmt="b")
while True:
p.process()
p.update_channel('cntr', 1)
sleep_ms(20)Place src/pupremote_hub.py on the hub and reference it:
# examples/openmv_simple/pybricks_spike.py
from pupremote_hub import PUPRemoteHub
hub = PUPRemoteHub()
hub.add_channel('demo', to_hub_fmt="b")
while True:
hub.process()
hub.update_channel('demo', 1)- Async/Sync modes: Use
process()for synchronous polling in loops, orprocess_async()+call_multitask()for concurrent async operations with callback queues. - Pybricks has a known 32-byte packet limitation. Pass
max_packet_size=16when constructingPUPRemoteHuborPUPRemoteSensorto avoid checksum errors. - For Pybricks, prefer
pupremote_hub.pyto save space (it only containsPUPRemoteHub). - LMS-ESP32 firmware already includes dependencies; do not re-upload
pupremote.pyorlpf2.pythere.
src/core implementation (pupremote.py,pupremote_hub.py,lpf2.py)examples/runnable demos for LMS-ESP32, OpenMV, and Pybricksdocs/Sphinx docs with API referencesimg/project assets (logo)
- Target runtime is MicroPython; verify every proposed import exists there (e.g.,
inspectis unavailable—avoid it). - Prefer
ustruct,uasyncio, andmicropython.const; gate CPython-only helpers behindtry/except ImportError. - Keep memory and dependency footprint small; avoid modules that pull in large transitive imports.
Contributions and protocol refinements are welcome. Please open an issue or PR with your ideas or projects built on PUPRemote.
GPL-3.0. See the LICENSE file for details.