Skip to content

Commit

Permalink
Add button effects (#192)
Browse files Browse the repository at this point in the history
  • Loading branch information
julianschill authored May 5, 2024
1 parent 58e010d commit d493923
Show file tree
Hide file tree
Showing 2 changed files with 171 additions and 11 deletions.
53 changes: 45 additions & 8 deletions docs/LED_Effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,22 +193,31 @@ Enable layer template recalculation on effect activation.

heater:
Specifies the heater to use for a heater effect. Use `extruder` for the
extruder and `heater_bed` for the bed. For temperature fans or sensors add the
extruder and `heater_bed` for the bed. For temperature fans or sensors add the
type and use quotes.
Example: `heater: "temperature_fan myfan"`

analog_pin:
Specifies the pin to use for effects using an analog signal.
Example: `analog_pin: PA1`

stepper:
Specifies the axis to use for the stepper effect. Possible values are:
`x`, `y` and `z`. Example: `stepper: x`
`x`, `y` and `z`.
Example: `stepper: x`

endstops:
Specifies the endstops the homing effect triggers on. Multiple endstops can be
specified as a comma seprated list. Possible values are: `x`, `y`, `z` and `probe`.
specified as a comma seperated list. Possible values are: `x`, `y`, `z` and `probe`.
Example: `endstops: x, y`

button_pins:
Specifies the pins the button effect trigger on. Multiple pins can be specified
as a comma seperated list. Then the effect will trigger on any of the buttons.
Using already assigned pins (such as endstop pins) is possible by using
`duplicate_pin_override` (see Klipper documentation for details).
Example: `button_pins: PC1, PC2`

## Defining LEDs

The `leds:` section is a list of Neopixel or Dotstar strips that will
Expand Down Expand Up @@ -264,8 +273,8 @@ Each layer is defined with the following parameters
Each layer must be on a single line and each line must be indented.
Color palettes can be of unlimited length but may be compressed depending
on the size of the frame or number of LEDs on a strip. Colors are defined
as groups of Red, Green, Blue and (optional) White. The white channel only used
on RGBW LEDs and ignored on RGB LEDs. The range for each color is a decimal
as groups of Red, Green, Blue and (optional) White. The white channel is only
used on RGBW LEDs and ignored on RGB LEDs. The range for each color is a decimal
number from 0.0 to 1.0. So for yellow, you would use ( 1.0, 1.0, 0.0 ). For
white you would use ( 1.0, 1.0, 1.0 ) on an RGB LED or ( 0.0, 0.0, 0.0, 1.0 )
on an RGBW LED.
Expand Down Expand Up @@ -462,9 +471,37 @@ layer reports print progress.
Cutoff: 0 Not used, but must be provided
Palette: Colors are cycled in order

LEDs turn on during homing when the endstop is triggered and fade out again. The
effect rate determines the time for the fade out. If a palette of multiple colors
is provided, it will cycle through those colors in order.
Needs an endstop defined. LEDs turn on during homing when the endstop is
triggered and fade out again. The effect rate determines the time for the fade
out. If a palette of multiple colors is provided, it will cycle through those
colors in order.

#### SwitchButton
Effect Rate: 1 Fade in time. Time until LEDs are on after button press
Cutoff: 1 Fade out time. Time until LEDs are off after button release
Palette: Colors are cycled in order

Needs a button_pin defined. LEDs turn on when the button is pressed and turn off
when the button is released again. Each press cycles through the colors of the
palette.

#### ToggleButton
Effect Rate: 1 Fade in time. Time to fade in next color
Cutoff: 1 Fade out time. Time to fade out previous color
Palette: Colors are cycled in order

Needs a button_pin defined. Cycles through the colors with each button press.
The transition times can be configured with the effect rate and cutoff parameters.
Hint: Define (0,0,0) as a color if you want to toggle between on and off.

#### FlashButton
Effect Rate: 0.1 Fade in time. Time to fade in.
Cutoff: 1 Fade out time. Time to fade out.
Palette: Colors are cycled in order

Needs a button_pin defined. When the button is pressed the LEDs fade on in the
defined time and fade off immediately afterwards. If a palette of multiple colors
is provided, it will cycle through those colors in order with each button press.

## Effect Layer Blending
If you have ever used image editing software you may be familiar with
Expand Down
129 changes: 126 additions & 3 deletions src/led_effect.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

from math import cos, exp, pi
from random import randint
import logging

ANALOG_SAMPLE_TIME = 0.001
ANALOG_SAMPLE_COUNT = 5
Expand Down Expand Up @@ -114,7 +113,6 @@ def _handle_shutdown(self):

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:
Expand Down Expand Up @@ -317,6 +315,7 @@ def __init__(self, config):
self.iteration = 0
self.layers = []
self.analogValue = 0
self.button_state = 0
self.fadeValue = 0.0
self.fadeTime = 0.0
self.fadeEndTime = 0
Expand Down Expand Up @@ -347,6 +346,7 @@ def __init__(self, config):
self.runOnShutown = config.getboolean('run_on_error', False)
self.heater = config.get('heater', None)
self.analogPin = config.get('analog_pin', None)
self.buttonPins = config.getlist('button_pins', None)
self.stepper = config.get('stepper', None)
self.recalculate = config.get('recalculate', False)
self.endstops = [x.strip() for x in config.get('endstops','').split(',')]
Expand All @@ -368,6 +368,10 @@ def __init__(self, config):
query_adc = self.printer.load_object(self.config, 'query_adc')
query_adc.register_adc(self.name, self.mcu_adc)

if self.buttonPins:
buttons = self.printer.load_object(config, "buttons")
buttons.register_buttons(self.buttonPins, self.button_callback)

cmd_SET_LED_help = 'Starts or Stops the specified led_effect'

def _handle_ready(self):
Expand Down Expand Up @@ -541,6 +545,9 @@ def _handle_shutdown(self):

def adcCallback(self, read_time, read_value):
self.analogValue = int(read_value * 1000.0) / 10.0

def button_callback(self, eventtime, state):
self.button_state = state

######################################################################
# LED Effect layers
Expand Down Expand Up @@ -1243,7 +1250,6 @@ def __init__(self, **kwargs):
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]

