/*
 * Wireless IoT network agregator
 * Uses ZigBee, RFID and Bluetooth technologies
 * Written by ZAOUALI Alaeddine for my second year of engineering internship in Lasaris Lab (Faculty of Informatics or Brno, Czech Republic)
 * This code deals with the communication bridge between users and sensor end-devices. It deals with authentication with RFID reader, connects to server
 * with Bluetooth communication and communicates with sensor controllers with ZigBee.
 * Bluetooth : hc05, ZigBee : Xbee S1, RFID : MFRC522 
 * Please read the document attached to this code for wiring.
 * This code is meant to be used in other situations, you can add your sensor information in LCD_sensor_display()
 * If you're facing problem making it work, please contact me at alaeddine.zaouali@insa-cvl.fr 
 */
#include "SoftwareSerial.h"
#define BUFFER_SIZE 64  //This will prevent buffer overruns.
#define DISPLAY_DURATION 2000 //Duration in ms for LCD to display messages
#define DISPLAY_DURATION_SENSOR 2000
#define DISPLAY_DURATION_AUTHENTICATION 1000
#define DISPLAY_DURATION_ERROR 1500
#define ZIGBEE_READ_DELAY 1500
SoftwareSerial XBSerial(A5, 6); // A5 to RX | D6 to TX
char inData[BUFFER_SIZE];//This is a character buffer where the data sent by the python script will go.
char inChar=-1;//Initialie the first character as nothing
int count=0;//This is the number of lines sent in from the python script
int i=0;//Arduinos are not the most capable chips in the world so I just create the looping variable once
char sensor_type;//0:Temperature - 1:Humidity
/* ------------------------------------- RFID ------------------------------------- */
#include <SPI.h>
#include <MFRC522.h>
#define RST_PIN 9
#define SS_PIN 10
#define SUCCESS_LED A1
#define FAILURE_LED A0
MFRC522 mfrc522(SS_PIN, RST_PIN);
/* ------------------------------------- LCD ------------------------------------- */
#include <LiquidCrystal.h>
const int rs = 8, en = 7, d4 = 5, d5 = 4, d6 = 3, d7 = 2;
LiquidCrystal lcd(rs, en, d4, d5, d6, d7);

void setup()
{
  Serial.begin(9600);//Initialize communications to the serial monitor in the Arduino IDE
  XBSerial.begin(9600);  // XBEE initialize communication
  Serial.println("Started");//Tell the serial monitor that the sketch has started.
  /* RFID */
  pinMode(SUCCESS_LED, OUTPUT);
  pinMode(FAILURE_LED, OUTPUT);
  while (!Serial);
  SPI.begin();
  mfrc522.PCD_Init();
  mfrc522.PCD_DumpVersionToSerial();
  /* LCD */
  lcd.begin(16, 2);
  lcd.print("Please show ID");
}
void loop()
{
  if (XBSerial.available()>0){
    XBSerial.read();    // Cleanup
  }
  lcd.setCursor(0, 0);
  if (mfrc522.PICC_IsNewCardPresent() ) 
  {
    unsigned long uid = getID();        
    delay(500);
    String idNumber = String(uid);  
    if(idNumber.indexOf("22020")>=0){
        String inDataString = String("CMD_TMP");
        XBSerial.listen();                            
        sensor_type=getSensor(inDataString);
        XB_protocol_send(inDataString);                 
        delay(ZIGBEE_READ_DELAY);
        if(XBSerial.available()>0){ 
            Serial.println("Processing reading XB");
            String XBString = XB_protocol_read();             
            Serial.println(XBString);
            if (XBString){
            LCD_authentication_success(uid, DISPLAY_DURATION_AUTHENTICATION);
            LED_alarm_success();
            LCD_sensor_display(sensor_type, XBString, DISPLAY_DURATION_SENSOR);
            } 
        }
        else{
            Serial.println("Failed to read Xbee");
            LCD_sensor_display_error(sensor_type, DISPLAY_DURATION_ERROR);
        }
    }
    else{
      Serial.println("Wrong Card");
      LCD_authentication_failure(uid, DISPLAY_DURATION_ERROR);
      LED_alarm();
    }
  }
  else{
    LCD_idle();
    LED_alarm_idle();
  }
  delay(100); //Pause for a moment
}

unsigned long getID(){                        // Allows to get ID
  if ( ! mfrc522.PICC_ReadCardSerial()) {     //Since a PICC placed get Serial and continue
    return -1;
  }
  unsigned long hex_num;
  hex_num =  mfrc522.uid.uidByte[0] << 24;
  hex_num += mfrc522.uid.uidByte[1] << 16;
  hex_num += mfrc522.uid.uidByte[2] <<  8;
  hex_num += mfrc522.uid.uidByte[3];
  mfrc522.PICC_HaltA(); // Stop reading
  return hex_num;
}

void LED_alarm(){
  for (int i=0; i<8; i++){
    delay (100);
    digitalWrite(FAILURE_LED, HIGH);
    digitalWrite(SUCCESS_LED, LOW);
    delay (100);
    digitalWrite(FAILURE_LED, LOW);
    digitalWrite(SUCCESS_LED, HIGH);
  }
  digitalWrite(SUCCESS_LED, LOW);
}
void LED_alarm_success(){
  for (int i=0; i<3; i++){
    digitalWrite(SUCCESS_LED, HIGH);
    delay (500);
    digitalWrite(SUCCESS_LED, LOW);
    delay (200);
  }
}
void LED_alarm_idle(){ 
  for (int i=0; i<1; i++){
    digitalWrite(FAILURE_LED, HIGH);
    delay (100);
    digitalWrite(FAILURE_LED, LOW);
    delay (100);
  }
}

void LCD_idle(){
  // Display text when nothing is happening
  LCD_clean();
  lcd.setCursor(0, 0);
  lcd.print("Please show card");
}
void LCD_authentication_success(unsigned long uid, int duration){
    LCD_clean();
    lcd.setCursor(0,0);
    lcd.print("Access Granted");
    lcd.setCursor(0, 1);
    lcd.print(uid);
    delay(duration);
}
void LCD_authentication_failure(unsigned long uid, int duration){
    LCD_clean();
    lcd.setCursor(0,0);
    lcd.print("Access Denied");
    lcd.setCursor(0, 1);
    lcd.print(uid);   
    delay(duration);
}
void LCD_clean(){
  //Clean every LCD cell
  lcd.setCursor(0, 1);
  lcd.print("                ");
  lcd.setCursor(0, 0);
  lcd.print("                ");
}
void LCD_temperature(String tmp){
  //Display Temperature on LCD, delay is handled by LCD_sensor_display
  LCD_clean();
  lcd.setCursor(0, 0);
  lcd.print("Temperature :");
  lcd.setCursor(0, 1);
  lcd.print(tmp);
}
void LCD_temperature_unk(){
  //Display error message if no value is returned
  LCD_clean();
  lcd.setCursor(0, 0);
  lcd.print("Temperature :");
  lcd.setCursor(0, 1);
  lcd.print("Not detected");
}
void LCD_humidity(String hmd){
  //Display Humidity on LCD, delay is handled by LCD_sensor_display
  LCD_clean();
  lcd.setCursor(0, 0);
  lcd.print("Humidity :");
  lcd.setCursor(0, 1);
  lcd.print(hmd);
}
void LCD_humidity_unk(){
  //Display error message if no value is returned
  LCD_clean();
  lcd.setCursor(0, 0);
  lcd.print("humidity :");
  lcd.setCursor(0, 1);
  lcd.print("Not detected");
}
void LCD_sensor_display(char sensor_type, String msg, int duration){
  //Display sensor information according to sensor_type
  switch (sensor_type){
    case 0:                   // Temperature
      LCD_temperature(msg);
      break;
    case 1:                   // Humidity
      LCD_humidity(msg);
      break;
  }
  delay(duration);
}
void LCD_sensor_display_error(char sensor_type, int duration){
  //Display sensor error message according to sensor_type
  switch (sensor_type){
    case 0:                   // Temperature
      LCD_temperature_unk();
      break;
    case 1:                   // Humidity
      LCD_humidity_unk();
      break;
  }
  delay(duration);
}
void XB_protocol_send(String arg){
  //Send command to end-device ZigBee
  if(arg.indexOf("TMP")>=0){
    XBSerial.write("CMD_TMP");
    Serial.println("Temperature request sent");
  }
  else if(arg.indexOf("HMD")>=0){
    XBSerial.write("CMD_HMD");
    Serial.println("Humidity request sent");
  }
}
String XB_protocol_read(){
  //Read end-device return value
  i=0; // initialize
  while (XBSerial.available()>0 && i<BUFFER_SIZE){
    inData[i] = XBSerial.read();
    i++;
  }
  String inDataString = String(inData);
  if(inDataString) return inDataString;
  else{
    Serial.println("Failed to read Xbee sensor device.");
    return;
  }
}

char getSensor(String msg){
  //Returns the proper sensor type according to command value
  char sensor_type;
  if(msg.indexOf("CMD_TMP")>=0){
    sensor_type=0;
  }
  else if(msg.indexOf("CMD_HMD")>=0){
    sensor_type=1;
  }
  return sensor_type;
}
