//připojení knihoven
#include <WiFi.h> //knihovna pro Wifi
#include <Wire.h> //knihovna pro komunikaci I2C
#include "Arduino.h" //knihovna arduino
#include "ThingSpeak.h" //knihovna pro komunikaci se reverem ThingSpeak
#include <SDL_Arduino_INA3221.h> //knihovna pro senzory napětí a proudu INA3221
#include "RTClib.h" //knihovna pro RTC
#include "driver/gpio.h" //knihovna GPIO

const char* ssid = "XXXXXXXX";   // nazev WIFI: zde zadat název Wifi
const char* password = "XXXXXXXX";   // heslo: zde zadat heslo Wifi

WiFiClient  client; //Wifi klient

//nastavení parametrů pro ThingSpeak
unsigned long Channel_ID = XXXXXXXX;  
const char * API_key = "XXXXXXXX";

//vytvoření a nastavení objektu z knihovny pro moduly INA3221
SDL_Arduino_INA3221 ina3221t(0x40, 0.05);
SDL_Arduino_INA3221 ina3221f(0x41);
SDL_Arduino_INA3221 ina3221n(0x42, 0.05);

RTC_DS1307 DS1307;//vytvoření objektu z knihovny RTClib pro  DS1307

//vytvoření proměnných pro měření elektrických veličin
float odber;
float vykon_FV;
float vykon_nab;
float napeti_FV;
float napeti_nab;
float proud_nab;
float napeti_bat;
float proud_bat;

//definování připojených pinů
#define fotorezistor1 35
#define fotorezistor2 34
#define fotorezistor3 38
#define fotorezistor4 37
#define in1 26
#define in2 25
#define in3 4
#define in4 2
#define potenciometr 33
#define spinac1 32
#define spinac2 27
#define spinac3 14
#define spinac4 12
#define spinani_motory 5

#define U_S_TO_S 1000000 //převod us na s
#define ESP32_SLEEP_TIME 300 //definování jak dlouho má být ESP32 v Deep Sleep módu (v sekundách)

void sleep(int s_sleep); //uspání

void pozice_vychozi(void); //hledání raní výchozí pozice
RTC_DATA_ATTR bool hledani_pozice = false; //sleduje, jestli se má hledat výchozí pozice
uint8_t stav_hledani = 0; //sleduje stav hledání pozice

bool odeslano = false; //odeslání dat

int nahore = 0;
int dole = 0;
int vlevo = 0;
int vpravo = 0;

int spinac_dole = 0;
int spinac_nahore = 0;
int spinac_vlevo = 0;
int spinac_vpravo = 0;

int tolerance = 0;

RTC_DATA_ATTR bool prvni_zapnuti = true; //RTC při prvním zapnutí


void setup() {

  //snížení frekvence procesoru na 80 MHz
  setCpuFrequencyMhz(80);

  //nastaveni pinů  pro ovládání motorů
  pinMode(in1, OUTPUT);
  pinMode(in2, OUTPUT);
  pinMode(in3, OUTPUT);
  pinMode(in4, OUTPUT);  
  //nastaveni pinů pro napěťové děliče s fotorezistory
  pinMode(fotorezistor1,INPUT);  
  pinMode(fotorezistor2,INPUT); 
  pinMode(fotorezistor3,INPUT);
  pinMode(fotorezistor4,INPUT);
  //nastaveni pinů pro potenciometr
  pinMode(potenciometr,INPUT);  
  //nastaveni pinů pro spínače
  pinMode(spinac1,INPUT);
  pinMode(spinac2,INPUT);
  pinMode(spinac3,INPUT);
  pinMode(spinac4,INPUT);
  //nastaveni pinů pro spínání napájení modulu L298N
  pinMode (spinani_motory, OUTPUT);

  //nastaveni vychozich hodnot pro ovládání DC motorů (motory zastaveny)
  digitalWrite(in1, LOW);
  digitalWrite(in2, LOW);
  digitalWrite(in3, LOW);
  digitalWrite(in4, LOW);    
  
  digitalWrite(spinani_motory, HIGH); // sepnutí n-kanálového Mosfet trancistoru pro odepínání modulu  L298N

  // zahájení komunikace s moduly
  ina3221t.begin();
  ina3221f.begin();  
  ina3221n.begin();
  if(!DS1307.begin());

  //zahájení kominikace přes WiFi
  WiFi.begin(ssid, password);
  while(!WiFi.isConnected()){delay(10);}
  //zahájení komunikace s ThingSpeak serverem
  ThingSpeak.begin(client);
  
  if(prvni_zapnuti) //podmínka RTC při prvním zapnutí
  {
    hledani_pozice = true;
    DateTime buildTime(F(__DATE__), F(__TIME__));
    DS1307.adjust(buildTime);
    prvni_zapnuti= false;
  }

  if(5 > DS1307.now().hour()>22) // nastavení spánku od 22:00 do 5:00
  {
    hledani_pozice = true;
    sleep(1800);
  }

  if(hledani_pozice){
    pozice_vychozi();
  }
}


