Skip to content
This repository was archived by the owner on Mar 5, 2023. It is now read-only.

Commit 90a4a88

Browse files
GMagicianthinkyhead
andcommitted
Co-Authored-By: Scott Lahteine <github@thinkyhead.com>
1 parent 27f662a commit 90a4a88

21 files changed

Lines changed: 592 additions & 44 deletions

Marlin/Configuration_adv.h

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,40 @@
603603
#define COOLER_AUTO_FAN_TEMPERATURE 18
604604
#define COOLER_AUTO_FAN_SPEED 255
605605

606+
/**
607+
* Hotend Cooling Fans tachometers
608+
*
609+
* Define one or more tachometer pins to enable fan speed
610+
* monitoring, and reporting of fan speeds with M123.
611+
*
612+
* NOTE: Only works with fans up to 7000 RPM.
613+
*/
614+
//#define FOURWIRES_FANS // Needed with AUTO_FAN when 4-wire PWM fans are installed
615+
//#define E0_FAN_TACHO_PIN -1
616+
//#define E0_FAN_TACHO_PULLUP
617+
//#define E0_FAN_TACHO_PULLDOWN
618+
//#define E1_FAN_TACHO_PIN -1
619+
//#define E1_FAN_TACHO_PULLUP
620+
//#define E1_FAN_TACHO_PULLDOWN
621+
//#define E2_FAN_TACHO_PIN -1
622+
//#define E2_FAN_TACHO_PULLUP
623+
//#define E2_FAN_TACHO_PULLDOWN
624+
//#define E3_FAN_TACHO_PIN -1
625+
//#define E3_FAN_TACHO_PULLUP
626+
//#define E3_FAN_TACHO_PULLDOWN
627+
//#define E4_FAN_TACHO_PIN -1
628+
//#define E4_FAN_TACHO_PULLUP
629+
//#define E4_FAN_TACHO_PULLDOWN
630+
//#define E5_FAN_TACHO_PIN -1
631+
//#define E5_FAN_TACHO_PULLUP
632+
//#define E5_FAN_TACHO_PULLDOWN
633+
//#define E6_FAN_TACHO_PIN -1
634+
//#define E6_FAN_TACHO_PULLUP
635+
//#define E6_FAN_TACHO_PULLDOWN
636+
//#define E7_FAN_TACHO_PIN -1
637+
//#define E7_FAN_TACHO_PULLUP
638+
//#define E7_FAN_TACHO_PULLDOWN
639+
606640
/**
607641
* Part-Cooling Fan Multiplexer
608642
*
@@ -3607,6 +3641,12 @@
36073641
*/
36083642
//#define CNC_COORDINATE_SYSTEMS
36093643

3644+
/**
3645+
* Auto-report fan speed with M123 S<seconds>
3646+
* Requires fans with tachometer pins
3647+
*/
3648+
//#define AUTO_REPORT_FANS
3649+
36103650
/**
36113651
* Auto-report temperatures with M155 S<seconds>
36123652
*/

Marlin/src/MarlinCore.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,10 @@
212212

213213
#include "module/tool_change.h"
214214

