For Christmas this year I decided to use a glass block, a PWM controller and some LED fairy light strands to give my wife a colorful gift.
Step 1: Parts
Here are the parts you will need.
This doesn't need to be anything particularly big, fast nor having lots of pins (you only need 2 data pins for the I2C connections). I used the Adafruit Trinket M0 because it's small, I like this format and I wanted to learn to work with CircuitPython.
16 Channel PWM break-out board
There are many similar kinds of PWM break-out boards, including the one from Adafruit. Even if your controller has a lot of available PWM pins, I'd still avoid trying to power all your strands from there, and opt instead for the break-out board: the LEDs can draw a lot more current than the controller will allow. Most controllers will protect against this, but some might actually go up in smoke. Best to use the break-out board.
There are many, many colors, types and lengths of fairy lights available cheaply, even some with RGB lights. They cost about $1 per strand, give or take. I like the kind where each strand is a single color because it's easier to control the effects. Here's one option on Amazon. Each strand comes with its own fob containing coin batteries and a switch. You won't be able to power more strands than the number of devices supported by your PWM break-out board (in my case, 16).
You can find different kinds of glass blocks at crafts stores and even at hardware stores. I recommend against buying them online because they're overly expensive (probably because of their weight). They come flat, wavy, clear, diffuse, colored, in different sizes, etc. They're very pretty, but also breakable, slippery and very, very heavy. Make sure the one you get has an oblong hole on one side, and a plastic insert which pops into and covers that hole.
If the weight or size of the glass block is a problem, just about any transparent container will work. You can even use a (clean, dry) wine or liquor bottle, a glass globe, an oversized champagne glass, or other clear container. I would use something rather large, though, since the effect is better when the container is large, in my opinion. If you depart from the glass block, you'll have to skip or improvise some of the instruction steps.
Plastic enclosure for the circuitry
I like to put all my electronics into one place. Pick your favorite enclosure, roughly 2 inches by 4 inches by 1 inch (for instance this one from Amazon), but, if you're going to follow this example, be sure it's small enough to fit on the outside of the block comfortably, but large enough to accommodate the controller, PWM board and wires.
What I really wanted to use was a hollow wooden base on which the block could sit. Unfortunately, I couldn't find one of those, and didn't have the time to make one myself. You can even skip the enclosure entirely, and just push the controller and break-out board into the glass block, if you don't mind how that looks.
5v/2A adapter or battery pack
Most of the PWM boards have a MAX of 6v, so keep the voltage under that, e.g., about 5 volts (for instance, this one on Amazon). Also, I wouldn't go much less than 2A because the LEDs demand quite a bit. You can try a battery pack as well.
Just to keep things simple, I always power everything with 5.5mm / 2.1mm jacks.
You also need: a switch (it's easier to drill holes for round switches), barrel-connector socket (matching the adapter's jack), patch wires, female headers, screws, bolts, nuts, soldering equipment, diagonal cutters, drill, hot glue gun, silver sharpie, etc.
Step 2: Connect the Controller and PWM Board
You can follow these directions in order to connect the controller and PWM board. Connect the SCL and SDA pins on each device to each other. You should run the Vout line from the PWM board to the controller's power input, and the controller's Vout to the PWM board's Vcc pin.
There's no need for a circuit- or even a bread-board, since you can use patch wires to connect the male pins from one device to the male pins of the other.
Next you'll need some way temporarily to connect 5v DC to the PWM board's input screw blocks. If you have a bench power supply, connect that. Otherwise, you'll need to solder together a socket which matches the adapter's jack (use a volt meter to make sure you're soldering positive and negative correctly) and run the wires to the input screw blocks.
Step 3: Prepare the Fairy Light Strands
We're not going to be using the battery fobs. Before cutting the wires, though, make sure you find the tiny resistor soldered in series between the battery and the first LED. DO NOT cut that off (in other words, that should stay with the strand, not the fob). After that, use your favorite sharp tool, such as diagonal cutters, to separate the battery fob from the strand. I like to leave an inch or two of wire on the battery-fob side so it can be used again in the future.
Don't worry yet about which wire is positive and which is negative. While that is always important when working with LEDs, conveniently for us, it doesn't matter at this point. The strands I bought actually have a light grey stripe on the positive wire anyway. Don't worry if yours do not.
The PWM break-out board has 16 clusters of three pins: ground, 5v and signal, so each strand will need its own cluster of 3 female header sockets to match. We're only going to use the outer pins (ground and signal), so, after you cut your cluster from the long header strip, just pull out the female header's middle (unneeded) pin. Solder the wires for each strand to the outer pins of one female header cluster.
After you've soldered each strand, you should test it. I did this by powering up the controller and PWM board, and then loading a temporary program which simply turns on all 16 sets of pins.
If you're using a CircuitPython controller (like the M0 Trinket), Adafruit provides an excellent tutorial on how to start up, update and program the board. Once that is done, and the board is connected via a USB cable to your computer, you can write and save a file named "code.py" to the controller board's root directory. The built-in software on the controller will execute the python program in code.py. The simple program I used to test the strands is attached below, named test_code.py. You should rename this to code.py and copy it to the Trinket M0's root directory.
If you're not using CircuitPython, you should use the Arduino IDE or some other way to program your controller to send the signals necessary to turn all of the PWM ports to full output.
When the program is loaded, test a strand by pushing the strand's female header onto any set of PWM pins. If the strand doesn't light, pull it off, turn it around, and push it back on. If that still doesn't work, you should resolder the wires and try again. Once the strand lights up, you should mark the positive ("signal") side of the header somehow so you'll know the right way to push it on next time. I used a silver sharpie to mark the positive side of each tested header.
After you've tested the soldering, you'll want to insulate the solder points by putting a drop or two of hot glue on the exposed wire, solder and metal. I decided to use hot glue (as opposed to shrink tube) because it is clear, like the wires. In addition to avoiding shorts, this has the benefit of stabilizing and strengthening the connection, so it's less likely to bend and break.
When all strands are soldered, tested and insulated, you should be able to slide ALL the headers onto the PWM board's pins, and ALL strands should light up. The order of the colors won't be important for this prototype.
Step 4: Prepare the Glass Block and Enclosure
As I wrote above, the glass block must have an oblong hole in it, and must also have some sort of hard plastic insert which pops into place to cover the hole. First of all, you have to decide which way the block will be oriented, and, therefore, on which side the enclosure will be: left, top or right. I opted to put it on the right. I wanted the switch to be on the back, and the adapter socket to be on the bottom.
We now must secure our electronics enclosure to this insert.
We need to fit two (or more) small bolts through the two plastic pieces in order to attach one to the other. While the insert is popped into the glass block, hold the enclosure to it so they're centered. Tape them together. Carefully remove the insert-and-enclosure from the block without changing their relative position. Decide on two points which will secure the two pieces together well. Find a safe place to set them down and drill 2 holes through both. Remove the tape, pull them apart and clean up the holes. Put them back together, push bolts through, and fit and tighten the nuts.
Now that the enclosure can be attached to the glass block, we need a way for the strands to go through it. I used a very large bore drill bit and drilled a hole through the center of the back of the enclosure. My insert already had a large hole in the center. If yours doesn't, just drill through both.
You should now prepare your switch and adapter socket. Drill holes accordingly. I had to use a sanding dremel to widen the hole for the switch. The hole for the switch will need a small groove as well, so use a file or a sharp, sturdy knife. Push the switch through (it pops into place). Push the jack through and fit the washers and nuts to the outside; tighten.
Find some black and red 20 AWG wire. Solder the wires to the adapter socket; the inner pin is positive and the outer housing is negative; use a volt meter to figure out which pin matches. I like to put the positive (red) through the switch, but it doesn't really matter. Strip the free ends of the wire, and test by inserting the adapter's jack, switching on, and testing the voltage. If there's a problem, unsolder and resolder as necessary. Also check that the voltage is zero when the switch is off.
Put the controller and PWM board into the enclosure. Insert the power wires into the PWM board's screw blocks: red to positive and black to negative.
Step 5: Insert Strands Into the Block
Unplug the adapter.
With the insert pushed into the glass block, slowly push a strand through the enclosure's hole, into the block. Just let it curl around naturally as you gently push it in, without trying to control much. When it's far enough in, push the header onto a set of PWM board pins, being careful to align the positive side correctly. Repeat for every strand. As it gets more crowded in the block, the strands will wind and twist around each other more artfully.
When the last strand's header has been pushed on to the PWM board pins, plug in the adapter and turn it on. All the strands should light. If some don't, recheck the orientation of the strand's header on the pins. Also check the solder connections, something may have broken. Fix what's necessary until all strands are lit.
Put the lid on the electronics enclosure and screw it down. Since the block's insert pops out of the glass block rather easily, I thought it would be a good idea to secure it better, so I used some packing tape for that.
Step 6: Breathe
Now we need to write a more interesting program for the lights.
Plug the controller back into the computer.
My name for this piece is "Breathe", so I wanted the strands to appear to be "breathing", in then out, and then pause a random amount of time before breathing again, each strand working independently of the others. Below is the python script producing the result I liked; copy this to the board to see it work. By all means, experiment with different patterns, timing, pauses, flashes, etc., in order to make what you think is pretty.