STARLEAP / SPIKE PRIME PYTHON / 2026–27
Build 2026.09.22-090630-0400
On this page
- Goal
- Step 1: Start With A Fresh Code Outline
- Step 2: Keep The Robot Still
- Step 3: Define A Beep With A Fixed Pause
- Step 4: Call The Helper Once
- Step 5: Hear The Fixed-Pause Limit
- Step 6: Give The Helper A Parameter
- Step 7: Let One Call Choose A Shorter Pause
- Step 8: Start With Two Distance Choices
- Step 9: Add The Stop Choice
- Step 10: Add One Warning Zone
- Step 11: Split Off A Fast-Beep Zone
- Step 12: Add The Middle Zone
- Check The Whole Chain
- Step 13: Add Movement To The Tested Choices
- Step 14: Tune A Slow Approach
- Physical Challenge: Alarm Parking
- Teacher Check
- Before You Put The Kit Away
Project 09: Parking Approach Alarm
You need: a LEGO SPIKE Prime set and the SPIKE App Python editor. Keep Driving Base 2 from Project 08, including its forward-facing distance-sensor attachment: left wheel motor on Port C, right wheel motor on Port D, and distance sensor on Port F. Keep the same wall or bin target.
Goal
Make a parking alarm with slow, medium, and fast beeps as the robot approaches a wall. First test the sounds with the robot still. Then build the distance choices one at a time. Add movement after those choices work.
Step 1: Start With A Fresh Code Outline
Create a new Python project in the SPIKE App named Parking Approach Alarm. Keep Driving Base 2 assembled from Project 08. Type this outline into the new project:
from hub import light_matrix, port
import distance_sensor
import motor_pair
import runloop
left_wheel = port.C
right_wheel = port.D
distance_port = port.F
async def main():
pass
runloop.run(main())
The imports make our tools available. The three port variables match the robot's wires. main() is where we will add instructions, and the final line starts it.
pass is a placeholder that does nothing. Python needs something inside the function while we build the program, so pass lets this outline run.
Run the program.
Observe: The program ends without moving or beeping. That is expected. If you see an error, check the imports, capitalization, colon, and the four spaces before pass.
Using the code: gray lines are already there; dark lines are the edit. Keep the shown indentation: four spaces inside main(), eight inside its loop, and twelve inside a choice in that loop. A line that moves back to the left ends the indented part above it.
Before the next test: lift the drive wheels. Stop the program before moving wires or changing the build. Save after each successful step so you can continue next time.
Step 2: Keep The Robot Still
Replace only the pass line with these two instructions inside main(). Keep the imports and port variables above it and the final runloop.run(main()) below it.
async def main():
motor_pair.pair(motor_pair.PAIR_1, left_wheel, right_wheel)
motor_pair.stop(motor_pair.PAIR_1)
The first line groups the two wheel motors as a pair. The second tells that pair to stop. We will keep this stop at the beginning of main() as we add the alarm.
Run the program.
Observe: The wheels stay still. The program ends without beeping. This gives us a quiet place to test the new sound helper.
Step 3: Define A Beep With A Fixed Pause
Change the first import to include sound:
from hub import light_matrix, port, sound
Add this helper above main() and below your variables. Start async def at the left edge. Its two instructions begin four spaces in.
async def warning_beep():
await sound.beep(700, 80, 100)
await runloop.sleep_ms(700)
The helper makes one beep, then waits 700 milliseconds. The empty parentheses mean we are not asking a call to supply a value yet. Keep async and await: this helper waits for the sound and the pause to finish.
Run the program.
Observe: You still hear nothing. Defining a function gives it a name and instructions. We have not called it yet.
Step 4: Call The Helper Once
Add one line at the end of main(), four spaces in:
async def main():
motor_pair.pair(motor_pair.PAIR_1, left_wheel, right_wheel)
motor_pair.stop(motor_pair.PAIR_1)
await warning_beep()
Run the program.
Observe: You hear one beep. The call runs the helper's instructions. After its pause, the program ends. await means this part of the program waits for the helper to finish.
Fix it: If there is no sound, check that the call is inside main() and that the final runloop.run(main()) line is still at the left edge.
Step 5: Hear The Fixed-Pause Limit
Add two more calls directly below the first, at the same indentation:
await warning_beep()
await warning_beep()
await warning_beep()
Run the program.
Observe: You hear three beeps with the same pause between them. Each call runs the same two instructions.
In the helper, change only await runloop.sleep_ms(700) to await runloop.sleep_ms(150). Leave the sound.beep line unchanged.
Run the program again.
Observe: Both pauses between the three beeps are shorter. Changing the fixed number changes every call. We need a way for one call to choose a long pause and another call to choose a short pause.
Change the helper's pause back to 700, then run the program once more to hear the original spacing.
Step 6: Give The Helper A Parameter
We can give each call control of the pause. Make both edits below before running.
First, add the name gap_ms inside the helper's parentheses. Replace the fixed pause number with that name:
async def warning_beep(gap_ms):
await sound.beep(700, 80, 100)
await runloop.sleep_ms(gap_ms)
gap_ms is a parameter: a name for a value the helper receives when it is called. Here it controls the pause after a beep, in milliseconds. It does not change the sound's pitch or length.
Next, put 700 inside the parentheses of all three calls in main():
await warning_beep(700)
await warning_beep(700)
await warning_beep(700)
700 is an argument: the value this call supplies. For warning_beep(700), the helper uses 700 as gap_ms, so its last line waits 700 milliseconds.
Run the program.
Observe: The beeps sound just like Step 5's original version. We changed how the pause is supplied; each call still supplies the same value.
Fix it: If the program reports a missing argument, find a leftover warning_beep() call with empty parentheses. It now needs a pause value.
Step 7: Let One Call Choose A Shorter Pause
Change only the middle call's argument:
await warning_beep(700)
await warning_beep(150)
await warning_beep(700)
Predict: Which two beeps will be closer together?
Run the program.
Observe: There is a long pause after the first beep and a short pause after the second. The third call also waits after its beep, but there is no fourth beep to mark the end of that pause.
The helper is defined once. Each call supplies its own argument. Try 350 in the middle call, then run the program again. That pause should fall between the long and short pauses you just heard. Keep the helper itself unchanged.
Step 8: Start With Two Distance Choices
Keep the helper and its parameter. Replace only main() with this version. This replaces the three sound-test calls with our first sensor loop. Keep your imports, variables, and final start line.
async def main():
motor_pair.pair(motor_pair.PAIR_1, left_wheel, right_wheel)
motor_pair.stop(motor_pair.PAIR_1)
while True:
distance = distance_sensor.distance(distance_port)
print(distance)
if distance == -1:
motor_pair.stop(motor_pair.PAIR_1)
print("no reading")
else:
print("valid reading")
await runloop.sleep_ms(100)
if tests the first condition. else handles the remaining case. Here, -1 means the sensor has no usable reading; it is not a distance to the wall.
Run the program and move a flat target in front of the sensor.
Observe: A usable distance prints valid reading. When the sensor reports -1, the program prints no reading. The robot stays still and silent. Stop the program before the next edit.
Indentation check: if and else line up eight spaces in. Their instructions begin twelve spaces in. The final pause moves back to eight spaces, so it runs after either choice on each trip through the loop. Keep it there in the next steps.
Step 9: Add The Stop Choice
First, define the stopping distance. Add the dark line directly below distance_port, above the helper and outside both functions. Start it at the left edge, with no indentation. Keep the helper's existing body below its gray header.
distance_port = port.F
too_close = 80
async def warning_beep(gap_ms):
too_close is a variable storing our stopping distance: 80 millimeters. The assignment gives the name a value; the condition below reads that value.
Next, insert this new elif just before the existing else. Change the text inside that else to clear. Keep the first if distance == -1 block above this edit. Make both edits before running.
elif distance <= too_close:
motor_pair.stop(motor_pair.PAIR_1)
print("stop")
light_matrix.show_image(light_matrix.IMAGE_YES)
break
else:
print("clear")
await runloop.sleep_ms(100)
elif means "otherwise, if." This condition is tested only when the first if was false. <= means "less than or equal to," so this branch handles a valid distance of 80 millimeters or less. break ends the loop after the success image.
Run the program with the target farther than 80 millimeters away. Move it slowly closer while watching the printed distance.
Observe: The console changes from clear to stop at 80 or less. The success image appears and the loop ends. Move the target back and run the program again for another test.
Step 10: Add One Warning Zone
First, add too_far below too_close in the same variable block. It stores 300 millimeters, the outer edge of the warning zone. Keep these assignments at the left edge, above the helper.
too_close = 80
too_far = 300
async def warning_beep(gap_ms):
Insert one elif directly before the final else. Keep the no-reading and stop choices above it:
elif distance <= too_far:
print("slow beep")
await warning_beep(700)
else:
print("clear")
await runloop.sleep_ms(100)
The stop choice already catches 80 or less. This new choice therefore handles distances above 80 and up to 300. The remaining else handles distances above 300.
Run the program. Start beyond 300, then move the target to about 250 millimeters.
Observe: The robot stays still. clear is silent; slow beep repeats the helper with a 700-millisecond pause. Move to 80 or less to check that the stop choice still ends the loop.
Step 11: Split Off A Fast-Beep Zone
First, add close_distance between the existing cutoffs above the helper. It stores 130 millimeters, the outer edge of the fast-beep zone. Only the dark assignment is new:
too_close = 80
close_distance = 130
too_far = 300
Insert a new elif immediately before elif distance <= too_far. Keep the existing slow-beep branch below it:
elif distance <= close_distance:
print("fast beep")
await warning_beep(150)
elif distance <= too_far:
print("slow beep")
await warning_beep(700)
Run the program with the target around 100 millimeters away. Then move it to about 250.
Observe: Around 100, you get fast beeps. Around 250, you still get slow beeps. The stop and clear choices still work.
Why this order matters: A distance of 100 is less than both 130 and 300. Python uses the first true branch in this chain and skips the later branches for that reading. The smaller cutoff must come first. Keep elif aligned with if; adding a separate if would start a second chain.
Step 12: Add The Middle Zone
First, add medium_distance to the same variable block above the helper. It stores 200 millimeters, the outer edge of the medium-beep zone. Your four cutoff definitions should now look like this:
too_close = 80
close_distance = 130
medium_distance = 200
too_far = 300
These four numbers are distances in millimeters. The arguments in warning_beep(...) are pauses in milliseconds; they control something different.
Next, insert one more branch between the fast-beep and slow-beep branches:
elif distance <= close_distance:
print("fast beep")
await warning_beep(150)
elif distance <= medium_distance:
print("medium beep")
await warning_beep(350)
elif distance <= too_far:
print("slow beep")
await warning_beep(700)
Run the program. Try readings near 100, 160, and 250 millimeters.
Observe: You hear fast, medium, and slow spacing. Only one branch responds to each reading. A smaller argument gives a shorter pause after the beep.
Check The Whole Chain
Read your choices from top to bottom. There is one if, four elif lines, and one final else. They should appear in this order:
| Reading in millimeters | First matching choice | Result |
|---|---|---|
| -1 | if distance == -1 | No reading; stop motors, no beep |
| 0 through 80 | elif distance <= too_close | Stop, success image, end loop |
| 81 through 130 | elif distance <= close_distance | Fast beep; argument 150 |
| 131 through 200 | elif distance <= medium_distance | Medium beep; argument 350 |
| 201 through 300 | elif distance <= too_far | Slow beep; argument 700 |
| Above 300 | else | Clear; no beep |
Predict: For a reading of 160, which conditions are false before Python reaches the matching branch? Which argument reaches gap_ms?
Run the program to check. Then test the stop choice and a missing reading. Keep the final 100-millisecond loop pause; during a warning, it comes after the helper's beep and chosen pause.
Step 13: Add Movement To The Tested Choices
Before you run: keep the wheels lifted for this edit. Leave both motor_pair.stop lines in the no-reading and stop choices. Leave the stop at the beginning of main() too.
Add one movement line after the print in each of the four choices below. These are separate edits to branches you already tested. Make all four edits before running; keep the rest of the chain and the final loop pause.
Inside the fast-beep choice:
print("fast beep")
motor_pair.move(motor_pair.PAIR_1, 0, velocity=80)
await warning_beep(150)
Inside the medium-beep choice:
print("medium beep")
motor_pair.move(motor_pair.PAIR_1, 0, velocity=120)
await warning_beep(350)
Inside the slow-beep choice:
print("slow beep")
motor_pair.move(motor_pair.PAIR_1, 0, velocity=160)
await warning_beep(700)
Inside the final else:
print("clear")
motor_pair.move(motor_pair.PAIR_1, 0, velocity=200)
Run the program with the wheels lifted and move the target through the zones.
Observe: The wheels turn forward and slow down as the target gets closer. A reading of 80 or less, or a missing reading, stops them. If a direction is wrong, stop and check the Project 08 wiring and wheel test before continuing.
Step 14: Tune A Slow Approach
Stop the program. Put the robot on a clear floor with the sensor facing the wall. Run the program and keep the stop control ready.
The robot approaches forward. The beeps imitate a reversing vehicle's warning pattern. The wheels keep moving during each beep and pause; the sensor is checked again on the next trip through the loop. A long pause lets the robot travel farther before that next check.
Change one thing at a time, then run the program after each change:
- To change where a zone begins, change its distance cutoff. Keep
too_close < close_distance < medium_distance < too_far. - To change how quickly that zone beeps, change the argument in its
warning_beep(...)call. Keep the helper'sgap_msparameter as it is. - To slow the robot's approach, lower the velocity in the relevant branch. If it travels too far between checks, shorten the pause or slow the robot and test again with the wheels lifted first.
Physical Challenge: Alarm Parking
Use the Parking Wall setup from the Challenge Setups sheet.
- Approach silently while the wall is beyond the warning zone.
- Show slow, medium, then fast beeps as the robot gets closer.
- Stop before touching the wall.
- Make one purposeful adjustment and repeat the approach three times.
Teacher Check
Show your teacher:
- the helper's
gap_msparameter and one call's argument; explain how that value reachessleep_ms - why the fixed-pause helper could not give different calls different pauses
- which branch runs for a reading near 160, and why later branches are skipped
- slow, medium, and fast beeps; a stop before the wall; and a stop when the sensor has no reading
- one cutoff or argument you changed and what changed on the robot
Before You Put The Kit Away
Save your program. Stop it before moving wires or changing the build. Keep your place for next time.