Audio Analyzer does not decay the spectrum

Audio Spectrum Analyzer using ESP32, MSGEQ7 and TFT 1.8" display.
Code complies but spectrum does not decay. What is it that I am missing?
Any help will be appreciated.

```cpp
/* 
BY: Juan Acevedo 9/25/2022

This PROGRAM needs work -  does not decay the bands

Using: ESP32, MSGEQ7, TFT 1.8" Display
Code for the MSGEQ7 taken from public domain
Code for ESP32 and TFT 1.8" adapted from: SoundAnalyzer_ESP32_TTGO

*/

#include <TFT_eSPI.h>

TFT_eSPI display = TFT_eSPI();

// PINOUT FOR TFT 1.8" 128 x 160 DISPLAY
#define TFT_SDA 23
#define TFT_SCL 18
#define TFT_CS   5
#define TFT_DC   2
#define TFT_RST  4

// PINOUT FOR ESP32
int strobePin = 26;
int resetPin  = 27;
int outPin    = 34;
int level[7];

void setup(){
  Serial.begin(115200);
  display.init();
  display.fillScreen(TFT_BLACK);
  display.setRotation(1);
  display.setTextColor(TFT_WHITE);
  display.setTextSize(1);

  display.drawString("Audio Spectrum Analyzer",10,5);
  display.setCursor(0,0);
  display.drawFastVLine(0,15,97, TFT_BLUE);
  display.drawFastHLine(0,112,159,TFT_BLUE);
  display.drawString("63 .16 .4  1  2.5  6  16K",5,120);

  // define our pinModes 
  pinMode (resetPin, OUTPUT);
  pinMode (strobePin, OUTPUT);
  pinMode (outPin, INPUT);
  // end of pinModes

  // Create an initial state for our pins
  digitalWrite (resetPin, LOW);
  digitalWrite (strobePin, LOW);
  delay(1);   

  // Reset the MSGEQ7 as per the datasheet timing diagram
  digitalWrite(resetPin, HIGH);
  delay(1);
  digitalWrite(resetPin,LOW);
  digitalWrite(strobePin,HIGH);
  delay(1);
} // end of setup

void rectangleGR (int x0, int w, int h) {
  const uint16_t colors[8] = {TFT_DARKGREEN, TFT_GREEN, TFT_GREENYELLOW, TFT_YELLOW,TFT_GOLD, TFT_ORANGE, TFT_RED, TFT_BROWN};
  const int decal = 16;
  int x = x0 * (w + 2);
  int dh = (128 - decal) / 8;
  for (int i = 1; i < 8; i++) {
    if (h > i * dh) {
      int y = 128 - decal - i * dh;
      display.fillRect(x, y, w, dh, colors[i - 1]);
    }else{
      int y = 128 - decal - h;
      display.fillRect(x, y, w, h - (i - 1) * dh, colors[i - 1]);
      break;
    }
    
  } 
}

void loop(){ 
  // Cycle through each frequency band by pulsating the strobe
  for ( int band = 0; band < 7; band++){
    digitalWrite( strobePin, LOW);
    delayMicroseconds(100); // Delay necessary due to timing diagram
    level[band] = analogRead(outPin);    
    digitalWrite ( strobePin, HIGH);
    delayMicroseconds(100);    
  }
  
  // Affichage du spectre 
  for(int i = 0; i < 7; i++){    
    int amplitude = (int)level[i]/30;
     amplitude = min(amplitude, 120);
    rectangleGR (i, 20, amplitude+1);       
  }  
}

A guess - are you clearing the bars, after they are written and before a new bar is written? I.E. restoring the background colour?

The comment at the top where it says:

Looks like the decay is a feature that has not been added yet.

Erm, that’s the OP’s comment

Oh. If that is the case:

You are missing the part of "rectangleGR()" where you fill the part of the column above the rectangle with black.

The most efficient way to draw variable vertical bars is to define a maximum number of vertical pixels. Then through the map() function, obtain the equivalent number of pixels of the data.

The bar that will draw the data in equivalent pixels will be the lower one. The top bar will draw the difference between the maximum pixels and the equivalent number of pixels in the data.

In this way there is no need to refresh anything on the screen, the bars themselves will be drawn in a more natural way and the most important with a few lines of code.

  readMSGEQ7();

  unsigned long currentMillisRefresh= micros();        
  if(currentMillisRefresh - previousMillisRefresh > intervalRefresh)
  {
    previousMillisRefresh = currentMillisRefresh;    
    for (band = 0; band < 7; band++)
     {
      if(left[band]>0)
       {
        valY[band]=map(left[band], 0, LimSupY, 0, maxPX);
        tft.fillRect(X0+band*sepX+band*XPx,Y0,XPx,maxPX-valY[band],SUP);
        tft.fillRect(X0+band*sepX+band*XPx,Y0+maxPX-valY[band],XPx,valY[band],INF);  
       }
     }
  }

Teensy 4 + ST7735-touch + MSGEQ7

This is not the most efficient way. You are redrawing the whole bar at lower signal from top to bottom.

I see what you mean. It's great to write the background so there is no ghosting, and this is more efficient than blindly writing the entire bar to background colour.

But, if you keep track of the previously written value, only a few of the bar strips need to be changed. Further, you never need to write background if it's increasing. When decreasing, you only need to write background.

In both cases, knowing the current and previous data values, you can overwrite only the bar strips that have actually changed - with either indicator or background, depending on the direction up/down.

In most cases, this would result in about 5 times faster updates than writing the whole bar, depending on how many bar strips differ in a typical update cycle.

exactly, I did this for 14 bar stereo analyser and it looks very responsive, even had to slow it down a bit. In a nutshell you are drawing either a little bit of background or a little bit of foreground with each update never together.

Based on your feedback I made some changes here and there... The most important was:
"display.fillRect(x, y, w, (i-1) * dh,TFT_Black" Now I have decay but some other problems arise.

/* 
BY: Juan Acevedo 10/6/2022

Using: ESP32, MSGEQ7, TFT 1.8" Display
Code for the MSGEQ7 taken from public domain
Code for ESP32 and TFT 1.8" adapted from: SoundAnalyzer_ESP32_TTGO

Minor problems still exist in this version, perhaps a bad MSGEQ7 CHIP?
This version handles noise suppression but I don't see any improvement
*/

#include <TFT_eSPI.h>
TFT_eSPI display = TFT_eSPI();

// PINOUT FOR TFT 1.8" 128 x 160 DISPLAY
#define TFT_SDA 23
#define TFT_SCL 18
#define TFT_CS   5
#define TFT_DC   2
#define TFT_RST  4

// PINOUT FOR ESP32
int strobePin = 26;
int resetPin  = 27;
int outPin    = 34;
int level[7];
int hauteur = 128;

void setup(){
  Serial.begin(115200);
  display.init();
  display.fillScreen(TFT_BLACK);
  display.setRotation(1);
  display.setTextColor(TFT_WHITE);
  display.setTextSize(1);
  display.setCursor(0,0);
  display.drawString("Audio Spectrum Analyzer",10,5);
  display.drawFastVLine(0,16,100, TFT_BLUE);
  display.drawFastHLine(0,116,159,TFT_BLUE);
  display.drawString("63 .16 .4  1  2.5  6 16K",7,120);
  
  // define our pinModes 
  pinMode (resetPin, OUTPUT);
  pinMode (strobePin, OUTPUT);
  pinMode (outPin, INPUT);
} // end of setup

void rectangleGR (int x0, int w, int h) {
  const uint16_t colors[8] = {TFT_MAGENTA, TFT_GREEN, TFT_GREENYELLOW, TFT_YELLOW,TFT_GOLD, TFT_ORANGE, TFT_RED, TFT_BROWN};
  const int decal = 16;
  int x = x0 * (w + 2) +2;
  int dh = (hauteur -(2 * decal )) / 8; // = 12
  for (int i = 1; i <= 8; i++) {
    if (h > i * dh) {
      int y = hauteur +4 -decal - i * dh;
      display.fillRect(x, y , w, dh, colors[i - 1]);
    }else{
      int y = hauteur +4 -decal -h; 
      display.fillRect(x, y, w, (i-1)*dh, TFT_BLACK); // note: this is where the problem is, need to fix this line
      break;
    }    
  } 
}

void readMSGEQ7(){
  // Cycle through each frequency band by pulsating the strobe
  // Pulse the reset signal to latch the current MSGEQ7 values
  digitalWrite( strobePin, LOW);
  digitalWrite (resetPin, HIGH);
  digitalWrite (resetPin, LOW);
  delayMicroseconds(75);   // Delay to meet minimum reset-to-strobe timing
  for ( int band = 0; band < 7; band++){
    digitalWrite ( strobePin, HIGH);
    digitalWrite( strobePin, LOW);
    delayMicroseconds(36); // Delay necessary due to timing diagram
    level[band] = analogRead(outPin); 
       
    // Filter any noise out of bottom of signals
    if ( level[band]<30) level[band] = 0;

    digitalWrite ( strobePin, HIGH);
    delayMicroseconds(40);    
  }
}
void loop(){ 
  readMSGEQ7();  
  
  // Affichage du spectre
  const int decal = 16;
  for(int i = 0; i < 7; i++){ 
    int ampmax = 0;
    int imax = 0; 
    int amplitude = (int)level[i]/30;
    if (amplitude > ampmax) {
      ampmax = amplitude;
      imax = i;
    }
    amplitude = min(amplitude, hauteur - decal);
    rectangleGR (i, 20, amplitude);       
  }  
}
![IMG_20221007_192940405_HDR|375x500](upload://hGgh1bq55nRuCJlAE8sjZtcbPEY.jpeg)
![IMG_20221007_192956874_HDR|375x500](upload://28gEHYijrr9bxSf7atxhPcGVXMU.jpeg)

Such as?


Don't know how to include a video file.

Fails to make black the brown, this is the 8th top color.
Looks to me it is too fast, I am just guessing at the delays, copying from here and there.

It would be a beautiful display if I could fix the minor problems, especially because this code is so
simple (if debugged) ... Most of the ASA codes I have seen are so long and complicated and difficult to follow for an 81 years old person.

I have made 13 ASA using the Fourier but using lines rather than bars. See attached. I use them to
monitor the output of the crossovers in my HT because there are so many wires in the back of the
monitor I want instant verification that all the speakers are connected and working.

You have to host it somewhere off this site and provide a web link to that.

Here is my first guess at a sketch that will do what you want:

/*
  BY: Juan Acevedo 10/6/2022

  Using: ESP32, MSGEQ7, TFT 1.8" Display
  Code for the MSGEQ7 taken from public domain
  Code for ESP32 and TFT 1.8" adapted from: SoundAnalyzer_ESP32_TTGO

  Minor problems still exist in this version, perhaps a bad MSGEQ7 CHIP?
  This version handles noise suppression but I don't see any improvement
*/
#include <TFT_eSPI.h>
TFT_eSPI display = TFT_eSPI();

// PINOUT FOR TFT 1.8" 128 x 160 DISPLAY
#define TFT_SDA 23
#define TFT_SCL 18
#define TFT_CS   5
#define TFT_DC   2
#define TFT_RST  4

const uint16_t colors[8] =
{TFT_MAGENTA, TFT_GREEN, TFT_GREENYELLOW, TFT_YELLOW, TFT_GOLD, TFT_ORANGE, TFT_RED, TFT_BROWN};


// PINOUT FOR ESP32
const int strobePin = 26;
const int resetPin  = 27;
const int outPin    = 34;
int level[7];
const int hauteur = 128;
const int decal = 16;
const int dh = (hauteur - (2 * decal )) / 8; // = 12

void setup()
{
  Serial.begin(115200);
  display.init();
  display.fillScreen(TFT_BLACK);
  display.setRotation(1);
  display.setTextColor(TFT_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.drawString("Audio Spectrum Analyzer", 10, 5);
  display.drawFastVLine(0, 16, 100, TFT_BLUE);
  display.drawFastHLine(0, 116, 159, TFT_BLUE);
  display.drawString("63 .16 .4  1  2.5  6 16K", 7, 120);

  // define our pinModes
  pinMode (resetPin, OUTPUT);
  pinMode (strobePin, OUTPUT);
  pinMode (outPin, INPUT);
} // end of setup

void DrawBar (int index, int amplitude)
{
  // Calculate the left and right sides of the column
  int xLeft = index * 22 + 2;
  const int xWidth = 20;

  // Truncate top of bar at height of display
  if (amplitude > (8 * dh))
    amplitude = 8 * dh;

  // Draw the eight color levels
  for (int i = 0; i < 8; i++)
  {
    int blockBottom = (i * dh) + decal;
    int blockTop = ((i + 1) * dh) + decal;

    if (amplitude >= blockBottom && amplitude <= blockTop)
    {
      // Block is entirely below 'amplitude'
      display.fillRect(xLeft, blockBottom , xWidth, dh, colors[i]);
    }
    else if (blockBottom > amplitude)
    {
      // Block is entirely above 'amplitude'
      display.fillRect(xLeft, blockBottom , xWidth, dh, TFT_BLACK);
    }
    else if (blockBottom < amplitude && blockTop > amplitude)
    {
      // 'amplitude' cuts the block into two parts
      display.fillRect(xLeft, blockBottom , xWidth, amplitude - blockBottom, colors[i]);
      display.fillRect(xLeft, amplitude , xWidth, blockTop - amplitude, TFT_BLACK);
    }
  }
}

void readMSGEQ7()
{
  // Cycle through each frequency band by pulsating the strobe
  // Pulse the reset signal to latch the current MSGEQ7 values
  digitalWrite( strobePin, LOW);
  digitalWrite (resetPin, HIGH);
  digitalWrite (resetPin, LOW);
  delayMicroseconds(75);   // Delay to meet minimum reset-to-strobe timing
  for (int band = 0; band < 7; band++)
  {
    digitalWrite( strobePin, HIGH);
    digitalWrite( strobePin, LOW);
    delayMicroseconds(36); // Delay necessary due to timing diagram
    level[band] = analogRead(outPin);

    digitalWrite( strobePin, HIGH);
    delayMicroseconds(40);
  }
}

void loop()
{
  readMSGEQ7();

  // Affichage du spectre
  for (int i = 0; i < 7; i++)
  {
    DrawBar (i, level[i] / 30);
  }
}

Thanks very much for your code. It is not perfect but a lot better than what i had.
The spectrum is reversed in that it starts at the top of the display instead of the bottom.
and blacks some bars out of synchronism.

Oops. I didn't know that the Y-axis was upside-down (0 at the top and higher numbers going down). The changes should all be in DrawBar(). Here is a quick edit to reverse the Y-axis. I hope it works!

void DrawBar (int index, int amplitude)
{
  // Calculate the left and right sides of the column
  int xLeft = index * 22 + 2;
  const int xWidth = 20;

  // Truncate top of bar at height of display
  if (amplitude > (8 * dh))
    amplitude = 8 * dh;

  amplitude = hauteur - amplitude;  // Y axis goes backward

  // Draw the eight color levels
  for (int i = 0; i < 8; i++)
  {
    int blockBottom = hauteur - ((i * dh) + decal);
    int blockTop = hauteur - (((i + 1) * dh) + decal);

    if (amplitude <= blockBottom && amplitude >= blockTop)
    {
      // Block is entirely below 'amplitude'
      display.fillRect(xLeft, blockBottom , xWidth, dh, colors[i]);
    }
    else if (blockBottom < amplitude)
    {
      // Block is entirely above 'amplitude'
      display.fillRect(xLeft, blockBottom , xWidth, dh, TFT_BLACK);
    }
    else if (blockBottom > amplitude && blockTop < amplitude)
    {
      // 'amplitude' cuts the block into two parts
      display.fillRect(xLeft, blockBottom , xWidth, blockBottom - amplitude, colors[i]);
      display.fillRect(xLeft, amplitude , xWidth, amplitude -  blockTop, TFT_BLACK);
    }
  }
}

Thanks again for taking the time to help me with this.
I changed the amplitude to 9*dh or really 112. This gives me the Brown bar at the top.
I also changed blockBottom to -8 so the display starts just above the Horizontal line.
"display.fillRect(xLeft, blockBottom - 8, xWidth, dh, colors[i]); " -- changed in 3 lines.
Now I am happy with the outcome.

I just wanted to give a copy of the final code, so if anyone wants to replicate what I did, he would start where I left off.

/*
Audio_Analyzer_version_MSGEQ7.ino

  BY: Juan Acevedo 10/8/2022

  Using: ESP32, MSGEQ7, TFT 1.8" Display
  Code for the MSGEQ7 taken from public domain
  Code for ESP32 and TFT 1.8" adapted from: SoundAnalyzer_ESP32_TTGO

  Special thanks to Johnwasser for fixing the code 
*/
#include <TFT_eSPI.h>
TFT_eSPI display = TFT_eSPI();

// PINOUT FOR TFT 1.8" 128 x 160 DISPLAY
#define TFT_SDA 23
#define TFT_SCL 18
#define TFT_CS   5
#define TFT_DC   2
#define TFT_RST  4

const uint16_t colors[8] =
{TFT_MAGENTA, TFT_GREEN, TFT_GREENYELLOW, TFT_YELLOW, TFT_GOLD, TFT_ORANGE, TFT_RED, TFT_BROWN};

// PINOUT FOR ESP32
const int strobePin = 26;
const int resetPin  = 27;
const int outPin    = 34;
int level[7];
const int hauteur = 128;
const int decal = 16;
const int dh = (hauteur - (2 * decal )) / 8; // = 12

void setup()
{
  Serial.begin(115200);
  display.init();
  display.fillScreen(TFT_BLACK);
  display.setRotation(1);
  display.setTextColor(TFT_WHITE);
  display.setTextSize(1);
  display.setCursor(0, 0);
  display.drawString("Audio Spectrum Analyzer", 10, 5);
  display.drawFastVLine(0, 16, 100, TFT_BLUE);
  display.drawFastHLine(0, 116, 159, TFT_BLUE);
  display.drawString("63 .16 .4  1  2.5  6 16K", 7, 120);

  // define our pinModes
  pinMode (resetPin, OUTPUT);
  pinMode (strobePin, OUTPUT);
  pinMode (outPin, INPUT);
} // end of setup

void DrawBar (int index, int amplitude)
{
  // Calculate the left and right sides of the column
  int xLeft = index * 22 + 2;
  const int xWidth = 20;

  // Truncate top of bar at height of display
  if (amplitude > (8 * dh))
    amplitude = 9 * dh; // Changed to = 112

  amplitude = hauteur - amplitude;  // Y axis goes backward

  // Draw the eight color levels
  for (int i = 0; i < 8; i++)
  {
    int blockBottom = hauteur - ((i * dh) + decal);
    int blockTop = hauteur - (((i + 1) * dh) + decal);

    if (amplitude <= blockBottom && amplitude >= blockTop)
    {
      // Block is entirely below 'amplitude'
      display.fillRect(xLeft, blockBottom -8, xWidth, dh, colors[i]); // Added -8 to blockBottom
    }
    else if (blockBottom < amplitude)
    {
      // Block is entirely above 'amplitude'
      display.fillRect(xLeft, blockBottom -8 , xWidth, dh, TFT_BLACK); // Added -8 to blockBottom
    }
    else if (blockBottom > amplitude && blockTop < amplitude)
    {
      // 'amplitude' cuts the block into two parts
      display.fillRect(xLeft, blockBottom - 8 , xWidth, blockBottom - amplitude, colors[i]); // Added -8 to blockBottom
      display.fillRect(xLeft, amplitude , xWidth, amplitude -  blockTop, TFT_BLACK);
    }
  }
}

void readMSGEQ7()
{
  // Cycle through each frequency band by pulsating the strobe
  // Pulse the reset signal to latch the current MSGEQ7 values
  digitalWrite( strobePin, LOW);
  digitalWrite (resetPin, HIGH);
  digitalWrite (resetPin, LOW);
  delayMicroseconds(75);   // Delay to meet minimum reset-to-strobe timing
  for (int band = 0; band < 7; band++)
  {
    digitalWrite( strobePin, HIGH);
    digitalWrite( strobePin, LOW);
    delayMicroseconds(36); // Delay necessary due to timing diagram
    level[band] = analogRead(outPin);

    digitalWrite( strobePin, HIGH);
    delayMicroseconds(40);
  }
}

void loop()
{
  readMSGEQ7();

  // Affichage du spectre
  for (int i = 0; i < 7; i++)
  {
    DrawBar (i, level[i] / 30);
  }
}

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.