tinue / apa102-pi

Pure Python library to drive APA102 LED stripes; Use with Raspberry Pi.
GNU General Public License v2.0
201 stars 71 forks source link

Allows individual pixel updates after each show(). #13

Closed jmb closed 7 years ago

jmb commented 7 years ago

By sending a copy of the pixel buffer to the SPI device, we can update individual pixels rather than having to set the entire buffer from scratch each time show() is called.

jmb commented 7 years ago

This might be a bit out of scope or implemented clumsily, but I've started this request in case it is useful.

tinue commented 7 years ago

Hmm, I assumed that the buffer is still usable after calling spi.xfer, and could be reused. That's why there is a clearStrip method, so that one can start with a fresh buffer if desired. Why do you think a copy is required?

jmb commented 7 years ago

Hmm, when I tried it seemed to clear the buffer. Let me try again...

tinue commented 7 years ago

Ok, thanks! As I say: I assumed, but never tested my assumption ;)

jmb commented 7 years ago

Here is what I'm trying - using the python console for ease of testing...

>>> import apa102
>>> strip = apa102.APA102(numLEDs=60, globalBrightness=5, order='rgb')
>>> strip.leds
[229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0, 229, 0, 0, 0]
>>> strip.setPixel(30, 255, 0, 0)
>>> strip.show()
>>> strip.leds
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> strip.setPixel(30, 0, 255, 0)
>>> strip.leds
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
>>> strip.show()
>>> strip.leds
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

After I try and re-set pixel 30 from red to green and then call strip.show() the second time, the first LED lights up green and the 30th stays red. It's quite likely that I've missed a step and this pull request isn't necessary!

tinue commented 7 years ago

Your observation matches the buffer content 100%: One can sent as many zeroes as one likes to the strip, and nothing happens. As soon as the first "pixel start" command is sent (the start frame), the first pixel sets its color. So, the first pixel is green (after many zeroes, a start frame and the color green arrive), and pixel 30 remains red, because no command reaches LED 30 to change anything. So apparently the buffer gets cleared, I just wonder why...

jmb commented 7 years ago

I guess it's something to do with the self.spi.xfer2(self.leds) call - hence my idea to copy the buffer to it rather than pass a reference. I guess both ways of working are valid, depending on what you want to achieve! :)

tinue commented 7 years ago

I just spent an entire afternoon debugging a problem with the rotate() method and finally found that spi.xfer kills the buffer. Then I came back to review the open requests and found our discussion. Now I feel a bit stupid... Anyway: I have to reject your pull request, because I already fixed the same problem...

jmb commented 7 years ago

Oops! Your solution looks better though - no need to create a copy of the buffer! 👍