New MPCNC build: Primo + Manta MP8 + Klipper in BC, Canada

Thanks Matt appreciate your support and agree on the pseudo steps. No need for you to switch back to klipper yet, I’ll take a stab first. I’m switching back and forth between klipper setup and designing 3D parts for drag chains. I’ve got some prototypes printing so I’ll try working on the resume code before work this morning.

Update: I’m close, I see how it works and am able to store the position at the point the process is paused. I just need to get the syntax right for retrieving them in the resume macro when saved in the pause macro.
I see how other functions do it and so expect to crack it tonight when I take another look.

Wasn’t too difficult and not finished yet as I want to put in some more safety coverage and not stopping the starting the spindle, but I do have it returning to X,Y before Z. I’ll paste the code here for anyone looking, and I’ll repost a more complete version later.

[gcode_macro PAUSE]
variable_restore: {'absolute': {'coordinates': True}, 'speed': 1500}
description: Pause the actual running print
rename_existing: PAUSE_BASE
gcode:
  PAUSE_BASE
  {% set restore = {'pos': 
    {'x': printer.gcode_move.gcode_position.x, 'y': printer.gcode_move.gcode_position.y, 'z': printer.gcode_move.gcode_position.z }
    , 'speed': printer.gcode_move.speed} %}
  SET_GCODE_VARIABLE MACRO=PAUSE VARIABLE=restore VALUE="{restore}"
  M117 Paused
  _TOOLHEAD_PARK_PAUSE_CANCEL {rawparams}
[gcode_macro RESUME]
description: Resume the actual running print
rename_existing: RESUME_BASE
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set pause_macro_found = True if printer['gcode_macro PAUSE'] is defined else False %}
  {% set paused_at = printer['gcode_macro PAUSE'] %}
  {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %}
  {% set use_fw_retract = False if not macro_found
                     else False if client.use_fw_retract is not defined
                     else True  if client.use_fw_retract|lower == 'true' and printer.firmware_retraction is defined
                     else False %} 
  {% set unretract      = 1.0  if not macro_found else client.unretract|default(1.0)|abs %}
  {% set sp_unretract   = 2100 if not macro_found else client.speed_unretract|default(35) * 60 %}
  {% set sp_move        = velocity if not macro_found else client.speed_move|default(velocity) %}
  ##### end of definitions #####

 #### move back to the saved position before resuming
   M117 Resume {paused_at.restore.pos.x},{paused_at.restore.pos.y},{paused_at.restore.pos.z}
   G90
   G1 X{paused_at.restore.pos.x} Y{paused_at.restore.pos.y} F2000
   G1 Z{paused_at.restore.pos.z} F200
  RESUME_BASE VELOCITY={params.VELOCITY|default(sp_move)}  
2 Likes

Wow. I’m not that into coding. I just tried this on my Voron Trident and it didn’t function as I expected, but like I said, I’m not that into coding to fully understand what it is supposed to do. It is funny that I was about to start playing around with figuring this out and you posted. I’m trying something a bit more simpler. It might not have a lot of safety built in like you are planning but it is simpler and I can follow along and understand it :wink:.
I just might have to reconnect my Pi (I only have a zero that will not work for cutting, but it worked for moving around) and re-test your macros. It wont be an issue since I still have the image on a SD card that I can re-insert into the Pi and I’m back to where I left it when I switched to Marlin.

EDITED: Well, I’m now running into whenever I PAUSE it moves the router/spindle to the MAX X,Y when it would be more helpful to move to the MIN X,Y (0,0) to be able to change the bit. Trying to figure out how to change the PARK location for the PAUSE.

3rd EDIT: I found my mistake - I had the PAUSE macro defined multiple times (I never looked into the mailsail.cfg file where it was original defined). Neil, your RESUME works nicely.

1 Like

Hello,
Very nice project :grin:
I’m also on klipper on an octopus BTT for my MPCNC Primo.
For the resumption of the break, it’s quite complicated, what I do is that I make a gcode for each change of tools.
You have a risk of moving your motors by changing the tool.

Sorry my English is very bad, I am French speaking.

2 Likes

Cool!
I didn’t give instructions as I’ll do that once I have it working the way I want. I just shared the code in it’s current basic state as I’d solved the initial problem and so folks following along can benefit.

