/*
面向车尾   
p32  左边电机  左正
p33  右边电机  右正
p25  左边电机  左反
p26  右边电机  右反
p27  升   正
p14  降   反
p12  关发动机  触动3秒后断开
P15  开机通电一直手摸线 可以遥控方向相互转换
p4   设置 射频遥控 与 WIFI遥控 切换
p21  PWM脚
p0   Power8266 输出高电平增加射频遥控 8键射频433遥控
手机要用火狐浏览器


P36 前进
P39 后退
P34 左拐弯
P35 右拐弯
P17 移动速度+
P16 移动速度-
*/
#include <WiFi.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <FS.h>
#include "SPIFFS.h"
#include "ESPmDNS.h"

#include <Arduino.h>
#include <WiFi.h>
#include <WiFiMulti.h>
#include <WiFiClientSecure.h>
#include <ArduinoJson.h>
#include <WebSocketsClient.h>

String direction = "/boolStr.txt";
String controlMode="/controlMode.txt";
WebServer webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80
String request = "";

//方向相互转换
int touch15 = 15;
//射频遥控  感应切换开关
int touch4=4;

boolean globalBL=false;

//转换成功指示灯
int light22 = 22;

//PWM控制
int pwm1 = 21;
//管道esp32有16个管道,0-7为80hz 高速管道  8-16为低速管道
const int channel1 = 0;
//分辨率  2的8次方 256
const int resolution = 8;
//PWM频率
const int freq = 1000;
//初始值 41%
int PwmNow = 105;
//累加数
int fadeAmount = 5;
//接通esp8266电源
int Power8266 = 0;

WiFiMulti WiFiMulti;
WebSocketsClient webSocket;
DynamicJsonDocument jsonBuffer(100);
JsonObject root;

void hexdump(const void* mem, uint32_t len, uint8_t cols = 16) {
  const uint8_t* src = (const uint8_t*)mem;
  Serial.printf("\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)", (ptrdiff_t)src, len, len);
  for (uint32_t i = 0; i < len; i++) {
    if (i % cols == 0) {
      Serial.printf("\n[0x%08X] 0x%08X: ", (ptrdiff_t)src, i);
    }
    Serial.printf("%02X ", *src);
    src++;
  }
  Serial.printf("\n");
}

String html = String("")+
"<!DOCTYPE html><html lang=\"en\">"+
"<head>"+
    "<meta charset=\"UTF-8\">"+
    "<meta name=\"viewport\" <meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\" />"+
    "<title>遥控车</title>"+
    "<script>window.onload = function () {"+
            "document.getElementById(\"xyzUp\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest();"+
                "xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=↑\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzLeft\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\"); xhr.send(\"xyz=←\");"+
                "xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzLeft\").addEventListener(\"touchend\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\"); xhr.send(\"xyz=■\");"+
                "xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzStop\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=■\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzRight\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=→\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzRight\").addEventListener(\"touchend\", function () {"+
                "let xhr = new XMLHttpRequest();"+
                "xhr.open(\"POST\", \"./xyzgo\", true); xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=■\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"xyzDonw\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=↓\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"rise\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest();"+
                "xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=rise\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"rise\").addEventListener(\"touchend\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=■\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"decline\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=decline\"); xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"decline\").addEventListener(\"touchend\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\"); xhr.send(\"xyz=■\");"+
                "xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"TurnOff\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=TurnOff\");"+
                "xhr.onreadystatechange = function () {"+
                   "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"speedA\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=speedA\");"+
                "xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
            "document.getElementById(\"speedB\").addEventListener(\"touchstart\", function () {"+
                "let xhr = new XMLHttpRequest(); xhr.open(\"POST\", \"./xyzgo\", true);"+
                "xhr.setRequestHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");"+
                "xhr.send(\"xyz=speedB\");"+
                "xhr.onreadystatechange = function () {"+
                    "if (this.readyState == 4 && this.status == 200) {"+
                        "document.getElementById(\"show\").innerHTML = this.responseText;"+
                    "}}});"+
        "}</script>"+
"</head>"+
"<style> *{ -moz-user-select: none; -ms-user-select:none; user-select:none; } </style>"+
"<body>"+
    "<div style=\"width: auto;margin-top:30px;text-align: center;\" id=\"show\"></div>"+
    "<div style=\"padding: 20px;width:auto;margin: 80px auto auto; background-color: #EEF0F2;\">"+
        "<p><input type=\"submit\" style=\"margin:auto auto 20% 42%;\" id=\"xyzUp\" value=\"&nbsp ↑ &nbsp\"></p>"+
        "<p><input type=\"submit\" style=\"margin-left: 10%;\" id=\"xyzLeft\" value=\"&nbsp ← &nbsp\">"+
        "<input type=\"submit\" style=\"margin:auto 17% auto 17%\" id=\"xyzStop\" value=\"&nbsp ■ &nbsp\">"+
        "<input type=\"submit\" style=\"margin-right: 10%;\" id=\"xyzRight\" value=\"&nbsp → &nbsp\"></p>"+
        "<p></p><input type=\"submit\" style=\"margin:20% auto auto 42%;\" id=\"xyzDonw\" value=\"&nbsp ↓ &nbsp\"></p>"+
    "</div>"+
    "<div style=\"width:auto;margin: 50px auto;text-align: center; background-color: #EEF0F2;\">"+
        "<p><input type=\"submit\" style=\"float:left;\" id=\"rise\" value=\"&nbsp 机器升 &nbsp\"></p>"+
        "<p><input type=\"submit\" style=\"margin-left:50%;\" id=\"decline\" value=\"&nbsp 机器降 &nbsp\"><br />"+
        "<p><input type=\"submit\" value=\"速度+\" id=\"speedA\"></p>"+
        "<p><input type=\"submit\" value=\"速度-\" id=\"speedB\"></p><br />"+
        "<p><input type=\"submit\" id=\"TurnOff\" value=\"&nbsp 关闭发动机 &nbsp\"></p>"+
    "</div></body></html>";

int LeftAdvance = 32;
int RightAdvance = 33;
int LeftRetreat = 25;
int RightRetreat = 26;
int rise = 27;
int decline = 14;
//发动机
int TurnOff = 12;

//touch 最大65  手摸20以下 但是运行环境 触摸达不到20以下,20-50间,应该设置为50
const int ConstTouch = 50;
String runState = "■";
String WebSocKetUserName="cart1";

