From b8aeaa3bf49b856aead82bd96e8b3272638e143a Mon Sep 17 00:00:00 2001
From: Mike La Spina <mike.laspina@shaw.ca>
Date: Thu, 22 Apr 2021 18:19:41 -0500
Subject: [PATCH] M10-M11 Air Evacuation for Spindle/Laser (#21668)

Co-authored-by: Scott Lahteine <thinkyhead@users.noreply.github.com>
---
 Marlin/Configuration_adv.h                 |  6 +++
 Marlin/src/feature/spindle_laser.cpp       | 14 +++++++
 Marlin/src/feature/spindle_laser.h         |  9 +++++
 Marlin/src/gcode/control/M10-M11.cpp       | 47 ++++++++++++++++++++++
 Marlin/src/gcode/gcode.cpp                 |  4 ++
 Marlin/src/gcode/gcode.h                   |  6 +++
 Marlin/src/lcd/language/language_en.h      |  2 +
 Marlin/src/lcd/menu/menu_spindle_laser.cpp |  5 +++
 ini/features.ini                           |  1 +
 platformio.ini                             |  1 +
 10 files changed, 95 insertions(+)
 create mode 100644 Marlin/src/gcode/control/M10-M11.cpp

diff --git a/Marlin/Configuration_adv.h b/Marlin/Configuration_adv.h
index e1124edf50b..ac9b49fe4d6 100644
--- a/Marlin/Configuration_adv.h
+++ b/Marlin/Configuration_adv.h
@@ -3160,6 +3160,12 @@
 
   #define SPINDLE_LASER_FREQUENCY       2500   // (Hz) Spindle/laser frequency (only on supported HALs: AVR and LPC)
 
+  //#define AIR_EVACUATION                     // Cutter Vacuum / Laser Blower motor control with G-codes M10-M11
+  #if ENABLED(AIR_EVACUATION)
+    #define AIR_EVACUATION_ACTIVE       LOW    // Set to "HIGH" if the on/off function is active HIGH
+    #define AIR_EVACUATION_PIN          42     // Override the default Cutter Vacuum or Laser Blower pin
+  #endif
+
   //#define SPINDLE_SERVO         // A servo converting an angle to spindle power
   #ifdef SPINDLE_SERVO
     #define SPINDLE_SERVO_NR   0  // Index of servo used for spindle control
diff --git a/Marlin/src/feature/spindle_laser.cpp b/Marlin/src/feature/spindle_laser.cpp
index 66c04a001c0..78fa75cac66 100644
--- a/Marlin/src/feature/spindle_laser.cpp
+++ b/Marlin/src/feature/spindle_laser.cpp
@@ -68,6 +68,9 @@ void SpindleLaser::init() {
     set_pwm_frequency(pin_t(SPINDLE_LASER_PWM_PIN), SPINDLE_LASER_FREQUENCY);
     TERN_(MARLIN_DEV_MODE, frequency = SPINDLE_LASER_FREQUENCY);
   #endif
+  #if ENABLED(AIR_EVACUATION)
+    OUT_WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE);            // Init Vacuum/Blower OFF
+  #endif
 }
 
 #if ENABLED(SPINDLE_LASER_PWM)
@@ -135,4 +138,15 @@ void SpindleLaser::apply_power(const uint8_t opwr) {
   }
 #endif
 
+#if ENABLED(AIR_EVACUATION)
+
+  // Enable / disable Cutter Vacuum or Laser Blower motor
+  void SpindleLaser::air_evac_enable()  { WRITE(AIR_EVACUATION_PIN,  AIR_EVACUATION_ACTIVE); } // Turn ON
+
+  void SpindleLaser::air_evac_disable() { WRITE(AIR_EVACUATION_PIN, !AIR_EVACUATION_ACTIVE); } // Turn OFF
+
+  void SpindleLaser::air_evac_toggle()  { TOGGLE(AIR_EVACUATION_PIN); } // Toggle state
+
+#endif
+
 #endif // HAS_CUTTER
diff --git a/Marlin/src/feature/spindle_laser.h b/Marlin/src/feature/spindle_laser.h
index 88cf372a022..c3454d0b3c3 100644
--- a/Marlin/src/feature/spindle_laser.h
+++ b/Marlin/src/feature/spindle_laser.h
@@ -212,6 +212,15 @@ public:
     static bool is_reverse() { return false; }
   #endif
 
