Skip to content
This repository was archived by the owner on Apr 20, 2022. It is now read-only.

Implement adafruit_pypixelbuf necessary for neopixel and dotstar without _pixelbuf #3

Merged
merged 15 commits into from
Jan 7, 2020
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ bundles
*.DS_Store
.eggs
dist
**/*.egg-info
**/*.egg-info
.vscode
7 changes: 5 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@ python:
cache:
pip: true

# TODO: if deployment to PyPi is desired, change 'DEPLOY_PYPI' to "true",
# or remove the env block entirely and remove the condition in the
# deploy block.
env:
- DEPLOY_PYPI="true"
- DEPLOY_PYPI="false"

deploy:
- provider: releases
Expand All @@ -39,7 +42,7 @@ install:
- pip install --force-reinstall pylint==1.9.2

script:
- pylint pypixelbuf.py
- pylint adafruit_pypixelbuf.py
- ([[ ! -d "examples" ]] || pylint --disable=missing-docstring,invalid-name,bad-whitespace examples/*.py)
- circuitpython-build-bundles --filename_prefix adafruit-circuitpython-pypixelbuf --library_location .
- cd docs && sphinx-build -E -W -b html . _build/html && cd ..
4 changes: 2 additions & 2 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
The MIT License (MIT)

Based on the Adafruit NeoPixel and Adafruit DotStar CircuitPython drivers.
Copyright (c) 2019 Roy Hooper
Based on the Adafruit NeoPixel and Adafruit DotStar CircuitPython libraries.
Copyright (c) 2019-2020 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
Expand Down
59 changes: 9 additions & 50 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,15 @@ This driver depends on:

Please ensure all dependencies are available on the CircuitPython filesystem.
This is easily achieved by downloading
`the Adafruit library and driver bundle <https://github.com/adafruit/Adafruit_CircuitPython_Bundle>`_.
`the Adafruit library and driver bundle <https://circuitpython.org/libraries>`_.

Installing from PyPI
=====================
.. note:: This library is not available on PyPI yet. Install documentation is included
as a standard element. Stay tuned for PyPI availability!

.. todo:: Remove the above note if PyPI version is/will be available at time of release.
If the library is not planned for PyPI, remove the entire 'Installing from PyPI' section.

On supported GNU/Linux systems like the Raspberry Pi, you can install the driver locally `from
PyPI <https://pypi.org/project/adafruit-circuitpython-pypixelbuf/>`_. To install for current user:
Expand Down Expand Up @@ -74,60 +79,14 @@ This example tests that the pypixelbuf works.

print(callback_called)


Contributing
============

Contributions are welcome! Please read our `Code of Conduct
<https://github.com/adafruit/Adafruit_CircuitPython_Pypixelbuf/blob/master/CODE_OF_CONDUCT.md>`_
before contributing to help this project stay welcoming.

Building locally
================

Zip release files
-----------------

To build this library locally you'll need to install the
`circuitpython-build-tools <https://github.com/adafruit/circuitpython-build-tools>`_ package.

.. code-block:: shell

python3 -m venv .env
source .env/bin/activate
pip install circuitpython-build-tools

Once installed, make sure you are in the virtual environment:

.. code-block:: shell

source .env/bin/activate

Then run the build:

.. code-block:: shell

circuitpython-build-bundles --filename_prefix adafruit-circuitpython-pypixelbuf --library_location .

Sphinx documentation
-----------------------

Sphinx is used to build the documentation based on rST files and comments in the code. First,
install dependencies (feel free to reuse the virtual environment from above):

.. code-block:: shell

python3 -m venv .env
source .env/bin/activate
pip install Sphinx sphinx-rtd-theme

Now, once you have the virtual environment activated:

.. code-block:: shell

cd docs
sphinx-build -E -W -b html . _build/html
Documentation
=============

This will output the documentation to ``docs/_build/html``. Open the index.html in your browser to
view them. It will also (due to -W) error out on any warning like Travis will. This is a good way to
locally verify it will pass.
For information on building library documentation, please check out `this guide <https://learn.adafruit.com/creating-and-sharing-a-circuitpython-library/sharing-our-docs-on-readthedocs#sphinx-5-1>`_.
105 changes: 39 additions & 66 deletions pypixelbuf.py → adafruit_pypixelbuf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# The MIT License (MIT)
#
# Based on the Adafruit NeoPixel and Adafruit Dotstar CircuitPython drivers.
# Copyright (c) 2019 Roy Hooper
# Copyright (c) 2019-2020 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
Expand All @@ -24,60 +24,34 @@
"""
`pypixelbuf` - A pure python implementation of pixelbuf
=======================================================
This class is used when _pixelbuf is not available in a build.

This is a work in progress.
This class is used when _pixelbuf is not available in CircuitPython. It is based on the work
in neopixel.py and adafruit_dotstar.py.

* Author(s): Damien P. George & Limor Fried & Scott Shawcroft & Roy Hooper
"""
import math
import re

RGB = "RGB"
RBG = "RBG"
GRB = "GRB"
GBR = "GBR"
BRG = "BRG"
BGR = "BGR"
RGBW = "RGBW"
RBGW = "RBGW"
GRBW = "GRBW"
GBRW = "GBRW"
BRGW = "BRGW"
BGRW = "BGRW"
RGBD = "RGBD"
RBGD = "RBGD"
GRBD = "GRBD"
GBRD = "GBRD"
BRGD = "BRGD"
BGRD = "BGRD"

DOTSTAR_LED_START_FULL_BRIGHT = 0xFF
DOTSTAR_LED_START = 0b11100000 # Three "1" bits, followed by 5 brightness bits
DOTSTAR_LED_BRIGHTNESS = 0b00011111

IS_PURE_PYTHON = True


class PixelBuf(object): # pylint: disable=too-many-instance-attributes
"""
A sequence of RGB/RGBW pixels.

This is the pure python implementation of _pixelbuf.
This is the pure python implementation of CircuitPython's _pixelbuf.

:param ~int n: Number of pixels
:param ~bytearray buf: Bytearray to store pixel data in
:param ~str byteorder: Byte order string constant (also sets the bpp)
:param ~str byteorder: Byte order string constant (also sets bpp)
:param ~float brightness: Brightness (0 to 1.0, default 1.0)
:param ~bytearray rawbuf: Bytearray to store raw pixel colors in
:param ~int offset: Offset from start of buffer (default 0)
:param ~bool auto_write: Whether to automatically write pixels (Default False)
:param ~callable write_function: (optional) Callable to use to send pixels
:param ~list write_args: (optional) Tuple or list of args to pass to ``write_function``. The
PixelBuf instance is appended after these args.
"""
def __init__(self, n, buf, byteorder=BGR, brightness=1.0, # pylint: disable=too-many-locals,too-many-arguments
rawbuf=None, offset=0, auto_write=False, write_function=None, write_args=None):
def __init__(self, n, buf, byteorder="BGR", brightness=1.0, # pylint: disable=too-many-locals,too-many-arguments
rawbuf=None, offset=0, auto_write=False):

bpp, byteorder_tuple, has_white, dotstar_mode = self.parse_byteorder(byteorder)
if not isinstance(buf, bytearray):
Expand Down Expand Up @@ -115,11 +89,6 @@ def __init__(self, n, buf, byteorder=BGR, brightness=1.0, # pylint: disable=too
self._byteorder_tuple = (byteorder_tuple[0] + 1, byteorder_tuple[1] + 1,
byteorder_tuple[2] + 1, 0)

self._write_function = write_function
self._write_args = ()
if write_args:
self._write_args = tuple(self._write_args) + (self, )

self._brightness = min(1.0, max(0, brightness))

if dotstar_mode:
Expand All @@ -137,7 +106,7 @@ def parse_byteorder(byteorder):
G - Green
B - Blue
W - White
D - Dotstar luminositry
P - PWM (PWM Duty cycle for pixel - dotstars 0 - 1.0)

:param: ~str bpp: bpp string.
:return: ~tuple: bpp, byteorder, has_white, dotstar_mode
Expand All @@ -146,7 +115,7 @@ def parse_byteorder(byteorder):
dotstar_mode = False
has_white = False

if re.search(r'[^RGBWd]', byteorder):
if re.search(r'[^RGBWP]', byteorder):
raise ValueError("Invalid Byteorder string")

try:
Expand All @@ -158,15 +127,15 @@ def parse_byteorder(byteorder):
if 'W' in byteorder:
w = byteorder.index("W")
byteorder = (r, g, b, w)
elif 'd' in byteorder:
lum = byteorder.index("D")
elif 'P' in byteorder:
lum = byteorder.index("P")
byteorder = (r, g, b, lum)
dotstar_mode = True
else:
byteorder = (r, g, b)

return bpp, byteorder, has_white, dotstar_mode


@property
def bpp(self):
"""
Expand All @@ -190,7 +159,7 @@ def brightness(self):
def brightness(self, value):
self._brightness = min(max(value, 0.0), 1.0)

# Adjust brightness when two buffers are available
# Adjust brightness of existing pixels when two buffers are available
if self._two_buffers:
offset_check = self._offset % self._pixel_step
for i in range(self._offset, self._bytes + self._offset):
Expand All @@ -217,8 +186,7 @@ def show(self):
"""
Call the associated write function to display the pixels
"""
if self._write_function:
self._write_function(*self._write_args)
raise NotImplementedError("Must be subclassed")

def _set_item(self, index, value): # pylint: disable=too-many-locals,too-many-branches
if index < 0:
Expand Down Expand Up @@ -262,6 +230,7 @@ def _set_item(self, index, value): # pylint: disable=too-many-locals,too-many-b
self._bytearray[offset + self._byteorder[0]] = int(r * self._brightness)
self._bytearray[offset + self._byteorder[1]] = int(g * self._brightness)
self._bytearray[offset + self._byteorder[2]] = int(b * self._brightness)

if has_w:
if self._dotstar_mode:
# LED startframe is three "1" bits, followed by 5 brightness bits
Expand All @@ -282,11 +251,6 @@ def _set_item(self, index, value): # pylint: disable=too-many-locals,too-many-b
def __setitem__(self, index, val):
if isinstance(index, slice):
start, stop, step = index.indices(self._pixels)
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:
Expand All @@ -296,14 +260,18 @@ def __setitem__(self, index, val):
self.show()

def _getitem(self, index):
start = self._offset + (index * self.bpp)
value = [
self._bytearray[start + self._byteorder[0]],
self._bytearray[start + self._byteorder[1]],
self._bytearray[start + self._byteorder[2]],
]
if self._has_white:
return tuple(self._bytearray[self._offset + (index * self.bpp) +
self._byteorder[i]] for i in range(self.bpp))
return tuple(
[self._bytearray[self._offset + (index * self.bpp) + self._byteorder[i]]
for i in range(3)] + [(self._bytearray[self._offset + (index * self.bpp) +
self._byteorder[3]] &
DOTSTAR_LED_BRIGHTNESS) / 31.0])
value.append(self._bytearray[start + self._byteorder[2]])
elif self._dotstar_mode:
value.append((self._bytearray[start + self._byteorder[3]] & DOTSTAR_LED_BRIGHTNESS) /
31.0)
return value

def __getitem__(self, index):
if isinstance(index, slice):
Expand All @@ -317,14 +285,6 @@ def __getitem__(self, index):
raise IndexError
return self._getitem(index)

def fill_wheel(self, n, step):
"""
fill the buffer with a colorwheel starting at offset n, and stepping by step
"""
self[0:len(self)] = [wheel((n + (step * i)) % 255) for i in range(len(self))]
if self.auto_write:
self.show()


def wheel(pos):
"""
Expand All @@ -344,3 +304,16 @@ def wheel(pos):
return 0, 255 - pos * 3, pos * 3
pos -= 170
return pos * 3, 0, 255 - pos * 3


def fill(pixelbuf, color):
"""
Helper to fill the strip a specific color.
:param pixelbuf: A pixel object.
:param color: Color to set.
"""
auto_write = pixelbuf.auto_write
pixelbuf.auto_write = False
for i, _ in enumerate(pixelbuf):
pixelbuf[i] = color
pixelbuf.auto_write = auto_write
2 changes: 1 addition & 1 deletion docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
.. If your library file(s) are nested in a directory (e.g. /adafruit_foo/foo.py)
.. use this format as the module name: "adafruit_foo.foo"

.. automodule:: pypixelbuf
.. automodule:: adafruit_pypixelbuf
:members:
12 changes: 6 additions & 6 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
master_doc = 'index'

# General information about the project.
project = u'Adafruit Pypixelbuf Library'
copyright = u'2019 Roy Hooper'
project = u'Adafruit PyPixelBuf Library'
copyright = u'2020 Roy Hooper'
author = u'Roy Hooper'

# The version info for the project you're documenting, acts as replacement for
Expand Down Expand Up @@ -135,7 +135,7 @@
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'AdafruitPypixelbufLibrary.tex', u'AdafruitPypixelbuf Library Documentation',
(master_doc, 'AdafruitPyPixelBufLibrary.tex', u'AdafruitPyPixelBuf Library Documentation',
author, 'manual'),
]

Expand All @@ -144,7 +144,7 @@
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'AdafruitPypixelbuflibrary', u'Adafruit Pypixelbuf Library Documentation',
(master_doc, 'AdafruitPyPixelBuflibrary', u'Adafruit PyPixelBuf Library Documentation',
[author], 1)
]

Expand All @@ -154,7 +154,7 @@
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'AdafruitPypixelbufLibrary', u'Adafruit Pypixelbuf Library Documentation',
author, 'AdafruitPypixelbufLibrary', 'One line description of project.',
(master_doc, 'AdafruitPyPixelBufLibrary', u'Adafruit PyPixelBuf Library Documentation',
author, 'AdafruitPyPixelBufLibrary', 'One line description of project.',
'Miscellaneous'),
]
4 changes: 2 additions & 2 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Simple test

Ensure your device works with this simple test.

.. literalinclude:: ../examples/pypixelbuf_simpletest.py
:caption: examples/pypixelbuf_simpletest.py
.. literalinclude:: ../examples/adafruit_pypixelbuf_simpletest.py
:caption: examples/adafruit_pypixelbuf_simpletest.py
:linenos:
Loading