From 3147eb752dc8d2f6d1ac40b12d6bde83c175747d Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 11:34:49 -0700 Subject: [PATCH 01/12] Add an api-breaking Advanced Dotstar class that lets you set individual brightness --- advanced_dotstar.py | 64 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 advanced_dotstar.py diff --git a/advanced_dotstar.py b/advanced_dotstar.py new file mode 100644 index 0000000..dda0594 --- /dev/null +++ b/advanced_dotstar.py @@ -0,0 +1,64 @@ +from adafruit_dotstar import DotStar, START_HEADER_SIZE +LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits + + +""" +This is different than the standard Adafruit Dotstar library because it +allows you to use the hardware brightness value on a per-pixel basis. +This is functionality the Neopixel does not support, and the standard +adafruit dotstar/neopixel libraries should match. + +The brightness argument here is the default brightness of each pixel, but +you can override it if you use set_pixel +""" + + +class AdvancedDotstar(DotStar): + + def _set_item(self, index, value): + """ + value can be either a (r,g,b) tuple, or (r,g,b, brightness) + brightness should be a float + + Set a pixel value. You can use brightness here, if it's not passed it + will default to the strip's 'brightness' setting. + + Important notes about the per-pixel brightness - it's accomplished by + PWMing the entire output of the LED, and that PWM is at a much + slower clock than the rest of the LEDs. This can cause problems in + Persistence of Vision Applications + + """ + + offset = index * 4 + START_HEADER_SIZE + rgb = value + + if len(value) == 4: + brightness = value[4] + rgb = value[:3] + else: + brightness = self.brightness + + brightness_byte = int(brightness * 31) & 0b00011111 + + # LED startframe is three "1" bits, followed by 5 brightness bits + + self._buf[offset] = brightness_byte | LED_START + self._buf[offset + 1] = rgb[self.pixel_order[0]] + self._buf[offset + 2] = rgb[self.pixel_order[1]] + self._buf[offset + 3] = rgb[self.pixel_order[2]] + + if self.auto_write: + self.show() + + def show(self): + """ + Override parent show method because we don't do the float brightness + correction anymore. + """ + + if self._spi: + self._spi.write(self._buf) + else: + self._ds_writebytes(self._buf) + self.cpin.value = False From cc75991b4188b194dcc2c08618e4790ce450649d Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 11:37:53 -0700 Subject: [PATCH 02/12] spacing --- advanced_dotstar.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/advanced_dotstar.py b/advanced_dotstar.py index dda0594..dca9458 100644 --- a/advanced_dotstar.py +++ b/advanced_dotstar.py @@ -1,7 +1,4 @@ from adafruit_dotstar import DotStar, START_HEADER_SIZE -LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits - - """ This is different than the standard Adafruit Dotstar library because it allows you to use the hardware brightness value on a per-pixel basis. @@ -12,6 +9,8 @@ you can override it if you use set_pixel """ +LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits + class AdvancedDotstar(DotStar): From 8d5ffd86add7e72b6561b613af34b4b9dd76b760 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 11:50:56 -0700 Subject: [PATCH 03/12] Improvements from testing --- advanced_dotstar.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/advanced_dotstar.py b/advanced_dotstar.py index dca9458..b4d2c62 100644 --- a/advanced_dotstar.py +++ b/advanced_dotstar.py @@ -1,4 +1,6 @@ -from adafruit_dotstar import DotStar, START_HEADER_SIZE +# PixelOrder is unused but I want people to be able to import it from here +from adafruit_dotstar import DotStar, START_HEADER_SIZE, PixelOrder +from math import ceil """ This is different than the standard Adafruit Dotstar library because it allows you to use the hardware brightness value on a per-pixel basis. @@ -12,7 +14,7 @@ LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits -class AdvancedDotstar(DotStar): +class AdvancedDotStar(DotStar): def _set_item(self, index, value): """ @@ -33,13 +35,12 @@ def _set_item(self, index, value): rgb = value if len(value) == 4: - brightness = value[4] + brightness = value[-1] rgb = value[:3] else: brightness = self.brightness - brightness_byte = int(brightness * 31) & 0b00011111 - + brightness_byte = ceil(brightness * 31) & 0b00011111 # LED startframe is three "1" bits, followed by 5 brightness bits self._buf[offset] = brightness_byte | LED_START From bab0a7f3c91cf2671c85c57e5b08f62096471d42 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 11:54:12 -0700 Subject: [PATCH 04/12] This is cleaner --- advanced_dotstar.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/advanced_dotstar.py b/advanced_dotstar.py index b4d2c62..91ed0bf 100644 --- a/advanced_dotstar.py +++ b/advanced_dotstar.py @@ -1,5 +1,7 @@ -# PixelOrder is unused but I want people to be able to import it from here -from adafruit_dotstar import DotStar, START_HEADER_SIZE, PixelOrder +from adafruit_dotstar import DotStar, START_HEADER_SIZE +# These are unused is unused but I want people to be able to import it from here +from adafruit_dotstar import RGB, RBG, GRB, GBR, BRG, BGR + from math import ceil """ This is different than the standard Adafruit Dotstar library because it From db655e0895f74aa47337f56bb42832457880824c Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 12:33:12 -0700 Subject: [PATCH 05/12] Move this into a subfolder --- advanced_dotstar.py => advanced_dotstar/advanced_dotstar.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename advanced_dotstar.py => advanced_dotstar/advanced_dotstar.py (100%) diff --git a/advanced_dotstar.py b/advanced_dotstar/advanced_dotstar.py similarity index 100% rename from advanced_dotstar.py rename to advanced_dotstar/advanced_dotstar.py From 4eed4e0a8d66c1538ff83cc17d15567538b04fbf Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 12:46:49 -0700 Subject: [PATCH 06/12] Merge advanced dotstar and regular dotstar at Scott's direction --- adafruit_dotstar.py | 61 +++++++++++++++---------- advanced_dotstar/advanced_dotstar.py | 66 ---------------------------- 2 files changed, 39 insertions(+), 88 deletions(-) delete mode 100644 advanced_dotstar/advanced_dotstar.py diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 03bc111..4ba1b73 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -137,19 +137,48 @@ def __repr__(self): return "[" + ", ".join([str(x) for x in self]) + "]" def _set_item(self, index, value): + """ + value can be one of three things: + either a (r,g,b) list/tuple + a (r,g,b, brightness) list or tuple + or a single int that contains RGB values, like 0xFFFFFF + brightness, if specified should be a float + + Set a pixel value. You can set per-pixel brightness here, if it's not passed it + will use send the max value for pixel brightness value, which is a good default. + + If the strip wide brightness value is <1, we'll use it to scale the rgb values. + This may seem like strange behavior, but we're trying to not expose the per-pixel brightness + unless you specifically request it. + + Important notes about the per-pixel brightness - it's accomplished by + PWMing the entire output of the LED, and that PWM is at a much + slower clock than the rest of the LEDs. This can cause problems in + Persistence of Vision Applications + """ + offset = index * 4 + START_HEADER_SIZE rgb = value if isinstance(value, int): rgb = (value >> 16, (value >> 8) & 0xff, value & 0xff) - # Each pixel starts with 0xFF, then red/green/blue. Although the data - # sheet suggests using a global brightness in the first byte, we don't - # do that because it causes further issues with persistence of vision - # projects. - self._buf[offset] = 0xff # redundant; should already be set - self._buf[offset + 1] = rgb[self.pixel_order[0]] - self._buf[offset + 2] = rgb[self.pixel_order[1]] - self._buf[offset + 3] = rgb[self.pixel_order[2]] + offset = index * 4 + START_HEADER_SIZE + rgb = value + + if len(value) == 4: + brightness = value[-1] + rgb = value[:3] + else: + brightness = 100 + + brightness_byte = ceil(brightness * 31) & 0b00011111 + # LED startframe is three "1" bits, followed by 5 brightness bits + # then 8 bits for each of R, G, and B. The order of those 3 are configurable and + # vary based on hardware + self._buf[offset] = brightness_byte | LED_START + self._buf[offset + 1] = int(rgb[self.pixel_order[0]] * self.brightness) + self._buf[offset + 2] = int(rgb[self.pixel_order[1]] * self.brightness) + self._buf[offset + 3] = (rgb[self.pixel_order[2]] * self.brightness) def __setitem__(self, index, val): if isinstance(index, slice): @@ -220,21 +249,9 @@ def show(self): The colors may or may not be showing after this function returns because it may be done asynchronously.""" - # Create a second output buffer if we need to compute brightness - buf = self._buf - if self.brightness < 1.0: - buf = bytearray(self._buf) - # Four empty bytes to start. - for i in range(START_HEADER_SIZE): - buf[i] = 0x00 - for i in range(START_HEADER_SIZE, self.end_header_index): - buf[i] = self._buf[i] if i % 4 == 0 else int(self._buf[i] * self._brightness) - # Four 0xff bytes at the end. - for i in range(self.end_header_index, len(buf)): - buf[i] = 0xff if self._spi: - self._spi.write(buf) + self._spi.write(self._buf) else: - self._ds_writebytes(buf) + self._ds_writebytes(self._buf) self.cpin.value = False diff --git a/advanced_dotstar/advanced_dotstar.py b/advanced_dotstar/advanced_dotstar.py deleted file mode 100644 index 91ed0bf..0000000 --- a/advanced_dotstar/advanced_dotstar.py +++ /dev/null @@ -1,66 +0,0 @@ -from adafruit_dotstar import DotStar, START_HEADER_SIZE -# These are unused is unused but I want people to be able to import it from here -from adafruit_dotstar import RGB, RBG, GRB, GBR, BRG, BGR - -from math import ceil -""" -This is different than the standard Adafruit Dotstar library because it -allows you to use the hardware brightness value on a per-pixel basis. -This is functionality the Neopixel does not support, and the standard -adafruit dotstar/neopixel libraries should match. - -The brightness argument here is the default brightness of each pixel, but -you can override it if you use set_pixel -""" - -LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits - - -class AdvancedDotStar(DotStar): - - def _set_item(self, index, value): - """ - value can be either a (r,g,b) tuple, or (r,g,b, brightness) - brightness should be a float - - Set a pixel value. You can use brightness here, if it's not passed it - will default to the strip's 'brightness' setting. - - Important notes about the per-pixel brightness - it's accomplished by - PWMing the entire output of the LED, and that PWM is at a much - slower clock than the rest of the LEDs. This can cause problems in - Persistence of Vision Applications - - """ - - offset = index * 4 + START_HEADER_SIZE - rgb = value - - if len(value) == 4: - brightness = value[-1] - rgb = value[:3] - else: - brightness = self.brightness - - brightness_byte = ceil(brightness * 31) & 0b00011111 - # LED startframe is three "1" bits, followed by 5 brightness bits - - self._buf[offset] = brightness_byte | LED_START - self._buf[offset + 1] = rgb[self.pixel_order[0]] - self._buf[offset + 2] = rgb[self.pixel_order[1]] - self._buf[offset + 3] = rgb[self.pixel_order[2]] - - if self.auto_write: - self.show() - - def show(self): - """ - Override parent show method because we don't do the float brightness - correction anymore. - """ - - if self._spi: - self._spi.write(self._buf) - else: - self._ds_writebytes(self._buf) - self.cpin.value = False From 401c316297d8475b8a685bfdfa373510b83418a2 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:00:15 -0700 Subject: [PATCH 07/12] I realized rebuilding the buffer on every show is an important part of the behavior --- adafruit_dotstar.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 4ba1b73..7d792f4 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -37,6 +37,7 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_DotStar.git" START_HEADER_SIZE = 4 +LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits # Pixel color order constants RGB = (0, 1, 2) @@ -171,14 +172,14 @@ def _set_item(self, index, value): else: brightness = 100 - brightness_byte = ceil(brightness * 31) & 0b00011111 + brightness_byte = math.ceil(brightness * 31) & 0b00011111 # LED startframe is three "1" bits, followed by 5 brightness bits # then 8 bits for each of R, G, and B. The order of those 3 are configurable and # vary based on hardware self._buf[offset] = brightness_byte | LED_START - self._buf[offset + 1] = int(rgb[self.pixel_order[0]] * self.brightness) - self._buf[offset + 2] = int(rgb[self.pixel_order[1]] * self.brightness) - self._buf[offset + 3] = (rgb[self.pixel_order[2]] * self.brightness) + self._buf[offset + 1] = int(rgb[self.pixel_order[0]]) + self._buf[offset + 2] = int(rgb[self.pixel_order[1]]) + self._buf[offset + 3] = (rgb[self.pixel_order[2]]) def __setitem__(self, index, val): if isinstance(index, slice): @@ -249,7 +250,23 @@ def show(self): The colors may or may not be showing after this function returns because it may be done asynchronously.""" + # Create a second output buffer if we need to compute brightness + buf = self._buf + if self.brightness < 1.0: + buf = bytearray(self._buf) + # Four empty bytes to start. + for i in range(START_HEADER_SIZE): + buf[i] = 0x00 + for i in range(START_HEADER_SIZE, self.end_header_index): + buf[i] = self._buf[i] if i % 4 == 0 else math.ceil(self._buf[i] * self._brightness) + # Four 0xff bytes at the end. + for i in range(self.end_header_index, len(buf)): + buf[i] = 0xff + if self._spi: + self._spi.write(buf) + else: + self._ds_writebytes(buf) if self._spi: self._spi.write(self._buf) else: From a89675983dc3ba0aee50ea4e105985bce27807b1 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:05:39 -0700 Subject: [PATCH 08/12] Update doc --- adafruit_dotstar.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 7d792f4..61f4c27 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -139,18 +139,14 @@ def __repr__(self): def _set_item(self, index, value): """ - value can be one of three things: - either a (r,g,b) list/tuple - a (r,g,b, brightness) list or tuple - or a single int that contains RGB values, like 0xFFFFFF - brightness, if specified should be a float + value can be one of three things: + a (r,g,b) list/tuple + a (r,g,b, brightness) list/tuple + a single, longer int that contains RGB values, like 0xFFFFFF + brightness, if specified should be a float 0-1 Set a pixel value. You can set per-pixel brightness here, if it's not passed it - will use send the max value for pixel brightness value, which is a good default. - - If the strip wide brightness value is <1, we'll use it to scale the rgb values. - This may seem like strange behavior, but we're trying to not expose the per-pixel brightness - unless you specifically request it. + will use the max value for pixel brightness value, which is a good default. Important notes about the per-pixel brightness - it's accomplished by PWMing the entire output of the LED, and that PWM is at a much From d4f2e98cca22e78b0105d328597b7388f3cfdaa9 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:18:23 -0700 Subject: [PATCH 09/12] polish --- adafruit_dotstar.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 61f4c27..2cd7581 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -263,8 +263,4 @@ def show(self): self._spi.write(buf) else: self._ds_writebytes(buf) - if self._spi: - self._spi.write(self._buf) - else: - self._ds_writebytes(self._buf) self.cpin.value = False From f41df7635da2417cd5d1c644df068ebcb6369f37 Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:32:52 -0700 Subject: [PATCH 10/12] polish --- adafruit_dotstar.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 2cd7581..0274d0e 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -159,19 +159,16 @@ def _set_item(self, index, value): if isinstance(value, int): rgb = (value >> 16, (value >> 8) & 0xff, value & 0xff) - offset = index * 4 + START_HEADER_SIZE - rgb = value - if len(value) == 4: brightness = value[-1] rgb = value[:3] else: - brightness = 100 + brightness = 1 - brightness_byte = math.ceil(brightness * 31) & 0b00011111 # LED startframe is three "1" bits, followed by 5 brightness bits # then 8 bits for each of R, G, and B. The order of those 3 are configurable and # vary based on hardware + brightness_byte = math.ceil(brightness * 31) & 0b00011111 self._buf[offset] = brightness_byte | LED_START self._buf[offset + 1] = int(rgb[self.pixel_order[0]]) self._buf[offset + 2] = int(rgb[self.pixel_order[1]]) From 91ea1887207edf89f4fcf97fa7408877df56aa3d Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:43:51 -0700 Subject: [PATCH 11/12] polish --- adafruit_dotstar.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index 0274d0e..d96fe68 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -170,9 +170,9 @@ def _set_item(self, index, value): # vary based on hardware brightness_byte = math.ceil(brightness * 31) & 0b00011111 self._buf[offset] = brightness_byte | LED_START - self._buf[offset + 1] = int(rgb[self.pixel_order[0]]) - self._buf[offset + 2] = int(rgb[self.pixel_order[1]]) - self._buf[offset + 3] = (rgb[self.pixel_order[2]]) + self._buf[offset + 1] = rgb[self.pixel_order[0]] + self._buf[offset + 2] = rgb[self.pixel_order[1]] + self._buf[offset + 3] = rgb[self.pixel_order[2]] def __setitem__(self, index, val): if isinstance(index, slice): From 8ed5aa40fe42f7e31251eee5b0274dae78e2fb2d Mon Sep 17 00:00:00 2001 From: mcscope Date: Mon, 14 May 2018 13:44:36 -0700 Subject: [PATCH 12/12] reduce scope of changes --- adafruit_dotstar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_dotstar.py b/adafruit_dotstar.py index d96fe68..2dade24 100755 --- a/adafruit_dotstar.py +++ b/adafruit_dotstar.py @@ -251,7 +251,7 @@ def show(self): for i in range(START_HEADER_SIZE): buf[i] = 0x00 for i in range(START_HEADER_SIZE, self.end_header_index): - buf[i] = self._buf[i] if i % 4 == 0 else math.ceil(self._buf[i] * self._brightness) + buf[i] = self._buf[i] if i % 4 == 0 else int(self._buf[i] * self._brightness) # Four 0xff bytes at the end. for i in range(self.end_header_index, len(buf)): buf[i] = 0xff