Implement reflow process
This commit is contained in:
parent
6f052edad2
commit
f4b60aad42
147
src/main.rs
147
src/main.rs
|
@ -10,18 +10,23 @@ use cortex_m::delay::Delay;
|
||||||
use display_interface::DisplayError;
|
use display_interface::DisplayError;
|
||||||
|
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
mono_font::{iso_8859_1::FONT_6X10, MonoTextStyle},
|
mono_font::{iso_8859_1::FONT_10X20, MonoTextStyle},
|
||||||
pixelcolor::BinaryColor,
|
pixelcolor::BinaryColor,
|
||||||
prelude::*,
|
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 max6675::{Error, Max6675};
|
||||||
|
|
||||||
|
use nb::Error::WouldBlock;
|
||||||
|
|
||||||
use panic_halt as _;
|
use panic_halt as _;
|
||||||
|
|
||||||
use rp_pico::{
|
use rp_pico::{
|
||||||
|
@ -30,6 +35,7 @@ use rp_pico::{
|
||||||
clocks::{self, Clock},
|
clocks::{self, Clock},
|
||||||
gpio::PinState,
|
gpio::PinState,
|
||||||
i2c::I2C,
|
i2c::I2C,
|
||||||
|
timer::CountDown,
|
||||||
usb::UsbBus,
|
usb::UsbBus,
|
||||||
Sio, Timer, Watchdog,
|
Sio, Timer, Watchdog,
|
||||||
},
|
},
|
||||||
|
@ -43,6 +49,22 @@ use usb_device::{bus::UsbBus as UsbBusTrait, bus::UsbBusAllocator, prelude::*};
|
||||||
|
|
||||||
use usbd_serial::{SerialPort, USB_CLASS_CDC};
|
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>(
|
fn print_serial<'a, B: UsbBusTrait>(
|
||||||
serial: &mut SerialPort<'a, B>,
|
serial: &mut SerialPort<'a, B>,
|
||||||
msg: &str,
|
msg: &str,
|
||||||
|
@ -50,13 +72,18 @@ fn print_serial<'a, B: UsbBusTrait>(
|
||||||
let buf = msg.as_bytes();
|
let buf = msg.as_bytes();
|
||||||
let mut pos = 0;
|
let mut pos = 0;
|
||||||
|
|
||||||
|
let mut retry_count = 0;
|
||||||
while pos < buf.len() {
|
while pos < buf.len() {
|
||||||
match serial.write(&buf[pos..]) {
|
match serial.write(&buf[pos..]) {
|
||||||
Ok(count) => {
|
Ok(count) => {
|
||||||
pos += count;
|
pos += count;
|
||||||
|
retry_count = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Err(UsbError::WouldBlock) if retry_count < 100 => {
|
||||||
|
retry_count += 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Err(UsbError::WouldBlock) => continue,
|
|
||||||
Err(err) => return Err(err),
|
Err(err) => return Err(err),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,6 +115,7 @@ fn stringify_display_error(err: DisplayError) -> &'static str {
|
||||||
fn display_text<DI, SIZE>(
|
fn display_text<DI, SIZE>(
|
||||||
display: &mut Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>,
|
display: &mut Ssd1306<DI, SIZE, BufferedGraphicsMode<SIZE>>,
|
||||||
text: &str,
|
text: &str,
|
||||||
|
baseline: Baseline,
|
||||||
) -> Result<(), DisplayError>
|
) -> Result<(), DisplayError>
|
||||||
where
|
where
|
||||||
DI: WriteOnlyDataCommand,
|
DI: WriteOnlyDataCommand,
|
||||||
|
@ -95,11 +123,14 @@ where
|
||||||
{
|
{
|
||||||
let (w, h) = display.dimensions();
|
let (w, h) = display.dimensions();
|
||||||
|
|
||||||
Text::with_alignment(
|
Text::with_text_style(
|
||||||
text,
|
text,
|
||||||
Point::new((w / 2).into(), (h / 2).into()),
|
Point::new((w / 2).into(), (h / 2).into()),
|
||||||
MonoTextStyle::new(&FONT_6X10, BinaryColor::On),
|
MonoTextStyle::new(&FONT_10X20, BinaryColor::On),
|
||||||
Alignment::Center,
|
TextStyleBuilder::new()
|
||||||
|
.alignment(Alignment::Center)
|
||||||
|
.baseline(baseline)
|
||||||
|
.build(),
|
||||||
)
|
)
|
||||||
.draw(display)?;
|
.draw(display)?;
|
||||||
|
|
||||||
|
@ -153,9 +184,8 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = [0u8];
|
let mut buf = [0u8];
|
||||||
match serial.read(&mut buf[..]) {
|
if let Ok(_) = serial.read(&mut buf[..]) {
|
||||||
Ok(_) => break,
|
break;
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,9 +207,8 @@ fn main() -> ! {
|
||||||
)
|
)
|
||||||
.into_buffered_graphics_mode();
|
.into_buffered_graphics_mode();
|
||||||
|
|
||||||
let result = display.init().map_err(stringify_display_error);
|
if let Err(err) = display.init() {
|
||||||
if let Err(msg) = result {
|
let _ = print_serial(&mut serial, stringify_display_error(err));
|
||||||
let _ = print_serial(&mut serial, msg);
|
|
||||||
panic!();
|
panic!();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,17 +224,41 @@ fn main() -> ! {
|
||||||
|
|
||||||
let mut led = pins.led.into_push_pull_output_in_state(PinState::Low);
|
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 counter = 0;
|
||||||
|
let mut current_temperature = Option::<u16>::None;
|
||||||
|
let mut state = State::Off;
|
||||||
loop {
|
loop {
|
||||||
delay.delay_ms(5);
|
delay.delay_ms(5);
|
||||||
counter = (counter + 1) % 100;
|
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 {
|
if counter == 0 {
|
||||||
led.toggle().unwrap();
|
led.toggle().unwrap();
|
||||||
|
|
||||||
let mut bytes: [u8; 7] = [32, 32, 32, 32, 194, 176, 67];
|
let mut bytes: [u8; 7] = [32, 32, 32, 32, 194, 176, 67];
|
||||||
let text = match thermocouple.read_temperature(&mut timer.count_down()) {
|
let text = match thermocouple.read_temperature(&mut timer.count_down()) {
|
||||||
Ok(mut temperature) => {
|
Ok(mut temperature) => {
|
||||||
|
current_temperature = Some(temperature);
|
||||||
temperature >>= 2;
|
temperature >>= 2;
|
||||||
|
|
||||||
for i in (0..3).rev() {
|
for i in (0..3).rev() {
|
||||||
|
@ -227,14 +280,65 @@ fn main() -> ! {
|
||||||
|
|
||||||
display.clear_buffer();
|
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));
|
let _ = println_serial(&mut serial, stringify_display_error(err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if button.is_low().unwrap() {
|
if let Some(temperature) = current_temperature {
|
||||||
relay.toggle().unwrap();
|
match state {
|
||||||
delay.delay_ms(1000);
|
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]) {
|
if !usb_dev.poll(&mut [&mut serial]) {
|
||||||
|
@ -242,11 +346,8 @@ fn main() -> ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf = [0u8; 64];
|
let mut buf = [0u8; 64];
|
||||||
match serial.read(&mut buf[..]) {
|
if let Ok(count) = serial.read(&mut buf[..]) {
|
||||||
Ok(count) => {
|
let _ = serial.write(&buf[..count]);
|
||||||
let _ = serial.write(&buf[..count]);
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue