adafruit / Adafruit_CircuitPython_seesaw

seesaw helper IC driver for circuitPython
MIT License
60 stars 35 forks source link

Use pixelbuf #106

Closed Neradoc closed 2 years ago

Neradoc commented 2 years ago

Use PixelBuf to back the pixels buffer in the neopixel submodule. This adds compatibility with the adafruit_led_animation library, in particular it adds: __getitem__ and slices support. It sends pixel data all at once, but in chunks to avoid IO errors. It no longer sends pixel data as they are changed. Also switches the pixel order constants to strings, to match the neopixel API.

The size of the chunks is an estimate, I'm not sure if there are devices with seesaw builds that require a smaller size. Sending the whole strip of pixels at once can be quite slow with long pixel strips. This is the only way to do it with PixelBuf however, since the only access to the buffer is in _transmit.

Comparatively the original code would send pixels as they are modified, and only send a "show" command on show to the seesaw, basically putting the double buffer on the seesaw. This can actually be beneficial for some specific types of animations, but it's a tradeoff.

An alternative to this PR would be to add the PixelBuf API in python to the current implementation to keep the double buffering (and also optimize slices with chunks), for which the python version of PixelBuf might help.

I did some tests on Feather RP2040 with a Neokey 1x4 and a SAMD09 and ATTiny8x7 seesaw breakouts with a 30 pixels strip, no RGBW device. Additional tests on other seesaw boards would be nice.

Here is the test code:

import time
import board
import busio

i2c_bus = board.I2C()

# Test with seesaw breakout
from adafruit_seesaw import neopixel
from adafruit_seesaw.seesaw import Seesaw
seesaw = Seesaw(i2c_bus)
pixels = neopixel.NeoPixel(seesaw, 3, 30)

# test with Neokey 1x4:
"""
from adafruit_neokey.neokey1x4 import NeoKey1x4
neokey = NeoKey1x4(i2c_bus)
neokey.pixels.fill(0)
pixels = neokey.pixels
"""

from adafruit_led_animation.animation.rainbow import Rainbow
from adafruit_led_animation.animation.chase import Chase
from adafruit_led_animation.animation.comet import Comet
from adafruit_led_animation.sequence import AnimationSequence
from adafruit_led_animation.color import AMBER, JADE

# works in the original version
comet = Comet(pixels, speed=0.02, color=JADE, tail_length=10, bounce=True)
# needs __getitem__, causes a MemoryError in the original version
chase = Chase(pixels, speed=0.04, size=3, spacing=6, color=AMBER)
# needs slices, causes TypeError in the original version
rainbow = Rainbow(pixels, speed=0.04)

while True:
    for x in range(100):
        comet.animate()
        time.sleep(0.01)
    for x in range(100):
        chase.animate()
        time.sleep(0.01)
    for x in range(100):
        rainbow.animate()
        time.sleep(0.01)

This should fix #105