2 Likes

Here’s the latest version of the mainsail config I have for klipper which allow me to use Pause and Resume.
NOTE: This should be used with caution, it is not complete AND not yet been tested on a CNC with an actual spindle connected
I haven’t yet worked out how to persist the speed the spindle was running at before pausing, so it’s hardcoded to resume at 5000.
I didn’t get much time to work on it yesterday and won’t tomorrow so wanted to share where I’m at.

This is my current complete mainsail.cfg

## Mainsail klipper macro definitions
##
## Copyright (C) 2022 Alex Zellner <alexander.zellner@googlemail.com>
##
## This file may be distributed under the terms of the GNU GPLv3 license

## Version 2.0

## add [include mainsail.cfg] to your printer.cfg

## Customization:
## copy the following macro to your printer.cfg (outside of mainsail.cfg)

#[gcode_macro _CLIENT_VARIABLE]
#variable_use_custom_pos  : True ; use custom park coordinates for x,y [True/False] 
#variable_custom_park_x   : 100.0   ; custom x position; value must be within your defined min and max of X
#variable_custom_park_y   : 100.0   ; custom y position; value must be within your defined min and max of Y
#variable_custom_park_dz  : 2.0   ; custom dz value; the value in mm to lift the nozzle when move to park position 
#variable_speed_hop       : 200.0  ; z move speed in mm/s
#variable_speed_move      : 200.0 ; move speed in mm/s
#variable_park_at_cancel  : False ; allow to move the toolhead to park while execute CANCEL_PRINT [True,False]
#gcode:

## After you uncomment it add your custom values

## You now can use your PAUSE macro direct in your M600 here a short example:

#[gcode_macro M600]
#description: Tool change
#gcode: PAUSE X=10 Y=10 Z_MIN=50

## That will park your head front left with a minimum hight of 50mm above the bed. If your head
## is already above 50mm it will use only the z_hop specified with dz.

[virtual_sdcard]
path: ~/printer_data/gcodes
on_error_gcode: CANCEL_PRINT

[pause_resume]

[display_status]

[gcode_macro CANCEL_PRINT]
description: Cancel the actual running print
rename_existing: CANCEL_PRINT_BASE
gcode:
  ##### get user parameters or use default #####
  {% set allow_park = False if printer['gcode_macro _CLIENT_VARIABLE'] is not defined
                 else False if printer['gcode_macro _CLIENT_VARIABLE'].park_at_cancel is not defined
                 else True  if printer['gcode_macro _CLIENT_VARIABLE'].park_at_cancel|lower == 'true' 
                 else False %}
  ##### end of definitions #####
  {% if not printer.pause_resume.is_paused and allow_park %} _TOOLHEAD_PARK_PAUSE_CANCEL {% endif %}

  CANCEL_PRINT_BASE

[gcode_macro PAUSE]

variable_restore: {'pos': {'x': 0, 'y':0, 'z':0}}
description: Pause the actual running print
rename_existing: PAUSE_BASE
gcode:
  PAUSE_BASE
  {% set restore = {'pos': 
    {'x': printer.gcode_move.gcode_position.x, 'y': printer.gcode_move.gcode_position.y, 'z': printer.gcode_move.gcode_position.z },
    'rpm': 5000} %}
  SET_GCODE_VARIABLE MACRO=PAUSE VARIABLE=restore VALUE="{restore}"
  M117 Paused at X{restore.pos.x|int} Y{restore.pos.y|int} Z{restore.pos.z} spinning at {restore.rpm}rpm
  _TOOLHEAD_PARK_PAUSE_CANCEL {rawparams}

[gcode_macro RESUME]
description: Resume the actual running print
rename_existing: RESUME_BASE
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set paused_at = printer['gcode_macro PAUSE'] %}
  {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %}
  {% set sp_move      = velocity * 60 if not macro_found else client.speed_move|default(velocity) * 60 %}
  {% set sp_hop       = 900  if not macro_found else client.speed_hop|default(15) * 60 %}

  ##### define resume position
  {% set x_resume = printer['gcode_macro PAUSE'].restore.pos.x %}
  {% set y_resume = printer['gcode_macro PAUSE'].restore.pos.y %}
  {% set z_resume = printer['gcode_macro PAUSE'].restore.pos.z %}
  {% set rpm_resume = printer['gcode_macro PAUSE'].restore.rpm %}

  ##### end of definitions #####


 #### move back to the saved position before resuming
   M117 Resume to X{x_resume|int} Y{y_resume|int} then Z{z_resume} spinning as {rpm_resume}rpm
   G90
   #### restarting spindle, need to 
   M3 S{rpm_resume}
   G1 X{x_resume} Y{y_resume} F{sp_move}
   G1 Z{z_resume} F{sp_hop}
  RESUME_BASE VELOCITY={params.VELOCITY|default(sp_move)}  

[gcode_macro _TOOLHEAD_PARK_PAUSE_CANCEL]
description: Helper: park toolhead used in PAUSE and CANCEL_PRINT
gcode:
  ##### get user parameters or use default #####
  {% set macro_found = True if printer['gcode_macro _CLIENT_VARIABLE'] is defined else False %}
  {% set client = printer['gcode_macro _CLIENT_VARIABLE'] %}
  {% set velocity = printer.configfile.settings.pause_resume.recover_velocity %}
  {% set use_custom     = False if not macro_found
                     else False if client.use_custom_pos is not defined
                     else True  if client.use_custom_pos|lower == 'true' 
                     else False %}
  {% set custom_park_x  = 0.0 if not macro_found else client.custom_park_x|default(0.0) %}
  {% set custom_park_y  = 0.0 if not macro_found else client.custom_park_y|default(0.0) %}
  {% set park_dz        = 2.0 if not macro_found else client.custom_park_dz|default(2.0)|abs %}

  {% set sp_hop       = 900  if not macro_found else client.speed_hop|default(15) * 60 %}
  {% set sp_move      = velocity * 60 if not macro_found else client.speed_move|default(velocity) * 60 %}
  ##### get config and toolhead values #####
  {% set act = printer.toolhead.position %}
  {% set max = printer.toolhead.axis_maximum %}
  {% set cone = printer.toolhead.cone_start_z|default(max.z) %} ; hight as long the toolhead can reach max and min of an delta
  {% set round_bed = True if printer.configfile.settings.printer.kinematics is in ['delta','polar','rotary_delta','winch'] 
                else False %}
  ##### define park position #####
  {% set z_min = params.Z_MIN|default(0)|float %}
  {% set z_park = [[(act.z + park_dz), z_min]|max, max.z]|min %}
  {% set x_park = params.X       if params.X is defined
             else custom_park_x  if use_custom
             else 0.0            if round_bed 
             else (max.x - 5.0) %}
  {% set y_park = params.Y       if params.Y is defined
             else custom_park_y  if use_custom
             else (max.y - 5.0)  if round_bed and z_park < cone
             else 0.0            if round_bed
             else (max.y - 5.0) %}

  {% if "xyz" in printer.toolhead.homed_axes %}
    G90
    G1 Z{z_park} F{sp_hop}
    #### stopping spindle
    M5
    G1 X{x_park} Y{y_park} F{sp_move}
    {% if not printer.gcode_move.absolute_coordinates %} G91 {% endif %}
  {% else %}
    {action_respond_info("Printer not homed")}
  {% endif %}

I currently have these variables include via my printer.cfg;

## Customization:
## copy the following macro to your printer.cfg (outside of mainsail.cfg)

[gcode_macro _CLIENT_VARIABLE]
variable_use_custom_pos  : True ; use custom park coordinates for x,y [True/False] 
variable_custom_park_x   : 100.0   ; custom x position; value must be within your defined min and max of X
variable_custom_park_y   : 100.0   ; custom y position; value must be within your defined min and max of Y
variable_custom_park_dz  : 40.0   ; custom dz value; the value in mm to lift the nozzle when move to park position 
variable_speed_hop       : 5.0  ; z move speed in mm/s
variable_speed_move      : 80.0 ; move speed in mm/s
#variable_park_at_cancel  : False ; allow to move the toolhead to park while execute CANCEL_PRINT [True,False]
gcode:

I’ll be focusing on the drag chains and other modifications next, and still some time away from being ready for cutting. i.e. I need to build a table, speed control for the Makita etc.

1 Like



