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

#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_Sensor.h>
#include <DHT.h>
#include <DHT_U.h>

#include <WebServer.h>
#include <HTTPClient.h>
#include <FS.h>
#include "SPIFFS.h"
#include <Ticker.h>
#include "esp_sleep.h"
String version = "1.24.3";
DynamicJsonDocument jsonBuffer(100);
JsonObject jsonArr;

WebSocketsClient webSocket;
WebServer webserver;
Ticker myTicker;
WiFiClient wificlient;

#define doorOpen 22       //升
#define doorClose 21      //降
#define doorOpenEXTI 26   //升 中断
#define doorCloseEXTI 25  //降 中断
#define dhtPin 0          //温湿度传感器
#define DHTTYPE DHT11     //传感器类型
DHT_Unified dht(dhtPin, DHTTYPE);

#define billboard1 4           //广告牌开关
#define billboard2 13          //寄快递广告牌
bool guangGaoPaiBool = false;  //guangGaoPaiBool为false代表没有开灯箱 true为已开启灯箱

#define broadcast 2   //播报电源开关
#define doorPower 15  //电源总开关

bool xrty = false;
//开关状态 10为上升上限  0为下降上限 5为可以上升可降
int stroke = 5;
String localServerIP = "192.168.0.254";
String upBinUrl = "http://" + localServerIP + "/tool/ota/GolangCpp/mydoor1.bin";

String localDateH = "http://" + localServerIP + "/time/h";  //获取小时

String localDate = "http://" + localServerIP + "/time/date";  //获取时间
String ssid = "";                                             //wifi账号
String passwd = "";                                           //wifi密码
String html;                                                  //存html
String flash_wifi = "/wifi.txt";                              //被读取的文件位置和名称
int wsTimeOut = 0;
String UserExec;

int execStatus = 9;     //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表
bool scanBool = false;  //ture为开启扫描状态

// 函数声明
void IRAM_ATTR handleInterruptOpen();
void IRAM_ATTR handleInterruptClose();

volatile bool extiOpen = false;   //true 代表上升中断触发了
volatile bool extiClose = false;  //true 代表下降中断触发了

