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

Files and programs

The brick has a small flash file system for programs (.rxe), sounds (.rso), pictures (.ric) and data files. brick.files() gives access to it:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::protocol::FileName;

let files = brick.files();
let name = FileName::new("notes.txt")?;

files.write(&name, b"hello, brick").await?;         // create (fails if it exists)
files.upload(&name, b"replaced").await?;            // create or replace
let data = files.read(&name).await?;                 // Vec<u8>
assert!(files.exists(&name).await?);
files.delete(&name).await?;
Ok(()) });

File names

Names are at most 15 characters, a dot and a 3-character extension, using printable ASCII characters. Spaces are allowed: the firmware’s own sounds are called ! Startup.rso and ! Click.rso. FileName::new checks this. Search patterns may use *, for example *.* or *.rxe.

Listing files

nxt_rs::doc_example(|brick| async move {
use nxt_rs::protocol::FileName;

for file in brick.files().list(&FileName::new("*.*")?).await? {
    println!("{:<20} {:>6} bytes", file.name, file.size);
}
Ok(()) });

find returns the same results as a stream, without collecting them first:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::protocol::FileName;

let mut programs = brick.files().find(&FileName::new("*.rxe")?);
while let Some(program) = programs.next().await {
    println!("{}", program?.name);
}
Ok(()) });

Streaming

For large files, read and write in chunks. A telegram carries at most 58 bytes of file data when reading and 61 when writing, so a 10 KB program takes a couple of hundred requests.

nxt_rs::doc_example(|brick| async move {
use nxt_rs::protocol::FileName;

let name = FileName::new("log.dat")?;
let mut writer = brick.files().create(&name, 1000).await?;   // exact size up front
writer.write_all(&[0u8; 1000]).await?;
writer.close().await?;

let mut reader = brick.files().open(&name).await?;
println!("{} bytes", reader.size());
while let Some(chunk) = reader.read_chunk().await? {
    // chunk derefs to &[u8]
}
Ok(()) });

FileReader implements futures_io::AsyncRead, and FileWriter implements AsyncWrite. In synchronous code, wrap them in nxt_rs::blocking::SyncIo to get std::io::Read / Write:

use std::io::Read;
use nxt_rs::{prelude::*, blocking::SyncIo};

fn main() -> Result<(), Box<dyn std::error::Error>> {
let brick = Brick::mock();
brick.files().write(&FileName::new("notes.txt")?, b"text").block()?;
let mut text = String::new();
SyncIo(brick.files().open(&FileName::new("notes.txt")?).block()?).read_to_string(&mut text)?;
Ok(()) }

File kinds

The brick stores files in three ways:

FileKindUsed forCreated by
FragmentedMost filescreate, write, upload (default)
LinearExecutables: .rxe, .ric, .rtm, .sysChosen automatically for those extensions
DataLogs you append to latercreate_with(name, max_size, FileKind::Data)
nxt_rs::doc_example(|brick| async move {
use nxt_rs::{fs::FileKind, protocol::FileName};

let log = FileName::new("run.log")?;
let mut w = brick.files().create_with(&log, 4096, FileKind::Data).await?;
w.write_all(b"started\n").await?;
w.close().await?;

let mut w = brick.files().append(&log).await?;
w.write_all(b"still running\n").await?;
Ok(()) });

A fragmented or linear file must be written to exactly the size given at creation. A data file can be shorter, and grows by appending.

Handles close themselves

The brick has very few file handles. Readers, writers and searches close theirs when they’re dropped: the close is sent just before the next command. Call close().await explicitly if you want to see errors.

With the low-level system commands, handles are plain numbers and closing them is up to you. To hand a raw handle over to the high-level API, use FileReader::from_handle or FileWriter::from_handle.

Uploading and running programs

Compile NXC programs with NBC (nbc -O=prog.rxe prog.nxc), then:

nxt_rs::doc_example(|brick| async move {
use nxt_rs::protocol::FileName;

let rxe = vec![0u8; 64];
// let rxe = std::fs::read("prog.rxe")?;
let name = FileName::new("prog.rxe")?;
brick.stop_program().await?;
brick.files().upload(&name, &rxe).await?;
brick.start_program(&name).await?;
Ok(()) });

See the upload recipe for a complete tool.

Free space

brick.info().await?.free_flash reports the free flash in bytes. The NXT has about 256 KB of flash in total, part of which holds the firmware.