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:
| Sensor | Take with | read() returns |
|---|---|---|
| Touch | brick.touch(S1) / S1.touch() | bool |
| Light | brick.light(S3) / S3.light() | Percent |
| Sound | brick.sound(S2) / S2.sound() | Percent |
| Colour (NXT 2.0), detecting | brick.color(S3) / S3.color() | Option<ColorName> |
| Colour, lamp or ambient | color.lamp::<Red>(), color.ambient() | Percent |
| Ultrasonic | brick.ultrasonic(S4) / S4.ultrasonic() | Option<Centimeters> |
| Anything analog | brick.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 type | What it does | read() |
|---|---|---|
Detect | Recognises black, blue, green, yellow, red and white | Option<ColorName> |
Lamp<Red>, Lamp<Green>, Lamp<Blue> | Lights the lamp in one colour and measures the reflection | Percent |
Ambient | Lamp off, measures ambient light | Percent |
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:falseright after a mode change, until fresh data arrives.
Common methods
Every sensor has:
| Method | Returns |
|---|---|
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.