Dispensing robot secondary calibration

Basic Information:

MCU / Printerboard: Octopus V1.1

Hey, guys.

I need some advice. I’ve tried everything and I don’t know what else to do. And I haven’t even found any topic here that would point me to a solution.

I have redesigned my desktop dispensing robot to be controlled by a clipper. It’s a standard 3 axis robot. I find that when I want to dispense something accurately, I have problems with the tolerances of the dispensing needles. Sometimes they are off axis by as much as 2mm. Yes, I can adjust it manually, and at the moment I do, but it’s not quite right.

I was thinking of using something like this:
Video

Z axis is not a problem, I calibrate that one via PROBE. The problem is that I am not able to create a macro that can monitor the status on pins PG12 and PG13, to which I have optical gates connected.

In practice, the way this should work is that the optical gates would determine the start and end of the dispense needle pass, thereby determining its center relative to the axis and setting a new X and Y axis value. Is it even possible to do something like that?

Thank you for any ideas

Most probably you can try to implement special macro by using gcode_button.
gcode_button can monitor pin and execute press_gcode and release_gcode.

Each optical gate will have separate gcode_button and will control separate axis.

press_gcode should execute your macro for storing start coordinates, release_gcode should execute your macro with current coordinates, your macro will calculate differences and will update offsets.

Hello, thank you for your reply.

I have tried to use these inputs as a button too, but I am not able to achieve a working code.

Next I have no idea how to set the conditions and variables for press_gcode and release_gcode. Do you have any sample code?

You are lucky :slight_smile:
This task did look as a challenge for me so I went and build it.
It’s logic exactly as I suggested, 2 instances of gcode_button which are responsible for fetching coordinates and pushing them to macro set_nozzle_offset, macro do_calibration_move is just doing calibration moves in X and Y coordinates, macro set_nozzle_offset is fetching collected coordinates and setting offsets.

To prepare these macros for your environment yo need to do following:

  • re-define pins in gcode_button macros, reverse them with “!” if you gettring release trigger before press
  • if you will see “MCU protocol error” after restart - that means your main MCU don’t have buttons support in firmware (that was my case) - you need to add it or another approach you can try to use your host machine with Linux host MCU and use pins from there (I did this way)
  • re-define start position in do_calibration_move currently it’s X50Y50Z200
  • re-define microstep_count in do_calibration_move, currently it’s 400 microsteps (40mm)
  • re-define microstep_speed in do_calibration_move for your needs, smaller values will give better resolution, higher values will make it coarser.
  • check move_speed in do_calibration_move if it’s good for you machine
  • check z_jump in do_calibration_move if it’s good for your machine (Start Z position + z_jump)
  • add commands for setting offsets in end of set_nozzle_offset, currently it’s just showing delta values, don’t forget to divide delta values by 2 to get center point of needle.

Then you can call your calibration, but it should consist from 2 separate commands because coordinates will propagate only after some macro is finished execution, commands:

do_calibration_move
set_nozzle_offset

With this macro I was able to se resolutions up to 0.02mm when I did set speed=5mm/min, but it was just usual endstop hardware (not optical)

[gcode_button calibration_X_button]
pin: !host:gpiochip0/gpio30
press_gcode:
  {% set position = printer.motion_report.live_position %}
  SET_GCODE_VARIABLE MACRO=set_nozzle_offset VARIABLE=x0 VALUE={position.x}
  {action_respond_info("X-press-trigger X: %.2f "% (position.x))}
release_gcode:
   {% set position = printer.motion_report.live_position %}
  SET_GCODE_VARIABLE MACRO=set_nozzle_offset VARIABLE=x1 VALUE={position.x}
  {action_respond_info("X-release-trigger X: %.2f "% (position.x))}

[gcode_button calibration_Y_button]
pin: !host:gpiochip0/gpio31
press_gcode:
  {% set position = printer.motion_report.live_position %}
  SET_GCODE_VARIABLE MACRO=set_nozzle_offset VARIABLE=y0 VALUE={position.y}
  {action_respond_info("Y-press-trigger Y: %.2f "% (position.y))}
release_gcode:
   {% set position = printer.motion_report.live_position %}
  SET_GCODE_VARIABLE MACRO=set_nozzle_offset VARIABLE=y1 VALUE={position.y}
  {action_respond_info("Y-release-trigger Y: %.2f "% (position.y))}

[gcode_macro do_calibration_move]
description: Do calibration moves in X and In Y
gcode:
  {% set microstep_count = 400 %}
  {% set microstep_size = -0.1 %}
  {% set microstep_speed = 300 %}
  {% set move_speed = 3000 %}
  {% set z_jump = 50 %}
  SAVE_GCODE_STATE NAME=calibrate_1_state
  G90
  #Go to starting position for X calibration
  G0X50Y50Z200 F{move_speed}
  G91
  #Do microsteps in X
  {% for step in range(microstep_count) %}
    G1 X{microstep_size} F{microstep_speed}
  {% endfor %}
  #Ok we finished X, now we need to return to start position 
  #but need to jump over trigger
  G0Z{z_jump} F{move_speed}
  #Go in reverse to starting position for Y calibration
  G0 X{microstep_size*microstep_count*(-1)} F{move_speed}
  G0Z-{z_jump} F{move_speed}
  #Do microsteps in Y
  {% for step in range(microstep_count) %}
    G1 Y{microstep_size} F{microstep_speed}
  {% endfor %}
  G90
  RESTORE_GCODE_STATE NAME=calibrate_1_state
  
[gcode_macro set_nozzle_offset]
description: Fetch stored X and Y and set offsets
variable_x0: 0.0
variable_x1: 0.0
variable_y0: 0.0
variable_y1: 0.0
gcode:
  {% set me = printer["gcode_macro set_nozzle_offset"] %}
  {% set dx = me.x0 - me.x1 %}
  {% set dy = me.y0 - me.y1 %}
  {action_respond_info("dX: %.2f, dY: %.2f" % (dx, dy))  }
  # Do call setting of offset

P.S as a “Thank you” - I’m expecting from you to record a video of calibration in action and then publishing it with some description in our “macro” category, don’t forget to add macros.
If you need some additional details - fell free to ask.

P.P.S This can be build much simpler and better way with additional python scripts for klipper, but main intention was not to introduce some customization to klipper code base.

1 Like

Pretty tough :wink:

Hi, thank you very much for your solution. I’ll test it during today and let you know how it works. I’ll record the video and post it of course.

Thank you very much again and I appreciate your efforts.

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.