Mirko Pavleski
Published © GPL3+

DIY Big Arduino VU Meter on 40x2 LCD Display

The advantages of this device are that it is easy to build, and there is a fairly accurate representation of sound level.

BeginnerFull instructions provided5,889
DIY Big Arduino VU Meter on 40x2 LCD Display

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
40x2 LCD display with HD44780 chip
×1
Rotary potentiometer (generic)
Rotary potentiometer (generic)
×1
Resistor 100k ohm
Resistor 100k ohm
×1
Resistor 10k ohm
Resistor 10k ohm
×2
Resistor 22.1k ohm
Resistor 22.1k ohm
×1
Capacitor 100 nF
Capacitor 100 nF
×2

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic

Code

Code

C/C++
/*
  Arduino based VU meter by mircemk.
  Developed by ThomAce (Tamas Kamocsai) based on siemenwauters, theredstonelabz and michiel H's VU meter.

  GNU GPL License v3

  Developer: ThomAce (Tamas Kamocsai)
  Mail: thomacepcg@gmail.com
  Version: 1.0
  Last modification date: 2019.09.24

  Original version:
  https://www.instructables.com/id/ARDUINO-VU-METER/

  Original description:
  VU meter by siemenwauters, theredstonelabz and michiel H don't forget to like and subscribe to support my work. tnx

  Modified by "mircemk" date: 2021.07.13 
*/

#include <LiquidCrystal.h>

byte Bar[8] = {
        B11111,
        B00000,
        B00000,
        B11111,
        B11111,
        B00000,
        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,
        B00011,
        B00011,
        B00011,
        B00011,
        B00000,
        B11111
};

String main_version = "1.1";
int left, right;                        //Variables to store and calculate the channel levels             
const int numReadings = 1;              //Refresh rate. Lower value = higher rate. 5 is the defaul
int indexL = 0;                         //Actual channel index
int totalL = 0;                         //Total channel data
int maxL = 0;                           //Maximal level
int indexR = 0;                         
int totalR = 0;                         
int maxR = 0;
int inputPinL = A1;                     //Input pin Analog 1 for LEFT channel
int inputPinR = A0;                     //Input pin Analog 0 for RIGHT channel
int volL = 0;
int volR = 0;
int rightAvg = 0;
int leftAvg = 0;
long peakHoldTime = 1500;               //peak hold time in miliseconds
long peakHold = 0;
int rightPeak = 0;
int leftPeak = 0;
long decayTime = 0;
long actualMillis = 0;

LiquidCrystal lcd(12, 11, 5, 4, 3, 2);  //lcd configuration

void setup()
{  
  lcd.begin(40, 2); //Setting up LCD. 40 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);

  //Showing loading  message and loading bar
  
  String mircemk = "* mircemk *";  
  
  for (int i = 0; i <= 40; i++)
  {    
    lcd.setCursor(15, 0); 
    lcd.print(mircemk.substring(0, i));
    delay(50);
  }  

  mircemk = "              VU meter " + main_version;
  
  for (int i = 0; i <= mircemk.length(); i++)
  {    
    lcd.setCursor(0, 1);
    lcd.print(mircemk.substring(0, i));
    delay(50);
  }

  delay(500);
  
  lcd.clear();
  lcd.setCursor(15, 0); 
  lcd.print("Loading...");

  for (int i = 0; i < 40; i++)
  {
    lcd.setCursor(i, 1); 
    lcd.write(4);
  }
  
  for (int i = 0; i < 40; i++)
  {
    lcd.setCursor(i, 1); 
    lcd.write(1);

    delay(50);
  }  
    
  delay(5);
  lcd.clear();

  decayTime = millis();
}

void loop()
{    
  actualMillis = millis();
  
  lcd.setCursor(0, 0);        //L channel index
  lcd.write(2);               //L symbol 
  lcd.setCursor(0, 1);        //R channel index
  lcd.write(3);               //R symbol
  lcd.setCursor(39, 0);       //closing tag / end mark index 1
  lcd.write(5);               //closing tag / end mark
  lcd.setCursor(39, 1);       //closing tag / end mark index 2
  lcd.write(5);               //closing tag / end mark
   
  totalL = analogRead(inputPinL) / 4 - 128; //reducing the detected hum and noise
    
  if(totalL > maxL)
  {
    maxL = totalL;
  }
   
  indexL++;
   
  if (indexL >= numReadings)
  {             
    indexL = 0;                         
    left = maxL;
    maxL = 0;
  }   
     
  totalR = analogRead(inputPinR) / 4 - 128; //reducing the detected hum and noise
    
  if(totalR > maxR)
  {
    maxR = totalR;
  }
   
  indexR++;
   
  if (indexR >= numReadings)
  {             
    indexR = 0;                         
    right = maxR;
    maxR = 0;
  } 
    
  volR = right / 3;
    
  if(volR > 38)
  {
    volR = 38;
  }

  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(volR, rightPeak, 1);

  volL = left / 3;
   
  if(volL > 38)
  {
    volL = 38;
  }

  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;
  }

  drawBar(volL, leftPeak, 0);

  if (decayTime < actualMillis)
    decayTime = (millis() + 50);

  if (peakHold < actualMillis)
  {
    peakHold = (millis() + peakHoldTime);
    rightPeak = -1;
    leftPeak = -1;
  }
}

void drawBar(int data, int peakData, int row)
{
  //If the previous peak data is 1 or 0, then not taking care of the value.
  if (peakData < 2)
  {
    peakData = -1;
  }

  //First char (idx 0) = R or L
  //Last (16th) char (idx 15) is the closing mark of the bar.
  //We have 14 chars to write.
  for (int col = 1; col < 39; col++)
  {
    lcd.setCursor(col, row);

    if (col < data)
    {
      lcd.write(1); //write bar element
    }
    else if (peakData == col)
    {
      lcd.write(6); //write the peak marker
    }
    else
    {
      lcd.write(4); //write "empty" 
    }
  }
}

Credits

Mirko Pavleski

Mirko Pavleski

148 projects • 1276 followers

Comments