XLR Microphone VU Meter

Hello,

I am trying to make a VU meter that gets the level from a condenser microphone (internal battery, no +48v needed).

I know how to leds part, I just can't workout how to read the volume from the mic.

Many thanks for your help in advance.

Use one of these circuits Envelope detector - Wikipedia between the microphone’s amplified output and the Arduino’s analogue input.

You’ll need an envelope follower. This follows the peaks of the incoming signal:

The raw audio signal from the microphone is AC: if you would connect it to the Arduino directly, it could damage the inputs (because it goes negative half of the time), and your VU meter would flicker.

Here’s a simple envelope detector circuit:

It’s very similar to a normal inverting amplifier, but the diode at the output prevents current flowing into the op-amp. When the output of the op-amp is higher than the voltage at the output of the circuit, C2 will be charged. When the output of the op-amp is lower (it will go to the absolute minimum voltage that the op-amp can output, because of the negative feedback), C2 can’t discharge into the output of the op-amp because of the diode, so it will keep most of its charge, only slowly discharging through R5 (and R2 and R1). The values of C2 and R5 determine the decay of the envelope follower, and how fast the VU meter will drop when going from full volume to zero.

R3 and R4 are a voltage divider that biases the non-inverting input of the op-amp. This means that the virtual ground of the amplifier will be around 2.5V above the Arduino’s reference. It basically adds 2.5V to the AC input signal, so that all parts of the waveform are above 0.

R1 and R2 determine the “gain” of the amplifier. You can use a trimpot to make it variable if you want to.
If the gain isn’t high enough, you could change the values of R1 and R2, or add a preamp stage. (Just an op-amp to create another inverting amplifier.) If you use an NE5532 for example, you get 2 op-amps in a single 8-pin DIP package, so you can use both op-amps.

Because the op-amp’s output can’t go higher or lower than it’s power lines (5V and 0V), the input of the Arduino can’t be damaged if the input voltage goes negative or higher than 5V.

Here’s the code I used for my VU meters. I used shift registers (74HC595) to drive the LEDs, in order to save pins.

const int latchPin = 10;  // Pin connected to ST_CP of 74HC595
const int clockPin = 13;  // Pin connected to SH_CP of 74HC595
const int dataPin = 12;   // Pin connected to DS of 74HC595

const size_t nb_LEDs = 15;

const float zeroOffset = 549;  // biasing voltage (should be around Vcc / 2 ≃ 2.5V ≃ 512)
const float _0dB = 700;        // maximum value (clipping), depends on gain and equipment used

const float dB_levels[nb_LEDs] = { -23, -13, -10, -8, -6, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 };


void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  float value = (analogRead(A0) - zeroOffset) / (_0dB - zeroOffset);
  float dB = 20 * log10(value);
  uint32_t shiftVal = dB_to_VU(dB, dB_levels, nb_LEDs);

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, shiftVal);      // shift out the bits:
  digitalWrite(latchPin, HIGH);      //take the latch pin high so the LEDs will light up:
}

uint32_t dB_to_VU (float value, float dB_levels[], size_t LEDs) {
  uint32_t result = 0;
  for (unsigned int i = 0; i < LEDs; i++) {
    float threshold = dB_levels[i];
    if (value > threshold) {
      result |= ((uint32_t)1) << i;  // turn on the LED
    }
  }
  return result;
}

template <class T> void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, T val) {
  if (bitOrder == LSBFIRST) {
    for (int i = 0; i < sizeof(val); i++) {
      shiftOut(dataPin, clockPin, LSBFIRST, (uint8_t) (val >> (i * 8)));
    }
  } else {
    for (int i = sizeof(val) - 1; i >= 0; i--) {
      shiftOut(dataPin, clockPin, MSBFIRST, (uint8_t) (val >> (i * 8)));
    }
  }
}

Here’s a version that uses RGB LEDs (e.g. an addressable RGB LED strip):

const int latchPin = 10;  // Pin connected to ST_CP of 74HC595
const int clockPin = 13;  // Pin connected to SH_CP of 74HC595
const int dataPin = 12;   // Pin connected to DS of 74HC595

// LEDs should be connected to the shift registers as followed:
// Register: |------------- 1 -------------|-------------- 2 -------------|------------- 3 -------------| 
// Bit:      |  0  1  2   3  4  5   6  7   |   8   9  10 11  12 13 14 15  |  16 17  18 19 20  21 22 23  |
// LED:      |  R  G  B | R  G  B | R  G       B | R  G  B | R  G  B | R     G  B | R  G  B | R  G  B   |

const uint8_t green = 1;  // green is bit 1 (RGB)
const uint8_t red = 0;    // red is bit 0 (RGB)

const size_t nb_LEDs = 8;

const float zeroOffset = 549;  // biasing voltage (should be around Vcc / 2 ≃ 2.5V ≃ 512)
const float _0dB = 700;        // maximum value (clipping), depends on gain and equipment used

const float dB_levels[nb_LEDs] = { -20, -15, -10, -7, -5, -3, 0, 1 };


void setup() {
  pinMode(latchPin, OUTPUT);
  pinMode(clockPin, OUTPUT);
  pinMode(dataPin, OUTPUT);
}

void loop() {
  float value = (analogRead(A0) - zeroOffset) / (_0dB - zeroOffset);
  float dB = 20 * log10(value);
  uint32_t shiftVal = dB_to_VU(dB, dB_levels, nb_LEDs);

  digitalWrite(latchPin, LOW);
  shiftOut(dataPin, clockPin, MSBFIRST, shiftVal);      // shift out the bits:
  digitalWrite(latchPin, HIGH);      //take the latch pin high so the LEDs will light up:
}

uint32_t dB_to_VU (float value, float dB_levels[], size_t LEDs) {
  uint32_t result = 0;
  for (unsigned int i = 0; i < LEDs; i++) {
    float threshold = dB_levels[i];
    uint8_t color = threshold >= 0 ? red : green;  // change color from green to red when higher than 0dB
    if (value > threshold) {
      result |= ((uint32_t)1) << (i * 3 + color);  // turn on the LED
    }
  }
  return result;
}

template <class T> void shiftOut(uint8_t dataPin, uint8_t clockPin, uint8_t bitOrder, T val) {
  if (bitOrder == LSBFIRST) {
    for (int i = 0; i < sizeof(val); i++) {
      shiftOut(dataPin, clockPin, LSBFIRST, (uint8_t) (val >> (i * 8)));
    }
  } else {
    for (int i = sizeof(val) - 1; i >= 0; i--) {
      shiftOut(dataPin, clockPin, MSBFIRST, (uint8_t) (val >> (i * 8)));
    }
  }
}

Pieter

you would connect it to the Arduino directly, it could damage the inputs (because it goes negative half of the time), and your VU meter would flicker.

The simple circuit in the link I showed. Protects the Arduino from this.

Grumpy_Mike:
The simple circuit in the link I showed. Protects the Arduino from this.

Correct. (I started typing before you posted :wink: )
However, the voltage drop of the diode would attenuate the signal, and you'd completely lose the quiet bits. Adding it to the feedback loop solves this problem.
But it's pretty much the same circuit and the same principle, just an active version.

Pieter

Thanks guys that's awesome!.

I've ordered the parts and going to have a crack at it.