za/ch

Volumetric Display

Allegedly, volumetric displays look really cool in person, and not so cool in videos. So, I decided to make one to properly behold it. It kind of worked. (Code is on Github here)

Silent video of a spinning green LED matrix displaying a vague suggestion of a wireframe cube

I was inspired by and loosely copied this design from mitxela, but ended up using different hardware, and rewriting all the software. Their display is much more polished and very cool; please go read about it.

Hardware

I have selected a Raspberry Pi Pico for some important reasons:

For the LED matrix, I wanted an off the shelf part that’s:

That refresh rate requirement rules out, as far as I could tell, all matrices that use I2C. It’s definitely possible to use larger displays with RGB LEDs that use some other protocol like APA102 (adafruit calls that “Dotstar”), and there is a very cool example here. I couldn’t find any of those in a reasonably small size, though, so I selected a monochrome matrix that I can directly wire to GPIO: the Adafruit LED Charlieplexed Matrix - 9x16 LEDs.

Now to find the forward voltage of the LEDs. The closest thing to a data sheet for the LED matrix doesn’t have much information about the LEDs themselves, because the expectation is that you’ll drive them with the adafruit IS31FL3731 driver chip. I would have done this, but with 400kHz I2C and 8 bits of PWM data per pixel, I couldn’t see a way to make it support the 1200 Hz refresh rate I’m targeting. Someone asked about specs for the LEDs in the adafruit forums but they didn’t get an answer. The data sheet does say the LEDs average 3.2 mA so I targeted that, even though it seemed really low. In practice, they look fine like that, and it means I won’t be anywhere near maxing out power from the Pico’s GPIOs, so this is good enough.

Testing

I made a version on a breadboard to test things out before soldering all my parts together. It also let me hook up another pico to debug, which wouldn’t work so well on the spinning one.

A LED grid on a breadboard with a smiley face pattern illuminated in yellow. It’s connected with 18 blue wires to another breadboard, which has two raspberry pi picos hooked up to it.
A friendly testing and debugging setup

Charlieplexing

Reading about charlieplexing, I found some sources claiming that a charlieplexed matrix of LEDs can only have one LED lit at once. Going back and looking again, the sources that claim this are mostly instructables and posts from 2009 in the arduino forums, which in hindsight I suppose I could have disregarded sooner. I gave up hope briefly, but then dug deeper when the data sheet for the matrix/driver claimed that it has a 1/9 cycle rate, which means 16 LEDs need to be lit at once.

It turns out you can address n-1 LEDs at a time in an n by (n-1) grid. This blog addresses the misconception about controlling multiple LEDs at once when charlieplexing and explains how you can address a whole row (or row-ish group of lights). It’s also just a good explanation in general.

The matrix I got is divided into two 9x8 sub-grids, separately charlieplexed. So I will be lighting up a maximum of 8 LEDs per grid (16 total), and I need to see if that draws more current from the GPIO pins than the pico is rated for (apparently that rating is 50 mA, from the authoritative source, a post buried in this raspberry pi forum thread). There is definitely a way to calculate this, but I don’t know it well enough to trust the numbers I come up with. Instead I measured with a multimeter and somehow it only draws 3.2 mA to light up 8 LEDs. I was shooting for 3.2mA for each LED so this is great. Assuming I measured correctly and that is true, then it’s great news, because I can just use GPIO to directly power the LEDs. Yay.

I used resistors to limit the current to each LED because I didn’t think I could write code that wouldn’t freeze and kill my LEDs. This was the correct choice; I was very happy to have the resistors. As an added bonus they meant I could use breakpoints to help debug the code, which was really really helpful. I know it’s possible to do it without since mitxela just used PWM to limit the current, but I am not that brave yet.

It would be worth exploring the pico’s PWM for brightness control because I’m basically drowning in extra clock cycles. There’s a 133MHz clock rate, 2 cores, and 4 PIO state machines I’ve got to play with. The upper limit is extremely far from anything I’m doing here. For example, this project squeezed DVI out of a pico somehow.

The code

I wrote the code in C. I considered micropython, but for the parts of the C SDK I was trying to use and how they were documented, it ended up easier to write in C. Setting up the environment and getting it running and debuggable actually wasn’t bad, and I’m hoping this being a completely air-gapped toy will mitigate the risks of whatever questionable C I’ve written.

To modify the input/output/high/low state of all relevant GPIO pins at once I use gpio_set_dir_masked() and gpio_put_masked(). “Input” is functioning here as “HiZ” or high impedance (the third state in charlieplex/tri-state multiplexing). It’s perhaps not quite high enough, since there are LEDs that are partially illuminated even when they’re not really supposed to be. It’s pretty good though.

I considered doing something with PIO, but it feels like I’m already deep in the realm of premature optimization, so for now I just did it the simple way, even though it’s not as cool. PIO would be a good direction to go in for a larger display or more (any) color/brightness fidelity. Or if I wanted to do anything else with the chip other than dump pre-rendered pixels onto the display.

Data format

Two rows of LEDs can be set at a time because of the two separate sub-grids. I’m saying “row”, but it’s a zig zaggy row-like object, not really a row. Considering just one grid for now, the row is set by driving one pin high, enabling other pins for output. The high pin indicates the row/scan line, so I iterate over a hard coded list of those. Since that’s implicit, the frame can then be stored as 9 numbers whose bits indicate which pins to set to output for each scan line (and therefore which lights to turn on).

