Skip to content

Instantly share code, notes, and snippets.

@dlech
Last active March 28, 2023 14:09
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save dlech/fa48f9b2a3a661c79c2c5880684b63ae to your computer and use it in GitHub Desktop.
Save dlech/fa48f9b2a3a661c79c2c5880684b63ae to your computer and use it in GitHub Desktop.
Example of LEGO SPIKE Prime scratch program translated to MicroPython using generator functions as coroutines
# spike.control.wait_for_seconds doesn't allow decimal points
# so we replace it with the standard MicroPython sleep function instead
from utime import sleep as wait_for_seconds
from utime import ticks_diff, ticks_ms
# See https://docs.micropython.org/en/latest/library/utime.html for more info on the utime module
from spike import (PrimeHub, LightMatrix, Button, StatusLight, ForceSensor, MotionSensor,
Speaker, ColorSensor, App, DistanceSensor, Motor, MotorPair)
from spike.control import wait_until
# spike.control.Timer doesn't allow decimal points
class Timer():
"""Replacement Timer class that allows decimal points so we can measure times of less than one second."""
def __init__(self):
self.start_ticks = 0
def now(self):
"""Returns the time in seconds since the timer was last reset."""
return ticks_diff(ticks_ms(), self.start_ticks) / 1000
def reset(self):
"""Resets the timer."""
self.start_ticks = ticks_ms()
hub = PrimeHub()
background_beep_timer = Timer() # For use in background_beep()
background_image_timer = Timer() # For use in background_image_delay()
def background_beep(note, seconds):
"""Coroutine version of hub.speaker.beep().
Parameters
----------
note (int): The MIDI note number to play.
seconds (float): The duration of the beep in seconds.
"""
# this timer is used to measure how long it has been since we started
# the beep
background_beep_timer.reset()
# hub.speaker.beep() would block the rest of the program, so we have
# to use hub.speaker.start_beep() instead
hub.speaker.start_beep(note)
# then we use a timer to keep checking to see how long it has been since
# we started the beep
while background_beep_timer.now() < seconds:
# if it has not been long enought yet, then we "yield" to let the
# rest of the program run for a while then we will come back here
# later and check again
yield
# After it has been long enough, we have to stop the beep
hub.speaker.stop()
def play_melody():
"""Plays a melody in the background."""
while True:
# since background_beep() uses "yield" instead of "return", we have to
# say "yield from" here to keep calling background_beep() until it is done
yield from background_beep(72, 0.2)
yield from background_beep(74, 0.2)
yield from background_beep(76, 0.2)
yield from background_beep(77, 0.2)
yield from background_beep(79, 0.2)
yield from background_beep(81, 0.2)
yield from background_beep(83, 0.2)
yield from background_beep(84, 0.2)
def background_image_delay(seconds):
"""Delays for ``seconds``."""
background_image_timer.reset()
while background_image_timer.now() < seconds:
yield
def animate_image():
"""Shows different images in the background."""
while True:
hub.light_matrix.show_image('HAPPY')
yield from background_image_delay(0.5)
hub.light_matrix.show_image('SAD')
yield from background_image_delay(0.5)
# Since the play_melody() and animate_image() functions use "yield" instead of
# "return", the functions don't actually run here when we *call* them. Instead,
# they create *generator* objects that will be used to run the funcitons one
# "yield" at a time.
melody_generator = play_melody()
animation_generator = animate_image()
# Task runner
while True:
# perform the "next" step in the play_melody() function by running it
# to the next "yield" statement
next(melody_generator)
# perform the "next" step in the animate_image() function
next(animation_generator)
# wait a *short* time before trying the next step
wait_for_seconds(0.01)
@dlech
Copy link
Author

dlech commented Sep 1, 2020

@MarcosPompilio
Copy link

still can't understand how it works😥

@MarcosPompilio
Copy link

hello I'm a first lego league competitor and I wanted to know if this is the only way to perform actions simultaneously in micropython with the spike prime, wouldn't it have a similar function to the scratch broadcast block? Another thing I can apply your method to make my robot walk and turn a separate motor? (If any of these possibilities are really working, could you explain me how to do it? thanks for your attention ;) )

@mcajkovs
Copy link

Is there also alternative for EV3? Thanks

@dlech
Copy link
Author

dlech commented Sep 18, 2022

For EV3, have a look at pybricks/support#666 and pybricks/support#99.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment