/***
 * This code will provide a skeleton for the "Bathroom Manager" IOT
 * project, some information has to be filled in for the code to
 * work. Places will have XX holders where information is individual
 * and will only work with your specific information, in places where
 * the parameters were my preference it will be stated.
 */

/* Fill-in information from Blynk Device Info here */
#define BLYNK_TEMPLATE_ID "TXX"
#define BLYNK_TEMPLATE_NAME "XX"
#define BLYNK_AUTH_TOKEN "XX"

/* Comment this out to disable prints and save space */
#define BLYNK_PRINT SerialUSB


#include <ESP8266_Lib.h>
#include <BlynkSimpleShieldEsp8266.h>
#include <Servo.h>
#include <Adafruit_NeoPixel.h>
#include <Adafruit_CircuitPlayground.h>
#include <Wire.h>
#include <SPI.h>

/* We define a few program variables for simplicity */
#define NUMPIXELS 10 //number of pixels
#define PIN        8 //used for LEDs
#define speakerPin A0 //speaker pin


// Your WiFi credentials.
// Set password to "" for open networks.
char ssid[] = "XX";
char pass[] = "XX";

// Hardware Serial on Mega, Leonardo, Micro...
#define EspSerial Serial1

// or Software Serial on Uno, Nano...
//#include <SoftwareSerial.h>
//SoftwareSerial EspSerial(2, 3); // RX, TX

// Your ESP8266 baud rate:
#define ESP8266_BAUD 115200

ESP8266 wifi(&EspSerial);

/* Declaration of variables used in the code */
int lightValue; 

float tempValue;
float idealTemp = 21.00; //THIS IS MY IDEAL TEMP VALUE - CAN BE CHANGED

String message;

unsigned long timeStart = 0;

unsigned long updated = 0;

const int leftButton = 4;
const int rightButton = 5;
const int slideSwitch = 7;

int pinValue1;
String pinValue2;

/* neopixles element */
Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

/* servo element */
Servo myservo1;

/* speech variables for emergency sound */
const uint8_t spPLEASE[] PROGMEM = {0x02,0x28,0x92,0xAD,0x95,0x45,0x6A,0xBB,0xA8,0xDB,0x51,0x15,0x16,0x5D,0x4C,0x6B,0x56,0xB5,0x84,0xB4,0x39,0x75,0x5D,0x45,0xD7,0x61,0xEC,0xBC,0x66,0x65,0xD3,0x85,0x60,0xE8,0x9A,0x95,0x2E,0xEB,0x0C,0x1D,0x4B,0x46,0x36,0x8C,0x22,0x77,0xBE,0x19,0x59,0x57,0x4A,0x1A,0xFE,0x66,0xE4,0x5D,0xA9,0xA8,0xE9,0xE6,0x51,0xFE,0x10,0x22,0x2C,0x4E,0x0D,0xF0,0x8C,0xAB,0x02,0x7E,0x4A,0xF9,0x7F};
const uint8_t spUP[]     PROGMEM = {0x2D,0xCD,0x72,0xA2,0x55,0x77,0xDD,0xF6,0x36,0xB7,0xB9,0xD5,0xEA,0xB3,0xC9,0x6C,0xF1,0xD5,0xE9,0x4A,0xB6,0xBD,0x39,0x7F,0x21,0x50,0x84,0x72,0x17,0x91,0x0D,0x00,0x20,0x80,0x48,0xD3,0x08,0x90,0x54,0x28,0x06,0xFE,0x1F};
const uint8_t spHURRY[]  PROGMEM ={0x00,0x00,0x14,0xE0,0x40,0x4B,0xEC,0x16,0x19,0x95,0xD8,0x3B,0x19,0x89,0xCE,0x8D,0xF1,0xEC,0xB6,0x25,0xBA,0x26,0xC5,0x61,0x97,0x99,0xD4,0xB4,0xE5,0xBA,0xA9,0x55,0x49,0xDE,0x5E,0xAC,0xBA,0x45,0x67,0x8C,0x78,0xB3,0xEB,0xDD,0xCC,0xB6,0xB5,0xB5,0x18,0x72,0xE4,0x9D,0xDA,0xFA,0x4E,0xB7,0x00,0x0F,0x00,0x00};

/* get the value of the emergency command  from Blynk */
BLYNK_WRITE(V1)
 {
    pinValue1 = param.asInt(); // assigning incoming value from pin V1 to a variable
}

/* get the value of the roll paper command from Blynk */
BLYNK_WRITE(V0)
 {
    pinValue2 = param.asString(); // assigning incoming value from pin V0 to a variable
}

void setup()
{
  // Debug console
  SerialUSB.begin(115200);

  // Set ESP8266 baud rate
  EspSerial.begin(ESP8266_BAUD);
  delay(10);

  Blynk.begin(BLYNK_AUTH_TOKEN, wifi, ssid, pass);
  // You can also specify server:
  //Blynk.begin(BLYNK_AUTH_TOKEN, wifi, ssid, pass, "blynk.cloud", 80);
  //Blynk.begin(BLYNK_AUTH_TOKEN, wifi, ssid, pass, IPAddress(192,168,1,100), 8080);

  // Sets the leftButton pin as an input
  pinMode(leftButton, INPUT);

  // Sets the rightButton pin as an input
  pinMode(rightButton, INPUT_PULLDOWN);

  // Sets the slideSwitch pin as an input
  pinMode(slideSwitch, INPUT_PULLUP);

  // Sets the speaker pin as an output
  pinMode(speakerPin, OUTPUT);

  // Attaches the servo on pin 6 (digital) - can be defined for any pin 
  myservo1.attach(6);


  // Start pixel work and then make sure they are all off
  pixels.begin();
  pixels.clear();
}

/* A function which plays a short jingle being used at a later stage */
void playJingle() {
  tone(speakerPin, 262, 500);  // play C4
  delay(150);
  tone(speakerPin, 294, 500);  // play D4
  delay(150);
  tone(speakerPin, 330, 500);  // play E4
  delay(150);
  tone(speakerPin, 349, 500);  // play F4
  delay(150);
  noTone(speakerPin);  // stop playing
}

void loop()
{
  Blynk.run();

  /* We read the values from the temp and light sensors.
   *  The temp sensor reading has to be manipulated to display
   *  temp. in celcius.
   */
  lightValue = analogRead(A8);
  tempValue = ((analogRead(A9) * 5.0)/1024 - 0.5)*10;

  /* The part which interacts with the Blynk server has to contact
   *  the server only once in a while in order not to overflow the
   *  program thus this "if" statement using millis helps stall
   *  before contacting the server.
   */
  if(millis() - updated > 1000) {

    /* Send the light and temp. value to virtual pins in Blynk */
    Blynk.virtualWrite(V2, tempValue);

    Blynk.virtualWrite(V4, lightValue);

    /* If the light levels are above 50 (THIS PARAMETER FITS WELL
     *  IN MY ROOM SETTING PLEASE CHECK YOUR OWN AND SET ACCORDING
     *  TO THAT) and also the slide switch is off then normal
     *  operating mode activates.
     */
    if(lightValue > XX && (digitalRead(slideSwitch) == 0)){

      /* Occupied message sent to Blynk */
      message = "occupied";
      Blynk.virtualWrite(V3, message);

      /* Pixels are cleared in order to disable previous LED
       *  config if happened, does not show on the board as the
       *  code is not that heavy.
       */
      pixels.clear();

      /* Retract TP action, also lights up LEDS in the process */
      if(digitalRead(leftButton) == 1){
        myservo1.writeMicroseconds(1700);
        // Makes a rainbow type across all the pixels
        for (int i = 0; i < NUMPIXELS; i++) {
          pixels.setPixelColor(i, pixels.Color(i * 25, 255 - i * 25, i * 10));
          pixels.show(); // This sends the updated pixel color to the hardware
          delay(100); // Delays for a bit
        }
        
        // Turns off all the pixels in order
        for (int i = 0; i < NUMPIXELS; i++) {
          pixels.setPixelColor(i, pixels.Color(0, 0, 0));
          pixels.show(); // This sends the updated pixel color to the hardware
          delay(100); // Delays for a bit
        }
      }else{
        myservo1.writeMicroseconds(1450);
      }

      /* Normal operation of distrbuting TP when asked by online
       *  request, the pinValue2 comes from Blynk.
       */
      if(pinValue2 == "roll"){
        if(timeStart == 0){ // if timeStart is not set, set it to the current time
          timeStart = millis();
        }
        if(millis() - timeStart < 500){
          myservo1.writeMicroseconds(1200);
          playJingle();
        } else {
          pinValue2 = "default";
          timeStart = 0; // reset timeStart
          myservo1.writeMicroseconds(1450);
        } 
      }

      /* LEDs show a representation of the temp. difference from
       *  the ideal temp. defined at the top. 
       *  Each LED is 1 degree C away from the ideal, blue is colder
       *  and red is hotter. Green LEDS is within -/+ 1 degree from
       *  ideal.
       */
      if((tempValue - idealTemp) > 1){
        for (int i = 0; i < (tempValue-idealTemp-1); i++) {
          pixels.setPixelColor(i, pixels.Color(150, 0, 0));
          pixels.show(); // This sends the updated pixel color to the hardware
        }
      }else if(((tempValue - idealTemp) <= 1) || ((tempValue - idealTemp) >= -1)){
        
        pixels.setPixelColor(4, pixels.Color(0, 100, 0));
        pixels.setPixelColor(5, pixels.Color(0, 100, 0));
        pixels.show(); // This sends the updated pixel color to the hardware
       
      }else{
        for (int i = 0; i < (idealTemp-tempValue-1); i++) {
          pixels.setPixelColor(i, pixels.Color(0, 0, 150));
          pixels.show(); // This sends the updated pixel color to the hardware
        }
      }
        
    
    }
    /* If the slide switch is ON, manual mode activated */
    else if(digitalRead(slideSwitch) == 1){
      message = "occupied";
      Blynk.virtualWrite(V3, message);
      
      for (int i = 0; i < NUMPIXELS; i++) {
        pixels.setPixelColor(i, pixels.Color(80, 0, 80));
        pixels.show(); // This sends the updated pixel color to the hardware
      }
      if(digitalRead(rightButton) == 1){
          myservo1.writeMicroseconds(1200);      
      }else{
          myservo1.writeMicroseconds(1450);
      }
      
    }
    /* Deactivated mode, transfers data to Blynk but no LED
     *  or servo operation is done.
     */
    else{
      message = "free";
      Blynk.virtualWrite(V3, message);
      
      myservo1.writeMicroseconds(1450);
  
      // Space for lights turn off
      
      for (int i = 0; i < NUMPIXELS; i++) {
            pixels.setPixelColor(i, pixels.Color(0, 0, 0));
            pixels.show(); // This sends the updated pixel color to the hardware
      }
    }
    updated = millis();
  }

  /* Output emergency message to speaker */
  if(pinValue1 == 1){
    CircuitPlayground.speaker.say(spPLEASE);
    delay(1000);
    CircuitPlayground.speaker.say(spHURRY);
    delay(1000);
    CircuitPlayground.speaker.say(spUP);
    delay(1000);
    pinValue1 = 0;
  } 
}
