implement hid io wrapper

This commit is contained in:
soffee 2025-03-08 02:15:05 +03:00
parent 7d35bbedbf
commit 9d0444a87a
2 changed files with 180 additions and 71 deletions

111
firmware/src/hid.rs Normal file
View file

@ -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<NonZero<u32>>;
}
pub struct GPIOButton<'d, P: InputPin> {
pin: PinDriver<'d, P, Input>,
int_type: InterruptType,
notification: Notification,
notifier: Arc<Notifier>,
}
impl<'d, P: InputPin> GPIOButton<'d, P> {
pub fn new(pin: P, int_type: InterruptType) -> Result<Self, EspError> {
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<NonZero<u32>> {
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<Self> {
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<Self> {
return Ok(Self {
button: GPIOButton::new(btn_pin, btn_int_type)?,
indicator: GPIOIndicator::new(indicator_pin)?,
});
}
}

View file

@ -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},
gpio::{InterruptType, PinDriver},
i2c::{I2cConfig, I2cDriver},
prelude::Peripherals,
units::*,
task::notification::Notification
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
@ -33,15 +32,16 @@ fn main() -> Result<()> {
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 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,53 +61,8 @@ 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!");
loop {
let not = notification.wait(esp_idf_svc::hal::delay::NON_BLOCK);
match not {
Some(n) => {
println!("Button 1 pressed!");
button1.enable_interrupt()?;
if heater_drv_pin.is_set_high() {
heater_drv_pin.set_low()?;
} else {
heater_drv_pin.set_high()?;
}
},
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;
@ -115,27 +70,70 @@ fn main() -> Result<()> {
const B: f32 = 4285_f32;
let mut samples_sum = 0_f32;
for i in 0..64 {
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 {
// #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!");
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 => (),
}
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;
FreeRtos::delay_ms(10);
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);
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");
}
display.clear_buffer();
// #FIXME display updates take too long. Need some research for options.
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();
// display.clear_buffer();
display.flush().unwrap();
// 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));
}