335 lines
12 KiB
Rust
335 lines
12 KiB
Rust
//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<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|
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<WifiConfig, serde_json::Error> =
|
|
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<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|
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<Config, serde_json::Error> = 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<EspHttpServer<'static>> {
|
|
let server_config = Configuration {
|
|
stack_size: 8192,
|
|
..Default::default()
|
|
};
|
|
let mut server: Box<EspHttpServer<'static>> =
|
|
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<TestPump, serde_json::Error> =
|
|
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}<br>");
|
|
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}<br>");
|
|
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
|
|
}
|