Display
The Toit SDK includes support for writing text and printing icons on displays. We highlight in this section the basic functions of display use. You can also learn more about the different fonts available in the SDK by studying the font basics.
These examples assume you have installed the pixel_display package into your project. See the Packages quick start guide for information on getting started with packages.
You will also need at least one display driver, for example the SSD1306 driver
which is also available as a package, called ssd1306
. A color TFT driver that supports many common displays is available
in the toit-color-tft and can be installed as the color-tft
package.
Getting the display driver and PixelDisplay object
Both the color-tft
and ssd1306
packages have some helpful examples which you can see by cloning the
github repos linked above. We will assume that you have a get_display.toit
file in your project, which
might look something like the following. Details will depend on your hardware configuration, in particular which
pins and bus you are using to connect your display.
Example contents of get_display.toit
import gpio import i2c import ssd1306 show * import pixel_display show * get_display -> TwoColorPixelDisplay: scl := gpio.Pin 4 sda := gpio.Pin 5 bus := i2c.Bus --sda=sda --scl=scl --frequency=800_000 devices := bus.scan if not devices.contains Ssd1306.I2C_ADDRESS: throw "No SSD1306 display found" driver := Ssd1306.i2c (bus.device Ssd1306.I2C_ADDRESS) return TwoColorPixelDisplay driver
Display architecture
A display will be an instance of one of the following classes, which have most of the same methods:
TwoColorPixelDisplay
for black/white displays.
ThreeColorPixelDisplay
for black/white/red displays (usually e-paper).
FourGrayPixelDisplay
for 4-tone grayscale displays (usually e-paper).
TrueColorPixelDisplay
for color displays like the ones supported by the color-tft
driver.
A display maintains a list of objects that it is currently displaying.
These objects are called Texture
s. They are mutable objects that
represent a shape on the display. For example a geometric shape or a
piece of text. Together, the textures added to a display describe a
scene.
To update the display, you add or remove textures, or modify the
existing textures. When you have made your changes you update the
physical display by calling the draw
method on the display object.
Normally you will import all the textures for your given color model
into your namespace with an import like import pixel_display.true_color show *
Write with built-in sans font
Let us start with a simple example using a text texture. In this example we use the sans10 font, which is a simple ASCII-only font that is built in to the Toit SDK and does not need to be imported with a package.
In this example we build up a 'scene' consisting of only one text
texture, and immediately render it to the screen with draw
.
Afterwards we exit the program, which leaves the image on the screen.
In this example there is no way to update the scene later, since
the program has terminated.
import font show * import pixel_display.texture show * import pixel_display.two_color show * // Provides WHITE and BLACK. import pixel_display show * import .get_display // Import file in current project, see above. sans ::= Font.get "sans10" display ::= get_display main: context := display.context --landscape --color=BLACK --font=sans display.text context 60 50 "Hello from Toit!" display.draw
When creating a texture to add to the scene there are a huge number of parameters to specify. This includes the color, the orientation, the font, and the coordinate system. We combine almost all these parameters into an immutable object called a graphics context.
In the above example we created a context that uses a landscape
orientation and coordinates, draws in black, and uses the
built-in sans10 font for text objects. Often, contexts are
created from other contexts using the with
method:
Using additional fonts
More fonts are available in the packages font_x11_adobe
,
font_clear
, font_clearly_u
, and font_tiny
packages.
Once you have installed the package in your project,
import specific fonts from font_x11_adobe
with:
import font show * import font_x11_adobe.sans_10_bold import font_x11_adobe.sans_24_bold import pixel_display.texture show* import pixel_display.two_color show WHITE BLACK import pixel_display show TwoColorPixelDisplay import .get_display // Import file in current project, see above. main: sans_10_bold ::= Font [ sans_10_bold.ASCII, sans_10_bold.LATIN_1_SUPPLEMENT] sans_24_font ::= Font [ sans_24_bold.ASCII, sans_24_bold.LATIN_1_SUPPLEMENT] display ::= get_display sans_10 := display.context --landscape --font = sans_10_bold --color = BLACK --alignment = TEXT_TEXTURE_ALIGN_RIGHT sans_24 := display.context --landscape --font = sans_24_font --color = BLACK --alignment = TEXT_TEXTURE_ALIGN_CENTER context := display.context --landscape --color=BLACK display.text sans_10 40 30 "Hello from" display.text sans_24 18 65 "TOITWARE" display.draw
Learn more about all available fonts here.
Using icons
All Material Design icons are available for use in Toit programs.
import pixel_display show TwoColorPixelDisplay // Use "jag pkg install pictogrammers_icons" to get this package. import pictogrammers_icons.size_48 as icons import .get_display // Import file in current project, see above. main: display := get_display context := display.context --landscape icon := display.icon context 63 85 icons.HUMAN_SCOOTER display.draw
Updating the display
In order to update the display we can build up a scene, then create a loop that updates the scene as new data arrives. Here is a bigger example that subscribes to an MQTT topic for its weather information, and uses the local time to update a digital clock on the same display.
import encoding.json import font show * import font_x11_adobe.sans_14_bold import monitor show Mutex import mqtt import net import pictogrammers_icons.size_48 as icons import pixel_display.two_color show * import pixel_display.texture show * import pixel_display show TwoColorPixelDisplay import .get_display // Import file in current project, see above. // Search for icon names on https://materialdesignicons.com/ // (hover over icons to get names). WMO_4501_ICONS ::= [ icons.WEATHER_SUNNY, icons.WEATHER_CLOUDY, icons.WEATHER_SNOWY, icons.WEATHER_SNOWY_HEAVY, icons.WEATHER_FOG, icons.WEATHER_PARTLY_RAINY, icons.WEATHER_RAINY, icons.WEATHER_SNOWY, icons.WEATHER_PARTLY_RAINY, icons.WEATHER_LIGHTNING, ] // We don't want separate tasks updating the display at the // same time, so this mutex is used to ensure the tasks only // have access one at a time. display_mutex := Mutex display := get_display main: sans_14_font ::= Font [ sans_14_bold.ASCII, // Regular characters. sans_14_bold.LATIN_1_SUPPLEMENT, // Degree symbol. ] // Build a scene with the metods `add`, `text`, `icon`, // and `filled_rectangle`. display.background = BLACK context := display.context --landscape --color=WHITE --font=sans_14_font black_context := context.with --color=BLACK // White circle as background of weather icon. We are just // using the window to draw a circle here, not as an actual // window with its own textures. DIAMETER ::= 56 CORNER_RADIUS ::= DIAMETER / 2 display.add RoundedCornerWindow 68 4 DIAMETER DIAMETER context.transform CORNER_RADIUS WHITE // Icon is added after the white dot so it is in a higher layer. icon_texture := display.icon black_context 72 48 icons.WEATHER_CLOUDY // Temperature is black on white. display.filled_rectangle context 0 0 64 32 temperature_context := black_context.with --alignment=TEXT_TEXTURE_ALIGN_CENTER temperature_texture := display.text temperature_context 32 23 "??°C" // Time is white on the black background, aligned by the // center so it looks right relative to the temperature // without having to zero-pad the hours. time_context := context.with --alignment=TEXT_TEXTURE_ALIGN_CENTER time_texture := display.text time_context 32 53 "??:??" // The scene is built, now start some tasks that will update // the display. task:: clock_task time_texture task:: weather_task icon_texture temperature_texture weather_task weather_icon/IconTexture temperature_texture/TextTexture: transport := mqtt.TcpTransport net.open --host="127.0.0.1" client := mqtt.Client --transport=transport client.start --client_id="toit-client-id" client.subscribe "weather":: | topic/string payload/ByteArray | map := json.decode payload code := map["wmo_4501"] temp := map["temperature_c"] display_mutex.do: weather_icon.icon = WMO_4501_ICONS[code] temperature_texture.text = "$(%.1f temp)°C" display.draw clock_task time_texture: while true: now := (Time.now).local display_mutex.do: // H:MM or HH:MM depending on time of day. time_texture.text = "$now.h:$(%02d now.m)" display.draw // Sleep this task until the next whole minute. sleep_time := 60 - now.s sleep --ms=sleep_time*1000