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

Async and blocking code

Every operation that talks to the brick is a Future. That lets several tasks share one brick, drive motors concurrently and wait without blocking threads.

With an async runtime

nxt-rs works with any runtime: tokio, async-std, smol, embassy-style executors, or a simple block_on. Just .await:

use nxt_rs::prelude::*;

#[tokio::main]
async fn main() -> nxt_rs::Result<()> {
    let brick = Brick::usb_first().await?;
    println!("battery: {}", brick.battery().await?);
    Ok(())
}

The Bluetooth transport is the one exception: its backend (bluer) needs a tokio runtime.

Without one: .block()

Import nxt_rs::blocking::Block (it’s in the prelude) and add .block() to any future or builder:

use nxt_rs::prelude::*;

fn main() -> nxt_rs::Result<()> {
let brick = Brick::mock(); // or Brick::usb_first().block()?
let battery = brick.battery().block()?;
let motor = brick.motor(A)?;
motor.run(power!(50)).degrees(degrees!(90)).wait().block()?;
let raw = brick.direct().get_battery_level().block()?;
Ok(()) }

.block() works the same on both API tiers, so there is no separate “blocking” copy of the API to learn. With the bluetooth feature enabled, it runs futures on a shared tokio runtime. Otherwise it uses a lightweight executor.

Don’t call .block() from inside an async task: it blocks that task’s thread. Use .await there.

nxt_rs::blocking::SyncIo adapts the async file reader and writer to std::io::Read/Write. See Files.

Concurrency

Brick is Clone + Send + Sync. Each request and its reply happen under an internal lock, so concurrent tasks never receive each other’s replies:

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

let (a, b) = brick.take((A.motor(), B.motor())).await?;
// Start both motors concurrently.
let (ra, rb) = zip(a.run(power!(50)).into_future(), b.run(power!(-50)).into_future()).await;
ra?;
rb?;
Ok(()) });

The brick handles one command at a time, so concurrency saves waiting time in your program, not time on the wire.

Background keep-alive

The NXT switches itself off after its sleep timeout. To keep it awake during a long program, spawn brick.keep_alive_task(interval) on your runtime:

async fn f(brick: nxt_rs::Brick<nxt_rs::transport::Usb>) {
use std::time::Duration;
tokio::spawn(brick.keep_alive_task(Duration::from_secs(60)));
}