Hi! I wrote this code with the serialport-rs and dialoguer crate.
fn run(
serial: &mut Box<dyn SerialPort>,
faders: &HashMap<u8, u8>,
midi: &mut MidiOutputConnection,
) {
let stop = Arc::new(AtomicBool::new(false));
let stop_me = stop.clone();
let mut buf: [u8; 2] = [0; 2];
let thread = thread::spawn(move || {
let input = ask_for_quitting();
if input == "q" {
stop_me.store(true, Ordering::Relaxed);
}
});
loop {
serial.read(buf.as_mut_slice()).unwrap();
let arduino_pin = &buf[0];
let value = &buf[1];
let Some(cc) = faders.get(&arduino_pin) else { continue };
let message = midi_control::control_change(Channel::Ch1, *cc, *value);
let message_byte: Vec<u8> = message.into();
let _ = &mut midi.send(&message_byte).unwrap();
if stop.load(Ordering::Relaxed) == true {
break;
}
}
thread.join().unwrap();
}
As you can see, I'm using an atomic bool to communicate between the key input thread and the serial loop. When I press "q" during the loop, the loop doesn't break. It breaks only when I get a new buffer read from the serial port.
How can I immediately interrupt the loop by pressing the key input?
Thanks.
When I press "q" I have to press Enter too. It's by design from the dialogue crate in input mode.
I noticed that if I comment serial.read() then pressing Enter the program quit as expected.
I'm guessing the read() method is blocking and won't return until it receives data. Maybe check if there is an option for setting a timeout so the read() method will error out after a pre-defined time (e.g. 100ms) and give you a chance to check the stop flag again.
unfortunately, the serialport crate only support reading the serial port via std::io::Read trait, you can't do much besides setting a timeout and polling the device for reading.
you can try tokio-serial, I don't know how the dialoguer crate would fit tokio though:
#[tokio::main]
async fn main() {
let mut port = tokio_serial::new("COM1", 9600).open_native_async().unwrap();
let stop = Arc::new(tokio::sync::Notify::new());
let notifier = stop.clone();
// you can spawn regular thread just fine, you don't have to spawn async task,
// I suppose dialoguer would work just fine, but I didn't test it
tokio::task::spawn_blocking(move || {
let input = ask_for_quitting();
if input == "q" {
notifier.notify_one();
}
});
let mut buf = [0; 1024];
loop {
tokio::select! {
io_result = port.read(&mut buf) => {
let nbytes = io_result.unwrap();
println!("received {} bytes", nbytes);
let data = &buf[..nbytes];
// use data
}
_ = stop.notified() => {
println!("notified");
break;
}
}
}
}