LED Audio Spectrum Display


An LED bar graph fabricated on a perf board. Some of the columns are lit up.This project came about, as usual, by accident. I was rooting through my box of IC’s for another project and came across a chip that I must have ordered sometime in the past and forgot about: the MSGEQ7, a “Seven Band Graphic Equalizer” chip. Not the best name — the chip doesn’t do any sort of equalization; it just analyzes an audio signal to generate information that can drive a display.

When I looked up the datasheet, it dawned on me that I had ordered the chip at one point with the intent of making something along the lines of a sound organ. Now that I had looked it up, though, I thought it would be more interesting to make bar-graph display that I could integrate into some future project.

This is a typical application for this chip and some quick web surfing turned up a few descriptions of arduino-based projects that put the chip to just such a use [1,2,3,4]. Between their descriptions and plenty of sample code, it didn’t seem like that big a task to roll my own.

So, let me describe what I did. The overall goal was to take an audio signal, feed it to the MSGEQ7 chip, and have it essentially break the signal into seven frequency bands, i.e., take the signal and display it very coarsely in frequency domain — sort of a poor man’s FFT.

Details are below, or skip right the demo video to see a working version of the display.

The Display

Of course, everything is a bit more difficult when you live off the beaten path, as I do. Starting with the display: I did, in fact, have an 8×8 red LED matrix display in the parts bin, but since the chip only breaks the signal into seven frequency ranges, that display would have one too many columns. Further, when I started mapping the wiring of the headers on that matrix, I was surprised by how seemingly random the layout was. Why not just lay out the pins according to column and row?

The deal breaker for me, though, was that all of the LEDs in the matrix were red. I thought it would be more interesting to have green at the lower amplitudes, then a yellow zone, and finally a top line of red to indicate signals grazing a maximum, more like a traditional VU meter.

The backside of the display, wired up to drive rows and columns of LEDs (see the schematic, below).

I have plenty of LEDs on hand, so the real question was how to wire them up in a fairly dense configuration. My solution was a little labor-intense, but it worked. I used strip board to make connections to the anodes of each LED column. I made the cathode leads a bit longer and wired them above the board into rows. I had to drill away the copper trace around each cathode lead to prevent them from making contact with each other along columns.

This yielded the equivalent of a two-layer PC board with columns wired in one plane and rows in the other. Then, I brought out the rows and columns with ribbon cable and terminated each with 0.1″ male Dupont headers.

Taped up, ready for hot glue.
One solid glob. If I anticipated mounting someplace hot, I guess I could have used epoxy.
How do you get a black perf board? Lots of sharpie.


I did have a concern about the potential for the elevated wire frame work to get crushed, so I borrowed a technique from the days when I used to pour agarose and polyacrylamide gels for DNA electrophoresis: I taped around the periphery of the board and poured in some hot glue. When it set, I had a nice block that provided solid support for the wire frame. As long as I don’t install this in a very hot cabinet, this should work fine.

General Design

The MSGEQ7 has a single output, and in response to driving clock signals, it will constantly cycle through various frequency bands (centered on 63, 160, 400, 1000, 2500, 6250, and 16000 Hz), outputting a voltage proportional to the signal amplitude in each band. A microprocessor, in this case, the ATmega328, handles both the signals that drive the MSGEQ7, measures the output from that chip, and then drives the LED display.

Input and Op Amp

I am not sure where this project will end up, so I built in some flexibility. I made the input impedance relatively high input (10k) since this display will likely just tap into a signal meant somewhere along its intended route, so I didn’t want to make this a significant load. Also, the higher impedance should help preserve measurement of low frequencies if a somewhat reasonably valued coupling capacitor is used upstream.

Picture of the perf board from the side.
Input is at left, and looking from left to right, chips are the LM358 op amp, MSGEQ7 spectrum chip, and the ATmega328 microprocessor.

Taking a look at the schematic, I interposed an op-amp between signal input and the MSGEQ7 so I could adjust the level. The way the negative feedback is set up, the op amp could either attenuate or boost the signal depending on the setting of a rheostat, which probably would be brought to a front panel. I used an LM358 because it was the first one I found in my box. It goes down to ground, but only gets within about 1.5V of the positive rail, in this case 5V. Consequently, I set its virtual ground at about 1.75V, halfway to a 3.5V maximal swing. I used the other half of the op amp as a buffer.



I coupled into the MSGEQ7 with a 0.1uF cap rather than the 0.01uF shown on the typical application in the data sheet, again to try to preserve the low end signal. The rest, though, follows the data sheet. One write up had mentioned increasing the value of the decoupling cap on pin 6 (virtual ground) and bridging between pin 5 (signal input) and pin 6 with a 22pF cap to address high frequency noise, but I did not find that either modification was necessary in my application.

The MSGEQ7 requires two timing signals: RESET to kick off each cycle, and STROBE to advance the output from one audio band to the next. These two signals are output by the ATmega328. The MSGEQ7’s multiplexed output exits the chip on pin3, and that voltage level is directly fed to the one of the analog input pins on the ATmega328 (A5, pin 28).


For good measure, I stuck a 100uF cap across the supply lines after the board was running; this isn’t reflected on the schematic. It isn’t strictly necessary. I think it only shows up on this view.

