DEV Community

Go Gams
Go Gams

Posted on

Waiting for chamber heat before an ASA first layer, with a Klipper macro and a small Python check

Disclosure: this post contains affiliate links; I may earn a commission at no extra cost to you.

ASA is less about heroic retraction settings and more about not starting the first layer while the chamber is still a cold box. I have not tuned ASA on a machine of my own for this article. The temperatures below are typical starting ranges from manufacturer sheets, and the code is a pattern you should adapt to your heater and sensor names, not a drop-in profile.

Why the first layer cares about chamber air

ASA shrinks as it cools. If the bed is at 100°C but the cabinet is still near room temperature, the first few layers dump heat into a cold draft. Corners lift, the brim peels, and you spend the next hour blaming glue when the real problem was a start sequence that only waited on the bed.

A closed door helps. An actively heated chamber that you can set, for example the 65°C chamber heat published for current QIDI enclosed models such as the QIDI Q2, is a stronger tool because you can wait for a number instead of hoping the bed warms the air. On 11 November 2026 the global Q2 product page listed a $449 sale price against a $499 regular price. I have not printed on a Q2; that is a spec and a price, not a review.

Typical starting points, always overridden by your filament sheet: nozzle around 240–260°C, bed around 90–110°C, chamber often 50–60°C if the printer can hold it, first-layer speed slower than later layers, little or no part cooling for the first several layers, and a brim on large footprints. Heatsoak matters. Give the bed and the cabinet time to stabilize after they report the target.

A Klipper-style wait you can actually read

Many printers already heat the bed in start G-code and then dive into G28. Add an explicit chamber wait, with a timeout, so a failed heater does not sit there forever. Sensor names differ; on some machines the cabinet is heater_generic chamber, on others a temperature_sensor. Check printer.heaters and your config before you paste.

[gcode_macro START_ASA_WAIT]
description: Heat bed and chamber, then wait with a timeout
gcode:
    {% set BED = params.BED|default(100)|float %}
    {% set CHAMBER = params.CHAMBER|default(55)|float %}
    {% set BED_MIN = BED - 1 %}
    {% set CH_MIN = CHAMBER - 2 %}
    M140 S{BED}
    SET_HEATER_TEMPERATURE HEATER=chamber TARGET={CHAMBER}
    M117 Waiting for bed and chamber
    TEMPERATURE_WAIT SENSOR=heater_bed MINIMUM={BED_MIN}
    TEMPERATURE_WAIT SENSOR=heater_generic chamber MINIMUM={CH_MIN}
    M117 Chamber ready, start first layer
Enter fullscreen mode Exit fullscreen mode

If your firmware has no chamber heater, do not fake this macro. A TEMPERATURE_WAIT on a missing heater will error, which is better than silently starting, but the useful fallback is a timed heatsoak after the bed is ready, with the door closed, plus a temperature sensor if you have one. The point is to refuse the first layer until the air is close to the target, not to decorate the start G-code.

After the wait, keep the first layer boring: square the bed, use a clean PEI or other surface your ASA actually likes, and resist the urge to turn on a part-cooling fan to “sharpen” corners. Sharpening comes later. If a corner still lifts, check drafts around the door and the brim before you raise the bed another 10°C.

A small Python check against Moonraker

If you start jobs from a script or from OctoPrint/Moonraker instead of the printer’s start macro alone, a few lines of Python can refuse to send the file until the chamber is warm enough. This talks to a local Moonraker API and exits non-zero if the wait times out.

import sys, time, urllib.request, json

HOST = "http://127.0.0.1:7125"
SENSOR = "heater_generic chamber"
TARGET, TOLERANCE, TIMEOUT = 55.0, 2.0, 900

def chamber_temp():
    url = f"{HOST}/printer/objects/query?{SENSOR}"
    with urllib.request.urlopen(url, timeout=5) as r:
        obj = json.load(r)["result"]["status"][SENSOR]
    return float(obj.get("temperature") or obj.get("temp"))

start = time.time()
while True:
    t = chamber_temp()
    print(f"chamber {t:.1f} C, target {TARGET} C")
    if t >= TARGET - TOLERANCE:
        sys.exit(0)
    if time.time() - start > TIMEOUT:
        print("timeout waiting for chamber", file=sys.stderr)
        sys.exit(1)
    time.sleep(5)
Enter fullscreen mode Exit fullscreen mode

Wire it in front of your upload-and-start command. Combined with the macro, you get a belt-and-suspenders start: the host will not send the job early, and the printer will not slice into layer one early if the host check is skipped.

The QIDI Plus5 publishes the same 65°C chamber-heat class with a larger 320×320×300 mm volume, at $699 sale / $749 regular on that same date. Pick the volume you need; the wait logic does not change.

Start the first ASA layer when the cabinet is actually at temperature, not when the bed first beeps. That single habit prevents more failed bricks than a new glue stick.

Top comments (0)