Reviving the dockable_probe

@BelgarionNL / @YaaJ If you could both test this version it should behave the same/fix the trigger issue :crossed_fingers:

I would recommend testing with a simple PROBE command to ensure it behaves as expected before doing more complex probing. Let me know what happens! Thank you!

On my side no problems, but can’t test probe_sensor (microswitches still on the road, can’t make the probe)

The dock sensor seems to be working as expected ; after homing and then removing the probe from the dock, if the command ATTACH_PROBE is issued, Dockable_Probe stops the command with the message "!! Attach Probe: Probe not detected in dock, aborting" ; without the dock sensor, given the overriden macro I use, it is "!! Probe attach failed!" (could have been a crash, depending on the macros ; my head moves to a position where the probe is supposed to jump and auto attach ; if the macro was waiting for some probe circuit closing or opening, it’d be a crash)

dock_sensor = success.

dock_sensor

Can’t wait to see how it behaves with the probe sensor activated !

[EDIT] same expected error message when homing with no probe in the dock

[EDIT 2] potential bug ? If the dock sensor is activated, then the probe attached (ATTACH_PROBE command), if the command PROBE is issued, Dockable_Probe is not happy.

This does not happen if the dock sensor is deactivated.

To me, it sounds like the dock has some priority over the probe. At this point, I didn’t look at the code : currently preparing a video demonstrating Dockable_Probe with and without the dock sensor. Suspecting some conditional precedence or whatever.

Hmm, I’m not sure I understand the dock sensor issue. Can you attach the klippy.log?

Video showing the glitch.
(VNC and video stream are very laggy).

Don’t take it too seriously. This is stress testing. Not even sure that ATTACH_PROBE + PROBE + DETACH_PROBE are intended to be used this way. When using just the PROBE command alone, it always attaches the probe, goes back to its previous location, probes, docks and finally returns where it was in the first place.

The configuration code, with all the RESPONDs. If you want some more, just tell me, I’ll test.

#####################################################################
# Dockable_Probe.cfg
#####################################################################

#___________________________________________________________________
[delayed_gcode autorun]
;description: poor man's "Configuration.h"
initial_duration: 1
gcode:
  SAVE_VARIABLE VARIABLE=x_probe VALUE={317} ; countersunk scew X ccordinate
  SAVE_VARIABLE VARIABLE=verbose_dockable_probe VALUE=True
  SAVE_VARIABLE VARIABLE=verbose_type VALUE='"echo"'

#___________________________________________________________________
; https://github.com/TypQxQ/KnobProbe_Klipper
[knobprobe]

pin: !P1.27 ; SKR 1.4 Z- STOP
deactivate_on_each_sample: False
z_offset: 0.0
speed: 5 ; /!\ 10 too fast for good repeatability, unlike bltouch
sample_retract_dist: 2.0
lift_speed: 100 ;5
; zero retries when setting 'samples_tolerance' to 0.01 -> 1 is enough
;samples_tolerance: 0.01 ; 100% success
samples: 1
;samples_tolerance_retries: 2

#___________________________________________________________________
[gcode_macro KNOBPROBE_ACCURACY]
description: KnobProbe Z-height accuracy at dock position, and return to previous position
gcode:
  {% set pos = printer.gcode_move.gcode_position %}
  {% set lift_speed = printer.configfile.settings.dockable_probe.lift_speed | float %} ; mm/s  
  {% set travel_speed = printer.configfile.settings.dockable_probe.travel_speed | float %} ; mm/s
  MOVE_TO_EXTRACT_PROBE
  PROBE_KNOBACCURACY {rawparams}
  DETACH_PROBE
  SAVE_GCODE_STATE NAME=state_KNOBPROBE_ACCURACY
  G90
  G0 Z{pos.z} F{60 * lift_speed}
  G0 X{pos.x} Y{pos.y} F{60 * travel_speed}
  RESTORE_GCODE_STATE NAME=state_KNOBPROBE_ACCURACY

#___________________________________________________________________
[dockable_probe]

pin: ^P0.10 ; SKR 1.4 BLTouch probe pin ; NC switch
dock_sense_pin: !P1.0 ; SKR 1.4 Z+ STOP / PwrDet
;probe_sense_pin: !P2.0 ; SKR 1.4 BLTouch servo out

