11 Commits

Author SHA1 Message Date
5bc20d312a Merge branch 'feature/enable-mqtt-login' into develop 2025-09-11 20:21:34 +02:00
4faae86a6b reduce amount of warnings 2025-09-11 20:17:18 +02:00
4fa1a05fc3 remove duplicate import 2025-09-11 20:04:27 +02:00
3a24dcec53 add release profile 2025-09-11 19:56:53 +02:00
e7895577ca update frontent to include inputs for mqtt auth 2025-09-11 19:56:50 +02:00
be9a076b33 add configuration options to enable mqtt user login 2025-09-11 19:56:48 +02:00
47ad4c70de Merge branch 'can' into develop 2025-09-03 19:08:55 +02:00
c4aa3a1450 1.3 baseplane 2025-08-29 21:11:04 +02:00
f0d89417b6 Merge branch 'premerge' into develop 2025-08-29 21:09:20 +02:00
50b2e994ca can prepare stuff 2025-08-14 22:10:53 +02:00
7a7f000743 add missing sensor port labels 2025-08-08 23:24:05 +02:00
16 changed files with 2089 additions and 2335 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"board": { "board": {
"active_layer": 4, "active_layer": 0,
"active_layer_preset": "All Layers", "active_layer_preset": "All Layers",
"auto_track_width": false, "auto_track_width": false,
"hidden_netclasses": [], "hidden_netclasses": [],

View File

@@ -472,6 +472,7 @@
"single_global_label": "ignore", "single_global_label": "ignore",
"unannotated": "error", "unannotated": "error",
"unconnected_wire_endpoint": "warning", "unconnected_wire_endpoint": "warning",
"undefined_netclass": "error",
"unit_value_mismatch": "error", "unit_value_mismatch": "error",
"unresolved_variable": "error", "unresolved_variable": "error",
"wire_dangling": "error" "wire_dangling": "error"

View File

@@ -17,6 +17,16 @@ incremental = true
opt-level = 2 opt-level = 2
[profile.release]
# Explicitly disable LTO which the Xtensa codegen backend has issues
lto = false
strip = true
debug = false
overflow-checks = false
panic = "abort"
incremental = true
opt-level = "z"
[package.metadata.cargo_runner] [package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo. # The string `$TARGET_FILE` will be replaced with the path from cargo.
command = [ command = [
@@ -86,6 +96,7 @@ pca9535 = { version = "2.0.0", features = ["std"] }
ina219 = { version = "0.2.0", features = ["std"] } ina219 = { version = "0.2.0", features = ["std"] }
embedded-storage = "=0.3.1" embedded-storage = "=0.3.1"
ekv = "1.0.0" ekv = "1.0.0"
embedded-can = "0.4.1"
[patch.crates-io] [patch.crates-io]

View File

@@ -11,6 +11,8 @@ pub struct NetworkConfig {
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 mqtt_user: Option<heapless::String<32>>,
pub mqtt_password: Option<heapless::String<64>>,
pub max_wait: u32, pub max_wait: u32,
} }
impl Default for NetworkConfig { impl Default for NetworkConfig {
@@ -21,6 +23,8 @@ impl Default for NetworkConfig {
password: None, password: None,
mqtt_url: None, mqtt_url: None,
base_topic: None, base_topic: None,
mqtt_user: None,
mqtt_password: None,
max_wait: 10000, max_wait: 10000,
} }
} }

View File

@@ -419,6 +419,8 @@ impl Esp<'_> {
}), }),
client_id: Some("plantctrl"), client_id: Some("plantctrl"),
keep_alive_interval: Some(Duration::from_secs(60 * 60 * 2)), keep_alive_interval: Some(Duration::from_secs(60 * 60 * 2)),
username: network_config.mqtt_user.as_ref().map(|v| &**v),
password: network_config.mqtt_password.as_ref().map(|v| &**v),
//room for improvement //room for improvement
..Default::default() ..Default::default()
}; };

