// Do not hook up LED pixels directly to the ESP32
// LEDs draw too much current and will break the ESP32
// LEDs need direct power supply input
// All GNDs need to be connected for LEDs to work

#include <Adafruit_NeoPixel.h>
#include <arduinoFFT.h>

// --- Microphone Setup (MAX9814 Analog Input) ---
#define MIC_PIN 34        // Analog pin connected to the microphone output (MAX9814)

// --- LED Strip Setup ---
#define LED_PIN 15        // Data pin connected to the LED strip
#define NUM_LEDS 20       // Number of LEDs in your strip. Increased for finer resolution
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);

// --- FFT Setup ---
#define SAMPLES 128      // Must be a power of 2
#define SAMPLING_FREQUENCY 10000.0 // Use a double literal (add .0)

// --- Global declaration of vReal and vImag ---
float vReal[SAMPLES];
float vImag[SAMPLES];

// Declare FFT object with explicit <float> template
ArduinoFFT<float> FFT = ArduinoFFT<float>(vReal, vImag, SAMPLES, SAMPLING_FREQUENCY);

unsigned long microseconds;

// --- Smoothing Setup (for averaging over time) ---
#define AVERAGE_SAMPLES 4 
int magnitudeHistory[NUM_LEDS][AVERAGE_SAMPLES] = {0};
int historyIndex[NUM_LEDS] = {0};

// --- Sensitivity Adjustment ---
#define SENSITIVITY_PIN 35 // Analog pin for potentiometer
double sensitivity = 1500.0; // Initial sensitivity value.  Adjust as needed

// --- Color Cycling ---
uint8_t hue = 0; // Global hue value for color cycling
#define HUE_INCREMENT 2   // Amount to increment hue each frame (adjust for speed)
#define SATURATION 255 // Fixed saturation value (for vivid colors)

// --- Minimum Brightness ---
#define MIN_BRIGHTNESS_PERCENT 80 // Minimum brightness percentage

// Function to perform FFT and update LEDs
void displaySpectrum() {
  FFT.windowing(FFT_WIN_TYP_HAMMING, FFT_FORWARD);
  FFT.compute(FFT_FORWARD);
  FFT.complexToMagnitude();

  int step = SAMPLES / (2.0 * NUM_LEDS); // Use only the first half of the samples

  for (int i = 0; i < NUM_LEDS; i++) {
    int peak = 0;
    // Sample a range of frequencies for each LED
    for (int j = i * step; j < (i + 1) * step; j++) {
      if (vReal[j] > peak) {
        peak = vReal[j];
      }
    }

    // Map peak value to a magnitude
    int magnitude = map(peak, 0, sensitivity, 0, 255);

    magnitudeHistory[i][historyIndex[i]] = magnitude;
    historyIndex[i] = (historyIndex[i] + 1) % AVERAGE_SAMPLES;

    int avgMagnitude = 0;
    for (int j = 0; j < AVERAGE_SAMPLES; j++) {
      avgMagnitude += magnitudeHistory[i][j];
    }
    avgMagnitude /= AVERAGE_SAMPLES;

    // Calculate minimum brightness
    uint8_t minBrightness = (uint8_t)(255 * (MIN_BRIGHTNESS_PERCENT / 100.0));

    // Map the magnitude to a brightness value, ensuring it's always at least minBrightness
    uint8_t brightness = map(avgMagnitude, 0, 255, minBrightness, 255);

    uint32_t color = Wheel((i * 2 + hue) & 255); // More varied color distribution
    // Extract R, G, B components
    uint8_t r = (color >> 16) & 255;
    uint8_t g = (color >> 8) & 255;
    uint8_t b = color & 255;

    // Adjust each component's brightness while ensuring it doesn't go below minBrightness
    r = map(r, 0, 255, minBrightness, brightness);
    g = map(g, 0, 255, minBrightness, brightness);
    b = map(b, 0, 255, minBrightness, brightness);

    strip.setPixelColor(i, strip.Color(r, g, b));
  }
  strip.show();

  hue += HUE_INCREMENT; // Increment hue for the next frame
}

