About Matrices - AlbertGBarber/PixelSpork GitHub Wiki
Overview:
On this page I'll discuss using LED matrices with Pixel Spork and give an example of a segment set for an 8x8 matrix. Note that you should read Segment Basics before reading this page, as I won't be going over Segment Sections, Segments, or Segment Sets in detail.
Jump To:
Pixel Spork and Matrices:
Firstly, while Pixel Spork is perfectly capable of creating matrices using segment sets, they are not the focus of the library. Pixel Spork is generalized to map more varied LED shapes and arrangements, and so matrix support is more limited than with other, matrix-based libraries.
Specifically, this means that:
-
Representing both orientations of a matrix requires two separate segment sets (see the example below). Due to the general nature of segment sets, it's not possible to easily reorient or transpose a matrix using a single segment set.
-
There is no native support for drawing text. Text is a quite complicated to implement, and while I'd like to add it to Pixel Spork, I currently have no timeline/plans to do so. Note that you can create and scroll your own text messages manually using Shift Patterns and the Pattern Shifter effect, but this is a bit tedious because you need to create all the letters by hand.
-
Like with text, there is no support for drawing 2D shapes in Pixel Spork (circles, squares, etc).
-
There is no support for drawing more complex objects or animated gifs, aside from using Shift Patterns as mentioned above. I do not have plans to add this kind of feature.
-
Currently, Pixel Spork doesn't have any true "matrix" effects. Although effects can be drawn in 2D, all the current effects do this by repeating themselves along segment lines or segments. For most effects, this is fine, and is what you would expect the effect to look like in 2D, but they aren't truly using the matrix as a 2D coordinate plane. For an example of what I mean by a "matrix" effect, checkout the WLED "Black Hole" effect sample (scroll down to see it). Eventually I want to implement some of these effects, and have included some helper drawing functions here, which should make porting effects from elsewhere a bit easier.
Overall, while Pixel Spork's matrix support may be limited, keep in mind that the library is a companion to FastLED, and doesn't prevent you from using native FastLED code or other libraries alongside it. So you are free to use a library with better matrix support, such as FastLED NeoMatrix to fill in the gaps in Pixel Spork. (Note that I have not used/tested FastLED NeoMatrix, and linked it mainly as an example, and because it looks well featured. You may need to do your own Googling to find something that fits your specific needs).
8x8 Matrix Segment Set Example:
For this example, we'll be taking a common 8x8 WS2812b matrix, as pictured to to the left, and re-creating it virtually in Pixel Spork. As shown in the image, we'll assume that the board's LEDs are physically connected in a serpentine line, zigzagging down the board.
To create the matrix, we'll use two separate segment sets, one for a horizontal matrix orientation, and one for vertical. Currently, there is no way to "rotate" a segment set in Pixel Spork, which is why two segment sets are required. Note that, in your own code, you may only need/want to use one orientation, so you can skip the other. Likewise, you can reverse each matrix's direction by adjusting it's segment directions (see Segments for more info).
Overall, the examples below should be easy enough to extend to any sized matrix, and will hopefully serve as a good starting point.
Horizontal Orientation:
In this orientation, the matrix direction follows the same direction as the continuous "rows" of LEDs. To create the matrix, we'll split each row into a segment, and combine them all into a segment set. This is by far the easiest way to represent the matrix as we can use one continuous section for each segment, using the starting LED of each row and the row's length.
In code:
//Create a segment for each row using continuous sections.
//Note that because the LEDs connected in serpentine, the direction of every other
//segment alternates (true, false, true, etc...)
//This means that virtually, the segments are all pointing in the same direction.
const PROGMEM segmentSecCont rowSegment0_arr[] = { {0, 7} }; //Segment 0, LEDs 0 - 7
SegmentPS rowSegment0 = {rowSegment0_arr, SIZE(rowSegment0_arr), true};
const PROGMEM segmentSecCont rowSegment1_arr[] = { {8, 7} }; //Segment 1, LEDs 8 - 15
SegmentPS rowSegment1 = {rowSegment1_arr, SIZE(rowSegment1_arr), false};
const PROGMEM segmentSecCont rowSegment2_arr[] = { {16, 7} }; //Segment 2, LEDs 16 - 23
SegmentPS rowSegment2 = {rowSegment2_arr, SIZE(rowSegment2_arr), true};
const PROGMEM segmentSecCont rowSegment3_arr[] = { {24, 7} }; //Segment 3, LEDs 24 - 31
SegmentPS rowSegment3 = {rowSegment3_arr, SIZE(rowSegment3_arr), false};
const PROGMEM segmentSecCont rowSegment4_arr[] = { {32, 7} }; //Segment 4, LEDs 32 - 39
SegmentPS rowSegment4 = {rowSegment4_arr, SIZE(rowSegment4_arr), true};
const PROGMEM segmentSecCont rowSegment5_arr[] = { {40, 7} }; //Segment 5, LEDs 40 - 47
SegmentPS rowSegment5 = {rowSegment5_arr, SIZE(rowSegment5_arr), false};
const PROGMEM segmentSecCont rowSegment6_arr[] = { {48, 7} }; //Segment 6, LEDs 48 - 55
SegmentPS rowSegment6= {rowSegment6_arr, SIZE(rowSegment6_arr), true};
const PROGMEM segmentSecCont rowSegment7_arr[] = { {56, 7} }; //Segment 5, LEDs 56 - 63
SegmentPS rowSegment7 = {rowSegment7_arr, SIZE(rowSegment7_arr), false};
//The "rows" are grouped together into a segment set to form the matrix
SegmentPS *rows_arr[] = { &rowSegment0, &rowSegment1, &rowSegment2, &rowSegment3,
&rowSegment4, &rowSegment5, &rowSegment6, &rowSegment7 };
SegmentSetPS horzMatrix( leds, SIZE(leds), rows_arr, SIZE(rows_arr) );
The code above creates the horizontal representation of the matrix, storing it as the horzMatrix
segment set. Note that because the LEDs are connected in serpentine, the direction of every other segment needs to alternate (true, false, true, etc...), so that virtually, the segments are all pointing in the same direction, left to right. If your matrix doesn't zigzag, simply change any "false" directions to "true". Remember that you can also change the directions of the segments during runtime using the Segment Set Direction Functions.
Finally, note that, as shown in the image to the right, in this orientation the segment lines will point vertically down the matrix. For most effects, this means they will be drawn moving from left to right across the matrix.
Vertical Orientation:
This orientation is the same as horizontal, but rotated clockwise by 90 degrees, so that the matrix's "rows" are now vertical columns. To create this matrix, we'll create a segment for each column using a single mixed section for each. Recall that for mixed sections, we list all the pixels in the section in order. This will make defining the matrix quite a bit more tedious as we essentially have to re-write all the pixel addresses.
In Code:
//Create a segment for each column using mixed sections.
//Segment 0
const PROGMEM uint16_t col0_sec_arr = { 0, 8, 16, 24, 32, 40, 48, 56 }; //The array of LED addresses for column 0
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment0_arr[] = { {col0_sec_arr , SIZE(col0_sec_arr)} };
SegmentPS colSegment0 = {colSegment0_arr, SIZE(colSegment0_arr), true};
//Segment 1
const PROGMEM uint16_t col1_sec_arr = { 1, 9, 17, 25, 33, 41, 49, 57 }; //The array of LED addresses for column 1
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment1_arr[] = { {col1_sec_arr , SIZE(col1_sec_arr)} };
SegmentPS colSegment1 = {colSegment1_arr, SIZE(colSegment1_arr), true};
//Segment 2
const PROGMEM uint16_t col2_sec_arr = { 2, 10, 18, 26, 34, 42, 50, 58 }; //The array of LED addresses for column 2
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment2_arr[] = { {col2_sec_arr , SIZE(col2_sec_arr)} };
SegmentPS colSegment2 = {colSegment2_arr, SIZE(colSegment2_arr), true};
//Segment 3
const PROGMEM uint16_t col3_sec_arr = { 3, 11, 19, 27, 35, 43, 51, 59 }; //The array of LED addresses for column 3
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment3_arr[] = { {col3_sec_arr , SIZE(col3_sec_arr)} };
SegmentPS colSegment3 = {colSegment3_arr, SIZE(colSegment3_arr), true};
//Segment 4
const PROGMEM uint16_t col4_sec_arr = { 4, 12, 20, 28, 36, 44, 52, 60 }; //The array of LED addresses for column 4
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment4_arr[] = { {col4_sec_arr , SIZE(col4_sec_arr)} };
SegmentPS colSegment4 = {colSegment4_arr, SIZE(colSegment4_arr), true};
//Segment 5
const PROGMEM uint16_t col5_sec_arr = { 5, 13, 21, 29, 37, 45, 53, 61 }; //The array of LED addresses for column 5
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment5_arr[] = { {col5_sec_arr , SIZE(col5_sec_arr)} };
SegmentPS colSegment5 = {colSegment5_arr, SIZE(colSegment5_arr), true};
//Segment 6
const PROGMEM uint16_t col6_sec_arr = { 6, 14, 22, 30, 38, 46, 52, 62 }; //The array of LED addresses for column 6
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment6_arr[] = { {col6_sec_arr , SIZE(col6_sec_arr)} };
SegmentPS colSegment6 = {colSegment6_arr, SIZE(colSegment6_arr), true};
//Segment 7
const PROGMEM uint16_t col7_sec_arr = { 7, 15, 23, 31, 39, 47, 53, 63 }; //The array of LED addresses for column 7
//Create the segment using a single mixed section formed from the address array above
const PROGMEM segmentSecMix colSegment7_arr[] = { {col7_sec_arr , SIZE(col7_sec_arr)} };
SegmentPS colSegment7 = {colSegment7_arr, SIZE(colSegment7_arr), true};
//The columns are grouped together into a segment set to form the matrix
SegmentPS *cols_arr[] = { &colSegment0, &colSegment1, &colSegment2, &colSegment3,
&colSegment4, &colSegment5, &colSegment6, &colSegment7 };
SegmentSetPS vertMatrix( leds, SIZE(leds), cols_arr, SIZE(cols_arr) );
The code above creates the vertically oriented matrix, storing is as the vertMatrix
segment set. Note that unlike the horizontal matrix, the segment directions are all the same. This is because with mixed sections we are setting the order of the LEDs manually, so we can have the all point in the same direction. In this case I have arranged them from top to bottom, but like with the horizontal matrix, you can change the segment directions during runtime using the Segment Set Direction Functions.
Although defining this segment set is a bit tedious, remember that you only have to do it once, and can re-use it in any of your programs. In fact, if you're using an 8x8, you can just copy the code above!
Finally, as you may have already guessed, and as shown in the image to the right, in this orientation the segment lines will point horizontally across the matrix. For most effects, this means they will be drawn moving from top to bottom down the matrix.
Bonus - Vertical Matrix Using "Single" Sections:
Pixel Spork allows you to treat a line of pixels as a single LED by marking the line section as "single". This is explained more on the Advanced Segment Usage page, but in short, we make a section "single" buy setting a flag when we make the section. When drawing, Pixel Spork copies the color of the section's first LED into all the others in the section.
By using single sections, we can "cheat" in creating the vertical matrix above by using a set of single sections to represent the segment lines. To do this, we'll re-use the segment sections from the horizontal matrix, but set them to be single. We'll place the sections in a single segment as the left most column of the matrix. Note that because our matrix is serpentine, we'll have to use some negative lengths to define the reversed sections, you can read more about that here.
In Code:
//Create an array of "single" sections, with starting LEDs down the left most column
//Note the additional "true" flag in each section, this marks the section as "single"
//Also because the matrix is serpentine we need to use negative lengths for the reversed direction rows
//so that the first LED in each section is in the left most matrix column
const PROGMEM segmentSecCont colSingleSec[] = { {0, 7, true}, {15, -7, true}, {16, 7, true}, {31, -7, true},
{32, 7, true}, {47, -7, true}, {48, 7, true}, {63, -7, true} };
//Create the single segment using the array of single sections
SegmentPS colSingleSegment = { colSingleSec, SIZE(colSingleSec), true };
//Create the segment set
SegmentPS *singleColSeg_arr[] = { &colSingleSegment };
SegmentSetPS singleVertMatrix( leds, SIZE(leds), singleColSeg_arr, SIZE(singleColSeg_arr) );
The code above creates the segment set, singleVertMatrix
, which contains our lone segment of "single" sections. As far as effects are concerned, the segment set consists only of the left most column of the matrix, having 8 LEDs: { 0, 15, 16, 31, 32, 47, 38, 63 }. When the effect is drawn, Pixel Spork's show()
function will copy the colors of the 8 LEDs down each row, so that each row is a single color.
The pros and cons of defining the matrix this way are as follows:
Pros:
-
Defining the matrix this way requires less memory that the previous vertical definition.
-
Because the matrix is now a single line, some effects may run a bit faster.
-
Many 2D effects work by extending themselves along segment lines, using a single color for each line. For these effect's, their output will be the same as with a fully 2D matrix definition. This applies to most effects tagged with "Seg Line".
Cons:
-
Because you lose the 2D aspect of the matrix, any effects that vary their colors along segment lines, or draw on whole segments, will not work properly.
-
Most Color Modes vary colors along segment lines, or look best with multiple segments, so they will not work well with the single segment in the definition.
Overall, this example is probably more illustrative than useful as you're probably better off taking the memory/performance hit than working around the restrictions of a "single" line matrix. However, "single" sections are a useful thing to know about, particularly when working with more "wacky" shapes.