[bouLED] A proto-demonstrator

Early demonstrator

Since last post, all triangles have been soldered, including their power supplies. Getting them to work was a bit of a pain, since some LEDs got fried, so Alexis spent a lot of time replacing LEDs. Last Friday, we had to do a demo of our progress, and here’s a video of it:

Friday’s demo

There were five triangles we couldn’t use because they were missing a connector, but Alexis finished them this week-end.

The board Lucas is moving (far left) is the main board, with the AHRS array. The image is supposed to move to “counteract” the main board’s rotation: when it is inside, it would make the image static even when rotating the icosahedron.

Lucas used the projection algorithm he describes in this post, which works really well in my opinion. There’s still room for improvement, though: for instance, we haven’t figured the axes out yet. We’re also thinking about what to do in the final demo.

Mechanical design

Ranting time

There are holes in the triangles that allow us to bolt them to 3D-printed pieces. Hichem designed pieces for this in OpenSCAD to join triangles together at a shared corner, but the angles were off. We also tried using FreeCAD, but it was a huge pain to use, even for simple stuff like drawing pyramids. The problem was that when using OpenSCAD, there’s a lot of calculations to do, which are easy to get wrong, and in this case, it’s way simpler to use a constraint-based CAD package. Next, we tried FreeCAD, which was somewhere between “huge pain” and “nightmare” on the user-experience scale. Even for simple stuff like drawing pyramids, it was really unpleasant to work with, although I do concede that a) we’re not mechanical engineering students, b) we’re not familiar with mechanical CAD packages, and c) we didn’t have the time to learn FreeCAD properly (didn’t take it, either).

Enter SolveSpace. This one was a bit of a surprise: it’s a TINY (5.46MiB for the Arch package !) open-source constraint-based CAD package, as opposed to OpenSCAD which uses constructive solid geometry. Even though its UI doesn’t look very modern, SolveSpace very simple and intuitive to use, and what’s more, it doesn’t try to be as featureful as <proprietary CAD package everyone knows>: its simplicity is its best asset. There are some quirks (no loft operations, for instance) but there are often ways around them. All in all, I’m very impressed by SolveSpace: it’s a lesson in minimalism.

A simple structure

I redesigned Hichem’s corner-piece using SolveSpace. It’s meant to join five triangles together at a corner. Here’s what it looks like:

There are hexagonal wells for the nuts. To house the electronics, I designed a plate and supports (yes, also bolted to the triangles). The supports will be glued on the plate. Here they are:

The notches in the plate are there to let the bolts protrude from the supports.

Finally, here’s a view of the whole structure:

BouLED’s skeleton

The corner pieces have all been 3D printed as hollow pieces, which made them way faster to print, and they feel strong enough. We’ll have to laser-cut the plate once I finish its design (mostly figuring out where to put holes for velcro straps for the LiPo battery), print the supports (~20 minutes each, from my early experiments), and finally glue the supports and the plate together.

The insides look like this so far:

Yummy spaghetti

Way messier than the screenshots, huh ? The grey ribbons are for the SPI connections to the LEDs, and the other wires are for power. Soldering the wires to a proper power distribution board (i.e, a two-sided un-etched PCB) and shortening them will probably make this a bit more manageable.

[bouLED] Adding Wifi

BouLed will be inside a plexiglas sphere, so there will be no button. The two ways of interacting with it are its orientation and a WiFi remote control, which I made with an ESP32 DevKit. It opens a access point and runs an HTTP server. You connect to it with your phone (or whichever WiFi-enabled device you like), select some parameters on a web page, and send them to the ESP32. Which in turn sends them to our main board via SPI. Naturally, there’s no noticeable latency.

However, I noticed something strange. The card doesn’t automatically boot in flash when powered on, while it should (according to Espressif) when GPIO0 is not grounded. I tried connecting it to Vcc instead of letting it floating, and it boots in flash. Same behaviour on other kevkits. Except for the brand new one soldered on our main PCB, fortunately.

Clever mistake

When Matthias was working with the simulation, he noticed the faces weren’t ordered correctly. The top 5 faces of the icosahedron, enumerated in clockwise order, would haves indices 0 1 2 4 3 in the array of matrices giving their position, instead of 0 1 2 3 4. Yet I could do all the pretty map projections without any issue, as my implementation didn’t rely on a particular ordering of the matrices. Here’s a pseudo-code version of the function that would compute these matrices :

