├── README.md ├── candle.ino ├── SpectrumAnalyzerFHT.ino ├── TimsProject.ino ├── OrbLight.ino └── myMPU6050.ino /README.md: -------------------------------------------------------------------------------- 1 | # Arduino-projects 2 | Some arduino projects I built 3 | 4 | -------------------------------------------------------------------------------- /candle.ino: -------------------------------------------------------------------------------- 1 | // candle for Adafruit NeoPixel 2 | // 8 pixel version 3 | // by Tim Bartlett, December 2013 4 | 5 | #include 6 | #define PIN 6 7 | 8 | // color variables: mix RGB (0-255) for desired yellow 9 | int redPx = 0xff; 10 | int grnHigh = 0x64; 11 | int bluePx = 0xa; 12 | 13 | uint32_t brightest = 0xff640a; 14 | uint32_t dimmest = 0xaf1a00; 15 | 16 | // animation time variables, with recommendations 17 | int burnDepth = 20; //how much green dips below grnHigh for normal burn - 18 | int flutterDepth = 50; //maximum dip for flutter 19 | int cycleTime = 120; //duration of one dip in milliseconds 20 | 21 | // pay no attention to that man behind the curtain 22 | int fDelay; 23 | int fRep; 24 | int flickerDepth; 25 | int burnDelay; 26 | int burnLow; 27 | int flickDelay; 28 | int flickLow; 29 | int flutDelay; 30 | int flutLow; 31 | 32 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(64, PIN, NEO_GRB + NEO_KHZ800); 33 | 34 | // 35 | // Blend two colors together based on the ratio. ratio of 0.0 will be 100% color a and 36 | // ratio of 1.0 will be 100% color b. 37 | uint32_t blend (uint32_t ina, uint32_t inb, uint32_t ratio) 38 | { 39 | uint32_t r = ((((ina >> 16) & 0xff) * (256-ratio)) + (((inb >> 16) & 0xff) * ratio)) >> 8; 40 | uint32_t g = ((((ina >> 8) & 0xff) * (256-ratio)) + (((inb >> 8) & 0xff) * ratio)) >> 8; 41 | uint32_t b = ((((ina >> 0) & 0xff) * (256-ratio)) + (((inb >> 0) & 0xff) * ratio)) >> 8; 42 | return ((r << 16) | (g << 8) | b); 43 | } 44 | 45 | void setup() { 46 | strip.setBrightness(0xff); 47 | flickerDepth = (burnDepth + flutterDepth) / 2.4; 48 | burnLow = grnHigh - burnDepth; 49 | burnDelay = (cycleTime / 2) / burnDepth; 50 | flickLow = grnHigh - flickerDepth; 51 | flickDelay = (cycleTime / 2) / flickerDepth; 52 | flutLow = grnHigh - flutterDepth; 53 | flutDelay = ((cycleTime / 2) / flutterDepth); 54 | 55 | strip.begin(); 56 | strip.show(); 57 | } 58 | 59 | // In loop, call CANDLE STATES, with duration in seconds 60 | // 1. on() = solid yellow 61 | // 2. burn() = candle is burning normally, flickering slightly 62 | // 3. flicker() = candle flickers noticably 63 | // 4. flutter() = the candle needs air! 64 | 65 | void loop() { 66 | burn(10); 67 | flicker(5); 68 | burn(8); 69 | flutter(6); 70 | burn(3); 71 | on(10); 72 | burn(10); 73 | flicker(10); 74 | } 75 | 76 | 77 | // basic fire funciton - not called in main loop 78 | void fire(int grnLow) { 79 | for (int grnPx = grnHigh; grnPx > grnLow; grnPx--) { 80 | int halfGrn = grnHigh - ((grnHigh - grnPx) / 2); 81 | uint32_t darkGrn = grnPx - 70; 82 | darkGrn = constrain(darkGrn, 5, 255); 83 | uint32_t max = brightest; 84 | uint32_t min = (0xaf0000) | (darkGrn << 8); 85 | strip.setPixelColor(0, min); 86 | strip.setPixelColor(1, min); 87 | strip.setPixelColor(2, blend (max, min, 168)); 88 | strip.setPixelColor(3, blend (max, min, 84)); 89 | strip.setPixelColor(4, redPx, grnPx, bluePx); 90 | strip.setPixelColor(5, redPx, grnPx, bluePx); 91 | strip.setPixelColor(6, redPx, halfGrn, bluePx); 92 | strip.setPixelColor(7, max); 93 | strip.show(); 94 | delay(fDelay); 95 | } 96 | for (int grnPx = grnLow; grnPx < grnHigh; grnPx++) { 97 | int halfGrn = grnHigh - ((grnHigh - grnPx) / 2); 98 | int darkGrn = grnPx-70; 99 | darkGrn = constrain(darkGrn, 5, 255); 100 | strip.setPixelColor(0, redPx-180, darkGrn, 0); 101 | strip.setPixelColor(1, redPx-180, darkGrn, 0); 102 | strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5); 103 | strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2); 104 | strip.setPixelColor(4, redPx, grnPx, bluePx); 105 | strip.setPixelColor(5, redPx, grnPx, bluePx); 106 | strip.setPixelColor(6, redPx, halfGrn, bluePx); 107 | strip.setPixelColor(7, redPx, grnHigh, bluePx); 108 | strip.show(); 109 | delay(fDelay); 110 | } 111 | } 112 | 113 | // fire animation 114 | void on(int f) { 115 | fRep = f * 1000; 116 | int grnPx = grnHigh - 6; 117 | strip.setPixelColor(0, redPx-180, grnPx-70, 0); 118 | strip.setPixelColor(1, redPx-180, grnPx-70, 0); 119 | strip.setPixelColor(2, redPx-120, grnPx-50, bluePx-5); 120 | strip.setPixelColor(3, redPx-60, grnPx-35, bluePx-2); 121 | strip.setPixelColor(4, redPx, grnPx, bluePx); 122 | strip.setPixelColor(5, redPx, grnPx, bluePx); 123 | strip.setPixelColor(6, redPx, grnPx, bluePx); 124 | strip.setPixelColor(7, redPx, grnHigh, bluePx); 125 | strip.show(); 126 | delay(fRep); 127 | } 128 | 129 | void burn(int f) { 130 | fRep = f * 8; 131 | fDelay = burnDelay; 132 | for (int var = 0; var < fRep; var++) { 133 | fire(burnLow); 134 | } 135 | } 136 | 137 | void flicker(int f) { 138 | fRep = f * 8; 139 | fDelay = burnDelay; 140 | fire(burnLow); 141 | fDelay = flickDelay; 142 | for (int var = 0; var < fRep; var++) { 143 | fire(flickLow); 144 | } 145 | fDelay = burnDelay; 146 | fire(burnLow); 147 | fire(burnLow); 148 | fire(burnLow); 149 | } 150 | 151 | void flutter(int f) { 152 | fRep = f * 8; 153 | fDelay = burnDelay; 154 | fire(burnLow); 155 | fDelay = flickDelay; 156 | fire(flickLow); 157 | fDelay = flutDelay; 158 | for (int var = 0; var < fRep; var++) { 159 | fire(flutLow); 160 | } 161 | fDelay = flickDelay; 162 | fire(flickLow); 163 | fire(flickLow); 164 | fDelay = burnDelay; 165 | fire(burnLow); 166 | fire(burnLow); 167 | } 168 | -------------------------------------------------------------------------------- /SpectrumAnalyzerFHT.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define FHT_N 256 3 | #define LIN_OUT 1 4 | #include 5 | #include 6 | 7 | // 8 | // Spectrum analyzer 9 | // Pete Reiter 10 | // Spectrum analyzer with a Adafruit neopixel strip as output. Unlike an ordinary 2-dimensional spectrum 11 | // analyzer that uses Y-axis is display intensity of each frequency band, this uses color and brightness of 12 | // the LEDs to indicate the intensity. This code was originally adapted from the PICCOLO tiny music visualizer 13 | // on the Adafruit web site. 14 | // 15 | // Software requirements: 16 | // - FHT library for Arduino 17 | // - Adafruit Neopixel library. 18 | 19 | #define PIN 6 // digital pin for programming neopixels 20 | #define NUM_PIXELS 144 // this is the size of my neopixel strip 21 | 22 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800); 23 | 24 | // Microphone connects to Analog Pin 0. Corresponding ADC channel number 25 | // varies among boards...it's ADC0 on Uno and Mega, ADC7 on Leonardo. 26 | // Other boards may require different settings; refer to datasheet. 27 | #ifdef __AVR_ATmega32U4__ 28 | #define ADC_CHANNEL 7 29 | #else 30 | #define ADC_CHANNEL 0 31 | #endif 32 | 33 | volatile uint32_t samplePos = 0; // Buffer position counter 34 | 35 | static const uint8_t PROGMEM 36 | // This is low-level noise that's subtracted from each FHT output column 37 | // This was experimentally determined in a quiet room. 38 | noise[128]={ 39 | 50, 12, 10, 8, 7, 6, 6, 5, // 0 40 | 5, 5, 4, 4, 4, 4, 4, 4, // 8 41 | 4, 4, 4, 4, 4, 4, 4, 4, // 16 42 | 4, 4, 4, 4, 4, 4, 4, 4, // 24 43 | 4, 4, 4, 4, 4, 4, 4, 4, // 32 44 | 3, 3, 3, 4, 3, 3, 3, 3, // 40 45 | 3, 3, 3, 3, 3, 3, 3, 3, // 48 46 | 3, 3, 3, 3, 3, 3, 3, 3, // 56 47 | 3, 3, 3, 3, 3, 3, 3, 3, // 64 48 | 3, 3, 3, 3, 3, 3, 3, 3, // 72 49 | 3, 3, 3, 3, 3, 3, 3, 3, // 80 50 | 3, 3, 3, 3, 3, 3, 3, 3, // 88 51 | 3, 3, 3, 3, 3, 3, 3, 3, // 96 52 | 3, 3, 3, 3, 3, 3, 3, 3, // 104 53 | 3, 3, 3, 3, 3, 3, 3, 3, // 112 54 | 3, 3, 3, 3, 3, 3, 3, 3 // 120 55 | }; 56 | 57 | static const uint8_t PROGMEM 58 | calibration[128] = { 59 | 1, 1, 1, 1, 1, 1, 1, 1, 60 | 1, 1, 1, 1, 1, 1, 1, 1, 61 | 1, 1, 1, 1, 1, 1, 1, 1, 62 | 1, 1, 1, 1, 1, 1, 1, 1, 63 | 1, 1, 1, 1, 1, 1, 1, 1, 64 | 1, 1, 1, 1, 1, 1, 1, 1, 65 | 1, 1, 1, 1, 1, 1, 1, 1, 66 | 1, 1, 1, 1, 1, 1, 1, 1, 67 | 2, 2, 2, 2, 2, 2, 2, 2, 68 | 2, 2, 2, 2, 2, 2, 2, 2, 69 | 2, 2, 2, 2, 2, 2, 2, 2, 70 | 2, 2, 2, 2, 2, 2, 2, 2, 71 | 3, 3, 3, 3, 3, 3, 3, 3, 72 | 3, 3, 3, 3, 3, 3, 3, 3, 73 | 3, 3, 3, 3, 3, 3, 3, 3, 74 | 3, 3, 3, 3, 3, 3, 3, 3 75 | } ; 76 | 77 | static const byte PROGMEM slots[160] = 78 | // combine the 128 FFT frequency output slots into buckets based on octaves. 79 | // for the lower frequencies multiple leds display a single frequency slot. 80 | // for the higher frequencies multiple frequency slots are combined into a aingle led 81 | { 82 | 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7, 8, 8, 8, 8, 83 | 9, 9, 9, 9, 10, 10, 10, 10, 11, 11, 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15, 84 | 16, 16, 17, 17, 18, 18, 19, 19, 20, 20, 21, 21, 22, 22, 23, 23, 24, 24, 25, 25, 26, 26, 27, 27, 28, 28, 29, 29, 30, 30, 31, 31, 85 | 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 47, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 86 | 65, 67, 69, 71, 73, 75, 77, 79, 81, 83, 85, 87, 89, 91, 93, 95, 97, 99, 101, 103, 105, 107, 109, 111, 113, 115, 117, 119, 121, 123, 125, 127 87 | }; 88 | 89 | // This is the mapping from the values in the buckets to the colors representing those values. These numbers were generated by some C code that I've pasted into the bottom of this 90 | // file. The colors go from blue->green->red with an increase in intensity as the values increase. There's also a log10 based response curve figured in. 91 | #define NUM_COLORS 64 92 | static const uint32_t PROGMEM colors[NUM_COLORS] = { 93 | 0x42, 0x1c37, 0x332e, 0x4823, 0x5b1a, 0x6c11, 0x7c08, 0x8c00, 0x88900, 0x108600, 0x178300, 0x1e8100, 0x267e00, 0x2d7b00, 0x347700, 0x3a7400, 0x407100, 0x466e00, 0x4d6b00, 0x526800, 0x586500, 0x5e6200, 0x636000, 0x685d00, 0x6d5a00, 0x725700, 0x775400, 0x7c5100, 0x804f00, 0x864c00, 0x8a4900, 0x8f4600, 0x924400, 0x974100, 0x9b3f00, 0x9f3c00, 0xa33a00, 0xa83700, 0xab3500, 0xaf3200, 0xb33000, 0xb72e00, 0xbb2b00, 0xbe2900, 0xc12700, 0xc52400, 0xc92200, 0xcc2000, 0xd01e00, 0xd31c00, 0xd71900, 0xda1700, 0xdc1500, 0xe01300, 0xe31100, 0xe60f00, 0xe90d00, 0xec0b00, 0xf00800, 0xf30600, 0xf60400, 0xf90200, 0xfc0000, 0xff0000 94 | }; 95 | 96 | #define THRESHOLD 1 97 | // The prescaler settings determine the frequency of audio sampling. We can sample higher 98 | // frequencies with a lower prescaler value, but it will also raise the lowest frequency that 99 | // we can sample. With this setup, I seem to be getting around 300Hz-9.6KHz response. There is 100 | // some aliasing going on, meaning that frequencies > 9.6KHz will show up in lower frequency 101 | // response. 102 | void setup() { 103 | // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion 104 | ADMUX = ADC_CHANNEL; // Channel sel, right-adj, use AREF pin 105 | ADCSRA = _BV(ADEN) | // ADC enable 106 | _BV(ADSC) | // ADC start 107 | _BV(ADATE) | // Auto trigger 108 | _BV(ADIE) | // Interrupt enable 109 | // select the prescaler value. Note that the max frequency our FFT will 110 | // display is half the sample rate. 111 | // _BV(ADPS2) | _BV(ADPS0); // 32:1 / 13 = 38,460 Hz 112 | _BV(ADPS2) | _BV(ADPS1); // 64:1 / 13 = 19,230 Hz 113 | // _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz 114 | ADCSRB = 0; // Free run mode, no high MUX bit 115 | DIDR0 = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin 116 | TIMSK0 = 0; // Timer0 off 117 | 118 | // Neopixels setup 119 | // Initialize all pixels to 'off' 120 | strip.setBrightness(255); 121 | strip.begin(); // Initialize all pixels to 'off' 122 | cli(); // disable interrupts when writing neopixels 123 | strip.show(); 124 | sei(); // Enable interrupts 125 | Serial.begin(9600); // set up Serial library at 9600 bps for debugging purposes 126 | 127 | } 128 | 129 | 130 | void loop() { 131 | uint16_t x, L; 132 | while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish 133 | 134 | fht_window(); 135 | fht_reorder(); 136 | fht_run(); 137 | fht_mag_lin(); 138 | samplePos = 0; // Reset sample counter 139 | ADCSRA |= _BV(ADIE); // Resume sampling interrupt 140 | 141 | // Remove noise 142 | for(x=0; x 2) 158 | { // this led combines the results of multiple frequency slots 159 | for (int j = current; j < next; j++) 160 | value += fht_lin_out[j]; 161 | } 162 | // if we have one slot going to multiple leds, do some averaging on the boundary so that the transition isn't so abrupt 163 | else if (current == previous && current != next) 164 | value = (fht_lin_out[current] + fht_lin_out[next])/2; 165 | else // just use the value 166 | value = fht_lin_out[current]; 167 | 168 | value >>= 6; 169 | if (value < 6) 170 | value = 0; 171 | if (value >= NUM_COLORS) 172 | value = NUM_COLORS - 1; 173 | strip.setPixelColor(i, pgm_read_dword(&colors[value])); 174 | } 175 | cli(); // no interrupts while writing the neopixels 176 | strip.show(); 177 | sei(); // restore interrupts 178 | } 179 | 180 | // interrupt service routine. This gets called each time the ADC finishes 1 sample. 181 | ISR(ADC_vect) { // Audio-sampling interrupt 182 | // shift the unsigned input to be centered around 0. The 10-bit ADC is 183 | // capable of producing values from 0 - 1023, but with a microphone that outputs 184 | // 2V max and a reference voltage of 3.3V we will never hit the max. 185 | fht_input[samplePos] = (ADC - 512) << 6; 186 | // Serial.println(fht_input[samplePos]); 187 | if(++samplePos >= FHT_N) ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off 188 | } 189 | 190 | // 191 | // Some C code I used to generate the values->colors map. I didn't run this code on the Arduino. I used ideone.com 192 | // and then pasted the output into my arduino code. 193 | #if 0 194 | #include 195 | #include 196 | #include 197 | using namespace std; 198 | 199 | 200 | 201 | const int numValues = 64; // number of colors in our output array. This should correspond 202 | // to the max value you want to display. 203 | double breakPoint = log10(numValues)/2.0; 204 | 205 | // 206 | // Blend two colors together based on the ratio. ratio of 0.0 will be 100% color a and 207 | // ratio of 1.0 will be 100% color b. 208 | uint32_t blend (uint32_t ina, uint32_t inb, double ratio) 209 | { 210 | int r = (((ina >> 16) & 0xff) * (1.0-ratio)) + (((inb >> 16) & 0xff) * ratio); 211 | int g = (((ina >> 8) & 0xff) * (1.0-ratio)) + (((inb >> 8) & 0xff) * ratio); 212 | int b = (((ina >> 0) & 0xff) * (1.0-ratio)) + (((inb >> 0) & 0xff) * ratio); 213 | return ((r << 16) | (g << 8) | b); 214 | } 215 | 216 | // 217 | // Scale the intensity of the passed in color. I am using max brightness colors and 218 | // 0.0 - 1.0 as the scale value. 219 | uint32_t scale (uint32_t ina, double scale_value) 220 | { 221 | int r = ((ina >> 16) & 0xff) * scale_value; 222 | int g = ((ina >> 8) & 0xff) * scale_value; 223 | int b = ((ina >> 0) & 0xff) * scale_value; 224 | return ((r << 16) | (g << 8) | b); 225 | } 226 | 227 | // 228 | // Fade blue -> green -> red. I've built in a logarithmic response to make it more of a 229 | // dB meter. 230 | int main() { 231 | for (int i = 1; i <= numValues; i++) 232 | { 233 | double logValue = log10(i); 234 | double scaleValue = log10(i+2) / log10(numValues+2); 235 | double ratio = (logValue < breakPoint) ? logValue / breakPoint : (logValue - breakPoint) / breakPoint; 236 | uint32_t color = 0; 237 | 238 | if (logValue < breakPoint) 239 | color = blend (0x0000ff, 0x00ff00, ratio); 240 | else 241 | color = blend (0x00ff00, 0xff0000, ratio); 242 | color = scale (color, scaleValue); 243 | cout << "0x" << hex << color << ", "; 244 | } 245 | return 0; 246 | } 247 | #endif 248 | 249 | -------------------------------------------------------------------------------- /TimsProject.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #define FHT_N 256 3 | #define LIN_OUT 1 4 | #include 5 | #include 6 | #include 7 | unsigned long millis(void); 8 | 9 | #define PIN 6 // digital pin for programming neopixels 10 | #define NUM_PIXELS 48 // this is the size of my neopixel strip 11 | #define GROUP_SIZE 6 // num pixels in each of the sections 12 | //#define NUM_PIXELS 64 // this is the size of my neopixel strip 13 | //#define GROUP_SIZE 8 // num pixels in each of the sections 14 | #define BUTTON 3 // Pushbutton connected to gpio 3 15 | #define ROTARY_A 2 // Rotary encoder connected to gpio 2 and 5 16 | #define ROTARY_B 5 17 | 18 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800); 19 | LED_animations animations (strip, 50, GROUP_SIZE); 20 | 21 | #define ADC_CHANNEL 0 // microphone input 22 | volatile uint32_t samplePos = 0; // Buffer position counter 23 | 24 | static const uint8_t PROGMEM 25 | // This is low-level noise that's subtracted from each FHT output column 26 | // This was experimentally determined in a quiet room. 27 | noise[128]={ 28 | 50, 12, 10, 8, 7, 6, 6, 5, // 0 29 | 5, 5, 4, 4, 4, 4, 4, 4, // 8 30 | 4, 4, 4, 4, 4, 4, 4, 4, // 16 31 | 4, 4, 4, 4, 4, 4, 4, 4, // 24 32 | 4, 4, 4, 4, 4, 4, 4, 4, // 32 33 | 3, 3, 3, 4, 3, 3, 3, 3, // 40 34 | 3, 3, 3, 3, 3, 3, 3, 3, // 48 35 | 3, 3, 3, 3, 3, 3, 3, 3, // 56 36 | 3, 3, 3, 3, 3, 3, 3, 3, // 64 37 | 3, 3, 3, 3, 3, 3, 3, 3, // 72 38 | 3, 3, 3, 3, 3, 3, 3, 3, // 80 39 | 3, 3, 3, 3, 3, 3, 3, 3, // 88 40 | 3, 3, 3, 3, 3, 3, 3, 3, // 96 41 | 3, 3, 3, 3, 3, 3, 3, 3, // 104 42 | 3, 3, 3, 3, 3, 3, 3, 3, // 112 43 | 3, 3, 3, 3, 3, 3, 3, 3 // 120 44 | }; 45 | 46 | // the 128 output buckets of the fht transform get compressed down 47 | // to fewer slots for display. This array is the starting output 48 | // bucket for each display slot. You use more output buckets per 49 | // display slot as you go up. 50 | static const uint8_t PROGMEM 51 | slots[] = { 52 | 1, 2, 3, 6, 10, 18, 34, 66, 127 53 | } ; 54 | 55 | #define THRESHOLD 1 56 | 57 | void setup() { 58 | strip.setBrightness(255); 59 | // set up the input and interrupt for the mode switch button press 60 | pinMode (BUTTON, INPUT_PULLUP); 61 | attachInterrupt(digitalPinToInterrupt(BUTTON), ISR2, FALLING); 62 | pinMode (ROTARY_A, INPUT_PULLUP); 63 | attachInterrupt(digitalPinToInterrupt(ROTARY_A), ISR3, CHANGE); 64 | pinMode (ROTARY_B, INPUT_PULLUP); 65 | 66 | // The prescaler settings determine the frequency of audio sampling. We can sample higher 67 | // frequencies with a lower prescaler value, but it will also raise the lowest frequency that 68 | // we can sample. With this setup, I seem to be getting around 300Hz-9.6KHz response. There is 69 | // some aliasing going on, meaning that frequencies > 9.6KHz will show up in lower frequency 70 | // response. 71 | // Init ADC free-run mode; f = ( 16MHz/prescaler ) / 13 cycles/conversion 72 | ADMUX = _BV(6) | ADC_CHANNEL; // Channel sel, right-adj, use AREF pin 73 | ADCSRA = _BV(ADEN) | // ADC enable 74 | _BV(ADSC) | // ADC start 75 | _BV(ADATE) | // Auto trigger 76 | _BV(ADIE) | // Interrupt enable 77 | // select the prescaler value. Note that the max frequency our FFT will 78 | // display is half the sample rate. 79 | // _BV(ADPS2) | _BV(ADPS0); // 32:1 / 13 = 38,460 Hz 80 | _BV(ADPS2) | _BV(ADPS1); // 64:1 / 13 = 19,230 Hz 81 | // _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0); // 128:1 / 13 = 9615 Hz 82 | ADCSRB = 0; // Free run mode, no high MUX bit 83 | DIDR0 = 1 << ADC_CHANNEL; // Turn off digital input for ADC pin 84 | // Neopixels setup 85 | strip.begin(); // Initialize all pixels to 'off' 86 | strip.show(); // write to neopixels 87 | Serial.begin(9600); // set up Serial library at 9600 bps for debugging purposes 88 | Serial.println("begin final"); 89 | } 90 | 91 | // Animation modes that you can cycle through 92 | enum Mode { 93 | ModeSpectrumAnalyzer, 94 | ModeVUMeter, 95 | ModeRainbowChase, 96 | ModeColorWheel, 97 | ModeRainbow2, 98 | ModeWipeRed, 99 | ModeWipeGreen, 100 | ModeWipeBlue, 101 | ModeChaseRed, 102 | ModeChaseGreen, 103 | ModeChaseBlue, 104 | ModeCount 105 | }; 106 | volatile int CurrentMode = ModeSpectrumAnalyzer; 107 | volatile int brightness = 0xff; 108 | volatile unsigned long lastInterrupt = 0; 109 | 110 | void loop() { 111 | strip.setBrightness(brightness); 112 | switch (CurrentMode) 113 | { 114 | case ModeSpectrumAnalyzer: SpectrumAnalyzerMode(); break; 115 | case ModeVUMeter: VUMeterMode(); break; 116 | case ModeColorWheel: animations.Rainbow(80); break; // display about 1/3 of the spectrum at once 117 | case ModeRainbow2: animations.Rainbow(256); break; // display whole spectrum at once 118 | case ModeWipeRed: animations.ColorWipe(0xff0000, 200); break; 119 | case ModeWipeGreen: animations.ColorWipe(0x00ff00, 200); break; 120 | case ModeWipeBlue: animations.ColorWipe(0x0000ff, 200); break; 121 | case ModeChaseRed: animations.TheaterChase(0xff0000, 200); break; 122 | case ModeChaseGreen: animations.TheaterChase(0x00ff00, 200); break; 123 | case ModeChaseBlue: animations.TheaterChase(0x0000ff, 200); break; 124 | case ModeRainbowChase: animations.TheaterChaseRainbow(2000); break; 125 | } 126 | } 127 | 128 | // 129 | // Run a FHT algorithm and massage the results into colors to display on LEDs 130 | void SpectrumAnalyzerMode() 131 | { 132 | while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish 133 | // Do the FHT algorithm 134 | fht_window(); 135 | fht_reorder(); 136 | fht_run(); 137 | fht_mag_lin(); 138 | // 139 | // remove low level noise, stops the LEDs from blinking when there is no sound 140 | // this is frequency dependant and experimentally determined. 141 | for (int i = 0; i < 128; i++) 142 | { 143 | int noiseVal = pgm_read_byte(&noise[i]) << 6; 144 | fht_lin_out[i] = (fht_lin_out[i] > noiseVal) ? fht_lin_out[i]-noiseVal : 0; 145 | } 146 | samplePos = 0; // Reset sample counter 147 | ADCSRA |= _BV(ADIE); // Resume sampling interrupt 148 | 149 | // Combine the 128 frequency output slots into a smaller number of output 150 | // buckets, since we only have 8 lights. The FHT output is linear. Each 151 | // bucket accounts for the same range of frequencies. This is non-ideal for 152 | // audio because we percieve frequencies with a log2 response. So allocate 153 | // more FHT output buckets to display slots as you go up the frequency range. 154 | for (int i = 0; i < 8; i++) 155 | { 156 | int value = 0; 157 | int start = pgm_read_byte(&slots[i]); 158 | int end = pgm_read_byte(&slots[i+1]); 159 | for (int i2 = start; i2 < end; i2++) 160 | value += (fht_lin_out[i2] >> 6); 161 | if (value < THRESHOLD) 162 | value = 0; 163 | 164 | // display the output on the LED array. 165 | animations.setPixelColor(i, animations.logWheel(value)); 166 | } 167 | animations.show(); // write to the neopixel strip 168 | } 169 | 170 | // 171 | // Technically this isn't a real VU meter. I'm just summing the low frequency bins of the FHT output 172 | // to get the bass intensity. This bounces up and down nicely with the beat if you are listening to 173 | // dancy music. 174 | void VUMeterMode() 175 | { 176 | const float FUDGE_FACTOR = 1.25; // adjust so that we get a good response going up to the top bar 177 | while(ADCSRA & _BV(ADIE)); // Wait for audio sampling to finish 178 | // Do the FHT algorithm 179 | fht_window(); 180 | fht_reorder(); 181 | fht_run(); 182 | fht_mag_lin(); 183 | // 184 | // remove low level noise, stops the LEDs from blinking when there is no sound 185 | for (int i = 0; i < 128; i++) 186 | { 187 | int noiseVal = pgm_read_byte(&noise[i]) << 6; 188 | fht_lin_out[i] = (fht_lin_out[i] > noiseVal) ? fht_lin_out[i]-noiseVal : 0; 189 | } 190 | samplePos = 0; // Reset sample counter 191 | ADCSRA |= _BV(ADIE); // Resume sampling interrupt 192 | 193 | int32_t rms_Level = 0; // not really RMS 194 | for (int bucket = 0; bucket < 4; bucket++) // summing the 4 low frequency bins of the FHT output 195 | rms_Level += (fht_lin_out[bucket] >> 6); // down convert from 16 bit signed values of the FHT 196 | rms_Level *= FUDGE_FACTOR; // typical music never triggerd the max level, so scale it up 197 | 198 | for (uint8_t row = 0; row < 8; row++) 199 | { 200 | uint32_t rowColor = (rms_Level > row*8) ? animations.logWheel(row*8) : 0; 201 | animations.setPixelColor(row, rowColor); 202 | } 203 | animations.show(); 204 | } 205 | 206 | // interrupt service routine. This gets called each time the ADC finishes 1 sample. 207 | ISR(ADC_vect) { // Audio-sampling interrupt 208 | uint32_t sample = ADC - 256; // ADC values are 0-1024, but with this mic and aref we'll only see half that. 209 | // convert to +/- 256 by subtracting off 256 210 | fht_input[samplePos] = sample << 6; // then left shift it up to a 16 bit signed for the FFT algorithm 211 | // Since the FFT is using integer arithmetic, we want the values as large 212 | // as possible. 213 | if(++samplePos >= FHT_N) 214 | ADCSRA &= ~_BV(ADIE); // Buffer full, interrupt off 215 | } 216 | 217 | // another interrupt service routine for the button input 218 | void ISR2() { 219 | unsigned long m = millis(); 220 | if ((m - lastInterrupt) > 500) // we get multiple interrupts for each button press 221 | { // so only allow one mode switch per second 222 | CurrentMode = (CurrentMode+1) % ModeCount; 223 | animations.SetInterruptFlag(); // make the current animation bail out early 224 | lastInterrupt = m; 225 | Serial.print ("mode="); 226 | Serial.println(CurrentMode); 227 | } 228 | } 229 | 230 | 231 | enum { NOT_LEGAL, CW, CCW}; // impossible state, clockwise turn, counter clockwise turn 232 | 233 | // 234 | // This is a table of every combination of AB (previous read) -> AB (current read). 235 | // Only certain state transitions are meaningful. 236 | static const uint8_t PROGMEM 237 | legalTransitions[] = { 238 | NOT_LEGAL, // 00->00 239 | CCW, // 00->01 240 | CW, // 00->10 241 | NOT_LEGAL, // 00->11 242 | CW, // 01->00 243 | NOT_LEGAL, // 01->01 244 | NOT_LEGAL, // 01->10 245 | CCW, // 01->11 246 | CCW, // 10->00 247 | NOT_LEGAL, // 10->01 248 | NOT_LEGAL, // 10->10 249 | CW, // 10->11 250 | NOT_LEGAL, // 11->00 251 | CW, // 11->01 252 | CCW, // 11->10 253 | NOT_LEGAL // 11->11 254 | }; 255 | 256 | // 257 | // interrupt service routine for the rotary encoder 258 | // When we get this interrupt, we start reading both the A and the B inputs for the rotary 259 | // encoder until we see a legal state transition. The transisiton should tell us which way 260 | // the encoder was turned. 261 | void ISR3() { 262 | unsigned long m = millis(); 263 | if ((m - lastInterrupt) < 50) // we get multiple interrupts for each turn of the encoder 264 | return; // so don't process one right after another 265 | // 266 | // Read the input pins up to 50 times looking for a legal transition. 267 | int lastState = -1; // unknown previous state 268 | for (int i = 0; i < 5000; i++) 269 | { 270 | int state = ((digitalRead(ROTARY_A) == HIGH) ? 0x2 : 0) | ((digitalRead(ROTARY_B) == HIGH) ? 0x1 : 0); 271 | int transition = lastState << 2 | state; 272 | if (lastState == -1) // unknown previous state 273 | { 274 | lastState = state; 275 | } 276 | else 277 | { 278 | int transitionState = pgm_read_byte(&legalTransitions[transition]); 279 | if (transitionState == CW || transitionState == CCW) 280 | { 281 | brightness = constrain(brightness + ((transitionState == CW) ? -20 : 20), 0, 255); 282 | animations.SetInterruptFlag(); // make the current animation bail out early 283 | // Serial.println(brightness); // debug 284 | // Serial.println(transitionState == CW ? "CW" : "CCW"); 285 | lastInterrupt = m; 286 | break; 287 | } 288 | } 289 | } 290 | } 291 | 292 | -------------------------------------------------------------------------------- /OrbLight.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include // wifi access 3 | extern "C" { 4 | #include "user_interface.h" 5 | } 6 | #include 7 | 8 | ////////////////////// 9 | // WiFi Definitions // 10 | ////////////////////// 11 | const char clientName[] = "OrbLight"; 12 | #define PIN 5 13 | #define NUM_PIXELS 16 14 | #define RED 0xFF0000 15 | #define GREEN 0x00FF00 16 | #define BLUE 0x0000FF 17 | #define WHITE 0x7F7F7F 18 | 19 | const int LED_PIN = 5; // Thing's onboard, green LED 20 | 21 | // Parameter 1 = number of pixels in strip 22 | // Parameter 2 = Arduino pin number (most are valid) 23 | // Parameter 3 = pixel type flags, add together as needed: 24 | // NEO_KHZ800 800 KHz bitstream (most NeoPixel products w/WS2812 LEDs) 25 | // NEO_KHZ400 400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers) 26 | // NEO_GRB Pixels are wired for GRB bitstream (most NeoPixel products) 27 | // NEO_RGB Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2) 28 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800); 29 | 30 | WiFiServer server(80); 31 | 32 | 33 | // Input a value 0 to 255 to get a color value. 34 | // The colours are a transition r - g - b - back to r. 35 | uint32_t Wheel(byte WheelPos) { 36 | WheelPos = 255 - WheelPos; 37 | if(WheelPos < 85) { 38 | return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3); 39 | } 40 | if(WheelPos < 170) { 41 | WheelPos -= 85; 42 | return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3); 43 | } 44 | WheelPos -= 170; 45 | return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0); 46 | } 47 | 48 | // 49 | // Blend two colors together based on the ratio. ratio of 0.0 will be 100% color a and 50 | // ratio of 1.0 will be 100% color b. 51 | uint32_t blend (uint32_t ina, uint32_t inb, double ratio) 52 | { 53 | uint32_t r = (((ina >> 16) & 0xff) * (1.0-ratio)) + (((inb >> 16) & 0xff) * ratio); 54 | uint32_t g = (((ina >> 8) & 0xff) * (1.0-ratio)) + (((inb >> 8) & 0xff) * ratio); 55 | uint32_t b = (((ina >> 0) & 0xff) * (1.0-ratio)) + (((inb >> 0) & 0xff) * ratio); 56 | return ((r << 16) | (g << 8) | b); 57 | } 58 | 59 | struct colorValue { 60 | uint8_t value; 61 | uint32_t color; 62 | }; 63 | 64 | uint32_t getColorForValue(int value, const struct colorValue *colorValues, int numValues) 65 | { 66 | int foundIndex; 67 | for (foundIndex = 0; foundIndex < numValues; foundIndex++) 68 | if (value < pgm_read_byte(&(colorValues[foundIndex].value))) 69 | break; 70 | double ratio = 0; 71 | if (foundIndex == 0) 72 | return pgm_read_dword(&(colorValues[0].color)); 73 | else if (foundIndex == numValues) 74 | return pgm_read_dword(&(colorValues[numValues-1].color)); 75 | else 76 | { 77 | uint32_t prevColor = pgm_read_dword(&(colorValues[foundIndex-1].color)); 78 | uint32_t nextColor = pgm_read_dword(&(colorValues[foundIndex].color)); 79 | uint8_t nextValue = pgm_read_byte(&(colorValues[foundIndex].value)); 80 | uint8_t prevValue = pgm_read_byte(&(colorValues[foundIndex-1].value)); 81 | double ratio = ((double)value - prevValue) / (nextValue - prevValue); 82 | return blend (prevColor, nextColor, ratio); 83 | } 84 | return 0; 85 | } 86 | 87 | // Functions list 88 | enum functions 89 | { 90 | TEMPERATURE, 91 | COLOR, 92 | PULSE, 93 | COLOR_CHASE, 94 | RAINBOW 95 | }; 96 | 97 | int currentFunction = TEMPERATURE; // default 98 | uint32_t COLOR_parameter = 0xffffff; // parameter for color function 99 | uint32_t COLOR_parameter2 = 0xffffff; // parameter for color function 100 | 101 | float currentTemperature = 0; 102 | 103 | void connectWiFi() 104 | { 105 | strip.begin(); 106 | strip.setPixelColor(0, 0xff0000); 107 | strip.show(); // Initialize all pixels to 'off' 108 | wifi_station_set_hostname((char*)clientName); 109 | WiFiManager wifiManager; 110 | //reset saved settings 111 | //wifiManager.resetSettings(); 112 | wifiManager.autoConnect("OrbLightAP"); 113 | 114 | strip.setPixelColor(0, 0xffff00); 115 | strip.show(); // Initialize all pixels to 'off' 116 | Serial.println("WiFi connected"); 117 | Serial.println("IP address: "); 118 | Serial.println(WiFi.localIP()); 119 | } 120 | 121 | void connectServer() 122 | { 123 | // Start the server 124 | server.begin(); 125 | Serial.println("Server started"); 126 | 127 | // Print the IP address 128 | Serial.print("Use this URL to connect: "); 129 | Serial.print("http://"); 130 | Serial.print(WiFi.localIP()); 131 | Serial.println("/"); 132 | 133 | } 134 | 135 | 136 | void pollServer() 137 | { 138 | // Check if a client has connected 139 | WiFiClient client = server.available(); 140 | if (!client) { 141 | return; 142 | } 143 | 144 | // Wait until the client sends some data 145 | int counter = 0; 146 | while(!client.available()){ 147 | delay(10); 148 | if (counter++ > 100) // timeout 149 | { 150 | Serial.println ("client timeout"); 151 | return; 152 | } 153 | } 154 | 155 | // Read the first line of the request 156 | String request = client.readStringUntil('\r'); 157 | // Serial.println(request); 158 | int startIndex = request.indexOf("?"); 159 | if (startIndex >= 0) 160 | { 161 | String params = request.substring(startIndex+1, request.indexOf(" ", startIndex)); 162 | startIndex = 0; 163 | while (1) 164 | { 165 | int equalIndex = params.indexOf("=", startIndex); 166 | int andIndex = params.indexOf("&", startIndex); 167 | if (equalIndex >= 0) 168 | { 169 | String key = params.substring(startIndex, equalIndex); 170 | String value = (andIndex >= 0) ? params.substring(equalIndex+1, andIndex) : params.substring(equalIndex+1); 171 | if (key == "mode") 172 | { 173 | if (value == "temperature") 174 | { 175 | Serial.println("Temperature function"); 176 | currentFunction = TEMPERATURE; 177 | } else if (value == "color") { 178 | Serial.println("Color function"); 179 | currentFunction = COLOR; 180 | } else if (value == "pulse") { 181 | Serial.println("Pulse color function"); 182 | currentFunction = PULSE; 183 | } else if (value == "color_chase") { 184 | Serial.println("Color chase function"); 185 | currentFunction = COLOR_CHASE; 186 | } else if (value == "rainbow") { 187 | Serial.println("Rainbow function"); 188 | currentFunction = RAINBOW; 189 | } 190 | } 191 | else if (key == "color1") 192 | { 193 | COLOR_parameter = strtoul(value.c_str(), 0, 0); 194 | Serial.println("Color parameter"); 195 | Serial.println(value); 196 | } 197 | else if (key == "color2") 198 | { 199 | COLOR_parameter2 = strtoul(value.c_str(), 0, 0); 200 | Serial.println("Color parameter 2"); 201 | Serial.println(value); 202 | } 203 | } 204 | if (equalIndex == -1 || andIndex == -1) 205 | break; 206 | startIndex = andIndex+1; 207 | } 208 | } 209 | client.flush(); 210 | 211 | // Return the response 212 | client.println("HTTP/1.1 200 OK"); 213 | client.println("Content-Type: text/html"); 214 | client.println(""); // do not forget this one 215 | client.println(""); 216 | client.println(""); 217 | client.println("Display temperature: "); 218 | client.println(currentTemperature); 219 | client.println("
"); 220 | client.println("Display color RED
"); 221 | client.println("Display color GREEN
"); 222 | client.println("Display color BLUE
"); 223 | client.println("Pulse color RED
"); 224 | client.println("Pulse color GREEN
"); 225 | client.println("Pulse color BLUE
"); 226 | client.println("Color chase RED/GREEN
"); 227 | client.println("Rainbow mode
"); 228 | client.println(""); 229 | } 230 | 231 | ///////////////////////////////////////////////////////////////////////////////////////////// 232 | // Temperature display function 233 | // 234 | // The temperature -> color mapping table is going into program space to save ram 235 | #define NUM_TEMPS 8 236 | static const PROGMEM struct colorValue colorsForTemps[NUM_TEMPS] = 237 | { 238 | { 30, 0xff00ff }, // purple 239 | { 40, 0x3f00ff }, // blue purple 240 | { 50, 0x0000ff }, // blue 241 | { 60, 0x00ffd0 }, // turquoise 242 | { 70, 0x00ff00 }, // green 243 | { 80, 0xfdff00 }, // yellow 244 | { 90, 0xFFa000 }, // orange 245 | { 100, 0xff0000 } // red 246 | }; 247 | 248 | void getTime() 249 | { 250 | const int TIME_CHECK_FREQUENCY = 600000; // 10 minutes 251 | static unsigned long lastPoll = 0; 252 | static const char server[] = "www.timeapi.org"; 253 | static const char location[] = "GET /utc/now/ HTTP/1.1"; 254 | WiFiClient client; 255 | 256 | // We only want to grab the temperature every 10 minutes 257 | unsigned long currentTime = millis(); 258 | if (lastPoll == 0 || (currentTime - lastPoll) > TIME_CHECK_FREQUENCY) 259 | { 260 | lastPoll = currentTime; 261 | 262 | //connect to the server 263 | Serial.print("connecting to "); 264 | Serial.println(server); 265 | 266 | //port 80 is typical of a www page 267 | if (client.connect(server, 80)) { 268 | Serial.println(location); 269 | client.println(location); 270 | client.println("Connection: close"); 271 | client.println(); 272 | 273 | String response; 274 | while (client.available()) 275 | response += (char)client.read(); 276 | Serial.print ("Response = "); 277 | Serial.println(response); 278 | 279 | // if the server's disconnected, stop the client: 280 | if (!client.connected()) { 281 | Serial.println(); 282 | Serial.println("disconnecting from server."); 283 | client.stop(); 284 | } 285 | } 286 | } 287 | } 288 | 289 | void showTemp(){ 290 | const int TEMPERATURE_CHECK_FREQUENCY = 600000; // 10 minutes 291 | static unsigned long lastPoll = 0; 292 | static uint32_t currentTemperatureColor = 0; 293 | static const char server[] = "api.openweathermap.org"; 294 | static const char location[] = "GET /data/2.5/weather?zip=94538,us&APPID=b02ef0450929750e35b63e7591fcdbc2&units=imperial"; 295 | WiFiClient client; 296 | 297 | // We only want to grab the temperature every 10 minutes 298 | unsigned long currentTime = millis(); 299 | if (lastPoll == 0 || (currentTime - lastPoll) > TEMPERATURE_CHECK_FREQUENCY) 300 | { 301 | lastPoll = currentTime; 302 | 303 | //connect to the server 304 | Serial.println(server); 305 | 306 | //port 80 is typical of a www page 307 | if (client.connect(server, 80)) { 308 | Serial.println(location); 309 | client.println(location); 310 | client.println(); 311 | 312 | String response; 313 | while (client.available()) 314 | response += (char)client.read(); 315 | 316 | //Serial.println(response); 317 | 318 | int indexOfTemp = response.indexOf("\"temp\":"); 319 | if (indexOfTemp >= 0) 320 | { 321 | int indexOfCommaAfterTemp = (indexOfTemp >= 0) ? response.indexOf(",", indexOfTemp) : -1; 322 | String tempString = (indexOfCommaAfterTemp >= 0) ? response.substring (indexOfTemp+7, indexOfCommaAfterTemp) : ""; 323 | float tempKelvin = (tempString.length() > 0) ? atof(tempString.c_str()) : 0; 324 | currentTemperature = (tempKelvin > 0) ? (tempKelvin * 9 / 5 - 459.67) : -1; 325 | // Serial.println(temperature); 326 | 327 | currentTemperatureColor = getColorForValue(currentTemperature, colorsForTemps, NUM_TEMPS); 328 | } 329 | } 330 | } 331 | for (int pixel = 0; pixel < NUM_PIXELS; pixel++) 332 | strip.setPixelColor(pixel, currentTemperatureColor); 333 | strip.show(); 334 | } 335 | 336 | //////////////////////////////////////////////////////////////////////////// 337 | // Solid color function 338 | void showColor() 339 | { 340 | for (int pixel = 0; pixel < NUM_PIXELS; pixel++) 341 | strip.setPixelColor(pixel, COLOR_parameter); 342 | strip.show(); 343 | } 344 | 345 | //////////////////////////////////////////////////////////////////////////// 346 | // Pulse color function 347 | void pulseColor() 348 | { 349 | for (int i = 0; i < 510; i++) 350 | { 351 | if (i < 256) 352 | strip.setBrightness(255-i); 353 | else 354 | strip.setBrightness(i-255); 355 | showColor(); 356 | pollServer(); 357 | if (currentFunction != PULSE) 358 | { 359 | strip.setBrightness(255); 360 | break; 361 | } 362 | delay(10); 363 | } 364 | } 365 | 366 | 367 | ///////////////////////////////////////////////////////////////////////////////////////////// 368 | // Color chase function 369 | void colorChase() 370 | { 371 | for (int i1 = 0; i1 < NUM_PIXELS; i1++) 372 | { 373 | for (int i2 = 0; i2 < NUM_PIXELS; i2++) 374 | strip.setPixelColor((i2 + i1) % NUM_PIXELS, (i2 < NUM_PIXELS/2) ? COLOR_parameter : COLOR_parameter2); 375 | strip.show(); 376 | delay(50); 377 | pollServer(); 378 | if (currentFunction != COLOR_CHASE) 379 | break; 380 | } 381 | } 382 | 383 | ///////////////////////////////////////////////////////////////////////////////////////////// 384 | // Color chase function 385 | void rainbow() 386 | { 387 | for (int i1 = 0; i1 < 256; i1++) 388 | { 389 | for (int i2 = 0; i2 < NUM_PIXELS; i2++) 390 | strip.setPixelColor (i2, Wheel(i1)); 391 | strip.show(); 392 | delay(50); 393 | pollServer(); 394 | if (currentFunction != RAINBOW) 395 | break; 396 | } 397 | } 398 | 399 | ///////////////////////////////////////////////////////////////////////////////////////////// 400 | // Setup and main loop 401 | // 402 | void setup() { 403 | Serial.begin(9600); 404 | connectWiFi(); 405 | connectServer(); 406 | 407 | strip.setBrightness (0xff); 408 | strip.begin(); 409 | strip.show(); // Initialize all pixels to 'off' 410 | 411 | } 412 | 413 | void loop(){ 414 | 415 | pollServer(); 416 | // getTime(); 417 | switch (currentFunction) 418 | { 419 | case COLOR: showColor(); break; 420 | case TEMPERATURE: showTemp(); break; 421 | case PULSE: pulseColor(); break; 422 | case COLOR_CHASE: colorChase(); break; 423 | case RAINBOW: rainbow(); break; 424 | } 425 | delay(10); // give the processor time to do its thing 426 | } 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | -------------------------------------------------------------------------------- /myMPU6050.ino: -------------------------------------------------------------------------------- 1 | // I2C device class (I2Cdev) demonstration Arduino sketch for MPU6050 class using DMP (MotionApps v2.0) 2 | // 6/21/2012 by Jeff Rowberg 3 | // Updates should (hopefully) always be available at https://github.com/jrowberg/i2cdevlib 4 | // 5 | // Changelog: 6 | // 2013-05-08 - added seamless Fastwire support 7 | // - added note about gyro calibration 8 | // 2012-06-21 - added note about Arduino 1.0.1 + Leonardo compatibility error 9 | // 2012-06-20 - improved FIFO overflow handling and simplified read process 10 | // 2012-06-19 - completely rearranged DMP initialization code and simplification 11 | // 2012-06-13 - pull gyro and accel data from FIFO packet instead of reading directly 12 | // 2012-06-09 - fix broken FIFO read sequence and change interrupt detection to RISING 13 | // 2012-06-05 - add gravity-compensated initial reference frame acceleration output 14 | // - add 3D math helper file to DMP6 example sketch 15 | // - add Euler output and Yaw/Pitch/Roll output formats 16 | // 2012-06-04 - remove accel offset clearing for better results (thanks Sungon Lee) 17 | // 2012-06-01 - fixed gyro sensitivity to be 2000 deg/sec instead of 250 18 | // 2012-05-30 - basic DMP initialization working 19 | 20 | /* ============================================ 21 | I2Cdev device library code is placed under the MIT license 22 | Copyright (c) 2012 Jeff Rowberg 23 | 24 | Permission is hereby granted, free of charge, to any person obtaining a copy 25 | of this software and associated documentation files (the "Software"), to deal 26 | in the Software without restriction, including without limitation the rights 27 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 28 | copies of the Software, and to permit persons to whom the Software is 29 | furnished to do so, subject to the following conditions: 30 | 31 | The above copyright notice and this permission notice shall be included in 32 | all copies or substantial portions of the Software. 33 | 34 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 35 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 36 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 37 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 38 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 39 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 40 | THE SOFTWARE. 41 | =============================================== 42 | */ 43 | #include 44 | #include 45 | 46 | // I2Cdev and MPU6050 must be installed as libraries, or else the .cpp/.h files 47 | // for both classes must be in the include path of your project 48 | #include "I2Cdev.h" 49 | 50 | #include "MPU6050_6Axis_MotionApps20.h" 51 | //#include "MPU6050.h" // not necessary if using MotionApps include file 52 | 53 | // Arduino Wire library is required if I2Cdev I2CDEV_ARDUINO_WIRE implementation 54 | // is used in I2Cdev.h 55 | #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE 56 | #include "Wire.h" 57 | #endif 58 | 59 | // class default I2C address is 0x68 60 | // specific I2C addresses may be passed as a parameter here 61 | // AD0 low = 0x68 (default for SparkFun breakout and InvenSense evaluation board) 62 | // AD0 high = 0x69 63 | MPU6050 mpu; 64 | //MPU6050 mpu(0x69); // <-- use for AD0 high 65 | 66 | #define PIN 6 // digital pin for programming neopixels 67 | #define NUM_PIXELS 64 // this is the size of my neopixel strip 68 | #define GROUP_SIZE 1 // num pixels in each of the sections 69 | //#define NUM_PIXELS 64 // this is the size of my neopixel strip 70 | //#define GROUP_SIZE 8 // num pixels in each of the sections 71 | 72 | 73 | Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_PIXELS, PIN, NEO_GRB + NEO_KHZ800); 74 | LED_animations animations (strip, 50, GROUP_SIZE); 75 | 76 | /* ========================================================================= 77 | NOTE: In addition to connection 3.3v, GND, SDA, and SCL, this sketch 78 | depends on the MPU-6050's INT pin being connected to the Arduino's 79 | external interrupt #0 pin. On the Arduino Uno and Mega 2560, this is 80 | digital I/O pin 2. 81 | * ========================================================================= */ 82 | 83 | /* ========================================================================= 84 | NOTE: Arduino v1.0.1 with the Leonardo board generates a compile error 85 | when using Serial.write(buf, len). The Teapot output uses this method. 86 | The solution requires a modification to the Arduino USBAPI.h file, which 87 | is fortunately simple, but annoying. This will be fixed in the next IDE 88 | release. For more info, see these links: 89 | 90 | http://arduino.cc/forum/index.php/topic,109987.0.html 91 | http://code.google.com/p/arduino/issues/detail?id=958 92 | * ========================================================================= */ 93 | 94 | 95 | 96 | // uncomment "OUTPUT_READABLE_QUATERNION" if you want to see the actual 97 | // quaternion components in a [w, x, y, z] format (not best for parsing 98 | // on a remote host such as Processing or something though) 99 | //#define OUTPUT_READABLE_QUATERNION 100 | 101 | // uncomment "OUTPUT_READABLE_EULER" if you want to see Euler angles 102 | // (in degrees) calculated from the quaternions coming from the FIFO. 103 | // Note that Euler angles suffer from gimbal lock (for more info, see 104 | // http://en.wikipedia.org/wiki/Gimbal_lock) 105 | //#define OUTPUT_READABLE_EULER 106 | 107 | // uncomment "OUTPUT_READABLE_YAWPITCHROLL" if you want to see the yaw/ 108 | // pitch/roll angles (in degrees) calculated from the quaternions coming 109 | // from the FIFO. Note this also requires gravity vector calculations. 110 | // Also note that yaw/pitch/roll angles suffer from gimbal lock (for 111 | // more info, see: http://en.wikipedia.org/wiki/Gimbal_lock) 112 | #define OUTPUT_READABLE_YAWPITCHROLL 113 | 114 | // uncomment "OUTPUT_READABLE_REALACCEL" if you want to see acceleration 115 | // components with gravity removed. This acceleration reference frame is 116 | // not compensated for orientation, so +X is always +X according to the 117 | // sensor, just without the effects of gravity. If you want acceleration 118 | // compensated for orientation, us OUTPUT_READABLE_WORLDACCEL instead. 119 | //#define OUTPUT_READABLE_REALACCEL 120 | 121 | // uncomment "OUTPUT_READABLE_WORLDACCEL" if you want to see acceleration 122 | // components with gravity removed and adjusted for the world frame of 123 | // reference (yaw is relative to initial orientation, since no magnetometer 124 | // is present in this case). Could be quite handy in some cases. 125 | //#define OUTPUT_READABLE_WORLDACCEL 126 | 127 | // uncomment "OUTPUT_TEAPOT" if you want output that matches the 128 | // format used for the InvenSense teapot demo 129 | //#define OUTPUT_TEAPOT 130 | 131 | 132 | 133 | #define LED_PIN 13 // (Arduino is 13, Teensy is 11, Teensy++ is 6) 134 | bool blinkState = false; 135 | 136 | // MPU control/status vars 137 | bool dmpReady = false; // set true if DMP init was successful 138 | uint8_t mpuIntStatus; // holds actual interrupt status byte from MPU 139 | uint8_t devStatus; // return status after each device operation (0 = success, !0 = error) 140 | uint16_t packetSize; // expected DMP packet size (default is 42 bytes) 141 | uint16_t fifoCount; // count of all bytes currently in FIFO 142 | uint8_t fifoBuffer[64]; // FIFO storage buffer 143 | 144 | // orientation/motion vars 145 | Quaternion q; // [w, x, y, z] quaternion container 146 | VectorInt16 aa; // [x, y, z] accel sensor measurements 147 | VectorInt16 aaReal; // [x, y, z] gravity-free accel sensor measurements 148 | VectorInt16 aaWorld; // [x, y, z] world-frame accel sensor measurements 149 | VectorFloat gravity; // [x, y, z] gravity vector 150 | float euler[3]; // [psi, theta, phi] Euler angle container 151 | float ypr[3]; // [yaw, pitch, roll] yaw/pitch/roll container and gravity vector 152 | 153 | // packet structure for InvenSense teapot demo 154 | uint8_t teapotPacket[14] = { '$', 0x02, 0,0, 0,0, 0,0, 0,0, 0x00, 0x00, '\r', '\n' }; 155 | 156 | 157 | 158 | // ================================================================ 159 | // === INTERRUPT DETECTION ROUTINE === 160 | // ================================================================ 161 | 162 | volatile bool mpuInterrupt = false; // indicates whether MPU interrupt pin has gone high 163 | void dmpDataReady() { 164 | mpuInterrupt = true; 165 | } 166 | 167 | 168 | 169 | // ================================================================ 170 | // === INITIAL SETUP === 171 | // ================================================================ 172 | 173 | void setup() { 174 | // join I2C bus (I2Cdev library doesn't do this automatically) 175 | #if I2CDEV_IMPLEMENTATION == I2CDEV_ARDUINO_WIRE 176 | Wire.begin(); 177 | TWBR = 24; // 400kHz I2C clock (200kHz if CPU is 8MHz). Comment this line if having compilation difficulties with TWBR. 178 | #elif I2CDEV_IMPLEMENTATION == I2CDEV_BUILTIN_FASTWIRE 179 | Fastwire::setup(400, true); 180 | #endif 181 | 182 | // initialize serial communication 183 | // (115200 chosen because it is required for Teapot Demo output, but it's 184 | // really up to you depending on your project) 185 | Serial.begin(115200); 186 | while (!Serial); // wait for Leonardo enumeration, others continue immediately 187 | 188 | // NOTE: 8MHz or slower host processors, like the Teensy @ 3.3v or Ardunio 189 | // Pro Mini running at 3.3v, cannot handle this baud rate reliably due to 190 | // the baud timing being too misaligned with processor ticks. You must use 191 | // 38400 or slower in these cases, or use some kind of external separate 192 | // crystal solution for the UART timer. 193 | 194 | // initialize device 195 | Serial.println(F("Initializing I2C devices...")); 196 | mpu.initialize(); 197 | 198 | // verify connection 199 | Serial.println(F("Testing device connections...")); 200 | Serial.println(mpu.testConnection() ? F("MPU6050 connection successful") : F("MPU6050 connection failed")); 201 | 202 | // wait for ready 203 | Serial.println(F("\nSend any character to begin DMP programming and demo: ")); 204 | while (Serial.available() && Serial.read()); // empty buffer 205 | while (!Serial.available()); // wait for data 206 | while (Serial.available() && Serial.read()); // empty buffer again 207 | 208 | // load and configure the DMP 209 | Serial.println(F("Initializing DMP...")); 210 | devStatus = mpu.dmpInitialize(); 211 | 212 | // supply your own gyro offsets here, scaled for min sensitivity 213 | mpu.setXGyroOffset(220); 214 | mpu.setYGyroOffset(76); 215 | mpu.setZGyroOffset(-85); 216 | mpu.setZAccelOffset(1788); // 1688 factory default for my test chip 217 | 218 | // make sure it worked (returns 0 if so) 219 | if (devStatus == 0) { 220 | // turn on the DMP, now that it's ready 221 | Serial.println(F("Enabling DMP...")); 222 | mpu.setDMPEnabled(true); 223 | 224 | // enable Arduino interrupt detection 225 | Serial.println(F("Enabling interrupt detection (Arduino external interrupt 0)...")); 226 | attachInterrupt(0, dmpDataReady, RISING); 227 | mpuIntStatus = mpu.getIntStatus(); 228 | 229 | // set our DMP Ready flag so the main loop() function knows it's okay to use it 230 | Serial.println(F("DMP ready! Waiting for first interrupt...")); 231 | dmpReady = true; 232 | 233 | // get expected DMP packet size for later comparison 234 | packetSize = mpu.dmpGetFIFOPacketSize(); 235 | } else { 236 | // ERROR! 237 | // 1 = initial memory load failed 238 | // 2 = DMP configuration updates failed 239 | // (if it's going to break, usually the code will be 1) 240 | Serial.print(F("DMP Initialization failed (code ")); 241 | Serial.print(devStatus); 242 | Serial.println(F(")")); 243 | } 244 | 245 | // configure LED for output 246 | pinMode(LED_PIN, OUTPUT); 247 | strip.setBrightness (0xf); 248 | strip.begin(); 249 | } 250 | 251 | 252 | 253 | // ================================================================ 254 | // === MAIN PROGRAM LOOP === 255 | // ================================================================ 256 | 257 | void loop() { 258 | // if programming failed, don't try to do anything 259 | if (!dmpReady) return; 260 | 261 | // wait for MPU interrupt or extra packet(s) available 262 | while (!mpuInterrupt && fifoCount < packetSize) { 263 | // other program behavior stuff here 264 | // . 265 | // . 266 | // . 267 | // if you are really paranoid you can frequently test in between other 268 | // stuff to see if mpuInterrupt is true, and if so, "break;" from the 269 | // while() loop to immediately process the MPU data 270 | // . 271 | // . 272 | // . 273 | } 274 | 275 | // reset interrupt flag and get INT_STATUS byte 276 | mpuInterrupt = false; 277 | mpuIntStatus = mpu.getIntStatus(); 278 | 279 | // get current FIFO count 280 | fifoCount = mpu.getFIFOCount(); 281 | 282 | // check for overflow (this should never happen unless our code is too inefficient) 283 | if ((mpuIntStatus & 0x10) || fifoCount == 1024) { 284 | // reset so we can continue cleanly 285 | mpu.resetFIFO(); 286 | Serial.println(F("FIFO overflow!")); 287 | 288 | // otherwise, check for DMP data ready interrupt (this should happen frequently) 289 | } else if (mpuIntStatus & 0x02) { 290 | // wait for correct available data length, should be a VERY short wait 291 | while (fifoCount < packetSize) fifoCount = mpu.getFIFOCount(); 292 | 293 | // read a packet from FIFO 294 | mpu.getFIFOBytes(fifoBuffer, packetSize); 295 | 296 | // track FIFO count here in case there is > 1 packet available 297 | // (this lets us immediately read more without waiting for an interrupt) 298 | fifoCount -= packetSize; 299 | 300 | #ifdef OUTPUT_READABLE_QUATERNION 301 | // display quaternion values in easy matrix form: w x y z 302 | mpu.dmpGetQuaternion(&q, fifoBuffer); 303 | Serial.print("quat\t"); 304 | Serial.print(q.w); 305 | Serial.print("\t"); 306 | Serial.print(q.x); 307 | Serial.print("\t"); 308 | Serial.print(q.y); 309 | Serial.print("\t"); 310 | Serial.println(q.z); 311 | #endif 312 | 313 | #ifdef OUTPUT_READABLE_EULER 314 | // display Euler angles in degrees 315 | mpu.dmpGetQuaternion(&q, fifoBuffer); 316 | mpu.dmpGetEuler(euler, &q); 317 | Serial.print("euler\t"); 318 | Serial.print(euler[0] * 180/M_PI); 319 | Serial.print("\t"); 320 | Serial.print(euler[1] * 180/M_PI); 321 | Serial.print("\t"); 322 | Serial.println(euler[2] * 180/M_PI); 323 | #endif 324 | 325 | #ifdef OUTPUT_READABLE_YAWPITCHROLL 326 | // display Euler angles in degrees 327 | mpu.dmpGetQuaternion(&q, fifoBuffer); 328 | mpu.dmpGetGravity(&gravity, &q); 329 | mpu.dmpGetYawPitchRoll(ypr, &q, &gravity); 330 | Serial.print("ypr\t"); 331 | Serial.print(ypr[0] * 180/M_PI); 332 | Serial.print("\t"); 333 | Serial.print(ypr[1] * 180/M_PI); 334 | Serial.print("\t"); 335 | Serial.println(ypr[2] * 180/M_PI); 336 | #endif 337 | Color r = ypr[0] * 128/M_PI + 128; 338 | Color g = ypr[1] * 128/M_PI + 128; 339 | Color b = ypr[2] * 128/M_PI + 128; 340 | Color color = r << 16 | g << 8 | b; 341 | animations.Solid (color, 0); 342 | 343 | 344 | #ifdef OUTPUT_READABLE_REALACCEL 345 | // display real acceleration, adjusted to remove gravity 346 | mpu.dmpGetQuaternion(&q, fifoBuffer); 347 | mpu.dmpGetAccel(&aa, fifoBuffer); 348 | mpu.dmpGetGravity(&gravity, &q); 349 | mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); 350 | Serial.print("areal\t"); 351 | Serial.print(aaReal.x); 352 | Serial.print("\t"); 353 | Serial.print(aaReal.y); 354 | Serial.print("\t"); 355 | Serial.println(aaReal.z); 356 | #endif 357 | 358 | #ifdef OUTPUT_READABLE_WORLDACCEL 359 | // display initial world-frame acceleration, adjusted to remove gravity 360 | // and rotated based on known orientation from quaternion 361 | mpu.dmpGetQuaternion(&q, fifoBuffer); 362 | mpu.dmpGetAccel(&aa, fifoBuffer); 363 | mpu.dmpGetGravity(&gravity, &q); 364 | mpu.dmpGetLinearAccel(&aaReal, &aa, &gravity); 365 | mpu.dmpGetLinearAccelInWorld(&aaWorld, &aaReal, &q); 366 | Serial.print("aworld\t"); 367 | Serial.print(aaWorld.x); 368 | Serial.print("\t"); 369 | Serial.print(aaWorld.y); 370 | Serial.print("\t"); 371 | Serial.println(aaWorld.z); 372 | #endif 373 | 374 | #ifdef OUTPUT_TEAPOT 375 | // display quaternion values in InvenSense Teapot demo format: 376 | teapotPacket[2] = fifoBuffer[0]; 377 | teapotPacket[3] = fifoBuffer[1]; 378 | teapotPacket[4] = fifoBuffer[4]; 379 | teapotPacket[5] = fifoBuffer[5]; 380 | teapotPacket[6] = fifoBuffer[8]; 381 | teapotPacket[7] = fifoBuffer[9]; 382 | teapotPacket[8] = fifoBuffer[12]; 383 | teapotPacket[9] = fifoBuffer[13]; 384 | Serial.write(teapotPacket, 14); 385 | teapotPacket[11]++; // packetCount, loops at 0xFF on purpose 386 | #endif 387 | 388 | // blink LED to indicate activity 389 | blinkState = !blinkState; 390 | digitalWrite(LED_PIN, blinkState); 391 | } 392 | } 393 | --------------------------------------------------------------------------------