Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactoring + compatibility for latest spike/ri firmware #4

Merged
merged 1 commit into from
Aug 23, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

77 changes: 63 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,68 @@
# PrimePowerUP
# PrimePoweredUP

`PrimePoweredUP` contains a library for Lego Spike Prime to connect to PoweredUP Remote (Handset) over BLE.
The core is based on the MicroPython ubluetooth low level api.

### Compatibility
- Works with the latest version of `Spike Prime` and `Mindstorms Robot Inventor`.

### Description
- the library use lot of memory. i recommend to pre compile the library from `remote/control` and install it on the prime hub.
a very good way to do that is using this awesome tool: [Spike Tools](https://github.com/XenseEducation/spiketools-release/releases)
pre compiled library can also be downloaded in releases section: [Pre-Compiled Library](https://github.com/Vinz1911/PrimePowerUP/releases)

- there are two examples in `examples` folder. The first one shows how to light up dot's on Prime Hub and the second one
shows how to control a motor pair with the remote. examples are created by using the control.py installed as pre compiled lib
(it's also possible to copy all together and load it on the hub)

### Known Problems:
- the ubluetooth class has some problems with event loop based functions from Lego. This means, if you run a event loop based
function within the button pressed callback, the entire hub will freeze. This is currently not possible to fix that, maybe with
a new firmare which supports uasyncio library. **Event based functions ?!** are functions like playing sound until end, wait for or
motor functions like run_to_position or run_for_degrees and so on.
- the library is build for the use inside the `Python VM`. This means you need the advanced Python setup for the Spike Prime.
- for an easy start with the advanced Python setup, it's recommended to use VSCode with this plugin: [Spike Prime/RI Extension](https://marketplace.visualstudio.com/items?itemName=PeterStaev.lego-spikeprime-mindstorms-vscode).
- **WARNING: LIBRARY DOES NOT WORK WITH THE REGULAR PYTHON SETUP**
- examples can be found in `./examples` directory.

### Usage
- The pre-compiled library is inside of the `./remote` directory, it's recommended to copy the library inside the `./spike` directory
of the Spike Prime. You can do this by using a script or with [rshell](https://github.com/dhylands/rshell).

```bash
# example using rshell
Connecting to /dev/cu.usbmodem3382397933381 (buffer-size 512)...
Trying to connect to REPL connected
Retrieving sysname ... LEGO Technic Large Hub
...
Welcome to rshell. Use Control-D (or the exit command) to exit rshell.

# copy file to hub
/remote> cp ./remote.mpy /pyboard/spike/
```
#### Example
```python
from runtime import VirtualMachine
from spike import PrimeHub, MotorPair
from spike.remote import Remote, Buttons
from util.print_override import spikeprint as print

# create remote
remote = Remote()


async def on_start(vm, stack):
print("connecting...")
await remote.connect() # wait for connecting establishment
print("connected")

while True:
buttons = remote.pressed() # read pressed buttons
print(buttons) # Output is a tuple for example: (LEFT_PLUS, RIGHT_PLUS, CENTER)
yield


async def on_cancel(vm, stack):
remote.cancel() # disconnect if the program exit's


def setup(rpc, system, stop):
vm = VirtualMachine(rpc, system, stop, "3f157bda4908")
vm.register_on_start("f76afdd318a1", on_start)
vm.register_on_button("accda9ebca74", on_cancel, "center", "pressed")
return vm
```

### Known Issues:
- The library uses an async connection process, this is why we need the python vm for the usage. performance is also better.
- The library uses internally ble notification service, sometimes the hub needs a restart to make this work (if tuple is empty on button press).
- I didn't found a good way to disconnect the remote if you reach the end of a program (there is currently no way to run `cleanup` code on program's end). fallback solution is to use the `Start/Stop` button.
So just for clarification: **remote needs to be reconnected on program start**. if your program is exited and the remote is still connected
you need to unpair the remote by holding the green button until it's unpaired.
83 changes: 26 additions & 57 deletions examples/dots/dots.py
Original file line number Diff line number Diff line change
@@ -1,65 +1,34 @@
from spike import PrimeHub, LightMatrix, Button, StatusLight, ForceSensor, MotionSensor, Speaker, ColorSensor, App, DistanceSensor, Motor, MotorPair
from spike.control import wait_for_seconds, wait_until, Timer
from remote.control import PoweredUPRemote, PoweredUPColors, PoweredUPButtons
from runtime import VirtualMachine
from spike import PrimeHub
from spike.remote import Remote, Buttons
from util.print_override import spikeprint as print

"""
LEGO(R) SPIKE PRIME + POWERED UP
--------------------------------
# create remote
remote = Remote()

This is a basic example:
This example let light up different dot's on
a prime/inventor hub for different buttons pressed
on the powered up remote
"""

async def on_start(vm, stack):
hub = PrimeHub()
print("connecting...")
await remote.connect()
print("connected")
hub.status_light.on('blue')

def on_connect():
"""
callback on connect
"""
hub.status_light.on("blue")
while True:
buttons = remote.pressed()
if Buttons.LEFT in buttons: hub.light_matrix.set_pixel(0, 0, brightness=100)
if Buttons.LEFT not in buttons: hub.light_matrix.set_pixel(0, 0, brightness=0)
if Buttons.RIGHT in buttons: hub.light_matrix.set_pixel(0, 1, brightness=100)
if Buttons.RIGHT not in buttons: hub.light_matrix.set_pixel(0, 1, brightness=0)
yield


def on_disconnect():
"""
callback on disconnect
"""
hub.status_light.on("white")
async def on_cancel(vm, stack):
remote.cancel()


def on_button(button):
"""
callback on button press
:param button: button id
"""
hub.light_matrix.off()
if button == PoweredUPButtons.LEFT_PLUS:
hub.light_matrix.set_pixel(0, 0, brightness=100)
elif button == PoweredUPButtons.LEFT_RED:
hub.light_matrix.set_pixel(1, 0, brightness=100)
elif button == PoweredUPButtons.LEFT_MINUS:
hub.light_matrix.set_pixel(2, 0, brightness=100)
elif button == PoweredUPButtons.RIGHT_PLUS:
hub.light_matrix.set_pixel(3, 0, brightness=100)
elif button == PoweredUPButtons.RIGHT_RED:
hub.light_matrix.set_pixel(4, 0, brightness=100)
elif button == PoweredUPButtons.RIGHT_MINUS:
hub.light_matrix.set_pixel(0, 1, brightness=100)
elif button == PoweredUPButtons.LEFT_PLUS_RIGHT_PLUS:
hub.light_matrix.set_pixel(0, 2, brightness=100)
elif button == PoweredUPButtons.RELEASED:
hub.light_matrix.off()
else:
hub.light_matrix.off()


# set up hub
hub = PrimeHub()

# create remote and connect
remote = PoweredUPRemote()
remote.debug = True
remote.on_connect(callback=on_connect)
remote.on_disconnect(callback=on_disconnect)
remote.on_button(callback=on_button)
remote.connect()
def setup(rpc, system, stop):
vm = VirtualMachine(rpc, system, stop, "3f157bda4908")
vm.register_on_start("f76afdd318a1", on_start)
vm.register_on_button("accda9ebca74", on_cancel, "center", "pressed")
return vm
106 changes: 40 additions & 66 deletions examples/driving/driving.py
Original file line number Diff line number Diff line change
@@ -1,66 +1,40 @@
from spike import PrimeHub, LightMatrix, Button, StatusLight, ForceSensor, MotionSensor, Speaker, ColorSensor, App, DistanceSensor, Motor, MotorPair
from spike.control import wait_for_seconds, wait_until, Timer
from remote.control import PoweredUPRemote, PoweredUPColors, PoweredUPButtons

"""
LEGO(R) SPIKE PRIME + POWERED UP
--------------------------------

This is a basic example:
This example let control a motor pair
with the powered up remote
"""


def on_connect():
"""
callback on connect
"""
hub.status_light.on("blue")


def on_disconnect():
"""
callback on disconnect
"""
hub.status_light.on("white")
motor_pair.stop()


def on_button(button):
"""
callback on button press

:param button: button id
"""
if button == PoweredUPButtons.RIGHT_PLUS:
motor_pair.start(speed=75)
elif button == PoweredUPButtons.RIGHT_MINUS:
motor_pair.start(speed=-75)
elif button == PoweredUPButtons.LEFT_PLUS_RIGHT_PLUS:
motor_pair.start(steering=-45, speed=75)
elif button == PoweredUPButtons.LEFT_MINUS_RIGHT_PLUS:
motor_pair.start(steering=45, speed=75)
elif button == PoweredUPButtons.LEFT_MINUS_RIGHT_MINUS:
motor_pair.start(steering=45, speed=-75)
elif button == PoweredUPButtons.LEFT_PLUS_RIGHT_MINUS:
motor_pair.start(steering=-45, speed=-75)
elif button == PoweredUPButtons.RELEASED:
motor_pair.stop()
else:
motor_pair.stop()


# set up hub
hub = PrimeHub()

# set up motors
motor_pair = MotorPair('A', 'B')
motor_pair.set_stop_action('coast')

# create remote and connect
remote = PoweredUPRemote()
remote.on_connect(callback=on_connect)
remote.on_disconnect(callback=on_disconnect)
remote.on_button(callback=on_button)
remote.connect()
from runtime import VirtualMachine
from spike import PrimeHub, MotorPair
from spike.remote import Remote, Buttons
from util.print_override import spikeprint as print

# create remote
remote = Remote()


async def on_start(vm, stack):
hub = PrimeHub()
pair = MotorPair('A', 'B')
pair.set_stop_action('coast')

print("connecting...")
await remote.connect()
print("connected")
hub.status_light.on('blue')

while True:
buttons = remote.pressed()
if buttons == (Buttons.RIGHT_PLUS,): pair.start(speed=65)
elif buttons == (Buttons.RIGHT_MINUS,): pair.start(speed=-65)
elif buttons == (Buttons.LEFT_MINUS, Buttons.RIGHT_PLUS): pair.start(speed=65, steering=-45)
elif buttons == (Buttons.LEFT_PLUS, Buttons.RIGHT_PLUS): pair.start(speed=65, steering=45)
elif buttons == (Buttons.LEFT_MINUS, Buttons.RIGHT_MINUS): pair.start(speed=-65, steering=-45)
elif buttons == (Buttons.LEFT_PLUS, Buttons.RIGHT_MINUS): pair.start(speed=-65, steering=45)
else: pair.stop()
yield


async def on_cancel(vm, stack):
remote.cancel()


def setup(rpc, system, stop):
vm = VirtualMachine(rpc, system, stop, "3f157bda4908")
vm.register_on_start("f76afdd318a1", on_start)
vm.register_on_button("accda9ebca74", on_cancel, "center", "pressed")
return vm
Loading