volatile unsigned long extiTime = 0;

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");
}

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

      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测
      if (digitalRead(doorOpenEXTI) == HIGH && stroke != 10) {
        stroke = 10;
        closeAll();
        Serial.println("Disconnected  上限关闭");
      }
      //查询是否下降 限制,及时把状态改好
      if (digitalRead(doorCloseEXTI) == HIGH && stroke != 0) {
        stroke = 0;
        closeAll();
        Serial.println("Disconnected  下限关闭");
      }
      wsTimeOut++;
      //无法连接远程WSS服务器15次就自动 启动网页控制
      if (wsTimeOut > 30) {
        closeAll();
        delay(500);
        webSocket.disconnect();
        delay(500);
        //计数器归0
        wsTimeOut = 0;
        ESP.restart();
      }
      break;
    case WStype_CONNECTED:
      Serial.printf("[WSc] Connected to url: %s\n", payload);
      //计数器归0
      wsTimeOut = 0;
      delay(200);
      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测
      if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {
        stroke = 5;
      } else {
        if (digitalRead(doorOpenEXTI) == HIGH) {
          stroke = 10;
        } else {
          stroke = 0;
        }
      }
      tmpH = esp32get(localDateH);
      if (tmpH.length() > 0) {
        int dateL = tmpH.toInt();
        if (dateL > 6 && dateL < 18) {
          digitalWrite(billboard1, HIGH);  //连接成功 这个时间段打开广告牌
        }
      }
      break;
    case WStype_TEXT:
      Serial.printf("[WSc] get text: %s\n", payload);
      deserializeJson(jsonBuffer, payload);
      jsonArr = jsonBuffer.as<JsonObject>();
      if (jsonArr["message"] != "") {
        String user = jsonArr["user"];
        String date = jsonArr["date"];
        String message = jsonArr["message"];
        if (message == "mydoor1.open") {
          xrty = false;
          //限位开关导通为低电平 表明还没有达到限位,并且没有达到上升最大限制 能正常上升,断开为高电平表明达到限位了
          if (digitalRead(doorOpenEXTI) == LOW && stroke != 10) {
            //主电源接通
            digitalWrite(doorPower, HIGH);
            delay(200);
            //开 上升
            webSocket.sendTXT("正在开门 ...");
            digitalWrite(doorClose, LOW);
            delay(50);
            digitalWrite(doorOpen, HIGH);
            UserExec = user + " 开门 " + date;
            execStatus = 1;

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

        if (message == "mydoor1.stop") {
          Serial.println("停止");
          closeAll();
          //如果不在上限也不在下限时  悬停设置
          if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {
            stroke = 5;
            if (!xrty) {
              xrty = true;
              webSocket.sendTXT("悬停播报设置成功");
            } else {
              webSocket.sendTXT("悬停");
            }
            delay(200);
          } else {
            //不是上升上限就是下降上限
            xrty = false;
            //查询是否下降 限制,及时把状态改好
            if (digitalRead(doorOpenEXTI) == HIGH) {
              stroke = 10;
              String tempstr = guangGaoPaiBool == true ? "灯箱亮" : "灯箱关";
              String rez = "开门达到限制 " + tempstr;
              webSocket.sendTXT(rez);
            } else {
              stroke = 0;
              String tempstr = guangGaoPaiBool == true ? "灯箱亮" : "灯箱关";
              String rez = "关门达到限制 " + tempstr;
              webSocket.sendTXT(rez);
            }
          }
          return;
        }
        if (message == "mydoor1.close") {
          xrty = false;
          //限位开关导通时为低电平表明还没有达到限位,并且没到达到下降最大限制  能正常下降,断开为高电平表明达到限位了
          if (digitalRead(doorCloseEXTI) == LOW && stroke != 0) {
            //主电源接通
            digitalWrite(doorPower, HIGH);
            delay(200);
            //关 下降
            webSocket.sendTXT("正在关门 ...");
            digitalWrite(doorOpen, LOW);
            delay(50);
            digitalWrite(doorClose, HIGH);
            UserExec = user + " 关门 " + date;
            execStatus = 0;
          } else {
            webSocket.sendTXT("无效,关门达到限制");
          }
          return;
        }
        if (message == "mydoor1.TemperatureHumidity") {
          float temperature;
          float humidity;
          sensors_event_t event;
          dht.temperature().getEvent(&event);
          if (isnan(event.temperature)) {
            Serial.println(F("Error reading temperature!"));
          } else {
            temperature = event.temperature;
          }
          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;
          }
          String th = String(temperature) + "," + String(int(humidity));
          webSocket.sendTXT(th);
          return;
        }
        if (message == "mydoor1.records") {  //查看最后操作人
          webSocket.sendTXT(UserExec);
          return;
        }
        if (message == "mydoor1.billboard") {
          if (guangGaoPaiBool == false) {
            digitalWrite(billboard1, HIGH);
            guangGaoPaiBool = true;
            if (user == "系统任务") {
              String strtem = "系统任务 广告牌1已开";
              webSocket.sendTXT(strtem);
            } else {
              String strtem = user + " 广告牌1已开";
              webSocket.sendTXT(strtem);
            }
          } else {
            digitalWrite(billboard1, LOW);
            guangGaoPaiBool = false;
            if (user == "系统任务") {
              String strtem = "系统任务 广告牌1已关";
              webSocket.sendTXT(strtem);
            } else {
              String strtem = user + " 广告牌1已关";
              webSocket.sendTXT(strtem);
            }
          }
          return;
        }
        if (message == "mydoor1.billboardOn1") {
          guangGaoPaiBool = true;
          digitalWrite(billboard1, HIGH);
          String strtem = "系统任务 广告牌1灯已开";
          webSocket.sendTXT(strtem);
          return;
        }
        if (message == "mydoor1.billboardOff1") {
          guangGaoPaiBool = false;
          digitalWrite(billboard1, LOW);
          String strtem = "系统任务 广告牌1灯已关";
          webSocket.sendTXT(strtem);
          return;
        }
        if (message == "mydoor1.dormancy") {  //休眠两个小时
          espSleep();
          return;
        }

        if (message == "mydoor1.ScanOpen") {  //扫码开
          scanBool = true;
          digitalWrite(broadcast, HIGH);
        }
        if (message == "mydoor1.ScanClose") {  //扫码关 按停止键也是会关的
          scanBool = false;
          digitalWrite(broadcast, LOW);
        }

        if (message == "mydoor1.reboot") {
          ESP.restart();
        }
        if (message == "mydoor1.version") {
          webSocket.sendTXT(version);
        }
        if (message == "mydoor1.updateBin") {
          // 执行 OTA 更新前的准备工作
          noInterrupts();                                         // 关闭全局中断
          detachInterrupt(digitalPinToInterrupt(doorOpenEXTI));   // 禁用特定中断源（如果需要）
          detachInterrupt(digitalPinToInterrupt(doorCloseEXTI));  // 禁用特定中断源（如果需要）
          delay(200);
          while (1) {
            t_httpUpdate_return ret =ESPhttpUpdate.update(upBinUrl);
            switch (ret) {
              case HTTP_UPDATE_FAILED:  //更新失败
                webSocket.sendTXT("更新失败");
                Serial.printf("HTTP_UPDATE_FAILD Error (%d): %s", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());
                break;
              case HTTP_UPDATE_NO_UPDATES:  //无需更新
                webSocket.sendTXT("无需更新");
                break;
              case HTTP_UPDATE_OK:  //更新成功
                webSocket.sendTXT("更新成功");
                Serial.println("HTTP_UPDATE_OK");
                break;
            }

            interrupts();  // 恢复全局中断
            espReboot();
          }
        }
        if (message.indexOf("mydoor1.billboardOn2") != -1) {  //13脚高电平 广告牌2开启
          digitalWrite(billboard2, HIGH);
          String strtem = "系统任务 广告牌2已开";
          webSocket.sendTXT(strtem);
        }
        if (message == "mydoor1.billboardOff2") {  //13脚低电平 广告牌2关闭
          digitalWrite(billboard2, LOW);
          String strtem = "系统任务 广告牌2已关";
          webSocket.sendTXT(strtem);
        }
      }
      // send message to server
      // webSocket.sendTXT("message here");
      break;
    case WStype_BIN:
      //语音是以 []byte 传输
      Serial.printf("[WSc] get binary length: %u\n", length);
      //hexdump(payload, length);
      //防止大门开启的时候,同时播报导致大门控制失灵 只有在上,下限的时候才播报
      if (digitalRead(doorOpenEXTI) == HIGH) {
        if (scanBool) {
          Serial.write(payload, length);
        } else {
          bobao(payload, length);
        }
        return;
      } else {
        if (digitalRead(doorCloseEXTI) == HIGH) {
          if (scanBool) {
            Serial.write(payload, length);
          } else {
            bobao(payload, length);
          }
          return;
        }
      }

      if (xrty) {
        if (scanBool) {
          Serial.write(payload, length);
        } else {
          bobao(payload, length);
        }
        return;
      }

      // send data to server
      // webSocket.sendBIN(payload, length);
      break;
    case WStype_ERROR:
    case WStype_FRAGMENT_TEXT_START:
    case WStype_FRAGMENT_BIN_START:
    case WStype_FRAGMENT:
    case WStype_FRAGMENT_FIN:
      break;
  }
}

