Browse Source

Laser Coolant Flow Meter / Safety Shutdown (#21431)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
Mike La Spina 3 years ago
parent
commit
ccdbffbf3f
No account linked to committer's email address

+ 15
- 0
Marlin/Configuration_adv.h View File

@@ -204,6 +204,20 @@
204 204
   #endif
205 205
 #endif
206 206
 
207
+//
208
+// Laser Coolant Flow Meter
209
+//
210
+//#define LASER_COOLANT_FLOW_METER
211
+#if ENABLED(LASER_COOLANT_FLOW_METER)
212
+  #define FLOWMETER_PIN         20  // Requires an external interrupt-enabled pin (e.g., RAMPS 2,3,18,19,20,21)
213
+  #define FLOWMETER_PPL       5880  // (pulses/liter) Flow meter pulses-per-liter on the input pin
214
+  #define FLOWMETER_INTERVAL  1000  // (ms) Flow rate calculation interval in milliseconds
215
+  #define FLOWMETER_SAFETY          // Prevent running the laser without the minimum flow rate set below
216
+  #if ENABLED(FLOWMETER_SAFETY)
217
+    #define FLOWMETER_MIN_LITERS_PER_MINUTE 1.5 // (liters/min) Minimum flow required when enabled
218
+  #endif
219
+#endif
220
+
207 221
 /**
208 222
  * Thermal Protection provides additional protection to your printer from damage
209 223
  * and fire. Marlin always includes safe min and max temperature ranges which
@@ -1539,6 +1553,7 @@
1539 1553
   #define STATUS_CHAMBER_ANIM         // Use a second bitmap to indicate chamber heating
1540 1554
   //#define STATUS_CUTTER_ANIM        // Use a second bitmap to indicate spindle / laser active
1541 1555
   //#define STATUS_COOLER_ANIM        // Use a second bitmap to indicate laser cooling
1556
+  //#define STATUS_FLOWMETER_ANIM     // Use multiple bitmaps to indicate coolant flow
1542 1557
   //#define STATUS_ALT_BED_BITMAP     // Use the alternative bed bitmap
1543 1558
   //#define STATUS_ALT_FAN_BITMAP     // Use the alternative fan bitmap
1544 1559
   //#define STATUS_FAN_FRAMES 3       // :[0,1,2,3,4] Number of fan animation frames

+ 1
- 0
Marlin/src/core/language.h View File

@@ -130,6 +130,7 @@
130 130
 #define STR_COUNT_A                         " Count A:"
131 131
 #define STR_WATCHDOG_FIRED                  "Watchdog timeout. Reset required."
132 132
 #define STR_ERR_KILLED                      "Printer halted. kill() called!"
133
+#define STR_FLOWMETER_FAULT                 "Coolant flow fault. Flowmeter safety is active. Attention required."
133 134
 #define STR_ERR_STOPPED                     "Printer stopped due to errors. Fix the error and use M999 to restart. (Temperature is reset. Set it after restarting)"
134 135
 #define STR_ERR_SERIAL_MISMATCH             "Serial status mismatch"
135 136
 #define STR_BUSY_PROCESSING                 "busy: processing"

+ 16
- 6
Marlin/src/feature/cooler.cpp View File

@@ -27,11 +27,21 @@
27 27
 #include "cooler.h"
28 28
 Cooler cooler;
29 29
 
30
-uint16_t Cooler::flowrate;        // Flow meter reading in liters, 0 will result in shutdown if equiped
31
-uint8_t Cooler::mode = 0;         // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling
32
-uint16_t Cooler::capacity;        // Cooling capacity in watts
33
-uint16_t Cooler::load;            // Cooling load in watts
34
-bool Cooler::flowmeter = false;
35
-bool Cooler::state = false;       // on = true, off = false
30
+uint8_t Cooler::mode = 0;
31
+uint16_t Cooler::capacity;
32
+uint16_t Cooler::load;
33
+bool Cooler::enabled = false;
36 34
 
35
+#if ENABLED(LASER_COOLANT_FLOW_METER)
36
+  bool Cooler::flowmeter = false;
37
+  millis_t Cooler::flowmeter_next_ms; // = 0
38
+  volatile uint16_t Cooler::flowpulses;
39
+  float Cooler::flowrate;
37 40
 #endif
41
+
42
+#if ENABLED(FLOWMETER_SAFETY)
43
+  bool Cooler::flowsafety_enabled = true;
44
+  bool Cooler::fault = false;
45
+#endif
46
+
47
+#endif // HAS_COOLER

+ 79
- 18
Marlin/src/feature/cooler.h View File

@@ -21,30 +21,91 @@
21 21
  */
22 22
 #pragma once
23 23
 
24
-#include <stdint.h>
24
+#include "../inc/MarlinConfigPre.h"
25 25
 
26
-#define _MSG_COOLER(M) MSG_COOLER_##M
27
-#define MSG_COOLER(M) _MSG_COOLER(M)
26
+#ifndef FLOWMETER_PPL
27
+  #define FLOWMETER_PPL      5880 // Pulses per liter
28
+#endif
29
+#ifndef FLOWMETER_INTERVAL
30
+  #define FLOWMETER_INTERVAL 1000 // milliseconds
31
+#endif
28 32
 
29 33
 // Cooling device
30 34
 
31 35
 class Cooler {
32 36
 public:
33
-  static uint16_t flowrate;        // Flow meter reading in liters, 0 will result in shutdown if equiped
34
-  static uint8_t mode;             // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling
35
-  static uint16_t capacity;        // Cooling capacity in watts
36
-  static uint16_t load;            // Cooling load in watts
37
-  static bool flowmeter;
38
-  static bool state;               // on = true, off = false
39
-
40
-  static bool is_enabled()                    { return state; }
41
-  static void enable()                        { state = true; }
42
-  static void disable()                       { state = false; }
43
-  static void set_mode(const uint8_t m)       { mode = m; }
44
-  static void set_flowmeter(const bool sflag) { flowmeter = sflag; }
45
-  static uint16_t get_flowrate()              { return flowrate; }
46
-  static void update_flowrate(uint16_t flow)  { flowrate = flow; }
47
-  //static void init() { set_state(false); }
37
+  static uint16_t capacity;   // Cooling capacity in watts
38
+  static uint16_t load;       // Cooling load in watts
39
+
40
+  static bool enabled;
41
+  static void enable()  { enabled = true; }
42
+  static void disable() { enabled = false; }
43
+  static void toggle()  { enabled = !enabled; }
44
+
45
+  static uint8_t mode;                  // 0 = CO2 Liquid cooling, 1 = Laser Diode TEC Heatsink Cooling
46
+  static void set_mode(const uint8_t m) { mode = m; }
47
+
48
+  #if ENABLED(LASER_COOLANT_FLOW_METER)
49
+    static float flowrate;                // Flow meter reading in liters-per-minute.
50
+    static bool flowmeter;                // Flag to monitor the flow
51
+    static volatile uint16_t flowpulses;  // Flowmeter IRQ pulse count
52
+    static millis_t flowmeter_next_ms;    // Next time at which to calculate flow
53
+
54
+    static void set_flowmeter(const bool sflag) {
55
+      if (flowmeter != sflag) {
56
+        flowmeter = sflag;
57
+        if (sflag) {
58
+          flowpulses = 0;
59
+          flowmeter_next_ms = millis() + FLOWMETER_INTERVAL;
60
+        }
61
+      }
62
+    }
63
+
64
+    // To calculate flow we only need to count pulses
65
+    static void flowmeter_ISR() { flowpulses++; }
66
+
67
+    // Enable / Disable the flow meter interrupt
68
+    static void flowmeter_interrupt_enable() {
69
+      attachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN), flowmeter_ISR, RISING);
70
+    }
71
+    static void flowmeter_interrupt_disable() {
72
+      detachInterrupt(digitalPinToInterrupt(FLOWMETER_PIN));
73
+    }
74
+
75
+    // Enable / Disable the flow meter interrupt
76
+    static void flowmeter_enable()  { set_flowmeter(true); flowpulses = 0; flowmeter_interrupt_enable(); }
77
+    static void flowmeter_disable() { set_flowmeter(false); flowmeter_interrupt_disable(); flowpulses = 0; }
78
+
79
+    // Get the total flow (in liters per minute) since the last reading
80
+    static void calc_flowrate() {
81
+      //flowmeter_interrupt_disable();
82
+      //  const uint16_t pulses = flowpulses;
83
+      //flowmeter_interrupt_enable();
84
+      flowrate = flowpulses * 60.0f * (1000.0f / (FLOWMETER_INTERVAL)) * (1000.0f / (FLOWMETER_PPL));
85
+      flowpulses = 0;
86
+    }
87
+
88
+    // Userland task to update the flow meter
89
+    static void flowmeter_task(const millis_t ms=millis()) {
90
+      if (!flowmeter)       // !! The flow meter must always be on !!
91
+        flowmeter_enable(); // Init and prime
92
+      if (ELAPSED(ms, flowmeter_next_ms)) {
93
+        calc_flowrate();
94
+        flowmeter_next_ms = ms + FLOWMETER_INTERVAL;
95
+      }
96
+    }
97
+
98
+    #if ENABLED(FLOWMETER_SAFETY)
99
+      static bool fault;                // Flag that the cooler is in a fault state
100
+      static bool flowsafety_enabled;   // Flag to disable the cutter if flow rate is too low
101
+      static void flowsafety_toggle()   { flowsafety_enabled = !flowsafety_enabled; }
102
+      static bool check_flow_too_low() {
103
+        const bool too_low = flowsafety_enabled && flowrate < (FLOWMETER_MIN_LITERS_PER_MINUTE);
104
+        if (too_low) fault = true;
105
+        return too_low;
106
+      }
107
+    #endif
108
+  #endif
48 109
 };
