ThePiHut / rgbxmastree

Code examples for the RGB Xmas Tree
90 stars 38 forks source link

Very slow #21

Open jirihajek opened 3 years ago

jirihajek commented 3 years ago

Hi, my tree works, but is very slow. Setting a single value takes about a second (or more). I found out that it's device specific, it's slow on the old Pi 1.2B, but works fine on Pi 4B. Is it because of the software based SPI transfer? Any idea how to make this faster?

I'd need to use it on the old Pi and in the current state it's hard to code any meaningful effect, as you literally see the LEDs changing one by one.

Thanks!

bennuttall commented 3 years ago

Yes it's software SPI. Your best bet is to inspect the source code and try to figure out a more efficient method without the abstraction.

I didn't think it would be that slow on a Pi 1. I'll take a look when I get chance.

jirihajek commented 3 years ago

I'm not much familiar with SPI, just tried to look up something. Is there a documentation of the RGB tree implementation anywhere? I mean like timing, etc. It seems that HW implementation is very often silently expected. I mean like several node.js SPI libraries, none of them supporting SW SPI, only HW (mentioning node.js as I know it better than Python).

bennuttall commented 3 years ago

Not that I know of, but the underlying SPI code is here: https://github.com/gpiozero/gpiozero/blob/master/gpiozero/spi_devices.py#L52 and the tree-specific stuff is here: https://github.com/ThePiHut/rgbxmastree/blob/master/tree.py

captain-gilliam commented 3 years ago

It is indeed slow, I'm using it on a Pi Zero. But there's a workaround. You can assign directly to the tree.value property, which is a tuple made up of as many tuples as you have LEDs on your tree. Which means you could code a loop and for each iteration, build a tuple made of all the colors you'd like, then assign it to tree.value.

So in my opinion, you could try that before going deeper. I've already experimented a bit on it but I intend to keep going to develop something with some effects.

jirihajek commented 3 years ago

But there's a workaround. You can assign directly to the tree.value property, which is a tuple made up of as many tuples as you have LEDs on your tree.

Yes, but that's actually what's pretty slow, e.g. just changing tree.color, which internally changes tree.value in a single step results in one-by-one LED changes (for the older/slower Pi).

However, I found a solution. It's Node.js based, mainly because I'm more familiar with node than python, but also I found a good library that works really fast: https://www.npmjs.com/package/pigpio. A simple code that goes through red, blue and green colors for all the 25 LEDs is so fast, that you don't even see the individual colors, just a jittery white! So, probably 100x or more faster than the Python code. ;-) Just needs to be run as root, but it's not a big deal for me.

limsim commented 3 years ago

@jirihajek Could you share some code using pigpio? I don't know anything about GPIO so am struggling to understand enough to convert what I've done in Python to Nodejs using the library. TIA.

jirihajek commented 3 years ago

Sure, the basics are actually very easy, here's how to send a bit:

const clock = new rpio(25);
const mosi = new rpio(12);

function sendBit(value) {
  mosi.digitalWrite(value ? 1 : 0);
  clock.digitalWrite(1);
  clock.digitalWrite(0);
}

There might be some timeout needed after the clock pin write, but it works like this fine on my old Pi.

And this does all the other basic stuff needed to update all the LEDs:

function sendByte(value) {
  for (var bit = 0x80; bit; bit >>= 1) {
    sendBit(Boolean(value & bit));
  }
}

function sendLED(inten, r, g, b) {
  sendByte(0xe0 | inten);
  sendByte(b);
  sendByte(g);
  sendByte(r);
}

function updateTree() {
  // Init
  for (var i = 0; i < 4; i++)
    sendByte(0);

  // LEDs
  for (var i = 0; i < 25; i++) {
    const led = leds[i];
    if (led && led.l) {
      sendLED(bright, led.r, led.g, led.b)
    } else {
      sendLED(0, 0, 0, 0);
    }
  }

  // Finish
  for (var i = 0; i < 5; i++)
    sendByte(0);
}

Note that sudo node tree.js is needed to run the code as root.

Just finishing Blockly visual language implementation to let my son program the LED tree from his iPad... ;-)

limsim commented 3 years ago

Thanks for sharing @jirihajek . Will have a look into Blockly. sounds interesting.

I've been creating different lighting sequences over here. If I get it working I'll translate them into nodejs and try to do more sequences. There were a few things that I tried when using the Python module that didn't work out due to the slowness.

Ideally I would like to circle back and improve the tree.py module once I understand what's going on.

gilesknap commented 2 years ago

FYI @limsim I have made a minor change in #26 that lets you batch up changes to pixel colour for much faster animations (each update is still just as slow but you can change as many pixels as you like on each update)

jwhitham commented 2 years ago

For Python users, the speed improves a lot if you use the pigpio library instead of rpigpio. (This may be the same library suggested by jirihajek; it has a Python interface as well as Node.)

You have to install the pigpio and python3-pygpio packages, and then you have to run the examples with the environment variable GPIOZERO_PIN_FACTORY set to "pigpio". The pigpio daemon has to be started as well. For some reason this is about ten times faster than rpigpio. On my RPi 3B, assigning to "tree.color" takes about 22 milliseconds with pigpio, but about 270 milliseconds with rpigpio.

pigpio still uses software SPI, but the speed is similar to driving the SPI pins directly with GPIO.output from within the tree "value" method.

tinue commented 2 years ago

I just updated a generic library for APA102 LED strips, see https://github.com/tinue/apa102-pi The library does work with the xmas tree, so if you are interested then give it a try. To prevent disappointment: On a Pi Zero W, the update is not instantaneous. A small "ripple" is visible when all LEDs change color at the same time. Smooth effects, like going through a rainbow, look fine though. On a Pi 4 all updates look fine, even instant color changes.

karabaja4 commented 1 month ago

If anyone is still interested in this, since I also wasn't happy with CPU usage of rpigpio, based on jirihajek's comment I put together a full Node.js version of onebyone.py using pigpio: https://github.com/karabaja4/nodexmastree