Using Sensors

The official documentation is HERE and HERE. See also the Sensor Modes page.

As of October 2018, I find that quite often a sensor is not recognised by the EV3dev operating system when first plugged in. In the case of the ultrasonic and infrared sensors this is immediately obvious because in that case the sensor's LEDs don't light up. For other sensors it will will only be obvious that the sensor has not been recognised when you try to run the script and get a message that the sensor is not connected. Unplugging the sensor and plugging it again is almost certain to solve the problem.

EV3 Python is compatible with all the standard EV3 and NXT sensors except the NXT light sensor cannot be used to detect colors (as of September 2016).

EV3 and/or NXT sensors can be attached to any of the EV3's four sensor ports but I will adhere to the useful convention that they should be attached as follows:

       port 1 = touch, port 2 = gyro, port 3 = color, port 4 = infrared or ultrasonic.

This convention is useful because it means you don't have to reconnect the sensors as you move from one EV3 tutorial to another.

if you don’t attach more than one sensor of a particular type, then it will not matter which port a sensor is plugged in to. You don't need to include any reference on your code to the sensor port number - your program will just work. However, if you connect more than one sensor of a given type then you need to specify the port number with INPUT_1, INPUT_2 etc.

As usual, I only mention what I think is most likely to be useful to you as a beginner. For sensors, that would include InfraredSensor, TouchSensor, UltrasonicSensor, ColorSensor, LightSensor, GyroSensor, and SoundSensor. Note the distinction between the EV3 'color' sensor and the NXT 'light' sensor.

In the new EV3 Python v2 library it is no longer necessary to set sensor 'modes'.  This happens automatically according to what function you are using. For example, if you use the property ambient_light_intensity with the EV3 color sensor it will automatically be put into ambient light measuring mode. This is a major improvement relative to the version 1 library. Other major changes include the introduction of a useful wait_for_bump() function for the touch sensor and a wait_until_angle_changed_by(delta) function for the gyro sensor.

Even though mode selection is now automatic, that does not mean that modes no longer exist. As in the standard Lego EV3 software, many sensors can be used in different modes. Information about the different modes available for different Lego sensors can be found in the Lego section of the sensor-specific-resources section of THIS PAGE and selected information from that page is HERE.

Here is essential information on each type of sensor. Click the title of each section to see the official documentation. To see these functions in use, check the Example Scripts section lower down this page.

is_pressed

A boolean indicating whether the current touch sensor is being pressed.

wait_for_pressed()

Wait for the touch sensor to be pressed down.

wait_for_released()

Wait for the touch sensor to be released.

wait_for_bump()

Wait for the touch sensor to be 'bumped' (pressed and then released).

The EV3 'color sensor' should not be confused with the NXT  'light sensor'.

reflected_light_intensity

Reflected light intensity as an integer percentage 0-100. Light on sensor is red.

ambient_light_intensity

Returns an integer in the range 0-100 that represents the ambient light intensity. Light on sensor is dimly lit blue.

color

Color detected by the sensor, represented by an integer:

0: No color   1: Black   2: Blue   3: Green   4: Yellow   5: Red   6: White   7: Brown

Color detection works best if the colors are close to the official Lego colors (the colors of the Lego bricks) and if the object is about 8mm away from the sensor.

color_name

Returns  a string: 'NoColor', 'Black', 'Blue', etc

rgb

Red, green, and blue components of the detected color, scaled to be in the range 0-255. 

raw 

raw is the same as rgb except that the integers that make up raw are in the range 0 to 1020.

calibrate_white()

Infrared Sensor (includes Remote Control functions)

An infrared sensor is included in the home version of the EV3 kit but not in the education version (it can be bought separately).

proximity

A measurement of the distance between the sensor and an object, as a percentage. The proximity is calculated based on the intensity of the reflected IR radiation - the stronger the reflection the smaller the proximity value. Therefore the proximity value will be much bigger for a small black object (weak reflection) than for a large white object in the same location (strong reflection). For large white objects the proximity value is VERY roughly equal to the distance in cm.  For objects that are 'medium sized' and neither very light nor very dark colored:

You can experiment to get more accurate conversion factors for the particular object and distance ranges that you are using.

heading(channel=1)

