Best approach for non-rectangular LED array

Status
Not open for further replies.
I am working with an artist on a large interactive LED wall that is shaped like a waving ribbon. Hard to describe, but imagine a long strip where the top and bottom surfaces are like sine waves.

WS2811 strips are arranged vertically along the length of the ribbon, in approximately 87 columns on 4" centers. The sine waves are not "in sync", so the result is non-uniform heights (or, between 18 and 35 LEDs, depending on where in the strip you are).

Attempting to use an Arduino Mega at first, the refresh rate is far too slow to fill out the entire array, so I've moved on to Teensy.

My question is that considering how OCTO is designed for roughly rectangular displays, what is my best approach to achieving a coherent graphic display, for example, using movie2serial, with the ribbon shape effectively cropping the video?

If think of the display as a really tall rectangle on its side, with height = 80 and width = 35, then if I use a single Teensy, with 10 columns zig-zagging left to right top to bottom, the image is lost because the strips are not uniform.

Unless there is a way to define the pixel locations outside of a rectangular configuration, is my best bet to just get enough Teensys to run every single strip independently (87 strips)? This isn't out of the question, as I don't care about the cost, but seems like 8 or 9 USB connections may be problematic.

Any thoughts for irregular-shaped LED arrays?

-Will
 
If you're using movie2serial, probably the simplest way is to replace the copy() function with your own code that copies the original image pixels to the LED pixel in whatever way you need.

Here's the relevant code:

Code:
    // copy a portion of the movie's image to the LED image
    int xoffset = percentage(m.width, ledArea[i].x);
    int yoffset = percentage(m.height, ledArea[i].y);
    int xwidth =  percentage(m.width, ledArea[i].width);
    int yheight = percentage(m.height, ledArea[i].height);
    ledImage[i].copy(m, xoffset, yoffset, xwidth, yheight,
                     0, 0, ledImage[i].width, ledImage[i].height);

Those first 4 lines use the numbers the VideoDisplay was programmed with the figure out which portion of the entire video image you wanted sent to this particular Teensy3. Of course, if you configured VideoDisplay with 0,0,100,100, then you're requesting the entire video frame. But you can use 0,0,50,50 to request only the upper left quadrant, or 0,0,25,25 to request the upper left 1/16th of the video image, which you'd do it you had 16 Teensy3 driving an incredibly large and awesome number of pixels.

Processing image copy() function then copies just the chunk of the video you wanted for this Teensy3 and scales that chunk to whatever number of LEDs you connected.

So rather than use these 5 lines of code, you could write code to copy whatever portions of the original video frame you want. If you have 87 columns of LEDs, a lengthy but simple approach might involve simply placing 87 copy() calls into the code. If it's only intended to work on your specific project, and if your video source is always the same resolution, you could just delete all that math and put the actual numbers you need right into the code.

For example...

Code:
    ledImage[i].copy(m, 10, 20, 400, 15, 0, 0, 20, 1);

This will copy a 400x15 chunk of the original video starting at x=10,y=20 into the first 20 LED pixels. It's extremely simple. Refer to that Processing copy() function documentation for the meaning of each number.

You can compose code in movie2serial to copy ANY section of the original video to any LEDs you want. It's completely up to you. All that's needed is a little bit of programming.
 
I would think of the screen just as you have (an 80 x 35) rectangle, but with a "mask" of sorts. Separate your visual coordinate system from actual teensy pixel index either by logically shifting things or with a predefined lookup array. An example of the former is already done in some of Paul's code to handle things like the serpentine pixel layout.

My setup is rectangular, but has a big chunk cut out of one side and hole in the middle. This kills any ability to use the built-in layouts (especially with long strips that double back on themselves a few times). So I ended up building a map


Code:
From your description, I picture something like this (only sideways).

Actual pixel layout and idx:

--00-01-02-03-04-05
-----06-07-08-09-10-11
--------12-13-14-15-16-17
-----18-19-20-21-22-23
--24-25-26-27-28-29

virtual pixel layout as a rectangular display that contains all of the led positions plus "fillter" virtual pixels so that you have a rectangular coordinate system:

--[COLOR="#FF0000"]00-01-02-03-04-05[/COLOR]-06-07
--08-[COLOR="#FF0000"]09-10-11-12-13-14[/COLOR]-15
--16-17-[COLOR="#FF0000"]18-19-20-21-22-23[/COLOR]
--24-[COLOR="#FF0000"]25-26-27-28-29-30[/COLOR]-31
--[COLOR="#FF0000"]32-33-34-35-36[/COLOR]-37-38-39

and the mapping of rectangular pixel idx to actual teensy pixel idx:

[
0=0,
1=1,
..etc..,
6=-1 /* or any other special value to mean 'masked' or 'not really there, man!' */,
7=-1 /* ditto */,
8=-1 /* ditto */,
9=6,
10=7,
..etc..
]


I'm not sure if that made enough sense to be helpful, but good luck in any case! :D
 
Last edited:
Wow, thank you kindly for such fast information! You guys are awesome.