void loop() {
  // podmínka pro odeslání hodnot
  if (!odeslano) {
    //měření modulem INA3221
    odber = ina3221t.getCurrent_mA(1)*ina3221t.getBusVoltage_V(1)*0.001;  
    vykon_FV = ina3221f.getCurrent_mA(1)*ina3221f.getBusVoltage_V(1)*0.001;
    vykon_nab = ina3221n.getCurrent_mA(1)*ina3221n.getBusVoltage_V(1)*0.001;
    napeti_FV = ina3221f.getBusVoltage_V(1);
    napeti_nab = ina3221n.getBusVoltage_V(1);  
    proud_nab = ina3221n.getCurrent_mA(1); 
    napeti_bat = ina3221t.getBusVoltage_V(1); 
    proud_bat = ina3221t.getCurrent_mA(1);

    //nastavení polí pro ThingSpeak
    ThingSpeak.setField(1,odber);
    ThingSpeak.setField(2,vykon_FV);
    ThingSpeak.setField(3,vykon_nab); 
    ThingSpeak.setField(4,napeti_FV);
    ThingSpeak.setField(5,napeti_nab);
    ThingSpeak.setField(6,proud_nab); 
    ThingSpeak.setField(7,napeti_bat); 
    ThingSpeak.setField(8,proud_bat); 

    //zapsání polí na ThingSpeak
    ThingSpeak.writeFields(Channel_ID, API_key);    

    odeslano = true;
  }

  //průměry fotorezistoru
  int nahore = (analogRead(fotorezistor1)+analogRead(fotorezistor2))/2;  
  int dole = (analogRead(fotorezistor3)+analogRead(fotorezistor4))/2;
  int vlevo = (analogRead(fotorezistor1)+analogRead(fotorezistor3))/2;
  int vpravo = (analogRead(fotorezistor2)+analogRead(fotorezistor4))/2;

  int elevace = (nahore - dole); //určení elevace
  int azimut = (vlevo - vpravo); //určení azimutu

  //koncové spínače
  int spinac_dole = digitalRead(spinac1);
  int spinac_nahore = digitalRead(spinac2);  
  int spinac_vlevo = digitalRead(spinac3); 
  int spinac_vpravo = digitalRead(spinac4);   
  
  //nastavení tolerance pomocí potenciometru
  int tolerance = (analogRead(potenciometr)/2);  

  bool elevace_dokoncena =false;
  bool azimut_dokoncen = false;

  //sledování slunce v elevaci (naklánění panelu kolmo ke Slunci)
  if(tolerance<abs(elevace)) {  
    if(nahore > dole) {
      // pokud je sepnut koncový spínač pro elevaci dole, motory se zastaví
      if(spinac_dole==HIGH) {
        digitalWrite(in3,LOW);
        digitalWrite(in4,LOW);        
      }
      else{
        digitalWrite(in3,LOW);
        digitalWrite(in4,HIGH);
      }      
                   
    }
    else if(nahore < dole) {
      // pokud je sepnut koncový spínač pro elevaci nahoře, motory se zastaví
      if(spinac_nahore==HIGH) {
        digitalWrite(in3,LOW);
        digitalWrite(in4,LOW);         
      }
      else {
        digitalWrite(in3,HIGH);
        digitalWrite(in4,LOW);         
      } 
    }
  }
  
  // pokud je tolerance větší, než absolutní hodnota elevace, panel je správně nakloněn a není potřeba měnit jeho směr
  if(tolerance>abs(elevace)){
    digitalWrite(in3,LOW);
    digitalWrite(in4,LOW);
    elevace_dokoncena = true;
  }

  //sledování slunce v elevaci (naklánění panelu kolmo ke Slunci)
  if(tolerance<abs(azimut)) {
    if(vlevo > vpravo) {
      // pokud je sepnut koncový spínač pro azimut vlevo, motory se zastaví
      if(spinac_vlevo==HIGH){
        digitalWrite(in1,LOW);
        digitalWrite(in2,LOW);        
      }
      else{    
        digitalWrite(in1,HIGH);
        digitalWrite(in2,LOW);  
      }      
    }
    else if(vlevo < vpravo) {
      // pokud je sepnut koncový spínač pro azimut vpravo, motory se zastaví
      if(spinac_vpravo==HIGH){
        digitalWrite(in1,LOW);
        digitalWrite(in2,LOW);        
      }
      else{     
        digitalWrite(in1,LOW);
        digitalWrite(in2,HIGH);        
      }
    } 

  }

  // pokud je tolerance větší, než absolutní hodnota azimutu, panel je správně nakloněn a není potřeba měnit jeho směr
  if(tolerance>abs(azimut)) {
    digitalWrite(in1,LOW);
    digitalWrite(in2,LOW);
    azimut_dokoncen = true;
  }

//pokud je pohyb elevevace i azimutu dokončen, mikrokontroler je uspán
if(elevace_dokoncena && azimut_dokoncen && odeslano)
    sleep(ESP32_SLEEP_TIME);
}