mat4 face = some_correct_somputation();

mat4 rotation = some_rotation(2*pi/5);


for (int i = 0; i < 4; i++) {

faces.add(rotation * face);

rotation = rotation * rotation;

The purpose of this was to place one face, rotate it by 2*pi/5 around the correct axis and use this as the second face. Rotate the 2nd face by 2*pi/5 to get the 3rd one, etc… This is obviously not the behaviour of this code, as the rotation matrix is squared each time, instead of being rotated by 2*pi/5. But this actually computes the right matrices! Basic group theory in Z/5Z you’d say. Funny bug I say.

Finishing the firmware

For the different features of bouLED, we worked on different devboard, because we can’t all work on our only STM32H7 devboard. Git merge after git merge, we’re done making it all work on the same board.

Finishing the hardware

We soldered the LEDs on the triangular PCBs last week. That was not too painful thanks to our teachers and a pick and place machine.

The pick-and-place machine in action, x16 speed up

We’ll now start mounting the whole thing.

[bouLED] News from the LED panels

Back from a long posting hiatus … The triangles are almost finished. Some of them are missing connectors, and others miss a second Micromatch connector for chaining. Unsurprisingly, when ordering the LEDs from China, we didn’t get APA102 LEDs but some clones: SK9822. It’s a bit different though, but this article sums it up nicely. I had to tweak my LED strip driver but it wasn’t a big deal.

Another surprise: everyone thought that we had 20 triangular PCBs, but no: we found another one. This is good news, especially given Murphy’s law.

Speaking of Murphy’s law, on Friday, we tested a triangle, whose power supply started to smoke … I don’t have any pictures but on Saturday we found out with Alexis that there was solder paste under the inductor of the buck converter, which short-circuited it and fried the IC. Fortunately, after changing the regulator and the inductor, everything works as expected, as far as power supplies are concerned.

However, as for the LEDs, it’s another story. I tested all triangles, and 9 of them don’t work properly: there are dead SK9822s on them, but it’s easy to find where the problem comes from with the testing code I wrote and an oscilloscope as an overkill logic probe. Replacing them should do the trick.

Here’s what the test setup looks like:

There’s a program I wrote on the STM32F7 Discovery board that sends a simple test animation to the triangle. Here’s what it looks like on a good panel:

On defective panels, the white flash at the beginning stops at the failure point.

As to the main board, it’s finished and we’re writing the firmware for it.

[bouLED] Trying a projection algorithm

Over the holidays, I tried implementing a projection algorithm to display an image on the icosahedron. Roughly speaking, the idea is, for each LED of the icosahedron, to find out in which triangle in the un-rotated icosahedron it is. This step works fine. Then, on this triangle, we can put axes and get 2D coordinates, and finally find out which pixel (i.e, which colour) that is. It turns out that this step doesn’t work as well as it should. There were annoying glitches in the coordinates, for instance, and that was a nightmare to debug.

On Monday, we decided to ditch this idea and to go with Lucas’ method, which works fine, and what’s more, which is simple. Simplicity is underrated.

Tomorrow, we’ll build and test the main PCB

[bouLED] Drawing on bouLED

During the holidays, I worked (a bit) on displaying an image on bouLED. I first chose the simplest method that came to my mind: the equirectangular projection, used for world maps.

This is a very convenient representation because it’s simple. Storing a rectangular image in memory is straightforward. And, given the position of one LED as a height and an angle around the vertical axis, computing its color is easy: these two values are the actual latitude and longitude.


Computing this angle from the cartesian coordinates, however, involves a trigonometric function, atan2. This is probably the most expensive part of the computation. If we cannot avoid it, we should at least find an efficient implementation of it.

On the other hand, getting the cartesian coordinates of each LED is cheap. The faces of bouLED are flat, so you only need to make additions if you already have the position of the corners of the triangle.
As we chose an MCU with an FPU, using floating point numbers was a no-brainer.

Of course, the first thing I tried to display on the simulation was a world map. Here are the results:

The map is stabilized while the icosahedron rotates
Without the icosahedron rotating
Camera rotating around rotating bouLED

The simulation uses the exact same code as the MCU. On our 400MHz STM32H7, the function that takes a quaternion as argument and computes each LED’s color takes about 10ms to compute. This represents a very small yet sensible lag. The function can still be optimized, but we’ll need to add some smoothing/antialiasing.

Why are the LEDs so big on the gif ? Aren’t they smaller in reality ? Yes they are, but the result is far less convincing with small light points. That means we may need to “smoke” the plexiglass ball to discern the image.

Also, the equirectangular representation makes it easy to draw on the ball, but leads to some distortion. Matthias is currently working on another, possibly faster method.

We should very soon receive the PCBs. By that time, I’ll be playing with the WiFi module.

[bouLED] Done with the PCBs

We finished the PCB design this week (they are currently being reviewed by Alexis). The voltage regulator placed in the center of the triangular PCB had a few constraints, but routing the LEDs was simple enough so I could use the autorouting and just make a few modifications to it, like shortening the VCC paths and widening these traces.

The voltage regulator

We placed a few mounting holes, to screw the triangles on folded metal sheets keeping the icosahedron together.

The triangular PCB

By the time the PCBs are delivered, we’ll write as much software as possible. We’ll use our devkit to play with the ESP32 and some SD card reader, and, most important, we’ll write the display algorithm (we fortunately happen to have a simulation to help us).

[bouLED] Finishing the main PCB

Last time, I routed the power supply. Since then, I placed and routed all the components and added the ground planes. We’re now waiting for the teachers to review the boards. Below is a screenshot of Xpedition PCB showing what I’ve got so far (ground planes are not displayed, but they’re there):

The green traces are VCC (3.3V). It’s not obvious from the colouring, but some parts of these VCC traces are on the back of the PCB. U3 (the tiny chip above D2) is a LDO regulator (LT1762) that drops the 14.8V VBAT voltage from the LiPo battery (J2) to 5V for the U5 3.3V-to-5V level shifter used for the SPIs (the connectors on the left). U1 is a LT1765 switching power supply for the 3.3V rail. U2 is the STM32H7 MCU. Finally, there’s an SD card (J1) and an ESP32-DevkitC for network connectivity (U4).

[bouLED] Placing LEDs

This week, we did a few corrections and improvements on the schematics and started the the actual design of the PCBs.

We chose the plexiglass sphere for bouLED. Its internal diameter is 350mm so we took a margin and reduced the triangles’ sides to 179mm. This was enough information to place the LEDs. Having done the triangle design with OpenScad revealed helpful to check the placing as I imported a DXF version of it in Mentor’s software, as Alexis suggested. I was also happy that we chose a rather regular LED repartition,  so I could use grids instead of manually copying 78 coordinates.

The triangle PCB in Xpedition PCB

For the routing, however, we’ll need to know the positions of the screw holes in the PCB. Hichem is currently working on that, designing the pieces with SolidWorks. (I started doing it with FreeCAD but the SolidWorks lobby had the last word.)

This will be a 2-layer PCB, with a ground plane on the bottom. The voltage regulator will go on the back side of the PCB, with the jumpers.

[bouLED] Starting placement and routing for the main board

I started placing the 3V3 buck converter first. Here’s what it looks like on the schematic:

The LT1765 switches at a high frequency (1.25MHz), which constrains the routing. The datasheet explains that the most important thing is to make the path from pin 3 to pin 2 through D2 and C2 as short as possible. My layout looks like that so far:

Vias to the ground plane aren’t pictured, but the path I was talking about is really short, and I think it will do the trick. The layout and schematics have yet to be reviewed, though.

[bouLED] Drawing the main board’s schematics

Today, I started drawing the main board’s schematics. It’s not finished yet: the decoupling capacitors have yet to be chosen and I’m not done with the STM32H7 (pins like VBAT, VREF, BOOT0, etc.). As a teaser, here’s the power supply:

It takes a 14.80V input (from a 4S LiPo) and steps it down to 3.3V. That’s not the right buck converter (it should be a LT1765-3.3), but the pinout is the same, so when it’s done I’ll only have to change U1. The SDHDN_n pin is left floating on purpose, according to the datasheet.

Here’s a sneak peek at the unfinished parts of the schematic: