diff --git a/src/main.rs b/src/main.rs index cbd0812..03e8e3c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,18 +10,23 @@ use cortex_m::delay::Delay; use display_interface::DisplayError; use embedded_graphics::{ - mono_font::{iso_8859_1::FONT_6X10, MonoTextStyle}, + mono_font::{iso_8859_1::FONT_10X20, MonoTextStyle}, pixelcolor::BinaryColor, prelude::*, - text::{Alignment, Text}, + text::{Alignment, Baseline, Text, TextStyleBuilder}, }; -use embedded_hal::digital::v2::{InputPin, ToggleableOutputPin}; +use embedded_hal::{ + digital::v2::{InputPin, OutputPin, ToggleableOutputPin}, + timer::CountDown as _, +}; -use fugit::RateExtU32; +use fugit::{ExtU64, MicrosDurationU64, RateExtU32}; use max6675::{Error, Max6675}; +use nb::Error::WouldBlock; + use panic_halt as _; use rp_pico::{ @@ -30,6 +35,7 @@ use rp_pico::{ clocks::{self, Clock}, gpio::PinState, i2c::I2C, + timer::CountDown, usb::UsbBus, Sio, Timer, Watchdog, }, @@ -43,6 +49,22 @@ use usb_device::{bus::UsbBus as UsbBusTrait, bus::UsbBusAllocator, prelude::*}; use usbd_serial::{SerialPort, USB_CLASS_CDC}; +const HYSTERESIS: u16 = 5; + +const PEAK_TEMPERATURE: u16 = 240; + +const PREHEAT_TEMPERATURE: u16 = 150; + +const SOAK_TIME: MicrosDurationU64 = MicrosDurationU64::secs(90); + +enum State<'a> { + Off, + Preheat, + Soak(CountDown<'a>), + Reflow, + Cooling, +} + fn print_serial<'a, B: UsbBusTrait>( serial: &mut SerialPort<'a, B>, msg: &str, @@ -50,13 +72,18 @@ fn print_serial<'a, B: UsbBusTrait>( let buf = msg.as_bytes(); let mut pos = 0; + let mut retry_count = 0; while pos < buf.len() { match serial.write(&buf[pos..]) { Ok(count) => { pos += count; + retry_count = 0; + continue; + } + Err(UsbError::WouldBlock) if retry_count < 100 => { + retry_count += 1; continue; } - Err(UsbError::WouldBlock) => continue, Err(err) => return Err(err), } } @@ -88,6 +115,7 @@ fn stringify_display_error(err: DisplayError) -> &'static str { fn display_text( display: &mut Ssd1306>, text: &str, + baseline: Baseline, ) -> Result<(), DisplayError> where DI: WriteOnlyDataCommand, @@ -95,11 +123,14 @@ where { let (w, h) = display.dimensions(); - Text::with_alignment( + Text::with_text_style( text, Point::new((w / 2).into(), (h / 2).into()), - MonoTextStyle::new(&FONT_6X10, BinaryColor::On), - Alignment::Center, + MonoTextStyle::new(&FONT_10X20, BinaryColor::On), + TextStyleBuilder::new() + .alignment(Alignment::Center) + .baseline(baseline) + .build(), ) .draw(display)?; @@ -153,9 +184,8 @@ fn main() -> ! { } let mut buf = [0u8]; - match serial.read(&mut buf[..]) { - Ok(_) => break, - _ => (), + if let Ok(_) = serial.read(&mut buf[..]) { + break; } } @@ -177,9 +207,8 @@ fn main() -> ! { ) .into_buffered_graphics_mode(); - let result = display.init().map_err(stringify_display_error); - if let Err(msg) = result { - let _ = print_serial(&mut serial, msg); + if let Err(err) = display.init() { + let _ = print_serial(&mut serial, stringify_display_error(err)); panic!(); } @@ -195,17 +224,41 @@ fn main() -> ! { let mut led = pins.led.into_push_pull_output_in_state(PinState::Low); + let mut button_held = false; + let mut button_cooldown = timer.count_down(); + button_cooldown.start(0.secs()); + let mut counter = 0; + let mut current_temperature = Option::::None; + let mut state = State::Off; loop { delay.delay_ms(5); counter = (counter + 1) % 100; + if let Ok(_) = button_cooldown.wait() { + let button_pressed = button.is_low().unwrap(); + if button_pressed && !button_held { + state = if let State::Off = state { + State::Preheat + } else { + State::Off + }; + + button_cooldown.start(500.millis()); + } else { + button_cooldown.start(0.secs()); + } + + button_held = button_pressed; + } + if counter == 0 { led.toggle().unwrap(); let mut bytes: [u8; 7] = [32, 32, 32, 32, 194, 176, 67]; let text = match thermocouple.read_temperature(&mut timer.count_down()) { Ok(mut temperature) => { + current_temperature = Some(temperature); temperature >>= 2; for i in (0..3).rev() { @@ -227,14 +280,65 @@ fn main() -> ! { display.clear_buffer(); - if let Err(err) = display_text(&mut display, text) { + if let Err(err) = display_text(&mut display, text, Baseline::Bottom) { + let _ = println_serial(&mut serial, stringify_display_error(err)); + } + + let state_str = match state { + State::Off => "OFF", + State::Preheat => "PREHEAT", + State::Soak(_) => "SOAK", + State::Reflow => "REFLOW", + State::Cooling => "COOLING", + }; + + if let Err(err) = display_text(&mut display, state_str, Baseline::Top) { let _ = println_serial(&mut serial, stringify_display_error(err)); } } - if button.is_low().unwrap() { - relay.toggle().unwrap(); - delay.delay_ms(1000); + if let Some(temperature) = current_temperature { + match state { + State::Off => (), + State::Preheat => { + if temperature < PREHEAT_TEMPERATURE { + relay.set_high().unwrap(); + } else { + let mut time = timer.count_down(); + time.start(SOAK_TIME); + state = State::Soak(time); + } + } + State::Soak(ref mut time) => match time.wait() { + Ok(_) => state = State::Reflow, + Err(WouldBlock) => { + if temperature < PREHEAT_TEMPERATURE - HYSTERESIS { + relay.set_high().unwrap(); + } else if temperature > PREHEAT_TEMPERATURE + HYSTERESIS * 2 { + state = State::Reflow; + } else if temperature > PREHEAT_TEMPERATURE + HYSTERESIS { + relay.set_low().unwrap(); + } + } + Err(_) => state = State::Off, + }, + State::Reflow => { + if temperature < PEAK_TEMPERATURE { + relay.set_high().unwrap(); + } else { + relay.set_low().unwrap(); + + state = State::Cooling; + } + } + State::Cooling => state = State::Off, + } + } else { + state = State::Off; + } + + if let State::Off = state { + relay.set_low().unwrap(); } if !usb_dev.poll(&mut [&mut serial]) { @@ -242,11 +346,8 @@ fn main() -> ! { } let mut buf = [0u8; 64]; - match serial.read(&mut buf[..]) { - Ok(count) => { - let _ = serial.write(&buf[..count]); - } - _ => (), + if let Ok(count) = serial.read(&mut buf[..]) { + let _ = serial.write(&buf[..count]); } } }