diff --git a/configs/default.cfg b/configs/default.cfg index e0cc7fc3..92f2ad0f 100644 --- a/configs/default.cfg +++ b/configs/default.cfg @@ -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 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 @@ -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] diff --git a/redeem/ColdEnd.py b/redeem/ColdEnd.py index d5630ef6..f662c59a 100644 --- a/redeem/ColdEnd.py +++ b/redeem/ColdEnd.py @@ -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 diff --git a/redeem/Fan.py b/redeem/Fan.py index 03f87fc8..38874ac9 100644 --- a/redeem/Fan.py +++ b/redeem/Fan.py @@ -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): @@ -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): @@ -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 \ No newline at end of file + return + + def __str__(self): + return self.name \ No newline at end of file diff --git a/redeem/Heater.py b/redeem/Heater.py index d6f0f334..722a2695 100644 --- a/redeem/Heater.py +++ b/redeem/Heater.py @@ -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 @@ -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) @@ -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""" @@ -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 """ @@ -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 @@ -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() @@ -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 @@ -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) @@ -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 diff --git a/redeem/Printer.py b/redeem/Printer.py index 6f67d0d5..c61ffebb 100755 --- a/redeem/Printer.py +++ b/redeem/Printer.py @@ -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 diff --git a/redeem/Redeem.py b/redeem/Redeem.py index 1dbe3f61..27326f72 100755 --- a/redeem/Redeem.py +++ b/redeem/Redeem.py @@ -298,15 +298,19 @@ def initialize(self, configs=[]): # update the channel information for fans if self.revision == "00A3": + self.printer.fans = [None]*3 for i, c in enumerate([0,1,2]): self.printer.config["Fans"]["Fan-{}".format(i)]["channel"] = c elif self.revision == "0A4A": + self.printer.fans = [None]*3 for i, c in enumerate([8,9,10]): self.printer.config["Fans"]["Fan-{}".format(i)]["channel"] = c elif self.revision in ["00B1", "00B2", "00B3", "0B3A"]: + self.printer.fans = [None]*4 for i, c in enumerate([7,8,9,10]): self.printer.config["Fans"]["Fan-{}".format(i)]["channel"] = c if printer.config.reach_revision == "00A0": + self.printer.fans = [None]*3 for i, c in enumerate([14,15,7]): self.printer.config["Fans"]["Fan-{}".format(i)]["channel"] = c @@ -336,9 +340,8 @@ def initialize(self, configs=[]): # build and connect all of the temperature control infrastructure - self.printer.controlled_fans = [] - self.printer.fans = [] self.printer.heaters = {} + self.printer.command_connect = {} # available control unit types, see TemperatureControl.py control_units = {"alias":Alias, "difference":Difference, @@ -347,7 +350,8 @@ def initialize(self, configs=[]): "on-off-control":OnOffControl, "pid-control":PIDControl, "proportional-control":ProportionalControl, - "fan":Fan, "heater":Heater, "safety":Safety} + "fan":Fan, "heater":Heater, "safety":Safety, + "gcode":CommandCode} # generate units all_built = True diff --git a/redeem/TemperatureControl.py b/redeem/TemperatureControl.py index ff512a16..4b16cde5 100644 --- a/redeem/TemperatureControl.py +++ b/redeem/TemperatureControl.py @@ -96,7 +96,7 @@ def get_unit(self, name, units): return sensor else: #assume it is a constant c_name = "Constant-{}".format(self.counter) - unit = ConstantControl(c_name, {"input":int(name), "sleep":1.0}, self.printer) + unit = ConstantControl(c_name, {"value":int(name)}, self.printer) units[c_name] = unit return unit @@ -110,6 +110,9 @@ def initialise(self): def check(self): """ run any checks that need to be performed after full initialisation""" return + + def __str__(self): + return self.name class Alias(Unit): @@ -119,11 +122,8 @@ def __init__(self, name, options, printer): self.name = name self.options = options self.printer = printer - self.input = options["input"] - self.output = None - if "output" in options: - self.output = options["output"] + self.input = self.options["input"] self.counter += 1 @@ -131,12 +131,12 @@ def __init__(self, name, options, printer): def connect(self, units): self.input = self.get_unit(self.input, units) - if self.output: - self.output = self.get_unit(self.output, units) - self.output.input = self - def get_temperature(self): - return self.input.get_temperature() + def get_value(self): + return self.input.get_value() + + def check(self): + logging.info("{} --> {} ".format(self.input, self.name)) class Compare(Unit): @@ -144,41 +144,38 @@ def __init__(self, name, options, printer): self.name = name self.options = options self.printer = printer - self.inputs = [] + + self.input = [] for i in range(2): - self.inputs.append(options["input-{}".format(i)]) - - self.output = None - if "output" in options: - self.output = options["output"] + self.input.append(options["input-{}".format(i)]) self.counter += 1 return def connect(self, units): + for i in range(2): - self.inputs[i] = self.get_unit(self.inputs[i], units) - if self.output: - self.output = self.get_unit(self.output, units) - self.output.input = self + self.input[i] = self.get_unit(self.input[i], units) + + def check(self): + logging.info("({} and {}) --> {}".format(self.input[0].name, self.input[1].name, self.name)) class Difference(Compare): - def get_temperature(self): - return self.inputs[0].get_temperature() - self.inputs[1].get_temperature() + def get_value(self): + return self.input[0].get_value() - self.input[1].get_value() class Maximum(Compare): - def get_temperature(self): - return max(self.inputs[0].get_temperature(), self.inputs[1].get_temperature()) + def get_value(self): + return max(self.input[0].get_value(), self.input[1].get_value()) class Minimum(Compare): - def get_temperature(self): - return min(self.inputs[0].get_temperature(), self.inputs[1].get_temperature()) - - + def get_value(self): + return min(self.input[0].get_value(), self.input[1].get_value()) + class Safety(Unit): def __init__(self, name, options, printer): @@ -202,19 +199,23 @@ def __init__(self, name, options, printer): return def connect(self, units): - self.input = self.get_unit(self.input, units) self.heater = self.get_unit(self.heater, units) - return def initialise(self): # insert into the attached heater, if it isn't already there - if self not in self.heater.safety: + if not self.heater.safety: + self.heater.safety = [self] + elif self not in self.heater.safety: self.heater.safety.append(self) + + input_sensor = self.input + if isinstance(self.input, Alias): + input_sensor = self.input.input - if (not isinstance(self.input, TemperatureSensor)) and (not isinstance(self.input, ColdEnd)): + if (not isinstance(input_sensor, TemperatureSensor)) and (not isinstance(input_sensor, ColdEnd)): msg = "{} will not work, input = {} is not a temperature sensor".format(self.name, self.input.name) logging.error(msg) @@ -230,6 +231,9 @@ def initialise(self): return + def check(self): + logging.info("{} --> {} --> {}".format(self.input.name, self.name, self.heater.name)) + def set_min_temp_enabled(self, flag): """ enable the min_temp flag """ self.min_temp_enabled = flag @@ -240,7 +244,7 @@ def run_safety_checks(self): # add to ring buffers self.time.append(time.time()) - self.temp.append(self.input.get_temperature()) + self.temp.append(self.input.get_value()) # get ordered lists times = [self.time[i] for i in range(self.time.get_length())] @@ -259,7 +263,7 @@ def run_safety_checks(self): time_delta = times[-1] - times[0] # heater info - target_temp = self.heater.input.target_temperature + target_temp = self.heater.input.target_value power_on = self.heater.mosfet.get_power() > 0 # track when the heater was first turned on @@ -309,13 +313,12 @@ def __init__(self, name, options, printer): self.name = name self.options = options self.printer = printer - self.input = options["input"] + self.input = None self.output = None - if "output" in options: - self.output = options["output"] - self.power = 0.0 + self.value = 0.0 + self.sleep = 0.25 self.get_options() @@ -324,6 +327,7 @@ def __init__(self, name, options, printer): return def get_options(self): + return def connect(self, units): @@ -336,6 +340,9 @@ def connect(self, units): def reset(self): return + + def check(self): + logging.info("{} --> {} --> {}".format(self.input, self.name, self.output)) class ConstantControl(Control): @@ -343,12 +350,74 @@ class ConstantControl(Control): feedback_control = False def get_options(self): - self.power = int(self.options['input'])/255.0 - self.sleep = 1.0 # use a default + + self.output = None + if "output" in self.options: + self.output = self.options["output"] + + self.value = int(self.options['value'])/255.0 return - def get_power(self): - return self.power + def connect(self, units): + if self.output: + self.output = self.get_unit(self.output, units) + self.output.input = self + + + def get_value(self): + return self.value + + def set_target_value(self, value): + self.value = float(value) + + def ramp_to(self, value, delay): + save_sleep = self.sleep + self.sleep = delay/2.0 + for w in range(int(self.value*255.0), int(value*255.0), (1 if value >= self.value else -1)): + self.value = w/255.0 + time.sleep(delay) + self.value = value + self.sleep = save_sleep + + + def check(self): + logging.info("{} --> {} --> {}".format(self.value, self.name, self.output)) + + + +class CommandCode(ConstantControl): + """ + For connecting G and M codes + """ + + def get_options(self): + self.command = [c.strip() for c in self.options["command"].split(",")] + for command in self.command: + if command in self.printer.command_connect: + logging.warning("multiple instances of {} used in [Temperature Control]".format(self.command)) + self.printer.command_connect[command] = self + + self.output = [] + if "output" in self.options: + self.output = [o.strip() for o in self.options["output"].split(",")] + + self.input = self.command + + + def connect(self, units): + for i, output in enumerate(self.output): + self.output[i] = self.get_unit(output, units) + self.output[i].input = self + + def check(self): + outputs = "[" + for output in self.output: + outputs += "{}, ".format(output) + outputs = outputs[0:-2] + "]" + logging.info("{} --> {} --> {}".format(self.input, self.name, outputs)) + + def __str__(self): + return str(self.name) class OnOffControl(Control): @@ -356,43 +425,47 @@ class OnOffControl(Control): feedback_control = True def get_options(self): - self.target_temperature = float(self.options['target_temperature']) + self.input = self.options["input"] + self.output = None + if "output" in self.options: + self.output = self.options["output"] + self.target_value = float(self.options['target_value']) self.on_offset = float(self.options['on_offset']) self.off_offset = float(self.options['off_offset']) - self.max_power = float(self.options['on_power'])/255.0 - self.off_power = float(self.options['off_power'])/255.0 + self.max_value = float(self.options['on_value'])/255.0 + self.off_value = float(self.options['off_value'])/255.0 self.sleep = float(self.options['sleep']) - self.on_temperature = self.target_temperature + self.on_offset - self.off_temperature = self.target_temperature + self.off_offset + self.on_value = self.target_value + self.on_offset + self.off_value = self.target_value + self.off_offset - self.power = self.off_power + self.value = self.off_value return - def set_target_temperature(self, temp): - """ set the target temperature """ + def set_target_value(self, value): + """ set the target value """ - self.target_temperature = float(temp) - self.on_temperature = self.target_temperature + self.on_offset - self.off_temperature = self.target_temperature + self.off_offset + self.target_value = float(value) + self.on_value = self.target_value + self.on_offset + self.off_value = self.target_value + self.off_offset return - def get_target_temperature(self): - """ get the target temperature """ - return self.target_temperature + def get_target_value(self): + """ get the target value """ + return self.target_value - def get_power(self): + def get_value(self): - temp = self.input.get_temperature() + value = self.input.get_value() - if temp <= self.on_temperature: - self.power = self.max_power - elif temp >= self.off_temperature: - self.power = self.off_power + if value <= self.on_value: + self.value = self.max_value + elif value >= self.off_value: + self.value = self.off_value - return self.power + return self.value class ProportionalControl(Control): @@ -401,53 +474,60 @@ class ProportionalControl(Control): def get_options(self): """ Init """ - self.current_temp = 0.0 - self.target_temperature = float(self.options['target_temperature']) # Target temperature (Ts). Start off. + self.input = self.options["input"] + self.output = None + if "output" in self.options: + self.output = self.options["output"] + self.current_value = 0.0 + self.target_value = float(self.options['target_value']) # Target value (Ts). Start off. self.P = float(self.options['proportional']) # Proportional - self.max_power = min(1.0, float(self.options['max_power'])/255.0) - self.min_power = max(0, float(self.options['min_power'])/255.0) + self.max_value = min(1.0, float(self.options['max_value'])/255.0) + self.min_value = max(0, float(self.options['min_value'])/255.0) self.ok_range = float(self.options['ok_range']) self.sleep = float(self.options['sleep']) - def set_target_temperature(self, temp): - """ set the target temperature """ - self.target_temperature = float(temp) + def set_target_value(self, value): + """ set the target value """ + self.target_value = float(value) return - def get_target_temperature(self): - """ get the target temperature """ - return self.target_temperature + def get_target_value(self): + """ get the target value """ + return self.target_value - def get_power(self): - """ PID Thread that keeps the temperature stable """ - self.current_temp = self.input.get_temperature() - error = self.target_temperature-self.current_temp + def get_value(self): + """ PID Thread that keeps the value stable """ + self.current_value = self.input.get_value() + error = self.target_value-self.current_value if error <= self.ok_range: return 0.0 - power = self.P*error # The formula for the PID (only P) - power = max(min(power, 1.0), 0.0) # Normalize to 0,1 + value = self.P*error # The formula for the PID (only P) + value = max(min(value, 1.0), 0.0) # Normalize to 0,1 - # Clamp the max power - power = min(power, self.max_power) - # Clamp min power - power = max(power, self.min_power) + # Clamp the max value + value = min(value, self.max_value) + # Clamp min value + value = max(value, self.min_value) - return power + return value class PIDControl(Control): feedback_control = True def get_options(self): - - self.target_temperature = float(self.options['target_temperature']) + self.input = self.options["input"] + self.output = None + if "output" in self.options: + self.output = self.options["output"] + self.target_value = float(self.options['target_value']) self.Kp = float(self.options['pid_Kp']) self.Ti = float(self.options['pid_Ti']) self.Td = float(self.options['pid_Td']) self.ok_range = float(self.options['ok_range']) - self.max_power = min(1.0, float(self.options['max_power'])/255.0) + self.max_value = min(1.0, float(self.options['max_value'])/255.0) self.sleep = float(self.options['sleep']) return @@ -459,46 +539,46 @@ def initialise(self): self.errors = [0]*self.avg self.averages = [0]*self.avg - current_temp = self.input.get_temperature() - self.temperatures = [current_temp] + current_value = self.input.get_value() + self.values = [current_value] - self.error_integral = 0.0 # Accumulated integral since the temperature came within the boudry - self.error_integral_limit = 100.0 # Integral temperature boundary + self.error_integral = 0.0 # Accumulated integral since the value came within the boudry + self.error_integral_limit = 100.0 # Integral value boundary - def set_target_temperature(self, temp): - """ set the target temperature """ - self.target_temperature = float(temp) + def set_target_value(self, value): + """ set the target value """ + self.target_value = float(value) return - def get_target_temperature(self): - """ get the target temperature """ - return self.target_temperature + def get_target_value(self): + """ get the target value """ + return self.target_value - def get_power(self): + def get_value(self): - current_temp = self.input.get_temperature() - self.temperatures.append(current_temp) - self.temperatures[:-max(int(60/self.sleep), self.avg)] = [] # Keep only this much history + current_value = self.input.get_value() + self.values.append(current_value) + self.values[:-max(int(60/self.sleep), self.avg)] = [] # Keep only this much history - self.error = self.target_temperature-current_temp + self.error = self.target_value-current_value self.errors.append(self.error) self.errors.pop(0) derivative = self.get_error_derivative() integral = self.get_error_integral() # The standard formula for the PID - power = self.Kp*(self.error + (1.0/self.Ti)*integral + self.Td*derivative) - power = max(min(power, self.max_power, 1.0), 0.0) # Normalize to 0, max + value = self.Kp*(self.error + (1.0/self.Ti)*integral + self.Td*derivative) + value = max(min(value, self.max_value, 1.0), 0.0) # Normalize to 0, max - return power + return value def get_error_derivative(self): - """ Get the derivative of the temperature""" - # Using temperature and not error for calculating derivative + """ Get the derivative of the value""" + # Using value and not error for calculating derivative # gets rid of the derivative kick. dT/dt - der = (self.temperatures[-2]-self.temperatures[-1])/self.sleep + der = (self.values[-2]-self.values[-1])/self.sleep self.averages.append(der) if len(self.averages) > 11: self.averages.pop(0) @@ -509,7 +589,7 @@ def get_error_integral(self): self.error_integral += self.error*self.sleep # Avoid windup by clippping the integral part # to the reciprocal of the integral term - self.error_integral = np.clip(self.error_integral, 0, self.max_power*self.Ti/self.Kp) + self.error_integral = np.clip(self.error_integral, 0, self.max_value*self.Ti/self.Kp) return self.error_integral def reset(self): diff --git a/redeem/TemperatureSensor.py b/redeem/TemperatureSensor.py index 493cbc47..0574a020 100644 --- a/redeem/TemperatureSensor.py +++ b/redeem/TemperatureSensor.py @@ -86,6 +86,9 @@ def get_temperature(self): if not self.sensor: return 0.0 return self.sensor.get_temperature(voltage) + + def get_value(self): + return self.get_temperature() """ @@ -108,6 +111,9 @@ def read_adc(self): TemperatureSensor.mutex.release() return voltage + + def __str__(self): + return self.name """ This class represents standard thermistor sensors. diff --git a/redeem/Util.py b/redeem/Util.py index 106df86c..02a9e883 100644 --- a/redeem/Util.py +++ b/redeem/Util.py @@ -219,4 +219,4 @@ def _plot(x, mph, mpd, threshold, edge, valley, ax, ind): ax.set_title("%s (mph=%s, mpd=%d, threshold=%s, edge='%s')" % (mode, str(mph), mpd, str(threshold), edge)) # plt.grid() - plt.show() + plt.show() \ No newline at end of file diff --git a/redeem/gcodes/M106_M107.py b/redeem/gcodes/M106_M107.py index 97e31385..9db4b6a2 100644 --- a/redeem/gcodes/M106_M107.py +++ b/redeem/gcodes/M106_M107.py @@ -15,25 +15,23 @@ class M106(GCodeCommand): def execute(self, gcode): - fans = [] + # Get the value, 255 if not present + value = float(gcode.get_float_by_letter("S", 255)) / 255.0 + + fan_controller = None if gcode.has_letter("P"): fan_no = gcode.get_int_by_letter("P", 0) if fan_no < len(self.printer.fans): - fans.append(self.printer.fans[fan_no]) - else: # No P in gcode, use fans from settings file - fans = self.printer.controlled_fans - - # Get the value, 255 if not present - value = float(gcode.get_float_by_letter("S", 255)) / 255.0 + fan_controller = self.printer.fans[fan_no].input + else: + fan_controller = self.printer.command_connect["M106"] + - for fan in fans: - # exit any control loop that the fan maybe in - fan.disable() - if gcode.has_letter("R"): # Ramp to value - delay = gcode.get_float_by_letter("R", 0.01) - fan.ramp_to(value, delay) - else: - fan.set_value(value) + if gcode.has_letter("R"): # Ramp to value + delay = gcode.get_float_by_letter("R", 0.01) + fan_controller.ramp_to(value, delay) + else: + fan_controller.set_target_value(value) def get_description(self): return "Set fan power." @@ -51,16 +49,15 @@ def is_buffered(self): class M107(GCodeCommand): def execute(self, gcode): - fans = [] + fan_controller = None if gcode.has_letter("P"): fan_no = gcode.get_int_by_letter("P", 0) if fan_no < len(self.printer.fans): - fans.append(self.printer.fans[fan_no]) - else: # No P in gcode, use fans from settings file - fans = self.printer.controlled_fans - - for fan in fans: - fan.set_value(0) + fan_controller = self.printer.fans[fan_no].input + else: + fan_controller = self.printer.command_connect["M107"] + + fan_controller.set_target_value(0.0) def get_description(self): return "set fan off"