49 110
 
50 111
 extern Cooler cooler;

+ 2
- 3
Marlin/src/feature/spindle_laser.h View File

@@ -215,8 +215,7 @@ public:
215 215
   static inline void disable() { isReady = false; set_enabled(false); }
216 216
 
217 217
   #if HAS_LCD_MENU
218
-
219
-    static inline void enable_with_dir(const bool reverse) {
218
+      static inline void enable_with_dir(const bool reverse) {
220 219
       isReady = true;
221 220
       const uint8_t ocr = TERN(SPINDLE_LASER_PWM, upower_to_ocr(menuPower), 255);
222 221
       if (menuPower)
@@ -245,8 +244,8 @@ public:
245 244
        * If not set defaults to 80% power
246 245
        */
247 246
       static inline void test_fire_pulse() {
248
-        enable_forward();                  // Turn Laser on (Spindle speak but same funct)
249 247
         TERN_(USE_BEEPER, buzzer.tone(30, 3000));
248
+        enable_forward();                  // Turn Laser on (Spindle speak but same funct)
250 249
         delay(testPulse);                  // Delay for time set by user in pulse ms menu screen.
251 250
         disable();                         // Turn laser off
252 251
       }

+ 11
- 0
Marlin/src/gcode/gcode.cpp View File

@@ -57,6 +57,10 @@ GcodeSuite gcode;
57 57
   #include "../feature/spindle_laser.h"
58 58
 #endif
59 59
 
60
+#if ENABLED(FLOWMETER_SAFETY)
61
+  #include "../feature/cooler.h"
62
+#endif
63
+
60 64
 #if ENABLED(PASSWORD_FEATURE)
61 65
   #include "../feature/password/password.h"
62 66
 #endif
@@ -278,6 +282,13 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
278 282
     }
279 283
   #endif
280 284
 
285
+  #if ENABLED(FLOWMETER_SAFETY)
286
+    if (cooler.fault) {
287
+      SERIAL_ECHO_MSG(STR_FLOWMETER_FAULT);
288
+      return;
289
+    }
290
+  #endif
291
+
281 292
   // Handle a known G, M, or T
282 293
   switch (parser.command_letter) {
283 294
     case 'G': switch (parser.codenum) {

+ 4
- 0
Marlin/src/inc/SanityCheck.h View File

@@ -1895,6 +1895,10 @@ static_assert(hbm[Z_AXIS] >= 0, "HOMING_BUMP_MM.Z must be greater than or equal
1895 1895
   #error "TEMP_SENSOR_COOLER requires LASER_FEATURE and TEMP_COOLER_PIN."
1896 1896
 #endif
1897 1897
 
1898
+#if ENABLED(LASER_COOLANT_FLOW_METER) && !(PIN_EXISTS(FLOWMETER) && ENABLED(LASER_FEATURE))
1899
+  #error "LASER_COOLANT_FLOW_METER requires FLOWMETER_PIN and LASER_FEATURE."
1900
+#endif
1901
+
1898 1902
 #if ENABLED(CHAMBER_FAN) && !(defined(CHAMBER_FAN_MODE) && WITHIN(CHAMBER_FAN_MODE, 0, 2))
1899 1903
   #error "CHAMBER_FAN_MODE must be between 0 and 2."
1900 1904
 #endif

+ 74
- 21
Marlin/src/lcd/HD44780/marlinui_HD44780.cpp View File

@@ -46,6 +46,10 @@
46 46
   #include "../../gcode/parser.h"
47 47
 #endif
48 48
 
49
+#if HAS_COOLER || HAS_FLOWMETER
50
+  #include "../../feature/cooler.h"
51
+#endif
52
+
49 53
 #if ENABLED(AUTO_BED_LEVELING_UBL)
50 54
   #include "../../feature/bedlevel/bedlevel.h"
51 55
 #endif
@@ -517,6 +521,7 @@ FORCE_INLINE void _draw_axis_value(const AxisEnum axis, const char *value, const
517 521
     lcd_put_u8str(value);
518 522
 }
519 523
 
524
+
520 525
 FORCE_INLINE void _draw_heater_status(const heater_id_t heater_id, const char prefix, const bool blink) {
521 526
   #if HAS_HEATED_BED
522 527
     const bool isBed = TERN(HAS_HEATED_CHAMBER, heater_id == H_BED, heater_id < 0);
@@ -550,6 +555,43 @@ FORCE_INLINE void _draw_heater_status(const heater_id_t heater_id, const char pr
550 555
   }
551 556
 }
552 557
 
558
+#if HAS_COOLER
559
+FORCE_INLINE void _draw_cooler_status(const char prefix, const bool blink) {
560
+  const float t1 = thermalManager.degCooler(), t2 = thermalManager.degTargetCooler();
561
+
562
+  if (prefix >= 0) lcd_put_wchar(prefix);
563
+
564
+  lcd_put_u8str(i16tostr3rj(t1 + 0.5));
565
+  lcd_put_wchar('/');
566
+
567
+  #if !HEATER_IDLE_HANDLER
568
+    UNUSED(blink);
569
+  #else
570
+    if (!blink && thermalManager.heater_idle[thermalManager.idle_index_for_id(heater_id)].timed_out) {
571
+      lcd_put_wchar(' ');
572
+      if (t2 >= 10) lcd_put_wchar(' ');
573
+      if (t2 >= 100) lcd_put_wchar(' ');
574
+    }
575
+    else
576
+  #endif
577
+      lcd_put_u8str(i16tostr3left(t2 + 0.5));
578
+
579
+  if (prefix >= 0) {
580
+    lcd_put_wchar(LCD_STR_DEGREE[0]);
581
+    lcd_put_wchar(' ');
582
+    if (t2 < 10) lcd_put_wchar(' ');
583
+  }
584
+}
585
+#endif
586
+
587
+#if HAS_FLOWMETER
588
+  FORCE_INLINE void _draw_flowmeter_status() {
589
+    lcd_put_u8str("~ ");
590
+    lcd_put_u8str(ftostr11ns(cooler.flowrate));
591
+    lcd_put_wchar('L');
592
+  }
593
+#endif
594
+
553 595
 FORCE_INLINE void _draw_bed_status(const bool blink) {
554 596
   _draw_heater_status(H_BED, TERN0(HAS_LEVELING, blink && planner.leveling_active) ? '_' : LCD_STR_BEDTEMP[0], blink);
555 597
 }
@@ -747,17 +789,19 @@ void MarlinUI::draw_status_screen() {
747 789
       //
748 790
       // Hotend 0 Temperature
749 791
       //
750
-      _draw_heater_status(H_E0, -1, blink);
751
-
752
-      //
753
-      // Hotend 1 or Bed Temperature
754
-      //
755
-      #if HAS_MULTI_HOTEND
756
-        lcd_moveto(8, 0);
757
-        _draw_heater_status(H_E1, LCD_STR_THERMOMETER[0], blink);
758
-      #elif HAS_HEATED_BED
759
-        lcd_moveto(8, 0);
760
-        _draw_bed_status(blink);
792
+      #if HAS_HOTEND
793
+        _draw_heater_status(H_E0, -1, blink);
794
+
795
+        //
796
+        // Hotend 1 or Bed Temperature
797
+        //
798
+        #if HAS_MULTI_HOTEND
799
+          lcd_moveto(8, 0);
800
+          _draw_heater_status(H_E1, LCD_STR_THERMOMETER[0], blink);
801
+        #elif HAS_HEATED_BED
802
+          lcd_moveto(8, 0);
803
+          _draw_bed_status(blink);
804
+        #endif
761 805
       #endif
762 806
 
763 807
     #else // LCD_WIDTH >= 20
@@ -765,17 +809,26 @@ void MarlinUI::draw_status_screen() {
765 809
       //
766 810
       // Hotend 0 Temperature
767 811
       //
768
-      _draw_heater_status(H_E0, LCD_STR_THERMOMETER[0], blink);
812
+      #if HAS_HOTEND
813
+        _draw_heater_status(H_E0, LCD_STR_THERMOMETER[0], blink);
814
+
815
+        //
816
+        // Hotend 1 or Bed Temperature
817
+        //
818
+        #if HAS_MULTI_HOTEND
819
+          lcd_moveto(10, 0);
820
+          _draw_heater_status(H_E1, LCD_STR_THERMOMETER[0], blink);
821
+        #elif HAS_HEATED_BED
822
+          lcd_moveto(10, 0);
823
+          _draw_bed_status(blink);
824
+        #endif
825
+      #endif
769 826
 
770
-      //
771
-      // Hotend 1 or Bed Temperature
772
-      //
773
-      #if HAS_MULTI_HOTEND
774
-        lcd_moveto(10, 0);
775
-        _draw_heater_status(H_E1, LCD_STR_THERMOMETER[0], blink);
776
-      #elif HAS_HEATED_BED
777
-        lcd_moveto(10, 0);
778
-        _draw_bed_status(blink);
827
+      #if HAS_COOLER
828
+        _draw_cooler_status('*', blink);
829
+      #endif
830
+      #if HAS_FLOWMETER
831
+        _draw_flowmeter_status();
779 832
       #endif
780 833
 
781 834
     #endif // LCD_WIDTH >= 20

+ 112
- 56
Marlin/src/lcd/dogm/dogm_Statusscreen.h View File

@@ -77,9 +77,12 @@
77 77
 #ifndef STATUS_CUTTER_WIDTH
78 78
   #define STATUS_CUTTER_WIDTH 0
79 79
 #endif
80
+  #ifndef STATUS_CUTTER_BYTEWIDTH
81
+    #define STATUS_CUTTER_BYTEWIDTH BW(STATUS_CUTTER_WIDTH)
82
+  #endif
80 83
 
81 84
 //
82
-// Laser Cooler
85
+// Laser cooler
83 86
 //
84 87
 #if !STATUS_COOLER_WIDTH && HAS_COOLER
85 88
   #include "status/cooler.h"
@@ -87,6 +90,24 @@
87 90
 #ifndef STATUS_COOLER_WIDTH
88 91
   #define STATUS_COOLER_WIDTH 0
89 92
 #endif
93
+#ifndef STATUS_COOLER_BYTEWIDTH
94
+  #define STATUS_COOLER_BYTEWIDTH BW(STATUS_COOLER_WIDTH)
95
+#endif
96
+
97
+//
98
+// Laser Flowmeter
99
+//
100
+#if !STATUS_FLOWMETER_WIDTH && HAS_FLOWMETER
101
+  #include "status/cooler.h"
102
+#endif
103
+#ifndef STATUS_FLOWMETER_WIDTH
104
+  #define STATUS_FLOWMETER_WIDTH 0
105
+#endif
106
+#ifndef STATUS_FLOWMETER_BYTEWIDTH
107
+  #define STATUS_FLOWMETER_BYTEWIDTH BW(STATUS_FLOWMETER_WIDTH)
108
+#endif
109
+
110
+
90 111
 
91 112
 //
92 113
 // Bed
@@ -425,46 +446,45 @@
425 446
 //
426 447
 // Cutter Bitmap Properties
427 448
 //
428
-#ifndef STATUS_CUTTER_BYTEWIDTH
429
-  #define STATUS_CUTTER_BYTEWIDTH BW(STATUS_CUTTER_WIDTH)
430
-#endif
431
-#if STATUS_CUTTER_WIDTH
449
+#if HAS_CUTTER
450
+  #if STATUS_CUTTER_WIDTH
432 451
 
433
-  #ifndef STATUS_CUTTER_X
434
-    #define STATUS_CUTTER_X (LCD_PIXEL_WIDTH - (STATUS_CUTTER_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH) * 8)
435
-  #endif
452
+    #ifndef STATUS_CUTTER_X
453
+      #define STATUS_CUTTER_X (LCD_PIXEL_WIDTH - (STATUS_CUTTER_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH) * 8)
454
+    #endif
436 455
 
437
-  #ifndef STATUS_CUTTER_HEIGHT
438
-    #ifdef STATUS_CUTTER_ANIM
439
-      #define STATUS_CUTTER_HEIGHT(S) ((S) ? sizeof(status_cutter_on_bmp) / (STATUS_CUTTER_BYTEWIDTH) : sizeof(status_cutter_bmp) / (STATUS_CUTTER_BYTEWIDTH))
440
-    #else
441
-      #define STATUS_CUTTER_HEIGHT(S) (sizeof(status_cutter_bmp) / (STATUS_CUTTER_BYTEWIDTH))
456
+    #ifndef STATUS_CUTTER_HEIGHT
457
+      #ifdef STATUS_CUTTER_ANIM
458
+        #define STATUS_CUTTER_HEIGHT(S) ((S) ? sizeof(status_cutter_on_bmp) / (STATUS_CUTTER_BYTEWIDTH) : sizeof(status_cutter_bmp) / (STATUS_CUTTER_BYTEWIDTH))
459
+      #else
460
+        #define STATUS_CUTTER_HEIGHT(S) (sizeof(status_cutter_bmp) / (STATUS_CUTTER_BYTEWIDTH))
461
+      #endif
442 462
     #endif
443
-  #endif
444 463
 
445
-  #ifndef STATUS_CUTTER_Y
446
-    #define STATUS_CUTTER_Y(S) 4
447
-  #endif
464
+    #ifndef STATUS_CUTTER_Y
465
+      #define STATUS_CUTTER_Y(S) 4
466
+    #endif
448 467
 
449
-  #ifndef STATUS_CUTTER_TEXT_X
450
-    #define STATUS_CUTTER_TEXT_X (STATUS_CUTTER_X -1)
451
-  #endif
468
+    #ifndef STATUS_CUTTER_TEXT_X
469
+      #define STATUS_CUTTER_TEXT_X (STATUS_CUTTER_X -1)
470
+    #endif
452 471
 
453
-  #ifndef STATUS_CUTTER_TEXT_Y
454
-    #define STATUS_CUTTER_TEXT_Y 28
455
-  #endif
472
+    #ifndef STATUS_CUTTER_TEXT_Y
473
+      #define STATUS_CUTTER_TEXT_Y 28
474
+    #endif
456 475
 
457
-  static_assert(
458
-    sizeof(status_cutter_bmp) == (STATUS_CUTTER_BYTEWIDTH) * (STATUS_CUTTER_HEIGHT(0)),
459
-    "Status cutter bitmap (status_cutter_bmp) dimensions don't match data."
460
-  );
461
-  #ifdef STATUS_CUTTER_ANIM
462 476
     static_assert(
463
-      sizeof(status_cutter_on_bmp) == (STATUS_CUTTER_BYTEWIDTH) * (STATUS_CUTTER_HEIGHT(1)),
464
-      "Status cutter bitmap (status_cutter_on_bmp) dimensions don't match data."
477
+      sizeof(status_cutter_bmp) == (STATUS_CUTTER_BYTEWIDTH) * (STATUS_CUTTER_HEIGHT(0)),
478
+      "Status cutter bitmap (status_cutter_bmp) dimensions don't match data."
465 479
     );
466
-  #endif
480
+    #ifdef STATUS_CUTTER_ANIM
481
+      static_assert(
482
+        sizeof(status_cutter_on_bmp) == (STATUS_CUTTER_BYTEWIDTH) * (STATUS_CUTTER_HEIGHT(1)),
483
+        "Status cutter bitmap (status_cutter_on_bmp) dimensions don't match data."
484
+      );
485
+    #endif
467 486
 
487
+  #endif
468 488
 #endif
469 489
 
470 490
 //
@@ -511,42 +531,72 @@
511 531
 //
512 532
 // Cooler Bitmap Properties
513 533
 //
514
-#ifndef STATUS_COOLER_BYTEWIDTH
515
-  #define STATUS_COOLER_BYTEWIDTH BW(STATUS_COOLER_WIDTH)
516
-#endif
517
-#if STATUS_COOLER_WIDTH
534
+#if HAS_COOLER
535
+  #if STATUS_COOLER_WIDTH
518 536
 
519
-  #ifndef STATUS_COOLER_X
520
-    #define STATUS_COOLER_X (LCD_PIXEL_WIDTH - (STATUS_COOLER_BYTEWIDTH + STATUS_FAN_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH) * 8)
521
-  #endif
537
+    #ifndef STATUS_COOLER_X
538
+      #define STATUS_COOLER_X (LCD_PIXEL_WIDTH - (STATUS_COOLER_BYTEWIDTH + STATUS_FAN_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH) * 8)
539
+    #endif
540
+
541
+    #ifndef STATUS_COOLER_HEIGHT
542
+      #define STATUS_COOLER_HEIGHT(S) (sizeof(status_cooler_bmp1) / (STATUS_COOLER_BYTEWIDTH))
543
+    #endif
544
+
545
+    #ifndef STATUS_COOLER_Y
546
+      #define STATUS_COOLER_Y(S) (18 - STATUS_COOLER_HEIGHT(S))
547
+    #endif
522 548
 
523
-  #ifndef STATUS_COOLER_HEIGHT
549
+    #ifndef STATUS_COOLER_TEXT_X
550
+      #define STATUS_COOLER_TEXT_X (STATUS_COOLER_X + 8)
551
+    #endif
552
+
553
+    static_assert(
554
+      sizeof(status_cooler_bmp1) == (STATUS_COOLER_BYTEWIDTH) * (STATUS_COOLER_HEIGHT(0)),
555
+      "Status cooler bitmap (status_cooler_bmp1) dimensions don't match data."
556
+    );
524 557
     #ifdef STATUS_COOLER_ANIM
525
-      #define STATUS_COOLER_HEIGHT(S) ((S) ? sizeof(status_cooler_on_bmp) / (STATUS_COOLER_BYTEWIDTH) : sizeof(status_cooler_bmp) / (STATUS_COOLER_BYTEWIDTH))
526
-    #else
527
-      #define STATUS_COOLER_HEIGHT(S) (sizeof(status_cooler_bmp) / (STATUS_COOLER_BYTEWIDTH))
558
+      static_assert(
559
+        sizeof(status_cooler_bmp2) == (STATUS_COOLER_BYTEWIDTH) * (STATUS_COOLER_HEIGHT(1)),
560
+        "Status cooler bitmap (status_cooler_bmp2) dimensions don't match data."
561
+      );
528 562
     #endif
529
-  #endif
530 563
 
531
-  #ifndef STATUS_COOLER_Y
532
-    #define STATUS_COOLER_Y(S) (18 - STATUS_COOLER_HEIGHT(S))
533 564
   #endif
565
+#endif
534 566
 
535
-  #ifndef STATUS_COOLER_TEXT_X
536
-    #define STATUS_COOLER_TEXT_X (STATUS_COOLER_X + 8)
537
-  #endif
567
+//
568
+//  Flowmeter Bitmap Properties
569
+//
570
+#if HAS_FLOWMETER
571
+  #if STATUS_FLOWMETER_WIDTH
572
+
573
+    #ifndef STATUS_FLOWMETER_X
574
+      #define STATUS_FLOWMETER_X (LCD_PIXEL_WIDTH - (STATUS_FLOWMETER_BYTEWIDTH + STATUS_FAN_BYTEWIDTH + STATUS_CUTTER_BYTEWIDTH + STATUS_COOLER_BYTEWIDTH) * 8)
575
+    #endif
576
+
577
+    #ifndef STATUS_FLOWMETER_HEIGHT
578
+      #define STATUS_FLOWMETER_HEIGHT(S) (sizeof(status_flowmeter_bmp1) / (STATUS_FLOWMETER_BYTEWIDTH))
579
+    #endif
580
+
581
+    #ifndef STATUS_FLOWMETER_Y
582
+      #define STATUS_FLOWMETER_Y(S) (20 - STATUS_FLOWMETER_HEIGHT(S))
583
+    #endif
584
+
585
+    #ifndef STATUS_FLOWMETER_TEXT_X
586
+      #define STATUS_FLOWMETER_TEXT_X (STATUS_FLOWMETER_X + 8)
587
+    #endif
538 588
 
539
-  static_assert(
540
-    sizeof(status_cooler_bmp) == (STATUS_COOLER_BYTEWIDTH) * (STATUS_COOLER_HEIGHT(0)),
541
-    "Status cooler bitmap (status_cooler_bmp) dimensions don't match data."
542
-  );
543
-  #ifdef STATUS_COOLER_ANIM
544 589
     static_assert(
545
-      sizeof(status_cooler_on_bmp) == (STATUS_COOLER_BYTEWIDTH) * (STATUS_COOLER_HEIGHT(1)),
546
-      "Status cooler bitmap (status_cooler_on_bmp) dimensions don't match data."
590
+      sizeof(status_flowmeter_bmp1) == (STATUS_FLOWMETER_BYTEWIDTH) * STATUS_FLOWMETER_HEIGHT(0),
591
+      "Status flowmeter bitmap (status_flowmeter_bmp1) dimensions don't match data."
547 592
     );
593
+    #ifdef STATUS_COOLER_ANIM
594
+      static_assert(
595
+        sizeof(status_flowmeter_bmp2) == (STATUS_FLOWMETER_BYTEWIDTH) * STATUS_FLOWMETER_HEIGHT(1),
596
+        "Status flowmeter bitmap (status_flowmeter_bmp2) dimensions don't match data."
597
+      );
598
+    #endif
548 599
   #endif
549
-
550 600
 #endif
551 601
 
552 602
 //
@@ -639,6 +689,9 @@
639 689
 #if HAS_COOLER
640 690
   #define DO_DRAW_COOLER 1
641 691
 #endif
692
+#if HAS_FLOWMETER
693
+  #define DO_DRAW_FLOWMETER 1
694
+#endif
642 695
 
643 696
 #if HAS_TEMP_CHAMBER && STATUS_CHAMBER_WIDTH && HOTENDS <= 4
644 697
   #define DO_DRAW_CHAMBER 1
@@ -661,6 +714,9 @@
661 714
 #if BOTH(DO_DRAW_COOLER, STATUS_COOLER_ANIM)
662 715
   #define ANIM_COOLER 1
663 716
 #endif
717
+#if BOTH(DO_DRAW_FLOWMETER, STATUS_FLOWMETER_ANIM)
718
+  #define ANIM_FLOWMETER 1
719
+#endif
664 720
 #if ANIM_HOTEND || ANIM_BED || ANIM_CHAMBER || ANIM_CUTTER
665 721
   #define ANIM_HBCC 1
666 722
 #endif

+ 68
- 24
Marlin/src/lcd/dogm/status/cooler.h View File

@@ -24,12 +24,9 @@
24 24
 //
25 25
 // lcd/dogm/status/cooler.h - Status Screen Laser Cooler bitmaps
26 26
 //
27
-
28
-#define STATUS_COOLER_WIDTH 16
29
-
30
-#ifdef STATUS_COOLER_ANIM
31
-
32
-  const unsigned char status_cooler_on_bmp[] PROGMEM = {
27
+#if HAS_COOLER
28
+  #define STATUS_COOLER_WIDTH 16
29
+  const unsigned char status_cooler_bmp2[] PROGMEM = {
33 30
     B00010000,B00001000,
34 31
     B00010010,B01001001,
35 32
     B01010100,B00101010,
@@ -47,24 +44,71 @@
47 44
     B00000010,B10100000,
48 45
     B00000100,B10010000
49 46
   };
47
+  const unsigned char status_cooler_bmp1[] PROGMEM = {
48
+    B00010000,B00001000,
49
+    B00010010,B01001001,
50
+    B01010100,B00101010,
51
+    B00101000,B00010100,
52
+    B11000111,B01100011,
53
+    B00101000,B00010100,
54
+    B01010100,B00101010,
55
+    B10010000,B10001001,
56
+    B00010000,B10000000,
57
+    B00000100,B10010000,
58
+    B00000010,B10100000,
59
+    B00000001,B01000000,
60
+    B00011110,B00111100,
61
+    B00000001,B01000000,
62
+    B00000010,B10100000,
63
+    B00000100,B10010000
64
+  };
65
+#endif
50 66
 
67
+#if HAS_FLOWMETER
68
+  #define STATUS_FLOWMETER_WIDTH 24
69
+  const unsigned char status_flowmeter_bmp2[] PROGMEM = {
70
+    B00000001,B11111000,B00000000,
71
+    B00000110,B00000110,B00000000,
72
+    B00001000,B01100001,B00000000,
73
+    B00010000,B01100000,B10000000,
74
+    B00100000,B01100000,B01000000,
75
+    B00100000,B01100000,B01000000,
76
+    B01000000,B01100000,B00100000,
77
+    B01000000,B01100000,B00100000,
78
+    B01011111,B11111111,B10100000,
79
+    B01011111,B11111111,B10100000,
80
+    B01000000,B01100000,B00100000,
81
+    B01000000,B01100000,B00100000,
82
+    B00100000,B01100000,B01000000,
83
+    B00100000,B01100000,B01000000,
84
+    B00010000,B01100000,B10000000,
85
+    B00001000,B01100001,B00000000,
86
+    B00000110,B00000110,B00000000,
87
+    B00000001,B11111000,B00000000,
88
+    B00000000,B01100000,B00000000,
89
+    B00011111,B11111111,B10000000
90
+  };
91
+  const unsigned char status_flowmeter_bmp1[] PROGMEM = {
92
+    B00000001,B11111000,B00000000,
93
+    B00000110,B00000110,B00000000,
94
+    B00001000,B00000001,B00000000,
95
+    B00010100,B00000010,B10000000,
96
+    B00101110,B00000111,B01000000,
97
+    B00100111,B00001110,B01000000,
98
+    B01000011,B10011100,B00100000,
99
+    B01000001,B11111000,B00100000,
100
+    B01000000,B11110000,B00100000,
101
+    B01000000,B11110000,B00100000,
102
+    B01000001,B11111000,B00100000,
103
+    B01000011,B10011100,B00100000,
104
+    B00100111,B00001110,B01000000,
105
+    B00101110,B00000111,B01000000,
106
+    B00010100,B00000010,B10000000,
107
+    B00001000,B00000001,B00000000,
108
+    B00000110,B00000110,B00000000,
109
+    B00000001,B11111000,B00000000,
110
+    B00000000,B01100000,B00000000,
111
+    B00011111,B11111111,B10000000
112
+  };
51 113
 #endif
52 114
 
53
-const unsigned char status_cooler_bmp[] PROGMEM = {
54
-  B00010000,B00001000,
55
-  B00010010,B01001001,
56
-  B01010100,B00101010,
57
-  B00101000,B00010100,
58
-  B11000111,B01100011,
59
-  B00101000,B00010100,
60
-  B01010100,B00101010,
61
-  B10010000,B10001001,
62
-  B00010000,B10000000,
63
-  B00000100,B10010000,
64
-  B00000010,B10100000,
65
-  B00000001,B01000000,
66
-  B00011110,B00111100,
67
-  B00000001,B01000000,
68
-  B00000010,B10100000,
69
-  B00000100,B10010000
70
-};

+ 49
- 31
Marlin/src/lcd/dogm/status_screen_DOGM.cpp View File

@@ -53,6 +53,10 @@
53 53
   #include "../../feature/spindle_laser.h"
54 54
 #endif
55 55
 
56
+#if HAS_COOLER || HAS_FLOWMETER
57
+  #include "../../feature/cooler.h"
58
+#endif
59
+
56 60
 #if HAS_POWER_MONITOR
57 61
   #include "../../feature/power_monitor.h"
58 62
 #endif
@@ -83,40 +87,34 @@
83 87
 
84 88
 #if ANIM_HBCC
85 89
   enum HeatBits : uint8_t {
86
-    HEATBIT_HOTEND,
87
-    HEATBIT_BED = HOTENDS,
88
-    HEATBIT_CHAMBER,
89
-    HEATBIT_COOLER,
90
-    HEATBIT_CUTTER
90
+    DRAWBIT_HOTEND,
91
+    DRAWBIT_BED = HOTENDS,
92
+    DRAWBIT_CHAMBER,
93
+    DRAWBIT_CUTTER
91 94
   };
92
-  IF<(HEATBIT_CUTTER > 7), uint16_t, uint8_t>::type heat_bits;
95
+  IF<(DRAWBIT_CUTTER > 7), uint16_t, uint8_t>::type draw_bits;
93 96
 #endif
94 97
 
95 98
 #if ANIM_HOTEND
96
-  #define HOTEND_ALT(N) TEST(heat_bits, HEATBIT_HOTEND + N)
99
+  #define HOTEND_ALT(N) TEST(draw_bits, DRAWBIT_HOTEND + N)
97 100
 #else
98 101
   #define HOTEND_ALT(N) false
99 102
 #endif
100 103
 #if ANIM_BED
101
-  #define BED_ALT() TEST(heat_bits, HEATBIT_BED)
104
+  #define BED_ALT() TEST(draw_bits, DRAWBIT_BED)
102 105
 #else
103 106
   #define BED_ALT() false
104 107
 #endif
105 108
 #if ANIM_CHAMBER
106
-  #define CHAMBER_ALT() TEST(heat_bits, HEATBIT_CHAMBER)
109
+  #define CHAMBER_ALT() TEST(draw_bits, DRAWBIT_CHAMBER)
107 110
 #else
108 111
   #define CHAMBER_ALT() false
109 112
 #endif
110 113
 #if ANIM_CUTTER
111
-  #define CUTTER_ALT(N) TEST(heat_bits, HEATBIT_CUTTER)
114
+  #define CUTTER_ALT(N) TEST(draw_bits, DRAWBIT_CUTTER)
112 115
 #else
113 116
   #define CUTTER_ALT() false
114 117
 #endif
115
-#if ANIM_COOLER
116
-  #define COOLER_ALT(N) TEST(heat_bits, HEATBIT_COOLER)
117
-#else
118
-  #define COOLER_ALT() false
119
-#endif
120 118
 
121 119
 #if DO_DRAW_HOTENDS
122 120
   #define MAX_HOTEND_DRAW _MIN(HOTENDS, ((LCD_PIXEL_WIDTH - (STATUS_LOGO_BYTEWIDTH + STATUS_FAN_BYTEWIDTH) * 8) / (STATUS_HEATERS_XSPACE)))
@@ -194,6 +192,15 @@ FORCE_INLINE void _draw_centered_temp(const celsius_t temp, const uint8_t tx, co
194 192
   lcd_put_wchar(LCD_STR_DEGREE[0]);
195 193
 }
196 194
 
195
+#if DO_DRAW_FLOWMETER
196
+  FORCE_INLINE void _draw_centered_flowrate(const float flow, const uint8_t tx, const uint8_t ty) {
197
+    const char *str = ftostr11ns(flow);
198
+    const uint8_t len = str[0] != ' ' ? 3 : str[1] != ' ' ? 2 : 1;
199
+    lcd_put_u8str(tx - len * (INFO_FONT_WIDTH) / 2 + 1, ty, &str[3-len]);
200
+    lcd_put_u8str("L");
201
+  }
202
+#endif
203
+
197 204
 #if DO_DRAW_HOTENDS
198 205
 
199 206
   // Draw hotend bitmap with current and target temperatures
@@ -384,6 +391,13 @@ FORCE_INLINE void _draw_centered_temp(const celsius_t temp, const uint8_t tx, co
384 391
   }
385 392
 #endif
386 393
 
394
+#if DO_DRAW_FLOWMETER
395
+  FORCE_INLINE void _draw_flowmeter_status() {
396
+    if (PAGE_CONTAINS(28 - INFO_FONT_ASCENT, 28 - 1))
397
+      _draw_centered_flowrate(cooler.flowrate, STATUS_FLOWMETER_TEXT_X, 28);
398
+  }
399
+#endif
400
+
387 401
 //
388 402
 // Before homing, blink '123' <-> '???'.
389 403
 // Homed but unknown... '123' <-> '   '.
@@ -451,17 +465,14 @@ void MarlinUI::draw_status_screen() {
451 465
     #if ANIM_HBCC
452 466
       uint8_t new_bits = 0;
453 467
       #if ANIM_HOTEND
454
-        HOTEND_LOOP() if (thermalManager.isHeatingHotend(e)) SBI(new_bits, HEATBIT_HOTEND + e);
468
+        HOTEND_LOOP() if (thermalManager.isHeatingHotend(e)) SBI(new_bits, DRAWBIT_HOTEND + e);
455 469
       #endif
456
-      if (TERN0(ANIM_BED, thermalManager.isHeatingBed())) SBI(new_bits, HEATBIT_BED);
470
+      if (TERN0(ANIM_BED, thermalManager.isHeatingBed())) SBI(new_bits, DRAWBIT_BED);
457 471
       #if DO_DRAW_CHAMBER && HAS_HEATED_CHAMBER
458
-        if (thermalManager.isHeatingChamber()) SBI(new_bits, HEATBIT_CHAMBER);
459
-      #endif
460
-      #if DO_DRAW_COOLER && HAS_COOLER
461
-        if (thermalManager.isLaserCooling()) SBI(new_bits, HEATBIT_COOLER);
472
+        if (thermalManager.isHeatingChamber()) SBI(new_bits, DRAWBIT_CHAMBER);
462 473
       #endif
463
-      if (TERN0(ANIM_CUTTER, cutter.enabled())) SBI(new_bits, HEATBIT_CUTTER);
464
-      heat_bits = new_bits;
474
+      if (TERN0(ANIM_CUTTER, cutter.enabled())) SBI(new_bits, DRAWBIT_CUTTER);
475
+      draw_bits = new_bits;
465 476
     #endif
466 477
 
467 478
     const xyz_pos_t lpos = current_position.asLogical();
@@ -646,17 +657,21 @@ void MarlinUI::draw_status_screen() {
646 657
 
647 658
     // Laser Cooler
648 659
     #if DO_DRAW_COOLER
649
-      #if ANIM_COOLER
650
-        #define COOLER_BITMAP(S) ((S) ? status_cooler_bmp : status_cooler_on_bmp)
651
-      #else
652
-        #define COOLER_BITMAP(S) status_cooler_bmp
653
-      #endif
654
-      const uint8_t coolery = STATUS_COOLER_Y(COOLER_ALT()),
655
-                    coolerh = STATUS_COOLER_HEIGHT(COOLER_ALT());
660
+      const uint8_t coolery = STATUS_COOLER_Y(status_cooler_bmp1),
661
+                    coolerh = STATUS_COOLER_HEIGHT(status_cooler_bmp1);
656 662
       if (PAGE_CONTAINS(coolery, coolery + coolerh - 1))
657
-        u8g.drawBitmapP(STATUS_COOLER_X, coolery, STATUS_COOLER_BYTEWIDTH, coolerh, COOLER_BITMAP(COOLER_ALT()));
663
+        u8g.drawBitmapP(STATUS_COOLER_X, coolery, STATUS_COOLER_BYTEWIDTH, coolerh, blink && cooler.enabled ? status_cooler_bmp2 : status_cooler_bmp1);
658 664
     #endif
659 665
 
666
+    // Laser Cooler Flow Meter
667
+    #if DO_DRAW_FLOWMETER
668
+      const uint8_t flowmetery = STATUS_FLOWMETER_Y(status_flowmeter_bmp1),
669
+                    flowmeterh = STATUS_FLOWMETER_HEIGHT(status_flowmeter_bmp1);
670
+       if (PAGE_CONTAINS(flowmetery, flowmetery + flowmeterh - 1))
671
+        u8g.drawBitmapP(STATUS_FLOWMETER_X, flowmetery, STATUS_FLOWMETER_BYTEWIDTH, flowmeterh, blink && cooler.flowpulses ? status_flowmeter_bmp2 : status_flowmeter_bmp1);
672
+    #endif
673
+
674
+
660 675
     // Heated Bed
661 676
     TERN_(DO_DRAW_BED, _draw_bed_status(blink));
662 677
 
@@ -666,6 +681,9 @@ void MarlinUI::draw_status_screen() {
666 681
     // Cooler
667 682
     TERN_(DO_DRAW_COOLER, _draw_cooler_status());
668 683
 
684
+    // Flowmeter
685
+    TERN_(DO_DRAW_FLOWMETER, _draw_flowmeter_status());
686
+
669 687
     // Fan, if a bitmap was provided
670 688
     #if DO_DRAW_FAN
671 689
       if (PAGE_CONTAINS(STATUS_FAN_TEXT_Y - INFO_FONT_ASCENT, STATUS_FAN_TEXT_Y - 1)) {

+ 2
- 1
Marlin/src/lcd/language/language_en.h View File

@@ -116,10 +116,10 @@ namespace Language_en {
116 116
   PROGMEM Language_Str MSG_LASER_TOGGLE                    = _UxGT("Toggle Laser");
117 117
   PROGMEM Language_Str MSG_LASER_PULSE_MS                  = _UxGT("Test Pulse ms");
118 118
   PROGMEM Language_Str MSG_LASER_FIRE_PULSE                = _UxGT("Fire Pulse");
119
+  PROGMEM Language_Str MSG_FLOWMETER_FAULT                 = _UxGT("Coolant Flow Fault");
119 120
   PROGMEM Language_Str MSG_SPINDLE_TOGGLE                  = _UxGT("Toggle Spindle");
120 121
   PROGMEM Language_Str MSG_SPINDLE_FORWARD                 = _UxGT("Spindle Forward");
121 122
   PROGMEM Language_Str MSG_SPINDLE_REVERSE                 = _UxGT("Spindle Reverse");
122
-
123 123
   PROGMEM Language_Str MSG_SWITCH_PS_ON                    = _UxGT("Switch Power On");
124 124
   PROGMEM Language_Str MSG_SWITCH_PS_OFF                   = _UxGT("Switch Power Off");
125 125
   PROGMEM Language_Str MSG_EXTRUDE                         = _UxGT("Extrude");
@@ -278,6 +278,7 @@ namespace Language_en {
278 278
   PROGMEM Language_Str MSG_CHAMBER                         = _UxGT("Enclosure");
279 279
   PROGMEM Language_Str MSG_COOLER                          = _UxGT("Laser Coolant");
280 280
   PROGMEM Language_Str MSG_COOLER_TOGGLE                   = _UxGT("Toggle Cooler");
281
+  PROGMEM Language_Str MSG_FLOWMETER_SAFETY                = _UxGT("Flow Safety");
281 282
   PROGMEM Language_Str MSG_LASER                           = _UxGT("Laser");
282 283
   PROGMEM Language_Str MSG_FAN_SPEED                       = _UxGT("Fan Speed");
283 284
   PROGMEM Language_Str MSG_FAN_SPEED_N                     = _UxGT("Fan Speed ~");

+ 6
- 0
Marlin/src/lcd/marlinui.cpp View File

@@ -1513,6 +1513,12 @@ void MarlinUI::update() {
1513 1513
     TERN_(HAS_LCD_MENU, return_to_status());
1514 1514
   }
1515 1515
 
1516
+  void MarlinUI::flow_fault() {
1517
+    LCD_ALERTMESSAGEPGM(MSG_FLOWMETER_FAULT);
1518
+    TERN_(HAS_BUZZER, buzz(1000, 440));
1519
+    TERN_(HAS_LCD_MENU, return_to_status());
1520
+  }
1521
+
1516 1522
   #if ANY(PARK_HEAD_ON_PAUSE, SDSUPPORT)
1517 1523
     #include "../gcode/queue.h"
1518 1524
   #endif

+ 5
- 0
Marlin/src/lcd/marlinui.h View File

@@ -43,6 +43,10 @@
43 43
   #define HAS_ENCODER_ACTION 1
44 44
 #endif
45 45
 
46
+#if HAS_STATUS_MESSAGE
47
+  #define START_OF_UTF8_CHAR(C) (((C) & 0xC0u) != 0x80U)
48
+#endif
49
+
46 50
 #if E_MANUAL > 1
47 51
   #define MULTI_MANUAL 1
48 52
 #endif
@@ -311,6 +315,7 @@ public:
311 315
     static void abort_print();
312 316
     static void pause_print();
313 317
     static void resume_print();
318
+    static void flow_fault();
314 319
 
315 320
     #if HAS_WIRED_LCD
316 321
 

+ 11
- 3
Marlin/src/lcd/menu/menu_temperature.cpp View File

@@ -35,7 +35,7 @@
35 35
   #include "../../module/motion.h"
36 36
 #endif
37 37
 
38
-#if HAS_COOLER
38
+#if HAS_COOLER || HAS_FLOWMETER
39 39
   #include "../../feature/cooler.h"
40 40
 #endif
41 41
 
@@ -192,12 +192,20 @@ void menu_temperature() {
192 192
   // Cooler:
193 193
   //
194 194
   #if HAS_COOLER
195
-    editable.state = cooler.is_enabled();
196
-    EDIT_ITEM(bool, MSG_COOLER(TOGGLE), &cooler.state, []{ if (editable.state) cooler.disable(); else cooler.enable(); });
195
+    bool cstate = cooler.enabled;
196
+    EDIT_ITEM(bool, MSG_COOLER_TOGGLE, &cstate, cooler.toggle);
197 197
     EDIT_ITEM_FAST(int3, MSG_COOLER, &thermalManager.temp_cooler.target, COOLER_MIN_TARGET, COOLER_MAX_TARGET, thermalManager.start_watching_cooler);
198 198
   #endif
199 199
 
200 200
   //
201
+  // Flow Meter Safety Shutdown:
202
+  //
203
+  #if ENABLED(FLOWMETER_SAFETY)
204
+    bool fstate = cooler.flowsafety_enabled;
205
+    EDIT_ITEM(bool, MSG_FLOWMETER_SAFETY, &fstate, cooler.flowsafety_toggle);
206
+  #endif
207
+
208
+  //
201 209
   // Fan Speed:
202 210
   //
203 211
   #if HAS_FAN

+ 9
- 0
Marlin/src/libs/numtostr.cpp View File

@@ -177,6 +177,15 @@ const char* i16tostr4signrj(const int16_t i) {
177 177
   return &conv[3];
178 178
 }
179 179
 
180
+// Convert unsigned float to string with 1.1 format
181
+const char* ftostr11ns(const float &f) {
182
+  const long i = UINTFLOAT(f, 1);
183
+  conv[4] = DIGIMOD(i, 10);
184
+  conv[5] = '.';
185
+  conv[6] = DIGIMOD(i, 1);
186
+  return &conv[4];
187
+}
188
+
180 189
 // Convert unsigned float to string with 1.23 format
181 190
 const char* ftostr12ns(const float &f) {
182 191
   const long i = UINTFLOAT(f, 2);

+ 3
- 0
Marlin/src/libs/numtostr.h View File

@@ -61,6 +61,9 @@ const char* i16tostr3left(const int16_t xx);
61 61
 // Convert signed int to rj string with _123, -123, _-12, or __-1 format
62 62
 const char* i16tostr4signrj(const int16_t x);
63 63
 
64
+// Convert unsigned float to string with 1.2 format
65
+const char* ftostr11ns(const float &x);
66
+
64 67
 // Convert unsigned float to string with 1.23 format
65 68
 const char* ftostr12ns(const float &x);
66 69
 

+ 17
- 2
Marlin/src/module/temperature.cpp View File

@@ -35,7 +35,7 @@
35 35
 #include "endstops.h"
36 36
 #include "planner.h"
37 37
 
38
-#if HAS_COOLER
38
+#if HAS_COOLER || HAS_FLOWMETER
39 39
   #include "../feature/cooler.h"
40 40
   #include "../feature/spindle_laser.h"
41 41
 #endif
@@ -52,6 +52,10 @@
52 52
   #include "../lcd/extui/ui_api.h"
53 53
 #endif
54 54
 
55
+#if ENABLED(HOST_PROMPT_SUPPORT)
56
+  #include "../feature/host_actions.h"
57
+#endif
58
+
55 59
 // LIB_MAX31855 can be added to the build_flags in platformio.ini to use a user-defined library
56 60
 #if LIB_USR_MAX31855
57 61
   #include <Adafruit_MAX31855.h>
@@ -1506,7 +1510,7 @@ void Temperature::manage_heater() {
1506 1510
 
1507 1511
     static bool flag_cooler_state; // = false
1508 1512
 
1509
-    if (cooler.is_enabled()) {
1513
+    if (cooler.enabled) {
1510 1514
       flag_cooler_state = true; // used to allow M106 fan control when cooler is disabled
1511 1515
       if (temp_cooler.target == 0) temp_cooler.target = COOLER_MIN_TARGET;
1512 1516
       if (ELAPSED(ms, next_cooler_check_ms)) {
@@ -1542,8 +1546,19 @@ void Temperature::manage_heater() {
1542 1546
     #if ENABLED(THERMAL_PROTECTION_COOLER)
1543 1547
       tr_state_machine[RUNAWAY_IND_COOLER].run(temp_cooler.celsius, temp_cooler.target, H_COOLER, THERMAL_PROTECTION_COOLER_PERIOD, THERMAL_PROTECTION_COOLER_HYSTERESIS);
1544 1548
     #endif
1549
+
1545 1550
   #endif // HAS_COOLER
1546 1551
 
1552
+  #if HAS_FLOWMETER
1553
+    cooler.flowmeter_task(ms);
1554
+    #if ENABLED(FLOWMETER_SAFETY)
1555
+      if (cutter.enabled() && cooler.check_flow_too_low()) {
1556
+        cutter.disable();
1557
+        ui.flow_fault();
1558
+      }
1559
+    #endif
1560
+  #endif
1561
+
1547 1562
   UNUSED(ms);
1548 1563
 }
1549 1564
 

+ 20
- 0
buildroot/tests/mega2560 View File

@@ -168,6 +168,26 @@ exec_test $1 $2 "Azteeg X3 | Mixing Extruder (x5) | Gradient Mix | Greek" "$3"
168 168
 #exec_test $1 $2 "Stuff" "$3"
169 169
 
170 170
 #
171
+# Test Laser features with 12864 LCD
172
+#
173
+restore_configs
174
+opt_set MOTHERBOARD BOARD_RAMPS_14_EFB LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 EXTRUDERS 0 TEMP_SENSOR_1 0
175
+opt_enable REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER SDSUPPORT EEPROM_SETTINGS EEPROM_BOOT_SILENT EEPROM_AUTO_INIT \
176
+           LASER_FEATURE LASER_COOLANT_FLOW_METER
177
+
178
+exec_test $1 $2 "REPRAP MEGA2560 RAMPS | Laser Feature | Cooler | Flowmeter | 12864 LCD " "$3"
179
+
180
+#
181
+# Test Laser features with 44780 LCD
182
+#
183
+restore_configs
184
+opt_set MOTHERBOARD BOARD_RAMPS_14_EFB LCD_LANGUAGE en TEMP_SENSOR_COOLER 1 EXTRUDERS 0 TEMP_SENSOR_1 0
185
+opt_enable REPRAP_DISCOUNT_SMART_CONTROLLER SDSUPPORT EEPROM_SETTINGS EEPROM_BOOT_SILENT EEPROM_AUTO_INIT \
186
+           LASER_FEATURE LASER_COOLANT_FLOW_METER
187
+
188
+exec_test $1 $2 "REPRAP MEGA2560 RAMPS | Laser Feature | Cooler | Flowmeter | 44780 LCD " "$3"
189
+
190
+#
171 191
 # Language files test with REPRAP_DISCOUNT_FULL_GRAPHIC_SMART_CONTROLLER
172 192
 #
173 193
 #restore_configs

+ 2
- 2
platformio.ini View File

@@ -82,6 +82,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
82 82
   -<src/feature/caselight.cpp> -<src/gcode/feature/caselight>
83 83
   -<src/feature/closedloop.cpp>
84 84
   -<src/feature/controllerfan.cpp> -<src/gcode/feature/controllerfan>
85
+  -<src/feature/cooler.cpp>  -<src/gcode/temp/M143_M193.cpp>
85 86
   -<src/feature/dac> -<src/feature/digipot>
86 87
   -<src/feature/direct_stepping.cpp> -<src/gcode/motion/G6.cpp>
87 88
   -<src/feature/e_parser.cpp>
@@ -198,7 +199,6 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
198 199
   -<src/gcode/sd/M32.cpp>
199 200
   -<src/gcode/sd/M808.cpp>
200 201
   -<src/gcode/temp/M104_M109.cpp>
201
-  -<src/gcode/temp/M143_M193.cpp>
202 202
   -<src/gcode/temp/M155.cpp>
203 203
   -<src/gcode/units/G20_G21.cpp>
204 204
   -<src/gcode/units/M149.cpp>
@@ -411,7 +411,7 @@ SDSUPPORT               = src_filter=+<src/sd/cardreader.cpp> +<src/sd/Sd2Card.c
411 411
 HAS_MEDIA_SUBCALLS      = src_filter=+<src/gcode/sd/M32.cpp>
412 412
 GCODE_REPEAT_MARKERS    = src_filter=+<src/feature/repeat.cpp> +<src/gcode/sd/M808.cpp>
413 413
 HAS_EXTRUDERS           = src_filter=+<src/gcode/temp/M104_M109.cpp> +<src/gcode/config/M221.cpp>
414
-HAS_COOLER              = src_filter=-<src/gcode/temp/M143_M193.cpp>
414
+HAS_COOLER              = src_filter=+<src/feature/cooler.cpp> +<src/gcode/temp/M143_M193.cpp>
415 415
 AUTO_REPORT_TEMPERATURES = src_filter=+<src/gcode/temp/M155.cpp>
416 416
 INCH_MODE_SUPPORT       = src_filter=+<src/gcode/units/G20_G21.cpp>
417 417
 TEMPERATURE_UNITS_SUPPORT = src_filter=+<src/gcode/units/M149.cpp>

Loading…
Cancel
Save