Close

Generating the GCode Profiles

A project log for Not Just a Reflow Oven

Making a GCode controlled Toaster Oven with Klipper and Octoprint.

clearchrisclearchris 05/17/2020 at 22:590 Comments

You might think, as did I, that now would be the easy part.  Write some 5-10 line gcode scripts, and you are good to go, right?  Not exactly.

It all revolves around the "G4" command, dwell, and how OctoPrint handles it.  By default, OctoPrint basically stops for the duration of the dwell command.  It doesn't update the temperature graph, even when klipper reports back temperatures.  That's not ideal for monitoring progress and tuning. 

The alternative is to uncheck the box "Actively pause communication during G4 dwell command" in the OctoPrint settings, but that causes OctoPrint to go directly to the next line of the gcode script, effectively eliminating the pause.

As a solution, I wrote a small python script to generate the desired behavior, chopping longer waits into one second G4 calls with a M105 in between to update the temperature status.

def print_dwell_gcode(time, increment):
    print ";begin dwell"
    print ";total time:", time, "seconds"
    print ";increments:", increment
    for i in range(0,time//increment):
        print "M105"
        print "G4 P" + str(increment*1000)
    print ";end dwell"
    print ";"
    return

Pretty simple really, and the outcome is exactly as desired.  The oven holds at the temperature (assigned before) and updates the temperature graph.

This and ramping up the temperature slowly the the basis for most of my use cases.  So let's see the ramp up code.

def measured_ramp_gcode(time, increment, startC, endC, heaterName):
    deltaTemp = endC - startC
    increments = time / increment
    deltaPerIncrement = deltaTemp / float(increments)
    print ";begin measured ramp"
    print ";" +str(time), "seconds in", increment, "second increments for", increments, "increments"
    print ";start at", str(startC)+"C end at", str(endC)+"C"
    print ";"+str(deltaTemp/float(time))+"C per second ramp for", str(deltaTemp)+"C"
    for i in range(0,time//increment):
        print "SET_HEATER_TEMPERATURE HEATER="+ str(heaterName), \
        "TARGET="+"%.3f" % (startC + deltaPerIncrement*(i+1) )
        #print "G4 P" + str(increment*1000)
        for j in range(0, increment):  # one per second
            print "M105"
            print "G4 P1000"

    print ";end measured ramp from", str(startC)+"C to", str(endC)+"C"
    print ";"
    return

 This ramps up the temperature over a given time, inserting the necessary waits and temperature checks in between.  You might think that updating the heater temperature more frequently would result in a smoother temperature ramp, but in my testing, that had an unintended effect.  The PID controller, seeing the setpoint was nearby, would not turn up the heaters enough, and the measured temperature would end up lagging the setpoint.  Setting the heater update increment to 5 to 10 seconds resulted in the temperatures more accurately following the desired ramp curve. 

Finally, adding a piezo buzzer became necessary, as some use cases, e.g. preheating a mobile phone for screen removal, require immediate notification and action before the object heated cools.  So we simply add this:

def ring_buzzer(seconds, pin):
    print ";begin ring buzzer"
    print ";total time:", seconds, "seconds"
    print "SET_PIN PIN="+pin, "VALUE=1"
    for i in range(0, seconds):
        print "M105"
        print "G4 P1000" 
    print "SET_PIN PIN="+pin, "VALUE=0"
    print ";end ring buzzer"
    print ";"

The called pin needs to be set up in the configuration file.

Pulling it all together, this script to dehydrate ABS, starts by ramping the temperature to 65C over three minutes.  Then it dwells for three hours at temperature.  Finally, it rings the buzzer and turns off the heating elements.

#!/usr/bin/env python2
#
import oven 

print ";start dehydrateABS"
oven.start_gcode()
print "setup_low_halogen"
print "G4 P1000"
oven.measured_ramp_gcode(60*3,10,32,65,"lowhalogen")
oven.dwell_gcode(60*60*3,1)
oven.ring_buzzer(10, "buzzer_pin")
oven.end_gcode()
print ";end dehydrateABS"

 That 11 line script generates a 290 line gcode file, and works great to control the oven.

Now, the main event, a reflow profile.  This is currently under development, I don't have it working perfectly yet, but that's mostly due to my need to tune the profile to work with my specific solder paste.  You should absolutely do a few test runs with your oven before doing actual reflow work.

#!/usr/bin/env python2
#
import oven 

oven.start_gcode()
print "setup_full_halogen_resistive"
print "G4 P1000"
oven.max_ramp_gcode(50,37,100,"fullhalogenresistive")
oven.measured_ramp_gcode(90,10,100,150,"fullhalogenresistive")
oven.max_ramp_gcode(30,150,183,"fullhalogenresistive")
oven.measured_ramp_gcode(60,10,183,235,"fullhalogenresistive")
oven.dwell_gcode(10,1)
print "TURN_OFF_HEATERS"
print "G4 P100"
#alert to open oven to assist cooling
oven.ring_buzzer(10, "buzzer_pin")
#show maximum downward ramp on graph
#don't exceed 4c per second drop
oven.measured_ramp_gcode(13,1,235,183,"fullhalogenresistive")
oven.end_gcode()
 


 


Discussions