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

Values and units

The NXT protocol is full of numbers with restricted ranges: motor power is −100…100, tone frequencies 200…14 000 Hz, file names at most 15+3 characters. nxt-rs wraps each one in a small type that can only hold valid values. You validate once, when you create the value, and the rest of your program can rely on it.

Creating values

Each type has a fallible constructor. Literals also have a macro that checks at compile time:

use nxt_rs::{power, degrees, turn_ratio, units::*};

fn main() -> nxt_rs::Result<()> {
let fast = power!(80);                 // checked by the compiler
let computed = Power::new(-35)?;       // checked at run time
let clamped = Power::saturating(250);  // clamped to 100
assert_eq!(clamped, Power::MAX);

let quarter = degrees!(90);
let two_turns = Degrees::from_rotations(2)?;
let spin = turn_ratio!(100);
Ok(()) }
let too_fast = nxt_rs::power!(101);    // error: power must be in -100..=100
let nothing = nxt_rs::degrees!(0);     // error: degrees must be non-zero

The types

TypeRangeNotes
Power−100 … 100power!, Power::new, Power::saturating, .reverse(), -p
TurnRatio−100 … 100turn_ratio!; 0 straight, ±50 one wheel stopped, ±100 spin
Degrees1 … u32::MAXdegrees!, Degrees::from_rotations
TachoLimitUnlimited or Degrees(d)“Run forever” is its own variant, never 0
Hz200 … 14 000Note constants Hz::C4 … Hz::C5
Millis160 … 65 535 msFrom Duration with try_from
Percent0 … 100Light and sound readings
Centimeters0 … 254Ultrasonic distance
Millivoltsany u16Battery voltage; .volts()

Protocol strings and payloads work the same way:

TypeRule
FileNameprintable ASCII, at most 15 characters + . + 3 (* allowed for searches)
BrickNameASCII, 1–15 characters
Messageat most 58 bytes
LsBytesat most 16 bytes (one I²C transfer)
MailboxM1 … M10
I2cAddresseven (8-bit form)
Slope0 … 31
use nxt_rs::protocol::{FileName, Message};

fn main() -> nxt_rs::Result<()> {
let program: FileName = "robot.rxe".parse()?;
assert_eq!(program.extension(), Some("rxe"));
assert!(FileName::new("a-very-long-file-name.rxe").is_err());

let msg = Message::new("hello")?;
assert_eq!(msg.as_str(), Some("hello"));
Ok(()) }

Errors

A failed constructor returns a ValueError naming the value and its valid range. ? converts it into nxt_rs::Error::Value:

use nxt_rs::units::Power;

let err = Power::new(101).unwrap_err();
assert_eq!(err.to_string(), "invalid power: must be in -100..=100");