/*
 ESP32 Receiver (ESP-NOW + Web Server)
  Complete project details at https://RandomNerdTutorials.com/esp32-esp-now-wi-fi-web-server/
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files.
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
*/
//08:3A:F2:A7:80:6C //esp32 желтые ноги
//08:3A:F2:66:31:30 //esp32 lilygo2,3
//08:3A:F2: //esp32 lilygo4,7
#define SD_MISO     12
#define SD_MOSI     13
#define SD_SCLK     14
#define SD_CS       15
#include <esp_task_wdt.h>
#define WDT_TIMEOUT 10
#include <WiFi.h>
#include "ESPAsyncWebSrv.h"
#include <AsyncTCP.h>
#include <Arduino_JSON.h>
#include <esp_now.h>
#include "FS.h"
#include "SD.h"
#include "SPI.h"
#include <SPIFFS.h>

#include "NTPClient.h"
#include "WiFiUdp.h"
#include <UnixTime.h>
UnixTime stamp(0);  // указать GMT (3 для Москвы)
// Define deep sleep options
String dataMessage;
// Define NTP Client to get time
WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP);
#include "nRF24L01.h"
#include "RF24.h"
char msg[16];
//RF24 radio(12, 14, 26, 25, 27);
//RF24 radio(27, 26, 32, 33, 25);//server (ce,csn,sck,miso,mosi)esp32

RF24 radio(17, 16, 32, 33, 25);//server (ce,csn,sck,miso,mosi)esp32
const uint64_t pip = 0x02E423E4E2LL;
SPIClass sdSPI(HSPI);
File myFile;
String jsonString;//"{\"id\":2,\"temperature\":9.6733450317382812,\"humidity\":50.41815185546875,\"readingId\":\"2\"}";
String jsonString1;
String jsonString2;//="{\"tkom1\":\"20.0\",\"hkom1\":\"30.0\",\"batkom1\":\"0.00\",\"timekom1\":\"00:00\",\"tkom2\":\"\",\"hkom2\":\"\",\"batkom2\":\"\",\"timekom2\":\"\",\"tkor\":\"\",\"hkor\":\"\",\"batkor\":\"\",\"timekor\":\"\",\"tbal\":\"\",\"hbal\":\"\",\"batbal\":\"\",\"timebal\":\"\"}"
;
// Replace with your network credentials (STATION)
const char* ssid = "VOVA-47";
const char* password = "vovak26102003";

  char *time_buf;

const char* PARAM_INPUT_1 = "output";
const char* PARAM_INPUT_2 = "state";

// Structure example to receive data
// Must match the sender structure
typedef struct struct_message {
  int id=0;
  float temp=0;
  float hum=0;
  unsigned int readingId=0;
} struct_message;

struct_message incomingReadings;

JSONVar board;
JSONVar board1;
JSONVar board2;



AsyncWebServer server(80);
AsyncEventSource events("/events");
//*************************************************************************************************
char* strd[] = {"01","02","03","04","05","06","07","08","09","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31"};
  char* strm[] = {"январь","февраль","март","апрель","май","июнь","июль","август","сентябрь","октябрь","ноябрь","декабрь"};
  char* strweek[] = {"воскресенье","понедельник","вторник","среда","четверг","пятница","суббота","воскресенье"};
 int nt=0;//номер термометра
  int nti=0;//номер термометра для сайта
 int temperature1=10;
 double ts;
 double hs;
String t1;
 String h1;
   String t10;//время работы сервера
    String tim3;// время от нтп сервера  при приходе данных с NOW
    String tim2;//время  получ от устройства NOW
    String munthserv;//месяц
    String weekserv;//день недели

String tkom1;//температура
String hkom1;//влажность
String batkom1;
String timekom1;//время прихода данных комната1

String tkom2;
String hkom2;
String batkom2;
String timekom2;

String tkor;
String hkor;
String batkor;
String timekor;

String tbal;
String hbal;
String batbal;
String timebal;

String timeul1;
String tul1;
String hul1;
String osv1;
int ct1=0;//счетчик пропуска данные каждые 3 минуты записываем каждые 20 те 7 пропусков

    
 int time17=0;
int hour1=10;
int min1=7;
int sek=0;
int hk=0;//часы для исключения повтора записи данных
int mk=0;//минуты
int hb=0;//часы для исключения повтора записи данных
int mb=0;//минуты
int hkr=0;//часы для исключения повтора записи данных
int mkr=0;//минуты
int timev=0;//для пропуска 20 минут
//String timev1;//для записи на флэш при сбросе

