/*测试项目
  正常网络状态下
  1.上升时达到限位自动断开 如果已经是上升限位了 再点击上升按钮是不会再进行上升操作防止多次按,多次少量上升位移弄坏限位开关
  2.下降时达到限位自动断开 如果已经是下降限位了 再点击上升按钮是不会再进行下降操作防止多次按,多次少量下降位移弄坏限位开关
  3.上升限位时可以按下降,下降限位可以按上升  不存在两个都限位(除非故障)
  4.不管上升或者下降,按 停止键立即停止 如果上升或者下降 有一个达到限位了,按停止键 就会提示已经达到 上升或者下降限位
  5.没有网络的时候 自动休眠 2小时
  6.esp主动发送ping 服务器返回 pong, 虽然服务器有ping 有时运行中,突然无网,返回Disconnected 很慢,或有时直接卡住需要手动重启ESP  webSocket.enableHeartbeat(15000, 1500, 2);也要加上
  7.服务器也主动发送ping esp也返回pong (服务器为了看看哪个ESP掉线了,好在服务器里删除对应用户登陆)
  特殊情况
  8.在按上升 或者 下降时 运行途中突然无网络 所以用到上面第 5项可以解决
  9.设备休眠 时间 > 6  小于 18  Timing存入2(代表 断开WIFI 打开广告灯)  否则存入1 (代表 断开WIFI)  存入0 (代表 不进行设备休眠)
  10.在上升下降是,突然有语言播报,会导致失控,所以只能在开门上限或关门上限时播报
*/

#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPClient.h>
#include <WebSocketsClient.h>
#include <ArduinoJson.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>
#include <Ticker.h>
#include <SoftwareSerial.h>
#include <ESP8266httpUpdate.h>

//WIFI密码 //固件链接，在巴法云控制台复制、粘贴到这里即可
String upUrl = "http://jyan.nobiggie.cn/tool/bin/nobiggiedoor1/file.bin";
String version = "3.0";
Ticker myTicker;
WiFiClient wificlient;

#define DHTPIN 13  // 数字引脚连接DHT传感器
// Feather HUZZAH ESP8266 note: use pins 3, 4, 5, 12, 13 or 14 --
#define DHTTYPE DHT11  // DHT 11
DHT_Unified dht(DHTPIN, DHTTYPE);
WebSocketsClient webSocket;
// 温度输入脚接开发板数字引脚13
#define d7 13
OneWire oneWire(d7);
DallasTemperature sensors(&oneWire);
int open = 5;
int close = 4;

int d0 = 16;
int d5 = 14;
int d6 = 12;
//广告牌定时开关输出电平脚 D4脚 2 不能使用 使为低电平时WIFI芯片灯会一直亮
int d8 = 15;

//开关状态 10为上升上限  0为下降上限 5为可以上升可降
int state = 5;
//此时执行中的状态  0关门  1开门  2停止 9什么也不代表
int exec_status = 9;
int humidity = 0;
float temperature = 0;
String hus;
String tes;
//最近操作物联用户相关信息
String UserExec = "";

//ggb为false代表没有开灯箱 true为已开启灯箱
bool ggb = false;
//几个全局变量
DynamicJsonDocument jsonBuffer(100);
JsonObject jsonStr;
//被读取的文件位置和名称
String flash_wifi = "/wifi.txt";

ESP8266WebServer ESP8266webServer;  // 创建网络服务器对象,HTTP服务的标准端口为：80
String ssid = "";
String passwd = "";

String html;
//当前连接是哪个服务器 true为远程 false为本地
bool networkRemoteLocal = true;
//连接外网失败计数器
int websocketTimeOut = 0;

String Timing = "/timing.txt";
String remoteDateUrl = "http://jyan.nobiggie.cn/query/hour";
String localServerIP = "server.nobiggie.cn";
String localDateUrl = "http://server.nobiggie.cn/query/date";

