#![feature(type_alias_impl_trait)]
use clap::{Parser, Subcommand};
use core::future::Future;
use embedded_io::adapters::FromTokio;
use embedded_update::{
device::{Serial, Simulator},
service::InMemory,
DeviceStatus, FirmwareDevice, FirmwareUpdater, UpdaterConfig,
};
use std::fs::File;
use std::io::Read;
use std::path::PathBuf;
use drgdfu::*;
#[derive(Parser, Debug)]
struct Args {
#[clap(short, long, parse(from_occurrences))]
verbose: usize,
#[clap(subcommand)]
mode: Mode,
}
#[derive(Debug, Subcommand, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Mode {
Generate {
#[clap(long)]
version: String,
#[clap(long)]
file: PathBuf,
},
Upload {
#[clap(subcommand)]
transport: Transport,
},
}
#[derive(Debug, Subcommand, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum Transport {
#[cfg(feature = "ble")]
BleGatt {
#[clap(long)]
enable_discovery: bool,
#[clap(long)]
device: String,
#[clap(subcommand)]
source: FirmwareSource,
},
Serial {
#[clap(long)]
port: PathBuf,
#[clap(subcommand)]
source: FirmwareSource,
},
Simulated {
#[clap(long)]
version: String,
#[clap(subcommand)]
source: FirmwareSource,
},
}
#[derive(Debug, Subcommand, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub enum FirmwareSource {
File {
#[clap(long)]
firmware: PathBuf,
#[clap(long)]
metadata: PathBuf,
},
Cloud {
#[clap(long)]
http: String,
#[clap(long)]
application: String,
#[clap(long)]
device: String,
#[clap(long)]
password: String,
},
}
impl FirmwareSource {
async fn run<F: FirmwareDevice>(&mut self, mut d: F) -> Result<(), anyhow::Error> {
match self {
FirmwareSource::File { firmware, metadata } => {
let metadata = FirmwareFileMeta::from_file(&metadata)?;
let mut file = File::open(&firmware)?;
let mut data = Vec::new();
file.read_to_end(&mut data)?;
let service = InMemory::new(metadata.version.as_bytes(), &data[..]);
let mut updater = FirmwareUpdater::new(service, Default::default());
loop {
if let Ok(DeviceStatus::Synced(_)) = updater.run(&mut d, &mut Timer).await {
break;
}
}
}
FirmwareSource::Cloud {
http,
application,
device,
password,
} => {
let user = format!("{}@{}", device, application);
let timeout = std::time::Duration::from_secs(30);
let service = DrogueFirmwareService::new(http, &user, password, timeout);
let mut updater = FirmwareUpdater::new(
service,
UpdaterConfig {
timeout_ms: 30_000,
backoff_ms: 5_000,
},
);
loop {
if let Ok(DeviceStatus::Synced(_)) = updater.run(&mut d, &mut Timer).await {
break;
}
}
}
}
println!("Firmware updated");
Ok(())
}
}
#[tokio::main(flavor = "current_thread")]
async fn main() -> anyhow::Result<()> {
let args = Args::parse();
stderrlog::new().verbosity(args.verbose).init().unwrap();
match args.mode {
Mode::Generate { version, file } => {
let firmware = FirmwareFileMeta::new(&version, &file)?;
println!("{}", serde_json::to_string(&firmware)?);
}
Mode::Upload { transport } => match transport {
#[cfg(feature = "ble")]
Transport::BleGatt {
enable_discovery,
device,
mut source,
} => {
use btleplug::api::{Central, Manager as _, ScanFilter};
use btleplug::platform::Manager;
let manager = Manager::new().await?;
let central = manager
.adapters()
.await?
.into_iter()
.nth(0)
.ok_or(anyhow::anyhow!("no adapter found"))?;
if enable_discovery {
central.start_scan(ScanFilter::default()).await?;
}
let s = GattBoard::new(&device, central);
source.run(s).await?;
}
Transport::Serial { port, mut source } => {
let p: String = port.to_str().unwrap().to_string();
let builder = tokio_serial::new(p, 115200);
let s = Serial::new(FromTokio::new(tokio_serial::SerialStream::open(&builder)?));
source.run(s).await?;
}
Transport::Simulated {
version,
mut source,
} => {
let s = Simulator::new(version.as_bytes());
source.run(s).await?;
}
},
}
Ok(())
}
pub struct Timer;
impl embedded_hal_async::delay::DelayUs for Timer {
type Error = core::convert::Infallible;
type DelayUsFuture<'m> = impl Future<Output = Result<(), Self::Error>> + 'm where Self: 'm;
fn delay_us(&mut self, i: u32) -> Self::DelayUsFuture<'_> {
async move {
tokio::time::sleep(tokio::time::Duration::from_micros(i as u64)).await;
Ok(())
}
}
type DelayMsFuture<'m> = impl Future<Output = Result<(), Self::Error>> + 'm where Self: 'm;
fn delay_ms(&mut self, i: u32) -> Self::DelayMsFuture<'_> {
async move {
tokio::time::sleep(tokio::time::Duration::from_millis(i as u64)).await;
Ok(())
}
}
}