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 Textures. 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:

  // Create a new context for white textures.
  white-context := context.with --color=WHITE

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.

Weather station example screenshot
Weather station example screenshot
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