a “row”
a "row" of LEDs

I generated the masks in blender and Python on a regular computer. It occurred to me this pre-rendering approach is analogous to 3D printing, where a computer slices the model and then just sends the path to the printer so it doesn’t have to think too hard. That’s my excuse, anyway. It would certainly be possible to set it up so I can send something recognizable as a normal image to the microcontroller, and use the pico to render it for this particular LED matrix, but that sounded more difficult.

Make it spin

Motor

I decided to control the motor’s speed with a diode so that it’s kind of close to my target speed (30 rotations per second * 60 seconds = 1800 RPM), then measure the actual speed of rotation of the LEDs/Pico with an infrared sensor.

The motor is a 1.5-3V motor rated for 10,000 RPM. This was essentially selected at random at Micro Center, and may not have been the best choice since I don’t need it to go that fast. I think this is the datasheet, but micro center’s website doesn’t say, and I lost the bag that had a part number on it. It doesn’t really need to go a specific speed since I’ll be detecting the rotations anyway.

It might have been a good plan to use gears to make the motor spin at a more appropriate speed, but by the time I considered that I had already glued everything onto it, so maybe in a later version.

Sensor

To detect the start of a rotation I’m using a TCRT5000L IR reflective sensor. Initially I tried to use a hall effect sensor, and I made the rookie mistake of only buying one. So, when I broke it, I ended up ordering some IR sensors, and those worked swimmingly. I used a 2 kΩ resistor from VSYS to the IR LED, and a 10 kΩ resistor from VSYS to the sensor half. I realized after soldering that I should definitely hooked them up to 3v3 out instead but, whoops, oh well, it works. This will also be fixed in a later version if I make one.

There was one unexpected side effect of the IR sensor: when I took a video or photo, sometimes whatever IR my phone’s camera was putting out would interfere with the timing.

Power

I considered following mixtela’s example and using a battery that rotates with the LEDs and microcontroller, but then I remembered inductive power exists and that sounded really cool. I couldn’t find much about powering a microcontroller straight off of an inductive loop, so I thought I’d run into some weird problems with this, or that it would make something important on the pico explode, but it seems to work. (Since I made this, I learned of one example that’s a lot more mission critical).

I used this inductive charging set, which again has a very sparse (nonexistent) data sheet. Maybe it’s supposed to be obvious, but I spent a surprising amount of time googling to try and figure out which side is which. If you’re wandering that same road: The more square side goes to the power supply, and the skinnier rectangle goes to the thing you’re powering.

Assembly

“The mostly assembled thing. The pico/LED sandwich is sticking up off of a round white piece of card with the charging coil glued onto it. There are some resistors and wires sticking out.”

I soldered the pico and the LEDs together using resistors as spacers. To attach everything to the motor, I tucked the inductive loop’s receiver chip between the LED matrix and the pico, threaded the power lines for the IR sensor through the same gap, finished the soldering, and then hot glued it all together. The white platform is a piece of cardstock cut out of the box my soldering iron came in. Below it I glued the other half of the inductive power supply to a piece of plastic cut out of I don’t remember what. Might have been the glue gun’s packaging. You could use more cardstock or 3D print something.

A note on 3D printing: I made it a bit of a challenge to do this with off the shelf parts and no special materials or 3D printed parts. You could totally 3d print some better looking pieces.

Debugging and troubleshooting

I put it together and spun it around, and saw a rotating cloud of points. Womp womp. I realized I needed to reset the counter at the beginning of rotation and then got a mostly stationary cloud of points. I can kind of tell it was supposed to be something kind of cube shaped. Progress!

A raspberry pi pico with green LEDS strapped to it spinning really fast. You can kind of see a wireframe cube if you squint
You can kind of see a shape, sort of!

I took another look through the code because I need it to be a software problem. If it’s the wiring I’m totally screwed. Since the points are stationary, the timing must be somewhat acceptable, so the problem could be in the images I’m sending to it. I found some errors in the table I’m using to convert the image into the format for the pico to put out on GPIO, but fixing that just left me with a slightly different, still not very cube shaped cloud of points.

At this point I also realized it may have been more important than I realized that the candle project uses a grid of LEDs with a negligible thickness. I was hoping that mine would still be thin enough that you can see approximately 180 degrees of LEDs, but that might not have been a valid assumption. This is pretty much where I am until I order a bunch of replacement parts and try again.

Ideas for a future version

Takeaways

This was a really fun “first” hardware project. “First” is in quotes because I have done some soldering, played with neopixels, and made some circuits on breadboards. This was the first time I’ve done it all together and then written firmware for the thing. Learning about hardware usually seems really impenetrable for some reason, and this wasn’t. Or I was just motivated enough to push past that. It was fun to write some C in its natural habitat, and remember how electricity works (sort of).

Additional resources that were helpful:

All the Raspberry Pi Pico documentation, especially https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html and https://datasheets.raspberrypi.com/pico/getting-started-with-pico.pdf

This book for some helpful, pragmatic basics about electronics https://www.makershed.com/products/make-electronics-3rd-edition-print

Tags: