Ultimate Stereo VU Meter with Peak and Hold using Arduino and Serial Shift register

 


Hey friend in this blog post I am making an Ultimate and Smooth VU Meter using Arduino and 74HC595 ICs (Integrated Circuit) or Serial Shift Register IC. It is a Stereo VU Meter which work with Two Analog Inputs for both Stereo Channels (Left and Right) and respond with the Analog Values by proper Map function to Decibels values on Arduino. It has 40 LEDs which respond to Analog Values corresponding to each LEDs. I saw a lots of VU Meter and try to make one but not every VU Meter is efficient and smooth and sometime blink and Haze effect, so I decided to make one by own which work like Studio Equipment type and also visualize like real Studio Audio Equipment’s like Amplifier, Audio Mixing Console, Microphone Tuner, Audio Interface, MIDI Controller and Many More (I also get the Idea from here and very curious for it, I like Audio DIY Stuff so Much so also stay tune with me and Subscribe my Channel). It enchase with the Peak and Hold feature, It show real time peak values for 1 second in each of the LEDs and slowly (1 sec time dependent) goes down to starting LED.

You can also enjoy with your Home Theatre or Home DJ System but please don't turn up the volume too much in excitement; otherwise, it could break the glass in other people's homes (If you have DJ in your home).  So let’s make it one.

 

My YouTube Video Here- Stereo VU Meter with Peak and Hold , You can watch here and please Subscribe my channel also comments if you have any problem.

Part Needed On this project

 


1x Arduino Microcontroller (Nano or Uno, etc. I recommend you to add ATMEGA328P-PU IC which is same IC use in Arduino common boards, it save lots of space in Circuit Board and easy to program).

8x 10 LED Segment bar Graph LED (Choose any of colours I use 2x Green means 20 LED for bottom, 1x Yellow means 10 LED for middle part and after 1x Red means 10 Red LED for top, You can choose what colour you want to choose).

10x 74HC595 IC Serial Shift Register (Always Solder with IC base for future troubleshooting or upgrades)

40x 220 Ohms Registers

1x PCB Board (Perf Board)

3x 2 Pin Connectors

1x 104J Capacitor

1x 1000uF or 470uF Capacitor 25V (Choose with your voltage fluctuations need)

Some Wires

Soldering Station and Tools

 

How it’s Work

A VU (Volume Unit) meter is an audio measuring device that displays the signal level in audio equipment. It is typically used to indicate the loudness of audio signals, providing a visual representation of audio levels. The VU meter measures average levels, making it suitable for monitoring consistent audio signals. It helps audio engineers ensure audio signals remain within an optimal range, preventing distortion and maintaining audio quality.

VU meters consist of several components:

  1. Input Stage: This receives the audio signal from the source.
  2. Rectifier and Filter: The audio signal is rectified to convert AC signals into a DC signal that can be measured. A filter smooths the signal to eliminate rapid fluctuations.
  3. Meter Movement: The processed signal drives a needle or LED display, indicating the signal level.

How This Arduino-Based VU Meter Works



This project creates a stereo VU meter using an Arduino and 74HC595 shift registers to drive an 80-LED display (40 LEDs for each channel). Here's how it works:

1.    Audio Input: The audio signal is taken directly from the audio source (left and right channels). Each channel's audio signal is fed into separate analog pins (A0 for the left and A1 for the right) on the Arduino.

2.    Signal Conditioning: To stabilize the signal and reduce noise, each audio input is connected to a filter circuit with both analog inputs.

3.    Analog Reading: The Arduino reads the analog input values from A0 and A1. These values correspond to the amplitude of the audio signals.

4.    Peak Detection: The code includes a peak detection mechanism. It captures the highest value in a given period, emulating the "peak hold" feature found in traditional VU meters.

5.    Scaling and Mapping: The analog values are scaled and mapped to a range suitable for driving the LED display. This ensures that the LED levels correspond accurately to the audio signal levels.

6.    LED Display: The processed values are used to determine how many LEDs should light up. The Arduino uses the 74HC595 shift registers to control the LEDs. Each shift register controls 8 LEDs, allowing for efficient control of the 40-LED display. The LEDs light up in sequence, providing a visual representation of the audio levels.

7.    Peak Hold: The peak hold feature keeps the highest LED lit for a short duration, giving a visual indication of the highest audio level reached.

This Arduino-based VU meter project provides a visually appealing and functional way to monitor audio levels, enhancing the audio experience by providing real-time feedback on the audio signal's amplitude.

 

