# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2016 Damien P. George
|
|
# Copyright (c) 2017 Scott Shawcroft for Adafruit Industries
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
|
|
"""
|
|
`neopixel` - NeoPixel strip driver
|
|
====================================================
|
|
|
|
* Author(s): Damien P. George & Scott Shawcroft
|
|
"""
|
|
|
|
import math
|
|
|
|
import digitalio
|
|
from neopixel_write import neopixel_write
|
|
|
|
__version__ = "3.3.0"
|
|
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_NeoPixel.git"
|
|
|
|
# Pixel color order constants
|
|
RGB = (0, 1, 2)
|
|
"""Red Green Blue"""
|
|
GRB = (1, 0, 2)
|
|
"""Green Red Blue"""
|
|
RGBW = (0, 1, 2, 3)
|
|
"""Red Green Blue White"""
|
|
GRBW = (1, 0, 2, 3)
|
|
"""Green Red Blue White"""
|
|
|
|
class NeoPixel:
|
|
"""
|
|
A sequence of neopixels.
|
|
|
|
:param ~microcontroller.Pin pin: The pin to output neopixel data on.
|
|
:param int n: The number of neopixels in the chain
|
|
:param int bpp: Bytes per pixel. 3 for RGB and 4 for RGBW pixels.
|
|
:param float brightness: Brightness of the pixels between 0.0 and 1.0 where 1.0 is full
|
|
brightness
|
|
:param bool auto_write: True if the neopixels should immediately change when set. If False,
|
|
`show` must be called explicitly.
|
|
:param tuple pixel_order: Set the pixel color channel order. GRBW is set by default.
|
|
|
|
Example for Circuit Playground Express:
|
|
|
|
.. code-block:: python
|
|
|
|
import neopixel
|
|
from board import *
|
|
|
|
RED = 0x100000 # (0x10, 0, 0) also works
|
|
|
|
pixels = neopixel.NeoPixel(NEOPIXEL, 10)
|
|
for i in range(len(pixels)):
|
|
pixels[i] = RED
|
|
|
|
Example for Circuit Playground Express setting every other pixel red using a slice:
|
|
|
|
.. code-block:: python
|
|
|
|
import neopixel
|
|
from board import *
|
|
import time
|
|
|
|
RED = 0x100000 # (0x10, 0, 0) also works
|
|
|
|
# Using ``with`` ensures pixels are cleared after we're done.
|
|
with neopixel.NeoPixel(NEOPIXEL, 10) as pixels:
|
|
pixels[::2] = [RED] * (len(pixels) // 2)
|
|
time.sleep(2)
|
|
"""
|
|
def __init__(self, pin, n, *, bpp=3, brightness=1.0, auto_write=True, pixel_order=None):
|
|
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."""
|
|
for i in range(len(self.buf)):
|
|
self.buf[i] = 0
|
|
neopixel_write(self.pin, self.buf)
|
|
self.pin.deinit()
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exception_type, exception_value, traceback):
|
|
self.deinit()
|
|
|
|
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):
|
|
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:
|
|
if self.bpp == 3:
|
|
r, g, b = value
|
|
else:
|
|
r, g, b, w = value
|
|
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
|
|
|
|
Use ``show`` instead. It matches Micro:Bit and Arduino APIs."""
|
|
self.show()
|
|
|
|
def show(self):
|
|
"""Shows the new colors on the pixels themselves if they haven't already
|
|
been autowritten.
|
|
|
|
The colors may or may not be showing after this function returns because
|
|
it may be done asynchronously."""
|
|
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]))
|