void setup(void) {
  pinMode(open, OUTPUT);
  pinMode(close, OUTPUT);
  pinMode(d0, OUTPUT);
  pinMode(d8, OUTPUT);

  pinMode(d5, INPUT_PULLUP);
  pinMode(d6, INPUT_PULLUP);

  digitalWrite(open, LOW);
  digitalWrite(close, LOW);
  digitalWrite(d0, LOW);
  digitalWrite(d8, LOW);

  Serial.begin(115200);

  SPIFFS.begin();
  if (SPIFFS.exists(Timing) == false) {
    SPIFFS.begin();
    File dataFile = SPIFFS.open(Timing, "w");
    dataFile.println("0");
    dataFile.close();
  }
  File dataFile = SPIFFS.open(Timing, "r");
  String resultTiming = dataFile.readString();
  resultTiming.trim();
  //Serial.print(resultTiming);
  if (resultTiming == "1") {
    //休眠状态 断开wifi
    delay(200);
    SPIFFS.begin();
    File dataFile = SPIFFS.open(Timing, "w");
    dataFile.println("0");
    dataFile.close();
    delay(200);
    Serial.print("定时两个半小时");
    myTicker.once(8460, espReboot);
  } else if (resultTiming == "2") {
    //休眠状态 断开wifi,打开广告牌
    digitalWrite(d8, HIGH);
    delay(200);
    SPIFFS.begin();
    File dataFile = SPIFFS.open(Timing, "w");
    dataFile.println("0");
    dataFile.close();
    delay(200);
    Serial.print("定时两个半小时");
    myTicker.once(8460, espReboot);
  } else {

    //pinMode(flash_rst, INPUT_PULLUP);
    delay(200);
    dht.begin();
    sensor_t sensor;
    dht.temperature().getSensor(&sensor);
    dht.humidity().getSensor(&sensor);

    delay(1000);
    if (FlashConnetWifi() == false) {
      HtmlSetWifi();
    }
    delay(300);


    String dateTmp1 = esp8266get(localDateUrl);
    //如果本地服务器正常
    if (dateTmp1.length() > 15) {
      dateTmp1 = dateTmp1.substring(11, 13);
      int dateI = dateTmp1.toInt();
      if (dateI > 6 && dateI < 18) {
        digitalWrite(d8, HIGH);
      }
      webSocket.begin(localServerIP, 80, "/service/wss");
    } else {
      //如果本地服务器异常 ->重启后 休眠2小时
      SPIFFS.begin();
      File dataFile = SPIFFS.open(Timing, "w");
      dataFile.println("1");
      dataFile.close();
      delay(200);
      ESP.restart();
    }

    webSocket.onEvent(webSocketEvent);
    webSocket.setAuthorization("user=mydoor1&passwd=efghijk&room=admin");
    // try ever 5000 again if connection has failed
    webSocket.setReconnectInterval(500);
    /*虽然服务器有ping 但是有时运行中,突然无网络,返回连接失败Disconnected 很慢,或有时直接卡住需要手动重启ESP
    所以 webSocket.enableHeartbeat(15000, 1500, 2);也要加上  */
    webSocket.enableHeartbeat(15000, 2000, 2);
    //防止 电源重启,门达到上限了,没有把状态改正,在这进行验证一下
    if (digitalRead(d5) == HIGH) {
      state = 10;
    }
    if (digitalRead(d6) == HIGH) {
      state = 0;
    }
  }
}
void loop(void) {
  webSocket.loop();
  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开
  if (digitalRead(d5) == HIGH && state != 10 && exec_status == 1) {
    //关闭所有开关
    digitalWrite(open, LOW);
    digitalWrite(close, LOW);
    delay(200);
    //断开电源接通
    digitalWrite(d0, LOW);
    Serial.println("开门达到上限");
    state = 10;
    webSocket.sendTXT("loop上升达到限制");
    //执行状态为空
    exec_status = 9;

    String tmpH = esp8266get(remoteDateUrl);
    if (tmpH.length() > 0) {
      int dateL = tmpH.toInt();
      if (dateL > 6 && dateL < 18) {
        digitalWrite(d8, HIGH);
        //这里长度记得除下2    使用前校验一下String的长度是否正确
      }
    }
  }
  //操作运行时 下降达到限位了,并且上次状态不是下降上限 并且执行状态是下降  限位开关开路
  if (digitalRead(d6) == HIGH && state != 0 && exec_status == 0) {
    digitalWrite(open, LOW);
    digitalWrite(close, LOW);
    delay(200);
    //断开电源接通
    digitalWrite(d0, LOW);
    Serial.println("关门达到上限");

    state = 0;
    webSocket.sendTXT("loop关门达到限制");

    //执行状态为空
    exec_status = 9;

    //关门就把广告灯箱关了
    digitalWrite(d8, LOW);
    //告诉服务器让其知道灯箱现在是关闭状态
    webSocket.sendTXT("mydoor1灯箱关");
    delay(500);
  }
  //即没有上限也没有下限状态
  if (digitalRead(d5) == LOW && digitalRead(d6) == LOW && state != 5) {
    state = 5;
  }
}

void webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {
  switch (type) {
    case WStype_DISCONNECTED:
      digitalWrite(open, LOW);
      digitalWrite(close, LOW);
      Serial.println("Disconnected 开关状态全部关闭一下");

      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测
      if (digitalRead(d5) == HIGH && state != 10) {
        state = 10;
        CloseAll();
        Serial.println("Disconnected  上限关闭");
      }
      //查询是否下降 限制,及时把状态改好
      if (digitalRead(d6) == HIGH && state != 0) {
        state = 0;
        CloseAll();
        Serial.println("Disconnected  下限关闭");
      }
      websocketTimeOut++;
      //无法连接远程WSS服务器15次就自动 启动网页控制
      if (websocketTimeOut > 25) {
        CloseAll();
        delay(500);
        webSocket.disconnect();
        delay(500);
        //计数器归0
        websocketTimeOut = 0;
        HtmlSetWifi();
      }
      break;
    case WStype_CONNECTED:
      {
        Serial.printf("[WSc] Connected to url: %s\n", payload);
        //计数器归0
        websocketTimeOut = 0;
        delay(200);
      }
      break;
    case WStype_TEXT:
      Serial.printf("[WSc] get text: %s\n", payload);

      deserializeJson(jsonBuffer, payload);
      jsonStr = jsonBuffer.as<JsonObject>();
      if (jsonStr["message"] != "") {
        String user = jsonStr["user"];
        String date = jsonStr["date"];
        String message = jsonStr["message"];
        if (message == "mydoor1.↓") {
          //限位开关导通时为低电平表明还没有达到限位,并且没到达到下降最大限制  能正常下降,断开为高电平表明达到限位了
          if (digitalRead(d6) == LOW && state != 0) {
            //主电源接通
            digitalWrite(d0, HIGH);
            delay(200);
            //关 下降
            webSocket.sendTXT("正在关门 ...");
            digitalWrite(open, LOW);
            delay(200);
            digitalWrite(close, HIGH);
            UserExec = user + " mydoor1.↓ " + date;
            exec_status = 0;
          } else {
            webSocket.sendTXT("关门达到限制,关门指令失效");
          }
          return;
        }
        if (message == "mydoor1.↑") {
          //限位开关导通为低电平 表明还没有达到限位,并且没有达到上升最大限制 能正常上升,断开为高电平表明达到限位了
          if (digitalRead(d5) == LOW && state != 10) {
            //主电源接通
            digitalWrite(d0, HIGH);
            delay(200);
            //开 上升
            webSocket.sendTXT("正在开门 ...");
            digitalWrite(close, LOW);

            delay(200);
            digitalWrite(open, HIGH);
            UserExec = user + " mydoor1.↑ " + date;
            exec_status = 1;

          } else {
            webSocket.sendTXT("达到限制,上升指令失效");
          }
          return;
        }

        if (message == "mydoor1.■") {
          //如果已经在上限或下限时只更新响应时间
          Serial.println("停止");
          CloseAll();
          if (digitalRead(d5) == LOW && digitalRead(d6) == LOW) {
            webSocket.sendTXT("停止");
            state = 5;
            delay(200);
            return;
          }
          //状态不为空说明 不是上升上限就是下降上限
          if (digitalRead(d5) == HIGH || digitalRead(d6) == HIGH) {
            //查询是否下降 限制,及时把状态改好
            if (digitalRead(d5) == HIGH) {
              state = 10;
              String tempstr = ggb == true ? "灯箱亮" : "灯箱关";
              String rez = "开门达到限制 " + tempstr;
              webSocket.sendTXT(rez);
            } else {
              state = 0;
              String tempstr = ggb == true ? "灯箱亮" : "灯箱关";
              String rez = "关门达到限制 " + tempstr;
              webSocket.sendTXT(rez);
            }
          }
          return;
        }
        if (message == "mydoor1.设备休眠") {
          espSleep();
        }
        if (message.indexOf("mydoor1.温馨提示") != -1) {
          //防止大门开启的时候,同时播报导致大门控制失灵 只有在上,下限的时候才播报
          if (state == 0 || state == 10) {
            digitalWrite(d0, HIGH);
            delay(2000);
            String data = message.substring(message.indexOf("mydoor1.温馨提示"), message.length());
            data = data.substring(20);
            data.replace(" ", "");
            //String data = "020104C0";
            //这里长度记得除下2    使用前校验一下String的长度是否正确
            byte byteArray[data.length() / 2] = { 0 };
            hexCharacterStringToBytes(byteArray, data.c_str());
            //两个参数，第一个接收byte的数组,第二个要String.c_str()
            Serial.write(byteArray, data.length() / 2);
            delay(25000);
            digitalWrite(d0, LOW);
            return;
          }
        }
        if (message.indexOf("mydoor1.铃声") != -1) {
          digitalWrite(d0, HIGH);
          delay(2000);
          String s = message.substring(message.length() - 1);
          String data = "FD 00 08 01 01 72 69 6E 67 5F 3" + s;
          data.replace(" ", "");
          //String data = "020104C0";
          //这里长度记得除下2    使用前校验一下String的长度是否正确
          byte byteArray[data.length() / 2] = { 0 };
          hexCharacterStringToBytes(byteArray, data.c_str());
          //两个参数，第一个接收byte的数组,第二个要String.c_str()
          Serial.write(byteArray, data.length() / 2);
          delay(7000);
          digitalWrite(d0, LOW);
          return;
        }
        if (message.indexOf("mydoor1.信息提示音") != -1) {
          digitalWrite(d0, HIGH);
          delay(2000);
          String s = message.substring(message.length() - 1);
          String data = "FD 00 0B 01 01 6D 65 73 73 61 67 65 5F 3" + s;
          data.replace(" ", "");
          //String data = "020104C0";
          //这里长度记得除下2    使用前校验一下String的长度是否正确
          byte byteArray[data.length() / 2] = { 0 };
          hexCharacterStringToBytes(byteArray, data.c_str());
          //两个参数，第一个接收byte的数组,第二个要String.c_str()
          Serial.write(byteArray, data.length() / 2);
          delay(7000);
          digitalWrite(d0, LOW);
          return;
        }
        if (message.indexOf("mydoor1.警示音") != -1) {
          digitalWrite(d0, HIGH);
          delay(2000);
          String s = message.substring(message.length() - 1);
          String data = "FD 00 09 01 01 61 6C 65 72 74 5F 3" + s;
          data.replace(" ", "");
          //String data = "020104C0";
          //这里长度记得除下2    使用前校验一下String的长度是否正确
          byte byteArray[data.length() / 2] = { 0 };
          hexCharacterStringToBytes(byteArray, data.c_str());
          //两个参数，第一个接收byte的数组,第二个要String.c_str()
          Serial.write(byteArray, data.length() / 2);
          delay(7000);
          digitalWrite(d0, LOW);
          return;
        }
        if (message == "mydoor1.温湿度") {
          sensors_event_t event;
          dht.temperature().getEvent(&event);
          if (isnan(event.temperature)) {
            Serial.println(F("Error reading temperature!"));
          } else {
            temperature = event.temperature;
            if (temperature < -10) {
              tes = "防冻";
            }
            if (temperature >= -10 && temperature < 18) {
              tes = "寒冷";
            }
            if (temperature >= 18 && temperature <= 25) {
              tes = "舒适";
            }
            if (temperature > 25 && temperature <= 36) {
              tes = "炎热";
            }
            if (temperature > 36) {
              tes = "防暑";
            }
          }
          delay(500);
          // Get humidity event and print its value.
          dht.humidity().getEvent(&event);
          if (isnan(event.relative_humidity)) {
            Serial.println(F("Error reading humidity!"));
          } else {
            humidity = event.relative_humidity;
            if (humidity < 40) {
              hus = "干燥";
            }
            if (humidity >= 40 && humidity <= 70) {
              hus = "舒适";
            }
            if (humidity > 70) {
              hus = "潮湿";
            }
          }
          String th = String(temperature) + "°C" + tes + "   " + String(humidity) + "%湿度" + hus;
          webSocket.sendTXT(th);
          return;
        }

        if (message == "mydoor1.灯箱开") {
          digitalWrite(d8, HIGH);
          ggb = true;
          if (user == "系统任务") {
            String strtem = "系统任务 广告牌灯箱已开";
            webSocket.sendTXT(strtem);
          } else {
            String strtem = user + " 广告牌灯箱已开";
            webSocket.sendTXT(strtem);
          }
          return;
        }
        if (message == "mydoor1.灯箱关") {
          digitalWrite(d8, LOW);
          ggb = false;
          if (user == "系统任务") {
            String strtem = "系统任务 广告牌灯箱已关";
            webSocket.sendTXT(strtem);
          } else {
            String strtem = user + " 广告牌灯箱已关";
            webSocket.sendTXT(strtem);
          }
          return;
        }
        if (message == "mydoor1.重启") {
          ESP.restart();
        }
        if (message == "mydoor1.version") {
          webSocket.sendTXT(version);
        }
        if (message == "mydoor1.updateBin") {
          while (1) {
            // Add optional callback notifiers
            ESPhttpUpdate.onStart(update_started);
            ESPhttpUpdate.onEnd(update_finished);
            ESPhttpUpdate.onProgress(update_progress);
            ESPhttpUpdate.onError(update_error);
            t_httpUpdate_return ret = ESPhttpUpdate.update(wificlient, upUrl);
            switch (ret) {
              case HTTP_UPDATE_FAILED: Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s\n", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str()); break;

              case HTTP_UPDATE_NO_UPDATES: Serial.println("HTTP_UPDATE_NO_UPDATES"); break;

              case HTTP_UPDATE_OK: Serial.println("HTTP_UPDATE_OK"); break;
            }
          }
          espReboot();
        }
      }
      break;
    case WStype_BIN:
      Serial.printf("[WSc] get binary length: % u\n", length);
      //hexdump(payload, length);
      // webSocket.sendBIN(payload, length);
      break;
    case WStype_PING:
      // pong will be send automatically
      //Serial.printf("[WSc] get ping\n");
      break;
    case WStype_PONG:
      // answer to a ping we send
      Serial.printf("[WSc] get pong\n");
      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测
      if (digitalRead(d5) == HIGH && state != 10) {
        state = 10;
        CloseAll();
      }
      //查询是否下降 限制,及时把状态改好
      if (digitalRead(d6) == HIGH && state != 0) {
        state = 0;
        CloseAll();
      }
      //如果websocket连接网络失败重启时又能连网就重置0
      if (websocketTimeOut != 0) {
        websocketTimeOut = 0;
      }
      break;
  }
}
void SetWifi() {
  ssid = ESP8266webServer.arg(0);
  passwd = ESP8266webServer.arg(1);
  String sp = ssid + "&" + passwd;
  Serial.println(sp);
  Serial.println("密码在前面");
  Serial.begin(115200);
  delay(500);
  const char* Ssid = ssid.c_str();
  const char* WifiPw = passwd.c_str();
  WiFi.begin(Ssid, WifiPw);
  WiFi.mode(WIFI_STA);
  delay(200);
  Serial.println("wifi_connet");
  int ii = 0;
  for (ii; ii < 20; ii++) {
    delay(500);
    Serial.print(".");
    if (WiFi.status() == WL_CONNECTED) {
      ESP8266webServer.send(200, "text/plane", "Wifi密码正确,ESP准备重启");
      ESP8266webServer.close();
      delay(500);
      WiFi.disconnect();
      delay(500);

      //写入数据
      SPIFFS.format();
      delay(500);
      SPIFFS.begin();
      File dataFile = SPIFFS.open(flash_wifi, "w");
      dataFile.println(sp);
      dataFile.close();

      Serial.println("HTML连接成功");
      delay(500);
      ESP.restart();
    }
  }
  ESP8266webServer.send(200, "text/plane", "Wifi密码错误,请重新输入");
  //HtmlSetWifi();
}
//------- 展示页面-------
void handlejsonStr() {
  ESP8266webServer.send(500, "text/html", html);
}
void handleNotFound(void) {
  ESP8266webServer.send(404, "text/plain", "404-The page is not Found!");
}

