Final – Juan Rivera

“Piano Sleeve” – Final

Materials:

  • Copper tape
  • red lose wire
  • green lose wire
  • Adafruit MPR121 capacitive touch sensors
  • Adafruit Trinket Pro 5V
  • Proto Board
  • Tiny audio buzzer
  • Adafruit liion/lipoly backpack battery
  • 2 compression sleeves
  • Sewing thread and needle
  • velcro strips
  • 2 Neopixel strips

CODE:

#include <Wire.h>
#include <Adafruit_MPR121.h>
#include “pitches.h”
//#include <LED_animations.h>

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h>
#endif

/* BE SURE TO SET THE NUMBER OF NEOPIXELS
++++++++++++++++++++++++++++++++++++++++++++++++++ */
#define NUMPIXELS_STRIP 24 // Number of LEDs in neopixel strip
#define NUMPIXELS_RING 12
#define DATAPIN 6

#define BUZZER_PIN A0 // The output pin for the piezo buzzer
#define NUM_OF_SAMPLES 5 // Higher number whens more delay but more consistent readings

// You can have up to 4 on one i2c bus but one is enough for testing!
Adafruit_MPR121 cap = Adafruit_MPR121();

// Keeps track of the last pins touched
// so we know when buttons are ‘released’
uint16_t lasttouched = 0;
uint16_t currtouched = 0;
int note;
uint16_t showType;
uint8_t i;

// Pattern types supported:
enum pattern { NONE, RAINBOW_CYCLE, RAINBOW, TWINKLERAND, SIMPLEWAVE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE };
// Patern directions supported:
enum direction { FORWARD, REVERSE };

// NeoPattern Class – derived from the Adafruit_NeoPixel class
class NeoPatterns : public Adafruit_NeoPixel
{
public:

// Member Variables:
pattern ActivePattern; // which pattern is running
direction Direction; // direction to run the pattern

unsigned long Interval; // milliseconds between updates
unsigned long lastUpdate; // last update of position

uint32_t Color1, Color2; // What colors are in use
uint16_t TotalSteps; // total number of steps in the pattern
uint16_t Index; // current step within the pattern

void (*OnComplete)(); // Callback on completion of pattern

// Constructor – calls base-class constructor to initialize strip
NeoPatterns(uint16_t pixels, uint8_t pin, uint8_t type, void (*callback)())
: Adafruit_NeoPixel(pixels, pin, type)
{
OnComplete = callback;
}

// Update the pattern
void Update()
{
if ((millis() – lastUpdate) > Interval) // time to update
{
lastUpdate = millis();
switch (ActivePattern)
{
case RAINBOW_CYCLE:
RainbowCycleUpdate();
break;
case THEATER_CHASE:
TheaterChaseUpdate();
break;
case COLOR_WIPE:
ColorWipeUpdate();
break;
case SCANNER:
ScannerUpdate();
break;
case TWINKLERAND:
twinkleRandUpdate();
break;
case SIMPLEWAVE:
simpleWaveUpdate();
break;
case FADE:
FadeUpdate();
break;
default:
break;
}
}
}

// Increment the Index and reset at the end
void Increment()
{
if (Direction == FORWARD)
{
Index++;
if (Index >= TotalSteps)
{
Index = 0;
if (OnComplete != NULL)
{
OnComplete(); // call the completion callback
}
}
}
else // Direction == REVERSE
{
–Index;
if (Index <= 0)
{
Index = TotalSteps – 1;
if (OnComplete != NULL)
{
OnComplete(); // call the comlpetion callback
}
}
}
}

// Reverse pattern direction
void Reverse()
{
if (Direction == FORWARD)
{
Direction = REVERSE;
Index = TotalSteps – 1;
}
else
{
Direction = FORWARD;
Index = 0;
}
}

// Initialize for a RainbowCycle
void RainbowCycle(uint8_t interval, direction dir = FORWARD)
{
ActivePattern = RAINBOW_CYCLE;
Interval = interval;
TotalSteps = 255;
Index = 0;
Direction = dir;
}

// Update the Rainbow Cycle Pattern
void RainbowCycleUpdate()
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, Wheel(((i * 256 / numPixels()) + Index) & 255));
}
show();
Increment();
}

// Initialize for a Theater Chase
void TheaterChase(uint32_t color1, uint32_t color2, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = THEATER_CHASE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}

