Add smooth effect to RGB LEDs using MSGEQ7

Hi there, using the classic David Wang code I’ve got a LED RGB strip + MSGEQ7 setup working just fine.

My question is how can I add a smooth effect to the LEDs when they go from off to on and vice versa, so the reaction with the music playing isn’t too aggressive.

Any ideas?

Here’s the code:

/*
Arduino script which drives our LED circuit.  Outputs at the gate of switching transistors.
Written by David Wang and others on the interwebs, modified
*/

int analogPin = 0; // MSGEQ7 OUT
int strobePin = 2; // MSGEQ7 STROBE
int resetPin = 4; // MSGEQ7 RESET
int spectrumValue[7];
 
// MSGEQ7 OUT pin produces values around 50-80
// when there is no input, so use this value to
// filter out a lot of the chaff.
int filterValue = 80;
 
// LED pins connected to the PWM pins on the Arduino
 
int ledPinR = 9;
int ledPinG = 10;
int ledPinB = 11;
 
void setup()
{
  Serial.begin(9600);
  // Read from MSGEQ7 OUT
  pinMode(analogPin, INPUT);
  // Write to MSGEQ7 STROBE and RESET
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
 
  // Set analogPin's reference voltage
  analogReference(DEFAULT); // 5V
 
  // Set startup values for pins
  digitalWrite(resetPin, LOW);
  digitalWrite(strobePin, HIGH);
}
 
void loop()
{
  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);
 
  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);
 
    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();
 
   // Write the PWM values to the LEDs
   // I find that with three LEDs, these three spectrum values work the best
   analogWrite(ledPinR, spectrumValue[1]);
   analogWrite(ledPinG, spectrumValue[4]);
   analogWrite(ledPinB, spectrumValue[6]);
}

My question is how can I add a smooth effect to the LEDs when they go from off to on and vice versa, so the reaction with the music playing isn't too aggressive.

The simplest way is to not read the spectrum chip as often as you do. Reading the outputs will also apply a specific amount of decay to the channels, so read not so frequently and thus decay will not be as strong.

However perhaps the simplest way is to not use the raw signals for each channel but to use a running average. The more samples in this running average the less aggressive is the response.

Grumpy_Mike:
The simplest way is to not read the spectrum chip as often as you do. Reading the outputs will also apply a specific amount of decay to the channels, so read not so frequently and thus decay will not be as strong.

However perhaps the simplest way is to not use the raw signals for each channel but to use a running average. The more samples in this running average the less aggressive is the response.

Thank you for the reply, this would add a small decay/fade out after the LEDs are on and a small fade in when they light up or just make the LEDs less responsive thus less aggressive?

What I have in mind is the first, so I don't lose the sync with the music.

I outlined two strategies the first makes the lights less aggressive , it is simply a matter of inserting a delay so that is easy enough to test and see the effects of different delays.

this would add a small decay/fade out after the LEDs are on and a small fade in when they light up

No it effects the decay circuits inside the chip it adds nothing in the way of fading to the code itself. You will not perceive any delay at all.

The second technique would smooth the effects of change.

Don’t be too hasty in rejecting anything as you can’t predict exactly how things will look when you alter the dynamics subtlety like this.

Grumpy_Mike:
I outlined two strategies the first makes the lights less aggressive , it is simply a matter of inserting a delay so that is easy enough to test and see the effects of different delays.No it effects the decay circuits inside the chip it adds nothing in the way of fading to the code itself. You will not perceive any delay at all.

The second technique would smooth the effects of change.

Don’t be too hasty in rejecting anything as you can’t predict exactly how things will look when you alter the dynamics subtlety like this.

Sounds good and definitely worth to try both strategies, then see what brings the better results. How would you add each of them, could you help me with the code please?

How would you add each of them

The first I have told you already just add a delay in the loop function.

The second you need to put each spectrumValue into an array for each colour, say readRed, readGreen and readBlue.
The index you put it in will change by one each time you do a reading. When the index reaches the maximum wrap it round and make it a zero.

Then find the value to write to each colour of LED by adding up all the values in the appropriate read array and dividing by the number of samples you have.

Grumpy_Mike:
The first I have told you already just add a delay in the loop function.

This should make the trick then play with the value and see what’s work best?

void loop()
{

delay(10);

  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);

Grumpy_Mike:
The second you need to put each spectrumValue into an array for each colour, say readRed, readGreen and readBlue.
The index you put it in will change by one each time you do a reading. When the index reaches the maximum wrap it round and make it a zero.

