Housekeeping #18
7
rust/.idea/dictionaries/project.xml
generated
7
rust/.idea/dictionaries/project.xml
generated
@ -1,7 +1,14 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="project">
|
||||
<words>
|
||||
<w>buildtime</w>
|
||||
<w>deepsleep</w>
|
||||
<w>githash</w>
|
||||
<w>lightstate</w>
|
||||
<w>mppt</w>
|
||||
<w>plantstate</w>
|
||||
<w>sntp</w>
|
||||
<w>vergen</w>
|
||||
</words>
|
||||
</dictionary>
|
||||
</component>
|
@ -63,14 +63,14 @@ pub struct MqttClient<'a> {
|
||||
mqtt_client: EspMqttClient<'a>,
|
||||
base_topic: heapless::String<64>,
|
||||
}
|
||||
pub struct ESP<'a> {
|
||||
pub struct Esp<'a> {
|
||||
pub(crate) mqtt_client: Option<MqttClient<'a>>,
|
||||
pub(crate) wifi_driver: EspWifi<'a>,
|
||||
pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||
pub(crate) delay: Delay,
|
||||
}
|
||||
|
||||
impl ESP<'_> {
|
||||
impl Esp<'_> {
|
||||
const SPIFFS_PARTITION_NAME: &'static str = "storage";
|
||||
const CONFIG_FILE: &'static str = "/spiffs/config.cfg";
|
||||
const BASE_PATH: &'static str = "/spiffs";
|
||||
@ -310,7 +310,7 @@ impl ESP<'_> {
|
||||
filename: file.file_name().into_string().unwrap(),
|
||||
size: file
|
||||
.metadata()
|
||||
.and_then(|it| Ok(it.len()))
|
||||
.map(|it| it.len())
|
||||
.unwrap_or_default()
|
||||
as usize,
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::hal::esp::ESP;
|
||||
use crate::hal::esp::Esp;
|
||||
use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
|
||||
use crate::{
|
||||
config::PlantControllerConfig,
|
||||
@ -13,7 +13,7 @@ use measurements::{Current, Voltage};
|
||||
|
||||
pub struct Initial<'a> {
|
||||
pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||
pub(crate) esp: ESP<'a>,
|
||||
pub(crate) esp: Esp<'a>,
|
||||
pub(crate) config: PlantControllerConfig,
|
||||
pub(crate) battery: Box<dyn BatteryInteraction + Send>,
|
||||
}
|
||||
@ -22,7 +22,7 @@ pub(crate) fn create_initial_board(
|
||||
free_pins: FreePeripherals,
|
||||
fs_mount_error: bool,
|
||||
config: PlantControllerConfig,
|
||||
esp: ESP<'static>,
|
||||
esp: Esp<'static>,
|
||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
|
||||
let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
|
||||
general_fault.set_pull(Pull::Floating)?;
|
||||
@ -41,7 +41,7 @@ pub(crate) fn create_initial_board(
|
||||
}
|
||||
|
||||
impl<'a> BoardInteraction<'a> for Initial<'a> {
|
||||
fn get_esp(&mut self) -> &mut ESP<'a> {
|
||||
fn get_esp(&mut self) -> &mut Esp<'a> {
|
||||
&mut self.esp
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use crate::{
|
||||
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
|
||||
hal::{
|
||||
battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor},
|
||||
esp::ESP,
|
||||
esp::Esp,
|
||||
},
|
||||
log::{log, LogMessage},
|
||||
};
|
||||
@ -37,12 +37,12 @@ use esp_idf_sys::{
|
||||
esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW,
|
||||
};
|
||||
use esp_ota::mark_app_valid;
|
||||
use measurements::{Current, Voltage};
|
||||
use once_cell::sync::Lazy;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::result::Result::Ok as OkStd;
|
||||
use std::sync::Mutex;
|
||||
use std::time::Duration;
|
||||
use measurements::{Current, Voltage};
|
||||
|
||||
//Only support for 8 right now!
|
||||
pub const PLANT_COUNT: usize = 8;
|
||||
@ -84,25 +84,15 @@ pub struct HAL<'a> {
|
||||
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
|
||||
pub struct BackupHeader {
|
||||
pub timestamp: i64,
|
||||
crc16: u16,
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
impl Default for BackupHeader {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
timestamp: Default::default(),
|
||||
crc16: Default::default(),
|
||||
size: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait BoardInteraction<'a> {
|
||||
fn get_esp(&mut self) -> &mut ESP<'a>;
|
||||
fn get_esp(&mut self) -> &mut Esp<'a>;
|
||||
fn get_config(&mut self) -> &PlantControllerConfig;
|
||||
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
||||
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
|
||||
@ -225,7 +215,7 @@ impl PlantHal {
|
||||
gpio30: peripherals.pins.gpio30,
|
||||
};
|
||||
|
||||
let mut esp = ESP {
|
||||
let mut esp = Esp {
|
||||
mqtt_client: None,
|
||||
wifi_driver,
|
||||
boot_button,
|
||||
|
@ -5,7 +5,7 @@ use crate::hal::{
|
||||
use crate::log::{log, LogMessage};
|
||||
use crate::{
|
||||
config::PlantControllerConfig,
|
||||
hal::{battery::BatteryInteraction, esp::ESP},
|
||||
hal::{battery::BatteryInteraction, esp::Esp},
|
||||
};
|
||||
use anyhow::{anyhow, bail, Ok, Result};
|
||||
use chrono::{DateTime, Utc};
|
||||
@ -79,7 +79,7 @@ const FAULT_2: usize = 23;
|
||||
pub struct V3<'a> {
|
||||
config: PlantControllerConfig,
|
||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||
esp: ESP<'a>,
|
||||
esp: Esp<'a>,
|
||||
shift_register: ShiftRegister40<
|
||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||
@ -107,7 +107,7 @@ pub struct V3<'a> {
|
||||
|
||||
pub(crate) fn create_v3(
|
||||
peripherals: FreePeripherals,
|
||||
esp: ESP<'static>,
|
||||
esp: Esp<'static>,
|
||||
config: PlantControllerConfig,
|
||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
|
||||
@ -270,7 +270,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
||||
fn get_mptt_current(&mut self) -> Result<Current> {
|
||||
bail!("Board does not have current sensor")
|
||||
}
|
||||
fn get_esp(&mut self) -> &mut ESP<'a> {
|
||||
fn get_esp(&mut self) -> &mut Esp<'a> {
|
||||
&mut self.esp
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::config::PlantControllerConfig;
|
||||
use crate::hal::battery::BatteryInteraction;
|
||||
use crate::hal::esp::ESP;
|
||||
use crate::hal::esp::Esp;
|
||||
use crate::hal::{
|
||||
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
|
||||
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
|
||||
@ -22,15 +22,15 @@ use esp_idf_hal::i2c::I2cDriver;
|
||||
use esp_idf_hal::pcnt::{
|
||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||
};
|
||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError};
|
||||
use ina219::address::{Address, Pin};
|
||||
use ina219::calibration::{Calibration, UnCalibrated};
|
||||
use ina219::calibration::UnCalibrated;
|
||||
use ina219::configuration::{Configuration, OperatingMode};
|
||||
use ina219::SyncIna219;
|
||||
use measurements::{Current, Resistance, Voltage};
|
||||
use one_wire_bus::OneWire;
|
||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
||||
use std::result::Result::Ok as OkStd;
|
||||
use ina219::configuration::{Configuration, OperatingMode};
|
||||
|
||||
const MS0: u8 = 1_u8;
|
||||
const MS1: u8 = 0_u8;
|
||||
@ -47,23 +47,27 @@ pub enum Charger<'a> {
|
||||
},
|
||||
}
|
||||
|
||||
impl<'a> Charger<'a> {
|
||||
pub(crate) fn powersave(&mut self) {
|
||||
match self { Charger::SolarMpptV1 { mppt_ina, .. } => {
|
||||
let _ = mppt_ina.set_configuration(Configuration {
|
||||
impl Charger<'_> {
|
||||
pub(crate) fn power_save(&mut self) {
|
||||
match self {
|
||||
Charger::SolarMpptV1 { mppt_ina, .. } => {
|
||||
let _ = mppt_ina
|
||||
.set_configuration(Configuration {
|
||||
reset: Default::default(),
|
||||
bus_voltage_range: Default::default(),
|
||||
shunt_voltage_range: Default::default(),
|
||||
bus_resolution: Default::default(),
|
||||
shunt_resolution: Default::default(),
|
||||
operating_mode: OperatingMode::PowerDown,
|
||||
}).map_err(|e| {
|
||||
})
|
||||
.map_err(|e| {
|
||||
println!(
|
||||
"Error setting ina mppt configuration during deep sleep preparation{:?}",
|
||||
e
|
||||
);
|
||||
});
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
||||
match self {
|
||||
@ -105,7 +109,7 @@ impl<'a> Charger<'a> {
|
||||
}
|
||||
|
||||
pub struct V4<'a> {
|
||||
esp: ESP<'a>,
|
||||
esp: Esp<'a>,
|
||||
charger: Charger<'a>,
|
||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||
config: PlantControllerConfig,
|
||||
@ -130,10 +134,10 @@ pub struct V4<'a> {
|
||||
|
||||
pub(crate) fn create_v4(
|
||||
peripherals: FreePeripherals,
|
||||
esp: ESP<'static>,
|
||||
esp: Esp<'static>,
|
||||
config: PlantControllerConfig,
|
||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||
) -> anyhow::Result<Box<dyn BoardInteraction + Send + '_>> {
|
||||
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
|
||||
let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
|
||||
awake.set_high()?;
|
||||
|
||||
@ -252,7 +256,7 @@ pub(crate) fn create_v4(
|
||||
shunt_resolution: ina219::configuration::Resolution::Avg128,
|
||||
operating_mode: Default::default(),
|
||||
})?;
|
||||
//TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access?
|
||||
//TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access?
|
||||
esp.delay.delay_ms(
|
||||
mppt_ina
|
||||
.configuration()?
|
||||
@ -288,7 +292,7 @@ pub(crate) fn create_v4(
|
||||
}
|
||||
|
||||
impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
fn get_esp(&mut self) -> &mut ESP<'a> {
|
||||
fn get_esp(&mut self) -> &mut Esp<'a> {
|
||||
&mut self.esp
|
||||
}
|
||||
|
||||
@ -306,7 +310,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
|
||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
||||
self.awake.set_low().unwrap();
|
||||
self.charger.powersave();
|
||||
self.charger.power_save();
|
||||
deep_sleep(duration_in_ms);
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ pub fn get_log() -> Vec<LogEntry> {
|
||||
read_copy.push(copy);
|
||||
}
|
||||
drop(buffer);
|
||||
return read_copy;
|
||||
read_copy
|
||||
}
|
||||
|
||||
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
config::BoardVersion::INITIAL,
|
||||
hal::{PlantHal, HAL, PLANT_COUNT},
|
||||
webserver::webserver::httpd,
|
||||
webserver::httpd,
|
||||
};
|
||||
use anyhow::bail;
|
||||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||
@ -12,7 +12,7 @@ use esp_idf_sys::{
|
||||
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,
|
||||
esp_ota_img_states_t_ESP_OTA_IMG_VALID,
|
||||
};
|
||||
use esp_ota::{mark_app_valid, rollback_and_reboot};
|
||||
use hal::battery::BatteryState;
|
||||
@ -28,13 +28,12 @@ mod hal;
|
||||
mod log;
|
||||
mod plant_state;
|
||||
mod tank;
|
||||
mod webserver;
|
||||
|
||||
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
|
||||
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
||||
|
||||
mod webserver {
|
||||
pub mod webserver;
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||
enum WaitType {
|
||||
@ -69,7 +68,7 @@ struct LightState {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
||||
///mqtt stuct to track pump activities
|
||||
///mqtt struct to track pump activities
|
||||
struct PumpInfo {
|
||||
enabled: bool,
|
||||
pump_ineffective: bool,
|
||||
@ -175,16 +174,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
}
|
||||
|
||||
println!("cur is {}", cur);
|
||||
match board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.average_current_milli_ampere()
|
||||
{
|
||||
Ok(charging) => {
|
||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||
}
|
||||
Err(_) => {}
|
||||
}
|
||||
update_charge_indicator(&mut board);
|
||||
|
||||
if board.board_hal.get_esp().get_restart_to_conf() {
|
||||
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
||||
@ -265,7 +255,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
address,
|
||||
ota_state_string,
|
||||
&mut board,
|
||||
&ip_address,
|
||||
ip_address,
|
||||
timezone_time,
|
||||
);
|
||||
publish_battery_state(&mut board);
|
||||
@ -426,7 +416,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.get_battery_state()
|
||||
.unwrap_or(hal::battery::BatteryState::Unknown);
|
||||
.unwrap_or(BatteryState::Unknown);
|
||||
|
||||
let mut light_state = LightState {
|
||||
enabled: board.board_hal.get_config().night_lamp.enabled,
|
||||
@ -556,6 +546,26 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
|
||||
}
|
||||
|
||||
fn update_charge_indicator(board: &mut MutexGuard<HAL>) {
|
||||
//we have mppt controller, ask it for charging current
|
||||
if let Ok(current) = board.board_hal.get_mptt_current() {
|
||||
let _ = board
|
||||
.board_hal
|
||||
.set_charge_indicator(current.as_milliamperes() > 20_f64);
|
||||
}
|
||||
//fallback to battery controller and ask it instead
|
||||
else if let Ok(charging) = board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.average_current_milli_ampere()
|
||||
{
|
||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||
} else {
|
||||
//who knows
|
||||
let _ = board.board_hal.set_charge_indicator(false);
|
||||
}
|
||||
}
|
||||
|
||||
fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> {
|
||||
//multisample should be moved to water_temperature_c
|
||||
let mut attempt = 1;
|
||||
@ -608,7 +618,7 @@ fn publish_plant_states(
|
||||
.zip(&board.board_hal.get_config().plants.clone())
|
||||
.enumerate()
|
||||
{
|
||||
match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, &timezone_time)) {
|
||||
match serde_json::to_string(&plant_state.to_mqtt_info(plant_conf, timezone_time)) {
|
||||
Ok(state) => {
|
||||
let plant_topic = format!("/plant{}", plant_id + 1);
|
||||
let _ = board
|
||||
@ -679,7 +689,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode {
|
||||
SntpMode::OFFLINE
|
||||
}
|
||||
};
|
||||
let mqtt_connected = if let Some(_) = board.board_hal.get_config().network.mqtt_url {
|
||||
let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() {
|
||||
let nw_config = &board.board_hal.get_config().network.clone();
|
||||
match board.board_hal.get_esp().mqtt(nw_config) {
|
||||
Ok(_) => {
|
||||
@ -750,27 +760,10 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
let delay = wait_type.blink_pattern();
|
||||
let mut led_count = 8;
|
||||
let mut pattern_step = 0;
|
||||
|
||||
let delay_handle = Delay::new_default();
|
||||
loop {
|
||||
unsafe {
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
|
||||
//we have mppt controller, ask it for charging current
|
||||
if let Ok(current) = board.board_hal.get_mptt_current() {
|
||||
let _ = board.board_hal.set_charge_indicator(current.as_milliamperes() > 20_f64);
|
||||
}
|
||||
//fallback to battery controller and ask it instead
|
||||
else if let Ok(charging) = board
|
||||
.board_hal
|
||||
.get_battery_monitor()
|
||||
.average_current_milli_ampere()
|
||||
{
|
||||
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||
}
|
||||
else {
|
||||
//who knows
|
||||
let _ = board.board_hal.set_charge_indicator(false);
|
||||
}
|
||||
update_charge_indicator(&mut board);
|
||||
|
||||
match wait_type {
|
||||
WaitType::MissingConfig => {
|
||||
@ -799,7 +792,8 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
|
||||
board.board_hal.general_fault(true);
|
||||
drop(board);
|
||||
vTaskDelay(delay);
|
||||
//cannot use shared delay as that is inside the mutex here
|
||||
delay_handle.delay_ms(delay);
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
board.board_hal.general_fault(false);
|
||||
|
||||
@ -808,7 +802,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
let _ = board.board_hal.fault(i, false);
|
||||
}
|
||||
drop(board);
|
||||
vTaskDelay(delay);
|
||||
delay_handle.delay_ms(delay);
|
||||
|
||||
if wait_type == WaitType::MqttConfig
|
||||
&& !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
|
||||
@ -822,12 +816,11 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let result = safe_main();
|
||||
match result {
|
||||
// this should not get triggered, safe_main should not return but go into deep sleep with sensbile
|
||||
// this should not get triggered, safe_main should not return but go into deep sleep with sensible
|
||||
// timeout, this is just a fallback
|
||||
Ok(_) => {
|
||||
println!("Main app finished, restarting");
|
||||
@ -849,13 +842,13 @@ fn main() {
|
||||
}
|
||||
|
||||
pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
||||
let curhour = cur.hour() as u8;
|
||||
let current_hour = cur.hour() as u8;
|
||||
//eg 10-14
|
||||
if start < end {
|
||||
curhour > start && curhour < end
|
||||
current_hour > start && current_hour < end
|
||||
} else {
|
||||
//eg 20-05
|
||||
curhour > start || curhour < end
|
||||
current_hour > start || current_hour < end
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user