While loop stops working when adding new code below

Hello there,

this is the final part of my question trilogy. The first two were posted in a different forum section, but I think this question belongs here.

I am building a VU-meter with an ESP32 Dev Kit V4 from AZ-Delivery using two WS2812b LED-stripes with 25 LEDs each. I am using a binary mic and some code to get a -good enough- "analog" signal and use that to calculate how many LEDs turn on.

I will describe my problem and then post the code below. I will leave the code explaining to my comments in the code, but if you have any questions... please ask :slight_smile: .
The problem is as follows:
When I comment out the LED-code, the mic code works perfectly and I get an anolog output. When putting the LED-code back in, I only get a binary output. This binary output is also pretty unreliable. I dont get how the code below the mic part can have an influence on it. The LEDs turn on though (12 of them since I declared Height as 12).
The commented code you see above the while loop is another option for the mic code that I have tried. When combining this with the LED-code, the mic gives me an analog reading, but the LEDs dont turn on.
I am looking for a way to get both code bits running without a problem. I hope I described the problem well enough.

#include <Adafruit_NeoPixel.h>

#define LED1 17
#define N_PIXELS 25

#define LED2 32

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(N_PIXELS, LED1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(N_PIXELS, LED2, NEO_GRB + NEO_KHZ800);

const int micPin = 33;
const int Sample_Time = 10;
unsigned long millisCurrent;
unsigned long millisLast;
unsigned long millisElapsed = 0;

int Volume = 0;

int i = 0;
int Height = 0;
 

double Sensitivity = 1;


void setup() 
{
  Serial.begin(9600);
  while( !Serial) delay(20); 
  pinMode(micPin , INPUT);

}

// while loop takes the binary output from the mic and turns it into a (faked but surprisingly well working) analog output to use for the VU-Meter
void loop() 
{
//  while(millisElapsed <= (Sample_Time+1))                       // while loop is needed so the code runs often enough to give me an output
//  {
//    millisCurrent = millis();                                   // millis() is time since the programm started              
//    millisElapsed = millisCurrent - millisLast;                 // millisElapsed is time since end of last Sample time
//    if(digitalRead(micPin) == HIGH)                             // HIGH = there is a sound detected (mic output is only binary)
//    {
//     Volume++;                                                  
//    }
//
//    if (millisElapsed > Sample_Time)                            //Sample time is 10 millis
//    {
//     Serial.println(Volume);                                    // basically just for testing purposes
//     Height = Volume / 240 * Sensitivity;                       // Height is how many of the 25 LEDs will light up (numbers probably need some tweaking)
//     Volume = 0;                                                // Volume reset for next loop
//     millisLast = millisCurrent;                                // in order to calculate the next millisElapsed
//   }
//  }

   Volume = 0;
  millisElapsed = 0; // NEW ADDITION
  while(millisElapsed <= Sample_Time)
  {
    millisCurrent = millis();                                     //millis() is time since the programm started              
    millisElapsed = millisCurrent - millisLast;                   //millisElapsed is time since end of last Sample time
    
    if(digitalRead(micPin) == HIGH)                               // HIGH = there is a sound detected (mic output is only binary)
    {
     Volume++;
    }
  }
  
  Height = Volume / 240 * Sensitivity;                          // Height is how many of the 25 LEDs will light up (numbers probably need some tweaking)
  millisLast = millisCurrent;
  Serial.println(Volume);

  Height = 12;                                                  // just to see if the LEDs work without relying on the mic data
 
  for (i = 0; i < N_PIXELS; i++)                                // rest is just for the LEDs
  {
    if (i >= Height)
    {
      strip1.setPixelColor(i, 0, 0, 0);
    }
    
    else
    {
      strip1.setPixelColor(i, 0, 0, 150);
    }
    strip1.show();
  }

  for (i = 0; i < N_PIXELS; i++)
  {
    if (i >= Height)
    {
      strip2.setPixelColor(i, 0, 0, 0);
    }
    
    else
    {
      strip2.setPixelColor(i, 220, 0, 0);
    }
    strip2.show();
  }
}

Thank you for reading it all and lets hope its a dumb mistake which can be easily fixed. I am pretty new to this all, so please consider that when giving an explanation. Have a wonderful weekend.

Nils

What value has your millisLast first time?

millisLast

not sure which part of the code you add or remove
can you post two versions of the code
the one that only has that lines of code that create analog output

a second version that has the additional code that creates "digital" output.

By the way the LED-code needs 50 iterations 2x25 = 50 and has additionals command
strip1.show()
strip2.show() where each command needs some execution time

not sure if this sums up what you call "digital output"

best regards Stefan

looks like you want to sample the mic every 10 ms. how long doe the mic code take to execute?

Yes of course, sorry for not making it clear from the get go. Another clarification because of the digital output: The mic code on its own (first code down below) gives me analog readings between 0 and around 6000/7000. When I add the LED code (2nd code below) I only get 1s and 0s (almost only 0).

This code gives the analog output

const int micPin = 33;
const int Sample_Time = 10;
unsigned long millisCurrent;
unsigned long millisLast;
unsigned long millisElapsed = 0;

int Volume = 0;

int i = 0;
int Height = 0;


double Sensitivity = 1;

void setup()
{
  Serial.begin(9600);
  while ( !Serial) delay(20);
  pinMode(micPin , INPUT);
}

void loop()
{
  Volume = 0;
  millisElapsed = 0; // NEW ADDITION
  while(millisElapsed <= Sample_Time)
  {
    millisCurrent = millis();                   //millis() is time since the programm started              
    millisElapsed = millisCurrent - millisLast; //millisElapsed is time since end of last Sample time
    if(digitalRead(micPin) == HIGH)             // HIGH = there is a sound detected (mic output is only binary)
    {
     Volume++;
    }
  }
  
  Height = Volume / 240 * Sensitivity;          // Height is how many of the 25 LEDs will light up (numbers probably need some tweaking)
  millisLast = millisCurrent;
  Serial.println(Volume);
}

This code makes 12 LEDs light up (as wanted for testing purposes), but the mic values are all 1s and 0s.

#include <Adafruit_NeoPixel.h>

#define LED1 17
#define N_PIXELS 25

#define LED2 32

Adafruit_NeoPixel strip1 = Adafruit_NeoPixel(N_PIXELS, LED1, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel strip2 = Adafruit_NeoPixel(N_PIXELS, LED2, NEO_GRB + NEO_KHZ800);

const int micPin = 33;
const int Sample_Time = 10;
unsigned long millisCurrent;
unsigned long millisLast;
unsigned long millisElapsed = 0;

int Volume = 0;

int i = 0;
int Height = 0;
 

double Sensitivity = 1;


void setup() 
{
  Serial.begin(9600);
  while( !Serial) delay(20); 
  pinMode(micPin , INPUT);

}

// while loop takes the binary output from the mic and turns it into a (faked but surprisingly well working) analog output to use for the VU-Meter

void loop() 
{
   Volume = 0;
  millisElapsed = 0; // NEW ADDITION
  while(millisElapsed <= Sample_Time)
  {
    millisCurrent = millis();                                     //millis() is time since the programm started              
    millisElapsed = millisCurrent - millisLast;                   //millisElapsed is time since end of last Sample time
    
    if(digitalRead(micPin) == HIGH)                               // HIGH = there is a sound detected (mic output is only binary)
    {
     Volume++;
    }
  }
  
  Height = Volume / 240 * Sensitivity;                          // Height is how many of the 25 LEDs will light up (numbers probably need some tweaking)
  millisLast = millisCurrent;
  Serial.println(Volume);

  Height = 12;                                                  // just to see if the LEDs work without relying on the mic data
 
  for (i = 0; i < N_PIXELS; i++)                                // rest is just for the LEDs
  {
    if (i >= Height)
    {
      strip1.setPixelColor(i, 0, 0, 0);
    }
    
    else
    {
      strip1.setPixelColor(i, 0, 0, 150);
    }
    strip1.show();
  }

  for (i = 0; i < N_PIXELS; i++)
  {
    if (i >= Height)
    {
      strip2.setPixelColor(i, 0, 0, 0);
    }
    
    else
    {
      strip2.setPixelColor(i, 220, 0, 0);
    }
    strip2.show();
  }
}

I copied the mic code between the two and when I comment the LED-code I get analog readings again. So there might be a timing problem?! I dunno

Exactly. Should be something along the lines of 10ms then in total. I dont really know how long these calculations need tbh. Do you think making the sample time smaller might help?

Zero - it's a global, and will have been initialised to zero by crt0.

no. i think your LED code needs to be broken up and run in the times between sample

Move the strip.show() function calls outside the for-loops. They are being called 25 x too often.

Each call of strip.show() will take around 750us, so 1.5ms total for the 2 strips. But because you are calling them 25x too often, it will take 35~40ms.

first of you should change the serialboaudrate from 9600 to 115200
You are printing each loop

 Serial.println(Volume);

this means in void setup()

instead of

Serial.begin(9600);

write

Serial.begin(115200);

change the baudrate in the serial monitor too

Then your void loop() without the LEDs is

void loop()
{
  Volume = 0;
  millisElapsed = 0; // NEW ADDITION
  while(millisElapsed <= Sample_Time)
  {
    millisCurrent = millis();                   //millis() is time since the programm started              
    millisElapsed = millisCurrent - millisLast; //millisElapsed is time since end of last Sample time
    if(digitalRead(micPin) == HIGH)             // HIGH = there is a sound detected (mic output is only binary)
    {
     Volume++;
    }
  }

without the for-loops for the leds this runs fast
and to keep it running fast you don't use a for-loop you use void-loop itself

a sketchy sketching looks like this


This is the basic idea
each time you take one sample your variable i is incremented by one 
and sets up the next LED in the stripe

void loop()
{
  i++;
  if (i > N_PIXELS) {
    strip1.show();
    i = 0;
  }

  if (i >= Height)
  {
    strip1.setPixelColor(i, 0, 0, 0);
  }
  
  else
  {
    strip1.setPixelColor(i, 0, 0, 150);
  }

  Volume = 0;
  millisElapsed = 0; // NEW ADDITION

  while(millisElapsed <= Sample_Time)
  {
    millisCurrent = millis();                   //millis() is time since the programm started              
    millisElapsed = millisCurrent - millisLast; //millisElapsed is time since end of last Sample time
    if(digitalRead(micPin) == HIGH)             // HIGH = there is a sound detected (mic output is only binary)
    {
     Volume++;
    }
  }

So if your sampling-time is too high the LED-strips might be not responsive enough.

You seem to use some weird kind of analog to digital conversion with a digital IO-pin.
everything woul dbecome much easier if you use a real ADC-IO-pin with the command
analogRead(myAnalog_MIC_Pin)

best regards Stefan

It works now, thank you so much. It is very "flickery" tho. I will incorporate some of the things Stefan said to further improve this, but thank you so much.

I will try that after lunch, thank you very much :slight_smile: . The mic I am using only puts out binary, there is nothing I could use analogRead on. I have an analog mic as well, but I wasnt sure with the wiring (didnt want to damage my ESP32 with the analogf audio signal. I have been told this might cause problems.

I expect that will be because of your strange way of sampling the mic signal. I think what's happening is that digitalRead() is giving random HIGH or LOW, but at low sound levels, LOW will be read more probable and at high sound levels, HIGH will be more probable. But at all sounds levels, there will be a lot of random noise causing flickering.

does your microphone have a digital I2S-interface? or is it a "classical" analog microphone?
You can buy microphone-modules that amplify the microphone-signal to a level that is suitable for microcontrollers
https://www.ebay.de/itm/393179304507?mkevt=1&mkcid=1&mkrid=707-53477-19255-0&campid=5338364437&customid=393179304507_12576&toolid=11000

For using I2S I found this tutorial

best regards Stefan

You could also try a FastLED derivative approach (with peak hold and decay) that as an added benefit does not interfere with ESP32 WIFI (in case you need to use that functionality later). It is using a board that outputs decibels as a voltage. I am using it to display noise in underground stations, but it's easy to modify for a PPM or VU meter.