Here’s the drag chain mount I’ve been working on, doesn’t require any screws or modifications to existing parts, just slides into the end of my Stainless Steel 1" tubes.
Please excuse the quality of the print, I made some tweaks and haven’t spent the time to retune

4 Likes

Good Stuff! Can’t wait to see you get this one up and going. I plan to cut foam tomorrow on mine :slight_smile:

Could you post your printer.cfg just for my curiosity

1 Like

Good luck on cutting the foam!
I split up the config sections into different files to make maintenance easier.

printer.cfg

[include mainsail.cfg]
[include Hardware/MPCNC.cfg]
[include Hardware/mcu.cfg]
[include Hardware/steppers.cfg]
[include Hardware/homing.cfg]
[include Hardware/fans.cfg]
[include Hardware/leds.cfg]
[include Hardware/thermistors.cfg]

[include Macros/mainsail_variables.cfg]
[include Macros/neils_macros/m600.cfg]

MPCNC.cfg

[printer]
kinematics: cartesian
max_velocity: 12000
max_accel: 3000
max_z_velocity: 40 #45 if needed
max_z_accel: 100

[gcode_arcs]
resolution: 1.0

mcu.cfg

[mcu]
serial: /dev/serial/by-id/usb-Klipper_stm32g0b1xx_*******

[mcu cb1]
serial: /tmp/klipper_host_mcu

stepper.cfg

[endstop_phase]

# Motor 1
[stepper_x]
step_pin: PE2
dir_pin: !PB4
enable_pin: !PC11
microsteps: 16
rotation_distance: 32
endstop_pin: ^!PF3
position_endstop: 0
position_max: 620
homing_speed: 40
homing_positive_dir: false

# Motor 2
[stepper_x1]
step_pin: PF12
dir_pin: PF11
enable_pin: !PB3
microsteps: 16
rotation_distance: 32
endstop_pin: ^!PF4
#position_endstop: 0
#position_max: 235
#homing_speed: 40

# Motor 4
[stepper_y]
step_pin: PD3
dir_pin: PD2
enable_pin: !PD5
microsteps: 16
rotation_distance: 32
endstop_pin: ^!PC0
position_endstop: 0
position_max: 345
homing_speed: 40
homing_positive_dir: false

# Motor 5
[stepper_y1]
step_pin: PC9
dir_pin: !PC8
enable_pin: !PD1
microsteps: 16
rotation_distance: 32
endstop_pin: ^!PC1
#position_endstop: 0
#position_max: 235
#homing_speed: 40

# Motor 6
[stepper_z]
step_pin: PA10
dir_pin: PD15
enable_pin: !PA15
microsteps: 16
rotation_distance: 8
endstop_pin: ^!PC2
position_endstop: 1.40
position_max: 70 # 90 when legs are in first wooden board
position_min: -5.0
homing_speed: 3
second_homing_speed: 3
homing_retract_dist: 3


########################################
# TMC2209 configuration
########################################

[tmc2209 stepper_x]
uart_pin: PC10
##diag_pin: PF3
run_current: 0.700
stealthchop_threshold: 999999

[tmc2209 stepper_x1]
uart_pin: PF13
##diag_pin: PF3
run_current: 0.700
stealthchop_threshold: 999999

[tmc2209 stepper_y]
uart_pin: PD4
##diag_pin: PF4
run_current: 0.700
stealthchop_threshold: 999999

[tmc2209 stepper_y1]
uart_pin: PD0
##diag_pin: PF4
run_current: 0.700
stealthchop_threshold: 999999

[tmc2209 stepper_z]
uart_pin: PF8
##diag_pin: PF5
run_current: 0.650
stealthchop_threshold: 999999

homing.cfg

[safe_z_home]
#home_xy_position: 320,172 Center of workspace
home_xy_position: 10,10
speed: 200 # up from 100
z_hop: 2                 # Move up 10mm
z_hop_speed: 10
#move_to_previous: True

[force_move]
enable_force_move: True

fans.cfg

[fan_generic X_fans]
pin: PE6
#stepper: stepper_x

[fan_generic Y_fans]
pin: PE0
#stepper: stepper_x1

[fan_generic Z_fan]
pin: PC12
#stepper: stepper_y

[fan_generic Laser]
pin: PE5
#stepper: stepper_y1

leds.cfg

#[neopixel my_neopixel_1]
#pin: PC6

#[neopixel my_neopixel_2]
#pin: PA9

thermistors.cfg

sensor_type: temperature_mcu
max_temp: 100

[temperature_sensor cb1]
sensor_type: temperature_host
max_temp: 70

mainsail_variables.cfg

## Customization:
## copy the following macro to your printer.cfg (outside of mainsail.cfg)

[gcode_macro _CLIENT_VARIABLE]
variable_use_custom_pos  : True ; use custom park coordinates for x,y [True/False]
variable_custom_park_x   : 100.0   ; custom x position; value must be within your defined min and max of X
variable_custom_park_y   : 100.0   ; custom y position; value must be within your defined min and max of Y
variable_custom_park_dz  : 40.0   ; custom dz value; the value in mm to lift the nozzle when move to park position
variable_speed_hop       : 5.0  ; z move speed in mm/s
variable_speed_move      : 80.0 ; move speed in mm/s
#variable_park_at_cancel  : False ; allow to move the toolhead to park while execute CANCEL_PRINT [True,False]
gcode:

m600.cfg

[gcode_macro M600]
description: Tool change
gcode:
  M117 Pausing for tool change
  PAUSE X=10 Y=10 Z_MIN=50
4 Likes

Following with interest!

2 Likes

@N7NJO I do like those stainless steel tubes. Could you provide the link of the exact ones you purchased from Metal Supermarkets? They have so many options. Thank you

I’m trying to follow along with all of this.
I put the cb1 on the manta and got it on my Wi-Fi. I can’t get the “make flash “ to work but Klipper will at least start. It always shows errors.
Because I don’t know what I’m doing I’m being really conservative. I haven’t connected the motors and when I do I’ll have the belts off.
So I keep starting Klipper and chasing errors. Each time it starts I see an error. I know I fixed it when I get the next error.

I put the hardware cfg files in a directory and the macros too. ( including Neil’s macros. ). Also I fixed a few duplicate pin used errors. The one that I am stuck on is “file has no header”

Maybe I started wrong. I used the generic klipper for the m8p board. Or is there another one that I should use?
Thank you for any clues.
Mark

Actually I followed the recommendations from many others and didn’t order online. I went in the store and asked to look what they had, for stainless tube with 1" outer diameter. I believe they called it “decorative”… and there wasn’t anything else to choose but it was just what I was looking for.
Beware, wasn’t cheap.

1 Like

Because it is decorative. :yum:

I started with the generic klipper file for the m8p, then tweaked it to work with my set up. The contents of the files you see above are the result of my taking that modified generic and splitting it into separate files.
So when Klipper starts it looks for the printer.cfg, you can see mine then includes all the specific files. I no longer reference the generic mp8 file.
As for the “make flash” did you follow the steps on page 36 of the Manta manual?
Can you share more of the error you get for “file has no header”

I’ve made more progress on the design of the drag chains mounts, although I’ve been slowed by having to troubleshoot some printer issues. I wanted a solution which kept the chains of the main surface, while not replacing any of the parts Ryan designed. So I’m taking most of the support from the tubes, while only resting on the CNC parts. I still have more to do, but once I have the cabling clear I’ll start working more with Klipper and plan to upload the parts if others want to use.




7 Likes

These look fancy. :slight_smile:

Unrelated: I just figured out today that if you sear the edges of the mesh thingy (where you put the cables in) they can’t unravel. I can’t see whether you did that. :slight_smile:

3 Likes

I had a similar experience. I called Metal supermarkets and asked for stainless 1 inch OD. 304 SS with a 0.65 wall. This is the common “plumbing” for Food and Pharma factories. (Plentiful in NJ).
I just read my cut list on the phone, and picked it up the next day. ~$105 for a 18 x 24 build area.

1 Like

Only partially. I used MobaXterm instead of Putty because they had a portable version that ran without an install.
I did the first part 6.2 Compile firmware “make menuconfig. I adjusted the menu choices to match the image.
The “make” command worked and I copied klipper.bin, renamed and put firmware.bin in the/biqu. It won’t let me put it in the root of the SD card.
More to come, I should be typing this on laptop not iPad so I can copy and paste.

These ones haven’t started to unravel, yet, but thanks that’s a good tip.

1 Like