//*******************************************************
//INCLUDES
//*******************************************************
#include <Esp.h>                //https://github.com/esp8266/Arduino
#include <stdint.h>
#include <ArduinoJson.h>        //https://github.com/bblanchon/ArduinoJson
#include <ESP8266WiFi.h>        //https://github.com/esp8266/Arduino
#include <DNSServer.h>
#include <ESP8266WebServer.h>
#include <WiFiManager.h>        //https://github.com/tzapu/WiFiManager
#include <PubSubClient.h>       //https://github.com/knolleary/pubsubclient
#include "MFRC522.h"            //https://github.com/miguelbalboa/rfid

extern "C" {
#include "user_interface.h"
}

//*******************************************************
//MQTT SETUP
//*******************************************************
const char* mqtt_server = "192.168.0.128";
WiFiClient espClient;
PubSubClient client(espClient);
long lastMsg = 0;
char msg[50];
int value = 0;

struct configuration{
    int qos = 2;
    uint32_t id = ESP.getChipId();
    String prefix = "MAT-CR-";
    char* intopic = "set/topic";
    char* outtopic = "esp/topic";
    char* lwttopic = "dead/topic";
    char* cardtopic = "card/topic";  
    char* mysqlprimar = "identifikace";
    char* mysqlsecundar = "teplota";  
    char* mysqlconfig = "konfigurace";
    char* valnameprimar = "cardid";  
    char* valnamesecundar = "temp";
    char* way = "in";     
};

typedef struct configuration Config;
Config pconfig;

//*******************************************************
//RFID SETUP
//*******************************************************
#define RST 5  
#define SS  4  
MFRC522 mfrc522(SS, RST);   

///*******************************************************
//WIFI SETUP
//********************************************************
WiFiManager wifiManager;

//*******************************************************
//GLOBAL VARIABLES
//*******************************************************
int previouscard = 0;
int data_recieved = 0;	

//*******************************************************
//DEFINES
//*******************************************************
#define led 2
#define rel 16		

//*******************************************************
//FUNCTIONS DECLARATION
//*******************************************************
extern void setup_wifi();
extern void reconnect();
extern void ledblink();
extern void senddata(int message);
extern void checkcard();
extern void onmsg(char* topic, byte* payload, unsigned int length); 

//*******************************************************
//START OF THE PROGRAM
//*******************************************************
void setup() {        
    Serial.begin(115200);
    Serial.println("I waked up!");
    SPI.begin();           
    mfrc522.PCD_Init();     
    Serial.println(mfrc522.PCD_GetAntennaGain());
    mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
    Serial.println(mfrc522.PCD_GetAntennaGain());  
    pinMode(led, OUTPUT);
    pinMode(rel, OUTPUT);
    digitalWrite(led, HIGH);
    digitalWrite(rel, LOW);
    mfrc522.PCD_AntennaOff();
    delay(100);
    setup_wifi();
    client.setServer(mqtt_server, 1883);
    client.setCallback(onmsg);
    Serial.println("Wifi set, starting loop");
    ledblink();       
}

void loop() {        		
	  yield();
	  delay(100);    
    check_card();       
}
//*******************************************************
//END OF THE PROGRAM
//*******************************************************

//*******************************************************
//START OF THE FUNCTIONS
//*******************************************************
void setup_wifi(){
    //WiFi.forceSleepWake();
    mfrc522.PCD_AntennaOff();
    String wifiname = "CARD_READER: " + String(pconfig.id);
    char awifiname[wifiname.length()];
    wifiname.toCharArray(awifiname, wifiname.length());
    
    wifiManager.autoConnect(awifiname);             
}

void ledblink(){
  digitalWrite(led, LOW);
  delay(250);
  digitalWrite(led, HIGH);
  delay(250);
  digitalWrite(led, LOW);
  delay(250);
  digitalWrite(led, HIGH);
  delay(250);
  digitalWrite(led, LOW);
  delay(250);
  digitalWrite(led, HIGH);
}

void reconnect() {    
     
    if (WiFi.status() != WL_CONNECTED) {   
        setup_wifi();
    }    
    mfrc522.PCD_AntennaOff();    
    int attempt = 0;
    while (!client.connected()) {
      Serial.print("Attempting MQTT connection...");
      String mqttname = pconfig.prefix + String(pconfig.id);
      String mqttlwt = mqttname + ": died";
      char clientname[mqttname.length()];
      char clientlwt[mqttlwt.length()];
      mqttname.toCharArray(clientname, mqttname.length());
      if (client.connect(clientname, "", "")) {
        Serial.println("connected");        
        client.subscribe(pconfig.cardtopic);
      } else {
        Serial.print("failed, rc=");
        Serial.print(client.state());
        Serial.println(" try again in 5 seconds");
        delay(1000);
      }
      attempt = attempt + 1;
      if (attempt >= 5){
        setup_wifi();
        break;  
      }
   }    
}

void senddata(int message){
   
  if(!client.connected()) {
    reconnect();
  }
  mfrc522.PCD_AntennaOff();
  char msgbuffer[256];
  
   StaticJsonBuffer<200> jsonBuffer;
   JsonObject& root = jsonBuffer.createObject();
   root["CHIP_ID"] = pconfig.id;
   root["mysql"] = pconfig.mysqlprimar;
   root["card"] = message;
   root["way"] = pconfig.way;
   root.printTo(msgbuffer, sizeof(msgbuffer));
   root.printTo(Serial);
    
   if(client.publish(pconfig.outtopic, msgbuffer)){
    Serial.println("Data sent!");
   }else {
    Serial.println("Failed to sent data!");    
  }
 
  data_recieved = 0;
  for(int i = 0; i < 1000; i++){
    delay(10);
    client.loop();
    yield();
    if(data_recieved == 1){
      delay(5000);
      break;
    }        
  }
  previouscard = 0;      
}

void onmsg(char* topic, byte* payload, unsigned int length){
    char message[length];
    StaticJsonBuffer<200> jsonBuffer;
    
    Serial.print("Message arrived [");
    Serial.print(topic);
    Serial.print("] ");
    for (int i = 0; i < length; i++) {
      Serial.print((char)payload[i]);
    }
    Serial.println();
    
    for (int i = 0; i < length; i++){
        message[i] = (char)payload[i];
    }
    
    JsonObject& root = jsonBuffer.parseObject(message);
    
    if (!root.success()) {
        Serial.println("parseObject() failed");
        return;
    }
    else{
        Serial.println("parseObject() successed");
        if(root["CHIP_ID"] == pconfig.id){
            if(root["card"] == previouscard) {
              ledblink();
              digitalWrite(rel, HIGH);
              data_recieved = 1;                          
            }    
        }
    }
}

void check_card(){
    mfrc522.PCD_AntennaOn();
    delay(10);     
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
        delay(50);
    return;
    }
  // Select one of the cards
    if ( ! mfrc522.PICC_ReadCardSerial()) {
        delay(50);
    return;
    }  

    uint32_t message = 0;
    for(int i = 0; i < mfrc522.uid.size; i++){
      message += mfrc522.uid.uidByte[i] << i * 8;   
    }    
    previouscard = (int)message;
    digitalWrite(led, LOW);
    delay(100);
    digitalWrite(led, HIGH);
    senddata(message);
    digitalWrite(rel, LOW); 
    previouscard = 0; 
    //delay(1000);
}
//*******************************************************
//END OF THE FUNCTIONS
//*******************************************************



