cleanup reboot logic, fix json upload
This commit is contained in:
parent
0f77ac163a
commit
c070e68349
@ -10,7 +10,17 @@ use chrono_tz::{Europe::Berlin, Tz};
|
||||
use config::Mode;
|
||||
use esp_idf_hal::delay::Delay;
|
||||
use esp_idf_sys::{
|
||||
esp_deep_sleep, esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW, esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED, esp_ota_img_states_t_ESP_OTA_IMG_VALID, esp_restart, esp_sleep_enable_ext1_wakeup, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, vTaskDelay, CONFIG_FREERTOS_HZ
|
||||
esp_ota_get_app_partition_count,
|
||||
esp_ota_get_running_partition,
|
||||
esp_ota_get_state_partition,
|
||||
esp_ota_img_states_t,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_ABORTED,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_INVALID,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_NEW,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_VALID,
|
||||
vTaskDelay
|
||||
};
|
||||
use log::error;
|
||||
use once_cell::sync::Lazy;
|
||||
@ -460,8 +470,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
board.last_pump_time(plant);
|
||||
state.active = true;
|
||||
for _ in 0..plant_config.pump_time_s {
|
||||
unsafe { vTaskDelay(CONFIG_FREERTOS_HZ) };
|
||||
//info message or something?
|
||||
Delay::new_default().delay_ms(1000);
|
||||
}
|
||||
|
||||
board.pump(plant, false)?;
|
||||
@ -557,10 +566,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
let _webserver = httpd(reboot_now.clone());
|
||||
wait_infinity(WaitType::MqttConfig, reboot_now.clone());
|
||||
}
|
||||
unsafe {
|
||||
esp_sleep_enable_ext1_wakeup(0b10u64, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW);
|
||||
esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64)
|
||||
};
|
||||
board.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
|
||||
}
|
||||
|
||||
fn publish_battery_state(
|
||||
@ -949,8 +955,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
}
|
||||
}
|
||||
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
|
||||
println!("Rebooting");
|
||||
esp_restart();
|
||||
BOARD_ACCESS.lock().unwrap().deep_sleep( 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -961,7 +966,7 @@ fn main() {
|
||||
match result {
|
||||
Ok(_) => {
|
||||
println!("Main app finished, restarting");
|
||||
unsafe { esp_restart() };
|
||||
BOARD_ACCESS.lock().unwrap().deep_sleep(1);
|
||||
}
|
||||
Err(err) => {
|
||||
println!("Failed main {}", err);
|
||||
|
@ -7,6 +7,7 @@ use embedded_hal_bus::i2c::MutexDevice;
|
||||
use embedded_svc::wifi::{
|
||||
AccessPointConfiguration, AccessPointInfo, AuthMethod, ClientConfiguration, Configuration,
|
||||
};
|
||||
|
||||
use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
|
||||
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
|
||||
use esp_idf_hal::adc::{attenuation, Resolution};
|
||||
@ -23,6 +24,8 @@ use esp_idf_svc::wifi::EspWifi;
|
||||
use measurements::Temperature;
|
||||
use once_cell::sync::Lazy;
|
||||
use plant_ctrl2::sipo::ShiftRegister40;
|
||||
use esp_idf_sys::esp_deep_sleep;
|
||||
use esp_idf_sys::esp_restart;
|
||||
|
||||
use anyhow::{anyhow, Context};
|
||||
use anyhow::{bail, Ok, Result};
|
||||
@ -53,6 +56,7 @@ use esp_idf_sys::{esp, esp_spiffs_check, gpio_hold_dis, gpio_hold_en, vTaskDelay
|
||||
use one_wire_bus::OneWire;
|
||||
|
||||
use crate::config::{self, PlantControllerConfig};
|
||||
use crate::espota::mark_app_valid;
|
||||
use crate::{plant_hal, to_string, STAY_ALIVE};
|
||||
|
||||
//Only support for 8 right now!
|
||||
@ -196,6 +200,21 @@ pub struct BatteryState {
|
||||
}
|
||||
|
||||
impl PlantCtrlBoard<'_> {
|
||||
pub fn deep_sleep(&mut self, duration_in_ms:u64) -> !{
|
||||
unsafe {
|
||||
//if we dont do this here, we might just revert a newly flashed firmeware
|
||||
mark_app_valid();
|
||||
//allow early wakup by pressing the boot button
|
||||
if duration_in_ms == 0 {
|
||||
esp_restart();
|
||||
} else {
|
||||
// esp_sleep_enable_ext1_wakeup(0b10u64, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW);
|
||||
esp_deep_sleep(duration_in_ms);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_battery_state(&mut self) -> BatteryState {
|
||||
let bat = BatteryState {
|
||||
voltage_milli_volt: to_string(self.voltage_milli_volt()),
|
||||
|
@ -4,8 +4,6 @@ use std::{
|
||||
str::from_utf8,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
|
||||
use esp_idf_svc::io::BufRead;
|
||||
use crate::{
|
||||
espota::OtaUpdate, get_version, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS,
|
||||
};
|
||||
@ -56,11 +54,8 @@ pub struct TestPump {
|
||||
fn write_time(
|
||||
request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
let mut buf = [0_u8; 3072];
|
||||
let read = request.read(&mut buf)?;
|
||||
let actual_data = &buf[0..read];
|
||||
println!("Raw data {}", from_utf8(actual_data)?);
|
||||
let time: SetTime = serde_json::from_slice(actual_data)?;
|
||||
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
||||
let time: SetTime = serde_json::from_slice(&actual_data)?;
|
||||
let parsed = DateTime::parse_from_rfc3339(time.time).map_err(|err| anyhow::anyhow!(err))?;
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
board.set_rtc_time(&parsed.to_utc())?;
|
||||
@ -144,16 +139,14 @@ fn get_config(
|
||||
fn set_config(
|
||||
request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
let mut buf = [0_u8; 3072];
|
||||
let read = request.read(&mut buf)?;
|
||||
let actual_data = &buf[0..read];
|
||||
println!("Raw data {}", from_utf8(actual_data).unwrap());
|
||||
let config: PlantControllerConfig = serde_json::from_slice(actual_data)?;
|
||||
let all = read_up_to_bytes_from_request(request, Some(3072))?;
|
||||
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
board.set_config(&config)?;
|
||||
anyhow::Ok(Some("saved".to_owned()))
|
||||
}
|
||||
|
||||
|
||||
fn get_battery_state(
|
||||
_request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
@ -172,9 +165,8 @@ fn get_version_web(
|
||||
fn pump_test(
|
||||
request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
let mut buf = [0_u8; 3072];
|
||||
let read = request.read(&mut buf)?;
|
||||
let pump_test: TestPump = serde_json::from_slice(&buf[0..read])?;
|
||||
let actual_data = read_up_to_bytes_from_request(request, None)?;
|
||||
let pump_test: TestPump = serde_json::from_slice(&actual_data)?;
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
board.test_pump(pump_test.pump)?;
|
||||
anyhow::Ok(None)
|
||||
@ -285,7 +277,7 @@ fn query_param(uri: &str, param_name: &str) -> Option<std::string::String> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
let server_config = Configuration {
|
||||
stack_size: 32768,
|
||||
..Default::default()
|
||||
@ -358,6 +350,25 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
handle_error_to500(request, list_files)
|
||||
})
|
||||
.unwrap();
|
||||
let reboot_now_for_reboot = reboot_now.clone();
|
||||
server
|
||||
.fn_handler("/reboot", Method::Post, move |_| {
|
||||
BOARD_ACCESS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_restart_to_conf(true);
|
||||
reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let reboot_now_for_exit = reboot_now.clone();
|
||||
server
|
||||
.fn_handler("/exit", Method::Post, move |_| {
|
||||
reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/file", Method::Get, move |request| {
|
||||
let filename = query_param(request.uri(), "filename").unwrap();
|
||||
@ -407,6 +418,8 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
.unwrap()
|
||||
.get_file_handle(&filename, true);
|
||||
match file_handle {
|
||||
//TODO get free filesystem size, check against during write if not to large
|
||||
|
||||
Ok(mut file_handle) => {
|
||||
const BUFFER_SIZE: usize = 512;
|
||||
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
@ -482,7 +495,6 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
server
|
||||
.fn_handler("/", Method::Get, move |request| {
|
||||
let mut response = request.into_ok_response()?;
|
||||
@ -559,3 +571,25 @@ fn handle_error_to500(
|
||||
}
|
||||
return anyhow::Ok(());
|
||||
}
|
||||
|
||||
fn read_up_to_bytes_from_request(request: &mut Request<&mut EspHttpConnection<'_>>, limit: Option<usize>) -> Result<Vec<u8>, anyhow::Error> {
|
||||
let max_read = limit.unwrap_or(1024);
|
||||
let mut data_store = Vec::new();
|
||||
let mut total_read = 0;
|
||||
loop{
|
||||
let mut buf = [0_u8; 64];
|
||||
let read = request.read(&mut buf)?;
|
||||
if read == 0 {
|
||||
break;
|
||||
}
|
||||
let actual_data = &buf[0..read];
|
||||
total_read += read;
|
||||
if total_read > max_read{
|
||||
bail!("Request too large {total_read} > {max_read}");
|
||||
}
|
||||
data_store.push(actual_data.to_owned());
|
||||
}
|
||||
let allvec = data_store.concat();
|
||||
println!("Raw data {}", from_utf8(&allvec)?);
|
||||
Ok(allvec)
|
||||
}
|
||||
|
@ -116,6 +116,9 @@
|
||||
<br>
|
||||
<textarea id="json" cols=50 rows=10></textarea>
|
||||
<script src="bundle.js"></script>
|
||||
|
||||
<button id="exit">Exit</button>
|
||||
<button id="reboot">Reboot</button>
|
||||
</div>
|
||||
|
||||
<div id="progressPane" class="progressPane">
|
||||
|
@ -24,7 +24,7 @@ export class Controller {
|
||||
controller.timeView.update(time.native, time.rtc)
|
||||
})
|
||||
.catch(error => {
|
||||
controller.timeView.update("n/a","n/a")
|
||||
controller.timeView.update("n/a", "n/a")
|
||||
console.log(error);
|
||||
});
|
||||
}
|
||||
@ -43,12 +43,12 @@ export class Controller {
|
||||
uploadNewFirmware(file: File) {
|
||||
var current = 0;
|
||||
var max = 100;
|
||||
controller.progressview.addProgress("ota_upload", (current/max) *100 , "Uploading firmeware ("+current+"/" + max+")")
|
||||
controller.progressview.addProgress("ota_upload", (current / max) * 100, "Uploading firmeware (" + current + "/" + max + ")")
|
||||
var ajax = new XMLHttpRequest();
|
||||
ajax.upload.addEventListener("progress", event => {
|
||||
current = event.loaded/1000;
|
||||
max = event.total/1000;
|
||||
controller.progressview.addProgress("ota_upload", (current/max) *100 , "Uploading firmeware ("+current+"/" + max+")")
|
||||
current = event.loaded / 1000;
|
||||
max = event.total / 1000;
|
||||
controller.progressview.addProgress("ota_upload", (current / max) * 100, "Uploading firmeware (" + current + "/" + max + ")")
|
||||
}, false);
|
||||
ajax.addEventListener("load", () => {
|
||||
//TODO wait for reboot here!
|
||||
@ -97,7 +97,7 @@ export class Controller {
|
||||
.then(text => statusCallback(text))
|
||||
controller.progressview.removeProgress("set_config")
|
||||
}
|
||||
syncRTCFromBrowser(){
|
||||
syncRTCFromBrowser() {
|
||||
controller.progressview.addIndeterminate("write_rtc", "Writing RTC")
|
||||
var value: SetTime = {
|
||||
time: new Date().toISOString()
|
||||
@ -122,12 +122,12 @@ export class Controller {
|
||||
testPlant(plantId: number) {
|
||||
let counter = 0
|
||||
let limit = 30
|
||||
controller.progressview.addProgress("test_pump", counter/limit*100, "Testing pump " + (plantId+1) + " for " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s")
|
||||
|
||||
let timerId: string | number | NodeJS.Timeout | undefined
|
||||
function updateProgress(){
|
||||
function updateProgress() {
|
||||
counter++;
|
||||
controller.progressview.addProgress("test_pump", counter/limit*100, "Testing pump " + (plantId+1) + " for " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("test_pump", counter / limit * 100, "Testing pump " + (plantId + 1) + " for " + (limit - counter) + "s")
|
||||
timerId = setTimeout(updateProgress, 1000);
|
||||
|
||||
}
|
||||
@ -151,7 +151,7 @@ export class Controller {
|
||||
)
|
||||
}
|
||||
|
||||
getConfig(): PlantControllerConfig{
|
||||
getConfig(): PlantControllerConfig {
|
||||
return {
|
||||
network: controller.networkView.getConfig(),
|
||||
tank: controller.tankView.getConfig(),
|
||||
@ -163,12 +163,12 @@ export class Controller {
|
||||
scanWifi() {
|
||||
let counter = 0
|
||||
let limit = 5
|
||||
controller.progressview.addProgress("scan_ssid", counter/limit*100, "Scanning for SSIDs for " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s")
|
||||
|
||||
let timerId: string | number | NodeJS.Timeout | undefined
|
||||
function updateProgress(){
|
||||
function updateProgress() {
|
||||
counter++;
|
||||
controller.progressview.addProgress("scan_ssid", counter/limit*100, "Scanning for SSIDs for " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("scan_ssid", counter / limit * 100, "Scanning for SSIDs for " + (limit - counter) + "s")
|
||||
timerId = setTimeout(updateProgress, 1000);
|
||||
|
||||
}
|
||||
@ -200,15 +200,15 @@ export class Controller {
|
||||
this.plantViews.setConfig(current.plants);
|
||||
}
|
||||
|
||||
measure_moisture (){
|
||||
measure_moisture() {
|
||||
let counter = 0
|
||||
let limit = 2
|
||||
controller.progressview.addProgress("measure_moisture", counter/limit*100, "Measure Moisture " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
||||
|
||||
let timerId: string | number | NodeJS.Timeout | undefined
|
||||
function updateProgress(){
|
||||
function updateProgress() {
|
||||
counter++;
|
||||
controller.progressview.addProgress("measure_moisture", counter/limit*100, "Measure Moisture " + (limit-counter)+"s")
|
||||
controller.progressview.addProgress("measure_moisture", counter / limit * 100, "Measure Moisture " + (limit - counter) + "s")
|
||||
timerId = setTimeout(updateProgress, 1000);
|
||||
|
||||
}
|
||||
@ -230,14 +230,48 @@ export class Controller {
|
||||
});
|
||||
}
|
||||
|
||||
exit() {
|
||||
fetch(PUBLIC_URL + "/exit", {
|
||||
method: "POST",
|
||||
})
|
||||
controller.progressview.addIndeterminate("rebooting", "Returned to normal mode, you can close this site now")
|
||||
|
||||
}
|
||||
|
||||
waitForReboot() {
|
||||
console.log("Check if controller online again")
|
||||
fetch(PUBLIC_URL + "/version", {
|
||||
method: "POST",
|
||||
signal: AbortSignal.timeout(5000)
|
||||
}).then(response => {
|
||||
console.log("Reached controller, reloading")
|
||||
window.location.reload();
|
||||
})
|
||||
.catch(err => {
|
||||
console.log("Not reached yet, retrying")
|
||||
setTimeout(controller.waitForReboot, 1000)
|
||||
})
|
||||
}
|
||||
|
||||
reboot() {
|
||||
fetch(PUBLIC_URL + "/reboot", {
|
||||
method: "POST",
|
||||
})
|
||||
controller.progressview.addIndeterminate("rebooting", "Rebooting")
|
||||
setTimeout(this.waitForReboot, 1000)
|
||||
}
|
||||
|
||||
|
||||
|
||||
readonly rebootBtn: HTMLButtonElement
|
||||
readonly exitBtn: HTMLButtonElement
|
||||
readonly timeView: TimeView;
|
||||
readonly plantViews: PlantViews;
|
||||
readonly networkView: NetworkConfigView;
|
||||
readonly tankView: TankConfigView;
|
||||
readonly nightLampView: NightLampView;
|
||||
readonly submitView: SubmitView;
|
||||
readonly firmWareView : OTAView;
|
||||
readonly firmWareView: OTAView;
|
||||
readonly progressview: ProgressView;
|
||||
readonly batteryView: BatteryView;
|
||||
constructor() {
|
||||
@ -250,6 +284,14 @@ export class Controller {
|
||||
this.submitView = new SubmitView(this)
|
||||
this.firmWareView = new OTAView(this)
|
||||
this.progressview = new ProgressView(this)
|
||||
this.rebootBtn = document.getElementById("reboot") as HTMLButtonElement
|
||||
this.rebootBtn.onclick = () => {
|
||||
controller.reboot();
|
||||
}
|
||||
this.exitBtn = document.getElementById("exit") as HTMLButtonElement
|
||||
this.exitBtn.onclick = () => {
|
||||
controller.exit();
|
||||
}
|
||||
}
|
||||
}
|
||||
const controller = new Controller();
|
||||
@ -258,4 +300,5 @@ controller.updateBatteryData();
|
||||
controller.downloadConfig();
|
||||
//controller.measure_moisture();
|
||||
controller.version();
|
||||
controller.progressview.removeProgress("rebooting");
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user