The display is directly driven from the ATmega328, with one set of outputs sourcing current to columns, and another set of pins sinking rows. So, to get any given LED to light, two pins have to be active, one high, one low. The way the code is set up, a single column is illuminated at a time, allowed to remain lit long enough for good persistence of vision, and then the column is turn off, followed by lighting up the next column.

So, at most, an entire column, i.e., eight LEDs, may be illuminated at any given time. I picked LEDs that are reasonably bright at around 10mA, so worst case would be 80mA source/sink on a pair of digital pins, which is within spec for the ATmega328. However, to get enough pins to handle the display in this manner and do the other things I wanted (and not resort to more complicated arrangements like charlieplexing), I ended up needing the microprocessor pins that are usually devoted to an external crystal oscillator.

This project can run fine using the ATmega328’s internal 8Mhz RC oscillator — the only timing considerations are minimal times needed for signal stability and ADC reads — so I repurposed the crystal pins to serve as digital I/O pins (as described in a tutorial).

When I had initially breadboarded this design, I noticed that some of the LEDs in the first row (i.e., low amplitude) of the display illuminated even in the absence of signal; more so those corresponding to higher frequencies. I recognized this as noise, and realized that I needed to set some sort of lower sensitivity threshold if I wanted the display dark in the absence of signal input.

Since I had constructed this on a solderless breadboard and components included devices producing high-frequency digital signals and devices with gain at audio frequencies, I figured that some of the noise could be attributed to shortcomings of solderless breadboards, jumper wires and ribbon cables are various lengths, and all around lack of shielding. Additionally, there is probably some jitter in the ATmega’s ADC. In some earlier sketches, I explictly coded a threshold level below which LEDs would not light, but when it came to soldering the project up, I thought it would be better to make this adjustable, so I used another analog input to read a potentiometer that sets this threshold. That pot is read (and averaged a few times) when the unit is powered up. Presumably, it will be set once for a given installation and not manipulated, so I didn’t see a need to poll it continuously.


This project’s arduino sketch has been deposited on github. The only bit that I haven’t described above relates to the scale employed in the histogram display. The simplest solution would be to divide amplitude into eight even bins and drive the LEDs accordingly. Taking the threshold level into account, a refinement would be to make the threshold the bottom of the scale and then divide the rest into eight even segments.

However, I decided to go a slightly different route. I somewhat arbitrarily set the highest level as anything registering above 1000 (out of 1023, or about 4.88V out of 5V max) on the ATmega’s input. The top “red” LED level will probably indicate some maximum value, perhaps the level at which something would clip or distort, so I tried to reserve it to represent the real upper edge of the range. If used in this way, the top level should rarely light up. This would meant that the other seven LEDs should reflect the distribution of amplitude between the threshold level and this high warning level.

Most audio meters do not display linearly, since the perception of loudness corresponds to the logarithm of sound power. Four scales were implemented and one is selected at power up according to position of two dip switches. The first scale is linear, but the others are logarithmic, so they light up more LEDs at lower input levels. Of course, this doesn’t really change what’s going on upstream of the display — the display will always reflect an input level to the ATmega between 0 and 5V, but the input amplitude or op amp gain could be adjusted, and one scale or the other might make more sense.

In playing around with the final build and driving the unit with output from my cell phone’s audio port, I found the linear display (mode 0) too boring, whereas the first logarithmic option was more lively and aesthetically pleasing (mode 1).


Picture of the bottom (solder) side of the board.

Since so much of this design fanned out laterally from the chips, I prototyped this on strip board, which involved enough drilling away of traces that I used an electric drill rather than my usual hand tool. I was pleased with how organized this looked when done.

Layout of components on the strip board. A more literal arduino sketch.

Mac Weirdness

I tested the set up using various audio signal generator apps running on my android cell phone, but when it came to make a demo video, I plugged in my Macbook Air. I played a song and the display functioned as expected, but when the song was over, a few bottom row LEDs remained lit. This was puzzling and bit concerning.

a perf board with colu

I scratched my head wondering what was going on. The computer was not plugged in and the display was likewise battery powered. If there were hum or pick up from any nearby equipment, I would have expected it to be at 50Hz, so skewed towards lower frequency. While I was thinking about this, the LEDs blinked off and remained that way. I hit “play” again, stopped the song, and saw the same thing. This time, I had a stopwatch (app) in hand, and noticed that the LEDs blinked off 30 seconds after hitting “stop” on playback.

I figured that the lit LEDs must correspond to a real signal, so I hooked up the scope. Here’s what I saw while the LEDs were lit:

Yes, low signal, but there. Here’s the signal when this shuts off after 30 seconds:

I didn’t hear this difference on speakers until I really cranked the volume on the external speakers, but now it’s evident that when something is plugged into the earphone port on the Mac and playback starts, it switches off its internal speakers and kicks on an external sound circuit which has some detectable level of hiss, so the display was functioning correctly. This circuit cuts out 30 seconds after being activated, probably to conserve power.

I had set the display threshold to just be off in the absence of signal, but this low hiss was enough to overcome that threshold and display. The hiss is probably not something most folks would notice listening on low impedance devices, particularly ear buds, but it really is there.





Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.