/* * Copyright (c) 2015 Molly Nicholas * * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, are permitted * (subject to the limitations in the disclaimer below) provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright notice, this list of conditions * and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright notice, this list of conditions * and the following disclaimer in the documentation and/or other materials provided with the * distribution. * * Neither the name of Molly Nicholas nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. * * NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS * SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include // Parameter 1 = number of pixels in strip // Parameter 2 = pin number (most are valid) // Parameter 3 = pixel type flags, add together as needed: // NEO_RGB Pixels are wired for RGB bitstream // NEO_GRB Pixels are wired for GRB bitstream // NEO_KHZ400 400 KHz bitstream (e.g. FLORA pixels) // NEO_KHZ800 800 KHz bitstream (e.g. High Density LED strip) int NUM_LEDS = 4; int LED_PIN = 4; Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800); const int HIGH_STRIKE_LIKELIHOOD = 5; const int LOW_STRIKE_LIKELIHOOD = 10; int currentDataPoint = 0; int chance = LOW_STRIKE_LIKELIHOOD; // Simple moving average plot int NUM_Y_VALUES = 17; float yValues[] = { 0, 7, 10, 9, 7.1, 7.5, 7.4, 12, 15, 10, 0, 3, 3.5, 4, 1, 7, 1 }; float simple_moving_average_previous = 0; float random_moving_average_previous = 0; float (*functionPtrs[10])(); //the array of function pointers int NUM_FUNCTIONS = 2; void setup() { // Setup the Serial connection to talk over Bluetooth Serial.begin(9600); // Neopixel setup strip.begin(); strip.show(); // Initialize all pixels to 'off' // initializes the array of function pointers. functionPtrs[0] = simple_moving_average; functionPtrs[1] = random_moving_average; } void loop() { bool strike = false; String trigger = readFromBluetooth(); if (trigger==String("f")) { strike = true; } else if (random(chance) == 0) { strike = true; } if (strike) { int led = random(NUM_LEDS); for (int i = 0; i < 10; i++) { // Use this line to keep the lightning focused in one LED. // lightningStrike(led): // Use this line if you want the lightning to spread out among multiple LEDs. lightningStrike(random(NUM_LEDS)); } // Once there's been one strike, I make it more likely that there will be a second. chance = HIGH_STRIKE_LIKELIHOOD; } else { chance = LOW_STRIKE_LIKELIHOOD; } turnAllPixelsOff(); delay(1000); } void turnAllPixelsOff() { for (int i = 0; i < NUM_LEDS; i++) { strip.setPixelColor(i, 0); } strip.show(); } void lightningStrike(int pixel) { float brightness = callFunction(random(NUM_FUNCTIONS)); float scaledWhite = abs(brightness*500); strip.setPixelColor(pixel, strip.Color(scaledWhite, scaledWhite, scaledWhite)); strip.show(); delay(random(5, 100)); currentDataPoint++; currentDataPoint = currentDataPoint%NUM_Y_VALUES; } /** * Read the data from the BLE, breaking on '\n' and '\r' characters. */ String readFromBluetooth() { String readString = ""; while (Serial.available()) { delay(10); //small delay to allow input buffer to fill char c = Serial.read(); //gets one byte from serial buffer if (c == '\n' || c == '\r') { break; } readString += c; } return readString; } float callFunction(int index) { return (*functionPtrs[index])(); //calls the function at the index of `index` in the array } // https://en.wikipedia.org/wiki/Moving_average#Simple_moving_average float simple_moving_average() { uint32_t startingValue = currentDataPoint; uint32_t endingValue = (currentDataPoint+1)%NUM_Y_VALUES; float simple_moving_average_current = simple_moving_average_previous + (yValues[startingValue])/NUM_Y_VALUES - (yValues[endingValue])/NUM_Y_VALUES; simple_moving_average_previous = simple_moving_average_current; return simple_moving_average_current; } // Same as simple moving average, but with randomly-generated data points. float random_moving_average() { float firstValue = random(1, 10); float secondValue = random(1, 10); float random_moving_average_current = random_moving_average_previous + firstValue/NUM_Y_VALUES - secondValue/NUM_Y_VALUES; random_moving_average_previous = random_moving_average_current; return random_moving_average_current; }