Skip to content
BetaDE
Lesson 5 of 8Robotics with LEGO SPIKE

Robotics with LEGO SPIKE · Lesson 5 · 25 minutes

Buttons and gyro sensor

What you'll learn

  • Wait for a button press on the hub
  • Measure the heading with the gyro sensor
  • Calculate the shortest turn angle with modulo

The code in this lesson comes from the book “Python for Robots”, chapter 2.3, and runs unchanged in the simulator. German edition on Amazon ↗

The hub is listening

The SPIKE hub has buttons, and your program can react to them. The centre button is normally the stop button. So your code may use it itself, you move the stop function to a combination of buttons:

from pybricks.hubs import PrimeHub
from pybricks.parameters import Button, Color
from pybricks.tools import wait

hub = PrimeHub()
# The center button is normally the STOP button for the entire program.
# We free it up here so we can check it ourselves in the code -
# from now on, stopping requires the Center AND Bluetooth buttons together.
hub.system.set_stop_button((Button.CENTER, Button.BLUETOOTH))

print("Press the center button to start ...")

# As long as the center button is NOT pressed, we wait
while Button.CENTER not in hub.buttons.pressed():
    wait(50)   # short pause so the loop doesn't hog the processor

hub.light.on(Color.GREEN)
print("Let's go!")

Press Run and then the centre button on the virtual hub on the right.

The gyro sensor

The hub contains a gyro sensor. It notices how far the robot has turned. hub.imu.heading() returns the angle in degrees:

from pybricks.hubs import PrimeHub
from pybricks.tools import wait

hub = PrimeHub()

# Reset the heading to 0 - afterwards it keeps counting (even beyond 360)
hub.imu.reset_heading(0)

for _ in range(10):
    angle = hub.imu.heading()   # current heading in degrees
    print(f"Current heading: {angle} degrees")
    wait(500)

The robot is standing still here, so the angle stays at 0. In the next example it turns – and then it gets interesting.

The gyro doesn’t know 360 degrees

The angle just keeps counting: after one and a half turns it says 540, not 180. With the modulo operator % you work out the shortest way back:

from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase

hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)

def shortest_turn_angle(current, target):
    difference = (target - current) % 360
    if difference > 180:
        difference = difference - 360
    return difference

hub.imu.reset_heading(0)          # starting direction = 0 degrees

drive_base.turn(400)              # the robot turns more than a full circle
# ... here your program would do something else ...

# Back to the starting direction - by the shortest way:
back = shortest_turn_angle(hub.imu.heading(), 0)
drive_base.turn(back)

(target - current) % 360 always gives a value between 0 and 359. If it’s bigger than 180, it’s shorter to turn the other way – that’s why we subtract 360.

Remember: Never compare angles with ==. The gyro often gives decimals and almost never hits a value exactly. Check a range instead, e.g. abs(difference) < 2.

Your task

Your turn

The robot turns away with drive_base.turn(250). Then bring it back to its starting direction by the shortest way. Use hub.imu.heading() and a function shortest_turn_angle(current, target). Tip: from 250 degrees back to 0 it's shorter to turn another 110 degrees than to turn 250 degrees back.

from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase

hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)

def shortest_turn_angle(current, target):
    # calculate the shortest angle (between -180 and 180)
    return 0

hub.imu.reset_heading(0)
drive_base.turn(250)

# back to the starting direction:

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

from pybricks.hubs import PrimeHub
from pybricks.pupdevices import Motor
from pybricks.parameters import Port, Direction
from pybricks.robotics import DriveBase

hub = PrimeHub()
left_motor = Motor(Port.A, positive_direction=Direction.COUNTERCLOCKWISE)
right_motor = Motor(Port.B, positive_direction=Direction.CLOCKWISE)
drive_base = DriveBase(left_motor, right_motor, wheel_diameter=56, axle_track=114)

def shortest_turn_angle(current, target):
    difference = (target - current) % 360
    if difference > 180:
        difference = difference - 360
    return difference

hub.imu.reset_heading(0)
drive_base.turn(250)

# back to the starting direction:
back = shortest_turn_angle(hub.imu.heading(), 0)
drive_base.turn(back)