A digital VU meter that looks and moves like an analog needle with mass and spring oscillations
To make the experience fit your profile, pick a username and tell us what interests you.
We found and based on your interests.
VU_Meter_Physics.cExample C Code for ATMEGA328P Potentiometer on analog input pin moves needle, mode digital input pin changes damping on needle, plus and minus digital input pins change display brightness (all setting changes are saved to nonvolatile eeprom) See defines at top for pin assignments
C Source File - 26.46 kB - 08/16/2021 at 14:39
Demo.gifQuick demonstration of needle physics
Graphics Interchange Format - 29.94 MB - 08/03/2021 at 12:22
The biggest limitation in the software implementation I described in the last log was bitbanging the spi. This of course enabled greater flexibility in gpio selection, but at the cost of max framerate. For the previous bitbang code, this meant that I was only able to get around 20Hz per display, writing to them interleaved (or 40Hz if only one display were used). I've since slightly modified the code to support hardware spi and with this I've measured framerates of up to 200Hz for a single display is possible (or 100Hz for two displays).
Once I've finished testing these changes I'll post the code in the files section. For now here's a video demonstration of the older bitbanged spi software.
Since filming this, I've moved to hardware spi and added two more buttons to allow adjusting brightness (mode and brightness settings are saved to nonvolatile eeprom).
So I'll be honest the beginnings of this project were very much inspired by this recent HAD project post https://hackaday.com/2021/07/14/analog-style-vu-meter-with-arduino-and-oled-display/
This is why I love HAD, coming up with creative original ideas is very difficult with the saturated diy maker world but seeing what other people are doing is a great source of inspiration to starting similar projects with improvements sparked by the lessons learned by past projects others have done!
So while what I've implemented is certainly not unique, it's interesting to me nonetheless. From the original project I referenced above I saw two main issues I'd like to resolve. The first is each display required it's own arduino to drive it, one for the left channel and one for the right one. I've implemented my own bitbanged spi oled driver code in the past, so it was simple to update it to support two displays each with their own chip select pin, but sharing the rest of the control pins. This meant one chip could drive two displays, sequentially. My bitbang spi code uses direct port manipulation, and can refresh one screen at just above 40Hz (so logically dividing the communication bandwidth between 2 displays will mean each display can updated at around 20Hz max each). It'd be trivial to change the code to support hardware spi which would greatly further increase speed, but I don't think that's necessary at the moment.
The second issue I seek to improve is that the meter movement in the original project looked a little too perfect and instantaneous, as you'd expect with a digital display. If you've ever seen a mechanical meter movement it's apparent that the mass of the needle and the spring that is attached to it to return it to zero when no signal is driving it add a physical response to impulses. The needle will take a little while to accelerate/decelerate, often overshoot the setpoint a little, and slightly oscillate around the point till it reaches equilibrium. This is the exact behavior I want to approximate with my virtual needle on a digital oled display.
To accomplish this I've implemented a PI controller (basically a PID controller without the differential term). Conventionally such a controller is used to smooth out physical responses so an output can reach a setpoint as quickly as possible without ringing or overshoot, but I'm ironically adding it to add those desirable features for this application lol, by purposely selecting gains which result in underdamping. Here's a snippet of code to demonstrate generally how the PI controller is implemented:
//these gains affect rise/fall time, overshoot, oscillations double p_gain=0.2; //proportional gain double i_gain=0.8; //integral gain val=analogRead(A0)/8; //val is the measured setpoint/reference, divided by 8 because the adc is 10 bits but the meter range I've chosen is only 7 bits err=val-pos; //the error is how far off we are from the setpoint, pos is the current needle position err_accum+=i_gain*err; //here we calculate the scaled factor of how long there's been an error pos+=(int) (p_gain*err+err_accum); //we add the accumulated error with the current error scaled by the proportional gain to calculate what the next position should be // The position must be an integer from 0-127 so we cast the calculated value from double to int if(pos>127) pos=127; //these are limiters to make sure the output never exceeds the max or min deflection of the needle if(pos<0) pos=0;
As you can see, if the setpoint is much larger than the current needle position the error will be large and the correction applied to the next position will be large as well. And if the error has been non-zero for quite awhile, the accumulated error term will be large, further increasing the corrective action. Notice also that these terms can be positive or negative in order to force the corrective action in either direction to keep acting until the needle position and setpoint are the same (thus...Read more »
Create an account to leave a comment. Already have an account? Log In.
This project is still a work in progress, I haven't finalized any front end circuitry or the sampling/scaling in software. I'll be sure to update the files once I do.
Ah great thanks for the update.
The project is coming along great.
A function to turn the screen off when there is no ADC activity would be a great addition to the code.
This would save the screen from burn in. A VU meter is something that might be on for a lot of hours in an amplifier ETC.
A simple way to do this is to call spi_write(CMD,0xAE,ch); to turn off the display and likewise spi_write(CMD,0xAF,ch); to turn it back on. This only turns off the oleds, the buffered image data remains in the controller's sram. Another option would be to auto dim the display using the function brightness(unsigned char b) I wrote. This accepts a value from 0-255 from full brightness all the way down to dim (I don't believe it goes completely off but will definitely prolong display life at lower brightnesses).
Thanks for the reply.
Indeed, In GnatStats and PhatStats all I do is turn off the display, it still runs in the background.
Did you do much testing with the SH1106, if so is the performance similar to the 1309?
All the displays, 1306/1309/1106, should have similar performance to eachother as they only mildly differ mostly in initialization and maybe in how the row addressing happens. My functions handle all this though behind the scenes so the programmer only need to declare the screen type, wire it correctly and call the functions to write to the displays.
Great job! You really did pay close attention to the detail of physical meter movements. Thanks for posting your project.
Great concept and love to see more of the project like this https://cocpureapk.com/
Great concept that I'd like to apply to another simulated analog gage I'm working on. Do you plan to post the code soon?
I posted code in the first project log for the PI compensator portion that basically simulates the analog response. You should be able to tweak this to use on your project. As for posting the final complete code, I still have to finish the analog input sampling and circuitry before I'd be comfortable releasing everything.
Cool retro. I'm sure you'll pick up lots of knowledge along the way.
Digital needs more analog like this :)
Looking forward to seeing more of the project :)
Thanks, this is only the start and I've got quite a few steps to go till I can close this project.
Become a member to follow this project and never miss any updates
I got the code up and running on a nano, do you have a schematic for the audio input front end?
To get full scale on the screen requires 5v which is very high for a typical fixed line out level. They are usually in the 2v range at max output
Thanks for the project :)