From f8de274db3ce9fed12c4c0f94534b4bd78f3f25d Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 14 May 2022 15:07:58 +0200 Subject: [PATCH 01/95] Split fan checks out of temperature.cpp --- Firmware/Marlin.h | 6 - Firmware/Marlin_main.cpp | 1 + Firmware/fancheck.cpp | 298 ++++++++++++++++++++++++++++++++++++ Firmware/fancheck.h | 36 +++++ Firmware/mmu.cpp | 1 + Firmware/planner.cpp | 1 + Firmware/temperature.cpp | 318 +-------------------------------------- Firmware/temperature.h | 37 +---- Firmware/ultralcd.cpp | 2 +- 9 files changed, 349 insertions(+), 351 deletions(-) create mode 100755 Firmware/fancheck.cpp create mode 100755 Firmware/fancheck.h diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index f98e0148c..3e2d67540 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -315,18 +315,12 @@ void homeaxis(uint8_t axis, uint8_t cnt = 1, uint8_t* pstep = 0); void homeaxis(uint8_t axis, uint8_t cnt = 1); #endif //TMC2130 - -#ifdef FAN_SOFT_PWM -extern unsigned char fanSpeedSoftPwm; -#endif - #ifdef FWRETRACT extern bool retracted[EXTRUDERS]; extern float retract_length_swap; extern float retract_recover_length_swap; #endif - extern uint8_t host_keepalive_interval; extern unsigned long starttime; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index e2069d204..2e88c1420 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -72,6 +72,7 @@ #include "planner.h" #include "stepper.h" #include "temperature.h" +#include "fancheck.h" #include "motion_control.h" #include "cardreader.h" #include "ConfigurationStore.h" diff --git a/Firmware/fancheck.cpp b/Firmware/fancheck.cpp new file mode 100755 index 000000000..6a3377408 --- /dev/null +++ b/Firmware/fancheck.cpp @@ -0,0 +1,298 @@ +// fan control and check +#include "fancheck.h" +#include "cardreader.h" +#include "ultralcd.h" +#include "sound.h" +#include "messages.h" +#include "temperature.h" +#include "stepper.h" + +#define FAN_CHECK_PERIOD 5000 //5s +#define FAN_CHECK_DURATION 100 //100ms + +#ifdef FANCHECK +volatile uint8_t fan_check_error = EFCE_OK; +#endif + +#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) + #ifdef EXTRUDER_ALTFAN_DETECT + static struct + { + uint8_t isAltfan : 1; + uint8_t altfanOverride : 1; + } altfanStatus; + #endif //EXTRUDER_ALTFAN_DETECT + + unsigned long extruder_autofan_last_check = _millis(); + bool fan_measuring = false; + static uint8_t fanState = 0; +#endif + +#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) + #if defined(FAN_PIN) && FAN_PIN > -1 + #if EXTRUDER_0_AUTO_FAN_PIN == FAN_PIN + #error "You cannot set EXTRUDER_0_AUTO_FAN_PIN equal to FAN_PIN" + #endif + #endif + +void setExtruderAutoFanState(uint8_t state) +{ + //If bit 1 is set (0x02), then the extruder fan speed won't be adjusted according to temperature. Useful for forcing + //the fan to either On or Off during certain tests/errors. + + fanState = state; + newFanSpeed = 0; + if (fanState & 0x01) + { +#ifdef EXTRUDER_ALTFAN_DETECT + if (altfanStatus.isAltfan && !altfanStatus.altfanOverride) newFanSpeed = EXTRUDER_ALTFAN_SPEED_SILENT; + else newFanSpeed = EXTRUDER_AUTO_FAN_SPEED; +#else //EXTRUDER_ALTFAN_DETECT + newFanSpeed = EXTRUDER_AUTO_FAN_SPEED; +#endif //EXTRUDER_ALTFAN_DETECT + } + timer4_set_fan0(newFanSpeed); +} + +#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1))))) + +void countFanSpeed() +{ + //SERIAL_ECHOPGM("edge counter 1:"); MYSERIAL.println(fan_edge_counter[1]); + fan_speed[0] = (fan_edge_counter[0] * (float(250) / (_millis() - extruder_autofan_last_check))); + fan_speed[1] = (fan_edge_counter[1] * (float(250) / (_millis() - extruder_autofan_last_check))); + /*SERIAL_ECHOPGM("time interval: "); MYSERIAL.println(_millis() - extruder_autofan_last_check); + SERIAL_ECHOPGM("extruder fan speed:"); MYSERIAL.print(fan_speed[0]); SERIAL_ECHOPGM("; edge counter:"); MYSERIAL.println(fan_edge_counter[0]); + SERIAL_ECHOPGM("print fan speed:"); MYSERIAL.print(fan_speed[1]); SERIAL_ECHOPGM("; edge counter:"); MYSERIAL.println(fan_edge_counter[1]); + SERIAL_ECHOLNPGM(" ");*/ + fan_edge_counter[0] = 0; + fan_edge_counter[1] = 0; +} + +//! Prints serialMsg to serial port, displays lcdMsg onto the LCD and beeps. +//! Extracted from fanSpeedError to save some space. +//! @param serialMsg pointer into PROGMEM, this text will be printed to the serial port +//! @param lcdMsg pointer into PROGMEM, this text will be printed onto the LCD +static void fanSpeedErrorBeep(const char *serialMsg, const char *lcdMsg){ + SERIAL_ECHOLNRPGM(serialMsg); + if (get_message_level() == 0) { + Sound_MakeCustom(200,0,true); + LCD_ALERTMESSAGERPGM(lcdMsg); + } +} + +void fanSpeedError(unsigned char _fan) { + if (get_message_level() != 0 && isPrintPaused) return; + //to ensure that target temp. is not set to zero in case that we are resuming print + if (card.sdprinting || usb_timer.running()) { + if (heating_status != HeatingStatus::NO_HEATING) { + lcd_print_stop(); + } + else { + fan_check_error = EFCE_DETECTED; //plans error for next processed command + } + } + else { + // SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); //Why pause octoprint? usb_timer.running() would be true in that case, so there is no need for this. + setTargetHotend0(0); + heating_status = HeatingStatus::NO_HEATING; + fan_check_error = EFCE_REPORTED; + } + switch (_fan) { + case 0: // extracting the same code from case 0 and case 1 into a function saves 72B + fanSpeedErrorBeep(PSTR("Extruder fan speed is lower than expected"), MSG_FANCHECK_EXTRUDER); + break; + case 1: + fanSpeedErrorBeep(PSTR("Print fan speed is lower than expected"), MSG_FANCHECK_PRINT); + break; + } +} + +void checkFanSpeed() +{ + uint8_t max_fan_errors[2]; +#ifdef FAN_SOFT_PWM + max_fan_errors[1] = 3; // 15 seconds (Print fan) + max_fan_errors[0] = 2; // 10 seconds (Extruder fan) +#else //FAN_SOFT_PWM + max_fan_errors[1] = 15; // 15 seconds (Print fan) + max_fan_errors[0] = 5; // 5 seconds (Extruder fan) +#endif //FAN_SOFT_PWM + + if(fans_check_enabled) + fans_check_enabled = (eeprom_read_byte((uint8_t*)EEPROM_FAN_CHECK_ENABLED) > 0); + static uint8_t fan_speed_errors[2] = { 0,0 }; +#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 >-1)) + if ((fan_speed[0] < 20) && (current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE)){ fan_speed_errors[0]++;} + else fan_speed_errors[0] = 0; +#endif +#if (defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) + if ((fan_speed[1] < 5) && ((blocks_queued() ? block_buffer[block_buffer_tail].fan_speed : fanSpeed) > MIN_PRINT_FAN_SPEED)) fan_speed_errors[1]++; + else fan_speed_errors[1] = 0; +#endif + + // drop the fan_check_error flag when both fans are ok + if( fan_speed_errors[0] == 0 && fan_speed_errors[1] == 0 && fan_check_error == EFCE_REPORTED){ + // we may even send some info to the LCD from here + fan_check_error = EFCE_FIXED; + } + if ((fan_check_error == EFCE_FIXED) && !PRINTER_ACTIVE){ + fan_check_error = EFCE_OK; //if the issue is fixed while the printer is doing nothing, reenable processing immediately. + lcd_reset_alert_level(); //for another fan speed error + } + if (fans_check_enabled && (fan_check_error == EFCE_OK)) + { + for (uint8_t fan = 0; fan < 2; fan++) + { + if (fan_speed_errors[fan] > max_fan_errors[fan]) + { + fan_speed_errors[fan] = 0; + fanSpeedError(fan); + } + } + } +} +#endif //(defined(TACH_0) && TACH_0 >-1) || (defined(TACH_1) && TACH_1 > -1) + +#ifdef EXTRUDER_ALTFAN_DETECT +ISR(INT6_vect) { + fan_edge_counter[0]++; +} + +bool extruder_altfan_detect() +{ + setExtruderAutoFanState(3); + + SET_INPUT(TACH_0); + + uint8_t overrideVal = eeprom_read_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE); + if (overrideVal == EEPROM_EMPTY_VALUE) + { + overrideVal = (calibration_status() == CALIBRATION_STATUS_CALIBRATED) ? 1 : 0; + eeprom_update_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE, overrideVal); + } + altfanStatus.altfanOverride = overrideVal; + + CRITICAL_SECTION_START; + EICRB &= ~(1 << ISC61); + EICRB |= (1 << ISC60); + EIMSK |= (1 << INT6); + fan_edge_counter[0] = 0; + CRITICAL_SECTION_END; + extruder_autofan_last_check = _millis(); + + _delay(1000); + + EIMSK &= ~(1 << INT6); + + countFanSpeed(); + altfanStatus.isAltfan = fan_speed[0] > 100; + setExtruderAutoFanState(1); + return altfanStatus.isAltfan; +} + +void altfanOverride_toggle() +{ + altfanStatus.altfanOverride = !altfanStatus.altfanOverride; + eeprom_update_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE, altfanStatus.altfanOverride); +} + +bool altfanOverride_get() +{ + return altfanStatus.altfanOverride; +} + +#endif //EXTRUDER_ALTFAN_DETECT + +void checkExtruderAutoFans() +{ +#if defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1 + if (!(fanState & 0x02)) + { + fanState &= ~1; + fanState |= current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE; + } + setExtruderAutoFanState(fanState); +#endif +} + +#endif // any extruder auto fan pins set + +#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) +void check_fans() { +#ifdef FAN_SOFT_PWM + if (READ(TACH_0) != fan_state[0]) { + if(fan_measuring) fan_edge_counter[0] ++; + fan_state[0] = !fan_state[0]; + } +#else //FAN_SOFT_PWM + if (READ(TACH_0) != fan_state[0]) { + fan_edge_counter[0] ++; + fan_state[0] = !fan_state[0]; + } +#endif + //if (READ(TACH_1) != fan_state[1]) { + // fan_edge_counter[1] ++; + // fan_state[1] = !fan_state[1]; + //} +} +#endif //TACH_0 + +void manageFans() +{ +#ifndef DEBUG_DISABLE_FANCHECK +#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) + +#ifdef FAN_SOFT_PWM +#ifdef FANCHECK + if ((_millis() - extruder_autofan_last_check > FAN_CHECK_PERIOD) && (!fan_measuring)) { + extruder_autofan_last_check = _millis(); + fanSpeedBckp = fanSpeedSoftPwm; + + if (fanSpeedSoftPwm >= MIN_PRINT_FAN_SPEED) { //if we are in rage where we are doing fan check, set full PWM range for a short time to measure fan RPM by reading tacho signal without modulation by PWM signal + // printf_P(PSTR("fanSpeedSoftPwm 1: %d\n"), fanSpeedSoftPwm); + fanSpeedSoftPwm = 255; + } + fan_measuring = true; + } + if ((_millis() - extruder_autofan_last_check > FAN_CHECK_DURATION) && (fan_measuring)) { + countFanSpeed(); + checkFanSpeed(); + //printf_P(PSTR("fanSpeedSoftPwm 1: %d\n"), fanSpeedSoftPwm); + fanSpeedSoftPwm = fanSpeedBckp; + //printf_P(PSTR("fan PWM: %d; extr fanSpeed measured: %d; print fan speed measured: %d \n"), fanSpeedBckp, fan_speed[0], fan_speed[1]); + extruder_autofan_last_check = _millis(); + fan_measuring = false; + } +#endif //FANCHECK + checkExtruderAutoFans(); +#else //FAN_SOFT_PWM + if(_millis() - extruder_autofan_last_check > 1000) // only need to check fan state very infrequently + { +#if (defined(FANCHECK) && ((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))) + countFanSpeed(); + checkFanSpeed(); +#endif //(defined(TACH_0) && TACH_0 >-1) || (defined(TACH_1) && TACH_1 > -1) + checkExtruderAutoFans(); + extruder_autofan_last_check = _millis(); + } +#endif //FAN_SOFT_PWM + +#endif +#endif //DEBUG_DISABLE_FANCHECK +} + +void hotendFanSetFullSpeed() +{ +#ifdef EXTRUDER_ALTFAN_DETECT + altfanStatus.altfanOverride = 1; //full speed +#endif //EXTRUDER_ALTFAN_DETECT + setExtruderAutoFanState(3); + SET_OUTPUT(FAN_PIN); +#ifdef FAN_SOFT_PWM + fanSpeedSoftPwm = 255; +#else //FAN_SOFT_PWM + analogWrite(FAN_PIN, 255); +#endif //FAN_SOFT_PWM + fanSpeed = 255; +} diff --git a/Firmware/fancheck.h b/Firmware/fancheck.h new file mode 100755 index 000000000..6f47efbdb --- /dev/null +++ b/Firmware/fancheck.h @@ -0,0 +1,36 @@ +// fan control and check +#pragma once + +#include "Configuration.h" +#include "config.h" + +#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) +enum { + EFCE_OK = 0, //!< normal operation, both fans are ok + EFCE_FIXED, //!< previous fan error was fixed + EFCE_DETECTED, //!< fan error detected, but not reported yet + EFCE_REPORTED //!< fan error detected and reported to LCD and serial +}; +extern volatile uint8_t fan_check_error; + +void check_fans(); +#endif //(defined(TACH_0)) + +#ifdef EXTRUDER_ALTFAN_DETECT +extern bool extruder_altfan_detect(); +extern void altfanOverride_toggle(); +extern bool altfanOverride_get(); +#endif //EXTRUDER_ALTFAN_DETECT + +#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) +#ifdef FAN_SOFT_PWM +extern bool fan_measuring; +#endif //FAN_SOFT_PWM + +extern unsigned long extruder_autofan_last_check; +void setExtruderAutoFanState(uint8_t state); +void checkExtruderAutoFans(); +#endif + +void manageFans(); +void hotendFanSetFullSpeed(); diff --git a/Firmware/mmu.cpp b/Firmware/mmu.cpp index 3da5c54eb..2ad15375c 100755 --- a/Firmware/mmu.cpp +++ b/Firmware/mmu.cpp @@ -10,6 +10,7 @@ #include "fsensor.h" #include "cardreader.h" #include "cmdqueue.h" +#include "stepper.h" #include "ultralcd.h" #include "menu.h" #include "sound.h" diff --git a/Firmware/planner.cpp b/Firmware/planner.cpp index 16f4dfee8..12641bbf4 100644 --- a/Firmware/planner.cpp +++ b/Firmware/planner.cpp @@ -55,6 +55,7 @@ #include "planner.h" #include "stepper.h" #include "temperature.h" +#include "fancheck.h" #include "ultralcd.h" #include "language.h" #include "ConfigurationStore.h" diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 6eea8de87..382af4bc1 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -28,27 +28,21 @@ */ - -#include "Marlin.h" -#include "cmdqueue.h" +#include "temperature.h" +#include "stepper.h" #include "ultralcd.h" #include "menu.h" -#include "conv2str.h" #include "sound.h" -#include "temperature.h" -#include "cardreader.h" +#include "fancheck.h" #include "SdFatUtil.h" #include #include "adc.h" #include "ConfigurationStore.h" -#include "messages.h" #include "Timer.h" #include "Configuration_prusa.h" -#include "config.h" - //=========================================================================== //=============================public variables============================ //=========================================================================== @@ -90,14 +84,6 @@ float current_temperature_bed = 0.0; bool pid_tuning_finished = false; #endif //PIDTEMP -#ifdef FAN_SOFT_PWM - unsigned char fanSpeedSoftPwm; -#endif - -#ifdef FANCHECK - volatile uint8_t fan_check_error = EFCE_OK; -#endif - unsigned char soft_pwm_bed; #ifdef BABYSTEPPING @@ -141,26 +127,11 @@ static volatile bool temp_meas_ready = false; static unsigned char soft_pwm[EXTRUDERS]; #ifdef FAN_SOFT_PWM + unsigned char fanSpeedSoftPwm; static unsigned char soft_pwm_fan; #endif - uint8_t fanSpeedBckp = 255; -#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) - unsigned long extruder_autofan_last_check = _millis(); - - bool fan_measuring = false; - uint8_t fanState = 0; -#ifdef EXTRUDER_ALTFAN_DETECT - struct - { - uint8_t isAltfan : 1; - uint8_t altfanOverride : 1; - } altfanStatus; -#endif //EXTRUDER_ALTFAN_DETECT -#endif - - #if EXTRUDERS > 3 # error Unsupported number of extruders #elif EXTRUDERS > 2 @@ -226,56 +197,6 @@ static void temp_runaway_check(uint8_t _heater_id, float _target_temperature, fl static void temp_runaway_stop(bool isPreheat, bool isBed); #endif -#ifdef EXTRUDER_ALTFAN_DETECT -ISR(INT6_vect) { - fan_edge_counter[0]++; -} - -bool extruder_altfan_detect() -{ - setExtruderAutoFanState(3); - - SET_INPUT(TACH_0); - - uint8_t overrideVal = eeprom_read_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE); - if (overrideVal == EEPROM_EMPTY_VALUE) - { - overrideVal = (calibration_status() == CALIBRATION_STATUS_CALIBRATED) ? 1 : 0; - eeprom_update_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE, overrideVal); - } - altfanStatus.altfanOverride = overrideVal; - - CRITICAL_SECTION_START; - EICRB &= ~(1 << ISC61); - EICRB |= (1 << ISC60); - EIMSK |= (1 << INT6); - fan_edge_counter[0] = 0; - CRITICAL_SECTION_END; - extruder_autofan_last_check = _millis(); - - _delay(1000); - - EIMSK &= ~(1 << INT6); - - countFanSpeed(); - altfanStatus.isAltfan = fan_speed[0] > 100; - setExtruderAutoFanState(1); - return altfanStatus.isAltfan; -} - -void altfanOverride_toggle() -{ - altfanStatus.altfanOverride = !altfanStatus.altfanOverride; - eeprom_update_byte((uint8_t *)EEPROM_ALTFAN_OVERRIDE, altfanStatus.altfanOverride); -} - -bool altfanOverride_get() -{ - return altfanStatus.altfanOverride; -} - -#endif //EXTRUDER_ALTFAN_DETECT - // return "false", if all extruder-heaters are 'off' (ie. "true", if any heater is 'on') bool checkAllHotends(void) { @@ -511,148 +432,6 @@ int getHeaterPower(int heater) { return soft_pwm[heater]; } -#if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) - - #if defined(FAN_PIN) && FAN_PIN > -1 - #if EXTRUDER_0_AUTO_FAN_PIN == FAN_PIN - #error "You cannot set EXTRUDER_0_AUTO_FAN_PIN equal to FAN_PIN" - #endif - #endif - -void setExtruderAutoFanState(uint8_t state) -{ - //If bit 1 is set (0x02), then the extruder fan speed won't be adjusted according to temperature. Useful for forcing - //the fan to either On or Off during certain tests/errors. - - fanState = state; - newFanSpeed = 0; - if (fanState & 0x01) - { -#ifdef EXTRUDER_ALTFAN_DETECT - if (altfanStatus.isAltfan && !altfanStatus.altfanOverride) newFanSpeed = EXTRUDER_ALTFAN_SPEED_SILENT; - else newFanSpeed = EXTRUDER_AUTO_FAN_SPEED; -#else //EXTRUDER_ALTFAN_DETECT - newFanSpeed = EXTRUDER_AUTO_FAN_SPEED; -#endif //EXTRUDER_ALTFAN_DETECT - } - timer4_set_fan0(newFanSpeed); -} - -#if (defined(FANCHECK) && (((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1))))) - -void countFanSpeed() -{ - //SERIAL_ECHOPGM("edge counter 1:"); MYSERIAL.println(fan_edge_counter[1]); - fan_speed[0] = (fan_edge_counter[0] * (float(250) / (_millis() - extruder_autofan_last_check))); - fan_speed[1] = (fan_edge_counter[1] * (float(250) / (_millis() - extruder_autofan_last_check))); - /*SERIAL_ECHOPGM("time interval: "); MYSERIAL.println(_millis() - extruder_autofan_last_check); - SERIAL_ECHOPGM("extruder fan speed:"); MYSERIAL.print(fan_speed[0]); SERIAL_ECHOPGM("; edge counter:"); MYSERIAL.println(fan_edge_counter[0]); - SERIAL_ECHOPGM("print fan speed:"); MYSERIAL.print(fan_speed[1]); SERIAL_ECHOPGM("; edge counter:"); MYSERIAL.println(fan_edge_counter[1]); - SERIAL_ECHOLNPGM(" ");*/ - fan_edge_counter[0] = 0; - fan_edge_counter[1] = 0; -} - -void checkFanSpeed() -{ - uint8_t max_fan_errors[2]; -#ifdef FAN_SOFT_PWM - max_fan_errors[1] = 3; // 15 seconds (Print fan) - max_fan_errors[0] = 2; // 10 seconds (Extruder fan) -#else //FAN_SOFT_PWM - max_fan_errors[1] = 15; // 15 seconds (Print fan) - max_fan_errors[0] = 5; // 5 seconds (Extruder fan) -#endif //FAN_SOFT_PWM - - if(fans_check_enabled) - fans_check_enabled = (eeprom_read_byte((uint8_t*)EEPROM_FAN_CHECK_ENABLED) > 0); - static uint8_t fan_speed_errors[2] = { 0,0 }; -#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 >-1)) - if ((fan_speed[0] < 20) && (current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE)){ fan_speed_errors[0]++;} - else fan_speed_errors[0] = 0; -#endif -#if (defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) - if ((fan_speed[1] < 5) && ((blocks_queued() ? block_buffer[block_buffer_tail].fan_speed : fanSpeed) > MIN_PRINT_FAN_SPEED)) fan_speed_errors[1]++; - else fan_speed_errors[1] = 0; -#endif - - // drop the fan_check_error flag when both fans are ok - if( fan_speed_errors[0] == 0 && fan_speed_errors[1] == 0 && fan_check_error == EFCE_REPORTED){ - // we may even send some info to the LCD from here - fan_check_error = EFCE_FIXED; - } - if ((fan_check_error == EFCE_FIXED) && !PRINTER_ACTIVE){ - fan_check_error = EFCE_OK; //if the issue is fixed while the printer is doing nothing, reenable processing immediately. - lcd_reset_alert_level(); //for another fan speed error - } - if (fans_check_enabled && (fan_check_error == EFCE_OK)) - { - for (uint8_t fan = 0; fan < 2; fan++) - { - if (fan_speed_errors[fan] > max_fan_errors[fan]) - { - fan_speed_errors[fan] = 0; - fanSpeedError(fan); - } - } - } -} - -//! Prints serialMsg to serial port, displays lcdMsg onto the LCD and beeps. -//! Extracted from fanSpeedError to save some space. -//! @param serialMsg pointer into PROGMEM, this text will be printed to the serial port -//! @param lcdMsg pointer into PROGMEM, this text will be printed onto the LCD -static void fanSpeedErrorBeep(const char *serialMsg, const char *lcdMsg){ - SERIAL_ECHOLNRPGM(serialMsg); - if (get_message_level() == 0) { - Sound_MakeCustom(200,0,true); - LCD_ALERTMESSAGERPGM(lcdMsg); - } -} - -void fanSpeedError(unsigned char _fan) { - if (get_message_level() != 0 && isPrintPaused) return; - //to ensure that target temp. is not set to zero in case that we are resuming print - if (card.sdprinting || usb_timer.running()) { - if (heating_status != HeatingStatus::NO_HEATING) { - lcd_print_stop(); - } - else { - fan_check_error = EFCE_DETECTED; //plans error for next processed command - } - } - else { - // SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); //Why pause octoprint? usb_timer.running() would be true in that case, so there is no need for this. - setTargetHotend0(0); - heating_status = HeatingStatus::NO_HEATING; - fan_check_error = EFCE_REPORTED; - } - switch (_fan) { - case 0: // extracting the same code from case 0 and case 1 into a function saves 72B - fanSpeedErrorBeep(PSTR("Extruder fan speed is lower than expected"), MSG_FANCHECK_EXTRUDER); - break; - case 1: - fanSpeedErrorBeep(PSTR("Print fan speed is lower than expected"), MSG_FANCHECK_PRINT); - break; - } -} -#endif //(defined(TACH_0) && TACH_0 >-1) || (defined(TACH_1) && TACH_1 > -1) - - -void checkExtruderAutoFans() -{ -#if defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1 - if (!(fanState & 0x02)) - { - fanState &= ~1; - fanState |= current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE; - } - setExtruderAutoFanState(fanState); -#endif -} - -#endif // any extruder auto fan pins set - // ready for eventually parameters adjusting void resetPID(uint8_t) // only for compiler-warning elimination (if function do nothing) //void resetPID(uint8_t extruder) @@ -765,50 +544,8 @@ void manage_heater() } } // End extruder for loop -#define FAN_CHECK_PERIOD 5000 //5s -#define FAN_CHECK_DURATION 100 //100ms + manageFans(); -#ifndef DEBUG_DISABLE_FANCHECK - #if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) - -#ifdef FAN_SOFT_PWM -#ifdef FANCHECK - if ((_millis() - extruder_autofan_last_check > FAN_CHECK_PERIOD) && (!fan_measuring)) { - extruder_autofan_last_check = _millis(); - fanSpeedBckp = fanSpeedSoftPwm; - - if (fanSpeedSoftPwm >= MIN_PRINT_FAN_SPEED) { //if we are in rage where we are doing fan check, set full PWM range for a short time to measure fan RPM by reading tacho signal without modulation by PWM signal - // printf_P(PSTR("fanSpeedSoftPwm 1: %d\n"), fanSpeedSoftPwm); - fanSpeedSoftPwm = 255; - } - fan_measuring = true; - } - if ((_millis() - extruder_autofan_last_check > FAN_CHECK_DURATION) && (fan_measuring)) { - countFanSpeed(); - checkFanSpeed(); - //printf_P(PSTR("fanSpeedSoftPwm 1: %d\n"), fanSpeedSoftPwm); - fanSpeedSoftPwm = fanSpeedBckp; - //printf_P(PSTR("fan PWM: %d; extr fanSpeed measured: %d; print fan speed measured: %d \n"), fanSpeedBckp, fan_speed[0], fan_speed[1]); - extruder_autofan_last_check = _millis(); - fan_measuring = false; - } -#endif //FANCHECK - checkExtruderAutoFans(); -#else //FAN_SOFT_PWM - if(_millis() - extruder_autofan_last_check > 1000) // only need to check fan state very infrequently - { -#if (defined(FANCHECK) && ((defined(TACH_0) && (TACH_0 >-1)) || (defined(TACH_1) && (TACH_1 > -1)))) - countFanSpeed(); - checkFanSpeed(); -#endif //(defined(TACH_0) && TACH_0 >-1) || (defined(TACH_1) && TACH_1 > -1) - checkExtruderAutoFans(); - extruder_autofan_last_check = _millis(); - } -#endif //FAN_SOFT_PWM - - #endif -#endif //DEBUG_DISABLE_FANCHECK - #ifndef PIDTEMPBED if(_millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) return; @@ -1389,17 +1126,7 @@ void temp_runaway_stop(bool isPreheat, bool isBed) SERIAL_ERROR_START; isBed ? SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HEATBED)") : SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HOTEND)"); -#ifdef EXTRUDER_ALTFAN_DETECT - altfanStatus.altfanOverride = 1; //full speed -#endif //EXTRUDER_ALTFAN_DETECT - setExtruderAutoFanState(3); - SET_OUTPUT(FAN_PIN); -#ifdef FAN_SOFT_PWM - fanSpeedSoftPwm = 255; -#else //FAN_SOFT_PWM - analogWrite(FAN_PIN, 255); -#endif //FAN_SOFT_PWM - fanSpeed = 255; + hotendFanSetFullSpeed(); } else { @@ -1501,17 +1228,8 @@ void max_temp_error(uint8_t e) { Stop(); #endif - SET_OUTPUT(FAN_PIN); - SET_OUTPUT(BEEPER); - WRITE(FAN_PIN, 1); - WRITE(BEEPER, 1); -#ifdef EXTRUDER_ALTFAN_DETECT - altfanStatus.altfanOverride = 1; //full speed -#endif //EXTRUDER_ALTFAN_DETECT - setExtruderAutoFanState(3); - // fanSpeed will consumed by the check_axes_activity() routine. - fanSpeed=255; - if (farm_mode) { prusa_statistics(93); } + hotendFanSetFullSpeed(); + if (farm_mode) { prusa_statistics(93); } } void min_temp_error(uint8_t e) { @@ -2271,26 +1989,6 @@ else { // ambient temperature is stan #endif //AMBIENT_THERMISTOR } -#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) -void check_fans() { -#ifdef FAN_SOFT_PWM - if (READ(TACH_0) != fan_state[0]) { - if(fan_measuring) fan_edge_counter[0] ++; - fan_state[0] = !fan_state[0]; - } -#else //FAN_SOFT_PWM - if (READ(TACH_0) != fan_state[0]) { - fan_edge_counter[0] ++; - fan_state[0] = !fan_state[0]; - } -#endif - //if (READ(TACH_1) != fan_state[1]) { - // fan_edge_counter[1] ++; - // fan_state[1] = !fan_state[1]; - //} -} -#endif //TACH_0 - #ifdef PIDTEMP // Apply the scale factors to the PID values diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 3dd1c999a..ee94d04c7 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -22,10 +22,6 @@ #define temperature_h #include "Marlin.h" -#include "planner.h" - -#include "stepper.h" - #include "config.h" @@ -235,39 +231,12 @@ FORCE_INLINE void autotempShutdown(){ void PID_autotune(float temp, int extruder, int ncycles); -void setExtruderAutoFanState(uint8_t state); -void checkExtruderAutoFans(); - - -#if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) - -enum { - EFCE_OK = 0, //!< normal operation, both fans are ok - EFCE_FIXED, //!< previous fan error was fixed - EFCE_DETECTED, //!< fan error detected, but not reported yet - EFCE_REPORTED //!< fan error detected and reported to LCD and serial -}; -extern volatile uint8_t fan_check_error; - -void countFanSpeed(); -void checkFanSpeed(); -void fanSpeedError(unsigned char _fan); - -void check_fans(); - -#endif //(defined(TACH_0)) - void check_min_temp(); void check_max_temp(); -#ifdef EXTRUDER_ALTFAN_DETECT - extern bool extruder_altfan_detect(); - extern void altfanOverride_toggle(); - extern bool altfanOverride_get(); -#endif //EXTRUDER_ALTFAN_DETECT - -extern unsigned long extruder_autofan_last_check; +#ifdef FAN_SOFT_PWM +extern unsigned char fanSpeedSoftPwm; +#endif extern uint8_t fanSpeedBckp; -extern bool fan_measuring; #endif diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index 3ed89f9f4..cb7e20f29 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -10,7 +10,7 @@ #include "Marlin.h" #include "language.h" #include "cardreader.h" -#include "temperature.h" +#include "fancheck.h" #include "stepper.h" #include "ConfigurationStore.h" #include "printers.h" From 13163e9fbf3e4e567256a654153f1b6165c8465a Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 14 May 2022 17:41:17 +0200 Subject: [PATCH 02/95] Move millis_nc to system_timer.h --- Firmware/Marlin.h | 21 --------------------- Firmware/system_timer.h | 21 +++++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 3e2d67540..26a892636 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -255,27 +255,6 @@ void prepare_arc_move(bool isclockwise); void clamp_to_software_endstops(float target[3]); void refresh_cmd_timeout(void); -// Timer counter, incremented by the 1ms Arduino timer. -// The standard Arduino timer() function returns this value atomically -// by disabling / enabling interrupts. This is costly, if the interrupts are known -// to be disabled. -#ifdef SYSTEM_TIMER_2 -extern volatile unsigned long timer2_millis; -#else //SYSTEM_TIMER_2 -extern volatile unsigned long timer0_millis; -#endif //SYSTEM_TIMER_2 - -// An unsynchronized equivalent to a standard Arduino _millis() function. -// To be used inside an interrupt routine. - -FORCE_INLINE unsigned long millis_nc() { -#ifdef SYSTEM_TIMER_2 - return timer2_millis; -#else //SYSTEM_TIMER_2 - return timer0_millis; -#endif //SYSTEM_TIMER_2 -} - #ifdef FAST_PWM_FAN void setPwmFrequency(uint8_t pin, int val); #endif diff --git a/Firmware/system_timer.h b/Firmware/system_timer.h index 9906460ae..d8ef8a266 100644 --- a/Firmware/system_timer.h +++ b/Firmware/system_timer.h @@ -4,6 +4,7 @@ #define FIRMWARE_SYSTEM_TIMER_H_ #include "Arduino.h" +#include "macros.h" #define SYSTEM_TIMER_2 #ifdef SYSTEM_TIMER_2 @@ -26,4 +27,24 @@ #define timer02_set_pwm0(pwm0) #endif //SYSTEM_TIMER_2 +// Timer counter, incremented by the 1ms Arduino timer. +// The standard Arduino timer() function returns this value atomically +// by disabling / enabling interrupts. This is costly, if the interrupts are known +// to be disabled. +#ifdef SYSTEM_TIMER_2 +extern volatile unsigned long timer2_millis; +#else //SYSTEM_TIMER_2 +extern volatile unsigned long timer0_millis; +#endif //SYSTEM_TIMER_2 + +// An unsynchronized equivalent to a standard Arduino _millis() function. +// To be used inside an interrupt routine. +FORCE_INLINE unsigned long millis_nc() { +#ifdef SYSTEM_TIMER_2 + return timer2_millis; +#else //SYSTEM_TIMER_2 + return timer0_millis; +#endif //SYSTEM_TIMER_2 +} + #endif /* FIRMWARE_SYSTEM_TIMER_H_ */ From 306f77ccbd8a6f323def888e7ecf6c84fb2953a0 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 14 May 2022 18:02:31 +0200 Subject: [PATCH 03/95] Disable IR_SENSOR due to the injected ADC read for now This is already reimplemented in the newer fsensor implementation --- Firmware/config.h | 3 ++- Firmware/fsensor.cpp | 58 ++------------------------------------------ 2 files changed, 4 insertions(+), 57 deletions(-) diff --git a/Firmware/config.h b/Firmware/config.h index acd6cb81f..d23132bd6 100644 --- a/Firmware/config.h +++ b/Firmware/config.h @@ -6,7 +6,8 @@ #include "pins.h" #if (defined(VOLT_IR_PIN) && defined(IR_SENSOR)) -# define IR_SENSOR_ANALOG +// TODO: IR_SENSOR_ANALOG currently disabled as being incompatible with the new thermal regulation +// # define IR_SENSOR_ANALOG #endif //ADC configuration diff --git a/Firmware/fsensor.cpp b/Firmware/fsensor.cpp index 3eab45383..85728001c 100755 --- a/Firmware/fsensor.cpp +++ b/Firmware/fsensor.cpp @@ -688,62 +688,8 @@ void fsensor_update(void) { if (READ(IR_SENSOR_PIN)) { // IR_SENSOR_PIN ~ H -#ifdef IR_SENSOR_ANALOG - if(!bIRsensorStateFlag) - { - bIRsensorStateFlag=true; - tIRsensorCheckTimer.start(); - } - else - { - if(tIRsensorCheckTimer.expired(IR_SENSOR_STEADY)) - { - uint8_t nMUX1,nMUX2; - uint16_t nADC; - bIRsensorStateFlag=false; - // sequence for direct data reading from AD converter - DISABLE_TEMPERATURE_INTERRUPT(); - nMUX1=ADMUX; // ADMUX saving - nMUX2=ADCSRB; - adc_setmux(VOLT_IR_PIN); - ADCSRA|=(1<4.6V - // If it does, it means a disconnected cables or faulty board - if( (oFsensorPCB == ClFsensorPCB::_Rev04) && ( (nADC*OVERSAMPLENR) > IRsensor_Hopen_TRESHOLD ) ) - { - fsensor_disable(); - fsensor_not_responding = true; - printf_P(PSTR("IR sensor not responding (%d)!\n"),1); - if((ClFsensorActionNA)eeprom_read_byte((uint8_t*)EEPROM_FSENSOR_ACTION_NA)==ClFsensorActionNA::_Pause) - - // if we are printing and FS action is set to "Pause", force pause the print - if(oFsensorActionNA==ClFsensorActionNA::_Pause) - lcd_pause_print(); - } - else - { -#endif //IR_SENSOR_ANALOG - fsensor_checkpoint_print(); - fsensor_enque_M600(); -#ifdef IR_SENSOR_ANALOG - } - } - } - } - else - { // IR_SENSOR_PIN ~ L - bIRsensorStateFlag=false; -#endif //IR_SENSOR_ANALOG + fsensor_checkpoint_print(); + fsensor_enque_M600(); } } #endif //PAT9125 From 82e221e3c759fc6fec4d2034b487ab446f6fe798 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 4 Jul 2022 22:34:34 +0200 Subject: [PATCH 04/95] Remove ADC pullup checks Setting pullups on the ADC should trigger the model-based check, making this redundant and wasteful. Keep the DEBUG_PULLUP_CRASH menu so that we can verify this behavior in the future. --- Firmware/Marlin.h | 1 - Firmware/Marlin_main.cpp | 4 ---- Firmware/stepper.cpp | 9 --------- Firmware/temperature.cpp | 10 ---------- 4 files changed, 24 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 26a892636..0e2427f97 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -477,7 +477,6 @@ void raise_z_above(float target, bool plan=true); extern "C" void softReset(); void stack_error(); -void pullup_error(bool fromTempISR); extern uint32_t IP_address; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 2e88c1420..2fc467d3e 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1693,10 +1693,6 @@ void stack_error() { crash_and_burn(dump_crash_reason::stack_error); } -void pullup_error(bool fromTempISR) { - crash_and_burn(fromTempISR ? dump_crash_reason::bad_pullup_temp_isr : dump_crash_reason::bad_pullup_step_isr); -} - #ifdef PRUSA_M28 void trace(); diff --git a/Firmware/stepper.cpp b/Firmware/stepper.cpp index 34fc19ad2..0b221911d 100644 --- a/Firmware/stepper.cpp +++ b/Firmware/stepper.cpp @@ -287,15 +287,6 @@ ISR(TIMER1_COMPA_vect) { if (sp < SP_min) SP_min = sp; #endif //DEBUG_STACK_MONITOR -#ifdef DEBUG_PULLUP_CRASH - // check for faulty pull-ups enabled on thermistor inputs - if ((PORTF & (uint8_t)(ADC_DIDR_MSK & 0xff)) || (PORTK & (uint8_t)((ADC_DIDR_MSK >> 8) & 0xff))) - pullup_error(false); -#else - PORTF &= ~(uint8_t)(ADC_DIDR_MSK & 0xff); - PORTK &= ~(uint8_t)((ADC_DIDR_MSK >> 8) & 0xff); -#endif // DEBUG_PULLUP_CRASH - #ifdef LIN_ADVANCE advance_isr_scheduler(); #else diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 382af4bc1..c5012a852 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1400,16 +1400,6 @@ void adc_ready(void) //callback from adc when sampling finished FORCE_INLINE static void temperature_isr() { -#ifdef DEBUG_PULLUP_CRASH - // check for faulty pull-ups enabled on thermistor inputs - if ((PORTF & (uint8_t)(ADC_DIDR_MSK & 0xff)) || (PORTK & (uint8_t)((ADC_DIDR_MSK >> 8) & 0xff))) - pullup_error(true); -#else - PORTF &= ~(uint8_t)(ADC_DIDR_MSK & 0xff); - PORTK &= ~(uint8_t)((ADC_DIDR_MSK >> 8) & 0xff); -#endif // DEBUG_PULLUP_CRASH - - if (!temp_meas_ready) adc_cycle(); lcd_buttons_update(); From 2535d072c414fdcfda5bcc87308a8e793a89cc21 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 14 May 2022 18:03:43 +0200 Subject: [PATCH 05/95] Do not recursively enter temperature_isr Disable the interrupt source instead, which avoids the added latency of reentering the isr in the first place. --- Firmware/temperature.cpp | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index c5012a852..7fe7166c9 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1777,15 +1777,11 @@ ISR(TIMER2_COMPB_vect) ISR(TIMER0_COMPB_vect) #endif //SYSTEM_TIMER_2 { - static bool _lock = false; - if (!_lock) - { - _lock = true; - sei(); - temperature_isr(); - cli(); - _lock = false; - } + DISABLE_TEMPERATURE_INTERRUPT(); + sei(); + temperature_isr(); + cli(); + ENABLE_TEMPERATURE_INTERRUPT(); } void check_max_temp() From e87188e7e3be314c269914b5aab5f32fa176458a Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 00:35:59 +0200 Subject: [PATCH 06/95] Ensure ADC and thermistor tables use the same oversampling The current code assumes that values are directly comparable --- Firmware/temperature.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 7fe7166c9..882ffcbb5 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -43,6 +43,10 @@ #include "Timer.h" #include "Configuration_prusa.h" +#if (ADC_OVRSAMPL != OVERSAMPLENR) +#error "ADC_OVRSAMPL oversampling must match OVERSAMPLENR" +#endif + //=========================================================================== //=============================public variables============================ //=========================================================================== From 16b9acf8bc8a202ed71d32d3b88968bec398a729 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 00:45:18 +0200 Subject: [PATCH 07/95] Decouple temperature ISR from ADC readings Read from ADC as fast as possible using the ADC interrupt to get more accurate instantaneous readings. Decouple the temperature_isr from the adc reading interval, so that the two can run independently for future use. --- Firmware/Dcodes.cpp | 2 + Firmware/adc.c | 95 ---------------------------------------- Firmware/adc.cpp | 81 ++++++++++++++++++++++++++++++++++ Firmware/adc.h | 34 +++----------- Firmware/temperature.cpp | 40 ++++++++--------- 5 files changed, 108 insertions(+), 144 deletions(-) delete mode 100644 Firmware/adc.c create mode 100644 Firmware/adc.cpp diff --git a/Firmware/Dcodes.cpp b/Firmware/Dcodes.cpp index 4a51b4d74..5a21328fe 100644 --- a/Firmware/Dcodes.cpp +++ b/Firmware/Dcodes.cpp @@ -573,6 +573,7 @@ void dcode_9() for (uint8_t i = 0; i < ADC_CHAN_CNT; i++) printf_P(PSTR("\tADC%d=%4d\t(%S)\n"), i, dcode_9_ADC_val(i) >> 4, dcode_9_ADC_name(i)); } +#if 0 else { uint8_t index = 0xff; @@ -588,6 +589,7 @@ void dcode_9() } } } +#endif } /*! diff --git a/Firmware/adc.c b/Firmware/adc.c deleted file mode 100644 index b41e58f69..000000000 --- a/Firmware/adc.c +++ /dev/null @@ -1,95 +0,0 @@ -//adc.c - -#include "adc.h" -#include -#include -#include -#include "pins.h" - -uint8_t adc_state; -uint8_t adc_count; -uint16_t adc_values[ADC_CHAN_CNT]; -uint16_t adc_sim_mask; - - -#ifdef ADC_CALLBACK - extern void ADC_CALLBACK(void); -#endif //ADC_CALLBACK - - -void adc_init(void) -{ - puts_P(PSTR("adc_init")); - adc_sim_mask = 0x00; - ADCSRA |= (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0); - ADMUX |= (1 << REFS0); - ADCSRA |= (1 << ADEN); -// ADCSRA |= (1 << ADIF) | (1 << ADSC); - DIDR0 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) & 0xff); - DIDR2 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) >> 8); - adc_reset(); -// adc_sim_mask = 0b0101; -// adc_sim_mask = 0b100101; -// adc_values[0] = 1023 * 16; -// adc_values[2] = 1023 * 16; -// adc_values[5] = 1002 * 16; -} - -void adc_reset(void) -{ - adc_state = 0; - adc_count = 0; - uint8_t i; for (i = 0; i < ADC_CHAN_CNT; i++) - if ((adc_sim_mask & (1 << i)) == 0) - adc_values[i] = 0; -} - -void adc_setmux(uint8_t ch) -{ - ch &= 0x0f; - if (ch & 0x08) ADCSRB |= (1 << MUX5); - else ADCSRB &= ~(1 << MUX5); - ADMUX = (ADMUX & ~(0x07)) | (ch & 0x07); -} - -uint8_t adc_chan(uint8_t index) -{ - uint8_t chan = 0; - uint16_t mask = 1; - while (mask) - { - if ((mask & ADC_CHAN_MSK) && (index-- == 0)) break; - mask <<= 1; - chan++; - } - return chan; -} - -void adc_cycle(void) -{ - if (adc_state & 0x80) - { - uint8_t index = adc_state & 0x0f; - if ((adc_sim_mask & (1 << index)) == 0) - adc_values[index] += ADC; - if (++index >= ADC_CHAN_CNT) - { - index = 0; - adc_count++; - if (adc_count >= ADC_OVRSAMPL) - { -#ifdef ADC_CALLBACK - ADC_CALLBACK(); -#endif //ADC_CALLBACK - adc_reset(); - } - } - adc_setmux(adc_chan(index)); - adc_state = index; - } - else - { - ADCSRA |= (1 << ADSC); //start conversion - adc_state |= 0x80; - } -} diff --git a/Firmware/adc.cpp b/Firmware/adc.cpp new file mode 100644 index 000000000..7503fdcba --- /dev/null +++ b/Firmware/adc.cpp @@ -0,0 +1,81 @@ +#include "adc.h" +#include +#include +#include +#include +#include +#include "pins.h" + +static uint8_t adc_count; //used for oversampling +static uint8_t adc_channel_idx; //bitmask index +volatile uint8_t adc_channel; //regular index +volatile uint16_t adc_values[ADC_CHAN_CNT]; + +static void adc_reset(); +static void adc_setmux(uint8_t ch); + +void adc_init() +{ + puts_P(PSTR("adc_init")); + DIDR0 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) & 0xff); //disable digital inputs PORTF + DIDR2 = ((ADC_CHAN_MSK & ADC_DIDR_MSK) >> 8); //disable digital inputs PORTK + ADMUX |= (1 << REFS0); //use AVCC as reference + + //enable ADC, set prescaler/128, enable interrupt + ADCSRA = (1 << ADEN) | (1 << ADPS2) | (1 << ADPS1) | (1 << ADPS0) | (1 << ADIF) | (1 << ADIE); +} + +static void adc_reset() +{ + static const uint8_t first_channel_idx = 0; + static_assert((1 << first_channel_idx) & ADC_CHAN_MSK); + + ADCSRA &= ~(1 << ADSC); //stop conversion just in case + adc_count = 0; + adc_channel = 0; + adc_channel_idx = first_channel_idx; + adc_setmux(adc_channel_idx); + memset((void*)adc_values, 0, sizeof(adc_values)); +} + +static void adc_setmux(uint8_t ch) +{ + ch &= 0x0f; + if (ch & 0x08) ADCSRB |= (1 << MUX5); + else ADCSRB &= ~(1 << MUX5); + ADMUX = (ADMUX & ~(0x07)) | (ch & 0x07); +} + +void adc_start_cycle() { + adc_reset(); + ADCSRA |= (1 << ADSC); //start conversion +} + +#ifdef ADC_CALLBACK +extern void ADC_CALLBACK(); +#endif //ADC_CALLBACK + +ISR(ADC_vect) +{ + adc_values[adc_channel] += ADC; + if (++adc_count == ADC_OVRSAMPL) + { + // go to the next channel + if (++adc_channel == ADC_CHAN_CNT) { +#ifdef ADC_CALLBACK + ADC_CALLBACK(); +#endif + return; // do not start the next measurement since there are no channels remaining + } + + // find the next channel + while (++adc_channel_idx) { + if (ADC_CHAN_MSK & (1 << adc_channel_idx)) { + adc_setmux(adc_channel_idx); + adc_count = 0; + break; + } + } + } + ADCSRA |= (1 << ADSC); //start conversion +} diff --git a/Firmware/adc.h b/Firmware/adc.h index 9ff137dfd..bece541e8 100644 --- a/Firmware/adc.h +++ b/Firmware/adc.h @@ -1,15 +1,8 @@ -//adc.h -#ifndef _ADC_H -#define _ADC_H +#pragma once #include #include "config.h" - -#if defined(__cplusplus) -extern "C" { -#endif //defined(__cplusplus) - /* http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html */ @@ -22,24 +15,9 @@ http://resnet.uoregon.edu/~gurney_j/jmpc/bitwise.html # error "ADC_CHAN_MSK oes not match ADC_CHAN_CNT" #endif -extern uint8_t adc_state; -extern uint8_t adc_count; -extern uint16_t adc_values[ADC_CHAN_CNT]; -extern uint16_t adc_sim_mask; +extern volatile uint8_t adc_channel; +extern volatile uint16_t adc_values[ADC_CHAN_CNT]; - -extern void adc_init(void); - -extern void adc_reset(void); - -extern void adc_setmux(uint8_t ch); - -extern uint8_t adc_chan(uint8_t index); - -extern void adc_cycle(void); - - -#if defined(__cplusplus) -} -#endif //defined(__cplusplus) -#endif //_ADC_H +extern void adc_init(); +extern void adc_start_cycle(); //should be called from an atomic context only +static inline bool adc_cycle_done() { return adc_channel >= ADC_CHAN_CNT; } diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 882ffcbb5..0c86f28b0 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -97,6 +97,7 @@ unsigned char soft_pwm_bed; //=========================================================================== //=============================private variables============================ //=========================================================================== +#define TEMP_MEAS_RATE 250 static volatile bool temp_meas_ready = false; #ifdef PIDTEMP @@ -265,10 +266,7 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle target_temperature[extruder] = (int)temp; // to display the requested target extruder temperature properly on the main screen } - - - - for(;;) { + for(;;) { #ifdef WATCHDOG wdt_reset(); #endif //WATCHDOG @@ -451,9 +449,13 @@ void manage_heater() float pid_input; float pid_output; - if(temp_meas_ready != true) //better readability - return; -// more precisely - this condition partially stabilizes time interval for regulation values evaluation (@ ~ 230ms) + // run at TEMP_MEAS_RATE + if(temp_meas_ready != true) return; + static unsigned long old_stamp = _millis(); + unsigned long new_stamp = _millis(); + unsigned long diff = new_stamp - old_stamp; + if(diff < TEMP_MEAS_RATE) return; + old_stamp = new_stamp; // ADC values need to be converted before checking: converted values are later used in MINTEMP updateTemperaturesFromRawValues(); @@ -774,6 +776,11 @@ static float analog2tempAmbient(int raw) and this function is called from normal context as it is too slow to run in interrupts and will block the stepper routine otherwise */ static void updateTemperaturesFromRawValues() { + CRITICAL_SECTION_START; + adc_start_cycle(); + temp_meas_ready = false; + CRITICAL_SECTION_END; + for(uint8_t e=0;e Date: Sun, 15 May 2022 00:50:03 +0200 Subject: [PATCH 08/95] Rename temperature_isr to soft_pwm_isr --- Firmware/temperature.cpp | 10 +++++----- Firmware/temperature.h | 8 ++++---- Firmware/xyzcal.cpp | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 0c86f28b0..a1741eddd 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -875,7 +875,7 @@ void tp_init() // timer2 already enabled earlier in the code // now enable the COMPB temperature interrupt OCR2B = 128; - ENABLE_TEMPERATURE_INTERRUPT(); + ENABLE_SOFT_PWM_INTERRUPT(); timer4_init(); //for tone and Extruder fan PWM @@ -1401,7 +1401,7 @@ void adc_ready(void) //callback from adc when sampling finished temp_meas_ready = true; } -FORCE_INLINE static void temperature_isr() +FORCE_INLINE static void soft_pwm_isr() { lcd_buttons_update(); @@ -1779,11 +1779,11 @@ ISR(TIMER2_COMPB_vect) ISR(TIMER0_COMPB_vect) #endif //SYSTEM_TIMER_2 { - DISABLE_TEMPERATURE_INTERRUPT(); + DISABLE_SOFT_PWM_INTERRUPT(); sei(); - temperature_isr(); + soft_pwm_isr(); cli(); - ENABLE_TEMPERATURE_INTERRUPT(); + ENABLE_SOFT_PWM_INTERRUPT(); } void check_max_temp() diff --git a/Firmware/temperature.h b/Firmware/temperature.h index ee94d04c7..eb2670580 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -27,13 +27,13 @@ #ifdef SYSTEM_TIMER_2 -#define ENABLE_TEMPERATURE_INTERRUPT() TIMSK2 |= (1<-1)) DISABLE_FANCHECK_INTERRUPT(); #endif //(defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) @@ -156,7 +156,7 @@ void xyzcal_meassure_leave(void) { DBG(_n("xyzcal_meassure_leave\n")); planner_abort_hard(); - ENABLE_TEMPERATURE_INTERRUPT(); + ENABLE_SOFT_PWM_INTERRUPT(); #if (defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) ENABLE_FANCHECK_INTERRUPT(); #endif //(defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) From bd2800731278349b6194bcb1f736154c514dca4d Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 00:54:34 +0200 Subject: [PATCH 09/95] Rename check_fans>readFanTach manageFans>checkFans --- Firmware/fancheck.cpp | 4 ++-- Firmware/fancheck.h | 4 ++-- Firmware/temperature.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Firmware/fancheck.cpp b/Firmware/fancheck.cpp index 6a3377408..adae2bdc9 100755 --- a/Firmware/fancheck.cpp +++ b/Firmware/fancheck.cpp @@ -219,7 +219,7 @@ void checkExtruderAutoFans() #endif // any extruder auto fan pins set #if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) -void check_fans() { +void readFanTach() { #ifdef FAN_SOFT_PWM if (READ(TACH_0) != fan_state[0]) { if(fan_measuring) fan_edge_counter[0] ++; @@ -238,7 +238,7 @@ void check_fans() { } #endif //TACH_0 -void manageFans() +void checkFans() { #ifndef DEBUG_DISABLE_FANCHECK #if (defined(EXTRUDER_0_AUTO_FAN_PIN) && EXTRUDER_0_AUTO_FAN_PIN > -1) diff --git a/Firmware/fancheck.h b/Firmware/fancheck.h index 6f47efbdb..841ffe78b 100755 --- a/Firmware/fancheck.h +++ b/Firmware/fancheck.h @@ -13,7 +13,7 @@ enum { }; extern volatile uint8_t fan_check_error; -void check_fans(); +void readFanTach(); #endif //(defined(TACH_0)) #ifdef EXTRUDER_ALTFAN_DETECT @@ -32,5 +32,5 @@ void setExtruderAutoFanState(uint8_t state); void checkExtruderAutoFans(); #endif -void manageFans(); +void checkFans(); void hotendFanSetFullSpeed(); diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index a1741eddd..935ea1ad9 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -550,7 +550,7 @@ void manage_heater() } } // End extruder for loop - manageFans(); + checkFans(); #ifndef PIDTEMPBED if(_millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) @@ -1768,7 +1768,7 @@ FORCE_INLINE static void soft_pwm_isr() if (!SdFatUtil::test_stack_integrity()) stack_error(); #if (defined(FANCHECK) && defined(TACH_0) && (TACH_0 > -1)) - check_fans(); + readFanTach(); #endif //(defined(TACH_0)) } From dfd8fee712870399678c95df8d2a10057b0ed969 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 01:00:34 +0200 Subject: [PATCH 10/95] Isolate babystep to a separate function --- Firmware/temperature.cpp | 47 ++++++++++++++++++++++------------------ 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 935ea1ad9..42518eaf0 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1401,6 +1401,31 @@ void adc_ready(void) //callback from adc when sampling finished temp_meas_ready = true; } +#ifdef BABYSTEPPING +FORCE_INLINE static void applyBabysteps() { + for(uint8_t axis=0;axis<3;axis++) + { + int curTodo=babystepsTodo[axis]; //get rid of volatile for performance + + if(curTodo>0) + { + CRITICAL_SECTION_START; + babystep(axis,/*fwd*/true); + babystepsTodo[axis]--; //less to do next time + CRITICAL_SECTION_END; + } + else + if(curTodo<0) + { + CRITICAL_SECTION_START; + babystep(axis,/*fwd*/false); + babystepsTodo[axis]++; //less to do next time + CRITICAL_SECTION_END; + } + } +} +#endif //BABYSTEPPING + FORCE_INLINE static void soft_pwm_isr() { lcd_buttons_update(); @@ -1740,28 +1765,8 @@ FORCE_INLINE static void soft_pwm_isr() #endif //ifndef SLOW_PWM_HEATERS - #ifdef BABYSTEPPING - for(uint8_t axis=0;axis<3;axis++) - { - int curTodo=babystepsTodo[axis]; //get rid of volatile for performance - - if(curTodo>0) - { - CRITICAL_SECTION_START; - babystep(axis,/*fwd*/true); - babystepsTodo[axis]--; //less to do next time - CRITICAL_SECTION_END; - } - else - if(curTodo<0) - { - CRITICAL_SECTION_START; - babystep(axis,/*fwd*/false); - babystepsTodo[axis]++; //less to do next time - CRITICAL_SECTION_END; - } - } + applyBabysteps(); #endif //BABYSTEPPING // Check if a stack overflow happened From 932fcbb33f51b33b8820f9e2139bbd3348c2d35f Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 01:07:06 +0200 Subject: [PATCH 11/95] Simplify soft_pwm_isr even further Isolate the PWM management into soft_pwm_core --- Firmware/temperature.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 42518eaf0..802a68992 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1426,10 +1426,8 @@ FORCE_INLINE static void applyBabysteps() { } #endif //BABYSTEPPING -FORCE_INLINE static void soft_pwm_isr() +FORCE_INLINE static void soft_pwm_core() { - lcd_buttons_update(); - static uint8_t pwm_count = (1 << SOFT_PWM_SCALE); static uint8_t soft_pwm_0; #ifdef SLOW_PWM_HEATERS @@ -1764,6 +1762,12 @@ FORCE_INLINE static void soft_pwm_isr() } //if ((pwm_count % 64) == 0) { #endif //ifndef SLOW_PWM_HEATERS +} + +FORCE_INLINE static void soft_pwm_isr() +{ + lcd_buttons_update(); + soft_pwm_core(); #ifdef BABYSTEPPING applyBabysteps(); From 38b3e53f67a2cf46277a7e9476aa359d26eb6cf4 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 01:13:44 +0200 Subject: [PATCH 12/95] Syntax/comment cleanup --- Firmware/temperature.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 802a68992..4f23054b1 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -434,11 +434,8 @@ int getHeaterPower(int heater) { return soft_pwm[heater]; } -// ready for eventually parameters adjusting -void resetPID(uint8_t) // only for compiler-warning elimination (if function do nothing) -//void resetPID(uint8_t extruder) -{ -} +// reset PID state after changing target_temperature +void resetPID(uint8_t extruder _UNUSED) {} void manage_heater() { From b56d31b5b3b27ef8548a67880fb48960b8eeb69d Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 19:01:11 +0200 Subject: [PATCH 13/95] Improve comment --- Firmware/Marlin_main.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 2fc467d3e..60f1ba8a8 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1273,9 +1273,14 @@ void setup() else { //printer version was changed so use default settings Config_ResetDefault(); } - SdFatUtil::set_stack_guard(); //writes magic number at the end of static variables to protect against overwriting static memory by stack - tp_init(); // Initialize temperature loop + // writes a magic number at the end of static variables to monitor against incorrect overwriting + // of static memory by stack (this needs to be done before soft_pwm_init, since the check is + // performed inside the soft_pwm_isr) + SdFatUtil::set_stack_guard(); + + // Initialize temperature loop + tp_init(); #ifdef EXTRUDER_ALTFAN_DETECT SERIAL_ECHORPGM(_n("Extruder fan type: ")); From 2ca16a06cd1c6e4a35a3e6ff6302b7ccff991669 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 19:01:50 +0200 Subject: [PATCH 14/95] Rename tp_init to soft_pwm_init for consistency --- Firmware/Marlin_main.cpp | 2 +- Firmware/temperature.cpp | 3 +-- Firmware/temperature.h | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 60f1ba8a8..c2153524e 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1280,7 +1280,7 @@ void setup() SdFatUtil::set_stack_guard(); // Initialize temperature loop - tp_init(); + soft_pwm_init(); #ifdef EXTRUDER_ALTFAN_DETECT SERIAL_ECHORPGM(_n("Extruder fan type: ")); diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4f23054b1..512afa976 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -799,7 +799,7 @@ static void updateTemperaturesFromRawValues() #endif //DEBUG_HEATER_BED_SIM } -void tp_init() +void soft_pwm_init() { #if MB(RUMBA) && ((TEMP_SENSOR_0==-1)||(TEMP_SENSOR_1==-1)||(TEMP_SENSOR_2==-1)||(TEMP_SENSOR_BED==-1)) //disable RUMBA JTAG in case the thermocouple extension is plugged on top of JTAG connector @@ -2036,4 +2036,3 @@ bool has_temperature_compensation() } #endif //PINDA_THERMISTOR - diff --git a/Firmware/temperature.h b/Firmware/temperature.h index eb2670580..0ac1a4a9d 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -39,7 +39,7 @@ // public functions -void tp_init(); //initialize the heating +void soft_pwm_init(); //initialize the soft pwm isr void manage_heater(); //it is critical that this is called periodically. extern bool checkAllHotends(void); From c6d0494cbc42a78b65fe22bf1ea77503311f2541 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 15 May 2022 23:35:46 +0200 Subject: [PATCH 15/95] Split temperature management into it's own ISR Use a new low-priority "temp_mgr_isr" running at constant rate for temperature management. This is done so that the temperatures are sampled at a constant independent interval *and* with reduced jitter. Likewise for actual PID management. This will require further adjustment for the min/max/runaway display, which cannot be done directly into this function anymore (the code will need to disable heaters but flag for display to be handled in manage_heaters). --- Firmware/Marlin_main.cpp | 5 +- Firmware/temperature.cpp | 496 ++++++++++++++++++++++----------------- Firmware/temperature.h | 1 + 3 files changed, 283 insertions(+), 219 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index c2153524e..098435980 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1279,8 +1279,9 @@ void setup() // performed inside the soft_pwm_isr) SdFatUtil::set_stack_guard(); - // Initialize temperature loop + // Initialize pwm/temperature loops soft_pwm_init(); + temp_mgr_init(); #ifdef EXTRUDER_ALTFAN_DETECT SERIAL_ECHORPGM(_n("Extruder fan type: ")); @@ -1365,7 +1366,9 @@ void setup() setup_photpin(); +#if 0 servo_init(); +#endif // Reset the machine correction matrix. // It does not make sense to load the correction matrix until the machine is homed. diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 512afa976..a85d11492 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -97,7 +97,6 @@ unsigned char soft_pwm_bed; //=========================================================================== //=============================private variables============================ //=========================================================================== -#define TEMP_MEAS_RATE 250 static volatile bool temp_meas_ready = false; #ifdef PIDTEMP @@ -442,208 +441,7 @@ void manage_heater() #ifdef WATCHDOG wdt_reset(); #endif //WATCHDOG - - float pid_input; - float pid_output; - - // run at TEMP_MEAS_RATE - if(temp_meas_ready != true) return; - static unsigned long old_stamp = _millis(); - unsigned long new_stamp = _millis(); - unsigned long diff = new_stamp - old_stamp; - if(diff < TEMP_MEAS_RATE) return; - old_stamp = new_stamp; - - // ADC values need to be converted before checking: converted values are later used in MINTEMP - updateTemperaturesFromRawValues(); - - check_max_temp(); - check_min_temp(); - -#ifdef TEMP_RUNAWAY_BED_HYSTERESIS - temp_runaway_check(0, target_temperature_bed, current_temperature_bed, (int)soft_pwm_bed, true); -#endif - - for(uint8_t e = 0; e < EXTRUDERS; e++) - { - -#ifdef TEMP_RUNAWAY_EXTRUDER_HYSTERESIS - temp_runaway_check(e+1, target_temperature[e], current_temperature[e], (int)soft_pwm[e], false); -#endif - - #ifdef PIDTEMP - pid_input = current_temperature[e]; - - #ifndef PID_OPENLOOP - if(target_temperature[e] == 0) { - pid_output = 0; - pid_reset[e] = true; - } else { - pid_error[e] = target_temperature[e] - pid_input; - if(pid_reset[e]) { - iState_sum[e] = 0.0; - dTerm[e] = 0.0; // 'dState_last[e]' initial setting is not necessary (see end of if-statement) - pid_reset[e] = false; - } -#ifndef PonM - pTerm[e] = cs.Kp * pid_error[e]; - iState_sum[e] += pid_error[e]; - iState_sum[e] = constrain(iState_sum[e], iState_sum_min[e], iState_sum_max[e]); - iTerm[e] = cs.Ki * iState_sum[e]; - // PID_K1 defined in Configuration.h in the PID settings - #define K2 (1.0-PID_K1) - dTerm[e] = (cs.Kd * (pid_input - dState_last[e]))*K2 + (PID_K1 * dTerm[e]); // e.g. digital filtration of derivative term changes - pid_output = pTerm[e] + iTerm[e] - dTerm[e]; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used) - if (pid_output > PID_MAX) { - if (pid_error[e] > 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration - pid_output=PID_MAX; - } else if (pid_output < 0) { - if (pid_error[e] < 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration - pid_output=0; - } -#else // PonM ("Proportional on Measurement" method) - iState_sum[e] += cs.Ki * pid_error[e]; - iState_sum[e] -= cs.Kp * (pid_input - dState_last[e]); - iState_sum[e] = constrain(iState_sum[e], 0, PID_INTEGRAL_DRIVE_MAX); - dTerm[e] = cs.Kd * (pid_input - dState_last[e]); - pid_output = iState_sum[e] - dTerm[e]; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used) - pid_output = constrain(pid_output, 0, PID_MAX); -#endif // PonM - } - dState_last[e] = pid_input; - #else - pid_output = constrain(target_temperature[e], 0, PID_MAX); - #endif //PID_OPENLOOP - #ifdef PID_DEBUG - SERIAL_ECHO_START; - SERIAL_ECHO(" PID_DEBUG "); - SERIAL_ECHO(e); - SERIAL_ECHO(": Input "); - SERIAL_ECHO(pid_input); - SERIAL_ECHO(" Output "); - SERIAL_ECHO(pid_output); - SERIAL_ECHO(" pTerm "); - SERIAL_ECHO(pTerm[e]); - SERIAL_ECHO(" iTerm "); - SERIAL_ECHO(iTerm[e]); - SERIAL_ECHO(" dTerm "); - SERIAL_ECHOLN(-dTerm[e]); - #endif //PID_DEBUG - #else /* PID off */ - pid_output = 0; - if(current_temperature[e] < target_temperature[e]) { - pid_output = PID_MAX; - } - #endif - - // Check if temperature is within the correct range - if((current_temperature[e] < maxttemp[e]) && (target_temperature[e] != 0)) - { - soft_pwm[e] = (int)pid_output >> 1; - } - else - { - soft_pwm[e] = 0; - } - } // End extruder for loop - - checkFans(); - - #ifndef PIDTEMPBED - if(_millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) - return; - previous_millis_bed_heater = _millis(); - #endif - - #if TEMP_SENSOR_BED != 0 - - #ifdef PIDTEMPBED - pid_input = current_temperature_bed; - - #ifndef PID_OPENLOOP - pid_error_bed = target_temperature_bed - pid_input; - pTerm_bed = cs.bedKp * pid_error_bed; - temp_iState_bed += pid_error_bed; - temp_iState_bed = constrain(temp_iState_bed, temp_iState_min_bed, temp_iState_max_bed); - iTerm_bed = cs.bedKi * temp_iState_bed; - - //PID_K1 defined in Configuration.h in the PID settings - #define K2 (1.0-PID_K1) - dTerm_bed= (cs.bedKd * (pid_input - temp_dState_bed))*K2 + (PID_K1 * dTerm_bed); - temp_dState_bed = pid_input; - - pid_output = pTerm_bed + iTerm_bed - dTerm_bed; - if (pid_output > MAX_BED_POWER) { - if (pid_error_bed > 0 ) temp_iState_bed -= pid_error_bed; // conditional un-integration - pid_output=MAX_BED_POWER; - } else if (pid_output < 0){ - if (pid_error_bed < 0 ) temp_iState_bed -= pid_error_bed; // conditional un-integration - pid_output=0; - } - - #else - pid_output = constrain(target_temperature_bed, 0, MAX_BED_POWER); - #endif //PID_OPENLOOP - - if(current_temperature_bed < BED_MAXTEMP) - { - soft_pwm_bed = (int)pid_output >> 1; - timer02_set_pwm0(soft_pwm_bed << 1); - } - else { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - } - - #elif !defined(BED_LIMIT_SWITCHING) - // Check if temperature is within the correct range - if(current_temperature_bed < BED_MAXTEMP) - { - if(current_temperature_bed >= target_temperature_bed) - { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - } - else - { - soft_pwm_bed = MAX_BED_POWER>>1; - timer02_set_pwm0(soft_pwm_bed << 1); - } - } - else - { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - WRITE(HEATER_BED_PIN,LOW); - } - #else //#ifdef BED_LIMIT_SWITCHING - // Check if temperature is within the correct band - if(current_temperature_bed < BED_MAXTEMP) - { - if(current_temperature_bed > target_temperature_bed + BED_HYSTERESIS) - { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - } - else if(current_temperature_bed <= target_temperature_bed - BED_HYSTERESIS) - { - soft_pwm_bed = MAX_BED_POWER>>1; - timer02_set_pwm0(soft_pwm_bed << 1); - } - } - else - { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - WRITE(HEATER_BED_PIN,LOW); - } - #endif - if(target_temperature_bed==0) - { - soft_pwm_bed = 0; - timer02_set_pwm0(soft_pwm_bed << 1); - } - #endif + checkFans(); } #define PGM_RD_W(x) (short)pgm_read_word(&x) @@ -769,15 +567,8 @@ static float analog2tempAmbient(int raw) } #endif //AMBIENT_THERMISTOR -/* Called to get the raw values into the the actual temperatures. The raw values are created in interrupt context, - and this function is called from normal context as it is too slow to run in interrupts and will block the stepper routine otherwise */ -static void updateTemperaturesFromRawValues() +static void setTemperaturesFromRawValues() { - CRITICAL_SECTION_START; - adc_start_cycle(); - temp_meas_ready = false; - CRITICAL_SECTION_END; - for(uint8_t e=0;e PID_MAX) { + if (pid_error[e] > 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration + pid_output=PID_MAX; + } else if (pid_output < 0) { + if (pid_error[e] < 0 ) iState_sum[e] -= pid_error[e]; // conditional un-integration + pid_output=0; + } +#else // PonM ("Proportional on Measurement" method) + iState_sum[e] += cs.Ki * pid_error[e]; + iState_sum[e] -= cs.Kp * (pid_input - dState_last[e]); + iState_sum[e] = constrain(iState_sum[e], 0, PID_INTEGRAL_DRIVE_MAX); + dTerm[e] = cs.Kd * (pid_input - dState_last[e]); + pid_output = iState_sum[e] - dTerm[e]; // subtraction due to "Derivative on Measurement" method (i.e. derivative of input instead derivative of error is used) + pid_output = constrain(pid_output, 0, PID_MAX); +#endif // PonM + } + dState_last[e] = pid_input; +#else //PID_OPENLOOP + pid_output = constrain(target_temperature[e], 0, PID_MAX); +#endif //PID_OPENLOOP + +#ifdef PID_DEBUG + SERIAL_ECHO_START; + SERIAL_ECHO(" PID_DEBUG "); + SERIAL_ECHO(e); + SERIAL_ECHO(": Input "); + SERIAL_ECHO(pid_input); + SERIAL_ECHO(" Output "); + SERIAL_ECHO(pid_output); + SERIAL_ECHO(" pTerm "); + SERIAL_ECHO(pTerm[e]); + SERIAL_ECHO(" iTerm "); + SERIAL_ECHO(iTerm[e]); + SERIAL_ECHO(" dTerm "); + SERIAL_ECHOLN(-dTerm[e]); +#endif //PID_DEBUG + +#else /* PID off */ + pid_output = 0; + if(current_temperature[e] < target_temperature[e]) { + pid_output = PID_MAX; + } +#endif + + // Check if temperature is within the correct range + if((current_temperature[e] < maxttemp[e]) && (target_temperature[e] != 0)) + soft_pwm[e] = (int)pid_output >> 1; + else + soft_pwm[e] = 0; +} + +static void pid_bed() +{ + float pid_input; + float pid_output; + +#ifdef TEMP_RUNAWAY_BED_HYSTERESIS + temp_runaway_check(0, target_temperature_bed, current_temperature_bed, (int)soft_pwm_bed, true); +#endif + +#ifndef PIDTEMPBED + if(_millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) + return; + previous_millis_bed_heater = _millis(); +#endif + +#if TEMP_SENSOR_BED != 0 + +#ifdef PIDTEMPBED + pid_input = current_temperature_bed; + +#ifndef PID_OPENLOOP + pid_error_bed = target_temperature_bed - pid_input; + pTerm_bed = cs.bedKp * pid_error_bed; + temp_iState_bed += pid_error_bed; + temp_iState_bed = constrain(temp_iState_bed, temp_iState_min_bed, temp_iState_max_bed); + iTerm_bed = cs.bedKi * temp_iState_bed; + + //PID_K1 defined in Configuration.h in the PID settings +#define K2 (1.0-PID_K1) + dTerm_bed= (cs.bedKd * (pid_input - temp_dState_bed))*K2 + (PID_K1 * dTerm_bed); + temp_dState_bed = pid_input; + + pid_output = pTerm_bed + iTerm_bed - dTerm_bed; + if (pid_output > MAX_BED_POWER) { + if (pid_error_bed > 0 ) temp_iState_bed -= pid_error_bed; // conditional un-integration + pid_output=MAX_BED_POWER; + } else if (pid_output < 0){ + if (pid_error_bed < 0 ) temp_iState_bed -= pid_error_bed; // conditional un-integration + pid_output=0; + } + +#else + pid_output = constrain(target_temperature_bed, 0, MAX_BED_POWER); +#endif //PID_OPENLOOP + + if(current_temperature_bed < BED_MAXTEMP) + { + soft_pwm_bed = (int)pid_output >> 1; + timer02_set_pwm0(soft_pwm_bed << 1); + } + else + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + } + +#elif !defined(BED_LIMIT_SWITCHING) + // Check if temperature is within the correct range + if(current_temperature_bed < BED_MAXTEMP) + { + if(current_temperature_bed >= target_temperature_bed) + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + } + else + { + soft_pwm_bed = MAX_BED_POWER>>1; + timer02_set_pwm0(soft_pwm_bed << 1); + } + } + else + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + WRITE(HEATER_BED_PIN,LOW); + } +#else //#ifdef BED_LIMIT_SWITCHING + // Check if temperature is within the correct band + if(current_temperature_bed < BED_MAXTEMP) + { + if(current_temperature_bed > target_temperature_bed + BED_HYSTERESIS) + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + } + else if(current_temperature_bed <= target_temperature_bed - BED_HYSTERESIS) + { + soft_pwm_bed = MAX_BED_POWER>>1; + timer02_set_pwm0(soft_pwm_bed << 1); + } + } + else + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + WRITE(HEATER_BED_PIN,LOW); + } +#endif //BED_LIMIT_SWITCHING + + if(target_temperature_bed==0) + { + soft_pwm_bed = 0; + timer02_set_pwm0(soft_pwm_bed << 1); + } +#endif //TEMP_SENSOR_BED +} + +static void temp_mgr_isr() +{ + // ADC values need to be converted before checking: converted values are later used in MINTEMP + setTemperaturesFromRawValues(); + + // TODO: this is now running inside an isr and cannot directly manipulate the lcd, + // this needs to disable temperatures and flag the error to be shown in manage_heaters! + check_max_temp(); + check_min_temp(); + + for(uint8_t e = 0; e < EXTRUDERS; e++) + pid_heater(e); + pid_bed(); +} + +ISR(TIMER5_COMPA_vect) +{ + // immediately schedule a new conversion + if(temp_meas_ready != true) return; + adc_start_cycle(); + temp_meas_ready = false; + + // run temperature management with interrupts enabled to reduce latency + DISABLE_TEMP_MGR_INTERRUPT(); + sei(); + temp_mgr_isr(); + cli(); + ENABLE_TEMP_MGR_INTERRUPT(); +} diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 0ac1a4a9d..427bb8969 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -40,6 +40,7 @@ // public functions void soft_pwm_init(); //initialize the soft pwm isr +void temp_mgr_init(); //initialize the temperature handler void manage_heater(); //it is critical that this is called periodically. extern bool checkAllHotends(void); From 9e826afee9f3335004c30d280fccf010973cc8cb Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 16 May 2022 12:25:40 +0200 Subject: [PATCH 16/95] Switch regulation interval to 270ms --- Firmware/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index a85d11492..50f577569 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1835,7 +1835,7 @@ bool has_temperature_compensation() #endif //PINDA_THERMISTOR -#define TEMP_MGR_INTV 0.3 // seconds, 33.3Hz +#define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz #define TIMER5_PRESCALE 256 #define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU)) #define ENABLE_TEMP_MGR_INTERRUPT() TIMSK5 |= (1< Date: Tue, 24 May 2022 12:01:13 +0200 Subject: [PATCH 17/95] temperature: Do not expose check_min/max_temp --- Firmware/temperature.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 427bb8969..0bdddbfb1 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -232,9 +232,6 @@ FORCE_INLINE void autotempShutdown(){ void PID_autotune(float temp, int extruder, int ncycles); -void check_min_temp(); -void check_max_temp(); - #ifdef FAN_SOFT_PWM extern unsigned char fanSpeedSoftPwm; #endif From 65cf8e541a15a093f2d6a480ddd8a8ba0bc3c448 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 12:57:35 +0200 Subject: [PATCH 18/95] Isolate current/target temperature in pid_heater/bed functions --- Firmware/temperature.cpp | 47 ++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 50f577569..1bbe2f34c 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -417,6 +417,7 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle void updatePID() { + // TODO: iState_sum_max and PID values should be synchronized for temp_mgr_isr #ifdef PIDTEMP for(uint_least8_t e = 0; e < EXTRUDERS; e++) { iState_sum_max[e] = PID_INTEGRAL_DRIVE_MAX / cs.Ki; @@ -1876,24 +1877,24 @@ void temp_mgr_init() CRITICAL_SECTION_END; } -static void pid_heater(uint8_t e) +static void pid_heater(uint8_t e, const float current, const int target) { float pid_input; float pid_output; #ifdef TEMP_RUNAWAY_EXTRUDER_HYSTERESIS - temp_runaway_check(e+1, target_temperature[e], current_temperature[e], (int)soft_pwm[e], false); + temp_runaway_check(e+1, target, current, (int)soft_pwm[e], false); #endif #ifdef PIDTEMP - pid_input = current_temperature[e]; + pid_input = current; #ifndef PID_OPENLOOP - if(target_temperature[e] == 0) { + if(target == 0) { pid_output = 0; pid_reset[e] = true; } else { - pid_error[e] = target_temperature[e] - pid_input; + pid_error[e] = target - pid_input; if(pid_reset[e]) { iState_sum[e] = 0.0; dTerm[e] = 0.0; // 'dState_last[e]' initial setting is not necessary (see end of if-statement) @@ -1926,7 +1927,7 @@ static void pid_heater(uint8_t e) } dState_last[e] = pid_input; #else //PID_OPENLOOP - pid_output = constrain(target_temperature[e], 0, PID_MAX); + pid_output = constrain(target[e], 0, PID_MAX); #endif //PID_OPENLOOP #ifdef PID_DEBUG @@ -1947,25 +1948,25 @@ static void pid_heater(uint8_t e) #else /* PID off */ pid_output = 0; - if(current_temperature[e] < target_temperature[e]) { + if(current[e] < target[e]) { pid_output = PID_MAX; } #endif // Check if temperature is within the correct range - if((current_temperature[e] < maxttemp[e]) && (target_temperature[e] != 0)) + if((current < maxttemp[e]) && (target != 0)) soft_pwm[e] = (int)pid_output >> 1; else soft_pwm[e] = 0; } -static void pid_bed() +static void pid_bed(const float current, const int target) { float pid_input; float pid_output; #ifdef TEMP_RUNAWAY_BED_HYSTERESIS - temp_runaway_check(0, target_temperature_bed, current_temperature_bed, (int)soft_pwm_bed, true); + temp_runaway_check(0, target, current, (int)soft_pwm_bed, true); #endif #ifndef PIDTEMPBED @@ -1977,10 +1978,10 @@ static void pid_bed() #if TEMP_SENSOR_BED != 0 #ifdef PIDTEMPBED - pid_input = current_temperature_bed; + pid_input = current; #ifndef PID_OPENLOOP - pid_error_bed = target_temperature_bed - pid_input; + pid_error_bed = target - pid_input; pTerm_bed = cs.bedKp * pid_error_bed; temp_iState_bed += pid_error_bed; temp_iState_bed = constrain(temp_iState_bed, temp_iState_min_bed, temp_iState_max_bed); @@ -2001,10 +2002,10 @@ static void pid_bed() } #else - pid_output = constrain(target_temperature_bed, 0, MAX_BED_POWER); + pid_output = constrain(target, 0, MAX_BED_POWER); #endif //PID_OPENLOOP - if(current_temperature_bed < BED_MAXTEMP) + if(current < BED_MAXTEMP) { soft_pwm_bed = (int)pid_output >> 1; timer02_set_pwm0(soft_pwm_bed << 1); @@ -2017,9 +2018,9 @@ static void pid_bed() #elif !defined(BED_LIMIT_SWITCHING) // Check if temperature is within the correct range - if(current_temperature_bed < BED_MAXTEMP) + if(current < BED_MAXTEMP) { - if(current_temperature_bed >= target_temperature_bed) + if(current >= target) { soft_pwm_bed = 0; timer02_set_pwm0(soft_pwm_bed << 1); @@ -2038,14 +2039,14 @@ static void pid_bed() } #else //#ifdef BED_LIMIT_SWITCHING // Check if temperature is within the correct band - if(current_temperature_bed < BED_MAXTEMP) + if(current < BED_MAXTEMP) { - if(current_temperature_bed > target_temperature_bed + BED_HYSTERESIS) + if(current > target + BED_HYSTERESIS) { soft_pwm_bed = 0; timer02_set_pwm0(soft_pwm_bed << 1); } - else if(current_temperature_bed <= target_temperature_bed - BED_HYSTERESIS) + else if(current <= target - BED_HYSTERESIS) { soft_pwm_bed = MAX_BED_POWER>>1; timer02_set_pwm0(soft_pwm_bed << 1); @@ -2059,7 +2060,7 @@ static void pid_bed() } #endif //BED_LIMIT_SWITCHING - if(target_temperature_bed==0) + if(target==0) { soft_pwm_bed = 0; timer02_set_pwm0(soft_pwm_bed << 1); @@ -2073,13 +2074,13 @@ static void temp_mgr_isr() setTemperaturesFromRawValues(); // TODO: this is now running inside an isr and cannot directly manipulate the lcd, - // this needs to disable temperatures and flag the error to be shown in manage_heaters! + // this needs to disable temperatures and flag the error to be shown in manage_heater! check_max_temp(); check_min_temp(); for(uint8_t e = 0; e < EXTRUDERS; e++) - pid_heater(e); - pid_bed(); + pid_heater(e, current_temperature[e], target_temperature[e]); + pid_bed(current_temperature_bed, target_temperature_bed); } ISR(TIMER5_COMPA_vect) From c1051e046cab7b0870e5fc57072d1c610cbdd7a2 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 17:45:14 +0200 Subject: [PATCH 19/95] Remove private declaration --- Firmware/temperature.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 0bdddbfb1..39f8af302 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -79,10 +79,6 @@ extern int current_voltage_raw_bed; extern uint16_t current_voltage_raw_IR; #endif //IR_SENSOR_ANALOG -#if defined(CONTROLLERFAN_PIN) && CONTROLLERFAN_PIN > -1 - extern unsigned char soft_pwm_bed; -#endif - extern bool bedPWMDisabled; #ifdef PIDTEMP From bcd84961135c607474b9b53c7ca3538f6ada96fb Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 18:37:07 +0200 Subject: [PATCH 20/95] Introduce a new set of temperature values for the PID regulation *_temperature_raw: buffer for the ADC ISR (read by temp ISR) *_temperature_isr: latest temperatures for PID regulation (copied from _raw values) *_temperature: latest temperature for user code The flow: - ADC ISR (async) - perform oversampling - call ADC callback: copy to _raw (async) - temp ISR (timer) - convert to C (_isr values) - user code (async) - check temp_meas_ready - call updateTemperature() - copy from _isr to current - syncronize target temperatures This removes PINDA value averaging (if needed, should be re-implemented by averaging in user code where needed) --- Firmware/temperature.cpp | 175 +++++++++++++++++++++++---------------- 1 file changed, 104 insertions(+), 71 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 1bbe2f34c..5e23b4dc9 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -38,6 +38,7 @@ #include "SdFatUtil.h" #include +#include #include "adc.h" #include "ConfigurationStore.h" #include "Timer.h" @@ -56,13 +57,12 @@ int current_temperature_raw[EXTRUDERS] = { 0 }; float current_temperature[EXTRUDERS] = { 0.0 }; #ifdef PINDA_THERMISTOR -uint16_t current_temperature_raw_pinda = 0 ; //value with more averaging applied -uint16_t current_temperature_raw_pinda_fast = 0; //value read from adc +uint16_t current_temperature_raw_pinda = 0; float current_temperature_pinda = 0.0; #endif //PINDA_THERMISTOR #ifdef AMBIENT_THERMISTOR -int current_temperature_raw_ambient = 0 ; +int current_temperature_raw_ambient = 0; float current_temperature_ambient = 0.0; #endif //AMBIENT_THERMISTOR @@ -174,7 +174,7 @@ static float analog2tempBed(int raw); #ifdef AMBIENT_MAXTEMP static float analog2tempAmbient(int raw); #endif -static void updateTemperaturesFromRawValues(); +static void updateTemperatures(); enum TempRunawayStates : uint8_t { @@ -270,7 +270,7 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle wdt_reset(); #endif //WATCHDOG if(temp_meas_ready == true) { // temp sample ready - updateTemperaturesFromRawValues(); + updateTemperatures(); input = (extruder<0)?current_temperature_bed:current_temperature[extruder]; @@ -442,6 +442,12 @@ void manage_heater() #ifdef WATCHDOG wdt_reset(); #endif //WATCHDOG + + // syncronize temperatures with isr + if(temp_meas_ready) + updateTemperatures(); + + // periodically check fans checkFans(); } @@ -568,43 +574,6 @@ static float analog2tempAmbient(int raw) } #endif //AMBIENT_THERMISTOR -static void setTemperaturesFromRawValues() -{ - for(uint8_t e=0;e> 2; - current_temperature_pinda = analog2tempBed(current_temperature_raw_pinda); -#endif - -#ifdef AMBIENT_THERMISTOR - current_temperature_ambient = analog2tempAmbient(current_temperature_raw_ambient); //thermistor for ambient is NTCG104LH104JT1 (2000) -#endif - -#ifdef DEBUG_HEATER_BED_SIM - current_temperature_bed = target_temperature_bed; -#else //DEBUG_HEATER_BED_SIM - current_temperature_bed = analog2tempBed(current_temperature_bed_raw); -#endif //DEBUG_HEATER_BED_SIM -} - -/* Called to get the raw values into the the actual temperatures. The raw values are created in interrupt context, - and this function is called from normal context as it is too slow to run in interrupts and will block the stepper routine otherwise */ -static void updateTemperaturesFromRawValues() -{ - // restart a new adc conversion - CRITICAL_SECTION_START; - adc_start_cycle(); - temp_meas_ready = false; - CRITICAL_SECTION_END; - - // update the previous values - setTemperaturesFromRawValues(); -} - void soft_pwm_init() { #if MB(RUMBA) && ((TEMP_SENSOR_0==-1)||(TEMP_SENSOR_1==-1)||(TEMP_SENSOR_2==-1)||(TEMP_SENSOR_BED==-1)) @@ -1175,28 +1144,6 @@ int read_max6675() } #endif -void adc_ready(void) //callback from adc when sampling finished -{ - current_temperature_raw[0] = adc_values[ADC_PIN_IDX(TEMP_0_PIN)]; //heater -#ifdef PINDA_THERMISTOR - current_temperature_raw_pinda_fast = adc_values[ADC_PIN_IDX(TEMP_PINDA_PIN)]; -#endif //PINDA_THERMISTOR - current_temperature_bed_raw = adc_values[ADC_PIN_IDX(TEMP_BED_PIN)]; -#ifdef VOLT_PWR_PIN - current_voltage_raw_pwr = adc_values[ADC_PIN_IDX(VOLT_PWR_PIN)]; -#endif -#ifdef AMBIENT_THERMISTOR - current_temperature_raw_ambient = adc_values[ADC_PIN_IDX(TEMP_AMBIENT_PIN)]; // 5->6 -#endif //AMBIENT_THERMISTOR -#ifdef VOLT_BED_PIN - current_voltage_raw_bed = adc_values[ADC_PIN_IDX(VOLT_BED_PIN)]; // 6->9 -#endif -#ifdef IR_SENSOR_ANALOG - current_voltage_raw_IR = adc_values[ADC_PIN_IDX(VOLT_IR_PIN)]; -#endif //IR_SENSOR_ANALOG - temp_meas_ready = true; -} - #ifdef BABYSTEPPING FORCE_INLINE static void applyBabysteps() { for(uint8_t axis=0;axis<3;axis++) @@ -1839,6 +1786,7 @@ bool has_temperature_compensation() #define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz #define TIMER5_PRESCALE 256 #define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU)) +#define TEMP_MGR_INTERRUPT_STATE() (TIMSK5 & (1<6 +#endif //AMBIENT_THERMISTOR +#ifdef VOLT_PWR_PIN + current_voltage_raw_pwr = adc_values[ADC_PIN_IDX(VOLT_PWR_PIN)]; +#endif +#ifdef VOLT_BED_PIN + current_voltage_raw_bed = adc_values[ADC_PIN_IDX(VOLT_BED_PIN)]; // 6->9 +#endif +#ifdef IR_SENSOR_ANALOG + current_voltage_raw_IR = adc_values[ADC_PIN_IDX(VOLT_IR_PIN)]; +#endif //IR_SENSOR_ANALOG + adc_values_ready = true; +} + +/* Syncronize temperatures: + - fetch updated values from temp_mgr_isr to current values + - update target temperatures for temp_mgr_isr regulation + This function is blocking: check temp_meas_ready before calling! */ +static void updateTemperatures() +{ + uint8_t temp_mgr_state; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + temp_mgr_state = TEMP_MGR_INTERRUPT_STATE(); + DISABLE_TEMP_MGR_INTERRUPT(); + } + + for(uint8_t e=0;e Date: Tue, 24 May 2022 18:43:33 +0200 Subject: [PATCH 21/95] Rename ADC callback --- Firmware/config.h | 2 +- Firmware/temperature.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/config.h b/Firmware/config.h index d23132bd6..28b85a109 100644 --- a/Firmware/config.h +++ b/Firmware/config.h @@ -21,7 +21,7 @@ #define ADC_CHAN_CNT 8 //number of used channels) #endif //!IR_SENSOR_ANALOG #define ADC_OVRSAMPL 16 //oversampling multiplier -#define ADC_CALLBACK adc_ready //callback function () +#define ADC_CALLBACK adc_callback //callback function () //SWI2C configuration //#define SWI2C_SDA 20 //SDA on P3 diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 5e23b4dc9..d0ce9221c 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2030,7 +2030,7 @@ float current_temperature_ambient_isr; #endif // ISR callback from adc when sampling finished -void adc_ready() +void adc_callback() { current_temperature_raw[0] = adc_values[ADC_PIN_IDX(TEMP_0_PIN)]; //heater current_temperature_bed_raw = adc_values[ADC_PIN_IDX(TEMP_BED_PIN)]; From ccaecc87aace99625c529876ec312bd5c94b84b3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 19:06:27 +0200 Subject: [PATCH 22/95] Reduce code duplication in setTargetHotendSafe --- Firmware/temperature.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 39f8af302..fe479e023 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -149,8 +149,7 @@ FORCE_INLINE void setTargetHotend(const float &celsius, uint8_t extruder) { static inline void setTargetHotendSafe(const float &celsius, uint8_t extruder) { if (extruder Date: Tue, 24 May 2022 19:44:40 +0200 Subject: [PATCH 23/95] Remove obsolete and commented variables --- Firmware/temperature.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index d0ce9221c..338fb8261 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -106,12 +106,9 @@ static volatile bool temp_meas_ready = false; static float pTerm[EXTRUDERS]; static float iTerm[EXTRUDERS]; static float dTerm[EXTRUDERS]; - //int output; static float pid_error[EXTRUDERS]; static float iState_sum_min[EXTRUDERS]; static float iState_sum_max[EXTRUDERS]; - // static float pid_input[EXTRUDERS]; - // static float pid_output[EXTRUDERS]; static bool pid_reset[EXTRUDERS]; #endif //PIDTEMP #ifdef PIDTEMPBED @@ -121,7 +118,6 @@ static volatile bool temp_meas_ready = false; static float pTerm_bed; static float iTerm_bed; static float dTerm_bed; - //int output; static float pid_error_bed; static float temp_iState_min_bed; static float temp_iState_max_bed; From 7659844012dd51b589ca4e910313fda345b80550 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 21:16:03 +0200 Subject: [PATCH 24/95] Reimplement disable_heater to take immediate effect Split off setIsrTargetTemperatures and temp_mgr_pid() so that we can propagate the target temperatures instantaneously down the pid/pwm chain during emergencies. This reduces the amount of code in disable_heater() itself, making it a bit more maintenable. The bed still isn't disabled on-the-spot yet, due to the heatbed_pwm automaton. To be improved later. --- Firmware/temperature.cpp | 139 ++++++++++++++++++++++----------------- Firmware/temperature.h | 2 +- 2 files changed, 79 insertions(+), 62 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 338fb8261..d41217de8 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -909,45 +909,6 @@ void temp_runaway_stop(bool isPreheat, bool isBed) } #endif - -void disable_heater() -{ - setAllTargetHotends(0); - setTargetBed(0); - #if defined(TEMP_0_PIN) && TEMP_0_PIN > -1 - target_temperature[0]=0; - soft_pwm[0]=0; - #if defined(HEATER_0_PIN) && HEATER_0_PIN > -1 - WRITE(HEATER_0_PIN,LOW); - #endif - #endif - - #if defined(TEMP_1_PIN) && TEMP_1_PIN > -1 && EXTRUDERS > 1 - target_temperature[1]=0; - soft_pwm[1]=0; - #if defined(HEATER_1_PIN) && HEATER_1_PIN > -1 - WRITE(HEATER_1_PIN,LOW); - #endif - #endif - - #if defined(TEMP_2_PIN) && TEMP_2_PIN > -1 && EXTRUDERS > 2 - target_temperature[2]=0; - soft_pwm[2]=0; - #if defined(HEATER_2_PIN) && HEATER_2_PIN > -1 - WRITE(HEATER_2_PIN,LOW); - #endif - #endif - - #if defined(TEMP_BED_PIN) && TEMP_BED_PIN > -1 - target_temperature_bed=0; - soft_pwm_bed=0; - timer02_set_pwm0(soft_pwm_bed << 1); - bedPWMDisabled = 0; - #if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1 - //WRITE(HEATER_BED_PIN,LOW); - #endif - #endif -} //! codes of alert messages for the LCD - it is shorter to compare an uin8_t //! than raw const char * of the messages themselves. //! Could be used for MAXTEMP situations too - after reaching MAXTEMP and turning off the heater automagically @@ -1786,6 +1747,25 @@ bool has_temperature_compensation() #define ENABLE_TEMP_MGR_INTERRUPT() TIMSK5 |= (1< -1 && EXTRUDERS > 0 + WRITE(HEATER_0_PIN,LOW); +#endif +#if defined(HEATER_1_PIN) && HEATER_1_PIN > -1 && EXTRUDERS > 1 + WRITE(HEATER_1_PIN,LOW); +#endif +#if defined(HEATER_2_PIN) && HEATER_2_PIN > -1 && EXTRUDERS > 2 + WRITE(HEATER_2_PIN,LOW); +#endif +#if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1 + // TODO: this doesn't take immediate effect! + timer02_set_pwm0(0); + bedPWMDisabled = 0; +#endif + + CRITICAL_SECTION_END; +} diff --git a/Firmware/temperature.h b/Firmware/temperature.h index fe479e023..08de851f3 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -210,7 +210,7 @@ FORCE_INLINE bool isCoolingBed() { #define CHECK_ALL_HEATERS (checkAllHotends()||(target_temperature_bed!=0)) int getHeaterPower(int heater); -void disable_heater(); // Disable all heaters +void disable_heater(); // Disable all heaters *instantaneously* void updatePID(); From 4a0203d6918f422cbed4519379ac40c5eef1f755 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 24 May 2022 21:44:10 +0200 Subject: [PATCH 25/95] Isolate oTimer4minTempHeater/Bed into check_min_temp --- Firmware/temperature.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index d41217de8..884fcf4e3 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -142,8 +142,6 @@ uint8_t fanSpeedBckp = 255; # define ARRAY_BY_EXTRUDERS(v1, v2, v3) { v1 } #endif -static ShortTimer oTimer4minTempHeater,oTimer4minTempBed; - // Init min and max temp with extreme values to prevent false errors during startup static int minttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS( HEATER_0_RAW_LO_TEMP , HEATER_1_RAW_LO_TEMP , HEATER_2_RAW_LO_TEMP ); static int maxttemp_raw[EXTRUDERS] = ARRAY_BY_EXTRUDERS( HEATER_0_RAW_HI_TEMP , HEATER_1_RAW_HI_TEMP , HEATER_2_RAW_HI_TEMP ); @@ -1636,6 +1634,9 @@ void check_min_temp() { static bool bCheckingOnHeater=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp) static bool bCheckingOnBed=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp) +static ShortTimer oTimer4minTempHeater; +static ShortTimer oTimer4minTempBed; + #ifdef AMBIENT_THERMISTOR #ifdef AMBIENT_MINTEMP check_min_temp_ambient(); From bd9a6acd590e37b58f1ee6e10e51ba6a9fc30817 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 25 May 2022 00:40:36 +0200 Subject: [PATCH 26/95] Correct handling of min/maxtemp - Flag the error condition from the temp_mgr_isr - Handle the error state from the user code Currently only handles min/maxtemp and relays the error to the original handler (which is a poor fit for the current design). --- Firmware/temperature.cpp | 280 +++++++++++++++++++++++++++------------ 1 file changed, 193 insertions(+), 87 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 884fcf4e3..aef3e684e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -431,6 +431,53 @@ int getHeaterPower(int heater) { // reset PID state after changing target_temperature void resetPID(uint8_t extruder _UNUSED) {} +enum class TempErrorSource : uint8_t +{ + hotend, + bed, + ambient, +}; + +enum class TempErrorType : uint8_t +{ + min, + max, + runaway, +}; + +// error state (updated via set_temp_error from isr context) +volatile static union +{ + uint8_t v; + struct + { + uint8_t error: 1; // error condition + uint8_t assert: 1; // error is still asserted + uint8_t source: 2; // source + uint8_t index: 2; // source index + uint8_t type: 2; // error type + }; +} temp_error_state; + +// set the error type from within the temp_mgr isr to be handled in manager_heater +// - immediately disable all heaters and turn on all fans at full speed +// - prevent the user to set temperatures until all errors are cleared +void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) +{ + // keep disabling heaters and keep fans on as long as the condition is asserted + disable_heater(); + hotendFanSetFullSpeed(); + + // set the error state + temp_error_state.error = true; + temp_error_state.assert = true; + temp_error_state.source = (uint8_t)source; + temp_error_state.index = index; + temp_error_state.type = (uint8_t)type; +} + +void handle_temp_error(); + void manage_heater() { #ifdef WATCHDOG @@ -441,6 +488,10 @@ void manage_heater() if(temp_meas_ready) updateTemperatures(); + // handle temperature errors + if(temp_error_state.v) + handle_temp_error(); + // periodically check fans checkFans(); } @@ -1493,37 +1544,38 @@ ISR(TIMER0_COMPB_vect) ENABLE_SOFT_PWM_INTERRUPT(); } -void check_max_temp() +void check_max_temp_raw() { -//heater + //heater #if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP if (current_temperature_raw[0] <= maxttemp_raw[0]) { #else if (current_temperature_raw[0] >= maxttemp_raw[0]) { #endif - max_temp_error(0); + set_temp_error(TempErrorSource::hotend, 0, TempErrorType::max); } -//bed + //bed #if defined(BED_MAXTEMP) && (TEMP_SENSOR_BED != 0) #if HEATER_BED_RAW_LO_TEMP > HEATER_BED_RAW_HI_TEMP if (current_temperature_bed_raw <= bed_maxttemp_raw) { #else if (current_temperature_bed_raw >= bed_maxttemp_raw) { #endif - bed_max_temp_error(); + set_temp_error(TempErrorSource::bed, 0, TempErrorType::max); } #endif -//ambient + //ambient #if defined(AMBIENT_MAXTEMP) && (TEMP_SENSOR_AMBIENT != 0) #if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP if (current_temperature_raw_ambient <= ambient_maxttemp_raw) { #else if (current_temperature_raw_ambient >= ambient_maxttemp_raw) { #endif - ambient_max_temp_error(); + set_temp_error(TempErrorSource::ambient, 0, TempErrorType::max); } #endif } + //! number of repeating the same state with consecutive step() calls //! used to slow down text switching struct alert_automaton_mintemp { @@ -1581,23 +1633,12 @@ static alert_automaton_mintemp alert_automaton_hotend(m2hotend), alert_automaton void check_min_temp_heater0() { -//heater #if HEATER_0_RAW_LO_TEMP > HEATER_0_RAW_HI_TEMP if (current_temperature_raw[0] >= minttemp_raw[0]) { #else if (current_temperature_raw[0] <= minttemp_raw[0]) { #endif - menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); - min_temp_error(0); - } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_HEATER) ) { - // no recovery, just force the user to restart the printer - // which is a safer variant than just continuing printing - // The automaton also checks for hysteresis - the temperature must have reached a few degrees above the MINTEMP, before - // we shall signalize, that MINTEMP has been fixed - // Code notice: normally the alert_automaton instance would have been placed here - // as static alert_automaton_mintemp alert_automaton_hotend, but - // due to stupid compiler that takes 16 more bytes. - alert_automaton_hotend.step(current_temperature[0], minttemp[0] + TEMP_HYSTERESIS); + set_temp_error(TempErrorSource::hotend, 0, TempErrorType::min); } } @@ -1608,12 +1649,7 @@ void check_min_temp_bed() #else if (current_temperature_bed_raw <= bed_minttemp_raw) { #endif - menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); - bed_min_temp_error(); - } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_BED) ){ - // no recovery, just force the user to restart the printer - // which is a safer variant than just continuing printing - alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS); + set_temp_error(TempErrorSource::bed, 0, TempErrorType::min); } } @@ -1625,72 +1661,70 @@ void check_min_temp_ambient() #else if (current_temperature_raw_ambient <= ambient_minttemp_raw) { #endif - ambient_min_temp_error(); + set_temp_error(TempErrorSource::ambient, 0, TempErrorType::min); } } #endif -void check_min_temp() +void handle_temp_error() { -static bool bCheckingOnHeater=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp) -static bool bCheckingOnBed=false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp) -static ShortTimer oTimer4minTempHeater; -static ShortTimer oTimer4minTempBed; + // TODO: Stop and the various *_error() functions need an overhaul! + // The heaters are kept disabled already at a higher level, making almost + // all the code inside the invidual handlers useless! -#ifdef AMBIENT_THERMISTOR -#ifdef AMBIENT_MINTEMP -check_min_temp_ambient(); -#endif -#if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP -if(current_temperature_raw_ambient>(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) // thermistor is NTC type -#else -if(current_temperature_raw_ambient=<(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) -#endif - { // ambient temperature is low -#endif //AMBIENT_THERMISTOR -// *** 'common' part of code for MK2.5 & MK3 -// * nozzle checking -if(target_temperature[active_extruder]>minttemp[active_extruder]) - { // ~ nozzle heating is on - bCheckingOnHeater=bCheckingOnHeater||(current_temperature[active_extruder]>(minttemp[active_extruder]+TEMP_HYSTERESIS)); // for eventually delay cutting - if(oTimer4minTempHeater.expired(HEATER_MINTEMP_DELAY)||(!oTimer4minTempHeater.running())||bCheckingOnHeater) - { - bCheckingOnHeater=true; // not necessary - check_min_temp_heater0(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active - } - } -else { // ~ nozzle heating is off - oTimer4minTempHeater.start(); - bCheckingOnHeater=false; - } -// * bed checking -if(target_temperature_bed>BED_MINTEMP) - { // ~ bed heating is on - bCheckingOnBed=bCheckingOnBed||(current_temperature_bed>(BED_MINTEMP+TEMP_HYSTERESIS)); // for eventually delay cutting - if(oTimer4minTempBed.expired(BED_MINTEMP_DELAY)||(!oTimer4minTempBed.running())||bCheckingOnBed) - { - bCheckingOnBed=true; // not necessary - check_min_temp_bed(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active - } - } -else { // ~ bed heating is off - oTimer4minTempBed.start(); - bCheckingOnBed=false; - } -// *** end of 'common' part -#ifdef AMBIENT_THERMISTOR - } -else { // ambient temperature is standard - check_min_temp_heater0(); - check_min_temp_bed(); - } -#endif //AMBIENT_THERMISTOR + // relay to the original handler + switch((TempErrorSource)temp_error_state.source) { + case TempErrorSource::hotend: + switch((TempErrorType)temp_error_state.type) { + case TempErrorType::min: + if(temp_error_state.assert) { + menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); + min_temp_error(temp_error_state.index); + } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_HEATER) ) { + // no recovery, just force the user to restart the printer + // which is a safer variant than just continuing printing + // The automaton also checks for hysteresis - the temperature must have reached a few degrees above the MINTEMP, before + // we shall signalize, that MINTEMP has been fixed + // Code notice: normally the alert_automaton instance would have been placed here + // as static alert_automaton_mintemp alert_automaton_hotend, but + alert_automaton_hotend.step(current_temperature[0], minttemp[0] + TEMP_HYSTERESIS); + } + break; + case TempErrorType::max: + max_temp_error(temp_error_state.index); + break; + } + break; + case TempErrorSource::bed: + switch((TempErrorType)temp_error_state.type) { + case TempErrorType::min: + if(temp_error_state.assert) { + menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); + bed_min_temp_error(); + } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_BED) ){ + // no recovery, just force the user to restart the printer + // which is a safer variant than just continuing printing + alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS); + } + break; + case TempErrorType::max: + bed_max_temp_error(); + break; + } + break; + case TempErrorSource::ambient: + switch((TempErrorType)temp_error_state.type) { + case TempErrorType::min: ambient_min_temp_error(); break; + case TempErrorType::max: ambient_max_temp_error(); break; + case TempErrorType::runaway: break; // not needed + } + break; + } } - + #ifdef PIDTEMP // Apply the scale factors to the PID values - float scalePID_i(float i) { return i*PID_dT; @@ -2051,13 +2085,14 @@ static void setIsrTargetTemperatures() /* Synchronize temperatures: - fetch updated values from temp_mgr_isr to current values - - update target temperatures for temp_mgr_isr regulation + - update target temperatures for temp_mgr_isr regulation *if* no temperature error is set This function is blocking: check temp_meas_ready before calling! */ static void updateTemperatures() { TempMgrGuard temp_mgr_guard; setCurrentTemperaturesFromIsr(); - setIsrTargetTemperatures(); + if(!temp_error_state.v) + setIsrTargetTemperatures(); temp_meas_ready = false; } @@ -2085,15 +2120,18 @@ static void temp_mgr_pid() pid_bed(current_temperature_bed_isr, target_temperature_bed_isr); } +void check_temp_raw(); + static void temp_mgr_isr() { // update *_isr temperatures from raw values for PID regulation setIsrTemperaturesFromRawValues(); - // TODO: this is now running inside an isr and cannot directly manipulate the lcd, - // this needs to disable temperatures and flag the error to be shown in manage_heater! - check_max_temp(); - check_min_temp(); + // clear the error assertion flag before checking again + temp_error_state.assert = false; + + // check min/max temp using raw values + check_temp_raw(); // PID regulation temp_mgr_pid(); @@ -2144,3 +2182,71 @@ void disable_heater() CRITICAL_SECTION_END; } + +void check_min_temp_raw() +{ + static bool bCheckingOnHeater = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp) + static bool bCheckingOnBed = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp) + static ShortTimer oTimer4minTempHeater; + static ShortTimer oTimer4minTempBed; + +#ifdef AMBIENT_THERMISTOR +#ifdef AMBIENT_MINTEMP + // we need to check ambient temperature + check_min_temp_ambient(); +#endif +#if AMBIENT_RAW_LO_TEMP > AMBIENT_RAW_HI_TEMP + if(current_temperature_raw_ambient>(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) // thermistor is NTC type +#else + if(current_temperature_raw_ambient=<(OVERSAMPLENR*MINTEMP_MINAMBIENT_RAW)) +#endif + { + // ambient temperature is low +#endif //AMBIENT_THERMISTOR + // *** 'common' part of code for MK2.5 & MK3 + // * nozzle checking + if(target_temperature_isr[active_extruder]>minttemp[active_extruder]) { + // ~ nozzle heating is on + bCheckingOnHeater=bCheckingOnHeater||(current_temperature_isr[active_extruder]>(minttemp[active_extruder]+TEMP_HYSTERESIS)); // for eventually delay cutting + if(oTimer4minTempHeater.expired(HEATER_MINTEMP_DELAY)||(!oTimer4minTempHeater.running())||bCheckingOnHeater) { + bCheckingOnHeater=true; // not necessary + check_min_temp_heater0(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active + } + } + else { + // ~ nozzle heating is off + oTimer4minTempHeater.start(); + bCheckingOnHeater=false; + } + // * bed checking + if(target_temperature_bed_isr>BED_MINTEMP) { + // ~ bed heating is on + bCheckingOnBed=bCheckingOnBed||(current_temperature_bed_isr>(BED_MINTEMP+TEMP_HYSTERESIS)); // for eventually delay cutting + if(oTimer4minTempBed.expired(BED_MINTEMP_DELAY)||(!oTimer4minTempBed.running())||bCheckingOnBed) { + bCheckingOnBed=true; // not necessary + check_min_temp_bed(); // delay is elapsed or temperature is/was over minTemp => periodical checking is active + } + } + else { + // ~ bed heating is off + oTimer4minTempBed.start(); + bCheckingOnBed=false; + } + // *** end of 'common' part +#ifdef AMBIENT_THERMISTOR + } + else { + // ambient temperature is standard + check_min_temp_heater0(); + check_min_temp_bed(); + } +#endif //AMBIENT_THERMISTOR +} + +void check_temp_raw() +{ + // order is relevant: check_min_temp_raw requires max to be reliable due to + // ambient temperature being used for low handling temperatures + check_max_temp_raw(); + check_min_temp_raw(); +} From de77a479066f885e569d08cd1c8288d846727d51 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 25 May 2022 01:09:51 +0200 Subject: [PATCH 27/95] Correct handling of preheat/runaway errors As for min/maxtemp, flag the error in the isr, then handle it in the user code calling the original handler. --- Firmware/temperature.cpp | 76 +++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index aef3e684e..d2cb7ddde 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -442,6 +442,7 @@ enum class TempErrorType : uint8_t { min, max, + preheat, runaway, }; @@ -454,8 +455,8 @@ volatile static union uint8_t error: 1; // error condition uint8_t assert: 1; // error is still asserted uint8_t source: 2; // source - uint8_t index: 2; // source index - uint8_t type: 2; // error type + uint8_t index: 1; // source index + uint8_t type: 3; // error type }; } temp_error_state; @@ -797,7 +798,7 @@ void soft_pwm_init() #if (defined (TEMP_RUNAWAY_BED_HYSTERESIS) && TEMP_RUNAWAY_BED_TIMEOUT > 0) || (defined (TEMP_RUNAWAY_EXTRUDER_HYSTERESIS) && TEMP_RUNAWAY_EXTRUDER_TIMEOUT > 0) void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _current_temperature, float _output, bool _isbed) { - float __delta; + float __delta; float __hysteresis = 0; uint16_t __timeout = 0; bool temp_runaway_check_active = false; @@ -881,11 +882,8 @@ void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _cu } if (__preheat_errors[_heater_id] > ((_isbed) ? 3 : 5)) - { - if (farm_mode) { prusa_statistics(0); } - temp_runaway_stop(true, _isbed); - if (farm_mode) { prusa_statistics(91); } - } + set_temp_error((_isbed?TempErrorSource::bed:TempErrorSource::hotend), _heater_id, TempErrorType::preheat); + __preheat_start[_heater_id] = _current_temperature; __preheat_counter[_heater_id] = 0; } @@ -922,11 +920,7 @@ void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _cu { temp_runaway_error_counter[_heater_id]++; if (temp_runaway_error_counter[_heater_id] * 2 > __timeout) - { - if (farm_mode) { prusa_statistics(0); } - temp_runaway_stop(false, _isbed); - if (farm_mode) { prusa_statistics(90); } - } + set_temp_error((_isbed?TempErrorSource::bed:TempErrorSource::hotend), _heater_id, TempErrorType::runaway); } } } @@ -955,6 +949,11 @@ void temp_runaway_stop(bool isPreheat, bool isBed) } Stop(); + + if (farm_mode) { + prusa_statistics(0); + prusa_statistics(isPreheat? 91 : 90); + } } #endif @@ -1673,10 +1672,10 @@ void handle_temp_error() // all the code inside the invidual handlers useless! // relay to the original handler - switch((TempErrorSource)temp_error_state.source) { - case TempErrorSource::hotend: - switch((TempErrorType)temp_error_state.type) { - case TempErrorType::min: + switch((TempErrorType)temp_error_state.type) { + case TempErrorType::min: + switch((TempErrorSource)temp_error_state.source) { + case TempErrorSource::hotend: if(temp_error_state.assert) { menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); min_temp_error(temp_error_state.index); @@ -1690,14 +1689,7 @@ void handle_temp_error() alert_automaton_hotend.step(current_temperature[0], minttemp[0] + TEMP_HYSTERESIS); } break; - case TempErrorType::max: - max_temp_error(temp_error_state.index); - break; - } - break; - case TempErrorSource::bed: - switch((TempErrorType)temp_error_state.type) { - case TempErrorType::min: + case TempErrorSource::bed: if(temp_error_state.assert) { menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); bed_min_temp_error(); @@ -1707,16 +1699,36 @@ void handle_temp_error() alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS); } break; - case TempErrorType::max: - bed_max_temp_error(); + case TempErrorSource::ambient: + ambient_min_temp_error(); break; } break; - case TempErrorSource::ambient: - switch((TempErrorType)temp_error_state.type) { - case TempErrorType::min: ambient_min_temp_error(); break; - case TempErrorType::max: ambient_max_temp_error(); break; - case TempErrorType::runaway: break; // not needed + case TempErrorType::max: + switch((TempErrorSource)temp_error_state.source) { + case TempErrorSource::hotend: + max_temp_error(temp_error_state.index); + break; + case TempErrorSource::bed: + bed_max_temp_error(); + break; + case TempErrorSource::ambient: + ambient_max_temp_error(); + break; + } + break; + case TempErrorType::preheat: + case TempErrorType::runaway: + switch((TempErrorSource)temp_error_state.source) { + case TempErrorSource::hotend: + case TempErrorSource::bed: + temp_runaway_stop( + ((TempErrorType)temp_error_state.type == TempErrorType::preheat), + ((TempErrorSource)temp_error_state.source == TempErrorSource::bed)); + break; + case TempErrorSource::ambient: + // not needed + break; } break; } From 283d5566f14eca280005ad21c5d13686a8e98ce4 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 25 May 2022 19:44:41 +0200 Subject: [PATCH 28/95] Limit the rate manage_heater() as it did previously Avoid running the user-level error handlers too fast. --- Firmware/temperature.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index d2cb7ddde..40b10f2dc 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -485,9 +485,13 @@ void manage_heater() wdt_reset(); #endif //WATCHDOG + // limit execution to the same rate as temp_mgr (low-level fault handling is already handled - + // any remaining error handling is just user-facing and can wait one extra cycle) + if(!temp_meas_ready) + return; + // syncronize temperatures with isr - if(temp_meas_ready) - updateTemperatures(); + updateTemperatures(); // handle temperature errors if(temp_error_state.v) From 026733e75f4c07449a7e78dcf83d6dba40a87ea9 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 25 May 2022 19:45:47 +0200 Subject: [PATCH 29/95] Improve comments --- Firmware/temperature.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 40b10f2dc..e93ffadba 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2107,13 +2107,15 @@ static void updateTemperatures() { TempMgrGuard temp_mgr_guard; setCurrentTemperaturesFromIsr(); - if(!temp_error_state.v) + if(!temp_error_state.v) { + // refuse to update target temperatures in any error condition! setIsrTargetTemperatures(); + } temp_meas_ready = false; } /* Convert raw values into actual temperatures for temp_mgr. The raw values are created in the ADC - interrupt context, while this function runs from the temp_mgr isr which is preemptible as + interrupt context, while this function runs from temp_mgr_isr which *is* preemptible as analog2temp is relatively slow */ static void setIsrTemperaturesFromRawValues() { From 61575995e043c564fd30b79e2dd22e82c625036e Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 25 May 2022 19:46:00 +0200 Subject: [PATCH 30/95] Isolate temp runaway checks from PID management --- Firmware/temperature.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index e93ffadba..c6c53510d 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1857,10 +1857,6 @@ static void pid_heater(uint8_t e, const float current, const int target) float pid_input; float pid_output; -#ifdef TEMP_RUNAWAY_EXTRUDER_HYSTERESIS - temp_runaway_check(e+1, target, current, (int)soft_pwm[e], false); -#endif - #ifdef PIDTEMP pid_input = current; @@ -1940,10 +1936,6 @@ static void pid_bed(const float current, const int target) float pid_input; float pid_output; -#ifdef TEMP_RUNAWAY_BED_HYSTERESIS - temp_runaway_check(0, target, current, (int)soft_pwm_bed, true); -#endif - #ifndef PIDTEMPBED if(_millis() - previous_millis_bed_heater < BED_CHECK_INTERVAL) return; @@ -2138,6 +2130,17 @@ static void temp_mgr_pid() pid_bed(current_temperature_bed_isr, target_temperature_bed_isr); } +static void check_temp_runaway() +{ +#ifdef TEMP_RUNAWAY_EXTRUDER_HYSTERESIS + for(uint8_t e = 0; e < EXTRUDERS; e++) + temp_runaway_check(e+1, target_temperature_isr[e], current_temperature_isr[e], soft_pwm[e], false); +#endif +#ifdef TEMP_RUNAWAY_BED_HYSTERESIS + temp_runaway_check(0, target_temperature_bed_isr, current_temperature_bed_isr, soft_pwm_bed, true); +#endif +} + void check_temp_raw(); static void temp_mgr_isr() @@ -2147,9 +2150,8 @@ static void temp_mgr_isr() // clear the error assertion flag before checking again temp_error_state.assert = false; - - // check min/max temp using raw values - check_temp_raw(); + check_temp_raw(); // check min/max temp using raw values + check_temp_runaway(); // classic temperature hysteresis check // PID regulation temp_mgr_pid(); From 3eda8b61eea7e70610b6d8df5a041d6e4de1e82a Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 26 May 2022 19:22:17 +0200 Subject: [PATCH 31/95] Include the model checker with hard-coded constants --- Firmware/temperature.cpp | 101 +++++++++++++++++- .../variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 3 + 2 files changed, 101 insertions(+), 3 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index c6c53510d..fc8af8d43 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -444,6 +444,7 @@ enum class TempErrorType : uint8_t max, preheat, runaway, + model, }; // error state (updated via set_temp_error from isr context) @@ -1735,6 +1736,26 @@ void handle_temp_error() break; } break; + case TempErrorType::model: +#ifdef TEMP_MODEL_CHECK + static bool is_new = true; + static bool beep_on = false; + printf_P(PSTR("TM: err:%u ass:%u\n"), (unsigned)temp_error_state.error, + (unsigned)temp_error_state.assert); + if(is_new) { + beep_on = true; + is_new = false; + } + WRITE(BEEPER, beep_on); + beep_on = !beep_on; + if(!temp_error_state.assert) { + printf_P(PSTR("TM: assertion cleared - resetting\n")); + temp_error_state.v = 0; + WRITE(BEEPER, LOW); + is_new = true; + } +#endif + break; } } @@ -2141,7 +2162,10 @@ static void check_temp_runaway() #endif } -void check_temp_raw(); +static void check_temp_raw(); +#ifdef TEMP_MODEL_CHECK +static void check_temp_model(); +#endif static void temp_mgr_isr() { @@ -2152,6 +2176,9 @@ static void temp_mgr_isr() temp_error_state.assert = false; check_temp_raw(); // check min/max temp using raw values check_temp_runaway(); // classic temperature hysteresis check +#ifdef TEMP_MODEL_CHECK + check_temp_model(); // model-based heater check +#endif // PID regulation temp_mgr_pid(); @@ -2203,7 +2230,7 @@ void disable_heater() CRITICAL_SECTION_END; } -void check_min_temp_raw() +static void check_min_temp_raw() { static bool bCheckingOnHeater = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over heaterMintemp) static bool bCheckingOnBed = false; // state variable, which allows to short no-checking delay (is set, when temperature is (first time) over bedMintemp) @@ -2263,10 +2290,78 @@ void check_min_temp_raw() #endif //AMBIENT_THERMISTOR } -void check_temp_raw() +static void check_temp_raw() { // order is relevant: check_min_temp_raw requires max to be reliable due to // ambient temperature being used for low handling temperatures check_max_temp_raw(); check_min_temp_raw(); } + +#ifdef TEMP_MODEL_CHECK +static const float TM_P = 38.; // heater power (W) +static const float TM_C = 11.9; // heatblock capacitance (J/K) +static const float TM_R = 27.; // heatblock resistance +static const float TM_Rf = -20.; // full-power fan resistance change +static const float TM_aC = -5; // ambient temperature correction (K) + +static const float TM_dTl = 2.1; // temperature transport delay +static const uint8_t TM_dTs = (TM_dTl / TEMP_MGR_INTV + 0.5); // temperature transport delay (samples) + +static const float TM_fS = 0.065; // simulation (1st-order IIR factor) +static const float TM_fE = 0.05; // error (1st-order IIR factor) + +static const float TM_dErr_p = 0.25; // error threshold (K, positive, actively heating) +static const float TM_dErr_n = 0.25; // error threshold (K, negative, cooling) + +static float TM_dT_buf[TM_dTs]; // transport delay buffer +static uint8_t TM_dT_idx = 0; // transport delay buffer index +static float TM_dErr = 0; // last error +static float TM_T = 0; // last temperature + +#ifndef MAX +#define MAX(A, B) (A >= B? A: B) +#endif +// samples required for settling the model (crude approximation) +static uint8_t TM_dT_smp = MAX(TM_dTs, MAX(3/TM_fS, 3/TM_fE)); + +static void check_temp_model() +{ + // input values + float heater_scale = (float)soft_pwm[0] / ((1 << 7) - 1); + float fan_scale = (float)soft_pwm_fan / ((1 << FAN_SOFT_PWM_BITS) - 1); + float cur_temp_heater = current_temperature_isr[0]; + float cur_temp_ambient = current_temperature_ambient_isr + TM_aC; + + // model invariants + float C_i = (TEMP_MGR_INTV / TM_C); + + float dP = TM_P * heater_scale; // current power [W] + float R = TM_R + TM_Rf * fan_scale; // resistance (constant + fan modulation) + float dPl = (cur_temp_heater - cur_temp_ambient) / R; // [W] leakage power + float dT = (dP - dPl) * C_i; // expected temperature difference (K) + + // filter and lag dT + uint8_t next_dT_idx = (TM_dT_idx == (TM_dTs - 1) ? 0: TM_dT_idx + 1); + float lag_dT = TM_dT_buf[next_dT_idx]; + float prev_dT = TM_dT_buf[TM_dT_idx]; + float dTf = (prev_dT * (1. - TM_fS)) + (dT * TM_fS); + TM_dT_buf[next_dT_idx] = dTf; + TM_dT_idx = next_dT_idx; + + // calculate and filter dErr + float dErr = (cur_temp_heater - TM_T) - lag_dT; + float dErrf = (TM_dErr * (1. - TM_fE)) + (dErr * TM_fE); + TM_T = cur_temp_heater; + TM_dErr = dErrf; + + // check and trigger errors + if(TM_dT_smp) { + // model not ready + --TM_dT_smp; + } else { + if(dErrf > TM_dErr_p || dErrf < -TM_dErr_n) + set_temp_error(TempErrorSource::hotend, 0, TempErrorType::model); + } +} +#endif diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index ddd40186f..b53fe9f57 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -417,6 +417,9 @@ #define TEMP_RUNAWAY_EXTRUDER_HYSTERESIS 15 #define TEMP_RUNAWAY_EXTRUDER_TIMEOUT 45 +// model-based temperature check +#define TEMP_MODEL_CHECK 1 + /*------------------------------------ MOTOR CURRENT SETTINGS *------------------------------------*/ From bc53bd5305f4ed3974482461b109880a808348d6 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 26 May 2022 19:34:33 +0200 Subject: [PATCH 32/95] Make the error threshold be sample-rate invariant --- Firmware/temperature.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index fc8af8d43..70dac692e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2299,25 +2299,25 @@ static void check_temp_raw() } #ifdef TEMP_MODEL_CHECK -static const float TM_P = 38.; // heater power (W) -static const float TM_C = 11.9; // heatblock capacitance (J/K) -static const float TM_R = 27.; // heatblock resistance -static const float TM_Rf = -20.; // full-power fan resistance change -static const float TM_aC = -5; // ambient temperature correction (K) +static const float TM_P = 38.; // heater power (W) +static const float TM_C = 11.9; // heatblock capacitance (J/K) +static const float TM_R = 27.; // heatblock resistance +static const float TM_Rf = -20.; // full-power fan resistance change +static const float TM_aC = -5; // ambient temperature correction (K) -static const float TM_dTl = 2.1; // temperature transport delay +static const float TM_dTl = 2.1; // temperature transport delay (s) static const uint8_t TM_dTs = (TM_dTl / TEMP_MGR_INTV + 0.5); // temperature transport delay (samples) static const float TM_fS = 0.065; // simulation (1st-order IIR factor) static const float TM_fE = 0.05; // error (1st-order IIR factor) -static const float TM_dErr_p = 0.25; // error threshold (K, positive, actively heating) -static const float TM_dErr_n = 0.25; // error threshold (K, negative, cooling) +static const float TM_err = 1.; // error threshold (K/s) +static const float TM_err_s = TM_err / (1./TEMP_MGR_INTV); // error threshold (per sample) -static float TM_dT_buf[TM_dTs]; // transport delay buffer -static uint8_t TM_dT_idx = 0; // transport delay buffer index -static float TM_dErr = 0; // last error -static float TM_T = 0; // last temperature +static float TM_dT_buf[TM_dTs]; // transport delay buffer +static uint8_t TM_dT_idx = 0; // transport delay buffer index +static float TM_dT_err = 0; // last error +static float TM_T = 0; // last temperature #ifndef MAX #define MAX(A, B) (A >= B? A: B) @@ -2349,18 +2349,18 @@ static void check_temp_model() TM_dT_buf[next_dT_idx] = dTf; TM_dT_idx = next_dT_idx; - // calculate and filter dErr - float dErr = (cur_temp_heater - TM_T) - lag_dT; - float dErrf = (TM_dErr * (1. - TM_fE)) + (dErr * TM_fE); + // calculate and filter dT_err + float dT_err = (cur_temp_heater - TM_T) - lag_dT; + float dT_err_f = (TM_dT_err * (1. - TM_fE)) + (dT_err * TM_fE); TM_T = cur_temp_heater; - TM_dErr = dErrf; + TM_dT_err = dT_err_f; // check and trigger errors if(TM_dT_smp) { // model not ready --TM_dT_smp; } else { - if(dErrf > TM_dErr_p || dErrf < -TM_dErr_n) + if(dT_err_f > TM_err_s || dT_err_f < -TM_err_s) set_temp_error(TempErrorSource::hotend, 0, TempErrorType::model); } } From 929843e295dccc89b7640d181b8d1e71d2355c10 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 26 May 2022 20:02:41 +0200 Subject: [PATCH 33/95] Switch two divisions to faster multiplications --- Firmware/temperature.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 70dac692e..43bcf9c0d 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2327,9 +2327,12 @@ static uint8_t TM_dT_smp = MAX(TM_dTs, MAX(3/TM_fS, 3/TM_fE)); static void check_temp_model() { + const float soft_pwm_inv = 1. / ((1 << 7) - 1); + const float soft_pwm_fan_inv = 1. / ((1 << FAN_SOFT_PWM_BITS) - 1); + // input values - float heater_scale = (float)soft_pwm[0] / ((1 << 7) - 1); - float fan_scale = (float)soft_pwm_fan / ((1 << FAN_SOFT_PWM_BITS) - 1); + float heater_scale = soft_pwm_inv * soft_pwm[0]; + float fan_scale = soft_pwm_fan_inv * soft_pwm_fan; float cur_temp_heater = current_temperature_isr[0]; float cur_temp_ambient = current_temperature_ambient_isr + TM_aC; From a15f2807d94ee4ae4a551442fb8151dca09d1967 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 26 May 2022 20:19:16 +0200 Subject: [PATCH 34/95] Allow to continue printing with TEMP_MODEL_CHECK_WARN_ONLY for debugging --- Firmware/temperature.cpp | 6 ++++++ Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 1 + 2 files changed, 7 insertions(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 43bcf9c0d..8ba78e3e3 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -467,8 +467,14 @@ volatile static union void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) { // keep disabling heaters and keep fans on as long as the condition is asserted +#ifdef TEMP_MODEL_CHECK_WARN_ONLY + if(type != TempErrorType::model) { +#endif disable_heater(); hotendFanSetFullSpeed(); +#ifdef TEMP_MODEL_CHECK_WARN_ONLY + } +#endif // set the error state temp_error_state.error = true; diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index b53fe9f57..461d65448 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -419,6 +419,7 @@ // model-based temperature check #define TEMP_MODEL_CHECK 1 +#define TEMP_MODEL_CHECK_WARN_ONLY 1 /*------------------------------------ MOTOR CURRENT SETTINGS From 70093fc9dc49395ecb128b82b8b3dc290a10cabc Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 26 May 2022 23:51:07 +0200 Subject: [PATCH 35/95] Allow to redefine basic model constants --- Firmware/temperature.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 8ba78e3e3..00cd8a8e1 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2305,11 +2305,13 @@ static void check_temp_raw() } #ifdef TEMP_MODEL_CHECK +#ifndef TEMP_MODEL_VARS static const float TM_P = 38.; // heater power (W) static const float TM_C = 11.9; // heatblock capacitance (J/K) static const float TM_R = 27.; // heatblock resistance static const float TM_Rf = -20.; // full-power fan resistance change static const float TM_aC = -5; // ambient temperature correction (K) +#endif static const float TM_dTl = 2.1; // temperature transport delay (s) static const uint8_t TM_dTs = (TM_dTl / TEMP_MGR_INTV + 0.5); // temperature transport delay (samples) From 0c1c350a9302693f0f514272cff30c7b703fbd4f Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 27 May 2022 00:10:23 +0200 Subject: [PATCH 36/95] Simplify one expression --- Firmware/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 00cd8a8e1..5a10df022 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2320,7 +2320,7 @@ static const float TM_fS = 0.065; // simulation (1st-order IIR factor) static const float TM_fE = 0.05; // error (1st-order IIR factor) static const float TM_err = 1.; // error threshold (K/s) -static const float TM_err_s = TM_err / (1./TEMP_MGR_INTV); // error threshold (per sample) +static const float TM_err_s = (TM_err * TEMP_MGR_INTV); // error threshold (per sample) static float TM_dT_buf[TM_dTs]; // transport delay buffer static uint8_t TM_dT_idx = 0; // transport delay buffer index From c15d599f716cf16383a50064f92768cabbd30d38 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 2 Jun 2022 21:06:32 +0200 Subject: [PATCH 37/95] Convert two PID_autotune strings to PROGMEM --- Firmware/temperature.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 5a10df022..98fcc4ee8 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -235,13 +235,13 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle ||(extruder < 0) #endif ){ - SERIAL_ECHOLN("PID Autotune failed. Bad extruder number."); + SERIAL_ECHOLNPGM("PID Autotune failed. Bad extruder number."); pid_tuning_finished = true; pid_cycle = 0; return; } - SERIAL_ECHOLN("PID Autotune start"); + SERIAL_ECHOLNPGM("PID Autotune start"); disable_heater(); // switch off all heaters. From 92418e9d1b5c226f8c5cca74e64c0ad7094b8997 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 2 Jun 2022 23:33:21 +0200 Subject: [PATCH 38/95] Do not perform PID management while autotune is running --- Firmware/temperature.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 98fcc4ee8..bfb4b74ad 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2187,7 +2187,8 @@ static void temp_mgr_isr() #endif // PID regulation - temp_mgr_pid(); + if (pid_tuning_finished) + temp_mgr_pid(); } ISR(TIMER5_COMPA_vect) From 442b2e16de8705c91127c2d4efaf010847491942 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 3 Jun 2022 12:29:29 +0200 Subject: [PATCH 39/95] Set pid_tuning_finished globally to true Use pid_tuning_finished as a flag to prevent automatic PID management. As a result, set the default start-up state to true and adjust the dependent code accordingly. --- Firmware/temperature.cpp | 2 +- Firmware/ultralcd.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index bfb4b74ad..f705c0089 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -85,7 +85,7 @@ float current_temperature_bed = 0.0; #ifdef PIDTEMP float _Kp, _Ki, _Kd; int pid_cycle, pid_number_of_cycles; - bool pid_tuning_finished = false; + bool pid_tuning_finished = true; #endif //PIDTEMP unsigned char soft_pwm_bed; diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index cb7e20f29..c233299a5 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -1018,6 +1018,7 @@ void lcd_commands() lcd_commands_step = 3; } if (lcd_commands_step == 3 && !blocks_queued()) { //PID calibration + pid_tuning_finished = false; // ensure we don't move to the next step early sprintf_P(cmd1, PSTR("M303 E0 S%3u"), pid_temp); // setting the correct target temperature (for visualization) is done in PID_autotune enquecommand(cmd1); @@ -1025,7 +1026,6 @@ void lcd_commands() lcd_commands_step = 2; } if (lcd_commands_step == 2 && pid_tuning_finished) { //saving to eeprom - pid_tuning_finished = false; custom_message_state = 0; lcd_setstatuspgm(_i("PID cal. finished"));////MSG_PID_FINISHED c=20 setAllTargetHotends(0); // reset all hotends temperature including the number displayed on the main screen From f1d88ebd40c0c9a966e27c487db49c514ff2dee2 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 3 Jun 2022 12:55:30 +0200 Subject: [PATCH 40/95] Protect pid_tuning_finished behind temperature.cpp Setting pid_tuning_finished can result in the heaters stuck to full power. As a result, we need to ensure that when PID management is disabled, heaters are also. --- Firmware/temperature.cpp | 17 +++++++++++++---- Firmware/temperature.h | 4 +++- Firmware/ultralcd.cpp | 4 ++-- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index f705c0089..48551eccd 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -85,7 +85,17 @@ float current_temperature_bed = 0.0; #ifdef PIDTEMP float _Kp, _Ki, _Kd; int pid_cycle, pid_number_of_cycles; - bool pid_tuning_finished = true; + static bool pid_tuning_finished = true; + + bool pidTuningRunning() { + return !pid_tuning_finished; + } + + void preparePidTuning() { + // ensure heaters are disabled before we switch off PID management! + disable_heater(); + pid_tuning_finished = false; + } #endif //PIDTEMP unsigned char soft_pwm_bed; @@ -207,8 +217,9 @@ bool checkAllHotends(void) // codegen bug causing a stack overwrite issue in process_commands() void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycles) { + preparePidTuning(); + pid_number_of_cycles = ncycles; - pid_tuning_finished = false; float input = 0.0; pid_cycle=0; bool heating = true; @@ -242,8 +253,6 @@ void __attribute__((noinline)) PID_autotune(float temp, int extruder, int ncycle } SERIAL_ECHOLNPGM("PID Autotune start"); - - disable_heater(); // switch off all heaters. if (extruder<0) { diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 08de851f3..c62d388ae 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -84,11 +84,13 @@ extern bool bedPWMDisabled; #ifdef PIDTEMP extern int pid_cycle, pid_number_of_cycles; extern float _Kp,_Ki,_Kd; - extern bool pid_tuning_finished; float scalePID_i(float i); float scalePID_d(float d); float unscalePID_i(float i); float unscalePID_d(float d); + + bool pidTuningRunning(); // returns true if PID tuning is still running + void preparePidTuning(); // non-blocking call to set "pidTuningRunning" to true immediately #endif diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index c233299a5..a7fe85fa9 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -1018,14 +1018,14 @@ void lcd_commands() lcd_commands_step = 3; } if (lcd_commands_step == 3 && !blocks_queued()) { //PID calibration - pid_tuning_finished = false; // ensure we don't move to the next step early + preparePidTuning(); // ensure we don't move to the next step early sprintf_P(cmd1, PSTR("M303 E0 S%3u"), pid_temp); // setting the correct target temperature (for visualization) is done in PID_autotune enquecommand(cmd1); lcd_setstatuspgm(_i("PID cal."));////MSG_PID_RUNNING c=20 lcd_commands_step = 2; } - if (lcd_commands_step == 2 && pid_tuning_finished) { //saving to eeprom + if (lcd_commands_step == 2 && !pidTuningRunning()) { //saving to eeprom custom_message_state = 0; lcd_setstatuspgm(_i("PID cal. finished"));////MSG_PID_FINISHED c=20 setAllTargetHotends(0); // reset all hotends temperature including the number displayed on the main screen From f82048977a729d279bf606d0c49da04691e0c606 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 9 Jun 2022 00:10:30 +0200 Subject: [PATCH 41/95] Start PWM timers _after_ initializing min/maxtemp ranges This would otherwise cause check_temp_raw() to operate on unitialized values and trigger failures too early. --- Firmware/temperature.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 48551eccd..4033f2c9e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -704,15 +704,6 @@ void soft_pwm_init() digitalWrite(MAX6675_SS,1); #endif - timer0_init(); //enables the heatbed timer. - - // timer2 already enabled earlier in the code - // now enable the COMPB temperature interrupt - OCR2B = 128; - ENABLE_SOFT_PWM_INTERRUPT(); - - timer4_init(); //for tone and Extruder fan PWM - #ifdef HEATER_0_MINTEMP minttemp[0] = HEATER_0_MINTEMP; while(analog2temp(minttemp_raw[0], 0) < HEATER_0_MINTEMP) { @@ -813,6 +804,15 @@ void soft_pwm_init() #endif } #endif //AMBIENT_MAXTEMP + + timer0_init(); //enables the heatbed timer. + + // timer2 already enabled earlier in the code + // now enable the COMPB temperature interrupt + OCR2B = 128; + ENABLE_SOFT_PWM_INTERRUPT(); + + timer4_init(); //for tone and Extruder fan PWM } #if (defined (TEMP_RUNAWAY_BED_HYSTERESIS) && TEMP_RUNAWAY_BED_TIMEOUT > 0) || (defined (TEMP_RUNAWAY_EXTRUDER_HYSTERESIS) && TEMP_RUNAWAY_EXTRUDER_TIMEOUT > 0) From 8220d0196b3c981e5c39e789b7a1d7b034e5afab Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 9 Jun 2022 11:08:54 +0200 Subject: [PATCH 42/95] Lock onto the first error source until cleared Do not overwrite the error source if the error flag is already set. As checks are performed in priority order, this ensures min/maxtemp user-level handlers are triggered even if the thermal model can detect an issue in the same cycle. This restores MAXTEMP handling, which was simply shadowed. --- Firmware/temperature.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4033f2c9e..49da6fab5 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -485,12 +485,16 @@ void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) } #endif - // set the error state + // set the initial error source + if(!temp_error_state.error) { + temp_error_state.source = (uint8_t)source; + temp_error_state.index = index; + temp_error_state.type = (uint8_t)type; + } + + // always set the error state temp_error_state.error = true; temp_error_state.assert = true; - temp_error_state.source = (uint8_t)source; - temp_error_state.index = index; - temp_error_state.type = (uint8_t)type; } void handle_temp_error(); From 690affe5a2331d84c466c23275ac878fb2f32d48 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 9 Jun 2022 11:43:46 +0200 Subject: [PATCH 43/95] Further enhance thermal error priorities When triggering a thermal error, allow higher-priority errors to override the initial error source. This allows a fatal error such as maxtemp to trigger to a full stop even if thermal runaway has already been triggered. Reorder error types according to their priority. --- Firmware/temperature.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 49da6fab5..f8ea4012e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -447,10 +447,11 @@ enum class TempErrorSource : uint8_t ambient, }; +// thermal error type (in order of decreasing priority!) enum class TempErrorType : uint8_t { - min, max, + min, preheat, runaway, model, @@ -485,8 +486,8 @@ void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) } #endif - // set the initial error source - if(!temp_error_state.error) { + // set the initial error source to the highest priority error + if(!temp_error_state.error || (uint8_t)type < temp_error_state.type) { temp_error_state.source = (uint8_t)source; temp_error_state.index = index; temp_error_state.type = (uint8_t)type; From fabf511b97cfb41c882462cadc887f0a0483b28a Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 14 Jun 2022 12:11:58 +0200 Subject: [PATCH 44/95] Add the ability to log continuously TEMP_MODEL_LOGGING enables D70 to record precisely each cycle of the temperature regulation for offline model simulation --- Firmware/Marlin_main.cpp | 8 ++ Firmware/temperature.cpp | 85 +++++++++++++++++++ Firmware/temperature.h | 3 + .../variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 1 + 4 files changed, 97 insertions(+) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 098435980..e8027aa3e 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9144,6 +9144,14 @@ Sigma_Exit: }; #endif +#ifdef TEMP_MODEL_LOGGING + case 70: { + if(code_seen('I')) + temp_model_log_enable(code_value_short()); + break; + } +#endif + #ifdef HEATBED_ANALYSIS /*! diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index f8ea4012e..d9353d2ea 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -500,6 +500,11 @@ void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) void handle_temp_error(); +#ifdef TEMP_MODEL_LOGGING +static void temp_model_log_usr(); +static void temp_model_log_isr(); +#endif + void manage_heater() { #ifdef WATCHDOG @@ -520,6 +525,10 @@ void manage_heater() // periodically check fans checkFans(); + +#ifdef TEMP_MODEL_LOGGING + temp_model_log_usr(); +#endif } #define PGM_RD_W(x) (short)pgm_read_word(&x) @@ -2200,6 +2209,10 @@ static void temp_mgr_isr() check_temp_model(); // model-based heater check #endif +#ifdef TEMP_MODEL_LOGGING + temp_model_log_isr(); +#endif + // PID regulation if (pid_tuning_finished) temp_mgr_pid(); @@ -2390,4 +2403,76 @@ static void check_temp_model() set_temp_error(TempErrorSource::hotend, 0, TempErrorType::model); } } + +#ifdef TEMP_MODEL_LOGGING +static struct +{ + volatile struct + { + uint32_t stamp; + int8_t delta_ms; + uint8_t counter; + uint8_t cur_pwm; + float cur_temp; + float cur_amb; + } entry; + + uint8_t serial; + bool enabled; +} temp_model_log_buf; + +static void temp_model_log_usr() +{ + if(!temp_model_log_buf.enabled) + return; + + uint8_t counter = temp_model_log_buf.entry.counter; + if (counter == temp_model_log_buf.serial) + return; + + int8_t delta_ms; + uint8_t cur_pwm; + float cur_temp; + float cur_amb; + { + TempMgrGuard temp_mgr_guard; + delta_ms = temp_model_log_buf.entry.delta_ms; + counter = temp_model_log_buf.entry.counter; + cur_pwm = temp_model_log_buf.entry.cur_pwm; + cur_temp = temp_model_log_buf.entry.cur_temp; + cur_amb = temp_model_log_buf.entry.cur_amb; + } + + uint8_t d = counter - temp_model_log_buf.serial; + temp_model_log_buf.serial = counter; + + printf_P(PSTR("TML %d %d %x %lx %lx\n"), (unsigned)d - 1, (int)delta_ms + 1, (int)cur_pwm, + *(unsigned long*)&cur_temp, *(unsigned long*)&cur_amb); +} + +static void temp_model_log_isr() +{ + if(!temp_model_log_buf.enabled) + return; + + uint32_t stamp = _millis(); + uint8_t delta_ms = stamp - temp_model_log_buf.entry.stamp - (TEMP_MGR_INTV * 1000); + temp_model_log_buf.entry.stamp = stamp; + + ++temp_model_log_buf.entry.counter; + temp_model_log_buf.entry.delta_ms = delta_ms; + temp_model_log_buf.entry.cur_pwm = soft_pwm[0]; + temp_model_log_buf.entry.cur_temp = current_temperature_isr[0]; + temp_model_log_buf.entry.cur_amb = current_temperature_ambient_isr; +} + +void temp_model_log_enable(bool enable) +{ + if(enable) { + TempMgrGuard temp_mgr_guard; + temp_model_log_buf.entry.stamp = _millis(); + } + temp_model_log_buf.enabled = enable; +} +#endif #endif diff --git a/Firmware/temperature.h b/Firmware/temperature.h index c62d388ae..558c1fe27 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -228,6 +228,9 @@ FORCE_INLINE void autotempShutdown(){ } void PID_autotune(float temp, int extruder, int ncycles); +#ifdef TEMP_MODEL_LOGGING +void temp_model_log_enable(bool enable); +#endif #ifdef FAN_SOFT_PWM extern unsigned char fanSpeedSoftPwm; diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index 461d65448..205e43f0e 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -420,6 +420,7 @@ // model-based temperature check #define TEMP_MODEL_CHECK 1 #define TEMP_MODEL_CHECK_WARN_ONLY 1 +#define TEMP_MODEL_LOGGING 1 /*------------------------------------ MOTOR CURRENT SETTINGS From b0b2ff5f9e3bbd0df2e9aecc68cf5d9f7666f0a6 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 24 Jun 2022 16:04:00 +0200 Subject: [PATCH 45/95] Rewrite/modularize the model checker - Allow all parameters to be changed at runtime through M310 - Move the model prototypes into a separate temp_model.h header - Allow the checked to be enabled/disabled at runtime - Introduce a warning threshold --- Firmware/Marlin_main.cpp | 59 ++- Firmware/temp_model.h | 110 +++++ Firmware/temperature.cpp | 399 +++++++++++------- Firmware/temperature.h | 11 +- .../variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 30 +- 5 files changed, 462 insertions(+), 147 deletions(-) create mode 100644 Firmware/temp_model.h diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index e8027aa3e..ed71ce352 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7760,6 +7760,54 @@ Sigma_Exit: PID_autotune(temp, e, c); } break; + + /*! + ### M310 - Temperature model + #### Usage + + M310 [ C ] [ P ] [ I R ] [ S ] [ E ] [ W ] [ A ] [ T ] + + #### Parameters + - `P` - power + - `C` - capacitance + - `I` - resistance index position + - `R` - resistance value (requires `I`) + - `S` - set 0=disable 1=enable (default) + - `E` - error threshold (define min/max values in variants) + - `W` - warning threshold (define min/max values in variants) + - `T` - ambient temperature correction + - `A` - autotune C+R values + */ + case 310: + { + // parse all parameters + float P = NAN, C = NAN, R = NAN, E = NAN, W = NAN, T = NAN, A = NAN; + int8_t I = -1, S = -1; + if(code_seen('C')) C = code_value(); + if(code_seen('P')) P = code_value(); + if(code_seen('I')) I = code_value_short(); + if(code_seen('R')) R = code_value(); + if(code_seen('S')) S = code_value_short(); + if(code_seen('E')) E = code_value(); + if(code_seen('W')) W = code_value(); + if(code_seen('T')) T = code_value(); + if(code_seen('A')) A = code_value(); + + // report values if nothing has been requested + if(isnan(C) && isnan(P) && isnan(R) && isnan(E) && isnan(W) && isnan(T) && isnan(A) && I < 0 && S < 0) { + temp_model_report_settings(); + break; + } + + // update all set parameters + if(S >= 0) temp_model_set_enabled(S); + if(!isnan(C) || !isnan(P) || !isnan(T) || !isnan(W) || !isnan(E)) temp_model_set_params(C, P, T, W, E); + if(I >= 0 && !isnan(R)) temp_model_set_resistance(I, R); + + // run autotune + if(!isnan(A)) temp_model_autotune(A != 0? A: NAN); + } + break; /*! ### M400 - Wait for all moves to finish M400: Wait for current moves to finish @@ -9144,7 +9192,16 @@ Sigma_Exit: }; #endif -#ifdef TEMP_MODEL_LOGGING +#ifdef TEMP_MODEL_DEBUG + /*! + ## D70 - Enable low-level temperature model logging for offline simulation + #### Usage + + D70 [ I ] + + #### Parameters + - `I` - Enable 0-1 (default 0) + */ case 70: { if(code_seen('I')) temp_model_log_enable(code_value_short()); diff --git a/Firmware/temp_model.h b/Firmware/temp_model.h new file mode 100644 index 000000000..30445f0f9 --- /dev/null +++ b/Firmware/temp_model.h @@ -0,0 +1,110 @@ +// model-based temperature safety checker declarations +#ifndef TEMP_MGR_INTV +#error "this file is not a public interface, it should be used *only* within temperature.cpp!" +#endif + +#include "planner.h" + +constexpr uint8_t TEMP_MODEL_CAL_S = 60; // Maximum recording lenght during calibration (s) +constexpr float TEMP_MODEL_fS = 0.065; // simulation filter (1st-order IIR factor) +constexpr float TEMP_MODEL_fE = 0.05; // error filter (1st-order IIR factor) + +// transport delay buffer size (samples) +constexpr uint8_t TEMP_MODEL_LAG_SIZE = (TEMP_MODEL_LAG / TEMP_MGR_INTV + 0.5); + +// resistance values for all fan levels +constexpr uint8_t TEMP_MODEL_R_SIZE = (1 << FAN_SOFT_PWM_BITS); + +namespace temp_model { + +// recording scratch buffer +struct rec_entry +{ + float temp; // heater temperature + uint8_t pwm; // heater PWM +}; + +constexpr uint16_t rec_buffer_size = TEMP_MODEL_CAL_S / TEMP_MGR_INTV; +static rec_entry* const rec_buffer = (rec_entry*)block_buffer; // oh-hey, free memory! +static_assert(sizeof(rec_entry[rec_buffer_size]) <= sizeof(block_buffer), + "recording length too long to fit within available buffer"); + +struct model_data +{ + // temporary buffers + float dT_lag_buf[TEMP_MODEL_LAG_SIZE]; // transport delay buffer + uint8_t dT_lag_idx = 0; // transport delay buffer index + float dT_err_prev = 0; // previous temperature delta error + float T_prev = 0; // last temperature extruder + + // configurable parameters + float P; // heater power (W) + float C; // heatblock capacitance (J/K) + float R[TEMP_MODEL_R_SIZE]; // heatblock resistance for all fan levels (K/W) + float Ta_corr; // ambient temperature correction (K) + + // thresholds + float warn; // pre-warning threshold (K/s) + float err; // error threshold (K/s) + + // status flags + union + { + bool flags; + struct + { + bool uninitialized: 1; // model is not initialized + bool error: 1; // error threshold set + bool warning: 1; // warning threshold set + } flag_bits; + }; + + // pre-computed values (initialized via reset) + float C_i; // heatblock capacitance (precomputed dT/C) + float warn_s; // pre-warning threshold (per sample) + float err_s; // error threshold (per sample) + + // simulation functions + void reset(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp); + void step(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp); +}; + +static bool enabled; // model check enabled +static model_data data; // default heater data + +static void init(); // initialize and setup the model subsystem +static bool calibrated(); // return calibration/model validity status +static void check(); // check and trigger errors or warnings based on current state + +// warning state (updated from from isr context) +volatile static struct +{ + float dT_err; // temperature delta error (per sample) + bool warning: 1; // warning condition + bool assert: 1; // warning is still asserted +} warning_state; + +static void handle_warning(); // handle warnings from user context + +#ifdef TEMP_MODEL_DEBUG +static struct +{ + volatile struct + { + uint32_t stamp; + int8_t delta_ms; + uint8_t counter; + uint8_t cur_pwm; + float cur_temp; + float cur_amb; + } entry; + + uint8_t serial; + bool enabled; +} log_buf; + +static void log_usr(); // user log handler +static void log_isr(); // isr log handler +#endif + +} // namespace temp_model diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index d9353d2ea..f306618f0 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -48,6 +48,19 @@ #error "ADC_OVRSAMPL oversampling must match OVERSAMPLENR" #endif +// temperature manager timer configuration +#define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz +#define TIMER5_PRESCALE 256 +#define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU)) +#define TEMP_MGR_INTERRUPT_STATE() (TIMSK5 & (1<= B? A: B) -#endif -// samples required for settling the model (crude approximation) -static uint8_t TM_dT_smp = MAX(TM_dTs, MAX(3/TM_fS, 3/TM_fE)); - -static void check_temp_model() +void model_data::reset(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp) { - const float soft_pwm_inv = 1. / ((1 << 7) - 1); - const float soft_pwm_fan_inv = 1. / ((1 << FAN_SOFT_PWM_BITS) - 1); + // pre-compute invariant values + C_i = (TEMP_MGR_INTV / C); + warn_s = warn * TEMP_MGR_INTV; + err_s = err * TEMP_MGR_INTV; + + // initial values + memset(dT_lag_buf, 0, sizeof(dT_lag_buf)); + dT_lag_idx = 0; + dT_err_prev = 0; + T_prev = heater_temp; + + // perform one step to initialize the first delta + step(heater_pwm, fan_pwm, heater_temp, ambient_temp); + + // clear the initialization flag + flag_bits.uninitialized = false; +} + +void model_data::step(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp) +{ + constexpr float soft_pwm_inv = 1. / ((1 << 7) - 1); // input values - float heater_scale = soft_pwm_inv * soft_pwm[0]; - float fan_scale = soft_pwm_fan_inv * soft_pwm_fan; - float cur_temp_heater = current_temperature_isr[0]; - float cur_temp_ambient = current_temperature_ambient_isr + TM_aC; + const float heater_scale = soft_pwm_inv * heater_pwm; + const float cur_heater_temp = heater_temp; + const float cur_ambient_temp = ambient_temp + Ta_corr; + const float cur_R = R[fan_pwm]; // resistance at current fan power (K/W) - // model invariants - float C_i = (TEMP_MGR_INTV / TM_C); - - float dP = TM_P * heater_scale; // current power [W] - float R = TM_R + TM_Rf * fan_scale; // resistance (constant + fan modulation) - float dPl = (cur_temp_heater - cur_temp_ambient) / R; // [W] leakage power + float dP = P * heater_scale; // current power [W] + float dPl = (cur_heater_temp - cur_ambient_temp) / cur_R; // [W] leakage power float dT = (dP - dPl) * C_i; // expected temperature difference (K) // filter and lag dT - uint8_t next_dT_idx = (TM_dT_idx == (TM_dTs - 1) ? 0: TM_dT_idx + 1); - float lag_dT = TM_dT_buf[next_dT_idx]; - float prev_dT = TM_dT_buf[TM_dT_idx]; - float dTf = (prev_dT * (1. - TM_fS)) + (dT * TM_fS); - TM_dT_buf[next_dT_idx] = dTf; - TM_dT_idx = next_dT_idx; + uint8_t dT_next_idx = (dT_lag_idx == (TEMP_MODEL_LAG_SIZE - 1) ? 0: dT_lag_idx + 1); + float dT_lag = dT_lag_buf[dT_next_idx]; + float dT_lag_prev = dT_lag_buf[dT_lag_idx]; + float dT_f = (dT_lag_prev * (1.f - TEMP_MODEL_fS)) + (dT * TEMP_MODEL_fS); + dT_lag_buf[dT_next_idx] = dT_f; + dT_lag_idx = dT_next_idx; // calculate and filter dT_err - float dT_err = (cur_temp_heater - TM_T) - lag_dT; - float dT_err_f = (TM_dT_err * (1. - TM_fE)) + (dT_err * TM_fE); - TM_T = cur_temp_heater; - TM_dT_err = dT_err_f; + float dT_err = (cur_heater_temp - T_prev) - dT_lag; + float dT_err_f = (dT_err_prev * (1.f - TEMP_MODEL_fE)) + (dT_err * TEMP_MODEL_fE); + T_prev = cur_heater_temp; + dT_err_prev = dT_err_f; // check and trigger errors - if(TM_dT_smp) { - // model not ready - --TM_dT_smp; - } else { - if(dT_err_f > TM_err_s || dT_err_f < -TM_err_s) - set_temp_error(TempErrorSource::hotend, 0, TempErrorType::model); + flag_bits.error = (fabsf(dT_err_f) > err_s); + flag_bits.warning = (fabsf(dT_err_f) > warn_s); +} + +// verify calibration status and trigger a model reset if valid +void setup() +{ + if(!calibrated()) enabled = false; + data.flag_bits.uninitialized = true; +} + +void init() +{ + // TODO: initialize the model with eeprom values + data.P = TEMP_MODEL_P; + data.C = TEMP_MODEL_C; + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) + data.R[i] = TEMP_MODEL_R; + data.Ta_corr = TEMP_MODEL_Ta_corr; + data.warn = TEMP_MODEL_W; + data.err = TEMP_MODEL_E; + + enabled = true; + setup(); +} + +bool calibrated() +{ + if(!(data.P >= 0)) return false; + if(!(data.C >= 0)) return false; + if(!(data.Ta_corr != NAN)) return false; + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) { + if(!(temp_model::data.R[i] >= 0)) + return false; + } + if(!(data.warn != NAN)) return false; + if(!(data.err != NAN)) return false; + return true; +} + +void check() +{ + if(!enabled) return; + + uint8_t heater_pwm = soft_pwm[0]; + uint8_t fan_pwm = soft_pwm_fan; + float heater_temp = current_temperature_isr[0]; + float ambient_temp = current_temperature_ambient_isr; + + // check if a reset is required to seed the model: this needs to be done with valid + // ADC values, so we can't do that directly in init() + if(data.flag_bits.uninitialized) + data.reset(heater_pwm, fan_pwm, heater_temp, ambient_temp); + + // step the model + data.step(heater_pwm, fan_pwm, heater_temp, ambient_temp); + + // handle errors + if(data.flag_bits.error) + set_temp_error(TempErrorSource::hotend, 0, TempErrorType::model); + + // handle warning conditions as lower-priority but with greater feedback + warning_state.assert = data.flag_bits.warning; + if(warning_state.assert) { + warning_state.warning = true; + warning_state.dT_err = temp_model::data.dT_err_prev; } } -#ifdef TEMP_MODEL_LOGGING -static struct +void handle_warning() { - volatile struct + // update values + float warn = data.warn; + float dT_err; { - uint32_t stamp; - int8_t delta_ms; - uint8_t counter; - uint8_t cur_pwm; - float cur_temp; - float cur_amb; - } entry; + TempMgrGuard temp_mgr_guard; + dT_err = warning_state.dT_err; + } + dT_err /= TEMP_MGR_INTV; // per-sample => K/s - uint8_t serial; - bool enabled; -} temp_model_log_buf; + // TODO: alert the user on the lcd + printf_P(PSTR("TM: error |%f|>%f\n"), (double)dT_err, (double)warn); -static void temp_model_log_usr() + static bool beeper = false; + if(warning_state.assert) { + // beep periodically + beeper = !beeper; + WRITE(BEEPER, beeper); + } else { + // warning cleared, reset state + warning_state.warning = false; + beeper = false; + WRITE(BEEPER, LOW); + } +} + +#ifdef TEMP_MODEL_DEBUG +void log_usr() { - if(!temp_model_log_buf.enabled) - return; + if(!log_buf.enabled) return; - uint8_t counter = temp_model_log_buf.entry.counter; - if (counter == temp_model_log_buf.serial) - return; + uint8_t counter = log_buf.entry.counter; + if (counter == log_buf.serial) return; int8_t delta_ms; uint8_t cur_pwm; - float cur_temp; - float cur_amb; + + // avoid strict-aliasing warnings + union { float cur_temp; uint32_t cur_temp_b; }; + union { float cur_amb; uint32_t cur_amb_b; }; + { TempMgrGuard temp_mgr_guard; - delta_ms = temp_model_log_buf.entry.delta_ms; - counter = temp_model_log_buf.entry.counter; - cur_pwm = temp_model_log_buf.entry.cur_pwm; - cur_temp = temp_model_log_buf.entry.cur_temp; - cur_amb = temp_model_log_buf.entry.cur_amb; + delta_ms = log_buf.entry.delta_ms; + counter = log_buf.entry.counter; + cur_pwm = log_buf.entry.cur_pwm; + cur_temp = log_buf.entry.cur_temp; + cur_amb = log_buf.entry.cur_amb; } - uint8_t d = counter - temp_model_log_buf.serial; - temp_model_log_buf.serial = counter; + uint8_t d = counter - log_buf.serial; + log_buf.serial = counter; - printf_P(PSTR("TML %d %d %x %lx %lx\n"), (unsigned)d - 1, (int)delta_ms + 1, (int)cur_pwm, - *(unsigned long*)&cur_temp, *(unsigned long*)&cur_amb); + printf_P(PSTR("TML %d %d %x %lx %lx\n"), (unsigned)d - 1, (int)delta_ms + 1, + (int)cur_pwm, (unsigned long)cur_temp_b, (unsigned long)cur_amb_b); } -static void temp_model_log_isr() +void log_isr() { - if(!temp_model_log_buf.enabled) - return; + if(!log_buf.enabled) return; uint32_t stamp = _millis(); - uint8_t delta_ms = stamp - temp_model_log_buf.entry.stamp - (TEMP_MGR_INTV * 1000); - temp_model_log_buf.entry.stamp = stamp; + uint8_t delta_ms = stamp - log_buf.entry.stamp - (TEMP_MGR_INTV * 1000); + log_buf.entry.stamp = stamp; - ++temp_model_log_buf.entry.counter; - temp_model_log_buf.entry.delta_ms = delta_ms; - temp_model_log_buf.entry.cur_pwm = soft_pwm[0]; - temp_model_log_buf.entry.cur_temp = current_temperature_isr[0]; - temp_model_log_buf.entry.cur_amb = current_temperature_ambient_isr; + ++log_buf.entry.counter; + log_buf.entry.delta_ms = delta_ms; + log_buf.entry.cur_pwm = soft_pwm[0]; + log_buf.entry.cur_temp = current_temperature_isr[0]; + log_buf.entry.cur_amb = current_temperature_ambient_isr; +} +#endif + +} // namespace temp_model + +void temp_model_set_enabled(bool enabled) +{ + // set the enabled flag + { + TempMgrGuard temp_mgr_guard; + temp_model::enabled = enabled; + temp_model::setup(); + } + + // verify that the model has been enabled + if(enabled && !temp_model::enabled) + SERIAL_ECHOLNPGM("TM: invalid parameters, cannot enable"); } +void temp_model_set_params(float C, float P, float Ta_corr, float warn, float err) +{ + TempMgrGuard temp_mgr_guard; + if(!isnan(C) && C > 0) temp_model::data.C = C; + if(!isnan(P) && P > 0) temp_model::data.P = P; + if(!isnan(Ta_corr)) temp_model::data.Ta_corr = Ta_corr; + if(!isnan(err) && err > 0) temp_model::data.err = err; + if(!isnan(warn) && warn > 0) temp_model::data.warn = warn; + + // ensure warn <= err + if (temp_model::data.warn > temp_model::data.err) + temp_model::data.warn = temp_model::data.err; + + temp_model::setup(); +} + +void temp_model_set_resistance(uint8_t index, float R) +{ + if(index >= TEMP_MODEL_R_SIZE || R <= 0) + return; + + TempMgrGuard temp_mgr_guard; + temp_model::data.R[index] = R; + temp_model::setup(); +} + +void temp_model_report_settings() +{ + printf_P(PSTR("%STemperature Model settings:\n" + "%S M310 P%.2f C%.2f S%u E%.2f W%.2f T%.2f\n"), + echomagic, echomagic, (double)temp_model::data.P, (double)temp_model::data.C, (unsigned)temp_model::enabled, + (double)temp_model::data.err, (double)temp_model::data.warn, (double)temp_model::data.Ta_corr); + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) + printf_P(PSTR("%S M310 I%u R%.2f\n"), echomagic, (unsigned)i, (double)temp_model::data.R[i]); +} + +void temp_model_autotune(float temp) +{ + // TODO +} + +#ifdef TEMP_MODEL_DEBUG void temp_model_log_enable(bool enable) { if(enable) { TempMgrGuard temp_mgr_guard; - temp_model_log_buf.entry.stamp = _millis(); + temp_model::log_buf.entry.stamp = _millis(); } - temp_model_log_buf.enabled = enable; + temp_model::log_buf.enabled = enable; } #endif #endif diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 558c1fe27..ad170ecf3 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -228,9 +228,18 @@ FORCE_INLINE void autotempShutdown(){ } void PID_autotune(float temp, int extruder, int ncycles); -#ifdef TEMP_MODEL_LOGGING + +#ifdef TEMP_MODEL +void temp_model_set_enabled(bool enabled); +void temp_model_set_params(float C = NAN, float P = NAN, float Ta_corr = NAN, float warn = NAN, float err = NAN); +void temp_model_set_resistance(uint8_t index, float R); +void temp_model_report_settings(); +void temp_model_autotune(float temp = NAN); + +#ifdef TEMP_MODEL_DEBUG void temp_model_log_enable(bool enable); #endif +#endif #ifdef FAN_SOFT_PWM extern unsigned char fanSpeedSoftPwm; diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index 205e43f0e..574689ccb 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -418,9 +418,33 @@ #define TEMP_RUNAWAY_EXTRUDER_TIMEOUT 45 // model-based temperature check -#define TEMP_MODEL_CHECK 1 -#define TEMP_MODEL_CHECK_WARN_ONLY 1 -#define TEMP_MODEL_LOGGING 1 +#define TEMP_MODEL 1 // enable model-based temperature checks +#define TEMP_MODEL_DEBUG 1 // extended runtime logging + +#define TEMP_MODEL_P 38. // heater power (W) + +#define TEMP_MODEL_C 11. // initial guess for heatblock capacitance (J/K) +#define TEMP_MODEL_Cl 5 // C estimation lower limit +#define TEMP_MODEL_Ch 20 // C estimation upper limit +#define TEMP_MODEL_C_thr 0.01 // C estimation iteration threshold +#define TEMP_MODEL_C_itr 30 // C estimation iteration limit + +#define TEMP_MODEL_R 25 // initial guess for heatblock resistance (K/W) +#define TEMP_MODEL_Rl 5 // R estimation lower limit +#define TEMP_MODEL_Rh 50 // R estimation upper limit +#define TEMP_MODEL_R_thr 0.01 // R estimation iteration threshold +#define TEMP_MODEL_R_itr 30 // R estimation iteration limit + +#define TEMP_MODEL_Rf_D -15 // initial guess for resistance change with full-power fan +#define TEMP_MODEL_Ta_corr -7 // Default ambient temperature correction +#define TEMP_MODEL_LAG 2.1 // Temperature transport delay (s) + +#define TEMP_MODEL_W 1.2 // Default pre-warning threshold (K/s) +#define TEMP_MODEL_E 1.74 // Default error threshold (K/s) + +#define TEMP_MODEL_CAL_Th 230 // Default calibration working temperature (C) +#define TEMP_MODEL_CAL_Tl 50 // Default calibration cooling temperature (C) + /*------------------------------------ MOTOR CURRENT SETTINGS From 6832ec76484c440aaf5687ebc687b692879541d2 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 26 Jun 2022 13:13:59 +0200 Subject: [PATCH 46/95] Allow to save/restore temperature model settings This currently bypasses the ConfigurationStore, which doesn't fit the malin model nicely. temp_model is using it's own private copy directly. But maybe we should change this in the future. --- Firmware/ConfigurationStore.cpp | 13 ++++++ Firmware/Marlin_main.cpp | 9 ++-- Firmware/eeprom.h | 10 ++++- Firmware/temp_model.h | 1 - Firmware/temperature.cpp | 77 +++++++++++++++++++++++---------- Firmware/temperature.h | 5 +++ 6 files changed, 87 insertions(+), 28 deletions(-) diff --git a/Firmware/ConfigurationStore.cpp b/Firmware/ConfigurationStore.cpp index 77ef6df66..0917ee181 100644 --- a/Firmware/ConfigurationStore.cpp +++ b/Firmware/ConfigurationStore.cpp @@ -75,6 +75,9 @@ void Config_StoreSettings() if (EEPROM_writeData(reinterpret_cast(EEPROM_M500_base),reinterpret_cast(&cs),sizeof(cs),0), "cs, invalid version") { +#ifdef TEMP_MODEL + temp_model_save_settings(); +#endif strcpy(cs.version,EEPROM_VERSION); //!< validate data if write succeed EEPROM_writeData(reinterpret_cast(EEPROM_M500_base->version), reinterpret_cast(cs.version), sizeof(cs.version), "cs.version valid"); } @@ -173,6 +176,9 @@ void Config_PrintSettings(uint8_t level) printf_P(PSTR( "%SArc Settings: P:Max length(mm) S:Min length (mm) N:Corrections R:Min segments F:Segments/sec.\n%S M214 P%.2f S%.2f N%d R%d F%d\n"), echomagic, echomagic, cs.mm_per_arc_segment, cs.min_mm_per_arc_segment, cs.n_arc_correction, cs.min_arc_segments, cs.arc_segments_per_sec); +#ifdef TEMP_MODEL + temp_model_report_settings(); +#endif } #endif @@ -321,6 +327,10 @@ bool Config_RetrieveSettings() // Call updatePID (similar to when we have processed M301) updatePID(); +#ifdef TEMP_MODEL + temp_model_load_settings(); +#endif + SERIAL_ECHO_START; SERIAL_ECHOLNPGM("Stored settings retrieved"); } @@ -353,6 +363,9 @@ void Config_ResetDefault() #ifdef PIDTEMP updatePID(); #endif//PIDTEMP +#ifdef TEMP_MODEL + temp_model_reset_settings(); +#endif calculate_extruder_multipliers(); diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index ed71ce352..b3176d639 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7765,13 +7765,16 @@ Sigma_Exit: ### M310 - Temperature model #### Usage - M310 [ C ] [ P ] [ I R ] [ S ] [ E ] [ W ] [ A ] [ T ] + M310 + M310 [ I ] [ R ] + M310 [ P ] [ C ] [ S ] [ E ] [ W ] [ T ] + M310 [ A ] #### Parameters - - `P` - power - - `C` - capacitance - `I` - resistance index position - `R` - resistance value (requires `I`) + - `P` - power + - `C` - capacitance - `S` - set 0=disable 1=enable (default) - `E` - error threshold (define min/max values in variants) - `W` - warning threshold (define min/max values in variants) diff --git a/Firmware/eeprom.h b/Firmware/eeprom.h index abfe33b58..d3ef0a28f 100644 --- a/Firmware/eeprom.h +++ b/Firmware/eeprom.h @@ -550,8 +550,16 @@ static Sheets * const EEPROM_Sheets_base = (Sheets*)(EEPROM_SHEETS_BASE); #define EEPROM_ECOOL_ENABLE (EEPROM_JOB_ID-1) // uint8_t #define EEPROM_FW_CRASH_FLAG (EEPROM_ECOOL_ENABLE-1) // uint8_t +#define EEPROM_TEMP_MODEL_ENABLE (EEPROM_FW_CRASH_FLAG-1) // uint8_t +#define EEPROM_TEMP_MODEL_P (EEPROM_TEMP_MODEL_ENABLE-4) // float +#define EEPROM_TEMP_MODEL_C (EEPROM_TEMP_MODEL_P-4) // float +#define EEPROM_TEMP_MODEL_R (EEPROM_TEMP_MODEL_C-4*16) // float[16] +#define EEPROM_TEMP_MODEL_Ta_corr (EEPROM_TEMP_MODEL_R-4) // float +#define EEPROM_TEMP_MODEL_W (EEPROM_TEMP_MODEL_Ta_corr-4) // float +#define EEPROM_TEMP_MODEL_E (EEPROM_TEMP_MODEL_W-4) // float + //This is supposed to point to last item to allow EEPROM overrun check. Please update when adding new items. -#define EEPROM_LAST_ITEM EEPROM_FW_CRASH_FLAG +#define EEPROM_LAST_ITEM EEPROM_TEMP_MODEL_E // !!!!! // !!!!! this is end of EEPROM section ... all updates MUST BE inserted before this mark !!!!! // !!!!! diff --git a/Firmware/temp_model.h b/Firmware/temp_model.h index 30445f0f9..5fed2be7a 100644 --- a/Firmware/temp_model.h +++ b/Firmware/temp_model.h @@ -72,7 +72,6 @@ struct model_data static bool enabled; // model check enabled static model_data data; // default heater data -static void init(); // initialize and setup the model subsystem static bool calibrated(); // return calibration/model validity status static void check(); // check and trigger errors or warnings based on current state diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index f306618f0..904085680 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1891,10 +1891,6 @@ void temp_mgr_init() TCNT5 = 0; OCR5A = TIMER5_OCRA_OVF; -#ifdef TEMP_MODEL - temp_model::init(); -#endif - // clear pending interrupts, enable COMPA TIFR5 |= (1<= 0)) return false; @@ -2567,12 +2548,62 @@ void temp_model_set_resistance(uint8_t index, float R) void temp_model_report_settings() { - printf_P(PSTR("%STemperature Model settings:\n" - "%S M310 P%.2f C%.2f S%u E%.2f W%.2f T%.2f\n"), - echomagic, echomagic, (double)temp_model::data.P, (double)temp_model::data.C, (unsigned)temp_model::enabled, - (double)temp_model::data.err, (double)temp_model::data.warn, (double)temp_model::data.Ta_corr); + SERIAL_ECHO_START; + SERIAL_ECHOLNPGM("Temperature Model settings:"); for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) printf_P(PSTR("%S M310 I%u R%.2f\n"), echomagic, (unsigned)i, (double)temp_model::data.R[i]); + printf_P(PSTR("%S M310 P%.2f C%.2f S%u B%u E%.2f W%.2f T%.2f\n"), + echomagic, (double)temp_model::data.P, (double)temp_model::data.C, + (unsigned)temp_model::enabled, (unsigned)temp_model::warn_beep, + (double)temp_model::data.err, (double)temp_model::data.warn, + (double)temp_model::data.Ta_corr); +} + +void temp_model_reset_settings() +{ + TempMgrGuard temp_mgr_guard; + + temp_model::data.P = TEMP_MODEL_P; + temp_model::data.C = NAN; + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) + temp_model::data.R[i] = NAN; + temp_model::data.Ta_corr = TEMP_MODEL_Ta_corr; + temp_model::data.warn = TEMP_MODEL_W; + temp_model::data.err = TEMP_MODEL_E; + temp_model::enabled = false; +} + +void temp_model_load_settings() +{ + static_assert(TEMP_MODEL_R_SIZE == 16); // ensure we don't desync with the eeprom table + TempMgrGuard temp_mgr_guard; + + temp_model::enabled = eeprom_read_byte((uint8_t*)EEPROM_TEMP_MODEL_ENABLE); + temp_model::data.P = eeprom_read_float((float*)EEPROM_TEMP_MODEL_P); + temp_model::data.C = eeprom_read_float((float*)EEPROM_TEMP_MODEL_C); + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) + temp_model::data.R[i] = eeprom_read_float((float*)EEPROM_TEMP_MODEL_R + i); + temp_model::data.Ta_corr = eeprom_read_float((float*)EEPROM_TEMP_MODEL_Ta_corr); + temp_model::data.warn = eeprom_read_float((float*)EEPROM_TEMP_MODEL_W); + temp_model::data.err = eeprom_read_float((float*)EEPROM_TEMP_MODEL_E); + + if(!temp_model::calibrated()) { + SERIAL_ECHOLNPGM("TM: stored calibration invalid, resetting"); + temp_model_reset_settings(); + } + temp_model::setup(); +} + +void temp_model_save_settings() +{ + eeprom_update_byte((uint8_t*)EEPROM_TEMP_MODEL_ENABLE, temp_model::enabled); + eeprom_update_float((float*)EEPROM_TEMP_MODEL_P, temp_model::data.P); + eeprom_update_float((float*)EEPROM_TEMP_MODEL_C, temp_model::data.C); + for(uint8_t i = 0; i != TEMP_MODEL_R_SIZE; ++i) + eeprom_update_float((float*)EEPROM_TEMP_MODEL_R + i, temp_model::data.R[i]); + eeprom_update_float((float*)EEPROM_TEMP_MODEL_Ta_corr, temp_model::data.Ta_corr); + eeprom_update_float((float*)EEPROM_TEMP_MODEL_W, temp_model::data.warn); + eeprom_update_float((float*)EEPROM_TEMP_MODEL_E, temp_model::data.err); } void temp_model_autotune(float temp) diff --git a/Firmware/temperature.h b/Firmware/temperature.h index ad170ecf3..7e41cdc2f 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -233,7 +233,12 @@ void PID_autotune(float temp, int extruder, int ncycles); void temp_model_set_enabled(bool enabled); void temp_model_set_params(float C = NAN, float P = NAN, float Ta_corr = NAN, float warn = NAN, float err = NAN); void temp_model_set_resistance(uint8_t index, float R); + void temp_model_report_settings(); +void temp_model_reset_settings(); +void temp_model_load_settings(); +void temp_model_save_settings(); + void temp_model_autotune(float temp = NAN); #ifdef TEMP_MODEL_DEBUG From 8620059067401fe4a29d0b1b5e268fa25378cce3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 26 Jun 2022 15:57:43 +0200 Subject: [PATCH 47/95] Allow to disable the model warning beeping Mostly useful for debugging --- Firmware/Marlin_main.cpp | 7 +++++-- Firmware/temp_model.h | 5 +++-- Firmware/temperature.cpp | 21 ++++++++++++++++----- Firmware/temperature.h | 1 + 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index b3176d639..db337b14d 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7776,6 +7776,7 @@ Sigma_Exit: - `P` - power - `C` - capacitance - `S` - set 0=disable 1=enable (default) + - `B` - beep on warning threshold 0=disable 1=enable (default) - `E` - error threshold (define min/max values in variants) - `W` - warning threshold (define min/max values in variants) - `T` - ambient temperature correction @@ -7785,25 +7786,27 @@ Sigma_Exit: { // parse all parameters float P = NAN, C = NAN, R = NAN, E = NAN, W = NAN, T = NAN, A = NAN; - int8_t I = -1, S = -1; + int8_t I = -1, S = -1, B = -1; if(code_seen('C')) C = code_value(); if(code_seen('P')) P = code_value(); if(code_seen('I')) I = code_value_short(); if(code_seen('R')) R = code_value(); if(code_seen('S')) S = code_value_short(); + if(code_seen('B')) B = code_value_short(); if(code_seen('E')) E = code_value(); if(code_seen('W')) W = code_value(); if(code_seen('T')) T = code_value(); if(code_seen('A')) A = code_value(); // report values if nothing has been requested - if(isnan(C) && isnan(P) && isnan(R) && isnan(E) && isnan(W) && isnan(T) && isnan(A) && I < 0 && S < 0) { + if(isnan(C) && isnan(P) && isnan(R) && isnan(E) && isnan(W) && isnan(T) && isnan(A) && I < 0 && S < 0 && B < 0) { temp_model_report_settings(); break; } // update all set parameters if(S >= 0) temp_model_set_enabled(S); + if(B >= 0) temp_model_set_warn_beep(B); if(!isnan(C) || !isnan(P) || !isnan(T) || !isnan(W) || !isnan(E)) temp_model_set_params(C, P, T, W, E); if(I >= 0 && !isnan(R)) temp_model_set_resistance(I, R); diff --git a/Firmware/temp_model.h b/Firmware/temp_model.h index 5fed2be7a..0e3bdb0dd 100644 --- a/Firmware/temp_model.h +++ b/Firmware/temp_model.h @@ -69,8 +69,9 @@ struct model_data void step(uint8_t heater_pwm, uint8_t fan_pwm, float heater_temp, float ambient_temp); }; -static bool enabled; // model check enabled -static model_data data; // default heater data +static bool enabled; // model check enabled +static bool warn_beep = true; // beep on warning threshold +static model_data data; // default heater data static bool calibrated(); // return calibration/model validity status static void check(); // check and trigger errors or warnings based on current state diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 904085680..08933fad2 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2446,14 +2446,18 @@ void handle_warning() static bool beeper = false; if(warning_state.assert) { - // beep periodically - beeper = !beeper; - WRITE(BEEPER, beeper); + if(warn_beep) { + // beep periodically + beeper = !beeper; + WRITE(BEEPER, beeper); + } } else { // warning cleared, reset state warning_state.warning = false; - beeper = false; - WRITE(BEEPER, LOW); + if(warn_beep) { + beeper = false; + WRITE(BEEPER, LOW); + } } } @@ -2520,9 +2524,15 @@ void temp_model_set_enabled(bool enabled) SERIAL_ECHOLNPGM("TM: invalid parameters, cannot enable"); } +void temp_model_set_warn_beep(bool enabled) +{ + temp_model::warn_beep = enabled; +} + void temp_model_set_params(float C, float P, float Ta_corr, float warn, float err) { TempMgrGuard temp_mgr_guard; + if(!isnan(C) && C > 0) temp_model::data.C = C; if(!isnan(P) && P > 0) temp_model::data.P = P; if(!isnan(Ta_corr)) temp_model::data.Ta_corr = Ta_corr; @@ -2570,6 +2580,7 @@ void temp_model_reset_settings() temp_model::data.Ta_corr = TEMP_MODEL_Ta_corr; temp_model::data.warn = TEMP_MODEL_W; temp_model::data.err = TEMP_MODEL_E; + temp_model::warn_beep = true; temp_model::enabled = false; } diff --git a/Firmware/temperature.h b/Firmware/temperature.h index 7e41cdc2f..e120d6277 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -231,6 +231,7 @@ void PID_autotune(float temp, int extruder, int ncycles); #ifdef TEMP_MODEL void temp_model_set_enabled(bool enabled); +void temp_model_set_warn_beep(bool enabled); void temp_model_set_params(float C = NAN, float P = NAN, float Ta_corr = NAN, float warn = NAN, float err = NAN); void temp_model_set_resistance(uint8_t index, float R); From 14622bc5773d8c46d8cd05d68c98d4ece419a469 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 26 Jun 2022 17:06:40 +0200 Subject: [PATCH 48/95] Actually disable the temperature manager in TempMgrGuard --- Firmware/temperature.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 08933fad2..013077fc8 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1853,6 +1853,7 @@ public: TempMgrGuard() { ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { temp_mgr_state = TEMP_MGR_INTERRUPT_STATE(); + DISABLE_TEMP_MGR_INTERRUPT(); } } From ec74b88ebc94ee3a462debecca10f8118fc74a6e Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 26 Jun 2022 22:27:40 +0200 Subject: [PATCH 49/95] Correct pre-warning to just warning --- Firmware/temp_model.h | 4 ++-- Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Firmware/temp_model.h b/Firmware/temp_model.h index 0e3bdb0dd..2bae63712 100644 --- a/Firmware/temp_model.h +++ b/Firmware/temp_model.h @@ -44,7 +44,7 @@ struct model_data float Ta_corr; // ambient temperature correction (K) // thresholds - float warn; // pre-warning threshold (K/s) + float warn; // warning threshold (K/s) float err; // error threshold (K/s) // status flags @@ -61,7 +61,7 @@ struct model_data // pre-computed values (initialized via reset) float C_i; // heatblock capacitance (precomputed dT/C) - float warn_s; // pre-warning threshold (per sample) + float warn_s; // warning threshold (per sample) float err_s; // error threshold (per sample) // simulation functions diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index 574689ccb..4b3ad61d8 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -439,7 +439,7 @@ #define TEMP_MODEL_Ta_corr -7 // Default ambient temperature correction #define TEMP_MODEL_LAG 2.1 // Temperature transport delay (s) -#define TEMP_MODEL_W 1.2 // Default pre-warning threshold (K/s) +#define TEMP_MODEL_W 1.2 // Default warning threshold (K/s) #define TEMP_MODEL_E 1.74 // Default error threshold (K/s) #define TEMP_MODEL_CAL_Th 230 // Default calibration working temperature (C) From cc96a47e7f635e46e9ac0b9084a101f8b18c3618 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 00:17:46 +0200 Subject: [PATCH 50/95] Implement temperature model autotuning Calibrate C/R values via univariate minimization using golden section. This is done in several passes: - Bootstrap C by setting an initial high R value - Calibrate R at the requested working temperature - Cooldown - Refine C to the final value - Estimate R losses for a subset of fan speeds - Interpolate remaining values to speed-up the process This results in robust values which are tailored to the current filtering constants, and avoid having to sample for an extended time to reach the required resolution. The refining pass could avoid cooldown if the recording buffer was at least twice as large, so that we could record both the heating and the steady-state, saving _considerable_ time. --- Firmware/Marlin.h | 1 + Firmware/Marlin_main.cpp | 12 +- Firmware/temp_model.h | 35 +-- Firmware/temperature.cpp | 203 +++++++++++++++++- Firmware/temperature.h | 2 +- .../variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h | 1 - 6 files changed, 227 insertions(+), 27 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 0e2427f97..bc76988e6 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -440,6 +440,7 @@ extern uint8_t calc_percent_done(); #define KEEPALIVE_STATE(n) do { busy_state = n;} while (0) extern void host_keepalive(); +extern void host_autoreport(); //extern MarlinBusyState busy_state; extern int8_t busy_state; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index db337b14d..4dda163e2 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1779,7 +1779,7 @@ void serial_read_stream() { * Output autoreport values according to features requested in M155 */ #if defined(AUTO_REPORT) -static void host_autoreport() +void host_autoreport() { if (autoReportFeatures.TimerExpired()) { @@ -7785,8 +7785,8 @@ Sigma_Exit: case 310: { // parse all parameters - float P = NAN, C = NAN, R = NAN, E = NAN, W = NAN, T = NAN, A = NAN; - int8_t I = -1, S = -1, B = -1; + float P = NAN, C = NAN, R = NAN, E = NAN, W = NAN, T = NAN; + int8_t I = -1, S = -1, B = -1, A = -1; if(code_seen('C')) C = code_value(); if(code_seen('P')) P = code_value(); if(code_seen('I')) I = code_value_short(); @@ -7796,10 +7796,10 @@ Sigma_Exit: if(code_seen('E')) E = code_value(); if(code_seen('W')) W = code_value(); if(code_seen('T')) T = code_value(); - if(code_seen('A')) A = code_value(); + if(code_seen('A')) A = code_value_short(); // report values if nothing has been requested - if(isnan(C) && isnan(P) && isnan(R) && isnan(E) && isnan(W) && isnan(T) && isnan(A) && I < 0 && S < 0 && B < 0) { + if(isnan(C) && isnan(P) && isnan(R) && isnan(E) && isnan(W) && isnan(T) && I < 0 && S < 0 && B < 0 && A < 0) { temp_model_report_settings(); break; } @@ -7811,7 +7811,7 @@ Sigma_Exit: if(I >= 0 && !isnan(R)) temp_model_set_resistance(I, R); // run autotune - if(!isnan(A)) temp_model_autotune(A != 0? A: NAN); + if(A >= 0) temp_model_autotune(A); } break; diff --git a/Firmware/temp_model.h b/Firmware/temp_model.h index 2bae63712..d2afcf973 100644 --- a/Firmware/temp_model.h +++ b/Firmware/temp_model.h @@ -5,9 +5,10 @@ #include "planner.h" -constexpr uint8_t TEMP_MODEL_CAL_S = 60; // Maximum recording lenght during calibration (s) -constexpr float TEMP_MODEL_fS = 0.065; // simulation filter (1st-order IIR factor) -constexpr float TEMP_MODEL_fE = 0.05; // error filter (1st-order IIR factor) +constexpr uint8_t TEMP_MODEL_CAL_S = 60; // Maximum recording lenght during calibration (s) +constexpr uint8_t TEMP_MODEL_CAL_R_STEP = 4; // Fan interpolation steps during calibration +constexpr float TEMP_MODEL_fS = 0.065; // simulation filter (1st-order IIR factor) +constexpr float TEMP_MODEL_fE = 0.05; // error filter (1st-order IIR factor) // transport delay buffer size (samples) constexpr uint8_t TEMP_MODEL_LAG_SIZE = (TEMP_MODEL_LAG / TEMP_MGR_INTV + 0.5); @@ -17,18 +18,6 @@ constexpr uint8_t TEMP_MODEL_R_SIZE = (1 << FAN_SOFT_PWM_BITS); namespace temp_model { -// recording scratch buffer -struct rec_entry -{ - float temp; // heater temperature - uint8_t pwm; // heater PWM -}; - -constexpr uint16_t rec_buffer_size = TEMP_MODEL_CAL_S / TEMP_MGR_INTV; -static rec_entry* const rec_buffer = (rec_entry*)block_buffer; // oh-hey, free memory! -static_assert(sizeof(rec_entry[rec_buffer_size]) <= sizeof(block_buffer), - "recording length too long to fit within available buffer"); - struct model_data { // temporary buffers @@ -108,3 +97,19 @@ static void log_isr(); // isr log handler #endif } // namespace temp_model + +namespace temp_model_cal { + +// recording scratch buffer +struct rec_entry +{ + float temp; // heater temperature + uint8_t pwm; // heater PWM +}; + +constexpr uint16_t REC_BUFFER_SIZE = TEMP_MODEL_CAL_S / TEMP_MGR_INTV; +static rec_entry* const rec_buffer = (rec_entry*)block_buffer; // oh-hey, free memory! +static_assert(sizeof(rec_entry[REC_BUFFER_SIZE]) <= sizeof(block_buffer), + "recording length too long to fit within available buffer"); + +} // namespace temp_model_cal diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 013077fc8..981cd1d25 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -52,6 +52,8 @@ #define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz #define TIMER5_PRESCALE 256 #define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU)) +#define TEMP_MGR_INT_FLAG_STATE() (TIFR5 & (1<= temp) && + (current_temperature[0] >= (current_temperature_ambient + temp_model::data.Ta_corr + TEMP_HYSTERESIS))) + waiting_handler(); + fanSpeedSoftPwm = old_speed; +} + +uint16_t record(uint16_t samples = REC_BUFFER_SIZE) { + TempMgrGuard temp_mgr_guard; + + uint16_t pos = 0; + while(pos < samples) { + if(!TEMP_MGR_INT_FLAG_STATE()) { + // temperatures not ready yet + waiting_handler(); + continue; + } + TEMP_MGR_INT_FLAG_CLEAR(); + + // manually repeat what the regular isr would do + if(adc_values_ready != true) continue; + adc_values_ready = false; + adc_start_cycle(); + temp_mgr_isr(); + + // record a new entry + rec_entry& entry = rec_buffer[pos]; + entry.temp = current_temperature_isr[0]; + entry.pwm = soft_pwm[0]; + ++pos; + } + + return pos; +} + +float cost_fn(uint16_t samples, float* const var, float v, float ambient) +{ + *var = v; + uint8_t fan_pwm = soft_pwm_fan; + temp_model::data.reset(rec_buffer[0].pwm, fan_pwm, rec_buffer[0].temp, ambient); + float err = 0; + for(uint16_t i = 1; i < samples; ++i) { + temp_model::data.step(rec_buffer[i].pwm, fan_pwm, rec_buffer[i].temp, ambient); + err += fabsf(temp_model::data.dT_err_prev); + } + return (err / (samples - 1)); +} + +constexpr float GOLDEN_RATIO = 0.6180339887498949; + +void update_section(float points[2], const float bounds[2]) +{ + float d = GOLDEN_RATIO * (bounds[1] - bounds[0]); + points[0] = bounds[0] + d; + points[1] = bounds[1] - d; +} + +float estimate(uint16_t samples, float* const var, float min, float max, float thr, uint16_t max_itr, float ambient) +{ + float e = NAN; + float points[2]; + float bounds[2] = {min, max}; + update_section(points, bounds); + + for(uint8_t it = 0; it != max_itr; ++it) { + float c1 = cost_fn(samples, var, points[0], ambient); + float c2 = cost_fn(samples, var, points[1], ambient); + bool dir = (c2 < c1); + bounds[dir] = points[!dir]; + update_section(points, bounds); + float x = points[!dir]; + e = (1-GOLDEN_RATIO) * fabsf((bounds[0]-bounds[1]) / x); + + printf_P(PSTR("TM iter:%u v:%.2f e:%.3f\n"), it, x, e); + if(e < thr) return e; + } + + SERIAL_ECHOLNPGM("TM estimation did not converge"); + return e; +} + +void autotune(int16_t cal_temp) +{ + SERIAL_ECHOLNPGM("TM: autotune start"); + uint16_t samples; + + // disable the model checking during self-calibration + bool was_enabled = temp_model::enabled; + temp_model_set_enabled(false); + + // bootstrap C/R values without fan + fanSpeedSoftPwm = 0; + + for(uint8_t i = 0; i != 2; ++i) { + const char* PROGMEM verb = (i == 0? PSTR("initial"): PSTR("refining")); + + target_temperature[0] = 0; + if(current_temperature[0] >= TEMP_MODEL_CAL_Tl) { + printf_P(PSTR("TM: cooling down to %dC\n"), TEMP_MODEL_CAL_Tl); + cooldown(TEMP_MODEL_CAL_Tl); + wait(10000); + } + + // we need a valid R value for the initial C guess + if(isnan(temp_model::data.R[0])) + temp_model::data.R[0] = TEMP_MODEL_Rh; + + printf_P(PSTR("TM: %S C estimation\n"), verb); + target_temperature[0] = cal_temp; + samples = record(); + estimate(samples, &temp_model::data.C, + TEMP_MODEL_Cl, TEMP_MODEL_Ch, TEMP_MODEL_C_thr, TEMP_MODEL_C_itr, + current_temperature_ambient); + + wait_temp(); + if(i) break; // we don't need to refine R + wait(30000); // settle PID regulation + + printf_P(PSTR("TM: %S R estimation @ %dC\n"), verb, cal_temp); + samples = record(); + estimate(samples, &temp_model::data.R[0], + TEMP_MODEL_Rl, TEMP_MODEL_Rh, TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, + current_temperature_ambient); + } + + // Estimate fan losses at regular intervals, starting from full speed to avoid low-speed + // kickstart issues, although this requires us to wait more for the PID stabilization. + // Normally exhibits logarithmic behavior with the stock fan+shroud, so the shorter interval + // at lower speeds is helpful to increase the resolution of the interpolation. + fanSpeedSoftPwm = 255; + wait(30000); + + for(int8_t i = TEMP_MODEL_R_SIZE; i > 0; i -= TEMP_MODEL_CAL_R_STEP) { + fanSpeedSoftPwm = 256 / TEMP_MODEL_R_SIZE * i - 1; + wait(10000); + + printf_P(PSTR("TM: R[%u] estimation\n"), (unsigned)soft_pwm_fan); + samples = record(); + estimate(samples, &temp_model::data.R[soft_pwm_fan], + TEMP_MODEL_Rl, temp_model::data.R[0], TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, + current_temperature_ambient); + } + + // interpolate remaining steps to speed-up calibration + int8_t next = TEMP_MODEL_R_SIZE - 1; + for(uint8_t i = TEMP_MODEL_R_SIZE - 2; i != 0; --i) { + if(!((TEMP_MODEL_R_SIZE - i - 1) % TEMP_MODEL_CAL_R_STEP)) { + next = i; + continue; + } + int8_t prev = next - TEMP_MODEL_CAL_R_STEP; + if(prev < 0) prev = 0; + float f = (float)(i - prev) / TEMP_MODEL_CAL_R_STEP; + float d = (temp_model::data.R[next] - temp_model::data.R[prev]); + temp_model::data.R[i] = temp_model::data.R[prev] + d * f; + } + + // restore the initial state + fanSpeedSoftPwm = 0; + target_temperature[0] = 0; + temp_model_set_enabled(was_enabled); +} + +} // namespace temp_model_cal + +void temp_model_autotune(int16_t temp) +{ + // TODO: ensure printer is idle/queue empty/buffer empty + KEEPALIVE_STATE(IN_PROCESS); + temp_model_cal::autotune(temp > 0 ? temp : TEMP_MODEL_CAL_Th); + temp_model_report_settings(); } #ifdef TEMP_MODEL_DEBUG diff --git a/Firmware/temperature.h b/Firmware/temperature.h index e120d6277..bae77091b 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -240,7 +240,7 @@ void temp_model_reset_settings(); void temp_model_load_settings(); void temp_model_save_settings(); -void temp_model_autotune(float temp = NAN); +void temp_model_autotune(int16_t temp = 0); #ifdef TEMP_MODEL_DEBUG void temp_model_log_enable(bool enable); diff --git a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h index 4b3ad61d8..501868578 100644 --- a/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h @@ -435,7 +435,6 @@ #define TEMP_MODEL_R_thr 0.01 // R estimation iteration threshold #define TEMP_MODEL_R_itr 30 // R estimation iteration limit -#define TEMP_MODEL_Rf_D -15 // initial guess for resistance change with full-power fan #define TEMP_MODEL_Ta_corr -7 // Default ambient temperature correction #define TEMP_MODEL_LAG 2.1 // Temperature transport delay (s) From 47d2e9e61c41e293d4dc761e3de3b7b22f6561be Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 10:35:49 +0200 Subject: [PATCH 51/95] Handle failures during calibration Break out of the autotuning if a thermal error condition is detected and attempt to restore a safe initial state irregardless of the error handlers. Also error out if the estimation fails to converge. --- Firmware/temperature.cpp | 80 ++++++++++++++++++++++++++++++---------- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 981cd1d25..2e2f9683d 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2632,23 +2632,33 @@ void waiting_handler() void wait(unsigned ms) { unsigned long mark = _millis() + ms; - while(_millis() < mark) + while(_millis() < mark) { + if(temp_error_state.v) break; waiting_handler(); + } } void wait_temp() { - while(current_temperature[0] < (target_temperature[0] - TEMP_HYSTERESIS)) + while(current_temperature[0] < (target_temperature[0] - TEMP_HYSTERESIS)) { + if(temp_error_state.v) break; waiting_handler(); + } } void cooldown(float temp) { float old_speed = fanSpeedSoftPwm; fanSpeedSoftPwm = 255; - while((current_temperature[0] >= temp) && - (current_temperature[0] >= (current_temperature_ambient + temp_model::data.Ta_corr + TEMP_HYSTERESIS))) + while(current_temperature[0] >= temp) { + if(temp_error_state.v) break; + float ambient = current_temperature_ambient + temp_model::data.Ta_corr; + if(current_temperature[0] < (ambient + TEMP_HYSTERESIS)) { + // do not get stuck waiting very close to ambient temperature + break; + } waiting_handler(); + } fanSpeedSoftPwm = old_speed; } @@ -2670,6 +2680,10 @@ uint16_t record(uint16_t samples = REC_BUFFER_SIZE) { adc_start_cycle(); temp_mgr_isr(); + // stop recording for an hard error condition + if(temp_error_state.v) + return 0; + // record a new entry rec_entry& entry = rec_buffer[pos]; entry.temp = current_temperature_isr[0]; @@ -2723,17 +2737,13 @@ float estimate(uint16_t samples, float* const var, float min, float max, float t } SERIAL_ECHOLNPGM("TM estimation did not converge"); - return e; + return NAN; } -void autotune(int16_t cal_temp) +bool autotune(int16_t cal_temp) { - SERIAL_ECHOLNPGM("TM: autotune start"); uint16_t samples; - - // disable the model checking during self-calibration - bool was_enabled = temp_model::enabled; - temp_model_set_enabled(false); + float e; // bootstrap C/R values without fan fanSpeedSoftPwm = 0; @@ -2755,9 +2765,14 @@ void autotune(int16_t cal_temp) printf_P(PSTR("TM: %S C estimation\n"), verb); target_temperature[0] = cal_temp; samples = record(); - estimate(samples, &temp_model::data.C, + if(temp_error_state.v || !samples) + return true; + + e = estimate(samples, &temp_model::data.C, TEMP_MODEL_Cl, TEMP_MODEL_Ch, TEMP_MODEL_C_thr, TEMP_MODEL_C_itr, current_temperature_ambient); + if(isnan(e)) + return true; wait_temp(); if(i) break; // we don't need to refine R @@ -2765,9 +2780,14 @@ void autotune(int16_t cal_temp) printf_P(PSTR("TM: %S R estimation @ %dC\n"), verb, cal_temp); samples = record(); - estimate(samples, &temp_model::data.R[0], + if(temp_error_state.v || !samples) + return true; + + e = estimate(samples, &temp_model::data.R[0], TEMP_MODEL_Rl, TEMP_MODEL_Rh, TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, current_temperature_ambient); + if(isnan(e)) + return true; } // Estimate fan losses at regular intervals, starting from full speed to avoid low-speed @@ -2783,9 +2803,14 @@ void autotune(int16_t cal_temp) printf_P(PSTR("TM: R[%u] estimation\n"), (unsigned)soft_pwm_fan); samples = record(); - estimate(samples, &temp_model::data.R[soft_pwm_fan], + if(temp_error_state.v || !samples) + return true; + + e = estimate(samples, &temp_model::data.R[soft_pwm_fan], TEMP_MODEL_Rl, temp_model::data.R[0], TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, current_temperature_ambient); + if(isnan(e)) + return true; } // interpolate remaining steps to speed-up calibration @@ -2802,10 +2827,7 @@ void autotune(int16_t cal_temp) temp_model::data.R[i] = temp_model::data.R[prev] + d * f; } - // restore the initial state - fanSpeedSoftPwm = 0; - target_temperature[0] = 0; - temp_model_set_enabled(was_enabled); + return false; } } // namespace temp_model_cal @@ -2814,8 +2836,26 @@ void temp_model_autotune(int16_t temp) { // TODO: ensure printer is idle/queue empty/buffer empty KEEPALIVE_STATE(IN_PROCESS); - temp_model_cal::autotune(temp > 0 ? temp : TEMP_MODEL_CAL_Th); - temp_model_report_settings(); + + // disable the model checking during self-calibration + bool was_enabled = temp_model::enabled; + temp_model_set_enabled(false); + + SERIAL_ECHOLNPGM("TM: autotune start"); + bool err = temp_model_cal::autotune(temp > 0 ? temp : TEMP_MODEL_CAL_Th); + + // always reset temperature + target_temperature[0] = 0; + + if(err) { + SERIAL_ECHOLNPGM("TM: autotune failed"); + if(temp_error_state.v) + fanSpeedSoftPwm = 255; + } else { + fanSpeedSoftPwm = 0; + temp_model_set_enabled(was_enabled); + temp_model_report_settings(); + } } #ifdef TEMP_MODEL_DEBUG From 0680c0b428aaa3f677b2bfb1790f288052bb8808 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 11:54:10 +0200 Subject: [PATCH 52/95] Refresh the lcd periodically during calibration --- Firmware/temperature.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 2e2f9683d..0e46f1aef 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2627,6 +2627,7 @@ void waiting_handler() manage_heater(); host_keepalive(); host_autoreport(); + lcd_update(0); } void wait(unsigned ms) @@ -2668,8 +2669,8 @@ uint16_t record(uint16_t samples = REC_BUFFER_SIZE) { uint16_t pos = 0; while(pos < samples) { if(!TEMP_MGR_INT_FLAG_STATE()) { - // temperatures not ready yet - waiting_handler(); + // temperatures not ready yet, just manage heaters while waiting to reduce jitter + manage_heater(); continue; } TEMP_MGR_INT_FLAG_CLEAR(); @@ -2689,6 +2690,9 @@ uint16_t record(uint16_t samples = REC_BUFFER_SIZE) { entry.temp = current_temperature_isr[0]; entry.pwm = soft_pwm[0]; ++pos; + + // it's now safer to give regular serial/lcd updates a shot + waiting_handler(); } return pos; From 9c971b1b89c1910cb10abd59904f1360ebc1f1f9 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 12:03:35 +0200 Subject: [PATCH 53/95] Add a note about resetting waiting_inside_plan_buffer_line_print_aborted --- Firmware/planner.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/planner.cpp b/Firmware/planner.cpp index 12641bbf4..5b513034d 100644 --- a/Firmware/planner.cpp +++ b/Firmware/planner.cpp @@ -703,12 +703,14 @@ float junction_deviation = 0.1; // calculation the caller must also provide the physical length of the line in millimeters. void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate, uint8_t extruder, const float* gcode_target) { - // Calculate the buffer head after we push this byte + // Calculate the buffer head after we push this byte uint8_t next_buffer_head = next_block_index(block_buffer_head); + // TODO: shouldn't this be reset only in the outer marlin loop? + waiting_inside_plan_buffer_line_print_aborted = false; + // If the buffer is full: good! That means we are well ahead of the robot. // Rest here until there is room in the buffer. - waiting_inside_plan_buffer_line_print_aborted = false; if (block_buffer_tail == next_buffer_head) { do { manage_heater(); From ea0840dee909c02977891d108cf0d7dbdc4bac4b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 12:25:38 +0200 Subject: [PATCH 54/95] Refuse to run TM calibration if the printer is not idle --- Firmware/Marlin.h | 2 ++ Firmware/Marlin_main.cpp | 4 ++++ Firmware/temperature.cpp | 6 +++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index bc76988e6..868f9790b 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -358,6 +358,8 @@ extern LongTimer safetyTimer; #define PRINT_PERCENT_DONE_INIT 0xff #define PRINTER_ACTIVE (IS_SD_PRINTING || usb_timer.running() || isPrintPaused || (custom_message_type == CustomMsg::TempCal) || saved_printing || (lcd_commands_type == LcdCommands::Layer1Cal) || mmu_print_saved || homing_flag || mesh_bed_leveling_flag) +extern bool printer_active(); + //! Beware - mcode_in_progress is set as soon as the command gets really processed, //! which is not the same as posting the M600 command into the command queue //! There can be a considerable lag between posting M600 and its real processing which might result diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 4dda163e2..f05856b0c 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -572,6 +572,10 @@ void servo_init() #endif } +bool printer_active() +{ + return PRINTER_ACTIVE; +} bool fans_check_enabled = true; diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 0e46f1aef..0a95a2194 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2838,7 +2838,11 @@ bool autotune(int16_t cal_temp) void temp_model_autotune(int16_t temp) { - // TODO: ensure printer is idle/queue empty/buffer empty + if(moves_planned() || printer_active()) { + SERIAL_ECHOLNPGM("TM: printer needs to be idle for calibration"); + return; + } + KEEPALIVE_STATE(IN_PROCESS); // disable the model checking during self-calibration From 49048fdcd6a666bb9cd40497e1c77cfa61ba300b Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 13:45:58 +0200 Subject: [PATCH 55/95] Add temperature model configuration for MK3 --- .../variants/1_75mm_MK3-EINSy10a-E3Dv6full.h | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h index f31999afd..d32369a84 100644 --- a/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h +++ b/Firmware/variants/1_75mm_MK3-EINSy10a-E3Dv6full.h @@ -413,6 +413,34 @@ #define TEMP_RUNAWAY_EXTRUDER_HYSTERESIS 15 #define TEMP_RUNAWAY_EXTRUDER_TIMEOUT 45 +// model-based temperature check +#define TEMP_MODEL 1 // enable model-based temperature checks +#define TEMP_MODEL_DEBUG 1 // extended runtime logging + +#define TEMP_MODEL_P 38. // heater power (W) + +#define TEMP_MODEL_C 11. // initial guess for heatblock capacitance (J/K) +#define TEMP_MODEL_Cl 5 // C estimation lower limit +#define TEMP_MODEL_Ch 20 // C estimation upper limit +#define TEMP_MODEL_C_thr 0.01 // C estimation iteration threshold +#define TEMP_MODEL_C_itr 30 // C estimation iteration limit + +#define TEMP_MODEL_R 25 // initial guess for heatblock resistance (K/W) +#define TEMP_MODEL_Rl 5 // R estimation lower limit +#define TEMP_MODEL_Rh 50 // R estimation upper limit +#define TEMP_MODEL_R_thr 0.01 // R estimation iteration threshold +#define TEMP_MODEL_R_itr 30 // R estimation iteration limit + +#define TEMP_MODEL_Ta_corr -7 // Default ambient temperature correction +#define TEMP_MODEL_LAG 2.1 // Temperature transport delay (s) + +#define TEMP_MODEL_W 1.2 // Default warning threshold (K/s) +#define TEMP_MODEL_E 1.74 // Default error threshold (K/s) + +#define TEMP_MODEL_CAL_Th 230 // Default calibration working temperature (C) +#define TEMP_MODEL_CAL_Tl 50 // Default calibration cooling temperature (C) + + /*------------------------------------ MOTOR CURRENT SETTINGS *------------------------------------*/ From 92bc7554b6fb433fac4ff5c76d0746bd31b575b0 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 27 Jun 2022 14:22:37 +0200 Subject: [PATCH 56/95] Enable the model last, if set We need all the values to be set before we can attempt to enable the model. --- Firmware/Marlin_main.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index f05856b0c..8f41500ff 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7808,12 +7808,14 @@ Sigma_Exit: break; } - // update all set parameters - if(S >= 0) temp_model_set_enabled(S); + // update all parameters if(B >= 0) temp_model_set_warn_beep(B); if(!isnan(C) || !isnan(P) || !isnan(T) || !isnan(W) || !isnan(E)) temp_model_set_params(C, P, T, W, E); if(I >= 0 && !isnan(R)) temp_model_set_resistance(I, R); + // enable the model last, if requested + if(S >= 0) temp_model_set_enabled(S); + // run autotune if(A >= 0) temp_model_autotune(A); } From 50c71924a26c8e8106efe3d26fbf71a44a483d00 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 28 Jun 2022 11:28:56 +0200 Subject: [PATCH 57/95] Include fan measurements when estimating resistance --- Firmware/temperature.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 0a95a2194..4e7bd5339 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2627,6 +2627,7 @@ void waiting_handler() manage_heater(); host_keepalive(); host_autoreport(); + checkFans(); lcd_update(0); } @@ -2698,10 +2699,9 @@ uint16_t record(uint16_t samples = REC_BUFFER_SIZE) { return pos; } -float cost_fn(uint16_t samples, float* const var, float v, float ambient) +float cost_fn(uint16_t samples, float* const var, float v, uint8_t fan_pwm, float ambient) { *var = v; - uint8_t fan_pwm = soft_pwm_fan; temp_model::data.reset(rec_buffer[0].pwm, fan_pwm, rec_buffer[0].temp, ambient); float err = 0; for(uint16_t i = 1; i < samples; ++i) { @@ -2720,7 +2720,10 @@ void update_section(float points[2], const float bounds[2]) points[1] = bounds[1] - d; } -float estimate(uint16_t samples, float* const var, float min, float max, float thr, uint16_t max_itr, float ambient) +float estimate(uint16_t samples, + float* const var, float min, float max, + float thr, uint16_t max_itr, + uint8_t fan_pwm, float ambient) { float e = NAN; float points[2]; @@ -2728,8 +2731,8 @@ float estimate(uint16_t samples, float* const var, float min, float max, float t update_section(points, bounds); for(uint8_t it = 0; it != max_itr; ++it) { - float c1 = cost_fn(samples, var, points[0], ambient); - float c2 = cost_fn(samples, var, points[1], ambient); + float c1 = cost_fn(samples, var, points[0], fan_pwm, ambient); + float c2 = cost_fn(samples, var, points[1], fan_pwm, ambient); bool dir = (c2 < c1); bounds[dir] = points[!dir]; update_section(points, bounds); @@ -2774,7 +2777,7 @@ bool autotune(int16_t cal_temp) e = estimate(samples, &temp_model::data.C, TEMP_MODEL_Cl, TEMP_MODEL_Ch, TEMP_MODEL_C_thr, TEMP_MODEL_C_itr, - current_temperature_ambient); + 0, current_temperature_ambient); if(isnan(e)) return true; @@ -2789,7 +2792,7 @@ bool autotune(int16_t cal_temp) e = estimate(samples, &temp_model::data.R[0], TEMP_MODEL_Rl, TEMP_MODEL_Rh, TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, - current_temperature_ambient); + 0, current_temperature_ambient); if(isnan(e)) return true; } @@ -2805,14 +2808,16 @@ bool autotune(int16_t cal_temp) fanSpeedSoftPwm = 256 / TEMP_MODEL_R_SIZE * i - 1; wait(10000); - printf_P(PSTR("TM: R[%u] estimation\n"), (unsigned)soft_pwm_fan); + printf_P(PSTR("TM: R[%u] estimation\n"), (unsigned)i); samples = record(); if(temp_error_state.v || !samples) return true; - e = estimate(samples, &temp_model::data.R[soft_pwm_fan], + // a fixed fan pwm (the norminal value) is used here, as soft_pwm_fan will be modified + // during fan measurements and we'd like to include that skew during normal operation. + e = estimate(samples, &temp_model::data.R[i], TEMP_MODEL_Rl, temp_model::data.R[0], TEMP_MODEL_R_thr, TEMP_MODEL_R_itr, - current_temperature_ambient); + i, current_temperature_ambient); if(isnan(e)) return true; } From 6751586db6bdc3ac0fd8ab1af9ecd3d4da47c23c Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 28 Jun 2022 17:07:12 +0200 Subject: [PATCH 58/95] Cleanup original thermal error handlers. Remove useless repeated calls to disable heaters and turn on the fans, since this is done at a higher level. Avoid repeating messages on the serial. Do it just once. Make a critical alert sound unconditionally. --- Firmware/Marlin.h | 7 +- Firmware/Marlin_main.cpp | 48 +++++++----- Firmware/temperature.cpp | 158 +++++++++++++++------------------------ 3 files changed, 92 insertions(+), 121 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 868f9790b..0d555165f 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -241,9 +241,10 @@ void prepare_move(); void kill(const char *full_screen_message = NULL, unsigned char id = 0); void finishAndDisableSteppers(); -void UnconditionalStop(); // Stop heaters, motion and clear current print status -void Stop(); // Emergency stop used by overtemp functions which allows recovery -bool IsStopped(); // Returns true if the print has been stopped +void UnconditionalStop(); // Stop heaters, motion and clear current print status +void ThermalStop(bool pause = false); // Emergency stop used by overtemp functions which allows + // recovery (with pause=true) +bool IsStopped(); // Returns true if the print has been stopped //put an ASCII command at the end of the current buffer, read from flash #define enquecommand_P(cmd) enquecommand(cmd, true) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 8f41500ff..217ee1299 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9967,7 +9967,9 @@ void UnconditionalStop() CRITICAL_SECTION_END; } -// Stop: Emergency stop used by overtemp functions which allows recovery +// Emergency stop used by overtemp functions which allows recovery +// +// This function is called *continuously* during a thermal failure. // // In addition to stopping the print, this prevents subsequent G[0-3] commands to be // processed via USB (using "Stopped") until the print is resumed via M999 or @@ -9977,29 +9979,35 @@ void UnconditionalStop() // will introduce either over/under extrusion on the current segment, and will not // survive a power panic. Switching Stop() to use the pause machinery instead (with // the addition of disabling the headers) could allow true recovery in the future. -void Stop() +void ThermalStop(bool pause) { - // Keep disabling heaters - disable_heater(); + if(Stopped == false) { + Stopped = true; + if(pause && (IS_SD_PRINTING || usb_timer.running())) { + if (!isPrintPaused) { + // we cannot make a distinction for an host here, the pause must be instantaneous + lcd_pause_print(); + } + } else { + // We got a hard thermal error and/or there is no print going on. Just stop. + lcd_print_stop(); + } + Stopped_gcode_LastN = gcode_LastN; // Save last g_code for "restart" - // Call the regular stop function if that's the first time during a new print - if(Stopped == false) { - Stopped = true; - lcd_print_stop(); - Stopped_gcode_LastN = gcode_LastN; // Save last g_code for restart + // Eventually report the stopped status (though this is usually overridden by a + // higher-priority alert status message) + SERIAL_ERROR_START; + SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED); + LCD_MESSAGERPGM(_T(MSG_STOPPED)); - // Eventually report the stopped status (though this is usually overridden by a - // higher-priority alert status message) - SERIAL_ERROR_START; - SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED); - LCD_MESSAGERPGM(_T(MSG_STOPPED)); - } + Sound_MakeCustom(1000,0,true); + } - // Return to the status screen to stop any pending menu action which could have been - // started by the user while stuck in the Stopped state. This also ensures the NEW - // error is immediately shown. - if (menu_menu != lcd_status_screen) - lcd_return_to_status(); + // Return to the status screen to stop any pending menu action which could have been + // started by the user while stuck in the Stopped state. This also ensures the NEW + // error is immediately shown. + if (menu_menu != lcd_status_screen) + lcd_return_to_status(); } bool IsStopped() { return Stopped; }; diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4e7bd5339..97ed1be48 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -842,7 +842,7 @@ void soft_pwm_init() } #if (defined (TEMP_RUNAWAY_BED_HYSTERESIS) && TEMP_RUNAWAY_BED_TIMEOUT > 0) || (defined (TEMP_RUNAWAY_EXTRUDER_HYSTERESIS) && TEMP_RUNAWAY_EXTRUDER_TIMEOUT > 0) -void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _current_temperature, float _output, bool _isbed) +static void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _current_temperature, float _output, bool _isbed) { float __delta; float __hysteresis = 0; @@ -851,7 +851,6 @@ void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _cu static float __preheat_start[2] = { 0,0}; //currently just bed and one extruder static uint8_t __preheat_counter[2] = { 0,0}; static uint8_t __preheat_errors[2] = { 0,0}; - if (_millis() - temp_runaway_timer[_heater_id] > 2000) { @@ -974,32 +973,32 @@ void temp_runaway_check(uint8_t _heater_id, float _target_temperature, float _cu } } -void temp_runaway_stop(bool isPreheat, bool isBed) +static void temp_runaway_stop(bool isPreheat, bool isBed) { - disable_heater(); - Sound_MakeCustom(200,0,true); - - if (isPreheat) - { - lcd_setalertstatuspgm(isBed? PSTR("BED PREHEAT ERROR") : PSTR("PREHEAT ERROR"), LCD_STATUS_CRITICAL); - SERIAL_ERROR_START; - isBed ? SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HEATBED)") : SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HOTEND)"); - - hotendFanSetFullSpeed(); - } - else - { - lcd_setalertstatuspgm(isBed? PSTR("BED THERMAL RUNAWAY") : PSTR("THERMAL RUNAWAY"), LCD_STATUS_CRITICAL); - SERIAL_ERROR_START; - isBed ? SERIAL_ERRORLNPGM(" HEATBED THERMAL RUNAWAY") : SERIAL_ERRORLNPGM(" HOTEND THERMAL RUNAWAY"); - } - - Stop(); - - if (farm_mode) { - prusa_statistics(0); - prusa_statistics(isPreheat? 91 : 90); + if(IsStopped() == false) { + if (isPreheat) { + lcd_setalertstatuspgm(isBed? PSTR("BED PREHEAT ERROR") : PSTR("PREHEAT ERROR"), LCD_STATUS_CRITICAL); + SERIAL_ERROR_START; + if (isBed) { + SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HEATBED)"); + } else { + SERIAL_ERRORLNPGM(" THERMAL RUNAWAY (PREHEAT HOTEND)"); + } + } else { + lcd_setalertstatuspgm(isBed? PSTR("BED THERMAL RUNAWAY") : PSTR("THERMAL RUNAWAY"), LCD_STATUS_CRITICAL); + SERIAL_ERROR_START; + if (isBed) { + SERIAL_ERRORLNPGM(" HEATBED THERMAL RUNAWAY"); + } else { + SERIAL_ERRORLNPGM(" HOTEND THERMAL RUNAWAY"); + } + } + if (farm_mode) { + prusa_statistics(0); + prusa_statistics(isPreheat? 91 : 90); + } } + ThermalStop(); } #endif @@ -1011,12 +1010,12 @@ enum { LCDALERT_NONE = 0, LCDALERT_HEATERMINTEMP, LCDALERT_BEDMINTEMP, LCDALERT_ //! remember the last alert message sent to the LCD //! to prevent flicker and improve speed -uint8_t last_alert_sent_to_lcd = LCDALERT_NONE; +static uint8_t last_alert_sent_to_lcd = LCDALERT_NONE; //! update the current temperature error message //! @param type short error abbreviation (PROGMEM) -void temp_update_messagepgm(const char* PROGMEM type) +static void temp_update_messagepgm(const char* PROGMEM type) { char msg[LCD_WIDTH]; strcpy_P(msg, PSTR("Err: ")); @@ -1027,7 +1026,7 @@ void temp_update_messagepgm(const char* PROGMEM type) //! signal a temperature error on both the lcd and serial //! @param type short error abbreviation (PROGMEM) //! @param e optional extruder index for hotend errors -void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS) +static void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS) { temp_update_messagepgm(type); @@ -1044,60 +1043,38 @@ void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS) } -void max_temp_error(uint8_t e) { - disable_heater(); - if(IsStopped() == false) { - temp_error_messagepgm(PSTR("MAXTEMP"), e); - } - #ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE - Stop(); - #endif - - hotendFanSetFullSpeed(); - if (farm_mode) { prusa_statistics(93); } -} - -void min_temp_error(uint8_t e) { -#ifdef DEBUG_DISABLE_MINTEMP - return; +static void max_temp_error(uint8_t e) { + if(IsStopped() == false) { + temp_error_messagepgm(PSTR("MAXTEMP"), e); + if (farm_mode) prusa_statistics(93); + } +#ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE + ThermalStop(); #endif - disable_heater(); -//if (current_temperature_ambient < MINTEMP_MINAMBIENT) return; - static const char err[] PROGMEM = "MINTEMP"; - if(IsStopped() == false) { - temp_error_messagepgm(err, e); - last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; - } else if( last_alert_sent_to_lcd != LCDALERT_HEATERMINTEMP ){ // only update, if the lcd message is to be changed (i.e. not the same as last time) - // we are already stopped due to some error, only update the status message without flickering - temp_update_messagepgm(err); - last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; - } - #ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE -// if( last_alert_sent_to_lcd != LCDALERT_HEATERMINTEMP ){ -// last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; -// lcd_print_stop(); -// } - Stop(); - #endif - if (farm_mode) { prusa_statistics(92); } - } -void bed_max_temp_error(void) { - disable_heater(); - if(IsStopped() == false) { - temp_error_messagepgm(PSTR("MAXTEMP BED")); - } - #ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE - Stop(); - #endif +static void min_temp_error(uint8_t e) { + static const char err[] PROGMEM = "MINTEMP"; + if(IsStopped() == false) { + temp_error_messagepgm(err, e); + last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; + if (farm_mode) prusa_statistics(92); + } else if( last_alert_sent_to_lcd != LCDALERT_HEATERMINTEMP ){ // only update, if the lcd message is to be changed (i.e. not the same as last time) + // we are already stopped due to some error, only update the status message without flickering + temp_update_messagepgm(err); + last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; + } + ThermalStop(); } -void bed_min_temp_error(void) { -#ifdef DEBUG_DISABLE_MINTEMP - return; -#endif - disable_heater(); +static void bed_max_temp_error(void) { + if(IsStopped() == false) { + temp_error_messagepgm(PSTR("MAXTEMP BED")); + } + ThermalStop(); +} + +static void bed_min_temp_error(void) { static const char err[] PROGMEM = "MINTEMP BED"; if(IsStopped() == false) { temp_error_messagepgm(err); @@ -1107,34 +1084,23 @@ void bed_min_temp_error(void) { temp_update_messagepgm(err); last_alert_sent_to_lcd = LCDALERT_BEDMINTEMP; } -#ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE - Stop(); -#endif + ThermalStop(); } #ifdef AMBIENT_THERMISTOR -void ambient_max_temp_error(void) { - disable_heater(); +static void ambient_max_temp_error(void) { if(IsStopped() == false) { temp_error_messagepgm(PSTR("MAXTEMP AMB")); } -#ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE - Stop(); -#endif + ThermalStop(); } -void ambient_min_temp_error(void) { -#ifdef DEBUG_DISABLE_MINTEMP - return; -#endif - disable_heater(); +static void ambient_min_temp_error(void) { if(IsStopped() == false) { temp_error_messagepgm(PSTR("MINTEMP AMB")); } -#ifndef BOGUS_TEMPERATURE_FAILSAFE_OVERRIDE - Stop(); -#endif + ThermalStop(); } #endif @@ -1713,10 +1679,6 @@ void check_min_temp_ambient() void handle_temp_error() { - // TODO: Stop and the various *_error() functions need an overhaul! - // The heaters are kept disabled already at a higher level, making almost - // all the code inside the invidual handlers useless! - // relay to the original handler switch((TempErrorType)temp_error_state.type) { case TempErrorType::min: From dc2d596f40f9fc1b1ee57ddeb15f4655c0f70ac2 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 28 Jun 2022 19:33:32 +0200 Subject: [PATCH 59/95] Do not allow lcd_updatestatus to be called directly Enfoce the usage of lcd_setalertstatuspgm and lcd_setstatus so what we consistently have alert severities properly set. --- Firmware/temperature.cpp | 4 ++-- Firmware/ultralcd.cpp | 5 +++-- Firmware/ultralcd.h | 9 +++------ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 97ed1be48..5d04e3db7 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1626,12 +1626,12 @@ public: last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; break; case States::ShowPleaseRestart: // displaying "Please restart" - lcd_updatestatuspgm(m1); + lcd_setalertstatuspgm(m1); substep(States::ShowMintemp); last_alert_sent_to_lcd = LCDALERT_PLEASERESTART; break; case States::ShowMintemp: // displaying "MINTEMP fixed" - lcd_updatestatuspgm(m2); + lcd_setalertstatuspgm(m2); substep(States::ShowPleaseRestart); last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; break; diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index a7fe85fa9..b952d8e72 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -7956,7 +7956,7 @@ void lcd_setstatus(const char* message) lcd_updatestatus(message); } -void lcd_updatestatuspgm(const char *message){ +static void lcd_updatestatuspgm(const char *message){ strncpy_P(lcd_status_message, message, LCD_WIDTH); lcd_status_message[LCD_WIDTH] = 0; lcd_finishstatus(); @@ -7971,7 +7971,8 @@ void lcd_setstatuspgm(const char* message) lcd_updatestatuspgm(message); } -void lcd_updatestatus(const char *message){ +static void lcd_updatestatus(const char *message) +{ strncpy(lcd_status_message, message, LCD_WIDTH); lcd_status_message[LCD_WIDTH] = 0; lcd_finishstatus(); diff --git a/Firmware/ultralcd.h b/Firmware/ultralcd.h index b7f63ac97..713225d51 100755 --- a/Firmware/ultralcd.h +++ b/Firmware/ultralcd.h @@ -25,13 +25,10 @@ void lcd_setstatuspgm(const char* message); void lcd_setalertstatus(const char* message, uint8_t severity = LCD_STATUS_ALERT); void lcd_setalertstatuspgm(const char* message, uint8_t severity = LCD_STATUS_ALERT); -//! only update the alert message on the main status screen -//! has no sideeffects, may be called multiple times -void lcd_updatestatus(const char *message); -void lcd_updatestatuspgm(const char *message); - -void lcd_reset_alert_level(); +//! Get/reset the current alert level uint8_t get_message_level(); +void lcd_reset_alert_level(); + void lcd_adjust_z(); void lcd_pick_babystep(); void lcd_alright(); From 8d9d367d6be7e001c4a81446a97f1f9ac06cdb0e Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 28 Jun 2022 21:05:03 +0200 Subject: [PATCH 60/95] Implement Timer::expired_cont() Returns true if the timer is not running, effectively allowing to check if a certain set time in the future has passed. --- Firmware/Timer.cpp | 6 ++++++ Firmware/Timer.h | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Firmware/Timer.cpp b/Firmware/Timer.cpp index f63bf2969..d0a552b0a 100644 --- a/Firmware/Timer.cpp +++ b/Firmware/Timer.cpp @@ -77,5 +77,11 @@ T Timer::elapsed() { return m_isRunning ? (_millis() - m_started) : 0; } +template +bool Timer::expired_cont(T msPeriod) +{ + return !m_isRunning || expired(msPeriod); +} + template class Timer; template class Timer; diff --git a/Firmware/Timer.h b/Firmware/Timer.h index dcff52232..9cb18a304 100644 --- a/Firmware/Timer.h +++ b/Firmware/Timer.h @@ -21,8 +21,9 @@ public: void start(); void stop(){m_isRunning = false;} bool running()const {return m_isRunning;} - bool expired(T msPeriod); - T elapsed(); + bool expired(T msPeriod); // returns true only once after expiration, then stops running + T elapsed(); // returns the time in milliseconds since the timer was started or 0 otherwise + bool expired_cont(T msPeriod); // return true when continuosly when expired / not running protected: T started()const {return m_started;} private: From b3ca70a0077e8c77ff32a71adb88d13f1f30f1d3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 28 Jun 2022 21:06:39 +0200 Subject: [PATCH 61/95] Show thermal warnings using the new LCD_MESSAGE_INFO priority Add a new LCD_MESSAGE_INFO priority which can be overridden by regular status updates, but only if a certain amount of time has passed. Assign a time stamp to all message updates, so that the time since the last update can be determined. Also switch the message type to Status, so that the message always becomes visibile. Always show status or info messages when printing via SD if the message is recent enough. --- Firmware/messages.cpp | 3 ++ Firmware/messages.h | 3 ++ Firmware/temperature.cpp | 20 ++++----- Firmware/ultralcd.cpp | 88 ++++++++++++++++++++++++---------------- Firmware/ultralcd.h | 13 ++++-- 5 files changed, 78 insertions(+), 49 deletions(-) diff --git a/Firmware/messages.cpp b/Firmware/messages.cpp index 35a7e3bf8..5cec8102c 100644 --- a/Firmware/messages.cpp +++ b/Firmware/messages.cpp @@ -159,6 +159,9 @@ const char MSG_IR_04_OR_NEWER[] PROGMEM_I1 = ISTR(" 0.4 or newer");////MSG_IR_04 const char MSG_IR_03_OR_OLDER[] PROGMEM_I1 = ISTR(" 0.3 or older");////MSG_IR_03_OR_OLDER c=18 const char MSG_IR_UNKNOWN[] PROGMEM_I1 = ISTR("unknown state");////MSG_IR_UNKNOWN c=18 #endif +#ifdef TEMP_MODEL +extern const char MSG_THERMAL_ANOMALY[] PROGMEM_I1 = ISTR("THERMAL ANOMALY");////c=20 +#endif //not internationalized messages const char MSG_AUTO_DEPLETE[] PROGMEM_N1 = ISTR("SpoolJoin"); ////MSG_AUTO_DEPLETE c=13 diff --git a/Firmware/messages.h b/Firmware/messages.h index 2c9a4a5fc..b5116776c 100644 --- a/Firmware/messages.h +++ b/Firmware/messages.h @@ -168,6 +168,9 @@ extern const char MSG_IR_04_OR_NEWER[]; extern const char MSG_IR_03_OR_OLDER[]; extern const char MSG_IR_UNKNOWN[]; #endif +#ifdef TEMP_MODEL +extern const char MSG_THERMAL_ANOMALY[]; +#endif //not internationalized messages extern const char MSG_BROWNOUT_RESET[]; diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 5d04e3db7..b261c5f2a 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -34,6 +34,7 @@ #include "menu.h" #include "sound.h" #include "fancheck.h" +#include "messages.h" #include "SdFatUtil.h" @@ -2406,23 +2407,22 @@ void handle_warning() } dT_err /= TEMP_MGR_INTV; // per-sample => K/s - // TODO: alert the user on the lcd printf_P(PSTR("TM: error |%f|>%f\n"), (double)dT_err, (double)warn); - static bool beeper = false; + static bool first = true; if(warning_state.assert) { - if(warn_beep) { - // beep periodically - beeper = !beeper; - WRITE(BEEPER, beeper); + if (first) { + lcd_setalertstatuspgm(MSG_THERMAL_ANOMALY, LCD_STATUS_INFO); + if(warn_beep) WRITE(BEEPER, HIGH); + first = false; + } else { + if(warn_beep) TOGGLE(BEEPER); } } else { // warning cleared, reset state warning_state.warning = false; - if(warn_beep) { - beeper = false; - WRITE(BEEPER, LOW); - } + if(warn_beep) WRITE(BEEPER, LOW); + first = true; } } diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index b952d8e72..9fecee9af 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -94,14 +94,15 @@ static bool lcd_autoDeplete; static float manual_feedrate[] = MANUAL_FEEDRATE; +/* LCD message status */ +static LongTimer lcd_status_message_timeout; +static uint8_t lcd_status_message_level; +static char lcd_status_message[LCD_WIDTH + 1] = WELCOME_MSG; + /* !Configuration settings */ -uint8_t lcd_status_message_level; -char lcd_status_message[LCD_WIDTH + 1] = WELCOME_MSG; - static uint8_t lay1cal_filament = 0; - static const char separator[] PROGMEM = "--------------------"; /** forward declarations **/ @@ -597,7 +598,12 @@ void lcdui_print_status_line(void) break; } } - else if ((IS_SD_PRINTING) && (custom_message_type == CustomMsg::Status)) { // If printing from SD, show what we are printing + else if ((IS_SD_PRINTING) && + (custom_message_type == CustomMsg::Status) && + (lcd_status_message_level <= LCD_STATUS_INFO) && + lcd_status_message_timeout.expired_cont(LCD_STATUS_INFO_TIMEOUT)) + { + // If printing from SD, show what we are printing const char* longFilenameOLD = (card.longFilename[0] ? card.longFilename : card.filename); if(strlen(longFilenameOLD) > LCD_WIDTH) { uint8_t gh = scrollstuff; @@ -7949,58 +7955,70 @@ void lcd_finishstatus() { } -void lcd_setstatus(const char* message) +static bool lcd_message_check(uint8_t priority) { - if (lcd_status_message_level > 0) - return; - lcd_updatestatus(message); + // regular priority check + if (priority >= lcd_status_message_level) + return true; + + // check if we can override an info message yet + if (lcd_status_message_level == LCD_STATUS_INFO) { + return lcd_status_message_timeout.expired_cont(LCD_STATUS_INFO_TIMEOUT); + } + + return false; } -static void lcd_updatestatuspgm(const char *message){ - strncpy_P(lcd_status_message, message, LCD_WIDTH); +static void lcd_updatestatus(const char *message, bool progmem = false) +{ + if (progmem) + strncpy_P(lcd_status_message, message, LCD_WIDTH); + else + strncpy(lcd_status_message, message, LCD_WIDTH); + lcd_status_message[LCD_WIDTH] = 0; lcd_finishstatus(); // hack lcd_draw_update to 1, i.e. without clear lcd_draw_update = 1; } +void lcd_setstatus(const char* message) +{ + if (lcd_message_check(LCD_STATUS_NONE)) + lcd_updatestatus(message); +} + void lcd_setstatuspgm(const char* message) { - if (lcd_status_message_level > 0) - return; - lcd_updatestatuspgm(message); + if (lcd_message_check(LCD_STATUS_NONE)) + lcd_updatestatus(message, true); } -static void lcd_updatestatus(const char *message) +void lcd_setalertstatus_(const char* message, uint8_t severity, bool progmem) { - strncpy(lcd_status_message, message, LCD_WIDTH); - lcd_status_message[LCD_WIDTH] = 0; - lcd_finishstatus(); - // hack lcd_draw_update to 1, i.e. without clear - lcd_draw_update = 1; -} - -void lcd_setalertstatuspgm(const char* message, uint8_t severity) -{ - if (severity > lcd_status_message_level) { - lcd_updatestatuspgm(message); - lcd_status_message_level = severity; - lcd_return_to_status(); - } + if (lcd_message_check(severity)) { + lcd_updatestatus(message, progmem); + lcd_status_message_timeout.start(); + lcd_status_message_level = severity; + custom_message_type = CustomMsg::Status; + custom_message_state = 0; + lcd_return_to_status(); + } } void lcd_setalertstatus(const char* message, uint8_t severity) { - if (severity > lcd_status_message_level) { - lcd_updatestatus(message); - lcd_status_message_level = severity; - lcd_return_to_status(); - } + lcd_setalertstatus_(message, severity, false); +} + +void lcd_setalertstatuspgm(const char* message, uint8_t severity) +{ + lcd_setalertstatus_(message, severity, true); } void lcd_reset_alert_level() { - lcd_status_message_level = 0; + lcd_status_message_level = 0; } uint8_t get_message_level() diff --git a/Firmware/ultralcd.h b/Firmware/ultralcd.h index 713225d51..d3c94e897 100755 --- a/Firmware/ultralcd.h +++ b/Firmware/ultralcd.h @@ -9,14 +9,19 @@ extern void menu_lcd_lcdupdate_func(void); // Call with a false parameter to suppress the LCD update from various places like the planner or the temp control. void ultralcd_init(); -void lcd_setstatus(const char* message); -void lcd_setstatuspgm(const char* message); //! LCD status severities -#define LCD_STATUS_CRITICAL 2 //< Heater failure -#define LCD_STATUS_ALERT 1 //< Other hardware issue +#define LCD_STATUS_CRITICAL 3 //< Heater failure +#define LCD_STATUS_ALERT 2 //< Other hardware issue +#define LCD_STATUS_INFO 1 //< Message times out after a while #define LCD_STATUS_NONE 0 //< No alert message set +#define LCD_STATUS_INFO_TIMEOUT 20000 + +// Set the current status message (equivalent to LCD_STATUS_NONE) +void lcd_setstatus(const char* message); +void lcd_setstatuspgm(const char* message); + //! return to the main status screen and display the alert message //! Beware - it has sideeffects: //! - always returns the display to the main status screen From fc10ca31466e1c58bcb5b9b90976a489239a6fde Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 29 Jun 2022 13:51:57 +0200 Subject: [PATCH 62/95] Change how "Stopped" is handled internally, do not inhibit motion Do not inhibit motion when Stopped is set. We actually do need to move to move away the extruder from the bed, and setting Stopped breaks it without adding any sort of security (M* commands, such as M600 could still perform moves and still pass through, while M104 would still set heaters). During a hard error the internal queue is cleared (and sd file closed, if any), so no new "unforeseen" command can be read. Handle "Stopped" instead as a flag to inhibit serial processing and automatically switch to "paused for user" state. While in this state simply drop any input without incrementing the processed gcode line number, behaving as-if the last command was still being processed. This allows "Stopped" to correctly handle a printer-initiated paused state and recover as expected by requesting a resend when resuming. --- Firmware/Marlin_main.cpp | 34 ++++++++++++++++------------------ Firmware/cmdqueue.cpp | 40 +++++++++++++++++++++++++--------------- Firmware/cmdqueue.h | 1 - Firmware/messages.cpp | 2 +- 4 files changed, 42 insertions(+), 35 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 217ee1299..73d151aa1 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1843,7 +1843,14 @@ void host_keepalive() { // Before loop(), the setup() function is called by the main() routine. void loop() { - KEEPALIVE_STATE(NOT_BUSY); + if(Stopped) { + // Currently Stopped (possibly due to an error) and not accepting new serial commands. + // Signal to the host that we're currently busy waiting for supervision. + KEEPALIVE_STATE(PAUSED_FOR_USER); + } else { + // Printer is available for processing, reset state + KEEPALIVE_STATE(NOT_BUSY); + } if (isPrintPaused && saved_printing_type == PRINTING_TYPE_USB) { //keep believing that usb is being printed. Prevents accessing dangerous menus while pausing. usb_timer.start(); @@ -4648,7 +4655,7 @@ eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,0xFFFF); */ case 0: // G0 -> G1 case 1: // G1 - if(Stopped == false) { + { get_coordinates(); // For X Y Z E F // When recovering from a previous print move, restore the originally @@ -4707,7 +4714,7 @@ eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,0xFFFF); */ case 2: - if(Stopped == false) { + { get_arc_coordinates(); prepare_arc_move(true); } @@ -4715,7 +4722,7 @@ eeprom_update_word((uint16_t*)EEPROM_NOZZLE_DIAMETER_uM,0xFFFF); // ------------------------------- case 3: - if(Stopped == false) { + { get_arc_coordinates(); prepare_arc_move(false); } @@ -8777,16 +8784,6 @@ Sigma_Exit: } break; - /*! - ### M999 - Restart after being stopped M999: Restart after being stopped by error - @todo Usually doesn't work. Should be fixed or removed. Most of the time, if `Stopped` it set, the print fails and is unrecoverable. - */ - case 999: - Stopped = false; - lcd_reset_alert_level(); - gcode_LastN = Stopped_gcode_LastN; - FlushSerialRequestResend(); - break; /*! #### End of M-Commands */ @@ -8930,7 +8927,7 @@ Sigma_Exit: active_extruder = tmp_extruder; plan_set_position_curposXYZE(); // Move to the old position if 'F' was in the parameters - if (make_move && Stopped == false) { + if (make_move) { prepare_move(); } } @@ -9992,12 +9989,13 @@ void ThermalStop(bool pause) // We got a hard thermal error and/or there is no print going on. Just stop. lcd_print_stop(); } - Stopped_gcode_LastN = gcode_LastN; // Save last g_code for "restart" - // Eventually report the stopped status (though this is usually overridden by a - // higher-priority alert status message) + // Report the status on the serial, switch to a busy state SERIAL_ERROR_START; SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED); + + // Eventually report the stopped status on the lcd (though this is usually overridden by a + // higher-priority alert status message) LCD_MESSAGERPGM(_T(MSG_STOPPED)); Sound_MakeCustom(1000,0,true); diff --git a/Firmware/cmdqueue.cpp b/Firmware/cmdqueue.cpp index 0380c16d1..f7a382c9a 100755 --- a/Firmware/cmdqueue.cpp +++ b/Firmware/cmdqueue.cpp @@ -28,7 +28,6 @@ ShortTimer serialTimeoutTimer; long gcode_N = 0; long gcode_LastN = 0; -long Stopped_gcode_LastN = 0; uint32_t sdpos_atomic = 0; @@ -464,8 +463,6 @@ void get_command() // Don't parse N again with code_seen('N') cmdbuffer[bufindw + CMDHDRSIZE] = '$'; - //if no errors, continue parsing - gcode_LastN = gcode_N; } // if we don't receive 'N' but still see '*' if ((cmdbuffer[bufindw + CMDHDRSIZE] != 'N') && (cmdbuffer[bufindw + CMDHDRSIZE] != '$') && (strchr(cmdbuffer+bufindw+CMDHDRSIZE, '*') != NULL)) @@ -478,35 +475,48 @@ void get_command() serial_count = 0; return; } + // Handle KILL early, even when Stopped + if(strcmp(cmdbuffer+bufindw+CMDHDRSIZE, "M112") == 0) + kill(MSG_M112_KILL, 2); + // Handle the USB timer if ((strchr_pointer = strchr(cmdbuffer+bufindw+CMDHDRSIZE, 'G')) != NULL) { if (!IS_SD_PRINTING) { usb_timer.start(); } - if (Stopped == true) { - if (code_value_uint8() <= 3) { - SERIAL_ERRORLNRPGM(MSG_ERR_STOPPED); - LCD_MESSAGERPGM(_T(MSG_STOPPED)); - } - } - } // end of 'G' command + } + if (Stopped == true) { + // Stopped can be set either during error states (thermal error: cannot continue), or + // when a printer-initiated action is processed. In such case the printer will send to + // the host an action, but cannot know if the action has been processed while new + // commands are being sent. In this situation we just drop the command while issuing + // periodic "busy" messages in the main loop. Since we're not incrementing the received + // line number, a request for resend will happen (if necessary), ensuring we don't skip + // commands whenever Stopped is cleared and processing resumes. + serial_count = 0; + return; + } + + // Command is complete: store the current line into buffer, move to the next line. - //If command was e-stop process now - if(strcmp(cmdbuffer+bufindw+CMDHDRSIZE, "M112") == 0) - kill(MSG_M112_KILL, 2); - - // Store the current line into buffer, move to the next line. // Store type of entry cmdbuffer[bufindw] = gcode_N ? CMDBUFFER_CURRENT_TYPE_USB_WITH_LINENR : CMDBUFFER_CURRENT_TYPE_USB; + #ifdef CMDBUFFER_DEBUG SERIAL_ECHO_START; SERIAL_ECHOPGM("Storing a command line to buffer: "); SERIAL_ECHO(cmdbuffer+bufindw+CMDHDRSIZE); SERIAL_ECHOLNPGM(""); #endif /* CMDBUFFER_DEBUG */ + + // Store command itself bufindw += strlen(cmdbuffer+bufindw+CMDHDRSIZE) + (1 + CMDHDRSIZE); if (bufindw == sizeof(cmdbuffer)) bufindw = 0; ++ buflen; + + // Update the processed gcode line + gcode_LastN = gcode_N; + #ifdef CMDBUFFER_DEBUG SERIAL_ECHOPGM("Number of commands in the buffer: "); SERIAL_ECHO(buflen); diff --git a/Firmware/cmdqueue.h b/Firmware/cmdqueue.h index 0a573c477..5229b96b0 100644 --- a/Firmware/cmdqueue.h +++ b/Firmware/cmdqueue.h @@ -54,7 +54,6 @@ extern char *strchr_pointer; extern long gcode_N; extern long gcode_LastN; -extern long Stopped_gcode_LastN; extern bool cmdqueue_pop_front(); extern void cmdqueue_reset(); diff --git a/Firmware/messages.cpp b/Firmware/messages.cpp index 5cec8102c..66ada492d 100644 --- a/Firmware/messages.cpp +++ b/Firmware/messages.cpp @@ -189,7 +189,7 @@ const char MSG_OK[] PROGMEM_N1 = "ok"; //// const char MSG_SD_OPEN_FILE_FAIL[] PROGMEM_N1 = "open failed, File: "; //// const char MSG_ENDSTOP_OPEN[] PROGMEM_N1 = "open"; //// const char MSG_POWERUP[] PROGMEM_N1 = "PowerUp"; //// -const char MSG_ERR_STOPPED[] PROGMEM_N1 = "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"; //// +const char MSG_ERR_STOPPED[] PROGMEM_N1 = "Printer stopped due to errors. Supervision required."; //// const char MSG_ENDSTOP_HIT[] PROGMEM_N1 = "TRIGGERED"; //// const char MSG_OCTOPRINT_PAUSE[] PROGMEM_N1 = "// action:pause"; //// const char MSG_OCTOPRINT_PAUSED[] PROGMEM_N1 = "// action:paused"; //// From 142db85bbb5e5170222b5905efd98d05e6cd155c Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Wed, 29 Jun 2022 14:30:37 +0200 Subject: [PATCH 63/95] Pause: only move to park position when homed This can happen is pause is issued before homing. --- Firmware/Marlin_main.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 73d151aa1..b6236ed75 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -2222,6 +2222,7 @@ void raise_z_above(float target, bool plan) // Z needs raising current_position[Z_AXIS] = target; + clamp_to_software_endstops(current_position); #if defined(Z_MIN_PIN) && (Z_MIN_PIN > -1) && !defined(DEBUG_DISABLE_ZMINLIMIT) bool z_min_endstop = (READ(Z_MIN_PIN) != Z_MIN_ENDSTOP_INVERTING); @@ -10839,15 +10840,15 @@ void long_pause() //long pause print // Stop heaters setAllTargetHotends(0); - //lift z - current_position[Z_AXIS] += Z_PAUSE_LIFT; - clamp_to_software_endstops(current_position); - plan_buffer_line_curposXYZE(15); + // Lift z + raise_z_above(current_position[Z_AXIS] + Z_PAUSE_LIFT, true); - //Move XY to side - current_position[X_AXIS] = X_PAUSE_POS; - current_position[Y_AXIS] = Y_PAUSE_POS; - plan_buffer_line_curposXYZE(50); + // Move XY to side + if (axis_known_position[X_AXIS] && axis_known_position[Y_AXIS]) { + current_position[X_AXIS] = X_PAUSE_POS; + current_position[Y_AXIS] = Y_PAUSE_POS; + plan_buffer_line_curposXYZE(50); + } // Turn off the print fan fanSpeed = 0; From 48c7c9d464115e2ef77b06def1b2f6a326b38bf3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 30 Jun 2022 14:54:36 +0200 Subject: [PATCH 64/95] Exclude M310 if TEMP_MODEL is not enabled --- Firmware/Marlin_main.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index b6236ed75..3ca6681f5 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7773,6 +7773,7 @@ Sigma_Exit: } break; +#ifdef TEMP_MODEL /*! ### M310 - Temperature model #### Usage @@ -7828,6 +7829,7 @@ Sigma_Exit: if(A >= 0) temp_model_autotune(A); } break; +#endif /*! ### M400 - Wait for all moves to finish M400: Wait for current moves to finish From 5965572e88038488a403cfa1e18cb28d333a6dcf Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 4 Jul 2022 23:13:50 +0200 Subject: [PATCH 65/95] Enforce full-loop handling of aborted commands After calling planner_abort_hard() no motion command can be scheduled until we return to the main loop since the call can potentially be scheduled inside a nested process_command call. Despite previous fixes, bugs keep creeping in due to nested calls not being obvious to detect at all. Stop allowing motion _completely_ for the entire processing loop by default. That is, instead of aborting the current plan_buffer_line call, abort the entire command until we can actually schedule motion safely again. This benefits handling of pretty much all g/m-codes, since this flag (now "planner_aborted" for clarity) becomes a general "command aborted" call. This also now ensures that the flag prevents _any_ new block (including blocks partially planned while servicing an interrupt) are scheduled after planner_abort_hard is called. There are only two exceptions where it's safe to resume in this context: - Within uvlo_, where we never return to the main processing loop - When we're intentionally scheduling a new process_command loop for a MK3 filament recheck (which is *bad*) Handle those two cases as exceptions. --- Firmware/Marlin_main.cpp | 24 +++++++++---------- Firmware/motion_control.cpp | 2 +- Firmware/planner.cpp | 46 +++++++++++++++---------------------- Firmware/planner.h | 2 +- 4 files changed, 31 insertions(+), 43 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 3ca6681f5..149a18760 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1843,6 +1843,9 @@ void host_keepalive() { // Before loop(), the setup() function is called by the main() routine. void loop() { + // Reset a previously aborted command, we can now start processing motion again + planner_aborted = false; + if(Stopped) { // Currently Stopped (possibly due to an error) and not accepting new serial commands. // Signal to the host that we're currently busy waiting for supervision. @@ -2999,7 +3002,7 @@ static void gcode_G28(bool home_x_axis, bool home_y_axis, bool home_z_axis) static void gcode_G80() { st_synchronize(); - if (waiting_inside_plan_buffer_line_print_aborted) + if (planner_aborted) return; mesh_bed_leveling_flag = true; @@ -3093,7 +3096,7 @@ static void gcode_G80() plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE); // Wait until the move is finished. st_synchronize(); - if (waiting_inside_plan_buffer_line_print_aborted) + if (planner_aborted) { custom_message_type = custom_message_type_old; custom_message_state = custom_message_state_old; @@ -3168,7 +3171,7 @@ static void gcode_G80() //printf_P(PSTR("after clamping: [%f;%f]\n"), current_position[X_AXIS], current_position[Y_AXIS]); plan_buffer_line_curposXYZE(XY_AXIS_FEEDRATE); st_synchronize(); - if (waiting_inside_plan_buffer_line_print_aborted) + if (planner_aborted) { custom_message_type = custom_message_type_old; custom_message_state = custom_message_state_old; @@ -9546,7 +9549,7 @@ void mesh_plan_buffer_line(const float &x, const float &y, const float &z, const current_position[Z_AXIS] + t * dz, current_position[E_AXIS] + t * de, feed_rate, extruder, gcode_target); - if (waiting_inside_plan_buffer_line_print_aborted) + if (planner_aborted) return; } } @@ -10957,6 +10960,7 @@ void uvlo_() // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and // command queues are empty, SD card printing is disabled, usb is inhibited. + planner_aborted = false; sei(); // Retract @@ -11089,6 +11093,7 @@ void uvlo_tiny() // Enable stepper driver interrupt to move Z axis. This should be fine as the planner and // command queues are empty, SD card printing is disabled, usb is inhibited. + planner_aborted = false; sei(); // The axis was moved: adjust Z as done on a regular UVLO. @@ -11568,7 +11573,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move) st_reset_timer(); sei(); if ((z_move != 0) || (e_move != 0)) { // extruder or z move -#if 1 + // Rather than calling plan_buffer_line directly, push the move into the command queue so that // the caller can continue processing. This is used during powerpanic to save the state as we // move away from the print. @@ -11600,13 +11605,6 @@ void stop_and_save_print_to_ram(float z_move, float e_move) // If this call is invoked from the main Arduino loop() function, let the caller know that the command // in the command queue is not the original command, but a new one, so it should not be removed from the queue. repeatcommand_front(); -#else - plan_buffer_line(saved_pos[X_AXIS], saved_pos[Y_AXIS], saved_pos[Z_AXIS] + z_move, saved_pos[E_AXIS] + e_move, homing_feedrate[Z_AXIS], active_extruder); - st_synchronize(); //wait moving - memcpy(current_position, saved_pos, sizeof(saved_pos)); - set_destination_to_current(); -#endif - waiting_inside_plan_buffer_line_print_aborted = true; //unroll the stack } } @@ -11690,7 +11688,7 @@ void restore_print_from_ram_and_continue(float e_move) lcd_setstatuspgm(MSG_WELCOME); saved_printing_type = PRINTING_TYPE_NONE; saved_printing = false; - waiting_inside_plan_buffer_line_print_aborted = true; //unroll the stack + planner_aborted = true; // unroll the stack } // Cancel the state related to a currently saved print diff --git a/Firmware/motion_control.cpp b/Firmware/motion_control.cpp index e24428944..5b062d3f1 100644 --- a/Firmware/motion_control.cpp +++ b/Firmware/motion_control.cpp @@ -151,7 +151,7 @@ void mc_arc(float* position, float* target, float* offset, float feed_rate, floa // Insert the segment into the buffer plan_buffer_line(position[X_AXIS], position[Y_AXIS], position[Z_AXIS], position[E_AXIS], feed_rate, extruder, position); // Handle the situation where the planner is aborted hard. - if (waiting_inside_plan_buffer_line_print_aborted) + if (planner_aborted) return; } } diff --git a/Firmware/planner.cpp b/Firmware/planner.cpp index 5b513034d..2b07c77bc 100644 --- a/Firmware/planner.cpp +++ b/Firmware/planner.cpp @@ -69,6 +69,9 @@ #include "tmc2130.h" #endif //TMC2130 +#include + + //=========================================================================== //=============================public variables ============================ //=========================================================================== @@ -593,17 +596,7 @@ void check_axes_activity() #endif } -bool waiting_inside_plan_buffer_line_print_aborted = false; -/* -void planner_abort_soft() -{ - // Empty the queue. - while (blocks_queued()) plan_discard_current_block(); - // Relay to planner wait routine, that the current line shall be canceled. - waiting_inside_plan_buffer_line_print_aborted = true; - //current_position[i] -} -*/ +bool planner_aborted = false; #ifdef PLANNER_DIAGNOSTICS static inline void planner_update_queue_min_counter() @@ -665,6 +658,9 @@ void planner_abort_hard() #endif } #endif + // Relay to planner wait routine, that the current line shall be canceled. + planner_aborted = true; + // Clear the planner queue, reset and re-enable the stepper timer. quickStop(); @@ -680,9 +676,6 @@ void planner_abort_hard() plan_reset_next_e_queue = false; plan_reset_next_e_sched = false; - - // Relay to planner wait routine, that the current line shall be canceled. - waiting_inside_plan_buffer_line_print_aborted = true; } void plan_buffer_line_curposXYZE(float feed_rate) { @@ -706,10 +699,7 @@ void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate // Calculate the buffer head after we push this byte uint8_t next_buffer_head = next_block_index(block_buffer_head); - // TODO: shouldn't this be reset only in the outer marlin loop? - waiting_inside_plan_buffer_line_print_aborted = false; - - // If the buffer is full: good! That means we are well ahead of the robot. + // If the buffer is full: good! That means we are well ahead of the robot. // Rest here until there is room in the buffer. if (block_buffer_tail == next_buffer_head) { do { @@ -718,18 +708,14 @@ void plan_buffer_line(float x, float y, float z, const float &e, float feed_rate manage_inactivity(false); lcd_update(0); } while (block_buffer_tail == next_buffer_head); - if (waiting_inside_plan_buffer_line_print_aborted) { - // Inside the lcd_update(0) routine the print has been aborted. - // Cancel the print, do not plan the current line this routine is waiting on. -#ifdef PLANNER_DIAGNOSTICS - planner_update_queue_min_counter(); -#endif /* PLANNER_DIAGNOSTICS */ - return; - } } #ifdef PLANNER_DIAGNOSTICS planner_update_queue_min_counter(); #endif /* PLANNER_DIAGNOSTICS */ + if(planner_aborted) { + // avoid planning the block early if aborted + return; + } // Prepare to set up new block block_t *block = &block_buffer[block_buffer_head]; @@ -1334,8 +1320,12 @@ Having the real displacement of the head, we can calculate the total movement le if (block->step_event_count.wide <= 32767) block->flag |= BLOCK_FLAG_DDA_LOWRES; - // Move the buffer head. From now the block may be picked up by the stepper interrupt controller. - block_buffer_head = next_buffer_head; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + // Move the buffer head ensuring the current block hasn't been cancelled from an isr context + // (this is possible both during crash detection *and* uvlo, thus needing a global cli) + if(planner_aborted) return; + block_buffer_head = next_buffer_head; + } // Update position memcpy(position, target, sizeof(target)); // position[] = target[] diff --git a/Firmware/planner.h b/Firmware/planner.h index 72235703b..75066a80b 100644 --- a/Firmware/planner.h +++ b/Firmware/planner.h @@ -259,7 +259,7 @@ FORCE_INLINE bool planner_queue_full() { // wait for the steppers to stop, // update planner's current position and the current_position of the front end. extern void planner_abort_hard(); -extern bool waiting_inside_plan_buffer_line_print_aborted; +extern bool planner_aborted; #ifdef PREVENT_DANGEROUS_EXTRUDE extern int extrude_min_temp; From f835537e88714691d8559f139a1b33c8c36177c3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 4 Jul 2022 23:34:23 +0200 Subject: [PATCH 66/95] Exclude TempErrorSource::ambient for boards without a thermistor --- Firmware/temperature.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index b261c5f2a..484ac588f 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -460,7 +460,9 @@ enum class TempErrorSource : uint8_t { hotend, bed, +#ifdef AMBIENT_THERMISTOR ambient, +#endif }; // thermal error type (in order of decreasing priority!) @@ -1708,9 +1710,11 @@ void handle_temp_error() alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS); } break; +#ifdef AMBIENT_THERMISTOR case TempErrorSource::ambient: ambient_min_temp_error(); break; +#endif } break; case TempErrorType::max: @@ -1721,9 +1725,11 @@ void handle_temp_error() case TempErrorSource::bed: bed_max_temp_error(); break; +#ifdef AMBIENT_THERMISTOR case TempErrorSource::ambient: ambient_max_temp_error(); break; +#endif } break; case TempErrorType::preheat: @@ -1735,9 +1741,11 @@ void handle_temp_error() ((TempErrorType)temp_error_state.type == TempErrorType::preheat), ((TempErrorSource)temp_error_state.source == TempErrorSource::bed)); break; +#ifdef AMBIENT_THERMISTOR case TempErrorSource::ambient: // not needed break; +#endif } break; #ifdef TEMP_MODEL From d1864011f4f9d6871ee952b7d6e9f2286826d3eb Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 5 Jul 2022 00:08:45 +0200 Subject: [PATCH 67/95] Handle pause/stop in the main loop, again Force processing of the pause and stop "parking" commands _after_ the main loop completes. This was/is currently done in lcd_commands, which is a poor place to continue processing, since it can be called already within an aborted command. This requires checking for planner_aborted before any action can be performed. --- Firmware/Marlin_main.cpp | 2 + Firmware/ultralcd.cpp | 106 +++++++++++++++++++++++---------------- 2 files changed, 65 insertions(+), 43 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 149a18760..194d90839 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9951,6 +9951,7 @@ void UnconditionalStop() // Disable all heaters and unroll the temperature wait loop stack disable_heater(); cancel_heatup = true; + heating_status = HeatingStatus::NO_HEATING; // Clear any saved printing state cancel_saved_printing(); @@ -10843,6 +10844,7 @@ void long_pause() //long pause print start_pause_print = _millis(); // Stop heaters + heating_status = HeatingStatus::NO_HEATING; setAllTargetHotends(0); // Lift z diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index 9fecee9af..a7add6ff6 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -872,14 +872,34 @@ void lcd_status_screen() // NOT static due to using ins } } +void print_stop(); + void lcd_commands() { + if (planner_aborted) { + // we are still within an aborted command. do not process any LCD command until we return + return; + } + + if (lcd_commands_type == LcdCommands::StopPrint) + { + if (!blocks_queued() && !homing_flag) + { + custom_message_type = CustomMsg::Status; + lcd_setstatuspgm(_T(MSG_PRINT_ABORTED)); + lcd_commands_type = LcdCommands::Idle; + lcd_commands_step = 0; + print_stop(); + } + } + if (lcd_commands_type == LcdCommands::LongPause) { if (!blocks_queued() && !homing_flag) { if (custom_message_type != CustomMsg::M117) { + custom_message_type = CustomMsg::Status; lcd_setstatuspgm(_i("Print paused"));////MSG_PRINT_PAUSED c=20 } lcd_commands_type = LcdCommands::Idle; @@ -1070,12 +1090,16 @@ void lcd_return_to_status() void lcd_pause_print() { stop_and_save_print_to_ram(0.0, -default_retraction); - lcd_return_to_status(); - isPrintPaused = true; - if (LcdCommands::Idle == lcd_commands_type) { - lcd_commands_type = LcdCommands::LongPause; + + if (!card.sdprinting) { + SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_PAUSED); } - SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); + + isPrintPaused = true; + + // return to status is required to continue processing in the main loop! + lcd_commands_type = LcdCommands::LongPause; + lcd_return_to_status(); } //! @brief Send host action "pause" @@ -6289,38 +6313,19 @@ static void lcd_sd_updir() menu_data_reset(); //Forces reloading of cached variables. } -void lcd_print_stop() +// continue stopping the print from the main loop after lcd_print_stop() is called +void print_stop() { - if (!card.sdprinting) { - SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); // for Octoprint - } - UnconditionalStop(); + // save printing time + stoptime = _millis(); + unsigned long t = (stoptime - starttime - pause_time) / 1000; //time in s + save_statistics(total_filament_used, t); - // TODO: all the following should be moved in the main marlin loop! -#ifdef MESH_BED_LEVELING - mbl.active = false; //also prevents undoing the mbl compensation a second time in the second planner_abort_hard() -#endif + // lift Z + raise_z_above(current_position[Z_AXIS] + 10, true); - lcd_setstatuspgm(_T(MSG_PRINT_ABORTED)); - stoptime = _millis(); - unsigned long t = (stoptime - starttime - pause_time) / 1000; //time in s - pause_time = 0; - save_statistics(total_filament_used, t); - - // reset current command - lcd_commands_step = 0; - lcd_commands_type = LcdCommands::Idle; - - lcd_cooldown(); //turns off heaters and fan; goes to status screen. - - if (axis_known_position[Z_AXIS]) { - current_position[Z_AXIS] += Z_CANCEL_LIFT; - clamp_to_software_endstops(current_position); - plan_buffer_line_curposXYZE(manual_feedrate[Z_AXIS] / 60); - } - - if (axis_known_position[X_AXIS] && axis_known_position[Y_AXIS]) //if axis are homed, move to parked position. - { + // if axis are homed, move to parking position. + if (axis_known_position[X_AXIS] && axis_known_position[Y_AXIS]) { current_position[X_AXIS] = X_CANCEL_POS; current_position[Y_AXIS] = Y_CANCEL_POS; plan_buffer_line_curposXYZE(manual_feedrate[0] / 60); @@ -6328,17 +6333,32 @@ void lcd_print_stop() st_synchronize(); if (mmu_enabled) extr_unload(); //M702 C - finishAndDisableSteppers(); //M84 - - lcd_setstatuspgm(MSG_WELCOME); - custom_message_type = CustomMsg::Status; - - planner_abort_hard(); //needs to be done since plan_buffer_line resets waiting_inside_plan_buffer_line_print_aborted to false. Also copies current to destination. - axis_relative_modes = E_AXIS_MASK; //XYZ absolute, E relative - - isPrintPaused = false; //clear isPrintPaused flag to allow starting next print after pause->stop scenario. +} + +void lcd_print_stop() +{ + // UnconditionalStop() will internally cause planner_abort_hard(), meaning we _cannot_ plan + // any more move in this call! Any further move must happen inside print_stop(), which is called + // by the main loop one iteration later. + UnconditionalStop(); + + if (!card.sdprinting) { + SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_CANCEL); // for Octoprint + } + +#ifdef MESH_BED_LEVELING + mbl.active = false; +#endif + + // clear any pending paused state immediately + pause_time = 0; + isPrintPaused = false; + + // return to status is required to continue processing in the main loop! + lcd_commands_type = LcdCommands::StopPrint; + lcd_return_to_status(); } void lcd_sdcard_stop() From 5dc0d5f7fa6181d0550b4b087c73ae51b8b77674 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 00:27:57 +0200 Subject: [PATCH 68/95] TM autotune: fail if value is outside of the boundaries Ensure we never fall into the boundary values provided by the min/max limits. Save/restore the initial guess value, so that a convergence failure restores the initial model state. --- Firmware/temperature.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 484ac588f..6a8b0798e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2695,6 +2695,7 @@ float estimate(uint16_t samples, float thr, uint16_t max_itr, uint8_t fan_pwm, float ambient) { + float orig = *var; float e = NAN; float points[2]; float bounds[2] = {min, max}; @@ -2710,10 +2711,19 @@ float estimate(uint16_t samples, e = (1-GOLDEN_RATIO) * fabsf((bounds[0]-bounds[1]) / x); printf_P(PSTR("TM iter:%u v:%.2f e:%.3f\n"), it, x, e); - if(e < thr) return e; + if(e < thr) { + if(x == min || x == max) { + // real value likely outside of the search boundaries + break; + } + + *var = x; + return e; + } } SERIAL_ECHOLNPGM("TM estimation did not converge"); + *var = orig; return NAN; } From f454d1ecf22ba1c282f2b7911e0089ad079ce465 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 00:46:52 +0200 Subject: [PATCH 69/95] TM autotune: fix off-by-one in fan power levels --- Firmware/temperature.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 6a8b0798e..042d3d30a 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2784,8 +2784,8 @@ bool autotune(int16_t cal_temp) fanSpeedSoftPwm = 255; wait(30000); - for(int8_t i = TEMP_MODEL_R_SIZE; i > 0; i -= TEMP_MODEL_CAL_R_STEP) { - fanSpeedSoftPwm = 256 / TEMP_MODEL_R_SIZE * i - 1; + for(int8_t i = TEMP_MODEL_R_SIZE - 1; i > 0; i -= TEMP_MODEL_CAL_R_STEP) { + fanSpeedSoftPwm = 256 / TEMP_MODEL_R_SIZE * (i + 1) - 1; wait(10000); printf_P(PSTR("TM: R[%u] estimation\n"), (unsigned)i); @@ -2803,6 +2803,7 @@ bool autotune(int16_t cal_temp) } // interpolate remaining steps to speed-up calibration + // TODO: verify that the sampled values are monotically increasing? int8_t next = TEMP_MODEL_R_SIZE - 1; for(uint8_t i = TEMP_MODEL_R_SIZE - 2; i != 0; --i) { if(!((TEMP_MODEL_R_SIZE - i - 1) % TEMP_MODEL_CAL_R_STEP)) { From b9fc73c4c360ecc743771dc47c64bc77e74d68c1 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 10:12:57 +0200 Subject: [PATCH 70/95] TM: Pause the print and allow recovery from a thermal error Do not allow resuming until all thermal and fan errors are clear. Call the appropriate resume function when resuming a printing depending on the saved_print state (is saved_print is available, then we always need to resume from the saved state even when printing via usb). Clear the Stopped state when resuming, so that commands can be accepted again. --- Firmware/Marlin_main.cpp | 16 ++++++++++++---- Firmware/messages.cpp | 1 + Firmware/messages.h | 1 + Firmware/temperature.cpp | 12 ++++++++++-- Firmware/temperature.h | 1 + Firmware/ultralcd.cpp | 31 +++++++++++++++++++++++-------- 6 files changed, 48 insertions(+), 14 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 194d90839..f83d3c799 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9989,7 +9989,7 @@ void ThermalStop(bool pause) Stopped = true; if(pause && (IS_SD_PRINTING || usb_timer.running())) { if (!isPrintPaused) { - // we cannot make a distinction for an host here, the pause must be instantaneous + // we cannot make a distinction for the host here, the pause must be instantaneous lcd_pause_print(); } } else { @@ -10005,7 +10005,9 @@ void ThermalStop(bool pause) // higher-priority alert status message) LCD_MESSAGERPGM(_T(MSG_STOPPED)); - Sound_MakeCustom(1000,0,true); + // Make a warning sound! We cannot use Sound_MakeCustom as this would stop further moves. + // Turn on the speaker here (if not already), and turn it off when back in the main loop. + WRITE(BEEPER, HIGH); } // Return to the status screen to stop any pending menu action which could have been @@ -10857,8 +10859,14 @@ void long_pause() //long pause print plan_buffer_line_curposXYZE(50); } - // Turn off the print fan - fanSpeed = 0; + // did we come here from a thermal error? + if(get_temp_error()) { + // time to stop the error beep + WRITE(BEEPER, LOW); + } else { + // Turn off the print fan + fanSpeed = 0; + } } void serialecho_temperatures() { diff --git a/Firmware/messages.cpp b/Firmware/messages.cpp index 66ada492d..2b5e21d61 100644 --- a/Firmware/messages.cpp +++ b/Firmware/messages.cpp @@ -161,6 +161,7 @@ const char MSG_IR_UNKNOWN[] PROGMEM_I1 = ISTR("unknown state");////MSG_IR_UNKNOW #endif #ifdef TEMP_MODEL extern const char MSG_THERMAL_ANOMALY[] PROGMEM_I1 = ISTR("THERMAL ANOMALY");////c=20 +extern const char MSG_PAUSED_THERMAL_ERROR[] PROGMEM_I1 = ISTR("PAUSED THERMAL ERROR");////c=20 #endif //not internationalized messages diff --git a/Firmware/messages.h b/Firmware/messages.h index b5116776c..1cfbe6c37 100644 --- a/Firmware/messages.h +++ b/Firmware/messages.h @@ -170,6 +170,7 @@ extern const char MSG_IR_UNKNOWN[]; #endif #ifdef TEMP_MODEL extern const char MSG_THERMAL_ANOMALY[]; +extern const char MSG_PAUSED_THERMAL_ERROR[]; #endif //not internationalized messages diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 042d3d30a..4a7aeca1a 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -512,6 +512,11 @@ void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) temp_error_state.assert = true; } +bool get_temp_error() +{ + return temp_error_state.v; +} + void handle_temp_error(); void manage_heater() @@ -1751,8 +1756,11 @@ void handle_temp_error() #ifdef TEMP_MODEL case TempErrorType::model: if(temp_error_state.assert) { - // TODO: do something meaningful - SERIAL_ECHOLNPGM("TM: error triggered!"); + if(IsStopped() == false) { + lcd_setalertstatuspgm(MSG_PAUSED_THERMAL_ERROR, LCD_STATUS_CRITICAL); + SERIAL_ECHOLNPGM("TM: error triggered!"); + } + ThermalStop(true); WRITE(BEEPER, HIGH); } else { temp_error_state.v = 0; diff --git a/Firmware/temperature.h b/Firmware/temperature.h index bae77091b..571165784 100755 --- a/Firmware/temperature.h +++ b/Firmware/temperature.h @@ -42,6 +42,7 @@ void soft_pwm_init(); //initialize the soft pwm isr void temp_mgr_init(); //initialize the temperature handler void manage_heater(); //it is critical that this is called periodically. +bool get_temp_error(); //return true if any thermal error is set extern bool checkAllHotends(void); diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index a7add6ff6..e2e1a4680 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -5698,16 +5698,20 @@ static bool fan_error_selftest() void lcd_resume_print() { lcd_return_to_status(); - lcd_reset_alert_level(); //for fan speed error - if (fan_error_selftest()) { + lcd_reset_alert_level(); + + // ensure thermal issues (temp or fan) are resolved before we allow to resume + if (get_temp_error() || fan_error_selftest()) { if (usb_timer.running()) SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); - return; //abort if error persists + return; // abort if error persists } + cmdqueue_serial_disabled = false; lcd_setstatuspgm(_T(MSG_FINISHING_MOVEMENTS)); st_synchronize(); custom_message_type = CustomMsg::Resuming; isPrintPaused = false; + Stopped = false; // resume processing USB commands again restore_print_from_ram_and_continue(default_retraction); pause_time += (_millis() - start_pause_print); //accumulate time when print is paused for correct statistics calculation refresh_cmd_timeout(); @@ -5899,14 +5903,16 @@ static void lcd_main_menu() } if(isPrintPaused) { + // only allow resuming if hardware errors (temperature or fan) are cleared + if(!get_temp_error() #ifdef FANCHECK - if((fan_check_error == EFCE_FIXED) || (fan_check_error == EFCE_OK)) + && ((fan_check_error == EFCE_FIXED) || (fan_check_error == EFCE_OK)) #endif //FANCHECK - { - if (usb_timer.running()) { - MENU_ITEM_SUBMENU_P(_T(MSG_RESUME_PRINT), lcd_resume_usb_print); - } else { + ) { + if (saved_printing) { MENU_ITEM_SUBMENU_P(_T(MSG_RESUME_PRINT), lcd_resume_print); + } else { + MENU_ITEM_SUBMENU_P(_T(MSG_RESUME_PRINT), lcd_resume_usb_print); } } } @@ -6332,6 +6338,15 @@ void print_stop() } st_synchronize(); + // did we come here from a thermal error? + if(get_temp_error()) { + // time to stop the error beep + WRITE(BEEPER, LOW); + } else { + // Turn off the print fan + fanSpeed = 0; + } + if (mmu_enabled) extr_unload(); //M702 C finishAndDisableSteppers(); //M84 axis_relative_modes = E_AXIS_MASK; //XYZ absolute, E relative From 9ef80226c90ed2201f05b90700e5b5f61b9eb041 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 10:24:09 +0200 Subject: [PATCH 71/95] Rename argument of ThermalStop for clarity --- Firmware/Marlin.h | 8 ++++---- Firmware/Marlin_main.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 0d555165f..79b78cc7a 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -241,10 +241,10 @@ void prepare_move(); void kill(const char *full_screen_message = NULL, unsigned char id = 0); void finishAndDisableSteppers(); -void UnconditionalStop(); // Stop heaters, motion and clear current print status -void ThermalStop(bool pause = false); // Emergency stop used by overtemp functions which allows - // recovery (with pause=true) -bool IsStopped(); // Returns true if the print has been stopped +void UnconditionalStop(); // Stop heaters, motion and clear current print status +void ThermalStop(bool allow_pause = false); // Emergency stop used by overtemp functions which allows + // recovery (with pause=true) +bool IsStopped(); // Returns true if the print has been stopped //put an ASCII command at the end of the current buffer, read from flash #define enquecommand_P(cmd) enquecommand(cmd, true) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index f83d3c799..6ddbb6575 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9983,11 +9983,11 @@ void UnconditionalStop() // will introduce either over/under extrusion on the current segment, and will not // survive a power panic. Switching Stop() to use the pause machinery instead (with // the addition of disabling the headers) could allow true recovery in the future. -void ThermalStop(bool pause) +void ThermalStop(bool allow_pause) { if(Stopped == false) { Stopped = true; - if(pause && (IS_SD_PRINTING || usb_timer.running())) { + if(allow_pause && (IS_SD_PRINTING || usb_timer.running())) { if (!isPrintPaused) { // we cannot make a distinction for the host here, the pause must be instantaneous lcd_pause_print(); From 8f02262bbd5139ffd5e1857fa0f6516031501321 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 10:52:30 +0200 Subject: [PATCH 72/95] Save/restore bed temperature during pause This will be needed during a thermal error pause that disables the bed as an additional safety precaution. --- Firmware/Marlin_main.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 6ddbb6575..267e04b25 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -381,6 +381,7 @@ static uint16_t saved_feedrate2 = 0; //!< Default feedrate (truncated from float static int saved_feedmultiply2 = 0; static uint8_t saved_active_extruder = 0; static float saved_extruder_temperature = 0.0; //!< Active extruder temperature +static float saved_bed_temperature = 0.0; //!< Bed temperature static bool saved_extruder_relative_mode = false; static int saved_fanSpeed = 0; //!< Print fan speed //! @} @@ -11573,6 +11574,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move) saved_feedmultiply2 = feedmultiply; //save feedmultiply saved_active_extruder = active_extruder; //save active_extruder saved_extruder_temperature = degTargetHotend(active_extruder); + saved_bed_temperature = degBed(); saved_extruder_relative_mode = axis_relative_modes & E_AXIS_MASK; saved_fanSpeed = fanSpeed; cmdqueue_reset(); //empty cmdqueue @@ -11637,9 +11639,12 @@ void restore_print_from_ram_and_continue(float e_move) if (fan_check_error == EFCE_FIXED) fan_check_error = EFCE_OK; //reenable serial stream processing if printing from usb #endif -// for (int axis = X_AXIS; axis <= E_AXIS; axis++) -// current_position[axis] = st_get_position_mm(axis); - active_extruder = saved_active_extruder; //restore active_extruder + // restore bed temperature (bed can be disabled during a thermal warning) + if (degBed() != saved_bed_temperature) + setTargetBed(saved_bed_temperature); + + // restore active_extruder + active_extruder = saved_active_extruder; fanSpeed = saved_fanSpeed; if (degTargetHotend(saved_active_extruder) != saved_extruder_temperature) { From 63dab63f2e15cda3e574372fe3c60340e4e06340 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 10:53:42 +0200 Subject: [PATCH 73/95] uvlo: Never wait for bed temperature when recovering If the user accepted to resume, do not wait for bed temperature when recovering. This only adds a pointless delay when recovering a short pause and doesn't really improve the recovery quality after a long pause. --- Firmware/Marlin_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 267e04b25..667f66328 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -11226,7 +11226,7 @@ void recover_print(uint8_t automatic) { // Set the target bed and nozzle temperatures and wait. sprintf_P(cmd, PSTR("M104 S%d"), target_temperature[active_extruder]); enquecommand(cmd); - sprintf_P(cmd, PSTR("M190 S%d"), target_temperature_bed); + sprintf_P(cmd, PSTR("M140 S%d"), target_temperature_bed); enquecommand(cmd); sprintf_P(cmd, PSTR("M109 S%d"), target_temperature[active_extruder]); enquecommand(cmd); From eccfcd7424169da8d73a95fea629f0d1e9be3996 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 11:04:02 +0200 Subject: [PATCH 74/95] Save/restore temperatures during a thermal pause Re-used saved_* vars (as normally used during the paused state) to backup the original values when a thermal error occurs. --- Firmware/Marlin.h | 3 +++ Firmware/Marlin_main.cpp | 12 ++++++++++-- Firmware/temperature.cpp | 6 ++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 79b78cc7a..08b3c21ba 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -338,6 +338,9 @@ extern uint8_t saved_printing_type; #define PRINTING_TYPE_USB 1 #define PRINTING_TYPE_NONE 2 +extern float saved_extruder_temperature; //!< Active extruder temperature +extern float saved_bed_temperature; //!< Bed temperature + //save/restore printing in case that mmu is not responding extern bool mmu_print_saved; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 667f66328..ee1424639 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -380,8 +380,8 @@ static float saved_pos[4] = { X_COORD_INVALID, 0, 0, 0 }; static uint16_t saved_feedrate2 = 0; //!< Default feedrate (truncated from float) static int saved_feedmultiply2 = 0; static uint8_t saved_active_extruder = 0; -static float saved_extruder_temperature = 0.0; //!< Active extruder temperature -static float saved_bed_temperature = 0.0; //!< Bed temperature +float saved_extruder_temperature = 0.0; //!< Active extruder temperature +float saved_bed_temperature = 0.0; //!< Bed temperature static bool saved_extruder_relative_mode = false; static int saved_fanSpeed = 0; //!< Print fan speed //! @} @@ -9991,7 +9991,15 @@ void ThermalStop(bool allow_pause) if(allow_pause && (IS_SD_PRINTING || usb_timer.running())) { if (!isPrintPaused) { // we cannot make a distinction for the host here, the pause must be instantaneous + // so we call the lcd_pause_print to save the print state internally. Thermal errors + // disable heaters and save the original temperatures to saved_*, which will get + // overwritten by stop_and_save_print_to_ram. For this corner-case, re-instate the + // original values after the pause handler is called. + float bed_temp = saved_bed_temperature; + float ext_temp = saved_extruder_temperature; lcd_pause_print(); + saved_bed_temperature = bed_temp; + saved_extruder_temperature = ext_temp; } } else { // We got a hard thermal error and/or there is no print going on. Just stop. diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4a7aeca1a..4d71937bc 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -496,6 +496,12 @@ volatile static union // - prevent the user to set temperatures until all errors are cleared void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) { + // save the original target temperatures for recovery before disabling heaters + if(!temp_error_state.error) { + saved_bed_temperature = target_temperature_bed; + saved_extruder_temperature = target_temperature[index]; + } + // keep disabling heaters and keep fans on as long as the condition is asserted disable_heater(); hotendFanSetFullSpeed(); From c98e133ae210d0480302e78568e4e2628f4bcb20 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 11:09:54 +0200 Subject: [PATCH 75/95] Never stop spinning the extruder fan on thermal failures --- Firmware/fancheck.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Firmware/fancheck.cpp b/Firmware/fancheck.cpp index adae2bdc9..202283806 100755 --- a/Firmware/fancheck.cpp +++ b/Firmware/fancheck.cpp @@ -211,6 +211,7 @@ void checkExtruderAutoFans() { fanState &= ~1; fanState |= current_temperature[0] > EXTRUDER_AUTO_FAN_TEMPERATURE; + fanState |= get_temp_error(); } setExtruderAutoFanState(fanState); #endif From 1eb7871bab7962751dd326d154b0452e61b74dee Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 11:20:33 +0200 Subject: [PATCH 76/95] Also save fan speeds during a thermal error pause Similarly to temperatures, the fan speed is overwritten and needs to be restored appropriately. --- Firmware/Marlin.h | 1 + Firmware/Marlin_main.cpp | 8 +++++--- Firmware/temperature.cpp | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Firmware/Marlin.h b/Firmware/Marlin.h index 08b3c21ba..6c4ff484f 100755 --- a/Firmware/Marlin.h +++ b/Firmware/Marlin.h @@ -340,6 +340,7 @@ extern uint8_t saved_printing_type; extern float saved_extruder_temperature; //!< Active extruder temperature extern float saved_bed_temperature; //!< Bed temperature +extern int saved_fan_speed; //!< Print fan speed //save/restore printing in case that mmu is not responding extern bool mmu_print_saved; diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index ee1424639..44051f358 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -383,7 +383,7 @@ static uint8_t saved_active_extruder = 0; float saved_extruder_temperature = 0.0; //!< Active extruder temperature float saved_bed_temperature = 0.0; //!< Bed temperature static bool saved_extruder_relative_mode = false; -static int saved_fanSpeed = 0; //!< Print fan speed +int saved_fan_speed = 0; //!< Print fan speed //! @} static int saved_feedmultiply_mm = 100; @@ -9997,9 +9997,11 @@ void ThermalStop(bool allow_pause) // original values after the pause handler is called. float bed_temp = saved_bed_temperature; float ext_temp = saved_extruder_temperature; + int fan_speed = saved_fan_speed; lcd_pause_print(); saved_bed_temperature = bed_temp; saved_extruder_temperature = ext_temp; + saved_fan_speed = fan_speed; } } else { // We got a hard thermal error and/or there is no print going on. Just stop. @@ -11584,7 +11586,7 @@ void stop_and_save_print_to_ram(float z_move, float e_move) saved_extruder_temperature = degTargetHotend(active_extruder); saved_bed_temperature = degBed(); saved_extruder_relative_mode = axis_relative_modes & E_AXIS_MASK; - saved_fanSpeed = fanSpeed; + saved_fan_speed = fanSpeed; cmdqueue_reset(); //empty cmdqueue card.sdprinting = false; // card.closefile(); @@ -11653,7 +11655,7 @@ void restore_print_from_ram_and_continue(float e_move) // restore active_extruder active_extruder = saved_active_extruder; - fanSpeed = saved_fanSpeed; + fanSpeed = saved_fan_speed; if (degTargetHotend(saved_active_extruder) != saved_extruder_temperature) { setTargetHotendSafe(saved_extruder_temperature, saved_active_extruder); diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 4d71937bc..85885eb0c 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -500,6 +500,7 @@ void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) if(!temp_error_state.error) { saved_bed_temperature = target_temperature_bed; saved_extruder_temperature = target_temperature[index]; + saved_fan_speed = fanSpeed; } // keep disabling heaters and keep fans on as long as the condition is asserted From 39ad53ab11f5671bcfcd3f96d835e171463595b2 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Thu, 7 Jul 2022 12:10:44 +0200 Subject: [PATCH 77/95] Simplify fan checks now that lcd pause works correctly --- Firmware/Marlin_main.cpp | 17 ----------------- Firmware/fancheck.cpp | 21 +++++++++++---------- Firmware/fancheck.h | 1 - Firmware/ultralcd.cpp | 33 ++++++++++++++++++++++++--------- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 44051f358..6b9f26a97 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -1863,13 +1863,6 @@ void loop() ; } -#ifdef FANCHECK - if (fan_check_error && isPrintPaused && !IS_SD_PRINTING) { - KEEPALIVE_STATE(PAUSED_FOR_USER); - host_keepalive(); //prevent timeouts since usb processing is disabled until print is resumed. This is for a crude way of pausing a print on all hosts. - } -#endif - #ifdef PRUSA_M28 if (prusa_sd_card_upload) { @@ -4197,16 +4190,6 @@ There are reasons why some G Codes aren't in numerical order. void process_commands() { -#ifdef FANCHECK - if(fan_check_error == EFCE_DETECTED) { - fan_check_error = EFCE_REPORTED; - if (usb_timer.running()) - lcd_pause_usb_print(); - else - lcd_pause_print(); - } -#endif - if (!buflen) return; //empty command #ifdef CMDBUFFER_DEBUG diff --git a/Firmware/fancheck.cpp b/Firmware/fancheck.cpp index 202283806..421216f37 100755 --- a/Firmware/fancheck.cpp +++ b/Firmware/fancheck.cpp @@ -82,21 +82,22 @@ static void fanSpeedErrorBeep(const char *serialMsg, const char *lcdMsg){ } void fanSpeedError(unsigned char _fan) { - if (get_message_level() != 0 && isPrintPaused) return; - //to ensure that target temp. is not set to zero in case that we are resuming print - if (card.sdprinting || usb_timer.running()) { - if (heating_status != HeatingStatus::NO_HEATING) { - lcd_print_stop(); - } - else { - fan_check_error = EFCE_DETECTED; //plans error for next processed command + if (fan_check_error == EFCE_REPORTED) return; + fan_check_error = EFCE_REPORTED; + + if (IS_SD_PRINTING || usb_timer.running()) { + // A print is ongoing, pause the print normally + if(!isPrintPaused) { + if (usb_timer.running()) + lcd_pause_usb_print(); + else + lcd_pause_print(); } } else { - // SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); //Why pause octoprint? usb_timer.running() would be true in that case, so there is no need for this. + // Nothing is going on, but still turn off heaters and report the error setTargetHotend0(0); heating_status = HeatingStatus::NO_HEATING; - fan_check_error = EFCE_REPORTED; } switch (_fan) { case 0: // extracting the same code from case 0 and case 1 into a function saves 72B diff --git a/Firmware/fancheck.h b/Firmware/fancheck.h index 841ffe78b..312143972 100755 --- a/Firmware/fancheck.h +++ b/Firmware/fancheck.h @@ -8,7 +8,6 @@ enum { EFCE_OK = 0, //!< normal operation, both fans are ok EFCE_FIXED, //!< previous fan error was fixed - EFCE_DETECTED, //!< fan error detected, but not reported yet EFCE_REPORTED //!< fan error detected and reported to LCD and serial }; extern volatile uint8_t fan_check_error; diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index e2e1a4680..8f8f0caac 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -5692,19 +5692,30 @@ static bool fan_error_selftest() return 0; } +bool resume_print_checks() { + // reset the lcd status so that a newer error will be shown + lcd_return_to_status(); + lcd_reset_alert_level(); + + // ensure thermal issues (temp or fan) are resolved before we allow to resume + if (get_temp_error() +#ifdef FANCHECK + || fan_error_selftest() +#endif + ) { + return false; // abort if error persists + } + + return true; +} + //! @brief Resume paused print, send host action "resumed" //! @todo It is not good to call restore_print_from_ram_and_continue() from function called by lcd_update(), //! as restore_print_from_ram_and_continue() calls lcd_update() internally. void lcd_resume_print() { - lcd_return_to_status(); - lcd_reset_alert_level(); - - // ensure thermal issues (temp or fan) are resolved before we allow to resume - if (get_temp_error() || fan_error_selftest()) { - if (usb_timer.running()) SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSED); - return; // abort if error persists - } + // reset lcd and ensure we can resume first + if (!resume_print_checks()) return; cmdqueue_serial_disabled = false; lcd_setstatuspgm(_T(MSG_FINISHING_MOVEMENTS)); @@ -5722,7 +5733,11 @@ void lcd_resume_print() //! @brief Resume paused USB/host print, send host action "resume" void lcd_resume_usb_print() { - SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_RESUME); //resume octoprint + // reset lcd and ensure we can resume first + if (!resume_print_checks()) return; + + // resume the usb host + SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_RESUME); } static void change_sheet() From 19df196e1f720581c896108472162c681f6e6a7a Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sat, 9 Jul 2022 15:56:44 +0200 Subject: [PATCH 78/95] Better differentiation between pause/paused resume/resumed states Use OCTOPRINT_ASK_* for the present form of actions. In these cases the host will perform the pausing manouvers for us. Use OCTOPRINT_* instead for the past tense forms when we are in charge. Also always emit the action, whether we are or not sd-printing. This is due to the new Stopped handling behaving correctly in either case. --- Firmware/messages.cpp | 4 ++-- Firmware/messages.h | 4 ++-- Firmware/ultralcd.cpp | 9 +++------ 3 files changed, 7 insertions(+), 10 deletions(-) diff --git a/Firmware/messages.cpp b/Firmware/messages.cpp index 2b5e21d61..817890a05 100644 --- a/Firmware/messages.cpp +++ b/Firmware/messages.cpp @@ -192,9 +192,9 @@ const char MSG_ENDSTOP_OPEN[] PROGMEM_N1 = "open"; //// const char MSG_POWERUP[] PROGMEM_N1 = "PowerUp"; //// const char MSG_ERR_STOPPED[] PROGMEM_N1 = "Printer stopped due to errors. Supervision required."; //// const char MSG_ENDSTOP_HIT[] PROGMEM_N1 = "TRIGGERED"; //// -const char MSG_OCTOPRINT_PAUSE[] PROGMEM_N1 = "// action:pause"; //// +const char MSG_OCTOPRINT_ASK_PAUSE[] PROGMEM_N1 = "// action:pause"; //// const char MSG_OCTOPRINT_PAUSED[] PROGMEM_N1 = "// action:paused"; //// -const char MSG_OCTOPRINT_RESUME[] PROGMEM_N1 = "// action:resume"; //// +const char MSG_OCTOPRINT_ASK_RESUME[] PROGMEM_N1 = "// action:resume"; //// const char MSG_OCTOPRINT_RESUMED[] PROGMEM_N1 = "// action:resumed"; //// const char MSG_OCTOPRINT_CANCEL[] PROGMEM_N1 = "// action:cancel"; //// const char MSG_FANCHECK_EXTRUDER[] PROGMEM_N1 = "Err: EXTR. FAN ERROR"; ////c=20 diff --git a/Firmware/messages.h b/Firmware/messages.h index 1cfbe6c37..c25a25ad7 100644 --- a/Firmware/messages.h +++ b/Firmware/messages.h @@ -197,9 +197,9 @@ extern const char MSG_ERR_STOPPED[]; extern const char MSG_ENDSTOP_HIT[]; extern const char MSG_EJECT_FILAMENT[]; extern const char MSG_CUT_FILAMENT[]; -extern const char MSG_OCTOPRINT_PAUSE[]; +extern const char MSG_OCTOPRINT_ASK_PAUSE[]; extern const char MSG_OCTOPRINT_PAUSED[]; -extern const char MSG_OCTOPRINT_RESUME[]; +extern const char MSG_OCTOPRINT_ASK_RESUME[]; extern const char MSG_OCTOPRINT_RESUMED[]; extern const char MSG_OCTOPRINT_CANCEL[]; extern const char MSG_FANCHECK_EXTRUDER[]; diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index 8f8f0caac..ce5adf3cc 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -1091,10 +1091,7 @@ void lcd_pause_print() { stop_and_save_print_to_ram(0.0, -default_retraction); - if (!card.sdprinting) { - SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_PAUSED); - } - + SERIAL_ECHOLNRPGM(MSG_OCTOPRINT_PAUSED); isPrintPaused = true; // return to status is required to continue processing in the main loop! @@ -1105,7 +1102,7 @@ void lcd_pause_print() //! @brief Send host action "pause" void lcd_pause_usb_print() { - SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_PAUSE); + SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_ASK_PAUSE); } static void lcd_move_menu_axis(); @@ -5737,7 +5734,7 @@ void lcd_resume_usb_print() if (!resume_print_checks()) return; // resume the usb host - SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_RESUME); + SERIAL_PROTOCOLLNRPGM(MSG_OCTOPRINT_ASK_RESUME); } static void change_sheet() From e77a5345a21d00cdf041653f4a71ad340663c016 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 11 Jul 2022 13:07:00 +0200 Subject: [PATCH 79/95] Do not show "THERMAL ANOMALY" with "M310 B0" This allows us to use "M310 B0 W0.01" as a way to report the current error continuosly on the serial without 1) more code and 2) without preventing regular usage. --- Firmware/temperature.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 85885eb0c..6d707eb24 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -2435,9 +2435,10 @@ void handle_warning() static bool first = true; if(warning_state.assert) { if (first) { - lcd_setalertstatuspgm(MSG_THERMAL_ANOMALY, LCD_STATUS_INFO); - if(warn_beep) WRITE(BEEPER, HIGH); - first = false; + if(warn_beep) { + lcd_setalertstatuspgm(MSG_THERMAL_ANOMALY, LCD_STATUS_INFO); + WRITE(BEEPER, HIGH); + } } else { if(warn_beep) TOGGLE(BEEPER); } From cb3fec5caca86e068ebc570b6e7a94b28a1f1128 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 11 Jul 2022 13:08:45 +0200 Subject: [PATCH 80/95] Improve M310 documentation --- Firmware/Marlin_main.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 6b9f26a97..0d55c184d 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -4149,6 +4149,7 @@ extern uint8_t st_backlash_y; //!@n M302 - Allow cold extrudes, or set the minimum extrude S. //!@n M303 - PID relay autotune S sets the target temperature. (default target temperature = 150C) //!@n M304 - Set bed PID parameters P I and D +//!@n M310 - Temperature model settings //!@n M400 - Finish all moves //!@n M401 - Lower z-probe if present //!@n M402 - Raise z-probe if present @@ -7762,13 +7763,13 @@ Sigma_Exit: #ifdef TEMP_MODEL /*! - ### M310 - Temperature model + ### M310 - Temperature model settings #### Usage - M310 - M310 [ I ] [ R ] - M310 [ P ] [ C ] [ S ] [ E ] [ W ] [ T ] - M310 [ A ] + M310 ; report values + M310 [ I ] [ R ] ; set resistance at specifiex index + M310 [ P ] [ C ] [ S ] [ B ] [ E ] [ W ] [ T ] ; other parameters + M310 [ A ] ; autotune #### Parameters - `I` - resistance index position @@ -7776,7 +7777,7 @@ Sigma_Exit: - `P` - power - `C` - capacitance - `S` - set 0=disable 1=enable (default) - - `B` - beep on warning threshold 0=disable 1=enable (default) + - `B` - beep and warn when reaching warning threshold 0=disable 1=enable (default) - `E` - error threshold (define min/max values in variants) - `W` - warning threshold (define min/max values in variants) - `T` - ambient temperature correction From 4ca0012077c4a2b870d78d6b384d97eeae53b169 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 18 Jul 2022 15:57:56 +0200 Subject: [PATCH 81/95] Prevent longpress if a serious error is set --- Firmware/ultralcd.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index ce5adf3cc..e23a67b26 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -8076,9 +8076,9 @@ uint8_t get_message_level() void menu_lcd_longpress_func(void) { backlight_wake(); - if (homing_flag || mesh_bed_leveling_flag || menu_menu == lcd_babystep_z || menu_menu == lcd_move_z) + if (homing_flag || mesh_bed_leveling_flag || menu_menu == lcd_babystep_z || menu_menu == lcd_move_z || menu_block_entering_on_serious_errors != SERIOUS_ERR_NONE) { - // disable longpress during re-entry, while homing or calibration + // disable longpress during re-entry, while homing, calibration or if a serious error lcd_quick_feedback(); return; } From 49a288e6cf77bc1699f767932b714122719bf660 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 18 Jul 2022 16:28:00 +0200 Subject: [PATCH 82/95] Restore the "MINTEMP * fixed" message Set the LCD messages with the correct priority --- Firmware/temperature.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 6d707eb24..bf8c2bb21 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1636,17 +1636,17 @@ public: // i.e. do not transfer to state 1 break; case States::TempAboveMintemp: // the temperature has risen above the hysteresis check - lcd_setalertstatuspgm(m2); + lcd_setalertstatuspgm(m2, LCD_STATUS_CRITICAL); substep(States::ShowMintemp); last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; break; case States::ShowPleaseRestart: // displaying "Please restart" - lcd_setalertstatuspgm(m1); + lcd_setalertstatuspgm(m1, LCD_STATUS_CRITICAL); substep(States::ShowMintemp); last_alert_sent_to_lcd = LCDALERT_PLEASERESTART; break; case States::ShowMintemp: // displaying "MINTEMP fixed" - lcd_setalertstatuspgm(m2); + lcd_setalertstatuspgm(m2, LCD_STATUS_CRITICAL); substep(States::ShowPleaseRestart); last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; break; @@ -1702,7 +1702,7 @@ void handle_temp_error() if(temp_error_state.assert) { menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); min_temp_error(temp_error_state.index); - } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_HEATER) ) { + } else { // no recovery, just force the user to restart the printer // which is a safer variant than just continuing printing // The automaton also checks for hysteresis - the temperature must have reached a few degrees above the MINTEMP, before @@ -1716,7 +1716,7 @@ void handle_temp_error() if(temp_error_state.assert) { menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); bed_min_temp_error(); - } else if( menu_is_serious_error(SERIOUS_ERR_MINTEMP_BED) ){ + } else { // no recovery, just force the user to restart the printer // which is a safer variant than just continuing printing alert_automaton_bed.step(current_temperature_bed, BED_MINTEMP + TEMP_HYSTERESIS); From 374b829fb604d5b8da0fdbf451a1a681820c25bc Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 18 Jul 2022 16:45:11 +0200 Subject: [PATCH 83/95] Generalize menu_block_entering_on_serious_errors for menu lockout Call this variable menu_block_mask instead. We don't need to know the exact reason of why we're locking the menu. We will be able to reuse this to prevent menu entry during more activities in a cleaner way than testing for each condition as it's currently done for both menu entry and longpress. --- Firmware/Marlin_main.cpp | 3 +++ Firmware/menu.cpp | 2 +- Firmware/menu.h | 25 +++++++++++-------------- Firmware/temperature.cpp | 3 +-- Firmware/ultralcd.cpp | 4 ++-- 5 files changed, 18 insertions(+), 19 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 0d55c184d..0c78258b9 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9990,6 +9990,9 @@ void ThermalStop(bool allow_pause) } else { // We got a hard thermal error and/or there is no print going on. Just stop. lcd_print_stop(); + + // Also prevent further menu entry + menu_set_block(MENU_BLOCK_THERMAL_ERROR); } // Report the status on the serial, switch to a busy state diff --git a/Firmware/menu.cpp b/Firmware/menu.cpp index 2d5c2c9d1..b4761304f 100755 --- a/Firmware/menu.cpp +++ b/Firmware/menu.cpp @@ -24,7 +24,7 @@ uint8_t menu_data[MENU_DATA_SIZE]; #endif uint8_t menu_depth = 0; -uint8_t menu_block_entering_on_serious_errors = SERIOUS_ERR_NONE; +uint8_t menu_block_mask = MENU_BLOCK_NONE; uint8_t menu_line = 0; uint8_t menu_item = 0; uint8_t menu_row = 0; diff --git a/Firmware/menu.h b/Firmware/menu.h index 54c8a2cbb..b725adbf7 100755 --- a/Firmware/menu.h +++ b/Firmware/menu.h @@ -29,26 +29,23 @@ extern uint8_t menu_data[MENU_DATA_SIZE]; extern uint8_t menu_depth; -//! definition of serious errors possibly blocking the main menu +//! definition of reasons blocking the main menu //! Use them as bit mask, so that the code may set various errors at the same time enum ESeriousErrors { - SERIOUS_ERR_NONE = 0, - SERIOUS_ERR_MINTEMP_HEATER = 0x01, - SERIOUS_ERR_MINTEMP_BED = 0x02 + MENU_BLOCK_NONE = 0, + MENU_BLOCK_THERMAL_ERROR = 0x01, }; // and possibly others in the future. -//! this is a flag for disabling entering the main menu. If this is set -//! to anything != 0, the only the main status screen will be shown on the -//! LCD and the user will be prevented from entering the menu. -//! Now used only to block doing anything with the printer when there is -//! the infamous MINTEMP error (SERIOUS_ERR_MINTEMP). -extern uint8_t menu_block_entering_on_serious_errors; +//! this is a flag for disabling entering the main menu and longpress. If this is set to anything != +//! 0, the only the main status screen will be shown on the LCD and the user will be prevented from +//! entering the menu. +extern uint8_t menu_block_mask; -//! a pair of macros for manipulating the serious errors +//! a pair of macros for manipulating menu entry //! a c++ class would have been better -#define menu_set_serious_error(x) menu_block_entering_on_serious_errors |= x; -#define menu_unset_serious_error(x) menu_block_entering_on_serious_errors &= ~x; -#define menu_is_serious_error(x) (menu_block_entering_on_serious_errors & x) != 0 +#define menu_set_block(x) menu_block_mask |= x; +#define menu_unset_block(x) menu_block_mask &= ~x; +#define menu_is_blocked(x) (menu_block_mask & x) != 0 extern uint8_t menu_line; extern uint8_t menu_item; diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index bf8c2bb21..b976acbc4 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1700,7 +1700,6 @@ void handle_temp_error() switch((TempErrorSource)temp_error_state.source) { case TempErrorSource::hotend: if(temp_error_state.assert) { - menu_set_serious_error(SERIOUS_ERR_MINTEMP_HEATER); min_temp_error(temp_error_state.index); } else { // no recovery, just force the user to restart the printer @@ -1714,7 +1713,6 @@ void handle_temp_error() break; case TempErrorSource::bed: if(temp_error_state.assert) { - menu_set_serious_error(SERIOUS_ERR_MINTEMP_BED); bed_min_temp_error(); } else { // no recovery, just force the user to restart the printer @@ -1772,6 +1770,7 @@ void handle_temp_error() } else { temp_error_state.v = 0; WRITE(BEEPER, LOW); + menu_unset_block(MENU_BLOCK_THERMAL_ERROR); SERIAL_ECHOLNPGM("TM: error cleared"); } break; diff --git a/Firmware/ultralcd.cpp b/Firmware/ultralcd.cpp index e23a67b26..f9c420554 100755 --- a/Firmware/ultralcd.cpp +++ b/Firmware/ultralcd.cpp @@ -863,7 +863,7 @@ void lcd_status_screen() // NOT static due to using ins } if (current_click - && ( menu_block_entering_on_serious_errors == SERIOUS_ERR_NONE ) // or a serious error blocks entering the menu + && ( menu_block_mask == MENU_BLOCK_NONE ) // or a serious error blocks entering the menu ) { menu_depth = 0; //redundant, as already done in lcd_return_to_status(), just to be sure @@ -8076,7 +8076,7 @@ uint8_t get_message_level() void menu_lcd_longpress_func(void) { backlight_wake(); - if (homing_flag || mesh_bed_leveling_flag || menu_menu == lcd_babystep_z || menu_menu == lcd_move_z || menu_block_entering_on_serious_errors != SERIOUS_ERR_NONE) + if (homing_flag || mesh_bed_leveling_flag || menu_menu == lcd_babystep_z || menu_menu == lcd_move_z || menu_block_mask != MENU_BLOCK_NONE) { // disable longpress during re-entry, while homing, calibration or if a serious error lcd_quick_feedback(); From 79161f829e6c1969ca49b572384249f0971d1738 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 18 Jul 2022 16:58:28 +0200 Subject: [PATCH 84/95] Remove last_alert_sent_to_lcd and simplify mintemp alert automaton Thanks to LCD message priorities this not needed anymore and it's just overhead. --- Firmware/temperature.cpp | 56 ++++++++-------------------------------- 1 file changed, 11 insertions(+), 45 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index b976acbc4..ae77de075 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1017,33 +1017,15 @@ static void temp_runaway_stop(bool isPreheat, bool isBed) } #endif -//! codes of alert messages for the LCD - it is shorter to compare an uin8_t -//! than raw const char * of the messages themselves. -//! Could be used for MAXTEMP situations too - after reaching MAXTEMP and turning off the heater automagically -//! the heater/bed may cool down and a similar alert message like "MAXTERM fixed..." may be displayed. -enum { LCDALERT_NONE = 0, LCDALERT_HEATERMINTEMP, LCDALERT_BEDMINTEMP, LCDALERT_MINTEMPFIXED, LCDALERT_PLEASERESTART }; - -//! remember the last alert message sent to the LCD -//! to prevent flicker and improve speed -static uint8_t last_alert_sent_to_lcd = LCDALERT_NONE; - - -//! update the current temperature error message -//! @param type short error abbreviation (PROGMEM) -static void temp_update_messagepgm(const char* PROGMEM type) -{ - char msg[LCD_WIDTH]; - strcpy_P(msg, PSTR("Err: ")); - strcat_P(msg, type); - lcd_setalertstatus(msg, LCD_STATUS_CRITICAL); -} - //! signal a temperature error on both the lcd and serial //! @param type short error abbreviation (PROGMEM) //! @param e optional extruder index for hotend errors static void temp_error_messagepgm(const char* PROGMEM type, uint8_t e = EXTRUDERS) { - temp_update_messagepgm(type); + char msg[LCD_WIDTH]; + strcpy_P(msg, PSTR("Err: ")); + strcat_P(msg, type); + lcd_setalertstatus(msg, LCD_STATUS_CRITICAL); SERIAL_ERROR_START; @@ -1072,12 +1054,7 @@ static void min_temp_error(uint8_t e) { static const char err[] PROGMEM = "MINTEMP"; if(IsStopped() == false) { temp_error_messagepgm(err, e); - last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; if (farm_mode) prusa_statistics(92); - } else if( last_alert_sent_to_lcd != LCDALERT_HEATERMINTEMP ){ // only update, if the lcd message is to be changed (i.e. not the same as last time) - // we are already stopped due to some error, only update the status message without flickering - temp_update_messagepgm(err); - last_alert_sent_to_lcd = LCDALERT_HEATERMINTEMP; } ThermalStop(); } @@ -1093,12 +1070,7 @@ static void bed_min_temp_error(void) { static const char err[] PROGMEM = "MINTEMP BED"; if(IsStopped() == false) { temp_error_messagepgm(err); - last_alert_sent_to_lcd = LCDALERT_BEDMINTEMP; - } else if( last_alert_sent_to_lcd != LCDALERT_BEDMINTEMP ){ // only update, if the lcd message is to be changed (i.e. not the same as last time) - // we are already stopped due to some error, only update the status message without flickering - temp_update_messagepgm(err); - last_alert_sent_to_lcd = LCDALERT_BEDMINTEMP; - } + } ThermalStop(); } @@ -1613,9 +1585,10 @@ private: States state = States::Init; uint8_t repeat = ALERT_AUTOMATON_SPEED_DIV; - void substep(States next_state){ + void substep(const char* next_msg, States next_state){ if( repeat == 0 ){ state = next_state; // advance to the next state + lcd_setalertstatuspgm(next_msg, LCD_STATUS_CRITICAL); repeat = ALERT_AUTOMATON_SPEED_DIV; // and prepare repeating for it too } else { --repeat; @@ -1630,25 +1603,18 @@ public: switch(state){ case States::Init: // initial state - check hysteresis if( current_temp > mintemp ){ + lcd_setalertstatuspgm(m2, LCD_STATUS_CRITICAL); state = States::TempAboveMintemp; } // otherwise keep the Err MINTEMP alert message on the display, // i.e. do not transfer to state 1 break; case States::TempAboveMintemp: // the temperature has risen above the hysteresis check - lcd_setalertstatuspgm(m2, LCD_STATUS_CRITICAL); - substep(States::ShowMintemp); - last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; + case States::ShowMintemp: // displaying "MINTEMP fixed" + substep(m1, States::ShowPleaseRestart); break; case States::ShowPleaseRestart: // displaying "Please restart" - lcd_setalertstatuspgm(m1, LCD_STATUS_CRITICAL); - substep(States::ShowMintemp); - last_alert_sent_to_lcd = LCDALERT_PLEASERESTART; - break; - case States::ShowMintemp: // displaying "MINTEMP fixed" - lcd_setalertstatuspgm(m2, LCD_STATUS_CRITICAL); - substep(States::ShowPleaseRestart); - last_alert_sent_to_lcd = LCDALERT_MINTEMPFIXED; + substep(m2, States::ShowMintemp); break; } } From 41abe1689cd56da72315a4ea78ab559be581e07c Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Mon, 18 Jul 2022 17:30:34 +0200 Subject: [PATCH 85/95] Block LCD during temperature model autocalibration --- Firmware/menu.h | 7 +++++-- Firmware/temperature.cpp | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Firmware/menu.h b/Firmware/menu.h index b725adbf7..31d364836 100755 --- a/Firmware/menu.h +++ b/Firmware/menu.h @@ -32,8 +32,11 @@ extern uint8_t menu_depth; //! definition of reasons blocking the main menu //! Use them as bit mask, so that the code may set various errors at the same time enum ESeriousErrors { - MENU_BLOCK_NONE = 0, - MENU_BLOCK_THERMAL_ERROR = 0x01, + MENU_BLOCK_NONE = 0, + MENU_BLOCK_THERMAL_ERROR = 0x01, +#ifdef TEMP_MODEL + MENU_BLOCK_TEMP_MODEL_AUTOTUNE = 0x02, +#endif }; // and possibly others in the future. //! this is a flag for disabling entering the main menu and longpress. If this is set to anything != diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index ae77de075..f04cb24fe 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -35,6 +35,7 @@ #include "sound.h" #include "fancheck.h" #include "messages.h" +#include "language.h" #include "SdFatUtil.h" @@ -2810,7 +2811,11 @@ void temp_model_autotune(int16_t temp) return; } + // lockout the printer during calibration KEEPALIVE_STATE(IN_PROCESS); + menu_set_block(MENU_BLOCK_TEMP_MODEL_AUTOTUNE); + lcd_setstatuspgm(_i("Temp. model autotune")); + lcd_return_to_status(); // disable the model checking during self-calibration bool was_enabled = temp_model::enabled; @@ -2824,13 +2829,17 @@ void temp_model_autotune(int16_t temp) if(err) { SERIAL_ECHOLNPGM("TM: autotune failed"); + lcd_setstatuspgm(_i("TM autotune failed")); if(temp_error_state.v) fanSpeedSoftPwm = 255; } else { + lcd_setstatuspgm(MSG_WELCOME); fanSpeedSoftPwm = 0; temp_model_set_enabled(was_enabled); temp_model_report_settings(); } + + menu_unset_block(MENU_BLOCK_TEMP_MODEL_AUTOTUNE); } #ifdef TEMP_MODEL_DEBUG From c5c2557c5e6c832049da347878a759c3fe5507ae Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Fri, 22 Jul 2022 18:45:45 +0200 Subject: [PATCH 86/95] Improve M310 documentation --- Firmware/Marlin_main.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 0c78258b9..946032bea 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7767,20 +7767,20 @@ Sigma_Exit: #### Usage M310 ; report values - M310 [ I ] [ R ] ; set resistance at specifiex index + M310 [ I ] [ R ] ; set resistance at index M310 [ P ] [ C ] [ S ] [ B ] [ E ] [ W ] [ T ] ; other parameters M310 [ A ] ; autotune #### Parameters - - `I` - resistance index position - - `R` - resistance value (requires `I`) - - `P` - power - - `C` - capacitance - - `S` - set 0=disable 1=enable (default) - - `B` - beep and warn when reaching warning threshold 0=disable 1=enable (default) - - `E` - error threshold (define min/max values in variants) - - `W` - warning threshold (define min/max values in variants) - - `T` - ambient temperature correction + - `I` - resistance index position (0-15) + - `R` - resistance value at index (K/W; requires `I`) + - `P` - power (W) + - `C` - capacitance (J/K) + - `S` - set 0=disable 1=enable + - `B` - beep and warn when reaching warning threshold 0=disable 1=enable (default: 1) + - `E` - error threshold (K/s; default in variant) + - `W` - warning threshold (K/s; default in variant) + - `T` - ambient temperature correction (K; default in variant) - `A` - autotune C+R values */ case 310: From e60bb935e574cb17a23c7b008a142b1c373061b5 Mon Sep 17 00:00:00 2001 From: 3d-gussner <3d.gussner@gmail.com> Date: Tue, 2 Aug 2022 07:38:53 +0200 Subject: [PATCH 87/95] Remove test.sh from travis build --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3609fb30c..2d88cecc8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ before_install: - sudo iptables -A OUTPUT -o lo -j ACCEPT - sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT script: - - bash -x test.sh - cp Firmware/variants/1_75mm_MK3S-EINSy10a-E3Dv6full.h Firmware/Configuration_prusa.h - bash -x build.sh || { echo "1_75mm_MK3S-EINSy10a-E3Dv6full variant failed" && false; } - rm Firmware/Configuration_prusa.h From 8cbe69e2850dd418f67fb83b61a3bc22e58759b8 Mon Sep 17 00:00:00 2001 From: 3d-gussner <3d.gussner@gmail.com> Date: Tue, 2 Aug 2022 17:56:53 +0200 Subject: [PATCH 88/95] Update RepRap documentation --- Firmware/Marlin_main.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index 946032bea..c133b6788 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -7763,13 +7763,16 @@ Sigma_Exit: #ifdef TEMP_MODEL /*! - ### M310 - Temperature model settings + ### M310 - Temperature model settings M310: Temperature model settings #### Usage M310 ; report values - M310 [ I ] [ R ] ; set resistance at index - M310 [ P ] [ C ] [ S ] [ B ] [ E ] [ W ] [ T ] ; other parameters M310 [ A ] ; autotune + M310 [ S ] ; set 0=disable 1=enable + M310 [ I ] [ R ] ; set resistance at index + M310 [ P | C ] ; set power, capacitance + M310 [ B | E | W ] ; set beeper, warning and error threshold + M310 [ T ] ; set ambient temperature correction #### Parameters - `I` - resistance index position (0-15) From 30dccb3252e4b5d772e2166743d882c277270ad3 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 21 Aug 2022 14:27:26 +0200 Subject: [PATCH 89/95] Switch to ATOMIC sections instead of cli/sei/CRITICAL_SECTION --- Firmware/temperature.cpp | 96 ++++++++++++++++++++-------------------- 1 file changed, 47 insertions(+), 49 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index f04cb24fe..8daed3174 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1157,18 +1157,18 @@ FORCE_INLINE static void applyBabysteps() { if(curTodo>0) { - CRITICAL_SECTION_START; - babystep(axis,/*fwd*/true); - babystepsTodo[axis]--; //less to do next time - CRITICAL_SECTION_END; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + babystep(axis,/*fwd*/true); + babystepsTodo[axis]--; //less to do next time + } } else if(curTodo<0) { - CRITICAL_SECTION_START; - babystep(axis,/*fwd*/false); - babystepsTodo[axis]++; //less to do next time - CRITICAL_SECTION_END; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { + babystep(axis,/*fwd*/false); + babystepsTodo[axis]++; //less to do next time + } } } } @@ -1537,9 +1537,9 @@ ISR(TIMER0_COMPB_vect) #endif //SYSTEM_TIMER_2 { DISABLE_SOFT_PWM_INTERRUPT(); - sei(); - soft_pwm_isr(); - cli(); + NONATOMIC_BLOCK(NONATOMIC_FORCEOFF) { + soft_pwm_isr(); + } ENABLE_SOFT_PWM_INTERRUPT(); } @@ -1825,32 +1825,32 @@ void temp_mgr_init() adc_start_cycle(); // initialize timer5 - CRITICAL_SECTION_START; + ATOMIC_BLOCK(ATOMIC_RESTORESTATE) { - // CTC - TCCR5B &= ~(1< -1 && EXTRUDERS > 0 - WRITE(HEATER_0_PIN,LOW); + WRITE(HEATER_0_PIN,LOW); #endif #if defined(HEATER_1_PIN) && HEATER_1_PIN > -1 && EXTRUDERS > 1 - WRITE(HEATER_1_PIN,LOW); + WRITE(HEATER_1_PIN,LOW); #endif #if defined(HEATER_2_PIN) && HEATER_2_PIN > -1 && EXTRUDERS > 2 - WRITE(HEATER_2_PIN,LOW); + WRITE(HEATER_2_PIN,LOW); #endif #if defined(HEATER_BED_PIN) && HEATER_BED_PIN > -1 - // TODO: this doesn't take immediate effect! - timer02_set_pwm0(0); - bedPWMDisabled = 0; + // TODO: this doesn't take immediate effect! + timer02_set_pwm0(0); + bedPWMDisabled = 0; #endif - - CRITICAL_SECTION_END; + } } static void check_min_temp_raw() From d8d085287ea16423fef70d6a3692b8a51a126de5 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 21 Aug 2022 14:48:00 +0200 Subject: [PATCH 90/95] Re-enable bed temperature automatically for transitory errors --- Firmware/temperature.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 8daed3174..a7fdbdd4e 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -1738,6 +1738,11 @@ void handle_temp_error() temp_error_state.v = 0; WRITE(BEEPER, LOW); menu_unset_block(MENU_BLOCK_THERMAL_ERROR); + + // hotend error was transitory and disappeared, re-enable bed + if (!target_temperature_bed) + target_temperature_bed = saved_bed_temperature; + SERIAL_ECHOLNPGM("TM: error cleared"); } break; From b672be90b2c74a9f85259a149418b8d6a1ca21ec Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 21 Aug 2022 14:53:42 +0200 Subject: [PATCH 91/95] Do not overwrite saved values if TM error occurs while paused --- Firmware/temperature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index a7fdbdd4e..53199cd61 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -498,7 +498,7 @@ volatile static union void set_temp_error(TempErrorSource source, uint8_t index, TempErrorType type) { // save the original target temperatures for recovery before disabling heaters - if(!temp_error_state.error) { + if(!temp_error_state.error && !saved_printing) { saved_bed_temperature = target_temperature_bed; saved_extruder_temperature = target_temperature[index]; saved_fan_speed = fanSpeed; From 7cd888cd0afab25d97923405725765a1a51d6d6f Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Sun, 21 Aug 2022 15:21:26 +0200 Subject: [PATCH 92/95] Update documentation of ThermalStop() --- Firmware/Marlin_main.cpp | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/Firmware/Marlin_main.cpp b/Firmware/Marlin_main.cpp index c133b6788..936628f7e 100644 --- a/Firmware/Marlin_main.cpp +++ b/Firmware/Marlin_main.cpp @@ -9960,17 +9960,12 @@ void UnconditionalStop() } // Emergency stop used by overtemp functions which allows recovery +// WARNING: This function is called *continuously* during a thermal failure. // -// This function is called *continuously* during a thermal failure. -// -// In addition to stopping the print, this prevents subsequent G[0-3] commands to be -// processed via USB (using "Stopped") until the print is resumed via M999 or -// manually started from scratch with the LCD. -// -// Note that the current instruction is completely discarded, so resuming from Stop() -// will introduce either over/under extrusion on the current segment, and will not -// survive a power panic. Switching Stop() to use the pause machinery instead (with -// the addition of disabling the headers) could allow true recovery in the future. +// This either pauses (for thermal model errors) or stops *without recovery* depending on +// "allow_pause". If pause is allowed, this forces a printer-initiated instantanenous pause (just +// like an LCD pause) that bypasses the host pausing functionality. In this state the printer is +// kept in busy state and *must* be recovered from the LCD. void ThermalStop(bool allow_pause) { if(Stopped == false) { From 35708a61feb4ef0e4902b83202b2d51c3ed71697 Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 23 Aug 2022 17:13:54 +0200 Subject: [PATCH 93/95] No longer disable temperature management in xyzcal We already disable the heaters upon entering, and the new temperature isr doesn't perform any direct movement until we return to the main loop. This allows us to remove direct control of the soft_pwm interrupt from the header, which is dangerous. --- Firmware/temperature.cpp | 8 ++++++++ Firmware/temperature.h | 14 -------------- Firmware/xyzcal.cpp | 30 ++++++++++++------------------ 3 files changed, 20 insertions(+), 32 deletions(-) diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 53199cd61..80356bd49 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -50,6 +50,14 @@ #error "ADC_OVRSAMPL oversampling must match OVERSAMPLENR" #endif +#ifdef SYSTEM_TIMER_2 +#define ENABLE_SOFT_PWM_INTERRUPT() TIMSK2 |= (1<-1)) - DISABLE_FANCHECK_INTERRUPT(); -#endif //(defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) + st_synchronize(); + + // disable incompatible interrupts DISABLE_STEPPER_DRIVER_INTERRUPT(); #ifdef WATCHDOG wdt_disable(); #endif //WATCHDOG + + // setup internal callbacks sm4_stop_cb = 0; sm4_update_pos_cb = xyzcal_update_pos; sm4_calc_delay_cb = xyzcal_calc_delay; @@ -156,20 +159,15 @@ void xyzcal_meassure_leave(void) { DBG(_n("xyzcal_meassure_leave\n")); planner_abort_hard(); - ENABLE_SOFT_PWM_INTERRUPT(); -#if (defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) - ENABLE_FANCHECK_INTERRUPT(); -#endif //(defined(FANCHECK) && defined(TACH_1) && (TACH_1 >-1)) - ENABLE_STEPPER_DRIVER_INTERRUPT(); + + // re-enable interrupts #ifdef WATCHDOG wdt_enable(WDTO_4S); #ifdef EMERGENCY_HANDLERS WDTCSR |= (1 << WDIE); #endif //EMERGENCY_HANDLERS #endif //WATCHDOG - sm4_stop_cb = 0; - sm4_update_pos_cb = 0; - sm4_calc_delay_cb = 0; + ENABLE_STEPPER_DRIVER_INTERRUPT(); } @@ -999,13 +997,9 @@ BedSkewOffsetDetectionResultType xyzcal_scan_and_process(){ return ret; } -BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy(void){ - BedSkewOffsetDetectionResultType ret = BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND; - - //@size=258 +BedSkewOffsetDetectionResultType xyzcal_find_bed_induction_sensor_point_xy(void) { // DBG(_n("xyzcal_find_bed_induction_sensor_point_xy x=%ld y=%ld z=%ld\n"), count_position[X_AXIS], count_position[Y_AXIS], count_position[Z_AXIS]); - st_synchronize(); - + BedSkewOffsetDetectionResultType ret = BED_SKEW_OFFSET_DETECTION_POINT_NOT_FOUND; xyzcal_meassure_enter(); if (xyzcal_searchZ()) ret = xyzcal_scan_and_process(); From 7907e14cbf390c35c30f311ed58635683d107a9e Mon Sep 17 00:00:00 2001 From: Yuri D'Elia Date: Tue, 23 Aug 2022 17:16:15 +0200 Subject: [PATCH 94/95] Resync planner position upon exiting xyzcal Split the planner sync code out of planner_abort_hard() so that we can independently resync the planner position from the counters. This is needed in xyzcal as we directly modify the stepper counters (bypassing both planner and stepper). Call this new function instead of planner_abort_hard() when leaving, so that motion can resume in the middle of the gcode_M45 instruction. --- Firmware/planner.cpp | 29 +++++++++++++++++++---------- Firmware/planner.h | 3 +++ Firmware/xyzcal.cpp | 4 +++- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/Firmware/planner.cpp b/Firmware/planner.cpp index 2b07c77bc..c3f5262cc 100644 --- a/Firmware/planner.cpp +++ b/Firmware/planner.cpp @@ -609,12 +609,8 @@ static inline void planner_update_queue_min_counter() extern volatile uint32_t step_events_completed; // The number of step events executed in the current block -void planner_abort_hard() +void planner_reset_position() { - // Abort the stepper routine and flush the planner queue. - DISABLE_STEPPER_DRIVER_INTERRUPT(); - - // Now the front-end (the Marlin_main.cpp with its current_position) is out of sync. // First update the planner's current position in the physical motor steps. position[X_AXIS] = st_get_position(X_AXIS); position[Y_AXIS] = st_get_position(Y_AXIS); @@ -626,6 +622,7 @@ void planner_abort_hard() current_position[Y_AXIS] = st_get_position_mm(Y_AXIS); current_position[Z_AXIS] = st_get_position_mm(Z_AXIS); current_position[E_AXIS] = st_get_position_mm(E_AXIS); + // Apply the mesh bed leveling correction to the Z axis. #ifdef MESH_BED_LEVELING if (mbl.active) { @@ -658,11 +655,6 @@ void planner_abort_hard() #endif } #endif - // Relay to planner wait routine, that the current line shall be canceled. - planner_aborted = true; - - // Clear the planner queue, reset and re-enable the stepper timer. - quickStop(); // Apply inverse world correction matrix. machine2world(current_position[X_AXIS], current_position[Y_AXIS]); @@ -670,10 +662,27 @@ void planner_abort_hard() #ifdef LIN_ADVANCE memcpy(position_float, current_position, sizeof(position_float)); #endif +} + +void planner_abort_hard() +{ + // Abort the stepper routine and flush the planner queue. + DISABLE_STEPPER_DRIVER_INTERRUPT(); + + // Now the front-end (the Marlin_main.cpp with its current_position) is out of sync. + planner_reset_position(); + + // Relay to planner wait routine that the current line shall be canceled. + planner_aborted = true; + + // Clear the planner queue, reset and re-enable the stepper timer. + quickStop(); + // Resets planner junction speeds. Assumes start from rest. previous_nominal_speed = 0.0; memset(previous_speed, 0, sizeof(previous_speed)); + // Reset position sync requests plan_reset_next_e_queue = false; plan_reset_next_e_sched = false; } diff --git a/Firmware/planner.h b/Firmware/planner.h index 75066a80b..8534288ea 100644 --- a/Firmware/planner.h +++ b/Firmware/planner.h @@ -255,6 +255,9 @@ FORCE_INLINE bool planner_queue_full() { return block_buffer_tail == next_block_index; } +// Reset machine position from stepper counters +extern void planner_reset_position(); + // Abort the stepper routine, clean up the block queue, // wait for the steppers to stop, // update planner's current position and the current_position of the front end. diff --git a/Firmware/xyzcal.cpp b/Firmware/xyzcal.cpp index b8acc9480..e8689d3b5 100644 --- a/Firmware/xyzcal.cpp +++ b/Firmware/xyzcal.cpp @@ -158,7 +158,9 @@ void xyzcal_meassure_enter(void) void xyzcal_meassure_leave(void) { DBG(_n("xyzcal_meassure_leave\n")); - planner_abort_hard(); + + // resync planner position from counters (changed by xyzcal_update_pos) + planner_reset_position(); // re-enable interrupts #ifdef WATCHDOG From f2f136e0147b49c179ef9d4619334b2a0b79dbc1 Mon Sep 17 00:00:00 2001 From: Alex Voinea Date: Wed, 24 Aug 2022 10:18:45 +0300 Subject: [PATCH 95/95] Use timer3 instead of timer5 on miniRambo --- Firmware/MarlinSerial.h | 5 ++-- Firmware/macros.h | 3 ++ Firmware/pins_Einsy_1_0.h | 3 ++ Firmware/pins_Rambo_1_0.h | 2 ++ Firmware/pins_Rambo_1_3.h | 2 ++ Firmware/temperature.cpp | 63 ++++++++++++++++++++++++++------------- 6 files changed, 55 insertions(+), 23 deletions(-) diff --git a/Firmware/MarlinSerial.h b/Firmware/MarlinSerial.h index 3bb0926b2..61da27603 100644 --- a/Firmware/MarlinSerial.h +++ b/Firmware/MarlinSerial.h @@ -37,11 +37,10 @@ // These are macros to build serial port register names for the selected SERIAL_PORT (C preprocessor // requires two levels of indirection to expand macro values properly) -#define SERIAL_REGNAME(registerbase,number,suffix) SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) #if SERIAL_PORT == 0 && (!defined(UBRR0H) || !defined(UDR0)) // use un-numbered registers if necessary -#define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##suffix +#define SERIAL_REGNAME(registerbase,number,suffix) _REGNAME_SHORT(registerbase, suffix) #else -#define SERIAL_REGNAME_INTERNAL(registerbase,number,suffix) registerbase##number##suffix +#define SERIAL_REGNAME(registerbase,number,suffix) _REGNAME(registerbase, number, suffix) #endif // Registers used by MarlinSerial class (these are expanded diff --git a/Firmware/macros.h b/Firmware/macros.h index 95a737e1e..aa282dd5e 100644 --- a/Firmware/macros.h +++ b/Firmware/macros.h @@ -11,6 +11,9 @@ #define CRITICAL_SECTION_END SREG = _sreg; #endif //CRITICAL_SECTION_START +#define _REGNAME(registerbase,number,suffix) registerbase##number##suffix +#define _REGNAME_SHORT(registerbase,suffix) registerbase##suffix + // Macros to make a string from a macro #define STRINGIFY_(M) #M #define STRINGIFY(M) STRINGIFY_(M) diff --git a/Firmware/pins_Einsy_1_0.h b/Firmware/pins_Einsy_1_0.h index 3f73bdd9f..d8c316b44 100755 --- a/Firmware/pins_Einsy_1_0.h +++ b/Firmware/pins_Einsy_1_0.h @@ -78,6 +78,9 @@ #define VOLT_IR_PIN 8 //A8 +#define TEMP_TIM 5 + + #define E0_TMC2130_CS 66 #define E0_TMC2130_DIAG 65 #define E0_STEP_PIN 34 diff --git a/Firmware/pins_Rambo_1_0.h b/Firmware/pins_Rambo_1_0.h index 5cd0a6704..1bccd5c57 100644 --- a/Firmware/pins_Rambo_1_0.h +++ b/Firmware/pins_Rambo_1_0.h @@ -57,6 +57,8 @@ #define TEMP_PINDA_PIN 1 //A1 +#define TEMP_TIM 3 + #define E0_STEP_PIN 34 #define E0_DIR_PIN 43 diff --git a/Firmware/pins_Rambo_1_3.h b/Firmware/pins_Rambo_1_3.h index f733c105a..c5f0e4c1c 100644 --- a/Firmware/pins_Rambo_1_3.h +++ b/Firmware/pins_Rambo_1_3.h @@ -60,6 +60,8 @@ #define TEMP_PINDA_PIN 1 //A1 +#define TEMP_TIM 3 + #define E0_STEP_PIN 34 #define E0_DIR_PIN 43 diff --git a/Firmware/temperature.cpp b/Firmware/temperature.cpp index 80356bd49..a96eb0f4b 100755 --- a/Firmware/temperature.cpp +++ b/Firmware/temperature.cpp @@ -60,13 +60,36 @@ // temperature manager timer configuration #define TEMP_MGR_INTV 0.27 // seconds, ~3.7Hz -#define TIMER5_PRESCALE 256 -#define TIMER5_OCRA_OVF (uint16_t)(TEMP_MGR_INTV / ((long double)TIMER5_PRESCALE / F_CPU)) -#define TEMP_MGR_INT_FLAG_STATE() (TIFR5 & (1<