diff --git a/README.md b/README.md index b096a95..67b75c4 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,8 @@ How do I use this chess clock? 5. If the game needs to be paused at any time, press the START button. To unpause, press the the P1 or P2 buttons depending on who is going next with the same schematics as the start screen. -6. When the game is over, press START to go back to the time selection screen. +6. When the game is over (the buzzer will sound one long beep if it's + connected), press START to go back to the time selection screen. ## States @@ -101,6 +102,8 @@ Time's up! [P2] Down button => Arduino d2 & GND (also functions as P1 button) Start button => Arduino d3 & GND Up button => Arduino d4 & GND (also functions as P2 button) +3. **Buzzer** (optional) + Buzzer anode => Arduino d6 ## Build Instructions diff --git a/src/countdown.rs b/src/countdown.rs index 90597ed..b6d9281 100644 --- a/src/countdown.rs +++ b/src/countdown.rs @@ -1,7 +1,7 @@ use core::cell::RefCell; use arduino_hal::{delay_ms, Delay}; -use embedded_hal::digital::v2::InputPin; +use embedded_hal::digital::v2::{InputPin, OutputPin}; use hd44780_driver::{bus::DataBus, HD44780}; use ufmt::uwrite; @@ -14,6 +14,7 @@ use crate::{ }; const LOOP_DELAY: u16 = 5; +const BUZZER_LENGTH: u16 = 20; pub enum CountdownResult { FinishedP1, @@ -27,10 +28,11 @@ pub enum Turn { P2, } -pub fn countdown( +pub fn countdown( down_pin: &mut DP, up_pin: &mut UP, start_pin: &mut SP, + buzzer_pin: &mut BP, delay: &mut Delay, lcd: &RefCell>, writer: &mut LcdWriter<'_, B>, @@ -50,30 +52,52 @@ pub fn countdown( let mut last_turn = turn.clone(); let mut last_change_time = millis(); + let mut remaining_buzzer_duration = 0; Ok(loop { let time_since_change = millis() - last_change_time; - let new_p1_time = if *turn == Turn::P1 { - convert_time(match p1_ms_at_change.checked_sub(time_since_change) { + let new_p1_ms = if *turn == Turn::P1 { + match p1_ms_at_change.checked_sub(time_since_change) { Some(x) => x, None => { p1_ms_at_change = 0; break finish_countdown(p1_ms_at_change, p2_ms_at_change, p1_time, p2_time); } - }) + } } else { - convert_time(p1_ms_at_change) + p1_ms_at_change }; - let new_p2_time = if *turn == Turn::P2 { - convert_time(match p2_ms_at_change.checked_sub(time_since_change) { + let new_p1_time = convert_time(new_p1_ms); + let new_p2_ms = if *turn == Turn::P2 { + match p2_ms_at_change.checked_sub(time_since_change) { Some(x) => x, None => { p2_ms_at_change = 0; break finish_countdown(p1_ms_at_change, p2_ms_at_change, p1_time, p2_time); } - }) + } } else { - convert_time(p2_ms_at_change) + p2_ms_at_change }; + let new_p2_time = convert_time(new_p2_ms); + + // Update the buzzer + if remaining_buzzer_duration == 1 { + buzzer_pin + .set_low() + .map_err(|_| RuntimeError::PinWriteError)?; + } + if remaining_buzzer_duration > 0 { + remaining_buzzer_duration -= 1; + } + if (new_p1_ms <= 1000 * 10 && new_p1_ms % 1000 == 0) + || (new_p2_ms <= 1000 * 10 && new_p2_ms % 1000 == 0) + { + buzzer_pin + .set_high() + .map_err(|_| RuntimeError::PinWriteError)?; + remaining_buzzer_duration = BUZZER_LENGTH; + } + // Lazy render if *turn != last_turn || new_p1_time != last_p1_time || new_p2_time != last_p2_time { last_turn = turn.clone(); diff --git a/src/error.rs b/src/error.rs index 321851e..bbe54b4 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,4 +2,5 @@ pub enum RuntimeError { LcdError, PinReadError, + PinWriteError, } diff --git a/src/finish.rs b/src/finish.rs index bcf2845..175a00c 100644 --- a/src/finish.rs +++ b/src/finish.rs @@ -1,21 +1,27 @@ use core::cell::RefCell; use arduino_hal::{delay_ms, Delay}; -use embedded_hal::digital::v2::InputPin; +use embedded_hal::digital::v2::{InputPin, OutputPin}; use hd44780_driver::{bus::DataBus, HD44780}; use ufmt::uwrite; use crate::{countdown::Turn, error::RuntimeError, lcd_writer::LcdWriter}; const LOOP_DELAY: u16 = 5; +const BUZZER_LENGTH: u16 = 120; -pub fn finish( +pub fn finish( loser: &Turn, delay: &mut Delay, lcd: &RefCell>, writer: &mut LcdWriter<'_, B>, start_pin: &mut SP, + buzzer_pin: &mut BP, ) -> Result<(), RuntimeError> { + buzzer_pin + .set_high() + .map_err(|_| RuntimeError::PinWriteError)?; + lcd.borrow_mut() .set_cursor_pos(0, delay) .map_err(|_| RuntimeError::LcdError)?; @@ -25,7 +31,15 @@ pub fn finish( uwrite!(writer, "Time's up! [P2]").map_err(|_| RuntimeError::LcdError)?; } let mut start = debouncr::debounce_4(false); + let mut i = 0; loop { + if i < BUZZER_LENGTH { + i += 1; + } else { + buzzer_pin + .set_low() + .map_err(|_| RuntimeError::PinWriteError)?; + } if start.update(start_pin.is_low().map_err(|_| RuntimeError::PinReadError)?) == Some(debouncr::Edge::Rising) { diff --git a/src/main.rs b/src/main.rs index 3aeb1bc..29102bf 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use core::cell::RefCell; use arduino_hal::{delay_ms, Delay}; use countdown::Turn; -use embedded_hal::digital::v2::InputPin; +use embedded_hal::digital::v2::{InputPin, OutputPin}; use error::RuntimeError; use hd44780_driver::{bus::DataBus, DisplayMode, HD44780}; use lcd_writer::LcdWriter; @@ -38,6 +38,8 @@ fn main() -> ! { let up_btn = pins.d4.into_pull_up_input(); // Also P2 button let start_btn = pins.d3.into_pull_up_input(); + let buzzer = pins.d6.into_output(); + let mut lcd_delay = Delay::new(); let lcd_d4 = pins.d9.into_output(); let lcd_d5 = pins.d10.into_output(); @@ -73,10 +75,12 @@ fn main() -> ! { // Turn off the init light to show the successful end of initialization builtin_led.set_low(); + // The main runtime is in a wrapper to handle errors properly if let Err(err) = runtime( down_btn, up_btn, start_btn, + buzzer, &mut lcd_delay, &lcd, &mut writer, @@ -96,10 +100,11 @@ fn main() -> ! { } } -fn runtime( +fn runtime( mut down_btn: DP, mut up_btn: UP, mut start_btn: SP, + mut buzzer: BP, lcd_delay: &mut Delay, lcd: &RefCell>, writer: &mut LcdWriter<'_, B>, @@ -149,6 +154,7 @@ fn runtime( &mut down_btn, &mut up_btn, &mut start_btn, + &mut buzzer, lcd_delay, &lcd, writer, @@ -176,6 +182,6 @@ fn runtime( pause::PauseResult::Stopped => continue 'main, } }; - finish::finish(&loser, lcd_delay, &lcd, writer, &mut start_btn)?; + finish::finish(&loser, lcd_delay, &lcd, writer, &mut start_btn, &mut buzzer)?; } }