Returns heading in degrees (-25 to +25) to the beacon on the given channel.

distance(channel=1)

Returns distance (0 to 100) to the beacon on the given channel. Returns None when beacon is not found. Note that distance() is a function (note the parentheses) while proximity is a property (no parentheses). You always need to be alert as to the difference between properties and functions!

heading_and_distance(channel=1)

Returns heading and distance to the beacon on the given channel as a tuple.

top_left/bottom_left/top_right/bottom_right(channel=1)

Checks if corresponding button is pressed.

beacon(channel=1)

Checks if the beacon button is pressed and returns True of False.

buttons_pressed(channel=1)

Returns a list of currently pressed buttons. It will return (as a list of strings) a maximum of two buttons pressed (if you press more than two buttons then it returns an empty list). It correctly returns 'beacon' if ONLY the beacon button is pressed but if the beacon button is pressed at the same time as any other button then it returns an empty list.

process()

Checks for currently pressed buttons. If the new state differs from the old state, call the appropriate button event handlers.

An ultrasonic sensor is included in the educational version of the EV3 kit but not in the home version (it can be bought separately). These functions work for the NXT ultrasonic sensor as well as the EV3 ultrasonic sensor.

distance_centimeters

Measurement of the distance detected by the sensor, in centimeters, to one decimal place precision.

distance_inches

Measurement of the distance detected by the sensor, in inches , to one decimal place precision.

A gyro sensor is included in the educational version of the EV3 kit but not in the home version (it can be bought separately)

It's very important that the gyro sensor should be absolutely still when the program is launched otherwise its readings may wander away from the correct values.

angle

The number of degrees that the sensor has been rotated since the program was launched. Clockwise rotation is considered positive. So if the sensor has been rotated two rotations counterclockwise since the program was launched then angle will equal -720.

rate

The rate at which the sensor is rotating, in degrees/second.

angle_and_rate

Angle (degrees) and rotational speed (degrees/second). The values are returned in a tuple.

wait_until_angle_changed_by(degrees)

Wait until angle has changed by specified amount. This function does not care in which direction the sensor is turned, therefore degrees should always be positive.

Use a motor as a sensor

Yes, since you can read the position of a motor, you can use a motor as a sensor. See the motors page for more on this.

See the official documentation.

See the official documentation.

EXAMPLE SCRIPTS

Example 1 (touch sensor & IR sensor)

The example below uses the touch sensor and the IR sensor. It turns both pairs of LEDs from green to red when an object is brought close to the IR sensor (closer than very roughly 40cm), and turns them back to green when no object is far away. The while loop runs as long as the touch sensor button is NOT pressed. When the touch sensor is pressed the loop is exited and the program terminates.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import TouchSensor, InfraredSensor

from ev3dev2.led import Leds

from time import sleep

# Connect infrared and touch sensors to any sensor ports

ir = InfraredSensor() 

ts = TouchSensor()

leds = Leds()

leds.all_off() # stop the LEDs flashing (as well as turn them off)

# is_pressed and proximity are not functions and do not need parentheses

while not ts.is_pressed:  # Stop program by pressing the touch sensor button

    if ir.proximity < 40*1.4: # to detect objects closer than about 40cm

        leds.set_color('LEFT',  'RED')

        leds.set_color('RIGHT', 'RED')

    else:

        leds.set_color('LEFT',  'GREEN')

        leds.set_color('RIGHT', 'GREEN')

    sleep (0.01) # Give the CPU a rest

The line leds.all_off() is included in the scripts because without it the program would start up with a FLASHING green LEDs if the object is far away but the assumption is that we want steady colors. The flashing of the green LEDs only stops when they are turned off, either with all_off() or by setting both pairs of LEDs to red.

Example 2 (touch sensor and EV3 ultrasonic sensor)

An ultrasonic sensor is included in the educational version of the EV3 kit but not in the home version (it can be bought separately). If you have a single EV3 ultrasonic sensor attached to the EV3 then you can modify the code of Example 1 to make it work with the EV3 US sensor as shown below. The code below will make the color of both pairs of LEDs change from green to red when an object is within 40 cm of the US sensor. You can use inches if you prefer, as shown in the script.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import TouchSensor, UltrasonicSensor

from ev3dev2.led import Leds

from time import sleep

# Connect ultrasonic and touch sensors to any sensor port

us = UltrasonicSensor()

ts = TouchSensor()

leds = Leds()

leds.all_off() # stop the LEDs flashing (as well as turn them off)

while not ts.is_pressed:

    if us.distance_centimeters < 40: # to detect objects closer than 40cm

        # In the above line you can also use inches: us.distance_inches < 16

        leds.set_color('LEFT',  'RED')

        leds.set_color('RIGHT', 'RED')

    else:

        leds.set_color('LEFT',  'GREEN')

        leds.set_color('RIGHT', 'GREEN')

    sleep (0.01) # Give the CPU a rest

Example 3 (two touch sensors)

Two touch sensors are included with the education version of the EV3 but only one with the home version. Usually you will not have two sensors of the same type plugged into the EV3 and you will not have to specify or determine where the sensors are attached. But in this example let's assume we have TWO touch sensors plugged into the EV3. We will SPECIFY which ports the sensors are plugged into using 'INPUT_1' and 'INPUT_2' for sensor (input) ports 1 and 2. In this program the left LED pair is set to green if the touch sensor on port 1 is not pressed and red if it is pressed (more specifically, if the touch sensor is not pressed then it returns a value of zero and the code then uses item zero from the tuple). In a similar way the touch sensor on port 2 controls the right LED pair. To stop this program you will need to press the stop button if you launched it from VS Code or the Back button on the EV3 if you launched it from Brickman.

#!/usr/bin/env python3

# USe TWO touch sensors to control the left and right LED pairs independently

from ev3dev2.sensor import INPUT_1, INPUT_2

from ev3dev2.sensor.lego import TouchSensor

from ev3dev2.led import Leds

from time import sleep

# Connect TWO touch sensors to BOTH sensor ports 1 and 2

ts1 = TouchSensor(INPUT_1) # Don't forget to IMPORT INPUT_1 and INPUT_2

ts2 = TouchSensor(INPUT_2) # Note the absence of quotes

leds = Leds()

while True: # Forever

    leds.set_color('LEFT', ('RED',  'YELLOW')[ts1.is_pressed])

    leds.set_color('RIGHT', ('RED', 'YELLOW')[ts2.is_pressed])

    

    sleep (0.01) # Give the CPU a rest

The way the highlighted lines work may not be clear to you. Let's look at the first one. ('RED', 'YELLOW') is a tuple (like a list but not modifiable). 'RED' is element zero in the tuple and 'YELLOW' is element one. is_pressed is a Boolean - it can be True (1) or False (0). So if touch sensor ts1 is pressed then is_pressed has a value of True or 1. The square brackets indicate that we are giving the index number of the element we want to obtain from this tuple, so in this case 'YELLOW' is obtained from the tuple since it is element 1. Therefore if you press ts1 the left LED pair will glow yellow. Simple!

If you change 'RED' to 'GREEN' you may notice something odd happens (or doesn't happen) when the script starts running...

Example 4 (gyro sensor and touch sensor)

The gyro sensor is included with the educational version of the EV3 kit but not with the home version (it can be bought separately). The angle property gives the angle in degrees that the sensor has turned clockwise since the angle was last measured. It may not work reliably when the sensor turns very slowly. Note that it is vital that the gyro sensor be absolutely still when the program is launched otherwise the reading will wander later even when the sensor is held still.

The sensor can also measure the rate of change of the angle, in degrees per second, but as a beginner you are likely to find that less useful.

The program below prints the measured angle to the EV3's LCD screen, then plays for one second a tone whose frequency depends on the angle, then waits for 0.5 second before repeating the cycle. Press the touch sensor button for at least a second to stop the program.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import TouchSensor, GyroSensor

from ev3dev2.sound import Sound

from time import sleep

# Connect gyro and touch sensors to any sensor ports

gy = GyroSensor() 

ts = TouchSensor()

sound = Sound()

# Stop program by long-pressing touch sensor button

while not ts.is_pressed:

    angle = gy.angle

    print(str(angle) + ' degrees')

    sound.play_tone(1000+angle*10, 1)

    sleep(0.5)

Example 5: Color sensor

In this script uses reflected_light_intensity to obtain an integer (0-100) that represents the intensity of reflected red light. A value is obtained and printed to the EV3's LCD screen once per second. For best results, place the sensor about 3 mm from the reflecting surface. Using that separation, I get a value of about 80 with normal white paper and about 5 with a typical black surface.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import ColorSensor

from time import sleep

cl = ColorSensor() 

while True:

    print(cl.reflected_light_intensity)

    sleep(1)

    

# max is about 80 with white paper, 3mm separation 

# and 5 with black plastic, same separation

See also lesson 6 on this page.

Example 6: EV3 color sensor, buttons and motor pair

In this program the color sensor measures ambient light intensity, returning a value between 0 and 100. This value is used to control the speed of a pair of large motors, so the brighter the ambient light the faster the robot will advance. Press any button to stop the program.

#!/usr/bin/env python3

from ev3dev2.motor import MoveSteering, OUTPUT_B, OUTPUT_C

from ev3dev2.sensor.lego import ColorSensor

from ev3dev2.button import Button

from time import sleep

cl = ColorSensor()

btn = Button()

steer_pair = MoveSteering(OUTPUT_B, OUTPUT_C)

while not btn.any():    # Stop program by pressing any button

    steer_pair.on(steering=0,speed=cl.ambient_light_intensity)

    sleep(0.2)

Example 7: EV3 color sensor and touch sensor

When you ask the sensor to return a color or color_name value it tries to recognise the color of an object placed about 6-12mm in front of the sensor (the distance is critical). It is designed to recognise the colors of standard Lego bricks, and therefore colors that are close to the colors of Lego bricks work best.

            0: No color 1: Black 2: Blue 3: Green 4: Yellow 5: Red 6: White 7: Brown

The program below reads the color_name string once per second and displays the string on the EV3's LCD screen. Press the touch sensor button for at least a second to stop the program.

To make the program speak the colors as well as displaying their text strings, uncomment the penultimate line. This will slightly increase the time between measurements as the speak function pauses ('blocks') the program each time it is used.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import TouchSensor, ColorSensor

from ev3dev2.sound import Sound

from time import sleep

cl = ColorSensor() 

ts = TouchSensor()

sound = Sound()

# Stop program by long-pressing touch sensor button

while not ts.is_pressed:

    print(cl.color_name)

    # sound.speak(cl.color_name)

    sleep(1)

Example 8: EV3 color sensor and touch sensor

In this example we use the property rgb which returns a tuple containing 3 integers that represent the amounts of red, green and blue reflected light (any color can be obtained, or at least simulated, by mixing the correct amounts of red, green and blue light). Each integer can be between 0 and 255 (though values of more than 100 are rare for the blue component). The following program prints the tuple and then the three component values separately, once every second.

raw (not used in this script) is the same as rgb except that each integer is in the range 0 to 1020.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import TouchSensor, ColorSensor

from time import sleep

cl = ColorSensor() 

ts = TouchSensor()

# Stop program by long-pressing touch sensor button

while not ts.is_pressed:

    # rgb is a tuple containing three integers

    # each 0-255 representing the amount of

    # red, green and blue in the reflected light

    print(cl.rgb)

    red = cl.rgb[0]

    green=cl.rgb[1]

    blue=cl.rgb[2]

    print("Red: "+str(red)+", Green: "+str(green)+", Blue: "+str(blue)+'\n')

    # '\n' is the newline character so an extra (blank) line is printed

    sleep(1)

Example 9: Infrared remote control

If you only want you program to react to presses of a single button at a time then use the process() function which checks for currently pressed buttons and then, if the new state differs from the old state, calls the appropriate button event handlers. The event handlers themselves must be associated with functions you have defined, as in the script below.

The script below allows the presses of the buttons on the remote control to control a robot as follows:

You'll notice that I haven't mentioned the beacon button. You can write code for that too but it may need to be handled differently to the other buttons because it's designed to toggle the beacon on and off and as such it differs from the other buttons.

#!/usr/bin/env python3

from ev3dev2.motor import OUTPUT_B, OUTPUT_C, MoveSteering

from ev3dev2.sensor.lego import InfraredSensor

from time import sleep

steer_pair = MoveSteering(OUTPUT_B, OUTPUT_C)

ir = InfraredSensor()

# Set the remote to channel 1

def top_left_channel_1_action(state):

    if state: # state is True (pressed) or False

        steer_pair.on(steering=0, speed=40)

    else:

        steer_pair.off()

def bottom_left_channel_1_action(state):

    if state:

        steer_pair.on(steering=0, speed=-40)

    else:

        steer_pair.off()

def top_right_channel_1_action(state):

    if state:

        steer_pair.on(steering=100, speed=30)

    else:

        steer_pair.off()

def bottom_right_channel_1_action(state):

    if state:

        steer_pair.on(steering=-100, speed=30)

    else:

        steer_pair.off()

# Associate the event handlers with the functions defined above

ir.on_channel1_top_left = top_left_channel_1_action

ir.on_channel1_bottom_left = bottom_left_channel_1_action

ir.on_channel1_top_right = top_right_channel_1_action

ir.on_channel1_bottom_right = bottom_right_channel_1_action

while True:

    ir.process()

    sleep(0.01)

The process() function can only deal with presses of one button at a time. What if you want to be able to also use button pairs? For example, in addition to having the single buttons behaving as above, you also want to be able to press the two top buttons to turn on a medium motor in the forward direction and the two bottom buttons to turn on the same motor in the reverse direction. It's still possible to make use of process(), as shown in the script below. The process() function is just used to detect a change in the button states and to trigger a function called move(). That function uses the buttons_pressed() function to detect (as a list) which buttons are pressed (up to a maximum of two buttons).

#!/usr/bin/env python3

from ev3dev2.motor import MediumMotor, OUTPUT_B, OUTPUT_C, MoveSteering

from ev3dev2.sensor.lego import InfraredSensor

from time import sleep

steer_pair = MoveSteering(OUTPUT_B, OUTPUT_C)

medium_motor = MediumMotor()

ir = InfraredSensor()

# Set the remote to channel 1

def top_left_channel_1_action(state):

    move()

def bottom_left_channel_1_action(state):

    move()

def top_right_channel_1_action(state):

    move()

def bottom_right_channel_1_action(state):

    move()

def move():

    buttons = ir.buttons_pressed() # a list

    if len(buttons)==1:

        medium_motor.off()

        if buttons==['top_left']:

            steer_pair.on(steering=0, speed=40)

        elif buttons==['bottom_left']:

            steer_pair.on(steering=0, speed=-40)

        elif buttons==['top_right']:

            steer_pair.on(steering=100, speed=30)

        elif buttons==['bottom_right']:

            steer_pair.on(steering=-100, speed=30)

    elif len(buttons)==2:

        steer_pair.off()

        if buttons==['top_left', 'top_right']:

            medium_motor.on(speed_pct=30)

        elif buttons==['bottom_left', 'bottom_right']:

            medium_motor.on(speed_pct=-30)

    else: # len(buttons)==0

        medium_motor.off()

        steer_pair.off()

# Associate the event handlers with the functions defined above

ir.on_channel1_top_left = top_left_channel_1_action

ir.on_channel1_bottom_left = bottom_left_channel_1_action

ir.on_channel1_top_right = top_right_channel_1_action

ir.on_channel1_bottom_right = bottom_right_channel_1_action

while True:

    ir.process()

    sleep(0.01)

It's a long and ugly script, but for the moment there may not be any much better way of responding to button pair presses in addition to single button presses. Here is a similar script. Instead of using the button_pressed() function to determine which buttons are pressed it keeps track of the states of the four buttons using a list called 'states'. Each time process() detects a change in the state of one of the buttons the 'states' list is updated accordingly and then the move() function is called:

#!/usr/bin/env python3

from ev3dev2.motor import OUTPUT_B, OUTPUT_C, MoveSteering, MediumMotor

from ev3dev2.sensor.lego import InfraredSensor

from time import sleep

steer_pair = MoveSteering(OUTPUT_B, OUTPUT_C)

medium_motor = MediumMotor()

ir = InfraredSensor()

# Set the remote to channel 1

states = [0,0,0,0] # top left, top right, bottom left, bottom right

def top_left_channel_1_action(state):

    states[0]=state

    move()

def bottom_left_channel_1_action(state):

    states[2]=state

    move()

def top_right_channel_1_action(state):

    states[1]=state

    move()

    

def bottom_right_channel_1_action(state):

    states[3]=state

    move()

def move():

    medium_motor.off()

    steer_pair.off()

    if states == [1,0,0,0]:

        steer_pair.on(steering=0, speed=40)

    elif states == [0,0,1,0]:

        steer_pair.on(steering=0, speed=-40)

    elif states == [0,1,0,0]:

        steer_pair.on(steering=100, speed=30)

    elif states == [0,0,0,1]:

        steer_pair.on(steering=-100, speed=30)

    elif states == [1,1,0,0]:

        medium_motor.on(speed_pct=30)

    elif states == [0,0,1,1]:

        medium_motor.on(speed_pct=-30)

# Associate the event handlers with the functions defined above

ir.on_channel1_top_left = top_left_channel_1_action

ir.on_channel1_bottom_left = bottom_left_channel_1_action

ir.on_channel1_top_right = top_right_channel_1_action

ir.on_channel1_bottom_right = bottom_right_channel_1_action

while True:

    ir.process()

    sleep(0.01)

The script below is significantly shorter, but it works in a very different way to the ones above. The ones above issue motor commands only when the process() function detects CHANGES in the pattern of pushed buttons. If you press and hold the top_left button then the script above will only issue once the instruction to move the robot forwards whereas the script below will issue the instruction 10 times per second for as long as the button is pressed (but that's not really a problem).

#!/usr/bin/env python3

from ev3dev2.motor import MediumMotor, OUTPUT_B, OUTPUT_C, MoveSteering

from ev3dev2.sensor.lego import InfraredSensor

from time import sleep

steer_pair = MoveSteering(OUTPUT_B, OUTPUT_C)

# attach a medium motor

medium_motor = MediumMotor()

ir = InfraredSensor()

# Set the remote to channel 1

while True:

    buttons = ir.buttons_pressed() # a list

    if len(buttons)==1: # 1 button is pressed

        medium_motor.off()

        if buttons==['top_left']:

            steer_pair.on(steering=0, speed=40)

        elif buttons==['bottom_left']:

            steer_pair.on(steering=0, speed=-40)

        elif buttons==['top_right']:

            steer_pair.on(steering=100, speed=30)

        elif buttons==['bottom_right']:

            steer_pair.on(steering=-100, speed=30)

    elif len(buttons)==2: # 2 buttons are pressed

        steer_pair.off()

        if buttons==['top_left', 'top_right']:

            medium_motor.on(speed_pct=30)

        elif buttons==['bottom_left', 'bottom_right']:

            medium_motor.on(speed_pct=-30)

    else: # len(buttons)==0 # no buttons are pressed

        medium_motor.off()

        steer_pair.off()

    sleep(0.1)

Example 10: Infrared beacon

In this example we use the distance() function to detect the proximity of the infrared beacon to the infrared sensor. The distance() reading is not in cm but can be VERY roughly converted to cm by doubling the reading. You can also use this script to test the heading() and heading_and_distance() functions by replacing ir.distance() with ir.heading() or ir.heading_and_distance(). If the sensor doesn't detect the beacon then it returns None. This script prints distance readings, in groups of ten, to the EV3 screen and also to the VS Code output panel (assuming the script is launched from VS Code). A tone also gives an audible representation of the distance. This script assumes that the beacon is set to channel 1.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import InfraredSensor

from ev3dev2.sound import Sound

from sys import stderr

from time import sleep

sound = Sound()

ir = InfraredSensor()

while True:

    for i in range(10):

        # try replacing with ir.heading()

        # or ir.heading_and_distance()

        distance = ir.distance()

        if distance==None:

            # distance() returns None if no beacon detected

            print('Beacon off?', end=' ')

            print('Beacon off?', end=' ', file=stderr)

        else:

            # print to EV3 LCD screen

            # print a space instead of starting a new line

            print(distance, end=' ')

            # print to VS Code output panel

            print(distance, end=' ', file=stderr)

            sound.play_tone(1800-10*distance, 0.4)

        sleep(0.5)

    print('') # start new line on EV3 screen

    print('', file=stderr) # start new line in VS Code