Compare commits
No commits in common. "736925f7dec3805473e78db4b43880b199945bbd" and "1fb9d2ab1bb23ab422ac842b6ca416bf231175ba" have entirely different histories.
736925f7de
...
1fb9d2ab1b
7
rust/.idea/dictionaries/project.xml
generated
7
rust/.idea/dictionaries/project.xml
generated
@ -1,14 +1,7 @@
|
|||||||
<component name="ProjectDictionaryState">
|
<component name="ProjectDictionaryState">
|
||||||
<dictionary name="project">
|
<dictionary name="project">
|
||||||
<words>
|
<words>
|
||||||
<w>buildtime</w>
|
|
||||||
<w>deepsleep</w>
|
|
||||||
<w>githash</w>
|
|
||||||
<w>lightstate</w>
|
|
||||||
<w>mppt</w>
|
|
||||||
<w>plantstate</w>
|
|
||||||
<w>sntp</w>
|
<w>sntp</w>
|
||||||
<w>vergen</w>
|
|
||||||
</words>
|
</words>
|
||||||
</dictionary>
|
</dictionary>
|
||||||
</component>
|
</component>
|
@ -38,13 +38,13 @@ command = [
|
|||||||
partition_table = "partitions.csv"
|
partition_table = "partitions.csv"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "esp-idf-svc/native"]
|
default = ["std", "embassy", "esp-idf-svc/native"]
|
||||||
pio = ["esp-idf-svc/pio"]
|
pio = ["esp-idf-svc/pio"]
|
||||||
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
|
std = ["alloc", "esp-idf-svc/binstart", "esp-idf-svc/std"]
|
||||||
alloc = ["esp-idf-svc/alloc"]
|
alloc = ["esp-idf-svc/alloc"]
|
||||||
nightly = ["esp-idf-svc/nightly"]
|
nightly = ["esp-idf-svc/nightly"]
|
||||||
experimental = ["esp-idf-svc/experimental"]
|
experimental = ["esp-idf-svc/experimental"]
|
||||||
#embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
|
embassy = ["esp-idf-svc/embassy-sync", "esp-idf-svc/critical-section", "esp-idf-svc/embassy-time-driver"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
#ESP stuff
|
#ESP stuff
|
||||||
|
@ -22,22 +22,6 @@ fn main() {
|
|||||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
|
|
||||||
// move webpack results to rust webserver src
|
|
||||||
let _ = Command::new("cmd")
|
|
||||||
.arg("/K")
|
|
||||||
.arg("move")
|
|
||||||
.arg("./src_webpack/bundle.js")
|
|
||||||
.arg("./src/webserver")
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
let _ = Command::new("cmd")
|
|
||||||
.arg("/K")
|
|
||||||
.arg("move")
|
|
||||||
.arg("./src_webpack/index.html")
|
|
||||||
.arg("./src/webserver")
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
println!("Assuming build on linux");
|
println!("Assuming build on linux");
|
||||||
@ -50,18 +34,6 @@ fn main() {
|
|||||||
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
|
||||||
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
|
||||||
assert!(output.status.success());
|
assert!(output.status.success());
|
||||||
|
|
||||||
// move webpack results to rust webserver src
|
|
||||||
let _ = Command::new("mv")
|
|
||||||
.arg("./src_webpack/bundle.js")
|
|
||||||
.arg("./src/webserver")
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
let _ = Command::new("mv")
|
|
||||||
.arg("./src_webpack/index.html")
|
|
||||||
.arg("./src/webserver")
|
|
||||||
.output()
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use crate::to_string;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
|
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
|
||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
@ -97,7 +98,6 @@ impl BatteryInteraction for NoBatteryMonitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//TODO implement this battery monitor kind once controller is complete
|
//TODO implement this battery monitor kind once controller is complete
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct WchI2cSlave {}
|
pub struct WchI2cSlave {}
|
||||||
|
|
||||||
pub struct BQ34Z100G1<'a> {
|
pub struct BQ34Z100G1<'a> {
|
||||||
|
@ -63,14 +63,14 @@ pub struct MqttClient<'a> {
|
|||||||
mqtt_client: EspMqttClient<'a>,
|
mqtt_client: EspMqttClient<'a>,
|
||||||
base_topic: heapless::String<64>,
|
base_topic: heapless::String<64>,
|
||||||
}
|
}
|
||||||
pub struct Esp<'a> {
|
pub struct ESP<'a> {
|
||||||
pub(crate) mqtt_client: Option<MqttClient<'a>>,
|
pub(crate) mqtt_client: Option<MqttClient<'a>>,
|
||||||
pub(crate) wifi_driver: EspWifi<'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) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||||
pub(crate) delay: Delay,
|
pub(crate) delay: Delay,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Esp<'_> {
|
impl ESP<'_> {
|
||||||
const SPIFFS_PARTITION_NAME: &'static str = "storage";
|
const SPIFFS_PARTITION_NAME: &'static str = "storage";
|
||||||
const CONFIG_FILE: &'static str = "/spiffs/config.cfg";
|
const CONFIG_FILE: &'static str = "/spiffs/config.cfg";
|
||||||
const BASE_PATH: &'static str = "/spiffs";
|
const BASE_PATH: &'static str = "/spiffs";
|
||||||
@ -310,7 +310,7 @@ impl Esp<'_> {
|
|||||||
filename: file.file_name().into_string().unwrap(),
|
filename: file.file_name().into_string().unwrap(),
|
||||||
size: file
|
size: file
|
||||||
.metadata()
|
.metadata()
|
||||||
.map(|it| it.len())
|
.and_then(|it| Ok(it.len()))
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
as usize,
|
as usize,
|
||||||
};
|
};
|
||||||
|
@ -1,9 +1,7 @@
|
|||||||
use crate::hal::esp::Esp;
|
use crate::config::{BoardHardware, PlantControllerConfig};
|
||||||
|
use crate::hal::battery::{BatteryInteraction, NoBatteryMonitor};
|
||||||
|
use crate::hal::esp::ESP;
|
||||||
use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
|
use crate::hal::{deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor};
|
||||||
use crate::{
|
|
||||||
config::PlantControllerConfig,
|
|
||||||
hal::battery::{BatteryInteraction, NoBatteryMonitor},
|
|
||||||
};
|
|
||||||
use anyhow::{bail, Result};
|
use anyhow::{bail, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
@ -13,7 +11,7 @@ use measurements::{Current, Voltage};
|
|||||||
|
|
||||||
pub struct Initial<'a> {
|
pub struct Initial<'a> {
|
||||||
pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
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) config: PlantControllerConfig,
|
||||||
pub(crate) battery: Box<dyn BatteryInteraction + Send>,
|
pub(crate) battery: Box<dyn BatteryInteraction + Send>,
|
||||||
}
|
}
|
||||||
@ -22,7 +20,7 @@ pub(crate) fn create_initial_board(
|
|||||||
free_pins: FreePeripherals,
|
free_pins: FreePeripherals,
|
||||||
fs_mount_error: bool,
|
fs_mount_error: bool,
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
esp: Esp<'static>,
|
esp: ESP<'static>,
|
||||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
|
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
|
||||||
let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
|
let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?;
|
||||||
general_fault.set_pull(Pull::Floating)?;
|
general_fault.set_pull(Pull::Floating)?;
|
||||||
@ -41,7 +39,7 @@ pub(crate) fn create_initial_board(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BoardInteraction<'a> for Initial<'a> {
|
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
|
&mut self.esp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,7 +51,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
&mut self.battery
|
&mut self.battery
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_charge_indicator(&mut self, _charging: bool) -> Result<()> {
|
fn set_charge_indicator(&mut self, charging: bool) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +67,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn backup_config(&mut self, _bytes: &[u8]) -> Result<()> {
|
fn backup_config(&mut self, bytes: &[u8]) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,17 +82,17 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
fn tank_sensor_voltage(&mut self) -> Result<f32> {
|
fn tank_sensor_voltage(&mut self) -> Result<f32> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
fn light(&mut self, _enable: bool) -> Result<()> {
|
fn light(&mut self, enable: bool) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pump(&mut self, _plant: usize, _enable: bool) -> Result<()> {
|
fn pump(&mut self, plant: usize, enable: bool) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
fn fault(&mut self, _plant: usize, _enable: bool) -> Result<()> {
|
fn fault(&mut self, plant: usize, _enable: bool) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
fn measure_moisture_hz(&mut self, _plant: usize, _sensor: Sensor) -> Result<f32> {
|
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,10 +108,10 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
|
|||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_rtc_time(&mut self, _time: &DateTime<Utc>) -> Result<()> {
|
fn set_rtc_time(&mut self, time: &DateTime<Utc>) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
fn test_pump(&mut self, _plant: usize) -> Result<()> {
|
fn test_pump(&mut self, plant: usize) -> Result<()> {
|
||||||
bail!("Please configure board revision")
|
bail!("Please configure board revision")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,46 +4,55 @@ mod initial_hal;
|
|||||||
mod v3_hal;
|
mod v3_hal;
|
||||||
mod v4_hal;
|
mod v4_hal;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
|
|
||||||
hal::{
|
|
||||||
battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor},
|
|
||||||
esp::Esp,
|
|
||||||
},
|
|
||||||
log::{log, LogMessage},
|
|
||||||
};
|
|
||||||
use anyhow::{Ok, Result};
|
|
||||||
use battery::BQ34Z100G1;
|
use battery::BQ34Z100G1;
|
||||||
use bq34z100::Bq34z100g1Driver;
|
use bq34z100::Bq34z100g1Driver;
|
||||||
use chrono::{DateTime, Utc};
|
|
||||||
|
use crate::log::LogMessage;
|
||||||
|
use ds323x::DateTimeAccess;
|
||||||
|
use esp_ota::mark_app_valid;
|
||||||
|
|
||||||
|
use eeprom24x::Eeprom24xTrait;
|
||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
use esp_idf_hal::{
|
|
||||||
adc::ADC1,
|
use esp_idf_hal::adc::ADC1;
|
||||||
delay::Delay,
|
use esp_idf_hal::i2c::{APBTickType, I2cConfig, I2cDriver};
|
||||||
gpio::{
|
use esp_idf_hal::units::FromValueType;
|
||||||
Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18,
|
use esp_idf_svc::eventloop::EspSystemEventLoop;
|
||||||
Gpio2, Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3,
|
use esp_idf_svc::nvs::EspDefaultNvsPartition;
|
||||||
Gpio30, Gpio4, Gpio5, Gpio6, Gpio7, Gpio8, IOPin, PinDriver, Pull,
|
use esp_idf_svc::wifi::EspWifi;
|
||||||
},
|
use esp_idf_sys::esp_restart;
|
||||||
i2c::{APBTickType, I2cConfig, I2cDriver},
|
|
||||||
pcnt::PCNT0,
|
|
||||||
prelude::Peripherals,
|
|
||||||
reset::ResetReason,
|
|
||||||
units::FromValueType,
|
|
||||||
};
|
|
||||||
use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition, wifi::EspWifi};
|
|
||||||
use esp_idf_sys::{
|
use esp_idf_sys::{
|
||||||
esp_deep_sleep, esp_restart, esp_sleep_enable_ext1_wakeup,
|
esp_deep_sleep, esp_sleep_enable_ext1_wakeup,
|
||||||
esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW,
|
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 once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
use anyhow::{Ok, Result};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use chrono::{DateTime, Utc};
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
|
use crate::config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig};
|
||||||
|
use crate::hal::battery::{print_battery_bq34z100, BatteryInteraction, NoBatteryMonitor};
|
||||||
|
use crate::hal::esp::ESP;
|
||||||
|
use crate::hal::initial_hal::Initial;
|
||||||
|
use crate::log::log;
|
||||||
|
use embedded_hal::digital::OutputPin;
|
||||||
|
use esp_idf_hal::delay::Delay;
|
||||||
|
use esp_idf_hal::gpio::{
|
||||||
|
Gpio0, Gpio1, Gpio10, Gpio11, Gpio12, Gpio13, Gpio14, Gpio15, Gpio16, Gpio17, Gpio18, Gpio2,
|
||||||
|
Gpio21, Gpio22, Gpio23, Gpio24, Gpio25, Gpio26, Gpio27, Gpio28, Gpio29, Gpio3, Gpio30, Gpio4,
|
||||||
|
Gpio5, Gpio6, Gpio7, Gpio8, IOPin, PinDriver, Pull,
|
||||||
|
};
|
||||||
|
use esp_idf_hal::pcnt::PCNT0;
|
||||||
|
use esp_idf_hal::prelude::Peripherals;
|
||||||
|
use esp_idf_hal::reset::ResetReason;
|
||||||
|
use measurements::{Current, Voltage};
|
||||||
|
use pca9535::StandardExpanderInterface;
|
||||||
|
|
||||||
//Only support for 8 right now!
|
//Only support for 8 right now!
|
||||||
pub const PLANT_COUNT: usize = 8;
|
pub const PLANT_COUNT: usize = 8;
|
||||||
const REPEAT_MOIST_MEASURE: usize = 1;
|
const REPEAT_MOIST_MEASURE: usize = 1;
|
||||||
@ -52,6 +61,11 @@ const TANK_MULTI_SAMPLE: usize = 11;
|
|||||||
|
|
||||||
pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c);
|
pub static I2C_DRIVER: Lazy<Mutex<I2cDriver<'static>>> = Lazy::new(PlantHal::create_i2c);
|
||||||
|
|
||||||
|
#[non_exhaustive]
|
||||||
|
struct V3Constants;
|
||||||
|
|
||||||
|
impl V3Constants {}
|
||||||
|
|
||||||
const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
|
const X25: crc::Crc<u16> = crc::Crc::<u16>::new(&crc::CRC_16_IBM_SDLC);
|
||||||
|
|
||||||
fn deep_sleep(duration_in_ms: u64) -> ! {
|
fn deep_sleep(duration_in_ms: u64) -> ! {
|
||||||
@ -84,23 +98,36 @@ pub struct HAL<'a> {
|
|||||||
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, PartialEq, Debug, Default)]
|
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||||
pub struct BackupHeader {
|
pub struct BackupHeader {
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
crc16: u16,
|
crc16: u16,
|
||||||
pub size: usize,
|
pub size: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for BackupHeader {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
timestamp: Default::default(),
|
||||||
|
crc16: Default::default(),
|
||||||
|
size: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub trait BoardInteraction<'a> {
|
pub trait BoardInteraction<'a> {
|
||||||
fn get_esp(&mut self) -> &mut Esp<'a>;
|
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
|
||||||
|
fn is_day(&self) -> bool;
|
||||||
|
fn get_mptt_voltage(&mut self) -> Result<Voltage>;
|
||||||
|
fn get_mptt_current(&mut self) -> Result<Current>;
|
||||||
|
|
||||||
|
fn get_esp(&mut self) -> &mut ESP<'a>;
|
||||||
fn get_config(&mut self) -> &PlantControllerConfig;
|
fn get_config(&mut self) -> &PlantControllerConfig;
|
||||||
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
||||||
fn set_charge_indicator(&mut self, charging: bool) -> Result<()>;
|
|
||||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
|
fn deep_sleep(&mut self, duration_in_ms: u64) -> !;
|
||||||
fn get_backup_info(&mut self) -> Result<BackupHeader>;
|
fn get_backup_info(&mut self) -> Result<BackupHeader>;
|
||||||
fn get_backup_config(&mut self) -> Result<Vec<u8>>;
|
fn get_backup_config(&mut self) -> Result<Vec<u8>>;
|
||||||
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
|
fn backup_config(&mut self, bytes: &[u8]) -> Result<()>;
|
||||||
fn is_day(&self) -> bool;
|
|
||||||
//should be multsampled
|
//should be multsampled
|
||||||
fn water_temperature_c(&mut self) -> Result<f32>;
|
fn water_temperature_c(&mut self) -> Result<f32>;
|
||||||
/// return median tank sensor value in milli volt
|
/// return median tank sensor value in milli volt
|
||||||
@ -116,11 +143,8 @@ pub trait BoardInteraction<'a> {
|
|||||||
fn test_pump(&mut self, plant: usize) -> Result<()>;
|
fn test_pump(&mut self, plant: usize) -> Result<()>;
|
||||||
fn test(&mut self) -> Result<()>;
|
fn test(&mut self) -> Result<()>;
|
||||||
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
|
fn set_config(&mut self, config: PlantControllerConfig) -> Result<()>;
|
||||||
fn get_mptt_voltage(&mut self) -> anyhow::Result<Voltage>;
|
|
||||||
fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct FreePeripherals {
|
pub struct FreePeripherals {
|
||||||
pub gpio0: Gpio0,
|
pub gpio0: Gpio0,
|
||||||
pub gpio1: Gpio1,
|
pub gpio1: Gpio1,
|
||||||
@ -215,7 +239,7 @@ impl PlantHal {
|
|||||||
gpio30: peripherals.pins.gpio30,
|
gpio30: peripherals.pins.gpio30,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut esp = Esp {
|
let mut esp = ESP {
|
||||||
mqtt_client: None,
|
mqtt_client: None,
|
||||||
wifi_driver,
|
wifi_driver,
|
||||||
boot_button,
|
boot_button,
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
|
use crate::config::PlantControllerConfig;
|
||||||
|
use crate::hal::battery::BatteryInteraction;
|
||||||
|
use crate::hal::esp::ESP;
|
||||||
use crate::hal::{
|
use crate::hal::{
|
||||||
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
|
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, V3Constants, I2C_DRIVER,
|
||||||
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
|
PLANT_COUNT, REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
|
||||||
};
|
};
|
||||||
use crate::log::{log, LogMessage};
|
use crate::log::{log, LogMessage};
|
||||||
use crate::{
|
|
||||||
config::PlantControllerConfig,
|
|
||||||
hal::{battery::BatteryInteraction, esp::Esp},
|
|
||||||
};
|
|
||||||
use anyhow::{anyhow, bail, Ok, Result};
|
use anyhow::{anyhow, bail, Ok, Result};
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
use ds18b20::Ds18b20;
|
use ds18b20::Ds18b20;
|
||||||
@ -14,22 +13,21 @@ use ds323x::{DateTimeAccess, Ds323x};
|
|||||||
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
|
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
|
||||||
use embedded_hal::digital::OutputPin;
|
use embedded_hal::digital::OutputPin;
|
||||||
use embedded_hal_bus::i2c::MutexDevice;
|
use embedded_hal_bus::i2c::MutexDevice;
|
||||||
use esp_idf_hal::{
|
use esp_idf_hal::adc::oneshot::config::AdcChannelConfig;
|
||||||
adc::{
|
use esp_idf_hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
|
||||||
attenuation,
|
use esp_idf_hal::adc::{attenuation, Resolution};
|
||||||
oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver},
|
use esp_idf_hal::delay::Delay;
|
||||||
Resolution,
|
use esp_idf_hal::gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, PinDriver, Pull};
|
||||||
},
|
use esp_idf_hal::i2c::I2cDriver;
|
||||||
delay::Delay,
|
use esp_idf_hal::pcnt::{
|
||||||
gpio::{AnyInputPin, Gpio5, IOPin, InputOutput, PinDriver, Pull},
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||||
i2c::I2cDriver,
|
|
||||||
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, vTaskDelay, EspError};
|
||||||
use measurements::{Current, Voltage};
|
use measurements::{Current, Voltage};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
use plant_ctrl2::sipo::ShiftRegister40;
|
use plant_ctrl2::sipo::ShiftRegister40;
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
const PUMP8_BIT: usize = 0;
|
const PUMP8_BIT: usize = 0;
|
||||||
const PUMP1_BIT: usize = 1;
|
const PUMP1_BIT: usize = 1;
|
||||||
@ -79,13 +77,13 @@ const FAULT_2: usize = 23;
|
|||||||
pub struct V3<'a> {
|
pub struct V3<'a> {
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||||
esp: Esp<'a>,
|
esp: ESP<'a>,
|
||||||
shift_register: ShiftRegister40<
|
shift_register: ShiftRegister40<
|
||||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
|
||||||
>,
|
>,
|
||||||
_shift_register_enable_invert:
|
shift_register_enable_invert:
|
||||||
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
|
PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Output>,
|
||||||
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
|
tank_channel: AdcChannelDriver<'a, Gpio5, AdcDriver<'a, esp_idf_hal::adc::ADC1>>,
|
||||||
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
|
||||||
@ -107,10 +105,10 @@ pub struct V3<'a> {
|
|||||||
|
|
||||||
pub(crate) fn create_v3(
|
pub(crate) fn create_v3(
|
||||||
peripherals: FreePeripherals,
|
peripherals: FreePeripherals,
|
||||||
esp: Esp<'static>,
|
esp: ESP<'static>,
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||||
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
|
) -> Result<Box<dyn BoardInteraction + Send + '_>> {
|
||||||
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
|
let mut clock = PinDriver::input_output(peripherals.gpio15.downgrade())?;
|
||||||
clock.set_pull(Pull::Floating)?;
|
clock.set_pull(Pull::Floating)?;
|
||||||
let mut latch = PinDriver::input_output(peripherals.gpio3.downgrade())?;
|
let mut latch = PinDriver::input_output(peripherals.gpio3.downgrade())?;
|
||||||
@ -235,7 +233,7 @@ pub(crate) fn create_v3(
|
|||||||
battery_monitor,
|
battery_monitor,
|
||||||
esp,
|
esp,
|
||||||
shift_register,
|
shift_register,
|
||||||
_shift_register_enable_invert: shift_register_enable_invert,
|
shift_register_enable_invert,
|
||||||
tank_channel,
|
tank_channel,
|
||||||
solar_is_day,
|
solar_is_day,
|
||||||
light,
|
light,
|
||||||
@ -270,7 +268,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
|
|||||||
fn get_mptt_current(&mut self) -> Result<Current> {
|
fn get_mptt_current(&mut self) -> Result<Current> {
|
||||||
bail!("Board does not have current sensor")
|
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
|
&mut self.esp
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::config::PlantControllerConfig;
|
use crate::config::PlantControllerConfig;
|
||||||
use crate::hal::battery::BatteryInteraction;
|
use crate::hal::battery::BatteryInteraction;
|
||||||
use crate::hal::esp::Esp;
|
use crate::hal::esp::ESP;
|
||||||
use crate::hal::{
|
use crate::hal::{
|
||||||
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
|
deep_sleep, BackupHeader, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT,
|
||||||
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
|
REPEAT_MOIST_MEASURE, TANK_MULTI_SAMPLE, X25,
|
||||||
@ -22,15 +22,15 @@ use esp_idf_hal::i2c::I2cDriver;
|
|||||||
use esp_idf_hal::pcnt::{
|
use esp_idf_hal::pcnt::{
|
||||||
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex,
|
||||||
};
|
};
|
||||||
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, EspError};
|
use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
|
||||||
use ina219::address::{Address, Pin};
|
use ina219::address::{Address, Pin};
|
||||||
use ina219::calibration::UnCalibrated;
|
use ina219::calibration::{Calibration, UnCalibrated};
|
||||||
use ina219::configuration::{Configuration, OperatingMode};
|
|
||||||
use ina219::SyncIna219;
|
use ina219::SyncIna219;
|
||||||
use measurements::{Current, Resistance, Voltage};
|
use measurements::{Current, Resistance, Voltage};
|
||||||
use one_wire_bus::OneWire;
|
use one_wire_bus::OneWire;
|
||||||
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
|
||||||
use std::result::Result::Ok as OkStd;
|
use std::result::Result::Ok as OkStd;
|
||||||
|
use ina219::configuration::{Configuration, OperatingMode};
|
||||||
|
|
||||||
const MS0: u8 = 1_u8;
|
const MS0: u8 = 1_u8;
|
||||||
const MS1: u8 = 0_u8;
|
const MS1: u8 = 0_u8;
|
||||||
@ -47,27 +47,23 @@ pub enum Charger<'a> {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Charger<'_> {
|
impl<'a> Charger<'a> {
|
||||||
pub(crate) fn power_save(&mut self) {
|
pub(crate) fn powersave(&mut self) {
|
||||||
match self {
|
match self { Charger::SolarMpptV1 { mppt_ina, .. } => {
|
||||||
Charger::SolarMpptV1 { mppt_ina, .. } => {
|
let _ = mppt_ina.set_configuration(Configuration {
|
||||||
let _ = mppt_ina
|
reset: Default::default(),
|
||||||
.set_configuration(Configuration {
|
bus_voltage_range: Default::default(),
|
||||||
reset: Default::default(),
|
shunt_voltage_range: Default::default(),
|
||||||
bus_voltage_range: Default::default(),
|
bus_resolution: Default::default(),
|
||||||
shunt_voltage_range: Default::default(),
|
shunt_resolution: Default::default(),
|
||||||
bus_resolution: Default::default(),
|
operating_mode: OperatingMode::PowerDown,
|
||||||
shunt_resolution: Default::default(),
|
}).map_err(|e| {
|
||||||
operating_mode: OperatingMode::PowerDown,
|
println!(
|
||||||
})
|
"Error setting ina mppt configuration during deepsleep preparation{:?}",
|
||||||
.map_err(|e| {
|
|
||||||
println!(
|
|
||||||
"Error setting ina mppt configuration during deep sleep preparation{:?}",
|
|
||||||
e
|
e
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
}
|
} }
|
||||||
}
|
|
||||||
}
|
}
|
||||||
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
fn set_charge_indicator(&mut self, charging: bool) -> anyhow::Result<()> {
|
||||||
match self {
|
match self {
|
||||||
@ -109,7 +105,7 @@ impl Charger<'_> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct V4<'a> {
|
pub struct V4<'a> {
|
||||||
esp: Esp<'a>,
|
esp: ESP<'a>,
|
||||||
charger: Charger<'a>,
|
charger: Charger<'a>,
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
@ -134,10 +130,10 @@ pub struct V4<'a> {
|
|||||||
|
|
||||||
pub(crate) fn create_v4(
|
pub(crate) fn create_v4(
|
||||||
peripherals: FreePeripherals,
|
peripherals: FreePeripherals,
|
||||||
esp: Esp<'static>,
|
esp: ESP<'static>,
|
||||||
config: PlantControllerConfig,
|
config: PlantControllerConfig,
|
||||||
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
battery_monitor: Box<dyn BatteryInteraction + Send>,
|
||||||
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
|
) -> anyhow::Result<Box<dyn BoardInteraction + Send + '_>> {
|
||||||
let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
|
let mut awake = PinDriver::output(peripherals.gpio15.downgrade())?;
|
||||||
awake.set_high()?;
|
awake.set_high()?;
|
||||||
|
|
||||||
@ -248,7 +244,7 @@ pub(crate) fn create_v4(
|
|||||||
Address::from_pins(Pin::Vcc, Pin::Gnd),
|
Address::from_pins(Pin::Vcc, Pin::Gnd),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
mppt_ina.set_configuration(Configuration {
|
mppt_ina.set_configuration(Configuration{
|
||||||
reset: Default::default(),
|
reset: Default::default(),
|
||||||
bus_voltage_range: Default::default(),
|
bus_voltage_range: Default::default(),
|
||||||
shunt_voltage_range: Default::default(),
|
shunt_voltage_range: Default::default(),
|
||||||
@ -256,7 +252,7 @@ pub(crate) fn create_v4(
|
|||||||
shunt_resolution: ina219::configuration::Resolution::Avg128,
|
shunt_resolution: ina219::configuration::Resolution::Avg128,
|
||||||
operating_mode: Default::default(),
|
operating_mode: Default::default(),
|
||||||
})?;
|
})?;
|
||||||
//TODO this is probably already done until we are ready first time?, maybe add startup time comparison on access?
|
//TODO this is probably laready done untill we are ready first time?, maybee add startup time comparison on access?
|
||||||
esp.delay.delay_ms(
|
esp.delay.delay_ms(
|
||||||
mppt_ina
|
mppt_ina
|
||||||
.configuration()?
|
.configuration()?
|
||||||
@ -292,7 +288,7 @@ pub(crate) fn create_v4(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> BoardInteraction<'a> for V4<'a> {
|
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
|
&mut self.esp
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -310,7 +306,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
|||||||
|
|
||||||
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
|
||||||
self.awake.set_low().unwrap();
|
self.awake.set_low().unwrap();
|
||||||
self.charger.power_save();
|
self.charger.powersave();
|
||||||
deep_sleep(duration_in_ms);
|
deep_sleep(duration_in_ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ pub fn get_log() -> Vec<LogEntry> {
|
|||||||
read_copy.push(copy);
|
read_copy.push(copy);
|
||||||
}
|
}
|
||||||
drop(buffer);
|
drop(buffer);
|
||||||
read_copy
|
return read_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
|
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
|
||||||
|
204
rust/src/main.rs
204
rust/src/main.rs
@ -1,39 +1,43 @@
|
|||||||
use crate::{
|
use crate::webserver::webserver::httpd;
|
||||||
config::BoardVersion::INITIAL,
|
|
||||||
hal::{PlantHal, HAL, PLANT_COUNT},
|
|
||||||
webserver::httpd,
|
|
||||||
};
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use chrono::{DateTime, Datelike, Timelike, Utc};
|
use chrono::{DateTime, Datelike, Timelike, Utc};
|
||||||
use chrono_tz::Tz::{self, UTC};
|
use chrono_tz::Tz;
|
||||||
|
use chrono_tz::Tz::UTC;
|
||||||
use esp_idf_hal::delay::Delay;
|
use esp_idf_hal::delay::Delay;
|
||||||
use esp_idf_sys::{
|
use esp_idf_sys::{
|
||||||
esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition,
|
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_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_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_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED,
|
||||||
esp_ota_img_states_t_ESP_OTA_IMG_VALID,
|
esp_ota_img_states_t_ESP_OTA_IMG_VALID, vTaskDelay,
|
||||||
};
|
};
|
||||||
use esp_ota::{mark_app_valid, rollback_and_reboot};
|
use esp_ota::{mark_app_valid, rollback_and_reboot};
|
||||||
use hal::battery::BatteryState;
|
use hal::battery::BatteryState;
|
||||||
use log::{log, LogMessage};
|
use log::{log, LogMessage};
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use plant_state::PlantState;
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::{atomic::AtomicBool, Arc, Mutex, MutexGuard};
|
use std::sync::MutexGuard;
|
||||||
use tank::*;
|
use std::{
|
||||||
|
fmt::Display,
|
||||||
|
sync::{atomic::AtomicBool, Arc, Mutex},
|
||||||
|
};
|
||||||
mod config;
|
mod config;
|
||||||
mod hal;
|
mod hal;
|
||||||
mod log;
|
mod log;
|
||||||
mod plant_state;
|
mod plant_state;
|
||||||
mod tank;
|
mod tank;
|
||||||
mod webserver;
|
|
||||||
|
|
||||||
|
use crate::config::BoardVersion::INITIAL;
|
||||||
|
use crate::hal::battery::BatteryInteraction;
|
||||||
|
use crate::hal::{BoardInteraction, PlantHal, HAL, PLANT_COUNT};
|
||||||
|
use plant_state::PlantState;
|
||||||
|
use tank::*;
|
||||||
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
|
pub static BOARD_ACCESS: Lazy<Mutex<HAL>> = Lazy::new(|| PlantHal::create().unwrap());
|
||||||
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
pub static STAY_ALIVE: Lazy<AtomicBool> = Lazy::new(|| AtomicBool::new(false));
|
||||||
|
|
||||||
|
mod webserver {
|
||||||
|
pub mod webserver;
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq)]
|
||||||
enum WaitType {
|
enum WaitType {
|
||||||
@ -68,7 +72,7 @@ struct LightState {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
||||||
///mqtt struct to track pump activities
|
///mqtt stuct to track pump activities
|
||||||
struct PumpInfo {
|
struct PumpInfo {
|
||||||
enabled: bool,
|
enabled: bool,
|
||||||
pump_ineffective: bool,
|
pump_ineffective: bool,
|
||||||
@ -174,7 +178,16 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
println!("cur is {}", cur);
|
println!("cur is {}", cur);
|
||||||
update_charge_indicator(&mut board);
|
match board
|
||||||
|
.board_hal
|
||||||
|
.get_battery_monitor()
|
||||||
|
.average_current_milli_ampere()
|
||||||
|
{
|
||||||
|
Ok(charging) => {
|
||||||
|
let _ = board.board_hal.set_charge_indicator(charging > 20);
|
||||||
|
}
|
||||||
|
Err(_) => {}
|
||||||
|
}
|
||||||
|
|
||||||
if board.board_hal.get_esp().get_restart_to_conf() {
|
if board.board_hal.get_esp().get_restart_to_conf() {
|
||||||
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
||||||
@ -255,7 +268,7 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
address,
|
address,
|
||||||
ota_state_string,
|
ota_state_string,
|
||||||
&mut board,
|
&mut board,
|
||||||
ip_address,
|
&ip_address,
|
||||||
timezone_time,
|
timezone_time,
|
||||||
);
|
);
|
||||||
publish_battery_state(&mut board);
|
publish_battery_state(&mut board);
|
||||||
@ -411,12 +424,12 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
.state_charge_percent()
|
.state_charge_percent()
|
||||||
.unwrap_or(0.);
|
.unwrap_or(0.);
|
||||||
|
|
||||||
// try to load full battery state if failed the battery state is unknown
|
/// try to load full battery state if failed the battery state is unknown
|
||||||
let battery_state = board
|
let battery_state = board
|
||||||
.board_hal
|
.board_hal
|
||||||
.get_battery_monitor()
|
.get_battery_monitor()
|
||||||
.get_battery_state()
|
.get_battery_state()
|
||||||
.unwrap_or(BatteryState::Unknown);
|
.unwrap_or(hal::battery::BatteryState::Unknown);
|
||||||
|
|
||||||
let mut light_state = LightState {
|
let mut light_state = LightState {
|
||||||
enabled: board.board_hal.get_config().night_lamp.enabled,
|
enabled: board.board_hal.get_config().night_lamp.enabled,
|
||||||
@ -546,26 +559,6 @@ fn safe_main() -> anyhow::Result<()> {
|
|||||||
.deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64);
|
.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> {
|
fn obtain_tank_temperature(board: &mut MutexGuard<HAL>) -> anyhow::Result<f32> {
|
||||||
//multisample should be moved to water_temperature_c
|
//multisample should be moved to water_temperature_c
|
||||||
let mut attempt = 1;
|
let mut attempt = 1;
|
||||||
@ -618,7 +611,7 @@ fn publish_plant_states(
|
|||||||
.zip(&board.board_hal.get_config().plants.clone())
|
.zip(&board.board_hal.get_config().plants.clone())
|
||||||
.enumerate()
|
.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) => {
|
Ok(state) => {
|
||||||
let plant_topic = format!("/plant{}", plant_id + 1);
|
let plant_topic = format!("/plant{}", plant_id + 1);
|
||||||
let _ = board
|
let _ = board
|
||||||
@ -689,7 +682,7 @@ fn try_connect_wifi_sntp_mqtt(board: &mut MutexGuard<HAL>) -> NetworkMode {
|
|||||||
SntpMode::OFFLINE
|
SntpMode::OFFLINE
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let mqtt_connected = if board.board_hal.get_config().network.mqtt_url.is_some() {
|
let mqtt_connected = if let Some(_) = board.board_hal.get_config().network.mqtt_url {
|
||||||
let nw_config = &board.board_hal.get_config().network.clone();
|
let nw_config = &board.board_hal.get_config().network.clone();
|
||||||
match board.board_hal.get_esp().mqtt(nw_config) {
|
match board.board_hal.get_esp().mqtt(nw_config) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@ -746,8 +739,7 @@ fn pump_info(
|
|||||||
|
|
||||||
fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) {
|
fn publish_battery_state(board: &mut MutexGuard<'_, HAL<'_>>) {
|
||||||
let state = board.board_hal.get_battery_monitor().get_battery_state();
|
let state = board.board_hal.get_battery_monitor().get_battery_state();
|
||||||
if let Ok(serialized_battery_state_bytes) =
|
if let Ok(serialized_battery_state_bytes) = serde_json::to_string(&state).map(|s| s.into_bytes())
|
||||||
serde_json::to_string(&state).map(|s| s.into_bytes())
|
|
||||||
{
|
{
|
||||||
let _ = board
|
let _ = board
|
||||||
.board_hal
|
.board_hal
|
||||||
@ -760,59 +752,76 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
|||||||
let delay = wait_type.blink_pattern();
|
let delay = wait_type.blink_pattern();
|
||||||
let mut led_count = 8;
|
let mut led_count = 8;
|
||||||
let mut pattern_step = 0;
|
let mut pattern_step = 0;
|
||||||
let delay_handle = Delay::new_default();
|
|
||||||
loop {
|
loop {
|
||||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
unsafe {
|
||||||
update_charge_indicator(&mut board);
|
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||||
|
|
||||||
match wait_type {
|
//we have mppt controller, ask it for charging current
|
||||||
WaitType::MissingConfig => {
|
if let Ok(current) = board.board_hal.get_mptt_current() {
|
||||||
// Keep existing behavior: circular filling pattern
|
let _ = board.board_hal.set_charge_indicator(current.as_milliamperes() > 20_f64);
|
||||||
led_count %= 8;
|
}
|
||||||
led_count += 1;
|
//fallback to battery controller and ask it instead
|
||||||
for i in 0..8 {
|
else if let Ok(charging) = board
|
||||||
let _ = board.board_hal.fault(i, i < led_count);
|
.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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WaitType::ConfigButton => {
|
|
||||||
// Alternating pattern: 1010 1010 -> 0101 0101
|
board.board_hal.general_fault(true);
|
||||||
pattern_step = (pattern_step + 1) % 2;
|
drop(board);
|
||||||
for i in 0..8 {
|
vTaskDelay(delay);
|
||||||
let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0);
|
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::MqttConfig => {
|
drop(board);
|
||||||
// Moving dot pattern
|
vTaskDelay(delay);
|
||||||
pattern_step = (pattern_step + 1) % 8;
|
|
||||||
for i in 0..8 {
|
if wait_type == WaitType::MqttConfig
|
||||||
let _ = board.board_hal.fault(i, i == pattern_step);
|
&& !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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -820,7 +829,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
|
|||||||
fn main() {
|
fn main() {
|
||||||
let result = safe_main();
|
let result = safe_main();
|
||||||
match result {
|
match result {
|
||||||
// this should not get triggered, safe_main should not return but go into deep sleep with sensible
|
// this should not get triggered, safe_main should not return but go into deep sleep with sensbile
|
||||||
// timeout, this is just a fallback
|
// timeout, this is just a fallback
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
println!("Main app finished, restarting");
|
println!("Main app finished, restarting");
|
||||||
@ -841,14 +850,23 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn to_string<T: Display>(value: anyhow::Result<T>) -> String {
|
||||||
|
match value {
|
||||||
|
Ok(v) => v.to_string(),
|
||||||
|
Err(err) => {
|
||||||
|
format!("{:?}", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
pub fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
|
||||||
let current_hour = cur.hour() as u8;
|
let curhour = cur.hour() as u8;
|
||||||
//eg 10-14
|
//eg 10-14
|
||||||
if start < end {
|
if start < end {
|
||||||
current_hour > start && current_hour < end
|
curhour > start && curhour < end
|
||||||
} else {
|
} else {
|
||||||
//eg 20-05
|
//eg 20-05
|
||||||
current_hour > start || current_hour < end
|
curhour > start || curhour < end
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,10 @@
|
|||||||
use crate::{
|
|
||||||
config::PlantConfig,
|
|
||||||
hal::{Sensor, HAL},
|
|
||||||
in_time_range,
|
|
||||||
};
|
|
||||||
use chrono::{DateTime, TimeDelta, Utc};
|
use chrono::{DateTime, TimeDelta, Utc};
|
||||||
use chrono_tz::Tz;
|
use chrono_tz::Tz;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::hal::{BoardInteraction, Sensor, HAL};
|
||||||
|
use crate::{config::PlantConfig, in_time_range};
|
||||||
|
|
||||||
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin)
|
const MOIST_SENSOR_MAX_FREQUENCY: f32 = 7500.; // 60kHz (500Hz margin)
|
||||||
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels
|
const MOIST_SENSOR_MIN_FREQUENCY: f32 = 150.; // this is really, really dry, think like cactus levels
|
||||||
|
|
||||||
@ -239,11 +237,7 @@ impl PlantState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_mqtt_info(
|
pub fn to_mqtt_info(&self, plant_conf: &PlantConfig, current_time: &DateTime<Tz>) -> PlantInfo {
|
||||||
&self,
|
|
||||||
plant_conf: &PlantConfig,
|
|
||||||
current_time: &DateTime<Tz>,
|
|
||||||
) -> PlantInfo<'_> {
|
|
||||||
PlantInfo {
|
PlantInfo {
|
||||||
sensor_a: &self.sensor_a,
|
sensor_a: &self.sensor_a,
|
||||||
sensor_b: &self.sensor_b,
|
sensor_b: &self.sensor_b,
|
||||||
|
@ -100,7 +100,7 @@ macro_rules! ShiftRegisterBuilder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get embedded-hal output pins to control the shift register outputs
|
/// Get embedded-hal output pins to control the shift register outputs
|
||||||
pub fn decompose(&self) -> [ShiftRegisterPin<'_>; $size] {
|
pub fn decompose(&self) -> [ShiftRegisterPin; $size] {
|
||||||
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
// Create an uninitialized array of `MaybeUninit`. The `assume_init` is
|
||||||
// safe because the type we are claiming to have initialized here is a
|
// safe because the type we are claiming to have initialized here is a
|
||||||
// bunch of `MaybeUninit`s, which do not require initialization.
|
// bunch of `MaybeUninit`s, which do not require initialization.
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
use crate::{config::TankConfig, hal::HAL};
|
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
|
||||||
|
use crate::config::TankConfig;
|
||||||
|
use crate::hal::{BoardInteraction, HAL};
|
||||||
|
|
||||||
const OPEN_TANK_VOLTAGE: f32 = 3.0;
|
const OPEN_TANK_VOLTAGE: f32 = 3.0;
|
||||||
pub const WATER_FROZEN_THRESH: f32 = 4.0;
|
pub const WATER_FROZEN_THRESH: f32 = 4.0;
|
||||||
|
|
||||||
|
@ -1,12 +1,8 @@
|
|||||||
//offer ota and config mode
|
//offer ota and config mode
|
||||||
|
|
||||||
|
use crate::hal::battery::BatteryInteraction;
|
||||||
use crate::{
|
use crate::{
|
||||||
config::PlantControllerConfig,
|
determine_tank_state, get_version, log::LogMessage, plant_state::PlantState, BOARD_ACCESS,
|
||||||
determine_tank_state, get_version,
|
|
||||||
hal::PLANT_COUNT,
|
|
||||||
log::LogMessage,
|
|
||||||
plant_state::{MoistureSensorState, PlantState},
|
|
||||||
BOARD_ACCESS,
|
|
||||||
};
|
};
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use chrono::DateTime;
|
use chrono::DateTime;
|
||||||
@ -23,6 +19,10 @@ use std::{
|
|||||||
};
|
};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
use crate::config::PlantControllerConfig;
|
||||||
|
use crate::hal::{BoardInteraction, PLANT_COUNT};
|
||||||
|
use crate::plant_state::MoistureSensorState;
|
||||||
|
|
||||||
#[derive(Serialize, Debug)]
|
#[derive(Serialize, Debug)]
|
||||||
struct SSIDList<'a> {
|
struct SSIDList<'a> {
|
||||||
ssids: Vec<&'a String<32>>,
|
ssids: Vec<&'a String<32>>,
|
||||||
@ -221,7 +221,7 @@ fn set_config(
|
|||||||
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
let config: PlantControllerConfig = serde_json::from_slice(&all)?;
|
||||||
|
|
||||||
let mut board = BOARD_ACCESS.lock().expect("board access");
|
let mut board = BOARD_ACCESS.lock().expect("board access");
|
||||||
let _ = board.board_hal.set_config(config);
|
board.board_hal.set_config(config);
|
||||||
anyhow::Ok(Some("saved".to_owned()))
|
anyhow::Ok(Some("saved".to_owned()))
|
||||||
}
|
}
|
||||||
|
|
@ -49,7 +49,7 @@ module.exports = {
|
|||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: 'bundle.js',
|
filename: 'bundle.js',
|
||||||
path: path.resolve(__dirname, '.'),
|
path: path.resolve(__dirname, '../src/webserver'),
|
||||||
},
|
},
|
||||||
devServer: {
|
devServer: {
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user