void setup(void) {
  pinMode(LeftAdvance, OUTPUT);
  pinMode(LeftRetreat, OUTPUT);
  pinMode(RightAdvance, OUTPUT);
  pinMode(RightRetreat, OUTPUT);
  pinMode(rise, OUTPUT);
  pinMode(decline, OUTPUT);
  pinMode(TurnOff, OUTPUT);
  pinMode(light22, OUTPUT);
  //pinMode(d5, INPUT_PULLUP);
  //pinMode(d6, INPUT_PULLUP);

  digitalWrite(LeftAdvance, LOW);
  digitalWrite(LeftRetreat, LOW);
  digitalWrite(RightAdvance, LOW);
  digitalWrite(RightRetreat, LOW);

  digitalWrite(rise, LOW);
  digitalWrite(decline, LOW);
  digitalWrite(TurnOff, LOW);
  digitalWrite(light22, LOW);

  pinMode(pwm1, OUTPUT);
  ledcSetup(channel1, freq, resolution);
  ledcAttachPin(pwm1, channel1);
  ledcWrite(channel1, PwmNow);
  pinMode(Power8266, OUTPUT);
  digitalWrite(Power8266, LOW);

  Serial.begin(115200);
  delay(500);

  if (!SPIFFS.begin(true)) {
    Serial.println("SPIFFS加载失败");
  }
  if (SPIFFS.exists(direction) == false) {
    //写入数据
    SetSPIFFS(direction, "true");
    delay(300);
  }

  File dateFile = SPIFFS.open(direction, "r");
  String boolStr;
  for (int i = 0; i < dateFile.size(); i++) {
    //Serial.print((char)dateFile.read());
    boolStr += (char)dateFile.read();
  }
  //去除前后空格
  boolStr.trim();
  Serial.println(boolStr);
  delay(500);
  int intto = touchRead(touch15);
  delay(1000);
  if (intto < ConstTouch) {
    if (boolStr == "true") {
        Serial.println("转换遥控方向成 重");
        SetSPIFFS(direction, "false");
        delay(200);
        globalBL=false;
        ledFlicker();
    } else {
        Serial.println("转换遥控方向成 轻");
        SetSPIFFS(direction, "true");
        delay(200);
        globalBL=true;
        ledFlicker();
    }
  }
  delay(500);
  int intto4 = touchRead(touch4);
  delay(500);
  //touch4 是否触摸操作 true为触摸
  bool touch4BL=false;
  if(intto4 < ConstTouch){
    touch4BL=true;
    if (!SPIFFS.begin(true)) {
      Serial.println("SPIFFS加载失败");
    }
    if (SPIFFS.exists(controlMode) == false) {
      //写入数据  true代表 增加射频遥控 模式
      SetSPIFFS(controlMode, "false");
      delay(300);
    }else{
      String boolStr1;
      File dateFile = SPIFFS.open(controlMode, "r");
      for (int i = 0; i < dateFile.size(); i++) {
      //Serial.print((char)dateFile.read());
      boolStr1 += (char)dateFile.read();
      }
      //去除前后空格
      boolStr1.trim();
      Serial.println(boolStr1);
      //上次是true射频模式 现在就关闭射频模式
      if(boolStr1=="true"){
        Serial.println("关闭射频模式");
        SetSPIFFS(controlMode, "false");
      }else{
        //开户射频模式
        SetSPIFFS(controlMode, "true");
        Serial.println("开启射频模式");
        digitalWrite(Power8266,HIGH);
      }
    }
    ledFlicker();
  }

  if(touch4BL==false){
    if (!SPIFFS.begin(true)) {
      Serial.println("SPIFFS加载失败");
    }else{
      String boolStr2;
      File dateFile = SPIFFS.open(controlMode, "r");
      for (int i = 0; i < dateFile.size(); i++) {
      //Serial.print((char)dateFile.read());
      boolStr2 += (char)dateFile.read();
      }
      //去除前后空格
      boolStr2.trim();
      Serial.println(boolStr2);
      
      if(boolStr2=="true"){
        digitalWrite(Power8266,HIGH);
      }
    }
  }

  delay(200);
  ApStart();
  delay(200);
  Serial.println("启动网页遥控AP");
  delay(200);
  WebServerStart();
}

void loop(void) {
  webServer.handleClient();

}


//------- 展示页面-------
void handleRoot() {
  webServer.send(500, "text/html", html);
}
void handleNotFound(void) {
  webServer.send(404, "text/plain", "404-The page is not Found!");
}
void xyzgo() {
  request = webServer.arg("xyz");
  //Serial.println(request);
  if (request == "↑") {
    if(globalBL==false){
      LeftFore();
      RightFore();
    }else{
      LeftAft();
      RightAft();
    }
    runState = "↑";
    webServer.send(200, "text/plane", request);
    Serial.println("↑");
    return;
  }
  if (request == "↓") {
    if(globalBL==false){
      LeftAft();
      RightAft();
    }else{
      LeftFore();
      RightFore();
    }
    runState = "↓";
    webServer.send(200, "text/plane", request);
    Serial.println("↓");
    return;
  }
  if (request == "■") {
    StopAllExec();
    runState = "■";
    webServer.send(200, "text/plane", request);
    Serial.println("■");
    return;
  }
  if (request == "←") {
      RightFore();
      LeftAft();
    runState = "←";
    webServer.send(200, "text/plane", request);
    Serial.println("←");
    return;
  }
  if (request == "→") {
      LeftFore();
      RightAft();
    
    runState = "→";
    webServer.send(200, "text/plane", request);
    Serial.println("→");
    return;
  }
  if (request == "rise") {
    digitalWrite(decline, LOW);
    digitalWrite(rise, HIGH);
    webServer.send(200, "text/plane", request);
    return;
  }
  if (request == "decline") {
    digitalWrite(rise, LOW);
    digitalWrite(decline, HIGH);
    webServer.send(200, "text/plane", request);
    return;
  }
  if (request == "TurnOff") {
    digitalWrite(TurnOff, HIGH);
    webServer.send(200, "text/plane", request);
    delay(3000);
    digitalWrite(TurnOff, LOW);
    return;
  }
  if (request == "speedA") {
    if (PwmNow >= 178) {
      webServer.send(200, "text/plane", "已达到70%限速,无法继续加速");
    } else {
      PwmNow = PwmNow + fadeAmount;
      ledcWrite(channel1, PwmNow);
      int temp=PwmNow*100/255;
      String tempStr=String(temp)+"%";
      webServer.send(200, "text/plane", tempStr);
      Serial.println("speedA");
    }
    return;
  }
  if (request == "speedB") {
    if (PwmNow <= 77) {
      webServer.send(200, "text/plane", "已达到30%限速,无法继续减速");
    } else {
      PwmNow = PwmNow - fadeAmount;
      ledcWrite(channel1, PwmNow);
      int temp=PwmNow*100/255;
      String tempStr=String(temp)+"%";
      webServer.send(200, "text/plane", tempStr);
      Serial.println("speedB");
    }

    return;
  }
  //HtmlSetWifi();
}

