From 750bc6420296bc0b7776cba986512b2257a5562d Mon Sep 17 00:00:00 2001 From: InsanityAutomation Date: Sat, 5 Dec 2020 15:50:07 -0500 Subject: [PATCH 01/12] Add initial support for probes requiring Tare and Enable pins --- Marlin/Configuration.h | 22 ++++++++++++++++++++++ Marlin/src/module/endstops.cpp | 21 +++++++++++++++++++-- Marlin/src/module/motion.cpp | 4 ++++ Marlin/src/module/probe.cpp | 34 +++++++++++++++++++++++++++++++++- Marlin/src/module/probe.h | 4 ++++ 5 files changed, 82 insertions(+), 3 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index ebc3311d9e..af8fd4f0e2 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -996,6 +996,28 @@ // Feedrate (mm/min) for the "accurate" probe of each point #define Z_PROBE_SPEED_SLOW (Z_PROBE_SPEED_FAST / 2) +// For probes that require an explicit enable input such as feedback an arm is deployed or an opto switch reporting in-range +//#define PROBE_NEEDS_ENABLE +#if ENABLED(PROBE_NEEDS_ENABLE) + #define PROBE_NEEDS_ENABLE_STATE LOW +#endif +//#define PROBE_ENABLE_PIN PC6 // Override default probe enable pin + +// Probe requires Tare - Usefull for Strain guage or Piezo type probes which may see force from cabling or bowden tubes following moves +//#define PROBE_NEEDS_TARE +#if ENABLED(PROBE_NEEDS_TARE) + #define PROBE_TARE_TIME 200 // Time to hold tare pin + #define PROBE_TARE_DELAY 200 // Dwell following tare before continuing + #define PROBE_TARE_STATE HIGH // State to write pin for tare + //#define PROBE_TARE_PIN PA5 // Override default Tare pin + #if ENABLED(PROBE_NEEDS_ENABLE) + // Assume probe enable will come on in a Z window rather than detecting probe deployment. + // Useful when TARE can be assured outside enable range to avoid potential crash situations. + // Prevents tare unless enable switch is off + //#define PROBE_ENABLE_WINDOW + #endif +#endif + /** * Multiple Probing * diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index ef0b92a7ee..feabaaae0e 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -280,6 +280,14 @@ void Endstops::init() { #endif #endif + #if PIN_EXISTS(PROBE_ENABLE_PIN) + SET_INPUT(PROBE_ENABLE_PIN) + #endif + + #if ENABLED(PROBE_NEEDS_TARE) + probe.tare_z_probe(); + #endif + TERN_(ENDSTOP_INTERRUPTS_FEATURE, setup_endstop_interrupts()); // Enable endstops @@ -582,7 +590,7 @@ void Endstops::update() { #endif #endif - #if HAS_Z_MIN && !Z_SPI_SENSORLESS + #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_NEEDS_ENABLE) UPDATE_ENDSTOP_BIT(Z, MIN); #if ENABLED(Z_MULTI_ENDSTOPS) #if HAS_Z2_MIN @@ -608,10 +616,19 @@ void Endstops::update() { #endif // When closing the gap check the enabled probe - #if HAS_CUSTOM_PROBE_PIN + #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_NEEDS_ENABLE) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); #endif + #if ENABLED(PROBE_NEEDS_ENABLE) + #if HAS_CUSTOM_PROBE_PIN + UPDATE_ENDSTOP_BIT(Z, MIN); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)))); + #else + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)))); + #endif + #endif + #if HAS_Z_MAX && !Z_SPI_SENSORLESS // Check both Z dual endstops #if ENABLED(Z_MULTI_ENDSTOPS) diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 7b4c89e759..3039f07f8c 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1603,6 +1603,10 @@ void homeaxis(const AxisEnum axis) { if (axis == Z_AXIS && bltouch.deploy()) return; // The initial DEPLOY #endif + #if ENABLED(PROBE_NEEDS_TARE) + if (probe.tare_z_probe()) return; + #endif + #if DISABLED(DELTA) && defined(SENSORLESS_BACKOFF_MM) const xy_float_t backoff = SENSORLESS_BACKOFF_MM; if (((ENABLED(X_SENSORLESS) && axis == X_AXIS) || (ENABLED(Y_SENSORLESS) && axis == Y_AXIS)) && backoff[axis]) diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index 400206f83a..dd4d5fe55e 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -479,6 +479,26 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { return !probe_triggered; } +#if ENABLED(PROBE_NEEDS_TARE) +bool Probe::tare_z_probe() { + #if ENABLED(PROBE_ENABLE_WINDOW) + if ((READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)) { + SERIAL_ECHOLN("Cannot tare probe, already Enabled"); + return true; + } + #endif + + SERIAL_ECHOLN("Taring the probe"); + WRITE(PROBE_TARE_PIN, PROBE_TARE_STATE); + delay(PROBE_TARE_TIME); + WRITE(PROBE_TARE_PIN, !PROBE_TARE_STATE); + delay(PROBE_TARE_DELAY); + + endstops.hit_on_purpose(); + return false; +} +#endif + /** * @brief Probe at the current XY (possibly more than once) to find the bed Z. * @@ -492,6 +512,11 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { // Do a first probe at the fast speed + + #if ENABLED(PROBE_NEEDS_TARE) + if(tare_z_probe()) return true; + #endif + const bool probe_fail = probe_down_to_z(z_probe_low_point, fr_mm_s), // No probe trigger? early_fail = (scheck && current_position.z > -offset.z + clearance); // Probe triggered too high? #if ENABLED(DEBUG_LEVELING_FEATURE) @@ -505,7 +530,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #else UNUSED(plbl); #endif - return probe_fail || early_fail; + return (int)(probe_fail || early_fail); }; // Stop the probe before it goes too low to prevent damage. @@ -516,6 +541,10 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #if TOTAL_PROBING == 2 // Do a first probe at the fast speed + #if ENABLED(PROBE_NEEDS_TARE) + if(tare_z_probe()) return true; + #endif + if (try_to_probe(PSTR("FAST"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_FAST), sanity_check, Z_CLEARANCE_BETWEEN_PROBES) ) return NAN; @@ -554,6 +583,9 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #endif { // Probe downward slowly to find the bed + #if ENABLED(PROBE_NEEDS_TARE) + if(tare_z_probe()) return true; + #endif if (try_to_probe(PSTR("SLOW"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW), sanity_check, Z_CLEARANCE_MULTI_PROBE) ) return NAN; diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index e5ad892e37..adaf61fd61 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -206,6 +206,10 @@ public: static void set_probing_paused(const bool p); #endif + #if ENABLED(PROBE_NEEDS_TARE) + static bool tare_z_probe(); + #endif + private: static bool probe_down_to_z(const float z, const feedRate_t fr_mm_s); static void do_z_raise(const float z_raise); From f583d933ebe96d3d6048fade2ac15c9962ec254d Mon Sep 17 00:00:00 2001 From: InsanityAutomation Date: Sat, 5 Dec 2020 17:25:28 -0500 Subject: [PATCH 02/12] Tweaks following PR thread --- Marlin/Configuration.h | 16 ++++++++-------- Marlin/src/module/endstops.cpp | 12 ++++++------ Marlin/src/module/motion.cpp | 5 +++-- Marlin/src/module/probe.cpp | 18 +++++++++--------- Marlin/src/module/probe.h | 2 +- 5 files changed, 27 insertions(+), 26 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index af8fd4f0e2..96264ee5a1 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -997,24 +997,24 @@ #define Z_PROBE_SPEED_SLOW (Z_PROBE_SPEED_FAST / 2) // For probes that require an explicit enable input such as feedback an arm is deployed or an opto switch reporting in-range -//#define PROBE_NEEDS_ENABLE -#if ENABLED(PROBE_NEEDS_ENABLE) - #define PROBE_NEEDS_ENABLE_STATE LOW +//#define PROBE_ENABLED_INPUT +#if ENABLED(PROBE_ENABLED_INPUT) + #define PROBE_ENABLED_INPUT_STATE LOW + //#define PROBE_ENABLE_PIN PC6 // Override default probe enable pin #endif -//#define PROBE_ENABLE_PIN PC6 // Override default probe enable pin // Probe requires Tare - Usefull for Strain guage or Piezo type probes which may see force from cabling or bowden tubes following moves -//#define PROBE_NEEDS_TARE -#if ENABLED(PROBE_NEEDS_TARE) +//#define PROBE_CAN_TARE +#if ENABLED(PROBE_CAN_TARE) #define PROBE_TARE_TIME 200 // Time to hold tare pin #define PROBE_TARE_DELAY 200 // Dwell following tare before continuing #define PROBE_TARE_STATE HIGH // State to write pin for tare //#define PROBE_TARE_PIN PA5 // Override default Tare pin - #if ENABLED(PROBE_NEEDS_ENABLE) + #if ENABLED(PROBE_ENABLED_INPUT) // Assume probe enable will come on in a Z window rather than detecting probe deployment. // Useful when TARE can be assured outside enable range to avoid potential crash situations. // Prevents tare unless enable switch is off - //#define PROBE_ENABLE_WINDOW + //#define PROBE_TARE_WHILE_INACTIVE #endif #endif diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index feabaaae0e..a82c357863 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -284,7 +284,7 @@ void Endstops::init() { SET_INPUT(PROBE_ENABLE_PIN) #endif - #if ENABLED(PROBE_NEEDS_TARE) + #if ENABLED(PROBE_CAN_TARE probe.tare_z_probe(); #endif @@ -590,7 +590,7 @@ void Endstops::update() { #endif #endif - #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_NEEDS_ENABLE) + #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ENABLED_INPUTT) UPDATE_ENDSTOP_BIT(Z, MIN); #if ENABLED(Z_MULTI_ENDSTOPS) #if HAS_Z2_MIN @@ -616,16 +616,16 @@ void Endstops::update() { #endif // When closing the gap check the enabled probe - #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_NEEDS_ENABLE) + #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ENABLED_INPUTT) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); #endif - #if ENABLED(PROBE_NEEDS_ENABLE) + #if ENABLED(PROBE_ENABLED_INPUTT) #if HAS_CUSTOM_PROBE_PIN UPDATE_ENDSTOP_BIT(Z, MIN); - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)))); #else - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)))); #endif #endif diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 3039f07f8c..3bbff515a9 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1603,8 +1603,9 @@ void homeaxis(const AxisEnum axis) { if (axis == Z_AXIS && bltouch.deploy()) return; // The initial DEPLOY #endif - #if ENABLED(PROBE_NEEDS_TARE) - if (probe.tare_z_probe()) return; + #if ENABLED(PROBE_CAN_TARE + if(axis == Z_AXIS) + if (probe.tare_z_probe()) return; #endif #if DISABLED(DELTA) && defined(SENSORLESS_BACKOFF_MM) diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index dd4d5fe55e..a505398f75 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -479,10 +479,10 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { return !probe_triggered; } -#if ENABLED(PROBE_NEEDS_TARE) +#if ENABLED(PROBE_CAN_TARE bool Probe::tare_z_probe() { - #if ENABLED(PROBE_ENABLE_WINDOW) - if ((READ(PROBE_ENABLE_PIN) == PROBE_NEEDS_ENABLE_STATE)) { + #if ENABLED(PROBE_TARE_WHILE_INACTIVEACTIVE) + if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)) { SERIAL_ECHOLN("Cannot tare probe, already Enabled"); return true; } @@ -513,8 +513,8 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { // Do a first probe at the fast speed - #if ENABLED(PROBE_NEEDS_TARE) - if(tare_z_probe()) return true; + #if ENABLED(PROBE_CAN_TARE + if(tare_z_probe()) return NAN; #endif const bool probe_fail = probe_down_to_z(z_probe_low_point, fr_mm_s), // No probe trigger? @@ -530,7 +530,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #else UNUSED(plbl); #endif - return (int)(probe_fail || early_fail); + return (bool)(probe_fail || early_fail); }; // Stop the probe before it goes too low to prevent damage. @@ -541,8 +541,8 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #if TOTAL_PROBING == 2 // Do a first probe at the fast speed - #if ENABLED(PROBE_NEEDS_TARE) - if(tare_z_probe()) return true; + #if ENABLED(PROBE_CAN_TARE + if(tare_z_probe()) return NAN; #endif if (try_to_probe(PSTR("FAST"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_FAST), @@ -583,7 +583,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #endif { // Probe downward slowly to find the bed - #if ENABLED(PROBE_NEEDS_TARE) + #if ENABLED(PROBE_CAN_TARE if(tare_z_probe()) return true; #endif if (try_to_probe(PSTR("SLOW"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW), diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index adaf61fd61..ee6ce29e2b 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -206,7 +206,7 @@ public: static void set_probing_paused(const bool p); #endif - #if ENABLED(PROBE_NEEDS_TARE) + #if ENABLED(PROBE_CAN_TARE) static bool tare_z_probe(); #endif From ae24cfa655b7d4d082dce4d8232b7af1c11f4f19 Mon Sep 17 00:00:00 2001 From: InsanityAutomation Date: Sat, 5 Dec 2020 17:29:04 -0500 Subject: [PATCH 03/12] Fix borked search / replace --- Marlin/src/module/endstops.cpp | 2 +- Marlin/src/module/motion.cpp | 2 +- Marlin/src/module/probe.cpp | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index a82c357863..e895289e0c 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -284,7 +284,7 @@ void Endstops::init() { SET_INPUT(PROBE_ENABLE_PIN) #endif - #if ENABLED(PROBE_CAN_TARE + #if ENABLED(PROBE_CAN_TARE) probe.tare_z_probe(); #endif diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 3bbff515a9..1d0f71d051 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1603,7 +1603,7 @@ void homeaxis(const AxisEnum axis) { if (axis == Z_AXIS && bltouch.deploy()) return; // The initial DEPLOY #endif - #if ENABLED(PROBE_CAN_TARE + #if ENABLED(PROBE_CAN_TARE) if(axis == Z_AXIS) if (probe.tare_z_probe()) return; #endif diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index a505398f75..d1ab96d11d 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -479,7 +479,7 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { return !probe_triggered; } -#if ENABLED(PROBE_CAN_TARE +#if ENABLED(PROBE_CAN_TARE) bool Probe::tare_z_probe() { #if ENABLED(PROBE_TARE_WHILE_INACTIVEACTIVE) if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)) { @@ -513,7 +513,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { // Do a first probe at the fast speed - #if ENABLED(PROBE_CAN_TARE + #if ENABLED(PROBE_CAN_TARE) if(tare_z_probe()) return NAN; #endif @@ -541,7 +541,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #if TOTAL_PROBING == 2 // Do a first probe at the fast speed - #if ENABLED(PROBE_CAN_TARE + #if ENABLED(PROBE_CAN_TARE) if(tare_z_probe()) return NAN; #endif @@ -583,7 +583,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #endif { // Probe downward slowly to find the bed - #if ENABLED(PROBE_CAN_TARE + #if ENABLED(PROBE_CAN_TARE) if(tare_z_probe()) return true; #endif if (try_to_probe(PSTR("SLOW"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW), From 48b61b7cbd714c0d72e9624e64e57f07cc0575f3 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 5 Dec 2020 22:42:15 -0600 Subject: [PATCH 04/12] It's not golf --- Marlin/src/module/endstops.cpp | 10 +++++----- Marlin/src/module/probe.cpp | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index e895289e0c..fe79d344cb 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -590,7 +590,7 @@ void Endstops::update() { #endif #endif - #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ENABLED_INPUTT) + #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ENABLED_INPUT) UPDATE_ENDSTOP_BIT(Z, MIN); #if ENABLED(Z_MULTI_ENDSTOPS) #if HAS_Z2_MIN @@ -616,16 +616,16 @@ void Endstops::update() { #endif // When closing the gap check the enabled probe - #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ENABLED_INPUTT) + #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ENABLED_INPUT) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); #endif - #if ENABLED(PROBE_ENABLED_INPUTT) + #if ENABLED(PROBE_ENABLED_INPUT) #if HAS_CUSTOM_PROBE_PIN UPDATE_ENDSTOP_BIT(Z, MIN); - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)))); #else - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)))); #endif #endif diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index d1ab96d11d..98fcab3ca6 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -482,7 +482,7 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { #if ENABLED(PROBE_CAN_TARE) bool Probe::tare_z_probe() { #if ENABLED(PROBE_TARE_WHILE_INACTIVEACTIVE) - if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUTT_STATE)) { + if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)) { SERIAL_ECHOLN("Cannot tare probe, already Enabled"); return true; } From e8e4a742b08ebefb025698fbedaf2fb99e2e46f7 Mon Sep 17 00:00:00 2001 From: Scott Lahteine Date: Sat, 5 Dec 2020 22:45:04 -0600 Subject: [PATCH 05/12] fix typo --- Marlin/src/module/motion.cpp | 14 +++++++------- Marlin/src/module/probe.cpp | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index 1d0f71d051..ff0e7c1d0a 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1599,14 +1599,14 @@ void homeaxis(const AxisEnum axis) { // Fast move towards endstop until triggered if (DEBUGGING(LEVELING)) DEBUG_ECHOLNPGM("Home 1 Fast:"); - #if BOTH(HOMING_Z_WITH_PROBE, BLTOUCH) - if (axis == Z_AXIS && bltouch.deploy()) return; // The initial DEPLOY - #endif - - #if ENABLED(PROBE_CAN_TARE) - if(axis == Z_AXIS) + if (axis == Z_AXIS) { + #if BOTH(HOMING_Z_WITH_PROBE, BLTOUCH) + if (bltouch.deploy()) return; // The initial DEPLOY + #endif + #if ENABLED(PROBE_CAN_TARE) if (probe.tare_z_probe()) return; - #endif + #endif + } #if DISABLED(DELTA) && defined(SENSORLESS_BACKOFF_MM) const xy_float_t backoff = SENSORLESS_BACKOFF_MM; diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index 98fcab3ca6..e868fcb31f 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -481,7 +481,7 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { #if ENABLED(PROBE_CAN_TARE) bool Probe::tare_z_probe() { - #if ENABLED(PROBE_TARE_WHILE_INACTIVEACTIVE) + #if ENABLED(PROBE_TARE_WHILE_INACTIVE) if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)) { SERIAL_ECHOLN("Cannot tare probe, already Enabled"); return true; From 0bf9f04a4d10a6a5b86eb75d68dd5f882f821223 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 00:13:13 -0800 Subject: [PATCH 06/12] Renames --- Marlin/Configuration.h | 31 +++++++------ Marlin/src/module/endstops.cpp | 16 +++---- Marlin/src/module/motion.cpp | 2 +- Marlin/src/module/probe.cpp | 49 +++++++++++--------- Marlin/src/module/probe.h | 2 +- Marlin/src/pins/stm32f1/pins_CREALITY_V452.h | 2 +- 6 files changed, 55 insertions(+), 47 deletions(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index 96264ee5a1..d3be6875e5 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -996,25 +996,26 @@ // Feedrate (mm/min) for the "accurate" probe of each point #define Z_PROBE_SPEED_SLOW (Z_PROBE_SPEED_FAST / 2) -// For probes that require an explicit enable input such as feedback an arm is deployed or an opto switch reporting in-range -//#define PROBE_ENABLED_INPUT -#if ENABLED(PROBE_ENABLED_INPUT) - #define PROBE_ENABLED_INPUT_STATE LOW - //#define PROBE_ENABLE_PIN PC6 // Override default probe enable pin +// Fail to probe if the probe does not indicate itself as active. +// This may be a switch indicating proper deployment, or an optical switch to report the carriage is near the bed. +//#define PROBE_ACTIVE_INPUT +#if ENABLED(PROBE_ACTIVE_INPUT) + #define PROBE_ACTIVE_INPUT_STATE LOW // State indicating probe is active + //#define PROBE_ACTIVE_INPUT_PIN PC6 // Override default pin #endif -// Probe requires Tare - Usefull for Strain guage or Piezo type probes which may see force from cabling or bowden tubes following moves -//#define PROBE_CAN_TARE -#if ENABLED(PROBE_CAN_TARE) - #define PROBE_TARE_TIME 200 // Time to hold tare pin - #define PROBE_TARE_DELAY 200 // Dwell following tare before continuing +// Probe should be tared prior to each probe +// Useful for strain or piezo sensors which must exclude strain such +// as that from cables or bowden cables pulling on the carriage. +//#define PROBE_TARE +#if ENABLED(PROBE_TARE) + #define PROBE_TARE_TIME 200 // Time to hold tare pin (milliseconds) + #define PROBE_TARE_DELAY 200 // Delay after tare before (milliseconds) #define PROBE_TARE_STATE HIGH // State to write pin for tare - //#define PROBE_TARE_PIN PA5 // Override default Tare pin + //#define PROBE_TARE_PIN PA5 // Override default pin #if ENABLED(PROBE_ENABLED_INPUT) - // Assume probe enable will come on in a Z window rather than detecting probe deployment. - // Useful when TARE can be assured outside enable range to avoid potential crash situations. - // Prevents tare unless enable switch is off - //#define PROBE_TARE_WHILE_INACTIVE + // Fail to tare/probe if PROBE_ACTIVE_INPUT reports the probe to be active + //#define PROBE_TARE_ONLY_WHILE_INACTIVE #endif #endif diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index fe79d344cb..9366fe6e67 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -280,11 +280,11 @@ void Endstops::init() { #endif #endif - #if PIN_EXISTS(PROBE_ENABLE_PIN) - SET_INPUT(PROBE_ENABLE_PIN) + #if PIN_EXISTS(PROBE_ACTIVE_INPUT) + SET_INPUT(PROBE_ACTIVE_INPUT_PIN); #endif - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) probe.tare_z_probe(); #endif @@ -590,7 +590,7 @@ void Endstops::update() { #endif #endif - #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ENABLED_INPUT) + #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ACTIVE_INPUT) UPDATE_ENDSTOP_BIT(Z, MIN); #if ENABLED(Z_MULTI_ENDSTOPS) #if HAS_Z2_MIN @@ -616,16 +616,16 @@ void Endstops::update() { #endif // When closing the gap check the enabled probe - #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ENABLED_INPUT) + #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ACTIVE_INPUT) UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); #endif - #if ENABLED(PROBE_ENABLED_INPUT) + #if ENABLED(PROBE_ACTIVE_INPUT) #if HAS_CUSTOM_PROBE_PIN UPDATE_ENDSTOP_BIT(Z, MIN); - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE)))); #else - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)))); + SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE)))); #endif #endif diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index ff0e7c1d0a..dc894d6296 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1603,7 +1603,7 @@ void homeaxis(const AxisEnum axis) { #if BOTH(HOMING_Z_WITH_PROBE, BLTOUCH) if (bltouch.deploy()) return; // The initial DEPLOY #endif - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) if (probe.tare_z_probe()) return; #endif } diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index e868fcb31f..0bac312e1d 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -479,24 +479,31 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { return !probe_triggered; } -#if ENABLED(PROBE_CAN_TARE) -bool Probe::tare_z_probe() { - #if ENABLED(PROBE_TARE_WHILE_INACTIVE) - if ((READ(PROBE_ENABLE_PIN) == PROBE_ENABLED_INPUT_STATE)) { - SERIAL_ECHOLN("Cannot tare probe, already Enabled"); - return true; - } - #endif +#if ENABLED(PROBE_TARE) + /** + * @brief Tare the Z probe + * + * @details Signals to the probe to tare measurement + * + * @return TRUE if the tare cold not be completed + */ + bool Probe::tare_z_probe() { + #if ENABLED(PROBE_TARE_ONLY_WHILE_INACTIVE) + if ((READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE)) { + SERIAL_ECHOLN("Cannot tare probe, already active"); + return true; + } + #endif - SERIAL_ECHOLN("Taring the probe"); - WRITE(PROBE_TARE_PIN, PROBE_TARE_STATE); - delay(PROBE_TARE_TIME); - WRITE(PROBE_TARE_PIN, !PROBE_TARE_STATE); - delay(PROBE_TARE_DELAY); + SERIAL_ECHOLN("Taring the probe"); + WRITE(PROBE_TARE_PIN, PROBE_TARE_STATE); + delay(PROBE_TARE_TIME); + WRITE(PROBE_TARE_PIN, !PROBE_TARE_STATE); + delay(PROBE_TARE_DELAY); - endstops.hit_on_purpose(); - return false; -} + endstops.hit_on_purpose(); + return false; + } #endif /** @@ -510,10 +517,10 @@ bool Probe::tare_z_probe() { float Probe::run_z_probe(const bool sanity_check/*=true*/) { DEBUG_SECTION(log_probe, "Probe::run_z_probe", DEBUGGING(LEVELING)); - auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { + auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) -> bool { // Do a first probe at the fast speed - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) if(tare_z_probe()) return NAN; #endif @@ -530,7 +537,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #else UNUSED(plbl); #endif - return (bool)(probe_fail || early_fail); + return probe_fail || early_fail; }; // Stop the probe before it goes too low to prevent damage. @@ -541,7 +548,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #if TOTAL_PROBING == 2 // Do a first probe at the fast speed - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) if(tare_z_probe()) return NAN; #endif @@ -583,7 +590,7 @@ float Probe::run_z_probe(const bool sanity_check/*=true*/) { #endif { // Probe downward slowly to find the bed - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) if(tare_z_probe()) return true; #endif if (try_to_probe(PSTR("SLOW"), z_probe_low_point, MMM_TO_MMS(Z_PROBE_SPEED_SLOW), diff --git a/Marlin/src/module/probe.h b/Marlin/src/module/probe.h index ee6ce29e2b..fb9b1e53bc 100644 --- a/Marlin/src/module/probe.h +++ b/Marlin/src/module/probe.h @@ -206,7 +206,7 @@ public: static void set_probing_paused(const bool p); #endif - #if ENABLED(PROBE_CAN_TARE) + #if ENABLED(PROBE_TARE) static bool tare_z_probe(); #endif diff --git a/Marlin/src/pins/stm32f1/pins_CREALITY_V452.h b/Marlin/src/pins/stm32f1/pins_CREALITY_V452.h index 715fd89db8..ea463f9007 100644 --- a/Marlin/src/pins/stm32f1/pins_CREALITY_V452.h +++ b/Marlin/src/pins/stm32f1/pins_CREALITY_V452.h @@ -69,7 +69,7 @@ // Probe // #define PROBE_TARE_PIN PA5 -#define PROBE_ENABLE_PIN PC6 // Optoswitch to Enable Z Probe +#define PROBE_ACTIVE_INPUT_PIN PC6 // Optoswitch to indicate probe is near bed (active) // // Steppers From a870ebdf9f365d96e7ff4758a16094bee5ccf062 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 01:11:29 -0800 Subject: [PATCH 07/12] Add compile test for new feature --- buildroot/tests/STM32F103RET6_creality-tests | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/buildroot/tests/STM32F103RET6_creality-tests b/buildroot/tests/STM32F103RET6_creality-tests index 4e6c4f988b..4ead56ca5e 100644 --- a/buildroot/tests/STM32F103RET6_creality-tests +++ b/buildroot/tests/STM32F103RET6_creality-tests @@ -12,10 +12,18 @@ set -e use_example_configs "Creality/Ender-3 V2" opt_enable MARLIN_DEV_MODE exec_test $1 $2 "Ender 3 v2" "$3" +restore_configs use_example_configs "Creality/Ender-3 V2" opt_disable CLASSIC_JERK opt_add SDCARD_EEPROM_EMULATION exec_test $1 $2 "Ender 3 v2, SD EEPROM, w/o CLASSIC_JERK" "$3" - +restore_configs + +opt_set SERIAL_PORT 1 +opt_set MOTHERBOARD BOARD_CREALITY_V452 +opt_disable NOZZLE_TO_PROBE_OFFSET +opt_enable NOZZLE_AS_PROBE Z_SAFE_HOMING Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN +opt_enable PROBE_ACTIVE_INPUT PROBE_TARE PROBE_TARE_ONLY_WHILE_INACTIVE +exec_test $1 $2 "Creality V4.5.2 PROBE_ACTIVE_INPUT, PROBE_TARE_ONLY_WHILE_INACTIVE" "$3" restore_configs From 5ed93ec18a05449b53aece69f307f9c4fd96703d Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 01:18:20 -0800 Subject: [PATCH 08/12] Only tare when homing if using probe --- Marlin/src/module/motion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/module/motion.cpp b/Marlin/src/module/motion.cpp index dc894d6296..f5f9b6116a 100644 --- a/Marlin/src/module/motion.cpp +++ b/Marlin/src/module/motion.cpp @@ -1603,7 +1603,7 @@ void homeaxis(const AxisEnum axis) { #if BOTH(HOMING_Z_WITH_PROBE, BLTOUCH) if (bltouch.deploy()) return; // The initial DEPLOY #endif - #if ENABLED(PROBE_TARE) + #if BOTH(HOMING_Z_WITH_PROBE, PROBE_TARE) if (probe.tare_z_probe()) return; #endif } From d8fa698b256fa3aebb18ac4e7330d493b9e53541 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 01:21:56 -0800 Subject: [PATCH 09/12] Fix missed rename --- Marlin/Configuration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/Configuration.h b/Marlin/Configuration.h index d3be6875e5..659fd4a2e1 100644 --- a/Marlin/Configuration.h +++ b/Marlin/Configuration.h @@ -1013,7 +1013,7 @@ #define PROBE_TARE_DELAY 200 // Delay after tare before (milliseconds) #define PROBE_TARE_STATE HIGH // State to write pin for tare //#define PROBE_TARE_PIN PA5 // Override default pin - #if ENABLED(PROBE_ENABLED_INPUT) + #if ENABLED(PROBE_ACTIVE_INPUT) // Fail to tare/probe if PROBE_ACTIVE_INPUT reports the probe to be active //#define PROBE_TARE_ONLY_WHILE_INACTIVE #endif From 61a1fb7478c827554f3dacadd6d971826f40703e Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 19:40:21 -0800 Subject: [PATCH 10/12] Allow multiple endstops along with new feature, simplify code --- Marlin/src/module/endstops.cpp | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/Marlin/src/module/endstops.cpp b/Marlin/src/module/endstops.cpp index 9366fe6e67..23a1e5a8bd 100644 --- a/Marlin/src/module/endstops.cpp +++ b/Marlin/src/module/endstops.cpp @@ -590,7 +590,7 @@ void Endstops::update() { #endif #endif - #if HAS_Z_MIN && !Z_SPI_SENSORLESS && DISABLED(PROBE_ACTIVE_INPUT) + #if HAS_Z_MIN && NONE(Z_SPI_SENSORLESS, Z_MIN_PROBE_USES_Z_MIN_ENDSTOP_PIN) UPDATE_ENDSTOP_BIT(Z, MIN); #if ENABLED(Z_MULTI_ENDSTOPS) #if HAS_Z2_MIN @@ -615,19 +615,12 @@ void Endstops::update() { #endif #endif - // When closing the gap check the enabled probe - #if HAS_CUSTOM_PROBE_PIN && DISABLED(PROBE_ACTIVE_INPUT) - UPDATE_ENDSTOP_BIT(Z, MIN_PROBE); - #endif - #if ENABLED(PROBE_ACTIVE_INPUT) - #if HAS_CUSTOM_PROBE_PIN - UPDATE_ENDSTOP_BIT(Z, MIN); - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN_PROBE) && (READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE)))); - #else - SET_BIT_TO(live_state, _ENDSTOP(Z, MIN), ((READ(_ENDSTOP_PIN(Z, MIN)) != _ENDSTOP_INVERTING(Z, MIN) && (READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE)))); - #endif + if (READ(PROBE_ACTIVE_INPUT_PIN) == PROBE_ACTIVE_INPUT_STATE) #endif + { + UPDATE_ENDSTOP_BIT(Z, TERN(HAS_CUSTOM_PROBE_PIN, MIN_PROBE, MIN)); + } #if HAS_Z_MAX && !Z_SPI_SENSORLESS // Check both Z dual endstops From 6ce0f3dafbc4940b89c9a4bb0f3a7d452ec0b713 Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 19:54:31 -0800 Subject: [PATCH 11/12] Fix inconsistent lambda return type --- Marlin/src/module/probe.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index 0bac312e1d..d17bb9ef38 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -517,11 +517,11 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { float Probe::run_z_probe(const bool sanity_check/*=true*/) { DEBUG_SECTION(log_probe, "Probe::run_z_probe", DEBUGGING(LEVELING)); - auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) -> bool { + auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { // Do a first probe at the fast speed #if ENABLED(PROBE_TARE) - if(tare_z_probe()) return NAN; + if(tare_z_probe()) return true; #endif const bool probe_fail = probe_down_to_z(z_probe_low_point, fr_mm_s), // No probe trigger? From 524eae8e86076661111c8237d0a5b05e5321441d Mon Sep 17 00:00:00 2001 From: Jason Smith Date: Tue, 8 Dec 2020 19:59:42 -0800 Subject: [PATCH 12/12] explicit return type --- Marlin/src/module/probe.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Marlin/src/module/probe.cpp b/Marlin/src/module/probe.cpp index d17bb9ef38..e3bdb9fe8b 100644 --- a/Marlin/src/module/probe.cpp +++ b/Marlin/src/module/probe.cpp @@ -517,7 +517,7 @@ bool Probe::probe_down_to_z(const float z, const feedRate_t fr_mm_s) { float Probe::run_z_probe(const bool sanity_check/*=true*/) { DEBUG_SECTION(log_probe, "Probe::run_z_probe", DEBUGGING(LEVELING)); - auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) { + auto try_to_probe = [&](PGM_P const plbl, const float &z_probe_low_point, const feedRate_t fr_mm_s, const bool scheck, const float clearance) -> bool { // Do a first probe at the fast speed #if ENABLED(PROBE_TARE)