1. use core::usize;
  2. use embassy_futures::select::{select, select_slice, Either};
  3. use embassy_time::{Duration, Timer};
  4. use esp_hal::gpio::{AnyPin, Output, PushPull};
  5. use crate::{
  6. error::ErrorExt,
  7. state::{ValveEvent, VALVE_EVENT_CHANNEL},
  8. utils::OptFuture,
  9. };
  10. pub struct ValveControl<const SIZE: usize> {
  11. pins: [AnyPin<Output<PushPull>>; SIZE],
  12. deadlines: [OptFuture<Timer>; SIZE],
  13. }
  14. impl<const SIZE: usize> ValveControl<SIZE> {
  15. pub fn new(mut pins: [AnyPin<Output<PushPull>>; SIZE]) -> Self {
  16. for pin in &mut pins {
  17. pin.set_high();
  18. }
  19. let deadlines = [(); SIZE].map(|_| OptFuture::default());
  20. Self { pins, deadlines }
  21. }
  22. pub fn control(&mut self, id: u8) -> Option<Valve<'_>> {
  23. let pin = self.pins.get_mut(id as usize)?;
  24. let deadline = self.deadlines.get_mut(id as usize)?;
  25. Some(Valve { id, pin, deadline })
  26. }
  27. async fn wait_for_next_close(&mut self) -> Valve<'_> {
  28. let ((), index) = select_slice(&mut self.deadlines).await;
  29. self.deadlines[index] = OptFuture::default();
  30. Valve {
  31. id: index as u8,
  32. pin: &mut self.pins[index],
  33. deadline: &mut self.deadlines[index],
  34. }
  35. }
  36. }
  37. pub struct Valve<'a> {
  38. pub id: u8,
  39. pin: &'a mut AnyPin<Output<PushPull>>,
  40. deadline: &'a mut OptFuture<Timer>,
  41. }
  42. impl<'a> Valve<'a> {
  43. pub fn open(&mut self, duration: Duration) {
  44. log::info!("opening valve {} for {} secs", self.id, duration.as_secs());
  45. *self.deadline = Timer::after(duration).into();
  46. self.pin.set_low();
  47. }
  48. pub fn close(&mut self) {
  49. log::info!("closing valve {}", self.id);
  50. self.pin.set_high();
  51. *self.deadline = OptFuture::default();
  52. }
  53. }
  54. #[embassy_executor::task]
  55. pub async fn task(mut valves: ValveControl<4>) -> ! {
  56. let mut subscriber = VALVE_EVENT_CHANNEL.subscriber().log_unwrap();
  57. macro_rules! try_get_valve {
  58. ($id:expr) => {
  59. match valves.control($id) {
  60. Some(valve) => valve,
  61. None => {
  62. log::warn!("tried to control invalid pin {}", $id);
  63. continue;
  64. }
  65. }
  66. };
  67. }
  68. loop {
  69. match select(valves.wait_for_next_close(), subscriber.next_message_pure()).await {
  70. Either::First(mut valve) => {
  71. log::info!("deadline reached for valve {}", valve.id);
  72. valve.close()
  73. }
  74. Either::Second(ValveEvent::Open { id, duration }) => {
  75. let mut valve = try_get_valve!(id);
  76. valve.open(duration);
  77. }
  78. Either::Second(ValveEvent::Close(id)) => {
  79. let mut valve = try_get_valve!(id);
  80. valve.close();
  81. }
  82. }
  83. }
  84. }