View File

@@ -51,6 +51,7 @@ pub(crate) fn create_initial_board(
config: PlantControllerConfig, config: PlantControllerConfig,
esp: Esp<'static>, esp: Esp<'static>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
println!("Start initial");
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)?;
general_fault.set_low()?; general_fault.set_low()?;

View File

@@ -5,6 +5,7 @@ mod rtc;
mod v3_hal; mod v3_hal;
mod v4_hal; mod v4_hal;
mod water; mod water;
mod v4_sensor;
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction}; use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
use crate::hal::water::TankSensor; use crate::hal::water::TankSensor;
@@ -48,10 +49,10 @@ use once_cell::sync::Lazy;
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 esp_idf_hal::can::CAN;
//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 TANK_MULTI_SAMPLE: usize = 11; const TANK_MULTI_SAMPLE: usize = 11;
@@ -112,7 +113,7 @@ pub trait BoardInteraction<'a> {
impl dyn BoardInteraction<'_> { impl dyn BoardInteraction<'_> {
//the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons //the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons
fn progress(&mut self, counter: u32) { fn _progress(&mut self, counter: u32) {
let even = counter % 2 == 0; let even = counter % 2 == 0;
let current = counter / (PLANT_COUNT as u32); let current = counter / (PLANT_COUNT as u32);
for led in 0..PLANT_COUNT { for led in 0..PLANT_COUNT {
@@ -157,6 +158,7 @@ pub struct FreePeripherals {
pub pcnt0: PCNT0, pub pcnt0: PCNT0,
pub pcnt1: PCNT1, pub pcnt1: PCNT1,
pub adc1: ADC1, pub adc1: ADC1,
pub can: CAN,
} }
impl PlantHal { impl PlantHal {
@@ -186,6 +188,7 @@ impl PlantHal {
boot_button.set_pull(Pull::Floating)?; boot_button.set_pull(Pull::Floating)?;
let free_pins = FreePeripherals { let free_pins = FreePeripherals {
can: peripherals.can,
adc1: peripherals.adc1, adc1: peripherals.adc1,
pcnt0: peripherals.pcnt0, pcnt0: peripherals.pcnt0,
pcnt1: peripherals.pcnt1, pcnt1: peripherals.pcnt1,

View File

@@ -1,7 +1,7 @@
use crate::hal::rtc::RTCModuleInteraction; use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::water::TankSensor; use crate::hal::water::TankSensor;
use crate::hal::{ use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, PLANT_COUNT, REPEAT_MOIST_MEASURE, deep_sleep, BoardInteraction, FreePeripherals, Sensor, PLANT_COUNT,
}; };
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use crate::{ use crate::{
@@ -64,6 +64,9 @@ const FAULT_4: usize = 21;
const FAULT_1: usize = 22; const FAULT_1: usize = 22;
const FAULT_2: usize = 23; const FAULT_2: usize = 23;
const REPEAT_MOIST_MEASURE: usize = 1;
pub struct V3<'a> { pub struct V3<'a> {
config: PlantControllerConfig, config: PlantControllerConfig,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
@@ -91,6 +94,7 @@ pub(crate) fn create_v3(
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>, rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> { ) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
println!("Start v3");
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())?;
@@ -206,7 +210,7 @@ impl<'a> BoardInteraction<'a> for V3<'a> {
&self.config &self.config
} }
fn get_battery_monitor(&mut self) -> &mut Box<(dyn BatteryInteraction + Send + 'static)> { fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send + 'static> {
&mut self.battery_monitor &mut self.battery_monitor
} }

View File

@@ -2,16 +2,16 @@ 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::rtc::RTCModuleInteraction; use crate::hal::rtc::RTCModuleInteraction;
use crate::hal::v4_sensor::SensorImpl;
use crate::hal::v4_sensor::SensorInteraction;
use crate::hal::water::TankSensor; use crate::hal::water::TankSensor;
use crate::hal::{ use crate::hal::{
deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT, deep_sleep, BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT
REPEAT_MOIST_MEASURE,
}; };
use crate::log::{log, LogMessage}; use crate::log::{log, LogMessage};
use anyhow::bail; use anyhow::bail;
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::delay::Delay;
use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull}; use esp_idf_hal::gpio::{AnyInputPin, IOPin, InputOutput, Output, PinDriver, Pull};
use esp_idf_hal::i2c::I2cDriver; use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::{ use esp_idf_hal::pcnt::{
@@ -25,13 +25,9 @@ use ina219::SyncIna219;
use measurements::{Current, Resistance, Voltage}; use measurements::{Current, Resistance, Voltage};
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 embedded_can::Frame;
const MS0: u8 = 1_u8; use embedded_can::StandardId;
const MS1: u8 = 0_u8; use esp_idf_hal::can;
const MS2: u8 = 3_u8;
const MS3: u8 = 4_u8;
const MS4: u8 = 2_u8;
const SENSOR_ON: u8 = 5_u8;
pub enum Charger<'a> { pub enum Charger<'a> {
SolarMpptV1 { SolarMpptV1 {
@@ -119,13 +115,13 @@ pub struct V4<'a> {
rtc_module: Box<dyn RTCModuleInteraction + Send>, rtc_module: Box<dyn RTCModuleInteraction + Send>,
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
config: PlantControllerConfig, config: PlantControllerConfig,
signal_counter: PcntDriver<'a>,
awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, awake: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, light: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, pump_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>, pump_ina: Option<SyncIna219<MutexDevice<'a, I2cDriver<'a>>, UnCalibrated>>,
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>, sensor: SensorImpl<'a>,
extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, extra1: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>, extra2: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, Output>,
} }
@@ -137,6 +133,7 @@ pub(crate) fn create_v4(
battery_monitor: Box<dyn BatteryInteraction + Send>, battery_monitor: Box<dyn BatteryInteraction + Send>,
rtc_module: Box<dyn RTCModuleInteraction + Send>, rtc_module: Box<dyn RTCModuleInteraction + Send>,
) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> { ) -> anyhow::Result<Box<dyn BoardInteraction<'static> + Send + 'static>> {
println!("Start v4");
let mut awake = PinDriver::output(peripherals.gpio21.downgrade())?; let mut awake = PinDriver::output(peripherals.gpio21.downgrade())?;
awake.set_high()?; awake.set_high()?;
@@ -163,6 +160,11 @@ pub(crate) fn create_v4(
peripherals.pcnt1, peripherals.pcnt1,
)?; )?;
let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
let sensor = match sensor_expander.pin_into_output(GPIOBank::Bank0, 0) {
Ok(_) => {
println!("SensorExpander answered");
//pulse counter version
let mut signal_counter = PcntDriver::new( let mut signal_counter = PcntDriver::new(
peripherals.pcnt0, peripherals.pcnt0,
Some(peripherals.gpio22), Some(peripherals.gpio22),
@@ -185,6 +187,42 @@ pub(crate) fn create_v4(
}, },
)?; )?;
for pin in 0..8 {
let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
}
SensorImpl::PulseCounter {
signal_counter,
sensor_expander,
}
}
Err(_) => {
println!("Can bus mode ");
let timing = can::config::Timing::B25K;
let config = can::config::Config::new().timing(timing);
let can = can::CanDriver::new(peripherals.can, peripherals.gpio0, peripherals.gpio2, &config).unwrap();
let frame = StandardId::new(0x042).unwrap();
let tx_frame = Frame::new(frame, &[0, 1, 2, 3, 4, 5, 6, 7]).unwrap();
can.transmit(&tx_frame, 1000).unwrap();
if let Ok(rx_frame) = can.receive(1000) {
println!("rx {:}:", rx_frame);
}
//can bus version
SensorImpl::CanBus {
can
}
}
};
let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?; let mut solar_is_day = PinDriver::input(peripherals.gpio7.downgrade())?;
solar_is_day.set_pull(Pull::Floating)?; solar_is_day.set_pull(Pull::Floating)?;
@@ -203,13 +241,7 @@ pub(crate) fn create_v4(
let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin); let _ = pump_expander.pin_set_low(GPIOBank::Bank1, pin);
} }
let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
for pin in 0..8 {
let _ = sensor_expander.pin_into_output(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_into_output(GPIOBank::Bank1, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank0, pin);
let _ = sensor_expander.pin_set_low(GPIOBank::Bank1, pin);
}
let mppt_ina = SyncIna219::new( let mppt_ina = SyncIna219::new(
MutexDevice::new(&I2C_DRIVER), MutexDevice::new(&I2C_DRIVER),
@@ -252,17 +284,16 @@ pub(crate) fn create_v4(
esp, esp,
awake, awake,
tank_sensor, tank_sensor,
signal_counter,
light, light,
general_fault, general_fault,
pump_ina, pump_ina,
pump_expander, pump_expander,
sensor_expander,
config, config,
battery_monitor, battery_monitor,
charger, charger,
extra1, extra1,
extra2, extra2,
sensor,
}; };
Ok(Box::new(v)) Ok(Box::new(v))
} }
@@ -350,78 +381,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
} }
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> { fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> {
let mut results = [0_f32; REPEAT_MOIST_MEASURE]; self.sensor.measure_moisture_hz(plant, sensor)
for repeat in 0..REPEAT_MOIST_MEASURE {
self.signal_counter.counter_pause()?;
self.signal_counter.counter_clear()?;
//Disable all
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
let sensor_channel = match sensor {
Sensor::A => plant as u32,
Sensor::B => (15 - plant) as u32,
};
let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
if is_bit_set(0) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS0)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
}
if is_bit_set(1) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS1)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
}
if is_bit_set(2) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS2)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
}
if is_bit_set(3) {
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS3)?;
} else {
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
}
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?;
self.sensor_expander
.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?;
let delay = Delay::new_default();
let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32;
//give some time to stabilize
delay.delay_ms(10);
self.signal_counter.counter_resume()?;
delay.delay_ms(measurement);
self.signal_counter.counter_pause()?;
self.sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
self.sensor_expander
.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
self.sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
delay.delay_ms(10);
let unscaled = self.signal_counter.get_counter_value()? as i32;
let hz = unscaled as f32 * factor;
log(
LogMessage::RawMeasure,
unscaled as u32,
hz as u32,
&plant.to_string(),
&format!("{sensor:?}"),
);
results[repeat] = hz;
}
results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
let mid = results.len() / 2;
let median = results[mid];
anyhow::Ok(median)
} }
fn general_fault(&mut self, enable: bool) { fn general_fault(&mut self, enable: bool) {

115
rust/src/hal/v4_sensor.rs Normal file
View File

@@ -0,0 +1,115 @@
use embedded_hal_bus::i2c::MutexDevice;
use esp_idf_hal::can::CanDriver;
use esp_idf_hal::delay::Delay;
use esp_idf_hal::i2c::I2cDriver;
use esp_idf_hal::pcnt::PcntDriver;
use pca9535::{GPIOBank, Pca9535Immediate, StandardExpanderInterface};
use crate::hal::Sensor;
use crate::log::{log, LogMessage};
const REPEAT_MOIST_MEASURE: usize = 10;
pub trait SensorInteraction {
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32>;
}
const MS0: u8 = 1_u8;
const MS1: u8 = 0_u8;
const MS2: u8 = 3_u8;
const MS3: u8 = 4_u8;
const MS4: u8 = 2_u8;
const SENSOR_ON: u8 = 5_u8;
pub enum SensorImpl<'a> {
PulseCounter{
signal_counter: PcntDriver<'a>,
sensor_expander: Pca9535Immediate<MutexDevice<'a, I2cDriver<'a>>>,
},
CanBus{
can: CanDriver<'a>
}
}
impl SensorInteraction for SensorImpl<'_> {
fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> anyhow::Result<f32> {
match self {
SensorImpl::PulseCounter { signal_counter, sensor_expander, .. } => {
let mut results = [0_f32; REPEAT_MOIST_MEASURE];
for repeat in 0..REPEAT_MOIST_MEASURE {
signal_counter.counter_pause()?;
signal_counter.counter_clear()?;
//Disable all
sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
let sensor_channel = match sensor {
Sensor::A => plant as u32,
Sensor::B => (15 - plant) as u32,
};
let is_bit_set = |b: u8| -> bool { sensor_channel & (1 << b) != 0 };
if is_bit_set(0) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS0)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
}
if is_bit_set(1) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS1)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
}
if is_bit_set(2) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS2)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
}
if is_bit_set(3) {
sensor_expander.pin_set_high(GPIOBank::Bank0, MS3)?;
} else {
sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
}
sensor_expander.pin_set_low(GPIOBank::Bank0, MS4)?;
sensor_expander
.pin_set_high(GPIOBank::Bank0, SENSOR_ON)?;
let delay = Delay::new_default();
let measurement = 100; // TODO what is this scaling factor? what is its purpose?
let factor = 1000f32 / measurement as f32;
//give some time to stabilize
delay.delay_ms(10);
signal_counter.counter_resume()?;
delay.delay_ms(measurement);
signal_counter.counter_pause()?;
sensor_expander.pin_set_high(GPIOBank::Bank0, MS4)?;
sensor_expander
.pin_set_low(GPIOBank::Bank0, SENSOR_ON)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS0)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS1)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS2)?;
sensor_expander.pin_set_low(GPIOBank::Bank0, MS3)?;
delay.delay_ms(10);
let unscaled = signal_counter.get_counter_value()? as i32;
let hz = unscaled as f32 * factor;
log(
LogMessage::RawMeasure,
unscaled as u32,
hz as u32,
&plant.to_string(),
&format!("{sensor:?}"),
);
results[repeat] = hz;
}
results.sort_by(|a, b| a.partial_cmp(b).unwrap()); // floats don't seem to implement total_ord
let mid = results.len() / 2;
let median = results[mid];
anyhow::Ok(median)
}
SensorImpl::CanBus { .. } => {
todo!()
}
}
}
}

