// https://forum.arduino.cc/t/vu-meter-lcd-20x4/1403043
#include <LiquidCrystal.h>
byte Bar[8] = {B11111, B00000, B11111, B11111, B11111, B11111, B00000, B11111};
byte L[8] = {B00111, B01000, B10100, B10100, B10100, B10111, B01000, B00111};
byte R[8] = {B00111, B01000, B10110, B10101, B10110, B10101, B01000, B00111};
byte EndMark[8] = {B10000, B01000, B00100, B00100, B00100, B00100, B01000, B10000};
byte EmptyBar[8] = {B11111, B00000, B00000, B00000, B00000, B00000, B00000, B11111};
byte peakHoldChar[8] = {B11111, B00000, B01110, B01110, B01110, B01110, B00000, B11111};
byte Smile1[8] = {B00000, B00000, B00000, B00000, B00000, B00000, B10010, B00000};
byte Smile2[8] = {B10010, B01100, B00000, B00000, B00000, B00000, B00000, B00000};
const int numReadings = 5; //Refresh rate. Lower value = higher rate. 5 is the defaul
const int screenHeight = 4; // 4 rows
const int screenWidth = 20; // 20 columns
int left; //Variables to store and calculate the channel levels
int indexL = 0; //Actual channel index
int totalL = 0; //Total channel data
int maxL = 0; //Maximal level
int inputPinL = A0; //Input pin for LEFT channel
int volL = 0;
int leftAvg = 0;
int leftPeak = 0;
int right; //Variables to store and calculate the channel levels
int indexR = 0; //Actual channel index
int totalR = 0; //Total channel data
int maxR = 0; //Maximal level
int inputPinR = A1; //Input pin for RIGHT channel
int volR = 0;
int rightAvg = 0;
int rightPeak = 0;
long peakHoldTime = 1000; //peak hold time in miliseconds
long peakHold = 0;
long decayTime = 0;
long actualMillis = 0;
LiquidCrystal lcd(12, 10, 5, 4, 3, 2); //lcd configuration
void setup() {
lcd.begin(20, 4); //Setting up LCD. 16 chars and 2 rows
lcd.createChar(1, Bar);
lcd.createChar(2, L);
lcd.createChar(3, R);
lcd.createChar(4, EmptyBar);
lcd.createChar(5, EndMark);
lcd.createChar(6, peakHoldChar);
lcd.createChar(7, Smile1);
lcd.createChar(8, Smile2);
drawLeftChannels();
decayTime = millis();
}
void drawLeftChannels() {
lcd.setCursor(0, 0); //L channel index
lcd.write(2); //L symbol
lcd.setCursor(screenWidth / 2, screenHeight / 2 - 1);
lcd.write(7);
lcd.setCursor(screenWidth / 2, screenHeight / 2);
lcd.write(8);
lcd.setCursor(0, screenHeight - 1); //R channel index
lcd.write(3); //R symbol
lcd.setCursor(screenWidth - 1, 0); //closing tag / end mark index 1
lcd.write(5); //closing tag / end mark
lcd.setCursor(screenWidth - 1, screenHeight - 1); //closing tag / end mark index 1
lcd.write(5); //closing tag / end mark
}
void loop() {
actualMillis = millis();
// totalL = map(analogRead(inputPinL), 0, 1023, 1, screenWidth - 1);
// totalR = map(analogRead(inputPinR), 0, 1023, 1, screenWidth - 1);
totalL = analogRead(inputPinL);
totalR = analogRead(inputPinR);
//==========
if (totalL > maxL) {
maxL = totalL;
} indexL++;
if (totalR > maxR) {
maxR = totalR;
} indexR++;
//==========
if (indexL >= numReadings) {
indexL = 0;
left = maxL;
maxL = 0;
}
if (indexR >= numReadings) {
indexR = 0;
right = maxR;
maxR = 0;
}
//==========
volL = left / 2;
if (volL > 18) {
volL = 18;
}
volR = right / 2;
if (volR > 18) {
volR = 18;
}
//==========
if (volL < (leftAvg - 2)) {
if (decayTime < actualMillis)
leftAvg--;
volL = leftAvg;
} else if (volL > (leftAvg + 2)) {
volL = (leftAvg + 2);
leftAvg = volL;
} else {
leftAvg = volL;
}
if (volL > leftPeak) {
leftPeak = volL;
}
if (volR < (rightAvg - 2)) {
if (decayTime < actualMillis)
rightAvg--;
volR = rightAvg;
} else if (volR > (rightAvg + 2)) {
volR = (rightAvg + 2);
rightAvg = volR;
} else {
rightAvg = volR;
}
if (volR > rightPeak) {
rightPeak = volR;
}
//==========
drawBar(volL, leftPeak, 0);
drawBar(volR, rightPeak, screenHeight - 1);
//==========
if (decayTime < actualMillis)
decayTime = (millis() + 50);
if (peakHold < actualMillis) {
peakHold = (millis() + peakHoldTime);
leftPeak -= 1;
// leftPeak = -1;
rightPeak -= 1;
}
}
void drawBar(int data, int peakData, int row) {
if (peakData < 2) {
peakData = -1;
}
for (int col = 1; col < 19; col++) {
lcd.setCursor(col, row);
if (col < data) {
lcd.write(1);
} else if (peakData == col) {
lcd.write(6); //write the peak marker
} else {
lcd.write(4); //write "empty"
}
}
}