Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

The display

The brick’s LCD is 100×64 pixels. You can read what it shows and draw your own text and images on it.

Screenshots

brick.screenshot() captures the screen:

nxt_rs::doc_example(|brick| async move {
let screen = brick.screenshot().await?;

print!("{}", screen.to_text());                       // half-block characters in the terminal
std::fs::write(std::env::temp_dir().join("nxt.pbm"), screen.to_pbm())?;   // a PBM image
let dark = screen.pixel(10, 20);                     // single pixels, (0, 0) is top left
Ok(()) });
  • PBM is a simple image format that most image tools can open. Convert it with e.g. ImageMagick: magick nxt.pbm nxt.png.
  • screen.bytes() returns the raw 800-byte buffer: 8 rows of 100 bytes, each byte a vertical strip of 8 pixels with the least significant bit on top.

Text

brick.show_text(..) clears the screen and prints text from the top left corner. The built-in 5×7 pixel font sits on a 6×8 grid, so the screen fits 8 lines of 16 characters. \n starts a new line:

nxt_rs::doc_example(|brick| async move {
brick.show_text("Hello,\nNXT!").await?;
Ok(()) });

Long lines are cut off, not wrapped. Characters outside printable ASCII are drawn as ?.

Composing a screen

For more control, build a Screen and send it with brick.set_screen(..). draw_text places text at any pixel position; each character cell is drawn opaque, so text stays readable on top of a picture:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::display::{CHAR_HEIGHT, Image, Screen};

let mut screen = Screen::blank();
screen.draw_text(0, 0, "Distance");
screen.draw_text(0, CHAR_HEIGHT, &format!("{:>3} cm", 42));
screen.draw_image(60, 0, &Image::from_fn(40, 40, |x, y| x == y || x + y == 39));
screen.set_pixel(99, 63, true);
brick.set_screen(&screen).await?;
Ok(()) });

Images

An Image is a black and white picture of any size. Draw it with screen.draw_image(x, y, &image), or show it alone with brick.show_image(&image). Pixels past the edge of the screen are cut off. Create one:

  • From a PBM file with Image::from_pbm(..), binary (P4) or plain (P1). Turn any picture into one with ImageMagick: magick logo.png -resize 100x64 -monochrome logo.pbm.
  • From grayscale pixels with Image::from_luma(width, height, &pixels), one byte per pixel, 0 black and 255 white. Pixels darker than 50% gray become dark. For photos, Image::from_luma_dithered(..) keeps shades of gray visible as patterns of dots.
  • From a function with Image::from_fn(width, height, |x, y| ..).
nxt_rs::doc_example(|brick| async move {
use nxt_rs::display::Image;

let logo = Image::from_pbm(b"P1\n5 5\n00100 01110 11111 01110 00100")?;
brick.show_image(&logo).await?;
Ok(()) });

The library decodes no other formats. To show a PNG or JPEG, decode, scale and convert it with an image library such as the image crate, then pass the grayscale pixels on:

let gray = image::open("photo.jpg")?.resize(100, 64, image::imageops::FilterType::Lanczos3).to_luma8();
let photo = Image::from_luma_dithered(gray.width() as usize, gray.height() as usize, gray.as_raw())?;
brick.show_image(&photo).await?;

How it works

Screenshots and drawing read and write the screen buffer in the display module’s I/O map, in about 15 requests. That takes a moment over USB and a second or two over Bluetooth. The layout matches LEGO’s standard firmware. Custom firmware may lay out memory differently.

The brick shows whatever is in the buffer, so a drawing stays until something else draws over it: a running program, or the brick’s menu when you press a button. While the menu is open it also redraws its status bar (the top 8 rows) from time to time. To keep a drawing up, redraw it periodically or keep text out of the top line.