use clap::Parser; use tokio::{ io::AsyncReadExt, sync::Mutex, }; use tokio_serial::SerialPortBuilderExt; #[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] pub enum Uplink { Ping, } #[derive(Debug, Clone, PartialEq, PartialOrd, serde::Serialize, serde::Deserialize)] pub enum Downlink { Pong, PDM(heapless::Vec), } #[derive(clap::Parser, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct Args { #[clap(short, long)] #[cfg_attr(target_os = "linux", clap(default_value = "/dev/ttyACM0"))] pub serial_port: String, } #[tokio::main] async fn main() -> eyre::Result<()> { let args: Args = Args::parse(); let mut port = tokio_serial::new(args.serial_port, 115200).open_native_async()?; let mut cobs_acc = postcard::accumulator::CobsAccumulator::<256>::new(); let spec = hound::WavSpec { channels: 1, sample_rate: 48000 * 8, bits_per_sample: 16, sample_format: hound::SampleFormat::Int, }; let writer = tokio::task::spawn_blocking(move || hound::WavWriter::create("data.wav", spec)).await??; let writer = std::sync::Arc::new(Mutex::new(Some(writer))); tokio::task::spawn({ let writer = writer.clone(); async move { tokio::signal::ctrl_c().await.unwrap(); let mut writer = writer.lock().await; let writer = writer.take().unwrap(); writer.finalize().unwrap(); } }); loop { let mut bytes = [0u8; 1024]; let n = port.read(&mut bytes).await?; if n == 0 { eyre::bail!("serial port closed"); } for msg in molybdos_lib::util::cobs::do_deserialize::(&mut cobs_acc, &bytes[..n]) { match msg { Downlink::PDM(data) => { let writer = writer.clone(); tokio::task::spawn_blocking(move || { let mut writer = writer.try_lock().unwrap(); let writer = writer.as_mut().unwrap(); for b in data { for bit in 0..8 { let sample = (b >> bit) & 1; writer .write_sample(if sample == 1 { i16::MAX } else { i16::MIN }) .unwrap(); } } }) .await?; }, _ => { println!("{msg:?}"); }, } } } }