x_offset: -1.84
y_offset: 20.84
z_offset: 2.55 ; increasing value lowers the nozzle
approach_position: 327.5, 150.0, 15.0 ; z also for lifting before probing the coountersunk screw
dock_position:     327.5, 150.0 
detach_position:   290.0, 150.0,  1.0 ; wipe left @ Z = 1 mm
z_hop:             15.0 ; also used before probing countersunk screw reference
speed: 5            ; probing speed  ; /!\ 10 too fast for good repeatability, unlike bltouch
lift_speed:   100.0 ; 50.0 ;15.0 ; default = speed
attach_speed: 150.0 ; speed for MOVE_TO_DOCK_PROBE
detach_speed: 150.0 ; speed for MOVE_TO_DETACH_PROBE
travel_speed: 150.0 ; speed for MOVE_TO_APPROACH_PROBE

; zero retries when setting 'samples_tolerance' to 0.01 -> 1 sample is enough
;samples_tolerance: 0.01 ; 100% success
samples: 1               ; default = 1
sample_retract_dist: 2.0 ; default = 2.0
;samples_result: average
;samples_tolerance_retries: 2
check_open_attach: True
dock_retries: 0 ; not relevant

#___________________________________________________________________
[gcode_macro MOVE_TO_APPROACH_PROBE]
description: Move close to the probe dock before attaching
rename_existing: MOVE_TO_APPROACH_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="MOVE_TO_APPROACH_PROBE (overridden)"
  {% endif %}

  {% set x_approach, y_dummy, z_dummy = printer.configfile.settings.dockable_probe.approach_position.replace(' ', '').split(',') %}
  {% set travel_speed = printer.configfile.settings.dockable_probe.travel_speed | float %} ; mm/s
  {% set z_speed = printer.configfile.settings.dockable_probe.attach_speed | float %} ; mm/s  
  {% set x_dummy, y_dummy, z_detach = printer.configfile.settings.dockable_probe.detach_position.replace(' ', '').split(',') %}
  {% set z_hop = printer.configfile.settings.dockable_probe.z_hop | float %} ; mm/s
  SAVE_GCODE_STATE NAME=state_MOVE_TO_APPROACH_PROBE
  G90
  G0 Z{z_hop} F{60 * z_speed}
  G0 X{x_approach} F{60 * travel_speed}
  G0 Z{z_detach} F{60 * z_speed}
  RESTORE_GCODE_STATE NAME=state_MOVE_TO_APPROACH_PROBE

#___________________________________________________________________
[gcode_macro MOVE_TO_INSERT_PROBE]
description: Move near the dock with the probe attached before detaching. Default : alias for MOVE_TO_APPROACH_PROBE
rename_existing: MOVE_TO_INSERT_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="MOVE_TO_INSERT_PROBE (overridden)"
  {% endif %}

  {% set z_speed = printer.configfile.settings.dockable_probe.attach_speed | float %} ; mm/s  
  {% set z_hop = printer.configfile.settings.dockable_probe.z_hop | float %} ; mm/s    
  SAVE_GCODE_STATE NAME=state_MOVE_TO_INSERT_PROBE
  G90
  G0 Z{z_hop} F{60 * z_speed}
  RESTORE_GCODE_STATE NAME=state_MOVE_TO_INSERT_PROBE
  MOVE_TO_INSERT_PROBE_BASE  
  
#___________________________________________________________________
[gcode_macro ATTACH_PROBE]
description: Check probe status and attach probe using the movement gcodes
rename_existing: ATTACH_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="ATTACH_PROBE (overridden)"
  {% endif %}

  {% set pos = printer.gcode_move.gcode_position %}
  {% set travel_speed = printer.configfile.settings.dockable_probe.travel_speed | float %} ; mm/s
  {% set lift_speed = printer.configfile.settings.dockable_probe.lift_speed | float %} ; mm/s  
  ATTACH_PROBE_BASE
  SAVE_GCODE_STATE NAME=state_ATTACH_PROBE
  G90
  G0 Z{pos.z} F{60 * lift_speed}
  G0 X{pos.x} Y{pos.y} F{60 * travel_speed}
  RESTORE_GCODE_STATE NAME=state_ATTACH_PROBE

#___________________________________________________________________
[gcode_macro DETACH_PROBE]
description: Check probe status and detach probe using the movement gcodes
rename_existing: DETACH_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="DETACH_PROBE (default)"
  {% endif %}

  DETACH_PROBE_BASE

#___________________________________________________________________
[gcode_macro MOVE_TO_DOCK_PROBE]
description: Move to connect the toolhead/dock to the probe"
rename_existing: MOVE_TO_DOCK_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="MOVE_TO_DOCK_PROBE (default)"
  {% endif %}

  MOVE_TO_DOCK_PROBE_BASE

