Skip to content

Instantly share code, notes, and snippets.

@mohitbhoite
Created October 2, 2022 03:28
Show Gist options
  • Save mohitbhoite/5c3732cfeb7346f6b2cd1318612114aa to your computer and use it in GitHub Desktop.
Save mohitbhoite/5c3732cfeb7346f6b2cd1318612114aa to your computer and use it in GitHub Desktop.
ATtiny85 based VU meter
/*
* ATtiny85 based VU meter
* Author: Mohit Bhoite
* Date: 01 August 2022
*
* Based on original code by Adafruit Industries. Distributed under the BSD license.
* and David Johnson-Davies - www.technoblogy.com
* CC BY 4.0 Licensed under a Creative Commons Attribution 4.0 International license:
* http://creativecommons.org/licenses/by/4.0/
*
* For the board, select ATTinyCore in Arduino - ATtiny25/45/85 (No bootloader)
* ATtiny85 @ 8 MHz (internal oscillator; BOD disabled)
*
*/
#include <Adafruit_NeoPixel.h>
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#define N_PIXELS 10 // Number of pixels in strand
#define MIC_PIN A1 // Microphone is attached to this analog pin
#define LED_PIN 1 // NeoPixel LED strand is connected to this pin
#define DC_OFFSET 0 // DC offset in mic signal - if unusure, leave 0
#define NOISE 120 // Noise/hum/interference in mic signal
#define SAMPLES 40 // Length of buffer for dynamic level adjustment
#define TOP (N_PIXELS + 2) // Allow dot to go slightly off scale
#define PEAK_FALL 500 // Rate of peak falling dot
#define VUBRIGHTNESS 90 //can go upto 255
byte
peak = 0, // Used for falling dot
dotCount = 0, // Frame counter for delaying dot-falling speed
volCount = 0; // Frame counter for storing past volume data
unsigned int
vol[SAMPLES], // Collection of prior volume samples
lvl = 20, // Current "dampened" audio level
minLvlAvg = 0, // For dynamic adjustment of graph low & high
maxLvlAvg = 512;
Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);
void setup() {
clock_prescale_set(clock_div_1);
// Set up ADC in differential mode with 20x gain
ADMUX = 2<<REFS0 | 0<<REFS2 | 11<<MUX0; // Internal 1.1V ref, ADC0 vs ADC1 x20
ADCSRA = 1<<ADEN | 3<<ADPS0; // Enable, 125kHz ADC clock
memset(vol, 0, sizeof(vol));
strip.begin();
strip.setBrightness(80);
}
void loop() {
uint8_t i;
uint16_t minLvl, maxLvl;
unsigned int n, height;
n = LogBar(ReadADC()/7); //dividing by 7 to make it less sensitive. You can play with different values here.
//n = ReadADC();
//n = analogRead(MIC_PIN); // Raw reading from mic
//n = abs(n - 512 - DC_OFFSET); // Center on zero
//n = (n <= NOISE) ? 0 : (n - NOISE); // Remove noise/hum
lvl = ((lvl * 7) + n) >> 3; // "Dampened" reading (else looks twitchy)
// Calculate bar height based on dynamic min/max levels (fixed point):
height = TOP * (lvl - minLvlAvg) / (long)(maxLvlAvg - minLvlAvg);
if(height < 0L) height = 0; // Clip output
else if(height > TOP) height = TOP;
if(height > peak) peak = height; // Keep 'peak' dot at top
//Set pixel colors here
for(i=0; i<10; i++) {
if(i >= height)
{
strip.setPixelColor(i,0,0,0);
//strip.setPixelColor(i+8,0,0,0);
//strip.setPixelColor(i+16,0,0,0);
}
else
{
//strip.setPixelColor(i,Wheel(map(i,0,strip.numPixels()-1,30,150)));
//strip.setPixelColor(i+8,Wheel(map(i,0,strip.numPixels()-1,30,150)));
//strip.setPixelColor(i+16,Wheel(map(i,0,strip.numPixels()-1,30,150)));
strip.setPixelColor(i,VUcolorPicker(i));
}
}
// Draw peak dot
if(peak > 0 && peak <= 10-1)
{
strip.setPixelColor(peak,Wheel(map(peak,0,9,30,150)));
//strip.setPixelColor(peak+8,Wheel(map(peak,0,strip.numPixels()-1,30,150)));
//strip.setPixelColor(peak+16,Wheel(map(peak,0,strip.numPixels()-1,30,150)));
}
strip.show(); // Update strip
// Every few frames, make the peak pixel drop by 1:
if(++dotCount >= PEAK_FALL) { //fall rate
if(peak > 0) peak--;
dotCount = 0;
}
vol[volCount] = n; // Save sample for dynamic leveling
if(++volCount >= SAMPLES) volCount = 0; // Advance/rollover sample counter
// Get volume range of prior frames
minLvl = maxLvl = vol[0];
for(i=1; i<SAMPLES; i++) {
if(vol[i] < minLvl) minLvl = vol[i];
else if(vol[i] > maxLvl) maxLvl = vol[i];
}
// minLvl and maxLvl indicate the volume range over prior frames, used
// for vertically scaling the output graph (so it looks interesting
// regardless of volume level). If they're too close together though
// (e.g. at very low volume levels) the graph becomes super coarse
// and 'jumpy'...so keep some minimum distance between them (this
// also lets the graph go to zero when no sound is playing):
if((maxLvl - minLvl) < TOP) maxLvl = minLvl + TOP;
minLvlAvg = (minLvlAvg * 63 + minLvl) >> 6; // Dampen min/max levels
maxLvlAvg = (maxLvlAvg * 63 + maxLvl) >> 6; // (fake rolling average)
}
// The original code used these colors for the LEDs. I'm not using this effect.
// Input a value 0 to 255 to get a color value.
// The colors are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
if(WheelPos < 85) {
return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
} else if(WheelPos < 170) {
WheelPos -= 85;
return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
} else {
WheelPos -= 170;
return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
}
}
int LogBar (unsigned int x) {
x = x | x>>1;
x = x | x>>2;
x = x | x>>4;
x = x | x>>8;
return x;
}
// Analogue to Digital **********************************
unsigned int ReadADC() {
static int Read;
unsigned char low,high;
ADCSRA = ADCSRA | 1<<ADSC; // Start
do ; while (ADCSRA & 1<<ADSC); // Wait while conversion in progress
low = ADCL;
high = ADCH;
Read = max(Read>>1, high<<8 | low); // Add delay
return Read;
}
//Pick RGB color for each pixel. I have tried going from first pixel of Blue>Cyan>Green>Orange>Red last pixel
uint32_t VUcolorPicker(uint8_t i)
{
uint32_t vucolor = 0;
switch(i)
{
case 0:
vucolor = strip.Color(0,0,VUBRIGHTNESS);
break;
case 1:
vucolor = strip.Color(0,VUBRIGHTNESS/4,VUBRIGHTNESS);
break;
case 2:
vucolor = strip.Color(0,VUBRIGHTNESS,0);
break;
case 3:
vucolor = strip.Color(VUBRIGHTNESS/4,VUBRIGHTNESS,0);
break;
case 4:
vucolor = strip.Color(VUBRIGHTNESS,VUBRIGHTNESS,0);
break;
case 5:
vucolor = strip.Color(VUBRIGHTNESS,VUBRIGHTNESS,0);
break;
case 6:
vucolor = strip.Color(VUBRIGHTNESS,VUBRIGHTNESS/2,0);
break;
case 7:
vucolor = strip.Color(VUBRIGHTNESS,VUBRIGHTNESS/2,0);
break;
case 8:
vucolor = strip.Color(VUBRIGHTNESS,0,0);
break;
case 9:
vucolor = strip.Color(VUBRIGHTNESS,0,0);
break;
}
return vucolor;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment