Here’s a fun little project I’ve set up for the holidays. (Work in progress!)
I’ve been dabbling with the wonderful world of individally-addressable LEDs for a while and use them in all sorts of setups, but what got me really interested are the 5V-powered “fairy pixel” flexible LED strips, handy because they’re easily powered from a power bank. The first version I ordered came with a controller which, although it worked fairly well, did not allow for advanced configuration and some more complicated setups.
It took me a whole week of tinkering with it before I re-used the LED strip on a ESP8266 controller flashed with WLED, mounted it on two cable ducts and turned it into a 160 cm x 92 cm portable LED matrix with a resolution of 20 x 10 pixels.
The layout
Since my balcony has a transparent (albeit opaque) glass balustrade, I figured I would simply stick a LED matrix behind it and turn it into a large LED screen.
First trials with the portable LED matrix looked good enough, but I wanted more resolution.
My balustrade consists of two 129 x 89 cm glass panes, so after a bit of calculation I opted for two 20-metre LED fairy pixel strings of 400 LEDs each (spaced 50 mm apart), one for each pane, in a vertical serpentine pattern to avoid any need to rearrange, cut or solder the strip. In one piece, the whole setup is also removable post-holiday season, as I’m not certain how well the LED strip would survive the scorching heat and exposure to strong sunlight.
For my glass pane height, I could have easily fit 18 LEDs per column which would give me the effective resolution of 22 x 18 pixels plus a few to spare. Unfortunately, this would also mean that if the columns were spaced according to their spacing of 50 mm, which would drop to about 40 mm since the LEDs have to be rotated to face the glass pane, 22 columns x 40 mm would actually cover a 90 x 83 cm area on a 129 x 89 pane.
To overcome this, I opted to “sacrifice” the 18th led of each column, which gave me flexibility in spreading out the columns without cutting the strip. By arranging the columns 55 mm apart, the LED matrix would cover a rectangle of 120 x 83 cm on a 129 x 89 pane, which was much better.
In order to easily space the LEDs apart, I’ve designed and 3D-printed holders and spacers.
Top and bottom holders serve multiple purposes:
1. they keep the LEDs facing outwards towards the glass, because I need them to shine through the glass to be visible through the opaque pane. They shine quite brightly enough to be clearly visible from the inside.
2. they keep the led string taut and secure on the glass without the need to glue or fasten them in some other way.
3. they keep my LED string removable. At some point I can just remove the LED string and leave the holders in place all year round.
The height and the width of the holders was calculated so that they hold the strip firmly in place, also providing sufficient surface area for adhesion to the surface. I used transparent double-sided “nano tape” to stick them to the glass.
Spacers are a bit smaller than the holders and only serve to keep LED columns properly spaced and facing the glass. They can be stuck to the glass if necessary, but work quite well without any tape.
3D print models for the case and holders (Fusion360 and 3mf) can be found here.
The controller and wiring
WLED will be handling 800 LEDs at a time, so I’ve opted for an ESP32 controller, with each 400-LED string on a separate GPIO. To make the thing easily replaceable, the controller is on a proto-board in a 3D printed case with a DC-jack for power and two 3,5 mm stereo jacks to make the LED strips easily detachable.
The thin fairy-pixel wire has huge voltage drops due to its length of 20 metres, so although a strip can be connected to the controller at one end, it cannot be powered directly from the ESP32 pin, meaning you have to power them directly from the PSU on both ends. Even powered that way, the voltage drop is visible at higher brightness levels and LED colours that require all three LEDS (for example white), so make sure you limit the current in WLED to make this less noticeable. The whole thing is powered by a 5V, 10A PSU. I’ve decided to put the controller in the middle, between both strips, so as to avoid the need to run a separate data line. This way I only need a two-wire power line covering the entire span of the LEDS.
WLED configuration
Ironically, this part of the whole affair took a lot of time to get right, so I suggest you learn from my mistakes. After a few hours trying to get various AI models to work out how to configure the matrix panel with ‘skipped’ LEDs and failing miserably, the WLED jsonmap generator proved very helpful, although not perfect for my purposes. Good old spreadsheet with its sequential cell autocompletion to the rescue!
As I mentioned, each strip is on a different GPIO, so under LED preferences I have both strips configured identically on different pins. This means that leds 0-399 will be the right panel, while leds 400-799 will be the left panel.
Keeping in mind that I have ‘skipped’ pixels and extra LEDs that won’t ever be used, I’ve created this spreadsheet to make things easier.
As you can see, it’s a serpentine pattern, starting in the middle – three skipped LED’s, followed by a downward-facing spacer LED, followed by 17 pixels and ending with an upward-facing spacer LED. The pattern continues for a total of 22 columns to the right. The left one is mirrored. LEDs don’t start at zero because there are extras which I’ve left at the beginning and end to avoid having to extend the cable.
Also note that I have numbered the ‘sacrificed’ upward and downward facing LEDs which I’ll be able to use independently of the 2D matrix as two 1D strips (top and bottom).
I’ve done this in a spreadsheet so I can just copy the array layout LED order into a ledmap.json
file, which I then uploaded to http://wled/edit.
More on the WLED ledmap.json
According to the WLED jsonmap generator, the correct format for a ledmap.json
file for a 2D matrix is as follows:
{"n":"my_matrix","width":16,"height":5,"map":[
0,9,10,19,20,29,30,39,40,49,50,59,60,69,70,79,
1,8,11,18,21,28,31,38,41,48,51,58,61,68,71,78,
2,7,12,17,22,27,32,37,42,47,52,57,62,67,72,77,
3,6,13,16,23,26,33,36,43,46,53,56,63,66,73,76,
4,5,14,15,24,25,34,35,44,45,54,55,64,65,74,75
]}
The array has to be defined in size and the LEDs, regardless of the order, have to be defined as an array. If any LEDs aren’t supposed to be a part of the matrix, you just skip them and leave them for later. I’ve copied the matrix part of my spreadsheet, comma-separated, without any added spaces because WLED doesn’t tolerate them. If you don’t define a matrix in ledmap.json
and instead just shuffle the order of LEDs and try to define a matrix through the 2D settings, the 2D matrix will not work!
The great part about ledmap.json
is that, after you’ve defined your main matrix, you can reshuffle and renumber any leftover LEDs to use them as a 1D strip. So I’ve added my top and bottom strips, omitting only the unneeded LEDs. My final ledmap.json
is as follows – please note that comments are unsupported, I’ve included them in the code block to better explain what is what.
{"n":"2DMatrix","width":44,"height":17,"map":[
//first row of my 44x17 matrix 781,779,745,743,709,707,673,671,637,635,601,599,565,563,529,527,493,491,457,455,421,419,21,23,57,59,93,95,129,131,165,167,201,203,237,239,273,275,309,311,345,347,381,383,
//
// rows omitted
//
// final row of the matrix
797,763,761,727,725,691,689,655,653,619,617,583,581,547,545,511,509,475,473,439,437,403,5,39,41,75,77,111,113,147,149,183,185,219,221,255,257,291,293,327,329,363,365,399,
// 1D strip begins here - upwards-facing LEDs - top row
780,744,708,672,636,600,564,528,492,456,420,22,58,94,130,166,202,238,274,310,346,382,
// downwards-facing LEDs - bottom row
798,762,726,690,654,618,582,546,510,474,438,402,4,40,76,112,148,184,220,256,292,328,364]
}
WLED segment configuration
In order to be able to control the 2D matrix and the top/bottom strips independently, the segments in WLED need to be individually configured.
First I defined a 44×17 2D panel in Config > 2D configuration. This created a corresponding 2D segment using the first 748 LEDs, which are zero-indexed, so LEDs 0-747.
After that, I added two separate 1D segments, “Top” starts at LED 748 and ends at 770, “Bottom” starts at 770 and ends at 793 (yes, this strip has one LED more than the top one).
I’ll update this post with more information. For now, here’s a demo.
Bill of materials
- ESP32 mini prototype board (under 4 €)
- “20M400LEDs WS2812B LED String Dreamcolor Christmas Lights” AKA “fairy pixel LED strip” without a controller (around 18 € per 20 m at the time of writing, cheaper 3-wire version is sufficient for a serpentine layout)
- 5V 10A PSU + box if needed
- 3.5mm 3-pole (stereo) plug + socket (2 pcs)
- 12V Male + Female Connectors 2.1*5.5mm (3 pairs)
- Nano tape (dual sided transparent adhesive tape)
- cables and wires as needed