+  #if ENABLED(AIR_EVACUATION)
+    static void air_evac_enable();         // Turn On Cutter Vacuum or Laser Blower motor
+    static void air_evac_disable();        // Turn Off Cutter Vacuum or Laser Blower motor
+    static void air_evac_toggle();         // Toggle Cutter Vacuum or Laser Blower motor
+    static inline bool air_evac_state() {  // Get current state
+      return (READ(AIR_EVACUATION_PIN) == AIR_EVACUATION_ACTIVE);
+    }
+  #endif
+
   static inline void disable() { isReady = false; set_enabled(false); }
 
   #if HAS_LCD_MENU
diff --git a/Marlin/src/gcode/control/M10-M11.cpp b/Marlin/src/gcode/control/M10-M11.cpp
new file mode 100644
index 00000000000..26f67e6cb60
--- /dev/null
+++ b/Marlin/src/gcode/control/M10-M11.cpp
@@ -0,0 +1,47 @@
+/**
+ * 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 <https://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "../../inc/MarlinConfig.h"
+
+#if ENABLED(AIR_EVACUATION)
+
+#include "../gcode.h"
+#include "../../module/planner.h"
+#include "../../feature/spindle_laser.h"
+
+/**
+ * M10: Vacuum or Blower On
+ */
+void GcodeSuite::M10() {
+  planner.synchronize();      // Wait for move to arrive (TODO: asynchronous)
+  cutter.air_evac_enable();   // Turn on Vacuum or Blower motor
+}
+
+/**
+ * M11: Vacuum or Blower OFF
+ */
+void GcodeSuite::M11() {
+  planner.synchronize();      // Wait for move to arrive (TODO: asynchronous)
+  cutter.air_evac_disable();  // Turn off Vacuum or Blower motor
+}
+
+#endif // AIR_EVACUATION
diff --git a/Marlin/src/gcode/gcode.cpp b/Marlin/src/gcode/gcode.cpp
index efea0876027..bf26fe5d894 100644
--- a/Marlin/src/gcode/gcode.cpp
+++ b/Marlin/src/gcode/gcode.cpp
@@ -432,6 +432,10 @@ void GcodeSuite::process_parsed_command(const bool no_ok/*=false*/) {
         case 3: M3_M4(false); break;                              // M3: Turn ON Laser | Spindle (clockwise), set Power | Speed
         case 4: M3_M4(true ); break;                              // M4: Turn ON Laser | Spindle (counter-clockwise), set Power | Speed
         case 5: M5(); break;                                      // M5: Turn OFF Laser | Spindle
+        #if ENABLED(AIR_EVACUATION)
+          case 10: M10(); break;                                  // M10: Vacuum or Blower motor ON
+          case 11: M11(); break;                                  // M11: Vacuum or Blower motor OFF
+        #endif
       #endif
 
       #if ENABLED(COOLANT_CONTROL)
diff --git a/Marlin/src/gcode/gcode.h b/Marlin/src/gcode/gcode.h
index 977fc8bfd80..2904d30366a 100644
--- a/Marlin/src/gcode/gcode.h
+++ b/Marlin/src/gcode/gcode.h
@@ -86,6 +86,8 @@
  * M7   - Turn mist coolant ON. (Requires COOLANT_CONTROL)
  * M8   - Turn flood coolant ON. (Requires COOLANT_CONTROL)
  * M9   - Turn coolant OFF. (Requires COOLANT_CONTROL)
+ * M10  - Turn Vacuum or Blower motor ON (Requires AIR_EVACUATION)
+ * M11  - Turn Vacuum or Blower motor OFF (Requires AIR_EVACUATION)
  * M12  - Set up closed loop control system. (Requires EXTERNAL_CLOSED_LOOP_CONTROLLER)
  * M16  - Expected printer check. (Requires EXPECTED_PRINTER_CHECK)
  * M17  - Enable/Power all stepper motors
@@ -548,6 +550,10 @@ private:
   #if HAS_CUTTER
     static void M3_M4(const bool is_M4);
     static void M5();
+    #if ENABLED(AIR_EVACUATION)
+      static void M10();
+      static void M11();
+    #endif
   #endif
 
   #if ENABLED(COOLANT_CONTROL)
diff --git a/Marlin/src/lcd/language/language_en.h b/Marlin/src/lcd/language/language_en.h
index 81450d73854..c911120766b 100644
--- a/Marlin/src/lcd/language/language_en.h
+++ b/Marlin/src/lcd/language/language_en.h
@@ -114,10 +114,12 @@ namespace Language_en {
   PROGMEM Language_Str MSG_LASER_POWER                     = _UxGT("Laser Power");
   PROGMEM Language_Str MSG_SPINDLE_POWER                   = _UxGT("Spindle Pwr");
   PROGMEM Language_Str MSG_LASER_TOGGLE                    = _UxGT("Toggle Laser");
+  PROGMEM Language_Str MSG_LASER_EVAC_TOGGLE               = _UxGT("Toggle Blower");
   PROGMEM Language_Str MSG_LASER_PULSE_MS                  = _UxGT("Test Pulse ms");
   PROGMEM Language_Str MSG_LASER_FIRE_PULSE                = _UxGT("Fire Pulse");
   PROGMEM Language_Str MSG_FLOWMETER_FAULT                 = _UxGT("Coolant Flow Fault");
   PROGMEM Language_Str MSG_SPINDLE_TOGGLE                  = _UxGT("Toggle Spindle");
+  PROGMEM Language_Str MSG_SPINDLE_EVAC_TOGGLE             = _UxGT("Toggle Vacuum");
   PROGMEM Language_Str MSG_SPINDLE_FORWARD                 = _UxGT("Spindle Forward");
   PROGMEM Language_Str MSG_SPINDLE_REVERSE                 = _UxGT("Spindle Reverse");
   PROGMEM Language_Str MSG_SWITCH_PS_ON                    = _UxGT("Switch Power On");
diff --git a/Marlin/src/lcd/menu/menu_spindle_laser.cpp b/Marlin/src/lcd/menu/menu_spindle_laser.cpp
index 93ef224e6f6..f0e702e2ddc 100644
--- a/Marlin/src/lcd/menu/menu_spindle_laser.cpp
+++ b/Marlin/src/lcd/menu/menu_spindle_laser.cpp
@@ -51,6 +51,11 @@
     editable.state = is_enabled;
     EDIT_ITEM(bool, MSG_CUTTER(TOGGLE), &is_enabled, []{ if (editable.state) cutter.disable(); else cutter.enable_same_dir(); });
 
+    #if ENABLED(AIR_EVACUATION)
+      bool evac_state = cutter.air_evac_state();
+      EDIT_ITEM(bool, MSG_CUTTER(EVAC_TOGGLE), &evac_state, cutter.air_evac_toggle);
+    #endif
+
     #if ENABLED(SPINDLE_CHANGE_DIR)
       if (!is_enabled) {
         editable.state = is_rev;
diff --git a/ini/features.ini b/ini/features.ini
index 864befa7a80..3121ef72416 100644
--- a/ini/features.ini
+++ b/ini/features.ini
@@ -164,6 +164,7 @@ SD_ABORT_ON_ENDSTOP_HIT = src_filter=+<src/gcode/config/M540.cpp>
 BAUD_RATE_GCODE         = src_filter=+<src/gcode/config/M575.cpp>
 HAS_SMART_EFF_MOD       = src_filter=+<src/gcode/config/M672.cpp>
 COOLANT_CONTROL         = src_filter=+<src/gcode/control/M7-M9.cpp>
+AIR_EVACUATION          = src_filter=+<src/gcode/control/M10-M11.cpp>
 HAS_SOFTWARE_ENDSTOPS   = src_filter=+<src/gcode/control/M211.cpp>
 HAS_DUPLICATION_MODE    = src_filter=+<src/gcode/control/M605.cpp>
 LIN_ADVANCE             = src_filter=+<src/gcode/feature/advance>
diff --git a/platformio.ini b/platformio.ini
index 8a747404e30..75e7f6a58b8 100644
--- a/platformio.ini
+++ b/platformio.ini
@@ -155,6 +155,7 @@ default_src_filter = +<src/*> -<src/config> -<src/HAL> +<src/HAL/shared>
   -<src/gcode/calibrate/M425.cpp>
   -<src/gcode/calibrate/M666.cpp>
   -<src/gcode/calibrate/M852.cpp>
+  -<src/gcode/control/M10-M11.cpp>
   -<src/gcode/control/M42.cpp> -<src/gcode/control/M226.cpp>
   -<src/gcode/config/M43.cpp>
   -<src/gcode/config/M217.cpp>