// Update the Theater Chase Pattern
void TheaterChaseUpdate()
{
for (int i = 0; i < numPixels(); i++)
{
if ((i + Index) % 3 == 0)
{
setPixelColor(i, Color1);
}
else
{
setPixelColor(i, Color2);
}
}
show();
Increment();
}

// Initialize for a ColorWipe
void ColorWipe(uint32_t color, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = COLOR_WIPE;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
Direction = dir;
}

// Update the Color Wipe Pattern
void ColorWipeUpdate()
{
setPixelColor(Index, Color1);
show();
Increment();
}

// number, twinkle color, background color, delay
// twinkleRand(5,strip.Color(255,255,255),strip.Color(255, 0, 100),90);
// twinkle random number of pixels
void twinkleRand(int num, uint32_t color, uint32_t color1, uint8_t interval) {
ActivePattern = TWINKLERAND;
Interval = interval;
TotalSteps = numPixels();
Color1 = color;
Color2 = color1;
Index = 0;
}

// Update the Twinkle Random
void twinkleRandUpdate() {
int num;
//setPixelColor(Index, Color1);
// set background
ColorSet(Color(50, 50, 50)); // dimly lit
// for each num
for (int i = 0; i < num; i++) {
setPixelColor(random(numPixels()), Color1);
}
show();
Increment();
}

// very simple wave: velocity, cycles,delay between frames
// simpleWave(0.03,5,10);
void simpleWave(uint32_t color, uint8_t interval, direction dir = FORWARD) {
Interval = interval;
Direction = dir;
TotalSteps = numPixels();
Color1 = color;
Index = 0;
}

void simpleWaveUpdate() {
float pos = 0.0;
float rate;
int cycles;
// cycle through x times
for (int x = 0; x < (numPixels()*cycles); x++) {
pos = pos + rate;
for (int i = 0; i < numPixels(); i++) {
// sine wave, 3 offset waves make a rainbow!
float level = sin(i + pos) * 127 + 128;
// set color level
setPixelColor(i, (int)level, 0, 0);
}
show();
Increment();
}
}

// Initialize for a SCANNNER
void Scanner(uint32_t color1, uint8_t interval) {
ActivePattern = SCANNER;
Interval = interval;
TotalSteps = (numPixels() – 1) * 2;
Color1 = color1;
Index = 0;
}

// Update the Scanner Pattern
void ScannerUpdate() {
for (int i = 0; i < numPixels(); i++)
{
if (i == Index) // Scan Pixel to the right
{
setPixelColor(i, Color1);
}
else if (i == TotalSteps – Index) // Scan Pixel to the left
{
setPixelColor(i, Color1);
}
else // Fading tail
{
setPixelColor(i, DimColor(getPixelColor(i)));
}
}
show();
Increment();
}

// Initialize for a Fade
void Fade(uint32_t color1, uint32_t color2, uint16_t steps, uint8_t interval, direction dir = FORWARD)
{
ActivePattern = FADE;
Interval = interval;
TotalSteps = steps;
Color1 = color1;
Color2 = color2;
Index = 0;
Direction = dir;
}

// Update the Fade Pattern
void FadeUpdate()
{
// Calculate linear interpolation between Color1 and Color2
// Optimise order of operations to minimize truncation error
uint8_t red = ((Red(Color1) * (TotalSteps – Index)) + (Red(Color2) * Index)) / TotalSteps;
uint8_t green = ((Green(Color1) * (TotalSteps – Index)) + (Green(Color2) * Index)) / TotalSteps;
uint8_t blue = ((Blue(Color1) * (TotalSteps – Index)) + (Blue(Color2) * Index)) / TotalSteps;

ColorSet(Color(red, green, blue));
show();
Increment();
}

// Calculate 50% dimmed version of a color (used by ScannerUpdate)
uint32_t DimColor(uint32_t color)
{
// Shift R, G and B components one bit to the right
uint32_t dimColor = Color(Red(color) >> 1, Green(color) >> 1, Blue(color) >> 1);
return dimColor;
}

// Set all pixels to a color (synchronously)
void ColorSet(uint32_t color)
{
for (int i = 0; i < numPixels(); i++)
{
setPixelColor(i, color);
}
show();
}

// Returns the Red component of a 32-bit color
uint8_t Red(uint32_t color)
{
return (color >> 16) & 0xFF;
}

