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.Prototype (Arduino Mega Based)
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.Many Hours of Experimentation
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.Electronics Assembly and Full Integration Testing:
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).Next Steps
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:Final Touches
The only thing remaining now was to hide the cable chaos. For the initial version of "Fetch" we wanted to use laser cut plywood as a chassis, because it's a very replicable and simple solution. We will look into a making a neater metal finish in the next version, which will be considerably sleeker, for an even cleaner look.
For our own convenience, we also added a few buttons and switches that makes it simpler to show a variety of animations when displaying the project at maker festivals.
We make the animations in Aseprite, a powerful and free* pixel art editor, and parse them into a format we've found easy to read on the Teensy 3.6 that's at the heart of the platform.
(*As long as you can compile the source code yourself. )
While Fetch is now complete, we are not done working with ferrofluid, or electronics for that matter. We are currently researching ways of making a "flat screen" version, and even much smaller ferrofluid displays that are cheaper to replicate. We are also working on implementing more software features, like videogames, in the version we've already made. This paragraph marks the end of the project, but not the end of our ferrofluid display endeavours. Stay tuned!Update: Fetch Version 2.0
We've upgraded the Fetch hardware drastically! We are super excited to finally share what has been going on behind the scenes on this project since before COVID hit and slowed down our progress. First, have a look at our latest project update video, and then dive into the technical details below.Project Update VideoTechnical detailsWhy redesign?
When developing the first version of Fetch we quickly encountered a few problems that could only be solved with a complete PCB redesign. We had made the electronics too generic, and hadn't considered actual integration too thoroughly. So, when we actually attempted to attach wires to it for the first time, we soon realized that the wires weren't long enough to reach where we wanted them. Additionally, since we weren't quite sure how we wanted the software architecture to be, we implemented a lot of features that were more in the "nice to have" category than in the "need to have" category, resulting in an overall bulky design.
Apart from a poor form factor, we also soon discovered that we needed to be able to control the magnetic field strength of each individual electromagnet. The reason that this is necessary is because gravity will pull the ferrofluid down towards the bottom magnet if several pixels are ignited in the same column. The simplest way to control the magnetic field, without having to deal with any additional complex adjustable current circuitry, is by pulse-width modulating (PWM) the supply current that is fed to the electromagnets. We didn't really have a good way to do this though, as the PCBs were based on shift registers that parallelized a stream of serial data in order to open or close gates in ULN2004 Darlington transistor arrays. This means that, if we wanted to implement PWM, we had to do it in software, and the software would be highly resource consuming given that there is a lot of serial data to shift out in order to even get close to a decent PWM frequency.Design requirements
So when redesigning the board we had much stricter design requirements, both in terms of form factor, and in terms of necessary features.
- We wanted one single board controlling each row of electromagnets to simplify the code (and we conveniently had the same number of outputs on our PSU as rows in our electromagnet matrix, which steered us towards one board per row rather than one board per column).
- We wanted the board to have a thin and wide form factor so it would fit behind the electromagnet matrix and all the wires would reach their dedicated connector.
- We wanted to use an IC that could handle PWM for us, preferably through an actual serial protocol rather than just bit shifting.
- We needed to keep the things that worked well in our previous design (such as an alternating pattern of electromagnet polarities designed into the boards rather than having to manage electromagnets with different polarities decided by their connector orientation).
Three of the mentioned requirements were easy to implement, but finding a PWM capable IC that could provide enough current for our needs proved more difficult. After pondering a while we realized that there are a ton of PWM capable ICs in the LED driver segment.
Maybe we could just use an LED driver to power our magnets? The problem with that idea turns out to be the maximum current rating. LED drivers are typically not rated to sink more current than what is necessary to turn on an LED or two. While a normal LED can consume a fair bit of current (typically up to ~20mA), it's generally not even close to the consumption of an electromagnet (~200mA).
Ok, so maybe we could combine an LED driver with a transistor array in a similar fashion to how we combined shift registers and transistor arrays before? The problem with this idea is that most LED drivers are designed to be "open-drain", meaning they are supposed to be connected to the ground side of a load and sink current that is provided by some other source. They are therefore not able to provide a logic "high" signal without an external inverter or pull-up resistor that would quickly complicate our design significantly.
After acknowledging these two roadblocks it became apparent that we didn't really have many ICs to choose from any more. In fact, we could only find a single IC that could provide decent PWM capability at the same time as providing a totem-poled (logic high) signal on an array of outputs: the PCA9685. One IC is enough though! So we could proceed with our "trick" of linking the pulse-width modulated outputs of the PCA9685 to the inputs of an ULN2803 Darlington transistor array.
When spinning up our first prototype of the new design it quickly became evident that something was wrong. In fact, something was so wrong that a few ICs weren't even connected to power (whoops). But with a bit of bodging we could verify that the PCA to ULN concept worked, and we could then redesign the board and order a full set. This time they actually worked as expected (even though we had a really bad feeling at first due to hooking up some wrong wires). However, as soon as we tried to connect 12 of these boards in parallel with a bunch of jumper wires within a tight enclosure, more problems arose. One of them was easy to fix: broken jumper wires. Another one was worse: broken PCBs (we're still not sure if the PCB itself has broken in some way or if the components on it has suffered too much heat due to poor soldering - it could even be both for all we know). The way we fixed this was by repurposing some of the boards that were currently not in use (the top and bottom rows of the electromagnet matrix are too far away from ferrofluid to be useful with our current ferrofluid tank). This "solution" will probably come back to bite us in the future. Aaaanyway: we also found that the jumper wires added a lot of stray capacitance on the I2C bus. None of us had really worked much with the I2C bus before, so this took a while to figure out, but in the end (after learning a thing or two about pull-up resistance) we solved the problem by using stronger pull-ups (lower resistance).
So with 10 fully working rows of electromagnets, we could finally start looking at our software again. It had grown quite the beast when we were forced to implement PWM through shift-registers in software, so it was quite pleasing to just chuck most of it in the bin and start over. As a part of this rewrite, we decided to split out some of the code in separate libraries that are all specifically designed for our needs rather than attempting to be very generic. This is just a result of how little time we actually have to work on this project. The main firmware is still to be found in the FerroFetchFirmware repo, but it is now dependent on AprocAnimation and MagnetControllerV2-library. Hopefully this code will be much more readable than our previous solution. We've at least found it much easier to develop new features in this new system than in the old one.
As we're already talking about repositories, the new PCB files can be found in the FetchCADFiles repo, and will be uploaded here as well.