Then find the value to write to each colour of LED by adding up all the values in the appropriate read array and dividing by the number of samples you have.

This is where I get lost (I’m sorry), how can I achieve your suggestion with the second part of the code?

  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);
 
    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();
 
   // Write the PWM values to the LEDs
   // I find that with three LEDs, these three spectrum values work the best
   analogWrite(ledPinR, spectrumValue[1]);
   analogWrite(ledPinG, spectrumValue[4]);
   analogWrite(ledPinB, spectrumValue[6]);
}

Well lets say the number of samples to average is in a variable called numberToAverage. Then declare global arrays for each colour.

int readRed[numberToAverage];
 int readGreen [numberToAverage];
int readBlue [numberToAverage];

lets also have a global variable to keep track of our samples called sampNumber

Then we can put each reading into these arrays :-

readRed[sampNumber] = spectrumValue[1];
readGreen [sampNumber] = spectrumValue[4];
readBlue [sampNumber] = spectrumValue[6];
// now move on sampNumber for next time
sampNumber++;
if(sampNumber >= numberToAverage) sampNumber = 0 // wrap round back to the start

So we now generate the average of these samples, I’ll show only one here:-

total = 0
for(int i = 0; i< numberToAverage; ++){
total += readRed[i];
}
// now use this to write to your LED
analogWrite(ledPinR, total / numberToAverage);

Adding a simple delay didn’t make much of a difference and going over 10 caused desync with the music playing.

I’ve tried to do it myself, but I’m having issues applying the second suggestion to the original code.

Could you please help me with one colour, so I can do the same with the rest?

Thank you so much for your help by the way.

/*
Arduino script which drives our LED circuit.  Outputs at the gate of switching transistors.
Written by David Wang and others on the interwebs, modified
*/

int analogPin = 0; // MSGEQ7 OUT
int strobePin = 2; // MSGEQ7 STROBE
int resetPin = 4; // MSGEQ7 RESET
int spectrumValue[7];
 
// MSGEQ7 OUT pin produces values around 50-80
// when there is no input, so use this value to
// filter out a lot of the chaff.
int filterValue = 80;
 
// LED pins connected to the PWM pins on the Arduino
 
int ledPinR = 9;
int ledPinG = 10;
int ledPinB = 11;
 
void setup()
{
  Serial.begin(9600);
  // Read from MSGEQ7 OUT
  pinMode(analogPin, INPUT);
  // Write to MSGEQ7 STROBE and RESET
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
 
  // Set analogPin's reference voltage
  analogReference(DEFAULT); // 5V
 
  // Set startup values for pins
  digitalWrite(resetPin, LOW);
  digitalWrite(strobePin, HIGH);
}
 
void loop()
{
  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);
 
  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);
 
    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();
 
   // Write the PWM values to the LEDs
   // I find that with three LEDs, these three spectrum values work the best
   analogWrite(ledPinR, spectrumValue[1]);
   analogWrite(ledPinG, spectrumValue[4]);
   analogWrite(ledPinB, spectrumValue[6]);
}

I've tried to do it myself, but I'm having issues applying the second suggestion to the original code.

So you need to post your attempt at doing this and then we can guide you as to what you are doing wrong.

Grumpy_Mike:
So you need to post your attempt at doing this and then we can guide you as to what you are doing wrong.

Sure, this is what I tried:

/*
Arduino script which drives our LED circuit.  Outputs at the gate of switching transistors.
Written by David Wang and others on the interwebs, modified
*/

int analogPin = 0; // MSGEQ7 OUT
int strobePin = 2; // MSGEQ7 STROBE
int resetPin = 4; // MSGEQ7 RESET
int spectrumValue[7];
 
// MSGEQ7 OUT pin produces values around 50-80
// when there is no input, so use this value to
// filter out a lot of the chaff.
int filterValue = 80;
 
// LED pins connected to the PWM pins on the Arduino
 
int ledPinR = 9;
int ledPinG = 10;
int ledPinB = 11;

int numberToAverage;
int readRed [numberToAverage];
int readGreen [numberToAverage];
int readBlue [numberToAverage];
int sampNumber;
 
void setup()
{
  Serial.begin(9600);
  // Read from MSGEQ7 OUT
  pinMode(analogPin, INPUT);
  // Write to MSGEQ7 STROBE and RESET
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
 
  // Set analogPin's reference voltage
  analogReference(DEFAULT); // 5V
 
  // Set startup values for pins
  digitalWrite(resetPin, LOW);
  digitalWrite(strobePin, HIGH);
}
 
void loop()
{
  int numberToAverage;
  int readRed [numberToAverage];
  int readGreen [numberToAverage];
  int readBlue [numberToAverage];
  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);
 
  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);

    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();

    readRed [sampNumber] = spectrumValue[1];
    readGreen [sampNumber] = spectrumValue[4];
    readBlue [sampNumber] = spectrumValue[6];
    // now move on sampNumber for next time
    sampNumber++;
    if(sampNumber >= numberToAverage) sampNumber = 0 // wrap round back to the start

    total = 0
    for(int i = 0; i< numberToAverage; ++){
    total += readRed[i];
    total += readGreen[i];
    total += readBlue[i];
    }
    
    // now use this to write to your LED
    analogWrite(ledPinR, total / numberToAverage);
    analogWrite(ledPinG, total / numberToAverage);
    analogWrite(ledPinB, total / numberToAverage);
}

This code will not even compile. You should try anf fix any compile problems first.
Once code compiles it does not mean it will do what you want but it does mean the computer can run this code.

int ledPinB = 11;

int numberToAverage;
int readRed [numberToAverage];

The numberToAverage variable must be assigned a value like the variable above it. This is why you are getting a compile error here. It is best to replace that top line with:-

#define numberToAverage 10

Note no semicolon at the end and no equals sign. What this does is that every time the compiler sees the word numberToAverage it replaces it with 10.

The compiler says

error: array bound is not an integer constant before ']' token
 int readRed [numberToAverage];

This means I doesn't know how much memory to put aside for this array because the variable numberToAverage has not been set up.

int numberToAverage;
  int readRed [numberToAverage];
  int readGreen [numberToAverage];
  int readBlue [numberToAverage];

Remove this from the loop function. All this is doing is redefining variables and arrays already defined and thus overwriting them.

Next the error error:

expected ';' before 'total'
     total = 0
     ^

This tells you that before the word total was encountered the compiler expected a semi colon. So look at the line above and you will see one is missing. You might also notice one is missing after the "total" variable assignment.
Then you get:-

error: 'total' was not declared in this scope
     total = 0;
     ^

Where is this value declared? This can be inside or outside a function.

Next error points at the end of the for loop. See anything wrong here, just using ++. This is the increment operator, what are you incrementing?

This now compiles so see what it does.

Thank you.

Could be uploaded just fine, but the LEDs simply don’t light up anymore with it.

Here’s the code after the changes:

int analogPin = 0; // MSGEQ7 OUT
int strobePin = 2; // MSGEQ7 STROBE
int resetPin = 4; // MSGEQ7 RESET
int spectrumValue[7];
int filterValue = 80;
 
// LED pins connected to the PWM pins on the Arduino
 
int ledPinR = 9;
int ledPinG = 10;
int ledPinB = 11;

#define numberToAverage 10
int total = 0;
int readRed [numberToAverage];
int readGreen [numberToAverage];
int readBlue [numberToAverage];
int sampNumber;
 
void setup()
{
  Serial.begin(9600);
  // Read from MSGEQ7 OUT
  pinMode(analogPin, INPUT);
  // Write to MSGEQ7 STROBE and RESET
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
 
  // Set analogPin's reference voltage
  analogReference(DEFAULT); // 5V
 
  // Set startup values for pins
  digitalWrite(resetPin, LOW);
  digitalWrite(strobePin, HIGH);
}
 
