Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 11 of 11

Thread: Best approach for non-rectangular LED array

  1. #1

    Best approach for non-rectangular LED array

    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

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,320
    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.

  3. #3
    Senior Member
    Join Date
    Oct 2013
    Posts
    116
    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:
    
    --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-30-31
    --32-33-34-35-36-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!
    Last edited by tetsuo; 12-20-2013 at 12:24 AM.

  4. #4
    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!

    Click image for larger version. 

Name:	Color Extraction LED layout.jpg 
Views:	228 
Size:	172.8 KB 
ID:	1238

  5. #5
    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

    Click image for larger version. 

Name:	Color Extraction from Seed Image.jpg 
Views:	143 
Size:	274.4 KB 
ID:	1239

    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!

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,320
    Quote Originally Posted by corvusandcolumba View Post
    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[i]" (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

  7. #7
    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).

    Click image for larger version. 

Name:	Constructing PImage for Teensy.jpg 
Views:	197 
Size:	117.8 KB 
ID:	1242

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,320
    Quote Originally Posted by corvusandcolumba View Post
    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.

  9. #9
    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?

  10. #10
    Figured it out. Created a PImage whose pixels[] array is filled with the sketch Pixels[], on each draw then feed that to the copy loop.

  11. #11
    Quote Originally Posted by PaulStoffregen View Post

    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[i].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.
    Attached Thumbnails Attached Thumbnails Click image for larger version. 

Name:	imagecopy diagram.jpg 
Views:	101 
Size:	330.4 KB 
ID:	3388   Click image for larger version. 

Name:	imagecopy diagram2.jpg 
Views:	92 
Size:	347.5 KB 
ID:	3389  


Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •