Saturday, December 18, 2021

Christmas Tree Project Step 4: Improving the Mapping

Last time, I got the first 3D mapping of the LED coordinates. I wasn't entirely happy with the results, though, so it was time to improve them. So, I started by working on code that I could use to verify the results of the scan. The idea was to turn a few LEDs on at a time, so you can visually verify that those LEDs are in the right order on one of the axes. However, while the y axis is completely obvious, the x and z axes aren't visible in the real world, so that was the first thing I did: create a gradient from red to blue on one of the axes so you could identify the axis.

The x axis

From there, I turned the lights green one by one according to their order on that axis. My original idea was to turn just the light after it and the light before it on, or perhaps a few, but I ended up just turning everything on, with the ones that were before the green LED being red, and the ones after it blue. Then, you could press either space if the LED appeared to have the right coordinate when it came to that axis, or x if it didn't. The software would then remember the incorrect coordinates and write them to a file when done.

Verifying the green LED

A modified version of the mapper can now also be used to scan only the incorrect coordinates. In that case, it will read which LEDs need to be rescanned from the file. It then scans every such LED from all four directions, but only updates the coordinate components of the axes marked as incorrect. With those changes made, I got back to the physical tree and got working on my new coordinates.

I decided not to use my previous mapping results, but start over instead. I had marked the position and orientation of the tree and the camera stand hadn't moved, but the camera wasn't in it in the exact same way. The way the webcam was mounted on the stand was a bit improvised and the webcam was borrowed from my work setup, so it hadn't stayed in the stand and the result was that I didn't put it back in the exact same way.

Improvised does not mean bad!

So, I took a few pictures with the new setup to get the tree properly in frame again and then started scanning. I alternated between judging the coordinates and rescanning them several times until I ended up with something that I was happy with.

Deciding what LED was wrong and what wasn't always obvious. When two LEDs are in the incorrect order, this method just isn't great for determining if the first one or the second one was wrong. When there are more LEDs involved, it gets even more complicated. I did develop a bit of a feeling for it, I just chose for rescanning more than necessary in a number of cases and I restarted the verifying process without saving the results a number of times. I could probably improve things by allowing you to go back in the LED order as well, so I will probably do that if I end up mapping another tree.

I was also important to learn that the result just wasn't going to be perfect. Slight changes in perspective can have big results, and we're already combining several perspectives into one coordinate. Whenever two LEDs were a close call, it was best to just go with whatever result the scan had. The mapping wouldn't be perfect, but it was never going to be perfect and as long as it's close enough, it'll look good, especially considering that the viewer won't have a fixed perspective either.

Finally, there was one LED left that just didn't get a good scan for its Z coordinate. I checked the photos and saw that it was indeed on, but just wasn't visible from either of the two opposite sides. I decided to just set the coordinate manually, so I lit it up as well as some LEDs around it. I identified which were its closest neighbors and gave it a coordinate between them, which I manually put into the output file.

With the new mapping, the next thing to do was of course to run the test effect again. I think the actual changes were relatively small, but the whole effect looks a lot more smooth to me...

(Well, I promise it does in person, at least.)

As before, all the code I wrote for this project can be found on GitHub.

Is the this the end of this project, then? Not just yet. The basic setup is done now, but I do still need to run effects on the thing. I am planning to both write some of my own and run the ones submitted to project that serves as inspiration for this one...

Tuesday, December 14, 2021

Christmas Tree Project Step 3: Mapping

Spoilers of what I pulled off

With the hardware gathered and the lights working, the next step was to put the lights in the tree. I took a bag of zip ties that I think I bought with the intention of using the for cable management in my computer (I bought way too many, though). For the most part, this was pretty uneventful, but there were a few things that I would do different if I'd do this again. The first is that I would plan it more carefully and use more zip ties and attach in more places. The second is that I dislike the colored wires between the lights, so I think I would sleeve them in something green to make them stand out less.

The colored wires do stand out...

I also wanted wifi on my raspberry pi, so I wouldn't have to run a network cable to it. As I realized last time, the device itself doesn't have wifi hardware, so I went looking for wifi adapters. I found two. Both are old and were probably cheap, but they were worth a shot. The first adapter didn't get recognized by the Raspberry Pi, but the second one worked well and after some messing with the configuration, I was able to freely move the device anywhere I had power.

Getting wifi running

The next part was the real challenge: mapping the LEDs to three dimensional coordinates. I did actually send an email to Matt Parker to see if he could share the code he used for this, but unfortunately, he didn't have it in a usable form. Not wanting to wait, I started working on my own.

