/*************************
  ESP32 Wifi控制伺服馬
  使用滑桿控制0~180°
************************/

#include <WiFi.h>
#include <ESP32Servo.h>

Servo myservo;  //伺服馬達名稱
const int servoPin = 22;

// Replace with your network credentials
const char* ssid     = "LankuoAP";  //你的WIFI id
const char* password = "abcd1234";  //密碼
WiFiServer server(80);     //Wifi伺服器使用80埠

String header;     //記錄HTTP請求字串
String angleString = String(5);  //解析出來的角度值

//連線時間限制
unsigned long currentTime = millis();  //現在時間
unsigned long previousTime = 0;       //上次時間
const long timeoutTime = 2000;  //連線時間上限2秒

void setup() {
  Serial.begin(115200);
  myservo.attach(servoPin);       //啟動伺服馬達
  Serial.print("Connecting to "); //監看連線
  Serial.println(ssid);
  WiFi.begin(ssid, password);     //連接wifi
  while (WiFi.status() != WL_CONNECTED) {   //若尚未能連接時
    delay(500);
    Serial.print(".");                      //印出...
  }
  Serial.println("");
  Serial.println("WiFi已連線.");            //顯示已連線
  Serial.println("IP address: ");           //顯示連線的ip
  Serial.println(WiFi.localIP());           //網頁伺服器提供ip讓使用者連線
  server.begin();
}

void loop(){
  WiFiClient client = server.available();   //監聽…

  if (client) {                             //有人連入
    currentTime = millis();                 //記錄現在時間
    previousTime = currentTime;             //記錄上次時間
    Serial.println("New Client.");          //顯示「有人連入」
    String currentLine = "";                //空字串
    while (client.connected() && currentTime - previousTime <= timeoutTime) { //當使用者時限內有連線
      currentTime = millis();               //記錄連入時間
      if (client.available()) {             //有傳入文字
        char c = client.read();             //讀取文字
        Serial.write(c);                    //印出來
        header += c;                        //串接連入的文字成字串
        if (c == '\n') {                    //如果是換行指令 
          // 使用者最後按下enter會產生的\r\n (歸位+換行字元)
          if (currentLine.length() == 0) {          //使用者最後按下enter產生的新行        
            client.println("HTTP/1.1 200 OK");      //網頁的開始格式
            client.println("Content-type:text/html"); //網頁格式
            client.println("Connection: close");      //網頁格式
            client.println();                       //空白行

            //顯示網頁的部分
            client.println("<!DOCTYPE html><html>");  
            client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS 格式
            client.println("<style>body { text-align: center; font-family: \"Trebuchet MS\", Arial; margin-left:auto; margin-right:auto;}");
            client.println(".slider { width: 300px; }</style>");
            client.println("<script src=\"https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js\"></script>");
                     
            //網頁內容
            client.println("</head><body><h1>ESP32 Slide Control Servo </h1>");   //標題
            client.println("<p>Position: <span id=\"servoPos\"></span></p>"); //滑桿元件屬性         
            client.println("<input type=\"range\" min=\"0\" max=\"180\" class=\"slider\" id=\"servoSlider\" onchange=\"servo(this.value)\" angle=\""+angleString+"\"/>");
            
            client.println("<script>var slider = document.getElementById(\"servoSlider\");");    //使用JAVAScript程式碼處理滑桿位置之網頁更新
            client.println("var servoP = document.getElementById(\"servoPos\"); servoP.innerHTML = slider.value;");
            client.println("slider.oninput = function() { slider.value = this.value; servoP.innerHTML = this.value; }");
            client.println("$.ajaxSetup({timeout:1000}); function servo(pos) { ");
            client.println("$.get(\"/?angle=\" + pos + \"&\"); {Connection: close};}</script>");   //界定格式為: url/?angle=[SLIDER_POSITION]&。
           
            client.println("</body></html>");   
            /*
            http://192.168.1.216/?angle=0&   //轉到0
            http://192.168.1.216/?angle=180& //轉到180
            */  
            
            //GET /?angle=180&  GET方式的指定參考字串
            if(header.indexOf("GET /?angle=")>=0) {   //如果有指定的字串格式
              int pos1 = header.indexOf('=');             //找到=的位置
              int pos2 = header.indexOf('&');             //找到&的位置
              angleString = header.substring(pos1+1, pos2);  //角度值為=到&之間的數值文字。
              myservo.write(angleString.toInt());  //驅動servo到該角度
              Serial.print("伺服馬達現在角度位置=>");
              Serial.println(angleString);          //印出來
            }   
            client.println();   //以空白行結束網頁的請求
            break;    //離開迴圈
          } else {    
            currentLine = "";   //清除字串
          }
        } else if (c != '\r') {  //如果不是return鍵
          currentLine += c;      //繼續接收文字
        }
      }
    }
    header = ""; //網頁控制字串清空
    client.stop();
    Serial.println("");
  }
}