123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- /// @file
- #pragma once
- #include "mmu2_protocol_logic.h"
- struct E_Step;
- namespace MMU2 {
- static constexpr uint8_t MAX_RETRIES = 3U;
- /// @@TODO hmmm, 12 bytes... may be we can reduce that
- struct xyz_pos_t {
- float xyz[3];
- xyz_pos_t()=default;
- };
- // general MMU setup for MK3
- enum : uint8_t {
- FILAMENT_UNKNOWN = 0xffU
- };
- struct Version {
- uint8_t major, minor, build;
- };
- /// Top-level interface between Logic and Marlin.
- /// Intentionally named MMU2 to be (almost) a drop-in replacement for the previous implementation.
- /// Most of the public methods share the original naming convention as well.
- class MMU2 {
- public:
- MMU2();
-
- /// Powers ON the MMU, then initializes the UART and protocol logic
- void Start();
-
- /// Stops the protocol logic, closes the UART, powers OFF the MMU
- void Stop();
-
- /// States of a printer with the MMU:
- /// - Active
- /// - Connecting
- /// - Stopped
- ///
- /// When the printer's FW starts, the MMU2 mode is either Stopped or NotResponding (based on user's preference).
- /// When the MMU successfully establishes communication, the state changes to Active.
- enum class xState : uint_fast8_t {
- Active, ///< MMU has been detected, connected, communicates and is ready to be worked with.
- Connecting, ///< MMU is connected but it doesn't communicate (yet). The user wants the MMU, but it is not ready to be worked with.
- 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.
- };
-
- inline xState State() const { return state; }
-
- // @@TODO temporary wrappers to make old gcc survive the code
- inline bool Enabled()const { return State() == xState::Active; }
- /// Different levels of resetting the MMU
- enum ResetForm : uint8_t {
- Software = 0, ///< sends a X0 command into the MMU, the MMU will watchdog-reset itself
- ResetPin = 1, ///< trigger the reset pin of the MMU
- CutThePower = 2 ///< power off and power on (that includes +5V and +24V power lines)
- };
- /// Saved print state on error.
- enum SavedState: uint8_t {
- None = 0, // No state saved.
- ParkExtruder = 1, // The extruder was parked.
- Cooldown = 2, // The extruder was allowed to cool.
- CooldownPending = 4,
- };
- /// Source of operation error
- enum ReportErrorSource: uint8_t {
- ErrorSourcePrinter = 0,
- ErrorSourceMMU = 1,
- };
- /// Perform a reset of the MMU
- /// @param level physical form of the reset
- void Reset(ResetForm level);
-
- /// Power off the MMU (cut the power)
- void PowerOff();
-
- /// Power on the MMU
- void PowerOn();
- /// Read from a MMU register (See gcode M707)
- /// @param address Address of register in hexidecimal
- /// @returns true upon success
- bool ReadRegister(uint8_t address);
- /// Write from a MMU register (See gcode M708)
- /// @param address Address of register in hexidecimal
- /// @param data Data to write to register
- /// @returns true upon success
- bool WriteRegister(uint8_t address, uint16_t data);
- /// The main loop of MMU processing.
- /// Doesn't loop (block) inside, performs just one step of logic state machines.
- /// Also, internally it prevents recursive entries.
- void mmu_loop();
- /// The main MMU command - select a different slot
- /// @param index of the slot to be selected
- /// @returns false if the operation cannot be performed (Stopped)
- bool tool_change(uint8_t index);
-
- /// Handling of special Tx, Tc, T? commands
- bool tool_change(char code, uint8_t slot);
- /// Unload of filament in collaboration with the MMU.
- /// That includes rotating the printer's extruder in order to release filament.
- /// @returns false if the operation cannot be performed (Stopped or cold extruder)
- bool unload();
- /// Load (insert) filament just into the MMU (not into printer's nozzle)
- /// @returns false if the operation cannot be performed (Stopped)
- bool load_filament(uint8_t index);
-
- /// Load (push) filament from the MMU into the printer's nozzle
- /// @returns false if the operation cannot be performed (Stopped or cold extruder)
- bool load_filament_to_nozzle(uint8_t index);
- /// Move MMU's selector aside and push the selected filament forward.
- /// Usable for improving filament's tip or pulling the remaining piece of filament out completely.
- bool eject_filament(uint8_t index, bool recover);
- /// Issue a Cut command into the MMU
- /// Requires unloaded filament from the printer (obviously)
- /// @returns false if the operation cannot be performed (Stopped)
- bool cut_filament(uint8_t index);
- /// Issue a planned request for statistics data from MMU
- void get_statistics();
- /// Issue a Try-Load command
- /// It behaves very similarly like a ToolChange, but it doesn't load the filament
- /// all the way down to the nozzle. The sole purpose of this operation
- /// is to check, that the filament will be ready for printing.
- bool load_to_extruder(uint8_t index);
- /// @returns the active filament slot index (0-4) or 0xff in case of no active tool
- uint8_t get_current_tool() const;
- /// @returns The filament slot index (0 to 4) that will be loaded next, 0xff in case of no active tool change
- uint8_t get_tool_change_tool() const;
- bool set_filament_type(uint8_t index, uint8_t type);
- /// Issue a "button" click into the MMU - to be used from Error screens of the MMU
- /// to select one of the 3 possible options to resolve the issue
- void Button(uint8_t index);
-
- /// Issue an explicit "homing" command into the MMU
- void Home(uint8_t mode);
- /// @returns current state of FINDA (true=filament present, false=filament not present)
- inline bool FindaDetectsFilament()const { return logic.FindaPressed(); }
- inline uint16_t TotalFailStatistics()const { return logic.FailStatistics(); }
- /// @returns Current error code
- inline ErrorCode MMUCurrentErrorCode() const { return logic.Error(); }
- /// @returns the version of the connected MMU FW.
- /// In the future we'll return the trully detected FW version
- Version GetMMUFWVersion()const {
- if( State() == xState::Active ){
- return { logic.MmuFwVersionMajor(), logic.MmuFwVersionMinor(), logic.MmuFwVersionRevision() };
- } else {
- return { 0, 0, 0};
- }
- }
- // Helper variable to monitor knob in MMU error screen in blocking functions e.g. manage_response
- bool is_mmu_error_monitor_active;
- /// Method to read-only mmu_print_saved
- bool MMU_PRINT_SAVED() const { return mmu_print_saved != SavedState::None; }
- /// Automagically "press" a Retry button if we have any retry attempts left
- bool RetryIfPossible(uint16_t ec);
- /// Decrement the retry attempts, if in a retry.
- // Called by the MMU protocol when a sent button is acknowledged.
- void DecrementRetryAttempts();
- private:
- /// Reset the retryAttempts back to the default value
- void ResetRetryAttempts();
- /// Perform software self-reset of the MMU (sends an X0 command)
- void ResetX0();
-
- /// Trigger reset pin of the MMU
- void TriggerResetPin();
-
- /// Perform power cycle of the MMU (cold boot)
- /// Please note this is a blocking operation (sleeps for some time inside while doing the power cycle)
- void PowerCycle();
-
- /// Stop the communication, but keep the MMU powered on (for scenarios with incorrect FW version)
- void StopKeepPowered();
- /// Along with the mmu_loop method, this loops until a response from the MMU is received and acts upon.
- /// In case of an error, it parks the print head and turns off nozzle heating
- void manage_response(const bool move_axes, const bool turn_off_nozzle);
-
- /// Performs one step of the protocol logic state machine
- /// and reports progress and errors if needed to attached ExtUIs.
- /// Updates the global state of MMU (Active/Connecting/Stopped) at runtime, see @ref State
- StepStatus LogicStep();
-
- void filament_ramming();
- void execute_extruder_sequence(const E_Step *sequence, uint8_t steps);
- /// Reports an error into attached ExtUIs
- /// @param ec error code, see ErrorCode
- /// @param res reporter error source, is either Printer (0) or MMU (1)
- void ReportError(ErrorCode ec, uint8_t res);
- /// Reports progress of operations into attached ExtUIs
- /// @param pc progress code, see ProgressCode
- void ReportProgress(ProgressCode pc);
-
- /// Responds to a change of MMU's progress
- /// - plans additional steps, e.g. starts the E-motor after fsensor trigger
- void OnMMUProgressMsg(ProgressCode pc);
- /// Progress code changed - act accordingly
- void OnMMUProgressMsgChanged(ProgressCode pc);
- /// Repeated calls when progress code remains the same
- void OnMMUProgressMsgSame(ProgressCode pc);
-
- /// Save print and park the print head
- void SaveAndPark(bool move_axes, bool turn_off_nozzle);
- /// Resume hotend temperature, if it was cooled. Safe to call if we aren't saved.
- void ResumeHotendTemp();
- /// Resume position, if the extruder was parked. Safe to all if state was not saved.
- void ResumeUnpark();
- /// Check for any button/user input coming from the printer's UI
- void CheckUserInput();
- /// @brief Check whether to trigger a FINDA runout. If triggered this function will call M600 AUTO
- /// if SpoolJoin is enabled, otherwise M600 is called without AUTO which will prompt the user
- /// for the next filament slot to use
- void CheckFINDARunout();
- /// Entry check of all external commands.
- /// It can wait until the MMU becomes ready.
- /// Optionally, it can also emit/display an error screen and the user can decide what to do next.
- /// @returns false if the MMU is not ready to perform the command (for whatever reason)
- bool WaitForMMUReady();
- ProtocolLogic logic; ///< implementation of the protocol logic layer
- uint8_t extruder; ///< currently active slot in the MMU ... somewhat... not sure where to get it from yet
- uint8_t tool_change_extruder; ///< only used for UI purposes
- xyz_pos_t resume_position;
- int16_t resume_hotend_temp;
-
- ProgressCode lastProgressCode = ProgressCode::OK;
- ErrorCode lastErrorCode = ErrorCode::MMU_NOT_RESPONDING;
- Buttons lastButton = Buttons::NoButton;
- StepStatus logicStepLastStatus;
-
- enum xState state;
- uint8_t mmu_print_saved;
- bool loadFilamentStarted;
- bool unloadFilamentStarted;
-
- friend struct LoadingToNozzleRAII;
- /// true in case we are doing the LoadToNozzle operation - that means the filament shall be loaded all the way down to the nozzle
- /// unlike the mid-print ToolChange commands, which only load the first ~30mm and then the G-code takes over.
- bool loadingToNozzle;
-
- uint8_t retryAttempts;
- bool inAutoRetry;
- };
- /// following Marlin's way of doing stuff - one and only instance of MMU implementation in the code base
- /// + avoiding buggy singletons on the AVR platform
- extern MMU2 mmu2;
- } // namespace MMU2
|