BangLED - Prototyping Bangle with NeoPixels LED

3D Model

Thingiverse BangLED

Source Code

#include <Wire.h>
#include <Adafruit_NeoPixel.h>

#define N_PIXELS   20  // Number of pixels you are using
#define MIC_PIN    A0  // Microphone is attached to Trinket GPIO #2/Gemma D2 (A1)
#define LED_PIN    6   // NeoPixel LED strand is connected to GPIO #0 / D0
#define BUTTON_PIN 9   // Button pin for state change

#define DC_OFFSET  0   // DC offset in mic signal - if unusure, leave 0
#define NOISE      250 // Noise/hum/interference in mic signal
#define SAMPLES    60  // Length of buffer for dynamic level adjustment
#define TOP        (N_PIXELS +1) // Allow dot to go slightly off scale

#define MIC_MODE   0
#define ACC_MODE   1

Adafruit_NeoPixel strip = Adafruit_NeoPixel(N_PIXELS, LED_PIN, NEO_GRB + NEO_KHZ800);

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

int
vol[SAMPLES],       // Collection of prior volume samples
lvl       = 10,     // Current "dampened" audio level
minLvlAvg = 0,      // For dynamic adjustment of graph low & high
maxLvlAvg = 512;

uint8_t mode = MIC_MODE;

// Initiate Variables for Sound Leveling
uint8_t  i;
uint16_t minLvl, maxLvl;
int      n, height;
uint8_t bright = 255;

// Initiate Variables for Accelerometer
const int MPU=0x68;  // I2C address of the MPU-6050
int AcX,AcY,AcZ,GyX,GyY,GyZ;

void setup() {
  memset(vol, 0, sizeof(vol));

  Wire.begin();
  Wire.beginTransmission(MPU);
  Wire.write(0x6B);  // PWR_MGMT_1 register
  Wire.write(0);     // set to zero (wakes up the MPU-6050)
  Wire.endTransmission(true);

  pinMode(BUTTON_PIN, INPUT);

  strip.begin();
  strip.show();
  Serial.begin(9600);
}

void loop() {
  CheckMode();

  switch (mode) {
  case MIC_MODE:
    ReadSound();  
    DynamicLeveling();
    break;
  case ACC_MODE:
    ReadAccelerometer();
    break;
  }

  ShowStrips();
}

void CheckMode() {
  if ( digitalRead(BUTTON_PIN) == LOW ) return;

    Serial.println("HIGH DETECTED");

    if ( mode == MIC_MODE ) mode = ACC_MODE;
    else if ( mode == ACC_MODE ) mode = MIC_MODE;

    delay(1000);
}

void ReadSound() {
  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
}

void DynamicLeveling () {
  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)
}  

void ReadAccelerometer () {
  Wire.beginTransmission(MPU);
  Wire.write(0x3B);  // starting with register 0x3B (ACCEL_XOUT_H)
  Wire.endTransmission(false);
  Wire.requestFrom(MPU,14,true);  // request a total of 14 registers

  AcX=Wire.read()<<8|Wire.read();  // 0x3B (ACCEL_XOUT_H) & 0x3C (ACCEL_XOUT_L)     
  AcY=Wire.read()<<8|Wire.read();  // 0x3D (ACCEL_YOUT_H) & 0x3E (ACCEL_YOUT_L)
  AcZ=Wire.read()<<8|Wire.read();  // 0x3F (ACCEL_ZOUT_H) & 0x40 (ACCEL_ZOUT_L)
  GyX=Wire.read()<<8|Wire.read();  // 0x43 (GYRO_XOUT_H) & 0x44 (GYRO_XOUT_L)
  GyY=Wire.read()<<8|Wire.read();  // 0x45 (GYRO_YOUT_H) & 0x46 (GYRO_YOUT_L)
  GyZ=Wire.read()<<8|Wire.read();  // 0x47 (GYRO_ZOUT_H) & 0x48 (GYRO_ZOUT_L)

  Serial.print(GyX);
  Serial.print(" | ");
  Serial.print(GyY);
  Serial.print(" | ");
  Serial.println(GyZ);

  AcX=map(AcX, -17000, 17000, 0, 255);
  AcY=map(AcY, -17000, 17000, 0, 255);
  AcZ=map(AcZ, -17000, 17000, 0, 255);
  GyX=map(GyX, -30000, 30000, 0, 255);
  GyY=map(GyY, -30000, 30000, 0, 255);
  GyZ=map(GyZ, -30000, 30000, 0, 255);
}

void ShowStrips() {
  switch (mode) {
  case MIC_MODE:
//    strip.setBrightness(bright);    // Set LED brightness (if POT_PIN at top
    //  define commented out, will be full)

    // Color pixels based on rainbow gradient
    for(i=0; i<N_PIXELS; i++) {  
      if(i >= height)
        strip.setPixelColor(i, 0, 0, 0);
      else
        strip.setPixelColor(i,Wheel(map(i,0,(strip.numPixels()-1),150,30)));
    } 

    strip.show(); // Update strip
    break;
  case ACC_MODE:
    for ( int i=0; i<N_PIXELS; i++ ) {
      strip.setPixelColor(i, GyX, GyY, GyZ); 
    }
    strip.show();
    delay(1000);
    break;
  }
}


// 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);
  }
}
comments powered by Disqus