use crate::hwa;
use embassy_time::{Duration, Ticker};
#[cfg(not(feature = "native"))]
use num_traits::float::FloatCore;
use num_traits::ToPrimitive;
use printhor_hwa_common::{ControllerRef, DeferEvent, DeferType, EventBusRef};
use printhor_hwa_common::EventStatus;
use printhor_hwa_common::EventFlags;
use printhor_hwa_common::DeferChannelRef;
use pid;
use crate::hwa::controllers::HeaterController;
#[cfg(feature = "with-defmt")]
use crate::hwa::defmt;
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "with-defmt", derive(defmt::Format))]
enum State {
Duty,
Targeting,
Maintaining,
}
struct HeaterStateMachine {
pid: pid::Pid<f32>,
current_temp: f32,
last_temp: f32,
state: State,
#[cfg(feature = "native")]
t0: embassy_time::Instant,
}
impl HeaterStateMachine {
fn new() -> Self {
let mut pid: pid::Pid<f32> = pid::Pid::new(0.0f32, 100.0f32);
pid.p(5.0f32, 100.0f32);
pid.i(0.01f32, 100.0f32);
pid.d(1.5f32, 100.0f32);
Self {
#[cfg(feature = "native")]
t0: embassy_time::Instant::now(),
pid,
current_temp: 0f32,
last_temp: 0f32,
state: State::Duty,
}
}
async fn init<AdcPeri, AdcPin, PwmHwaDevice>(&mut self, ctrl: &ControllerRef<HeaterController<AdcPeri, AdcPin, PwmHwaDevice>>)
where
AdcPeri: hwa::device::AdcTrait + 'static,
AdcPin: hwa::device::AdcPinTrait<AdcPeri>,
PwmHwaDevice: embedded_hal_02::Pwm<Duty=u16> + 'static,
<PwmHwaDevice as embedded_hal_02::Pwm>::Channel: Copy
{
ctrl.lock().await.init().await;
}
async fn update<AdcPeri, AdcPin, PwmHwaDevice>(
&mut self, ctrl: &ControllerRef<HeaterController<AdcPeri, AdcPin, PwmHwaDevice>>,
event_bus: &EventBusRef,
defer_channel: &DeferChannelRef,
temperature_flag: EventFlags,
defer_flag: DeferEvent,
)
where
AdcPeri: hwa::device::AdcTrait + 'static,
AdcPin: hwa::device::AdcPinTrait<AdcPeri>,
PwmHwaDevice: embedded_hal_02::Pwm<Duty=u16> + 'static,
<PwmHwaDevice as embedded_hal_02::Pwm>::Channel: Copy
{
let mut m = ctrl.lock().await;
self.current_temp = m.read_temp().await;
let new_state = {
hwa::trace!("MEASURED_TEMP: {}", self.current_temp);
if m.is_on() {
let target_temp = m.get_target_temp();
self.pid.setpoint(target_temp);
self.last_temp = self.current_temp;
let delta = self.pid.next_control_output(self.current_temp).output;
m.set_current_temp(self.current_temp);
let power = if delta > 0.0f32 {
if delta < 100.0f32 {
delta / 100.0f32
}
else {
1.0f32
}
} else {
0.0f32
};
hwa::info!("TEMP {} -> {}, {} P={} [{}]", self.last_temp, self.current_temp, delta, power, target_temp);
m.set_power((power * 100.0f32).to_u8().unwrap_or(0)).await;
if (self.current_temp - m.get_target_temp()).abs() / target_temp < 0.25 {
State::Maintaining
} else {
State::Targeting
}
} else {
self.current_temp = 0.0;
if self.last_temp != self.current_temp {
self.last_temp = self.current_temp;
}
State::Duty
}
};
if new_state != self.state {
hwa::trace!("Temp changed to {:?}", new_state);
match new_state {
State::Duty => {
#[cfg(feature = "native")]
{
self.t0 = embassy_time::Instant::now();
}
event_bus.publish_event(EventStatus::not_containing(temperature_flag)).await;
}
State::Maintaining => {
defer_channel.send(defer_flag).await;
event_bus.publish_event(EventStatus::containing(temperature_flag)).await;
}
State::Targeting => {
#[cfg(feature = "native")]
{
self.t0 = embassy_time::Instant::now();
hwa::info!("Temp reatched. Firing {:?}", temperature_flag);
}
event_bus.publish_event(EventStatus::not_containing(temperature_flag)).await;
}
}
self.state = new_state;
}
else if self.state == State::Maintaining {
defer_channel.send(defer_flag).await;
}
}
}
#[embassy_executor::task(pool_size=1)]
pub async fn temp_task(
event_bus: EventBusRef,
defer_channel: DeferChannelRef,
#[cfg(feature = "with-hotend")]
hotend_controller: hwa::controllers::HotendControllerRef,
#[cfg(feature = "with-hotbed")]
hotbed_controller: hwa::controllers::HotbedControllerRef,
) -> ! {
hwa::debug!("temperature_task started");
let mut ticker = Ticker::every(Duration::from_secs(2));
#[cfg(feature = "with-hotend")]
let mut hotend_sm = HeaterStateMachine::new();
#[cfg(feature = "with-hotbed")]
let mut hotbed_sm = HeaterStateMachine::new();
#[cfg(feature = "with-hotend")]
hotend_sm.init(&hotend_controller).await;
#[cfg(feature = "with-hotbed")]
hotbed_sm.init(&hotbed_controller).await;
loop {
ticker.next().await;
#[cfg(feature = "with-hotend")]
hotend_sm.update(&hotend_controller, &event_bus, &defer_channel, EventFlags::HOTEND_TEMP_OK, DeferEvent::HotendTemperature(DeferType::Completed)).await;
#[cfg(feature = "with-hotbed")]
hotbed_sm.update(&hotbed_controller, &event_bus, &defer_channel, EventFlags::HOTBED_TEMP_OK, DeferEvent::HotbedTemperature(DeferType::Completed)).await;
}
}