diff --git a/neopixel.py b/neopixel.py index aca77d5..9840700 100644 --- a/neopixel.py +++ b/neopixel.py @@ -2,8 +2,6 @@ # # Copyright (c) 2016 Damien P. George # Copyright (c) 2017 Scott Shawcroft for Adafruit Industries -# Copyright (c) 2019 Carter Nelson -# Copyright (c) 2019 Roy Hooper # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal @@ -27,33 +25,28 @@ `neopixel` - NeoPixel strip driver ==================================================== -* Author(s): Damien P. George, Scott Shawcroft, Carter Nelson, Roy Hooper +* Author(s): Damien P. George & Scott Shawcroft """ +import math + import digitalio from neopixel_write import neopixel_write -try: - import _pixelbuf -except ImportError: - import adafruit_pypixelbuf as _pixelbuf - __version__ = "0.0.0-auto.0" __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel.git" - # Pixel color order constants -RGB = 'RGB' +RGB = (0, 1, 2) """Red Green Blue""" -GRB = 'GRB' +GRB = (1, 0, 2) """Green Red Blue""" -RGBW = 'RGBW' +RGBW = (0, 1, 2, 3) """Red Green Blue White""" -GRBW = 'GRBW' +GRBW = (1, 0, 2, 3) """Green Red Blue White""" - -class NeoPixel(_pixelbuf.PixelBuf): +class NeoPixel: """ A sequence of neopixels. @@ -64,7 +57,7 @@ class NeoPixel(_pixelbuf.PixelBuf): brightness :param bool auto_write: True if the neopixels should immediately change when set. If False, `show` must be called explicitly. - :param str: Set the pixel color channel order. GRBW is set by default. + :param tuple pixel_order: Set the pixel color channel order. GRBW is set by default. Example for Circuit Playground Express: @@ -94,37 +87,28 @@ class NeoPixel(_pixelbuf.PixelBuf): pixels[::2] = [RED] * (len(pixels) // 2) time.sleep(2) """ - bpp = None - n = 0 - def __init__(self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None): - self.bpp = bpp - self.n = n - - if not pixel_order: - pixel_order = GRB if bpp == 3 else GRBW - else: - self.bpp = bpp = len(pixel_order) - if isinstance(pixel_order, tuple): - order_chars = RGBW - order = [] - for char_no, order in enumerate(pixel_order): - order[pixel_order] = order_chars[char_no] - pixel_order = ''.join(order) - - super().__init__(n, bytearray(self.n * bpp), - brightness=brightness, - rawbuf=bytearray(self.n * bpp), - byteorder=pixel_order, - auto_write=auto_write) - self.pin = digitalio.DigitalInOut(pin) self.pin.direction = digitalio.Direction.OUTPUT + self.n = n + if pixel_order is None: + self.order = GRBW + self.bpp = bpp + else: + self.order = pixel_order + self.bpp = len(self.order) + self.buf = bytearray(self.n * self.bpp) + # Set auto_write to False temporarily so brightness setter does _not_ + # call show() while in __init__. + self.auto_write = False + self.brightness = brightness + self.auto_write = auto_write def deinit(self): """Blank out the NeoPixels and release the pin.""" - self.fill(0) - self.show() + for i in range(len(self.buf)): + self.buf[i] = 0 + neopixel_write(self.pin, self.buf) self.pin.deinit() def __enter__(self): @@ -136,6 +120,100 @@ def __exit__(self, exception_type, exception_value, traceback): def __repr__(self): return "[" + ", ".join([str(x) for x in self]) + "]" + def _set_item(self, index, value): + if index < 0: + index += len(self) + if index >= self.n or index < 0: + raise IndexError + offset = index * self.bpp + r = 0 + g = 0 + b = 0 + w = 0 + if isinstance(value, int): + if value>>24: + raise ValueError("only bits 0->23 valid for integer input") + r = value >> 16 + g = (value >> 8) & 0xff + b = value & 0xff + w = 0 + # If all components are the same and we have a white pixel then use it + # instead of the individual components. + if self.bpp == 4 and r == g and g == b: + w = r + r = 0 + g = 0 + b = 0 + elif (len(value) == self.bpp) or ((len(value) == 3) and (self.bpp == 4)): + if len(value) == 3: + r, g, b = value + else: + r, g, b, w = value + else: + raise ValueError("Color tuple size does not match pixel_order.") + + self.buf[offset + self.order[0]] = r + self.buf[offset + self.order[1]] = g + self.buf[offset + self.order[2]] = b + if self.bpp == 4: + self.buf[offset + self.order[3]] = w + + def __setitem__(self, index, val): + if isinstance(index, slice): + start, stop, step = index.indices(len(self.buf) // self.bpp) + length = stop - start + if step != 0: + length = math.ceil(length / step) + if len(val) != length: + raise ValueError("Slice and input sequence size do not match.") + for val_i, in_i in enumerate(range(start, stop, step)): + self._set_item(in_i, val[val_i]) + else: + self._set_item(index, val) + + if self.auto_write: + self.show() + + def __getitem__(self, index): + if isinstance(index, slice): + out = [] + for in_i in range(*index.indices(len(self.buf) // self.bpp)): + out.append(tuple(self.buf[in_i * self.bpp + self.order[i]] + for i in range(self.bpp))) + return out + if index < 0: + index += len(self) + if index >= self.n or index < 0: + raise IndexError + offset = index * self.bpp + return tuple(self.buf[offset + self.order[i]] + for i in range(self.bpp)) + + def __len__(self): + return len(self.buf) // self.bpp + + @property + def brightness(self): + """Overall brightness of the pixel""" + return self._brightness + + @brightness.setter + def brightness(self, brightness): + # pylint: disable=attribute-defined-outside-init + self._brightness = min(max(brightness, 0.0), 1.0) + if self.auto_write: + self.show() + + def fill(self, color): + """Colors all pixels the given ***color***.""" + auto_write = self.auto_write + self.auto_write = False + for i, _ in enumerate(self): + self[i] = color + if auto_write: + self.show() + self.auto_write = auto_write + def write(self): """.. deprecated: 1.0.0 @@ -148,8 +226,7 @@ def show(self): The colors may or may not be showing after this function returns because it may be done asynchronously.""" - neopixel_write(self.pin, self.buf) - - def fill(self, color): - """Colors all pixels the given ***color***.""" - _pixelbuf.fill(self, color) + if self.brightness > 0.99: + neopixel_write(self.pin, self.buf) + else: + neopixel_write(self.pin, bytearray([int(i * self.brightness) for i in self.buf])) diff --git a/requirements.txt b/requirements.txt index 5feab09..edf9394 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1 @@ Adafruit-Blinka -adafruit-circuitpython-pypixelbuf diff --git a/setup.py b/setup.py index 19b9d72..b2eb264 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ author='Adafruit Industries & Damien P. George', author_email='circuitpython@adafruit.com', - install_requires=['Adafruit-Blinka', 'adafruit-circuitpython-pypixelbuf'], + install_requires=['Adafruit-Blinka'], # Choose your license license='MIT',