diff --git a/rust/.idea/dictionaries/project.xml b/rust/.idea/dictionaries/project.xml
index e169be7..929ed9a 100644
--- a/rust/.idea/dictionaries/project.xml
+++ b/rust/.idea/dictionaries/project.xml
@@ -1,7 +1,14 @@
+ buildtime
+ deepsleep
+ githash
+ lightstate
+ mppt
+ plantstate
sntp
+ vergen
\ No newline at end of file
diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs
index 70ff6b8..d1fc437 100644
--- a/rust/src/hal/esp.rs
+++ b/rust/src/hal/esp.rs
@@ -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>,
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,
};
diff --git a/rust/src/hal/initial_hal.rs b/rust/src/hal/initial_hal.rs
index cda3d10..14238d2 100644
--- a/rust/src/hal/initial_hal.rs
+++ b/rust/src/hal/initial_hal.rs
@@ -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,
}
@@ -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 + 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
}
diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs
index 6d2ddbb..1155ebc 100644
--- a/rust/src/hal/mod.rs
+++ b/rust/src/hal/mod.rs
@@ -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 + 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;
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,
diff --git a/rust/src/hal/v3_hal.rs b/rust/src/hal/v3_hal.rs
index 3645905..cbca6f1 100644
--- a/rust/src/hal/v3_hal.rs
+++ b/rust/src/hal/v3_hal.rs
@@ -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,
- 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,
) -> Result + Send>> {
@@ -270,7 +270,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
fn get_mptt_current(&mut self) -> Result {
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
}
diff --git a/rust/src/hal/v4_hal.rs b/rust/src/hal/v4_hal.rs
index e3b121b..ddaae4a 100644
--- a/rust/src/hal/v4_hal.rs
+++ b/rust/src/hal/v4_hal.rs
@@ -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 {
- 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| {
- println!(
- "Error setting ina mppt configuration during deepsleep preparation{:?}",
+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| {
+ 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,
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,
-) -> anyhow::Result> {
+) -> anyhow::Result + Send + 'static>> {
let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
awake.set_high()?;
@@ -244,7 +248,7 @@ pub(crate) fn create_v4(
Address::from_pins(Pin::Vcc, Pin::Gnd),
)?;
- mppt_ina.set_configuration(Configuration{
+ mppt_ina.set_configuration(Configuration {
reset: Default::default(),
bus_voltage_range: Default::default(),
shunt_voltage_range: Default::default(),
@@ -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);
}
diff --git a/rust/src/log/mod.rs b/rust/src/log/mod.rs
index 0b3384b..3315333 100644
--- a/rust/src/log/mod.rs
+++ b/rust/src/log/mod.rs
@@ -65,7 +65,7 @@ pub fn get_log() -> Vec {
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) {
diff --git a/rust/src/main.rs b/rust/src/main.rs
index 5a9d60f..9f86aca 100644
--- a/rust/src/main.rs
+++ b/rust/src/main.rs
@@ -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> = Lazy::new(|| PlantHal::create().unwrap());
pub static STAY_ALIVE: Lazy = 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) {
+ //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) -> anyhow::Result {
//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) -> 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,76 +760,59 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! {
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();
+ let mut board = BOARD_ACCESS.lock().unwrap();
+ update_charge_indicator(&mut board);
- //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);
- }
-
- match wait_type {
- WaitType::MissingConfig => {
- // Keep existing behavior: circular filling pattern
- led_count %= 8;
- led_count += 1;
- for i in 0..8 {
- let _ = board.board_hal.fault(i, i < led_count);
- }
- }
- WaitType::ConfigButton => {
- // Alternating pattern: 1010 1010 -> 0101 0101
- pattern_step = (pattern_step + 1) % 2;
- for i in 0..8 {
- let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
- }
- }
- WaitType::MqttConfig => {
- // Moving dot pattern
- pattern_step = (pattern_step + 1) % 8;
- for i in 0..8 {
- let _ = board.board_hal.fault(i, i == pattern_step);
- }
+ match wait_type {
+ WaitType::MissingConfig => {
+ // Keep existing behavior: circular filling pattern
+ led_count %= 8;
+ led_count += 1;
+ for i in 0..8 {
+ let _ = board.board_hal.fault(i, i < led_count);
}
}
-
- board.board_hal.general_fault(true);
- drop(board);
- vTaskDelay(delay);
- let mut board = BOARD_ACCESS.lock().unwrap();
- board.board_hal.general_fault(false);
-
- // Clear all LEDs
- for i in 0..8 {
- let _ = board.board_hal.fault(i, false);
+ WaitType::ConfigButton => {
+ // Alternating pattern: 1010 1010 -> 0101 0101
+ pattern_step = (pattern_step + 1) % 2;
+ for i in 0..8 {
+ let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
+ }
}
- drop(board);
- vTaskDelay(delay);
+ WaitType::MqttConfig => {
+ // Moving dot pattern
+ pattern_step = (pattern_step + 1) % 8;
+ for i in 0..8 {
+ let _ = board.board_hal.fault(i, i == pattern_step);
+ }
+ }
+ }
- if wait_type == WaitType::MqttConfig
- && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
- {
- reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
- }
- if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
- //ensure clean http answer
- Delay::new_default().delay_ms(500);
- BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1);
- }
+ board.board_hal.general_fault(true);
+ drop(board);
+ //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);
+
+ // Clear all LEDs
+ for i in 0..8 {
+ let _ = board.board_hal.fault(i, false);
+ }
+ drop(board);
+ delay_handle.delay_ms(delay);
+
+ if wait_type == WaitType::MqttConfig
+ && !STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed)
+ {
+ reboot_now.store(true, std::sync::atomic::Ordering::Relaxed);
+ }
+ if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
+ //ensure clean http answer
+ Delay::new_default().delay_ms(500);
+ BOARD_ACCESS.lock().unwrap().board_hal.deep_sleep(1);
}
}
}
@@ -827,7 +820,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! {
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, 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
}
}
diff --git a/rust/src/webserver/webserver.rs b/rust/src/webserver/mod.rs
similarity index 100%
rename from rust/src/webserver/webserver.rs
rename to rust/src/webserver/mod.rs