Translate

Sunday, 8 November 2020

An experimental Arduino VU meter (and why it's no good)

 In an upcoming project (that is, as usual, taking far too long) requires some indication of audio input level.

So the plan is to have a meter, only this time drive it using an arduino PWM output. Seems like a good plan, as I already use said Arduino to perform one task on start-up, and it's sat there doing nothing most of the time.


So here's the simple input stage. It's job is to take our audio, and amplifying it, so it swings the whole range of our 5V rail, using a rail-to-rail opamp, and centering it on 2.5V, so it doesn't go negative. This is then fed into A0 of our arduino.



Now I need a peak value of our audio, so the usual trick is to rectify it. Rather than implementing this as , say a precision full wave rectifier, I thought I'd have a go in software.

Now the software reads the ADC, and if the result is greater than 512, the result stands and has 512 subrtracted from it. If the result is 511 or less, it's inverted and the result stands, IE is the ADC returns 0, it's inverted to 511, if it's 511, it's inverted to 0. That way, if our peaks are positive or negative, we're measuring a peak. It's a software rectifier.

And that's about all we care about. the output is divided by two and sent as a PWM to the meter. There's a bit in the software to decay the pointer.

At first glance, it appears to work well, given the limitations of the ATMEGA328's A/D ... but it's frequency response is awful...

The problem is with aliasing. Mr Nyquist tells us that we need to sample at least twice as fast as our highest frequency. The software may be going as fast as it can (albeit I haven't employed any techniques to optimise it) , but it's frequency response is poor, and unpredictable (or is it?)

Here's a quick video to show the issue... 

So it is predictable, and poor. Because we are sampling at a constant rate, at anything but that frequency, we will not be sampling the same point of our sinewave, let alone the peak, we could be sampling on the slope, or even at the central crossing point, and giving false readings. We can also see the frequency response improving at the higher frequencies. This is called "spectral folding" , and you can learn more about it here.

So there it is ... it might be OK to give a sort of visual response to some music, but it's woefully inadequate for my purposes. I could use a faster micro (and a better A/D), but that's overkill for this project. I've going back to doing this in good old fashioned analogue!

Here's the code if you fancy playing with it...

int LPeak;
int LoopCounter;

void setup()
{
  pinMode (5, OUTPUT);
  pinMode (3, OUTPUT);
  analogWrite(3, 255);
  delay (1000);
  analogWrite(3, 0);
}

void loop() {
  unsigned int Raw = 0;
  for (int i = 1; i <= 5; i++) {

    Raw += analogRead(A0);

  }
  Raw = Raw / 5;

  int LVal = Raw;

  if (LVal >= 513) {
    LVal = LVal - 512;
  }
  else {
    LVal = map(LVal, 0, 512, 512, 0);
  }
  LVal = (LVal / 2) - 1;

 if (LVal <= 35) {
    LVal =0;
  }
    //LVal =  (106* log10 (abs(LVal)));
  //Serial.println (LVal);
  if (LVal > LPeak) {
    LPeak = LVal;

  }
  if (LPeak >= 210) {
    digitalWrite(LED_BUILTIN, HIGH);
  }
  else {
    digitalWrite(LED_BUILTIN, LOW);
  }
  if (LPeak >= 229) {
    digitalWrite(12, HIGH);
  }
  else {
    digitalWrite(12, LOW);
  }
  if (LPeak <= 32) {
    LPeak =0;
  }
  analogWrite (3, LPeak);
  if (LoopCounter >= 1) {
    LPeak -= 1;
    LoopCounter = 0;
  }

  if (LPeak < 0) {
    LPeak = 0;
  }
  LoopCounter ++;

}

No comments:

Post a Comment