Learn EV3 Python‎ > ‎

LCD Screen

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:
  • the text in the Output panel will be easily legible whereas the text on the EV screen is by default tiny and hard to read on the non-backlit screen
  • the text in the Output panel can be easily copied and pasted if necessary
  • you can get live readings from your robot on your PC even if the robot is several meters away and connected wirelessly to the PC.
  • there is no limit to how much text you can print to the Output panel (you can scroll up if necessary) whereas if you print to the EV3 screen text is lost as soon as it has scrolled off the screen
  • Any text printed to the EV3 screen is lost as soon as the program terminates whereas the text in the Output screen remains.
  • even using the tiny default font, the text on the EV3 screen will wrap after 45 characters whereas in the VS Code output panel the text can be much longer before wrapping
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, x=10, y=10, width=80, height=40, fill_color='black', outline_color='black')
Draw a rectangle where the top left corner is at (x, y) and the bottom right corner is at (width, height). Some of the parameter names are incorrect. The function really behaves like this:
rectangle(clear_screen=True, x1=10, y1=10, x2=80, y2=40, fill_color='black', outline_color='black')
Since these four parameter names are incorrect, I suggest you omit them from your code.

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 (x, y)
# and the bottom right corner is at (width, height). 
# Parameter names are incorrect so have been omitted!
lcd.rectangle(False, 69, 90, 109, 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). There is some documentation on the EV3 Python bindings page but you will find much more 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:
  • arc(xy, start, end, fill=None)
  • bitmap(xy, bitmap, fill=None)
  • chord(xy, start, end, fill=None, outline=None)
  • ellipse(xy, fill=None, outline=None)
  • line(xy, fill=None, width=0)
  • pieslice(xy, start, end, fill=None, outline=None)
  • point(xy, fill=None)
  • polygon(xy, fill=None, outline=None)
  • rectangle(xy, fill=None, outline=None)
  • text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left")
  • multiline_text(xy, text, fill=None, font=None, anchor=None, spacing=0, align="left")
  • textsize(text, font=None, spacing=0)
  • multiline_textsize(text, font=None, spacing=0)
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 extensionIf you run code that addresses the screen from an SSH session without following these instructions then you may experience two problems:
  • the Brickman display may reappear before your script stops running, even after a second or less
  • or you might have the opposite problem: when the script stops it may take a minute or two before the the screen reverts back to the Brickman interface
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). :
  1. Run sudo chvt 6. This just switches the display to show you an empty terminal instead of Brickman, so that Brickman doesn't mess with you. You will need to enter the password for robot, which is 'maker'.
  2. Run your program. You should see it show up on the EV3 display, as you'd expect. You can stop the script running with Ctrl+C if necessary. You can work from this terminal like normal, run your program multiple times, etc.
  3. Run sudo chvt 1 to get Brickman back
Perfectionist's wayDisadvantage: More steps and you will be using a copy of the EV3's tiny display.
  1. Run sudo chvt 6. This just switches the display to show you an empty terminal instead of Brickman, so that Brickman doesn't mess with you. You will need to enter the password for robot, which is 'maker'.
  2. Run sudo conspy. This will make your SSH session show the same terminal that is on the EV3's display. Log in to the prompt it shows you with login = robot and password = maker.
  3. Run your program. You should see it show up on the EV3 display, as you'd expect. You can stop the script running with Ctrl+C if necessary. You can work from this terminal like normal, run your program multiple times, etc.
  4. Press the ESC key on the PC 3 times to exit the display terminal
  5. Run sudo chvt 1 to get Brickman back