//offer ota and config mode use std::{ io::BufRead, str::from_utf8, sync::{atomic::AtomicBool, Arc} }; use crate::{espota::OtaUpdate, BOARD_ACCESS}; use core::result::Result::Ok; use embedded_svc::http::Method; use esp_idf_hal::{delay::Delay, io::Write}; use esp_idf_svc::http::server::{Configuration, EspHttpServer}; use heapless::String; use serde::{Deserialize, Serialize}; use crate::{ config::{Config, WifiConfig}, plant_hal::PlantCtrlBoardInteraction, }; #[derive(Serialize, Debug)] struct SSIDList<'a> { ssids: Vec<&'a String<32>>, } #[derive(Serialize, Debug)] struct VersionInfo<'a> { git_hash: &'a str, build_time: &'a str, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct TestPump { pump: usize, } pub fn httpd_initial(reboot_now: Arc) -> Box> { let mut server = shared(); server .fn_handler("/", Method::Get, move |request| { let mut response = request.into_ok_response()?; response.write(include_bytes!("initial_config.html"))?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/wifiscan", Method::Post, move |request| { let mut response = request.into_ok_response()?; let mut board = BOARD_ACCESS.lock().unwrap(); match board.wifi_scan() { Err(error) => { response.write(format!("Error scanning wifi: {}", error).as_bytes())?; } Ok(scan_result) => { let mut ssids: Vec<&String<32>> = Vec::new(); scan_result.iter().for_each(|s| ssids.push(&s.ssid)); let ssid_json = serde_json::to_string(&SSIDList { ssids })?; println!("Sending ssid list {}", &ssid_json); response.write(ssid_json.as_bytes())?; } } anyhow::Ok(()) }) .unwrap(); server .fn_handler("/wifisave", Method::Post, move |mut request| { let mut buf = [0_u8; 2048]; let read = request.read(&mut buf); if read.is_err() { let error_text = read.unwrap_err().to_string(); println!("Could not parse wificonfig {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return anyhow::Ok(()); } let actual_data = &buf[0..read.unwrap()]; println!("raw {:?}", actual_data); println!("Raw data {}", from_utf8(actual_data).unwrap()); let wifi_config: Result = serde_json::from_slice(actual_data); if wifi_config.is_err() { let error_text = wifi_config.unwrap_err().to_string(); println!("Could not parse wificonfig {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return anyhow::Ok(()); } let mut board = BOARD_ACCESS.lock().unwrap(); board.set_wifi(&wifi_config.unwrap())?; let mut response = request.into_status_response(202)?; response.write("saved".as_bytes())?; reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); anyhow::Ok(()) }) .unwrap(); server } pub fn httpd(reboot_now: Arc) -> Box> { let mut server = shared(); server .fn_handler("/", Method::Get, move |request| { let mut response = request.into_ok_response()?; response.write(include_bytes!("config.html"))?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/get_config", Method::Get, move |request| { let mut response = request.into_ok_response()?; let mut board = BOARD_ACCESS.lock().unwrap(); match board.get_config() { Ok(config) => { let config_json = serde_json::to_string(&config)?; response.write(config_json.as_bytes())?; } Err(_) => { let config_json = serde_json::to_string(&Config::default())?; response.write(config_json.as_bytes())?; } } anyhow::Ok(()) }) .unwrap(); server .fn_handler("/set_config", Method::Post, move |mut request| { let mut buf = [0_u8; 3072]; let read = request.read(&mut buf); if read.is_err() { let error_text = read.unwrap_err().to_string(); println!("Could not parse config {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return anyhow::Ok(()); } let actual_data = &buf[0..read.unwrap()]; println!("Raw data {}", from_utf8(actual_data).unwrap()); let config: Result = serde_json::from_slice(actual_data); if config.is_err() { let error_text = config.unwrap_err().to_string(); println!("Could not parse config {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return Ok(()); } let mut board = BOARD_ACCESS.lock().unwrap(); board.set_config(&config.unwrap())?; let mut response = request.into_status_response(202)?; response.write("saved".as_bytes())?; reboot_now.store(true, std::sync::atomic::Ordering::Relaxed); Ok(()) }) .unwrap(); server } pub fn shared() -> Box> { let server_config = Configuration { stack_size: 8192, ..Default::default() }; let mut server: Box> = Box::new(EspHttpServer::new(&server_config).unwrap()); server .fn_handler("/version", Method::Get, |request| { let mut response = request.into_ok_response()?; let git_hash = env!("VERGEN_GIT_DESCRIBE"); let build_time = env!("VERGEN_BUILD_TIMESTAMP"); let version_info = VersionInfo { git_hash, build_time, }; let version_info_json = serde_json::to_string(&version_info)?; response.write(version_info_json.as_bytes())?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/bundle.js", Method::Get, |request| { let mut response = request.into_ok_response()?; response.write(include_bytes!("bundle.js"))?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/favicon.ico", Method::Get, |request| { let mut response = request.into_ok_response()?; response.write(include_bytes!("favicon.ico"))?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/ota", Method::Post, |mut request| { let ota = OtaUpdate::begin(); if ota.is_err() { let error_text = ota.unwrap_err().to_string(); request .into_status_response(500)? .write(error_text.as_bytes())?; return anyhow::Ok(()); } let mut ota = ota.unwrap(); println!("start ota"); //having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;) const BUFFER_SIZE: usize = 512; let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; let mut total_read: usize = 0; loop { let read = request.read(&mut buffer).unwrap(); total_read += read; println!("received {read} bytes ota {total_read}"); let to_write = &buffer[0..read]; let write_result = ota.write(to_write); if write_result.is_err() { let error_text = write_result.unwrap_err().to_string(); request .into_status_response(500)? .write(error_text.as_bytes())?; return Ok(()); } println!("wrote {read} bytes ota {total_read}"); if read == 0 { break; } } println!("finish ota"); let partition = ota.raw_partition(); println!("finalizing and changing boot partition to {partition:?}"); let finalizer = ota.finalize(); if finalizer.is_err() { let error_text = finalizer.err().unwrap().to_string(); request .into_status_response(500)? .write(error_text.as_bytes())?; return Ok(()); } let mut finalizer = finalizer.unwrap(); println!("changing boot partition"); finalizer.set_as_boot_partition().unwrap(); finalizer.restart(); }) .unwrap(); server .fn_handler("/boardtest", Method::Post, move |_| { let mut board = BOARD_ACCESS.lock().unwrap(); board.test()?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/pumptest", Method::Post, |mut request| { let mut buf = [0_u8; 3072]; let read = request.read(&mut buf); if read.is_err() { let error_text = read.unwrap_err().to_string(); println!("Could not parse testrequest {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return anyhow::Ok(()); } let actual_data = &buf[0..read.unwrap()]; println!("Raw data {}", from_utf8(actual_data).unwrap()); let pump_test: Result = serde_json::from_slice(actual_data); if pump_test.is_err() { let error_text = pump_test.unwrap_err().to_string(); println!("Could not parse TestPump {}", error_text); request .into_status_response(500)? .write(error_text.as_bytes())?; return Ok(()); } let mut board = BOARD_ACCESS.lock().unwrap(); board.test_pump(pump_test.unwrap().pump)?; anyhow::Ok(()) }) .unwrap(); server .fn_handler("/flashbattery", Method::Post, move |request| { let mut board = BOARD_ACCESS.lock().unwrap(); let mut response = request.into_ok_response().unwrap(); let delay = Delay::new(0); let firmware = include_bytes!("0100_2_02-bq34z100.df.fs"); response.write("Checking pass: \n".as_bytes()).unwrap(); for iter in firmware.lines() { delay.delay_us(1); let line = iter?; let msg = format!("{line}
"); println!("{line}"); response.write(msg.as_bytes()).unwrap(); let validate = board.flash_bq34_z100(&line, true); if validate.is_err() { response.write(validate.unwrap_err().to_string().as_bytes()).unwrap(); } } response.write("Executing flashing: \n".as_bytes()).unwrap(); let mut toggle = true; for iter in firmware.lines() { delay.delay_us(1); let line = iter?; let msg = format!("{line}
"); println!("{line}"); response.write(msg.as_bytes()).unwrap(); board.general_fault(toggle); toggle = !toggle; let write = board.flash_bq34_z100(&line, false); if write.is_err() { response.write(write.unwrap_err().to_string().as_bytes()).unwrap(); } } board.general_fault(false); anyhow::Ok(()) }) .unwrap(); server }