#___________________________________________________________________
[gcode_macro MOVE_TO_EXTRACT_PROBE]
description: Move away from the dock with the probe attached. Default : alias for MOVE_TO_APPROACH_PROBE.
rename_existing: MOVE_TO_EXTRACT_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="MOVE_TO_EXTRACT_PROBE (default)"
  {% endif %}

  MOVE_TO_EXTRACT_PROBE_BASE

#___________________________________________________________________
[gcode_macro MOVE_TO_DETACH_PROBE]
description: Move away from the dock to detach the probe
rename_existing: MOVE_TO_DETACH_PROBE_BASE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="MOVE_TO_DETACH_PROBE (default)"
  {% endif %}

  MOVE_TO_DETACH_PROBE_BASE

#___________________________________________________________________
[homing_override]

; parameters :
; X, Y, Z (separated with a blank space)
; M : performs a bed leveling after homing : saves detach + reattach moves

axes: xyz

gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="G28 : start"
  {% endif %}

  ; parameters ; TODO : XY if instead of X Y
  {% set _X_ = params.X|default("false") %}
  {% set _Y_ = params.Y|default("false") %}
  {% set _Z_ = params.Z|default("false") %}
  {% set _O_ = params.O|default("false") %}
  {% set _M_ = params.M|default("false") %}
  {% set _XY_ = "false" %} ; workaround : G28 X Y sets Z to true ; we need to fix this !

;  RESPOND MSG="_X_ : "{_X_}
;  RESPOND MSG="_Y_ : "{_Y_}
;  RESPOND MSG="_Z_ : "{_Z_}
;  RESPOND MSG="_O_ : "{_O_}
;  RESPOND MSG="_XY_ : "{_XY_}

  {% if _X_ != "false" %} {% set _X_ = "true" %} {% endif %}
  {% if _Y_ != "false" %} {% set _Y_ = "true" %} {% endif %}
  {% if _Z_ != "false" %} {% set _Z_ = "true" %} {% endif %}
  {% if _O_ != "false" %} {% set _O_ = "true" %} {% endif %}
  {% if _M_ != "false" %} {% set _M_ = "true" %} {% endif %}

  ; G28 XY
  {% if _X_ == "true" and _Y_ == "true" and _Z_ == "true" %}
    {% set _XY_ = "true" %}
  {% endif %}

  ; G28 = G28 XYZ
  {% if _X_ == "false" and _Y_ == "false" and _Z_ == "false" %}
    {% set _X_ = "true" %}
    {% set _Y_ = "true" %}
    {% set _Z_ = "true" %}
    {% set _XY_= "true" %}
  {% endif %}

