Phonic Bloom dev log 3
I received the PCBs from JLCPCB and soldered one. It’s working well!
Most of my recent work has been trying to speed up the framerate. Most of the CPU time was spent calculating the Fourier transform, so I focused on that. I was using the arduinoFFT library. That library defaults to double precision which is overkill when I’m only trying to turn on some LEDs, especialy considering that the LEDs only have 24-bit color. Also, the ESP32 only has a single precision FPU. I edited the library to make it use single precision, and it was much faster, but still not fast enough.
FFTs produce both real and imaginary components. In my case, all my inputs were real values (just readings from the microphone) and I was only processing the real outputs, so computing the imaginary components was unnecessary. I found Robin Scheibler’s FFT library, which has other improvements such as using sine values from a look-up table rather than computing inline, and it was much faster than arduinoFFT. In fact, I had to change my timing code to report microseconds rather than milliseconds. Now, updating the LEDs and collecting samples was taking most of the CPU time.
Next I looked at making updating the LED strips faster. WS2815 LED strips receive data at 800 kilobaud and each LED requires 24 bits. Each strip has 150 LEDs, so updating one strip takes 4.5 milliseconds. With 10 strips, if you update them serially, that’s 45 milliseconds, limiting you to 22 frames per second. FastLED supports multiple outputs using RMT, but it kept crashing. I eventually found I2S Clockless Led Driver which supports parallel output. Not only that, but it can run asynchronously by using DMA to actually write data to the strips.
The final thing I looked at was collecting samples from the microphone. You can’t just collect samples from the microphone more quickly or collect fewer samples, because that will affect which notes are identified by the FFT. However, the ESP32 is dual-core, so I just ran the sample collection on the second core. It stores the samples in a circular buffer, and the other core will periodically copy the most recent samples and process those.
All told, the project can now achieve a framerate of around 200. That’s really fast, faster than is pleasing to look at, so I need to artificially limit it.