I was intrigued by the possibilities of addressable “Neopixel” RGB LEDs. I wanted to learn how to control them and to use them in a challenging project. I had read a few projects that use LED arrays and matrices to represent the audio spectrum, much like the illuminated displays on graphic equalizers.
I thought of something a bit different. How about a vertical string of LEDs where the low frequencies would be at the bottom, the high frequencies would be at the top, and the amplitude of each frequency band would appear as the brightness of each LED? The music would then “pulse” along this string. I am certainly not the first to think of this or execute it, but I had not yet seen this particular application.
If the brightness of the LEDs was controlled by the music, could the colour of the LEDs be set by the device's user?
How It WorksThe circuit and the code for this device operate in two modes. These modes are selected with the mode switch at the right of the front panel. When the mode switch is in the left-hand position, the Colour Set mode is activated.
The Colour Set mode allows the user to set the colour range of the LED strip using the 5 potentiometers. Specifically, from left to right, the potentiometers adjust:
1. Value - the overall brightness of the LED strip
2. Saturation - this setting gradually shifts the Hues between vivid colours and pure white
3. Top Hue - specifies which Hue (colour) is at the top of the LED strip
4. Bottom Hue - specifies which Hue (colour) is at the bottom of the LED strip
When the LED strip contains more than one Hue, the colour sequence always follows the sequence of the colour spectrum: red, orange, yellow, green, blue, violet.
To select a single Hue for the entire LED strip, simply place the Top Hue and Bottom Hue potentiometers in the same position.
5. Spread - controls how widely the Hues are spread along the LED strip, from concentrated in the middle to “bunched up” at both ends.
I used the Hue, Saturation and Value (HSV) colour model to control the LEDs as opposed to RGB parameters, because HSV directly produces the effects I wanted. This article explains and compares the HSV and RGB colour models. Luckily, the Adafruit Neopixel library used in the code accommodates both RGB and HSV.
When the mode switch is in the right-hand position, the FFT mode is activated. In this mode, the potentiometers are deactivated.
The FFT (Fast Fourier Transform) mode accepts input from a music player's headphone connection. Using FFT, it calculates the Value (brightness) part of the HSV settings for each frequency band. It then combines this Value parameter with the colours chosen by the user and sends them to the appropriate LEDs. This article gives an easy-to-follow explanation of Fourier transforms. The actual calculations are over my head but once again, the generous Arduino community comes to the rescue in the form of the ArduinoFFT library.
The CircuitCircuit Diagram
In this diagram, the device labelled as Arduino Nano (Rev3.0) is in fact an Arduino Nano Every.
The device labelled Voltage Regulator 5V is in fact a 5V buck converter. The 9VDC wall adapter supplies power to the Nano through its Vin pin and supplies power to the buck convertor. The buck convertor's function is to supply 5V power to the LED strip. The Nano communicates to the LED strip through pin D3.
I used the audio input circuit (at the top left of the diagram) detailed in this project:
https://create.arduino.cc/projecthub/shajeeb/32-band-audio-spectrum-visualizer-analyzer-902f51.
This circuit uses the Nano's REF and 3.3V pins, and feeds into the A0 analog input.
The five potentiometers are used to set the colours and colour range of the LED strip. The five settings are described in the Code section. They are read by the Nano's analog inputs A1 to A5.
The mode switch is used to select the Colour Set or FFT mode. The switch position is read at the Nano's D2 pin.
The Fritzing file is available for download in the Schematics section below.
Breadboard Assembly
I included this diagram for those who would like to explore and modify the circuit as I did. Breadboarding is also the least frustrating way to test the code and all the components. The Fritzing file is available for download in the Schematics section below.
Here again, the device that looks like a voltage regulator is actually a 5V buck converter. The controller represented as an Arduino Nano (Rev3.0) is in fact an Arduino Nano Every. All red wires carry 5V, orange wires carry 9V and black wires go to ground.
Prototype Board and Box Assembly
This diagram is available for download in the Schematics section below. The board illustrated is larger than the one I used, but it allowed me to illustrate the layout more clearly.
By standing up the resistors and doubling some connections in perforations, I was able to squeeze the components onto a 7cm x 3cm board. It fits nicely, with plenty of room to spare, in the enclosure I chose.
The Nano is positioned with its USB connector slightly overhanging the edge of the board. I cut an access hole through the enclosure through which I could connect a USB cable. This allows me to reprogram the Nano without removing it.
I used rubber cement to initially position the Nano directly on the board, until the solder joints secured it in place.
I decided to install JST connectors between the board and the panel-mounted components. This might seem overzealous for a prototype, but having time to spare, I simply wanted to get familiar with these connectors. I also suspect that I will eventually want to rearrange things a bit.
I included my construction drawings in the Custom Parts and Enclosures section. These could save you time in measuring, positioning and mounting the components. You could also use them to print the symbols for the controls.
This photo shows the base of the LED column assembly. This project requires the LED strip to contain 29 LEDs. The back of the LED strip is self-adhesive, so I tacked it onto a piece of 1/2”x1/8” aluminum bar. I wanted to diffuse the light of the LEDs, and I thought of neoprene tubing. It often has a translucent white colour. I found such tubing with 12mm internal diameter which fit perfectly over the bar and LED strip. The light is not yet diffused to my liking, and I will be experimenting with other materials. My eldest daughter, an artist, suggested mylar film.
The CodeI added comments to almost every line of the code in order to explain it to myself as I learned. So here, I will describe it more generally.
Communicating With the LED Strip
The code uses the Adafruit NeoPixel Library to communicate with the LED strip. Here are links to the library and the library user guide.
Structure of the Main Loop
The main loop is divided into two loops; the Colour Set loop and the FFT loop. The position of the mode switch determines which loop is active. The loops are controlled by a “while” statement that reads the switch position.
void loop()
while (digitalRead(modeSwitchPin)==LOW){
//Colour Setting code goes here because the mode switch is in the LOW (Colour Set) position.
}
//FFT code goes here because the mode switch is in the HIGH (FFT) position, outside “while” loop.
//If the mode switch is moved to LOW (Colour Set), reset Nano to re-initialize variables and memory.
if (digitalRead(modeSwitchPin)==LOW){
resetFunc();
}
}
While the mode switch is in the Colour Set position (LOW), the Colour Set code will keep looping.
As soon as the mode switch is flicked to the FFT position (HIGH), the “while” loop breaks and the FFT code starts looping.
When the mode switch is again flicked to the Colour Set position, a soft Reset Function is activated, which re-initializes the Nano and allows the Colour Setting code to start looping again.
I included the soft reset function because the Colour Setting code refuses to run after the FFT code has run. I don't know why this happens, but a soft reset worked.
All instructions sent to the LED strip are in the form strip.setPixelColor() using the RGB standard. However, I chose to set colours using the HSV parameters, then convert them to RGB.
Colour Setting Mode: The Spread Adjustment
This feature needs some explanation. I added this adjustment because I wanted to alter the distribution of hues along the LED strip, from concentrated in the middle to “bunched up” at both ends. I applied a sigmoid curve (also called an s-curve) formula to the range of hues.
k=analogRead(kPin); //read k pot, 0-1023
This line reads the position of the Spread potentiometer.
hueSig[i] = hueMax/(1+(pow(2.718,(0-(k/4000000))*(hue-(hueMax/2))))); //apply s-curve
In this line, the value of the Spread potentiometer (k) is used to adjust the slope of the sigmoid curve, which alters the hue of each LED. The hue of each LED then becomes hueSig[i]. By the way, I arrived at the value 4000000 by experimentation.
This article explains sigmoid curves. I used the basic equation at the very start of the article.
FFT Mode
I certainly did not invent anything here. Thanks to Chris Parker for sharing his design and his code. I also drew from the examples in the Arduino FFT Library documentation.
Comments