Electro dude
Published © CC BY-SA

DIY Smart VU meter

How to make smart VU meter that you can use as smart desktop gadget

IntermediateFull instructions provided12 hours1,374

Things used in this project

Hardware components

ws2812b
×1
18650 battery
×1
tp4056
×1
esp8266
×1
Slide Switch, SPDT
Slide Switch, SPDT
×1

Software apps and online services

Autodesk EAGLE
Autodesk EAGLE
Arduino IDE
Arduino IDE
Circuit Maker
CircuitMaker by Altium Circuit Maker
KiCad
KiCad
easyeda

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Soldering Station, Temperature Controlled
Soldering Station, Temperature Controlled

Story

Read more

Code

Untitled file

C/C++
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <FastLED.h>

// --- CONFIGURATION ---
#define LED_PIN     14
#define NUM_LEDS    128
#define ROWS        16
#define COLS        8
#define SENSOR_PIN  A0

const char* ssid = "SSID";
const char* password = "PASSWORD";

ESP8266WebServer server(80);
CRGB leds[NUM_LEDS];
byte heat[NUM_LEDS]; 
int currentMode = 0;
CRGB staticColor = CRGB::Red;
float colHeights[COLS];
float weights[COLS] = {1.0, 0.85, 0.8, 0.9, 0.7, 0.7, 0.85, 1.0};

// --- ANIMATION FUNCTIONS (Declared before Loop) ---

void modeVUMeter() {
  int sensorValue = analogRead(SENSOR_PIN);
  int baseHeight = map(sensorValue, 100, 800, 0, ROWS); 
  baseHeight = constrain(baseHeight, 0, ROWS);
  FastLED.clear();
  for (int x = 0; x < COLS; x++) {
    float target = baseHeight * weights[x];
    if (target > colHeights[x]) colHeights[x] = target;
    else colHeights[x] -= 0.7;
    if (colHeights[x] < 0) colHeights[x] = 0;
    for (int y = 0; y < (int)colHeights[x]; y++) {
      leds[(x * ROWS) + y] = CHSV(130 + (y * 6), 255, 255);
    }
  }
}

void modeFire() {
  for(int i = 0; i < NUM_LEDS; i++) heat[i] = qsub8(heat[i], random8(0, 15));
  for(int x = 0; x < COLS; x++) {
    for(int k = ROWS - 1; k >= 2; k--) heat[(x * ROWS) + k] = (heat[(x * ROWS) + k - 1] + heat[(x * ROWS) + k - 2]) / 2.1;
    if(random8() < 100) heat[(x * ROWS) + random8(2)] = qadd8(heat[(x * ROWS)], random8(160, 255));
    for(int y = 0; y < ROWS; y++) leds[(x * ROWS) + y] = HeatColor(heat[(x * ROWS) + y]);
  }
}

void modeMatrix() {
  fadeToBlackBy(leds, NUM_LEDS, 90);
  for (int x = 0; x < COLS; x++) {
    if (random8() < 20) leds[(x * ROWS) + ROWS - 1] = CRGB::Gray;
    for (int y = 0; y < ROWS - 1; y++) {
      if (leds[(x * ROWS) + y + 1] == CRGB::Gray) leds[(x * ROWS) + y] = CRGB::Green;
      else if (leds[(x * ROWS) + y + 1]) leds[(x * ROWS) + y] = leds[(x * ROWS) + y + 1];
    }
    leds[(x * ROWS) + ROWS - 1] = CRGB::Black;
  }
}

void modeRain() {
  fadeToBlackBy(leds, NUM_LEDS, 120);
  if(random8() < 25) leds[(random8(COLS) * ROWS) + ROWS - 1] = CRGB::DeepSkyBlue;
  for(int x = 0; x < COLS; x++) {
    for(int y = 0; y < ROWS - 1; y++) {
      if(leds[(x * ROWS) + y + 1]) leds[(x * ROWS) + y] = leds[(x * ROWS) + y + 1];
    }
    leds[(x * ROWS) + ROWS - 1] = CRGB::Black;
  }
}

