Skip to content

Commit

Permalink
WIP: Flow meter & comments
Browse files Browse the repository at this point in the history
  • Loading branch information
nbars committed Feb 14, 2024
1 parent 0b4582b commit 428ea53
Show file tree
Hide file tree
Showing 10 changed files with 136 additions and 124 deletions.
61 changes: 45 additions & 16 deletions src/bin/flow_calibration.rs
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
#![no_std]
#![no_main]

use bambino_fw::hardware::{buttons, leds};
use bambino_fw::hardware::{
buttons::{self, ButtonState},
flow_meter::FlowMeter,
leds, pump,
};
use defmt::*;
use embassy_executor::Spawner;
use embassy_futures::select::select;
use embassy_time::Timer;
use {defmt_rtt as _, panic_probe as _};

#[embassy_executor::main]
async fn main(mut spawner: Spawner) -> ! {
let _p = embassy_stm32::init(Default::default());
//let mut pump = pump::Pump::new(&p);
let mut pump = unsafe { pump::Pump::new() };

let mut buttons = buttons::Buttons::new(&mut spawner);
let mut leds = leds::LEDs::new(&mut spawner);
let mut flow_meter = unsafe { FlowMeter::new(&mut spawner) };

let mut buttons = unsafe { buttons::Buttons::new(&mut spawner) };
let mut leds = unsafe { leds::LEDs::new(&mut spawner) };
leds.set_state_all(leds::LEDState::On);

/*
Expand All @@ -31,19 +39,40 @@ async fn main(mut spawner: Spawner) -> ! {
// pump.disable();

loop {
let event = buttons.wait_for_button_state_change().await;
let new_state = event.new_state().state();
let source = event.new_state().source();
info!("Button {:?} is now in state {:?}", &source, new_state);

match source {
buttons::ButtonKind::OneCup => {
leds.set_state(leds::LEDKind::OneCup, leds::LEDState::Blinking(2))
}
buttons::ButtonKind::TwoCup => {
leds.set_state(leds::LEDKind::TwoCup, leds::LEDState::Blinking(3))
let event = select(
buttons.wait_for_button_state_change(),
Timer::after_millis(100),
);
match event.await {
embassy_futures::select::Either::First(event) => {
let new_state = event.new_state().state();
let source = event.new_state().source();
info!("Button {:?} is now in state {:?}", &source, new_state);

match source {
buttons::ButtonKind::OneCup => {
if new_state == ButtonState::Pressed {
leds.set_state(leds::LEDKind::OneCup, leds::LEDState::Blinking(2));
pump.set_power(pump::PumpPower::Fraction(0.4));
pump.enable();
flow_meter.wait_for_amount(50000).await;
pump.disable();
}
}
buttons::ButtonKind::TwoCup => {
leds.set_state(leds::LEDKind::TwoCup, leds::LEDState::Blinking(3));
if new_state == ButtonState::Pressed {
leds.set_state(leds::LEDKind::OneCup, leds::LEDState::Blinking(2));
pump.set_power(pump::PumpPower::Fraction(1.0));
pump.enable();
flow_meter.wait_for_amount(50000).await;
pump.disable();
}
}
_ => (),
}
}
_ => (),
embassy_futures::select::Either::Second(_) => {}
}
}
}
4 changes: 2 additions & 2 deletions src/bin/pump_calibartion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use defmt::*;
#[embassy_executor::main]
async fn main(mut spawner: Spawner) -> ! {
let p = embassy_stm32::init(Default::default());
let mut pump = pump::Pump::new(&p);
let mut buttons = buttons::Buttons::new(&mut spawner);
let mut pump = unsafe { pump::Pump::new() };
let mut buttons = unsafe { buttons::Buttons::new(&mut spawner) };

pump.set_power(pump::PumpPower::Lowest);
pump.enable();
Expand Down
7 changes: 5 additions & 2 deletions src/hardware/buttons.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl ButtonEvent {
}

/// State of a button. Ether `Pressed` or `Released`.
#[derive(Clone, Copy, defmt::Format)]
#[derive(Clone, Copy, defmt::Format, PartialEq)]
pub enum ButtonState {
/// The button is pressed.
Pressed,
Expand Down Expand Up @@ -128,7 +128,10 @@ impl Buttons {
///
/// # Panics
/// If the spawner fails to spawn the task.
pub fn new(spawner: &mut Spawner) -> Self {
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new(spawner: &mut Spawner) -> Self {
spawner.spawn(button_task()).unwrap();
Buttons {}
}
Expand Down
147 changes: 51 additions & 96 deletions src/hardware/flow_meter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,32 +4,22 @@
//!
#![allow(clippy::new_without_default)]

use cortex_m::interrupt;
use defmt::*;
use embassy_executor::Spawner;
use embassy_stm32::interrupt::Interrupt;
use embassy_stm32::pac::timer::regs::Cnt16;
use embassy_stm32::exti::{Channel as _, ExtiInput};
use embassy_stm32::{
gpio::{self, AnyPin, Pin},
peripherals,
rcc::low_level::RccPeripheral,
timer::low_level::{
Basic16bitInstance, CaptureCompare16bitInstance, GeneralPurpose16bitInstance,
},
Peripheral, Peripherals,
Peripherals,
};
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_sync::signal::Signal;
use embassy_time::{Duration, Instant, Ticker};
use portable_atomic::AtomicU32;

// #[interrupt]
// unsafe fn TIM3() {
// EXECUTOR_MED.on_interrupt()
// }
static TOTAL_FLOW_IN_MG_SIGNAL: Signal<ThreadModeRawMutex, u32> = Signal::new();
static TOTAL_FLOW_IN_MG: AtomicU32 = AtomicU32::new(0);

/// The SI unit milliliter per seconds.
pub struct MilliliterPerSecond(pub u32);

static CURRENT_FLOW: Signal<ThreadModeRawMutex, MilliliterPerSecond> = Signal::new();
// TODO: Compensate for different pump speeds.
const MG_PER_PULSE: u32 = 416;

/// The flow meter of the machine used to measure the water flow.
pub struct FlowMeter;
Expand All @@ -46,11 +36,32 @@ impl FlowMeter {
spawner.spawn(flowmeter_task()).unwrap();
FlowMeter {}
}
}

/// The amount of water flowed so far.
pub fn flowed_mg(&self) -> u32 {
TOTAL_FLOW_IN_MG.load(portable_atomic::Ordering::SeqCst)
}

/// Wait until the flowed milligram value received an update and return the new value.
pub async fn wait_for_next_flowd_mg(&self) -> u32 {
TOTAL_FLOW_IN_MG_SIGNAL.wait().await
}

/// Wait until the specified amount of water has been poured.
pub async fn wait_for_amount(&self, amount_in_mg: u32) {
let start_mg = self.flowed_mg();
loop {
let new_value = self.wait_for_next_flowd_mg().await;
if new_value.wrapping_sub(start_mg) >= amount_in_mg {
return;
}
}
}
}

struct FlowMeterTask<'a> {
flow_enable_pin: gpio::Output<'a, AnyPin>,
flow_enable: gpio::Output<'a, AnyPin>,
signal: ExtiInput<'a, AnyPin>,
}

impl<'a> FlowMeterTask<'a> {
Expand All @@ -59,98 +70,42 @@ impl<'a> FlowMeterTask<'a> {
let p = unsafe { Peripherals::steal() };

let flow_enable_pin = p.PB11.degrade();
let signal_pin: AnyPin = p.PA7.degrade();

let flow_enable_pin =
gpio::Output::new(flow_enable_pin, gpio::Level::Low, gpio::Speed::Low);

let block = signal_pin.block();
block
.moder()
.modify(|r| r.set_moder(7, embassy_stm32::pac::gpio::vals::Moder::ALTERNATE));
block.afr(0).modify(|r| r.set_afr(7, 1));

embassy_stm32::peripherals::TIM3::enable_and_reset();
embassy_stm32::peripherals::TIM3::regs_gp16()
.ccmr_input(0)
.modify(|r| r.set_ccs(1, embassy_stm32::pac::timer::vals::CcmrInputCcs::TI4));
embassy_stm32::peripherals::TIM3::regs_gp16()
.ccer()
.modify(|v| {
v.set_ccp(1, false);
v.set_ccnp(1, false);
});
embassy_stm32::peripherals::TIM3::regs_gp16()
.smcr()
.modify(|v| v.set_sms(embassy_stm32::pac::timer::vals::Sms::EXT_CLOCK_MODE));
embassy_stm32::peripherals::TIM3::regs_gp16()
.smcr()
.modify(|v| v.set_ts(embassy_stm32::pac::timer::vals::Ts::TI2FP2));
embassy_stm32::peripherals::TIM3::regs_gp16()
.arr()
.modify(|w| w.set_arr(u16::MAX));
embassy_stm32::peripherals::TIM3::regs_gp16()
.cr1()
.modify(|r| r.set_cen(true));

// todo: overflow interrupt

FlowMeterTask { flow_enable_pin }
}
let signal_input: AnyPin = p.PA7.degrade();

let flow_enable = gpio::Output::new(flow_enable_pin, gpio::Level::Low, gpio::Speed::Low);

fn read_counter_and_reset(&mut self) -> u16 {
embassy_stm32::peripherals::TIM3::regs()
// FIXME: This is likely not atomic :/
.cnt()
.modify(|f| {
let old = f.0;
*f = Cnt16(0);
old
})
.try_into()
.unwrap()
let signal_input = gpio::Input::new(signal_input.degrade(), gpio::Pull::None);
let signal = ExtiInput::new(signal_input, p.EXTI4.degrade());

FlowMeterTask {
flow_enable,
signal,
}
}

fn counter_to_mg(counter: u32) -> f32 {
counter as f32 * 0.4858
async fn wait_for_pulse(&mut self) {
self.signal.wait_for_falling_edge().await;
}

fn enable(&mut self) {
embassy_stm32::peripherals::TIM3::regs()
.cnt()
.write_value(embassy_stm32::pac::timer::regs::Cnt16(0));
embassy_stm32::peripherals::TIM3::regs_gp16()
.cr1()
.modify(|r| r.set_cen(true));
self.flow_enable_pin.set_high();
self.flow_enable.set_high();
}

fn disable(&mut self) {
self.flow_enable_pin.set_low();
embassy_stm32::peripherals::TIM3::regs_gp16()
.cr1()
.modify(|r| r.set_cen(false));
}

fn update_flow(&mut self, elapsed_time: Duration) {
todo!();
let ctr = self.read_counter_and_reset();
//let flow = FlowMeterTask::counter_to_mg(ctr);
self.flow_enable.set_low();
}
}

#[embassy_executor::task]
async fn flowmeter_task() -> ! {
let mut flow_meter = FlowMeterTask::new();
let mut ticker = Ticker::every(Duration::from_millis(100));

CURRENT_FLOW.signal(MilliliterPerSecond(0));
flow_meter.enable();

loop {
let last_update = Instant::now();
ticker.next();

let elapsed = last_update.elapsed();
flow_meter.update_flow(elapsed);
flow_meter.wait_for_pulse().await;
let amount_mg = MG_PER_PULSE;
let new_amount_mg =
TOTAL_FLOW_IN_MG.fetch_add(amount_mg, portable_atomic::Ordering::SeqCst);
TOTAL_FLOW_IN_MG_SIGNAL.signal(new_amount_mg);
}
}
5 changes: 4 additions & 1 deletion src/hardware/heater.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ pub struct Heater<'a> {

impl<'a> Heater<'a> {
/// Create a new heater instance.
pub fn new(p: Peripherals) -> Self {
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new(p: Peripherals) -> Self {
let pin = Output::new(p.PB6.degrade(), Level::Low, Speed::Low);
Heater { pin }
}
Expand Down
7 changes: 5 additions & 2 deletions src/hardware/leds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,13 @@ pub enum LEDKind {
/// All controllable LEDs of the machine.
pub struct LEDs;
impl LEDs {
///
/// Cerate a new instance for controlling the LEDs.
/// # Panics
/// If there are insufficient ressource for spawning news tasks.
pub fn new(spawner: &mut Spawner) -> Self {
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new(spawner: &mut Spawner) -> Self {
let p = unsafe { Peripherals::steal() };
spawner
.spawn(led_task(LEDKind::OneCup, p.PA15.degrade()))
Expand Down
6 changes: 5 additions & 1 deletion src/hardware/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#![allow(clippy::new_without_default)]

pub mod buttons;
pub mod flow_meter;
pub mod heater;
pub mod leds;
pub mod pump;
pub mod pump;
pub mod solenoid;
pub mod temperature;
8 changes: 6 additions & 2 deletions src/hardware/pump.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@ pub struct Pump<'a, T> {
}

impl<'a> Pump<'a, TIM16> {
/// Create a new `Pump` instance.
pub fn new(p: &Peripherals) -> Self {
/// Create a new `Pump` instance in order to controll the pump.
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new() -> Self {
let p = unsafe { Peripherals::steal() };
let pin = PwmPin::new_ch1(unsafe { p.PB8.clone_unchecked() }, OutputType::PushPull);
let pwm = SimplePwm::new(
unsafe { p.TIM16.clone_unchecked() },
Expand Down
7 changes: 6 additions & 1 deletion src/hardware/solenoid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ pub struct Solenoid<'a> {
}

impl<'a> Solenoid<'a> {
pub fn new(p: &Peripherals) -> Self {
/// Create a new `Solenoid` instance to control whether water is poured via shower or steam wand.
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new() -> Self {
let p = unsafe { Peripherals::steal() };
let pin = p.PA11.degrade();
Solenoid {
pin: Output::new(pin, Level::Low, Speed::Low),
Expand Down
8 changes: 7 additions & 1 deletion src/hardware/temperature.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@ pub struct Temperature<'a> {
}

impl<'a> Temperature<'a> {
pub fn new(p: &Peripherals) -> Self {
/// Create a new `Temperature` instrance in order to measure the water temperature.
/// # Safety
/// This is only safe when called once and without concurrently calling any of the `new()``
/// methods of the other hardware components.
pub unsafe fn new(p: &Peripherals) -> Self {
let p = unsafe { Peripherals::steal() };

bind_interrupts!(struct Irqs {
ADC1 => adc::InterruptHandler<ADC>;
});
Expand Down

0 comments on commit 428ea53

Please sign in to comment.