adjust rust code to new config file, fix bq34z100 flasher
This commit is contained in:
parent
c89a617d9d
commit
74f9c17461
@ -1,68 +1,77 @@
|
|||||||
use std::{array::from_fn, str::FromStr};
|
use std::str::FromStr;
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::PLANT_COUNT;
|
use crate::PLANT_COUNT;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct Config {
|
pub struct NetworkConfig {
|
||||||
pub ap_ssid: heapless::String<32>,
|
pub ap_ssid: heapless::String<32>,
|
||||||
|
|
||||||
pub ssid: Option<heapless::String<32>>,
|
pub ssid: Option<heapless::String<32>>,
|
||||||
pub password: Option<heapless::String<64>>,
|
pub password: Option<heapless::String<64>>,
|
||||||
|
|
||||||
pub mqtt_url: Option<heapless::String<128>>,
|
pub mqtt_url: Option<heapless::String<128>>,
|
||||||
pub base_topic: Option<heapless::String<64>>,
|
pub base_topic: Option<heapless::String<64>>,
|
||||||
pub max_consecutive_pump_count: u8,
|
}
|
||||||
|
impl Default for NetworkConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
ap_ssid: heapless::String::from_str("PlantCtrl Init").unwrap(),
|
||||||
|
ssid: None,
|
||||||
|
password: None,
|
||||||
|
mqtt_url: None,
|
||||||
|
base_topic: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub tank_allow_pumping_if_sensor_error: bool,
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub struct NightLampConfig {
|
||||||
|
pub night_lamp_hour_start: u8,
|
||||||
|
pub night_lamp_hour_end: u8,
|
||||||
|
pub night_lamp_only_when_dark: bool,
|
||||||
|
}
|
||||||
|
impl Default for NightLampConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
night_lamp_hour_start: 19,
|
||||||
|
night_lamp_hour_end: 2,
|
||||||
|
night_lamp_only_when_dark: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub struct TankConfig {
|
||||||
pub tank_sensor_enabled: bool,
|
pub tank_sensor_enabled: bool,
|
||||||
|
pub tank_allow_pumping_if_sensor_error: bool,
|
||||||
pub tank_useable_ml: u32,
|
pub tank_useable_ml: u32,
|
||||||
pub tank_warn_percent: u8,
|
pub tank_warn_percent: u8,
|
||||||
pub tank_empty_percent: u8,
|
pub tank_empty_percent: u8,
|
||||||
pub tank_full_percent: u8,
|
pub tank_full_percent: u8,
|
||||||
|
|
||||||
pub night_lamp_hour_start: u8,
|
|
||||||
pub night_lamp_hour_end: u8,
|
|
||||||
pub night_lamp_only_when_dark: bool,
|
|
||||||
|
|
||||||
pub plants: [Plant; PLANT_COUNT],
|
|
||||||
}
|
}
|
||||||
|
impl Default for TankConfig {
|
||||||
impl Default for Config {
|
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
ap_ssid: heapless::String::from_str("Plantctrl").unwrap(),
|
tank_sensor_enabled: false,
|
||||||
ssid: None,
|
|
||||||
password: None,
|
|
||||||
|
|
||||||
base_topic: Some(heapless::String::from_str("plant/one").unwrap()),
|
|
||||||
mqtt_url: Some(heapless::String::from_str("mqtt://192.168.1.1:1883").unwrap()),
|
|
||||||
|
|
||||||
tank_allow_pumping_if_sensor_error: true,
|
tank_allow_pumping_if_sensor_error: true,
|
||||||
tank_sensor_enabled: true,
|
tank_useable_ml: 50000,
|
||||||
tank_warn_percent: 50,
|
tank_warn_percent: 40,
|
||||||
night_lamp_hour_start: 21,
|
tank_empty_percent: 5,
|
||||||
night_lamp_hour_end: 2,
|
tank_full_percent: 95,
|
||||||
night_lamp_only_when_dark: true,
|
|
||||||
plants: from_fn(|_i| Plant::default()),
|
|
||||||
max_consecutive_pump_count: 15,
|
|
||||||
tank_useable_ml: 5000,
|
|
||||||
tank_empty_percent: 0_u8,
|
|
||||||
tank_full_percent: 100_u8,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
|
||||||
pub enum Mode {
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||||
OFF,
|
pub struct PlantControllerConfig {
|
||||||
TargetMoisture,
|
pub network: NetworkConfig,
|
||||||
TimerOnly,
|
pub tank: TankConfig,
|
||||||
TimerAndDeadzone,
|
pub night_lamp: NightLampConfig,
|
||||||
|
pub plants: [PlantConfig; PLANT_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
pub struct Plant {
|
pub struct PlantConfig {
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub target_moisture: u8,
|
pub target_moisture: u8,
|
||||||
pub pump_time_s: u16,
|
pub pump_time_s: u16,
|
||||||
@ -70,17 +79,27 @@ pub struct Plant {
|
|||||||
pub pump_hour_start: u8,
|
pub pump_hour_start: u8,
|
||||||
pub pump_hour_end: u8,
|
pub pump_hour_end: u8,
|
||||||
pub sensor_b: bool,
|
pub sensor_b: bool,
|
||||||
|
pub max_consecutive_pump_count: u8,
|
||||||
}
|
}
|
||||||
impl Default for Plant {
|
impl Default for PlantConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
target_moisture: 40,
|
|
||||||
pump_time_s: 60,
|
|
||||||
pump_cooldown_min: 60,
|
|
||||||
pump_hour_start: 8,
|
|
||||||
pump_hour_end: 20,
|
|
||||||
mode: Mode::OFF,
|
mode: Mode::OFF,
|
||||||
|
target_moisture: 40,
|
||||||
|
pump_time_s: 30,
|
||||||
|
pump_cooldown_min: 60,
|
||||||
|
pump_hour_start: 9,
|
||||||
|
pump_hour_end: 20,
|
||||||
sensor_b: false,
|
sensor_b: false,
|
||||||
|
max_consecutive_pump_count: 10,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
pub enum Mode {
|
||||||
|
OFF,
|
||||||
|
TargetMoisture,
|
||||||
|
TimerOnly,
|
||||||
|
TimerAndDeadzone,
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
config::Config,
|
config::PlantControllerConfig,
|
||||||
espota::{mark_app_valid, rollback_and_reboot},
|
espota::{mark_app_valid, rollback_and_reboot},
|
||||||
webserver::webserver::httpd,
|
webserver::webserver::httpd,
|
||||||
};
|
};
|
||||||
@ -250,7 +250,7 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let config: Config;
|
let config: PlantControllerConfig;
|
||||||
match board.get_config() {
|
match board.get_config() {
|
||||||
Ok(valid) => {
|
Ok(valid) => {
|
||||||
config = valid;
|
config = valid;
|
||||||
@ -271,8 +271,12 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
let mut sntp = false;
|
let mut sntp = false;
|
||||||
println!("attempting to connect wifi");
|
println!("attempting to connect wifi");
|
||||||
let mut ip_address: Option<String> = None;
|
let mut ip_address: Option<String> = None;
|
||||||
if config.ssid.is_some() {
|
if config.network.ssid.is_some() {
|
||||||
match board.wifi(config.ssid.clone().unwrap(), config.password.clone(), 10000) {
|
match board.wifi(
|
||||||
|
config.network.ssid.clone().unwrap(),
|
||||||
|
config.network.password.clone(),
|
||||||
|
10000,
|
||||||
|
) {
|
||||||
Ok(ip_info) => {
|
Ok(ip_info) => {
|
||||||
ip_address = Some(ip_info.ip.to_string());
|
ip_address = Some(ip_info.ip.to_string());
|
||||||
wifi = true;
|
wifi = true;
|
||||||
@ -412,16 +416,16 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
let mut did_pump = false;
|
let mut did_pump = false;
|
||||||
match plant_to_pump {
|
match plant_to_pump {
|
||||||
Some(plant) => {
|
Some(plant) => {
|
||||||
|
let plant_config = &config.plants[plant];
|
||||||
|
|
||||||
let state = &mut plantstate[plant];
|
let state = &mut plantstate[plant];
|
||||||
state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
|
state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
|
||||||
board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
|
board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
|
||||||
if state.consecutive_pump_count > config.max_consecutive_pump_count.into() {
|
if state.consecutive_pump_count > plant_config.max_consecutive_pump_count.into() {
|
||||||
state.not_effective = true;
|
state.not_effective = true;
|
||||||
board.fault(plant, true);
|
board.fault(plant, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let plant_config = &config.plants[plant];
|
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"Trying to pump for {}s with pump {} now",
|
"Trying to pump for {}s with pump {} now",
|
||||||
plant_config.pump_time_s, plant
|
plant_config.pump_time_s, plant
|
||||||
@ -454,8 +458,8 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
light_state.is_day = is_day;
|
light_state.is_day = is_day;
|
||||||
light_state.out_of_work_hour = !in_time_range(
|
light_state.out_of_work_hour = !in_time_range(
|
||||||
&timezone_time,
|
&timezone_time,
|
||||||
config.night_lamp_hour_start,
|
config.night_lamp.night_lamp_hour_start,
|
||||||
config.night_lamp_hour_end,
|
config.night_lamp.night_lamp_hour_end,
|
||||||
);
|
);
|
||||||
|
|
||||||
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
let state_of_charge = board.state_charge_percent().unwrap_or(0);
|
||||||
@ -467,7 +471,7 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
light_state.battery_low = board.low_voltage_in_cycle();
|
light_state.battery_low = board.low_voltage_in_cycle();
|
||||||
|
|
||||||
if !light_state.out_of_work_hour {
|
if !light_state.out_of_work_hour {
|
||||||
if config.night_lamp_only_when_dark {
|
if config.night_lamp.night_lamp_only_when_dark {
|
||||||
if !light_state.is_day {
|
if !light_state.is_day {
|
||||||
if light_state.battery_low {
|
if light_state.battery_low {
|
||||||
board.light(false).unwrap();
|
board.light(false).unwrap();
|
||||||
@ -537,7 +541,7 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
|
|
||||||
fn publish_battery_state(
|
fn publish_battery_state(
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
) {
|
) {
|
||||||
let bat = BatteryState {
|
let bat = BatteryState {
|
||||||
voltage_milli_volt: &to_string(&board.voltage_milli_volt()),
|
voltage_milli_volt: &to_string(&board.voltage_milli_volt()),
|
||||||
@ -560,9 +564,9 @@ fn publish_battery_state(
|
|||||||
|
|
||||||
fn determine_tank_state(
|
fn determine_tank_state(
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
) -> TankState {
|
) -> TankState {
|
||||||
if config.tank_sensor_enabled {
|
if config.tank.tank_sensor_enabled {
|
||||||
let mut rv: TankState = TankState {
|
let mut rv: TankState = TankState {
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
@ -572,30 +576,30 @@ fn determine_tank_state(
|
|||||||
rv.raw = raw;
|
rv.raw = raw;
|
||||||
return map_range(
|
return map_range(
|
||||||
(
|
(
|
||||||
config.tank_empty_percent as f32,
|
config.tank.tank_empty_percent as f32,
|
||||||
config.tank_full_percent as f32,
|
config.tank.tank_full_percent as f32,
|
||||||
),
|
),
|
||||||
raw as f32,
|
raw as f32,
|
||||||
);
|
);
|
||||||
})
|
})
|
||||||
.and_then(|percent| {
|
.and_then(|percent| {
|
||||||
rv.left_ml = ((percent * config.tank_useable_ml as f32) / 100_f32) as u32;
|
rv.left_ml = ((percent * config.tank.tank_useable_ml as f32) / 100_f32) as u32;
|
||||||
println!(
|
println!(
|
||||||
"Tank sensor returned mv {} as {}% leaving {} ml useable",
|
"Tank sensor returned mv {} as {}% leaving {} ml useable",
|
||||||
rv.raw, percent as u8, rv.left_ml
|
rv.raw, percent as u8, rv.left_ml
|
||||||
);
|
);
|
||||||
if config.tank_warn_percent > percent as u8 {
|
if config.tank.tank_warn_percent > percent as u8 {
|
||||||
board.general_fault(true);
|
board.general_fault(true);
|
||||||
println!(
|
println!(
|
||||||
"Low water, current percent is {}, minimum warn level is {}",
|
"Low water, current percent is {}, minimum warn level is {}",
|
||||||
percent as u8, config.tank_warn_percent
|
percent as u8, config.tank.tank_warn_percent
|
||||||
);
|
);
|
||||||
rv.warn_level = true;
|
rv.warn_level = true;
|
||||||
}
|
}
|
||||||
if config.tank_empty_percent < percent as u8 {
|
if config.tank.tank_empty_percent < percent as u8 {
|
||||||
println!(
|
println!(
|
||||||
"Enough water, current percent is {}, minimum empty level is {}",
|
"Enough water, current percent is {}, minimum empty level is {}",
|
||||||
percent as u8, config.tank_empty_percent
|
percent as u8, config.tank.tank_empty_percent
|
||||||
);
|
);
|
||||||
rv.enough_water = true;
|
rv.enough_water = true;
|
||||||
}
|
}
|
||||||
@ -624,7 +628,7 @@ fn determine_state_target_moisture_for_plant(
|
|||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankState,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
@ -671,7 +675,7 @@ fn determine_state_target_moisture_for_plant(
|
|||||||
|
|
||||||
if a_low || b_low {
|
if a_low || b_low {
|
||||||
state.dry = true;
|
state.dry = true;
|
||||||
if tank_state.sensor_error && !config.tank_allow_pumping_if_sensor_error {
|
if tank_state.sensor_error && !config.tank.tank_allow_pumping_if_sensor_error {
|
||||||
//ignore is ok
|
//ignore is ok
|
||||||
} else if !tank_state.enough_water {
|
} else if !tank_state.enough_water {
|
||||||
state.no_water = true;
|
state.no_water = true;
|
||||||
@ -714,7 +718,7 @@ fn determine_state_timer_only_for_plant(
|
|||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankState,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
@ -730,7 +734,7 @@ fn determine_state_timer_only_for_plant(
|
|||||||
state.next_pump = Some(europe_time);
|
state.next_pump = Some(europe_time);
|
||||||
state.cooldown = true;
|
state.cooldown = true;
|
||||||
} else {
|
} else {
|
||||||
if tank_state.sensor_error && !config.tank_allow_pumping_if_sensor_error {
|
if tank_state.sensor_error && !config.tank.tank_allow_pumping_if_sensor_error {
|
||||||
state.do_water = true;
|
state.do_water = true;
|
||||||
} else if !tank_state.enough_water {
|
} else if !tank_state.enough_water {
|
||||||
state.no_water = true;
|
state.no_water = true;
|
||||||
@ -752,7 +756,7 @@ fn determine_state_timer_and_deadzone_for_plant(
|
|||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
plant: usize,
|
plant: usize,
|
||||||
state: &mut PlantState,
|
state: &mut PlantState,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
tank_state: &TankState,
|
tank_state: &TankState,
|
||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
) {
|
) {
|
||||||
@ -776,7 +780,7 @@ fn determine_state_timer_and_deadzone_for_plant(
|
|||||||
state.out_of_work_hour = true;
|
state.out_of_work_hour = true;
|
||||||
}
|
}
|
||||||
if !state.cooldown && !state.out_of_work_hour {
|
if !state.cooldown && !state.out_of_work_hour {
|
||||||
if tank_state.sensor_error && !config.tank_allow_pumping_if_sensor_error {
|
if tank_state.sensor_error && !config.tank.tank_allow_pumping_if_sensor_error {
|
||||||
state.do_water = true;
|
state.do_water = true;
|
||||||
} else if !tank_state.enough_water {
|
} else if !tank_state.enough_water {
|
||||||
state.no_water = true;
|
state.no_water = true;
|
||||||
@ -799,7 +803,7 @@ fn determine_next_plant(
|
|||||||
cur: DateTime<Tz>,
|
cur: DateTime<Tz>,
|
||||||
tank_state: &TankState,
|
tank_state: &TankState,
|
||||||
water_frozen: bool,
|
water_frozen: bool,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
) -> Option<usize> {
|
) -> Option<usize> {
|
||||||
for plant in 0..PLANT_COUNT {
|
for plant in 0..PLANT_COUNT {
|
||||||
@ -850,7 +854,7 @@ fn determine_next_plant(
|
|||||||
fn update_plant_state(
|
fn update_plant_state(
|
||||||
plantstate: &mut [PlantState; PLANT_COUNT],
|
plantstate: &mut [PlantState; PLANT_COUNT],
|
||||||
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
|
||||||
config: &Config,
|
config: &PlantControllerConfig,
|
||||||
) {
|
) {
|
||||||
for plant in 0..PLANT_COUNT {
|
for plant in 0..PLANT_COUNT {
|
||||||
let state = &plantstate[plant];
|
let state = &plantstate[plant];
|
||||||
|
@ -2,7 +2,6 @@ use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
|
|||||||
|
|
||||||
use ds323x::{DateTimeAccess, Ds323x};
|
use ds323x::{DateTimeAccess, Ds323x};
|
||||||
|
|
||||||
use eeprom24x::page_size::No;
|
|
||||||
use eeprom24x::{Eeprom24x, SlaveAddr};
|
use eeprom24x::{Eeprom24x, SlaveAddr};
|
||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
use embedded_svc::wifi::{
|
use embedded_svc::wifi::{
|
||||||
@ -14,7 +13,6 @@ use esp_idf_hal::adc::{attenuation, Resolution};
|
|||||||
use esp_idf_hal::i2c::{APBTickType, I2cConfig, I2cDriver, I2cError};
|
use esp_idf_hal::i2c::{APBTickType, I2cConfig, I2cDriver, I2cError};
|
||||||
use esp_idf_hal::units::FromValueType;
|
use esp_idf_hal::units::FromValueType;
|
||||||
use esp_idf_svc::eventloop::EspSystemEventLoop;
|
use esp_idf_svc::eventloop::EspSystemEventLoop;
|
||||||
use esp_idf_svc::io::vfs;
|
|
||||||
use esp_idf_svc::ipv4::IpInfo;
|
use esp_idf_svc::ipv4::IpInfo;
|
||||||
use esp_idf_svc::mqtt::client::QoS::AtLeastOnce;
|
use esp_idf_svc::mqtt::client::QoS::AtLeastOnce;
|
||||||
use esp_idf_svc::mqtt::client::QoS::ExactlyOnce;
|
use esp_idf_svc::mqtt::client::QoS::ExactlyOnce;
|
||||||
@ -30,8 +28,7 @@ use anyhow::{anyhow, Context};
|
|||||||
use anyhow::{bail, Ok, Result};
|
use anyhow::{bail, Ok, Result};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use std::ffi::CString;
|
use std::ffi::CString;
|
||||||
use std::fs::{self, DirEntry, File};
|
use std::fs::{self, File};
|
||||||
use std::io::{Read, Write};
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
@ -52,10 +49,10 @@ use esp_idf_hal::prelude::Peripherals;
|
|||||||
use esp_idf_hal::reset::ResetReason;
|
use esp_idf_hal::reset::ResetReason;
|
||||||
use esp_idf_svc::sntp::{self, SyncStatus};
|
use esp_idf_svc::sntp::{self, SyncStatus};
|
||||||
use esp_idf_svc::systime::EspSystemTime;
|
use esp_idf_svc::systime::EspSystemTime;
|
||||||
use esp_idf_sys::{esp, esp_spiffs_check, f_opendir, gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
use esp_idf_sys::{esp, esp_spiffs_check, gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
|
|
||||||
use crate::config::{self, Config};
|
use crate::config::{self, PlantControllerConfig};
|
||||||
use crate::{plant_hal, STAY_ALIVE};
|
use crate::{plant_hal, STAY_ALIVE};
|
||||||
|
|
||||||
//Only support for 8 right now!
|
//Only support for 8 right now!
|
||||||
@ -171,37 +168,35 @@ pub struct PlantCtrlBoard<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct FileInfo{
|
pub struct FileInfo {
|
||||||
filename:String,
|
filename: String,
|
||||||
size:usize
|
size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
pub struct FileList{
|
pub struct FileList {
|
||||||
files: Vec<FileInfo>,
|
files: Vec<FileInfo>,
|
||||||
file_system_corrupt: Option<String>,
|
file_system_corrupt: Option<String>,
|
||||||
iter_error: Option<String>,
|
iter_error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlantCtrlBoard<'_> {
|
impl PlantCtrlBoard<'_> {
|
||||||
pub fn list_files(&self, filename:&str) -> FileList {
|
pub fn list_files(&self, filename: &str) -> FileList {
|
||||||
let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
|
let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
|
||||||
let error = unsafe {
|
let error = unsafe {
|
||||||
esp!{
|
esp! {
|
||||||
esp_spiffs_check(storage.as_ptr())
|
esp_spiffs_check(storage.as_ptr())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut file_system_corrupt = match error {
|
let mut file_system_corrupt = match error {
|
||||||
OkStd(_) => {
|
OkStd(_) => None,
|
||||||
None
|
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
println!("Corrupt spiffs {err:?}");
|
println!("Corrupt spiffs {err:?}");
|
||||||
Some(format!("{err:?}"))
|
Some(format!("{err:?}"))
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut iter_error = None;
|
let mut iter_error = None;
|
||||||
let mut result = Vec::new();
|
let mut result = Vec::new();
|
||||||
|
|
||||||
@ -214,49 +209,53 @@ impl PlantCtrlBoard<'_> {
|
|||||||
println!("start loop");
|
println!("start loop");
|
||||||
match item {
|
match item {
|
||||||
OkStd(file) => {
|
OkStd(file) => {
|
||||||
let f = FileInfo{
|
let f = FileInfo {
|
||||||
filename: file.file_name().into_string().unwrap(),
|
filename: file.file_name().into_string().unwrap(),
|
||||||
size: file.metadata().and_then(|it| core::result::Result::Ok(it.len())).unwrap_or_default() as usize
|
size: file
|
||||||
|
.metadata()
|
||||||
|
.and_then(|it| core::result::Result::Ok(it.len()))
|
||||||
|
.unwrap_or_default()
|
||||||
|
as usize,
|
||||||
};
|
};
|
||||||
println!("fileinfo {f:?}");
|
println!("fileinfo {f:?}");
|
||||||
result.push(f);
|
result.push(f);
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
iter_error = Some (format!("{err:?}"));
|
iter_error = Some(format!("{err:?}"));
|
||||||
break;
|
break;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
file_system_corrupt = Some(format!("{err:?}"));
|
file_system_corrupt = Some(format!("{err:?}"));
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FileList{
|
return FileList {
|
||||||
file_system_corrupt,
|
file_system_corrupt,
|
||||||
files: result,
|
files: result,
|
||||||
iter_error
|
iter_error,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn delete_file(&self, filename:&str) -> Result<()>{
|
pub fn delete_file(&self, filename: &str) -> Result<()> {
|
||||||
let filepath = Path::new(BASE_PATH).join(Path::new(filename));
|
let filepath = Path::new(BASE_PATH).join(Path::new(filename));
|
||||||
match (fs::remove_file(filepath)){
|
match fs::remove_file(filepath) {
|
||||||
OkStd(_) => Ok(()),
|
OkStd(_) => Ok(()),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
bail!(format!("{err:?}"))
|
bail!(format!("{err:?}"))
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_file_handle(&self, filename:&str, write:bool) -> Result<File> {
|
pub fn get_file_handle(&self, filename: &str, write: bool) -> Result<File> {
|
||||||
let filepath = Path::new(BASE_PATH).join(Path::new(filename));
|
let filepath = Path::new(BASE_PATH).join(Path::new(filename));
|
||||||
return Ok(if write {
|
return Ok(if write {
|
||||||
File::create(filepath)?
|
File::create(filepath)?
|
||||||
} else {
|
} else {
|
||||||
File::open(filepath)?
|
File::open(filepath)?
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_day(&self) -> bool {
|
pub fn is_day(&self) -> bool {
|
||||||
@ -495,7 +494,10 @@ impl PlantCtrlBoard<'_> {
|
|||||||
delay.delay_ms(10);
|
delay.delay_ms(10);
|
||||||
let unscaled = self.signal_counter.get_counter_value()? as i32;
|
let unscaled = self.signal_counter.get_counter_value()? as i32;
|
||||||
let hz = (unscaled as f32 * factor) as i32;
|
let hz = (unscaled as f32 * factor) as i32;
|
||||||
println!("raw measure unscaled {} hz {}, plant {} sensor {:?}",unscaled, hz, plant, sensor);
|
println!(
|
||||||
|
"raw measure unscaled {} hz {}, plant {} sensor {:?}",
|
||||||
|
unscaled, hz, plant, sensor
|
||||||
|
);
|
||||||
results[repeat] = hz;
|
results[repeat] = hz;
|
||||||
//println!("Measuring {:?} @ {} with {}", sensor, plant, hz);
|
//println!("Measuring {:?} @ {} with {}", sensor, plant, hz);
|
||||||
}
|
}
|
||||||
@ -598,7 +600,7 @@ impl PlantCtrlBoard<'_> {
|
|||||||
max_files: 5,
|
max_files: 5,
|
||||||
format_if_mount_failed: true,
|
format_if_mount_failed: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO check fielsystem esp_spiffs_check
|
//TODO check fielsystem esp_spiffs_check
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -661,13 +663,13 @@ impl PlantCtrlBoard<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_config(&mut self) -> Result<config::Config> {
|
pub fn get_config(&mut self) -> Result<config::PlantControllerConfig> {
|
||||||
let cfg = File::open(CONFIG_FILE)?;
|
let cfg = File::open(CONFIG_FILE)?;
|
||||||
let config: Config = serde_json::from_reader(cfg)?;
|
let config: PlantControllerConfig = serde_json::from_reader(cfg)?;
|
||||||
Ok(config)
|
Ok(config)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_config(&mut self, config: &Config) -> Result<()> {
|
pub fn set_config(&mut self, config: &PlantControllerConfig) -> Result<()> {
|
||||||
let mut cfg = File::create(CONFIG_FILE)?;
|
let mut cfg = File::create(CONFIG_FILE)?;
|
||||||
serde_json::to_writer(&mut cfg, &config)?;
|
serde_json::to_writer(&mut cfg, &config)?;
|
||||||
println!("Wrote config config {:?}", config);
|
println!("Wrote config config {:?}", config);
|
||||||
@ -735,9 +737,17 @@ impl PlantCtrlBoard<'_> {
|
|||||||
config.exists()
|
config.exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mqtt(&mut self, config: &Config) -> Result<()> {
|
pub fn mqtt(&mut self, config: &PlantControllerConfig) -> Result<()> {
|
||||||
let base_topic = config.base_topic.as_ref().context("missing base topic")?;
|
let base_topic = config
|
||||||
let mqtt_url = config.mqtt_url.as_ref().context("missing mqtt url")?;
|
.network
|
||||||
|
.base_topic
|
||||||
|
.as_ref()
|
||||||
|
.context("missing base topic")?;
|
||||||
|
let mqtt_url = config
|
||||||
|
.network
|
||||||
|
.mqtt_url
|
||||||
|
.as_ref()
|
||||||
|
.context("missing mqtt url")?;
|
||||||
|
|
||||||
let last_will_topic = format!("{}/state", base_topic);
|
let last_will_topic = format!("{}/state", base_topic);
|
||||||
let mqtt_client_config = MqttClientConfiguration {
|
let mqtt_client_config = MqttClientConfiguration {
|
||||||
@ -865,7 +875,12 @@ impl PlantCtrlBoard<'_> {
|
|||||||
bail!("Mqtt did not fire connection callback in time");
|
bail!("Mqtt did not fire connection callback in time");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mqtt_publish(&mut self, config: &Config, subtopic: &str, message: &[u8]) -> Result<()> {
|
pub fn mqtt_publish(
|
||||||
|
&mut self,
|
||||||
|
config: &PlantControllerConfig,
|
||||||
|
subtopic: &str,
|
||||||
|
message: &[u8],
|
||||||
|
) -> Result<()> {
|
||||||
if self.mqtt_client.is_none() {
|
if self.mqtt_client.is_none() {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
@ -880,7 +895,7 @@ impl PlantCtrlBoard<'_> {
|
|||||||
let client = self.mqtt_client.as_mut().unwrap();
|
let client = self.mqtt_client.as_mut().unwrap();
|
||||||
let mut full_topic: heapless::String<256> = heapless::String::new();
|
let mut full_topic: heapless::String<256> = heapless::String::new();
|
||||||
if full_topic
|
if full_topic
|
||||||
.push_str(&config.base_topic.as_ref().unwrap())
|
.push_str(&config.network.base_topic.as_ref().unwrap())
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
println!("Some error assembling full_topic 1");
|
println!("Some error assembling full_topic 1");
|
||||||
|
@ -1,20 +1,23 @@
|
|||||||
//offer ota and config mode
|
//offer ota and config mode
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
collections::VecDeque, fs, io::{BufRead, Read, Write}, str::from_utf8, sync::{atomic::AtomicBool, Arc}
|
io::{BufRead, BufReader, Read, Write},
|
||||||
|
str::from_utf8,
|
||||||
|
sync::{atomic::AtomicBool, Arc},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{espota::OtaUpdate, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS};
|
use crate::{espota::OtaUpdate, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS};
|
||||||
|
use anyhow::bail;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
use url::Url;
|
|
||||||
use core::result::Result::Ok;
|
use core::result::Result::Ok;
|
||||||
use embedded_svc::http::Method;
|
use embedded_svc::http::Method;
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_svc::{http::server::{Configuration, EspHttpConnection, EspHttpServer, Request}};
|
use esp_idf_svc::http::server::{Configuration, EspHttpConnection, EspHttpServer, Request};
|
||||||
use heapless::String;
|
use heapless::String;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
use crate::config::Config;
|
use crate::config::PlantControllerConfig;
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct SSIDList<'a> {
|
struct SSIDList<'a> {
|
||||||
@ -87,8 +90,8 @@ fn get_data(
|
|||||||
match a_pct {
|
match a_pct {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
a.push(result);
|
a.push(result);
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(_) => {
|
||||||
a.push(200);
|
a.push(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -97,19 +100,18 @@ fn get_data(
|
|||||||
match b_pct {
|
match b_pct {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
b.push(result);
|
b.push(result);
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(_) => {
|
||||||
b.push(200);
|
b.push(200);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let data = LoadData {
|
let data = LoadData {
|
||||||
rtc: rtc.as_str(),
|
rtc: rtc.as_str(),
|
||||||
native: native.as_str(),
|
native: native.as_str(),
|
||||||
moisture_a: a,
|
moisture_a: a,
|
||||||
moisture_b: b
|
moisture_b: b,
|
||||||
};
|
};
|
||||||
let json = serde_json::to_string(&data)?;
|
let json = serde_json::to_string(&data)?;
|
||||||
|
|
||||||
@ -122,7 +124,7 @@ fn get_config(
|
|||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
let json = match board.get_config() {
|
let json = match board.get_config() {
|
||||||
Ok(config) => serde_json::to_string(&config)?,
|
Ok(config) => serde_json::to_string(&config)?,
|
||||||
Err(_) => serde_json::to_string(&Config::default())?,
|
Err(_) => serde_json::to_string(&PlantControllerConfig::default())?,
|
||||||
};
|
};
|
||||||
anyhow::Ok(Some(json))
|
anyhow::Ok(Some(json))
|
||||||
}
|
}
|
||||||
@ -134,7 +136,7 @@ fn set_config(
|
|||||||
let read = request.read(&mut buf)?;
|
let read = request.read(&mut buf)?;
|
||||||
let actual_data = &buf[0..read];
|
let actual_data = &buf[0..read];
|
||||||
println!("Raw data {}", from_utf8(actual_data).unwrap());
|
println!("Raw data {}", from_utf8(actual_data).unwrap());
|
||||||
let config: Config = serde_json::from_slice(actual_data)?;
|
let config: PlantControllerConfig = serde_json::from_slice(actual_data)?;
|
||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
board.set_config(&config)?;
|
board.set_config(&config)?;
|
||||||
anyhow::Ok(Some("saved".to_owned()))
|
anyhow::Ok(Some("saved".to_owned()))
|
||||||
@ -176,7 +178,7 @@ fn wifi_scan(
|
|||||||
fn list_files(
|
fn list_files(
|
||||||
request: &mut Request<&mut EspHttpConnection>,
|
request: &mut Request<&mut EspHttpConnection>,
|
||||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||||
let filename = query_param(request.uri(),"filename").unwrap_or_default();
|
let filename = query_param(request.uri(), "filename").unwrap_or_default();
|
||||||
let board = BOARD_ACCESS.lock().unwrap();
|
let board = BOARD_ACCESS.lock().unwrap();
|
||||||
let result = board.list_files(&filename);
|
let result = board.list_files(&filename);
|
||||||
let file_list_json = serde_json::to_string(&result)?;
|
let file_list_json = serde_json::to_string(&result)?;
|
||||||
@ -215,17 +217,54 @@ fn ota(
|
|||||||
finalizer.restart();
|
finalizer.restart();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn query_param(uri:&str, param_name:&str) -> Option<std::string::String>{
|
fn flash_bq(filename: &str, dryrun: bool) -> anyhow::Result<()> {
|
||||||
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
|
|
||||||
|
let mut toggle = true;
|
||||||
|
let delay = Delay::new(1);
|
||||||
|
|
||||||
|
let file_handle = board.get_file_handle(filename, false)?;
|
||||||
|
|
||||||
|
let mut reader = BufReader::with_capacity(512, file_handle).lines();
|
||||||
|
let mut line = 0;
|
||||||
|
loop {
|
||||||
|
board.general_fault(toggle);
|
||||||
|
toggle = !toggle;
|
||||||
|
|
||||||
|
delay.delay_us(2);
|
||||||
|
line += 1;
|
||||||
|
match reader.next() {
|
||||||
|
Some(next) => {
|
||||||
|
let input = next?;
|
||||||
|
println!("flashing bq34z100 dryrun:{dryrun} line {line} payload: {input}");
|
||||||
|
match board.flash_bq34_z100(&input, dryrun) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("ok")
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
bail!(
|
||||||
|
"Error flashing bq34z100 in dryrun: {dryrun} line: {line} error: {err}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
println!("Finished flashing file {line} lines processed");
|
||||||
|
board.general_fault(false);
|
||||||
|
return anyhow::Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn query_param(uri: &str, param_name: &str) -> Option<std::string::String> {
|
||||||
println!("{uri} get {param_name}");
|
println!("{uri} get {param_name}");
|
||||||
let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap();
|
let parsed = Url::parse(&format!("http://127.0.0.1/{uri}")).unwrap();
|
||||||
let value = parsed.query_pairs().filter(|it| it.0 == param_name).next();
|
let value = parsed.query_pairs().filter(|it| it.0 == param_name).next();
|
||||||
match value {
|
match value {
|
||||||
Some(found) => {
|
Some(found) => {
|
||||||
return Some(found.1.into_owned());
|
return Some(found.1.into_owned());
|
||||||
},
|
}
|
||||||
None => {
|
None => return None,
|
||||||
return None
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,18 +323,20 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
server
|
server
|
||||||
.fn_handler("/files", Method::Get, move |request| {
|
.fn_handler("/files", Method::Get, move |request| {
|
||||||
handle_error_to500(request, list_files)
|
handle_error_to500(request, list_files)
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
server
|
server
|
||||||
.fn_handler("/file", Method::Get, move |request| {
|
.fn_handler("/file", Method::Get, move |request| {
|
||||||
let filename = query_param(request.uri(),"filename").unwrap();
|
let filename = query_param(request.uri(), "filename").unwrap();
|
||||||
let file_handle = BOARD_ACCESS.lock().unwrap().get_file_handle(&filename, false);
|
let file_handle = BOARD_ACCESS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_file_handle(&filename, false);
|
||||||
match file_handle {
|
match file_handle {
|
||||||
Ok(mut file_handle) => {
|
Ok(mut file_handle) => {
|
||||||
|
let headers = [("Access-Control-Allow-Origin", "*")];
|
||||||
let headers = [("Access-Control-Allow-Origin","*")];
|
|
||||||
let mut response = request.into_response(200, None, &headers)?;
|
let mut response = request.into_response(200, None, &headers)?;
|
||||||
const BUFFER_SIZE: usize = 512;
|
const BUFFER_SIZE: usize = 512;
|
||||||
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||||
@ -303,7 +344,10 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
loop {
|
loop {
|
||||||
let read = file_handle.read(&mut buffer)?;
|
let read = file_handle.read(&mut buffer)?;
|
||||||
total_read += read;
|
total_read += read;
|
||||||
println!("sending {read} bytes of {total_read} for file {}", &filename);
|
println!(
|
||||||
|
"sending {read} bytes of {total_read} for file {}",
|
||||||
|
&filename
|
||||||
|
);
|
||||||
let to_write = &buffer[0..read];
|
let to_write = &buffer[0..read];
|
||||||
response.write(to_write)?;
|
response.write(to_write)?;
|
||||||
println!("wrote {read} bytes of {total_read} for file {filename}");
|
println!("wrote {read} bytes of {total_read} for file {filename}");
|
||||||
@ -313,8 +357,7 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
}
|
}
|
||||||
drop(file_handle);
|
drop(file_handle);
|
||||||
response.flush()?;
|
response.flush()?;
|
||||||
|
}
|
||||||
},
|
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
//todo set headers here for filename to be error
|
//todo set headers here for filename to be error
|
||||||
let error_text = err.to_string();
|
let error_text = err.to_string();
|
||||||
@ -327,8 +370,11 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
server
|
server
|
||||||
.fn_handler("/file", Method::Post, move |mut request| {
|
.fn_handler("/file", Method::Post, move |mut request| {
|
||||||
let filename = query_param(request.uri(),"filename").unwrap();
|
let filename = query_param(request.uri(), "filename").unwrap();
|
||||||
let file_handle = BOARD_ACCESS.lock().unwrap().get_file_handle(&filename,true);
|
let file_handle = BOARD_ACCESS
|
||||||
|
.lock()
|
||||||
|
.unwrap()
|
||||||
|
.get_file_handle(&filename, true);
|
||||||
match file_handle {
|
match file_handle {
|
||||||
Ok(mut file_handle) => {
|
Ok(mut file_handle) => {
|
||||||
const BUFFER_SIZE: usize = 512;
|
const BUFFER_SIZE: usize = 512;
|
||||||
@ -346,7 +392,7 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
cors_response(request, 200, &format!("saved {total_read} bytes"))?;
|
cors_response(request, 200, &format!("saved {total_read} bytes"))?;
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
//todo set headers here for filename to be error
|
//todo set headers here for filename to be error
|
||||||
let error_text = err.to_string();
|
let error_text = err.to_string();
|
||||||
@ -357,90 +403,51 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
server
|
server
|
||||||
.fn_handler("/file", Method::Delete, move |request| {
|
.fn_handler("/file", Method::Delete, move |request| {
|
||||||
let filename = query_param(request.uri(),"filename").unwrap();
|
let filename = query_param(request.uri(), "filename").unwrap();
|
||||||
let copy = filename.clone();
|
let copy = filename.clone();
|
||||||
let board = BOARD_ACCESS.lock().unwrap();
|
let board = BOARD_ACCESS.lock().unwrap();
|
||||||
match board.delete_file(&filename) {
|
match board.delete_file(&filename) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let info = format!("Deleted file {copy}");
|
let info = format!("Deleted file {copy}");
|
||||||
cors_response(request, 200, &info)?;
|
cors_response(request, 200, &info)?;
|
||||||
},
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let info = format!("Could not delete file {copy} {err:?}");
|
let info = format!("Could not delete file {copy} {err:?}");
|
||||||
cors_response(request, 400, &info)?;
|
cors_response(request, 400, &info)?;
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
server
|
server
|
||||||
.fn_handler("/flashbattery", Method::Post, move |mut request| {
|
.fn_handler("/flashbattery", Method::Post, move |request| {
|
||||||
|
let filename = query_param(request.uri(),"filename").unwrap();
|
||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
let dryrun = true;
|
||||||
let mut buffer: [u8; 128] = [0; 128];
|
match flash_bq(&filename, false) {
|
||||||
let mut line_buffer: VecDeque<u8> = VecDeque::new();
|
Ok(_) => {
|
||||||
|
if !dryrun {
|
||||||
let is_dry_run = !request.uri().ends_with("?flash=true");
|
match flash_bq(&filename, true) {
|
||||||
let mut total_read: usize = 0;
|
Ok(_) => {
|
||||||
|
cors_response(request, 200, "Sucessfully flashed bq34z100")?;
|
||||||
let mut toggle = true;
|
},
|
||||||
let delay = Delay::new(1);
|
Err(err) => {
|
||||||
todo!("Write to storage before attempting to flash!");
|
let info = format!("Could not flash bq34z100, could be bricked now! {filename} {err:?}");
|
||||||
loop {
|
cors_response(request, 500, &info)?;
|
||||||
delay.delay_us(2);
|
},
|
||||||
let read = request.read(&mut buffer).unwrap();
|
}
|
||||||
total_read += read;
|
} else {
|
||||||
if read == 0 {
|
cors_response(request, 200, "Sucessfully processed bq34z100")?;
|
||||||
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;
|
},
|
||||||
}
|
Err(err) => {
|
||||||
let to_write = &buffer[0..read];
|
let info = format!("Could not process firmware file for, bq34z100, refusing to flash! {filename} {err:?}");
|
||||||
|
cors_response(request, 500, &info)?;
|
||||||
line_buffer.write_all(to_write).unwrap();
|
},
|
||||||
board.general_fault(toggle);
|
};
|
||||||
toggle = !toggle;
|
|
||||||
loop {
|
|
||||||
let has_line = line_buffer.contains(&b'\n');
|
|
||||||
if !has_line {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
let mut line = std::string::String::new();
|
|
||||||
line_buffer.read_line(&mut line)?;
|
|
||||||
let line2 = &line[0..line.len()-1];
|
|
||||||
println!("Processing dry:{} line {}", is_dry_run, line2);
|
|
||||||
let validate = board.flash_bq34_z100(&line2, is_dry_run);
|
|
||||||
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();
|
|
||||||
println!("Error writing {}", err_str);
|
|
||||||
response
|
|
||||||
.write(err_msg)
|
|
||||||
.unwrap();
|
|
||||||
return anyhow::Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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(false);
|
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -448,7 +455,7 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
server
|
server
|
||||||
.fn_handler("/", Method::Get, move |request| {
|
.fn_handler("/", Method::Get, move |request| {
|
||||||
let mut response = request.into_ok_response()?;
|
let mut response = request.into_ok_response()?;
|
||||||
response.write(include_bytes!("index.html"))?;let mut buf = [0_u8; 3072];
|
response.write(include_bytes!("index.html"))?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
@ -458,7 +465,6 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
.into_ok_response()?
|
.into_ok_response()?
|
||||||
.write(include_bytes!("favicon.ico"))?;
|
.write(include_bytes!("favicon.ico"))?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
server
|
server
|
||||||
@ -467,14 +473,17 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
|||||||
.into_ok_response()?
|
.into_ok_response()?
|
||||||
.write(include_bytes!("bundle.js"))?;
|
.write(include_bytes!("bundle.js"))?;
|
||||||
anyhow::Ok(())
|
anyhow::Ok(())
|
||||||
|
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
server
|
server
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cors_response(request: Request<&mut EspHttpConnection>, status: u16, body: &str) -> Result<(), anyhow::Error>{
|
fn cors_response(
|
||||||
let headers = [("Access-Control-Allow-Origin","*")];
|
request: Request<&mut EspHttpConnection>,
|
||||||
|
status: u16,
|
||||||
|
body: &str,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let headers = [("Access-Control-Allow-Origin", "*")];
|
||||||
let mut response = request.into_response(status, None, &headers)?;
|
let mut response = request.into_response(status, None, &headers)?;
|
||||||
response.write(body.as_bytes())?;
|
response.write(body.as_bytes())?;
|
||||||
response.flush()?;
|
response.flush()?;
|
||||||
@ -504,4 +513,4 @@ fn handle_error_to500(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return anyhow::Ok(());
|
return anyhow::Ok(());
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user