/*
*This code is for a project named "IR 'Smart Outlet'" at:
* ""
*Design, code and project idea by: Adam Berger
*You are free to use this code anyway you'd like as long as you
*give some credit to the author (me)!
*Questions? Email me at: bergea20@gmail.com
*Looking for more projects? Visit my website: http://www.bergertech.com
*/
#include <MIDI.h>
// MIDI enable pin
#define MIDI_ENABLE 12
//Constants
#define fire A0
#define sens A1
#define ints A2
#define led 5
#define STEP_DELAY 30
#define AVERAGE_RESOLUTION 50
#define FLAME_THRESHOLD 20
#define STEP_DELAY 30
#define BAD_VALUE_DIFFERENCE 20
#define RESTART_THRESHOLD 10
#define LIGHTER_DELAY_LENGTH 25
//Variables
int intensity, prevReading, output, prevOutput, fireReading, count, total, flameCenter;

void setup() {
  //begin serial communication
  Serial.begin(9600);
  //setup pins
  pinMode(fire, INPUT);
  pinMode(sens, INPUT);
  pinMode(ints, INPUT);
  pinMode(led, OUTPUT);
  
  //calculate center 
  flameCenter = findCenter();
  
  //boot animation
  delay(500);
  digitalWrite(led, HIGH);
  delay(100);
  digitalWrite(led, LOW);
  delay(500);
  digitalWrite(led, HIGH);
  delay(100);
  digitalWrite(led, LOW);
}

void loop(){
  //get sens
  int sensitivity = map(analogRead(sens), 0, 1024, -127, 127);
  //get intens
  intensity = map(analogRead(ints), 0, 1024, 0, 127);
  //get fireReading
  fireReading = analogRead(fire);
  //convert flameReading to output
  output = map(fireReading, 0, 1024, 0, 127);
  //calculate output
  output += ((-fireReading + flameCenter) * intensity);
  output += sensitivity;
  //limit output
  if(output > 127) 
    output = 127;
  if(output <  0)
    output = 0;
  //update indicator LED
  analogWrite(led, output/2);
  //output CC
  outputCC(0xB2, 2, output);
  // wait for STEP_DELAY
  delay(STEP_DELAY); 
  //if the flame goes out, do LED animation
  if(prevReading > FLAME_THRESHOLD && fireReading > FLAME_THRESHOLD)
    wait();
  //save prev fireReading
  prevReading = fireReading;
  //save prevOutput
  prevOutput = output;
}

//An LED wait animation
void wait(){
    while(!isFlame()){
    digitalWrite(led, HIGH);
    delay(100);
    digitalWrite(led, LOW);
    delay(100);
    digitalWrite(led, HIGH);
    delay(100);
    digitalWrite(led, LOW);
    delay(200);
    digitalWrite(led, LOW);
    delay(200);
  } 
}

int findCenter(){
 wait();
  //leave time to remove lighter
  for(int i = 0; i < 256; i++){
   //if the flame goes out during, restart
   if(!isFlame())
     return findCenter();
   analogWrite(led, i);
   delay(LIGHTER_DELAY_LENGTH);
   digitalWrite(led, LOW);
  }
  //indicate that it is done waiting
  delay(200);
  digitalWrite(led, HIGH);
  delay(500);
  digitalWrite(led, LOW);
  //get initial average
  int sum = 0, average = 0;
  int rawReadings[AVERAGE_RESOLUTION];
  //we have to read in one more than the average resolution
  for(int i = 0; i < AVERAGE_RESOLUTION; i++){
    rawReadings[i] = analogRead(fire);
    sum += rawReadings[i];
    delay(STEP_DELAY);
  }
  average = sum / AVERAGE_RESOLUTION;
  //look for bad values
  //use sentinel value of -1 for bad value
  int goodValue = 0;
  for(int i = 0; i < AVERAGE_RESOLUTION; i++){
    if(abs(rawReadings[i] - average) > BAD_VALUE_DIFFERENCE){
      rawReadings[i] = -1;
    }else{
      goodValue++;
    }
  }
  //now calculate true average and 
  int finalReadings[goodValue];
  int index = 0;
  sum = 0;
  for(int i = 0; i < AVERAGE_RESOLUTION; i++){
    if(rawReadings[i] != -1){
      finalReadings[index] = rawReadings[i];
      sum += finalReadings[index];
      index++;
    }
  }
  //if too many values are removed, we must restart 
  if(abs(AVERAGE_RESOLUTION - goodValue) > RESTART_THRESHOLD)
    return findCenter();
  //return average
  return sum / goodValue;
}

//false if > flame_thresh else true
boolean isFlame(){
  if(analogRead(fire) > FLAME_THRESHOLD)
    return false;
  else
    return true;
}

// sends a Midi CC. 
void outputCC(byte CC, byte num, byte val){
  Serial.write(CC);
  Serial.write(num);
  Serial.write(val);
}


