ThomAce
Published © GPL3+

KTAudio VU Meter

Simple, easy to build 2 x 14 bars stereo VU meter for representing audio level.

BeginnerProtip16,187
KTAudio VU Meter

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Resistor 100k ohm
Resistor 100k ohm
×4
Through Hole Resistor, 22 kohm
Through Hole Resistor, 22 kohm
×1
Capacitor 100 nF
Capacitor 100 nF
×2
Alphanumeric LCD, 16 x 2
Alphanumeric LCD, 16 x 2
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Wiring diagram

Code

Main.cpp.ino

Arduino
/*
  Arduino based VU meter by KTAudio.
  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 
*/

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

String main_version = "1.0";
int left, right;                        //Variables to store and calculate the channel levels             
const int numReadings = 5;              //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(11, 10, 7, 6, 5, 4);  //lcd configuration

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

  //Showing loading  message and loading bar
  
  String KTAudio = "KTaudio project";  
  
  for (int i = 0; i <= 16; i++)
  {    
    lcd.setCursor(0, 0); 
    lcd.print(KTAudio.substring(0, i));
    delay(50);
  }  

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

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

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

    delay(50);
  }  
    
  delay(500);
  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(15, 0);       //closing tag / end mark index 1
  lcd.write(5);               //closing tag / end mark
  lcd.setCursor(15, 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 > 14)
  {
    volR = 14;
  }

  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 > 14)
  {
    volL = 14;
  }

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

ThomAce

ThomAce

1 project • 7 followers

Comments