/*
*/
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SerialFlash.h>
#include <FastLED.h>
#include <NXPMotionSense.h>
// GUItool: begin automatically generated code
AudioPlaySerialflashRaw playHUMraw; //xy=674,347
AudioPlaySerialflashRaw playSwingRaw; //xy=677,431
AudioPlaySerialflashRaw playFlashRaw1; //xy=679,388
AudioMixer4 mixer1; //xy=979,391
AudioOutputI2S i2s1; //xy=1209,399
AudioConnection patchCord1(playHUMraw, 0, mixer1, 0);
AudioConnection patchCord2(playSwingRaw, 0, mixer1, 2);
AudioConnection patchCord3(playFlashRaw1, 0, mixer1, 1);
AudioConnection patchCord4(mixer1, 0, i2s1, 0);
AudioConnection patchCord5(mixer1, 0, i2s1, 1);
// GUItool: end automatically generated code
NXPMotionSense imu;
NXPSensorFusion filter;
int motionThreshold = 2;
float lastPitch = 0, lastHeading = 0; // lastRoll = 0,
int buttonState = HIGH;
int bladeState = 0; //0 off, 1, fading up, 2 on, 3 fading down, 4 color change
int bladeUpDownDelay = 0; // 0 no delay, 1 wait for sound to load, before led shows, or more in case of blade down
bool flashRawPlaying = 0;
int fadeStep = 0;
int fadeStepSize = 2;
int selectedColor = 0;
int lastSelectedColor = 0;
bool isAnimating = 0;
bool bladeOn = 0;
uint32_t lastDebounceTime = 0; // the last time the output pin was toggled
uint32_t debounceDelay = 50;
int lastButtonState = HIGH;
int pendingPress = 0;
int brightStep = 0; // used to dim brightness when color changes
int brightStepSize = 20;
bool isDimming = 0;
int brightGlitch = 0; // used to make the lightsaber glitch when switching colors
//BC = Blade Color
int BCbuttonState = HIGH;
uint32_t BClastDebounceTime = 0; // the last time the output pin was toggled
uint32_t BCdebounceDelay = 50;
int BClastButtonState = HIGH;
int BCpendingPress = 0;
//int pendingColorPress = 0;
int BCbrightness = 180;
#define COLOR_BUTTON_PIN 1
#define POWER_BUTTON_PIN 0
#define COLOR_ORDER BGR
#define CHIPSET APA102
#define NUM_LEDS 120 // actual amount
#define DATA_PIN 11
#define CLOCK_PIN 13
#define LED_ACCESS_PIN 8 // 8 ! because I2S1 output on 7, 20 and 21
#define FLASH_CHIP_SELECT 6
#define BRIGHTNESS 100
#define FRAMES_PER_SECOND 70
int outputValue = 0;
int presetColors[] = {CRGB::Blue, CRGB::Green};
int totalPresetColors = (sizeof(presetColors) / sizeof(int));
int selectedColorIndex = 0;
int swingSounds = 4;
int lastSwingSound = swingSounds;
int clashSounds = 0;
CRGB leds[NUM_LEDS];
void setup() {
delay(500); // sanity delay
FastLED.addLeds<APA102, DATA_PIN, CLOCK_PIN, BGR>(leds, NUM_LEDS);
selectedColor = presetColors[selectedColorIndex];
lastSelectedColor = selectedColor;
// initiate motion sensor
imu.begin();
filter.begin(100);
pinMode(POWER_BUTTON_PIN, INPUT_PULLUP);
pinMode(COLOR_BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_ACCESS_PIN, OUTPUT);
digitalWrite(LED_ACCESS_PIN, LOW);
Serial.begin(6400);
// wait up to 2 seconds for the Serial device to become available
long unsigned debug_start = millis ();
while (!Serial && ((millis () - debug_start) <= 3000))
;
pinMode(LED_ACCESS_PIN, OUTPUT);
digitalWrite(LED_ACCESS_PIN, HIGH);
// Audio connections require memory to work.
AudioMemory(12);
// Set initial volume
mixer1.gain(1, 1.5f); //other sounds
mixer1.gain(0, 1.5f); //hum
// Start SerialFlash
if (!SerialFlash.begin(FLASH_CHIP_SELECT)) {
while (1)
{
Serial.println ("Cannot access SPI Flash chip");
delay (3000);
}
}
// test sounds >> ALL WORK in setup
playHUMraw.play("HUM.RAW");
playFlashRaw1.play("OPEN.RAW");
playSwingRaw.play("SWING.RAW");
triggerSound("SPARK.RAW");
// clear led strip
FastLED.clear(true);
delay(3000);
powerUpBlade();
}
void powerUpBlade() {
// animate UP
isAnimating = 1;
bladeState = 1;
Serial.println("Turn on blade");
bladeOn = 1;
bladeUpDownDelay = 1;
triggerSound("OPEN.RAW"); // does not work!
}
void powerDownBlade() {
//animate DOWN
fadeStep = NUM_LEDS;
isAnimating = 1;
bladeState = 3;
Serial.println("Turn off blade");
bladeOn = 0;
// extra delay because close sound starts later
bladeUpDownDelay = 40;
triggerSound("CLOSE.RAW");
stopHum();
}
void bladeIsOn() {
// play hum when ON
if (!playHUMraw.isPlaying()) {
Serial.println("Hum reload");
startHum();
}
//nothing needed here unless adding animation
}
void bladeIsAnimatingUp() {
// part of loop animation UP
Serial.println("bladeIsAnimatingUp");
int midpoint = NUM_LEDS / 2;
int newSection = fadeStep + fadeStepSize;
for ( int j = fadeStep; j < newSection; j++) {
leds[j] = selectedColor;
leds[NUM_LEDS - j] = selectedColor;
}
fadeStep = newSection;
if (fadeStep >= midpoint + fadeStepSize) {
fadeStep = NUM_LEDS;
isAnimating = 0;
bladeState = 2;
Serial.println("blade up complete");
startHum();
}
}
void bladeIsAnimatingDown() {
// part of loop animation DOWN
//Serial.println("bladeIsAnimatingDown");
int midpoint = NUM_LEDS / 2;
int newSection = fadeStep - fadeStepSize;
for ( int j = fadeStep; j > newSection; j--) {
leds[j - midpoint] = CRGB::Black;
leds[midpoint + NUM_LEDS - j] = CRGB::Black;
}
fadeStep = newSection;
if (fadeStep <= midpoint - fadeStepSize) {
fadeStep = 0;
isAnimating = 0;
bladeState = 0;
}
}
void bladeIsSwitchingColor() {
// part of loop animation UP
Serial.println("bladeIsSwitchingColor");
if (isDimming) {
// dimming
if (brightStep > 0){
brightStep = brightStep - brightStepSize - brightStepSize;
if (brightStep < 0){
brightStep = 0;
}
}else{
brightStep = 0;
// when reaching off, change color
for ( int j = 0; j < NUM_LEDS; j++) {
leds[j] = selectedColor;
}
// and set dimming to off to turn on brightness again
isDimming = 0;
brightGlitch = 13;
}
}else{
if (brightGlitch > 0){
brightGlitch = brightGlitch - 1;
brightStep = random(20, 80) * (brightGlitch % 3);
//Serial.println(brightStep);
}else{
// not dimming
if (brightStep < BCbrightness){
brightStep = brightStep + brightStepSize;
}else{
brightStep = BCbrightness; // back to standard brightness
brightGlitch = 0;
// done with color change
bladeState = 2;
}
}
} // end if else
//strip.setBrightness(brightStep); // Set global brightness 0-255
}
void detectMotion() {
float ax, ay, az;
float gx, gy, gz;
float mx, my, mz;
float pitch, heading; //roll,
if (imu.available()) {
// Read the motion sensors
imu.readMotionSensor(ax, ay, az, gx, gy, gz, mx, my, mz);
// Update the SensorFusion filter
filter.update(gx, gy, gz, ax, ay, az, mx, my, mz);
// print the heading, pitch and roll
// roll = filter.getRoll();
pitch = filter.getPitch();
heading = filter.getYaw();
float headingDiff = abs(lastHeading - heading);
float pitchDiff = abs(lastPitch - pitch);
if (lastHeading != 0) {
if (pitchDiff > motionThreshold || headingDiff > motionThreshold) {
//cyle through swing sounds
lastSwingSound++;
if (lastSwingSound > swingSounds) {
lastSwingSound = 1;
}
String swingFile = "SWING.RAW";
swingFile.replace("X", lastSwingSound);
char charBuf[50];
swingFile.toCharArray(charBuf, 50);
triggerSwing(charBuf); // needs sequence to iterate through
}
}
lastHeading = heading;
lastPitch = pitch;
}
}
void loop() {
// Add entropy to random number generator; we use a lot of it.
// random16_add_entropy( random() );
//handle color selector button
int BCreading = digitalRead(COLOR_BUTTON_PIN);
if (BCreading != BClastButtonState) {
// reset the debouncing timer
BClastDebounceTime = millis();
}
if ((millis() - BClastDebounceTime) > BCdebounceDelay) {
if (BCreading != BCbuttonState) {
BCbuttonState = BCreading;
if (BCbuttonState == HIGH && bladeState != 4) {
// only act when button is pressed and color change not active
nextColor();
}
}
}
BClastButtonState = BCreading;
selectedColor = presetColors[selectedColorIndex];
if (selectedColor != lastSelectedColor && bladeState != 0) {
Serial.println("COLOR CHANGE");
//Serial.println(selectedColorIndex);
//for ( int j = 0; j < NUMPIXELS; j++) {
// strip.setPixelColor(j, selectedColor);
//}
// color change
isDimming = 1; // start dimming
bladeState = 4;
}
lastSelectedColor = selectedColor;
//handle blade on/off
int reading = digitalRead(POWER_BUTTON_PIN);
//debounce
if (reading != lastButtonState) {
// reset the debouncing timer
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > debounceDelay) {
if (reading != buttonState) {
buttonState = reading;
}
}
lastButtonState = reading;
if ( pendingPress == 1 && buttonState != LOW) {
buttonState = HIGH;
pendingPress = 0;
//Serial.print("execute pending blade press");
if (bladeOn) {
powerDownBlade();
} else {
powerUpBlade();
}
}
if (buttonState == LOW && !isAnimating) {
//Serial.println("BLADE BUTTON IS PRESSED");
pendingPress = 1;
}
//Serial.println("before bladeState: ");
switch (bladeState) {
case 0:
//blade is off
break;
case 1:
//blade is animating up
//check if sound is playing, only then can we animate
if (bladeUpDownDelay == 1 && playFlashRaw1.isPlaying() == 1) {
bladeUpDownDelay = 0;
}
if (bladeUpDownDelay == 0) {
bladeIsAnimatingUp();
}
break;
case 2:
//blade is on
bladeIsOn();
break;
case 3:
//blade is animating down
//check if sound is playing, only then can we animate
flashRawPlaying = playFlashRaw1.isPlaying();
if (bladeUpDownDelay > 1 && flashRawPlaying == 1) {
bladeUpDownDelay = bladeUpDownDelay - 1;
} else if (bladeUpDownDelay == 1 && flashRawPlaying == 1) {
bladeUpDownDelay = 0;
}
if (bladeUpDownDelay == 0) {
bladeIsAnimatingDown();
}
break;
case 4:
//blade is switching color
bladeIsSwitchingColor();
break;
}
SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0));
digitalWrite(LED_ACCESS_PIN, HIGH); // enable access to LEDs
// Refresh strip
FastLED.show();
digitalWrite(LED_ACCESS_PIN, LOW);
SPI.endTransaction(); // allow other libs to use SPI again
delay(10);
if (bladeState == 2) {
detectMotion();
}
}
void nextColor() {
selectedColorIndex++;
if (selectedColorIndex >= totalPresetColors) {
selectedColorIndex = 0;
}
triggerSound("SPARK.RAW");
}
void triggerSound(const char *filename) {
playFlashRaw1.play(filename);
}
void triggerSwing(const char *filename) {
if (playSwingRaw.isPlaying() == 0) {
playSwingRaw.play(filename);
Serial.println("Start swing");
} else {
Serial.print(" pos: ");
Serial.println(playSwingRaw.positionMillis());
}
}
void startHum() {
Serial.println("startHum");
playHUMraw.play("HUM.RAW");
Serial.println(playHUMraw.isPlaying());
}
void stopHum() {
playHUMraw.stop();
}