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:
FileKind | Used for | Created by |
|---|---|---|
Fragmented | Most files | create, write, upload (default) |
Linear | Executables: .rxe, .ric, .rtm, .sys | Chosen automatically for those extensions |
Data | Logs you append to later | create_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.