Skip to content

Commit

Permalink
Merge pull request #4 from Vinz1911/feature/refactoring-2021
Browse files Browse the repository at this point in the history
refactoring + compatibility for latest spike/ri firmware
  • Loading branch information
Vinz1911 authored Aug 23, 2021
2 parents ff331c7 + c1359bc commit b4956fe
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 695 deletions.
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

0 comments on commit b4956fe

Please sign in to comment.