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

Sensors

Taking a sensor configures its input port on the brick and gives you a typed handle. Every sensor implements the Sensor trait, whose read() returns a reading of a meaningful type:

SensorTake withread() returns
Touchbrick.touch(S1) / S1.touch()bool
Lightbrick.light(S3) / S3.light()Percent
Soundbrick.sound(S2) / S2.sound()Percent
Colour (NXT 2.0), detectingbrick.color(S3) / S3.color()Option<ColorName>
Colour, lamp or ambientcolor.lamp::<Red>(), color.ambient()Percent
Ultrasonicbrick.ultrasonic(S4) / S4.ultrasonic()Option<Centimeters>
Anything analogbrick.raw_sensor(S2, ty, mode)InputValues

Bring the trait into scope (it’s in the prelude) to call read(). Each sensor also has a method with a more specific name, such as is_pressed(), level(), color() or distance().

Touch sensor

nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;
use std::time::Duration;

let touch = brick.touch(S1).await?;
if touch.read().await? {
    println!("pressed");
}
Ok(()) });

touch.wait_for_press(interval) polls until the button is pressed.

Light sensor

The light sensor has a small red floodlight. With it on (reflected mode) the sensor measures how much light the surface below reflects, which is ideal for following lines. With it off (ambient mode) it measures the surrounding light.

nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;

let light = brick.light(S3).await?;          // Light<S3, _, Reflected>
let surface = light.read().await?;           // Percent

let light = light.ambient().await?;          // Light<S3, _, Ambient>
let room = light.read().await?;

let light = light.reflected().await?;        // back to reflected
Ok(()) });

The mode is part of the type. Switching modes consumes the sensor and returns a new one, so the type always matches the hardware.

Sound sensor

nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;

let mic = brick.sound(S2).await?;            // Sound<S2, _, Db>: all frequencies
let loudness = mic.read().await?;
let mic = mic.weighted().await?;             // Sound<S2, _, Dba>: weighted like human hearing
Ok(()) });

Colour sensor (NXT 2.0)

The colour sensor from the NXT 2.0 set needs firmware 1.28 or newer. It has three modes:

Mode typeWhat it doesread()
DetectRecognises black, blue, green, yellow, red and whiteOption<ColorName>
Lamp<Red>, Lamp<Green>, Lamp<Blue>Lights the lamp in one colour and measures the reflectionPercent
AmbientLamp off, measures ambient lightPercent
nxt_rs::doc_example(|brick| async move {
use nxt_rs::{prelude::*, sensor::{Green, Red}};

let color = brick.color(S3).await?;
match color.read().await? {
    Some(ColorName::Red) => println!("stop"),
    Some(ColorName::Green) => println!("go"),
    Some(other) => println!("{other:?}"),
    None => println!("can't tell"),
}

let red = color.lamp::<Red>().await?;
let reflected_red = red.read().await?;
let green = red.lamp::<Green>().await?;
let back = green.detect().await?;
Ok(()) });

Because the reading type depends on the mode, mixing them up doesn’t compile:

async fn f(brick: nxt_rs::Brick<nxt_rs::transport::Mock>) -> nxt_rs::Result<()> {
use nxt_rs::prelude::*;
let color = brick.color(S3).await?;
let level: Percent = color.read().await?;   // error: expected `Percent`, found `Option<ColorName>`
Ok(()) }

Ultrasonic sensor

The ultrasonic sensor measures distances of up to about 2.5 m, in whole centimetres. It returns None when nothing reflects the sound:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;

let sonar = brick.ultrasonic(S4).await?;
match sonar.read().await? {
    Some(d) if d < Centimeters(20) => println!("too close: {d}"),
    Some(d) => println!("{d}"),
    None => println!("nothing in range"),
}
Ok(()) });

By default the sensor measures continuously. ping() switches to single-shot mode and returns up to eight echoes, nearest first, which is useful when several objects are in view:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;
let sonar = brick.ultrasonic(S4).await?;
let echoes = sonar.ping().await?;           // [Option<Centimeters>; 8]
Ok(()) });

The ultrasonic sensor is an I²C device. sonar.bus() gives access to its registers, see I²C devices.

Other analog sensors

Use RawSensor for sensors without a dedicated type, such as the RCX temperature, light and rotation sensors, or your own electronics. Pick a sensor type and mode, and read all values the firmware reports:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::{prelude::*, protocol::{SensorMode, SensorType}};

let temp = brick.raw_sensor(S2, SensorType::Temperature, SensorMode::Celsius).await?;
let values = temp.read().await?;
println!("{} °C (raw {})", f32::from(values.scaled) / 10.0, values.raw);
temp.reconfigure(SensorType::Custom, SensorMode::Raw).await?;
Ok(()) });

InputValues contains:

  • raw: the A/D value,
  • normalized: 0–1023,
  • scaled: depends on the mode,
  • valid: false right after a mode change, until fresh data arrives.

Common methods

Every sensor has:

MethodReturns
port()The InputPort, for the low-level API
brick()The brick
values()The raw InputValues from the firmware
release()Unconfigures the port and returns the claimed Port

Dropping a sensor releases its port without unconfiguring it. Call release() or brick.stop_all() to switch the sensor off.

Timing

After a sensor is configured, the firmware needs a moment before readings are valid. The first reading after taking a sensor may still be stale: check values().await?.valid if it matters. The ultrasonic sensor additionally needs about 100 ms to power up. nxt-rs waits for that automatically.