void setup() {
  pinMode(doorOpen, OUTPUT);    //门上升
  pinMode(doorClose, OUTPUT);   //门下降
  pinMode(billboard1, OUTPUT);  //广播牌
  pinMode(broadcast, OUTPUT);   //语音播报
  pinMode(doorPower, OUTPUT);   //总电源
  pinMode(billboard2, OUTPUT);
  digitalWrite(doorOpen, LOW);
  digitalWrite(doorClose, LOW);
  digitalWrite(billboard1, LOW);
  digitalWrite(broadcast, LOW);
  digitalWrite(doorPower, LOW);
  digitalWrite(billboard2, LOW);

  pinMode(doorOpenEXTI, INPUT_PULLUP);  //上拉电阻模式,默认高电平
  pinMode(doorCloseEXTI, INPUT_PULLUP);

  // 附加中断服务函数
  attachInterrupt(digitalPinToInterrupt(doorOpenEXTI), handleInterruptOpen, FALLING);
  attachInterrupt(digitalPinToInterrupt(doorCloseEXTI), handleInterruptClose, FALLING);
  /*参数说明：
  第一个参数digitalPinToInterrupt(INTERRUPT_PIN_1)：将数字引脚号转换为中断号。
  第二个参数handleInterrupt1：是中断触发后要执行的函数名，此函数不能带有参数，并且返回类型必须为void。
  第三个参数RISING：用来确定中断的触发方式。
  触发模式类型：
  LOW：当引脚为低电平时，会持续触发中断。
  CHANGE：只要引脚电平发生变化，无论是高变低还是低变高，都会触发中断。
  RISING：引脚电平从低电平变为高电平时，触发中断。
  FALLING：引脚电平从高电平变为低电平时，触发中断。
  HIGH（仅适用于 Arduino SAMD 板）：当引脚为高电平时，会持续触发中断。*/


  Serial.begin(115200);

  SPIFFS.begin();
  if (SPIFFS.exists(flash_wifi) == false) {
    SPIFFS.begin();
    File dataFile = SPIFFS.open(flash_wifi, "w");
    dataFile.println("webserver&www.nobiggie.cn");
    dataFile.close();
  }

  //pinMode(flash_rst, INPUT_PULLUP);
  delay(200);
  dht.begin();
  sensor_t sensor;
  dht.temperature().getSensor(&sensor);
  dht.humidity().getSensor(&sensor);
  delay(500);
  if (FlashConnetWifi() == false) {  //自动flash联网不成功 就启用 html配置wifi账号密码
    HtmlSetWifi();
  }
  delay(300);

  //通知服务器 我正在重启,验证一下广告牌开关状态 10秒后我启动完成发送广告牌开关状态给我
  billboardPost();
  webSocket.begin(localServerIP, 80, "/websocket");
  webSocket.onEvent(webSocketEvent);
  webSocket.setAuthorization("user=mydoor1&passwd=efghijk&room=door");
  webSocket.setReconnectInterval(5000);
  if (digitalRead(doorOpenEXTI) == HIGH) {
    stroke = 10;
  }
  if (digitalRead(doorCloseEXTI) == HIGH) {
    stroke = 0;
  }
}
void loop() {
  webSocket.loop();


  if (extiTime > 0) {
    unsigned long currentTime = millis();
    if ((extiTime - currentTime) > 1500) {
      execStatus = 5;  //中断 正常触发模式开启
      extiTime = 0;
      webSocket.sendTXT("中断 正常触发模式开启");
      return;
    }
    if (extiOpen) {  //上升中断触发
      xrty = false;
      extiOpen = false;
      if (execStatus == 1) {
        stroke = 10;
        execStatus = 9;  //执行状态为空
        extiTime = 0;
        digitalWrite(doorPower, LOW);  //断开电源接通
        webSocket.sendTXT("正常模式 上升中断触发");
      }
      return;
    }
    if (extiClose) {  //下降中断触发
      xrty = false;
      extiClose = false;
      if (execStatus == 0) {
        stroke = 0;
        execStatus = 9;  //执行状态为空
        extiTime = 0;
        digitalWrite(doorPower, LOW);   //断开电源接通
        digitalWrite(billboard1, LOW);  //关门就把广告灯箱关了
        webSocket.sendTXT("正常模式 下降中断触发");
      }
      return;
    }
  }
  //即没有上限也没有下限状态

  if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {
    stroke = 5;
    return;
  }
  if (digitalRead(doorOpenEXTI) == HIGH) {  //上升达到限制
    if (stroke != 10) {                     //第一次 加一层保险 停止上升
      digitalWrite(doorOpen, LOW);

      webSocket.sendTXT("loop上升限制触发");
    }
    stroke = 10;
    return;
  }
  if (digitalRead(doorCloseEXTI) == HIGH) {  //下降达到限制
    if (stroke != 0) {                       //第一次 加一层保险 停止下降
      digitalWrite(doorClose, LOW);

      webSocket.sendTXT("loop下降限制触发");
    }
    stroke = 0;
    return;
  }
}


