diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h index 402bc19b00..081210825a 100644 --- a/Marlin/Configuration_adv.h +++ b/Marlin/Configuration_adv.h @@ -603,6 +603,40 @@ #define COOLER_AUTO_FAN_TEMPERATURE 18 #define COOLER_AUTO_FAN_SPEED 255 +/** + * Hotend Cooling Fans tachometers + * + * Define one or more tachometer pins to enable fan speed + * monitoring, and reporting of fan speeds with M123. + * + * NOTE: Only works with fans up to 7000 RPM. + */ +//#define FOURWIRES_FANS // Needed with AUTO_FAN when 4-wire PWM fans are installed +//#define E0_FAN_TACHO_PIN -1 +//#define E0_FAN_TACHO_PULLUP +//#define E0_FAN_TACHO_PULLDOWN +//#define E1_FAN_TACHO_PIN -1 +//#define E1_FAN_TACHO_PULLUP +//#define E1_FAN_TACHO_PULLDOWN +//#define E2_FAN_TACHO_PIN -1 +//#define E2_FAN_TACHO_PULLUP +//#define E2_FAN_TACHO_PULLDOWN +//#define E3_FAN_TACHO_PIN -1 +//#define E3_FAN_TACHO_PULLUP +//#define E3_FAN_TACHO_PULLDOWN +//#define E4_FAN_TACHO_PIN -1 +//#define E4_FAN_TACHO_PULLUP +//#define E4_FAN_TACHO_PULLDOWN +//#define E5_FAN_TACHO_PIN -1 +//#define E5_FAN_TACHO_PULLUP +//#define E5_FAN_TACHO_PULLDOWN +//#define E6_FAN_TACHO_PIN -1 +//#define E6_FAN_TACHO_PULLUP +//#define E6_FAN_TACHO_PULLDOWN +//#define E7_FAN_TACHO_PIN -1 +//#define E7_FAN_TACHO_PULLUP +//#define E7_FAN_TACHO_PULLDOWN + /** * Part-Cooling Fan Multiplexer * @@ -3607,6 +3641,12 @@ */ //#define CNC_COORDINATE_SYSTEMS +/** + * Auto-report fan speed with M123 S + * Requires fans with tachometer pins + */ +//#define AUTO_REPORT_FANS + /** * Auto-report temperatures with M155 S */ diff --git a/Marlin/src/MarlinCore.cpp b/Marlin/src/MarlinCore.cpp index 503c77575b..ea486abfcd 100644 --- a/Marlin/src/MarlinCore.cpp +++ b/Marlin/src/MarlinCore.cpp @@ -212,6 +212,10 @@ #include "module/tool_change.h" +#if HAS_FANCHECK + #include "feature/fancheck.h" +#endif + #if ENABLED(USE_CONTROLLER_FAN) #include "feature/controllerfan.h" #endif @@ -832,6 +836,7 @@ void idle(bool no_stepper_sleep/*=false*/) { #if HAS_AUTO_REPORTING if (!gcode.autoreport_paused) { TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick()); + TERN_(AUTO_REPORT_FANS, fan_check.auto_reporter.tick()); TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick()); TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick()); TERN_(BUFFER_MONITORING, queue.auto_report_buffer_statistics()); @@ -1278,6 +1283,8 @@ void setup() { SETUP_RUN(controllerFan.setup()); #endif + TERN_(HAS_FANCHECK, fan_check.init()); + // UI must be initialized before EEPROM // (because EEPROM code calls the UI). diff --git a/Marlin/src/core/language.h b/Marlin/src/core/language.h index 03bffb8bd9..0a6870af26 100644 --- a/Marlin/src/core/language.h +++ b/Marlin/src/core/language.h @@ -140,6 +140,7 @@ #define STR_RESEND "Resend: " #define STR_UNKNOWN_COMMAND "Unknown command: \"" #define STR_ACTIVE_EXTRUDER "Active Extruder: " +#define STR_ERR_FANSPEED "Fan speed E" #define STR_PROBE_OFFSET "Probe Offset" #define STR_SKEW_MIN "min_skew_factor: " diff --git a/Marlin/src/feature/fancheck.cpp b/Marlin/src/feature/fancheck.cpp new file mode 100644 index 0000000000..1b47fadecc --- /dev/null +++ b/Marlin/src/feature/fancheck.cpp @@ -0,0 +1,207 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * fancheck.cpp - fan tachometer check + */ + +#include "../inc/MarlinConfig.h" + +#if HAS_FANCHECK + +#include "fancheck.h" +#include "../module/temperature.h" + +#if HAS_AUTO_FAN && EXTRUDER_AUTO_FAN_SPEED != 255 && DISABLED(FOURWIRES_FANS) + bool FanCheck::measuring = false; +#endif +bool FanCheck::tacho_state[TACHO_COUNT]; +uint16_t FanCheck::edge_counter[TACHO_COUNT]; +uint8_t FanCheck::rps[TACHO_COUNT]; +FanCheck::TachoError FanCheck::error = FanCheck::TachoError::NONE; +bool FanCheck::enabled; + +void FanCheck::init() { + #define _TACHINIT(N) TERN(E##N##_FAN_TACHO_PULLUP, SET_INPUT_PULLUP, TERN(E##N##_FAN_TACHO_PULLDOWN, SET_INPUT_PULLDOWN, SET_INPUT))(E##N##_FAN_TACHO_PIN) + #if HAS_E0_FAN_TACHO + _TACHINIT(0); + #endif + #if HAS_E1_FAN_TACHO + _TACHINIT(1); + #endif + #if HAS_E2_FAN_TACHO + _TACHINIT(2); + #endif + #if HAS_E3_FAN_TACHO + _TACHINIT(3); + #endif + #if HAS_E4_FAN_TACHO + _TACHINIT(4); + #endif + #if HAS_E5_FAN_TACHO + _TACHINIT(5); + #endif + #if HAS_E6_FAN_TACHO + _TACHINIT(6); + #endif + #if HAS_E7_FAN_TACHO + _TACHINIT(7); + #endif +} + +void FanCheck::update_tachometers() { + bool status; + + #define _TACHO_CASE(N) case N: status = READ(E##N##_FAN_TACHO_PIN); break; + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + #if HAS_E0_FAN_TACHO + _TACHO_CASE(0) + #endif + #if HAS_E1_FAN_TACHO + _TACHO_CASE(1) + #endif + #if HAS_E2_FAN_TACHO + _TACHO_CASE(2) + #endif + #if HAS_E3_FAN_TACHO + _TACHO_CASE(3) + #endif + #if HAS_E4_FAN_TACHO + _TACHO_CASE(4) + #endif + #if HAS_E5_FAN_TACHO + _TACHO_CASE(5) + #endif + #if HAS_E6_FAN_TACHO + _TACHO_CASE(6) + #endif + #if HAS_E7_FAN_TACHO + _TACHO_CASE(7) + #endif + default: continue; + } + + if (status != tacho_state[f]) { + if (measuring) ++edge_counter[f]; + tacho_state[f] = status; + } + } +} + +void FanCheck::compute_speed(uint16_t elapsedTime) { + static uint8_t errors_count[TACHO_COUNT]; + static uint8_t fan_reported_errors_msk = 0; + + uint8_t fan_error_msk = 0; + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + TERN_(HAS_E0_FAN_TACHO, case 0:) + TERN_(HAS_E1_FAN_TACHO, case 1:) + TERN_(HAS_E2_FAN_TACHO, case 2:) + TERN_(HAS_E3_FAN_TACHO, case 3:) + TERN_(HAS_E4_FAN_TACHO, case 4:) + TERN_(HAS_E5_FAN_TACHO, case 5:) + TERN_(HAS_E6_FAN_TACHO, case 6:) + TERN_(HAS_E7_FAN_TACHO, case 7:) + // Compute fan speed + rps[f] = edge_counter[f] * float(250) / elapsedTime; + edge_counter[f] = 0; + + // Check fan speed + constexpr int8_t max_extruder_fan_errors = TERN(HAS_PWMFANCHECK, 10000, 5000) / Temperature::fan_update_interval_ms; + + if (rps[f] >= 20 || TERN0(HAS_AUTO_FAN, thermalManager.autofan_speed[f] == 0)) + errors_count[f] = 0; + else if (errors_count[f] < max_extruder_fan_errors) + ++errors_count[f]; + else if (enabled) + SBI(fan_error_msk, f); + break; + } + } + + // Drop the error when all fans are ok + if (!fan_error_msk && error == TachoError::REPORTED) error = TachoError::FIXED; + + if (error == TachoError::FIXED && !printJobOngoing() && !printingIsPaused()) { + error = TachoError::NONE; // if the issue has been fixed while the printer is idle, reenable immediately + ui.reset_alert_level(); + } + + if (fan_error_msk & ~fan_reported_errors_msk) { + // Handle new faults only + LOOP_L_N(f, TACHO_COUNT) if (TEST(fan_error_msk, f)) report_speed_error(f); + } + fan_reported_errors_msk = fan_error_msk; +} + +void FanCheck::report_speed_error(uint8_t fan) { + if (printJobOngoing()) { + if (error == TachoError::NONE) { + if (thermalManager.degTargetHotend(fan) != 0) { + kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT)); + error = TachoError::REPORTED; + } + else + error = TachoError::DETECTED; // Plans error for next processed command + } + } + else if (!printingIsPaused()) { + thermalManager.setTargetHotend(0, fan); // Always disable heating + if (error == TachoError::NONE) error = TachoError::REPORTED; + } + + SERIAL_ERROR_MSG(STR_ERR_FANSPEED, fan); + LCD_ALERTMESSAGE(MSG_FAN_SPEED_FAULT); +} + +void FanCheck::print_fan_states() { + LOOP_L_N(s, 2) { + LOOP_L_N(f, TACHO_COUNT) { + switch (f) { + TERN_(HAS_E0_FAN_TACHO, case 0:) + TERN_(HAS_E1_FAN_TACHO, case 1:) + TERN_(HAS_E2_FAN_TACHO, case 2:) + TERN_(HAS_E3_FAN_TACHO, case 3:) + TERN_(HAS_E4_FAN_TACHO, case 4:) + TERN_(HAS_E5_FAN_TACHO, case 5:) + TERN_(HAS_E6_FAN_TACHO, case 6:) + TERN_(HAS_E7_FAN_TACHO, case 7:) + SERIAL_ECHOPGM("E", f); + if (s == 0) + SERIAL_ECHOPGM(":", 60 * rps[f], " RPM "); + else + SERIAL_ECHOPGM("@:", TERN(HAS_AUTO_FAN, thermalManager.autofan_speed[f], 255), " "); + break; + } + } + } + SERIAL_EOL(); +} + +#if ENABLED(AUTO_REPORT_FANS) + AutoReporter FanCheck::auto_reporter; + void FanCheck::AutoReportFan::report() { print_fan_states(); } +#endif + +#endif // HAS_FANCHECK diff --git a/Marlin/src/feature/fancheck.h b/Marlin/src/feature/fancheck.h new file mode 100644 index 0000000000..6e8038b498 --- /dev/null +++ b/Marlin/src/feature/fancheck.h @@ -0,0 +1,89 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ +#pragma once + +#include "../inc/MarlinConfig.h" + +#if HAS_FANCHECK + +#include "../MarlinCore.h" +#include "../lcd/marlinui.h" + +#if ENABLED(AUTO_REPORT_FANS) + #include "../libs/autoreport.h" +#endif + +#if ENABLED(PARK_HEAD_ON_PAUSE) + #include "../gcode/queue.h" +#endif + +/** + * fancheck.h + */ +#define TACHO_COUNT TERN(HAS_E7_FAN_TACHO, 8, TERN(HAS_E6_FAN_TACHO, 7, TERN(HAS_E5_FAN_TACHO, 6, TERN(HAS_E4_FAN_TACHO, 5, TERN(HAS_E3_FAN_TACHO, 4, TERN(HAS_E2_FAN_TACHO, 3, TERN(HAS_E1_FAN_TACHO, 2, 1))))))) + +class FanCheck { + private: + + enum class TachoError : uint8_t { NONE, DETECTED, REPORTED, FIXED }; + + #if HAS_PWMFANCHECK + static bool measuring; // For future use (3 wires PWM controlled fans) + #else + static constexpr bool measuring = true; + #endif + static bool tacho_state[TACHO_COUNT]; + static uint16_t edge_counter[TACHO_COUNT]; + static uint8_t rps[TACHO_COUNT]; + static TachoError error; + + static inline void report_speed_error(uint8_t fan); + + public: + + static bool enabled; + + static void init(); + static void update_tachometers(); + static void compute_speed(uint16_t elapsedTime); + static void print_fan_states(); + #if HAS_PWMFANCHECK + static inline void toggle_measuring() { measuring = !measuring; } + static inline bool is_measuring() { return measuring; } + #endif + + static inline void check_deferred_error() { + if (error == TachoError::DETECTED) { + error = TachoError::REPORTED; + TERN(PARK_HEAD_ON_PAUSE, queue.inject(F("M125")), kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT))); + } + } + + #if ENABLED(AUTO_REPORT_FANS) + struct AutoReportFan { static void report(); }; + static AutoReporter auto_reporter; + #endif +}; + +extern FanCheck fan_check; + +#endif // HAS_FANCHECK diff --git a/Marlin/src/gcode/control/M999.cpp b/Marlin/src/gcode/control/M999.cpp index b7219673a3..b7d6db9f23 100644 --- a/Marlin/src/gcode/control/M999.cpp +++ b/Marlin/src/gcode/control/M999.cpp @@ -22,7 +22,7 @@ #include "../gcode.h" -#include "../../lcd/marlinui.h" // for lcd_reset_alert_level +#include "../../lcd/marlinui.h" // for ui.reset_alert_level #include "../../MarlinCore.h" // for marlin_state #include "../queue.h" // for flush_and_request_resend diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp index 12e8f6c581..c05ac5494d 100644 --- a/Marlin/src/gcode/gcode.cpp +++ b/Marlin/src/gcode/gcode.cpp @@ -65,6 +65,10 @@ GcodeSuite gcode; #include "../feature/password/password.h" #endif +#if HAS_FANCHECK + #include "../feature/fancheck.h" +#endif + #include "../MarlinCore.h" // for idle, kill // Inactivity shutdown @@ -296,6 +300,8 @@ void GcodeSuite::dwell(millis_t time) { * Process the parsed command and dispatch it to its handler */ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { + TERN_(HAS_FANCHECK, fan_check.check_deferred_error()); + KEEPALIVE_STATE(IN_HANDLER); /** @@ -577,6 +583,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) { case 113: M113(); break; // M113: Set Host Keepalive interval #endif + #if HAS_FANCHECK + case 123: M123(); break; // M123: Report fan states or set fans auto-report interval + #endif + #if HAS_HEATED_BED case 140: M140(); break; // M140: Set bed temperature case 190: M190(); break; // M190: Wait for bed temperature to reach target diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h index a1e177573d..bbfb31a3fe 100644 --- a/Marlin/src/gcode/gcode.h +++ b/Marlin/src/gcode/gcode.h @@ -156,6 +156,7 @@ * M121 - Disable endstops detection. * * M122 - Debug stepper (Requires at least one _DRIVER_TYPE defined as TMC2130/2160/5130/5160/2208/2209/2660 or L6470) + * M123 - Report fan tachometers. (Requires En_FAN_TACHO_PIN) Optionally set auto-report interval. (Requires AUTO_REPORT_FANS) * M125 - Save current position and move to filament change position. (Requires PARK_HEAD_ON_PAUSE) * * M126 - Solenoid Air Valve Open. (Requires BARICUDA) @@ -737,6 +738,10 @@ private: static void M120(); static void M121(); + #if HAS_FANCHECK + static void M123(); + #endif + #if ENABLED(PARK_HEAD_ON_PAUSE) static void M125(); #endif diff --git a/Marlin/src/gcode/temp/M123.cpp b/Marlin/src/gcode/temp/M123.cpp new file mode 100644 index 0000000000..389656ef34 --- /dev/null +++ b/Marlin/src/gcode/temp/M123.cpp @@ -0,0 +1,48 @@ +/** + * Marlin 3D Printer Firmware + * Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin] + * + * Based on Sprinter and grbl. + * Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include "../../inc/MarlinConfig.h" + +#if HAS_FANCHECK + +#include "../gcode.h" +#include "../../feature/fancheck.h" + +/** + * M123: Report fan states -or- set interval for auto-report + * + * S : Set auto-report interval + */ +void GcodeSuite::M123() { + + #if ENABLED(AUTO_REPORT_FANS) + if (parser.seenval('S')) { + fan_check.auto_reporter.set_interval(parser.value_byte()); + return; + } + #endif + + fan_check.print_fan_states(); + +} + +#endif // HAS_FANCHECK diff --git a/Marlin/src/inc/Conditionals_post.h b/Marlin/src/inc/Conditionals_post.h index 9a9dd96b4e..84bb1d1330 100644 --- a/Marlin/src/inc/Conditionals_post.h +++ b/Marlin/src/inc/Conditionals_post.h @@ -2510,10 +2510,42 @@ #define AUTO_CHAMBER_IS_E 1 #endif +// Fans check +#if HAS_HOTEND && PIN_EXISTS(E0_FAN_TACHO) + #define HAS_E0_FAN_TACHO 1 +#endif +#if HOTENDS > 1 && PIN_EXISTS(E1_FAN_TACHO) + #define HAS_E1_FAN_TACHO 1 +#endif +#if HOTENDS > 2 && PIN_EXISTS(E2_FAN_TACHO) + #define HAS_E2_FAN_TACHO 1 +#endif +#if HOTENDS > 3 && PIN_EXISTS(E3_FAN_TACHO) + #define HAS_E3_FAN_TACHO 1 +#endif +#if HOTENDS > 4 && PIN_EXISTS(E4_FAN_TACHO) + #define HAS_E4_FAN_TACHO 1 +#endif +#if HOTENDS > 5 && PIN_EXISTS(E5_FAN_TACHO) + #define HAS_E5_FAN_TACHO 1 +#endif +#if HOTENDS > 6 && PIN_EXISTS(E6_FAN_TACHO) + #define HAS_E6_FAN_TACHO 1 +#endif +#if HOTENDS > 7 && PIN_EXISTS(E7_FAN_TACHO) + #define HAS_E7_FAN_TACHO 1 +#endif +#if ANY(HAS_E0_FAN_TACHO, HAS_E1_FAN_TACHO, HAS_E2_FAN_TACHO, HAS_E3_FAN_TACHO, HAS_E4_FAN_TACHO, HAS_E5_FAN_TACHO, HAS_E6_FAN_TACHO, HAS_E7_FAN_TACHO) + #define HAS_FANCHECK 1 + #if HAS_AUTO_FAN && EXTRUDER_AUTO_FAN_SPEED != 255 && DISABLED(FOURWIRES_FANS) + #define HAS_PWMFANCHECK 1 + #endif +#endif + #if !HAS_TEMP_SENSOR #undef AUTO_REPORT_TEMPERATURES #endif -#if ANY(AUTO_REPORT_TEMPERATURES, AUTO_REPORT_SD_STATUS, AUTO_REPORT_POSITION) +#if ANY(AUTO_REPORT_TEMPERATURES, AUTO_REPORT_SD_STATUS, AUTO_REPORT_POSITION, AUTO_REPORT_FANS) #define HAS_AUTO_REPORTING 1 #endif diff --git a/Marlin/src/inc/SanityCheck.h b/Marlin/src/inc/SanityCheck.h index c6b89ce89f..23d18a3f8e 100644 --- a/Marlin/src/inc/SanityCheck.h +++ b/Marlin/src/inc/SanityCheck.h @@ -2585,6 +2585,31 @@ static_assert(Y_MAX_LENGTH >= Y_BED_SIZE, "Movement bounds (Y_MIN_POS, Y_MAX_POS #endif #endif +/** + * Fan check + */ +#if HAS_FANCHECK + #if BOTH(E0_FAN_TACHO_PULLUP, E0_FAN_TACHO_PULLDOWN) + #error "Enable only one of E0_FAN_TACHO_PULLUP or E0_FAN_TACHO_PULLDOWN." + #elif BOTH(E1_FAN_TACHO_PULLUP, E1_FAN_TACHO_PULLDOWN) + #error "Enable only one of E1_FAN_TACHO_PULLUP or E1_FAN_TACHO_PULLDOWN." + #elif BOTH(E2_FAN_TACHO_PULLUP, E2_FAN_TACHO_PULLDOWN) + #error "Enable only one of E2_FAN_TACHO_PULLUP or E2_FAN_TACHO_PULLDOWN." + #elif BOTH(E3_FAN_TACHO_PULLUP, E3_FAN_TACHO_PULLDOWN) + #error "Enable only one of E3_FAN_TACHO_PULLUP or E3_FAN_TACHO_PULLDOWN." + #elif BOTH(E4_FAN_TACHO_PULLUP, E4_FAN_TACHO_PULLDOWN) + #error "Enable only one of E4_FAN_TACHO_PULLUP or E4_FAN_TACHO_PULLDOWN." + #elif BOTH(E5_FAN_TACHO_PULLUP, E5_FAN_TACHO_PULLDOWN) + #error "Enable only one of E5_FAN_TACHO_PULLUP or E5_FAN_TACHO_PULLDOWN." + #elif BOTH(E6_FAN_TACHO_PULLUP, E6_FAN_TACHO_PULLDOWN) + #error "Enable only one of E6_FAN_TACHO_PULLUP or E6_FAN_TACHO_PULLDOWN." + #elif BOTH(E7_FAN_TACHO_PULLUP, E7_FAN_TACHO_PULLDOWN) + #error "Enable only one of E7_FAN_TACHO_PULLUP or E7_FAN_TACHO_PULLDOWN." + #endif +#elif ENABLED(AUTO_REPORT_FANS) + #error "AUTO_REPORT_FANS requires one or more fans with a tachometer pin." +#endif + /** * Make sure only one EEPROM type is enabled */ diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h index 36dbd13865..279cf6f969 100644 --- a/Marlin/src/lcd/language/language_en.h +++ b/Marlin/src/lcd/language/language_en.h @@ -570,6 +570,7 @@ namespace Language_en { LSTR MSG_INFO_RUNAWAY_OFF = _UxGT("Runaway Watch: OFF"); LSTR MSG_INFO_RUNAWAY_ON = _UxGT("Runaway Watch: ON"); LSTR MSG_HOTEND_IDLE_TIMEOUT = _UxGT("Hotend Idle Timeout"); + LSTR MSG_FAN_SPEED_FAULT = _UxGT("Fan speed fault"); LSTR MSG_CASE_LIGHT = _UxGT("Case Light"); LSTR MSG_CASE_LIGHT_BRIGHTNESS = _UxGT("Light Brightness"); @@ -613,6 +614,7 @@ namespace Language_en { LSTR MSG_RUNOUT_SENSOR = _UxGT("Runout Sensor"); LSTR MSG_RUNOUT_DISTANCE_MM = _UxGT("Runout Dist mm"); LSTR MSG_RUNOUT_ENABLE = _UxGT("Enable Runout"); + LSTR MSG_FANCHECK = _UxGT("Fan Tacho Check"); LSTR MSG_KILL_HOMING_FAILED = _UxGT("Homing Failed"); LSTR MSG_LCD_PROBING_FAILED = _UxGT("Probing Failed"); diff --git a/Marlin/src/lcd/language/language_it.h b/Marlin/src/lcd/language/language_it.h index 15b83464d7..780f4e9743 100644 --- a/Marlin/src/lcd/language/language_it.h +++ b/Marlin/src/lcd/language/language_it.h @@ -556,6 +556,7 @@ namespace Language_it { LSTR MSG_INFO_RUNAWAY_OFF = _UxGT("Controllo fuga: OFF"); LSTR MSG_INFO_RUNAWAY_ON = _UxGT("Controllo fuga: ON"); LSTR MSG_HOTEND_IDLE_TIMEOUT = _UxGT("Timeout inatt.ugello"); + LSTR MSG_FAN_SPEED_FAULT = _UxGT("Err.vel.della ventola"); LSTR MSG_CASE_LIGHT = _UxGT("Luci Case"); LSTR MSG_CASE_LIGHT_BRIGHTNESS = _UxGT("Luminosità Luci"); @@ -597,6 +598,8 @@ namespace Language_it { LSTR MSG_FILAMENT_CHANGE_NOZZLE = _UxGT(" Ugello: "); LSTR MSG_RUNOUT_SENSOR = _UxGT("Sens.filo termin."); // Max 17 characters LSTR MSG_RUNOUT_DISTANCE_MM = _UxGT("Dist mm filo term."); + LSTR MSG_RUNOUT_ENABLE = _UxGT("Abil.filo termin."); + LSTR MSG_FANCHECK = _UxGT("Verif.tacho vent."); // Max 17 characters LSTR MSG_KILL_HOMING_FAILED = _UxGT("Home fallito"); LSTR MSG_LCD_PROBING_FAILED = _UxGT("Sondaggio fallito"); diff --git a/Marlin/src/lcd/marlinui.cpp b/Marlin/src/lcd/marlinui.cpp index 57157d2c00..5cf3e66826 100644 --- a/Marlin/src/lcd/marlinui.cpp +++ b/Marlin/src/lcd/marlinui.cpp @@ -1566,10 +1566,6 @@ constexpr uint8_t epps = ENCODER_PULSES_PER_STEP; TERN_(HAS_LCD_MENU, return_to_status()); } - #if ANY(PARK_HEAD_ON_PAUSE, SDSUPPORT) - #include "../gcode/queue.h" - #endif - void MarlinUI::pause_print() { #if HAS_LCD_MENU synchronize(GET_TEXT(MSG_PAUSING)); diff --git a/Marlin/src/lcd/menu/menu_configuration.cpp b/Marlin/src/lcd/menu/menu_configuration.cpp index b976224b8c..cd8bad9c8b 100644 --- a/Marlin/src/lcd/menu/menu_configuration.cpp +++ b/Marlin/src/lcd/menu/menu_configuration.cpp @@ -34,6 +34,10 @@ #include "../../feature/runout.h" #endif +#if HAS_FANCHECK + #include "../../feature/fancheck.h" +#endif + #if ENABLED(POWER_LOSS_RECOVERY) #include "../../feature/powerloss.h" #endif @@ -537,6 +541,10 @@ void menu_configuration() { EDIT_ITEM(bool, MSG_RUNOUT_SENSOR, &runout.enabled, runout.reset); #endif + #if HAS_FANCHECK + EDIT_ITEM(bool, MSG_FANCHECK, &fan_check.enabled); + #endif + #if ENABLED(POWER_LOSS_RECOVERY) EDIT_ITEM(bool, MSG_OUTAGE_RECOVERY, &recovery.enabled, recovery.changed); #endif diff --git a/Marlin/src/module/settings.cpp b/Marlin/src/module/settings.cpp index 82232cf4f2..119c75f982 100644 --- a/Marlin/src/module/settings.cpp +++ b/Marlin/src/module/settings.cpp @@ -154,6 +154,10 @@ #include "../libs/buzzer.h" #endif +#if HAS_FANCHECK + #include "../feature/fancheck.h" +#endif + #if ENABLED(DGUS_LCD_UI_MKS) #include "../lcd/extui/dgus/DGUSScreenHandler.h" #include "../lcd/extui/dgus/DGUSDisplayDef.h" @@ -491,6 +495,13 @@ typedef struct SettingsDataStruct { bool buzzer_enabled; #endif + // + // Fan tachometer check + // + #if HAS_FANCHECK + bool fan_check_enabled; + #endif + // // MKS UI controller // @@ -1433,6 +1444,13 @@ void MarlinSettings::postprocess() { EEPROM_WRITE(ui.buzzer_enabled); #endif + // + // Fan tachometer check + // + #if HAS_FANCHECK + EEPROM_WRITE(fan_check.enabled); + #endif + // // MKS UI controller // @@ -2339,6 +2357,14 @@ void MarlinSettings::postprocess() { EEPROM_READ(ui.buzzer_enabled); #endif + // + // Fan tachometer check + // + #if HAS_FANCHECK + _FIELD_TEST(fan_check_enabled); + EEPROM_READ(fan_check.enabled); + #endif + // // MKS UI controller // @@ -3036,6 +3062,11 @@ void MarlinSettings::reset() { #endif #endif + // + // Fan tachometer check + // + TERN_(HAS_FANCHECK, fan_check.enabled = true); + // // MKS UI controller // diff --git a/Marlin/src/module/temperature.cpp b/Marlin/src/module/temperature.cpp index 494c4250f2..acc388478f 100644 --- a/Marlin/src/module/temperature.cpp +++ b/Marlin/src/module/temperature.cpp @@ -298,7 +298,7 @@ PGMSTR(str_t_heating_failed, STR_T_HEATING_FAILED); redundant_info_t Temperature::temp_redundant; #endif -#if ENABLED(AUTO_POWER_E_FANS) +#if EITHER(AUTO_POWER_E_FANS, HAS_FANCHECK) uint8_t Temperature::autofan_speed[HOTENDS]; // = { 0 } #endif @@ -530,8 +530,9 @@ volatile bool Temperature::raw_temps_ready = false; millis_t Temperature::preheat_end_time[HOTENDS] = { 0 }; #endif -#if HAS_AUTO_FAN - millis_t Temperature::next_auto_fan_check_ms = 0; +#if HAS_FAN_LOGIC + constexpr millis_t Temperature::fan_update_interval_ms; + millis_t Temperature::fan_update_ms = 0; #endif #if ENABLED(FAN_SOFT_PWM) @@ -618,7 +619,7 @@ volatile bool Temperature::raw_temps_ready = false; bool heated = false; #endif - TERN_(HAS_AUTO_FAN, next_auto_fan_check_ms = next_temp_ms + 2500UL); + TERN_(HAS_FAN_LOGIC, fan_update_ms = next_temp_ms + fan_update_interval_ms); TERN_(EXTENSIBLE_UI, ExtUI::onPidTuning(ExtUI::result_t::PID_STARTED)); TERN_(DWIN_CREALITY_LCD_ENHANCED, DWIN_PidTuning(isbed ? PID_BED_START : PID_EXTR_START)); @@ -663,12 +664,7 @@ volatile bool Temperature::raw_temps_ready = false; ONHEATING(start_temp, current_temp, target); #endif - #if HAS_AUTO_FAN - if (ELAPSED(ms, next_auto_fan_check_ms)) { - checkExtruderAutoFans(); - next_auto_fan_check_ms = ms + 2500UL; - } - #endif + TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms)); if (heating && current_temp > target && ELAPSED(ms, t2 + 5000UL)) { heating = false; @@ -857,6 +853,7 @@ int16_t Temperature::getHeaterPower(const heater_id_t heater_id) { #define _EFANOVERLAP(A,B) _FANOVERLAP(E##A,B) #if HAS_AUTO_FAN + #if EXTRUDER_AUTO_FAN_SPEED != 255 #define INIT_E_AUTO_FAN_PIN(P) do{ if (P == FAN1_PIN || P == FAN2_PIN) { SET_PWM(P); SET_FAST_PWM_FREQ(P); } else SET_OUTPUT(P); }while(0) #else @@ -870,7 +867,7 @@ int16_t Temperature::getHeaterPower(const heater_id_t heater_id) { #define CHAMBER_FAN_INDEX HOTENDS - void Temperature::checkExtruderAutoFans() { + void Temperature::update_autofans() { #define _EFAN(B,A) _EFANOVERLAP(A,B) ? B : static const uint8_t fanBit[] PROGMEM = { 0 @@ -930,36 +927,43 @@ int16_t Temperature::getHeaterPower(const heater_id_t heater_id) { break; #endif default: - #if ENABLED(AUTO_POWER_E_FANS) + #if EITHER(AUTO_POWER_E_FANS, HAS_FANCHECK) autofan_speed[realFan] = fan_on ? EXTRUDER_AUTO_FAN_SPEED : 0; #endif break; } + #if BOTH(HAS_FANCHECK, HAS_PWMFANCHECK) + #define _AUTOFAN_SPEED() fan_check.is_measuring() ? 255 : EXTRUDER_AUTO_FAN_SPEED + #else + #define _AUTOFAN_SPEED() EXTRUDER_AUTO_FAN_SPEED + #endif + #define _AUTOFAN_CASE(N) case N: _UPDATE_AUTO_FAN(E##N, fan_on, _AUTOFAN_SPEED()); break + switch (f) { #if HAS_AUTO_FAN_0 - case 0: _UPDATE_AUTO_FAN(E0, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(0); #endif #if HAS_AUTO_FAN_1 - case 1: _UPDATE_AUTO_FAN(E1, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(1); #endif #if HAS_AUTO_FAN_2 - case 2: _UPDATE_AUTO_FAN(E2, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(2); #endif #if HAS_AUTO_FAN_3 - case 3: _UPDATE_AUTO_FAN(E3, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(3); #endif #if HAS_AUTO_FAN_4 - case 4: _UPDATE_AUTO_FAN(E4, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(4); #endif #if HAS_AUTO_FAN_5 - case 5: _UPDATE_AUTO_FAN(E5, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(5); #endif #if HAS_AUTO_FAN_6 - case 6: _UPDATE_AUTO_FAN(E6, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(6); #endif #if HAS_AUTO_FAN_7 - case 7: _UPDATE_AUTO_FAN(E7, fan_on, EXTRUDER_AUTO_FAN_SPEED); break; + _AUTOFAN_CASE(7); #endif #if HAS_AUTO_CHAMBER_FAN && !AUTO_CHAMBER_IS_E case CHAMBER_FAN_INDEX: _UPDATE_AUTO_FAN(CHAMBER, fan_on, CHAMBER_AUTO_FAN_SPEED); break; @@ -1391,20 +1395,14 @@ void Temperature::manage_heater() { _temp_error((heater_id_t)HEATER_ID(TEMP_SENSOR_REDUNDANT_TARGET), F(STR_REDUNDANCY), GET_TEXT_F(MSG_ERR_REDUNDANT_TEMP)); #endif - #if HAS_AUTO_FAN - if (ELAPSED(ms, next_auto_fan_check_ms)) { // only need to check fan state very infrequently - checkExtruderAutoFans(); - next_auto_fan_check_ms = ms + 2500UL; - } - #endif + // Manage extruder auto fans and/or read fan tachometers + TERN_(HAS_FAN_LOGIC, manage_extruder_fans(ms)); - #if ENABLED(FILAMENT_WIDTH_SENSOR) - /** - * Dynamically set the volumetric multiplier based - * on the delayed Filament Width measurement. - */ - filwidth.update_volumetric(); - #endif + /** + * Dynamically set the volumetric multiplier based + * on the delayed Filament Width measurement. + */ + TERN_(FILAMENT_WIDTH_SENSOR, filwidth.update_volumetric()); #if HAS_HEATED_BED @@ -3525,6 +3523,9 @@ void Temperature::isr() { babystep.task(); #endif + // Check fan tachometers + TERN_(HAS_FANCHECK, fan_check.update_tachometers()); + // Poll endstops state, if required endstops.poll(); diff --git a/Marlin/src/module/temperature.h b/Marlin/src/module/temperature.h index 050b3379e9..0e4302938e 100644 --- a/Marlin/src/module/temperature.h +++ b/Marlin/src/module/temperature.h @@ -37,6 +37,10 @@ #include "../libs/autoreport.h" #endif +#if HAS_FANCHECK + #include "../feature/fancheck.h" +#endif + #ifndef SOFT_PWM_SCALE #define SOFT_PWM_SCALE 0 #endif @@ -344,6 +348,10 @@ typedef struct { int16_t raw_min, raw_max; celsius_t mintemp, maxtemp; } temp_ra #endif +#if HAS_AUTO_FAN || HAS_FANCHECK + #define HAS_FAN_LOGIC 1 +#endif + class Temperature { public: @@ -372,7 +380,7 @@ class Temperature { static redundant_info_t temp_redundant; #endif - #if ENABLED(AUTO_POWER_E_FANS) + #if EITHER(AUTO_POWER_E_FANS, HAS_FANCHECK) static uint8_t autofan_speed[HOTENDS]; #endif #if ENABLED(AUTO_POWER_CHAMBER_FAN) @@ -459,6 +467,10 @@ class Temperature { static int16_t lpq_len; #endif + #if HAS_FAN_LOGIC + static constexpr millis_t fan_update_interval_ms = TERN(HAS_PWMFANCHECK, 5000, TERN(HAS_FANCHECK, 1000, 2500)); + #endif + private: #if ENABLED(WATCH_HOTENDS) @@ -510,8 +522,28 @@ class Temperature { static millis_t preheat_end_time[HOTENDS]; #endif - #if HAS_AUTO_FAN - static millis_t next_auto_fan_check_ms; + #if HAS_FAN_LOGIC + static millis_t fan_update_ms; + + static inline void manage_extruder_fans(millis_t ms) { + if (ELAPSED(ms, fan_update_ms)) { // only need to check fan state very infrequently + const millis_t next_ms = ms + fan_update_interval_ms; + #if HAS_PWMFANCHECK + #define FAN_CHECK_DURATION 100 + if (fan_check.is_measuring()) { + fan_check.compute_speed(ms + FAN_CHECK_DURATION - fan_update_ms); + fan_update_ms = next_ms; + } + else + fan_update_ms = ms + FAN_CHECK_DURATION; + fan_check.toggle_measuring(); + #else + TERN_(HAS_FANCHECK, fan_check.compute_speed(next_ms - fan_update_ms)); + fan_update_ms = next_ms; + #endif + TERN_(HAS_AUTO_FAN, update_autofans()); // Needed as last when HAS_PWMFANCHECK to properly force fan speed + } + } #endif #if ENABLED(PROBING_HEATERS_OFF) @@ -961,7 +993,7 @@ class Temperature { static int16_t read_max_tc(TERN_(HAS_MULTI_MAX_TC, const uint8_t hindex=0)); #endif - static void checkExtruderAutoFans(); + static void update_autofans(); #if HAS_HOTEND static float get_pid_output_hotend(const uint8_t e); diff --git a/Marlin/src/pins/stm32f4/pins_BTT_BTT002_V1_0.h b/Marlin/src/pins/stm32f4/pins_BTT_BTT002_V1_0.h index 7b99352ef4..653ebd82a1 100644 --- a/Marlin/src/pins/stm32f4/pins_BTT_BTT002_V1_0.h +++ b/Marlin/src/pins/stm32f4/pins_BTT_BTT002_V1_0.h @@ -188,6 +188,14 @@ #endif #endif +#ifndef E0_FAN_TACHO_PIN + #ifdef MK3_FAN_PINS + #define E0_FAN_TACHO_PIN PE1 // Fan1 + #else + #define E0_FAN_TACHO_PIN PE0 // Fan0 + #endif +#endif + /** * -----------------------------------BTT002 V1.0---------------------------------------- * ------ ------ | diff --git a/ini/features.ini b/ini/features.ini index b6f81d3c56..bd8b748d6c 100644 --- a/ini/features.ini +++ b/ini/features.ini @@ -108,6 +108,7 @@ CANCEL_OBJECTS = src_filter=+ + EXTERNAL_CLOSED_LOOP_CONTROLLER = src_filter=+ + USE_CONTROLLER_FAN = src_filter=+ +HAS_COOLER|LASER_COOLANT_FLOW_METER = src_filter=+ HAS_MOTOR_CURRENT_DAC = src_filter=+ DIRECT_STEPPING = src_filter=+ + EMERGENCY_PARSER = src_filter=+ - @@ -115,6 +116,7 @@ I2C_POSITION_ENCODERS = src_filter=+ HAS_SPI_FLASH = src_filter=+ HAS_ETHERNET = src_filter=+ + +HAS_FANCHECK = src_filter=+ + HAS_FANMUX = src_filter=+ FILAMENT_WIDTH_SENSOR = src_filter=+ + FWRETRACT = src_filter=+ + @@ -212,7 +214,6 @@ GCODE_REPEAT_MARKERS = src_filter=+ +< HAS_EXTRUDERS = src_filter=+ + + HAS_TEMP_PROBE = src_filter=+ HAS_COOLER = src_filter=+ -HAS_COOLER|LASER_COOLANT_FLOW_METER = src_filter=+ AUTO_REPORT_TEMPERATURES = src_filter=+ INCH_MODE_SUPPORT = src_filter=+ TEMPERATURE_UNITS_SUPPORT = src_filter=+ diff --git a/platformio.ini b/platformio.ini index e16c3752ac..ce148ce082 100644 --- a/platformio.ini +++ b/platformio.ini @@ -112,6 +112,7 @@ default_src_filter = + - - + - - - - + - - - - - - @@ -229,6 +230,7 @@ default_src_filter = + - - + - - - + - - - -