void loop()
{
  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);
 
  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);

    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();

    readRed [sampNumber] = spectrumValue[1];
    readGreen [sampNumber] = spectrumValue[4];
    readBlue [sampNumber] = spectrumValue[6];
    // now move on sampNumber for next time
    sampNumber++;
    if(sampNumber >= numberToAverage) sampNumber = 0; // wrap round back to the start

    total = 0;
    for(int i = 0; i< numberToAverage; ++total){
    total += readRed[i];
    total += readGreen[i];
    total += readBlue[i];
    }
    
    // now use this to write to your LED
    analogWrite(ledPinR, total / numberToAverage);
    analogWrite(ledPinG, total / numberToAverage);
    analogWrite(ledPinB, total / numberToAverage);
}
total = 0;
    for(int i = 0; i< numberToAverage; ++total){
    total += readRed[i];
    total += readGreen[i];
    total += readBlue[i];

You need a different total variable for each of the three colours if you are working out the average like this.

When looking at code try and read it and think what it is doing. Here you are adding all the values for each spectrum component together.

Thought I knew what you meant, but got the same result with this:

int analogPin = 0; // MSGEQ7 OUT
int strobePin = 2; // MSGEQ7 STROBE
int resetPin = 4; // MSGEQ7 RESET
int spectrumValue[7];
int filterValue = 80;
 
// LED pins connected to the PWM pins on the Arduino
 
int ledPinR = 10;
int ledPinG = 9;
int ledPinB = 11;

#define numberToAverage 10
int totalR = 0;
int totalG = 0;
int totalB = 0;
int readRed [numberToAverage];
int readGreen [numberToAverage];
int readBlue [numberToAverage];
int sampNumber;
 
void setup()
{
  Serial.begin(9600);
  // Read from MSGEQ7 OUT
  pinMode(analogPin, INPUT);
  // Write to MSGEQ7 STROBE and RESET
  pinMode(strobePin, OUTPUT);
  pinMode(resetPin, OUTPUT);
 
  // Set analogPin's reference voltage
  analogReference(DEFAULT); // 5V
 
  // Set startup values for pins
  digitalWrite(resetPin, LOW);
  digitalWrite(strobePin, HIGH);
}
 
void loop()
{
  // Set reset pin low to enable strobe
  digitalWrite(resetPin, HIGH);
  digitalWrite(resetPin, LOW);
 
  // Get all 7 spectrum values from the MSGEQ7
  for (int i = 0; i < 7; i++)
  {
    digitalWrite(strobePin, LOW);
    delayMicroseconds(30); // Allow output to settle
 
    spectrumValue[i] = analogRead(analogPin);

    // Constrain any value above 1023 or below filterValue
    spectrumValue[i] = constrain(spectrumValue[i], filterValue, 1023);
 
    // Remap the value to a number between 0 and 255
    spectrumValue[i] = map(spectrumValue[i], filterValue, 1023, 0, 255);
 
    // Remove serial stuff after debugging
    Serial.print(spectrumValue[i]);
    Serial.print(" ");
    digitalWrite(strobePin, HIGH);
   }
 
   Serial.println();

    readRed [sampNumber] = spectrumValue[1];
    readGreen [sampNumber] = spectrumValue[4];
    readBlue [sampNumber] = spectrumValue[6];
    // now move on sampNumber for next time
    sampNumber++;
    if(sampNumber >= numberToAverage) sampNumber = 0; // wrap round back to the start

    totalR = 0;
    totalG = 0;
    totalB = 0;
    
    for(int i = 0; i< numberToAverage; ++totalR){
    totalR += readRed[i];
    }
    for(int i = 0; i< numberToAverage; ++totalG){
    totalG += readGreen[i];
    }
    for(int i = 0; i< numberToAverage; ++totalB){
    totalB += readBlue[i];
    }
    
    // now use this to write to your LED
    analogWrite(ledPinR, totalR / numberToAverage);
    analogWrite(ledPinG, totalG / numberToAverage);
    analogWrite(ledPinB, totalB / numberToAverage);
}

So what do you get if you print those values?

Grumpy_Mike:
So what do you get if you print those values?

With that code I get one read of all 7 values and then it stops, it works again just fine when I go back to the old code.

Any ideas to try?

Why are you increment the total value in the for loop and not the index variable?

for(int i = 0; i< numberToAverage; ++totalR){
    totalR += readRed[i];
    }
    for(int i = 0; i< numberToAverage; ++totalG){
    totalG += readGreen[i];
    }
    for(int i = 0; i< numberToAverage; ++totalB){
    totalB += readBlue[i];
    }

Those loops can never end because the index i never changes.

Grumpy_Mike:
Those loops can never end because the index i never changes.

Post or pre increment for this?

Like This?:

for(int i = 0; i< numberToAverage; ++totalR){
    totalR += readRed[++i];
    }
    for(int i = 0; i< numberToAverage; ++totalG){
    totalG += readGreen[++i];
    }
    for(int i = 0; i< numberToAverage; ++totalB){
    totalB += readBlue[++i];
    }

Post or pre increment for this?

Doesn’t matter.

Like This?

No,
Read my last post again.

What controls a for loop? ( Rhetorical )