Skip to content
This repository has been archived by the owner on Feb 8, 2024. It is now read-only.

Commit

Permalink
added pathway for G- and M-codes to be passed through the temperature…
Browse files Browse the repository at this point in the history
… control interface
  • Loading branch information
darylbond committed Jan 18, 2018
1 parent 10967bc commit 85401de
Show file tree
Hide file tree
Showing 10 changed files with 318 additions and 203 deletions.
42 changes: 21 additions & 21 deletions configs/default.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -219,78 +219,83 @@ e_axis_active = True
# Set 'active' = True for all units that are to be parsed. A False value means
# it will be ignored

[[M106/M107]]
type = gcode
command = M106, M107
output = Fan-0, Fan-1, Fan-2, Fan-3

# default PID control for heaters
# When this config is parsed the 'active' flag is updated to reflect the
# available thermistors for your Replicape version
[[Control-E]]
type = pid-control
input = Thermistor-E
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1

This comment has been minimized.

Copy link
@Wackerbarth

Wackerbarth Jan 18, 2018

Contributor

Note that the Kp, Ki, and Kd values can also be set by an M-code.
Therefore, I suggest that we define the configuration element a bit differently. For example,:

[[He_kP]]
type = setting
code = M301
sub-code = P
default = 0.1

[[Control-E]]
type= pid-control
input = Thermistor-E
target_value = He_E_Temp
k_p = He_kP
...

[[He_E_Temp]]
type = setting
code = M104
default = 0.0

This comment has been minimized.

Copy link
@darylbond

darylbond Jan 18, 2018

Author Contributor

I am concerned that following this approach leads to an excessively verbose configuration. My thinking was that gcode type units provide access to the unit that contains the settings we want to vary, then all of the logic of changing that setting is contained within the M/G code. I know that this approach departs from the full implementation of the 'network of nodes' concept but I also don't want a ridiculously long config, especially as we would need to define 24 (6x4) of these setting units just for the heater controls.

I suppose the other question is do we need to provide this level of configurability for settings that will always apply to the same thing anyway? Having a configurable M106/7 makes sense as we want to selectively connect different fans to a single code. Codes for setting heater properties will always apply to an individual heater and so this approach is less useful.

pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.25


[[Control-H]]
type = pid-control
input = Thermistor-H
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1
pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.25


[[Control-A]]
type = pid-control
input = Thermistor-A
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1
pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.25


[[Control-B]]
type = pid-control
input = Thermistor-B
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1
pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.25


[[Control-C]]
type = pid-control
input = Thermistor-C
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1
pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.25


[[Control-HBP]]
type = pid-control
input = Thermistor-HBP
target_temperature = 0.0
target_value = 0.0
pid_Kp = 0.1
pid_Ti = 100.0
pid_Td = 0.3
ok_range = 4.0
max_power = 255
max_value = 255
sleep = 0.5

# safety limits for heaters
Expand Down Expand Up @@ -421,28 +426,23 @@ path_adc = /sys/bus/iio/devices/iio:device0/in_voltage6_raw

[[Fan-0]]
type = fan
input = 0
add-to-M106 = False
channel = 0
input = 0

[[Fan-1]]
type=fan
input = 0
add-to-M106 = False
channel = 0
input = 0

[[Fan-2]]
type = fan
input = 0
add-to-M106 = False
channel = 0
input = 0

[[Fan-3]]
type = fan
input = 0
add-to-M106 = False
channel = 0

input = 0

[Heaters]

Expand Down
6 changes: 6 additions & 0 deletions redeem/ColdEnd.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@ def get_temperature(self):
logging.warning("Unable to get temperature from "+self.name)
return -1
return temperature

def get_value(self):
return self.get_temperature()

def __str__(self):
return self.name
52 changes: 26 additions & 26 deletions redeem/Fan.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,37 +44,31 @@ def __init__(self, name, options, printer):
self.options = options
self.printer = printer

self.input = self.options["input"]
self.input = None
if "input" in self.options:
self.input = self.options["input"]

self.channel = int(self.options["channel"])
self.force_disable = False

self.printer.fans.append(self)
self.max_power = 1.0
# get fan index
i = int(name[-1])

self.printer.fans[i] = self
self.max_value = 1.0

self.counter += 1

return

def connect(self, units):
self.input = self.get_unit(self.input, units)

if self.options["add-to-M106"] == "True":
self.force_disable = True
if not isinstance(self.input, ConstantControl):
msg = "{} has a non-constant controller attached. For control by M106/M107 set config 'input' as a constant".format(self.name)
logging.error(msg)
raise RuntimeError(msg)
if self.input:
self.input = self.get_unit(self.input, units)
if not self.input.output:
self.input.output = self

self.printer.controlled_fans.append(self)
logging.info("Added {} to M106/M107".format(self.name))
def check(self):
logging.info("{} --> {}".format(self.input, self.name))

def initialise(self):
""" stuff to do after connecting"""

# inherit the sleep timer from controller
self.sleep = self.input.sleep

return


def set_PWM_frequency(self, value):
Expand All @@ -101,8 +95,8 @@ def run_controller(self):
""" follow a target PWM value 0..1"""

while self.enabled:
self.set_value(self.input.get_power())
time.sleep(self.sleep)
self.set_value(self.input.get_value())
time.sleep(self.input.sleep)
self.disabled = True

def disable(self):
Expand All @@ -116,13 +110,19 @@ def disable(self):

def enable(self):
""" starts the controller """
if self.force_disable:
self.disabled = True

if not self.input:
self.enabled = False
self.disabled = True
self.set_value(0.0)
return

self.enabled = True
self.disabled = False
self.t = Thread(target=self.run_controller, name=self.name)
self.t.daemon = True
self.t.start()
return
return

def __str__(self):
return self.name
63 changes: 42 additions & 21 deletions redeem/Heater.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,15 @@ def __init__(self, name, options, printer):
self.mosfet = self.options["mosfet"]
self.prefix = self.options["prefix"]

self.safety = [s.strip() for s in options["safety"].split(",")]
self.safety = None
if self.safety in self.options:
self.safety = [s.strip() for s in options["safety"].split(",")]

self.min_temp_enabled = False # Temperature error limit

self.input = self.options["input"]
self.input = None
if "input" in self.options:
self.input = self.options["input"]

self.heater_error = False

Expand All @@ -65,25 +70,37 @@ def connect(self, units):
self.mosfet = self.printer.mosfets[self.short_name]

# connect the controller
self.input = self.get_unit(self.input, units)
if self.input:
self.input = self.get_unit(self.input, units)
if not self.input.output:
self.input.output = self


#connect the safety
for i, s in enumerate(self.safety):
self.safety[i] = self.get_unit(s, units)
if self.safety:
for i, s in enumerate(self.safety):
self.safety[i] = self.get_unit(s, units)
if not self.safety[i].heater:
self.safety[i].heater = self

return

def initialise(self):
""" stuff to do after connecting"""

# inherit the sleep timer from controller
self.sleep = self.input.sleep
self.max_power = self.input.max_power
if self.input:
self.max_value = self.input.max_value
else:
self.max_value = 0.0

return

def check(self):
""" run checks"""

if not self.input:
logging.warning("{} is unconnected".format(self.name))
self.heater_error = True

if not self.safety:
self.mosfet.set_power(0.0)
Expand All @@ -105,7 +122,7 @@ def check(self):
def set_target_temperature(self, temp):
""" Set the target temperature of the controller """
self.min_temp_enabled = False
self.input.set_target_temperature(temp)
self.input.set_target_value(temp)

def get_temperature(self):
""" get the temperature of the thermistor and the control input"""
Expand All @@ -117,7 +134,7 @@ def get_temperature_raw(self):

def get_target_temperature(self):
""" get the target temperature"""
return self.input.get_target_temperature()
return self.input.get_target_value()

def is_target_temperature_reached(self):
""" Returns true if the target temperature is reached """
Expand All @@ -138,11 +155,11 @@ def is_temperature_stable(self, seconds=10):
""" Returns true if the temperature has been stable for n seconds """
target_temp = self.get_target_temperature()
ok_range = self.input.ok_range
if len(self.temperatures) < int(seconds/self.sleep):
if len(self.temperatures) < int(seconds/self.input.sleep):
return False
if max(self.temperatures[-int(seconds/self.sleep):]) > (target_temp + ok_range):
if max(self.temperatures[-int(seconds/self.input.sleep):]) > (target_temp + ok_range):
return False
if min(self.temperatures[-int(seconds/self.sleep):]) < (target_temp - ok_range):
if min(self.temperatures[-int(seconds/self.input.sleep):]) < (target_temp - ok_range):
return False
return True

Expand Down Expand Up @@ -183,9 +200,9 @@ def enable(self):
if self.heater_error:
self.enabled = False
return
self.avg = max(int(1.0/self.sleep), 3)
self.avg = max(int(1.0/self.input.sleep), 3)
self.prev_time = self.current_time = time.time()
self.temperatures = [self.input.input.get_temperature()]
self.temperatures = [self.input.input.get_value()]
self.enabled = True
self.t = Thread(target=self.run_controller, name=self.name)
self.t.start()
Expand All @@ -196,13 +213,14 @@ def run_controller(self):
while self.enabled:

# get the controllers recommendation
power = self.input.get_power()
power = max(min(power, self.max_power, 1.0), 0.0)
sleep = self.input.sleep
value = self.input.get_value()
value = max(min(value, self.max_value, 1.0), 0.0)

# get the controlling temperature
temp = self.input.input.get_temperature()
temp = self.input.input.get_value()
self.temperatures.append(temp)
self.temperatures[:-max(int(60/self.sleep), self.avg)] = [] # Keep only this much history
self.temperatures[:-max(int(60/sleep), self.avg)] = [] # Keep only this much history

# Run safety checks

Expand All @@ -211,10 +229,10 @@ def run_controller(self):

# Set temp if temperature is OK
if not self.heater_error:
self.mosfet.set_power(power)
self.mosfet.set_power(value)
else:
self.mosfet.set_power(0)
time.sleep(self.sleep)
time.sleep(sleep)
finally:
# Disable this mosfet if anything goes wrong
self.mosfet.set_power(0)
Expand All @@ -226,3 +244,6 @@ def check_temperature_error(self):
for s in self.safety:
s.set_min_temp_enabled(self.min_temp_enabled)
s.run_safety_checks()

def __str__(self):
return self.name
1 change: 1 addition & 0 deletions redeem/Printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ def __init__(self):
self.cold_ends = []
self.coolers = []
self.comms = {} # Communication channels
self.command_connect = {}
self.path_planner = None
self.speed_factor = 1.0
self.unit_factor = 1.0
Expand Down
Loading

0 comments on commit 85401de

Please sign in to comment.