;  RESPOND MSG="_X_ : "{_X_}
;  RESPOND MSG="_Y_ : "{_Y_}
;  RESPOND MSG="_Z_ : "{_Z_}
;  RESPOND MSG="_O_ : "{_O_}

  {% if'x' in printer.toolhead.homed_axes %}
    {% set _X_is_homed = "true" %}
  {% else %}
    {% set _X_is_homed = "false" %}
  {% endif %}
  {% if'y' in printer.toolhead.homed_axes %}
    {% set _Y_is_homed = "true" %}
  {% else %}
    {% set _Y_is_homed = "false" %}
  {% endif %}
  {% if'z' in printer.toolhead.homed_axes %}    
    {% set _Z_is_homed = "true" %}
  {% else %}
    {% set _Z_is_homed = "false" %}
  {% endif %}

  ; defined in startup delayed macro (see printer.cfg) ; stored in variables.cfg
  ; = dockable_probe position for reference Z offset (countersunk screw)
  {% set x_probe = printer.save_variables.variables.x_probe | float %}
  ; printer settings
  {% set x_bed_center, y_bed_center = printer.configfile.config.bed_mesh.zero_reference_position.replace(' ', '').split(',') %}
  {% set z_max = printer.configfile.config.stepper_z.position_max | float %}
  {% set lift_accel = printer.configfile.config.printer.max_z_accel | float %} ; mm/s²
  ; dockable_probe settings
  {% set travel_speed = printer.configfile.config.dockable_probe.travel_speed | float %} ; mm/s
  {% set lift_speed = printer.configfile.config.dockable_probe.lift_speed | float %} ; mm/s
  {% set z_hop = printer.configfile.settings.dockable_probe.z_hop | float %}
  {% set x_knobprobe, y_dummy = printer.configfile.config.dockable_probe.dock_position.replace(' ', '').split(',') %}

  SB_STATUS_HOMING ; Neopixel Stealthburner

  {% if _X_ == "true" or _Y_ == "true" or _Z_ == true %}
    FORCE_MOVE STEPPER=stepper_z DISTANCE={z_hop} VELOCITY={lift_speed} ACCEL={lift_accel} ; lift z, mm/s, mm/s²
    {% if _X_ == "false" and _Y_ == "false" and _Z_ == "false" %}
      {% set _Z_ = "true" %}
    {% endif %}
  {% endif %}

  ; individual homings ; TODO : 'O' flag
  {% if _X_ == "true" %}
    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Homing X..."
    {% endif %}
    G28 X
  {% endif %}
  {% if _Y_ == "true" %}
    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Homing Y..."
    {% endif %}
    G28 Y
  {% endif %}

  ; Z calibration, auto Z offset, home Z, optional bed mesh
  {% if _Z_ == "true" %}

    SB_STATUS_CALIBRATING_Z ; Neopixel Stealthburner

    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND MSG="Attaching probe and calibrating Z offset..."
    {% endif %}

    SAVE_GCODE_STATE NAME=state_G28
    SET_DOCKABLE_PROBE AUTO_ATTACH_DETACH=0

    ; probe is attached while knobprobing (probing nozzle)
    G90 ; absolute
    G0 X{x_knobprobe} F{60 * travel_speed} ; KnobProbe position
    {% set z_kinpos = z_max - z_hop  - 10 %}
    SET_KINEMATIC_POSITION Z={z_kinpos} ; gives "headroom" when moving the head down

    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Attach probe while getting Z nozzle position :"    
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="KNOBPROBE SAMPLES=3 SAMPLE_RETRACT_DIST=2"    
    {% endif %}

    KNOBPROBE SAMPLES=3 SAMPLE_RETRACT_DIST=2 ; attaches the Klicky probe + gets nozzle position : 1st reference

    ; probe reference spot
    G91 ; relative
    G0 Z{z_hop} F{60 * lift_speed} ; lift
    G90 ; absolute
    G0 X{x_probe} F{60 * travel_speed} ; probe is attached, align with countersunk screw

    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Get Z countersunk screw position :"
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="PROBE SAMPLES=3 SAMPLE_RETRACT_DIST=2"
    {% endif %}

    PROBE SAMPLES=3 SAMPLE_RETRACT_DIST=2 ; get Klicky position against countersunk screw : 2nd reference
    _SAVE_GCODE_OFFSET ; auto Z offset
    SB_STATUS_HOMING ; Neopixel Stealthburner

    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Homing Z..."
    {% endif %}

    ; reset Z, move to center, home Z
    SET_KINEMATIC_POSITION Z=0 
    G91 ; relative
    G0 Z{z_hop} F{60 * lift_speed} ; lift
    G90 ; absolute
    G0 X{x_bed_center} Y{y_bed_center} F{60 * travel_speed} ; bed center
    RESTORE_GCODE_STATE NAME=state_G28
    G28 Z

    ; bed mesh
    {% if _M_ == "true" and _X_ == "true" and _Y_ == "true" and _Z_ == "true" %}
      SB_STATUS_MESHING ; Neopixel Stealthburner

      {% if printer.save_variables.variables.verbose_dockable_probe == True %}
        RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Meshing..."      
      {% endif %}

      BED_MESH_CALIBRATE
    {% endif %}

    ; end
    DETACH_PROBE
    SET_DOCKABLE_PROBE AUTO_ATTACH_DETACH=1
    SB_STATUS_READY ; Neopixel Stealthburner

    {% if printer.save_variables.variables.verbose_dockable_probe == True %}
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="G28 : end"
      RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="Ready..."
    {% endif %}

  {% endif %}
  
#___________________________________________________________________
[gcode_macro BED_MESH_CALIBRATE]
description: Perform Mesh Bed Leveling
rename_existing: BASE_BED_MESH_CALIBRATE
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="BED_MESH_CALIBRATE"
  {% endif %}

  ATTACH_PROBE
  BASE_BED_MESH_CALIBRATE