Getting the camera to take a picture was both easy and hard at the same time. Basically, copying some code from the internet and saving an image wasn't that hard. However, there were basically three libraries to choose from, and none of them was exactly what I wanted and I definitely didn't like that you have to install non-python dependencies for each of them. In the end, I went with pygame, which uses sdl2 under the hood.

My complete mapping setup

There were also several issues with the camera that I had to solve once I started using it more extensively. The first was that the first photo would often have different brightness at the top than at the bottom, with a clear dividing line about a fifth from the top. However, this didn't show up at first, as it starts happening when you run the program more than once. I solved this by taking an image from the camera on startup, discarding that and then working only with the images taken later on.

The next problem was something I started noticing when I was working on the mapping: the photos taken were often from well before I gave the command, sometimes by ten or more seconds. I got around this by re-initializing the camera every time I wanted to take a picture, as then the picture would always end up being current. To prevent the previous brightness issue, though, I had to discard an image each time I did this.

With the camera working, the next step was to start identifying the LEDs and their location. The idea was simple: turn on a LED, take a picture, identify the brightest pixel in the picture and repeat this for the next LED. I also added a step of drawing some lines on the picture, so I could see the result.

Pretty orange lines

The first result wasn't bad for some of the LEDs, but just going with the brightest pixel didn't quite work for others. So, I changed my code to take the average of several bright pixels. My first implementation was quite bad and allowed a gradual increase in brightness to shift the average towards the top of the image. But even after fixing that, the results weren't to my satisfaction at all. So, I decided to take a different approach. Instead of looking for the brightest pixels, I just look with pixels that are above a hardcoded brightness.

For the most part, that gave pretty good results. However, when a lot of light was being reflected, the reflection of the LED would sometimes throw off the calculation. The way I solved this was by lowering the brightness of the LEDs. Originally, I had assumed that full brightness was a good idea, but in the end about 20% brightness gave better results because there were far fewer reflections that were bright enough to be taken into account.

Next, it was time to do multiple scans and combine the results. Doing multiple scans was straight forward: just put the code in a function and all that function four times, with a bit in between where tell the user to rotate the tree. For combining, I gave each mapping result a score, based on the brightness and on how many pixels were involved. Then, I took the y coordinate from the highest score on any of the four scans, the x coordinate from the best among the front and back scans and the z coordinate from the best of among the left and right scans. And those results I printed to a file.

In order to check the results, I quickly threw together some code that read the file (this was basically a copy-paste job from Matt's code) and then changed the color of each LED in order of either the x, y or z portion of the coordinate. And I must say, I'm not at all displeased with the result. It's not perfect, but it's quite good, especially if you consider this was based on the first time I did a full scan of the tree.

There are still LEDs that pretty clearly have inaccurate coordinates. So, that's what I'll have to do for next time: build the tools to see what LEDs are wrong and correct those...

Oh, also: my code is now on GitHub.

Sunday, December 5, 2021

Christmas Tree Project Step 2: The Lights

Last time, we left off with all supplies gathered. Now, it was time to wire things up and get the lights lit up in some pretty colors. First, though, I had to solder headers onto one of the breakout boards. Soldering isn't something I'm great at, but this seemed like a manageable task.


With that behind me, I could start wiring things up. My first try was to just connect the power wires to the lights. But the result was nothing. However, I didn't know if this was because I had made a mistake, or that they just wouldn't light up until there was a data signal telling them to do so. So, I took out a Raspberry Pi that wasn't doing anything at the moment. After spending way too much time on things that should have been simple (like trying to configure wifi on a device that doesn't have wifi), I managed to get things up and running.

Here, I used a little bit of a trick. In order to make it work. In order to make two power supplies work together nicely, you need to wire up the Grounds. However, the Dupont cables I was using didn't have an easy way to go from the breakout board to the lights and to the Pi. However, the Pi has multiple Ground pins, which are wired up internally, so I connected the Lights to one of them and breakout board to another, basically letting the power flow right through the Pi. Unfortunately, nothing happened yet again.


Still nope

Now, it was time to do some debugging. I wasn't even sure if I had gotten the right one of the two connectors, so I messed around with that for a bit, but to no avail. Then, I realized that I could just get rid of the power adapter and breakout board for a bit. As long as the power draw wasn't too high, powering the lights from the Pi's 5V rail shouldn't be a problem. And indeed, I got things to light up!

My one LED program works

This meant that the Pi, the code and the wiring were all good. The problem had to be in the adapter or the breakout board. I should probably have gotten out a multimeter and started measuring. I decided to try something else: powering the Pi from the breakout board. This could definitely potentially damage to Pi, I went with it anyway. And I did see that the Pi's power LED only powered up when I was touching the ground pin on the breakout board. Apparently, I had done poor job soldering and there wasn't a good contact there...

Green across the board!

After making sure the soldering job was done properly this time, I hooked things up again. And I got the lights to turn on this time. It seemed that the hardware part of it was done now. And because programming is what I feel comfortable doing, I was quickly writing several effects. These weren't actually getting me closer to my Christmas tree goals, but I liked doing it nonetheless.

The effect that I liked the most was one that picked a random color for the first LED, and another color for the last LED without changing the last LED just yet.  Then, it gradually moved through the lights in the order of the wire to turn them a color that was slightly closer to the color for the other end each time. Once done, it would pick a new color for the first LED, and do the same thing on the way back. I wanted to include a video of that happening, but the effect didn't show too well on video and there were also some interference patterns causing lines of darkness. So, instead, a picture of each LED in a different random color will have to do.

A poor substitute, I know...

Next time, I suppose I'll have to start involving the actual tree.

Friday, December 3, 2021

Christmas Tree Project Step 1: Materials

Early this year, I watched a video by Matt Parker on his project to make a Christmas tree with LED lights. The lights were individually addressable and he mapped their 3D coordinates so he could make effects that followed the tree rather than the wire of the Christmas lights. I was instantly inspired to make something similar. However, I didn't actually see the video until after the holiday season (which is when he posted a follow-up in which he runs people's code on it), so it didn't really make sense to do so at that time.

I'm not usually one who remembers to follow up on such ideas at the right time, but somehow I ended up at the video again a while ago, which was nicely just before Christmas. This time I was in the perfect position to actually make one myself.

Now, Matt has an important tip in his video for projects like this (in a much broader sense): start small cheap. That's not something I'm good at. I like to start on something really big and ambitious and work towards a grandiose goal, and not care too much about the money I'm spending on whatever new project I'm working on. It happens a lot that I then do not finish the project, but that's just something that I've made my peace with. However, I was also struggling with the fact that if I ordered the lights from an affordable place, I might not actually receive them until after Christmas. So, that's why I decided to go back to his tip and actually follow him.

I actually already owned most of the things I needed for the project. About two years ago, I was similarly inspired by a video on YouTube for another project involving Christmas lights, so I had ordered a string similar to the one Matt used, but I never ended up doing anything with them, so I still had them. I also have plenty SBCs and microcontrollers, so that wasn't going to be a problem either. The one thing I still needed was a tree.

The lights

In the video, the simple start was one with a clothes rack standing in for a tree. I didn't want to go quite that simple, I wanted to make a "Minimal Viable Product" this year and then I can see if I want to go bigger and better next year. I did want an artificial tree, though, as I don't want to deal with the mess and I want to make something that I can just retrieve next year (if I don't end up going bigger). I also didn't mind spending a little money on a tree because it was the only part of the project that would cost money. So, I visited a physical store and bought myself a 90cm (2'11") tree.

A 1.5L bottle of Pepsi for scale because I didn't have a banana

The next challenge was the power adapter. It turned out that that I actually had a lot more of those than I thought I did. However, almost all of them were 12V or above, and the lights needed 5V. One adapter that was 5V only supplied 1.2A. Now, the string is going to need about 3A when all lights are fully lit. I can probably get away with a bit less, but 1.2A seemed like it might be pushing it a bit. One other adapter was rated 5V and 3A which is perfect, but it had the drawback that it was a micro-usb cable that had snuck in with my barrel connector adapters.

One clutter of adapters

Micro-usb cables have two problems. The first is that it's not at all easy to connect to the power lines unless you have a proper connector. That problem disappeared when I realized that I had some micro-usb breakout boards that I can use for this. The second problem is that they are pretty infamous for not reaching the Amperage they are rated for. However, I also had a solution for that: tools to see how the adapter does if you draw up to 2A. That wasn't the full 3A, but it would still give me a good idea if the adapter was likely to work.

1.8 Amps...

As it turned out, the adapter fell well short of the 3A with only about 1.8A, but that was at least an amount that is likely to work as I have all my leds go on full white. And if it doesn't work, the advantage of using the micro-usb break-out board is that I can probably find other cables to use, either in my own possession or in a store.

The micro-usb break-out boards

With all the components collected, I guess the only thing left for me to do is to say that this story is... to be continued.