We are a group of students at the University of Oslo, that have designed and built a massive ferrofluid display with 252 electromagnetic "pixels." The display has a 12x21 resolution (the closest we could get to 16:9 on our budget), and is currently in the final testing phase. The matrix will control ferrofluid and act as a reduced functionality display, meaning it has some limitations.
Most evident is the mechanical time-delays caused by slowness in the ferrofluid itself. It can only be moved "so fast", and it has to be moved up against gravity - meaning it's impossible to magically turn a pixel on out of nowhere. "So fast" is however fast enough that it falls down quite quickly due to gravity. This means we do not get any help from persistence of vision, like normal screens do. In addition, gravity makes sure that if you power a column of adjacent magnets: more ferrofluid will accumulate on the lower pixels, leaving the upper pixels drained. Therefore our system requirements basically state:
1. Each pixel must be individually powered.
2. The system must implement a way of applying different holding force for each pixel.
Now, for point 2, the obvious solution is to PWM modulate the signal that powers each magnet (as described in point 1). However, we are not aware of any microcontrollers with 252 PWM pins. We are not aware of an affordable microcontroller with 252 of any pin, really. So, first of all we have to figure out a way to expand however many pins we have to the amount of pins needed. The solution we use are serial->parallel shift-registers. That solves point 1, but snaps us out of the dreams we had about individual PWM functionality... Or does it? Well, the way this has been solved is to implement the PWM in software. This is not optimal at all, because it makes our PWM frequency dependent on the execution time of the main loop. However, with some recent upgrades we made it seems to run fast enough to give us some leeway in terms of frequency-jitter. There's more on the topic of fast code towards the end of this story, but for now, let's take a look at how the project has evolved.
We were not sure how to do anything related to this project when we started, so the only logical thing to do was to start on a smaller scale than the 252-pixel "monster" that we had planned. Therefore we built a smaller, 6x6 prototype, hooked it up to a single driver-PCB (max 28 magnets, so we only connected 6x5 of them) to test how everything worked out. The results were surprisingly good, so we went ahead to commit to a design that we could use in the final display.
When we are done with the full scale display we may revisit the smaller version and make a separate project on that, since it's a lot cheaper and easier to reproduce for other ferrofluid enthusiasts.
From the time when we saw ourselves happy with the performance of our prototype to the point when our full-scale display was fully assembled a lot of time was spent experimenting with everything from circuitry to the actual ferrofluid tank itself. All of this is documented on our YouTube channel, and if you're interested in making your own ferrofluid display, we highly recommend that you check out those episodes, as we try to share all the pitfalls we've discovered.
In our most recent video we integrate the full system and test it after first having assembled and tested the electronics.
Everything is open source, and the files can be found below. However, we do not recommend anyone to attempt to rebuild the project in its current state. The electronics especially require a big revision (version 2 will be started later this fall).
As was evident in the video above, and as we discussed earlier in the article, the Arduino Mega is starting to get a bit overwhelmed by how we handle everything sequentially.
Now, because the system lacks help from persistence of vision, the magnets need individual control instead of multiplexing. This individual control is however achieved through PWM using serial->parallel shift-registers and ULN2004 driver-ICs, so we still have to shift out a serial sequence. Therefore, the way we've implemented this PWM, is in software. Essentially, our PWM frequency is dynamically changed by the execution time of our main loop, so that's why we have to make sure nothing hogs down a lot of resources/CPU time.
However, we had not parallelized the shift-out sequence at all, and the code was highly un-optimized in general! So we did a bit of tweaking, and managed to get a 420% increase in the PWM frequency (which left it at ~29Hz). While this was a lot better, we could still see a bit of wobble in the ferrofluid controlled by PWM magnets. At this point we realized that it was time for a faster microprocessor. While the new Arduino Nanos are really intriguing - we didn't have any of those laying around. What we did have though, was a Teensy3.6, which is 1125% faster than the Mega in clock frequency alone. And the Teensy is very Arduino friendly, operating smoothly in the known Arduino software environment. After we upgraded to this processor and completing the code optimization we had a total of around 45000% increase from our original code to ~4.2kHz... Welp...
We should add that this gives a bit of overhead, since we only need around 500Hz for highly effective PWM, so the new Arduino Nano series would most likely have been up for this task as well. However, this extra overhead may come in handy when we want to perform more computing between animations instead of just pre-loading them at compile-time. We have plans to add much more functionality to our display, but that will be for the future.
These upgrades have been documented and their effects can be seen in this YouTube video: