/*
VU Meter 20+20 Leds
Cidvaldo de Souza
[email protected]
Os Poteciometros Estao simulando Entrada de Audio,
Esquema certo esta Capa do Video.
https://www.youtube.com/results?search_query=vu+meter+stereo+20%2B20leds+esp32
https://wokwi.com/projects/407591494803483649
*/
//Cores Usadas
//=======================================================================//
#define ILI9341_GREYOUT 0x38E7
#define TFT_GREYOUT 0x38E7
#define ILI9341_BROWN 0x9A60 /* 150, 75, 0 */
//=======================================================================//
// Edit TFT_eSPI-master/Processors/TFT_eSPI_ESP32.h comment line 14 //#include "hal/gpio_ll.h" so that it copies
#include <TFT_eSPI.h> // https://github.com/Bodmer/TFT_eSPI
#define min(X, Y) (((X) < (Y)) ? (X) : (Y))
#define max(X, Y) (((X) > (Y)) ? (X) : (Y))
TFT_eSPI tft = TFT_eSPI(240, 320);
int SAMPLESR = 512;
int SAMPLESL = 512;
//=======================================================================//
bool SQL;
bool SQR;
int MStatusoldL;
int MStatusoldR;
int peakholdoldL;
int peakholdoldR;
int peakholdLtimer;
int peakholdRtimer;
uint16_t MStatus;
unsigned long peakholdLmillis;
unsigned long peakholdRmillis;
//=======================================================================//
struct eqBand {
const char *freqname;
uint16_t amplitude;
byte bandWidth;
int peak;
int lastpeak;
uint16_t curval;
uint16_t lastval;
unsigned long lastmeasured;
};
unsigned int sampling_period_us;
double vRealL[1024];
double vRealR[1024];
double vImag[1024];
unsigned long newTime;
bool adcread = false; // use adc raw or analogread
// booleans to manage the display state
bool displayvolume = true;
// volume level
long signalLAvg = 0, signalLMax = 0, signalLMin = 4096;
long signalRAvg = 0, signalRMax = 0, signalRMin = 4096;
bool isSaturatingL = false;
bool isSaturatingR = false;
uint16_t volL = 0;
uint16_t volR = 0;
uint16_t volWidthL = 0;
uint16_t volWidthR = 0;
float volModL = 1;
float volModR = 1;
void captureSoundSample() {
signalLAvg = 0;
signalRAvg = 0;
signalLMax = 0;
signalRMax = 0;
signalLMin = 4096;
signalRMin = 4096;
for (int L = 0; L < SAMPLESL; L++) {
newTime = micros();
if ( adcread ) {
delayMicroseconds(20);
} else {
vRealL[L] = analogRead(A0); // A0 = Pin 36
}
vImag[L] = 0;
if (displayvolume) {
signalLMin = min(signalLMin, vRealL[L]);
signalLMax = max(signalLMax, vRealL[L]);
signalLAvg += vRealL[L];
}
while ((micros() - newTime) < sampling_period_us) {
// do nothing to wait
yield();
}
}
for (int R = 0; R < SAMPLESR; R++) {
newTime = micros();
if ( adcread ) {
delayMicroseconds(20);
} else {
vRealR[R] = analogRead(A3); // A3 = Pin 39
}
vImag[R] = 0;
if (displayvolume) {
signalRMin = min(signalRMin, vRealR[R]);
signalRMax = max(signalRMax, vRealR[R]);
signalRAvg += vRealR[R];
}
while ((micros() - newTime) < sampling_period_us) {
// do nothing to wait
yield();
}
}
}
void renderSpectrometer() {
if (displayvolume) {
signalLAvg /= SAMPLESL;
signalRAvg /= SAMPLESR;
volL = (signalLMax - signalLMin);
volR = (signalRMax - signalRMin);
volWidthL = map(volL, 0, 4096, 1, TFT_WIDTH);
volWidthR = map(volR, 0, 4096, 1, TFT_WIDTH);
if (volModL >= .25) isSaturatingL = true;
else isSaturatingL = false;
if (volModR >= .25) isSaturatingR = true;
else isSaturatingR = false;
//=======================================================================//
int holdL;
if (SQL == false) {} else {
volWidthL = 0;
MStatusoldL = 1;
}
if (volWidthL != MStatusoldL || volWidthL < 10)
{
if (volWidthL > 0) {
tft.fillRect(21, 143, 12, 4, ILI9341_GREEN);
holdL = 21;
} else {
tft.fillRect(21, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 10) {
tft.fillRect(35, 143, 12, 4, ILI9341_GREEN);
holdL = 35;
} else {
tft.fillRect(35, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 20) {
tft.fillRect(49, 143, 12, 4, ILI9341_GREEN);
holdL = 49;
} else {
tft.fillRect(49, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 30) {
tft.fillRect(63, 143, 12, 4, ILI9341_GREEN);
holdL = 63;
} else {
tft.fillRect(63, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 40) {
tft.fillRect(77, 143, 12, 4, ILI9341_GREEN);
holdL = 77;
} else {
tft.fillRect(77, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 50) {
tft.fillRect(91, 143, 12, 4, ILI9341_GREEN);
holdL = 91;
} else {
tft.fillRect(91, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 60) {
tft.fillRect(105, 143, 12, 4, ILI9341_GREEN);
holdL = 105;
} else {
tft.fillRect(105, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 70) {
tft.fillRect(119, 143, 12, 4, ILI9341_GREEN);
holdL = 119;
} else {
tft.fillRect(119, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 80) {
tft.fillRect(133, 143, 12, 4, ILI9341_GREEN);
holdL = 133;
} else {
tft.fillRect(133, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 90) {
tft.fillRect(147, 143, 12, 4, ILI9341_GREEN);
holdL = 147;
} else {
tft.fillRect(147, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 100) {
tft.fillRect(161, 143, 12, 4, ILI9341_GREEN);
holdL = 161;
} else {
tft.fillRect(161, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 110) {
tft.fillRect(175, 143, 12, 4, ILI9341_GREEN);
holdL = 175;
} else {
tft.fillRect(175, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 120) {
tft.fillRect(189, 143, 12, 4, ILI9341_GREEN);
holdL = 189;
} else {
tft.fillRect(189, 143, 12, 4, ILI9341_GREYOUT);
}//
if (volWidthL > 130) {
tft.fillRect(203, 143, 12, 4, ILI9341_GREEN);
holdL = 203;
} else {
tft.fillRect(203, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 140) {
tft.fillRect(217, 143, 12, 4, ILI9341_GREEN);
holdL = 217;
} else {
tft.fillRect(217, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 150) {
tft.fillRect(231, 143, 12, 4, ILI9341_YELLOW);
holdL = 231;
} else {
tft.fillRect(231, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 160) {
tft.fillRect(245, 143, 12, 4, ILI9341_ORANGE);
holdL = 245;
} else {
tft.fillRect(245, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 170) {
tft.fillRect(259, 143, 12, 4, ILI9341_ORANGE);
holdL = 259;
} else {
tft.fillRect(259, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 180) {
tft.fillRect(273, 143, 12, 4, ILI9341_RED);
holdL = 273;
} else {
tft.fillRect(273, 143, 12, 4, ILI9341_GREYOUT);
}
if (volWidthL > 190) {
tft.fillRect(287, 143, 12, 4, ILI9341_RED);
holdL = 287;
} else {
tft.fillRect(287, 143, 12, 4, ILI9341_GREYOUT);
}
if (peakholdoldL < holdL) {
peakholdoldL = holdL;
}
if (peakholdoldL < 231) {
tft.fillRect(peakholdoldL, 143, 12, 4, ILI9341_GREEN);
} else
if (peakholdoldL == 231) {
tft.fillRect(peakholdoldL, 143, 12, 4, ILI9341_YELLOW);
} else
if (peakholdoldL == 245) {
tft.fillRect(peakholdoldL, 143, 12, 4, ILI9341_ORANGE);
} else
if (peakholdoldL == 259) {
tft.fillRect(peakholdoldL, 143, 12, 4, ILI9341_ORANGE);
} else {
tft.fillRect(peakholdoldL, 143, 12, 4, ILI9341_RED);
}
if (peakholdLmillis > peakholdLtimer + 3000) {
peakholdLtimer += 3000;
peakholdoldL = holdL;
}
peakholdLmillis = millis();
MStatusoldL = volWidthL;
}
//=======================================================================//
int holdR;
if (SQR == false) {} else {
volWidthR = 0;
MStatusoldR = 1;
}
if (volWidthR != MStatusoldR || volWidthR < 10)
{
if (volWidthR > 0) {
tft.fillRect(21, 173, 12, 4, ILI9341_GREEN);
holdR = 21;
} else {
tft.fillRect(21, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 10) {
tft.fillRect(35, 173, 12, 4, ILI9341_GREEN);
holdR = 35;
} else {
tft.fillRect(35, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 20) {
tft.fillRect(49, 173, 12, 4, ILI9341_GREEN);
holdR = 49;
} else {
tft.fillRect(49, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 30) {
tft.fillRect(63, 173, 12, 4, ILI9341_GREEN);
holdR = 63;
} else {
tft.fillRect(63, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 40) {
tft.fillRect(77, 173, 12, 4, ILI9341_GREEN);
holdR = 77;
} else {
tft.fillRect(77, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 50) {
tft.fillRect(91, 173, 12, 4, ILI9341_GREEN);
holdR = 91;
} else {
tft.fillRect(91, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 60) {
tft.fillRect(105, 173, 12, 4, ILI9341_GREEN);
holdR = 105;
} else {
tft.fillRect(105, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 70) {
tft.fillRect(119, 173, 12, 4, ILI9341_GREEN);
holdR = 119;
} else {
tft.fillRect(119, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 80) {
tft.fillRect(133, 173, 12, 4, ILI9341_GREEN);
holdR = 133;
} else {
tft.fillRect(133, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 90) {
tft.fillRect(147, 173, 12, 4, ILI9341_GREEN);
holdR = 147;
} else {
tft.fillRect(147, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 100) {
tft.fillRect(161, 173, 12, 4, ILI9341_GREEN);
holdR = 161;
} else {
tft.fillRect(161, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 110) {
tft.fillRect(175, 173, 12, 4, ILI9341_GREEN);
holdR = 175;
} else {
tft.fillRect(175, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 120) {
tft.fillRect(189, 173, 12, 4, ILI9341_GREEN);
holdR = 189;
} else {
tft.fillRect(189, 173, 12, 4, ILI9341_GREYOUT);
}//
if (volWidthR > 130) {
tft.fillRect(203, 173, 12, 4, ILI9341_GREEN);
holdR = 203;
} else {
tft.fillRect(203, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 140) {
tft.fillRect(217, 173, 12, 4, ILI9341_GREEN);
holdR = 217;
} else {
tft.fillRect(217, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 150) {
tft.fillRect(231, 173, 12, 4, ILI9341_YELLOW);
holdR = 231;
} else {
tft.fillRect(231, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 160) {
tft.fillRect(245, 173, 12, 4, ILI9341_ORANGE);
holdR = 245;
} else {
tft.fillRect(245, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 170) {
tft.fillRect(259, 173, 12, 4, ILI9341_ORANGE);
holdR = 259;
} else {
tft.fillRect(259, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 180) {
tft.fillRect(273, 173, 12, 4, ILI9341_RED);
holdR = 273;
} else {
tft.fillRect(273, 173, 12, 4, ILI9341_GREYOUT);
}
if (volWidthR > 190) {
tft.fillRect(287, 173, 12, 4, ILI9341_RED);
holdR = 287;
} else {
tft.fillRect(287, 173, 12, 4, ILI9341_GREYOUT);
}
if (peakholdoldR < holdR) {
peakholdoldR = holdR;
}
if (peakholdoldR < 231) {
tft.fillRect(peakholdoldR, 173, 12, 4, ILI9341_GREEN);
} else
if (peakholdoldR == 231) {
tft.fillRect(peakholdoldR, 173, 12, 4, ILI9341_YELLOW);
} else
if (peakholdoldR == 245) {
tft.fillRect(peakholdoldR, 173, 12, 4, ILI9341_ORANGE);
} else
if (peakholdoldR == 255) {
tft.fillRect(peakholdoldR, 173, 12, 4, ILI9341_ORANGE);
} else {
tft.fillRect(peakholdoldR, 173, 12, 4, ILI9341_RED);
}
if (peakholdRmillis > peakholdRtimer + 3000) {
peakholdRtimer += 3000;
peakholdoldR = holdR;
}
peakholdRmillis = millis();
MStatusoldR = volWidthR;
tft.drawRect(0, 120, 320, 80, TFT_DARKGREY); // 2 quadrado 39x320 de um Bite
tft.drawRect(17, 139, 286, 12, TFT_DARKGREY); // 2 quadrado 39x320 de um Bite
tft.drawRect(17, 169, 286, 12, TFT_DARKGREY); // 2 quadrado 39x320 de um Bite
tft.setTextColor(TFT_WHITE);
tft.drawCentreString("VU Meter", 160, 127, 1);
tft.drawString("L", 6, 138, 2);
tft.drawString("R", 6, 168, 2);
tft.drawString("dB -20", 21, 155, 1);
tft.drawString("-15", 76, 155, 1);
tft.drawString("-10", 114, 155, 1);
tft.drawString("-6", 152, 155, 1);
tft.drawString("-3", 190, 155, 1);
tft.drawString("0", 228, 155, 1);
tft.drawString("dB -20", 21, 185, 1);
tft.drawString("-15", 76, 185, 1);
tft.drawString("-10", 114, 185, 1);
tft.drawString("-6", 152, 185, 1);
tft.drawString("-3", 190, 185, 1);
tft.drawString("0", 228, 185, 1);
tft.setTextColor(TFT_RED);
tft.drawString("+2", 256, 155, 1);
tft.drawString("+4", 288, 155, 1);
tft.drawString("+2", 256, 185, 1);
tft.drawString("+4", 288, 185, 1);
}
//=======================================================================//
}
}
void setup() {
Serial.begin(115200);
tft.begin();
tft.setRotation( 3 );
tft.fillScreen(ILI9341_BLACK);
tft.setTextColor(ILI9341_WHITE);
}
void loop() {
captureSoundSample();
renderSpectrometer();
}