Automatic control of Rectangular HEATBED sections based on PRINT AREA

Our Voron 2.4 printer has 8 (each ~100x100mm) shaped heatbed sections; Arranged in a 4x2 grid, they are controlled by a 8 channel SSR. Each one is 100W AC Silicone pad heater, totaling 800W.
There are two SKR 1.3 Boards for the printer. SKR 1.3 supports these pins for thermistor analog inputs - 0.23, 0.24, 0.25, 1.30, 1.31.
The first 4 heatbeds are connected to the first SKR board (MCU Z), the next 4 heatbeds are connected to the other board (MCU XYE). Every heatbed and its thermistor is connected to the same MCU respectively.
SKR 1.3 pinout

However only after building the printer did we find out that multiple heatbeds are not yet fully supported by Slicers or Klipper. For a while we have been manually enabling the heatbeds. But this was not ideal. Thanks to the flexibility of Klipper macros, it is possible to enable the required heatbeds automatically.

In the configuration, the first heatbed is defined as the default heater_bed. The rest are defined as heater_generic ( heater_bed2, heater_bed3, heater_bed4,…)

The implementation of this method is simple and straightforward. The Print Area is compared with a Heatbed Area- If the two rectangles overlap, then the corresponding heatbed section is enabled

2 Likes

Current limitations:

  • The print area reported by the slicer is a rectangle, For some shapes this can cause a heater bed to be enabled even when there is no actual print on it. Here’s an example: In this case the heatbed 4 will be enabled since the assumed print area overlaps it.

  • Temperature wait commands are not yet implemented since the heatbeds reach their temperature very quickly. This can be an issue for some printers if heatbeds are slow.

1 Like

Example Setup:

Auto Heatbed macro:

[gcode_macro HEATBED_AUTO]
variable_parameter_AREA_START : 0,0
variable_parameter_AREA_END : 0,0
gcode:

#Set the coordinates of the heatbed sections
    {% set x0 = 0 %}
    {% set x1 = 100 %}
    {% set x2 = 200 %}
    {% set x3 = 300 %}
    {% set x4 = 400 %}

    {% set y0 = 0 %}
    {% set y1 = 105 %}
    {% set y2 = 210 %}
#Get parameters from Slicer
    {% set BED_TEMP = params.BED_TEMP|default(50)|float %}
    {% if params.AREA_START and params.AREA_END %}
        {% set A = params.AREA_START.split(",")[0]|float %}
    	{% set B = params.AREA_START.split(",")[1]|float %}
    	{% set C = params.AREA_END.split(",")[0]|float %}
    	{% set D = params.AREA_END.split(",")[1]|float %}

