cloud-lightning/cloud-lightning/cloud-lightning.ino

180 lines
5.6 KiB
C++

/*
* 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 <Adafruit_NeoPixel.h>
// 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;
}