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 .
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.
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"
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?
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)
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 . 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.
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.