void IRAM_ATTR handleInterruptOpen() {
  static unsigned long lastInterruptTime = 0;
  unsigned long currentTime = millis();
  if (execStatus == 1) {
    if (extiTime == 0) {
      extiTime = currentTime;
    }
  } else {
    // 消抖处理
    if (currentTime - lastInterruptTime > 20) {  //调到50有时候上升不能触发中断
      lastInterruptTime = currentTime;
      if (digitalRead(doorOpenEXTI) == HIGH) {
        digitalWrite(doorOpen, LOW);
        if (execStatus == 1) {           //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表
          digitalWrite(doorPower, LOW);  //断开电源接通
        }
        //开门中断
        extiOpen = true;
        //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启
      }
    }
  }
}


void IRAM_ATTR handleInterruptClose() {
  static unsigned long lastInterruptTime = 0;
  unsigned long currentTime = millis();
  if (execStatus == 0) {
    if (extiTime == 0) {
      extiTime = currentTime;
    }
  } else {
    // 消抖处理
    if (currentTime - lastInterruptTime > 20) {
      lastInterruptTime = currentTime;
      if (digitalRead(doorCloseEXTI) == HIGH) {
        digitalWrite(doorClose, LOW);
        if (execStatus == 0) {           //0关门 状态
          digitalWrite(doorPower, LOW);  //断开电源接通
        }
        //关门中断
        extiClose = true;
        //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启
      }
    }
  }
}
//关闭上升,下降,变压器主电源
void closeAll() {
  digitalWrite(doorOpen, LOW);
  digitalWrite(doorClose, LOW);
  delay(200);
  digitalWrite(doorPower, LOW);  //断开电源接通
  scanBool = false;              //扫描状态关闭
  digitalWrite(broadcast, LOW);  //扫描关
}
void espReboot() {
  ESP.restart();
}
//无网特殊状态下安全限位
void html_whlie() {
  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开
  if (digitalRead(doorOpenEXTI) == HIGH || digitalRead(doorCloseEXTI) == HIGH) {
    //关闭所有开关
    closeAll();
    Serial.println("达到上限");
    myTicker.detach();
    delay(200);
    ESP.restart();
  }
}
void billboardPost() {
  HTTPClient http;
  String response;
  String postStr = "a=a";
  String tmp = "http://" + localServerIP + "/time/bill-board";
  http.begin(wificlient, tmp);
  http.setTimeout(500);
  //需要传输的数据
  char dataBuff[postStr.length()];
  strcpy(dataBuff, postStr.c_str());
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.POST(dataBuff);
  http.end();
}

