diff options
author | Kevin O'Connor <kevin@koconnor.net> | 2020-09-28 13:44:36 -0400 |
---|---|---|
committer | Kevin O'Connor <kevin@koconnor.net> | 2020-10-29 11:59:15 -0400 |
commit | 2bb30265b513269f5e749f87a424bf6772f7fc11 (patch) | |
tree | 381f728be4b73378bb8a7869e09cf31a6ec315eb /klippy/extras/neopixel.py | |
parent | be4ad29fa343b4d20feb2fa55be9a93b84d62a51 (diff) | |
download | kutter-2bb30265b513269f5e749f87a424bf6772f7fc11.tar.gz kutter-2bb30265b513269f5e749f87a424bf6772f7fc11.tar.xz kutter-2bb30265b513269f5e749f87a424bf6772f7fc11.zip |
neopixel: Increase the maximum LED chain length
Rework neopixel updates to use an mcu buffer so that more than 18 LEDs
can be in a chain.
Signed-off-by: Kevin O'Connor <kevin@koconnor.net>
Diffstat (limited to 'klippy/extras/neopixel.py')
-rw-r--r-- | klippy/extras/neopixel.py | 57 |
1 files changed, 45 insertions, 12 deletions
diff --git a/klippy/extras/neopixel.py b/klippy/extras/neopixel.py index 07cad315..307651ea 100644 --- a/klippy/extras/neopixel.py +++ b/klippy/extras/neopixel.py @@ -1,14 +1,17 @@ # Support for "neopixel" leds # -# Copyright (C) 2019 Kevin O'Connor <kevin@koconnor.net> +# Copyright (C) 2019-2020 Kevin O'Connor <kevin@koconnor.net> # # This file may be distributed under the terms of the GNU GPLv3 license. +import logging BACKGROUND_PRIORITY_CLOCK = 0x7fffffff00000000 BIT_MAX_TIME=.000004 RESET_MIN_TIME=.000050 +MAX_MCU_SIZE = 500 # Sanity check on LED chain length + class PrinterNeoPixel: def __init__(self, config): self.printer = config.get_printer() @@ -21,27 +24,33 @@ class PrinterNeoPixel: self.pin = pin_params['pin'] self.mcu.register_config_callback(self.build_config) self.color_order_GRB = config.getboolean("color_order_GRB", True) - self.chain_count = config.getint('chain_count', 1, minval=1, maxval=18) - self.neopixel_send_cmd = None + self.chain_count = config.getint('chain_count', 1, + minval=1, maxval=MAX_MCU_SIZE//3) + self.neopixel_update_cmd = self.neopixel_send_cmd = None # Initial color + self.color_data = bytearray(self.chain_count * 3) 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.) self.update_color_data(red, green, blue) - self.printer.register_event_handler("klippy:connect", self.send_data) + self.old_color_data = bytearray([d ^ 1 for d in self.color_data]) # Register commands + self.printer.register_event_handler("klippy:connect", self.send_data) gcode = self.printer.lookup_object('gcode') gcode.register_mux_command("SET_LED", "LED", name, self.cmd_SET_LED, desc=self.cmd_SET_LED_help) def build_config(self): bmt = self.mcu.seconds_to_clock(BIT_MAX_TIME) rmt = self.mcu.seconds_to_clock(RESET_MIN_TIME) - self.mcu.add_config_cmd("config_neopixel oid=%d pin=%s" + self.mcu.add_config_cmd("config_neopixel oid=%d pin=%s data_size=%d" " bit_max_ticks=%d reset_min_ticks=%d" - % (self.oid, self.pin, bmt, rmt)) + % (self.oid, self.pin, self.chain_count * 3, + bmt, rmt)) cmd_queue = self.mcu.alloc_command_queue() - self.neopixel_send_cmd = self.mcu.lookup_command( - "neopixel_send oid=%c data=%*s", cq=cmd_queue) + self.neopixel_update_cmd = self.mcu.lookup_command( + "neopixel_update oid=%c pos=%hu data=%*s", cq=cmd_queue) + self.neopixel_send_cmd = self.mcu.lookup_query_command( + "neopixel_send oid=%c", "neopixel_result success=%c", cq=cmd_queue) def update_color_data(self, red, green, blue, index=None): red = int(red * 255. + .5) blue = int(blue * 255. + .5) @@ -51,13 +60,37 @@ class PrinterNeoPixel: else: color_data = [red, green, blue] if index is None: - self.color_data = color_data * self.chain_count + self.color_data[:] = color_data * self.chain_count else: self.color_data[(index-1)*3:index*3] = color_data def send_data(self, minclock=0): - self.neopixel_send_cmd.send([self.oid, self.color_data], - minclock=minclock, - reqclock=BACKGROUND_PRIORITY_CLOCK) + old_data, new_data = self.old_color_data, self.color_data + if new_data == old_data: + return + # Find the position of all changed bytes in this framebuffer + diffs = [[i, 1] for i, (n, o) in enumerate(zip(new_data, old_data)) + if n != o] + # Batch together changes that are close to each other + for i in range(len(diffs)-2, -1, -1): + pos, count = diffs[i] + nextpos, nextcount = diffs[i+1] + if pos + 5 >= nextpos and nextcount < 16: + diffs[i][1] = nextcount + (nextpos - pos) + del diffs[i+1] + # Transmit changes + ucmd = self.neopixel_update_cmd.send + for pos, count in diffs: + ucmd([self.oid, pos, new_data[pos:pos+count]], + reqclock=BACKGROUND_PRIORITY_CLOCK) + old_data[:] = new_data + # Instruct mcu to update the LEDs + scmd = self.neopixel_send_cmd.send + for i in range(8): + params = scmd([self.oid], minclock=minclock) + if params['success']: + break + else: + logging.info("Neopixel update did not succeed") cmd_SET_LED_help = "Set the color of an LED" def cmd_SET_LED(self, gcmd): # Parse parameters |