
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <WiFiClientSecure.h>
#include <ESP8266httpUpdate.h>
#include <ArduinoJson.h>
#include <EEPROM.h>

// =====================================================
// RELAY
// =====================================================

#define RELAY_PIN 0

// =====================================================
// VERSION
// =====================================================

int firmwareVersion = 1;

// =====================================================
// WEB SERVER
// =====================================================

ESP8266WebServer server(80);

// =====================================================
// EEPROM DATA
// =====================================================

String ssid = "";
String password = "";
String idperintah = "";

// =====================================================
// URL
// =====================================================

String dataURL =
"https://bintangterang.or.id/url/3/get.php";

String versionURL =
"https://bintangterang.or.id/url/firmware/version.txt";

String firmwareURL =
"https://bintangterang.or.id/url/firmware/firmware.bin";

String webURL =
"https://bintangterang.or.id/url/firmware/index.php";

// =====================================================
// TIMER
// =====================================================

unsigned long lastTime = 0;
unsigned long interval = 1000;

// =====================================================
// SAVE EEPROM
// =====================================================

void saveData(String s, String p, String id) {

  EEPROM.begin(512);

  // CLEAR EEPROM
  for (int i = 0; i < 32; i++) {

    EEPROM.write(i, 0);
    EEPROM.write(i + 32, 0);
    EEPROM.write(i + 64, 0);

  }

  // SAVE SSID
  for (int i = 0; i < s.length(); i++) {

    EEPROM.write(i, s[i]);

  }

  // SAVE PASSWORD
  for (int i = 0; i < p.length(); i++) {

    EEPROM.write(i + 32, p[i]);

  }

  // SAVE ID
  for (int i = 0; i < id.length(); i++) {

    EEPROM.write(i + 64, id[i]);

  }

  EEPROM.commit();

  EEPROM.end();

  Serial.println();
  Serial.println("EEPROM SAVED");

}

// =====================================================
// LOAD EEPROM
// =====================================================

void loadData() {

  EEPROM.begin(512);

  char s[33];
  char p[33];
  char id[33];

  for (int i = 0; i < 32; i++) {

    s[i] = EEPROM.read(i);

    p[i] = EEPROM.read(i + 32);

    id[i] = EEPROM.read(i + 64);

  }

  // NULL TERMINATOR
  s[32] = '\0';
  p[32] = '\0';
  id[32] = '\0';

  ssid = String(s);
  password = String(p);
  idperintah = String(id);

  EEPROM.end();

  Serial.println();
  Serial.println("======================");
  Serial.println("LOAD EEPROM");
  Serial.println("SSID : " + ssid);
  Serial.println("PASS : " + password);
  Serial.println("ID   : " + idperintah);
  Serial.println("======================");

}

// =====================================================
// OFFLINE HTML
// =====================================================

String offlineHTML() {

  String page = "";

  page += "<!DOCTYPE html>";
  page += "<html>";

  page += "<head>";

  page += "<meta charset='UTF-8'>";

  page += "<meta name='viewport' ";
  page += "content='width=device-width, initial-scale=1'>";

  page += "<title>ESP SETUP</title>";

  page += "<style>";

  page += "body{";
  page += "background:#0f172a;";
  page += "font-family:Arial;";
  page += "padding:20px;";
  page += "color:white;";
  page += "}";

  page += ".box{";
  page += "max-width:350px;";
  page += "margin:auto;";
  page += "background:#1e293b;";
  page += "padding:20px;";
  page += "border-radius:15px;";
  page += "}";

  page += "h2{";
  page += "text-align:center;";
  page += "margin-bottom:20px;";
  page += "}";

  page += "input{";
  page += "width:100%;";
  page += "padding:12px;";
  page += "margin-bottom:15px;";
  page += "border:none;";
  page += "border-radius:10px;";
  page += "box-sizing:border-box;";
  page += "}";

  page += "button{";
  page += "width:100%;";
  page += "padding:12px;";
  page += "background:#3b82f6;";
  page += "border:none;";
  page += "border-radius:10px;";
  page += "color:white;";
  page += "font-size:16px;";
  page += "}";

  page += "</style>";

  page += "</head>";

  page += "<body>";

  page += "<div class='box'>";

  page += "<h2>ESP WIFI SETUP</h2>";

  page += "<form action='/save'>";

  page += "<input type='text' ";
  page += "name='s' ";
  page += "placeholder='SSID WIFI'>";

  page += "<input type='password' ";
  page += "name='p' ";
  page += "placeholder='PASSWORD WIFI'>";

  page += "<input type='text' ";
  page += "name='id' ";
  page += "placeholder='ID PERINTAH'>";

  page += "<button type='submit'>";
  page += "SIMPAN";
  page += "</button>";

  page += "</form>";

  page += "</div>";

  page += "</body>";
  page += "</html>";

  return page;

}

