Tutorial: Arduino and the MSGEQ7 Spectrum Analyzer
This is a tutorial on using the MSGEQ7 Spectrum Analyser with Arduino, and chapter forty-eight of a series originally titled “Getting Started/Moving Forward with Arduino!” by John Boxall – A tutorial on the Arduino universe. The first chapter is here, the complete series is detailed here.
Updated 30/01/2013
In this article we’re going to explain how to make simple spectrum analysers with an Arduino-style board. (Analyser? Analyzer? Take your pick).
First of all, what is a spectrum analyser? Good question. Do you remember what this is?
It’s a mixed graphic equaliser/spectrum analyser deck for a hi-fi system. The display in the middle is the spectrum analyser, and roughly-speaking it shows the strength of different frequencies in the music being listened to – and looked pretty awesome doing it. We can recreate displays similar to this for entertainment and also as a base for creative lighting effects. By working through this tutorial you’ll have the base knowledge to recreate these yourself.
We’ll be using the MSGEQ7 “seven band graphic equaliser IC” from Mixed Signal Integration. Here’s the MSGEQ7 data sheet (.pdf). This little IC can accept a single audio source, analyse seven frequency bands of the audio, and output a DC representation of each frequency band. This isn’t super-accurate or calibrated in any way, but it works. You can get the IC separately, for example:
and then build your own circuit around it… or like most things in the Arduino world – get a shield. In this case, a derivative of the original Bliptronics shield by Sparkfun. It’s designed to pass through stereo audio via 3.5mm audio sockets and contains two MSGEQ7s, so we can do a stereo analyser:
As usual Sparkfun have saved a few cents by not including the stackable header sockets, so you’ll need to buy and solder those in yourself. There is also space for three header pins for direct audio input (left, right and common), which are useful – so if you can add those as well.
So now you have a shield that’s ready for use. Before moving forward let’s examine how the MSGEQ7 works for us. As mentioned earlier, it analyses seven frequency bands. These are illustrated in the following graph from the data sheet:
It will return the strengths of the audio at seven points – 63 Hz, 160 Hz, 400 Hz, 1 kHz, 2.5 kHz, 6.25 kHz and 16 kHz – and as you can see there is some overlap between the bands. The strength is returned as a DC voltage – which we can then simply measure with the Arduino’s analogue input and create a display of some sort. At this point audio purists, Sheldonites and RF people might get a little cranky, so once again – this is more for visual indication than any sort of calibration device.
However as an 8-pin IC a different approach is required to get the different levels. The IC will sequentially give out the levels for each band on pin 3- e.g. 63 Hz then 160 Hz then 400 Hz then 1 kHz then 2.5 kHz then 6.25 kHz then 16 kHz then back to 63 Hz and so on. To start this sequence we first reset the IC by pulsing the RESET pin HIGH then low. This tells the IC to start at the first band. Next, we set the STROBE pin to LOW, take the DC reading from pin 3 with analogue input, store the value in a variable (an array), then set the STROBE pin HIGH. We repeat the strobe-measure sequence six more times to get the rest of the data, then RESET the IC and start all over again. For the visual learners consider the diagram below from the data sheet:
To demonstrate this process, consider the function
readMSGEQ7()
in the following example sketch (download):
// Example 48.1 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 // MSGEQ7 spectrum analyser shield - basic demonstration
int strobe = 4; // strobe pins on digital 4 int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays int right[7];
int band;
void setup()
{
Serial.begin(115200);
pinMode(res, OUTPUT); // reset
pinMode(strobe, OUTPUT); // strobe
digitalWrite(res,LOW); // reset low
digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield
}
void readMSGEQ7()
// Function to read 7 band equalizers
{
digitalWrite(res, HIGH);
digitalWrite(res, LOW);
for(band=0; band <7; band++)
{
digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band
delayMicroseconds(30); //
left[band] = analogRead(0); // store left band reading
right[band] = analogRead(1); // ... and the right
digitalWrite(strobe,HIGH);
}
}
void loop()
{
readMSGEQ7();
// display values of left channel on serial monitor
for (band = 0; band < 7; band++)
{
Serial.print(left[band]);
Serial.print(" ");
}
Serial.println();
// display values of right channel on serial monitor
for (band = 0; band < 7; band++)
{
Serial.print(right[band]);
Serial.print(" ");
}
Serial.println();
}
If you follow through the sketch, you can see that it reads both left- and right-channel values from the two MSGEQ7s on the shield, then stores each value in the arrays left[] and right[]. These values are then sent to the serial monitor for display – for example:
If you have a function generator, connect the output to one of the channels and GND – then adjust the frequency and amplitude to see how the values change. The following video clip is a short demonstration of this – we set the generator to 1 kHz and adjust the amplitude of the signal. To make things easier to read we only measure and display the left channel:
Keep an eye on the fourth column of data – this is the analogRead() value returned by the Arduino when reading the 1khz frequency band. You can also see the affect on the other bands around 1 kHz as we increase and decrease the frequency. However that wasn’t really visually appealing – so now we’ll create a small and large graphical version.
First we’ll use an inexpensive LCD, the I2C model from akafugu reviewed previously. To save repeating myself, also review how to create custom LCD characters from here.
With the LCD with have two rows of sixteen characters. The plan is to use the top row for the levels, the left-channel’s on … the left, and the right on the right. Each character will be a little bar graph for the level. The bottom row can be for a label. We don’t have too many pixels to work with, but it’s a compact example:
We have eight rows for each character, and the results from an analogueRead() fall between 0 and 1023. So that’s 1024 possible values spread over eight sections. Thus each row of pixels in each character will represent 128 “units of analogue read” or around 0.63 V if the Arduino is running from true 5 V (remember your AREF notes?). The sketch will again read the values from the MSGEQ7, feed them into two arrays – then display the required character in each band space on the LCD.
Here’s the resulting sketch (download):
// Example 48.2 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 // MSGEQ7 spectrum analyser shield and I2C LCD from akafugu
// for akafugu I2C LCD #include #include "TWILiquidCrystal.h" LiquidCrystal lcd(50);
// create custom characters for LCD
byte level0[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111};
byte level1[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111};
byte level2[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111};
byte level3[8] = { 0b00000, 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111};
byte level4[8] = { 0b00000, 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level5[8] = { 0b00000, 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level6[8] = { 0b00000, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
byte level7[8] = { 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111, 0b11111};
int strobe = 4; // strobe pins on digital 4 int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays int right[7];
int band;
void setup()
{
Serial.begin(9600);
// setup LCD and custom characters
lcd.begin(16, 2);
lcd.setContrast(24);
lcd.clear();
lcd.createChar(0,level0); lcd.createChar(1,level1); lcd.createChar(2,level2); lcd.createChar(3,level3); lcd.createChar(4,level4); lcd.createChar(5,level5); lcd.createChar(6,level6); lcd.createChar(7,level7);
lcd.setCursor(0,1);
lcd.print("Left");
lcd.setCursor(11,1);
lcd.print("Right");
pinMode(res, OUTPUT); // reset pinMode(strobe, OUTPUT); // strobe digitalWrite(res,LOW); // reset low digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield }
void readMSGEQ7()
// Function to read 7 band equalizers
{
digitalWrite(res, HIGH);
digitalWrite(res, LOW);
for( band = 0; band < 7; band++ )
{
digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band
delayMicroseconds(30); //
left[band] = analogRead(0); // store left band reading
right[band] = analogRead(1); // ... and the right
digitalWrite(strobe,HIGH);
}
}
void loop()
{
readMSGEQ7();
// display values of left channel on LCD
for( band = 0; band < 7; band++ )
{
lcd.setCursor(band,0);
if (left[band]>=895) { lcd.write(7); } else
if (left[band]>=767) { lcd.write(6); } else
if (left[band]>=639) { lcd.write(5); } else
if (left[band]>=511) { lcd.write(4); } else
if (left[band]>=383) { lcd.write(3); } else
if (left[band]>=255) { lcd.write(2); } else
if (left[band]>=127) { lcd.write(1); } else
if (left[band]>=0) { lcd.write(0); }
}
// display values of right channel on LCD
for( band = 0; band < 7; band++ )
{
lcd.setCursor(band+9,0);
if (right[band]>=895) { lcd.write(7); } else
if (right[band]>=767) { lcd.write(6); } else
if (right[band]>=639) { lcd.write(5); } else
if (right[band]>=511) { lcd.write(4); } else
if (right[band]>=383) { lcd.write(3); } else
if (right[band]>=255) { lcd.write(2); } else
if (right[band]>=127) { lcd.write(1); } else
if (right[band]>=0) { lcd.write(0); }
}
}
If you’ve been reading through my tutorials there isn’t anything new to worry about. And now for the demo, with sound -
That would look great on the side of a Walkman, however it’s a bit small. Let’s scale it up by using a Freetronics Dot Matrix Display - you may recall these from Clock One. For some background knowledge check the review here. Don’t forget to use a suitable power supply for the DMD – 5 V at 4 A will do nicely. The DMD contains 16 rows of 32 LEDs. This gives us twice the “resolution” to display each band level if desired. The display style is subjective, so for this example we’ll use a single column of LEDs for each frequency band, with a blank column between each one.
We use a lot of line-drawing statements to display the levels, and clear the DMD after each display. With this and the previous sketches, there could be room for efficiency – however I write these with the beginner in mind. Here’s the sketch (download):
// Example 48.3 - tronixstuff.com/tutorials > chapter 48 - 30 Jan 2013 // MSGEQ7 spectrum analyser shield with a Freetronics DMD
// for DMD #include // for DMD #include // SPI.h must be included as DMD is written by SPI (the IDE complains otherwise) #include #include "SystemFont5x7.h" // keep next two lines if you want to add some text #include "Arial_black_16.h" DMD dmd(1, 1); // creates instance of DMD to refer to in sketch
void ScanDMD() // necessary interrupt handler for refresh scanning of DMD
{
dmd.scanDisplayBySPI();
}
int strobe = 4; // strobe pins on digital 4 int res = 5; // reset pins on digital 5
int left[7]; // store band values in these arrays int right[7];
int band;
void setup()
{
// for DMD
//initialize TimerOne's interrupt/CPU usage used to scan and refresh the display
Timer1.initialize( 5000 ); //period in microseconds to call ScanDMD. Anything longer than 5000 (5ms) and you can see flicker.
Timer1.attachInterrupt( ScanDMD ); //attach the Timer1 interrupt to ScanDMD which goes to dmd.scanDisplayBySPI()
dmd.clearScreen( true ); //true is normal (all pixels off), false is negative (all pixels on)
// for MSGEQ7
pinMode(res, OUTPUT); // reset
pinMode(strobe, OUTPUT); // strobe
digitalWrite(res,LOW); // reset low
digitalWrite(strobe,HIGH); //pin 5 is RESET on the shield
}
void readMSGEQ7()
// Function to read 7 band equalizers
{
digitalWrite(res, HIGH);
digitalWrite(res, LOW);
for( band = 0; band < 7; band++ )
{
digitalWrite(strobe,LOW); // strobe pin on the shield - kicks the IC up to the next band
delayMicroseconds(30); //
left[band] = analogRead(0); // store left band reading
right[band] = analogRead(1); // ... and the right
digitalWrite(strobe,HIGH);
}
}
void loop()
{
int xpos;
readMSGEQ7();
dmd.clearScreen( true );
// display values of left channel on DMD
for( band = 0; band < 7; band++ )
{
xpos = (band*2)+1;
if (left[band]>=895) { dmd.drawLine( xpos, 15, xpos, 1, GRAPHICS_NORMAL ); } else
if (left[band]>=767) { dmd.drawLine( xpos, 15, xpos, 3, GRAPHICS_NORMAL ); } else
if (left[band]>=639) { dmd.drawLine( xpos, 15, xpos, 5, GRAPHICS_NORMAL ); } else
if (left[band]>=511) { dmd.drawLine( xpos, 15, xpos, 7, GRAPHICS_NORMAL ); } else
if (left[band]>=383) { dmd.drawLine( xpos, 15, xpos, 9, GRAPHICS_NORMAL ); } else
if (left[band]>=255) { dmd.drawLine( xpos, 15, xpos, 11, GRAPHICS_NORMAL ); } else
if (left[band]>=127) { dmd.drawLine( xpos, 15, xpos, 13, GRAPHICS_NORMAL ); } else
if (left[band]>=0) { dmd.drawLine( xpos, 15, xpos, 15, GRAPHICS_NORMAL ); }
}
// display values of right channel on DMD
for( band = 0; band < 7; band++ )
{
xpos = (band*2)+18;
if (right[band]>=895) { dmd.drawLine( xpos, 15, xpos, 1, GRAPHICS_NORMAL ); } else
if (right[band]>=767) { dmd.drawLine( xpos, 15, xpos, 3, GRAPHICS_NORMAL ); } else
if (right[band]>=639) { dmd.drawLine( xpos, 15, xpos, 5, GRAPHICS_NORMAL ); } else
if (right[band]>=511) { dmd.drawLine( xpos, 15, xpos, 7, GRAPHICS_NORMAL ); } else
if (right[band]>=383) { dmd.drawLine( xpos, 15, xpos, 9, GRAPHICS_NORMAL ); } else
if (right[band]>=255) { dmd.drawLine( xpos, 15, xpos, 11, GRAPHICS_NORMAL ); } else
if (right[band]>=127) { dmd.drawLine( xpos, 15, xpos, 13, GRAPHICS_NORMAL ); } else
if (right[band]>=0) { dmd.drawLine( xpos, 15, xpos, 15, GRAPHICS_NORMAL ); }
}
}
… and here it is in action:
Conclusion
At this point you have the knowledge to use the MSGEQ7 ICs to create some interesting spectrum analysers for entertainment and visual appeal – now you just choose the type of display enjoy the results.
Have fun and keep checking into tronixstuff.com. Why not follow things on twitter, Google+, subscribe for email updates or RSS using the links on the right-hand column, or join our Google Group – dedicated to the projects and related items on this website. Sign up – it’s free, helpful to each other – and we can all learn something.
Review – Ikalogic SCANALOGIC2 Logic Analyser/Signal Generator
Hello Readers
Today we will take a first look at the Ikalogic “Scanalogic2” PC-based logic analyser and signal generator. This is a tiny and useful piece of test equipment that should be useful for beginners and experienced engineers alike. It has been developed by two guys in Europe that are dedicated to the craft, and I wish them well. First of all, let’s pull it out of the box and see what we have:
Upon opening the box, one finds a USB cable, the connector leads and the unit itself. It really is small, around 60 x 35 x 20mm. The USB cable is just under 900mm long. Finally a small instruction and welcome postcard which details a quick overview of the software and the unit’s specifications. Ikalogic are to be congratulated for the minimal level of packaging – finally a company that realises one can download the required items instead of printing books, burning DVDs and causing an increase in shipping weight.
The first thing you will need to do is download the latest software. It needs a Windows-based PC with .net framework. Installing took about two minutes, then the ubiquitous system restart. Finally the last preparation is to check for the latest firmware and update it. This is a simple procedure – download a .zip file, extract the .hexe file, then just file>update device firmware in the software. The desktop software checks for new versions before every startup, so you can be sure of having the latest version.
Here are the specifications of the unit from their web page:
Certainly there is a lot there to take advantage of. Personally I consider the logic analyser functions to be of great interest, and will now demonstrate those to see how they can be useful in debugging and generally figuring out what my designs are up to.
One can capture data in two ways, either by using a live sampling mode, or capture mode where you set the device to sample data into its memory, and then reviewing the data using the software. If you are using the live mode, the quality of the sampling will be affected by your PC resources. For example, consider this first demonstration. A very simple Arduino is setting a pin high and low:
In live mode you can still use the horizontal scroll feature to move backwards and forwards through the captured data. One can also expand the data display to the full width of the window. When using the live mode, I found that there was still some variation in the logic levels that was not programmed for. My PC is fairly up to date, consisting of an AMD PhenonII dual-core 3.1 GHz CPU, 2GB RAM at 1066 MHz, running Windows 7 x64. Perhaps I could use some more RAM? A better video chipset? Who knows… Unfortunately I don’t have a more powerful PC to test. Therefore I will stick to the normal capture mode. Doing so is also quite easy – here is the basic setup tab:
It is pretty self-explanatory. If you have a fair idea of your sampling rate, you can drop it down to increase the available sampling time. Here I have selected the lowest sampling rate, as I will just capture the pulses as shown in the earlier demonstration. Once your sample has been collected, you can scroll through it at your leisure, and also save the sample to disk.
In being able to save the data for later retrieval, there are three things that can be done with the data:
- As anyone can download the software, you can share your samples by emailing or sharing the files with colleagues – they can playback the sample without owning a Scanalogic themselves, by just using the software;
- You can keep the sample for later analysis
- You can blast out the captured data using the function generator feature. Neat! Let’s do that now…
Earlier on I captured the following from a TwentyTen board:
And now I can just right-click on the data (channel one) and select run data generator for this channel then click start on the left. Which results in the following output:
Very good (except for my old CRO). Also notice the log area at the bottom of the application screen – it relays unit status, error messages and so on.
Now let’s capture and look at some more interesting sample data. The following example is an example of captured data from an Arduino serial-out pin, which was programmed to send the letter “A” out at 2400 bps using serial.write();
Once you have captured the sample, you can select the parameters of the data stream and decode the sample. As you can see in the image above, the decoder shows the data stream in hexadecimal and the ASCII equivalent.
Next on the test is I2C. This is a common two wire data bus from Philips/NXP, used in many systems. More about I2C with Arduino is here. A very popular example of an I2C IC is the Maxim DS1307 real-time clock. We can use our Scanalogic to eavesdrop on the SCA and SCL data lines to see what is being said between the microcontroller and the DS1307 (click to enlarge):
So in the example above, the value 0×68 (binary 1101000) is sent down the bus. This is the unique identifier (slave address) for a DS1307 IC. So the Arduino is saying “Hey – DS1307 – wake up”. This is then followed by a 0×00 or directional bit. The DS1307 then replies by sending the time data back to the bus. The first piece of data in the reply is 0×68, which identifies to the I2C bus (recall that 0×68 is the DS1307 identifier) that the data is from the DS1307. Following this is the time and data data in hexadecimal, which is converted to binary-coded decimal in the microcontroller software.
When working with I2C, it really pays to have the data sheet for your IC with you. Then you can decipher the data, direction and timing with the sample data on one side and the timing diagrams on the other. For example, page twelve of the DS1307 data sheet. In doing so, it reminds me how much I dislike I2C
Moving along. Next we will have a look at some data from the SPI (serial peripheral interface) lines. Again, this is quite simple, you just connect the four hooks into the clock, MOSI, MISO and CS lines, and capture away. The software allows you to select which hook is connected to which line, so you can connect up quickly. At this point I will note that the IC hooks are somewhat inexpensive, and the designers could have spent a few more Euro on including some decent ones. Anyhow, here is the screen dump (click to enlarge):
At this point one can realise all sorts of monitoring possibilities. I wish I had one of these years ago when learning digital electronics – you could just monitor the highs and lows over four channels and debug things very quickly. Will keep this in mind when I get around to making a TTL clock.
Anyhow – the Scanalogic2 has a lot going for it in terms of data capturing ability, the price is right, you can update the software and firmware very easily, and the desktop software is freely available in order to share samples with others. There are a few cons though – the IC hooks could be better (I couldn’t connect four in a row onto an IC for the life of me); the unit could use some documentation in terms of a “Getting Started” guide or webpage – so due to this the learning curve is quite high. There is their version here, but I feel it could be expanded upon. Many beginners and amateurs will be attracted to this unit due to the price. However there is a support forum and so on, but answers can vary in quality and time.
However, don’t let the cons put you off – this thing is cheap, the software is very good – and it works. Two thumbs up!
To purchase a Scanalogic2, visit the Ikalogic home page. If you need to analyse some data, and don’t want to spend a bucket of money – this is for you.


















