use ::rocket::async_main; use anyhow::Result; use std::{path::PathBuf, time::Duration}; use clap::Parser; use espflash::{elf::FirmwareImageBuilder, Chip, FlashSize, PartitionTable}; use rocket::{response::content, State}; #[macro_use] extern crate rocket; #[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] struct Args { /// chip name #[clap(short, long)] chip: Chip, /// path to bootloader #[clap(short, long)] bootloader: Option, /// path to partition table csv #[clap(short, long)] partition_table: Option, elf: PathBuf, } #[get("/bootloader.bin")] fn bootloader(data: &State) -> Vec { data.bootloader.clone() } #[get("/partitions.bin")] fn partitions(data: &State) -> Vec { data.partitions.clone() } #[get("/firmware.bin")] fn firmware(data: &State) -> Vec { data.firmware.clone() } #[get("/")] fn index() -> content::RawHtml<&'static str> { content::RawHtml( "

ESP Web Flaher



NOTE: Make sure to close anything using your devices com port (e.g. Serial monitor)
Your browser does not support the Web Serial API. Try Chrome
", ) } #[get("/manifest.json")] fn manifest() -> content::RawJson<&'static str> { content::RawJson( r#" { "name": "ESP Application", "new_install_prompt_erase": true, "builds": [ { "chipFamily": "ESP32", "parts": [ { "path": "bootloader.bin", "offset": 4096 }, { "path": "partitions.bin", "offset": 32768 }, { "path": "firmware.bin", "offset": 65536 } ] }, { "chipFamily": "ESP32-C3", "parts": [ { "path": "bootloader.bin", "offset": 0 }, { "path": "partitions.bin", "offset": 32768 }, { "path": "firmware.bin", "offset": 65536 } ] }, { "chipFamily": "ESP32-S2", "parts": [ { "path": "bootloader.bin", "offset": 4096 }, { "path": "partitions.bin", "offset": 32768 }, { "path": "firmware.bin", "offset": 65536 } ] }, { "chipFamily": "ESP32-S3", "parts": [ { "path": "bootloader.bin", "offset": 0 }, { "path": "partitions.bin", "offset": 32768 }, { "path": "firmware.bin", "offset": 65536 } ] } ] } "#, ) } struct PartsData { chip: String, bootloader: Vec, partitions: Vec, firmware: Vec, } fn prepare() -> Result { let opts = Args::parse(); let elf = std::fs::read(opts.elf)?; let p = if let Some(p) = &opts.partition_table { Some(PartitionTable::try_from_bytes(std::fs::read(p)?)?) } else { None }; let b = if let Some(p) = &opts.bootloader { Some(std::fs::read(p)?) } else { None }; let firmware = FirmwareImageBuilder::new(&elf) .flash_size(Some(FlashSize::Flash4Mb)) // TODO make configurable .build()?; let chip = opts.chip; let chip_name = match chip { Chip::Esp32 => "ESP32", Chip::Esp32c3 => "ESP32-C3", Chip::Esp32s2 => "ESP32-S2", Chip::Esp32s3 => "ESP32-S3", Chip::Esp8266 => "ESP8266", }; let image = chip.get_flash_image(&firmware, b, p, None, None)?; let parts: Vec<_> = image.flash_segments().collect(); let bootloader = &parts[0]; let partitions = &parts[1]; let app = &parts[2]; Ok(PartsData { chip: chip_name.to_string(), bootloader: bootloader.data.to_vec(), partitions: partitions.data.to_vec(), firmware: app.data.to_vec(), }) } fn main() -> Result<()> { let data = prepare()?; std::thread::spawn(|| { std::thread::sleep(Duration::from_millis(1000)); opener::open_browser("http://127.0.0.1:8000/").ok(); }); async_main(async move { let _res = rocket::build() .mount( "/", routes![index, manifest, bootloader, partitions, firmware], ) .manage(data) .launch() .await .expect("Problem launching server"); }); Ok(()) }