void ApStart() {
  WiFi.disconnect();
  delay(500);
  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);

  //启动AP模式,并设置账号和密码
  WiFi.softAP("192.168.168.2", "www.nobiggie.cn");
}
void WebserverStart() {
  //-----------------启动webserver网络服务--------------------
  ESP8266webServer.begin(80);               // 启用网络服务
  ESP8266webServer.on("/", handlejsonStr);  // / 在HTTP中,表示请求首页服务,
  ESP8266webServer.on("/SetWifi", SetWifi);
  ESP8266webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数
  Serial.println("webserver started");
}

void ForPing() {
  //定时时间到了,先取消定时状态
  myTicker.detach();
  CloseAll();
  delay(500);

  ESP.restart();
}


//关闭上升,下降,变压器主电源
void CloseAll() {
  digitalWrite(open, LOW);
  digitalWrite(close, LOW);
  delay(200);
  //断开电源接通
  digitalWrite(d0, LOW);
  //取消定时状态
  myTicker.detach();
  webSocket.sendTXT("变压器电源已断开");
}


//扫描附件WIFI
void WifiScan() {
  Serial.begin(115200);
  WiFi.disconnect();
  WiFi.mode(WIFI_STA);
  delay(100);
  int n = WiFi.scanNetworks();
  if (n > 0) {
    Serial.println(n);
    String a = "附近WiFi <select id=\"ssid\">";
    for (int i = 0; i < n; i++) {
      Serial.println(WiFi.SSID(i));
      a = a + "<option value=\"" + WiFi.SSID(i) + "\">" + WiFi.SSID(i) + "</option>";
      delay(50);
    }
    String HtmlStart = String("") + "<!DOCTYPE html>" + "<html>" + "<head>" + "<meta charset=\"utf-8\">" + "<meta name=\"viewport\" content=\"width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\" />" + "<title>设置WiFi</title>" + "<link rel=\"stylesheet\" href=\"\">" + "</head>" + "<script>" + "onload  = function() {" + "document.getElementById(\"up\").onclick = function(){" + "var ssida=document.getElementById(\"ssid\").value;" + "var ssid=ssida.replace(/\\s+/g, \" \");" + "var passwda=document.getElementsByTagName('input')[0].value;" + "var passwd=passwda.replace(/\\s+/g, \" \");" + "xhr = new XMLHttpRequest();" + "xhr.open(\"POST\",\"./SetWifi\",true);" + "var posts = (\"ssid=\"+ssid+\"&passwd=\"+ passwd);" + "xhr.setRequestHeader(\"Content-Type\",\"application/x-www-form-urlencoded\");" + "xhr.send(posts);" + "xhr.onreadystatechange = function(){" + "if(this.readyState == 4 && this.status == 200){" + "alert(this.responseText);" + "}" + "}" + "}" + "}" + "</script>" + "<body>";
    String HtmlEnd = String("") + "<p>WiFi密码<input type=\"text\" id=\"passwd\"></p>" + "<input type=\"submit\" id=\"up\" value=\"设 置\">" + "<hr/>" + "<font color=\"red\">附近WiFi没有显示到自己的WIFI请重启或离信号源近点</font>" + "</body>" + "</html>";
    a = a + "</select>";
    html = HtmlStart + a + HtmlEnd;
  } else {
    html = "扫描不到网络";
    Serial.println("扫描不到网络");
  }
}
bool FlashConnetWifi() {
  Serial.begin(115200);
  delay(500);
  //是否存在数据
  SPIFFS.begin();
  if (SPIFFS.exists(flash_wifi) == false) {
    Serial.print(flash_wifi);
    Serial.println(" NOT FOUND");
    return false;
  }

  File dateFile = SPIFFS.open(flash_wifi, "r");
  String result = dateFile.readString();
  result.trim();
  Serial.println(result);
  int i = result.indexOf("&");
  String wifiname = result.substring(0, i);
  String wifipw = result.substring(i + 1);
  const char* Ssid = wifiname.c_str();
  const char* WifiPw = wifipw.c_str();
  Serial.println(Ssid);
  Serial.println(WifiPw);
  WiFi.begin(Ssid, WifiPw);
  delay(500);
  WiFi.mode(WIFI_STA);
  Serial.println("FlashConnetWifi->connet");
  int wifiTimeout = 0;
  for (wifiTimeout; wifiTimeout < 30; wifiTimeout++) {
    delay(500);
    Serial.print(" . ");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("FlashConnetWifi 闪存配置连接成功");
      return true;
    }
  }
  Serial.println("FlashConnetWifi-> timeout");
  return false;
}

