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>orColorName. - 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()andbrick.system()have one method per protocol command, with typed arguments and decoded replies.brick.send(command)sends anyCommand, 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 → to | How |
|---|---|
| high → low | motor.port(), sensor.port() give the runtime port; motor.brick() gives the brick; reader.handle() gives the raw file handle |
| low → high | brick.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.