#___________________________________________________________________
[gcode_macro AUTO_GCODE_OFFSET]
description: To be inserted in the start gcode, after G28, before first move
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="AUTO_GCODE_OFFSET"
  {% endif %}

  {% set gcode_offset = printer.save_variables.variables.gcode_offset | float %}

  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="AUTO GCODE OFFSET : SET_GCODE_OFFSET Z="{gcode_offset}  
  {% endif %}

  SET_GCODE_OFFSET Z={gcode_offset}

#___________________________________________________________________
[gcode_macro SET_REF_Z_OFFSET]
description: Save reference delta Z after calibration
; calls G28 and saves delta_z as ref_delta_z ; /!\ DO *NOT* REFACTORIZE /!\
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="SET_REF_Z_OFFSET"
  {% endif %}

  SAVE_VARIABLE VARIABLE=ref_delta_z VALUE={3.14159265358979323846} ; dummy ref_delta_z = flag
  G28 ; G28 will automatically reset ref_delta_z

#___________________________________________________________________
[gcode_macro _SAVE_GCODE_OFFSET]
description: Calculates and save the gcode offset given reference delta Z and current delta Z
; called by G28 ; /!\ DO *NOT* REFACTORIZE /!\
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="_SAVE_GCODE_OFFSET"
  {% endif %}

  {% set ref_delta_z = printer.save_variables.variables.ref_delta_z | default(3.14159265358979323846) | float %}
  {% if ref_delta_z == 3.14159265358979323846 %} ; flag : ref_delta_z == pi : not initialized
    _SAVE_REF_DELTA_Z ; save the the difference between KNOBPROBE and PROBE
  {% endif %}
  _SAVE_CUR_DELTA_Z ; save the the difference between KNOBPROBE and PROBE
  _CALC_GCODE_OFFSET ; calculate and save the gcode offset ( = reference delta_z - current delta_z)

#___________________________________________________________________
[gcode_macro _SAVE_REF_DELTA_Z]
description: Save nozzle delta Z as reference offset
; called by _SAVE_GCODE_OFFSET ; /!\ DO *NOT* REFACTORIZE /!\
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".._SAVE_REF_DELTA_Z"
  {% endif %}

  {% set probe_z = printer.probe.last_z_result %} ; ABL probe Z probing
  {% set knobprobe_z = printer.knobprobe.last_z_result %} ; KnobProbe Z probing
  {% set ref_delta_z = printer.probe.last_z_result - printer.knobprobe.last_z_result %} ; delta z for ref. tool

  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....cur_delta_z = probe.last_z_result - knobprobe.last_z_result"
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....probe.last_z_result = "{probe_z}" , knobprobe.last_z_result = "{knobprobe_z}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".... -> SAVE_VARIABLE VARIABLE=ref_delta_z VALUE="{ref_delta_z}
  {% endif %}

  SAVE_VARIABLE VARIABLE=ref_delta_z VALUE={ref_delta_z} ; save ref delta Z

#___________________________________________________________________
[gcode_macro _SAVE_CUR_DELTA_Z]
description: Save current nozzle delta Z
; called by _SAVE_GCODE_OFFSET ; /!\ DO *NOT* REFACTORIZE /!\
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".._SAVE_CUR_DELTA_Z"
  {% endif %}
  
  {% set probe_z = printer.probe.last_z_result %} ; ABL Z probing
  {% set knobprobe_z = printer.knobprobe.last_z_result %} ; knobprobe Z probing
  {% set cur_delta_z = probe_z - knobprobe_z %} ; delta z for current tool

  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....cur_delta_z = probe.last_z_result - knobprobe.last_z_result"
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....probe.last_z_result = "{probe_z}" , knobprobe.last_z_result = "{knobprobe_z}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".... -> SAVE_VARIABLE VARIABLE=cur_delta_z VALUE="{cur_delta_z}
  {% endif %}

  SAVE_VARIABLE VARIABLE=cur_delta_z VALUE={cur_delta_z} ; save current probing offsets

#___________________________________________________________________
[gcode_macro _CALC_GCODE_OFFSET]
description: Calculate and save gcode offset
; called by _SAVE_GCODE_OFFSET ; /!\ DO *NOT* REFACTORIZE /!\
gcode:
  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".._CALC_GCODE_OFFSET"
  {% endif %}

  {% set ref_delta_z = printer.save_variables.variables.ref_delta_z | default(3.14159265358979323846) | float %}
  {% set cur_delta_z = printer.save_variables.variables.cur_delta_z %}
  {% set gcode_offset = ref_delta_z - cur_delta_z %} ; gcode offset = difference

  {% if printer.save_variables.variables.verbose_dockable_probe == True %}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....gcode_offset = ref_delta_z - cur_delta_z : "
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG="....ref_delta_z = "{ref_delta_z}" , cur_delta_z = "{cur_delta_z}
    RESPOND TYPE={printer.save_variables.variables.verbose_type} MSG=".... -> SAVE_VARIABLE VARIABLE=gcode_offset VALUE="{gcode_offset}
  {% endif %}

  SAVE_VARIABLE VARIABLE=gcode_offset VALUE={gcode_offset} ; save gcode offset

