#include <MsTimer2.h>
#include <Arduino.h>  
#include <stdint.h>
#include <string.h>
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <SoftwareSerial.h>
#include <DS3231.h> 
#include <SD.h>
#include "pitches.h"

#define pin_interrupt 18 //pin pro přerušení od GM
#define pinDHT 8 //pin pro čidlo DHT22
#define typDHT22 DHT22 //definice typu čidla DHT
#define pin_rotary_A 6 //pin pro rotary
#define pin_rotary_B 7 //pin pro rotary
#define DEBUG true //debug pro wifi modul
#define LED3 9 //pin pro LED1
#define LED2 10 //pin pro LED2
#define LED1 11 //pin pro LED3
#define REP 12 //pin pro repro

DHT mojeDHT(pinDHT, typDHT22);
Adafruit_BMP085 bmp180;
LiquidCrystal_I2C lcd(0x27,20,4);
SoftwareSerial espSerial(2, 3);
DS3231  rtc(SDA, SCL);

volatile bool preruseniSenzory=false;
volatile bool preruseniCloud=false;
volatile int TimeSenzory=0;
volatile int TimeCloud=0;
static int pole_cpm[60];
static float pole_tepl[60];
static float pole_vlhk[60];
static float pole_tlak[60];
int counts=0; //pro GM
int cpm=0; //pro GM
int korekce_tlak=32; 
int otoceni = 0; //pro rotary
int osoby = 0;  //pro rotary
int stav; //pro rotary
int posledni_stav; //pro rotary
float teplota_prumer=0;
float vlhkost_prumer=0;
float tlak_prumer=0;
float PDE;
String nazevSSID = "Vrata";       
String hesloSSID = "rangers880"; 
String API_klic = "QX35TCIEDD59F8BL";   
String adresa = "api.thingspeak.com";
String port = "80";
String pole_1 = "field1";
String pole_2 = "field2";
String pole_3 = "field3";
String pole_4 = "field4";
String pole_5 = "field5";
const int pin_sd = 53; //pin pro SD modul

static void GM_impulz(void) { //preruseni od GM, pricita impulzy
  counts++;
  }

void preruseni(void) { //preruseni casove, pocita sekundy a minutu
  TimeSenzory++;
  TimeCloud++;
  if(TimeSenzory>=1) preruseniSenzory=true;
  if(TimeCloud>=59) preruseniCloud=true;
}

void vypocet_cpm(void) { // vypocet hodnoty CPM a PDE
  cpm=0;
  pole_cpm[TimeCloud]=counts;   
  counts=0;
  for (int i = 0; i < 60; i++) {
    cpm+=pole_cpm[i];
  } 
  //Serial.print("CPM:");
  //Serial.println(cpm);
  PDE=cpm*0.0101;
  lcd.setCursor(0,1); 
  lcd.print("PDE=");
  lcd.print(PDE);
  lcd.print("uSv/h"); 
}

void vypocet_tepl_vlhk(void) { // vypocet teploty a vlhkosti
   teplota_prumer=0;
   vlhkost_prumer=0;
   pole_tepl[TimeCloud]=mojeDHT.readTemperature();
   pole_vlhk[TimeCloud]=mojeDHT.readHumidity();
   for (int i = 0; i < 60; i++) {
     teplota_prumer+=pole_tepl[i];
     vlhkost_prumer+=pole_vlhk[i];
   }
   teplota_prumer/=60;
   vlhkost_prumer/=60;
   //Serial.println(teplota_prumer);
   //Serial.println(vlhkost_prumer);
  lcd.setCursor(0,2); 
  lcd.print("T=");
  lcd.print(teplota_prumer);
  lcd.print("C");
  lcd.setCursor(11,2); 
  lcd.print("RV=");
  lcd.print(vlhkost_prumer);
  lcd.print("%"); 
}

void vypocet_tlak(void) { // vypocet tlaku
   tlak_prumer=0;
   pole_tlak[TimeCloud]=((bmp180.readPressure() + korekce_tlak*100)/100.00);
   for (int i = 0; i < 60; i++) {
     tlak_prumer+=pole_tlak[i];
   }
   tlak_prumer/=60;
   //Serial.println(tlak_prumer);
   lcd.setCursor(0,3); 
   lcd.print("Tlak=");
   lcd.print(tlak_prumer);
   lcd.print("hPa");
}

void vypocet_pocetlidi(void) { // kontrola otoceni rotacniho koderu
    stav = digitalRead(pin_rotary_A); // aktualni stav pin_rotary_A
       // POkud je predchozi a aktualni stav pin_rotary_A rozdilny, probehlo otoceni
       if (stav != posledni_stav){     
         // Pokud je stav pin_rotary_B odlisny od stavu pin_rotary_A, koder se otaci ve smeru hodinovych rucicek
         if (digitalRead(pin_rotary_B) != stav) { 
           otoceni ++;
           if (otoceni == 10) { 
           osoby ++;
           otoceni = 0;
           //Serial.print("Počet osob: ");
           //Serial.println(osoby);
           lcd.setCursor(14,1); 
           lcd.print("osob:");
           lcd.print(osoby);
         }
         } else { // otoceni proti smeru hodinovych rucicek
           otoceni --;
           if (otoceni == -10) { 
           osoby --;
           if (osoby < 0) {
             osoby = 0; 
             }
           otoceni = 0;
           //Serial.print("Počet osob: ");
           //Serial.println(osoby);
           lcd.setCursor(14,1); 
           lcd.print("osob:");
           lcd.print(osoby);
         }
         }
        
       } 
       posledni_stav = stav; // aktualizovat posledni stav aktualnim stavem
}

String espData(String prikaz, const int prodleni, boolean debug) { //funkce na zpetnou kontrolu AT prikazu, jestli se spravne poslali na WiFi modul
  Serial.print("AT prikaz ==> ");
  Serial.print(prikaz);
  Serial.println("     ");
  
  String response = "";
  espSerial.println(prikaz);
  long int cas = millis();
  while ( (cas + prodleni) > millis())
  {
    while (espSerial.available())
    {
      char c = espSerial.read();
      response += c;
    }
  }
  if (debug)
  {
    //Serial.print(response);
  }
  return response;
}

void poslat_na_cloud(void) { //string ve tvaru pozadovanem ThingSpeakem, obsahuje vsechny parametry
    String sendData = "GET /update?api_key="+ API_klic +"&"+ pole_1 +"="+String(PDE)+ "&" + pole_2 + "="+String(teplota_prumer)+ "&" + pole_3 + "="+String(vlhkost_prumer)+ "&" + pole_4 + "="+String(tlak_prumer)+ "&" + pole_5 + "="+String(osoby);
    espData("AT+CIPMUX=1", 1000, DEBUG);       //povolit vice pripojeni
    espData("AT+CIPSTART=0,\"TCP\",\""+ adresa +"\","+ port, 1000, DEBUG); //pripojit na server ThingSpeak
    espData("AT+CIPSEND=0," +String(sendData.length()+4),1000,DEBUG);  // informace o velikosti posilanych dat
    espSerial.find(">"); 
    espSerial.println(sendData); //poslat data
    espData("AT+CIPCLOSE=0",1000,DEBUG); //odpojit
}

void cas(void) {
       
 lcd.setCursor(0,0);
 lcd.print(rtc.getTimeStr());
 lcd.print(" ");
 lcd.print(rtc.getDateStr());  
}

