#include <WiFi.h>
#include <PubSubClient.h>
#include <WebServer.h>
#include <HTTPClient.h>
#include <FS.h>
#include "SPIFFS.h"
#include "ESPmDNS.h"
#include <Ticker.h>
#include <DHT.h>
const char* mqttServer = "bemfa.com";
const int mqttPort = 9501;
const char* mqttUser = "door1";
const char* mqttPassword = "abcdefg";
const char* mqttClientId = "07b120062a7972cf9831d7d9bc8012bc";
const char* mqttTopic = "door1";

int botelu = 19200;
Ticker myTicker;
//被读取的文件位置和名称
String flashWifi = "/wifi.txt";
WebServer webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80
String ssid = "";
String passwd = "";
String html;

WiFiClient espClient;
PubSubClient client(espClient);

bool dx = false;  //灯箱状态 默认关

#define doorTop 16     //升限位
#define doorBottom 17  //降限位
//由于中断处理函数是异步执行的，所以我们需要使用 volatile 关键字来声明中断标志，以确保在多线程环境下的可靠性
volatile bool flagTop = false;     // 第一个中断标志
volatile bool flagBottom = false;  // 第二个中断标志

#define echoPin35 35  //超声距离感应echo脚
#define trigPin32 32  //超声距离感应trig脚
#define billboard 33  //广告牌
#define doorOpen 25   //升
#define doorClose 26  //降
#define dhtPin 27     //温湿度传感器
#define DHTTYPE DHT11  // DHT 11
DHT_Unified dht(dhtPin, DHTTYPE);
#define doorPower 14  //电源总开关
#define broadcast 12  //播报电源开关

//开关状态 10为上升上限  0为下降上限 5为可以上升可降
int state = 5;

int minUs = 500;    //0度是脉宽,单位us
int maxUs = 2500;   //180度脉宽,单位us
bool sr04 = false;  //默认不开启
void setup() {
  pinMode(trigPin32, OUTPUT);
  pinMode(echoPin35, INPUT);

  pinMode(doorTop, INPUT_PULLUP);                                         // 配置第一个中断引脚为输入模式，启用内部上拉电阻
  pinMode(doorBottom, INPUT_PULLUP);                                      // 配置第二个中断引脚为输入模式，启用内部上拉电阻
  attachInterrupt(digitalPinToInterrupt(doorTop), isrTop, CHANGE);        // 注册第一个中断处理函数，当引脚状态变化时触发
  attachInterrupt(digitalPinToInterrupt(doorBottom), isrTottom, CHANGE);  // 注册第二个中断处理函数，当引脚状态变化时触发

  pinMode(billboard, OUTPUT);
  pinMode(doorOpen, OUTPUT);
  pinMode(doorClose, OUTPUT);
  pinMode(doorPower, OUTPUT);
  pinMode(broadcast, OUTPUT);

  digitalWrite(billboard, LOW);
  digitalWrite(doorOpen, LOW);
  digitalWrite(doorClose, LOW);
  digitalWrite(doorPower, LOW);
  digitalWrite(broadcast, LOW);

  Serial.begin(botelu);
  if (FlashConnetWifi() == false) {
    HtmlSetWifi();
  }
  Serial.println("连接WIFIi成功");
  client.setServer(mqttServer, mqttPort);
  client.setCallback(callback);
  while (!client.connected()) {
    Serial.println("正在连接MQTT");
    if (client.connect(mqttClientId, mqttUser, mqttPassword)) {
      Serial.println("正在连接MQTT成功");
      client.subscribe(mqttTopic);
    } else {
      Serial.println("正在连接MQTT失败");
      delay(5000);
    }
  }
}
void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();
  if (flagTop) {
    Serial.println("Interrupt 1 occurred!");

    // flagTop = false;
  }

  if (flagBottom) {
    Serial.println("Interrupt 2 occurred!");

    //flagTottom = false;
  }

  if (sr04) {
    digitalWrite(trigPin32, HIGH);
    delayMicroseconds(15);  //us 间隔15微秒的脉冲信号供应给超声波HC-SR04
    digitalWrite(trigPin32, LOW);

    auto t = pulseIn(echoPin35, HIGH);
    double dis = t * 0.01715;  //cm 通过接收超声波返回的时间差计算距离 单位厘米
    Serial.println(dis);
    if (dis < 150) {
      sr04 = false;
    }
  }
}

// 第一个中断处理函数
void isrTop() {
  closeAll();
  state = 10;
  flagTop = true;
  Serial.println("开门达到限制");
}

// 第二个中断处理函数
void isrTottom() {
  closeAll();
  state = 0;
  flagBottom = true;
  Serial.println("关门达到限值");
}
void closeAll() {
  digitalWrite(doorOpen, LOW);
  digitalWrite(doorClose, LOW);
  delay(200);
  //断开电源接通
  digitalWrite(doorPower, LOW);
  //取消定时状态
  myTicker.detach();
  Serial.println("变压器电源已断开");
}