void HtmlSetWifi() {
  WifiScan();
  ApStart();
  delay(1000);
  Serial.println("启动网页");
  myTicker.detach();
  delay(200);
  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启
  myTicker.once(300, espReboot);
  delay(200);
  WebserverStart();
  while (1) {
    ESP8266webServer.handleClient();  // 检查网络服务请求并处理
  }
}
void espReboot() {
  ESP.restart();
}

//无网特殊状态下安全限位
void html_whlie() {
  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开
  if (digitalRead(d5) == HIGH || digitalRead(d6) == HIGH) {
    //关闭所有开关
    CloseAll();
    Serial.println("达到上限");
    myTicker.detach();
    delay(200);
    ESP.restart();
  }
}


String esp8266get(String string) {
  HTTPClient http;
  String response;
  http.begin(wificlient, string);
  http.setTimeout(3000);
  int httpCode = http.GET();
  if (httpCode == 200) {
    response = http.getString();
    return response;
  } else {
    return "";
  }
  http.end();
}
String esp8266post(String url, String postStr) {
  HTTPClient http;
  String response;
  http.begin(wificlient, url);
  http.setTimeout(3000);
  //需要传输的数据
  char dataBuff[postStr.length()];
  strcpy(dataBuff, postStr.c_str());
  //头信息,需要的反注释就可以
  //http.addHeader("Content-Type", "application/json;charset=utf-8");
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  int httpCode = http.POST(dataBuff);
  if (httpCode == 200) {
    response = http.getString();
    return response;
  } else {
    return "";
  }
  http.end();
}
void hexCharacterStringToBytes(byte* byteArray, const char* hexString) {
  bool oddLength = strlen(hexString) & 1;
  byte currentByte = 0;
  byte byteIndex = 0;
  for (byte charIndex = 0; charIndex < strlen(hexString); charIndex++) {
    bool oddCharIndex = charIndex & 1;
    if (oddLength) {
      // If the length is odd
      if (oddCharIndex) {
        // odd characters go in high nibble
        currentByte = nibble(hexString[charIndex]) << 4;
      } else {
        // Even characters go into low nibble
        currentByte |= nibble(hexString[charIndex]);
        byteArray[byteIndex++] = currentByte;
        currentByte = 0;
      }
    } else {
      // If the length is even
      if (!oddCharIndex) {
        // Odd characters go into the high nibble
        currentByte = nibble(hexString[charIndex]) << 4;
      } else {
        // Odd characters go into low nibble
        currentByte |= nibble(hexString[charIndex]);
        byteArray[byteIndex++] = currentByte;
        currentByte = 0;
      }
    }
  }
}