215+
#if HAS_FANCHECK
216+
#include "feature/fancheck.h"
217+
#endif
218+
215219
#if ENABLED(USE_CONTROLLER_FAN)
216220
#include "feature/controllerfan.h"
217221
#endif
@@ -832,6 +836,7 @@ void idle(bool no_stepper_sleep/*=false*/) {
832836
#if HAS_AUTO_REPORTING
833837
if (!gcode.autoreport_paused) {
834838
TERN_(AUTO_REPORT_TEMPERATURES, thermalManager.auto_reporter.tick());
839+
TERN_(AUTO_REPORT_FANS, fan_check.auto_reporter.tick());
835840
TERN_(AUTO_REPORT_SD_STATUS, card.auto_reporter.tick());
836841
TERN_(AUTO_REPORT_POSITION, position_auto_reporter.tick());
837842
TERN_(BUFFER_MONITORING, queue.auto_report_buffer_statistics());
@@ -1278,6 +1283,8 @@ void setup() {
12781283
SETUP_RUN(controllerFan.setup());
12791284
#endif
12801285

1286+
TERN_(HAS_FANCHECK, fan_check.init());
1287+
12811288
// UI must be initialized before EEPROM
12821289
// (because EEPROM code calls the UI).
12831290

Marlin/src/core/language.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
#define STR_RESEND "Resend: "
141141
#define STR_UNKNOWN_COMMAND "Unknown command: \""
142142
#define STR_ACTIVE_EXTRUDER "Active Extruder: "
143+
#define STR_ERR_FANSPEED "Fan speed E"
143144

144145
#define STR_PROBE_OFFSET "Probe Offset"
145146
#define STR_SKEW_MIN "min_skew_factor: "

Marlin/src/feature/fancheck.cpp

Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
/**
2+
* Marlin 3D Printer Firmware
3+
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4+
*
5+
* Based on Sprinter and grbl.
6+
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
23+
/**
24+
* fancheck.cpp - fan tachometer check
25+
*/
26+
27+
#include "../inc/MarlinConfig.h"
28+
29+
#if HAS_FANCHECK
30+
31+
#include "fancheck.h"
32+
#include "../module/temperature.h"
33+
34+
#if HAS_AUTO_FAN && EXTRUDER_AUTO_FAN_SPEED != 255 && DISABLED(FOURWIRES_FANS)
35+
bool FanCheck::measuring = false;
36+
#endif
37+
bool FanCheck::tacho_state[TACHO_COUNT];
38+
uint16_t FanCheck::edge_counter[TACHO_COUNT];
39+
uint8_t FanCheck::rps[TACHO_COUNT];
40+
FanCheck::TachoError FanCheck::error = FanCheck::TachoError::NONE;
41+
bool FanCheck::enabled;
42+
43+
void FanCheck::init() {
44+
#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)
45+
#if HAS_E0_FAN_TACHO
46+
_TACHINIT(0);
47+
#endif
48+
#if HAS_E1_FAN_TACHO
49+
_TACHINIT(1);
50+
#endif
51+
#if HAS_E2_FAN_TACHO
52+
_TACHINIT(2);
53+
#endif
54+
#if HAS_E3_FAN_TACHO
55+
_TACHINIT(3);
56+
#endif
57+
#if HAS_E4_FAN_TACHO
58+
_TACHINIT(4);
59+
#endif
60+
#if HAS_E5_FAN_TACHO
61+
_TACHINIT(5);
62+
#endif
63+
#if HAS_E6_FAN_TACHO
64+
_TACHINIT(6);
65+
#endif
66+
#if HAS_E7_FAN_TACHO
67+
_TACHINIT(7);
68+
#endif
69+
}
70+
71+
void FanCheck::update_tachometers() {
72+
bool status;
73+
74+
#define _TACHO_CASE(N) case N: status = READ(E##N##_FAN_TACHO_PIN); break;
75+
LOOP_L_N(f, TACHO_COUNT) {
76+
switch (f) {
77+
#if HAS_E0_FAN_TACHO
78+
_TACHO_CASE(0)
79+
#endif
80+
#if HAS_E1_FAN_TACHO
81+
_TACHO_CASE(1)
82+
#endif
83+
#if HAS_E2_FAN_TACHO
84+
_TACHO_CASE(2)
85+
#endif
86+
#if HAS_E3_FAN_TACHO
87+
_TACHO_CASE(3)
88+
#endif
89+
#if HAS_E4_FAN_TACHO
90+
_TACHO_CASE(4)
91+
#endif
92+
#if HAS_E5_FAN_TACHO
93+
_TACHO_CASE(5)
94+
#endif
95+
#if HAS_E6_FAN_TACHO
96+
_TACHO_CASE(6)
97+
#endif
98+
#if HAS_E7_FAN_TACHO
99+
_TACHO_CASE(7)
100+
#endif
101+
default: continue;
102+
}
103+
104+
if (status != tacho_state[f]) {
105+
if (measuring) ++edge_counter[f];
106+
tacho_state[f] = status;
107+
}
108+
}
109+
}
110+
111+
void FanCheck::compute_speed(uint16_t elapsedTime) {
112+
static uint8_t errors_count[TACHO_COUNT];
113+
static uint8_t fan_reported_errors_msk = 0;
114+
115+
uint8_t fan_error_msk = 0;
116+
LOOP_L_N(f, TACHO_COUNT) {
117+
switch (f) {
118+
TERN_(HAS_E0_FAN_TACHO, case 0:)
119+
TERN_(HAS_E1_FAN_TACHO, case 1:)
120+
TERN_(HAS_E2_FAN_TACHO, case 2:)
121+
TERN_(HAS_E3_FAN_TACHO, case 3:)
122+
TERN_(HAS_E4_FAN_TACHO, case 4:)
123+
TERN_(HAS_E5_FAN_TACHO, case 5:)
124+
TERN_(HAS_E6_FAN_TACHO, case 6:)
125+
TERN_(HAS_E7_FAN_TACHO, case 7:)
126+
// Compute fan speed
127+
rps[f] = edge_counter[f] * float(250) / elapsedTime;
128+
edge_counter[f] = 0;
129+
130+
// Check fan speed
131+
constexpr int8_t max_extruder_fan_errors = TERN(HAS_PWMFANCHECK, 10000, 5000) / Temperature::fan_update_interval_ms;
132+
133+
if (rps[f] >= 20 || TERN0(HAS_AUTO_FAN, thermalManager.autofan_speed[f] == 0))
134+
errors_count[f] = 0;
135+
else if (errors_count[f] < max_extruder_fan_errors)
136+
++errors_count[f];
137+
else if (enabled)
138+
SBI(fan_error_msk, f);
139+
break;
140+
}
141+
}
142+
143+
// Drop the error when all fans are ok
144+
if (!fan_error_msk && error == TachoError::REPORTED) error = TachoError::FIXED;
145+
146+
if (error == TachoError::FIXED && !printJobOngoing() && !printingIsPaused()) {
147+
error = TachoError::NONE; // if the issue has been fixed while the printer is idle, reenable immediately
148+
ui.reset_alert_level();
149+
}
150+
151+
if (fan_error_msk & ~fan_reported_errors_msk) {
152+
// Handle new faults only
153+
LOOP_L_N(f, TACHO_COUNT) if (TEST(fan_error_msk, f)) report_speed_error(f);
154+
}
155+
fan_reported_errors_msk = fan_error_msk;
156+
}
157+
158+
void FanCheck::report_speed_error(uint8_t fan) {
159+
if (printJobOngoing()) {
160+
if (error == TachoError::NONE) {
161+
if (thermalManager.degTargetHotend(fan) != 0) {
162+
kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT));
163+
error = TachoError::REPORTED;
164+
}
165+
else
166+
error = TachoError::DETECTED; // Plans error for next processed command
167+
}
168+
}
169+
else if (!printingIsPaused()) {
170+
thermalManager.setTargetHotend(0, fan); // Always disable heating
171+
if (error == TachoError::NONE) error = TachoError::REPORTED;
172+
}
173+
174+
SERIAL_ERROR_MSG(STR_ERR_FANSPEED, fan);
175+
LCD_ALERTMESSAGE(MSG_FAN_SPEED_FAULT);
176+
}
177+
178+
void FanCheck::print_fan_states() {
179+
LOOP_L_N(s, 2) {
180+
LOOP_L_N(f, TACHO_COUNT) {
181+
switch (f) {
182+
TERN_(HAS_E0_FAN_TACHO, case 0:)
183+
TERN_(HAS_E1_FAN_TACHO, case 1:)
184+
TERN_(HAS_E2_FAN_TACHO, case 2:)
185+
TERN_(HAS_E3_FAN_TACHO, case 3:)
186+
TERN_(HAS_E4_FAN_TACHO, case 4:)
187+
TERN_(HAS_E5_FAN_TACHO, case 5:)
188+
TERN_(HAS_E6_FAN_TACHO, case 6:)
189+
TERN_(HAS_E7_FAN_TACHO, case 7:)
190+
SERIAL_ECHOPGM("E", f);
191+
if (s == 0)
192+
SERIAL_ECHOPGM(":", 60 * rps[f], " RPM ");
193+
else
194+
SERIAL_ECHOPGM("@:", TERN(HAS_AUTO_FAN, thermalManager.autofan_speed[f], 255), " ");
195+
break;
196+
}
197+
}
198+
}
199+
SERIAL_EOL();
200+
}
201+
202+
#if ENABLED(AUTO_REPORT_FANS)
203+
AutoReporter<FanCheck::AutoReportFan> FanCheck::auto_reporter;
204+
void FanCheck::AutoReportFan::report() { print_fan_states(); }
205+
#endif
206+
207+
#endif // HAS_FANCHECK

Marlin/src/feature/fancheck.h

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/**
2+
* Marlin 3D Printer Firmware
3+
* Copyright (c) 2021 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
4+
*
5+
* Based on Sprinter and grbl.
6+
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
7+
*
8+
* This program is free software: you can redistribute it and/or modify
9+
* it under the terms of the GNU General Public License as published by
10+
* the Free Software Foundation, either version 3 of the License, or
11+
* (at your option) any later version.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU General Public License
19+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
20+
*
21+
*/
22+
#pragma once
23+
24+
#include "../inc/MarlinConfig.h"
25+
26+
#if HAS_FANCHECK
27+
28+
#include "../MarlinCore.h"
29+
#include "../lcd/marlinui.h"
30+
31+
#if ENABLED(AUTO_REPORT_FANS)
32+
#include "../libs/autoreport.h"
33+
#endif
34+
35+
#if ENABLED(PARK_HEAD_ON_PAUSE)
36+
#include "../gcode/queue.h"
37+
#endif
38+
39+
/**
40+
* fancheck.h
41+
*/
42+
#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)))))))
43+
44+
class FanCheck {
45+
private:
46+
47+
enum class TachoError : uint8_t { NONE, DETECTED, REPORTED, FIXED };
48+
49+
#if HAS_PWMFANCHECK
50+
static bool measuring; // For future use (3 wires PWM controlled fans)
51+
#else
52+
static constexpr bool measuring = true;
53+
#endif
54+
static bool tacho_state[TACHO_COUNT];
55+
static uint16_t edge_counter[TACHO_COUNT];
56+
static uint8_t rps[TACHO_COUNT];
57+
static TachoError error;
58+
59+
static inline void report_speed_error(uint8_t fan);
60+
61+
public:
62+
63+
static bool enabled;
64+
65+
static void init();
66+
static void update_tachometers();
67+
static void compute_speed(uint16_t elapsedTime);
68+
static void print_fan_states();
69+
#if HAS_PWMFANCHECK
70+
static inline void toggle_measuring() { measuring = !measuring; }
71+
static inline bool is_measuring() { return measuring; }
72+
#endif
73+
74+
static inline void check_deferred_error() {
75+
if (error == TachoError::DETECTED) {
76+
error = TachoError::REPORTED;
77+
TERN(PARK_HEAD_ON_PAUSE, queue.inject(F("M125")), kill(GET_TEXT_F(MSG_FAN_SPEED_FAULT)));
78+
}
79+
}
80+
81+
#if ENABLED(AUTO_REPORT_FANS)
82+
struct AutoReportFan { static void report(); };
83+
static AutoReporter<AutoReportFan> auto_reporter;
84+
#endif
85+
};
86+
87+
extern FanCheck fan_check;
88+
89+
#endif // HAS_FANCHECK

Marlin/src/gcode/control/M999.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
#include "../gcode.h"
2424

25-
#include "../../lcd/marlinui.h" // for lcd_reset_alert_level
25+
#include "../../lcd/marlinui.h" // for ui.reset_alert_level
2626
#include "../../MarlinCore.h" // for marlin_state
2727
#include "../queue.h" // for flush_and_request_resend
2828

0 commit comments

Comments
 (0)