STARLEAP / SPIKE PRIME PYTHON / 2026–27

Build 2026.09.22-090630-0400

On this page

Project 20: Smart Bike Trip Computer

You need: a LEGO SPIKE Prime set, the Smart Bike (Bike and Biker), motors C and E, support wheels, the SPIKE App Python editor, a ruler or tape measure, and removable floor markers. Finish Project 19 first. Use a clear, level floor.

Make a function that accepts centimetres, calculates motor degrees, and reports an estimated trip distance. A calculation predicts travel; it does not measure the floor distance. You will measure the real result separately.

Stop before editing. Gray lines show existing code. Keep imports and the final start call unless a step says to change them.

Step 1: Measure One Motor Turn

Create a new Python project named Bike Trip Computer. Type this starter:

CODE
from hub import port
import motor_pair
import runloop

bike_motor = port.C
rider_motor = port.E

async def main():
    motor_pair.pair(motor_pair.PAIR_1, bike_motor, rider_motor)
    await motor_pair.move_for_degrees(
        motor_pair.PAIR_1, 360, 0, velocity=150)
    motor_pair.stop(motor_pair.PAIR_1)

runloop.run(main())

bike_motor names the bike motor on C; rider_motor names the rider motor on E. Define these port variables after the imports, before any functions. Use the names in commands so the code tells you which parts it controls.

Mark where a fixed point on the bike starts. Run the program, then measure how far that same point moved forward. Reset the bike to the mark and repeat twice. Tell your teacher the three measurements in centimetres.

One motor turn is not necessarily one wheel turn. The build's gearing and wheels matter, so use your measured travel rather than borrowing another robot's wheel circumference.

Step 2: Store Your Calibration

Add these assignments after the imports. The numbers below are an example: replace all three with your own measurements before running again.

CODE
trial_1 = 20.0
trial_2 = 21.0
trial_3 = 19.0
cm_per_turn = (trial_1 + trial_2 + trial_3) / 3

Temporarily replace main() to check the calculation without movement:

EDIT VIEW · Gray = already there; dark = add or change
async def main():
    motor_pair.pair(motor_pair.PAIR_1, bike_motor, rider_motor)
    motor_pair.stop(motor_pair.PAIR_1)
    print("centimetres per motor turn")
    print(cm_per_turn)

Run the program. The example measurements average 20.0; yours may differ. Parentheses make Python add the three distances before dividing by three. Your calibration must be greater than zero.

Step 3: Calculate Motor Turns

Add the new target beside your calibration, above main():

EDIT VIEW · Gray = already there; dark = add or change
cm_per_turn = (trial_1 + trial_2 + trial_3) / 3
requested_cm = 50

Replace the two print lines inside main() with:

CODE
    turns = requested_cm / cm_per_turn
    print("motor turns needed")
    print(turns)

Run the program. The bike stays stopped. With the example calibration, 50 / 20 gives 2.5 motor turns. Explain why a shorter distance per turn needs more turns to reach the same target.

Step 4: Convert Turns To Degrees

Keep the turns calculation. Replace its two print lines:

EDIT VIEW · Gray = already there; dark = add or change
    turns = requested_cm / cm_per_turn
    degrees = int(turns * 360)
    print("motor degrees needed")
    print(degrees)

Run the program. The example now gives 900 degrees. The motor command takes an integer degree amount. int() drops the fractional part of this positive calculation; it does not round to the nearest integer.

Step 5: Put The Calculation In A Function

Add this ordinary function above main(). It calculates a number and does not move the bike:

CODE
def degrees_for_cm(distance_cm):
    turns = distance_cm / cm_per_turn
    return int(turns * 360)

Replace main() with this version:

EDIT VIEW · Gray = already there; dark = add or change
async def main():
    motor_pair.pair(motor_pair.PAIR_1, bike_motor, rider_motor)
    motor_pair.stop(motor_pair.PAIR_1)
    degrees = degrees_for_cm(requested_cm)
    print(degrees)

Run the program. The same answer should print. distance_cm receives the argument; return sends the calculated number back. This function does not need await because it only does arithmetic.

Step 6: Reject Invalid Requests

Replace degrees_for_cm() with this version. The early returns happen before the division:

EDIT VIEW · Gray = already there; dark = add or change
def degrees_for_cm(distance_cm):
    if cm_per_turn <= 0:
        return 0
    if distance_cm <= 0 or distance_cm > 100:
        return 0
    turns = distance_cm / cm_per_turn
    return int(turns * 360)

Run the program with requested_cm set to 0, then 101. Both should print 0. or means either invalid distance is enough. Try 50 again and keep that value. The 100 cm maximum is our classroom test limit, not a universal motor limit.

Fix it: If your real calibration is zero or negative, measure again. Do not invent a positive number just to pass the check.

Step 7: Add A Ride That Uses The Calculation

Add this async function after degrees_for_cm() and before main():

CODE
async def ride_cm(distance_cm):
    degrees = degrees_for_cm(distance_cm)
    if degrees <= 0:
        print("no ride: check distance and calibration")
        return 0
    await motor_pair.move_for_degrees(
        motor_pair.PAIR_1, degrees, 0, velocity=150)
    motor_pair.stop(motor_pair.PAIR_1)
    return degrees / 360 * cm_per_turn

Replace main():

EDIT VIEW · Gray = already there; dark = add or change
async def main():
    motor_pair.pair(motor_pair.PAIR_1, bike_motor, rider_motor)
    motor_pair.stop(motor_pair.PAIR_1)
    estimated_cm = await ride_cm(requested_cm)
    print("estimated centimetres")
    print(estimated_cm)

Before you run: Have your teacher check the calibration and clear at least the requested travel distance. Start at the mark with requested_cm = 50.

Run the program. Measure the actual travel. The returned value is estimated from commanded degrees and your calibration, including the effect of dropping a fractional degree. It is not a sensor measurement or proof that a stalled bike reached its target.

Step 8: Add Two Legs To A Trip

Replace only main(). Keep both helper functions:

EDIT VIEW · Gray = already there; dark = add or change
async def main():
    motor_pair.pair(motor_pair.PAIR_1, bike_motor, rider_motor)
    motor_pair.stop(motor_pair.PAIR_1)
    total_cm = 0
    leg_cm = await ride_cm(30)
    total_cm = total_cm + leg_cm
    await runloop.sleep_ms(1000)
    leg_cm = await ride_cm(20)
    total_cm = total_cm + leg_cm
    print("estimated trip centimetres")
    print(total_cm)

The old requested_cm assignment can stay, but this version uses the two call arguments instead. Point to 30 and 20 before running.

Run the program on a clear 50 cm route. It should ride, pause, ride again and report a total near 50. total_cm is an accumulator: it starts at zero and adds each returned estimate. Restarting the program resets the trip.

Step 9: Check The Estimate Against The Floor

Measure the complete two-leg trip. Compare actual travel with the printed estimate. Run three trials from the same starting mark.

Change only the velocity=150 in ride_cm() to velocity=100, then repeat. The calculated degrees stay the same. Discuss whether floor grip, stopping, or speed changed the real result. Restore 150 before saving.

Change the second call to ride_cm(0) and run once. It should report the invalid request, skip that leg, and add zero. Put the second call back to 20 afterwards.

Teacher Check

Show your teacher:

Save your program. Continue to Project 17: Smart Bike Slope Lab. Keep this calibration only while using the same build and wheels on a similar surface.