byte nibble(char c) {
  if (c >= '0' && c <= '9')
    return c - '0';

  if (c >= 'a' && c <= 'f')
    return c - 'a' + 10;

  if (c >= 'A' && c <= 'F')
    return c - 'A' + 10;

  return 0;  // Not a valid hexadecimal character
}
void update_started() {
  Serial.println("CALLBACK:  HTTP update process started");
}

void update_finished() {
  Serial.println("CALLBACK:  HTTP update process finished");
}

void update_progress(int cur, int total) {
  Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes...\n", cur, total);
  webSocket.sendTXT(Serial.printf("CALLBACK:  HTTP update process at %d of %d bytes...\n", cur, total));
}

void update_error(int err) {
  Serial.printf("CALLBACK:  HTTP update fatal error code %d\n", err);
}

void espSleep() {
  CloseAll();
  delay(500);
  String timingStr = "1";
  String tmepH = esp8266get(remoteDateUrl);
  if (tmepH.length() > 0) {
    int dateInt = tmepH.toInt();
    if (dateInt > 6 && dateInt < 18) {
      timingStr = "2";
    }
  }
  webSocket.sendTXT("mydoor1休眠两个半小时...");
  delay(200);
  webSocket.disconnect();
  delay(300);
  WiFi.disconnect();
  SPIFFS.begin();
  File dataFile = SPIFFS.open(Timing, "w");
  dataFile.println(timingStr);
  dataFile.close();
  espReboot();
}