boolean fdata=0;//флаг обновления данных темп и влажн
int fobnov=1;//флаг обновления данных темп и влажн
String timeserv;//время сервера
String dataserv;//дата
int v=0;
int v1=0;
long EpochTime=0;
 int ind=0;
 String strdat;//накопленые в памяти данные по темп и влажность передаваемые клиенту
  String tempdat;//
int ftdata=0;//флаг запроса данных темп и влажн
int fping=0;// флаг для задержки отр данных на страницу inform
int wright=0;
bool flread=0;
 char* strden[] = {"/01","/02","/03","/04","/05","/06","/07","/08","/09","/10","/11","/12","/13","/14","/15","/16","/17","/18","/19","/20","/21","/22","/23","/24","/25","/26","/27","/28","/29","/30","/31"};  
 char* strmen[] = {"/JAN","/FEBR","/MARCH","/APRIL","/MAY","/JUNE","/JULY","/AUG","/SEPT","/OCT","/NOV","/DEC"};
 char* stryen[] = {"/2014","/2015","/2016","/2017","/D-2018","/D-2019","/D-2020","/D-2021","/D-2022","/D-2023","/D-2024","/D-2025","/D-2026","/D-2027","/D-2028","/D-2029","/D-2030","/D-2031","/D-2032","D-2033","/D-2034","/D-2035"};
 char* strdaten[] = {"/T1.txt","/T2.txt","/T3.txt","/T4.txt","/T5.txt","/T6.txt","/T7.txt","/T8.txt","/T9.txt","/T10.txt"};
//************************************************************
hw_timer_t * timer = NULL;
volatile SemaphoreHandle_t timerSemaphore;
portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED;
volatile uint32_t isrCounter = 0;
volatile uint32_t lastIsrAt = 0;
//************************************************************************************************** 
// ntp
String formattedDate;
String formattedTime;
String dayStamp;
String timeStamp;


//*****************************************************************************

//****************************************************************************
String outputState(int output){
  if(digitalRead(output)){
    return "checked";
  }
  else {
    return "";
  }
}

String processor(const String& var){
  //Serial.println(var);
  if(var == "BUTTONPLACEHOLDER1"){
    String buttons = "";
  //  buttons += "<h4>Output - GPIO 12</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"12\" " + outputState(12) + "><span class=\"slider\"></span></label>";
    buttons += "<h4>Output - GPIO 13</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"13\" " + outputState(13) + "><span class=\"slider\"></span></label>";
  //  buttons += "<h4>Output - GPIO 14</h4><label class=\"switch\"><input type=\"checkbox\" onchange=\"toggleCheckbox(this)\" id=\"14\" " + outputState(14) + "><span class=\"slider\"></span></label>";
    return buttons;
  }
  return String();
}
 /* if(var == "HOLDERT1"){
    String buttons = "";
  buttons +="<p><span class=\"reading\">"+t1+" &deg;C</span></p>";
  String hour11=String(hour1, DEC);
  String min11=String(min1, DEC);
  if(min1<10){min11="0"+min11;}
 buttons +="<p>"+hour11+":"+min11+"</p>";
  return buttons;
}
 if(var == "HOLDERH1"){
    String buttons = "";
  buttons +="<p><span class=\"reading\">"+h1+" &percnt;</span></p>";
  String hour11=String(hour1, DEC);
  String min11=String(min1, DEC);
  if(min1<10){min11="0"+min11;}
 buttons +="<p>"+hour11+":"+min11+"</p>";
  return buttons;
}
*/
 /*if(var == "HOLDER2"){
    String buttons = "";

  buttons +="<p>"+t10+"</p>";
  return buttons;

}*/
//************************************************


//***********************************************************
void vrem();
void handle_OnConnect();
void ntp();
void ntphome();
void IRAM_ATTR onTimer();
 void event();
 void WiFisetup();
 void savedata();
 void JSstringU();
  void JSstringK();
  void Sdreaddata();
 void Nrfsetup();
 void nrf();
 void parsmsg();
 void settimer();
 void readdata();
 //void  ansv();
