/*──────────────────────────────────────────────────────────────────────────────┐
  Target: Generic ESP32 SIM800L TTGO Module
    - Default 4MB (1.2MB APP / 1.5MB SPIFFS)
  ─────────────────────────────────────────────────────────────────────────────*/

// 00-alpha, 02-beta, 03-RC, 04-release
const String firmware         = "000001";                                 // Current version

// ──────────────────────────────────────────────────────────────────────────────

uint32_t deviceID             = 0;                                        // Unique Device ID
String interrupt              = "OFF";                                    // If force wake up, set to true

// ──────────────────────────────────────────────────────────────────────────────

// CRC
uint32_t knownCRC32    = 0x6f50d767;
uint32_t knownFileSize = 1024;

// ──────────────────────────────────────────────────────────────────────────────

// GPRS details
const char apn[]              = "internet";                               // APN (example: internet.vodafone.pt)
const char gprsUser[]         = "guest";                                  // GPRS User
const char gprsPass[]         = "";                                       // GPRS Password
const char simPIN[]           = "0000";                                   // SIM card PIN (leave empty, if not defined)

// ──────────────────────────────────────────────────────────────────────────────

// Server details
const char server[]           = "reidefine.online";                       // domain name: example.com, maker.ifttt.com, etc
const char resource[]         = "/post-data.php";                         // resource path, for example: /post-data.php
const int  port               = 80;                                       // server port number
String apiKeyValue            = "tPmAT5Ab3j7F9";                          // apiKeyValue value must = /post-data.php value
String txt                    = "http://www.reidefine.online/firmware/Ground_Separation_Detector/Current/GSD/fw.txt";
// ──────────────────────────────────────────────────────────────────────────────

// GPS variables
static const int gpsRXPin = 0, gpsTXPin = 15;
bool gpsStatus                = false;
static const uint32_t GPSBaud = 9600;
double gpslong                = 1;
double gpslat                 = 0;

// ──────────────────────────────────────────────────────────────────────────────

// BME variables
float temp;
float humid;
float pressure;
float alt;
float gas;

// ──────────────────────────────────────────────────────────────────────────────

//=====================================================================================================================================//
//========================================CONFIG=======================================================================================//
//=====================================================================================================================================//

// TTGO T-Call pins

#define MODEM_RST             5
#define MODEM_PWKEY           4
#define MODEM_POWER_ON        23
#define MODEM_TX              27
#define MODEM_RX              26
// I2C bus1 pins
#define I2C_SDA               18
#define I2C_SCL               19
// I2C bus2 pins
#define I2C_SDA_2             21
#define I2C_SCL_2             22

//=====================================================================================================================================//
// Serial(s)

#define SerialMon Serial                                                  // Set serial for debug console
#define SerialAT Serial1                                                  // Set serial for modem

//=====================================================================================================================================//
// Configure TinyGSM library

#define TINY_GSM_MODEM_SIM800
#define TINY_GSM_RX_BUFFER    1024

//=====================================================================================================================================//
// Included Libraries

#include <Wire.h>
#include <TinyGsmClient.h>
#include "FS.h"
#include "SPIFFS.h"
#include <Adafruit_Sensor.h>
#include <CRC32.h>
#include "Adafruit_BME680.h"                                              // New BME680 Sensor
#include <TFMPI2C.h>                                                      // New TFmini Plus Sensor
#include <TinyGPS++.h>                                                    // GPS
#include <SoftwareSerial.h>
#include <Nextion.h>                                                      // New Nextion Display
#include "nextion_variables.h"
#include <Update.h>
#include <HTTPClient.h>
#include <DNSServer.h>
#include <ESPmDNS.h>
#include <ArduinoOTA.h>
#include <esp_sleep.h>                                                    // Added new
#include <driver/rtc_io.h>                                                // Added new


HTTPClient http;

//=====================================================================================================================================//
// TinyGSM Client

#ifdef DUMP_AT_COMMANDS
#include <StreamDebugger.h>
StreamDebugger debugger(SerialAT, SerialMon);
TinyGsm modem(debugger);
#else
TinyGsm modem(SerialAT);
#endif

TinyGsmClient client(modem);

//=====================================================================================================================================//
// I2C

// I2C for SIM800
TwoWire I2Cone                = TwoWire(0);
// I2C for other sensors
TwoWire I2Ctwo                = TwoWire(1);

//=====================================================================================================================================//
// TTGO T-Call Power setup

#define IP5306_ADDR           0x75
#define IP5306_REG_SYS_CTL0   0x00
bool setPowerBoostKeepOn(int en) {
  I2Cone.beginTransmission(IP5306_ADDR);
  I2Cone.write(IP5306_REG_SYS_CTL0);
  if (en) {
    I2Cone.write(0x37);                                                   // Set bit1: 1 enable 0 disable boost keep on
  } else {
    I2Cone.write(0x35);                                                   // 0x37 is default reg value
  }
  return I2Cone.endTransmission() == 0;
}

//=====================================================================================================================================//
// BME680

Adafruit_BME680 bme;
#define SEALEVELPRESSURE_HPA (1013.25)

//=====================================================================================================================================//
// TOFsensor                                                              //(Modified TFMPI2C.ccp from I2C_FORMAT_CM to I2C_FORMAT_MM)

TFMPI2C tfmP;
int16_t tfDist                = 0;                                        // Distance to object in mm
float total                   = 0;                                        // The running total
float average                 = 0;                                        // The average
boolean updateDistance        = false;
//=====================================================================================================================================//
// Battery

#define Battery_Pin           35
uint8_t percentage            = 100;
float voltage;

//=====================================================================================================================================//
// GPS

TinyGPSPlus gps;
SoftwareSerial ss(gpsRXPin, gpsTXPin);                                    // Activate GPS

//=====================================================================================================================================//
// Deep Sleep Timer setup

#define uS_TO_S_FACTOR 1000000UL                                          // Conversion micro seconds to seconds
#define TIME_TO_SLEEP  3600                                               // Time to sleep (in seconds) 3600 seconds = 1 hour

//=====================================================================================================================================//
// Nextion Display

static const int nex_RX = 14, nex_TX = 13;
SoftwareSerial HMISerial(nex_RX, nex_TX);

//=====================================================================================================================================//
// Laser

static const int lsr          = 2;

//=====================================================================================================================================//
//========================================END of CONFIG================================================================================//
//=====================================================================================================================================//

//=====================================================================================================================================//
//========================================SETUP========================================================================================//
//=====================================================================================================================================//

void setup() {

  SerialMon.flush();
  SerialMon.begin(9600);

  SerialMon.println("\n\n ======================================");
  SerialMon.println("      Ground Separation Detector     ");
  SerialMon.println(" --------------------------------------\n");

// ──────────────────────────────────────────────────────────────────────────────

  // Config Laser
  pinMode(lsr,OUTPUT);
  digitalWrite(lsr,LOW);

// ──────────────────────────────────────────────────────────────────────────────

  delay(1000);

// ──────────────────────────────────────────────────────────────────────────────

  // External wakeup - Vibration Sensor
  print_wakeup_reason();
  esp_sleep_enable_ext0_wakeup(GPIO_NUM_34, 0);                           // 1 = High, 0 = Low

// ──────────────────────────────────────────────────────────────────────────────

  // Check Battery
  SerialMon.println("- read Battery Voltage");
  get_Battery_Value();
  SerialMon.println();

// ──────────────────────────────────────────────────────────────────────────────

  // Get ChipID
  SerialMon.println("- read Device ID aka eFuse MAC");
  get_chipID();
  SerialMon.println(String(" - chipID: ") + (deviceID));
  SerialMon.println();

// ──────────────────────────────────────────────────────────────────────────────

  // Start I2C communication
  I2Cone.begin(I2C_SDA, I2C_SCL, 400000);
  I2Ctwo.begin(I2C_SDA_2, I2C_SCL_2, 400000);

// ──────────────────────────────────────────────────────────────────────────────

  // Start GPS
  ss.begin(GPSBaud);

// ──────────────────────────────────────────────────────────────────────────────

  delay(1000);

// ──────────────────────────────────────────────────────────────────────────────

  // Arduino wake up display
  if (interrupt == "ON") {
    nexInit();                                                            //Start TouchScreen
    HMISerial.print("sleep=0");
    HMISerial.write(0xff);
    HMISerial.write(0xff);
    HMISerial.write(0xff);
  }

// ──────────────────────────────────────────────────────────────────────────────

  // Check BME680
  SerialMon.println("- start BME680 Sensor");
  if (!bme.begin(0x77, &I2Ctwo)) {
    delay(1000);
    if (!bme.begin(0x77, &I2Ctwo)) {
      SerialMon.println(" - Sensor not found, check wiring!");
    }
  }
  get_BME_values();

// ──────────────────────────────────────────────────────────────────────────────

  // Check TFMini-Plus
  SerialMon.println("- start Range Sensor");
  if (!tfmP.status == TFMP_READY) {
    SerialMon.println(F(" - Failed to boot TFMini-Plus"));
    while (1);
  }
  get_distance();

// ──────────────────────────────────────────────────────────────────────────────

  // Set modem reset, enable, power pins
  pinMode(MODEM_PWKEY,          OUTPUT);
  pinMode(MODEM_RST,            OUTPUT);
  pinMode(MODEM_POWER_ON,       OUTPUT);
  digitalWrite(MODEM_PWKEY,     LOW);
  digitalWrite(MODEM_RST,       HIGH);
  digitalWrite(MODEM_POWER_ON,  HIGH);

// ──────────────────────────────────────────────────────────────────────────────

  // Set GSM module baud rate and UART pins
  SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
  delay(3000);
  SerialMon.println("- Initializing modem...\n");
  modem.restart();

// ──────────────────────────────────────────────────────────────────────────────

  // Unlock your SIM needed
  if (strlen(simPIN) && modem.getSimStatus() != 3 ) {
    modem.simUnlock(simPIN);
  }

// ──────────────────────────────────────────────────────────────────────────────

  // Keep power when running from battery
  bool isOk = setPowerBoostKeepOn(1);
  SerialMon.println(String("IP5306 KeepOn ") + (isOk ? "OK" : "FAIL") + "\n");

// ──────────────────────────────────────────────────────────────────────────────

  // Configure the wake up source as timer wake up
  esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR);

}
//=====================================================================================================================================//
//========================================End of SETUP=================================================================================//
//=====================================================================================================================================//

//=====================================================================================================================================//
//========================================LOOP=========================================================================================//
//=====================================================================================================================================//

void loop() {
// ──────────────────────────────────────────────────────────────────────────────

  if (gpslong == 1) {
    SerialMon.println("Waiting for GPS ");
    long timenow = millis() + 5000;
    while (timenow > millis()) {
      handleGPS();
    }
    gpslong = gps.location.lng();
    gpslat  = gps.location.lat();
    SerialMon.print("LONG: ");
    SerialMon.print(gpslong, 6);
    SerialMon.print(", LAT: ");
    SerialMon.println(gpslat, 6);
    SerialMon.println();
  }

// ──────────────────────────────────────────────────────────────────────────────

  if (interrupt != "OFF" && gpslong != 0){
    String data_from_display = "";
    if (HMISerial.available()) {    
      delay(30);
      while (HMISerial.available()) {
        char tempChar = char(HMISerial.read());
//        SerialMon.print(int(tempChar));
//        SerialMon.print(" ");
        if ((tempChar != 0xff) && (tempChar > 47)) data_from_display += tempChar;
      }
      data_from_display.trim();
      SerialMon.print("data_from_display: ");
      SerialMon.println(data_from_display);
      sendData(data_from_display);
    }else{

      if (updateDistance == true){
        tfmP.getData(tfDist);
        HMISerial.print("tDist3.txt=\"" + String(tfDist) + "\"");
        HMISerial.write(0xff);
        HMISerial.write(0xff);
        HMISerial.write(0xff);
        }
      }
      
// ──────────────────────────────────────────────────────────────────────────────
  } else {
// ──────────────────────────────────────────────────────────────────────────────
    SerialMon.print("Connecting to APN: ");
    SerialMon.print(apn);
    if (!modem.gprsConnect(apn, gprsUser, gprsPass)) {
      SerialMon.println(" fail");
    }
    else {
      SerialMon.println(" OK");
      SerialMon.print("Connecting to ");
      SerialMon.print(server);
      if (!client.connect(server, port)) {
        SerialMon.println(" fail");
      }
      else {
        SerialMon.println(" OK");

        SerialMon.println("Performing HTTP POST request...");
        String httpRequestData = "api_key=" + apiKeyValue + "&value1=" + String(average) + "&value2=" + String(percentage) + "&value3=" +
                                 String(temp) + "&value4=" + String(humid) + "&value5=" + String(pressure) + "&deviceID=" + String(deviceID) +
                                 "&gpslong=" + String(gpslong) + "&gpslat=" + String(gpslat) + "&alt=" + String(alt) + "&firmware=" + String(firmware) + "";
        SerialMon.println("HTTP POST string ready...");

        client.print(String("POST ") + resource + " HTTP/1.1\r\n");
        client.print(String("Host: ") + server + "\r\n");
        client.println("Connection: close");
        client.println("Content-Type: application/x-www-form-urlencoded");
        client.print("Content-Length: ");
        client.println(httpRequestData.length());
        client.println();
        client.println(httpRequestData);

        SerialMon.println("HTTP POST running...\n\n");

        unsigned long timeout = millis();
        while (client.connected() && millis() - timeout < 10000L) {
          while (client.available()) {
            char c = client.read();
            SerialMon.print(c);
            timeout = millis();
            yield();
          }
        }

        SerialMon.println();
        //execOTA();                                                                  //Disabled again - solve next
        client.stop();
        SerialMon.println(F("Server disconnected"));
        modem.gprsDisconnect();
        SerialMon.println(F("GPRS disconnected"));
      }
    }

    // Deep sleep mode (with timer wake up)
    SerialMon.println("\n ======================================");
    SerialMon.println("      Going to sleep now     ");
    SerialMon.println(" --------------------------------------\n");

    rtc_gpio_pullup_en(GPIO_NUM_34);
    rtc_gpio_hold_en(GPIO_NUM_34);
    
    esp_deep_sleep_start();
    
  }
}
//=====================================================================================================================================//
//========================================End of LOOP==================================================================================//
//=====================================================================================================================================//