// =====================================================
// ACCESS POINT
// =====================================================

void setupAP() {

  WiFi.mode(WIFI_AP);

  WiFi.softAP("ESP-SETUP", "12345678");

  Serial.println();
  Serial.println("======================");
  Serial.println("AP MODE");
  Serial.println("SSID : ESP-SETUP");
  Serial.println("PASS : 12345678");
  Serial.print("IP : ");
  Serial.println(WiFi.softAPIP());
  Serial.println("======================");

  // =====================================================
  // HOME PAGE
  // =====================================================

  server.on("/", []() {

    server.send(200,
    "text/html",
    offlineHTML());

  });

  // =====================================================
  // SAVE CONFIG
  // =====================================================

  server.on("/save", []() {

    String s = server.arg("s");

    String p = server.arg("p");

    String id = server.arg("id");

    Serial.println();
    Serial.println("SAVE CONFIG");
    Serial.println("SSID : " + s);
    Serial.println("PASS : " + p);
    Serial.println("ID   : " + id);

    saveData(s, p, id);

    server.send(200,
    "text/html",
    "<h1 style='font-family:Arial'>SETTING TERSIMPAN<br>ESP RESTART...</h1>");

    delay(3000);

    ESP.restart();

  });

  server.begin();

}

// =====================================================
// CONNECT WIFI
// =====================================================

void connectWiFi() {

  WiFi.mode(WIFI_STA);

  WiFi.begin(
  ssid.c_str(),
  password.c_str());

  Serial.println();
  Serial.println("CONNECT WIFI");

  int timeout = 0;

  while (WiFi.status() != WL_CONNECTED) {

    delay(500);

    Serial.print(".");

    timeout++;

    if (timeout > 20) {

      Serial.println();
      Serial.println("FAILED CONNECT");

      setupAP();

      return;

    }

  }

  Serial.println();
  Serial.println("======================");
  Serial.println("WIFI CONNECTED");
  Serial.print("IP : ");
  Serial.println(WiFi.localIP());
  Serial.println("======================");

}

// =====================================================
// OTA UPDATE
// =====================================================

void checkUpdate() {

  if (WiFi.status() != WL_CONNECTED) {
    return;
  }

  WiFiClientSecure client;

  client.setInsecure();

  HTTPClient http;

  http.begin(client, versionURL);

  int httpCode = http.GET();

  Serial.println();
  Serial.print("VERSION HTTP : ");
  Serial.println(httpCode);

  if (httpCode == 200) {

    int serverVersion =
    http.getString().toInt();

    Serial.print("SERVER VERSION : ");
    Serial.println(serverVersion);

    Serial.print("DEVICE VERSION : ");
    Serial.println(firmwareVersion);

    if (serverVersion > firmwareVersion) {

      Serial.println("START OTA UPDATE");

      ESPhttpUpdate.update(
      client,
      firmwareURL);

    }

  }

  http.end();

}

// =====================================================
// GET SERVER DATA
// =====================================================

void getDataFromServer() {

  if (WiFi.status() != WL_CONNECTED) {
    return;
  }

  WiFiClientSecure client;

  client.setInsecure();

  HTTPClient http;

  http.begin(client, dataURL);

  int httpCode = http.GET();

  Serial.print("DATA HTTP : ");
  Serial.println(httpCode);

  if (httpCode > 0) {

    String payload =
    http.getString();

    Serial.println(payload);

    DynamicJsonDocument doc(512);

    DeserializationError error =
    deserializeJson(doc, payload);

    if (!error) {

      if (doc.is<JsonArray>()) {

        for (JsonObject obj :
        doc.as<JsonArray>()) {

          String id_server =
          obj["id_perintah"];

          String data =
          obj["data"];

          Serial.print("ID : ");
          Serial.println(id_server);

          Serial.print("DATA : ");
          Serial.println(data);

          if (id_server ==
          idperintah) {

            if (data == "1") {

              digitalWrite(
              RELAY_PIN,
              HIGH);

              Serial.println(
              "RELAY ON");

            } else {

              digitalWrite(
              RELAY_PIN,
              LOW);

              Serial.println(
              "RELAY OFF");

            }

          }

        }

      }

    } else {

      Serial.println("JSON ERROR");

    }

  }

  http.end();

}

// =====================================================
// SETUP
// =====================================================

void setup() {

  Serial.begin(115200);

  pinMode(RELAY_PIN, OUTPUT);

  digitalWrite(RELAY_PIN, LOW);

  loadData();

  if (ssid.length() < 1) {

    Serial.println("NO WIFI CONFIG");

    setupAP();

  } else {

    connectWiFi();

    checkUpdate();

  }

}

// =====================================================
// LOOP
// =====================================================

void loop() {

  server.handleClient();

  if (millis() - lastTime > interval) {

    lastTime = millis();

    getDataFromServer();

  }

}
