Compare commits
11 Commits
a8e17cda3b
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
|
5bc20d312a
|
|||
|
4faae86a6b
|
|||
|
4fa1a05fc3
|
|||
|
3a24dcec53
|
|||
|
e7895577ca
|
|||
|
be9a076b33
|
|||
| 47ad4c70de | |||
| c4aa3a1450 | |||
| f0d89417b6 | |||
| 50b2e994ca | |||
| 7a7f000743 |
File diff suppressed because it is too large
Load Diff
@@ -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": [],
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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]
|
||||||
|
|||||||
@@ -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,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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()?;
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,27 +160,68 @@ pub(crate) fn create_v4(
|
|||||||
peripherals.pcnt1,
|
peripherals.pcnt1,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let mut signal_counter = PcntDriver::new(
|
let mut sensor_expander = Pca9535Immediate::new(MutexDevice::new(&I2C_DRIVER), 34);
|
||||||
peripherals.pcnt0,
|
let sensor = match sensor_expander.pin_into_output(GPIOBank::Bank0, 0) {
|
||||||
Some(peripherals.gpio22),
|
Ok(_) => {
|
||||||
Option::<AnyInputPin>::None,
|
println!("SensorExpander answered");
|
||||||
Option::<AnyInputPin>::None,
|
//pulse counter version
|
||||||
Option::<AnyInputPin>::None,
|
let mut signal_counter = PcntDriver::new(
|
||||||
)?;
|
peripherals.pcnt0,
|
||||||
|
Some(peripherals.gpio22),
|
||||||
|
Option::<AnyInputPin>::None,
|
||||||
|
Option::<AnyInputPin>::None,
|
||||||
|
Option::<AnyInputPin>::None,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
signal_counter.channel_config(
|
||||||
|
PcntChannel::Channel0,
|
||||||
|
PinIndex::Pin0,
|
||||||
|
PinIndex::Pin1,
|
||||||
|
&PcntChannelConfig {
|
||||||
|
lctrl_mode: PcntControlMode::Keep,
|
||||||
|
hctrl_mode: PcntControlMode::Keep,
|
||||||
|
pos_mode: PcntCountMode::Increment,
|
||||||
|
neg_mode: PcntCountMode::Hold,
|
||||||
|
counter_h_lim: i16::MAX,
|
||||||
|
counter_l_lim: 0,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
signal_counter.channel_config(
|
|
||||||
PcntChannel::Channel0,
|
|
||||||
PinIndex::Pin0,
|
|
||||||
PinIndex::Pin1,
|
|
||||||
&PcntChannelConfig {
|
|
||||||
lctrl_mode: PcntControlMode::Keep,
|
|
||||||
hctrl_mode: PcntControlMode::Keep,
|
|
||||||
pos_mode: PcntCountMode::Increment,
|
|
||||||
neg_mode: PcntCountMode::Hold,
|
|
||||||
counter_h_lim: i16::MAX,
|
|
||||||
counter_l_lim: 0,
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
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
115
rust/src/hal/v4_sensor.rs
Normal 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!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -72,8 +72,20 @@
|
|||||||
</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>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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,7 +70,9 @@ 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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user