klippy.zip (49.0 KB)

[EDIT] could the manual probing require also using SET_DOCKABLE_PROBE AUTO_ATTACH_DETACH=0 ??? Just thinking of it.
Will see tomorrow…

backing up my working config now for my new machine. will let you know after I reconfigure everything.

The auto attach/detach setting only applies when the probe is being attached or detached. If the probe is already attached it won’t have any effect (the “auto” part is always applied before the attach/detach action which calls the appropriate ATTACH/DETACH_PROBE gcode).

That is very strange behavior. After attaching the probe and before running PROBE, run QUERY_DOCKABLE_PROBE and see what state it reports. If the probe is attached this error shouldn’t be possible, attaching should be a no-op, unless the state isn’t being detected accurately. Proceed with PROBE to verify the error still occurs.

  • QUERY_DOCKABLE_PROBE reports UNKNOWN just before the error occurs ; should be ATTACHED ; it occurs at the second sequence, not the first one.

Macro that reproduces the “issue” :

[gcode_macro GENERATE_ERROR_1]
gcode:
  QUERY_DOCKABLE_PROBE
  G28
  QUERY_DOCKABLE_PROBE
  ATTACH_PROBE
  QUERY_DOCKABLE_PROBE
  PROBE
  QUERY_DOCKABLE_PROBE
  ATTACH_PROBE
  QUERY_DOCKABLE_PROBE
  PROBE
  QUERY_DOCKABLE_PROBE

  • This test macro also fails at some point :
[gcode_macro GENERATE_ERROR_2]
gcode:
  QUERY_DOCKABLE_PROBE
  G28
  QUERY_DOCKABLE_PROBE
  SET_DOCKABLE_PROBE AUTO_ATTACH_DETACH=1  
  ATTACH_PROBE
  QUERY_DOCKABLE_PROBE
  PROBE
  QUERY_DOCKABLE_PROBE
  DETACH_PROBE
  QUERY_DOCKABLE_PROBE

 ; this, repeated 10 times :

  ATTACH_PROBE
  QUERY_DOCKABLE_PROBE
  PROBE
  QUERY_DOCKABLE_PROBE
  DETACH_PROBE
  QUERY_DOCKABLE_PROBE

  ;...
  ;...
  ;...
  ;...

  SET_DOCKABLE_PROBE AUTO_ATTACH_DETACH=0

It seems at some point get_probe_state_with_time(self, curtime) reports an incorrect state.

Would it mean that self.dock_sense_pin is not None and ( not (a and not d) and not (d and not a) ) ? Quickly, without a pen and paper, and Bool algebra being really far, isn’t it a not xor ? In other words the probe being seen either docked and attached, or nowhere ?

There’s a LED on the dock sensor, and it looks fine. The sensivity is at its lowest so ambient light doesn’t affect the comparator, and the undocked state is detectected as soon as the probe moves up (1~2mm).
[EDIT] seems it reports UNKNOWN because of not a and not d

[UPDATE] This is REALLY weird. I switched to another port. The first gcode script passed the test with no errors. The second one failed after more iterations… But after a couple more tests, the error was back ! (also played with PROBE_VERIFY_DELAY - who knows - no success). Played with the sensor threshold. Couldn’t find a pattern…

I’m very far from being an electronics expert but this sounds like the dock sensor is producing inconsistent readings. IIUC this can be the result of voltages not registering as being high enough for a digital “on” signal. I believe a pull-up resistor would be used here (see the pin and Bltouch Klipper docs for how to enable them).

I could be way off so apologies and take this advice with salt.

You seem like someone who might own an oscilloscope; if you’ve got one I’d watch the dock sensor and see if it’s reliable.

I don’t suspect the probe pin but to double-check I’d comment out the dock sense pin in the config and rerun the tests (leave it wired in).

Maybe will hook the probes tomorrow (not easy because: the printer and the lab are not in the same room, because of noise… )
I really doubt your code is the “problem”. But could be at lower level…
I tested with and without the interrnal pullup activated (‘^’), and was thinking of some oscillation from the comparator. But the module output already is pulled up or down.

