VU meter assistance

Hello again,

Asking for some kind assistance with a project I have nearing completion, same as in the link below but with a car amplifier and SPI displays vice I2C.

https://create.arduino.cc/projecthub/mircemk/diy-analog-style-stereo-vu-meter-on-i2c-oled-00aca7

I'm at the point where the arduino boots correctly and displays my custom background image and the needle, but it seems to be far too sensitive to inputs at the A0 pin. To the point where simply touching A0 with my finger sends the needle full scale.

My understanding is the original VU meter project would have been looking at a line level signal on the order of 250mv max, and since I'm wanting to read the output of an amplifier I reasoned a resistive divider might work but noise is swamping out the signal.

So I think what I need is a higher level signal input to A0 to increase the signal>noise ratio. I did some digging, and it seems the analog inputs will accept 0-5v but I assume the code would need to be changed in order to deal with it. That's where I'm stuck.

Would I be correct in assuming line 144 in the example:

float MeterValue = PeaktoPeak * 330 / 1024;

needs to be changed to correct for the difference in input voltage?

No surprise there. A finger is a great noise injector.

You probably wired something incorrectly, though, as the input may be floating. Please post a schematic wiring diagram and YOUR code, using code tags.

If you've ever touched and tip of an RCA cable you've probably heard a buzz, and it can be loud. That buzz is electro-magnetic radiation from power lines and in a car you should be shielded from that, but you still might get something. Once you connect a low-impedance source it will be less sensitive to noise.

Is it connected to the speakers or to line-level between the head unit & power amp? Both vary a LOT. Line Level is about 1V, but that's at "full volume". Of course speaker levels are higher and a lot higher with a high-power amp.

If you can't turn-down the pot enough you can add a voltage divider. That should only be necessary if you are using a speaker signal.

Run the Analog Read Serial Example to check the raw readings. (Take-out the delay). The readings will "look random" because you're reading a waveform, and with the diode (and since the Arduino can't read negative voltages) about half the readings should be zero. But you should be able to get an idea of the peaks and you should see a difference between loud & quiet sounds.

The series diode is a problem... The diode doesn't conduct until there is about 0.7V across it. The first 0.7V is lost so weak signals will read as zero and then it will suddenly kick-in. The diode does serve a purpose... Negative voltages can damage the Arduino the Arduino, but there are better solutions.

This is the "normal" Arduino audio input solution:

Audio Input Schematic

It biases the input so zero reads about 512. You can subtract the bias to get the positive & negative readings.

Here is another solution that "kills" the negative voltage:

Audio input 2

P.S.
You can also adjust the sensitivity in software (as long as you're not clipping the ADC by over-driving it over 1023).

...I made a "giant VU meter" effect (the modern style with LEDs) and the software automatically adjusts to the 20-second moving average so I get lots of "meter action" and it goes up to (or near) the maximum with loud & quiet songs or when I change the volume. Of course, it's useless as a "meter" because it doesn't really measure anything... It's just an effect that reacts to the music.

Hi,

What if you apply finger to Audio Input.
The "fritzy" yuk..


Have you adjusted the 100K pots on each channel?
The level at the speaker terminals will depend on the amp power rating.

Tom... :smiley: :+1: :coffee: :australia:

Doug,

Interestingly enough I had already settled on a circuit similar to that second one. This particular amplifier originally had a single analog moving needle meter for a pair of channels, with 2 meters total for 4 channels. Being that it's capable of being bridged, the output of one of the channels is 180* out of phase with the other. This means all I have to do is clip the negative going waves from each channel with a diode, sum them, then feed that signal into A0 at the appropriate voltage with a divider, and the meter should respond to the peaks of both channels simultaneously.

Another interesting feature is since it's McIntosh, it has the powerguard circuit. This limits the output voltage, so that no matter how much input signal I feed it or how high I turn the gains, I'll only see around 18VAC at the output terminals because it detects distortion and limits output accordingly. In other words, it has a compressing sort of effect on the audio signals. This makes it very easy to spec out a divider, since I know what the maximum signal voltage will be.

This is the code.

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_MOSI   9
#define OLED_CLK   10
#define OLED_DC    11
#define OLED_CS    12
#define OLED_RESET 13
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT,
                         OLED_MOSI, OLED_CLK, OLED_DC, OLED_RESET, OLED_CS);