View File

@@ -93,14 +93,6 @@ pub struct PumpResult {
pump_time_s: u16, pump_time_s: u16,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq)]
/// humidity sensor error
enum SensorError {
Unknown,
ShortCircuit { hz: f32, max: f32 },
OpenCircuit { hz: f32, min: f32 },
}
#[derive(Serialize, Debug, PartialEq)] #[derive(Serialize, Debug, PartialEq)]
enum SntpMode { enum SntpMode {
OFFLINE, OFFLINE,
@@ -867,7 +859,7 @@ fn pump_info(
median_current_ma: u16, median_current_ma: u16,
max_current_ma: u16, max_current_ma: u16,
min_current_ma: u16, min_current_ma: u16,
error: bool, _error: bool,
) { ) {
let pump_info = PumpInfo { let pump_info = PumpInfo {
enabled: pump_active, enabled: pump_active,

View File

@@ -238,7 +238,7 @@ impl PlantState {
} }
PlantWateringMode::MinMoisture => { PlantWateringMode::MinMoisture => {
let (moisture_percent, _) = self.plant_moisture(); let (moisture_percent, _) = self.plant_moisture();
if let Some(moisture_percent) = moisture_percent { if let Some(_moisture_percent) = moisture_percent {
if self.pump_in_timeout(plant_conf, current_time) { if self.pump_in_timeout(plant_conf, current_time) {
false false
} else if !in_time_range( } else if !in_time_range(
@@ -247,10 +247,10 @@ impl PlantState {
plant_conf.pump_hour_end, plant_conf.pump_hour_end,
) { ) {
false false
} else if (true) { } else if true {
//if not cooldown min and below max //if not cooldown min and below max
true true
} else if (true) { } else if true {
//if below min disable cooldown min //if below min disable cooldown min
true true
} else { } else {

View File

@@ -29,6 +29,8 @@ export interface NetworkConfig {
password: string, password: string,
mqtt_url: string, mqtt_url: string,
base_topic: string, base_topic: string,
mqtt_user: string | null,
mqtt_password: string | null,
max_wait: number max_wait: number
} }

View File

@@ -72,6 +72,18 @@
</div> </div>
<input class="mqttvalue" type="text" id="base_topic" placeholder="plants/one"> <input class="mqttvalue" type="text" id="base_topic" placeholder="plants/one">
</div> </div>
<div class="flexcontainer">
<div class="mqttkey">
MQTT User
</div>
<input class="mqttvalue" type="text" id="mqtt_user" placeholder="">
</div>
<div class="flexcontainer">
<div class="mqttkey">
MQTT Password
</div>
<input class="mqttvalue" type="text" id="mqtt_password" placeholder="">
</div>
</div> </div>
</div> </div>

View File

@@ -16,6 +16,8 @@ export class NetworkConfigView {
private readonly mqtt_url: HTMLInputElement; private readonly mqtt_url: HTMLInputElement;
private readonly base_topic: HTMLInputElement; private readonly base_topic: HTMLInputElement;
private readonly max_wait: HTMLInputElement; private readonly max_wait: HTMLInputElement;
private readonly mqtt_user: HTMLInputElement;
private readonly mqtt_password: HTMLInputElement;
private readonly ssidlist: HTMLElement; private readonly ssidlist: HTMLElement;
constructor(controller: Controller, publicIp: string) { constructor(controller: Controller, publicIp: string) {
@@ -37,6 +39,10 @@ export class NetworkConfigView {
this.mqtt_url.onchange = controller.configChanged this.mqtt_url.onchange = controller.configChanged
this.base_topic = document.getElementById("base_topic") as HTMLInputElement; this.base_topic = document.getElementById("base_topic") as HTMLInputElement;
this.base_topic.onchange = controller.configChanged this.base_topic.onchange = controller.configChanged
this.mqtt_user = document.getElementById("mqtt_user") as HTMLInputElement;
this.mqtt_user.onchange = controller.configChanged
this.mqtt_password = document.getElementById("mqtt_password") as HTMLInputElement;
this.mqtt_password.onchange = controller.configChanged
this.ssidlist = document.getElementById("ssidlist") as HTMLElement this.ssidlist = document.getElementById("ssidlist") as HTMLElement
@@ -52,6 +58,8 @@ export class NetworkConfigView {
this.password.value = network.password; this.password.value = network.password;
this.mqtt_url.value = network.mqtt_url; this.mqtt_url.value = network.mqtt_url;
this.base_topic.value = network.base_topic; this.base_topic.value = network.base_topic;
this.mqtt_user.value = network.mqtt_user ?? "";
this.mqtt_password.value = network.mqtt_password ?? "";
this.max_wait.value = network.max_wait.toString(); this.max_wait.value = network.max_wait.toString();
} }
@@ -62,6 +70,8 @@ export class NetworkConfigView {
ssid: this.ssid.value ?? null, ssid: this.ssid.value ?? null,
password: this.password.value ?? null, password: this.password.value ?? null,
mqtt_url: this.mqtt_url.value ?? null, mqtt_url: this.mqtt_url.value ?? null,
mqtt_user: this.mqtt_user.value ? this.mqtt_user.value : null,
mqtt_password: this.mqtt_password.value ? this.mqtt_password.value : null,
base_topic: this.base_topic.value ?? null base_topic: this.base_topic.value ?? null
} }
} }