// funkce pro uspávání
void sleep(int s_sleep)
{
  WiFi.mode(WIFI_OFF);
  esp_sleep_enable_timer_wakeup(U_S_TO_S*s_sleep); //nastavení času spánku
  digitalWrite(spinani_motory,HIGH); //ponechání semnutého N-kanálového MOSFET pro modul L298N
  gpio_hold_en(GPIO_NUM_5); //ponechání sepnutého N-kanálového MOSFET pro modul L298N
  gpio_deep_sleep_hold_en();
  esp_deep_sleep_start(); // zahájení Deep Sleep
  delay(10); 
}

//výchozí hledání Slunce
void pozice_vychozi(void){
  while(hledani_pozice){
    int vlevo = (analogRead(fotorezistor1)+analogRead(fotorezistor3))/2; //průměry napěťových děličů vlevo
    int vpravo = (analogRead(fotorezistor2)+analogRead(fotorezistor4))/2; //průměry napěťových děličů vpravo
    int azimut = vlevo - vpravo; //určení azimutu
    int spinac_vlevo = digitalRead(spinac3); //spínač vlevo
    int spinac_vpravo = digitalRead(spinac4); // spínač vpravo 
    int tolerance = (analogRead(potenciometr)/2); //čtení tolerance
    switch(stav_hledani){
      // otáčení doprava
      case 0: 
        digitalWrite(in1,LOW);
        digitalWrite(in2,HIGH); 
        stav_hledani = 1;
        break;
      // kontrola hodnoty senzorů a koncového vypínače
      case 1:
        if(tolerance<abs(azimut)) {
          stav_hledani = 4;
          digitalWrite(in1,LOW);
          digitalWrite(in2,LOW);
            break;
        }
        if(spinac_vpravo==HIGH){
          digitalWrite(in1,LOW);
          digitalWrite(in2,LOW);   
          stav_hledani = 2; 
            break;   
        } 
        break;
      // pravý koncový vypínač aktivován, proto točení vlevo
      case 2:
        digitalWrite(in1,HIGH);
        digitalWrite(in2,LOW);
        stav_hledani = 3;
        break;
      // kontrola senzorů a koncový vypínač vlevo
      case 3:
        if(tolerance<abs(azimut)) {
          stav_hledani = 4;
          digitalWrite(in1,LOW);
          digitalWrite(in2,LOW);
            break;
        }
        if(spinac_vlevo==HIGH){
          digitalWrite(in1,LOW);
          digitalWrite(in2,LOW);   
          stav_hledani = 5;  
            break;  
        }
        break;
      // nalezena správná pozice senzorů
      case 4:
        hledani_pozice = false;
        break;
      // nenalezena správná pozice, uspání na 15 minut
      case 5:
        sleep(900);
        break;
    }    
  }
}