void callback(char* topic, byte* payload, unsigned int length) {
  //Serial.println(topic);
  String msg = "";
  for (int i = 0; i < length; i++) {
    msg += (char)payload[i];
  }
  Serial.print("Msg:");
  Serial.println(msg);
  if (msg == "■") {
    closeAll();
    sr04 = false;
    String tmp = "";
    if (dx == true) {
      tmp = "灯箱状态 开";
    } else {
      tmp = "灯箱状态 关";
    }
    tmp = "停止 " + tmp;
    client.publish(mqttTopic, tmp.c_str());
    return;
  }
  if (msg == "↑") {
    sr04 = false;
    if (state != 10) {
      client.publish(mqttTopic, "开门中...");
      digitalWrite(doorOpen, HIGH);
    } else {
      client.publish(mqttTopic, "上限,指令无效");
    }

    return;
  }
  if (msg == "↓") {
    sr04 = false;
    if (state != 0) {
      client.publish(mqttTopic, "关门中...");
      digitalWrite(doorClose, HIGH);
    } else {
      client.publish(mqttTopic, "下限,指令无效");
    }
    return;
  }
  if (msg == "温湿度") {
    // 获取温度事件并打印其值.
    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 wfud = String(temperature) + "°C" + " " + String(humidity);
   // Serial1.write(wfud.c_str(), wfud.length());
    return;
    client.publish(mqttTopic, wfud);
  }
  if (msg == "灯箱开") {
    digitalWrite(billboard, HIGH);
    dx = true;
    client.publish(mqttTopic, "灯箱已开");
  }
  if (msg == "灯箱关") {
    digitalWrite(billboard, LOW);
    dx = false;
    client.publish(mqttTopic, "灯箱已关");
  }
  if (msg.indexOf("温馨提示") != -1) {
    if (state == 0 || state == 10) {
      digitalWrite(broadcast, HIGH);
      delay(2000);
      String data = msg.substring(msg.indexOf("温馨提示"), msg.length());
      data = data.substring(12);
      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(broadcast, LOW);
      return;
    }
  }
  msg = "";
}

void reconnect() {
  // Loop until we're reconnected
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    // Attempt to connect
    if (client.connect(mqttClientId)) {
      Serial.println("connected");
      Serial.print("subscribe:");
      Serial.println(mqttTopic);
      //订阅主题，如果需要订阅多个主题，可发送多条订阅指令client.subscribe(topic2);client.subscribe(topic3);
      client.subscribe(mqttTopic);
    } else {
      Serial.print("failed, rc=");
      Serial.print(client.state());
      Serial.println(" try again in 5 seconds");
      // Wait 5 seconds before retrying
      delay(5000);
    }
  }
}


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

//扫描附件WIFI
void WifiScan() {
  Serial.begin(botelu);
  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/>" + "<div>如果附近WiFi没有显示到自己的WIFI<div>" + "<div>请重启或离信号近点</div>" + "<div>连接成功AP热点退出转WIFI_STA</div>" + "</body>" + "</html>";
    a = a + "</select>";
    html = HtmlStart + a + HtmlEnd;
  } else {
    html = "扫描不到网络";
    Serial.println("扫描不到网络");
  }
}
void setWifi() {
  ssid = webServer.arg(0);
  passwd = webServer.arg(1);
  const char* Ssid = ssid.c_str();
  const char* Passwd = passwd.c_str();
  String sp = ssid + "&" + passwd;

  Serial.println(sp);
  Serial.begin(botelu);
  delay(500);
  WiFi.begin(Ssid, Passwd);
  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(flashWifi, "w");
      dataFile.println(sp);
      dataFile.close();
      Serial.println("HTML连接成功");
      delay(1000);
      ESP.restart();
    }
  }
  webServer.send(200, "text/plane", "Wifi密码错误,请重新输入");
  //HtmlSetWifi();
}
//------- 展示页面-------
void handleRoot() {
  webServer.send(500, "text/html", html);
}
void handleNotFound(void) {
  webServer.send(404, "text/plain", "404-The page is not Found!");
}


void apStart() {
  Serial.begin(botelu);
  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模式，并设置账号和密码
  boolean result = WiFi.softAP("192.168.168.2", "192.168.168.2");
  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("/setWifi", setWifi);
  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数
  Serial.println("webserver started");
}

bool FlashConnetWifi() {
  Serial.begin(botelu);
  delay(500);
  //是否存在数据
  SPIFFS.begin();
  if (SPIFFS.exists(flashWifi) == false) {
    Serial.print(flashWifi);
    Serial.println(" NOT FOUND");
    return false;
  }

  File dateFile = SPIFFS.open(flashWifi, "r");
  String result;
  for (int i = 0; i < dateFile.size(); i++) {
    //Serial.print((char)dateFile.read());
    result += (char)dateFile.read();
  }
  //去除前后空格
  result.trim();
  Serial.println(result);
  int i = result.indexOf("&");

  String wifiname = result.substring(0, i);
  String wifipw = result.substring(i + 1);
  const char* WifiName = wifiname.c_str();
  const char* WifiPw = wifipw.c_str();
  Serial.println(WifiName);
  Serial.println(WifiPw);
  WiFi.begin(WifiName, WifiPw);
  delay(500);
  WiFi.mode(WIFI_STA);
  Serial.println("FlashConnetWifi->connet");
  int ii = 0;
  for (ii; ii < 20; ii++) {
    delay(500);
    Serial.print(" . ");
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("FlashConnetWifi 闪存配置连接成功");
      return true;
    }
  }
  Serial.println("FlashConnetWifi->false");
  return false;
}
void EspReboot() {
  ESP.restart();
}

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
}