use anyhow::{ Context, Result, }; use clap::{ Parser, Subcommand, }; use env_logger; use hex; use improv_setup::improv::{ IMPROV_HEADER, IMPROV_VERSION, PacketType, RPCCommand, CurrentState, calculate_checksum, }; use log::{ debug, log_enabled, info, Level, }; use tokio_serial; #[derive(Subcommand, Clone)] enum DeviceCommands { /// Set wifi credentials SetWifi { /// SSID of the network ssid: String, /// Password for the SSID password: String, }, } #[derive(Subcommand, Clone)] enum Commands { /// List available serial devices ListDevices, /// Device Device { /// Path to the serial device path: String, /// Baud rate used for connecting to the serial device #[arg(long, default_value_t = 115200)] baud_rate: u32, #[command(subcommand)] device_command: Option, }, } #[derive(Parser)] #[command(version, about, long_about = None)] struct Cli { #[command(subcommand)] command: Option, } fn to_ascii_debug(bytes: &Vec) -> String { let mut out = String::new(); for b in bytes { if b.is_ascii_graphic() { out += &b.escape_ascii().to_string(); } else { out.push_str("."); } } return out; } fn find_begin_of_improv_packet(buffer: &Vec) -> Result { let mut improv_header_char: usize = 0; for (i, b) in buffer.iter().enumerate() { if b == &IMPROV_HEADER[improv_header_char] { improv_header_char += 1; if improv_header_char >= IMPROV_HEADER.len() { return Ok(i - (IMPROV_HEADER.len() - 1)); } } else { improv_header_char = 0; if b == &IMPROV_HEADER[improv_header_char] { improv_header_char += 1; } } } return Err(String::from("Improv header not found")); } #[tokio::main] async fn main() -> Result<()>{ env_logger::init(); let cli = Cli::parse(); let command: Commands = match &cli.command { Some(command) => command.clone(), None => Commands::ListDevices, }; match &command { Commands::ListDevices => { println!("{}", tokio_serial::available_ports() .unwrap() .iter() .map(|serialport| serialport.port_name.clone()) .fold(String::new(), |a, b| a + &b + &String::from("\n"))); }, Commands::Device {path, baud_rate, device_command} => { let mut rpc_command_request_current_state: Vec = Vec::new(); rpc_command_request_current_state.extend(IMPROV_HEADER); rpc_command_request_current_state.push(IMPROV_VERSION); rpc_command_request_current_state.push(PacketType::RPCCommand as u8); let mut data: Vec = Vec::new(); data.push(RPCCommand::RequestCurrentState as u8); // command data.push(0x00); // command data length rpc_command_request_current_state.push(data.len().try_into().unwrap()); // length rpc_command_request_current_state.extend(data.clone()); // data let checksum: u8 = calculate_checksum(&rpc_command_request_current_state); println!("{}", hex::encode([checksum.clone()])); rpc_command_request_current_state.push(checksum); println!("{}", hex::encode(&rpc_command_request_current_state)); println!("{}", to_ascii_debug(&rpc_command_request_current_state)); let mut serial_interface = tokio_serial::new(path, *baud_rate).open().unwrap(); serial_interface.write(&rpc_command_request_current_state).unwrap(); let mut buffer: Vec = Vec::new(); serial_interface.read_to_end(&mut buffer); println!("{}", hex::encode(&buffer)); println!("{}", to_ascii_debug(&buffer)); println!("{}", std::str::from_utf8(&buffer).unwrap_or("")); let improv_packet_offset = find_begin_of_improv_packet(&buffer).unwrap(); println!("{}", improv_packet_offset); let mut packet_index = improv_packet_offset; // skip header packet_index += 6; // version println!("Version: {}", &buffer[packet_index]); packet_index += 1; // type let packet_type = PacketType::try_from(&buffer[packet_index]).unwrap(); println!("Type: {}", &packet_type); packet_index += 1; if packet_type == PacketType::CurrentState { // skip length packet_index +=1; let current_state = CurrentState::try_from(&buffer[packet_index]).unwrap(); println!("Current state: {}", ¤t_state); packet_index += 1; } }, }; Ok(()) }