Circuit Diagram

This is easy to build circuit diagram but many people find it difficult just by looking but actually not that is why also divided two parts of circuit diagram: - one is for the main VU Meter driver circuit and with LED Executions and the second part is the Adjustable Audio Filter Circuit which reduce unwanted noise.

The Part first is here:-

 


And the second part is here:-

 


Always remember that make sure the voltage level from input of Audio amplifier or etc sound equipment, due to this it can burn your Arduino or continuously reset which causes damage and add proper Capacitor with your Input specifications and Arduino 5V power line. After complete the circuit and double check everything, you can now add cords or wires for power and Inputs.

Setup and Code

Now after all these steps you need to open Arduino IDE or if you have not so download from here www.Arduino.cc then install it.

Copy the whole code from here and paste it to your Arduino IDE and Select the Com Port and Board.

/*
MIT License

Copyright (c) 2024 Akash Sharma

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// OLED display parameters
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Heart symbol bitmap (16x16 pixels)
const unsigned char heartBitmap[] PROGMEM = {
  0b00001100, 0b00110000,
  0b00011110, 0b01111000,
  0b00111111, 0b11111100,
  0b01111111, 0b11111110,
  0b01111111, 0b11111110,
  0b11111111, 0b11111111,
  0b11111111, 0b11111111,
  0b11111111, 0b11111111,
  0b01111111, 0b11111110,
  0b01111111, 0b11111110,
  0b00111111, 0b11111100,
  0b00011111, 0b11111000,
  0b00001111, 0b11110000,
  0b00000111, 0b11100000,
  0b00000011, 0b11000000,
  0b00000001, 0b10000000
};

const int latchPinLeft = 9;  // Pin connected to ST_CP of 74HC595 for left channel
const int clockPinLeft = 8;  // Pin connected to SH_CP of 74HC595 for left channel
const int dataPinLeft = 7;   // Pin connected to DS of 74HC595 for left channel

const int latchPinRight = 4;  // Pin connected to ST_CP of 74HC595 for right channel
const int clockPinRight = 3;  // Pin connected to SH_CP of 74HC595 for right channel
const int dataPinRight = 2;   // Pin connected to DS of 74HC595 for right channel

const int analogPinLeft = A0;  // Pin connected to the audio input for left channel
const int analogPinRight = A1; // Pin connected to the audio input for right channel

const size_t nb_LEDs = 40;
const float decayRate = 0.95; // Decay rate for peak detection
float peakValueLeft = 0; // Variable to store the peak value for left channel
float peakValueRight = 0; // Variable to store the peak value for right channel

int peakLevelLeft = 0;
unsigned long peakHoldTimeLeft = 0;
const unsigned long peakHoldDuration = 500; // Duration to hold the peak in milliseconds

int peakLevelRight = 0;
unsigned long peakHoldTimeRight = 0;

void setup() {
  pinMode(latchPinLeft, OUTPUT);
  pinMode(clockPinLeft, OUTPUT);
  pinMode(dataPinLeft, OUTPUT);
  pinMode(analogPinLeft, INPUT);

  pinMode(latchPinRight, OUTPUT);
  pinMode(clockPinRight, OUTPUT);
  pinMode(dataPinRight, OUTPUT);
  pinMode(analogPinRight, INPUT);

  // Initialize OLED display
  if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    Serial.println(F("SSD1306 allocation failed"));
    for (;;);
  }
  display.clearDisplay();
  display.display();
}

void loop() {
  processChannel(analogPinLeft, latchPinLeft, clockPinLeft, dataPinLeft, peakValueLeft, peakLevelLeft, peakHoldTimeLeft, "L");
  processChannel(analogPinRight, latchPinRight, clockPinRight, dataPinRight, peakValueRight, peakLevelRight, peakHoldTimeRight, "R");

  updateOLED();

  delay(20); // Adjust the delay for smoother or faster response
}

void processChannel(int analogPin, int latchPin, int clockPin, int dataPin, float &peakValue, int &peakLevel, unsigned long &peakHoldTime, const char* channel) {
  // Read the analog input
  float rawValue = analogRead(analogPin);

  // Apply peak detection
  if (rawValue > peakValue) {
    peakValue = rawValue;
  } else {
    peakValue *= decayRate;
  }

  // Scale the peak value to ensure it can reach full peak
  float scaledValue = peakValue * 2.0; // Increase sensitivity

  // Clip the scaled value to avoid exceeding the maximum range
  scaledValue = constrain(scaledValue, 0, 1023);

  // Map the scaled value to the number of LEDs
  int ledLevel = map(scaledValue, 0, 1023, 0, nb_LEDs);
  ledLevel = constrain(ledLevel, 0, nb_LEDs); // Ensure the value stays within 0 to nb_LEDs

  // Create the bit pattern for the LEDs, reversed direction
  uint64_t ledPattern = 0;
  for (int i = 0; i < ledLevel; i++) {
    ledPattern |= ((uint64_t)1) << (nb_LEDs - 1 - i);  // Reversing the bit order
  }

  // Update peak level
  if (ledLevel > peakLevel) {
    peakLevel = ledLevel;
    peakHoldTime = millis(); // Reset peak hold timer
  } else if (millis() - peakHoldTime > peakHoldDuration) {
    peakLevel = max(0, peakLevel - 1); // Gradually decrease peak level
    peakHoldTime = millis(); // Reset peak hold timer
  }

  // Add peak LED to the bit pattern
  if (peakLevel < nb_LEDs) {
    ledPattern |= ((uint64_t)1) << (nb_LED);  // Adjust for reversed bit order
  }

  // Output the bit pattern to the shift registers
  digitalWrite(latchPin, LOW);
  shiftOut64(dataPin, clockPin, MSBFIRST, ~ledPattern);
  digitalWrite(latchPin, HIGH);

  // Store values for display update
  storeDisplayValues(channel, rawValue, ledLevel, peakLevel);
}

// Template function for shifting out 64-bit data
void shiftOut64(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, uint64_t val) {
  if (bitOrder == LSBFIRST) {
    for (int i = 0; i < 64; i++) {
      digitalWrite(dataPin, !!(val &));
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
    }
  } else {
    for (int i = 63; i >= 0; i--) {
      digitalWrite(dataPin, !!(val &));
      digitalWrite(clockPin, HIGH);
      digitalWrite(clockPin, LOW);
    }
  }
}

struct DisplayValues {
  const char* channel;
  float analogValue;
  int ledLevel;
  int peakLevel;
};

DisplayValues leftChannelValues;
DisplayValues rightChannelValues;

void storeDisplayValues(const char* channel, float analogValue, int ledLevel, int peakLevel) {
  if (strcmp(channel, "L") == 0) {
    leftChannelValues = {channel, analogValue, ledLevel, peakLevel};
  } else if (strcmp(channel, "R") == 0) {
    rightChannelValues = {channel, analogValue, ledLevel, peakLevel};
  }
}

void updateOLED() {
  static unsigned long lastUpdate = 0;
  const unsigned long updateInterval = 200; // Update interval in milliseconds

  if (millis() - lastUpdate >= updateInterval) {
    lastUpdate = millis();

    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);

    display.setCursor(0, 0);
    display.print("Left Channel");

    display.setCursor(0, 10);
    display.print("VU Level: ");
    display.print(leftChannelValues.ledLevel);

    display.setCursor(0, 20);
    display.print("Peak Level: ");
    display.print(leftChannelValues.peakLevel);

    display.setCursor(0, 30);
    display.print("Right Channel");

    display.setCursor(0, 40);
    display.print("VU Level: ");
    display.print(rightChannelValues.ledLevel);

    display.setCursor(0, 50);
    display.print("Peak Level: ");
    display.print(rightChannelValues.peakLevel);

    // Draw the heart symbol based on the left channel analog value
    if (leftChannelValues.analogValue > 150) { // Adjust the threshold as needed
      display.drawBitmap(100, 0, heartBitmap, 16, 16, SSD1306_WHITE);
    }

    if (rightChannelValues.analogValue > 150) { // Adjust the threshold as needed
      display.drawBitmap(100, 40, heartBitmap, 16, 16, SSD1306_WHITE);
    }

    display.display();
  }
}

Enjoy Music

Now you can rock and shock everyone! If you have a big amplifier (e.g., 50W, 100W, 150W, 2000W, and so on), make sure to always check the input voltage level. Adjust the input variable for precise tuning of the LED levels to sync perfectly with your audio or music.

Comments

  1. Where is arduino code?

    ReplyDelete
  2. Nice project! Can you please share the arduino sketch? Thanks!

    ReplyDelete
  3. Interesting diy project but without the arduino code, it does not work! Where can I find it sir?

    ReplyDelete

Post a Comment