int analogInput = A0;
int hMeter = 65;
int vMeter = 85;
int rMeter = 80;
const int sampleWindow = 50;
unsigned int sample;
static const unsigned char PROGMEM VUMeter[] = { 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x07, 0x03, 0x03, 0x00, 0x00, 0x60, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x01, 0x80, 0x00, 
0x1f, 0x8f, 0x87, 0xc0, 0x00, 0x70, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x03, 0xc0, 0x00, 
0x3f, 0xbf, 0xff, 0xf0, 0x01, 0xfc, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xe0, 0x00, 
0x0f, 0xff, 0xff, 0xf0, 0x01, 0xfc, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xe0, 0x00, 
0x07, 0xff, 0xff, 0xc0, 0x00, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0xc7, 0xc7, 0xc0, 0x00, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xc0, 0x80, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xc1, 0xc0, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xc7, 0xf8, 0xf8, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xcf, 0xfc, 0xf8, 0x00, 0x00, 0x7f, 0xc0, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xcf, 0xc0, 0xf8, 0x00, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x00, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x10, 0x40, 0x1f, 0x00, 0x10, 0x00, 0x20, 0x07, 0xc0, 0x00, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x79, 0xf0, 0x1f, 0x00, 0xf8, 0x00, 0x70, 0x07, 0xc1, 0x80, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0xff, 0xfc, 0x1f, 0x1f, 0xfe, 0x00, 0xff, 0xc7, 0xc7, 0xe0, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0xff, 0xff, 0x1f, 0x1f, 0xff, 0xc7, 0xff, 0xc7, 0xff, 0xf8, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x7e, 0xfe, 0x1f, 0x1f, 0xff, 0xc7, 0xff, 0x87, 0xff, 0xf8, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x7c, 0x3c, 0x1f, 0x0f, 0x9f, 0xc7, 0xfe, 0x07, 0xf7, 0xe0, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x7c, 0x3c, 0x1f, 0x0f, 0x87, 0x87, 0xc0, 0x07, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc7, 0x80, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x87, 0xc0, 0x07, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc7, 0xc0, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x87, 0xc7, 0x07, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xcf, 0xfc, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x8f, 0xff, 0x87, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc7, 0xfc, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x87, 0xff, 0xc7, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc3, 0xe0, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x83, 0xf7, 0xc7, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc0, 0xc0, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x81, 0xc7, 0xc7, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc0, 0x00, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x80, 0x07, 0xc7, 0xc3, 0xe0, 
0x07, 0x83, 0xc3, 0xc0, 0x00, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0x87, 0x80, 0x07, 0xc7, 0xc3, 0xe0, 
0x07, 0xc7, 0xc3, 0xc0, 0x00, 0xf8, 0x3c, 0x3c, 0x1f, 0x0f, 0xe7, 0x87, 0xff, 0xc7, 0xc3, 0xe0, 
0x0f, 0xc7, 0xe7, 0xe0, 0x01, 0xf8, 0x7c, 0x7e, 0x1f, 0x8f, 0xff, 0xcf, 0xff, 0xc7, 0xe3, 0xf0, 
0x0f, 0xef, 0xe7, 0xf0, 0x01, 0xfc, 0xfe, 0x7f, 0x3f, 0x9f, 0xff, 0xcf, 0xff, 0xef, 0xe7, 0xf0, 
0x07, 0x83, 0xc3, 0xc0, 0x00, 0xf0, 0x38, 0x3c, 0x0e, 0x01, 0xfe, 0x01, 0xfc, 0x03, 0xc1, 0xc0, 
0x03, 0x01, 0x81, 0x80, 0x00, 0x20, 0x30, 0x18, 0x0e, 0x00, 0x78, 0x00, 0x78, 0x01, 0x80, 0x80, 
0x01, 0x01, 0x00, 0x80, 0x00, 0x20, 0x10, 0x08, 0x04, 0x00, 0x10, 0x00, 0x20, 0x01, 0x00, 0x80, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x73, 0xc6, 0x7b, 0x27, 0x0c, 0x76, 0xc7, 0xbd, 0xde, 0x31, 0xd8, 0xcc, 0xc8, 0xde, 0x00, 
0x00, 0x21, 0x09, 0x21, 0x24, 0x8c, 0x52, 0x82, 0x91, 0x48, 0x49, 0x48, 0x8c, 0x49, 0x28, 0x00, 
0x00, 0x21, 0x90, 0x31, 0xa4, 0x52, 0x79, 0x02, 0x99, 0xee, 0x85, 0xed, 0x92, 0x69, 0x0c, 0x00, 
0x00, 0x21, 0x13, 0xa1, 0x64, 0x5e, 0x49, 0x03, 0x91, 0x28, 0x85, 0x2a, 0x9e, 0x59, 0x08, 0x00, 
0x00, 0x25, 0x09, 0x21, 0x24, 0x92, 0x49, 0x02, 0x11, 0x28, 0x49, 0x2a, 0x92, 0x49, 0x28, 0x00, 
0x00, 0x7d, 0xc7, 0x39, 0x37, 0x33, 0x4b, 0x87, 0x1d, 0x28, 0x31, 0x28, 0xb3, 0x4c, 0xce, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

void setup(){

  pinMode(analogInput, INPUT);
  Serial.begin(115200);

  if(!display.begin(SSD1306_SWITCHCAPVCC)) {
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Don't proceed, loop forever
  }
}

void loop(){
  unsigned long startMillis = millis();
  unsigned int PeaktoPeak = 0;
  unsigned int SignalMax = 0;
  unsigned int SignalMin = 1024;

  while ( millis() - startMillis < sampleWindow ){

    sample = analogRead(analogInput);
    if (sample < 1024) {

      if (sample > SignalMax){

        SignalMax = sample;
      }

      else if (sample < SignalMin){

        SignalMin = sample;
      }
    }
  }
  PeaktoPeak = SignalMax - SignalMin;
  float MeterValue = PeaktoPeak * 330 / 1024;
  
  MeterValue = MeterValue - 34;
  display.clearDisplay();
  display.drawBitmap(0, 0, VUMeter, 128, 64, WHITE);
  int a1 = (hMeter + (sin(MeterValue / 57.296) * rMeter));
  int a2 = (vMeter - (cos(MeterValue / 57.296) * rMeter));
  display.drawLine(a1, a2, hMeter, vMeter, WHITE);
  display.display();

TomGeorge,

Yes, but being that it's insanely sensitive I'm not getting the expected result. In fact with a sinewave input I can start at minimum, advance the gain slowly and the meter will peak, start over again at zero at some point and then rise to maximum again! It will do this about 3 times over the range of the gain knob, kind of like it overruns and restarts at zero when it reaches a certain point. That's why I think the solution may lie in code, if I can get it to read the entire 5v range of the analog input pin that would be preferable to how it is now. Getting a voltage divider right and having better S/N ratio would be great.

Also, even with A0 tied to ground through 100k and nothing else connected, the needle bounces around near minimum- it should just stay pegged at zero and not move at all. Again I feel like the solution must lie in code, as I have everything connected like the example.

Lastly, I have read that the ADC is dependent on VCC provided to arduino. I'm using 5v, so if you see anything in the code that jumps out at you regarding the conversion, please let me know and I'll attempt to correct it.

You have made an unconventional connection to the signal source, and it's not the same kind of source that the project was designed for. Doesn't that seem like a big red flag that you have a hardware problem? Also you never really provided technical details on your input connection, just some yadda yadda.

Wouldn't it make sense to test it with a line level input that it's designed for? Then solve amp interfacing issues if that works, otherwise troubleshoot any issues in the build.

Generally, you should be doing less theorizing, and more divide and conquer troubleshooting.

If I had no idea what I was doing with electronics and just went jamming signals into microcontrollers with no regard for their level I could see that being a problem, but that's not what's happening here. It sort of works with line level sources, but my gut is telling me the narrow input range is going to prevent me from using it in an automotive environment. There's simply too much noise in a car for it to work at present, 350mv will max out the needle. A 0-5v input range would make a world of difference, but that solution is code based AFAIK.

All due respect, I wouldn't be asking a coding related question if I hadn't explored a hardware related solution first. So far I've tried resistive divider from speaker level, unity gain OPAMP buffer stage to sum the signals and provide a low-z source, line level sources, my phone, it doesn't like any of them- far too much sensitivity. Yes, the needle will move with a live level source, but when you couple that with switching power supply noise, alternator whine, relays and such, the signal is going to get swamped out I promise.

Perhaps the question I should be asking is: what is the code solution to getting the meter to accept a 0-5v input vice the 350mv sensitivity it currently has?

Hi,

If 350mV of noise maxes out the needle, why don't you here that noise?

What is your amplifier?
I would guess it is a differential output to get "mega watts" out.

You are connecting the meter gnd to the gnd of the amplifier and not the vehicle?
How are you powering the Nano?
Can you please post an image(s) of your project?
Can you please post a circuit of what YOU have now?
A hand drawn image will be fine, just include power supplies, component names and pin labels.

I would suggest you forget your main code for the moment and write some code that JUST reds the analog input and uses the IDE monitor to display the values.

Do you have a DMM?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

The operating frequency of a switchmode power supply for example (~47khz) is a bit outside my frequency range, but not outside the ability of a circuit to 'hear' it or at least some of it's lower octaves. Not sure what the bandwidth of an arduino is (I read 15khz somewhere), but my scope is seeing remnants of the SMPS at the input. Granted it's a very low amplitude, but that's just one example of noise I'm unable to hear that could still affect a circuit.

Now if the input were 0-5v instead of 350mv....

What is your amplifier? I would guess it is a differential output to get "mega watts" out.

McIntosh MC420M. It's only differential if you use it in bridge tied mode, I'll only be using it normally though. It's only 50wpc, so around 15-18v to a 4-ohm load depending on the speaker.

My 'megawatt' amp is a Soundigital 12k... :stuck_out_tongue:

You are connecting the meter gnd to the gnd of the amplifier and not the vehicle?
How are you powering the Nano?

The arduino must be grounded to the amplifier, more specifically the center tap of the onboard SMPS in order to correctly read the amplifier output. This is because vehicle ground is separate from amplifier secondary ground, which the output stage uses to drive a load. The amplifier has bipolar 15v supplies to run the OPAMPs and various other circuits it has, I tapped power for the controller off the +15v rail and ran it through a 7805 (5v regulator) and small filter cap. Output is a rock stable 4.99vdc, I don't read any AC with my DMM so it's clean as well.

Can you please post an image(s) of your project?

As follows:

Can you please post a circuit of what YOU have now?

Realistically no, I don't have schematic info on it anyway. An amplifier is an amplifier, no? If you want to see a scope trace of A0 input when I'm feeding it a signal for example though, that's pretty easy to do.

Let's assume I build and test my own amplifiers, that I know my way around test equipment, and that I've done things like this a time or two before. Coding not so much, can someone please have a look at the code??? lol

Hi

Draw the amp as a block with its outputs labeled, draw the Nano and the input circuit you have built, and show connections.
Please include power supplies, component names and pin labels.

Thanks.. Tom... :smiley: :+1: :coffee: :australia:
PS. What is with the displays? They can cause noise too.

I see from your reply that you're really not interested in any solutions other than one that you find yourself, and haven't found yet. You have a lot of confidence in your theories. So I will leave you to find it.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.