F.R.E.Q. (Frequency Reactive Emissive Quadrants)
Introduction
F.R.E.Q. is a very basic frequency/volume dependent music visualizer. This board uses an Arduino Nano and an MSGEQ7 Graphic Equalizer Display Filter. Pulling from SparkFun where I sourced this IC, “The seven band graphic equalizer IC is a CMOS chip that divides the audio spectrum into seven bands. 63Hz, 160Hz, 400Hz, 1kHz, 2.5kHz, 6.25kHz and 16kHz. The seven frequencies are peak detected and multiplexed to the output to provide a DC representation of the amplitude of each band.”
Mechanical
While never fabricated, a 3D printed case was designed in Fusion 360 utilizing the mounting holes on the four corners of the F.R.E.Q. PCB. This case features a clamping mechanism for the RGB strip to enter and be secured by the case. This would prevent stresses on the RGB strip wires and hopefully prevent the wires from pulling out of the screw terminals.
Hardware
- (1) – Arduino Nano
- (3) – TIP120 Darlington Transistors
- (1) – MSGEQ7 IC
- (1) – 3.5mm Audio Jack
- (1) – 12V Barrel Jack
- (2) – Screw Terminal Block
- (1) – COTS Voltage Regulator
I find the MSGEQ7 to be a fascinating IC as it has an interesting take on data output. Functionally, the IC samples audio and as the “Strobe” pin is triggered, the “Output” pin toggles over the seven frequencies, giving an analog voltage representative of the loudness of said frequency. Here is the timing diagram from the MSGEQ7 datasheet:
The Arduino simultaneously triggers the Strobe pin and reads the analog voltage of the IC. The values of the different frequency bands are stored in an array and the loudness of the three predominant frequency bands is converted to colors. These colors are then converted to PWM values for R, G, and B and the Arduino sends the PWM signals to the Base pin of the TIP120 of each color. This ultimately results in a frequency and volume-dependent light show.
Potential Redesign
This was one of my first boards and there are quite a few things I would change if I were to redesign F.R.E.Q. First of all, I avoided doing my own voltage regulation on this board, requiring an external COTS voltage regulator. With 20/20 hindsight and a lot of knowledge regarding PCB design and electronics in general gained since creating this board, I certainly should have included a local 5v regulator for the board. Other improvements in a potential redesign include a far more consolidated board, locking terminal blocks instead of screw terminal blocks, and thicker traces. From a software standpoint, the code was always finnicky and I never really got the exact rainbow frequency band I was expecting. In summary, as it stands now, F.R.E.Q. is flawed but at least it is functional.
Code
Admittedly, it has been a very long time since I have looked at this code, so if you decide to take inspiration from it, go over in detail yourself to make sure it all checks out. Last time I checked, the code worked.
/*--------------------------------------------------------------- Even Industries F.R.E.Q. Engineer: Joseph Even Description: Music Reactive RGB Light Strip Controller ----------------------------------------------------------------*/ #include #define PI 3.14159 #define COLOR_RATIO 0.078692 char buf[4]; // PIN ASSIGNMENTS const byte r_out = 3; //D3 const byte g_out = 6; // D6 const byte b_out = 9; // D9 const byte volume_led = 12; // D12 const byte ic_strobe = 14; // A0 const byte ic_read = 15; // A1 const byte ic_reset = 16; // A2 // VARIABLES long spectrumValue[7] = {0}; // top_indices void top_indices(int spectrumValue[]); int high_index[3] = {0}; int high = 0; int mid = 0; int low = 0; byte first_index = 0; byte second_index = 0; byte third_index = 0; // color_create void color_create(long spectrumValue[], int high, int mid, int low); /* float high_weight = 1.75; float mid_weight = 1.25; float low_weight = 1.15; */ float color_val = 0; // color_out void color_out(int color_val); float r_raw = 0; float g_raw = 0; float b_raw = 0; float r_constrain = 0; float g_constrain = 0; float b_constrain = 0; float r_val = 0; float g_val = 0; float b_val = 0; // EXTRA FUNCTIONS float mapf(float x, float in_min, float in_max, float out_min, float out_max); float strictMap(float val, float inmin, float inmax, float outmin, float outmax); void setup() { Serial.begin(115200); pinMode(ic_read, INPUT); pinMode(ic_strobe, OUTPUT); pinMode(ic_reset, OUTPUT); digitalWrite(volume_led, LOW); pinMode(r_out, OUTPUT); pinMode(g_out, OUTPUT); pinMode(b_out, OUTPUT); digitalWrite(ic_reset, LOW); digitalWrite(ic_strobe, HIGH); } // End setup void loop() { digitalWrite(ic_reset, HIGH); digitalWrite(ic_reset, LOW); for (int i = 0; i < 7; i++) { digitalWrite(ic_strobe, LOW); delayMicroseconds(40); // Allow the output to settle spectrumValue[i] = analogRead(ic_read); digitalWrite(ic_strobe, HIGH); } // End for /* // Print Reads for (int i = 0; i < 7; i++) { sprintf(buf, "%4d",spectrumValue[i]); Serial.print(buf); Serial.print(" "); } Serial.println(""); */ top_indices(spectrumValue); // Volume Tuning (Hardware Pot with LED indicator) if ( (spectrumValue[high_index[0]] > 1000) && (spectrumValue[high_index[0]] < 1023) ) { digitalWrite(volume_led, HIGH); } color_create(spectrumValue, first_index, second_index, third_index); color_out(color_val); } // End loop // EXTERNAL FUNCTIONS void top_indices(long spectrumValue[]) { // Find top 3 indices and corresponding values int i; for (i = 0; i < 7; i++) { if (spectrumValue[i] > high) { low = mid; mid = high; high = spectrumValue[i]; third_index = second_index; second_index = first_index; first_index = i; } // End if else if (spectrumValue[i] > mid) { low = mid; mid = spectrumValue[i]; third_index = second_index; second_index = i; } // End else if else if (spectrumValue[i] > low) { low = spectrumValue[i]; third_index = i; } // End else if } // End for // Index Numbers for Highest Values high_index[0] = first_index; high_index[1] = second_index; high_index[2] = third_index; } // End top_indices void color_create(long spectrumValue[], int high, int mid, int low) { //0 = purple, 1023 = red /* //Weight color to strongest frequency spectrumValue[high_index[0]] = (spectrumValue[high_index[0]] * high_weight); spectrumValue[high_index[1]] = (spectrumValue[high_index[2]] * mid_weight); spectrumValue[high_index[2]] = (spectrumValue[high_index[2]] * low_weight); constrain(spectrumValue[high], 1, 1023); constrain(spectrumValue[mid], 1, 1023); constrain(spectrumValue[low], 1, 1023); */ // PWM Summation int pwm_sum = 0; for (int i = 0; i < 7; i++) { pwm_sum += spectrumValue[i]; } /* Serial.print("Sum: "); Serial.println (pwm_sum); */ // Frequency Conversion float spectrumValue_adjusted[7] = {0}; int factor[7] = {63, 160, 400, 1000, 2500, 6250, 13000}; for (int i = 0; i < 7; i++) { spectrumValue_adjusted[i] = ( factor[i] * spectrumValue[i] / pwm_sum ); /* Serial.print("Factor: "); Serial.println(factor[i]); Serial.print("Val: "); Serial.println(spectrumValue[i]); Serial.print("Val_adj: "); Serial.println(spectrumValue_adjusted[i]); */ } // Frequency Summation float freq_sum = 0; for (int i = 0; i < 7; i++) { freq_sum += spectrumValue_adjusted[i]; } /* Serial.print("freq_sum: "); Serial.println(freq_sum); */ // Map from 0-1023 color_val = ( (freq_sum * (COLOR_RATIO)) - (63 * (COLOR_RATIO) ) ); Serial.print("cv: "); Serial.println(color_val); } // End color_create void color_out(float color_val) { // Accepts values from 0-1023 and outputs RGB spectrum (0 = purple, 1023 = red) // Map to trig tolerant value float color_val_pi_map = 0; color_val_pi_map = mapf(color_val, 100, 700, .1, ( (5*PI) / 6 ) ); float x = color_val_pi_map; Serial.print("cv_map: "); Serial.println(x); Serial.println(""); // Apply color phase shift r_raw = sin( 2*x + (5*PI / 6) ); g_raw = sin( 2*x + (3*PI / 2) ); b_raw = sin( 2*x + (PI / 6) ); Serial.print(r_raw); Serial.print(" | "); Serial.print(g_raw); Serial.print(" | "); Serial.println(b_raw); // Final Values r_constrain = strictMap(r_raw, 0, 1, 0, 255); g_constrain = strictMap(g_raw, 0, 1, 0, 255); b_constrain = strictMap(b_raw, 0, 1, 0, 255); // Print color values Serial.print(r_constrain); Serial.print(" | "); Serial.print(g_constrain); Serial.print(" | "); Serial.println(b_constrain); Serial.println(" "); // Write to output analogWrite(r_out, r_constrain); analogWrite(g_out, g_constrain); analogWrite(b_out, b_constrain); } // End color_out float mapf(float x, float in_min, float in_max, float out_min, float out_max) { return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min; } // End mapf float strictMap(float val, float inmin, float inmax, float outmin, float outmax) { if (val <= inmin) return outmin; if (val >= inmax) return outmax; return (val - inmin)*(outmax - outmin)/(inmax - inmin) + outmin; }