led: Add support for PWM controlled LEDs
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
This commit is contained in:
parent
3340bb2ffd
commit
96795def9c
|
@ -2505,7 +2505,125 @@ with the SET_FAN_SPEED [gcode command](G-Codes.md#fan_generic).
|
|||
# See the "fan" section for a description of the above parameters.
|
||||
```
|
||||
|
||||
## Additional servos, LEDs, buttons, and other pins
|
||||
## LEDs
|
||||
|
||||
### [led]
|
||||
|
||||
Support for LEDs (and LED strips) controlled via micro-controller PWM
|
||||
pins (one may define any number of sections with an "led" prefix). See
|
||||
the [command reference](G-Codes.md#led) for more information.
|
||||
|
||||
```
|
||||
[led my_led]
|
||||
#red_pin:
|
||||
#green_pin:
|
||||
#blue_pin:
|
||||
#white_pin:
|
||||
# The pin controlling the given LED color. At least one of the above
|
||||
# parameters must be provided.
|
||||
#cycle_time: 0.010
|
||||
# The amount of time (in seconds) per PWM cycle. It is recommended
|
||||
# this be 10 milliseconds or greater when using software based PWM.
|
||||
# The default is 0.010 seconds.
|
||||
#hardware_pwm: False
|
||||
# Enable this to use hardware PWM instead of software PWM. When
|
||||
# using hardware PWM the actual cycle time is constrained by the
|
||||
# implementation and may be significantly different than the
|
||||
# requested cycle_time. The default is False.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
#initial_WHITE: 0.0
|
||||
# Sets the initial LED color. Each value should be between 0.0 and
|
||||
# 1.0. The default for each color is 0.
|
||||
```
|
||||
|
||||
### [neopixel]
|
||||
|
||||
Neopixel (aka WS2812) LED support (one may define any number of
|
||||
sections with a "neopixel" prefix). See the
|
||||
[command reference](G-Codes.md#led) for more information.
|
||||
|
||||
```
|
||||
[neopixel my_neopixel]
|
||||
pin:
|
||||
# The pin connected to the neopixel. This parameter must be
|
||||
# provided.
|
||||
#chain_count:
|
||||
# The number of Neopixel chips that are "daisy chained" to the
|
||||
# provided pin. The default is 1 (which indicates only a single
|
||||
# Neopixel is connected to the pin).
|
||||
#color_order: GRB
|
||||
# Set the pixel order required by the LED hardware. Options are GRB,
|
||||
# RGB, BRG, BGR, GRBW, or RGBW. The default is GRB.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
#initial_WHITE: 0.0
|
||||
# See the "led" section for information on these parameters.
|
||||
```
|
||||
|
||||
### [dotstar]
|
||||
|
||||
Dotstar (aka APA102) LED support (one may define any number of
|
||||
sections with a "dotstar" prefix). See the
|
||||
[command reference](G-Codes.md#led) for more information.
|
||||
|
||||
```
|
||||
[dotstar my_dotstar]
|
||||
data_pin:
|
||||
# The pin connected to the data line of the dotstar. This parameter
|
||||
# must be provided.
|
||||
clock_pin:
|
||||
# The pin connected to the clock line of the dotstar. This parameter
|
||||
# must be provided.
|
||||
#chain_count:
|
||||
# See the "neopixel" section for information on this parameter.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
# See the "led" section for information on these parameters.
|
||||
```
|
||||
|
||||
### [pca9533]
|
||||
|
||||
PCA9533 LED support. The PCA9533 is used on the mightyboard.
|
||||
|
||||
```
|
||||
[pca9533 my_pca9533]
|
||||
#i2c_address: 98
|
||||
# The i2c address that the chip is using on the i2c bus. Use 98 for
|
||||
# the PCA9533/1, 99 for the PCA9533/2. The default is 98.
|
||||
#i2c_mcu:
|
||||
#i2c_bus:
|
||||
#i2c_speed:
|
||||
# See the "common I2C settings" section for a description of the
|
||||
# above parameters.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
#initial_WHITE: 0.0
|
||||
# See the "led" section for information on these parameters.
|
||||
```
|
||||
|
||||
### [pca9632]
|
||||
|
||||
PCA9632 LED support. The PCA9632 is used on the FlashForge Dreamer.
|
||||
|
||||
```
|
||||
[pca9632 my_pca9632]
|
||||
scl_pin:
|
||||
# The SCL "clock" pin. This parameter must be provided.
|
||||
sda_pin:
|
||||
# The SDA "data" pin. This parameter must be provided.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
#initial_WHITE: 0.0
|
||||
# See the "led" section for information on these parameters.
|
||||
```
|
||||
|
||||
## Additional servos, buttons, and other pins
|
||||
|
||||
### [servo]
|
||||
|
||||
|
@ -2538,100 +2656,6 @@ pin:
|
|||
# send any signal at startup.
|
||||
```
|
||||
|
||||
### [neopixel]
|
||||
|
||||
Neopixel (aka WS2812) LED support (one may define any number of
|
||||
sections with a "neopixel" prefix). One may set the LED color via
|
||||
"SET_LED LED=my_neopixel RED=0.1 GREEN=0.1 BLUE=0.1" type extended
|
||||
[g-code commands](G-Codes.md#neopixel).
|
||||
|
||||
```
|
||||
[neopixel my_neopixel]
|
||||
pin:
|
||||
# The pin connected to the neopixel. This parameter must be
|
||||
# provided.
|
||||
#chain_count:
|
||||
# The number of Neopixel chips that are "daisy chained" to the
|
||||
# provided pin. The default is 1 (which indicates only a single
|
||||
# Neopixel is connected to the pin).
|
||||
#color_order: GRB
|
||||
# Set the pixel order required by the LED hardware. Options are GRB,
|
||||
# RGB, BRG, BGR, GRBW, or RGBW. The default is GRB.
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
#initial_WHITE: 0.0
|
||||
# Sets the initial LED color of the Neopixel. Each value should be
|
||||
# between 0.0 and 1.0. The WHITE option is only available on RGBW
|
||||
# LEDs. The default for each color is 0.
|
||||
```
|
||||
|
||||
### [dotstar]
|
||||
|
||||
Dotstar (aka APA102) LED support (one may define any number of
|
||||
sections with a "dotstar" prefix). One may set the LED color via
|
||||
"SET_LED LED=my_dotstar RED=0.1 GREEN=0.1 BLUE=0.1" type extended
|
||||
[g-code commands](G-Codes.md#neopixel).
|
||||
|
||||
```
|
||||
[dotstar my_dotstar]
|
||||
data_pin:
|
||||
# The pin connected to the data line of the dotstar. This parameter
|
||||
# must be provided.
|
||||
clock_pin:
|
||||
# The pin connected to the clock line of the dotstar. This parameter
|
||||
# must be provided.
|
||||
#chain_count:
|
||||
#initial_RED: 0.0
|
||||
#initial_GREEN: 0.0
|
||||
#initial_BLUE: 0.0
|
||||
# See the "neopixel" section for information on these parameters.
|
||||
```
|
||||
|
||||
### [PCA9533]
|
||||
|
||||
PCA9533 LED support. The PCA9533 is used on the mightyboard.
|
||||
|
||||
```
|
||||
[pca9533 my_pca9533]
|
||||
#i2c_address: 98
|
||||
# The i2c address that the chip is using on the i2c bus. Use 98 for
|
||||
# the PCA9533/1, 99 for the PCA9533/2. The default is 98.
|
||||
#i2c_mcu:
|
||||
#i2c_bus:
|
||||
#i2c_speed:
|
||||
# See the "common I2C settings" section for a description of the
|
||||
# above parameters.
|
||||
#initial_RED: 0
|
||||
#initial_GREEN: 0
|
||||
#initial_BLUE: 0
|
||||
#initial_WHITE: 0
|
||||
# The PCA9533 only supports 1 or 0. The default is 0. On the
|
||||
# mightyboard, the white led is not populated.
|
||||
# Use GCODE to modify led values after startup.
|
||||
# set_led led=my_pca9533 red=1 green=1 blue=1
|
||||
```
|
||||
### [PCA9632]
|
||||
|
||||
PCA9632 LED support. The PCA9632 is used on the FlashForge Dreamer.
|
||||
|
||||
```
|
||||
[pca9632 my_pca9632]
|
||||
scl_pin:
|
||||
# The SCL "clock" pin. This parameter must be provided.
|
||||
sda_pin:
|
||||
# The SDA "data" pin. This parameter must be provided.
|
||||
#initial_RED: 0
|
||||
#initial_GREEN: 0
|
||||
#initial_BLUE: 0
|
||||
#initial_WHITE: 0
|
||||
# PCA9632 supports individual LED PWM.
|
||||
# Values range from 0.0 to 1.0. The default is 0.0.
|
||||
# On the FlashForge Dreamer, the white led is not populated.
|
||||
# Use GCODE to modify led values after startup.
|
||||
# set_led led=my_pca9632 red=1.0 green=1.0 blue=1.0 white=0.0
|
||||
```
|
||||
|
||||
### [gcode_button]
|
||||
|
||||
Execute gcode when a button is pressed or released (or when a pin
|
||||
|
|
|
@ -690,29 +690,29 @@ scheduled to run after the stepper move completes, however if a manual
|
|||
stepper move uses SYNC=0 then future G-Code movement commands may run
|
||||
in parallel with the stepper movement.
|
||||
|
||||
### [neopixel]
|
||||
### [led]
|
||||
|
||||
The following command is available when a
|
||||
[neopixel config section](Config_Reference.md#neopixel) or
|
||||
[dotstar config section](Config_Reference.md#dotstar) is enabled.
|
||||
The following command is available when any of the
|
||||
[led config sections](Config_Reference.md#leds) are enabled.
|
||||
|
||||
#### SET_LED
|
||||
`SET_LED LED=<config_name> RED=<value> GREEN=<value> BLUE=<value>
|
||||
WHITE=<value> [INDEX=<index>] [TRANSMIT=0] [SYNC=1]`: This sets the
|
||||
LED output. Each color `<value>` must be between 0.0 and 1.0. The
|
||||
WHITE option is only valid on RGBW LEDs. If multiple LED chips are
|
||||
daisy-chained then one may specify INDEX to alter the color of just
|
||||
the given chip (1 for the first chip, 2 for the second, etc.). If
|
||||
INDEX is not provided then all LEDs in the daisy-chain will be set to
|
||||
the provided color. If TRANSMIT=0 is specified then the color change
|
||||
will only be made on the next SET_LED command that does not specify
|
||||
TRANSMIT=0; this may be useful in combination with the INDEX parameter
|
||||
to batch multiple updates in a daisy-chain. By default, the SET_LED
|
||||
command will sync it's changes with other ongoing gcode commands.
|
||||
This can lead to undesirable behavior if LEDs are being set while the
|
||||
printer is not printing as it will reset the idle timeout. If careful
|
||||
timing is not needed, the optional SYNC=0 parameter can be specified
|
||||
to apply the changes instantly and not reset the idle timeout.
|
||||
WHITE option is only valid on RGBW LEDs. If the LED supports multiple
|
||||
chips in a daisy-chain then one may specify INDEX to alter the color
|
||||
of just the given chip (1 for the first chip, 2 for the second,
|
||||
etc.). If INDEX is not provided then all LEDs in the daisy-chain will
|
||||
be set to the provided color. If TRANSMIT=0 is specified then the
|
||||
color change will only be made on the next SET_LED command that does
|
||||
not specify TRANSMIT=0; this may be useful in combination with the
|
||||
INDEX parameter to batch multiple updates in a daisy-chain. By
|
||||
default, the SET_LED command will sync it's changes with other ongoing
|
||||
gcode commands. This can lead to undesirable behavior if LEDs are
|
||||
being set while the printer is not printing as it will reset the idle
|
||||
timeout. If careful timing is not needed, the optional SYNC=0
|
||||
parameter can be specified to apply the changes without resetting the
|
||||
idle timeout.
|
||||
|
||||
### [output_pin]
|
||||
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
# Support for PWM driven LEDs
|
||||
#
|
||||
# Copyright (C) 2019-2022 Kevin O'Connor <kevin@koconnor.net>
|
||||
#
|
||||
# This file may be distributed under the terms of the GNU GPLv3 license.
|
||||
import logging
|
||||
|
||||
# Helper code for common LED initialization and control
|
||||
class LEDHelper:
|
||||
def __init__(self, config, update_func, led_count=1, has_white=False):
|
||||
self.printer = config.get_printer()
|
||||
self.update_func = update_func
|
||||
self.led_count = led_count
|
||||
# Initial color
|
||||
red = config.getfloat('initial_RED', 0., minval=0., maxval=1.)
|
||||
green = config.getfloat('initial_GREEN', 0., minval=0., maxval=1.)
|
||||
blue = config.getfloat('initial_BLUE', 0., minval=0., maxval=1.)
|
||||
white = 0.
|
||||
if has_white:
|
||||
white = config.getfloat('initial_WHITE', 0., minval=0., maxval=1.)
|
||||
self.led_state = [(red, green, blue, white)] * led_count
|
||||
# Register commands
|
||||
name = config.get_name().split()[-1]
|
||||
gcode = self.printer.lookup_object('gcode')
|
||||
gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED,
|
||||
desc=self.cmd_SET_LED_help)
|
||||
cmd_SET_LED_help = "Set the color of an LED"
|
||||
def cmd_SET_LED(self, gcmd):
|
||||
# Parse parameters
|
||||
red = gcmd.get_float('RED', 0., minval=0., maxval=1.)
|
||||
green = gcmd.get_float('GREEN', 0., minval=0., maxval=1.)
|
||||
blue = gcmd.get_float('BLUE', 0., minval=0., maxval=1.)
|
||||
white = gcmd.get_float('WHITE', 0., minval=0., maxval=1.)
|
||||
index = gcmd.get_int('INDEX', None, minval=1, maxval=self.led_count)
|
||||
transmit = gcmd.get_int('TRANSMIT', 1)
|
||||
sync = gcmd.get_int('SYNC', 1)
|
||||
color = (red, green, blue, white)
|
||||
# Update and transmit data
|
||||
def lookahead_bgfunc(print_time):
|
||||
if index is None:
|
||||
new_led_state = [color] * self.led_count
|
||||
if self.led_state == new_led_state:
|
||||
return
|
||||
self.led_state = new_led_state
|
||||
else:
|
||||
if self.led_state[index - 1] == color:
|
||||
return
|
||||
self.led_state = led_state = list(self.led_state)
|
||||
led_state[index - 1] = color
|
||||
if transmit:
|
||||
try:
|
||||
self.update_func(self.led_state, print_time)
|
||||
except self.printer.command_error as e:
|
||||
logging.exception("led update transmit error")
|
||||
if sync:
|
||||
#Sync LED Update with print time and send
|
||||
toolhead = self.printer.lookup_object('toolhead')
|
||||
toolhead.register_lookahead_callback(lookahead_bgfunc)
|
||||
else:
|
||||
#Send update now (so as not to wake toolhead and reset idle_timeout)
|
||||
lookahead_bgfunc(None)
|
||||
def get_status(self, eventtime=None):
|
||||
return {'color_data': self.led_state}
|
||||
|
||||
# Main LED tracking code
|
||||
class PrinterLED:
|
||||
def __init__(self, config):
|
||||
self.printer = config.get_printer()
|
||||
def setup_helper(self, config, update_func, led_count=1, has_white=False):
|
||||
return LEDHelper(config, update_func, led_count, has_white)
|
||||
|
||||
PIN_MIN_TIME = 0.100
|
||||
MAX_SCHEDULE_TIME = 5.0
|
||||
|
||||
# Handler for PWM controlled LEDs
|
||||
class PrinterPWMLED:
|
||||
def __init__(self, config):
|
||||
self.printer = printer = config.get_printer()
|
||||
# Configure pwm pins
|
||||
ppins = printer.lookup_object('pins')
|
||||
cycle_time = config.getfloat('cycle_time', 0.010, above=0.,
|
||||
maxval=MAX_SCHEDULE_TIME)
|
||||
hardware_pwm = config.getboolean('hardware_pwm', False)
|
||||
self.pins = []
|
||||
for i, name in enumerate(("red", "green", "blue", "white")):
|
||||
pin_name = config.get(name + '_pin', None)
|
||||
if pin_name is None:
|
||||
continue
|
||||
mcu_pin = ppins.setup_pin('pwm', pin_name)
|
||||
mcu_pin.setup_max_duration(0.)
|
||||
mcu_pin.setup_cycle_time(cycle_time, hardware_pwm)
|
||||
self.pins.append((i, mcu_pin))
|
||||
if not self.pins:
|
||||
raise config.error("No LED pin definitions found in '%s'"
|
||||
% (config.get_name(),))
|
||||
self.last_print_time = 0.
|
||||
# Initialize color data
|
||||
pled = printer.load_object(config, "led")
|
||||
self.led_helper = pled.setup_helper(config, self.update_leds, 1, True)
|
||||
self.prev_color = color = self.led_helper.get_status()['color_data'][0]
|
||||
for idx, mcu_pin in self.pins:
|
||||
mcu_pin.setup_start_value(color[idx], 0.)
|
||||
def update_leds(self, led_state, print_time):
|
||||
if print_time is None:
|
||||
eventtime = self.printer.get_reactor().monotonic()
|
||||
mcu = self.pins[0][1].get_mcu()
|
||||
print_time = mcu.estimated_print_time(eventtime) + PIN_MIN_TIME
|
||||
print_time = max(print_time, self.last_print_time + PIN_MIN_TIME)
|
||||
color = led_state[0]
|
||||
for idx, mcu_pin in self.pins:
|
||||
if self.prev_color[idx] != color[idx]:
|
||||
mcu_pin.set_pwm(print_time, color[idx])
|
||||
self.last_print_time = print_time
|
||||
self.prev_color = color
|
||||
def get_status(self, eventtime=None):
|
||||
return self.led_helper.get_status(eventtime)
|
||||
|
||||
def load_config(config):
|
||||
return PrinterLED(config)
|
||||
|
||||
def load_config_prefix(config):
|
||||
return PrinterPWMLED(config)
|
Loading…
Reference in New Issue