void setup() {
  Serial.begin(115200);
  analogReadResolution(12); // Set ADC resolution to 12 bits

  strip.begin();
  strip.show(); // Initialize all pixels to 'off'
  strip.setBrightness(255); // Start with full brightness
  pinMode(SENSITIVITY_PIN, INPUT);
}

void loop() {
 
  microseconds = micros();

  for (int i = 0; i < SAMPLES; i++) {
    vReal[i] = analogRead(MIC_PIN);
    vImag[i] = 0;
    while ((micros() - microseconds) < (1000000 / SAMPLING_FREQUENCY)) {
      // Maintain the sampling rate
    }
    microseconds += (1000000 / SAMPLING_FREQUENCY);
  }

  sensitivity = map(analogRead(SENSITIVITY_PIN), 0, 4095, 500, 5000);

  displaySpectrum();
}

// Input a value 0 to 255 to get a color value.
// The colours are a transition r - g - b - back to r.
uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if(WheelPos < 85) {
    return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if(WheelPos < 170) {
    WheelPos -= 85;
    return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}
$abcdeabcde151015202530fghijfghij
esp:0
esp:2
esp:4
esp:5
esp:12
esp:13
esp:14
esp:15
esp:16
esp:17
esp:18
esp:19
esp:21
esp:22
esp:23
esp:25
esp:26
esp:27
esp:32
esp:33
esp:34
esp:35
esp:3V3
esp:EN
esp:VP
esp:VN
esp:GND.1
esp:D2
esp:D3
esp:CMD
esp:5V
esp:GND.2
esp:TX
esp:RX
esp:GND.3
esp:D1
esp:D0
esp:CLK
vcc1:VCC
gnd1:GND
mic1:1
mic1:2
pot1:GND
pot1:SIG
pot1:VCC
rgb1:VDD
rgb1:DOUT
rgb1:VSS
rgb1:DIN
rgb2:VDD
rgb2:DOUT
rgb2:VSS
rgb2:DIN
rgb3:VDD
rgb3:DOUT
rgb3:VSS
rgb3:DIN
rgb4:VDD
rgb4:DOUT
rgb4:VSS
rgb4:DIN
rgb5:VDD
rgb5:DOUT
rgb5:VSS
rgb5:DIN
rgb6:VDD
rgb6:DOUT
rgb6:VSS
rgb6:DIN
rgb7:VDD
rgb7:DOUT
rgb7:VSS
rgb7:DIN
rgb8:VDD
rgb8:DOUT
rgb8:VSS
rgb8:DIN
rgb9:VDD
rgb9:DOUT
rgb9:VSS
rgb9:DIN
rgb10:VDD
rgb10:DOUT
rgb10:VSS
rgb10:DIN
rgb11:VDD
rgb11:DOUT
rgb11:VSS
rgb11:DIN
rgb12:VDD
rgb12:DOUT
rgb12:VSS
rgb12:DIN
rgb13:VDD
rgb13:DOUT
rgb13:VSS
rgb13:DIN
rgb14:VDD
rgb14:DOUT
rgb14:VSS
rgb14:DIN
rgb15:VDD
rgb15:DOUT
rgb15:VSS
rgb15:DIN
rgb16:VDD
rgb16:DOUT
rgb16:VSS
rgb16:DIN
rgb17:VDD
rgb17:DOUT
rgb17:VSS
rgb17:DIN
rgb18:VDD
rgb18:DOUT
rgb18:VSS
rgb18:DIN
rgb19:VDD
rgb19:DOUT
rgb19:VSS
rgb19:DIN
rgb20:VDD
rgb20:DOUT
rgb20:VSS
rgb20:DIN
5V Power Supply
Adjust Sensitivity
Mic