//***********************************************************
void OnDataRecv(const uint8_t * mac_addr, const uint8_t *incomingData, int len) { 
  // Copies the sender mac address to a string
  char macStr[18];
  Serial.print("Packet received from: ");
  snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x",
           mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
  Serial.println(macStr);
  memcpy(&incomingReadings, incomingData, sizeof(incomingReadings));
  
  Serial.printf("Board ID %u: %u bytes\n", incomingReadings.id, len);
  Serial.printf("t value: %4.2f \n", incomingReadings.temp);
  t1 = incomingReadings.temp;
  Serial.printf("h value: %4.2f \n", incomingReadings.hum);
  h1=incomingReadings.hum;
  Serial.printf("readingID value: %d \n", incomingReadings.readingId);
  int tim11 =incomingReadings.readingId;
  hour1=tim11/100;
  min1=tim11%100;
//JSONстрока формирование
  ntphome();
  tim3=timeserv;
JSstringU(); 
events.send(jsonString.c_str(), "new_readings", millis());//обновление веба
// fdata=1;
 
 // fdata=0;
  savedata();
}//OnDataRecv
 //****************************************************************************
 void configureEvents() {
  events.onConnect([](AsyncEventSourceClient *client){
    if(client->lastId()){
      Serial.printf("Client connections. Id: %u\n", client->lastId());
    }
    // and set reconnect delay to 1 second
    client->send("hello from ESP32",NULL,millis(),1000);
  });
 
}// configureEvents
//*********************************************************************************************************
void setup() {
  // Initialize Serial Monitor
  pinMode(13, OUTPUT);
  digitalWrite(13, LOW);
  Serial.begin(115200);

if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

   sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  if (!SD.begin(SD_CS, sdSPI))
  {
    // if(!SD.begin()){
    Serial.println("Mounting card mount failed");
   // return;
  }

 
uint8_t cardType = SD.cardType();
if(cardType == CARD_NONE){
    Serial.println("No SD card attached");
    //return;
  }
  uint64_t cardSize = SD.cardSize() / (1024 * 1024);
  Serial.printf("SD Card Size: %lluMB\n", cardSize);
  Sdreaddata();
  Nrfsetup();
settimer();
WiFisetup();
  // Initialize a NTPClient to get time
  timeClient.begin();
  // Set offset time in seconds to adjust for your timezone, for example:
  // GMT +1 = 3600
  // GMT +8 = 28800
  // GMT -1 = -3600
  // GMT 0 = 0
  timeClient.setTimeOffset(10800);  
 // Once ESPNow is successfully Init, we will register for recv CB to
  // get recv packer info
  esp_now_register_recv_cb(OnDataRecv);
 ntp();//NTP init
 
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
     fobnov=1;
  });
  server.on("/index.html", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html", String(), false, processor);
  fobnov=1;
  
  });
   // Route to load style.css file
  server.on("/style.css", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/style.css", "text/css");
  });


   server.on("/fon.jpg", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/fon.jpg");
  });
  
  server.on("/upravlenie.html", HTTP_GET, [](AsyncWebServerRequest *request){
    fobnov=1;
    request->send(SPIFFS, "/upravlenie.html", String(), false, processor);
  });
  
   server.on("/setting.html", HTTP_GET, [](AsyncWebServerRequest *request){
    
    request->send(SPIFFS, "/setting.html");
  });
    server.on("/inform.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=1;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform.html");
  });

     server.on("/inform1.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=2;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform1.html");
  });

   server.on("/inform2.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=4;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform2.html");
  });
   server.on("/inform3.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=7;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform3.html");
  });
   server.on("/inform4.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=3;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform4.html");
  });
   server.on("/inform5.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=5;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform5.html");
  });
   server.on("/inform6.html", HTTP_GET, [](AsyncWebServerRequest *request){
     ftdata=6;
     fping=0;
   //  Serial.println ("ftdata==1");
    request->send(SPIFFS, "/inform6.html");
  });
    server.on("/favicon.ico", HTTP_GET, [](AsyncWebServerRequest *request){
    
    request->send(SPIFFS, "/favicon.ico");
  });
    server.on("/favicon-32x32.png", HTTP_GET, [](AsyncWebServerRequest *request){
    
    request->send(SPIFFS, "/favicon-32x32.png");
  });
   server.on("/favicon-16x16.png", HTTP_GET, [](AsyncWebServerRequest *request){
    
    request->send(SPIFFS, "/favicon-32x32.png");
  });
  
   // Send a GET request to <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>  принимаем get сообщение от клиента при нажати кнопки
   // принимаем номер контакта и его состояние 
 //  GET  http://192.168.1.100/update?output=13&state=1
  server.on("/update", HTTP_GET, [] (AsyncWebServerRequest *request) {
    String inputMessage1;
    String inputMessage2;
    // GET input1 value on <ESP_IP>/update?output=<inputMessage1>&state=<inputMessage2>

    
    if (request->hasParam(PARAM_INPUT_1) && request->hasParam(PARAM_INPUT_2)) {
      inputMessage1 = request->getParam(PARAM_INPUT_1)->value();
      inputMessage2 = request->getParam(PARAM_INPUT_2)->value();
      digitalWrite(inputMessage1.toInt(), inputMessage2.toInt());
    }
    else {
      inputMessage1 = "No message sent";
      inputMessage2 = "No message sent";
    }
    Serial.print("GPIO: ");
    Serial.print(inputMessage1);
    Serial.print(" - Set to: ");
    Serial.println(inputMessage2);
   
    request->send(200, "text/plain", "OK");
  });  

JSstringU();
JSstringK();

configureEvents();
 server.addHandler(&events);

  server.begin();
  esp_task_wdt_init(WDT_TIMEOUT, true); //включить панику, чтобы ESP32 перезапустился
  esp_task_wdt_add(NULL); //добавить текущий поток в WDT watch

}


//*********************************************************************************************************** 
//*********************************************************************************************************** 
//*********************************************************************************************************** 
void loop() 
{


  static unsigned long lastEventTime = millis();
 
  static const unsigned long EVENT_INTERVAL_MS = 5000;
  if (v>3) {//20sek
   ntphome();//считываем время
   v=0;//обнуляем счетчик 4*5=20с
  }
  if (v1>240) {//20min
   ntp();//считываем время
   v1=0;//обнуляем счетчик 240*5=1200с
  }

   //   digitalWrite(13, HIGH); // светодиод горит
// digitalWrite(13, LOW); // светодиод погашен
  if ((millis() - lastEventTime) > EVENT_INTERVAL_MS)
  {
        events.send("ping",NULL,millis());
        lastEventTime = millis();
        v++;
        v1++;
        
        vrem();//время работы сайта
         events.send(t10.c_str(), "t10", millis());//время работы сайта обн каждые 5сек
         events.send(timeserv.c_str(), "timeserv", millis());//время обн каждые 5сек
        
          fping++;//задержка отправки данных на стр до готовности стр принять их
        if(fping>1000){fping=0;}     
          nrf(); //проверяем не приш ли данные на NRF
         wright++;//сч времени для записи на флеш 
        Serial.println (wright);
        Serial.print ("ftdata=");
        Serial.println (ftdata);
         Serial.print ("fobnov=");
        Serial.println (fobnov);
      if(  ftdata==0)
        { if (fobnov>0) {//2,5sek
           fobnov++;
          if(fobnov>3){fobnov=0;}       
           events.send(jsonString.c_str(), "new_readings", millis());//обновление веба текущая температура
           events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление веба
          Serial.println("/index");
          }
       }

//*****************************************************************************************


//*******************************************************************************************
 
//*********************************************************************************************  



 //**********************************************************************************************
    if (ftdata==2)//пришел запрос табл данных /inform ЗАПАД
 {// tim3=formattedTime;
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление веба
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=1;
   readdata();
  // Serial.println("data1");
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }
//***************************************************************************************************
    if (ftdata==1)//пришел запрос табл данных /inform ВОСТОК
 {// tim3=formattedTime;
 
  if(fping>2){fping=0;}
  if(fping==1){
// events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление веба
 events.send(jsonString.c_str(), "new_readings", millis());//обновление данных ЗАПАД
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=0;
   readdata();
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
 // fobnov=1;
  
  }
 }

//***************************************************************************************************
    if (ftdata==3)//пришел запрос табл данных /inform коридор
 {// tim3=formattedTime;
 
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление данных Т Н ВАТ все кроме запад
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=5;
   readdata();
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }
//***************************************************************************************************
    if (ftdata==5)//пришел запрос табл данных /inform балкон
 {// tim3=formattedTime;
 
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление данных Т Н ВАТ все кроме запад
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=2;
   readdata();
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }

//******************************************************************************************************
     if (ftdata==4)//пришел запрос табл данных /inform К1
 {// tim3=formattedTime;
 
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление данных Т Н ВАТ все кроме запад
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=4;
   readdata();
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }


 //******************************************************************************************************
     if (ftdata==7)//пришел запрос табл данных /inform К2
 {// tim3=formattedTime;
 
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление данных Т Н ВАТ все кроме запад
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=3;
   readdata();
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }

 //*********************************************************************************************************
    if (ftdata==6)//пришел запрос табл данных /inform
 {// tim3=formattedTime;
  if(fping>2){fping=0;}
  if(fping==1){
 events.send(jsonString2.c_str(), "new_datatemp", millis());//обновление веба
  }
  if(fping>1){
  ftdata=0;
  fping=0;
  nti=1;
   readdata();
   Serial.println("data1");
  events.send(strdat.c_str(), "readings", millis());//отправка данных из памяти в таблицу на стр inform
  }
 }



 
  }
}