I will be playing with this today, and I think the masking/mapping approach is what I'm looking for. I think you both understood what I'm doing here, but the image below depicts the rough shape of the panel (it's 30 feet wide) and the two approaches I was thinking. The first one, with one or two Teensys and some image masking/mapping option is preferred, though I did just order and overnight 8 Teensys just in case I have to go with option two. Not sure how that many USB cords running around is going to work.

It will be some tedious work to map out the mask, but it only has to be done once!

Color Extraction LED layout.jpg
 
If I understand correctly, the solution proposed by Paul is to "sample" the source image (which is always the same resolution, by the way) by creating multiple vertical narrow copy regions, each corresponding to one LED column in the physical display.


This is actually exactly how we originally envisioned the concept - see image below: my concept approach

Color Extraction from Seed Image.jpg

My question is: for this approach each copy call becomes what is sent to one individual Teensy, right? So, if I have 87 copy calls, one for each column, then wouldn't I actually need an unwieldy 87 Teensys? Or, is there a way to bundle 8 copy calls and send them to each of the 8 outputs on the Teensy (still requiring 10 total for the display)?

I think creating the virtual pixel map tetsuo recommended, while not the most efficient due to processing and pumping out nonexistent pixels, may be easier on the wiring.

Again, thanks for your recommendations. It has helped me get un-stuck. I will post the results following installation!
 
My question is: for this approach each copy call becomes what is sent to one individual Teensy, right?

No. Please look at the movie2serial source code while reading this answer. You really *must* look at the source code to understand this. Reading only this message probably won't make sense.

The copy() happens inside a loop:

Code:
  for (int i=0; i < numPorts; i++) {

The "numPorts" variable is the number of Teensy boards you've connected. The loop runs for each Teensy. The copy command builds the image for each Teensy.

So, if I have 87 copy calls, one for each column, then wouldn't I actually need an unwieldy 87 Teensys? Or, is there a way to bundle 8 copy calls and send them to each of the 8 outputs on the Teensy (still requiring 10 total for the display)?

Sure. Inside the loop, you can compose each image any way you like. If each Teensy needs different code, you might do something like:

Code:
    if (i == 0) {
      // copy calls for first Teensy
    } else if (i == 1) {
      // copy calls for seconds Teensy
    } // ....

There's also no restrictions like having to do a copy for each physical strip. You've got a large Pimage representing the current video frame and you're copying pieces of it into the image sent to each Teensy. It's just pixels. You can copy them, or generate them any way you want. There's no restrictions on which copies target what. You can use as many or as few copies for each ledImage[].

Right after you compose pixels into "ledImage" (notice the array syntax... there is a Pimage for each Teensy3), the code immediately after that does some translations to turn it into OctoWS2811's raw data format, and then it's transmitted.

You can use any number of Teensy3s, and with proper programming, you can put whatever pixels you want into each of them. This project is physically large, but it's under 1000 LEDs, so it's relatively small for Teensy3. Using a small group probably makes the signal wiring easier, but you could run the whole thing with a single Teensy3.

Oh, opps, what I meant to say is you need to go buy 77 more Teensy3s right now! LOL ;)
 
Thank you for the followup message - I will become much more familiar with the source code as I go along.

As it stands, and based on your clarifications on the copy action in creating the LedImage, I will copy() narrow/tall rectangles out of the source with an arrangement matching the physical display as close as it can. Then, I will construct the PImage that gets processed and sent out to the Teensy. My concern with constructing the PImage from a number of non-uniform-height rectangles is that I can't have a non-rectangular PImage! So, if I reconstruct the rectangle arrangement within the destination PImage, pretending my physical LED array was fully rectangular, then when I send it out to the Teensys, the areas of the PImages with interesting content will be what I sampled from the source, and the nonexistent LEDs will be occupied with the filler space from the contstructed PImage.

There will be some tweaking to get everything aligned perfectly. Since my nonexistent LEDs are not actually in the circuit, or the shifting chain, will over-driving the strip be problematic (other than inefficient). For example, the Teensy expects to fill 35x8 = 280 LEDs, but in actuality there are only about 260 (due to the virtual ones, not physically there).

Constructing PImage for Teensy.jpg
 
There will be some tweaking to get everything aligned perfectly.

Yes, indeed.

Since my nonexistent LEDs are not actually in the circuit, or the shifting chain, will over-driving the strip be problematic (other than inefficient).

Definitely not a problem.

In fact, this scenario is documented on the OctoWS2811 page. Scroll down to the "LED Addressing & Different Strip Lengths" section.
 
Awesome! - I have this mostly working. There are a few flicker issues that will probably be solved when I carefully revisit grounding and receive the 74HCT245's I ordered.

Playing with movie2serial now - is there a simple way to make the target of the copy()'s, a Processing sketch that is running live, rather than an AVI file on disk?
 
For example...

Code:
    ledImage[i].copy(m, 10, 20, 400, 15, 0, 0, 20, 1);

This will copy a 400x15 chunk of the original video starting at x=10,y=20 into the first 20 LED pixels. It's extremely simple. Refer to that Processing copy() function documentation for the meaning of each number.

You can compose code in movie2serial to copy ANY section of the original video to any LEDs you want. It's completely up to you. All that's needed is a little bit of programming.

Hello, this thread is a little old but I'm wondering if you can help me with this code for the movie2serial file? I want to know if it's possible to take that same 400x15 chunk of video and flip it in the opposite direction. I tried typing it out like this: ledImage.copy(m, 409, 20, -400, 15, 0, 0, 20, 1); but it didn't work. I edited the image that was attached in this thread and added an extra step to show what I'm trying to do. There are 2 examples. One of the examples shows the image chunk being rotated 180 degrees (either 180 degrees or mirrored will work since I'm working with single line chunks). The other example shows the outputted LEDs with flipped colors but is generally the same idea. I can get away with typing out each pixel individually, but I'm working with 1500 LEDs, so that will take a while.
 

Attachments

  • imagecopy diagram.jpg
    imagecopy diagram.jpg
    330.4 KB · Views: 161
  • imagecopy diagram2.jpg
    imagecopy diagram2.jpg
    347.5 KB · Views: 178
Status
Not open for further replies.
Back
Top