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 two API tiers

nxt-rs has two public APIs. Both work on the same Brick, connection, error type and value types, and you can switch between them at any point.

High level: the robot

For building robots. You work with devices, not protocol commands:

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

let (motor, light) = brick.take((A.motor(), S3.light())).await?;
motor.run(power!(40)).degrees(degrees!(90)).wait().await?;
let level: Percent = light.read().await?;
Ok(()) });
  • Devices claim their ports, so two parts of your program can’t fight over one socket.
  • Sensor modes are part of the type, so readings come back as bool, Percent, Option<Centimeters> or ColorName.
  • Files, searches and module handles close themselves.

Low level: the protocol, typed

For tools, for porting code written against LEGO’s Bluetooth Developer Kit, and for anything the high-level API doesn’t model:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::{port::{OutputPort, InputPort}, protocol::*, units::*};

let d = brick.direct();
d.set_output_state(OutputPort::A, OutputState {
    power: Power::new(40)?,
    mode: OutputMode::MOTOR_ON,
    run_state: RunState::Running,
    tacho_limit: TachoLimit::Degrees(Degrees::new(90)?),
    ..OutputState::default()
}).await?;
d.set_input_mode(InputPort::S3, SensorType::LightActive, SensorMode::PercentFullScale).await?;
let values: InputValues = d.get_input_values(InputPort::S3).await?;
Ok(()) });
  • brick.direct() and brick.system() have one method per protocol command, with typed arguments and decoded replies.
  • brick.send(command) sends any Command, including ones you define. brick.raw_transact(bytes) deals in raw bytes.
  • Ports are plain enums (OutputPort::A, InputPort::S3) and handles are plain numbers. Nothing is claimed and nothing is cleaned up for you.

Mixing the tiers

The high-level API is built only on the public low-level API. Anything it does, you can do too, and you can drop down at any time:

From → toHow
high → lowmotor.port(), sensor.port() give the runtime port; motor.brick() gives the brick; reader.handle() gives the raw file handle
low → highbrick.motor(A), brick.port(S1) at any time; FileReader::from_handle / FileWriter::from_handle adopt raw handles
nxt_rs::doc_example(|brick| async move {
use nxt_rs::prelude::*;

let motor = brick.motor(C)?;
motor.run(power!(30)).degrees(degrees!(45)).wait().await?;

// Drop down for the full status record:
let status = motor.brick().direct().get_output_state(motor.port()).await?;
println!("block tacho count: {}", status.block_tacho_count);
Ok(()) });

A low-level call on a port that a high-level device owns won’t break the library. It may, however, change the device’s state behind its back. For example, set_input_mode on a light sensor’s port changes the mode its type claims. Do it knowingly.