void zapsat_na_SD(void) {
   // strin, ktery obsahuje data pro zapsani na SD kartu:
  String dataString = "";
   dataString += "Čas a datum:";
   dataString += rtc.getTimeStr();
   dataString += " ";
   dataString += rtc.getDateStr();
   dataString += " , ";
   dataString += "Teplota:";
   dataString += teplota_prumer;
   dataString += "°C";
   dataString += " , ";
   dataString += "Vlhkost:";
   dataString += vlhkost_prumer;
   dataString += "%";
   dataString += " , ";
   dataString += "PDE:";
   dataString += PDE;
   dataString += "uSv/h";
   dataString += " , ";
   dataString += "Tlak:";
   dataString += tlak_prumer;
   dataString += "hPa";
   dataString += " , ";
   dataString += "Počet lidí:";
   dataString += osoby;
   
  // otevrit soubor
  File dataFile = SD.open("datalog.txt", FILE_WRITE);

  // pokud je soubor k dispozici, zapis do nej:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // vypis na seriovku:
    Serial.println(dataString);
  }
  // Pokud nelze otevrit soubor, vypis error:
  else {
    Serial.println("Nelze otevřít datalog.txt");
  }
}

void LED_alarm(void){
  if (PDE>=50){ //alarm pro PDE
    digitalWrite(LED1, HIGH); // rozvitit LED
    tone(REP, NOTE_C5, 50); // spustit alarm
  }
  else {
    digitalWrite(LED1, LOW);
  }
  if (osoby>0){ // rozsvitit LED pro osoby>0
    digitalWrite(LED3, HIGH);
    tone(REP, NOTE_C5, 50);
  }
  else {
    digitalWrite(LED3, LOW);
  }
  if (teplota_prumer>=25 || vlhkost_prumer>=70 || tlak_prumer>=1050){ // rozsvitit LED a spustit alarm pri zvysenych meteorologickych hodnotach: unik media
    digitalWrite(LED2, HIGH);
    tone(REP, NOTE_C5, 50);
  }
  else {
    digitalWrite(LED2, LOW);
  }
}

void setup() {
  Serial.begin(9600); //seriovka
  espSerial.begin(115200);
  mojeDHT.begin(); //DHT, teplota, vlhkost
  bmp180.begin(); //tlak
  rtc.begin(); //RTC
  lcd.init(); //Displej
  lcd.backlight(); //Displej
  pinMode(pin_interrupt, INPUT); //pro GM
  pinMode (pin_rotary_A,INPUT); //pro rotary
  pinMode (pin_rotary_B,INPUT); //pro rotary
  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);
  pinMode(LED3, OUTPUT);
  pinMode(REP, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(pin_interrupt), GM_impulz, FALLING); //preruseni GM
  posledni_stav = digitalRead(pin_rotary_A); //stav pro rotary
  MsTimer2::set(1000, preruseni); //casove preruseni definice
  MsTimer2::start(); //casove preruseni start
  espData("AT+RST", 1000, DEBUG);                      //Resetovat wifi modul
  espData("AT+CWMODE=1", 1000, DEBUG);                 //Nastavit wifi modul jako stanici
  espData("AT+CWJAP=\""+ nazevSSID +"\",\""+ hesloSSID +"\"", 1000, DEBUG);   //Pripojit se na wifi
  Serial.print("Kontrola SD karty..."); //Kontrola SD modulu
  // Zkontroluj, jestli je SD karta pritomna a muze se s ni pracovat:
  if (!SD.begin(pin_sd)) {
    Serial.println("Nelze načíst kartu");
    // nedelej nic dalsiho:
    while (1);
  }
  Serial.println("SD karta připravena.");
}

void loop() {
  vypocet_pocetlidi();
  //LED_alarm();
  if(preruseniSenzory==true) {
    vypocet_cpm();
    vypocet_tepl_vlhk();
    vypocet_tlak();
    cas();
    LED_alarm();
    preruseniSenzory=false;
    TimeSenzory=0;      
  }
  if(preruseniCloud==true) {
    poslat_na_cloud();
    zapsat_na_SD();
    preruseniCloud=false;
    TimeCloud=0;
  }      
}
