From 9d0444a87a1017d63a16db2c7ba3d848c49ba231 Mon Sep 17 00:00:00 2001 From: soffee Date: Sat, 8 Mar 2025 02:15:05 +0300 Subject: [PATCH] implement hid io wrapper --- firmware/src/hid.rs | 111 ++++++++++++++++++++++++++++++++++ firmware/src/main.rs | 140 +++++++++++++++++++++---------------------- 2 files changed, 180 insertions(+), 71 deletions(-) create mode 100644 firmware/src/hid.rs diff --git a/firmware/src/hid.rs b/firmware/src/hid.rs new file mode 100644 index 0000000..0ee01e0 --- /dev/null +++ b/firmware/src/hid.rs @@ -0,0 +1,111 @@ +use anyhow::{Error, Result}; +use std::{num::{NonZero, NonZeroU32}, sync::Arc}; +use esp_idf_svc::{hal::{gpio::{Input, InputPin, InterruptType, Output, OutputPin, PinDriver}, task::notification::{Notification, Notifier}}, sys::EspError}; + +pub trait Indicator { + fn set_state(&mut self, state: bool) -> Result<()>; + fn get_state(&self) -> bool; +} + +pub trait Button { + fn subscribe(&mut self) -> Result<()>; + fn unsubscribe(&mut self) -> Result<()>; + fn wait_event(&self, timeout: u32) -> Option>; +} + +pub struct GPIOButton<'d, P: InputPin> { + pin: PinDriver<'d, P, Input>, + int_type: InterruptType, + notification: Notification, + notifier: Arc, +} + +impl<'d, P: InputPin> GPIOButton<'d, P> { + pub fn new(pin: P, int_type: InterruptType) -> Result { + let notification: Notification = Notification::new(); + let notifier = notification.notifier(); + return Ok(Self { + pin: PinDriver::input(pin)?, + int_type: int_type, + notification: notification, + notifier: notifier, + }); + } + + pub fn enable_interrupt(&mut self) -> Result<(), EspError>{ + self.pin.enable_interrupt()?; + return Ok(()); + } +} + +impl<'d, P: InputPin> Button for GPIOButton<'d, P> { + fn subscribe(&mut self) -> Result<()> { + self.pin.set_interrupt_type(self.int_type)?; + unsafe { + let notifier = self.notifier.clone(); + self.pin.subscribe(move || { + notifier.notify_and_yield(NonZeroU32::new(1).unwrap()); + })?; + } + self.pin.enable_interrupt()?; + return Ok(()); + } + + fn unsubscribe(&mut self) -> Result<()> { + self.pin.disable_interrupt()?; + self.pin.unsubscribe()?; + return Ok(()); + } + + fn wait_event(&self, timeout: u32) -> Option> { + return self.notification.wait(timeout); + } +} + +pub struct GPIOIndicator<'d, P: OutputPin> { + pin: PinDriver<'d, P, Output>, + inverted: bool, +} + +impl<'d, P: OutputPin> GPIOIndicator<'d, P> { + pub fn new(pin: P) -> Result { + let mut indicator = Self { + pin: PinDriver::output(pin)?, + inverted: false, + }; + indicator.set_state(false)?; + return Ok(indicator); + } + pub fn set_inverted(&mut self, value: bool) { + self.inverted = value; + } +} + +impl<'d, P: OutputPin> Indicator for GPIOIndicator<'d, P> { + fn set_state(&mut self, state: bool) -> Result<()> { + if state != !self.inverted { + self.pin.set_high()?; + } else { + self.pin.set_low()?; + } + return Ok(()); + } + + fn get_state(&self) -> bool { + return self.pin.is_set_high() != !self.inverted; + } +} + +pub struct GPIOIndicatedButton<'d, B: InputPin, I: OutputPin> { + pub button: GPIOButton<'d, B>, + pub indicator: GPIOIndicator<'d, I>, +} + +impl<'d, B: InputPin, I: OutputPin> GPIOIndicatedButton<'d, B, I> { + pub fn new(btn_pin: B, btn_int_type: InterruptType, indicator_pin: I) -> Result { + return Ok(Self { + button: GPIOButton::new(btn_pin, btn_int_type)?, + indicator: GPIOIndicator::new(indicator_pin)?, + }); + } +} \ No newline at end of file diff --git a/firmware/src/main.rs b/firmware/src/main.rs index f0ba90f..f90958e 100644 --- a/firmware/src/main.rs +++ b/firmware/src/main.rs @@ -1,23 +1,22 @@ -use std::{num::NonZeroU32, thread, time::Duration}; +mod hid; + +use std::{thread, time::{Duration, SystemTime}}; use anyhow::Result; use embedded_graphics::{ mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder}, pixelcolor::BinaryColor, - prelude::Point, - text::{Baseline, Text}, - Drawable }; use esp_idf_svc::hal::{ - i2c::{I2cConfig, I2cDriver}, adc::{attenuation::DB_11, oneshot::{config::AdcChannelConfig, AdcChannelDriver, AdcDriver}}, - delay::FreeRtos, - gpio::{InterruptType, PinDriver, Pull}, - prelude::Peripherals, - units::*, - task::notification::Notification + gpio::{InterruptType, PinDriver}, + i2c::{I2cConfig, I2cDriver}, + prelude::Peripherals, + units::* }; +use hid::{Button, GPIOIndicatedButton, Indicator}; use ssd1306::{mode::DisplayConfig, prelude::DisplayRotation, size::DisplaySize128x32, I2CDisplayInterface, Ssd1306}; + fn main() -> Result<()> { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 @@ -32,16 +31,17 @@ fn main() -> Result<()> { let i2c_sda_pin = peripherals.pins.gpio8; let i2c_scl_pin = peripherals.pins.gpio9; - let mut led_pin = PinDriver::output(peripherals.pins.gpio15).unwrap(); - let mut led2_pin = PinDriver::output(peripherals.pins.gpio3).unwrap(); + let mut led_pin= PinDriver::output(peripherals.pins.gpio15).unwrap(); + + let mut btn1 = GPIOIndicatedButton::new(peripherals.pins.gpio2, InterruptType::PosEdge, peripherals.pins.gpio3)?; + let mut btn2 = GPIOIndicatedButton::new(peripherals.pins.gpio4, InterruptType::PosEdge, peripherals.pins.gpio5)?; + let mut btn3 = GPIOIndicatedButton::new(peripherals.pins.gpio6, InterruptType::PosEdge, peripherals.pins.gpio7)?; + + btn1.button.subscribe()?; let mut heater_drv_pin = PinDriver::output(peripherals.pins.gpio13).unwrap(); let mut pump_drv_pin = PinDriver::output(peripherals.pins.gpio12).unwrap(); - let mut button1 = PinDriver::input(peripherals.pins.gpio2)?; - let mut button2 = PinDriver::input(peripherals.pins.gpio4)?; - let mut button3 = PinDriver::input(peripherals.pins.gpio6)?; - let config = AdcChannelConfig { attenuation: DB_11, ..Default::default() @@ -61,81 +61,79 @@ fn main() -> Result<()> { .text_color(BinaryColor::On) .build(); - button1.set_pull(Pull::Down)?; - button1.set_interrupt_type(InterruptType::PosEdge)?; - - button2.set_pull(Pull::Down)?; - button2.set_interrupt_type(InterruptType::PosEdge)?; - - button3.set_pull(Pull::Down)?; - button3.set_interrupt_type(InterruptType::PosEdge)?; - - let notification = Notification::new(); - let notifier = notification.notifier(); - - unsafe { - button1.subscribe(move || { - notifier.notify_and_yield(NonZeroU32::new(1).unwrap()); - })?; - } - - button1.enable_interrupt()?; - log::info!("Hello, world!"); + const R1: f32 = 100000_f32; + const R0: f32 = 100000_f32; + const V1: f32 = 5_f32; + const T0: f32 = 298.15_f32; + const B: f32 = 4285_f32; + + let mut samples_sum = 0_f32; + let mut temperature = 0_f32; + let mut i = 0_u32; + + let mut blink_time: SystemTime = SystemTime::now(); + let mut measure_time: SystemTime = SystemTime::now(); + loop { - let not = notification.wait(esp_idf_svc::hal::delay::NON_BLOCK); + // #FIXME button handler needs debounce. + let not = btn1.button.wait_event(esp_idf_svc::hal::delay::NON_BLOCK); match not { Some(n) => { println!("Button 1 pressed!"); - button1.enable_interrupt()?; + btn1.button.enable_interrupt()?; if heater_drv_pin.is_set_high() { heater_drv_pin.set_low()?; + btn1.indicator.set_state(false)?; } else { heater_drv_pin.set_high()?; + btn1.indicator.set_state(true)?; } }, None => (), } - led_pin.set_low().unwrap(); - led2_pin.set_low().unwrap(); - FreeRtos::delay_ms(1000); - - led_pin.set_high().unwrap(); - led2_pin.set_high().unwrap(); - FreeRtos::delay_ms(1000); - println!("blink"); - - const R1: f32 = 100000_f32; - const R0: f32 = 100000_f32; - const V1: f32 = 5_f32; - const T0: f32 = 298.15_f32; - const B: f32 = 4285_f32; - - let mut samples_sum = 0_f32; - for i in 0..64 { - let v = adc.read(&mut therm_pin)? as f32 / 1000_f32; - let r = R1 / (V1 / v - 1_f32); - let temp = T0 * B / (T0 * (r / R0).ln() + B) - 273.15_f32; - samples_sum += temp; - FreeRtos::delay_ms(10); + if measure_time.elapsed()?.as_millis() >= 10 { + if i < 64 { + let v = adc.read(&mut therm_pin)? as f32 / 1000_f32; + let r = R1 / (V1 / v - 1_f32); + let temp = T0 * B / (T0 * (r / R0).ln() + B) - 273.15_f32; + samples_sum += temp; + i += 1; + } else { + temperature = samples_sum / 64_f32; + println!("Temperature value: {}", temperature); + samples_sum = 0_f32; + i = 0; + } + measure_time = SystemTime::now(); } - let temp = samples_sum / 64_f32; - - println!("Temperature value: {}", temp); - display.clear_buffer(); + if blink_time.elapsed()?.as_millis() >= 1000 { + if led_pin.is_set_high() { + led_pin.set_low().unwrap(); + } else { + led_pin.set_high().unwrap(); + println!("blink"); + } - Text::with_baseline(format!("Temperature: {}", temp).as_str(), Point::zero(), text_style, Baseline::Top) - .draw(&mut display) - .unwrap(); - Text::with_baseline(format!("Relay: {}", heater_drv_pin.is_set_high()).as_str(), Point::new(0, 16), text_style, Baseline::Top) - .draw(&mut display) - .unwrap(); + // #FIXME display updates take too long. Need some research for options. - display.flush().unwrap(); + // display.clear_buffer(); + + // Text::with_baseline(format!("Temperature: {}", temperature).as_str(), Point::zero(), text_style, Baseline::Top) + // .draw(&mut display) + // .unwrap(); + // Text::with_baseline(format!("Relay: {}", heater_drv_pin.is_set_high()).as_str(), Point::new(0, 16), text_style, Baseline::Top) + // .draw(&mut display) + // .unwrap(); + + // display.flush().unwrap(); + + blink_time = SystemTime::now(); + } thread::sleep(Duration::from_millis(10)); }