void ApStart() {
  Serial.begin(115200);
  WiFi.mode(WIFI_AP);
  IPAddress local_IP(192, 168, 168, 2);
  IPAddress gateway(192, 168, 168, 1);
  IPAddress subnet(255, 255, 255, 0);
  WiFi.softAPConfig(local_IP, gateway, subnet);
  String mymac=WiFi.macAddress();
  Serial.println(mymac);
  mymac.replace(":","");
  mymac.toLowerCase();
  String temp1=mymac.substring(0,2);
  String temp2=mymac.substring(10,12);
  String ApPasswd=temp1+temp2;
  Serial.println(ApPasswd);
  //启动AP模式，并设置账号和密码
  boolean result = WiFi.softAP("esp32WebServer192.168.168.2", "www.360xyz.cn");
  if (result) {
    //输出 soft-ap ip地址
    Serial.println(String("Soft-AP IP address = ") + WiFi.softAPIP());
  }
}
//------- 展示页面-------

void WebServerStart() {
  //-----------------启动webserver网络服务--------------------
  webServer.begin();              // 启用网络服务
  webServer.on("/", handleRoot);  // / 在HTTP中，表示请求首页服务，
  webServer.on("/xyzgo", xyzgo);
  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数
  Serial.println("webserver started");
}

void SetSPIFFS(String direction, String str) {
  //写入数据
  SPIFFS.remove(direction);
  delay(500);
  SPIFFS.begin();
  File dataFile = SPIFFS.open(direction, "w");
  dataFile.println(str);
  dataFile.close();
}
//左轮前进
void LeftFore() {
  digitalWrite(LeftRetreat, LOW);
  digitalWrite(LeftAdvance, HIGH);
}
//左轮后退
void LeftAft() {
  digitalWrite(LeftAdvance, LOW);
  digitalWrite(LeftRetreat, HIGH);
}
//右轮前进
void RightFore() {
  digitalWrite(RightRetreat, LOW);
  digitalWrite(RightAdvance, HIGH);
}
//右轮后退
void RightAft() {
  digitalWrite(RightAdvance, LOW);
  digitalWrite(RightRetreat, HIGH);
}
void StopAllExec() {
  digitalWrite(LeftAdvance, LOW);
  digitalWrite(RightAdvance, LOW);
  digitalWrite(LeftRetreat, LOW);
  digitalWrite(RightRetreat, LOW);
  digitalWrite(rise, LOW);
  digitalWrite(decline, LOW);
  delay(500);
}
//LED 闪烁
void ledFlicker(){
  digitalWrite(light22, HIGH);
  delay(500);
  digitalWrite(light22, LOW);
  delay(500);
  digitalWrite(light22, HIGH);
  delay(500);
  digitalWrite(light22, LOW);
}