Expand All @@ -1261,6 +1267,123 @@ def nextFrame(self, eventtime):

return frame

class layerSwitchButton(_layerBase):
def __init__(self, **kwargs):
super(ledEffect.layerSwitchButton, self).__init__(**kwargs)
self.last_state = 0
self.coloridx = 0
self.fadeValue = 0.0
self.paletteColors = colorArray(COLORS, self.paletteColors)

for c in range(0, len(self.paletteColors)):
color = self.paletteColors[c]
self.thisFrame.append(colorArray(COLORS,color*self.ledCount))

def nextFrame(self, eventtime):
if self.handler.button_state > self.last_state:
self.coloridx = (self.coloridx + 1) % len(self.paletteColors)

self.last_state = self.handler.button_state

if self.last_state:
if self.effectRate > 0 and self.fadeValue < 1.0:
self.fadeValue += (self.handler.frameRate / self.effectRate)
else:
self.fadeValue = 1.0
else:
if self.effectCutoff > 0 and self.fadeValue > 0.0:
self.fadeValue -= (self.handler.frameRate / self.effectCutoff)
else:
self.fadeValue = 0.0

if self.fadeValue < 0: self.fadeValue = 0
if self.fadeValue > 1.0: self.fadeValue = 1.0
return [self.fadeValue * i for i in self.thisFrame[self.coloridx]]

class layerToggleButton(_layerBase):
def __init__(self, **kwargs):
super(ledEffect.layerToggleButton, self).__init__(**kwargs)
self.last_state = 0
self.last_coloridx = 0
self.coloridx = 0
self.fadeInValue = 0.0
self.fadeOutValue = 0.0
self.active = False
self.paletteColors = colorArray(COLORS, self.paletteColors)

for c in range(0, len(self.paletteColors)):
color = self.paletteColors[c]
self.thisFrame.append(colorArray(COLORS,color*self.ledCount))

def nextFrame(self, eventtime):
if self.handler.button_state > self.last_state:
self.last_coloridx = self.coloridx
self.coloridx = (self.coloridx + 1) % len(self.paletteColors)
self.last_state = self.handler.button_state
self.fadeInValue = 0
self.fadeOutValue = 1.0

self.last_state = self.handler.button_state

if self.effectRate > 0 and self.fadeInValue < 1.0:
self.fadeInValue += (self.handler.frameRate / self.effectRate)
else:
self.fadeInValue = 1.0
if self.effectCutoff > 0 and self.fadeOutValue > 0.0:
self.fadeOutValue -= (self.handler.frameRate / self.effectCutoff)
else:
self.fadeOutValue = 0.0

if self.fadeInValue < 0: self.fadeInValue = 0
if self.fadeInValue > 1.0: self.fadeInValue = 1.0

if self.fadeOutValue < 0: self.fadeOutValue = 0
if self.fadeOutValue > 1.0: self.fadeOutValue = 1.0

frameIn = [self.fadeInValue * i for i in self.thisFrame[self.coloridx]]
frameOut = [self.fadeOutValue * i for i in self.thisFrame[self.last_coloridx]]

return [ i + o for i, o in zip(frameIn,frameOut)]

class layerFlashButton(_layerBase):
def __init__(self, **kwargs):
super(ledEffect.layerFlashButton, self).__init__(**kwargs)
self.last_state = 0
self.active = False
self.coloridx = 0
self.fadeValue = 0.0
self.paletteColors = colorArray(COLORS, self.paletteColors)

for c in range(0, len(self.paletteColors)):
color = self.paletteColors[c]
self.thisFrame.append(colorArray(COLORS,color*self.ledCount))

def nextFrame(self, eventtime):

if self.handler.button_state > self.last_state:
self.coloridx = (self.coloridx + 1) % len(self.paletteColors)
self.active = True

self.last_state=self.handler.button_state

if self.active:
if self.effectRate > 0 and self.fadeValue < 1.0:
self.fadeValue += (self.handler.frameRate / self.effectRate)
else:
self.fadeValue = 1.0
if self.fadeValue >= 1.0:
self.fadeValue = 1.0
self.active = False
else:
if self.effectCutoff > 0 and self.fadeValue > 0.0:
self.fadeValue -= (self.handler.frameRate / self.effectCutoff)
else:
self.fadeValue = 0.0

if self.fadeValue <= 0:
self.fadeValue = 0

return [self.fadeValue * i for i in self.thisFrame[self.coloridx]]

def load_config_prefix(config):
return ledEffect(config)

0 comments on commit d493923

Please sign in to comment.