diff --git a/factory/src/led.rs b/factory/src/led.rs new file mode 100644 index 0000000..e3fa3ab --- /dev/null +++ b/factory/src/led.rs @@ -0,0 +1,41 @@ +use embedded_hal::blocking::delay::DelayMs; +use esp_idf_hal::delay::Ets; +use esp_idf_hal::peripherals::Peripherals; +use esp_idf_hal::rmt::config::TransmitConfig; +use esp_idf_hal::rmt::{FixedLengthSignal, PinState, Pulse, Transmit}; +use std::time::Duration; + +pub fn set_ota_led() { + let peripherals = Peripherals::take().unwrap(); + let led = peripherals.pins.gpio8.into_output().unwrap(); + let channel = peripherals.rmt.channel0; + let config = TransmitConfig::new().clock_divider(1); + let mut tx = Transmit::new(led, channel, &config).unwrap(); + + let rgb = 0xffa500; // Orange + + let ticks_hz = tx.counter_clock().unwrap(); + let t0h = Pulse::new_with_duration(ticks_hz, PinState::High, &ns(350)).unwrap(); + let t0l = Pulse::new_with_duration(ticks_hz, PinState::Low, &ns(800)).unwrap(); + let t1h = Pulse::new_with_duration(ticks_hz, PinState::High, &ns(700)).unwrap(); + let t1l = Pulse::new_with_duration(ticks_hz, PinState::Low, &ns(600)).unwrap(); + + let mut signal = FixedLengthSignal::<24>::new(); + for i in 0..24 { + let bit = 2_u32.pow(i) & rotate_rgb(rgb) != 0; + let (high_pulse, low_pulse) = if bit { (t1h, t1l) } else { (t0h, t0l) }; + signal.set(i as usize, &(high_pulse, low_pulse)).unwrap(); + } + tx.start_blocking(&signal).unwrap(); + Ets.delay_ms(10u8); +} + +fn ns(nanos: u64) -> Duration { + Duration::from_nanos(nanos) +} + +fn rotate_rgb(rgb: u32) -> u32 { + let b_mask: u32 = 0xff; + let blue = (rgb & b_mask) << 16; + blue | (rgb >> 8) +} diff --git a/factory/src/main.rs b/factory/src/main.rs index 2a6d7d8..194f812 100644 --- a/factory/src/main.rs +++ b/factory/src/main.rs @@ -1,5 +1,7 @@ +mod led; mod ota; mod sdcard; +use crate::led::set_ota_led; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use log::{error, info, warn}; use ota::{run_sdcard_ota_update, set_boot_main_app, UPDATE_BIN_PATH}; @@ -14,6 +16,7 @@ fn main() { esp_idf_sys::link_patches(); thread::sleep(Duration::from_secs(10)); + set_ota_led(); info!("Hello, world! Mounting sd card..."); sdcard::mount_sd_card(); info!("SD card mounted! Checking for update..."); diff --git a/factory/table.csv b/factory/table.csv index ef11890..b207bd0 100644 --- a/factory/table.csv +++ b/factory/table.csv @@ -3,5 +3,5 @@ nvs, data, nvs, 0x9000, 0x4000, otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 377K, -ota_0, app, ota_0, 0x70000, 3648K, +factory, app, factory, 0x10000, 448K, +ota_0, app, ota_0, 0x80000, 3584K, diff --git a/sphinx-key/src/core/events.rs b/sphinx-key/src/core/events.rs index d5e1e8c..5851abc 100644 --- a/sphinx-key/src/core/events.rs +++ b/sphinx-key/src/core/events.rs @@ -34,6 +34,7 @@ pub enum Status { ConnectingToMqtt, Connected, Signing, + Ota, } pub const ROOT_STORE: &str = "/sdcard/store"; @@ -116,7 +117,9 @@ pub fn make_event_loop( Event::Control(ref msg_bytes) => { log::info!("GOT A CONTROL MSG"); let cres = ctrlr.handle(msg_bytes); - if let Some(res) = handle_control_response(&root_handler, cres, network) { + if let Some(res) = + handle_control_response(&root_handler, cres, network, led_tx.clone()) + { let res_data = rmp_serde::to_vec(&res).expect("could not publish control response"); mqtt.publish(topics::CONTROL_RETURN, QOS, false, &res_data) @@ -133,6 +136,7 @@ fn handle_control_response( root_handler: &RootHandler, cres: anyhow::Result<(ControlMessage, ControlResponse)>, network: Network, + led_tx: mpsc::Sender, ) -> Option { match cres { Ok((control_msg, mut control_res)) => { @@ -171,7 +175,8 @@ fn handle_control_response( ControlResponse::Error(format!("OTA update cannot launch {:?}", e)) } else { thread::spawn(move || { - if let Err(e) = update_sphinx_key(params) { + led_tx.send(Status::Ota).unwrap(); + if let Err(e) = update_sphinx_key(params, led_tx) { log::error!("OTA update failed {:?}", e.to_string()); } else { log::info!("OTA flow complete, restarting esp..."); diff --git a/sphinx-key/src/ota.rs b/sphinx-key/src/ota.rs index afab4a5..344c731 100644 --- a/sphinx-key/src/ota.rs +++ b/sphinx-key/src/ota.rs @@ -1,8 +1,9 @@ +use crate::core::events::Status; use anyhow::{anyhow, Result}; use embedded_svc::http::client::Client; use embedded_svc::http::client::Request; use embedded_svc::http::client::Response; -use embedded_svc::http::Status; +use embedded_svc::http::Status as HttpStatus; use embedded_svc::io::Read; use embedded_svc::ota::Ota; use esp_idf_svc::http::client::EspHttpClient; @@ -14,6 +15,7 @@ use sphinx_key_signer::control::OtaParams; use std::fs::{remove_file, File}; use std::io::BufWriter; use std::io::Write; +use std::sync::mpsc; const BUFFER_LEN: usize = 3072; const UPDATE_BIN_PATH: &str = "/sdcard/update.bin"; @@ -31,7 +33,7 @@ fn factory_reset() -> Result<()> { } } -fn get_update(params: OtaParams) -> Result<()> { +fn get_update(params: OtaParams, led_tx: mpsc::Sender) -> Result<()> { let configuration = EspHttpClientConfiguration { buffer_size: Some(BUFFER_LEN), buffer_size_tx: Some(BUFFER_LEN / 3), @@ -62,6 +64,7 @@ fn get_update(params: OtaParams) -> Result<()> { write_tot += w; i += 1; if i % 20 == 0 { + led_tx.send(Status::Ota).unwrap(); info!("Cumulative bytes read: {}", read_tot); info!("Cumulative bytes written: {}", write_tot); } @@ -71,9 +74,9 @@ fn get_update(params: OtaParams) -> Result<()> { Ok(()) } -pub fn update_sphinx_key(params: OtaParams) -> Result<()> { +pub fn update_sphinx_key(params: OtaParams, led_tx: mpsc::Sender) -> Result<()> { info!("Getting the update..."); - get_update(params)?; + get_update(params, led_tx)?; info!("Update written to sd card, performing factory reset"); factory_reset()?; info!("Factory reset completed!"); diff --git a/sphinx-key/src/periph/led.rs b/sphinx-key/src/periph/led.rs index 505aa95..059437d 100644 --- a/sphinx-key/src/periph/led.rs +++ b/sphinx-key/src/periph/led.rs @@ -19,15 +19,16 @@ pub struct Led { fn states() -> BTreeMap { let mut s = BTreeMap::new(); - s.insert(Status::Starting, (0x000001, 100)); - s.insert(Status::MountingSDCard, (0x000102, 100)); - s.insert(Status::SyncingTime, (0x000122, 100)); - s.insert(Status::WifiAccessPoint, (0x000100, 100)); - s.insert(Status::Configuring, (0x010000, 20)); - s.insert(Status::ConnectingToWifi, (0x010100, 350)); - s.insert(Status::ConnectingToMqtt, (0x010001, 100)); - s.insert(Status::Connected, (0x000101, 400)); - s.insert(Status::Signing, (0x111111, 100)); + s.insert(Status::Starting, (0x000001, 100)); // Blue + s.insert(Status::MountingSDCard, (0x000102, 100)); // Cyan + s.insert(Status::SyncingTime, (0x000122, 100)); // Cyan + s.insert(Status::WifiAccessPoint, (0x000100, 100)); // Green + s.insert(Status::Configuring, (0x010000, 20)); // Red + s.insert(Status::ConnectingToWifi, (0x010100, 350)); // Yellow + s.insert(Status::ConnectingToMqtt, (0x010001, 100)); // Purple + s.insert(Status::Connected, (0x000101, 400)); // Cyan + s.insert(Status::Signing, (0x111111, 100)); // White + s.insert(Status::Ota, (0xffa500, 100)); // Orange s }