0

creating led-matrix from multiple rows of led strips LPD8806
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

creating led-matrix from multiple rows of led strips LPD8806

by silverline on Thu Dec 08, 2011 10:42 pm

I was just wondering whether it is possible to control multiple (let's say 16 rows of) led-strips LPD8806 using an Arduino.

We'd like to create a super long led-matrix-screen for animation purposes (and then feed processing with png-sequences for example)

Or can you just control one row of led strip (LPD8806) with an arduino?
If that's the case, would i'd be wise to just have processing control 16 arduino's?
silverline
 
Posts: 17
Joined: Thu Dec 08, 2011 10:36 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by wetterberg on Fri Dec 09, 2011 6:21 am

wetterberg
 
Posts: 10
Joined: Fri Nov 25, 2011 12:21 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Fri Dec 09, 2011 1:17 pm

Definitely possible. What you'd probably want isn't really 16 separate strips, but one hugely long contiguous strip arranged in a tight zig-zag pattern. The data going to each "zag" gets flipped in software.

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by silverline on Wed Dec 14, 2011 11:06 am

nice work guys...

we have a Teensy 2.0 where we attach one strip of 60 leds LPD8806. We have connected it to pin1 and pin2 of the Teensy and are now running processing with the Movie sketch from adavision.
We have loaded Arduino with a modified version of LEDstream (to be working with cjbaar's modfied library of adafruits LPD8806, which should be faster).

but we can't make it happen for more than 2 strips - merging these two libraries is quite difficult - someone else also bumping in to these problems?

p.s.

plus, we don't want a zig zag, because we are making a tree-like- installation.... is it also possible to connect multiple strips on one teensy?
silverline
 
Posts: 17
Joined: Thu Dec 08, 2011 10:36 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Wed Dec 14, 2011 3:04 pm

silverline wrote:We have loaded Arduino with a modified version of LEDstream (to be working with cjbaar's modfied library of adafruits LPD8806, which should be faster).


Not necessarily. FastSPI shines in that it efficiently supports some of the funky LED strips that require a clock signal...but with the 8806, both are doing pretty much the same thing.

silverline wrote:but we can't make it happen for more than 2 strips


Does this happen with both libraries? Or just one or the other? Wonder if it might be in the latch calculation.

silverline wrote:p.s. plus, we don't want a zig zag, because we are making a tree-like- installation.... is it also possible to connect multiple strips on one teensy?


Possible, yes. It'll take a long explanation, but I would basically suggest ditching the existing library code and taking an entirely different approach in this case. Are you comfortable with that as a possibility?

Also, is this something that's going to be driven strictly from an Arduino/Teensy, or will it be tethered to a PC?

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by silverline on Wed Dec 14, 2011 3:35 pm

pburgess wrote:Also, is this something that's going to be driven strictly from an Arduino/Teensy, or will it be tethered to a PC?


yes, we use processing to stream a movie to the teensy.
because its a teensy, usb is native, and because ldp8806 is lightning fast (or so ive heard), buffering isn't really required...

but thankfully we got it!
After two days of headache of understanding the libraries we never thought it would be this simple..

we wrote a bit code derived from ledstream that parses all data from serial port - that is the bytes from Serial.read, which are
Code: Select all | TOGGLE FULL SIZE
 [A,d,a, hi,lo, checksum, r0, g0, b0, r1,g1,b1...etc]


and puts these pixel values on the strip using
Code: Select all | TOGGLE FULL SIZE
strip.setPixelColor(i, r, g, b)

when the end has arrived, we use strip.show() and start all up again...


pburgess wrote:
silverline wrote:p.s. plus, we don't want a zig zag, because we are making a tree-like- installation.... is it also possible to connect multiple strips on one teensy?

Possible, yes. It'll take a long explanation, but I would basically suggest ditching the existing library code and taking an entirely different approach in this case. Are you comfortable with that as a possibility?

so it would be possible to connect multiple ledstrips (parallel) on more different ports on a teensy / teensy++ using the original SPI adafruit's library?? would be ideal in this situation

u guys think this would still work for 70 x 5m ledstrip of 180 leds???
silverline
 
Posts: 17
Joined: Thu Dec 08, 2011 10:36 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Wed Dec 14, 2011 5:21 pm

silverline wrote:because ldp8806 is lightning fast (or so ive heard), buffering isn't really required...

we wrote a bit code derived from ledstream that parses all data from serial port - that is the bytes from Serial.read, which are
Code: Select all | TOGGLE FULL SIZE
 [A,d,a, hi,lo, checksum, r0, g0, b0, r1,g1,b1...etc]


and puts these pixel values on the strip using
Code: Select all | TOGGLE FULL SIZE
strip.setPixelColor(i, r, g, b)

when the end has arrived, we use strip.show() and start all up again...


That will certainly work...and having something working trumps efficiency, so I wouldn't disrespect you for getting it going that way.

Here's the thing though: as you mentioned, buffering isn't necessary. The LPD8806 strips act like a big shift register...for all intents and purposes, they are external RAM, and then on top of that, they have their own special way of indicating the end-of-data (the latch), so really the header/checksum thing, and setting pixel colors and so forth...it's all a lot of wasted RAM and processor cycles on the Arduino (the entire existence of the LEDstream code is mostly due to the strangeness of the WS2801 LEDs, which have a latch based on time rather than data). So if speed is the bottom line, really all you need in your Arduino code (after initializing the SPI hardware and such) is a few lines something like this:

Code: Select all | TOGGLE FULL SIZE
int c;
for(;;) {
  while((c = Serial.read()) < 0); // Wait for next serial byte in
  SPI.transfer(c); // Copy to SPI out
}


And that's it. All of the "heavy lifting" can then be moved over to the Processing side: converting colors to 7 bit (and setting the high bit) and issuing the latch sequence (a number of zeros) to indicate the end-of-data. Zero need for the 'Ada' header in this case. You could even eke out a bit more speed by having it fetch the next serial byte while waiting for the SPI transfer to complete, but I digress.

silverline wrote:so it would be possible to connect multiple ledstrips (parallel) on more different ports on a teensy / teensy++ using the original SPI adafruit's library?? would be ideal in this situation


Yes and no. Possible to handle multiple strips in parallel, but not using the original library (not uber efficiently, anyway). This will require different code. Consider the explanation above as just Phase 1, and if that works reasonably well, then Phase 2 gets a bit more complex.

There's yet another crazy-fast approach that uses no Arduino at all, just a serial adapter like the FTDI Friend. I already have some code for this, and mostly just haven't had the time to write up a tutorial...it's a fairly complex thing to explain.

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by wetterberg on Wed Dec 14, 2011 5:54 pm

>There's yet another crazy-fast approach that uses no Arduino at all, just a serial adapter like the FTDI Friend. I already have some code for this, and mostly just haven't had the time to write up a tutorial...it's a fairly complex thing to explain.

wow. I hadn't thought about that - couldn't one get away with a really well-commented processing patch? I mean with the FTDI-friend aren't you down to "connect these two pins to these two pins, power strip here and here. Now on to the code!"?
wetterberg
 
Posts: 10
Joined: Fri Nov 25, 2011 12:21 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by wetterberg on Wed Dec 14, 2011 5:59 pm

by the way, I'm watching this thread like a hawk - being able to do pixel screens like this has been a dream for a long time, and this is the first time I've seen it this close to being an easy and elegant solution.

My only wish is that my code chops were... welll, existing, so I might help out. Sigh, better get back to some Processing tutorials.
wetterberg
 
Posts: 10
Joined: Fri Nov 25, 2011 12:21 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Wed Dec 14, 2011 6:31 pm

wetterberg: keep an eye on the blog...something cool is incoming. :D

Item in question will be WS2801-based...I really like those because you can vary the pixel pitch/spacing...but a lot of the ideas there could carry over to LPD8806 installations.

Aaaaand...lemme try to get a handle on my schedule for the rest of the week. If I can get the code and notes just a little cleaned up, then yes, it's something you might be able to fudge your way through until I have a proper tutorial.

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Wed Dec 14, 2011 7:41 pm

silverline: okay, here's a more complete example of the idea I was getting at...

First, a very simple Arduino sketch. Just takes bytes from Serial in and forwards them to SPI out:

Code: Select all | TOGGLE FULL SIZE
// LPD8806 streamer: redirects bytes from serial in to SPI out.
// No processing is performed on the data, strictly forwarding.

#include <SPI.h>

void setup() {
  int c;
  Serial.begin(115200); // 32u4 ignores BPS, runs full speed
  // SPI is run at 2 MHz.  LPD8806 can run much faster,
  // but unshielded wiring is susceptible to interference.
  // Feel free to experiment with other divider ratios.
  SPI.begin();
  SPI.setClockDivider(SPI_CLOCK_DIV8);
  SPDR = 0; // Dummy byte out to "prime" the SPI status register

  for(;;) { // loop() is avoided for max throughput
    while((c = Serial.read()) < 0); // Wait for next serial byte in
    while(!(SPSR & (1<<SPIF)));     // Wait for prior SPI byte out
    SPDR = c;                       // Issue new SPI byte out
  }
}

void loop() { } // Do nothing


Second, a Processing sketch than generates a color[] array, then converts this into LPD8806-ready format (7-bit color, latch at end-of-data) and issues it to the Arduino:

Code: Select all | TOGGLE FULL SIZE
import processing.serial.*;

static final int
        nLEDs     = 160,
        latchLen  = (nLEDs + 63) / 64;
color[] led       = new color[nLEDs];
byte[]  serialBuf = new byte[3 * (nLEDs + latchLen)],
        gamma     = new byte[256];
Serial  port;

void setup() {
  // The "gamma" table actually does three things: applies gamma
  // correction to input colors to produce a more perceptually linear
  // output range, reduces 8-bit inputs to 7-bit outputs, and sets the
  // high bit as required by the LPD8806 LED data protocol.
  for(int i=0; i<256; i++) {
    gamma[i] = (byte)(0x80 | (int)(pow(((float)i / 255.0), 2.5) * 127.0 + 0.5));
  }

  // Assumes Arduino is first/only serial device.  Change to suit:
  port = new Serial(this, Serial.list()[0], 115200);
  colorMode(HSB, 100); // Easy rainbows
}

void draw() {
  int i,j;

  // Fill the led color array with something interesting:
  for(i=0; i<led.length; i++) {
    led[i] = color((frameCount + i) % 100, 100, 100);
  }
  // Convert data from led color array to LPD8806-ready format:
  for(i=j=0; i<led.length; i++) {
    serialBuf[j++] = gamma[(led[i] >>  8) & 0xff];
    serialBuf[j++] = gamma[(led[i] >> 16) & 0xff];
    serialBuf[j++] = gamma[ led[i]        & 0xff];
  }
  // Arrays are zero-initialized in Processing/Java,
  // so no need to set up the latch data; it's already there.
  port.write(serialBuf); // Issue LPD8806 data to Arduino

  // You *might* need to comment out the above line and use
  // the following code instead. Long writes fail for some
  // unknown reason. RXTX lib? Processing? Java? OS? Hardware?
//  for(i=0; i<serialBuf.length; i=j) {
//    j = i + 255;
//    if(j > serialBuf.length) j = serialBuf.length;
//    port.write(Arrays.copyOfRange(serialBuf,i,j));
//  }
}


The 'color' type in Processing is the same as individual pixels in the PImage type, so it's fairly simple to get this working with images or with the Processing window contents.

Play around with that, see if it makes sense, and we can then see how fast we can push this thing.
Last edited by pburgess on Thu Dec 15, 2011 3:00 pm, edited 1 time in total.

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by silverline on Thu Dec 15, 2011 6:21 am

pburgess wrote:[ready-made sketches]


omg. thank u!

Image

well update u our progress today
silverline
 
Posts: 17
Joined: Thu Dec 08, 2011 10:36 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by pburgess on Thu Dec 15, 2011 3:06 pm

Cool, keep us posted!

Note that I've edited the Processing code above to address a couple of problems: first, I'd forgotten that the LPD8806 pixels want data in GRB order, not RGB. Second, I'd found that large serial writes fail (the definition of "large" varying from system to system), so there's optional code there (commented out by default) to issue data in smaller chunks. So if you increase nLEDs and suddenly find the strip not updating, try using the alternate piece of code.

pburgess
 
Posts: 4074
Joined: Sun Oct 26, 2008 2:29 am

Re: creating led-matrix from multiple rows of led strips LPD8806

by GrAnD on Sun Dec 18, 2011 9:28 pm

silverline wrote:
we wrote a bit code derived from ledstream that parses all data from serial port - that is the bytes from Serial.read, which are
Code: Select all | TOGGLE FULL SIZE
 [A,d,a, hi,lo, checksum, r0, g0, b0, r1,g1,b1...etc]


and puts these pixel values on the strip using
Code: Select all | TOGGLE FULL SIZE
strip.setPixelColor(i, r, g, b)

when the end has arrived, we use strip.show() and start all up again...


Any chance you can post your code to take a loook at? I'm trying to run lpd8806 strip connected to arduino from boblightd which runs under Linux and that's exactly what I'm looking for. Thanks in advance
GrAnD
 
Posts: 6
Joined: Sun Dec 18, 2011 8:28 pm

Re: creating led-matrix from multiple rows of led strips LPD8806

by silverline on Mon Dec 19, 2011 6:42 am

@pburgess:
our visual installation isn't a matrix, but will be more tree-like. It'll contain about 70 ledstrips of 150led each..
Our problem is - should we stick to one teensy which adresses ALL leds (and thus rewire the CLK/DATA wires) or should we have one teensy per tree-branch?

edit:
we are now sending 10500 pixels to three teensy's over an usb hub. Framerate in processing is around 11fps... :s
10500 to one teensy had a framerate of 8fps
10500 to two teensies had a framerate 6fps, but 10 when using a powered usb hub (?)
10500 to three teensies had a framerate of 11fps (??)

so we think we might use max 8 teensies connected to one pc for our installation, am i correct? there wouldn't be an maximum for the number of teensies connected to a pc right?

edit 2:
we tried different clockdividers while testing with two teensy2.0's on an powerd usb hub, each getting 5250 pixels and found out that
Code: Select all | TOGGLE FULL SIZE
SPI.setClockDivider(SPI_CLOCK_DIV2);

is the fastest (with 8,6 fps) :s

SPI_CLOCK_DIV8 took 6,5fps
SPI_CLOCK_DIV16 took 3,5fps
SPI_CLOCK_DIV64 took 2fps




GrAnD wrote:Any chance you can post your code to take a loook at? I'm trying to run lpd8806 strip connected to arduino from boblightd which runs under Linux and that's exactly what I'm looking for. Thanks in advance

there is any chance, yes.
connect clock and data to PIN1 and PIN2
Code: Select all | TOGGLE FULL SIZE

#include "LPD8806.h"
#include "SPI.h"

// LED pin for Teensy:
#define LED_DDR  DDRD
#define LED_PORT PORTD
#define LED_PIN  _BV(PORTD6)

// set number of total pixels here
#define NUMPIXELS 180
// mode names
#define WAITING 0
#define PROCESSING 1

// magic word
static const uint8_t magic[] = {
  'A','d','a'};

LPD8806 strip = LPD8806(NUMPIXELS);

void setup()
{
  uint8_t mode = WAITING;
  int16_t pixelIndex = 0;
  byte sArray[3];

  //  LED_DDR  |=  LED_PIN; // Enable output for LED
  //  LED_PORT &= ~LED_PIN; // LED off

  strip.begin();

  // loop() is avoided as even that small bit of function overhead
  // has a measurable impact on this code's overall throughput.
  for(;;) {
    if(Serial.available() > 0) { 
      sArray[0] = Serial.read();
      sArray[1] = Serial.read();
      sArray[2] = Serial.read();
      if(mode == WAITING) {
        if(sArray[0] == 'A' && sArray[1] == 'd' && sArray[2] == 'a') {
          sArray[0] = Serial.read();
          sArray[1] = Serial.read();
          sArray[2] = Serial.read();
          if(sArray[2] == (sArray[0] ^ sArray[1] ^ 0x55)) {
            mode = PROCESSING;
            pixelIndex = 0;
          }
          else {
            Serial.print("Ada\n");
            mode = WAITING;
          }
        }
        else {
          Serial.print("Ada\n");
          mode = WAITING;
        }         
      }
      else {
        strip.setPixelColor(pixelIndex++,sArray[0],sArray[1],sArray[2]);
        if(pixelIndex >= NUMPIXELS) {
          strip.show();
          mode = WAITING;
        }
      }
    }
    else {
      Serial.print("Ada\n");
    }

  } // end for(;;)
}

void loop()
{
  // Not used.
}


this code was based on LEDstream.
note that this is NOT optimized at all, but it worked for us (maybe because we use a teensy, which has a faster USB than the arduino)
silverline
 
Posts: 17
Joined: Thu Dec 08, 2011 10:36 pm

Please be positive and constructive with your questions and comments.