If they didn’t pull the unused comparator inputs to GND, it is a huge mistake. So I tested. Not only pins 5 and 6 are grounded, but also pin 7. (the schematics is inaccurate)

The weirdest thing is that the error generally occurs on the second “PROBE”, never on the first one.

Again, when using Dockable_Probe the way it is intended to be used, there is absolutely no problems ! Been using it on a daily basis for several weeks with zero issue (ALL the dock prototypes and probe prototypes were printed using Dockable_Probe. NEVER HAD ANY PROBLEMS.

If the microswitches are there tomorrow or the next day :pray: , the priority will be the probe sensor and its interoperability with the dock sensor. 3 days for testing, then 3 weeks without the printer.

[EDIT]

4:15 AM…
Found the problem. Of course it is absolutely stupid !
After attaching or detaching, the head goes back to it’s initial position. But when probing, the head goes down until the probe triggers and doesn’t move anymore. Then, when detaching, this position becomes the new “reference” position. After reattaching, the head goes to this position, the probe triggers, so Dockable_Probe doesn’t see it (the switch opens). As a result, it is not in dock, and it is not seen as attached !

A probe sensor would help a lot in this situation…

Since the begining, I’ve been assuming that head moves upa bit (by Z_hop) after probing.

I think I nailed it.

:man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming: :man_facepalming:

Feeling really bad…

(not easy, because the printer is in another room, with laggy video : coordinates and video are not in sync)

1 Like

Hmm, that’s odd. After a standalone probe movement (as opposed to a “multi probe” event like BED_MESH_CALIBRATE) the toolhead should move Z + 2 to prevent exactly the issue you’re seeing (this is done before calling the MOVE_TO_* gcodes or checking the auto attach/detach setting). I’ve tested it today to ensure it does and I do see the tiny hop before detaching.

(z_hop is used in the builtin MOVE_TO_APPROACH_PROBE/MOVE_TO_INSERT_PROBE gcodes, overriding them would disable that behavior unless the “renamed existing” gcode is called.)

Since the begining, I’ve been assuming that head moves upa bit (by Z_hop) after probing.

It should be moving up after probing. This sounds like a bug. Can you link a video of the behavior?

Sorry, I can’t for now : received the microswitches, and building the new probe and probe holder.

Could be related to DETACH_PROBE ; just retested. Last night I stopped as soon as I discovered why the probe was considered UNKNOWN. Was really tired, and the description probably is a bit inaccurate.

ATTACH_PROBE : the probe is attached, and goes back where it was
PROBE : probes, hops, goes to dock, then detaches, but does not go back to previous Z position (XY is Ok, not Z) : the probe touches the bed. Meaning the Z position now is equal to Z Offset. Confirmed by the display in Fluidd.

[EDIT] : of course the next attach/probe/detach will fail if Z = Z Offset !

The issue is somewhere in DETACH_PROBE (in the overridden macros or macros that should be). Until proven otherwise, the problem is on my side, not yours. Will see later.

Curiously, no problem while printing. The head moves near from the bed, but goes to the first mesh spot without any issue, and the brim is always between 0.18 and 0.21 mm every time !!! Never fails (with a 0.2 mm first layer in slicer).

Will see ASAP (making a probe with 3 contacts is not as easy as it looks : 2x3 magnets to align in the same plane, even with some sanding, for perfect electrical contact). Also, have to use epoxy, cyanoacrylate glue cures too quickly. Will take some time for curing. A few hours. Will see tonight (Western Europe).

@YaaJ If you get a chance and have time, would you be able to test your setup with this auto Z calibration module? It’s the de facto one for Voron printers with the nozzle endstop like you have. It’s also a reproducible way to trigger a bug with the dockable_probe module, though the bug might not exist with the latest changes in this thread (that you’ve been running for a couple days now). I can’t test as I don’t have a printer with a nozzle endstop.

Reworked everything from start. Removed all overrides, letting Dockable_Probe do its job instead of taking control on nearly each and every move. Except [homing_override] of course, as it is the heart of the auto Z offset

  • “standalone” PROBE : the head goes back to previous x & y, but z stays at probe Z (does not move vertically, considering the last Z is Zprobe)

  • ATTACH_PROBE returns to previous x and y, but z becomes approach_position.z

  • DETACH_PROBE returns to previous x, y and z

  • PROBE, after calling ATTACH_PROBE by hand : same as “standalone” PROBE

The return postion was fixed with :

  XXXX_BASE
  {% set pos = printer.gcode_move.gcode_position %}
  {% set lift_speed = printer.configfile.settings.dockable_probe.lift_speed | float %} ; mm/s  
  SAVE_GCODE_STATE NAME=state_XXXX
  G90
  ; pos and lift_speed are evaluated as soon as the macro is called
  G0 Z{pos.z} F{60 * lift_speed}
  RESTORE_GCODE_STATE NAME=state_XXXX

at the end of three overriden macros : ATTACH_PROBE, PROBE, and PROBE_ACCURACY

Now 100% works as expected. All commands related to the probe bring the head back to its initial position. ATTACH_PROBE, DETACH_PROBE, PROBE, PROBE_ACCURACY

The plugin link you provided : why do they probe the switch body and calculate a trigger position, when a fixed spot can be probed giving the required information ? Instructions and video are ununderstandable to me. Moreover, it is mechanically incompatible (can’t align two probes along the same axis : can’t probe the probe body ; again, why the body ??? Makes no sense).

I have been using Mentals Dockable Probe for a couple of years, If I use the quad gantry level command under tools with Fluidd it automatically picks up the probe and goes to the first probe point and probes and continues on to the next as expected.

So I installed your revision and attach and detach probe commands work as expected but if you run same command from fluidd it does not pickup the probe and goes to first probe point without the probe which is not good that is a major deviation from Mentals is that expected behaviour?

Welcome back!

No, that’s definitely not expected behavior. I suspect something is misconfigured or getting overridden.

When you say “quad gantry level under tools” does that directly invoke the the “QUAD_GANTRY_LEVEL” gcode?

Please attach your klipper.log, that will greatly assist in troubleshooting.

yes it does invoke “QUAD_GANTRY_LEVEL” gcode, the configuration is based on yours which is pretty much the same as Mentals there should be nothing overriding that gcode command.
I will run it again and get the log when the printer is free, the printer is in use atm with mentals code.

I just noticed in your post you state “does not pickup the probe and goes to the first probe point without the probe”. That is expected behavior. The very next movement should not be Z travel but instead the probe getting attached before probing. Then the Z travel as expected to complete the gantry level gcode, detaching the probe at the end. Are you stopping the printer immediately after moving to the first probe point or is it starting to crash into the bed when you stop it? I’m not sure the log will reflect this sequence one way or the other but it should mention if anything is amiss.

Yes I was stopping the printer just as it got to the first probe point as I did not want a probe to the bed without the probe that would not be a good outcome.

That seems like a lot of wasted movement in this case a travel move of nearly 300mm to the first probe point Without the probe, then a travel move of a return trip of nearly 300mm to pickup the probe to then return to the first probe point of another 300mm with the probe. not tested but if I understood correctly the intended movement?

Mentals code picks up the probe and then goes to the first probe point one trip of nearly 300mm

Sorry, I was slow to respond because I was expecting the log imminently. It would still be greatly appreciated.

Yes, that sequence of events is expected because there’s no way I know of, short of overriding specific code/gcodes to attach the probe before QGL, z tilt, etc (this is what the Klicky macros do). I reviewed mental’s code again and can’t see anywhere where it would know to get the probe sooner.

The way it works now, think of it like the Bltouch behavior where the toolhead moves into position, pauses, drops the Bltouch pin, then probes. Except instead of dropping the pin, the dockable probe is retrieved. This sequence is the way Klipper is coded (move to position, probe and in so doing retrieve if necessary). The movement is done outside the module where I can’t influence it unless I modify QGL, z tilt, etc like I mentioned above. The probing I can control as you see in the module.

To determine why it’s working for you with mental’s code but not this module, again, that log should shed some light.

(Note: if you want the behavior of getting the probe first, override QGL with a “ATTACH_PROBE” as the first line. In a print start macro you may find it useful to use the “AUTO_ATTACH_DETACH=0” feature, see more info in the docs.)

I have attached the log with mentals code.
Thanks for you explaination but mental’s code must have something there because it homes to the Z pin and the dock is approx X-50 from the Z pin when QGL is called it moves to the dock approach postion then attaches the probe next move is the the first QGL probe position front left of the bed. The Voron Z pin and dock is at the rear of the printer. hence the 300mm move rear to front.
klippy (3).log (186.3 KB)

I will test your code maybe in a few days and I will do QGL override.