Ev3rstorm

Python code and building instructions for the LEGO MINDSTORMS EV3 Main models (31313).

Ev3rstorm
Image credit: LEGO

EV3RSTORM is the most advanced of the LEGO® MINDSTORMS® robots. Equipped with a blasting bazooka and a spinning tri-blade, EV3RSTORM is superior in both intelligence as well as in fighting power.


This program requires LEGO® EV3 MicroPython v2.0 downloadable at https://education.lego.com/en-us/support/mindstorms-ev3/python-for-ev3.

Ev3rstorm works as follows:

  • You can drive Ev3rstorm around with the IR beacon.

  • Ev3rstorm dances by turning by random angles on the spot when the Beacon button is pressed.

  • Ev3rstorm blasts his bazooka when his Touch Sensor is pressed. If you cover the Color Sensor then he will shoot upwards, or he will shoot downwards.

The code for the Ev3rstorm class is in ev3rstorm.py as follows:

from pybricks.hubs import EV3Brick
from pybricks.ev3devices import Motor, TouchSensor, ColorSensor
from pybricks.media.ev3dev import SoundFile
from pybricks.parameters import Button, Direction, Port, Stop

from random import randint

from rc_tank_util import RemoteControlledTank


class Ev3rstorm(RemoteControlledTank):
    WHEEL_DIAMETER = 26   # milimeters
    AXLE_TRACK = 102      # milimeters

    def __init__(
            self,
            left_track_motor_port: Port = Port.B,
            right_track_motor_port: Port = Port.C,
            bazooka_blast_motor_port: Port = Port.A,
            touch_sensor_port: Port = Port.S1,
            color_sensor_port: Port = Port.S3,
            ir_sensor_port: Port = Port.S4,
            ir_beacon_channel: int = 1):
        super().__init__(
            wheel_diameter=self.WHEEL_DIAMETER,
            axle_track=self.AXLE_TRACK,
            left_motor_port=left_track_motor_port,
            right_motor_port=right_track_motor_port,
            ir_sensor_port=ir_sensor_port,
            ir_beacon_channel=ir_beacon_channel)

        self.ev3_brick = EV3Brick()

        self.bazooka_blast_motor = \
            Motor(port=bazooka_blast_motor_port,
                  positive_direction=Direction.CLOCKWISE)

        self.touch_sensor = TouchSensor(port=touch_sensor_port)
        self.color_sensor = ColorSensor(port=color_sensor_port)

    def dance_randomly_if_ir_beacon_button_pressed(self):
        """
        Ev3rstorm dances by turning by random angles on the spot
        when the Beacon button is pressed
        """
        while Button.BEACON in \
                self.ir_sensor.buttons(channel=self.ir_beacon_channel):
            self.drive_base.turn(angle=randint(-360, 360))

    def blast_bazooka_if_touched(self):
        """
        Ev3rstorm blasts his bazooka when his Touch Sensor is pressed
        (inspiration from LEGO Mindstorms EV3 Home Ed.: Ev3rstorm: Tutorial #5)
        """
        MEDIUM_MOTOR_ROTATIONAL_DEGREES_PER_BLAST = 3 * 360

        if self.touch_sensor.pressed():
            if self.color_sensor.ambient() < 15:
                self.ev3_brick.speaker.play_file(file=SoundFile.UP)

                self.bazooka_blast_motor.run_angle(
                    speed=2 * MEDIUM_MOTOR_ROTATIONAL_DEGREES_PER_BLAST,
                    rotation_angle=-MEDIUM_MOTOR_ROTATIONAL_DEGREES_PER_BLAST,
                    then=Stop.HOLD,
                    wait=True)

                self.ev3_brick.speaker.play_file(file=SoundFile.LAUGHING_1)

            else:
                self.ev3_brick.speaker.play_file(file=SoundFile.DOWN)

                self.bazooka_blast_motor.run_angle(
                    speed=2 * MEDIUM_MOTOR_ROTATIONAL_DEGREES_PER_BLAST,
                    rotation_angle=MEDIUM_MOTOR_ROTATIONAL_DEGREES_PER_BLAST,
                    then=Stop.HOLD,
                    wait=True)

                self.ev3_brick.speaker.play_file(file=SoundFile.LAUGHING_2)

Ev3rstorm uses a remote-controlled tank driving utility whose code is in rc_tank_util.py as follows:

from pybricks.ev3devices import Motor, InfraredSensor
from pybricks.robotics import DriveBase
from pybricks.parameters import Button, Direction, Port


class RemoteControlledTank:
    """
    This reusable mixin provides the capability of driving a robot
    with a Driving Base by the IR beacon
    """
    def __init__(
            self,
            wheel_diameter: float, axle_track: float,   # both in milimeters
            left_motor_port: Port = Port.B, right_motor_port: Port = Port.C,
            ir_sensor_port: Port = Port.S4, ir_beacon_channel: int = 1):
        self.drive_base = \
            DriveBase(
                left_motor=Motor(port=left_motor_port,
                                 positive_direction=Direction.CLOCKWISE),
                right_motor=Motor(port=right_motor_port,
                                  positive_direction=Direction.CLOCKWISE),
                wheel_diameter=wheel_diameter,
                axle_track=axle_track)

        self.ir_sensor = InfraredSensor(port=ir_sensor_port)
        self.ir_beacon_channel = ir_beacon_channel

    def drive_by_ir_beacon(
            self,
            speed: float = 1000,    # mm/s
            turn_rate: float = 90   # rotational speed deg/s
            ):
        ir_beacon_button_pressed = \
            set(self.ir_sensor.buttons(channel=self.ir_beacon_channel))

        # forward
        if ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=0)

        # backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN, Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=0)

        # turn left on the spot
        elif ir_beacon_button_pressed == {Button.LEFT_UP, Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=0,
                turn_rate=-turn_rate)

        # turn right on the spot
        elif ir_beacon_button_pressed == {Button.RIGHT_UP, Button.LEFT_DOWN}:
            self.drive_base.drive(
                speed=0,
                turn_rate=turn_rate)

        # turn left forward
        elif ir_beacon_button_pressed == {Button.LEFT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=-turn_rate)

        # turn right forward
        elif ir_beacon_button_pressed == {Button.RIGHT_UP}:
            self.drive_base.drive(
                speed=speed,
                turn_rate=turn_rate)

        # turn left backward
        elif ir_beacon_button_pressed == {Button.LEFT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=turn_rate)

        # turn right backward
        elif ir_beacon_button_pressed == {Button.RIGHT_DOWN}:
            self.drive_base.drive(
                speed=-speed,
                turn_rate=-turn_rate)

        # otherwise stop
        else:
            self.drive_base.stop()

The code for the main program is in main.py as follows:

#!/usr/bin/env pybricks-micropython


from pybricks.media.ev3dev import ImageFile
from pybricks.tools import wait

from ev3rstorm import Ev3rstorm


ev3rstorm = Ev3rstorm()

ev3rstorm.ev3_brick.screen.load_image(ImageFile.TARGET)

while True:
    ev3rstorm.drive_by_ir_beacon(speed=1000)
    ev3rstorm.dance_randomly_if_ir_beacon_button_pressed()
    ev3rstorm.blast_bazooka_if_touched()
    wait(1)


This project was submitted by The Lương-Phạm Family.