diff --git a/klippy/extras/led_effect.py b/klippy/extras/led_effect.py index 7232d14d7662..59ad3400a8b1 100644 --- a/klippy/extras/led_effect.py +++ b/klippy/extras/led_effect.py @@ -8,48 +8,59 @@ from math import cos, exp, pi from random import randint +import logging -ANALOG_SAMPLE_TIME = 0.001 +ANALOG_SAMPLE_TIME = 0.001 ANALOG_SAMPLE_COUNT = 5 -ANALOG_REPORT_TIME = 0.05 +ANALOG_REPORT_TIME = 0.05 + +COLORS = 4 ###################################################################### # Custom color value list, returns lists of [r, g ,b] values # from a one dimensional list ###################################################################### + class colorArray(list): + def __init__(self, num_colors, kwargs): + self.n = num_colors + super(colorArray, self).__init__(kwargs) + def __getitem__(self, a): if isinstance(a, int): return super(colorArray, self).__getitem__( - slice(a*3, a*3+3)) + slice(a*self.n, a*self.n+self.n)) if isinstance(a, slice): - start = a.start*3 if a.start != None else None - stop = a.stop*3 if a.stop != None else None - return colorArray( - super(colorArray, self).__getitem__( - slice(start, stop, a.step))) + start = a.start*self.n if a.start != None else None + stop = a.stop*self.n if a.stop != None else None + return colorArray(self.n, + super(colorArray, self).__getitem__( + slice(start, stop, a.step))) + def __getslice__(self, a, b): - return self.__getitem__(slice(a,b)) + return self.__getitem__(slice(a, b)) + def __setitem__(self, a, v): if isinstance(a, int): - super(colorArray, self).__setitem__(a*3 , v[0]) - super(colorArray, self).__setitem__(a*3+1, v[1]) - super(colorArray, self).__setitem__(a*3+2, v[2]) + for i in range(self.n): + super(colorArray, self).__setitem__(a*self.n + i, v[i]) + def __len__(self): - return super(colorArray, self).__len__() // 3 + return super(colorArray, self).__len__() // self.n + def reverse(self): - self.__init__([c for cl in range(len(self)-1,-1, -1) - for c in self[cl]]) + self.__init__(self.n, [c for cl in range(len(self)-1, -1, -1) + for c in self[cl]]) + def shift(self, shift=1, direction=True): if direction: - #shift array to the right - self.__init__(self[-shift:] + self[:-shift]) - else: - #shift array to the left - self.__init__(self[shift:] + self[:shift]) + shift *= -1 + self.__init__(self.n, self[shift:] + self[:shift]) + def padLeft(self, v, a): - self.__init__(v * a + self) + self.__init__(self.n, v * a + self) + def padRight(self, v, a): self += v * a @@ -57,29 +68,39 @@ def padRight(self, v, a): # LED Effect handler ###################################################################### + class ledFrameHandler: def __init__(self, config): self.printer = config.get_printer() - self.gcode = self.printer.lookup_object('gcode') + self.gcode = self.printer.lookup_object('gcode') self.printer.load_object(config, "display_status") self.heaters = {} self.printProgress = 0 self.effects = [] - self.stepperPositions = [0.0,0.0,0.0] - self.stepperTimer = None - self.heaterCurrent = {} - self.heaterTarget = {} - self.heaterLast = {} - self.heaterTimer = None + self.stepperPositions = [0.0, 0.0, 0.0] + self.stepperTimer = None + self.heaterCurrent = {} + self.heaterTarget = {} + self.heaterLast = {} + self.heaterTimer = None + self.homing = {} + self.homing_start_flag = {} + self.homing_end_flag = {} self.printer.register_event_handler('klippy:ready', self._handle_ready) - self.ledChains=[] + self.printer.register_event_handler("homing:homing_move_begin", + self._handle_homing_move_begin) + self.printer.register_event_handler("homing:homing_move_end", + self._handle_homing_move_end) + self.ledChains = [] self.gcode.register_command('STOP_LED_EFFECTS', self.cmd_STOP_LED_EFFECTS, desc=self.cmd_STOP_LED_EFFECTS_help) + self.shutdown = False cmd_STOP_LED_EFFECTS_help = 'Stops all led_effects' def _handle_ready(self): + self.shutdown = False self.reactor = self.printer.get_reactor() self.printer.register_event_handler('klippy:shutdown', self._handle_shutdown) @@ -87,30 +108,56 @@ def _handle_ready(self): self.displayStatus = self.printer.lookup_object('display_status') self.progressTimer = self.reactor.register_timer(self._pollProgress, self.reactor.NOW) - self.frameTimer = self.reactor.register_timer(self._getFrames, - self.reactor.NOW) + self.frameTimer = self.reactor.register_timer(self._getFrames, + self.reactor.NOW) def _handle_shutdown(self): - #for effect in self.effects: - # if not effect.run_on_error: - # for chain in self.ledChains: - # chain.color_data = [] * (chain.chain_count * 3) - # chain.send_data() + self.shutdown = True + for effect in self.effects: + if not effect.runOnShutown: + for chain in self.ledChains: + chain.led_helper.set_color(None, (0.0, 0.0, 0.0, 0.0)) + chain.led_helper.update_func( + chain.led_helper.led_state, None) pass + def _handle_homing_move_begin(self, hmove): + endstops_being_homed = [name for es, name in hmove.endstops] + logging.info(endstops_being_homed) + + for endstop in endstops_being_homed: + if endstop in self.homing_start_flag: + self.homing_start_flag[endstop] += 1 + else: + self.homing_start_flag[endstop] = 0 + + self.homing[endstop] = True + + def _handle_homing_move_end(self, hmove): + endstops_being_homed = [name for es, name in hmove.endstops] + + for endstop in endstops_being_homed: + if endstop in self.homing_end_flag: + self.homing_end_flag[endstop] += 1 + else: + self.homing_end_flag[endstop] = 0 + self.homing[endstop] = False + def addEffect(self, effect): if effect.heater: - effect.heater=effect.heater.strip('\"\'') - if effect.heater.startswith("temperature_fan "): - self.heaters[effect.heater] = self.printer.lookup_object(effect.heater) + effect.heater = effect.heater.strip('\"\'') + if effect.heater.startswith("temperature_fan ") or effect.heater.startswith("temperature_sensor "): + self.heaters[effect.heater] = self.printer.lookup_object( + effect.heater) else: pheater = self.printer.lookup_object('heaters') - self.heaters[effect.heater] = pheater.lookup_heater(effect.heater) + self.heaters[effect.heater] = pheater.lookup_heater( + effect.heater) self.heaterLast[effect.heater] = 100 self.heaterCurrent[effect.heater] = 0 - self.heaterTarget[effect.heater] = 0 + self.heaterTarget[effect.heater] = 0 if not self.heaterTimer: self.heaterTimer = self.reactor.register_timer(self._pollHeater, @@ -122,19 +169,19 @@ def addEffect(self, effect): if not self.stepperTimer: self.stepperTimer = self.reactor.register_timer( - self._pollStepper, - self.reactor.NOW) + self._pollStepper, + self.reactor.NOW) self.effects.append(effect) def _pollHeater(self, eventtime): - for heater in self.heaters.iterkeys(): + for heater in self.heaters.keys(): current, target = self.heaters[heater].get_temp(eventtime) self.heaterCurrent[heater] = current - self.heaterTarget[heater] = target + self.heaterTarget[heater] = target if target > 0: self.heaterLast[heater] = target - return eventtime + 1 + return eventtime + 0.3 # sensors get updated every 300ms def _pollStepper(self, eventtime): @@ -146,92 +193,123 @@ def _pollStepper(self, eventtime): for i in range(3): if pos[i] >= self.kin.axes_min[i] and pos[i] <= self.kin.axes_max[i]: self.stepperPositions[i] = int( - (pos[i] / (self.kin.axes_max[i] - self.kin.axes_min[i]) - * 100)- 1) + ((pos[i] - self.kin.axes_min[i]) / + (self.kin.axes_max[i] - self.kin.axes_min[i]) + * 100) - 1) return eventtime + 0.5 def _pollProgress(self, eventtime): status = self.displayStatus.get_status(eventtime) p = status.get('progress') - if p: + if p is not None: self.printProgress = int(p * 100) return eventtime + 1 - # Todo: Make color temperature configurable in Neopixel config and - # maybe move conversion function to Neopixel module - def _rgb_to_rgbw(self, colors, color_temp_of_w = (1.0,0.705,0.420)): - - minWhite = min( colors[0] / color_temp_of_w[0], - colors[1] / color_temp_of_w[1], - colors[2] / color_temp_of_w[2]) - - color_output = [0 for _ in range(4)] - - for i in range(3): - color_output[i] = colors[i] - minWhite * color_temp_of_w[i] - color_output[3] = minWhite - return color_output - - def _getColorData(self, colors, has_white, fade): - clamp = (lambda x : 0.0 if x < 0.0 else 1.0 if x > 1.0 else x) + def _getColorData(self, colors, fade): + clamp = (lambda x: 0.0 if x < 0.0 else 1.0 if x > 1.0 else x) colors = [x*clamp(fade) for x in colors] - if has_white: - colors = self._rgb_to_rgbw(colors) - else: - colors = colors + [0.0] + colors = colors + [0.0] * (4 - len(colors)) + colors = colors[:4] colors = [clamp(x) for x in colors] return tuple(colors) def _getFrames(self, eventtime): chainsToUpdate = set() - #first set all LEDs to 0, that should be updated - for effect in self.effects: - frame, update = effect.getFrame(eventtime) + frames = [(effect, effect.getFrame(eventtime)) + for effect in self.effects] + + # first set all LEDs to 0, that should be updated + for effect, (frame, update) in frames: if update: for i in range(effect.ledCount): - chain,index=effect.leds[i] + chain, index = effect.leds[i] chain.led_helper.led_state[index] = (0.0, 0.0, 0.0, 0.0) chainsToUpdate.add(chain) - #then sum up all effects for that LEDs - for effect in self.effects: - frame, update = effect.getFrame(eventtime) + # then sum up all effects for that LEDs + for effect, (frame, update) in frames: if update: for i in range(effect.ledCount): - chain,index=effect.leds[i] + chain, index = effect.leds[i] - has_white = False - if hasattr(chain, 'color_map'): - has_white = (len(chain.color_map) == 4) + current_state = list(chain.led_helper.led_state[index]) + effect_state = self._getColorData(frame[i*COLORS:i*COLORS+COLORS], + effect.fadeValue) - current_state=list(chain.led_helper.led_state[index]) - effect_state=self._getColorData(frame[i*3:i*3+3], - has_white, - effect.fadeValue) - - next_state=[min(1.0,a+b) for a,b in \ - zip(current_state, effect_state)] + next_state = [min(1.0, a+b) for a, b in + zip(current_state, effect_state)] chain.led_helper.led_state[index] = tuple(next_state) chainsToUpdate.add(chain) for chain in chainsToUpdate: - if hasattr(chain,"prev_data"): - chain.prev_data = None # workaround to force update of dotstars - chain.led_helper.update_func(chain.led_helper.led_state, None) - - next_eventtime=min(self.effects, key=lambda x: x.nextEventTime)\ - .nextEventTime + if hasattr(chain, "prev_data"): + chain.prev_data = None # workaround to force update of dotstars + if not self.shutdown: + chain.led_helper.update_func(chain.led_helper.led_state, None) + if self.effects: + next_eventtime = min(self.effects, key=lambda x: x.nextEventTime)\ + .nextEventTime + else: + next_eventtime = eventtime # run at least with 10Hz - next_eventtime=min(next_eventtime, eventtime + 0.1) + next_eventtime = min(next_eventtime, eventtime + 0.1) return next_eventtime + def parse_chain(self, chain): + chain = chain.strip() + leds = [] + parms = [parameter.strip() for parameter in chain.split() + if parameter.strip()] + if parms: + chainName = parms[0].replace(':', ' ') + ledIndices = ''.join(parms[1:]).strip('()').split(',') + for led in ledIndices: + if led: + if '-' in led: + start, stop = map(int, led.split('-')) + if stop == start: + ledList = [start-1] + elif stop > start: + ledList = list(range(start-1, stop)) + else: + ledList = list(reversed(range(stop-1, start))) + for i in ledList: + leds.append(int(i)) + else: + for i in led.split(','): + leds.append(int(i)-1) + + return chainName, leds + else: + return None, None + def cmd_STOP_LED_EFFECTS(self, gcmd): + ledParam = gcmd.get('LEDS', "") + stopAll = (ledParam == "") + for effect in self.effects: - if effect.enabled: - effect.set_fade_time(gcmd.get_float('FADETIME', 0.0)) - effect.set_enabled(False) + stopEffect = stopAll + if not stopAll: + try: + chainName, ledIndices = self.parse_chain(ledParam) + chain = self.printer.lookup_object(chainName) + except Exception as e: + raise gcmd.error("Unknown LED '%s'" % (ledParam,)) + + if ledIndices == [] and chain in effect.ledChains: + stopEffect = True + else: + for index in ledIndices: + if (chain, index) in effect.leds: + stopEffect = True + + if stopEffect: + if effect.enabled: + effect.set_fade_time(gcmd.get_float('FADETIME', 0.0)) + effect.set_enabled(False) + def load_config(config): return ledFrameHandler(config) @@ -240,149 +318,159 @@ def load_config(config): # LED Effect ###################################################################### + class ledEffect: def __init__(self, config): - self.config = config - self.printer = config.get_printer() - self.gcode = self.printer.lookup_object('gcode') - self.handler = self.printer.load_object(config, 'led_effect') - self.frameRate = 1.0 / config.getfloat('frame_rate', - default=24, minval=1, maxval=60) - self.enabled = False - self.iteration = 0 - self.layers = [] - self.analogValue = 0 - self.fadeValue = 1.0 - self.fadeTime = 0.0 - self.fadeEndTime = 0 - - #Basic functions for layering colors. t=top and b=bottom color - self.blendingModes = { - 'top' : (lambda t, b: t ), - 'bottom' : (lambda t, b: b ), - 'add' : (lambda t, b: t + b ), - 'subtract' : (lambda t, b: (b - t) * (b - t > 0)), + self.config = config + self.printer = config.get_printer() + self.gcode = self.printer.lookup_object('gcode') + self.handler = self.printer.load_object(config, 'led_effect') + self.frameRate = 1.0 / config.getfloat('frame_rate', + default=24, minval=1, maxval=60) + self.enabled = False + self.iteration = 0 + self.layers = [] + self.analogValue = 0 + self.fadeValue = 1.0 + self.fadeTime = 0.0 + self.fadeEndTime = 0 + + # Basic functions for layering colors. t=top and b=bottom color + self.blendingModes = { + 'top': (lambda t, b: t), + 'bottom': (lambda t, b: b), + 'add': (lambda t, b: t + b), + 'subtract': (lambda t, b: (b - t) * (b - t > 0)), 'subtract_b': (lambda t, b: (t - b) * (t - b > 0)), 'difference': (lambda t, b: (t - b) * (t > b) + (b - t) * (t <= b)), - 'average' : (lambda t, b: 0.5 * (t + b)), - 'multiply' : (lambda t, b: t * b), - 'divide' : (lambda t, b: t / b if b > 0 else 0 ), - 'divide_inv': (lambda t, b: b / t if t > 0 else 0 ), - 'screen' : (lambda t, b: 1.0 - (1.0-t)*(1.0-b) ), - 'lighten' : (lambda t, b: t * (t > b) + b * (t <= b)), - 'darken' : (lambda t, b: t * (t < b) + b * (t >= b)), - 'overlay' : (lambda t, b: \ - 2.0 * t * b if t > 0.5 else \ - 1.0 - (2.0 * (1.0-t) * (1.0-b))) - } - - self.name = config.get_name().split()[1] - - self.autoStart = config.getboolean('autostart', False) + 'average': (lambda t, b: 0.5 * (t + b)), + 'multiply': (lambda t, b: t * b), + 'divide': (lambda t, b: t / b if b > 0 else 0), + 'divide_inv': (lambda t, b: b / t if t > 0 else 0), + 'screen': (lambda t, b: 1.0 - (1.0-t)*(1.0-b)), + 'lighten': (lambda t, b: t * (t > b) + b * (t <= b)), + 'darken': (lambda t, b: t * (t < b) + b * (t >= b)), + 'overlay': (lambda t, b: + 2.0 * t * b if t > 0.5 else + 1.0 - (2.0 * (1.0-t) * (1.0-b))) + } + + self.name = config.get_name().split()[1] + + self.autoStart = config.getboolean('autostart', False) self.runOnShutown = config.getboolean('run_on_error', False) - self.heater = config.get('heater', None) - self.analogPin = config.get('analog_pin', None) - self.stepper = config.get('stepper', None) + self.heater = config.get('heater', None) + self.analogPin = config.get('analog_pin', None) + self.stepper = config.get('stepper', None) + self.endstops = [x.strip() + for x in config.get('endstops', '').split(',')] self.configLayers = config.get('layers') - self.configLeds = config.get('leds') + self.configLeds = config.get('leds') self.nextEventTime = 0 self.printer.register_event_handler('klippy:ready', self._handle_ready) self.gcode.register_mux_command('SET_LED_EFFECT', 'EFFECT', self.name, - self.cmd_SET_LED_EFFECT, - desc=self.cmd_SET_LED_help) + self.cmd_SET_LED_EFFECT, + desc=self.cmd_SET_LED_help) if self.analogPin: ppins = self.printer.lookup_object('pins') self.mcu_adc = ppins.setup_pin('adc', self.analogPin) self.mcu_adc.setup_minmax(ANALOG_SAMPLE_TIME, ANALOG_SAMPLE_COUNT) - self.mcu_adc.setup_adc_callback(ANALOG_REPORT_TIME, self.adcCallback) + self.mcu_adc.setup_adc_callback( + ANALOG_REPORT_TIME, self.adcCallback) query_adc = self.printer.load_object(self.config, 'query_adc') query_adc.register_adc(self.name, self.mcu_adc) cmd_SET_LED_help = 'Starts or Stops the specified led_effect' def _handle_ready(self): - chains = self.configLeds.split('\n') - self.ledChains = [] - self.leds = [] + self.configChains = self.configLeds.split('\n') + self.ledChains = [] + self.leds = [] self.enabled = self.autoStart - #map each LED from the chains to the "pixels" in the effect frame - for chain in chains: - chain = chain.strip() - parms = [parameter.strip() for parameter in chain.split() - if parameter.strip()] - - if parms: - ledChain = self.printer.lookup_object(parms[0]\ - .replace(':',' ')) - ledIndices = ''.join(parms[1:]).strip('()').split(',') - - #Add each discrete chain to the collection + self.printer.register_event_handler('klippy:shutdown', + self._handle_shutdown) + # map each LED from the chains to the "pixels" in the effect frame + for chain in self.configChains: + chainName, ledIndices = self.handler.parse_chain(chain) + if chainName is not None: + ledChain = self.printer.lookup_object(chainName) + + # Add each discrete chain to the collection if ledChain not in self.ledChains: self.ledChains.append(ledChain) - - for led in ledIndices: - if led: - if '-' in led: - start, stop = map(int,led.split('-')) - if stop == start: - ledList = [start-1] - elif stop > start: - ledList = list(range(start-1, stop)) - else: - ledList = list(reversed(range(stop-1, start))) - for i in ledList: - self.leds.append((ledChain, int(i))) - else: - for i in led.split(','): - self.leds.append((ledChain, \ - (int(i)-1))) - else: - for i in range(ledChain.chain_count): - self.leds.append((ledChain, int(i))) + if ledIndices == []: + for i in range(ledChain.led_helper.get_led_count()): + self.leds.append((ledChain, int(i))) + else: + for led in ledIndices: + self.leds.append((ledChain, led)) self.ledCount = len(self.leds) - self.frame = [0.0] * 3 * self.ledCount - - #enumerate all effects from the subclasses of _layerBase... - availableLayers = {str(c).rpartition('.layer')[2]\ - .replace("'>", "")\ - .lower() : c - for c in self._layerBase.__subclasses__() - if str(c).startswith("", "") + .lower(): c + for c in self._layerBase.__subclasses__() + if str(c).startswith(" COLORS: + raise Exception( + "Color %s has too many elements." % (str(i),)) + # pad to COLORS colors + palette = [pad(c) for c in palette] + # flatten list + palette = [k for c in palette for k in c] + except Exception as e: + raise self.printer.config_error( + "Error parsing palette in '%s' for layer \"%s\": %s" + % (self.config.get_name(), parms[0], e,)) + self.layers.insert(0, layer(handler=self, + frameHandler=self.handler, + effectRate=float(parms[1]), + effectCutoff=float(parms[2]), + paletteColors=palette, + frameRate=self.frameRate, + ledCount=len(self.leds), + blendingMode=parms[3])) self.handler.addEffect(self) @@ -391,25 +479,27 @@ def getFrame(self, eventtime): if self.nextEventTime < self.handler.reactor.NEVER: # Effect has just been disabled. Set colors to 0 and update once. self.nextEventTime = self.handler.reactor.NEVER - self.frame = [0.0] * 3 * self.ledCount + self.frame = [0.0] * COLORS * self.ledCount update = True else: update = False else: update = True - if eventtime > self.nextEventTime: + if eventtime >= self.nextEventTime: self.nextEventTime = eventtime + self.frameRate - self.frame = [0.0] * 3 * self.ledCount + self.frame = [0.0] * COLORS * self.ledCount for layer in self.layers: layerFrame = layer.nextFrame(eventtime) if layerFrame: blend = self.blendingModes[layer.blendingMode] - self.frame = [blend(t, b) for t, b in zip(layerFrame, self.frame)] + self.frame = [blend(t, b) + for t, b in zip(layerFrame, self.frame)] if (self.fadeEndTime > eventtime) and (self.fadeTime > 0.0): - remainingFade = ((self.fadeEndTime - eventtime) / self.fadeTime) + remainingFade = ( + (self.fadeEndTime - eventtime) / self.fadeTime) else: remainingFade = 0.0 @@ -418,22 +508,35 @@ def getFrame(self, eventtime): return self.frame, update def set_enabled(self, state): - self.enabled = state - if self.enabled: + if self.enabled != state: + self.enabled = state self.nextEventTime = self.handler.reactor.NOW + self.handler._getFrames(self.handler.reactor.NOW) def set_fade_time(self, fadetime): self.fadeTime = fadetime self.fadeEndTime = self.handler.reactor.monotonic() + fadetime + if self.fadeTime == 0.0: + self.fadeValue = 0.0 def cmd_SET_LED_EFFECT(self, gcmd): - self.set_fade_time(gcmd.get_float('FADETIME', 0.0)) - if gcmd.get_int('STOP', 0) == 1: + parmFadeTime = gcmd.get_float('FADETIME', 0.0) + + if gcmd.get_int('STOP', 0) >= 1: if self.enabled: - self.set_fade_time(gcmd.get_float('FADETIME', 0.0)) + self.set_fade_time(parmFadeTime) self.set_enabled(False) else: - self.set_fade_time(gcmd.get_float('FADETIME', 0.0)) + if gcmd.get_int('REPLACE', 0) >= 1: + for led in self.leds: + for effect in self.handler.effects: + if effect is not self and led in effect.leds: + if effect.enabled: + effect.set_fade_time(parmFadeTime) + effect.set_enabled(False) + + if not self.enabled: + self.set_fade_time(parmFadeTime) self.set_enabled(True) def _handle_shutdown(self): @@ -451,23 +554,23 @@ def adcCallback(self, read_time, read_value): # per call of nextFrame() class _layerBase(object): def __init__(self, **kwargs): - self.handler = kwargs['handler'] - self.frameHandler = kwargs['frameHandler'] - self.ledCount = kwargs['ledCount'] - self.paletteColors = colorArray(kwargs['paletteColors']) - self.effectRate = kwargs['effectRate'] - self.effectCutoff = kwargs['effectCutoff'] - self.frameRate = kwargs['frameRate'] - self.blendingMode = kwargs['blendingMode'] - self.frameNumber = 0 - self.thisFrame = [] - self.frameCount = 1 - self.lastAnalog = 0 + self.handler = kwargs['handler'] + self.frameHandler = kwargs['frameHandler'] + self.ledCount = kwargs['ledCount'] + self.paletteColors = colorArray(COLORS, kwargs['paletteColors']) + self.effectRate = kwargs['effectRate'] + self.effectCutoff = kwargs['effectCutoff'] + self.frameRate = kwargs['frameRate'] + self.blendingMode = kwargs['blendingMode'] + self.frameNumber = 0 + self.thisFrame = [] + self.frameCount = 1 + self.lastAnalog = 0 def nextFrame(self, eventtime): self.frameNumber += 1 self.frameNumber = self.frameNumber * \ - ( self.frameNumber < self.frameCount ) + (self.frameNumber < self.frameCount) self.lastFrameTime = eventtime return self.thisFrame[self.frameNumber] @@ -479,20 +582,21 @@ def _decayTable(self, factor=1, rate=1): p = (1.0 / self.frameRate) r = (p/15.0)*factor - for s in range(0, int((rate<1)+rate)): + for s in range(0, int((rate < 1)+rate)): frame.append(1.0) - for x in range(2, int(p / rate)+3): + for x in range(2, int(p / rate)): b = exp(1)**-(x/r) - frame.append(b*(b>.01)) - + if b > .004: + frame.append(b) return frame def _gradient(self, palette, steps, reverse=False, toFirst=False): - palette = colorArray(palette[:]) - if reverse: palette.reverse() + palette = colorArray(COLORS, palette[:]) + if reverse: + palette.reverse() if len(palette) == 1: - return colorArray(palette * steps) + return colorArray(COLORS, palette * steps) if toFirst: palette += palette[0] @@ -504,57 +608,58 @@ def _gradient(self, palette, steps, reverse=False, toFirst=False): else: intervals_per_step = 0 - gradient=palette[0] + gradient = palette[0] - for i in range(1,steps): + for i in range(1, steps): j = intervals_per_step * i k = int(j) r = j-k k = min(k, len(palette)-1) - if ( (k+1) >= len(palette) ) | (r == 0.0) : + if ((k+1) >= len(palette)) | (r == 0.0): z = palette[k] else: - z = [((1-r)*palette[k][m] + r*palette[k+1][m]) for m in range(3)] + z = [((1-r)*palette[k][m] + r*palette[k+1][m]) + for m in range(COLORS)] gradient += z return gradient - #Individual effects inherit from the LED Effect Base class - #each effect must support the nextFrame() method either by - #using the method from the base class or overriding it. + # Individual effects inherit from the LED Effect Base class + # each effect must support the nextFrame() method either by + # using the method from the base class or overriding it. - #Solid color + # Solid color class layerStatic(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerStatic, self).__init__(**kwargs) - self.paletteColors = colorArray(self.paletteColors) + self.paletteColors = colorArray(COLORS, self.paletteColors) gradientLength = int(self.ledCount) - gradient = colorArray(self._gradient(self.paletteColors, - gradientLength)) + gradient = colorArray(COLORS, self._gradient(self.paletteColors, + gradientLength)) self.thisFrame.append(gradient[0:self.ledCount]) self.frameCount = len(self.thisFrame) - #Slow pulsing of color + # Slow pulsing of color class layerBreathing(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerBreathing, self).__init__(**kwargs) brightness = [] - p = (1.0 / self.frameRate) * (self.effectRate * 0.5) + p = (1 / self.frameRate) * (self.effectRate * 0.5) o = int(p) f = 2 * pi for x in range(0, int(p)): if x < p: - v = (exp(-cos((f / p) * (x+o)))-0.367879) / 2.35040238 + v = (exp(-cos((f / p) * (x+o)))-0.367879) / 2.35040238 else: v = 0 - #clamp values + # clamp values if v > 1.0: v = 1.0 elif v < 0.0: @@ -569,46 +674,48 @@ def __init__(self, **kwargs): self.thisFrame += [[b * i for i in color] * self.ledCount] self.frameCount = len(self.thisFrame) + class layerLinearFade(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerLinearFade, self).__init__(**kwargs) gradientLength = int(self.effectRate / self.frameRate) - if gradientLength == 0: gradientLength = 1 + if gradientLength == 0: + gradientLength = 1 - gradient = colorArray(self._gradient(self.paletteColors, - gradientLength, toFirst=True)) + gradient = colorArray(COLORS, self._gradient(self.paletteColors, + gradientLength, toFirst=True)) for i in range(gradientLength): self.thisFrame.append(gradient[i]*self.ledCount) self.frameCount = len(self.thisFrame) - #Turns the entire strip on and off + # Turns the entire strip on and off class layerBlink(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerBlink, self).__init__(**kwargs) - dutyCycle= max(0,min(1.0, self.effectCutoff)) - frameCountOn = int(( 1.0 / self.frameRate ) * self.effectRate\ - * dutyCycle) - frameCountOff = int(( 1.0 / self.frameRate ) * self.effectRate\ - * (1-dutyCycle)) + dutyCycle = max(0, min(1.0, self.effectCutoff)) + frameCountOn = int((1.0 / self.frameRate) * self.effectRate + * dutyCycle) + frameCountOff = int((1.0 / self.frameRate) * self.effectRate + * (1-dutyCycle)) for c in range(0, len(self.paletteColors)): color = self.paletteColors[c] self.thisFrame += [color * self.ledCount] * frameCountOn - self.thisFrame += [[0,0,0] * self.ledCount] * frameCountOff + self.thisFrame += [[0]*COLORS * self.ledCount] * frameCountOff self.frameCount = len(self.thisFrame) - #Random flashes with decay + # Random flashes with decay class layerTwinkle(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerTwinkle, self).__init__(**kwargs) - self.thisFrame = colorArray([0.0, 0.0, 0.0] * self.ledCount) - self.lastBrightness = [-1] * self.ledCount + self.thisFrame = colorArray(COLORS, ([0.0]*COLORS) * self.ledCount) + self.lastBrightness = [-1] * self.ledCount self.decayTable = self._decayTable(factor=1 / self.effectCutoff) self.decayLen = len(self.decayTable) self.colorCount = len(self.paletteColors) - 1 @@ -627,30 +734,33 @@ def nextFrame(self, eventtime): if self.lastBrightness[i] != -1: if self.lastBrightness[i] == self.decayLen: self.lastBrightness[i] = -1 - self.thisFrame[i] = [0.0, 0.0, 0.0] + self.thisFrame[i] = ([0.0]*COLORS) else: x = self.lastBrightness[i] self.lastBrightness[i] += 1 self.thisFrame[i] = [self.decayTable[x] * l - for l in self.thisFrame[i]] + for l in self.thisFrame[i]] return self.thisFrame - #Blinking with decay + # Blinking with decay class layerStrobe(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerStrobe, self).__init__(**kwargs) - frameRate = int(1.0 / self.frameRate) - frameCount = int(frameRate * self.effectRate) - + frameRate = int(1.0 / self.frameRate) + if self.effectRate == 0: + frameCount = 1 + else: + frameCount = max(1, int(frameRate / self.effectRate)) + if self.effectCutoff == 0: + self.effectCutoff = 0.001 decayTable = self._decayTable(factor=1 / self.effectCutoff, - rate=self.effectRate) - - if len(decayTable) > frameRate: - decayTable = decayTable[:frameRate] + rate=1) + if len(decayTable) > frameCount: + decayTable = decayTable[:frameCount] else: - decayTable += [0.0] * (self.frameRate - len(decayTable)) + decayTable += [0.0] * (frameCount - len(decayTable)) for c in range(0, len(self.paletteColors)): color = self.paletteColors[c] @@ -660,7 +770,7 @@ def __init__(self, **kwargs): self.frameCount = len(self.thisFrame) - #Lights move sequentially with decay + # Lights move sequentially with decay class layerComet(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerComet, self).__init__(**kwargs) @@ -670,22 +780,27 @@ def __init__(self, **kwargs): self.direction = False self.effectRate *= -1 - if self.effectCutoff <= 0: self.effectCutoff = .1 + if self.effectCutoff <= 0: + self.effectCutoff = .1 - decayTable = self._decayTable(factor=len(self.paletteColors) * \ - self.effectCutoff, rate=1) + decayTable = self._decayTable(factor=len(self.paletteColors) * + self.effectCutoff, rate=1) - gradient = self.paletteColors[0] + \ + gradient = self.paletteColors[0] + \ self._gradient(self.paletteColors[1:], len(decayTable)+1) - decayTable = [c for b in zip(decayTable, decayTable, decayTable) \ - for c in b] + decayTable = [c for b in zip(decayTable, decayTable, decayTable, decayTable) + for c in b] - comet = colorArray([a * b for a, b in zip(gradient,decayTable)]) + comet = colorArray( + COLORS, [a * b for a, b in zip(gradient, decayTable)]) - comet.padRight([0.0,0.0,0.0], self.ledCount) + comet.padRight([0.0]*COLORS, self.ledCount - len(comet)) - if self.direction: comet.reverse() + if self.direction: + comet.reverse() + else: + comet.shift(self.ledCount - len(comet)) if self.effectRate == 0: self.thisFrame.append(comet[0:self.ledCount]) @@ -693,14 +808,14 @@ def __init__(self, **kwargs): for i in range(len(comet)): comet.shift(int(self.effectRate+(self.effectRate < 1)), self.direction) - self.thisFrame.append(comet[0:self.ledCount]) + self.thisFrame.append(comet[:self.ledCount]) for x in range(int((1/self.effectRate)-(self.effectRate <= 1))): - self.thisFrame.append(comet[0:self.ledCount]) + self.thisFrame.append(comet[:self.ledCount]) self.frameCount = len(self.thisFrame) - #Lights move sequentially with decay + # Lights move sequentially with decay class layerChase(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerChase, self).__init__(**kwargs) @@ -711,65 +826,85 @@ def __init__(self, **kwargs): self.direction = False self.effectRate *= -1 - decayTable = self._decayTable(factor=len(self.paletteColors) * \ - self.effectCutoff, rate=1) + if len(self.paletteColors) == 1: + self.paletteColors += colorArray(COLORS, COLORS*[0]) + + decayTable = self._decayTable(factor=len(self.paletteColors) * + self.effectCutoff, rate=1) - gradient = self.paletteColors[0] + \ + gradient = self.paletteColors[0] + \ self._gradient(self.paletteColors[1:], len(decayTable)+1) - decayTable = [c for b in zip(decayTable, decayTable, decayTable) \ - for c in b] - gradient = colorArray([a * b - for a, b in zip(gradient,decayTable)]) - - chase = gradient + decayTable = [c for b in zip(decayTable, decayTable, decayTable, decayTable) + for c in b] + gradient = colorArray(COLORS, [a * b + for a, b in zip(gradient, decayTable)]) - for i in range(int(self.ledCount/len(gradient))): - chase += gradient + k = int(self.ledCount/len(gradient))+1 + chase = colorArray(COLORS, k*gradient) - if self.direction: chase.reverse() + if self.direction: + chase.reverse() if self.effectRate == 0: self.thisFrame.append(chase[0:self.ledCount]) else: - for i in range(len(chase)): + for _ in range(len(chase)): chase.shift(int(self.effectRate+(self.effectRate < 1)), self.direction) self.thisFrame.append(chase[0:self.ledCount]) - for x in range(int((1/self.effectRate)-(self.effectRate <= 1))): + for _ in range(int((1/self.effectRate)-(self.effectRate <= 1))): self.thisFrame.append(chase[0:self.ledCount]) self.frameCount = len(self.thisFrame) - #Color gradient over all LEDs + # Color gradient over all LEDs class layerGradient(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerGradient, self).__init__(**kwargs) - if self.effectRate > 0: - self.direction = True - else: - self.direction = False - self.effectRate *= -1 - - gradientLength = int(self.ledCount) - gradient = colorArray(self._gradient(self.paletteColors, - gradientLength, toFirst=True)) + direction = -1 if self.effectRate < 0 else 1 if self.effectRate == 0: - self.thisFrame.append(gradient[0:self.ledCount]) + gradientLength = self.ledCount else: - for i in range(len(gradient)): - gradient.shift(int(self.effectRate+(self.effectRate < 1)), - self.direction) - self.thisFrame.append(gradient[0:self.ledCount]) + gradientLength = abs(int(1/(self.effectRate * self.frameRate))) + gradient = colorArray(COLORS, self._gradient(self.paletteColors, + gradientLength, + toFirst=True)) + + for i in range(gradientLength if self.effectRate != 0 else 1): + frame = colorArray(COLORS, ([0.0]*COLORS) * self.ledCount) + for led in range(self.ledCount): + frame[led] = gradient[int(i*direction + + self.effectCutoff * gradientLength * led + / self.ledCount) % gradientLength] + self.thisFrame.append(frame) - for x in range(int((1/self.effectRate)-(self.effectRate <= 1))): - self.thisFrame.append(gradient[0:self.ledCount]) + self.frameCount = len(self.thisFrame) + + class layerPattern(_layerBase): + def __init__(self, **kwargs): + super(ledEffect.layerPattern, self).__init__(**kwargs) + + self.paletteColors = colorArray(COLORS, self.paletteColors) + frame = colorArray(COLORS, []) + + for i in range(int(self.ledCount/len(self.paletteColors))+1): + frame += (self.paletteColors) + + if int(self.effectRate/self.frameRate) == 0: + self.thisFrame.append(frame) + else: + for _ in range(len(self.paletteColors) * (self.ledCount-1)): + for _ in range(int(self.effectRate/self.frameRate)): + self.thisFrame.append(colorArray(COLORS, frame)[ + :COLORS*self.ledCount]) + frame.shift(int(self.effectCutoff)) self.frameCount = len(self.thisFrame) - #Responds to heater temperature + # Responds to heater temperature class layerHeater(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerHeater, self).__init__(**kwargs) @@ -777,8 +912,8 @@ def __init__(self, **kwargs): if len(self.paletteColors) == 1: self.paletteColors += self.paletteColors - gradient = colorArray(self._gradient(self.paletteColors[:-1], 200) + - self.paletteColors[-1:]) + gradient = colorArray(COLORS, self._gradient(self.paletteColors[:-1], 200) + + self.paletteColors[-1:]) for i in range(len(gradient)): self.thisFrame.append(gradient[i] * self.ledCount) @@ -786,47 +921,68 @@ def __init__(self, **kwargs): self.frameCount = len(self.thisFrame) def nextFrame(self, eventtime): - if self.frameHandler.heaterTarget[self.handler.heater] > 0.0 and \ - self.frameHandler.heaterCurrent[self.handler.heater] > 0.0: - - if self.frameHandler.heaterCurrent[self.handler.heater] <= \ - self.frameHandler.heaterTarget[self.handler.heater]-5: + heaterTarget = self.frameHandler.heaterTarget[self.handler.heater] + heaterCurrent = self.frameHandler.heaterCurrent[self.handler.heater] + heaterLast = self.frameHandler.heaterLast[self.handler.heater] - s = int((self.frameHandler. - heaterCurrent[self.handler.heater] / - self.frameHandler. - heaterTarget[self.handler.heater]) * 200) - s = min(len(self.thisFrame)-1,s) - return self.thisFrame[s] - elif self.effectCutoff > 0: - return None + if heaterTarget > 0.0 and heaterCurrent > 0.0: + if (heaterCurrent >= self.effectRate): + if (heaterCurrent <= heaterTarget-2): + s = int( + ((heaterCurrent - self.effectRate) / heaterTarget) * 200) + s = min(len(self.thisFrame)-1, s) + return self.thisFrame[s] + elif self.effectCutoff > 0: + return None + else: + return self.thisFrame[-1] else: - return self.thisFrame[-1] - elif self.effectRate > 0 and \ - self.frameHandler.heaterCurrent[self.handler.heater] > 0.0: - if self.frameHandler.heaterCurrent[self.handler.heater] >= \ - self.effectRate and \ - self.frameHandler.heaterLast[self.handler.heater] > 0: - - s = int(((self.frameHandler. - heaterCurrent[self.handler.heater] - - self.effectRate) / - self.frameHandler. - heaterLast[self.handler.heater]) * 200) - s = min(len(self.thisFrame)-1,s) + return None + + elif self.effectRate > 0 and heaterCurrent > 0.0: + if heaterCurrent >= self.effectRate and heaterLast > 0: + s = int(((heaterCurrent - self.effectRate) / heaterLast) * 200) + s = min(len(self.thisFrame)-1, s) return self.thisFrame[s] return None - #Responds to analog pin voltage + # Responds to heater temperature + class layerTemperature(_layerBase): + def __init__(self, **kwargs): + + super(ledEffect.layerTemperature, self).__init__(**kwargs) + if len(self.paletteColors) == 1: + self.paletteColors = colorArray( + COLORS, ([0.0]*COLORS)) + self.paletteColors + gradient = colorArray( + COLORS, self._gradient(self.paletteColors, 200)) + for i in range(len(gradient)): + self.thisFrame.append(gradient[i] * self.ledCount) + self.frameCount = len(self.thisFrame) + + def nextFrame(self, eventtime): + if self.effectCutoff == self.effectRate: + s = 200 if self.frameHandler.heaterCurrent[self.handler.heater] >= self.effectRate else 0 + else: + s = int(((self.frameHandler.heaterCurrent[self.handler.heater] - + self.effectRate) / + (self.effectCutoff - self.effectRate)) * 200) + + s = min(len(self.thisFrame)-1, s) + s = max(0, s) + return self.thisFrame[s] + + # Responds to analog pin voltage class layerAnalogPin(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerAnalogPin, self).__init__(**kwargs) if len(self.paletteColors) == 1: - self.paletteColors = [0.0,0.0,0.0] + self.paletteColors + self.paletteColors = [0.0]*COLORS + self.paletteColors - gradient = colorArray(self._gradient(self.paletteColors, 101)) + gradient = colorArray( + COLORS, self._gradient(self.paletteColors, 101)) for i in range(len(gradient)): self.thisFrame.append(gradient[i] * self.ledCount) @@ -834,14 +990,15 @@ def __init__(self, **kwargs): def nextFrame(self, eventtime): v = int(self.handler.analogValue * self.effectRate) - if v > 100: v = 100 + if v > 100: + v = 100 if v > self.effectCutoff: return self.thisFrame[v] else: return self.thisFrame[0] - #Lights illuminate relative to stepper position + # Lights illuminate relative to stepper position class layerStepper(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerStepper, self).__init__(**kwargs) @@ -853,25 +1010,26 @@ def __init__(self, **kwargs): self.effectCutoff = self.ledCount if self.effectRate == 0: - trailing = colorArray([0.0,0.0,0.0] * self.ledCount) + trailing = colorArray(COLORS, [0.0]*COLORS * self.ledCount) else: - trailing = colorArray(self._gradient(self.paletteColors[1:], - int(self.effectRate), True)) - trailing.padLeft([0.0,0.0,0.0], self.ledCount) + trailing = colorArray(COLORS, self._gradient(self.paletteColors[1:], + int(self.effectRate), True)) + trailing.padLeft([0.0]*COLORS, self.ledCount) if self.effectCutoff == 0: - leading = colorArray([0.0,0.0,0.0] * self.ledCount) + leading = colorArray(COLORS, [0.0]*COLORS * self.ledCount) else: - leading = colorArray(self._gradient(self.paletteColors[1:], - int(self.effectCutoff), False)) - leading.padRight([0.0,0.0,0.0], self.ledCount) + leading = colorArray(COLORS, self._gradient(self.paletteColors[1:], + int(self.effectCutoff), False)) + leading.padRight([0.0]*COLORS, self.ledCount) - gradient = colorArray(trailing + self.paletteColors[0] + leading) + gradient = colorArray( + COLORS, trailing + self.paletteColors[0] + leading) gradient.shift(len(trailing)-1, 0) frames = [gradient[:self.ledCount]] for i in range(0, self.ledCount): - gradient.shift(1,1) + gradient.shift(1, 1) frames.append(gradient[:self.ledCount]) for i in range(101): @@ -881,29 +1039,65 @@ def __init__(self, **kwargs): self.frameCount = len(self.thisFrame) def nextFrame(self, eventtime): - if self.handler.stepper == 'x': axis = 0 - elif self.handler.stepper == 'y': axis = 1 - else: axis = 2 + if self.handler.stepper == 'x': + axis = 0 + elif self.handler.stepper == 'y': + axis = 1 + else: + axis = 2 p = self.frameHandler.stepperPositions[int(axis)] - if p < 0 : p=0 - if p > 100 : p=100 + if p < 0: + p = 0 + if p > 100: + p = 100 return self.thisFrame[int((p - 1) * (p > 0))] - #Shameless port of Fire2012 by Mark Kriegsman + class layerStepperColor(_layerBase): + def __init__(self, **kwargs): + super(ledEffect.layerStepperColor, self).__init__(**kwargs) + + if len(self.paletteColors) == 1: + self.paletteColors = [0.0]*COLORS + self.paletteColors + + gradient = colorArray( + COLORS, self._gradient(self.paletteColors, 101)) - #Shamelessly appropriated from the Arduino FastLED example files - #Fire2012.ino by Daniel Garcia + for i in range(len(gradient)): + self.thisFrame.append(gradient[i] * self.ledCount) + + def nextFrame(self, eventtime): + if self.handler.stepper == 'x': + axis = 0 + elif self.handler.stepper == 'y': + axis = 1 + else: + axis = 2 + + p = self.frameHandler.stepperPositions[int( + axis)]*self.effectRate+self.effectCutoff + + if p < 0: + p = 0 + if p > 100: + p = 100 + + return self.thisFrame[int(p)] + + # Shameless port of Fire2012 by Mark Kriegsman + + # Shamelessly appropriated from the Arduino FastLED example files + # Fire2012.ino by Daniel Garcia class layerFire(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerFire, self).__init__(**kwargs) - self.heatMap = [0.0] * self.ledCount - self.gradient = colorArray(self._gradient(self.paletteColors, - 102)) - self.frameLen = len(self.gradient) - self.heatLen = len(self.heatMap) + self.heatMap = [0.0] * self.ledCount + self.gradient = colorArray(COLORS, self._gradient(self.paletteColors, + 102)) + self.frameLen = len(self.gradient) + self.heatLen = len(self.heatMap) self.heatSource = int(self.ledCount / 10.0) self.effectRate = int(self.effectRate) @@ -914,19 +1108,19 @@ def nextFrame(self, eventtime): frame = [] for h in range(self.heatLen): - c = randint(0,self.effectCutoff) - self.heatMap[h] -= (self.heatMap[h] - c >= 0 ) * c + c = randint(0, self.effectCutoff) + self.heatMap[h] -= (self.heatMap[h] - c >= 0) * c for i in range(self.ledCount - 1, self.heatSource, -1): d = (self.heatMap[i - 1] + self.heatMap[i - 2] + - self.heatMap[i - 3] ) / 3 + self.heatMap[i - 3]) / 3 self.heatMap[i] = d * (d >= 0) if randint(0, 100) < self.effectRate: h = randint(0, self.heatSource) - self.heatMap[h] += randint(90,100) + self.heatMap[h] += randint(90, 100) if self.heatMap[h] > 100: self.heatMap[h] = 100 @@ -935,16 +1129,16 @@ def nextFrame(self, eventtime): return frame - #Fire that responds relative to actual vs target temp + # Fire that responds relative to actual vs target temp class layerHeaterFire(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerHeaterFire, self).__init__(**kwargs) - self.heatMap = [0.0] * self.ledCount - self.gradient = colorArray(self._gradient(self.paletteColors, - 102)) - self.frameLen = len(self.gradient) - self.heatLen = len(self.heatMap) + self.heatMap = [0.0] * self.ledCount + self.gradient = colorArray(COLORS, self._gradient(self.paletteColors, + 102)) + self.frameLen = len(self.gradient) + self.heatLen = len(self.heatMap) self.heatSource = int(self.ledCount / 10.0) self.effectRate = 0 @@ -954,12 +1148,12 @@ def __init__(self, **kwargs): def nextFrame(self, eventtime): frame = [] spark = 0 - heaterTarget = self.frameHandler.heaterTarget[self.handler.heater] + heaterTarget = self.frameHandler.heaterTarget[self.handler.heater] heaterCurrent = self.frameHandler.heaterCurrent[self.handler.heater] - heaterLast = self.frameHandler.heaterLast[self.handler.heater] + heaterLast = self.frameHandler.heaterLast[self.handler.heater] if heaterTarget > 0.0 and heaterCurrent > 0.0: - if heaterCurrent <= heaterTarget-5: + if heaterCurrent <= heaterTarget-2: spark = int((heaterCurrent / heaterTarget) * 80) brightness = int((heaterCurrent / heaterTarget) * 100) elif self.effectCutoff > 0: @@ -970,7 +1164,7 @@ def nextFrame(self, eventtime): elif self.effectRate > 0 and heaterCurrent > 0.0: if heaterCurrent >= self.effectRate: spark = int(((heaterCurrent - self.effectRate) - / heaterLast) * 80) + / heaterLast) * 80) brightness = int(((heaterCurrent - self.effectRate) / heaterLast) * 100) @@ -979,12 +1173,12 @@ def nextFrame(self, eventtime): for h in range(self.heatLen): c = randint(0, cooling) - self.heatMap[h] -= (self.heatMap[h] - c >= 0 ) * c + self.heatMap[h] -= (self.heatMap[h] - c >= 0) * c for i in range(self.ledCount - 1, self.heatSource, -1): d = (self.heatMap[i - 1] + self.heatMap[i - 2] + - self.heatMap[i - 3] ) / 3 + self.heatMap[i - 3]) / 3 self.heatMap[i] = d * (d >= 0) @@ -1002,7 +1196,7 @@ def nextFrame(self, eventtime): else: return None - #Progress bar using M73 gcode command + # Progress bar using M73 gcode command class layerProgress(_layerBase): def __init__(self, **kwargs): super(ledEffect.layerProgress, self).__init__(**kwargs) @@ -1014,28 +1208,31 @@ def __init__(self, **kwargs): self.effectCutoff = self.ledCount if self.effectRate == 0: - trailing = colorArray([0.0,0.0,0.0] * self.ledCount) + trailing = colorArray(COLORS, [0.0]*COLORS * self.ledCount) else: - trailing = colorArray(self._gradient(self.paletteColors[1:], - int(self.effectRate), True)) - trailing.padLeft([0.0,0.0,0.0], self.ledCount) + trailing = colorArray(COLORS, self._gradient(self.paletteColors[1:], + int(self.effectRate), True)) + trailing.padLeft([0.0]*COLORS, self.ledCount) if self.effectCutoff == 0: - leading = colorArray([0.0,0.0,0.0] * self.ledCount) + leading = colorArray(COLORS, [0.0]*COLORS * self.ledCount) else: - leading = colorArray(self._gradient(self.paletteColors[1:], - int(self.effectCutoff), False)) - leading.padRight([0.0,0.0,0.0], self.ledCount) + leading = colorArray(COLORS, self._gradient(self.paletteColors[1:], + int(self.effectCutoff), False)) + leading.padRight([0.0]*COLORS, self.ledCount) - gradient = colorArray(trailing + self.paletteColors[0] + leading) - gradient.shift(len(trailing)-1, 0) + gradient = colorArray( + COLORS, trailing + self.paletteColors[0] + leading) + gradient.shift(len(trailing), 0) frames = [gradient[:self.ledCount]] for i in range(0, self.ledCount): - gradient.shift(1,1) + gradient.shift(1, 1) frames.append(gradient[:self.ledCount]) - for i in range(101): + self.thisFrame.append(colorArray( + COLORS, [0.0]*COLORS * self.ledCount)) + for i in range(1, 101): x = int((i / 101.0) * self.ledCount) self.thisFrame.append(frames[x]) @@ -1043,7 +1240,49 @@ def __init__(self, **kwargs): def nextFrame(self, eventtime): p = self.frameHandler.printProgress - return self.thisFrame[p] #(p - 1) * (p > 0)] + return self.thisFrame[p] # (p - 1) * (p > 0)] + + class layerHoming(_layerBase): + def __init__(self, **kwargs): + super(ledEffect.layerHoming, self).__init__(**kwargs) + + self.paletteColors = colorArray(COLORS, self.paletteColors) + + gradientLength = int(self.ledCount) + gradient = colorArray(COLORS, self._gradient(self.paletteColors, + gradientLength)) + + for c in range(0, len(self.paletteColors)): + color = self.paletteColors[c] + self.thisFrame.append(colorArray(COLORS, color*self.ledCount)) + + self.decayTable = self._decayTable(factor=self.effectRate) + self.decayTable.append(0.0) + self.decayLen = len(self.decayTable) + self.counter = self.decayLen-1 + self.coloridx = -1 + self.my_flag = {} + for endstop in self.handler.endstops: + logging.info(endstop) + self.frameHandler.homing_end_flag[endstop] = 0 + self.my_flag[endstop] = self.frameHandler.homing_end_flag[endstop] + + def nextFrame(self, eventtime): + for endstop in self.handler.endstops: + + if self.my_flag[endstop] != self.frameHandler.homing_end_flag[endstop]: + self.counter = 0 + self.coloridx = ( + self.coloridx + 1) % len(self.paletteColors) + self.my_flag[endstop] = self.frameHandler.homing_end_flag[endstop] + + frame = [self.decayTable[self.counter] * + i for i in self.thisFrame[self.coloridx]] + if self.counter < self.decayLen-1: + self.counter += 1 + + return frame + def load_config_prefix(config): return ledEffect(config) diff --git a/stereotech_config/530/chamber_2.cfg b/stereotech_config/530/chamber_2.cfg index 07f07f057cf6..268140b34afe 100644 --- a/stereotech_config/530/chamber_2.cfg +++ b/stereotech_config/530/chamber_2.cfg @@ -119,28 +119,6 @@ gcode: UPDATE_DELAYED_GCODE ID=return_color DURATION=0.1 {% endif %} -; [led_effect strobe_efect] -; autostart:false -; frame_rate:24 -; leds: -; neopixel:case_led -; layers: -; strobe 0.6 1.5 add (1.0, 1.0, 1.0),(0.0, 0.0, 1.0); -; [led_effect breathing_efect] -; autostart: false -; frame_rate: 24 -; leds: -; neopixel:case_led -; layers: -; linearfade 6 0 top (1.0, 1.0, 1.0),(0.0, 0.0, 1.0); -; [led_effect fire_efect] -; autostart: false -; frame_rate: 24 -; leds: -; neopixel:case_led -; layers: -; fire 95 40 top (0.0, 0.0, 1.0),(1.0, 1.0, 1.0); - [gcode_macro M106] rename_existing: M106.1 gcode: @@ -165,4 +143,3 @@ gcode: {% else %} M107.1 {% endif %} - diff --git a/stereotech_config/630/board_stm32f4.cfg b/stereotech_config/630/board_stm32f4.cfg new file mode 100644 index 000000000000..a682fc1711ea --- /dev/null +++ b/stereotech_config/630/board_stm32f4.cfg @@ -0,0 +1,22 @@ +[board_pins] +aliases: + x_step_pin=PF13, x_dir_pin=PF12, x_en_pin=PF14, x_endstop_pin=PG6, + y_step_pin=PG0, y_dir_pin=PG1, y_en_pin=PF15, y_endstop_pin=PG9, + z_step_pin=PF11, z_dir_pin=PG3, z_en_pin=PG5, z_endstop_pin=PG10, + a_step_pin=PG4, a_dir_pin=PC1, a_en_pin=PA0, a_endstop_pin=PG11, + c_step_pin=PF9, c_dir_pin=PF10, c_en_pin=PG2, c_endstop_pin=PB7, + bed_heater=PA1, bed_sensor=PF5, + five_axis_pin=PG12, heatsink_fan_pin=PD12, + main_extruder_step_pin=PC13, main_extruder_dir_pin=PF0, + main_extruder_enable_pin=PF1, main_extruder_heater_pin=PA2, + main_extruder_sensor_pin=PF3, + main_extruder_filament_sensor_pin=PG14, + second_extruder_step_pin=PE2, second_extruder_dir_pin=PE3, + second_extruder_enable_pin=PD4, second_extruder_heater_pin=PA3, + second_extruder_sensor_pin=PF4, + second_extruder_filament_sensor_pin=PG15, + fiber_cut_step_pin=PE6, fiber_cut_dir_pin=PA14, + fiber_cut_enable_pin=PE0, + case_led=PE5, status_led=PB0, + # for debug + fan_pin=PD15, diff --git a/stereotech_config/630/chamber.cfg b/stereotech_config/630/chamber.cfg new file mode 100644 index 000000000000..810cfb358ee3 --- /dev/null +++ b/stereotech_config/630/chamber.cfg @@ -0,0 +1,166 @@ +[fan_generic chamber_fan] +pin: PA8 + +[temperature_fan bottom_fan] +pin: PD13 +sensor_type: temperature_host +control: pid +pid_Kp: 15 +pid_Ki: 0.5 +pid_Kd: 25 +min_temp: 0 +max_temp: 90 +target_temp: 45.0 +min_speed: 0.0 +gcode_id: E + +[gcode_macro M106] +rename_existing: M106.1 +gcode: + {% set p = params.P|default(0)|int %} + {% set s = params.S|default(0)|int %} + {% if p > 0 %} + {% if p == 3 %} + SET_FAN_SPEED FAN=chamber_fan SPEED={params.S|default(0)|int / 255} + {% endif %} + {% else %} + M106.1 S{s} + {% endif %} + +[gcode_macro M107] +rename_existing: M107.1 +gcode: + {% set p = params.P|default(0)|int %} + {% if p > 0 %} + {% if p == 3 %} + SET_FAN_SPEED FAN=chamber_fan SPEED=0.0 + {% endif %} + {% else %} + M107.1 + {% endif %} + +# LED Light controls + +[led case_led] +white_pin: case_led +initial_WHITE: 1 + +[neopixel status_led] +pin: status_led +chain_count: 100 +color_order: GRBW +initial_RED: 0.0 +initial_GREEN: 0.0 +initial_BLUE: 0.0 +initial_WHITE: 1.0 + +[delayed_gcode startup_light] +initial_duration: 0.1 +gcode: + SET_LED_EFFECT EFFECT=startup REPLACE=1 FADETIME=0 + SET_LED_EFFECT EFFECT=maintenance REPLACE=1 FADETIME=6.0 + +[gcode_macro TOGGLE_LIGHT] +gcode: + {% if printer['led case_led'].color_data[0][3] > 0.0 %} + SET_LED LED=case_led WHITE=0 + {% else %} + SET_LED LED=case_led WHITE=1 + {% endif %} + +[gcode_macro STATUS_LED] +gcode: + {% set status = params.STATUS|default(printing) %} + {% if status == "started" %} + {% set heater = params.HEATER|default(extruder) %} + SET_LED_EFFECT EFFECT=heatup_{heater} REPLACE=1 FADETIME=2.0 + {% else %} + SET_LED_EFFECT EFFECT={status} REPLACE=1 FADETIME=2.0 + {% endif %} + +[led_effect startup] +autostart: true +leds: + neopixel:status_led +layers: + comet 0.7 10 add (0, 1, 1), (0, .5, .5), (0, 0, 0) + comet -0.7 10 add (0, 1, 1), (0, .5, .5), (0, 0, 0) + +[led_effect maintenance] +leds: + neopixel:status_led +layers: + static 0 0 add (0, 1, 1) + +[led_effect idle] +leds: + neopixel:status_led +layers: + linearfade 5 0 add (0, 1, 1), (0, .5, .5) + +[led_effect heatup_extruder] +leds: + neopixel:status_led +layers: + linearfade 5 0 multiply (1, 1, 1), (.5, .5, .5) + temperature 20 100 add (0, 0, 1), (1, 0, 1) +heater: extruder + +[led_effect heatup_extruder1] +leds: + neopixel:status_led +layers: + linearfade 5 0 multiply (1, 1, 1), (.5, .5, .5) + temperature 20 100 add (0, 0, 1), (1, 0, 1) +heater: extruder1 + +[led_effect heatup_heater_bed] +leds: + neopixel:status_led +layers: + linearfade 5 0 multiply (1, 1, 1), (.5, .5, .5) + temperature 20 100 add (0, 0, 1), (1, 0, 1) +heater: heater_bed + +[led_effect printing] +leds: + neopixel:status_led +layers: + linearfade 5 0 multiply (1, 1, 1), (.5, .5, .5) + progress 99 0 add (0, .6, .6), (0, 1, 1) + static 0 0 add (.1, .1, .1) + +[led_effect paused] +leds: + neopixel:status_led +layers: + progress 99 0 add (.6, .6, 0), (1, 1, 00) + static 0 0 add (.1, .1, .1) + +[led_effect error_paused] +leds: + neopixel:status_led +layers: + linearfade 5 0 multiply (1, 1, 1), (.5, .5, .5) + progress 99 0 add (.6, .6, 0), (1, 1, 0) + static 0 0 add (.1, .1, .1) + +[led_effect cancelled] +leds: + neopixel:status_led +layers: + progress 99 0 add (.6, 0, 0), (1, 0, 0) + static 0 0 add (.1, .1, .1) + +[led_effect error] +run_on_error: true +leds: + neopixel:status_led (1-7) +layers: + strobe .6 .1 top (1, 0, 0) + +[led_effect completed] +leds: + neopixel:status_led +layers: + linearfade 5 0 add (0, 1, 0), (0, .5, 0) diff --git a/stereotech_config/750/print_macros.cfg b/stereotech_config/750/print_macros.cfg index 4f2ba6567263..c5203180591f 100644 --- a/stereotech_config/750/print_macros.cfg +++ b/stereotech_config/750/print_macros.cfg @@ -20,6 +20,7 @@ gcode: {% set act_z = printer.toolhead.position.z|float %} {% set lift_z = z|abs %} {% set fan_speed = printer.fan.speed * 255 | int %} + {% set status_led = params.STATUS_LED|default('paused') %} {% if act_z < (max_z - lift_z) %} {% set z_safe = lift_z %} {% else %} @@ -42,7 +43,7 @@ gcode: {% endif %} SET_IDLE_TIMEOUT TIMEOUT=360000 PAUSE_BASE - STATUS_LED STATUS=paused + STATUS_LED STATUS={status_led} {% if printer.probe %} {% if printer.heater_bed.temperature > 5 %} BED_MESH_CLEAR @@ -306,3 +307,12 @@ gcode: G1 Z{z_safe} F1600 {% endif %} G90 + +[gcode_macro SET_STATUS_TO_PRINT_QUEUE] +description: installing the print_queue server module in a maintenance or idle state, and set state to led_effect +gcode: + {% set new_status = params.NEW_STATUS|default('maintenance')|lower %} + {action_call_remote_method('set_status_print_queue', new_status=new_status)} + {% if printer['neopixel status_led'] %} + STATUS_LED STATUS={new_status} + {% endif %} diff --git a/stereotech_config/HTE630-0-C-23.cfg b/stereotech_config/HTE630-0-C-23.cfg index b5d194168be6..f843da523b64 100644 --- a/stereotech_config/HTE630-0-C-23.cfg +++ b/stereotech_config/HTE630-0-C-23.cfg @@ -7,14 +7,14 @@ path: /home/ste/uploads [display_status] -[include config/common/board_stm32f4.cfg] +[include config/630/board_stm32f4.cfg] [include config/630/kinematics.cfg] [include config/630/kinematics_tmc.cfg] [include config/630/homing_override.cfg] [include config/common/homing_macros.cfg] -[include config/530/chamber_2.cfg] +[include config/630/chamber.cfg] [include config/common/printhead.cfg] diff --git a/stereotech_config/common/extruder_macros.cfg b/stereotech_config/common/extruder_macros.cfg index a83c9b4442f3..fe14483656db 100644 --- a/stereotech_config/common/extruder_macros.cfg +++ b/stereotech_config/common/extruder_macros.cfg @@ -52,7 +52,6 @@ gcode: [gcode_macro M109] rename_existing: M1091 gcode: - STATUS_LED STATUS=started {% set extruder = params.T|default(-1)|int %} {% set temp =params.S|int %} {% set extruder_name = printer.toolhead.extruder %} @@ -60,6 +59,7 @@ gcode: {% if extruder >= 0 %} {% set extruder_name = "extruder" if extruder == 0 else "extruder" ~ extruder %} {% endif %} + STATUS_LED STATUS=started HEATER={extruder_name} M117 {extruder_name ~ heat_phase} {% if extruder >= 0 %} M1091 T{extruder} S{temp} diff --git a/stereotech_config/common/filament_control.cfg b/stereotech_config/common/filament_control.cfg index 19ea8db34646..8422e0731841 100644 --- a/stereotech_config/common/filament_control.cfg +++ b/stereotech_config/common/filament_control.cfg @@ -55,7 +55,7 @@ gcode: SET_GCODE_VARIABLE MACRO=RECOVER_EXTRUSION VARIABLE=enable_offset VALUE=0 {% endif %} {% if not printer.pause_resume.is_paused %} - PAUSE TURN_OFF_EXTRUDERS=0 E=0 + PAUSE TURN_OFF_EXTRUDERS=0 E=0 STATUS_LED=error_paused {% endif %} {% if printer["gcode_macro RECOVER_EXTRUSION"].enable_prime and (printer["gcode_macro RECOVER_EXTRUSION_BY_PRIME"].checks_made < printer["gcode_macro RECOVER_EXTRUSION_BY_PRIME"].check_count or printer["gcode_macro RECOVER_EXTRUSION_BY_PRIME"].use_cooldown) %} RECOVER_EXTRUSION_BY_PRIME SENSOR={sensor} @@ -87,7 +87,7 @@ gcode: {action_respond_warning('419: Algorithm the recover extrusion is disable.')} SET_GCODE_VARIABLE MACRO=RECOVER_EXTRUSION VARIABLE=count_triggered_sensor VALUE={printer["gcode_macro RECOVER_EXTRUSION"].count_triggered_sensor + 1} {% if not printer.pause_resume.is_paused %} - PAUSE TURN_OFF_EXTRUDERS=1 E=0 + PAUSE TURN_OFF_EXTRUDERS=1 E=0 STATUS_LED=error_paused {% endif %} {% endif %} diff --git a/stereotech_config/common/module_3d_macros.cfg b/stereotech_config/common/module_3d_macros.cfg index c298d474c4bf..dc602425a9b2 100644 --- a/stereotech_config/common/module_3d_macros.cfg +++ b/stereotech_config/common/module_3d_macros.cfg @@ -9,7 +9,7 @@ gcode: rename_existing: M191 gcode: {% if printer["gcode_button five_axis_module"].state == "RELEASED" %} - STATUS_LED STATUS=started + STATUS_LED STATUS=started HEATER=heater_bed M117 platform_heating M191 S{ params.S } STATUS_LED STATUS=printing diff --git a/stereotech_config/common/print_macros.cfg b/stereotech_config/common/print_macros.cfg index acc4304f6110..0d362b79d544 100644 --- a/stereotech_config/common/print_macros.cfg +++ b/stereotech_config/common/print_macros.cfg @@ -20,6 +20,7 @@ gcode: {% set act_z = printer.toolhead.position.z|float %} {% set lift_z = z|abs %} {% set fan_speed = printer.fan.speed * 255 | int %} + {% set status_led = params.STATUS_LED|default('paused') %} {% if act_z < (max_z - lift_z) %} {% set z_safe = lift_z %} {% else %} @@ -41,7 +42,7 @@ gcode: {% endif %} SET_IDLE_TIMEOUT TIMEOUT=360000 PAUSE_BASE - STATUS_LED STATUS=paused + STATUS_LED STATUS={status_led} {% if printer.probe %} {% if printer["gcode_button five_axis_module"].state == "RELEASED" %} BED_MESH_CLEAR @@ -322,3 +323,12 @@ gcode: G1 Z{z_safe} F1600 {% endif %} G90 + +[gcode_macro SET_STATUS_TO_PRINT_QUEUE] +description: installing the print_queue server module in a maintenance or idle state, and set state to led_effect +gcode: + {% set new_status = params.NEW_STATUS|default('maintenance')|lower %} + {action_call_remote_method('set_status_print_queue', new_status=new_status)} + {% if printer['neopixel status_led'] %} + STATUS_LED STATUS={new_status} + {% endif %}