void modePong() {
  static float ballX = 4, ballY = 8, dx = 0.35, dy = 0.25;
  fadeToBlackBy(leds, NUM_LEDS, 150);
  ballX += dx; ballY += dy;
  if(ballX <= 0 || ballX >= COLS-1) dx *= -1;
  if(ballY <= 0 || ballY >= ROWS-1) dy *= -1;
  leds[((int)ballX * ROWS) + (int)ballY] = CRGB::White;
  for(int i=0; i<3; i++) {
    int py = constrain((int)ballY - 1 + i, 0, ROWS-1);
    leds[(0 * ROWS) + py] = CRGB::Cyan;
    leds[((COLS-1) * ROWS) + py] = CRGB::Cyan;
  }
}

void modeStarfield() {
  fadeToBlackBy(leds, NUM_LEDS, 100);
  if(random8() < 60) leds[random16(NUM_LEDS)] = CRGB::White;
}

void modePlasma() {
  static uint16_t xCount = 0;
  for (int x = 0; x < COLS; x++) {
    for (int y = 0; y < ROWS; y++) {
      uint8_t hue = inoise8(x * 50, y * 50, xCount);
      leds[(x * ROWS) + y] = CHSV(hue, 255, 255);
    }
  }
  xCount += 3;
}

// --- WEB INTERFACE ---
void handleRoot() {
  String s = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1.0'>";
  s += "<style>body{background:#0a0a0a;color:#fff;font-family:sans-serif;text-align:center;margin:0;}";
  s += ".wrap{max-width:400px;margin:auto;padding:20px;}.card{background:#151515;border:1px solid #222;border-radius:15px;padding:20px;margin-bottom:20px;}";
  s += ".btn{display:block;width:100%;padding:15px;margin:10px 0;background:#222;color:#0fc;border:1px solid #333;border-radius:8px;font-weight:bold;cursor:pointer;text-transform:uppercase;}";
  s += ".btn:active{background:#0fc;color:#000;}input[type='color']{width:100%;height:60px;border-radius:8px;border:none;background:none;cursor:pointer;}</style>";
  s += "<script>function setM(m){fetch('/set?m='+m);}function setC(c){fetch('/color?c='+encodeURIComponent(c));}</script></head><body>";
  s += "<div class='wrap'><h2>SYSTEM CONTROL</h2><div class='card'>";
  s += "<button class='btn' onclick='setM(0)'>Visualizer</button><button class='btn' onclick='setM(2)'>Matrix</button>";
  s += "<button class='btn' onclick='setM(3)'>Fire</button><button class='btn' onclick='setM(7)'>Starfield</button>";
  s += "<button class='btn' onclick='setM(8)'>Plasma</button><button class='btn' onclick='setM(4)'>Rain</button>";
  s += "<button class='btn' onclick='setM(5)'>Pong</button></div>";
  s += "<div class='card'><h3>Static Color</h3><input type='color' oninput='setC(this.value)'></div></div></body></html>";
  server.send(200, "text/html", s);
}

// --- CORE SYSTEM ---
void setup() {
  Serial.begin(115200);
  FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
  FastLED.setBrightness(60);
  
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); }
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/set", [](){ currentMode = server.arg("m").toInt(); server.send(200); });
  server.on("/color", [](){
    String hex = server.arg("c");
    long number = strtol(&hex[1], NULL, 16);
    staticColor = CRGB(number >> 16, (number >> 8) & 0xFF, number & 0xFF);
    currentMode = 6;
    server.send(200);
  });
  server.begin();
}

void loop() {
  server.handleClient();
  switch(currentMode) {
    case 0: modeVUMeter(); break;
    case 2: modeMatrix(); break;
    case 3: modeFire(); break;
    case 4: modeRain(); break;
    case 5: modePong(); break;
    case 6: fill_solid(leds, NUM_LEDS, staticColor); break;
    case 7: modeStarfield(); break;
    case 8: modePlasma(); break;
    default: modeVUMeter(); break;
  }
  FastLED.show();
  delay(15);
}

Credits

Electro dude
20 projects β€’ 49 followers
Hey, I am Electro dude 😎.You can also find me. πŸ“Ή YouTube: ElectroDude Channel πŸ“Έ Instagram: @electro__dude πŸ“˜ Facebook: ElectroDude Page

Comments