LCD Screen

See also the official documentation HERE.

The EV3 has a 178 x 128 pixels monochrome (grayscale) LCD screen. The coordinates of the top-left pixel are (0, 0) and the coordinates of the bottom-right pixel are (177, 127).

Display text with the print() function

The print() function is very easy to use since it does not require you to create a Display() object and it does not require you to issue an update() command to apply pending changes to the screen. It is therefore easier to use than the text_pixels() and text_grid() functions described later. When you use the print() function text automatically wraps at the right edge of the screen and automatically scrolls up as necessary. You can change the font as in the following example. When you set a font that setting will be remembered until you turn off the brick.

The script below shows how to set the desired font. You can get a list of available fonts by running  ls /usr/share/consolefonts in the SSH terminal. In VS Code you can open the SSH terminal by right-clicking the green dot that indicates a successful connection to the EV3, then choosing 'Open SSH Terminal'. Note that the fonts used by the print() function and the procedure for specifying one, are different from the fonts used by the text_pixels() and text_grid() functions and the procedure for specifying one. The script below also shows how you can stop the print() function from starting a new line after printing. 

#!/usr/bin/env python3

from time import sleep

import os

os.system('setfont Lat15-TerminusBold14')

# os.system('setfont Lat15-TerminusBold32x16')  # Try this larger font

print('EV3 Python rules!')

print()  # print a blank line

print('EV3', 'Python rules!')  # comma means continue on same line

# print() has a parameter 'end' which by

# default is the new line character:

print('EV3')    # A new line will be started after this

print('Python rules!')

# Here the 'end' parameter's default new line

# character argument is replaced

# by an empty string so no new line is begun

print('EV3', end='')

print('Python rules!')

# Here the 'end' parameter's default new line

# character argument is replaced

# by a space character so no new line is begun

print('EV3', end=' ')

print('Python rules!')

sleep(15)  # display the text long enough for it to be seen

Here is the result of running the above script (you can do a screen capture by right-clicking the green dot that indicates a successful connection to the EV3, then choosing 'Take screenshot'):

Here is the result of running the same script but using the font Lat15-TerminusBold32x16 (most of the text has scrolled off the screen):

Print to the VS Code output window

A script that is running on the EV3 but which was launched from VS Code can print to VS Code's Output panel. There are many reasons why you may want to print to the Output panel rather than (or as well as) to the EV3 screen:

Note that printing to the Output panel like this has no analogy for EV3-G so it is an example of the many things that can be achieved with EV3 Python that can't be achieved with EV3-G.

This script below demonstrates how to print to both the EV3's LCD screen and the Output panel. It prints twice per second the ambient light intensity detected by the color sensor. Of course, you can only print to the VS Code output panel if you start the script from VS Code - if you start the script from Brickman then nothing will print to the Output panel. In fact the output will be placed in a file my_program_name.py.err.log in the same folder. 

#!/usr/bin/env python3

from ev3dev2.sensor.lego import ColorSensor

from sys import stderr

from time import sleep

cl = ColorSensor()

'''

To be able to print to the VS Code Output panel, this

script must be launched from VS Code, not from Brickman

'''

while True:

    # print to EV3 LCD screen

    print(cl.ambient_light_intensity) 

    # print to VS Code output panel

    print(cl.ambient_light_intensity, file=stderr

    sleep(0.5)

The above script prints just one reading per line, so the printout will soon start to scroll. The script below does the same as the above but prints 10 readings per line. The keyword argument 'end' is explained further down this page.

#!/usr/bin/env python3

from ev3dev2.sensor.lego import ColorSensor

from sys import stderr

from time import sleep

cl = ColorSensor()

while True:

    for i in range(10):

        # print to EV3 LCD screen

        # print a space instead of starting a new line

        print(cl.ambient_light_intensity, end=' ')

        # print to VS Code output panel

        print(cl.ambient_light_intensity, end=' ', file=stderr)

        sleep(0.5)

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

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

Display text with text_pixels() or text_grid()

Displaying text with these functions is more difficult than with print() but gives you more control, as you can specify where the text is to be placed . You can also display different fonts simultaneously, which you cannot do with print(). The text that you display with text_pixels() or text_grid() will not wrap at the right edge of the screen and will not scroll. Also, you need to update() the screen after using these functions otherwise nothing will be displayed on the screen.

text_pixels(text, clear_screen=True, x=0, y=0, text_color='black', font=None)

displays text such that the top-left corner of the text is in the pixel location given by x,y. Recall that the EV3 screen is 178 x 128 pixels and that the top-left pixel has coordinates (0,0). 

text_grid(text, clear_screen=True, x=0, y=0, text_color='black', font=None)

displays ‘text’ starting at grid (x, y) where the grid is made of cells 8 pixels wide and 10 pixels tall such that the EV3 display is 22 columns wide and 12 rows tall. The size of the grid cells does not depend on the size of the chosen font. There is no fixed-width font available which has dimensions that match the grid cell size and therefore I recommend that you avoid using this function and that you stick to using text_pixels() instead. Even if a font were available that matches the grid cell dimensions it would be a very small font (size 10, meaning 10 pixels tall) and would be very hard to read.

Both these functions have a clear_screen parameter which is True by default, meaning that if your script writes a line of text followed by a second line then if you forget to set clear_screen to False for the second line then the first line will not print.

You must update() the screen in order for pending changes to be written to the screen. If you forget to update the screen then nothing at all will print!

There are many fonts available for use with text_pixels() and text_grid() and you can find the full list HERE . All the fonts are available in the following sizes (heights in pixels): 8, 10, 12, 14, 18, 24.

For maximum legibility and attractiveness, I recommend that your choice of variable width font should be the 'helvB' set (that's 'B' for 'bold'). Fixed width fonts are less attractive but easier to work with. For fixed width fonts I recommend 'courB' and 'lutBS'. You can see what these fonts look like lower down the page. I recommend that you leave at least one pixel of vertical space between one line of text and the next. For example, if you choose to use a font with size 24 (24 pixels tall) then I recommend allowing 25 pixels of space for each line. In fact size 24 text allows for 5 lines of text to fit neatly on the EV3 screen if you print to these y coordinates: 0, 25, 50, 75, 100.

The fixed width fonts courB24 and lutBS24 have character widths of 15 pixels and 14 pixels respectively, meaning that 12 characters fit horizontally quite well on the EV3 screen, as you can see in the screenshots below.

Note that you can include the new line character '\n' in the strings that you ask these functions to print but I recommend against that because it gives a line spacing that I find too large.

Here is a script the first prints a chosen font set in sizes 10, 14, 18 and 24 and then, 6 seconds later, prints a whole screenful of text at size 24. Use different values for 'style' to try different font sets. Underneath the script is the result of running the script with style set to helvB, courB and lutBS respectively.

#!/usr/bin/env python3

from ev3dev2.display import Display

from ev3dev2.sound import Sound

from time import sleep

lcd = Display()

sound = Sound()

def show_for(seconds):

    lcd.update()

    sound.beep()

    sleep(seconds)

    lcd.clear()

# Try each of these different sets:

style = 'helvB'

#style = 'courB'

#style = 'lutBS'

y_value = 0

str1 = ' The quick brown fox jumped'

str2 = '123456789012345678901234567890'

for height in [10, 14, 18, 24]:

    text = style+str(height)+str1

    lcd.text_pixels(text, False, 0, y_value, font=style+str(height))

    y_value += height+1   # short for  y_value = y_value+height+1

    lcd.text_pixels(str2, False, 0, y_value, font=style+str(height))

    y_value += height+1

show_for(6)

strings = [] # create an empty list

# Screen width can accommodate 12 fixed

# width characters with widths 14 or 15

#               123456789012

strings.append(style+'24 The')

strings.append('quick brown ')

strings.append('fox jumps   ')

strings.append('over the dog')

strings.append('123456789012')

for i in range(len(strings)):

    lcd.text_pixels(strings[i], False, 0, 25*i, font=style+'24')

show_for(6)

Notes on the above script:

I usually include the parameter names (keywords) to make the code clearer but in some of the lines above I have omitted certain names because the lines would otherwise be too long. For example:

lcd.text_pixels(text, False, 0, y_value, font=style+str(height))

The arguments in bold are positional arguments whose meaning is known by the function according to their positions in the argument list, so the inclusion of parameter names is optional. However, I had to include the name 'font' - it could not be processed as a positional argument because it's in 'the wrong place'. In the function definition we saw

text_pixels(text, clear_screen=True, x=0, y=0, text_color='black', font=None)

so in order to be valid as a positional argument the font argument has to be the sixth argument but in my code it's the fifth argument since I have not specified a text_color value (I'm happy to use the default value, 'black'). Since the argument is not a valid positional argument when placed fifth, the name must be given, making it into a keyword argument, or 'kwarg'.

The script demonstrates one way of building a list (called an 'array' in EV3-G). 

Center the text

It's quite easy to horizontally center a line of text in a fixed-width font: for each character in the string deduct half a character width from 89 (the horizontal midpoint of the EV3 screen). For example if you want to center the string 'Hello' written in the font lutBS24 which has a character width of 14 then deduct 5x7=35 pixels from 89 to give 89-35=54 so print to x coordinate 54.

To vertically center a single line of text the procedure would be similar: deduct half the font height from 63 (the vertical midpoint of the EV3 screen). For example if you want to vertically center the string 'Hello' written in the font lutBS24 which has a height of 24 pixels then deduct 12 pixels from 63 to give 51 so print to y coordinate 51.

The following script is capable of calculating the y coordinates for printing multiple lines, one above the other. That's quite a bit more complicated - I'll let you figure it out for yourself. The script leaves one empty horizontal line of pixels between each line of text.

The script makes use of a standard Python function wrap(text, width) which wraps the single paragraph in text (a string) so every line is at most width characters long. It returns a list of output lines i.e. a list of strings. For example

wrap('The quick brown fox jumps over the lazy dog', width=12)

returns ['The quick', 'brown fox', 'jumps over', 'the lazy dog'].

In order to be able to use the wrap() function you must first import it with

from textwrap import wrap

Here's a script that horizontally and vertically centers some text printed in several different fixed-width fonts. I know it's a rather complex script that I haven't explained it in detail - it might be worth a few minutes of study. The text centering could be very useful in future scripts so it's been made into a user-defined function that we can use elsewhere. The result is underneath.

#!/usr/bin/env python3

from ev3dev2.display import Display

from textwrap import wrap

from time import sleep

lcd = Display()

def show_text(string, font_name='courB24', font_width=15, font_height=24):

    lcd.clear()

    strings = wrap(string, width=int(180/font_width))

    for i in range(len(strings)):

        x_val = 89-font_width/2*len(strings[i])

        y_val = 63-(font_height+1)*(len(strings)/2-i)

        lcd.text_pixels(strings[i], False, x_val, y_val, font=font_name)

    lcd.update()

my_text = 'The quick brown fox jumps over the lazy dog'

show_text(my_text, 'courB14', 9, 14)

sleep(5)

show_text(my_text, 'lutBS14', 9, 14)

sleep(5)

show_text(my_text, 'courB18', 11, 18)

sleep(5)

show_text(my_text, 'lutBS18', 11, 18)

sleep(5)

show_text(my_text, 'courB24', 15, 24)

# or simply show_text(my_text) since default values

# have been set for courB24

sleep(5)

show_text(my_text, 'lutBS24', 14, 24)

sleep(5)

Default values have been included in the function definition so that if you want to use the courB24 font all you have to do is:

show_text('This is my text')

Graphics

EV3 Python has a few easy-to-use commands for drawing simple shapes:

line(clear_screen=True, x1=10, y1=10, x2=50, y2=50, line_color='black', width=1)

Draw a line from (x1, y1) to (x2, y2)

circle(clear_screen=True, x=50, y=50, radius=40, fill_color='black', outline_color='black')

Draw a circle of ‘radius’ centered at (x, y)

rectangle(clear_screen=True, x1=10, y1=10, x2=80, y2=40, fill_color='black', outline_color='black')

Draw a rectangle where the top left corner is at (x1, y1) and the bottom right corner is at (x2, y2). 

point(clear_screen=True, x=10, y=10, point_color='black')

Draw a single pixel at (x, y)

The following script should be easy to understand. The result is underneath. With regard to the colors, know that the EV3 LCD screen can only display four shades of grey which correspond to these color names: black, grey, lightgrey, white. Stick to these color names because otherwise you will have an inconsistency between the LCD display (4 shades of grey) and screenshots (many more shades of grey).

#!/usr/bin/env python3

from ev3dev2.display import Display

from time import sleep

lcd = Display()

# Draw a circle of ‘radius’ centered at (x, y)

lcd.circle(clear_screen=False, x=89, y=64, radius=61, fill_color='lightgrey')

lcd.circle(False, x=65, y=45, radius=10, fill_color='black')

lcd.circle(False, x=113, y=45, radius=10, fill_color='black')

# Draw a line from (x1, y1) to (x2, y2)

lcd.line(False, x1=89, y1=50, x2=89, y2=80, line_color='grey', width=4)

# Draw a rectangle where the top left corner is at (x1, y1)

# and the bottom right corner is at (x2, y2). 

lcd.rectangle(False, x1=69, y1=90, x2=109, y2=105, fill_color='grey')

# Draw a single pixel at (x, y)

lcd.point(False, x=65, y=45, point_color='white')

lcd.point(False, x=113, y=45, point_color='white')

lcd.update()

sleep(10)

PIL(LOW) functions

Other shapes can be drawn by making use of the powerful graphics library called Pillow which is part of standard Python rather than something unique to EV3 Python. Note that Pillow evolved out of the Python Image Library (PIL). You can find detailed documentation in the official Pillow documentation HERE, especially the documentation on the ImageDraw module. The ImageDraw module is automatically imported when you import the Display class so you don't need to explicitly import it. However, when you want to display an image file you do need to import the Image class, as explained later. The ImageDraw module provides the following methods:

You may find this tutorial to be a good place to start. 

I have struck through the last four lines because I recommend that you use the EV3 Python functions text_pixels() and text_grid() to print text to the screen rather than the more complicated (but slightly more powerful) PIL text functions. The draw.text() function is very similar to the text_pixels() except that it adds the possibility of setting a fill (background) color for the text. If you absolutely want to learn about the PIL text functions then see THIS PAGE and the official documentation.

Smiley / Grumpy

This script uses draw.ellipse() to draw two circles (a circle is actually a kind of ellipse) for eyes then draw.arc() to draw an arc (in this case half an ellipse) which alternates between opening upwards (a smile) and opening downwards (grumpy). Stop the script with a long press on any button or with the 'Stop' button if you launched it from VS Code.

#!/usr/bin/env python3

from ev3dev2.display import Display

from ev3dev2.button import Button

from time import sleep

lcd = Display()

btn = Button()

smile = True

while not btn.any(): # exit loop with a long press on any button

    lcd.clear()

    lcd.draw.ellipse(( 20, 20,  60, 60))

    lcd.draw.ellipse((118, 20, 158, 60))

    if smile:

        lcd.draw.arc((20, 80, 158, 100), 0, 180)

    else:

        lcd.draw.arc((20, 80, 158, 100), 180, 360)

    smile = not smile  # toggle between True and False

    # Update lcd display

    lcd.update() # Applies pending changes to the screen.

    # Nothing will be drawn on the lcd screen

    # until this function is called.

    sleep(1)

Running the script makes the screen alternate once per second between the two images below:

Note that the draw.ellipse() command has parameters that are the top-left corner and bottom-right corner of a bounding box (an imaginary box that would contain the ellipse). The draw.arc() command needs the same pair of coordinates and also needs a starting angle and an ending angle, both in degrees. Angles are measured from 3 o’clock, increasing clockwise.

Display an image file

Before you can display an image file, you have to have an image file to display! Most of the popular image formats should work (jpg, bmp, gif, png etc) but they will be displayed in just four shades of grey (black, light grey, dark grey, black). Animated gif files will not 'play'. I suggest you start by doing what I did: copy all the standard Lego EV3 BMP image files that are included with the free Lego EV3 software into a directory called 'pics' within your 'robot' directory. You can see the BMP files listed on this page and at the bottom of that page is a link to a zip file (328KB) containing the 107 monochrome BMP files, each one perfectly dimensioned to fit the EV3 screen (178*128 pixels). Decompress the zip file to a location on your PC.

If you prefer, you can find the files on your Windows PC in a location like this: C:\Program Files (x86)\LEGO Software\LEGO MINDSTORMS EV3 Home Edition\Resources\BrickResources\Retail\Images\files. The 'Program Files (x86)' folder may also be called simply 'Program Files'. If you are getting the files from your PC I suggest you copy all the BMP files (not the RGF files) into a single folder.

Once you have all the image files in a folder called 'pics' on your PC you will want to download a copy of that folder into the 'robot' folder on the EV3. If you are using VS Code with the EV3 extension as I recommend then the best procedure is to open the 'pics' folder in VS Code and click the 'Send Project to Device' icon in the header of the EV3 Device Browser at the bottom left of the VS Code window. That will download the folder and its contents into your 'robot' folder on the EV3. Once you've done all of that you should be ready to run the following script, replacing 'Bomb.bmp' with the name of the image file of your choice. If you flash a new Stretch image to your SD card then the 'pics' and 'sounds' folders (and all your scripts) will be deleted so you will need to download them to the EV3 again.

#!/usr/bin/env python3

from ev3dev2.display import Display

from time import sleep

from PIL import Image

lcd = Display()

logo = Image.open('/home/robot/pics/Bomb.bmp')

lcd.image.paste(logo, (0,0))

lcd.update()

sleep(5)

Here is the result:

How to capture a screenshot of the EV3 screen

If you are using VS Code with the EV3 extension then taking a screenshot is as easy as right-clicking the green 'connected' dot in the EV3 Device Browser and choosing 'Take Screenshot'. Save the image with 'Save As' so that you can choose a convenient location for that image since the default location is not convenient. Be sure to save the image file with the extension .png.

Screenshots are deliberately given a yellow tint so that if you view them against a white background such as this page you can still see where the edges of the image are.

With regard to colors, know that the EV3 LCD screen can only display four shades of grey which correspond to these color names: black, grey, lightgrey, white. Stick to these color names because otherwise you will have an inconsistency between the LCD display (4 shades of grey) and screenshots (many more shades of grey).

How to run from the terminal a script that writes to the EV3's LCD display

This is for people who are not using the VS Code with the EV3 extension. If you run code that addresses the screen from an SSH session without following these instructions then you may experience two problems:

David L, a top contributor to the ev3dev project, gave me the following advice:

Easy way, recommended unless you are a perfectionist. Disadvantage: you will then see a little blinking cursor at the bottom of a small 'dead square' somewhere on the screen (you can see the cursor in the 'Hello World' image higher up this page). :

Perfectionist's way. Disadvantage: More steps and you will be using a copy of the EV3's tiny display.