/* Arduino and MPU6050 Accelerometer and Gyroscope Sensor Tutorial by Dejan, https://howtomechatronics.com */ #include #include #include #include byte LED_PIN = 6; byte mode = 5; // 0 for acceleration, 1 for fire, 2 for waterfall, 3 pride, 4 glitter, 5 pulse, 6 off byte parameter = 128; byte modeSelect; const byte NUM_MODES = 9; const byte debug = 3; const byte STRIPS = 6; const byte NUM_LEDS = 15; const byte FRAMES_PER_SECOND = 60; const float range = 0.5; //accelleration range in g byte BRIGHTNESS = 4; const byte COOLING = 80; const byte SPARKING = 60; uint8_t gHue = 0; const float GRAVITY = -9.81; // Downward (negative) acceleration of gravity in m/s^2 const float h0 = 1; // Starting height, in meters, of the ball (strip length) const byte NUM_BALLS = STRIPS; // Number of bouncing balls you want (recommend < 7, but 20 is fun in its own way) float h[NUM_BALLS] ; // An array of heights float vImpact0 = sqrt( -2 * GRAVITY * h0 ); // Impact velocity of the ball when it hits the ground if "dropped" from the top of the strip float vImpact[NUM_BALLS] ; // As time goes on the impact velocity will change, so make an array to store those values float tCycle[NUM_BALLS] ; // The time since the last time the ball struck the ground byte pos[NUM_BALLS] ; // The integer position of the dot on the strip (LED index) long tLast[NUM_BALLS] ; // The clock time of the last ground strike float COR[NUM_BALLS] ; // Coefficient of Restitution (bounce damping) float accelerationHistory [STRIPS]; int encoderPosition = 1; MPU6050 mpu(Wire); CRGB leds[NUM_LEDS * STRIPS]; CRGB ledsR[NUM_LEDS * STRIPS]; CRGBPalette16 gPal; CRGB flagcolors[3][6] {{CRGB::Red, CRGB::DarkOrange, CRGB::Yellow, CRGB::DarkGreen, CRGB::Blue, CRGB::DarkViolet}, {CRGB::DarkBlue, CRGB::DeepPink, CRGB::Gray, CRGB::Gray, CRGB::DeepPink, CRGB::DarkBlue}, {CRGB::Green, CRGB::Green, CRGB::Gray, CRGB::Gray, CRGB::Red, CRGB::Red}}; const byte modeSwitchPin = 2; RotaryEncoder encoder(A2, A3); void setup() { Serial.begin(19200); //setup LEDs FastLED.addLeds(ledsR, NUM_LEDS * 0, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(ledsR, NUM_LEDS * 1, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(ledsR, NUM_LEDS * 2, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(ledsR, NUM_LEDS * 3, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(ledsR, NUM_LEDS * 4, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.addLeds(ledsR, NUM_LEDS * 5, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.setBrightness( BRIGHTNESS ); FastLED.setMaxPowerInVoltsAndMilliamps(5,500); fill_rainbow(ledsR, NUM_LEDS*STRIPS, 0, 5); FastLED.show(); pinMode(modeSwitchPin, INPUT_PULLUP); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); digitalWrite(3, HIGH); digitalWrite(4, LOW); digitalWrite(5, LOW); attachInterrupt(digitalPinToInterrupt(modeSwitchPin), setMode, RISING); PCICR |= (1 << PCIE1); PCMSK1 |= (1 << PCINT10) | (1 << PCINT11); for (int i = 0; i stripEnd)){ for (int i = stripStart; i <= stripEnd; i++) { leds[i] = CRGB(0, 0, 64); } } else { for (int i = stripStart; i <= ledcutoff; i++) { leds[i] = CRGB(64, 0, 0); } for (int i = ledcutoff; i <= stripEnd; i++) { leds[i] = CRGB(0, 64, 0); } } } void enableLEDsOnAcceleration(float accCombined){ // draw all stored accelerations for (int i = 0; i < STRIPS; i++){ drawAccelerationOnStrip(i, accelerationHistory[i]); } //shift them to the front for (int i = 0; i < STRIPS-1; i++) { accelerationHistory[i] = accelerationHistory[i+1]; } //add new entry at the end accelerationHistory[STRIPS-1] = accCombined; } void calculateFire(int strip){ // Array of temperature readings at each simulation cell static byte heat[STRIPS][NUM_LEDS]; // Step 1. Cool down every cell a little for( int i = 0; i < NUM_LEDS; i++) { heat[strip][i] = qsub8( heat[strip][i], random8(0, ((COOLING * 10) / NUM_LEDS) + 2)); } // Step 2. Heat from each cell drifts 'up' and diffuses a little for( int k= NUM_LEDS - 1; k >= 2; k--) { heat[strip][k] = (heat[strip][k - 1] + heat[strip][k - 2] + heat[strip][k - 2] ) / 3; } // Step 3. Randomly ignite new 'sparks' of heat near the bottom if( random8() < SPARKING ) { int y = random8(7); heat[strip][y] = qadd8( heat[strip][y], random8(160,255) ); } // Step 4. Map from heat cells to LED colors for( int j = 0; j < NUM_LEDS; j++) { // Scale the heat value from 0-255 down to 0-240 // for best results with color palettes. byte colorindex = scale8( heat[strip][j], 240); CRGB color = ColorFromPalette( gPal, colorindex); int pixelnumber = (strip * NUM_LEDS) + j; leds[pixelnumber] = color; } } void drawFire(){ if (mode == 1) { gPal = CRGBPalette16( CRGB::Black, CRGB::Red, CRGB::Yellow, CRGB::White); } else { gPal = CRGBPalette16( CRGB::Black, CRGB::Blue, CRGB::Aqua, CRGB::White); } for (int i = 0; i < STRIPS; i++){ calculateFire(i); } } void drawPride(byte parameter){ for (int strip = 0; strip < STRIPS; strip++){ CRGB color = flagcolors[parameter % 3][strip]; for( int j = 0; j < NUM_LEDS; j++) { int pixelnumber = (strip * NUM_LEDS) + j; leds[pixelnumber] = color; } } } void drawGlitter(byte parameter){ fadeToBlackBy( leds, STRIPS*NUM_LEDS, parameter/10); int pos = random16(STRIPS*NUM_LEDS); leds[pos] += CHSV( gHue + random8(64), 200, 255); } void drawPulse(byte parameter){ static int current_step; float steps = 2000; CRGB color; for (int strip = 0; strip < STRIPS; strip++){ //float f = 0.5-0.5*cos(360/steps*float(current_step) + 360/steps*360/float(STRIPS)*float(strip)); float f = cos(360/steps*float(current_step) + 360/steps*360/float(2*STRIPS)*float(strip)); if (f < 0) f = 0; if (mode == 5) { color = CHSV(parameter , 255, int(255*f)); } else { color = CHSV(224 , 255, int(255*f)); } for( int j = 0; j < NUM_LEDS; j++) { int pixelnumber = (strip * NUM_LEDS) + j; leds[pixelnumber] = color; } } current_step++; } void bounceBalls(){ for (int i = 0 ; i < NUM_BALLS ; i++) { tCycle[i] = millis() - tLast[i] ; // Calculate the time since the last time the ball was on the ground // A little kinematics equation calculates positon as a function of time, acceleration (gravity) and intial velocity h[i] = 0.5 * GRAVITY * pow( tCycle[i]/1000 , 2.0 ) + vImpact[i] * tCycle[i]/1000; if ( h[i] < 0 ) { h[i] = 0; // If the ball crossed the threshold of the "ground," put it back on the ground vImpact[i] = COR[i] * vImpact[i] ; // and recalculate its new upward velocity as it's old velocity * COR tLast[i] = millis(); if ((vImpact[i] < 0.01 ) && (calculateOrientationData()>1.5)) { vImpact[i] = vImpact0; // If the ball is barely moving, "pop" it back up at vImpact0 } } pos[i] = round( h[i] * (NUM_LEDS - 1) / h0); // Map "h" to a "pos" integer index position on the LED strip } for (int i = 0 ; i < STRIPS*NUM_LEDS ; i++) { leds[i] = CRGB::Black; } //Choose color of LEDs, then the "pos" LED on for (int i = 0 ; i < NUM_BALLS ; i++) leds[(i * NUM_LEDS)+pos[i]] = CHSV( uint8_t (i * 40) , 255, 255); //Then off for the next loop around } void drawOff(){ fadeToBlackBy( leds, STRIPS*NUM_LEDS, 10); } void rainbow() { fill_rainbow( leds, NUM_LEDS * STRIPS, gHue, 5); } void setMode(){ static unsigned long last_interrupt_time = 0; unsigned long interrupt_time = millis(); if (interrupt_time - last_interrupt_time > 200) { if (modeSelect == 2) { encoder.setPosition(mode); modeSelect = 0; digitalWrite(3, HIGH); digitalWrite(4, LOW); digitalWrite(5, LOW); } else if (modeSelect == 0) { encoder.setPosition(parameter); modeSelect = 1; digitalWrite(3, LOW); digitalWrite(4, HIGH); digitalWrite(5, LOW); } else if (modeSelect == 1) { encoder.setPosition(BRIGHTNESS); modeSelect = 2; digitalWrite(3, LOW); digitalWrite(4, LOW); digitalWrite(5, HIGH); } last_interrupt_time = interrupt_time; } } void loop() { encoder.tick(); int newPos = encoder.getPosition(); if (encoderPosition != newPos) { encoderPosition = newPos; if (debug <= 3) { Serial.print("Position:\t"); Serial.print(encoderPosition); Serial.print("\t"); Serial.print("modeSelect:\t"); Serial.print(modeSelect); Serial.print("\t"); } if (modeSelect == 0) { mode = encoderPosition % NUM_MODES; } else if (modeSelect == 1){ parameter = encoderPosition % 255; } else if (modeSelect == 2){ BRIGHTNESS = encoderPosition % 32; } if (debug <= 3) { Serial.print("Blinkmode:\t"); Serial.print(mode); Serial.print("\t"); Serial.print("Parameter:\t"); Serial.print(parameter); Serial.print("\t"); Serial.print("Brightness:\t"); Serial.println(BRIGHTNESS); } } if (mode == 0) { // === Read acceleromter data === // //float accCombined = calculateOrientationData(); //enableLEDsOnAcceleration(accCombined); rainbow(); } else if ((mode == 1) || (mode == 2)) { random16_add_entropy( random()); drawFire(); } else if (mode == 3) { drawPride(parameter); } else if (mode == 4) { drawGlitter(parameter); } else if ((mode == 5) || (mode ==6)) { drawPulse(parameter); } else if (mode == 7) { bounceBalls(); } else { drawOff(); } FastLED.setBrightness( BRIGHTNESS * 4 ); if (mode != 2){ for (uint8_t i=0; i