#Check and enable the required heatbeds
    #Heatbed 1  x1,y0  x2,y1
        {% if ((C > x1) and (D > y0) and (A < x2) and (B < y1)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed TARGET={BED_TEMP}        
        {% endif %}
    #Heatbed 2  x0,y0  x1,y1
        {% if ((C > x0) and (D > y0) and (A < x1) and (B < y1)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed2 TARGET={BED_TEMP}         
        {% endif %}
    #Heatbed 3  x0,y1  x1,y2
        {% if ((C > x0) and (D > y1) and (A < x1) and (B < y2)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed3 TARGET={BED_TEMP}         
        {% endif %}
    #Heatbed 4  x1,y1  x2,y2
        {% if ((C > x1) and (D > y1) and (A < x2) and (B < y2)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed4 TARGET={BED_TEMP}        
        {% endif %}
    #Heatbed 5  x2,y1  x3,y2
        {% if ((C > x2) and (D > y1) and (A < x3) and (B < y2)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed5 TARGET={BED_TEMP}         
        {% endif %}
    #Heatbed 6  x3,y1  x4,y2
        {% if ((C > x3) and (D > y1) and (A < x4) and (B < y2)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed6 TARGET={BED_TEMP}         
        {% endif %}
    #Heatbed 7  x3,y0  x4,y1
        {% if ((C > x3) and (D > y0) and (A < x4) and (B < y1)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed7 TARGET={BED_TEMP}        
        {% endif %}
    #Heatbed 8  x2,y0  x3,y1
        {% if ((C > x2) and (D > y0) and (A < x3) and (B < y1)) %}
            SET_HEATER_TEMPERATURE HEATER=heater_bed8 TARGET={BED_TEMP}         
        {% endif %}
    {% endif %}

Slicer Setup:
Cura: requires MeshPrintSize.py post processing script by frankbags
START_PRINT EXTRUDER_TEMP={material_print_temperature_layer_0} BED_TEMP={material_bed_temperature_layer_0} AREA_START=%MINX%,%MINY% AREA_END=%MAXX%,%MAXY%

SuperSlicer / PrusaSlicer:
START_PRINT EXTRUDER_TEMP=[first_layer_temperature] BED_TEMP=[first_layer_bed_temperature] AREA_START={first_layer_print_min[0]},{first_layer_print_min[1]} AREA_END={first_layer_print_max[0]},{first_layer_print_max[1]}

PRINT_START macro:
The Heater bed command in the PRINT_START macro must be modified:
HEATBED_AUTO BED_TEMP={params.BED|default(50)|float} AREA_START={params.AREA_START|default("0,0")} AREA_END={params.AREA_END|default("0,0")}

2 Likes

So I was inspired by this post and I am currently building this on a ratrig v-core. Any ideas on what to modify in this start print macro to incorporate this into my build. Here is the start print macro from a v-Core

#####
# START PRINT MACROS
# Call this from your slicer (custom g-code).
# Read more here: https://rat-rig.github.io/V-CoreOS/#/slicers
#####

[gcode_macro START_PRINT]
description: Start print procedure, use this in your Slicer.
gcode:
  CLEAR_PAUSE
  SAVE_GCODE_STATE NAME=start_print_state
  # Metric values
  G21
  # Absolute positioning
  G90
  # Set extruder to absolute mode
  M82
  {% if printer["gcode_macro RatOS"].z_probe|lower == 'stowable' %}
  STOWABLE_PROBE_BEGIN_BATCH
  {% endif %}
  # Home if needed
  MAYBE_HOME
  {% if params.CHAMBER_TEMP is defined %}
  _START_PRINT_HEAT_CHAMBER CHAMBER_TEMP={params.CHAMBER_TEMP} BED_TEMP={printer["gcode_macro RatOS"].start_print_heat_chamber_bed_temp}
  {% endif %}
  M117 Heating bed...
  RESPOND MSG="Heating bed..."
  # Wait for bed to heat up
  M190 S{params.BED_TEMP|default(printer.heater_bed.target, true) }
  # Run the customizable "AFTER_HEATING_BED" macro.
  _START_PRINT_AFTER_HEATING_BED
  # Run the customizable "BED_MESH" macro
  _START_PRINT_BED_MESH
  {% if printer["gcode_macro RatOS"].z_probe|lower == 'stowable' %}
  STOWABLE_PROBE_END_BATCH
  {% endif %}
  # Start heating extruder
  M104 S{params.EXTRUDER_TEMP|default(printer.extruder.target, true) }
  # Run the customizable "PARK" macro
  _START_PRINT_PARK
  # Wait for extruder to heat up
  M117 Heating Extruder...
  RESPOND MSG="Heating Extruder..."
  M109 S{params.EXTRUDER_TEMP|default(printer.extruder.target, true) }
  # Run the customizable "AFTER_HEATING_EXTRUDER" macro.
  _START_PRINT_AFTER_HEATING_EXTRUDER
  M117 Printing...
  RESPOND MSG="Printing..."
  RESTORE_GCODE_STATE NAME=start_print_state
  # Set extrusion mode based on user configuration
  {% if printer["gcode_macro RatOS"].relative_extrusion|lower == 'true' %}
    M83
  {% else %}
    M82
  {% endif %}
  G92 E0