firmware streaming
This commit is contained in:
@@ -70,7 +70,7 @@ impl Default for Plant {
|
||||
pump_hour_start: 8,
|
||||
pump_hour_end: 20,
|
||||
mode: Mode::OFF,
|
||||
sensor_b: false
|
||||
sensor_b: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -60,6 +60,7 @@ enum WaitType {
|
||||
FlashError,
|
||||
NormalConfig,
|
||||
StayAlive,
|
||||
StayAliveBtn
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
||||
@@ -227,28 +228,41 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
|
||||
println!("cur is {}", cur);
|
||||
|
||||
let mut to_config = false;
|
||||
if board.is_config_reset() {
|
||||
board.general_fault(true);
|
||||
println!("Reset config is pressed, waiting 5s");
|
||||
for _i in 0..25 {
|
||||
for _i in 0..5 {
|
||||
board.general_fault(true);
|
||||
Delay::new_default().delay_ms(50);
|
||||
Delay::new_default().delay_ms(100);
|
||||
board.general_fault(false);
|
||||
Delay::new_default().delay_ms(50);
|
||||
Delay::new_default().delay_ms(100);
|
||||
}
|
||||
|
||||
if board.is_config_reset() {
|
||||
println!("Reset config is still pressed, deleting configs and reboot");
|
||||
match board.remove_configs() {
|
||||
Ok(case) => {
|
||||
println!("Succeeded in deleting config {}", case);
|
||||
to_config = true;
|
||||
println!("Reset config is still pressed, proceed to config mode");
|
||||
for _i in 0..25 {
|
||||
board.general_fault(true);
|
||||
Delay::new_default().delay_ms(25);
|
||||
board.general_fault(false);
|
||||
Delay::new_default().delay_ms(25);
|
||||
}
|
||||
if board.is_config_reset() {
|
||||
println!("Reset config is still pressed, proceed to delete configs");
|
||||
match board.remove_configs() {
|
||||
Ok(case) => {
|
||||
println!("Succeeded in deleting config {}", case);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Could not remove config files, system borked {}", err);
|
||||
//terminate main app and freeze
|
||||
|
||||
wait_infinity(WaitType::FlashError, Arc::new(AtomicBool::new(false)));
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Could not remove config files, system borked {}", err);
|
||||
//terminate main app and freeze
|
||||
} else {
|
||||
|
||||
wait_infinity(WaitType::FlashError, Arc::new(AtomicBool::new(false)));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
board.general_fault(false);
|
||||
@@ -310,7 +324,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
println!("Running logic at europe/berlin {}", europe_time);
|
||||
|
||||
let config: Config;
|
||||
match board.get_config() {
|
||||
match board.get_config() {
|
||||
Ok(valid) => {
|
||||
config = valid;
|
||||
}
|
||||
@@ -422,7 +436,9 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
&mut board,
|
||||
);
|
||||
|
||||
let stay_alive = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
|
||||
|
||||
let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
|
||||
let stay_alive = stay_alive_mqtt;
|
||||
println!("Check stay alive, current state is {}", stay_alive);
|
||||
|
||||
let mut did_pump = false;
|
||||
@@ -442,7 +458,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
"Trying to pump for {}s with pump {} now",
|
||||
plant_config.pump_time_s, plant
|
||||
);
|
||||
if !stay_alive {
|
||||
if !stay_alive && !to_config {
|
||||
did_pump = true;
|
||||
board.any_pump(true)?;
|
||||
board.store_last_pump_time(plant, cur);
|
||||
@@ -552,7 +568,15 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
//is deep sleep
|
||||
mark_app_valid();
|
||||
|
||||
if to_config {
|
||||
println!("Go to button triggerd stay alive");
|
||||
drop(board);
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
let _webserver = httpd(reboot_now.clone());
|
||||
wait_infinity(WaitType::StayAliveBtn, reboot_now.clone());
|
||||
}
|
||||
if stay_alive {
|
||||
println!("Go to stay alive move");
|
||||
drop(board);
|
||||
let reboot_now = Arc::new(AtomicBool::new(false));
|
||||
let _webserver = httpd(reboot_now.clone());
|
||||
@@ -926,12 +950,14 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
WaitType::FlashError => 100_u32,
|
||||
WaitType::NormalConfig => 500_u32,
|
||||
WaitType::StayAlive => 1000_u32,
|
||||
WaitType::StayAliveBtn => 25_u32
|
||||
};
|
||||
let led_count = match wait_type {
|
||||
WaitType::InitialConfig => 8,
|
||||
WaitType::FlashError => 8,
|
||||
WaitType::NormalConfig => 4,
|
||||
WaitType::StayAlive => 2,
|
||||
WaitType::StayAliveBtn => 5
|
||||
};
|
||||
loop {
|
||||
unsafe {
|
||||
|
@@ -191,7 +191,7 @@ pub trait PlantCtrlBoardInteraction {
|
||||
fn mqtt_publish(&mut self, config: &Config, subtopic: &str, message: &[u8]) -> Result<()>;
|
||||
|
||||
fn sensor_multiplexer(&mut self, n: u8) -> Result<()>;
|
||||
fn flash_bq34_z100(&mut self, line: &str, dryrun:bool) -> Result<()>;
|
||||
fn flash_bq34_z100(&mut self, line: &str, dryrun: bool) -> Result<()>;
|
||||
}
|
||||
|
||||
pub trait CreatePlantHal<'a> {
|
||||
@@ -206,7 +206,8 @@ pub struct PlantCtrlBoard<'a> {
|
||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||
>,
|
||||
shift_register_enable_invert: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
|
||||
shift_register_enable_invert:
|
||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
|
||||
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
|
||||
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||
boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||
@@ -359,7 +360,7 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
||||
5 => FAULT_6,
|
||||
6 => FAULT_7,
|
||||
7 => FAULT_8,
|
||||
_ => panic!("Invalid plant id {}", plant)
|
||||
_ => panic!("Invalid plant id {}", plant),
|
||||
};
|
||||
self.shift_register.decompose()[index]
|
||||
.set_state(enable.into())
|
||||
@@ -413,7 +414,7 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
||||
5 => SENSOR_A_6,
|
||||
6 => SENSOR_A_7,
|
||||
7 => SENSOR_A_8,
|
||||
_ => bail!("Invalid plant id {}", plant)
|
||||
_ => bail!("Invalid plant id {}", plant),
|
||||
},
|
||||
Sensor::B => match plant {
|
||||
0 => SENSOR_B_1,
|
||||
@@ -424,52 +425,45 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
||||
5 => SENSOR_B_6,
|
||||
6 => SENSOR_B_7,
|
||||
7 => SENSOR_B_8,
|
||||
_ => bail!("Invalid plant id {}", plant)
|
||||
_ => bail!("Invalid plant id {}", plant),
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
let mut results = [0;REPEAT_MOIST_MEASURE];
|
||||
let mut results = [0; REPEAT_MOIST_MEASURE];
|
||||
for repeat in 0..REPEAT_MOIST_MEASURE {
|
||||
self.signal_counter.counter_pause()?;
|
||||
self.signal_counter.counter_clear()?;
|
||||
//Disable all
|
||||
self.shift_register.decompose()[MS_4]
|
||||
self.shift_register.decompose()[MS_4].set_high().unwrap();
|
||||
|
||||
self.sensor_multiplexer(sensor_channel)?;
|
||||
|
||||
self.shift_register.decompose()[MS_4].set_low().unwrap();
|
||||
self.shift_register.decompose()[SENSOR_ON]
|
||||
.set_high()
|
||||
.unwrap();
|
||||
|
||||
self.sensor_multiplexer(sensor_channel)?;
|
||||
|
||||
self.shift_register.decompose()[MS_4]
|
||||
.set_low()
|
||||
.unwrap();
|
||||
self.shift_register.decompose()[SENSOR_ON]
|
||||
.set_high()
|
||||
.unwrap();
|
||||
|
||||
|
||||
let delay = Delay::new_default();
|
||||
let measurement = 10;
|
||||
let factor = 1000 as f32 / measurement as f32;
|
||||
|
||||
|
||||
//give some time to stabilize
|
||||
delay.delay_ms(10);
|
||||
self.signal_counter.counter_resume()?;
|
||||
delay.delay_ms(measurement);
|
||||
self.signal_counter.counter_pause()?;
|
||||
self.shift_register.decompose()[MS_4]
|
||||
.set_high()
|
||||
.unwrap();
|
||||
self.shift_register.decompose()[MS_4].set_high().unwrap();
|
||||
self.shift_register.decompose()[SENSOR_ON]
|
||||
.set_low()
|
||||
.unwrap();
|
||||
delay.delay_ms(10);
|
||||
.set_low()
|
||||
.unwrap();
|
||||
delay.delay_ms(10);
|
||||
let unscaled = self.signal_counter.get_counter_value()? as i32;
|
||||
let hz = (unscaled as f32 * factor) as i32;
|
||||
results[repeat] = hz;
|
||||
//println!("Measuring {:?} @ {} with {}", sensor, plant, hz);
|
||||
}
|
||||
results.sort();
|
||||
|
||||
|
||||
let mid = results.len() / 2;
|
||||
|
||||
Ok(results[mid])
|
||||
@@ -693,7 +687,7 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
||||
for plant in 0..PLANT_COUNT {
|
||||
let a = self.measure_moisture_hz(plant, plant_hal::Sensor::A);
|
||||
let b = self.measure_moisture_hz(plant, plant_hal::Sensor::B);
|
||||
print!("P:{} a:{:?} b:{:?}", plant, a ,b)
|
||||
print!("P:{} a:{:?} b:{:?}", plant, a, b)
|
||||
}
|
||||
println!();
|
||||
Delay::new_default().delay_ms(10);
|
||||
@@ -1006,7 +1000,7 @@ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
fn flash_bq34_z100(&mut self, line: &str, dryrun: bool) -> Result<()> {
|
||||
match &mut self.battery_driver {
|
||||
Some(driver) => match driver.write_flash_stream_i2c(line, dryrun) {
|
||||
@@ -1127,10 +1121,10 @@ impl CreatePlantHal<'_> for PlantHal {
|
||||
ms2.set_low()?;
|
||||
let ms3 = &mut shift_register.decompose()[MS_3];
|
||||
ms3.set_low()?;
|
||||
|
||||
|
||||
let ms4 = &mut shift_register.decompose()[MS_4];
|
||||
ms4.set_high()?;
|
||||
|
||||
|
||||
//init,reset rtc memory depending on cause
|
||||
let reasons = ResetReason::get();
|
||||
let reset_store = match reasons {
|
||||
@@ -1255,7 +1249,6 @@ impl CreatePlantHal<'_> for PlantHal {
|
||||
println!("Managed to comunnicate with battery");
|
||||
}
|
||||
|
||||
|
||||
let shift_register_enable_invert = PinDriver::output(peripherals.pins.gpio21.downgrade())?;
|
||||
|
||||
let rv = Mutex::new(PlantCtrlBoard {
|
||||
@@ -1276,14 +1269,13 @@ impl CreatePlantHal<'_> for PlantHal {
|
||||
battery_driver: Some(battery_driver),
|
||||
});
|
||||
|
||||
let _ = rv.lock().is_ok_and(|mut board|{
|
||||
unsafe { gpio_hold_dis(board.shift_register_enable_invert.pin())};
|
||||
board.shift_register_enable_invert.set_low().unwrap();
|
||||
unsafe { gpio_hold_en(board.shift_register_enable_invert.pin())};
|
||||
let _ = rv.lock().is_ok_and(|mut board| {
|
||||
unsafe { gpio_hold_dis(board.shift_register_enable_invert.pin()) };
|
||||
board.shift_register_enable_invert.set_low().unwrap();
|
||||
unsafe { gpio_hold_en(board.shift_register_enable_invert.pin()) };
|
||||
return true;
|
||||
});
|
||||
|
||||
|
||||
|
||||
Ok(rv)
|
||||
}
|
||||
}
|
||||
|
@@ -18,8 +18,15 @@
|
||||
</form>
|
||||
|
||||
<h2>Battery Firmeware (bq34z100 may be R2)</h2>
|
||||
<button id="flash5ah12vlifepo">Flash 6AH 12V Lifepo replacement (built in)</button>
|
||||
<div style="height: 100px; display: block; overflow-y: auto;" id = "flash_message"></div>
|
||||
<form id="upload_form" method="post">
|
||||
<input type="file" name="battery_flash_file" id="battery_flash_file"><br>
|
||||
<progress id="progressBar" value="0" max="100" style="width:300px;"></progress>
|
||||
<input type="button" name="battery_flash_button" id="battery_flash_button"><br>
|
||||
<h3 id="status"></h3>
|
||||
<div style="height: 100px; display: block; overflow-y: auto;" id = "battery_flash_message"></div>
|
||||
</form>
|
||||
|
||||
|
||||
<h2>config</h2>
|
||||
|
||||
<div id="configform">
|
||||
|
@@ -17,6 +17,16 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<h2>Battery Firmeware (bq34z100 may be R2)</h2>
|
||||
<form id="upload_form" method="post">
|
||||
<input type="file" name="battery_flash_file" id="battery_flash_file"><br>
|
||||
<progress id="battery_flash_progressBar" value="0" max="100" style="width:300px;"></progress>
|
||||
<input type="button" name="battery_flash_button" id="battery_flash_button"><br>
|
||||
<h3 id="battery_flash_status"></h3>
|
||||
<p id="battery_flash_loaded_n_total"></p>
|
||||
<div style="height: 100px; display: block; overflow-y: auto;" id = "battery_flash_message"></div>
|
||||
</form>
|
||||
|
||||
<div>
|
||||
<h2>WIFI</h2>
|
||||
<input type="button" id="scan" value="Scan">
|
||||
|
@@ -1,14 +1,18 @@
|
||||
//offer ota and config mode
|
||||
|
||||
use std::{
|
||||
io::BufRead, str::from_utf8, sync::{atomic::AtomicBool, Arc}
|
||||
collections::VecDeque,
|
||||
io::{BufRead, Read, Write},
|
||||
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 embedded_svc::http::{Method};
|
||||
use esp_idf_hal::delay::Delay;
|
||||
use esp_idf_svc::http::server::{Configuration, EspHttpServer};
|
||||
use esp_idf_sys::vTaskDelay;
|
||||
use heapless::String;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@@ -289,46 +293,82 @@ pub fn shared() -> Box<EspHttpServer<'static>> {
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/flashbattery", Method::Post, move |request| {
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
let mut response = request.into_ok_response().unwrap();
|
||||
|
||||
|
||||
.fn_handler("/flashbattery", Method::Post, move |mut request| {
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
let mut buffer: [u8; 32] = [0; 32];
|
||||
let mut line_buffer: VecDeque<u8> = VecDeque::new();
|
||||
|
||||
let delay = Delay::new(0);
|
||||
let firmware = include_bytes!("0100_2_02-bq34z100.df.fs");
|
||||
let is_dry_run = !request.uri().ends_with("?flash=true");
|
||||
let mut total_read: usize = 0;
|
||||
|
||||
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();
|
||||
let mut toggle = true;
|
||||
let delay = Delay::new(1);
|
||||
loop {
|
||||
delay.delay_us(2);
|
||||
let read = request.read(&mut buffer).unwrap();
|
||||
total_read += read;
|
||||
println!("received {read} bytes ota {total_read}");
|
||||
if read == 0 {
|
||||
if line_buffer.len() > 0 {
|
||||
println!("No further body but no endline");
|
||||
let mut line = std::string::String::new();
|
||||
line_buffer.read_to_string(&mut line).unwrap();
|
||||
let msg = format!("Finished reading, but there is still some leftover in buffer and no full line {line}<br>");
|
||||
println!("{}", msg);
|
||||
let mut response = request.into_status_response(400_u16).unwrap();
|
||||
response.write(msg.as_bytes()).unwrap();
|
||||
response.flush().unwrap();
|
||||
return anyhow::Ok(())
|
||||
}
|
||||
break;
|
||||
}
|
||||
let to_write = &buffer[0..read];
|
||||
line_buffer.write_all(to_write).unwrap();
|
||||
println!("Write to deque new lenght is {}", line_buffer.len());
|
||||
board.general_fault(toggle);
|
||||
toggle = !toggle;
|
||||
println!("Updated btn");
|
||||
|
||||
loop {
|
||||
let mut line = std::string::String::new();
|
||||
println!("Check for line");
|
||||
let has_line = line_buffer.read_line(&mut line);
|
||||
let mut line_size = 0;
|
||||
match has_line {
|
||||
Ok(size) => {
|
||||
line_size = size;
|
||||
if size == 0 {
|
||||
println!("Was no line no string read");
|
||||
break;
|
||||
}
|
||||
},
|
||||
Err(err) => {
|
||||
println!("Was no line and error {}", err);
|
||||
break;
|
||||
},
|
||||
}
|
||||
println!("Processing line with size {} {}", line_size, line);
|
||||
//let validate = board.flash_bq34_z100(&line, is_dry_run);
|
||||
let validate = anyhow::Ok(());
|
||||
delay.delay_us(2);
|
||||
if validate.is_err() {
|
||||
let mut response = request.into_status_response(400_u16).unwrap();
|
||||
let err = validate.unwrap_err();
|
||||
let err_str = err.to_string();
|
||||
let err_msg = err_str.as_bytes();
|
||||
response
|
||||
.write(err_msg)
|
||||
.unwrap();
|
||||
return anyhow::Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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}");
|
||||
let mut response = request.into_status_response(200_u16).unwrap();
|
||||
let msg = format!("Finished writing {total_read} bytes<br>");
|
||||
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();
|
||||
board.general_fault(false);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
}
|
||||
|
Reference in New Issue
Block a user