Skip to content

Commit

Permalink
Tools: Add run-fap to run a given FAP on a connected Flipper Zero
Browse files Browse the repository at this point in the history
  • Loading branch information
str4d committed Mar 17, 2023
1 parent 19b185e commit 6f24a99
Show file tree
Hide file tree
Showing 4 changed files with 148 additions and 0 deletions.
33 changes: 33 additions & 0 deletions tools/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ crossterm = "0.26.1"
csv = "1.1.6"
doxygen-rs = "0.3.1"
once_cell = "1.17.1"
rand = "0.8"
regex = "1.7.1"
serde = { version = "1.0.152", features = ["derive"] }
serde_json = "1.0.91"
Expand Down
109 changes: 109 additions & 0 deletions tools/src/bin/run-fap.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
use std::{fmt, io, path::PathBuf, thread, time::Duration};

use clap::Parser;
use flipperzero_tools::{serial, storage};
use rand::{thread_rng, Rng};

#[derive(Parser)]
#[command(author, version, about, long_about = None)]
struct Cli {
/// Serial port (e.g. `COM3` on Windows or `/dev/ttyUSB0` on Linux)
#[arg(short, long)]
port: Option<String>,

/// Path to the FAP binary to run.
fap: PathBuf,

/// Arguments to provide to the FAP binary.
args: Vec<String>,
}

enum Error {
FlipperZeroNotFound,
FailedToOpenSerialPort(serialport::Error),
FailedToStartSerialInterface(io::Error),
FailedToUploadFap(io::Error),
MkdirFailed(storage::FlipperPath, io::Error),
RemoveFailed(storage::FlipperPath, io::Error),
Io(io::Error),
}

impl fmt::Debug for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::FlipperZeroNotFound => write!(f, "unable to find Flipper Zero"),
Error::FailedToOpenSerialPort(e) => write!(f, "unable to open serial port: {}", e),
Error::FailedToStartSerialInterface(e) => {
write!(f, "unable to start serial interface: {}", e)
}
Error::FailedToUploadFap(e) => write!(f, "unable to upload FAP: {}", e),
Error::MkdirFailed(path, e) => write!(f, "unable to make directory '{}': {}", path, e),
Error::RemoveFailed(path, e) => write!(f, "unable to remove '{}': {}", path, e),
Error::Io(e) => e.fmt(f),
}
}
}

impl From<io::Error> for Error {
fn from(value: io::Error) -> Self {
Error::Io(value)
}
}

fn wait_for_idle(cli: &mut serial::SerialCli) -> io::Result<()> {
loop {
cli.send_and_wait_eol("loader info")?;
if cli
.consume_response()?
.contains("No application is running")
{
break Ok(());
}
thread::sleep(Duration::from_millis(200));
}
}

fn main() -> Result<(), Error> {
let cli = Cli::parse();

let port_info =
serial::find_flipperzero(cli.port.as_deref()).ok_or(Error::FlipperZeroNotFound)?;
let port = serialport::new(&port_info.port_name, serial::BAUD_115200)
.timeout(Duration::from_secs(30))
.open()
.map_err(Error::FailedToOpenSerialPort)?;
let mut store = storage::FlipperStorage::new(port);
store.start().map_err(Error::FailedToStartSerialInterface)?;

// Upload the FAP to a temporary directory.
let dest_dir =
storage::FlipperPath::from(format!("/ext/tmp-{:08x}", thread_rng().gen::<u32>()));
let dest_file = dest_dir.clone() + "rust-unit-tests.fap";
store
.mkdir(&dest_dir)
.map_err(|e| Error::MkdirFailed(dest_dir.clone(), e))?;
store
.send_file(&cli.fap, &dest_file)
.map_err(Error::FailedToUploadFap)?;

let cli = store.cli_mut();

// Wait for no application to be running.
wait_for_idle(cli)?;

// Run the FAP.
cli.send_and_wait_eol(&format!("loader open Applications {}\r", dest_file))?;

// Wait for the FAP to finish.
wait_for_idle(cli)?;

// Remove the FAP and temporary directory.
store
.remove(&dest_file)
.map_err(|e| Error::RemoveFailed(dest_file, e))?;
store
.remove(&dest_dir)
.map_err(|e| Error::RemoveFailed(dest_dir, e))?;

Ok(())
}
5 changes: 5 additions & 0 deletions tools/src/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,11 @@ impl FlipperStorage {
self.cli.port_mut()
}

/// Get mutable reference to underlying [`SerialCli`].
pub fn cli_mut(&mut self) -> &mut SerialCli {
&mut self.cli
}

/// List files and directories on the device.
pub fn list_tree(&mut self, path: &FlipperPath) -> io::Result<()> {
// Note: The `storage list` command expects that paths do not end with a slash.
Expand Down

0 comments on commit 6f24a99

Please sign in to comment.