mmu2.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. /// @file
  2. #pragma once
  3. #include "mmu2_protocol_logic.h"
  4. struct E_Step;
  5. namespace MMU2 {
  6. static constexpr uint8_t MAX_RETRIES = 3U;
  7. /// @@TODO hmmm, 12 bytes... may be we can reduce that
  8. struct xyz_pos_t {
  9. float xyz[3];
  10. xyz_pos_t()=default;
  11. };
  12. // general MMU setup for MK3
  13. enum : uint8_t {
  14. FILAMENT_UNKNOWN = 0xffU
  15. };
  16. struct Version {
  17. uint8_t major, minor, build;
  18. };
  19. /// Top-level interface between Logic and Marlin.
  20. /// Intentionally named MMU2 to be (almost) a drop-in replacement for the previous implementation.
  21. /// Most of the public methods share the original naming convention as well.
  22. class MMU2 {
  23. public:
  24. MMU2();
  25. /// Powers ON the MMU, then initializes the UART and protocol logic
  26. void Start();
  27. /// Stops the protocol logic, closes the UART, powers OFF the MMU
  28. void Stop();
  29. /// States of a printer with the MMU:
  30. /// - Active
  31. /// - Connecting
  32. /// - Stopped
  33. ///
  34. /// When the printer's FW starts, the MMU2 mode is either Stopped or NotResponding (based on user's preference).
  35. /// When the MMU successfully establishes communication, the state changes to Active.
  36. enum class xState : uint_fast8_t {
  37. Active, ///< MMU has been detected, connected, communicates and is ready to be worked with.
  38. Connecting, ///< MMU is connected but it doesn't communicate (yet). The user wants the MMU, but it is not ready to be worked with.
  39. Stopped ///< The user doesn't want the printer to work with the MMU. The MMU itself is not powered and does not work at all.
  40. };
  41. inline xState State() const { return state; }
  42. // @@TODO temporary wrappers to make old gcc survive the code
  43. inline bool Enabled()const { return State() == xState::Active; }
  44. /// Different levels of resetting the MMU
  45. enum ResetForm : uint8_t {
  46. Software = 0, ///< sends a X0 command into the MMU, the MMU will watchdog-reset itself
  47. ResetPin = 1, ///< trigger the reset pin of the MMU
  48. CutThePower = 2 ///< power off and power on (that includes +5V and +24V power lines)
  49. };
  50. /// Saved print state on error.
  51. enum SavedState: uint8_t {
  52. None = 0, // No state saved.
  53. ParkExtruder = 1, // The extruder was parked.
  54. Cooldown = 2, // The extruder was allowed to cool.
  55. CooldownPending = 4,
  56. };
  57. /// Source of operation error
  58. enum ErrorSource: uint8_t {
  59. ErrorSourcePrinter = 0,
  60. ErrorSourceMMU = 1,
  61. ErrorSourceNone = 0xFF,
  62. };
  63. /// Perform a reset of the MMU
  64. /// @param level physical form of the reset
  65. void Reset(ResetForm level);
  66. /// Power off the MMU (cut the power)
  67. void PowerOff();
  68. /// Power on the MMU
  69. void PowerOn();
  70. /// Read from a MMU register (See gcode M707)
  71. /// @param address Address of register in hexidecimal
  72. /// @returns true upon success
  73. bool ReadRegister(uint8_t address);
  74. /// Write from a MMU register (See gcode M708)
  75. /// @param address Address of register in hexidecimal
  76. /// @param data Data to write to register
  77. /// @returns true upon success
  78. bool WriteRegister(uint8_t address, uint16_t data);
  79. /// The main loop of MMU processing.
  80. /// Doesn't loop (block) inside, performs just one step of logic state machines.
  81. /// Also, internally it prevents recursive entries.
  82. void mmu_loop();
  83. /// The main MMU command - select a different slot
  84. /// @param slot of the slot to be selected
  85. /// @returns false if the operation cannot be performed (Stopped)
  86. bool tool_change(uint8_t slot);
  87. /// Handling of special Tx, Tc, T? commands
  88. bool tool_change(char code, uint8_t slot);
  89. /// Unload of filament in collaboration with the MMU.
  90. /// That includes rotating the printer's extruder in order to release filament.
  91. /// @returns false if the operation cannot be performed (Stopped or cold extruder)
  92. bool unload();
  93. /// Load (insert) filament just into the MMU (not into printer's nozzle)
  94. /// @returns false if the operation cannot be performed (Stopped)
  95. bool load_filament(uint8_t slot);
  96. /// Load (push) filament from the MMU into the printer's nozzle
  97. /// @returns false if the operation cannot be performed (Stopped or cold extruder)
  98. bool load_filament_to_nozzle(uint8_t slot);
  99. /// Move MMU's selector aside and push the selected filament forward.
  100. /// Usable for improving filament's tip or pulling the remaining piece of filament out completely.
  101. bool eject_filament(uint8_t slot, bool recover);
  102. /// Issue a Cut command into the MMU
  103. /// Requires unloaded filament from the printer (obviously)
  104. /// @returns false if the operation cannot be performed (Stopped)
  105. bool cut_filament(uint8_t slot);
  106. /// Issue a planned request for statistics data from MMU
  107. void get_statistics();
  108. /// Issue a Try-Load command
  109. /// It behaves very similarly like a ToolChange, but it doesn't load the filament
  110. /// all the way down to the nozzle. The sole purpose of this operation
  111. /// is to check, that the filament will be ready for printing.
  112. /// @param slot index of slot to be tested
  113. /// @returns true
  114. bool loading_test(uint8_t slot);
  115. /// @returns the active filament slot index (0-4) or 0xff in case of no active tool
  116. uint8_t get_current_tool() const;
  117. /// @returns The filament slot index (0 to 4) that will be loaded next, 0xff in case of no active tool change
  118. uint8_t get_tool_change_tool() const;
  119. bool set_filament_type(uint8_t slot, uint8_t type);
  120. /// Issue a "button" click into the MMU - to be used from Error screens of the MMU
  121. /// to select one of the 3 possible options to resolve the issue
  122. void Button(uint8_t index);
  123. /// Issue an explicit "homing" command into the MMU
  124. void Home(uint8_t mode);
  125. /// @returns current state of FINDA (true=filament present, false=filament not present)
  126. inline bool FindaDetectsFilament()const { return logic.FindaPressed(); }
  127. inline uint16_t TotalFailStatistics()const { return logic.FailStatistics(); }
  128. /// @returns Current error code
  129. inline ErrorCode MMUCurrentErrorCode() const { return logic.Error(); }
  130. /// @returns Last error source
  131. inline ErrorSource MMULastErrorSource() const { return lastErrorSource; }
  132. /// @returns the version of the connected MMU FW.
  133. /// In the future we'll return the trully detected FW version
  134. Version GetMMUFWVersion()const {
  135. if( State() == xState::Active ){
  136. return { logic.MmuFwVersionMajor(), logic.MmuFwVersionMinor(), logic.MmuFwVersionRevision() };
  137. } else {
  138. return { 0, 0, 0};
  139. }
  140. }
  141. // Helper variable to monitor knob in MMU error screen in blocking functions e.g. manage_response
  142. bool is_mmu_error_monitor_active;
  143. /// Method to read-only mmu_print_saved
  144. inline bool MMU_PRINT_SAVED() const { return mmu_print_saved != SavedState::None; }
  145. /// Automagically "press" a Retry button if we have any retry attempts left
  146. /// @param ec ErrorCode enum value
  147. /// @returns true if auto-retry is ongoing, false when retry is unavailable or retry attempts are all used up
  148. bool RetryIfPossible(uint16_t ec);
  149. /// Decrement the retry attempts, if in a retry.
  150. // Called by the MMU protocol when a sent button is acknowledged.
  151. void DecrementRetryAttempts();
  152. /// @return count for toolchange in current print
  153. inline uint16_t ToolChangeCounter() const { return toolchange_counter; };
  154. /// Set toolchange counter to zero
  155. inline void ClearToolChangeCounter() { toolchange_counter = 0; };
  156. inline uint16_t TMCFailures()const { return tmcFailures; }
  157. inline void IncrementTMCFailures() { ++tmcFailures; }
  158. inline void ClearTMCFailures() { tmcFailures = 0; }
  159. private:
  160. /// Reset the retryAttempts back to the default value
  161. void ResetRetryAttempts();
  162. /// Perform software self-reset of the MMU (sends an X0 command)
  163. void ResetX0();
  164. /// Trigger reset pin of the MMU
  165. void TriggerResetPin();
  166. /// Perform power cycle of the MMU (cold boot)
  167. /// Please note this is a blocking operation (sleeps for some time inside while doing the power cycle)
  168. void PowerCycle();
  169. /// Stop the communication, but keep the MMU powered on (for scenarios with incorrect FW version)
  170. void StopKeepPowered();
  171. /// Along with the mmu_loop method, this loops until a response from the MMU is received and acts upon.
  172. /// In case of an error, it parks the print head and turns off nozzle heating
  173. /// @returns false if the command could not have been completed (MMU interrupted)
  174. [[nodiscard]] bool manage_response(const bool move_axes, const bool turn_off_nozzle);
  175. /// The inner private implementation of mmu_loop()
  176. /// which is NOT (!!!) recursion-guarded. Use caution - but we do need it during waiting for hotend resume to keep comms alive!
  177. /// @param reportErrors true if Errors should raise MMU Error screen, false otherwise
  178. void mmu_loop_inner(bool reportErrors);
  179. /// Performs one step of the protocol logic state machine
  180. /// and reports progress and errors if needed to attached ExtUIs.
  181. /// Updates the global state of MMU (Active/Connecting/Stopped) at runtime, see @ref State
  182. /// @param reportErrors true if Errors should raise MMU Error screen, false otherwise
  183. StepStatus LogicStep(bool reportErrors);
  184. void filament_ramming();
  185. void execute_extruder_sequence(const E_Step *sequence, uint8_t steps);
  186. /// Reports an error into attached ExtUIs
  187. /// @param ec error code, see ErrorCode
  188. /// @param res reporter error source, is either Printer (0) or MMU (1)
  189. void ReportError(ErrorCode ec, ErrorSource res);
  190. /// Reports progress of operations into attached ExtUIs
  191. /// @param pc progress code, see ProgressCode
  192. void ReportProgress(ProgressCode pc);
  193. /// Responds to a change of MMU's progress
  194. /// - plans additional steps, e.g. starts the E-motor after fsensor trigger
  195. void OnMMUProgressMsg(ProgressCode pc);
  196. /// Progress code changed - act accordingly
  197. void OnMMUProgressMsgChanged(ProgressCode pc);
  198. /// Repeated calls when progress code remains the same
  199. void OnMMUProgressMsgSame(ProgressCode pc);
  200. /// @brief Save hotend temperature and set flag to cooldown hotend after 60 minutes
  201. /// @param turn_off_nozzle if true, the hotend temperature will be set to 0degC after 60 minutes
  202. void SaveHotendTemp(bool turn_off_nozzle);
  203. /// Save print and park the print head
  204. void SaveAndPark(bool move_axes);
  205. /// Resume hotend temperature, if it was cooled. Safe to call if we aren't saved.
  206. void ResumeHotendTemp();
  207. /// Resume position, if the extruder was parked. Safe to all if state was not saved.
  208. void ResumeUnpark();
  209. /// Check for any button/user input coming from the printer's UI
  210. void CheckUserInput();
  211. /// @brief Check whether to trigger a FINDA runout. If triggered this function will call M600 AUTO
  212. /// if SpoolJoin is enabled, otherwise M600 is called without AUTO which will prompt the user
  213. /// for the next filament slot to use
  214. void CheckFINDARunout();
  215. /// Entry check of all external commands.
  216. /// It can wait until the MMU becomes ready.
  217. /// Optionally, it can also emit/display an error screen and the user can decide what to do next.
  218. /// @returns false if the MMU is not ready to perform the command (for whatever reason)
  219. bool WaitForMMUReady();
  220. /// After MMU completes a tool-change command
  221. /// the printer will push the filament by a constant distance. If the Fsensor untriggers
  222. /// at any moment the test fails. Else the test passes, and the E-motor retracts the
  223. /// filament back to its original position.
  224. /// @returns false if test fails, true otherwise
  225. bool VerifyFilamentEnteredPTFE();
  226. /// Common processing of pushing filament into the extruder - shared by tool_change, load_to_nozzle and probably others
  227. void ToolChangeCommon(uint8_t slot);
  228. void HelpUnloadToFinda();
  229. ProtocolLogic logic; ///< implementation of the protocol logic layer
  230. uint8_t extruder; ///< currently active slot in the MMU ... somewhat... not sure where to get it from yet
  231. uint8_t tool_change_extruder; ///< only used for UI purposes
  232. xyz_pos_t resume_position;
  233. int16_t resume_hotend_temp;
  234. ProgressCode lastProgressCode = ProgressCode::OK;
  235. ErrorCode lastErrorCode = ErrorCode::MMU_NOT_RESPONDING;
  236. ErrorSource lastErrorSource = ErrorSource::ErrorSourceNone;
  237. Buttons lastButton = Buttons::NoButton;
  238. StepStatus logicStepLastStatus;
  239. enum xState state;
  240. uint8_t mmu_print_saved;
  241. bool loadFilamentStarted;
  242. bool unloadFilamentStarted;
  243. friend struct LoadingToNozzleRAII;
  244. /// true in case we are doing the LoadToNozzle operation - that means the filament shall be loaded all the way down to the nozzle
  245. /// unlike the mid-print ToolChange commands, which only load the first ~30mm and then the G-code takes over.
  246. bool loadingToNozzle;
  247. bool inAutoRetry;
  248. uint8_t retryAttempts;
  249. uint16_t toolchange_counter;
  250. uint16_t tmcFailures;
  251. };
  252. /// following Marlin's way of doing stuff - one and only instance of MMU implementation in the code base
  253. /// + avoiding buggy singletons on the AVR platform
  254. extern MMU2 mmu2;
  255. } // namespace MMU2