// Returns the Green component of a 32-bit color
uint8_t Green(uint32_t color)
{
return (color >> 8) & 0xFF;
}

// Returns the Blue component of a 32-bit color
uint8_t Blue(uint32_t color)
{
return color & 0xFF;
}

// 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 Color(255 – WheelPos * 3, 0, WheelPos * 3);
}
else if (WheelPos < 170)
{
WheelPos -= 85;
return Color(0, WheelPos * 3, 255 – WheelPos * 3);
}
else
{
WheelPos -= 170;
return Color(WheelPos * 3, 255 – WheelPos * 3, 0);
}
}
};

// Each key corresponds to a note, which are defined here. Uncomment the scale that you want to use:
int notes[] = {NOTE_A4, NOTE_AS4, NOTE_B4, NOTE_C4, NOTE_D4, NOTE_DS4, NOTE_E4, NOTE_F4, NOTE_FS4, NOTE_G4, NOTE_C5, NOTE_D5 }; // C-Major scale
//int notes[]={NOTE_A4,NOTE_B4,NOTE_C5,NOTE_D5,NOTE_E5,NOTE_F5,NOTE_G5,NOTE_A5}; // A-Minor scale
//int notes[]={NOTE_C4,NOTE_DS4,NOTE_F4,NOTE_FS4,NOTE_G4,NOTE_AS4,NOTE_C5,NOTE_DS5}; // C Blues scale

void Strip1Complete();
void Ring1Complete();
void StickComplete();

// // Define some NeoPatterns for a strip, a neopixel ring and a neopixel stick
// // as well as some completion routines
NeoPatterns Strip1(NUMPIXELS_STRIP, DATAPIN, NEO_GRB + NEO_KHZ800, &Strip1Complete);
NeoPatterns Ring1(NUMPIXELS_RING, 5, NEO_GRB + NEO_KHZ800, &Ring1Complete);
// NeoPatterns Ring2(16, 6, NEO_GRB + NEO_KHZ800, &Ring2Complete);
NeoPatterns Stick(16, 7, NEO_GRB + NEO_KHZ800, &StickComplete);

// Initialize everything and prepare to start
void setup() {
Serial.begin(9600);
// pinMode(8, INPUT_PULLUP);
// pinMode(9, INPUT_PULLUP);
pinMode(BUZZER_PIN, OUTPUT);

Serial.println(“Adafruit MPR121 Capacitive Touch sensor test”);
// Default address is 0x5A, if tied to 3.3V its 0x5B
// If tied to SDA its 0x5C and if SCL then 0x5D
if (!cap.begin(0x5A)) {
Serial.println(“MPR121 not found, check wiring?”);
while (1);
}
Serial.println(“MPR121 found!”);

// This is for Trinket 5V 16MHz, you can remove these three lines if you are not using a Trinket
#if defined (__AVR_ATtiny85__)
if (F_CPU == 16000000) clock_prescale_set(clock_div_1);
#endif
// End of trinket special code

// Initialize all the pixelStrips
Strip1.begin();
Ring1.begin();
Stick.begin();

// Kick off a pattern
Strip1.TheaterChase(Ring1.Color(255, 255, 0), Ring1.Color(0, 0, 50), 100);
Ring1.RainbowCycle(3);
Ring1.Color1 = Ring1.Color1;
Stick.Scanner(Ring1.Color(255, 0, 0), 55);
}

// Main loop
void loop() {
// Update the rings.
Ring1.Update();
Strip1.Update();

currtouched = cap.touched();

// check if a trigger was TOUCHED
for (int i = 0; i < 12; i++) {
// if it *is* touched and *wasnt* touched before, alert!
if ((currtouched & _BV(i)) && !(lasttouched & _BV(i)) ) {
Serial.print(i); Serial.println(“touched”);
delay(20);
Ring1.Interval = 10;
Strip1.Interval = 100;
Strip1.TotalSteps = Strip1.numPixels();

tone(BUZZER_PIN, notes[i]); // Plays the note corresponding to the key pressed
delay (750); // add this back in if necessary
noTone(BUZZER_PIN); // add this back in if necessary TO STOP THE BUZZER!!!!
showType++;
if (showType > 12) {
showType = 0;
}
startShow(showType);
}
// if it *was* touched and now *isnt*, alert!
if (!(currtouched & _BV(i)) && (lasttouched & _BV(i)) ) {
Serial.print(i); Serial.println(“released”);
noTone(BUZZER_PIN);
showType = 0;
// And update tbe stick
Stick.Update();
/*______________________________________________________________________________________________
// THEN RETURN TO – OR – DO THE AMBIENT PATTERN

//**** comment out the 5 lines below if you don’t want it to return to the former pattern
______________________________________________________________________________________________ */
Ring1.ActivePattern = TWINKLERAND;
Ring1.Interval = 100;
Strip1.ActivePattern = SIMPLEWAVE;
Strip1.TotalSteps = Strip1.numPixels();
Strip1.Interval = min(10, Strip1.Interval);
}
}
lasttouched = currtouched;
}
//————————————————————
//Completion Routines – get called on completion of a pattern
//————————————————————

// Ring1 Completion Callback
void Ring1Complete()
{
if (digitalRead(9) == LOW) // Button #2 pressed
{
// KEEP GOING AND CHANGE COLORS
Ring1.Interval = 40;
Ring1.Color1 = Ring1.Wheel(random(255));
Ring1.Interval = 20000;
}
else // Return to normal
{
// Ring1.twinkleRand(5, Colorl(50, 50, 50), Colorl(255, 0, 100), 20);
}
}

// Strip 1 Completion Callback
void Strip1Complete()
{
if (digitalRead(9) == LOW) // Button #2 pressed
{
// KEEP GOING AND CHANGE COLORS
Strip1.Interval = 20;
Strip1.Color1 = Strip1.Wheel(random(255));
Strip1.Interval = 20000;
}
else // Return to normal
{
Strip1.simpleWave(0.03, 5, 10);
}
}

// Stick Completion Callback
void StickComplete()
{
// Random color change for next scan
Stick.Color1 = Stick.Wheel(random(255));
}

//NONE, RAINBOW_CYCLE, RAINBOW, TWINKLERAND, SIMPLEWAVE, THEATER_CHASE, COLOR_WIPE, SCANNER, FADE
void startShow(int i) {
switch (i) {
case 0:
Ring1.ActivePattern = SIMPLEWAVE;
Strip1.ActivePattern = SIMPLEWAVE;
break;
case 1:
Ring1.ActivePattern = COLOR_WIPE;
Strip1.ActivePattern = COLOR_WIPE;
break;
case 2:
Ring1.ActivePattern = THEATER_CHASE;
Strip1.ActivePattern = THEATER_CHASE;
// Strip1.theaterChase(Color(0, 127, 0), 50); // Green
break;
case 3:
Ring1.ActivePattern = RAINBOW;
Strip1.ActivePattern = RAINBOW;
// theaterChase(Color(0, 0, 127), 50); // Blue
break;
case 4:
Ring1.ActivePattern = SCANNER;
Strip1.ActivePattern = SCANNER;
// theaterChase(Color(127, 0, 0), 50); // Red
break;
case 5:
Ring1.ActivePattern = FADE;
Strip1.ActivePattern = FADE;
// rainbowCycle(20);
break;
case 6:
Ring1.ActivePattern = RAINBOW_CYCLE;
Strip1.ActivePattern = RAINBOW_CYCLE;
// Ring1.twinkleRand(5,Color(50,50,50),Color(255, 0, 100),20);
// Strip1.twinkleRand(5, Color(255, 255, 255), Color(255, 0, 100), 90);
break;
case 7:
Ring1.ActivePattern = TWINKLERAND;
Strip1.ActivePattern = TWINKLERAND;
// rainbow(20);
break;
case 8:
Ring1.ActivePattern = COLOR_WIPE;
Strip1.ActivePattern = COLOR_WIPE;
// simpleWave(0.03, 5, 10);
break;
case 9:
Ring1.ActivePattern = THEATER_CHASE;
Strip1.ActivePattern = THEATER_CHASE;
// theaterChase(Color(0, 0, 127), 50); // Blue
break;
case 10:
Ring1.ActivePattern = SCANNER;
Strip1.ActivePattern = SCANNER;
// theaterChase(Color(0, 0, 127), 50); // Blue
break;
case 11:
Ring1.ActivePattern = FADE;
Strip1.ActivePattern = FADE;
// theaterChase(Color(0, 0, 127), 50); // Blue
break;
default:
noTone(BUZZER_PIN);
// show();
// if nothing else matches, do the default
// default is optional
break;
}
}