* PID Autotuning (M303) * * Alternately heat and cool the nozzle, observing its behavior to * determine the best PID values to achieve a stable temperature. * Needs sufficient heater power to make some overshoot at target * temperature to succeed. */
| 811 | * temperature to succeed. |
| 812 | */ |
| 813 | void Temperature::PID_autotune(const celsius_t target, const heater_id_t heater_id, const int8_t ncycles, const bool set_result/*=false*/) { |
| 814 | celsius_float_t current_temp = 0.0; |
| 815 | int cycles = 0; |
| 816 | bool heating = true; |
| 817 | |
| 818 | millis_t next_temp_ms = millis(), t1 = next_temp_ms, t2 = next_temp_ms; |
| 819 | long t_high = 0, t_low = 0; |
| 820 | |
| 821 | raw_pid_t tune_pid = { 0, 0, 0 }; |
| 822 | celsius_float_t maxT = 0, minT = 10000; |
| 823 | |
| 824 | const bool isbed = (heater_id == H_BED), |
| 825 | ischamber = (heater_id == H_CHAMBER); |
| 826 | |
| 827 | #if ENABLED(PIDTEMPCHAMBER) |
| 828 | #define C_TERN(T,A,B) ((T) ? (A) : (B)) |
| 829 | #else |
| 830 | #define C_TERN(T,A,B) (B) |
| 831 | #endif |
| 832 | #if ENABLED(PIDTEMPBED) |
| 833 | #define B_TERN(T,A,B) ((T) ? (A) : (B)) |
| 834 | #else |
| 835 | #define B_TERN(T,A,B) (B) |
| 836 | #endif |
| 837 | #define GHV(C,B,H) C_TERN(ischamber, C, B_TERN(isbed, B, H)) |
| 838 | #define SHV(V) C_TERN(ischamber, temp_chamber.soft_pwm_amount = V, B_TERN(isbed, temp_bed.soft_pwm_amount = V, temp_hotend[heater_id].soft_pwm_amount = V)) |
| 839 | #define ONHEATINGSTART() C_TERN(ischamber, printerEventLEDs.onChamberHeatingStart(), B_TERN(isbed, printerEventLEDs.onBedHeatingStart(), printerEventLEDs.onHotendHeatingStart())) |
| 840 | #define ONHEATING(S,C,T) C_TERN(ischamber, printerEventLEDs.onChamberHeating(S,C,T), B_TERN(isbed, printerEventLEDs.onBedHeating(S,C,T), printerEventLEDs.onHotendHeating(S,C,T))) |
| 841 | |
| 842 | #define WATCH_PID DISABLED(NO_WATCH_PID_TUNING) && (ALL(WATCH_CHAMBER, PIDTEMPCHAMBER) || ALL(WATCH_BED, PIDTEMPBED) || ALL(WATCH_HOTENDS, PIDTEMP)) |
| 843 | |
| 844 | #if WATCH_PID |
| 845 | #if ALL(THERMAL_PROTECTION_CHAMBER, PIDTEMPCHAMBER) |
| 846 | #define C_GTV(T,A,B) ((T) ? (A) : (B)) |
| 847 | #else |
| 848 | #define C_GTV(T,A,B) (B) |
| 849 | #endif |
| 850 | #if ALL(THERMAL_PROTECTION_BED, PIDTEMPBED) |
| 851 | #define B_GTV(T,A,B) ((T) ? (A) : (B)) |
| 852 | #else |
| 853 | #define B_GTV(T,A,B) (B) |
| 854 | #endif |
| 855 | #define GTV(C,B,H) C_GTV(ischamber, C, B_GTV(isbed, B, H)) |
| 856 | const uint16_t watch_temp_period = GTV(WATCH_CHAMBER_TEMP_PERIOD, WATCH_BED_TEMP_PERIOD, WATCH_TEMP_PERIOD); |
| 857 | const uint8_t watch_temp_increase = GTV(WATCH_CHAMBER_TEMP_INCREASE, WATCH_BED_TEMP_INCREASE, WATCH_TEMP_INCREASE); |
| 858 | const celsius_float_t watch_temp_target = celsius_float_t(target - (watch_temp_increase + GTV(TEMP_CHAMBER_HYSTERESIS, TEMP_BED_HYSTERESIS, TEMP_HYSTERESIS) + 1)); |
| 859 | millis_t temp_change_ms = next_temp_ms + SEC_TO_MS(watch_temp_period); |
| 860 | celsius_float_t next_watch_temp = 0.0; |
| 861 | bool heated = false; |
| 862 | #endif |
| 863 | |
| 864 | TERN_(HAS_FAN_LOGIC, fan_update_ms = next_temp_ms + fan_update_interval_ms); |
| 865 | |
| 866 | TERN_(EXTENSIBLE_UI, ExtUI::onPIDTuning(ischamber ? ExtUI::pidresult_t::PID_CHAMBER_STARTED : isbed ? ExtUI::pidresult_t::PID_BED_STARTED : ExtUI::pidresult_t::PID_STARTED)); |
| 867 | |
| 868 | if (target > GHV(CHAMBER_MAX_TARGET, BED_MAX_TARGET, hotend_max_target(heater_id))) { |
| 869 | SERIAL_ECHOPGM(STR_PID_AUTOTUNE); SERIAL_ECHOLNPGM(STR_PID_TEMP_TOO_HIGH); |
| 870 | TERN_(EXTENSIBLE_UI, ExtUI::onPIDTuning(ExtUI::pidresult_t::PID_TEMP_TOO_HIGH)); |
no test coverage detected