Remote control with an on-brick program
Commands sent over Bluetooth take about 60 ms each way, which is too slow for tight control. The usual solution is to split the work:
- an NXC program on the brick runs the fast loop: motors and sensors,
- your Rust program sends high-level commands and receives status through mailboxes.
On the brick
// remote.nxc — compile with: nbc -O=remote.rxe remote.nxc
task main() {
string cmd;
while (true) {
if (ReceiveRemoteString(MAILBOX1, true, cmd) == NO_ERR) {
if (cmd == "fwd") OnFwdSync(OUT_BC, 75, 0);
if (cmd == "back") OnRevSync(OUT_BC, 75, 0);
if (cmd == "left") OnFwdSync(OUT_BC, 50, -100);
if (cmd == "right") OnFwdSync(OUT_BC, 50, 100);
if (cmd == "stop") Off(OUT_BC);
SendResponseString(MAILBOX1, cmd);
}
if (SensorUS(IN_4) < 20) {
Off(OUT_BC);
SendResponseString(MAILBOX2, "obstacle");
}
Wait(10);
}
}
The program needs the ultrasonic sensor configured on port 4. Add
SetSensorLowspeed(IN_4); at the start of main in real code.
On the computer
use std::{io::BufRead, time::Duration};
use nxt_rs::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let brick = Brick::usb_first().block()?; // or Brick::bluetooth(..)
// Upload and start the program (see the upload recipe).
let program = FileName::new("remote.rxe")?;
brick.stop_program().block()?;
brick.files().upload(&program, &std::fs::read("remote.rxe")?).block()?;
brick.start_program(&program).block()?;
let messages = brick.messages();
println!("commands: fwd, back, left, right, stop");
for line in std::io::stdin().lock().lines() {
let cmd = line?;
messages.send(Mailbox::M1, cmd.trim()).block()?;
let ack = messages.wait(Mailbox::M1, Duration::from_millis(20)).block()?;
println!("brick: {}", ack.as_str().unwrap_or("?"));
if let Some(alert) = messages.recv(Mailbox::M2).block()? {
println!("brick: {}", alert.as_str().unwrap_or("?"));
}
}
brick.stop_program().block()?;
Ok(())
}
Why this works well
- The brick reacts to obstacles within 10 ms, whatever the Bluetooth latency.
- The computer only sends a command when something changes.
- Mailbox 1 carries commands and acknowledgements; mailbox 2 carries events. Use separate mailboxes for separate streams.