Skip to content

Commit 171998b

Browse files
authored
Merge pull request #25 from FoamyGuy/use_pixelbuf
Use adafruit_pixelbuf
2 parents 6604532 + a961df7 commit 171998b

File tree

3 files changed

+79
-106
lines changed

3 files changed

+79
-106
lines changed

adafruit_ws2801.py

Lines changed: 75 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,25 @@
22
# SPDX-FileCopyrightText: 2017 Ladyada for Adafruit Industries
33
# SPDX-FileCopyrightText: 2017 Scott Shawcroft for Adafruit Industries
44
# SPDX-FileCopyrightText: 2018 Kevin J. Walters
5+
# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries
56
#
67
# SPDX-License-Identifier: MIT
78

89
"""
910
`adafruit_ws2801` - WS2801 LED pixel string driver
1011
====================================================
1112
12-
* Author(s): Damien P. George, Limor Fried & Scott Shawcroft, Kevin J Walters
13+
* Author(s): Damien P. George, Limor Fried & Scott Shawcroft, Kevin J Walters, Tim Cocks
1314
"""
14-
import math
1515

16+
import adafruit_pixelbuf
1617
import busio
1718
import digitalio
1819

1920
try:
20-
from typing import Any, Union, Tuple, List
21+
from typing import Type, Optional
22+
from circuitpython_typing import ReadableBuffer
23+
from types import TracebackType
2124
from microcontroller import Pin
2225
except ImportError:
2326
pass
@@ -27,8 +30,22 @@
2730

2831
# based on https://github.com/adafruit/Adafruit_CircuitPython_DotStar
2932

30-
31-
class WS2801:
33+
# Pixel color order constants
34+
RBG = "PRBG"
35+
"""Red Blue Green"""
36+
RGB = "PRGB"
37+
"""Red Green Blue"""
38+
GRB = "PGRB"
39+
"""Green Red Blue"""
40+
GBR = "PGBR"
41+
"""Green Blue Red"""
42+
BRG = "PBRG"
43+
"""Blue Red Green"""
44+
BGR = "PBGR"
45+
"""Blue Green Red"""
46+
47+
48+
class WS2801(adafruit_pixelbuf.PixelBuf):
3249
"""
3350
A sequence of WS2801 controlled LEDs.
3451
@@ -38,7 +55,9 @@ class WS2801:
3855
:param float brightness: The brightness between 0.0 and (default) 1.0.
3956
:param bool auto_write: True if the dotstars should immediately change when
4057
set. If False, `show` must be called explicitly.
41-
58+
:param str pixel_order: Set the pixel order on the strip - different
59+
strips implement this differently. If you send red, and it looks blue
60+
or green on the strip, modify this! It should be one of the values above.
4261
4362
Example for Gemma M0:
4463
@@ -53,16 +72,34 @@ class WS2801:
5372
with adafruit_ws2801.WS2801(board.D2, board.D0, 25, brightness=1.0) as pixels:
5473
pixels[0] = darkred
5574
time.sleep(2)
75+
76+
.. py:method:: show()
77+
78+
Shows the new colors on the ws2801 LEDs themselves if they haven't already
79+
been autowritten.
80+
81+
The colors may or may not be showing after this function returns because
82+
it may be done asynchronously.
83+
84+
.. py:method:: fill(color)
85+
86+
Colors all ws2801 LEDs the given ***color***.
87+
88+
.. py:attribute:: brightness
89+
90+
Overall brightness of all ws2801 LEDs (0 to 1.0)
91+
5692
"""
5793

58-
def __init__(
94+
def __init__( # pylint: disable=too-many-arguments
5995
self,
6096
clock: Pin,
6197
data: Pin,
6298
n: int,
6399
*,
64100
brightness: float = 1.0,
65-
auto_write: bool = True
101+
auto_write: bool = True,
102+
pixel_order: str = "RGB",
66103
) -> None:
67104
self._spi = None
68105
try:
@@ -76,21 +113,31 @@ def __init__(
76113
self.dpin.direction = digitalio.Direction.OUTPUT
77114
self.cpin.direction = digitalio.Direction.OUTPUT
78115
self.cpin.value = False
79-
self._n = n
80-
self._buf = bytearray(n * 3)
81-
self._brightness = 1.0 # keeps pylint happy
82-
# Set auto_write to False temporarily so brightness setter does _not_
83-
# call show() while in __init__.
84-
self.auto_write = False
85-
self.brightness = brightness
86-
self.auto_write = auto_write
87-
# TODO - review/consider adding GRB support like that in c++ version
116+
117+
# Supply one extra clock cycle for each two pixels in the strip.
118+
trailer_size = n // 16
119+
if n % 16 != 0:
120+
trailer_size += 1
121+
122+
# Empty header.
123+
header = bytearray(0)
124+
# Zero bits, not ones, for the trailer, to avoid lighting up
125+
# downstream pixels, if there are more physical pixels than
126+
# the length of this object.
127+
trailer = bytearray(trailer_size)
128+
129+
super().__init__(
130+
n,
131+
byteorder=pixel_order,
132+
brightness=brightness,
133+
auto_write=auto_write,
134+
header=header,
135+
trailer=trailer,
136+
)
88137

89138
def deinit(self) -> None:
90-
"""Blank out the DotStars and release the resources."""
91-
self.auto_write = False
92-
black = (0, 0, 0)
93-
self.fill(black)
139+
"""Blank out the ws2801 LEDs and release the resources."""
140+
self.fill(0)
94141
self.show()
95142
if self._spi:
96143
self._spi.deinit()
@@ -102,81 +149,16 @@ def __enter__(self) -> "WS2801":
102149
return self
103150

104151
def __exit__(
105-
self, exception_type: Any, exception_value: Any, traceback: Any
152+
self,
153+
exception_type: Optional[Type[type]],
154+
exception_value: Optional[BaseException],
155+
traceback: Optional[TracebackType],
106156
) -> None:
107157
self.deinit()
108158

109159
def __repr__(self):
110160
return "[" + ", ".join([str(x) for x in self]) + "]"
111161

112-
def _set_item(self, index: int, value: Union[Tuple[int, ...], int]):
113-
offset = index * 3
114-
if isinstance(value, int):
115-
r = value >> 16
116-
g = (value >> 8) & 0xFF
117-
b = value & 0xFF
118-
else:
119-
r, g, b = value
120-
# red/green/blue order for WS2801
121-
self._buf[offset] = r
122-
self._buf[offset + 1] = g
123-
self._buf[offset + 2] = b
124-
125-
def __setitem__(self, index: int, val: Union[Tuple[int, ...], int]):
126-
if isinstance(index, slice):
127-
start, stop, step = index.indices(self._n)
128-
length = stop - start
129-
if step != 0:
130-
length = math.ceil(length / step)
131-
if len(val) != length:
132-
raise ValueError("Slice and input sequence size do not match.")
133-
for val_i, in_i in enumerate(range(start, stop, step)):
134-
self._set_item(in_i, val[val_i])
135-
else:
136-
self._set_item(index, val)
137-
138-
if self.auto_write:
139-
self.show()
140-
141-
def __getitem__(
142-
self, index: Union[slice, int]
143-
) -> Union[Tuple[int, ...], List[Tuple[int, ...]]]:
144-
if isinstance(index, slice):
145-
out = []
146-
for in_i in range(*index.indices(self._n)):
147-
out.append(tuple(self._buf[in_i * 3 + i] for i in range(3)))
148-
return out
149-
if index < 0:
150-
index += len(self)
151-
if index >= self._n or index < 0:
152-
raise IndexError
153-
offset = index * 3
154-
return tuple(self._buf[offset + i] for i in range(3))
155-
156-
def __len__(self) -> int:
157-
return self._n
158-
159-
@property
160-
def brightness(self) -> float:
161-
"""Overall brightness of the pixel"""
162-
return self._brightness
163-
164-
@brightness.setter
165-
def brightness(self, brightness: float) -> None:
166-
self._brightness = min(max(brightness, 0.0), 1.0)
167-
if self.auto_write:
168-
self.show()
169-
170-
def fill(self, color: Union[Tuple[int, ...], int]) -> None:
171-
"""Colors all pixels the given ***color***."""
172-
auto_write = self.auto_write
173-
self.auto_write = False
174-
for i, _ in enumerate(self):
175-
self[i] = color
176-
if auto_write:
177-
self.show()
178-
self.auto_write = auto_write
179-
180162
def _ds_writebytes(self, buf: bytearray) -> None:
181163
for b in buf:
182164
for _ in range(8):
@@ -185,21 +167,8 @@ def _ds_writebytes(self, buf: bytearray) -> None:
185167
self.cpin.value = False
186168
b = b << 1
187169

188-
def show(self) -> None:
189-
"""Shows the new colors on the pixels themselves if they haven't already
190-
been autowritten.
191-
192-
The colors may or may not be showing after this function returns because
193-
it may be done asynchronously."""
194-
# Create a second output buffer if we need to compute brightness
195-
buf = self._buf
196-
if self.brightness < 1.0:
197-
buf = bytearray(len(self._buf))
198-
for i, val in enumerate(self._buf):
199-
buf[i] = int(val * self._brightness)
200-
170+
def _transmit(self, buffer: ReadableBuffer) -> None:
201171
if self._spi:
202-
self._spi.write(buf)
172+
self._spi.write(buffer)
203173
else:
204-
self._ds_writebytes(buf)
205-
self.cpin.value = False
174+
self._ds_writebytes(buffer)

docs/api.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,8 @@
44
.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
55
.. use this format as the module name: "adafruit_foo.foo"
66
7+
API Reference
8+
=============
9+
710
.. automodule:: adafruit_ws2801
811
:members:

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@
44

55
Adafruit-Blinka
66
adafruit-circuitpython-busdevice
7+
adafruit-circuitpython-pixelbuf

0 commit comments

Comments
 (0)