About me

Music producer, (ex-)DJ, electronics tinkerer, Linux enthusiast and coder.

LED wall control with Raspberry Pi, part 2


Problems, problems and more problems... This project is obviously turning into a fight, so I dug up some Spock quotes to aid me. Some interesting findings and mildly foul language are to be expected.

Logic is the beginning of wisdom, not the end of it.

As I expected, the 3.3V logic voltage from Pi isn't enough to drive 5V WS2812B LED pixels. A logic level converter or a Schmitt trigger chip would fix that easily, but I want to keep the clock shaper as simple as possible, so in addition to some value adjustments I simply added a second transistor and a pair of resistors to raise the output voltage. See here how it's supposed to work.

The 800kHz pulse represents the SPI clock signal and the 400kHz one represents example SPI data (10101010101010...), and as you can see, a binary 1 produces a long pulse and 0 produces a short pulse. The WS2812B demands a voltage of at least 3.5V for logical high (I bet that's something Spock smokes) and a maximum of 1.5V for low, and the circuit seems to achieve those, but note that the circuit is not yet tested with real-world signals and components, so it may just as well fry a Pi or burn down your house. Physical reality is consistent with universal laws. Where the laws do not operate, there is no reality. Yeah, or reality turns into fires and explosions.

When you eliminate the impossible, whatever remains, however improbable, must be the truth.

While playing aroung with GStreamer I realized that I should somehow minimize the effect of sound volume on spectrum data, so some sort of automatic gain control (AGC) is necessary. Fortunately recent GStreamer versions contain the webrtcdsp element with a gain-control property, which seems to be precisely what I need. The other filter properties need to be disabled to avoid excess CPU usage.

Note that the "bad" plugin set (Debian package gstreamer1.0-plugins-bad) containing the webrtcdsp filter has very heavy dependencies (hundreds of megabytes), so if available SD card space is very limited, instead of installing the packages it may be best to compile a stripped-down GStreamer configuration, although it's said to be difficult.

Below is an example GStreamer pipeline to read the audio output of a PulseAudio server, resample it to 16kHz 16-bit mono (frequencies above ~5kHz are quite insignificant for decorative visualization), measure level (which can be used to control LED color or brightness, for example), add AGC (without other filter junk) and finally create spectrum data. Remember to replace SERVER_ADDRESS and MONITOR_SOURCE to match yours.

gst-launch-1.0 -m pulsesrc server=SERVER_ADDRESS device=MONITOR_SOURCE ! audio/x-raw,rate=16000,format=S16LE,channels=1 ! level interval=333333333 ! webrtcdsp gain-control=TRUE experimental-agc=FALSE echo-cancel=FALSE extended-filter=FALSE high-pass-filter=FALSE noise-suppression=FALSE ! spectrum interval=333333333 bands=512 threshold=-100 ! fakesink

The data looks pretty good now, although the level values show as NULL in console, but that's just a matter of handling their data type (GValueArray) correctly in code, when I finally get to write some.

Now to run the same on a Pi... "Illegal instruction" – what the hell? The same pipeline works on a PC, so apparently the ARM version is compiled wrong and some data gets interpreted as commands. That's dangerous! Debian "stable", my ass... I'll have to file a bug report or at least a fiery rant somewhere. Luckily that failure only occurs when experimental-agc is active, which is why it's set FALSE in the example, hoping it's not a feature I'm going to need.

I examined the problem from all angles, and it was plainly hopeless. Logic informed me that, under the circumstances, the only possible action would have to be one of desperation.

I also noticed that the compression-gain-db property of the webrtcdsp element isn't recognized and Python doesn't understand the GValueList type produced by the spectrum element. Both deficiencies are fixed in GStreamer 1.11.2, but the current version in Raspian (Debian Stretch) is 1.10.4. I don't think I want to wait for years for Raspbian to move on to Debian Buster with newer packages, so I have no choice but to start mixing stable and testing packages on this particular Raspbian setup, hoping it won't bring more problems than it fixes. Sigh. To boldly go where... Oh, shut up already, Spock!

Insufficient facts always invite danger.

At least my initial SPI transfer tests seemed promising. The web is full of outdated and conflicting information about SPI drivers/libraries and especially their DMA capabilities, so I was afraid the BCM2835 in Pi couldn't perform DMA transfers, which could cause CPU spikes and/or SPI timing problems when kilobytes of data need to be pushed to the LED strips, up to 30 times per second. Fortunately it looks like DMA actually works, as I didn't see excessive CPU usage in my tests. For now I'll believe those claiming that DMA kicks in automatically for any transfers of at least 96 bytes, even for userspace programs.

Note that Raspbian's default SPI buffer size seems to be 4096 bytes, which is enough for 1365 24-bit pixels, but very large LED matrices will need a bigger buffer. It's possible to grow it up to 65536 bytes (over 21000 pixels), but a screen that big can't have a high frame rate, because a SPI transfer so large will take nearly a whole second at the WS2812B's maximum rate of 800kHz. Also, at full blast such a number of LED pixels could suck well over 1000 amperes of current! Once again, fires and explosions, probably. Perhaps next time I should write about power supply concerns, because it can become an issue – as if I already don't have enough to deal with.

Live long and prosper.

And while you do that, please leave a comment if you know anything helpful.

0 comments:

Post a Comment