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