void espSleep() {
  closeAll();
  delay(500);
  webSocket.sendTXT("mydoor1休眠两个半小时...");
  delay(200);
  webSocket.disconnect();
  delay(300);
  WiFi.disconnect();
  myTicker.detach();
  delay(200);
  //进入深睡眠状态，将在 3 小时后唤醒
  esp_sleep_enable_timer_wakeup(10800000000);
  esp_deep_sleep_start();
}
String esp32post(String url) {
  HTTPClient http;
  http.begin(wificlient, url);
  http.setTimeout(3000);
  String postStr = "version=esp32test10";
  char dataBuff[postStr.length()];
  strcpy(dataBuff, postStr.c_str());
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  int httpResponseCode = http.POST(dataBuff);
  if (httpResponseCode > 0) {
    String response = http.getString();
    return response;
  }
  return "";
}
String esp32get(String urlstr) {
  HTTPClient http;
  String response;
  http.begin(wificlient, urlstr);
  http.setTimeout(3000);
  int httpCode = http.GET();
  // httpCode will be negative on error
  if (httpCode == 200) {
    response = http.getString();
    return response;
  } else {
    return "";
  }
  http.end();
}

void SetWifi() {
  ssid = webserver.arg(0);
  passwd = webserver.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) {
      webserver.send(200, "text/plane", "Wifi密码正确,ESP准备重启");
      webserver.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();
    }
  }
  webserver.send(200, "text/plane", "Wifi密码错误,请重新输入");
}
//------- 展示页面-------
void handlejsonArr() {
  webserver.send(500, "text/html", html);
}
void handleNotFound(void) {
  webserver.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", "");
}
void webserverStart() {
  //-----------------启动webserver网络服务--------------------
  webserver.begin(80);               // 启用网络服务
  webserver.on("/", handlejsonArr);  // / 在HTTP中,表示请求首页服务,
  webserver.on("/SetWifi", SetWifi);
  webserver.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数
  Serial.println("webserver started");
}

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

  ESP.restart();
}
//扫描附件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) {
    webserver.handleClient();  // 检查网络服务请求并处理
  }
}

void bobao(uint8_t* message, int len) {  //定时播报
  if (!scanBool) {                       //如果不是 扫描状态 延迟25秒关闭播报开关
    digitalWrite(broadcast, HIGH);
    delay(2000);
    //String data = "020104C0";
    Serial.write(message, len);

    delay(25000);
    digitalWrite(broadcast, LOW);
  }
}