(187,4,'ESP8266 AP配网','#include <ESP8266WiFi.h>\r\n#include <ESP8266WebServer.h>\r\n#include <ESP8266HTTPClient.h>\r\n#include <Ticker.h>\r\n\r\n\r\nTicker myTicker;\r\nWiFiClient wificlient;\r\n\r\n//被读取的文件位置和名称\r\nString flash_wifi = \"/wifi.txt\";\r\nESP8266WebServer ESP8266webServer;  // 创建网络服务器对象,HTTP服务的标准端口为：80\r\nString ssid = \"\";\r\nString passwd = \"\";\r\nString html;\r\n\r\nvoid setup(void) {\r\n  Serial.begin(115200);\r\n  delay(1000);\r\n  if (FlashConnetWifi() == false) {\r\n    HtmlSetWifi();\r\n  }\r\n}\r\nvoid loop(void) {\r\n}\r\n\r\nvoid SetWifi() {\r\n  ssid = ESP8266webServer.arg(0);\r\n  passwd = ESP8266webServer.arg(1);\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.println(\"密码在前面\");\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* WifiPw = passwd.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      ESP8266webServer.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      ESP8266webServer.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  ESP8266webServer.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handlejsonStr() {\r\n  ESP8266webServer.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  ESP8266webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\nvoid ApStart() {\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n  //启动AP模式,并设置账号和密码\r\n  WiFi.softAP(\"192.168.168.2\", \"nobiggie.cn\");\r\n}\r\nvoid WebserverStart() {\r\n  ESP8266webServer.begin(80);               // 启用网络服务\r\n  ESP8266webServer.on(\"/\", handlejsonStr);  // / 在HTTP中,表示请求首页服务,\r\n  ESP8266webServer.on(\"/SetWifi\", SetWifi);\r\n  ESP8266webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid ForPing() {\r\n  //定时时间到了,先取消定时状态\r\n  myTicker.detach();\r\n  delay(500);\r\n  ESP.restart();\r\n}\r\n\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.disconnect();\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n    Serial.println(\"扫描不到网络\");\r\n  }\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result = dateFile.readString();\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* Ssid = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi->false\");\r\n  return false;\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApStart();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  myTicker.detach();\r\n  delay(200);\r\n  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  WebserverStart();\r\n  while (1) {\r\n    ESP8266webServer.handleClient();  // 检查网络服务请求并处理\r\n  }\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\n','',1),
(188,4,'ESP32 AP配网','#include <WiFi.h>\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include \"ESPmDNS.h\"\r\n#include <Ticker.h>\r\n\r\nTicker myTicker;\r\nString flashWifi = \"/wifi.txt\";  //wifi密码存放文件\r\nWebServer webServer;\r\nString ssid;\r\nString passwd;\r\nString html;\r\n#define baudRate 115200\r\nvoid setup(void) {\r\n  if (FlashConnetWifi() == false) {\r\n    HtmlSetWifi();\r\n  }\r\n}\r\n\r\nvoid loop(void) {\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  apStart();\r\n  delay(1000);\r\n  Serial.println(\"start webserver\");\r\n  //5分后重启 防止停电后复电，路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式，所以这里只让AP激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  webserverStart();\r\n  while (1) {\r\n    webServer.handleClient();  //检查网络服务请求并处理\r\n  }\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  WiFi.disconnect();\r\n  Serial.begin(baudRate);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n  }\r\n}\r\nvoid setWifi() {\r\n  ssid = webServer.arg(0);\r\n  passwd = webServer.arg(1);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* Passwd = passwd.c_str();\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.begin(baudRate);\r\n  delay(500);\r\n  WiFi.begin(Ssid, Passwd);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webServer.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      webServer.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flashWifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n      Serial.println(\"HTML设置网络连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webServer.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handleRoot() {\r\n  webServer.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\nvoid apStart() {\r\n  Serial.begin(baudRate);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n  //启动AP模式，并设置账号和密码\r\n  boolean result = WiFi.softAP(\"192.168.168.2\", \"nobiggie.cn\");\r\n  if (result) {\r\n    Serial.println(String(\"Soft-AP IP address = \") + WiFi.softAPIP());\r\n  }\r\n}\r\nvoid webserverStart() {\r\n  webServer.begin(80);            // 启用网络服务\r\n  webServer.on(\"/\", handleRoot);  // / 在HTTP中，表示请求首页服务，\r\n  webServer.on(\"/setWifi\", setWifi);\r\n  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(baudRate);\r\n  delay(200);\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flashWifi) == false) {\r\n    Serial.println(\"NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flashWifi, \"r\");\r\n  String result;\r\n  for (int i = 0; i < dateFile.size(); i++) {\r\n    //Serial.print((char)dateFile.read());\r\n    result += (char)dateFile.read();\r\n  }\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* WifiName = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(WifiName);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(WifiName, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnet...\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"flash connet success\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"flash read error\");\r\n  return false;\r\n}','',1),
(189,4,'esp32舵机','#include <ESP32Servo.h>\r\nServo servo1;\r\nint minUs = 500;   //0度是脉宽,单位us\r\nint maxUs = 2500;  //180度脉宽,单位us\r\nint servo1Pin = 15;\r\nvoid setup() {\r\n  // put your setup code here, to run once:\r\n  Serial.begin(115200);\r\n  ESP32PWM::allocateTimer(0);  //指定使用的硬件定时器 esp32有4个\r\n  servo1.setPeriodHertz(50);   //指定PWM频率\r\n  servo1.attach(servo1Pin, minUs, maxUs);\r\n  servo1.write(90);  //转到指定角度(0-180);\r\n  servo1.detach();   //不需要的时候将引脚和ledc分离\r\n}\r\n\r\nvoid loop() {\r\n  // put your main code here, to run repeatedly:\r\n}\r\n','',1),
(190,4,'ESP32 WIFI 舵机','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <WiFiMulti.h>\r\n#include <WiFiClientSecure.h>\r\n#include <WebSocketsClient.h>\r\n#include <ESP32Servo.h>\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include \"ESPmDNS.h\"\r\n#include <Ticker.h>\r\n#include <ArduinoJson.h>\r\nServo servo1;\r\nint minUs = 500;   //0度是脉宽,单位us\r\nint maxUs = 2500;  //180度脉宽,单位us\r\nint servo1Pin = 15;\r\nint thisOpt=0;//当前角度\r\n\r\nTicker myTicker;\r\nDynamicJsonDocument  jsonBuffer(100);\r\nJsonObject desJson;\r\n//被读取的文件位置和名称\r\nString flash_wifi = \"/wifi.txt\";\r\nWebServer webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80\r\nString ssid = \"\";\r\nString passwd = \"\";\r\nString html;\r\n\r\nWiFiMulti WiFiMulti;\r\nWebSocketsClient webSocket;\r\n\r\n#define USE_SERIAL Serial1\r\n\r\nvoid hexdump(const void *mem, uint32_t len, uint8_t cols = 16) {\r\n  const uint8_t* src = (const uint8_t*) mem;\r\n  USE_SERIAL.printf(\"\\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)\", (ptrdiff_t)src, len, len);\r\n  for (uint32_t i = 0; i < len; i++) {\r\n    if (i % cols == 0) {\r\n      USE_SERIAL.printf(\"\\n[0x%08X] 0x%08X: \", (ptrdiff_t)src, i);\r\n    }\r\n    USE_SERIAL.printf(\"%02X \", *src);\r\n    src++;\r\n  }\r\n  USE_SERIAL.printf(\"\\n\");\r\n}\r\n\r\nvoid webSocketEvent(WStype_t type, uint8_t * payload, size_t length) {\r\n\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      USE_SERIAL.printf(\"[WSc] Disconnected!\\n\");\r\n      break;\r\n    case WStype_CONNECTED:\r\n      USE_SERIAL.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n\r\n      // send message to server when Connected\r\n      webSocket.sendTXT(\"Connected\");\r\n      break;\r\n    case WStype_TEXT:\r\n      USE_SERIAL.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      desJson = jsonBuffer.as<JsonObject>();\r\n      if (desJson[\"message\"] != \"\") {\r\n        String str = desJson[\"message\"];\r\n        Serial.println(str);\r\n        if(str==\"+\"){\r\n          if(thisOpt <=175){\r\n             thisOpt=thisOpt+5;\r\n             servo1.write(thisOpt);\r\n          }\r\n        }\r\n        if(str==\"-\"){\r\n          if(thisOpt<=5){\r\n             thisOpt=thisOpt-5;\r\n             servo1.write(thisOpt);\r\n          }\r\n        }\r\n        str=String(thisOpt);\r\n        webSocket.sendTXT(str);\r\n      }\r\n      // send message to server\r\n\r\n      break;\r\n    case WStype_BIN:\r\n      USE_SERIAL.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      hexdump(payload, length);\r\n\r\n      // send data to server\r\n      // webSocket.sendBIN(payload, length);\r\n      break;\r\n    case WStype_PING:\r\n      // pong will be send automatically\r\n      Serial.printf(\"[WSc] get ping\\n\");\r\n      break;\r\n    case WStype_PONG:\r\n      // answer to a ping we send\r\n      Serial.printf(\"[WSc] get pong\\n\");\r\n      break;\r\n\r\n    case WStype_ERROR:\r\n    case WStype_FRAGMENT_TEXT_START:\r\n    case WStype_FRAGMENT_BIN_START:\r\n    case WStype_FRAGMENT:\r\n    case WStype_FRAGMENT_FIN:\r\n      break;\r\n  }\r\n\r\n}\r\n\r\nvoid setup() {\r\n  // USE_SERIAL.begin(921600);\r\n  USE_SERIAL.begin(115200);\r\n\r\n  //Serial.setDebugOutput(true);\r\n  USE_SERIAL.setDebugOutput(true);\r\n\r\n  USE_SERIAL.println();\r\n  USE_SERIAL.println();\r\n  USE_SERIAL.println();\r\n\r\n  for (uint8_t t = 4; t > 0; t--) {\r\n    USE_SERIAL.printf(\"[SETUP] BOOT WAIT %d...\\n\", t);\r\n    USE_SERIAL.flush();\r\n    delay(1000);\r\n  }\r\n\r\n  if (FlashConnetWifi() == false) {\r\n    HtmlSetWifi();\r\n  }\r\n  ESP32PWM::allocateTimer(0);  //指定使用的硬件定时器 esp32有4个\r\n  servo1.setPeriodHertz(50);   //指定PWM频率\r\n  servo1.attach(servo1Pin, minUs, maxUs);\r\n  //WiFi.disconnect();\r\n\r\n  // server address, port and URL\r\n  webSocket.begin(\"192.168.0.254\", 80, \"/service/wss\");\r\n\r\n  // event handler\r\n  webSocket.onEvent(webSocketEvent);\r\n\r\n  // use HTTP Basic Authorization this is optional remove if not needed\r\n  webSocket.setAuthorization(\"user=mg996r&password=efghijk&room=root\");\r\n\r\n  // try ever 5000 again if connection has failed\r\n  webSocket.setReconnectInterval(5000);\r\n\r\n}\r\n\r\nvoid loop() {\r\n  webSocket.loop();\r\n}\r\n\r\n\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApOpen();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  //5分后重启 防止停电后复电，路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式，所以这里只让AP激活5分钟后就重启\r\n  myTicker.once(300, EspRestart);\r\n  delay(200);\r\n  WebServerOpen();\r\n  while (1) {\r\n    webServer.handleClient();   // 检查网络服务请求并处理\r\n  }\r\n}\r\nvoid EspRestart() {\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_STA);\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a =  \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a +  \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    String HtmlStart = String(\"\") + \"<!DOCTYPE html>\" +\r\n                       \"<html>\" +\r\n                       \"<head>\" +\r\n                       \"<meta charset=\\\"utf-8\\\">\" +\r\n                       \"<meta name=\\\"viewport\\\" content=\\\"width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no\\\" />\" +\r\n                       \"<title>设置WiFi</title>\" +\r\n                       \"<link rel=\\\"stylesheet\\\" href=\\\"\\\">\" +\r\n                       \"</head>\" +\r\n                       \"<script>\" +\r\n                       \"onload  = function() {\" +\r\n                       \"document.getElementById(\\\"up\\\").onclick = function(){\" +\r\n                       \"var ssida=document.getElementById(\\\"ssid\\\").value;\" +\r\n                       \"var ssid=ssida.replace(/\\\\s+/g, \\\" \\\");\" +\r\n                       \"var passwda=document.getElementsByTagName(\'input\')[0].value;\" +\r\n                       \"var passwd=passwda.replace(/\\\\s+/g, \\\" \\\");\" +\r\n                       \"xhr = new XMLHttpRequest();\" +\r\n                       \"xhr.open(\\\"POST\\\",\\\"./SetWifi\\\",true);\" +\r\n                       \"var posts = (\\\"ssid=\\\"+ssid+\\\"&passwd=\\\"+ passwd);\" +\r\n                       \"xhr.setRequestHeader(\\\"Content-Type\\\",\\\"application/x-www-form-urlencoded\\\");\" +\r\n                       \"xhr.send(posts);\" +\r\n                       \"xhr.onreadystatechange = function(){\" +\r\n                       \"if(this.readyState == 4 && this.status == 200){\" +\r\n                       \"alert(this.responseText);\" +\r\n                       \"}\" +\r\n                       \"}\" +\r\n                       \"}\" +\r\n                       \"}\" +\r\n                       \"</script>\" +\r\n                       \"<body>\";\r\n    String HtmlEnd = String(\"\") + \"<p>WiFi密码<input type=\\\"text\\\" id=\\\"passwd\\\"></p>\" +\r\n                     \"<input type=\\\"submit\\\" id=\\\"up\\\" value=\\\"设 置\\\">\" +\r\n                     \"<hr/>\" +\r\n                     \"<div>如果附近WiFi没有显示到自己的WIFI<div>\" +\r\n                     \"<div>请重启或离信号近点</div>\" +\r\n                     \"<div>连接成功AP热点退出转WIFI_STA</div>\" +\r\n                     \"</body>\" +\r\n                     \"</html>\";\r\n    a = a +  \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"no wifis\";\r\n    Serial.println(\"no wifis\");\r\n  }\r\n}\r\nvoid SetWifi() {\r\n  ssid = webServer.arg(0);\r\n  passwd = webServer.arg(1);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* Passwd = passwd.c_str();\r\n  String sp = ssid + \"&\" + passwd;\r\n\r\n  Serial.println(sp);\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  WiFi.begin(Ssid, Passwd);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webServer.send(200, \"text/plane\", \"Wifi密码正确，ESP准备重启\");\r\n      webServer.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(1000);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webServer.send(200, \"text/plane\", \"Wifi密码错误，请重新输入\");\r\n  //HtmlSetWifi();\r\n}\r\n//------- 展示页面-------\r\nvoid handleRoot() {\r\n  webServer.send(200, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\n\r\nvoid ApOpen() {\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet) ;\r\n\r\n  //启动AP模式，并设置账号和密码\r\n  boolean result = WiFi.softAP(\"192.168.168.2\", \"192.168.168.2\");\r\n  if (result) {\r\n    //输出 soft-ap ip地址\r\n    Serial.println(String(\"Soft-AP IP address = \") + WiFi.softAPIP());\r\n  }\r\n}\r\nvoid WebServerOpen() {\r\n  //-----------------启动webserver网络服务--------------------\r\n  webServer.begin(); // 启用网络服务\r\n  webServer.on(\"/\", handleRoot);   // / 在HTTP中，表示请求首页服务，\r\n  webServer.on(\"/SetWifi\", SetWifi);\r\n  webServer.onNotFound(handleNotFound);    // 当服务器没找到所需的服务，调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result;\r\n  for (int i = 0; i < dateFile.size(); i++) {\r\n    //Serial.print((char)dateFile.read());\r\n    result += (char)dateFile.read();\r\n  }\r\n  //去除前后空格\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* WifiName = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(WifiName);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(WifiName, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi->false\");\r\n  return false;\r\n}','',1),
(191,4,'ESP8266 pwm 控制直流电机速度','const int ledPin = 3; \r\n\r\nvoid setup() {\r\n  // put your setup code here, to run once:\r\n  pinMode(ledPin, OUTPUT);\r\n  //频率为 1kHz 默认也是1kHz\r\n  analogWriteFreq(1000);\r\n  //设置 最大值  默认情况下为 1023\r\n  analogWriteRange(100);\r\n  Serial.begin(115200);\r\n}\r\nvoid loop() {\r\n  // 增加电机速度\r\n  for(int dutyCycle = 0; dutyCycle < 100; dutyCycle++){   \r\n    // 通过PWM改变速度\r\n    analogWrite(ledPin, dutyCycle);\r\n    delay(5);\r\n  }\r\n  // 降低电机速度\r\n  for(int dutyCycle = 100; dutyCycle > 0; dutyCycle--){\r\n    // 通过PWM改变速度\r\n    analogWrite(ledPin, dutyCycle);\r\n    delay(5);\r\n  }\r\n\r\n}','',1),
(192,4,'esp32 pwm 控制直流电机速度','//PWM控制\r\nint pwm1 = 21;\r\n//管道esp32有16个管道,0-7为80hz 高速管道  8-16为低速管道\r\nconst int channel1 = 0;\r\n//分辨率  2的8次方 256\r\nconst int resolution = 8;\r\n//PWM频率\r\nconst int freq = 1000;\r\n//初始值 49%\r\nint PwmNow = 125;\r\n//累加数\r\nint fadeAmount = 5;  \r\nvoid setup(void) {\r\npinMode(pwm1, OUTPUT);\r\n  ledcSetup(channel1, freq, resolution);\r\n  ledcAttachPin(pwm1, channel1);\r\n  ledcWrite(channel1, PwmNow);\r\n  pinMode(infrared1, INPUT_PULLUP);\r\n  digitalWrite(infrared1, LOW);\r\n}\r\nvoid loop(void) {\r\n  if(PwmNow<255){\r\n      PwmNow = PwmNow + fadeAmount;\r\n      ledcWrite(channel1, PwmNow);\r\n      int temp=PwmNow*100/255;\r\n      String tempStr=String(temp)+\"%\";\r\nSerial.println(tempStr);\r\ndelay(1000);\r\n  }\r\n//上面这个是旧版的 下面是新版的使用方法\r\n#include <Arduino.h>\r\n\r\nint  pwmPin = 4;              //PWM控制\r\nconst uint32_t pwmFreq = 1000;   // PWM频率1kHz（1-10kHz适合电机控制）\r\nconst uint8_t pwmResolution = 8; // 8位分辨率（0-255）\r\n\r\nvoid setup(void) {\r\n  // 手动选择通道并绑定引脚\r\n  if (ledcAttach( pwmPin,pwmFreq, pwmResolution)) {\r\n    Serial.printf(\"PWM配置成功：频率=%dHz，分辨率=%dbits\\n\", pwmFreq, pwmResolution);\r\n  } else {\r\n     Serial.println(\"PWM配置失败，请检查参数或通道占用情况\");\r\n  }\r\n}\r\nledcWrite(pwmPin,angle);//这个设置占空比','',1),
(193,4,'土壤湿度传感器','                int shidu = 0; // 土壤传感器输入接模拟引脚 A0\r\nint value = 0; //初始化变量value\r\n\r\nvoid setup() {\r\n  Serial.begin(19200);//设置波特率为115200\r\n  Serial.println(\"\\n\");//换行\r\n}\r\n\r\nvoid loop() {\r\n  value = analogRead(shidu);//模拟数据读取（A0脚输入的数据）\r\n  value = value / 10.2;    //上面的value值除以10.2 值得多少要看空悬时也就是干燥时为100%赋值给value\r\n  Serial.print(value);\r\n  Serial.println(\"%\");\r\n  //value值小于50时，对结果进行判\r\n  if (value < 50){\r\n    Serial.println(\"土壤是湿润的\");\r\n  }else{\r\n    Serial.println(\"土壤是干燥的\");\r\n  }\r\n  delay(1000);//延时1s，每一秒读取一次\r\n}     \r\n\r\n/* 该传感器可以独立工作\r\nD0 LED灯不亮时  D0脚为高电平3.3v 亮为低电平，调节小电位器让其到指定的阀值LED灯点亮即低电平,\r\n如果需要读取湿度比例值就必须要接ESP单片机了\r\n*/        ','',1),
(194,4,'向芯片flash读写数据','闪存 相当于 电脑硬盘的C以外盘符\r\n每次重新烧录是不会被清除的只有手动操作<FS.h>库来清除\r\n比如 WIFI 帐号密码存到闪存中\r\n这样就不用向程序里写死WIFI帐号密码\r\n#include <FS.h> //库文件\r\nString flash_file = \"/false.txt\"; //被读取的文件位置和名称\r\n\r\n//是否存在数据\r\nif (SPIFFS.exists(flash_file)) {\r\n  Serial.print(file_name);\r\n  Serial.print(\" FOUND\");\r\n} else {\r\n  Serial.print(file_name);\r\n  Serial.println(\" NOT FOUND\");\r\n}\r\n\r\n//写入数据\r\n  SPIFFS.format();//格式化SPIFFS 慎用\r\n  delay(500);\r\n  SPIFFS.begin();//启动闪存写入模式\r\n  File dataFile = SPIFFS.open(\"./wifi.txt\", \"w\");\r\n  dataFile.println(str);\r\n  dataFile.close();\r\n  return true;\r\n\r\n//读取数据  wra 代表 w r a a+  其中a代表追加数据 a+代表可读可追加\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(\"./wifi.txt\") == false) {\r\n    Serial.print(path);\r\n    Serial.println(\" NOT FOUND\");\r\n    return \"\";\r\n  }\r\n  File dataFile = SPIFFS.open(\"./wifi.txt\", \"r\");\r\n  String result = dataFile.readString();\r\n  result.trim(); //清理字符两边的空格\r\n  Serial.print(result);\r\n\r\n\r\n//显示目录中文件内容以及文件大小\r\nDir dir = SPIFFS.openDir(flash_file); //建立“目录对象”dir\r\nwhile (dir.next()) //dir.next()用于检查目录中是否还有”下一个文件“\r\n{\r\n  Serial.println(dir.fileName());//输出文件名\r\n}\r\n\r\n//删除数据\r\nSPIFFS.remove(flash_file);      ','',1),
(195,4,'MQTT协议','MQTT连接服务器\r\n=======固定报头======\r\n10 ??(后面做完,再数有几个再转16进制值)\r\n\r\n======可变报头 协议名======\r\n00 04   4D 51 54 54(MQTT 转换成16进制的值)\r\n======可变报头 协议级别\r\n04\r\n======可变报头 连接标示======\r\n1(用户名) 1(密码) 0 0 0 0 1 0\r\n转换16进制是\r\nC2\r\n======可变报头 保持连接======\r\n00 78(120秒十进制转换16进制是78)\r\n可变报头串连\r\n10 ?? 00 04 4D 51 54 54 04 C2 00 78\r\n\r\n=========有效载荷=============\r\n//没有遗嘱\r\n有效载荷 = 客户ID + 用户 + 密码\r\nclient id的16进制长度+client id+mqtt username的16进制长度+mqtt username+mqtt password的16进\r\n\r\n制长度+mqtt password\r\n//有遗嘱\r\n有效载荷 = 客户ID + 遗嘱主题 + 遗嘱消息 + 用户 + 密码\r\n?? 超过128字节 请查看 第二条 MQTT剩余长度规则\r\n服务器返回 20 02 00 00 (00)代表成功\r\n\r\n//MQTT剩余长度规则\r\n小于128个字节的可以用 一个字节表示\r\n等于128就要用两个字节表示\r\n计算公式  剩余长度=128*a+b\r\nb是10进制转换成2进制 从后面开始数第8位置为1\r\n比如b=33 二进制是100001 第8位置1那么就等于\r\n10100001 再转换成16进制就是A1\r\n记往低位放前 A1 下面计算放后面的高位\r\na=1 二进制就是1 转换成16进制也是1 那行得到\r\nA1 01\r\n\r\n\r\n//MQTT订阅主题\r\n固定报头 + (可变报头16进制长度(00 xx)+可变报头)+权限等级01\r\n如下\r\n82 ?? 00 0A \r\n(可变报头16进制长度(00 xx)+可变报头)+权限等级01\r\n?? 是(可变报头16进制长度(00 xx)+可变报头)+权限等级01的总长度16进制\r\n服务器返回 90 03 00 0A 01 代表成功\r\n如权限等级是0则返回 90 03 00 0A 00\r\n\r\n//MQTT发布消息 固定报头+可变报头+\r\n30 ?? +发布可变报头长度+发布可变报头+01+消息内容长度+消息内容\r\n\r\n//MQTT取消订阅\r\n====取消订阅=====\r\n固定报头+可变报头+有效载荷\r\nA2 ??(固定报)\r\n00 0A (可变报头)\r\n之前 订阅主题 的16进制的长度+  订阅主题 的16进制(有效载荷)\r\n返回 B0 02 00 0A(代表退订成功)','',1),
(196,4,'arduino 自带函数库Ticker定时','Ticker myTicker;\r\nmyTicker.once(秒为单位只执行一次,方法名);\r\nmyTicker.once_ms(毫秒为单位只执行一次,方法名);\r\n\r\nmyTicker.attach(秒为单位死循环,方法名);\r\nmyTicker.attach_ms(毫秒为单位死循环,方法名);\r\n//也可以加第三个参数为 回调函数\r\n\r\nmyTicker.detch(); //停止定时\r\n\r\nmyTicker.active(); //查看是否有定时状态','',1),
(197,4,'esp32遥控小车AP(电容感应切换车头,射频遥控)','/*\r\n面向车尾   \r\np32  左边电机  左正\r\np33  右边电机  右正\r\np25  左边电机  左反\r\np26  右边电机  右反\r\np27  升   正\r\np14  降   反\r\np12  关发动机  触动3秒后断开\r\nP15  开机通电一直手摸线 可以遥控方向相互转换\r\np4   设置 射频遥控 与 WIFI遥控 切换\r\np21  PWM脚\r\np0   Power8266 输出高电平增加射频遥控 8键射频433遥控\r\n手机要用火狐浏览器\r\n\r\n\r\nP36 前进\r\nP39 后退\r\nP34 左拐弯\r\nP35 右拐弯\r\nP17 移动速度+\r\nP16 移动速度-\r\n*/\r\n#include <WiFi.h>\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include \"ESPmDNS.h\"\r\n\r\n#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <WiFiMulti.h>\r\n#include <WiFiClientSecure.h>\r\n#include <ArduinoJson.h>\r\n#include <WebSocketsClient.h>\r\n\r\nString direction = \"/boolStr.txt\";\r\nString controlMode=\"/controlMode.txt\";\r\nWebServer webServer(80);  // 创建网络服务器对象，HTTP服务的标准端口为：80\r\nString request = \"\";\r\n\r\n//方向相互转换\r\nint touch15 = 15;\r\n//射频遥控  感应切换开关\r\nint touch4=4;\r\n\r\nboolean globalBL=false;\r\n\r\n//转换成功指示灯\r\nint light22 = 22;\r\n\r\n//PWM控制\r\nint pwm1 = 21;\r\n//管道esp32有16个管道,0-7为80hz 高速管道  8-16为低速管道\r\nconst int channel1 = 0;\r\n//分辨率  2的8次方 256\r\nconst int resolution = 8;\r\n//PWM频率\r\nconst int freq = 1000;\r\n//初始值 41%\r\nint PwmNow = 105;\r\n//累加数\r\nint fadeAmount = 5;\r\n//接通esp8266电源\r\nint Power8266 = 0;\r\n\r\nWiFiMulti WiFiMulti;\r\nWebSocketsClient webSocket;\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject root;\r\n\r\nvoid hexdump(const void* mem, uint32_t len, uint8_t cols = 16) {\r\n  const uint8_t* src = (const uint8_t*)mem;\r\n  Serial.printf(\"\\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)\", (ptrdiff_t)src, len, len);\r\n  for (uint32_t i = 0; i < len; i++) {\r\n    if (i % cols == 0) {\r\n      Serial.printf(\"\\n[0x%08X] 0x%08X: \", (ptrdiff_t)src, i);\r\n    }\r\n    Serial.printf(\"%02X \", *src);\r\n    src++;\r\n  }\r\n  Serial.printf(\"\\n\");\r\n}\r\n\r\nString html = String(\"\")+\r\n\"<!DOCTYPE html><html lang=\\\"en\\\">\"+\r\n\"<head>\"+\r\n    \"<meta charset=\\\"UTF-8\\\">\"+\r\n    \"<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\\\" />\"+\r\n    \"<title>遥控车</title>\"+\r\n    \"<script>window.onload = function () {\"+\r\n            \"document.getElementById(\\\"xyzUp\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest();\"+\r\n                \"xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=↑\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzLeft\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\"); xhr.send(\\\"xyz=←\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzLeft\\\").addEventListener(\\\"touchend\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\"); xhr.send(\\\"xyz=■\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzStop\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=■\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzRight\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=→\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzRight\\\").addEventListener(\\\"touchend\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest();\"+\r\n                \"xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true); xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=■\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"xyzDonw\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=↓\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"rise\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest();\"+\r\n                \"xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=rise\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"rise\\\").addEventListener(\\\"touchend\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=■\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"decline\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=decline\\\"); xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"decline\\\").addEventListener(\\\"touchend\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\"); xhr.send(\\\"xyz=■\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"TurnOff\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=TurnOff\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                   \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"speedA\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=speedA\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n            \"document.getElementById(\\\"speedB\\\").addEventListener(\\\"touchstart\\\", function () {\"+\r\n                \"let xhr = new XMLHttpRequest(); xhr.open(\\\"POST\\\", \\\"./xyzgo\\\", true);\"+\r\n                \"xhr.setRequestHeader(\\\"Content-Type\\\", \\\"application/x-www-form-urlencoded\\\");\"+\r\n                \"xhr.send(\\\"xyz=speedB\\\");\"+\r\n                \"xhr.onreadystatechange = function () {\"+\r\n                    \"if (this.readyState == 4 && this.status == 200) {\"+\r\n                        \"document.getElementById(\\\"show\\\").innerHTML = this.responseText;\"+\r\n                    \"}}});\"+\r\n        \"}</script>\"+\r\n\"</head>\"+\r\n\"<style> *{ -moz-user-select: none; -ms-user-select:none; user-select:none; } </style>\"+\r\n\"<body>\"+\r\n    \"<div style=\\\"width: auto;margin-top:30px;text-align: center;\\\" id=\\\"show\\\"></div>\"+\r\n    \"<div style=\\\"padding: 20px;width:auto;margin: 80px auto auto; background-color: #EEF0F2;\\\">\"+\r\n        \"<p><input type=\\\"submit\\\" style=\\\"margin:auto auto 20% 42%;\\\" id=\\\"xyzUp\\\" value=\\\"&nbsp ↑ &nbsp\\\"></p>\"+\r\n        \"<p><input type=\\\"submit\\\" style=\\\"margin-left: 10%;\\\" id=\\\"xyzLeft\\\" value=\\\"&nbsp ← &nbsp\\\">\"+\r\n        \"<input type=\\\"submit\\\" style=\\\"margin:auto 17% auto 17%\\\" id=\\\"xyzStop\\\" value=\\\"&nbsp ■ &nbsp\\\">\"+\r\n        \"<input type=\\\"submit\\\" style=\\\"margin-right: 10%;\\\" id=\\\"xyzRight\\\" value=\\\"&nbsp → &nbsp\\\"></p>\"+\r\n        \"<p></p><input type=\\\"submit\\\" style=\\\"margin:20% auto auto 42%;\\\" id=\\\"xyzDonw\\\" value=\\\"&nbsp ↓ &nbsp\\\"></p>\"+\r\n    \"</div>\"+\r\n    \"<div style=\\\"width:auto;margin: 50px auto;text-align: center; background-color: #EEF0F2;\\\">\"+\r\n        \"<p><input type=\\\"submit\\\" style=\\\"float:left;\\\" id=\\\"rise\\\" value=\\\"&nbsp 机器升 &nbsp\\\"></p>\"+\r\n        \"<p><input type=\\\"submit\\\" style=\\\"margin-left:50%;\\\" id=\\\"decline\\\" value=\\\"&nbsp 机器降 &nbsp\\\"><br />\"+\r\n        \"<p><input type=\\\"submit\\\" value=\\\"速度+\\\" id=\\\"speedA\\\"></p>\"+\r\n        \"<p><input type=\\\"submit\\\" value=\\\"速度-\\\" id=\\\"speedB\\\"></p><br />\"+\r\n        \"<p><input type=\\\"submit\\\" id=\\\"TurnOff\\\" value=\\\"&nbsp 关闭发动机 &nbsp\\\"></p>\"+\r\n    \"</div></body></html>\";\r\n\r\nint LeftAdvance = 32;\r\nint RightAdvance = 33;\r\nint LeftRetreat = 25;\r\nint RightRetreat = 26;\r\nint rise = 27;\r\nint decline = 14;\r\n//发动机\r\nint TurnOff = 12;\r\n\r\n//touch 最大65  手摸20以下 但是运行环境 触摸达不到20以下,20-50间,应该设置为50\r\nconst int ConstTouch = 50;\r\nString runState = \"■\";\r\nString WebSocKetUserName=\"cart1\";\r\n\r\nvoid setup(void) {\r\n  pinMode(LeftAdvance, OUTPUT);\r\n  pinMode(LeftRetreat, OUTPUT);\r\n  pinMode(RightAdvance, OUTPUT);\r\n  pinMode(RightRetreat, OUTPUT);\r\n  pinMode(rise, OUTPUT);\r\n  pinMode(decline, OUTPUT);\r\n  pinMode(TurnOff, OUTPUT);\r\n  pinMode(light22, OUTPUT);\r\n  //pinMode(d5, INPUT_PULLUP);\r\n  //pinMode(d6, INPUT_PULLUP);\r\n\r\n  digitalWrite(LeftAdvance, LOW);\r\n  digitalWrite(LeftRetreat, LOW);\r\n  digitalWrite(RightAdvance, LOW);\r\n  digitalWrite(RightRetreat, LOW);\r\n\r\n  digitalWrite(rise, LOW);\r\n  digitalWrite(decline, LOW);\r\n  digitalWrite(TurnOff, LOW);\r\n  digitalWrite(light22, LOW);\r\n\r\n  pinMode(pwm1, OUTPUT);\r\n  ledcSetup(channel1, freq, resolution);\r\n  ledcAttachPin(pwm1, channel1);\r\n  ledcWrite(channel1, PwmNow);\r\n  pinMode(Power8266, OUTPUT);\r\n  digitalWrite(Power8266, LOW);\r\n\r\n  Serial.begin(115200);\r\n  delay(500);\r\n\r\n  if (!SPIFFS.begin(true)) {\r\n    Serial.println(\"SPIFFS加载失败\");\r\n  }\r\n  if (SPIFFS.exists(direction) == false) {\r\n    //写入数据\r\n    SetSPIFFS(direction, \"true\");\r\n    delay(300);\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(direction, \"r\");\r\n  String boolStr;\r\n  for (int i = 0; i < dateFile.size(); i++) {\r\n    //Serial.print((char)dateFile.read());\r\n    boolStr += (char)dateFile.read();\r\n  }\r\n  //去除前后空格\r\n  boolStr.trim();\r\n  Serial.println(boolStr);\r\n  delay(500);\r\n  int intto = touchRead(touch15);\r\n  delay(1000);\r\n  if (intto < ConstTouch) {\r\n    if (boolStr == \"true\") {\r\n        Serial.println(\"转换遥控方向成 重\");\r\n        SetSPIFFS(direction, \"false\");\r\n        delay(200);\r\n        globalBL=false;\r\n        ledFlicker();\r\n    } else {\r\n        Serial.println(\"转换遥控方向成 轻\");\r\n        SetSPIFFS(direction, \"true\");\r\n        delay(200);\r\n        globalBL=true;\r\n        ledFlicker();\r\n    }\r\n  }\r\n  delay(500);\r\n  int intto4 = touchRead(touch4);\r\n  delay(500);\r\n  //touch4 是否触摸操作 true为触摸\r\n  bool touch4BL=false;\r\n  if(intto4 < ConstTouch){\r\n    touch4BL=true;\r\n    if (!SPIFFS.begin(true)) {\r\n      Serial.println(\"SPIFFS加载失败\");\r\n    }\r\n    if (SPIFFS.exists(controlMode) == false) {\r\n      //写入数据  true代表 增加射频遥控 模式\r\n      SetSPIFFS(controlMode, \"false\");\r\n      delay(300);\r\n    }else{\r\n      String boolStr1;\r\n      File dateFile = SPIFFS.open(controlMode, \"r\");\r\n      for (int i = 0; i < dateFile.size(); i++) {\r\n      //Serial.print((char)dateFile.read());\r\n      boolStr1 += (char)dateFile.read();\r\n      }\r\n      //去除前后空格\r\n      boolStr1.trim();\r\n      Serial.println(boolStr1);\r\n      //上次是true射频模式 现在就关闭射频模式\r\n      if(boolStr1==\"true\"){\r\n        Serial.println(\"关闭射频模式\");\r\n        SetSPIFFS(controlMode, \"false\");\r\n      }else{\r\n        //开户射频模式\r\n        SetSPIFFS(controlMode, \"true\");\r\n        Serial.println(\"开启射频模式\");\r\n        digitalWrite(Power8266,HIGH);\r\n      }\r\n    }\r\n    ledFlicker();\r\n  }\r\n\r\n  if(touch4BL==false){\r\n    if (!SPIFFS.begin(true)) {\r\n      Serial.println(\"SPIFFS加载失败\");\r\n    }else{\r\n      String boolStr2;\r\n      File dateFile = SPIFFS.open(controlMode, \"r\");\r\n      for (int i = 0; i < dateFile.size(); i++) {\r\n      //Serial.print((char)dateFile.read());\r\n      boolStr2 += (char)dateFile.read();\r\n      }\r\n      //去除前后空格\r\n      boolStr2.trim();\r\n      Serial.println(boolStr2);\r\n      \r\n      if(boolStr2==\"true\"){\r\n        digitalWrite(Power8266,HIGH);\r\n      }\r\n    }\r\n  }\r\n\r\n  delay(200);\r\n  ApStart();\r\n  delay(200);\r\n  Serial.println(\"启动网页遥控AP\");\r\n  delay(200);\r\n  WebServerStart();\r\n}\r\n\r\nvoid loop(void) {\r\n  webServer.handleClient();\r\n\r\n}\r\n\r\n\r\n//------- 展示页面-------\r\nvoid handleRoot() {\r\n  webServer.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\nvoid xyzgo() {\r\n  request = webServer.arg(\"xyz\");\r\n  //Serial.println(request);\r\n  if (request == \"↑\") {\r\n    if(globalBL==false){\r\n      LeftFore();\r\n      RightFore();\r\n    }else{\r\n      LeftAft();\r\n      RightAft();\r\n    }\r\n    runState = \"↑\";\r\n    webServer.send(200, \"text/plane\", request);\r\n    Serial.println(\"↑\");\r\n    return;\r\n  }\r\n  if (request == \"↓\") {\r\n    if(globalBL==false){\r\n      LeftAft();\r\n      RightAft();\r\n    }else{\r\n      LeftFore();\r\n      RightFore();\r\n    }\r\n    runState = \"↓\";\r\n    webServer.send(200, \"text/plane\", request);\r\n    Serial.println(\"↓\");\r\n    return;\r\n  }\r\n  if (request == \"■\") {\r\n    StopAllExec();\r\n    runState = \"■\";\r\n    webServer.send(200, \"text/plane\", request);\r\n    Serial.println(\"■\");\r\n    return;\r\n  }\r\n  if (request == \"←\") {\r\n      RightFore();\r\n      LeftAft();\r\n    runState = \"←\";\r\n    webServer.send(200, \"text/plane\", request);\r\n    Serial.println(\"←\");\r\n    return;\r\n  }\r\n  if (request == \"→\") {\r\n      LeftFore();\r\n      RightAft();\r\n    \r\n    runState = \"→\";\r\n    webServer.send(200, \"text/plane\", request);\r\n    Serial.println(\"→\");\r\n    return;\r\n  }\r\n  if (request == \"rise\") {\r\n    digitalWrite(decline, LOW);\r\n    digitalWrite(rise, HIGH);\r\n    webServer.send(200, \"text/plane\", request);\r\n    return;\r\n  }\r\n  if (request == \"decline\") {\r\n    digitalWrite(rise, LOW);\r\n    digitalWrite(decline, HIGH);\r\n    webServer.send(200, \"text/plane\", request);\r\n    return;\r\n  }\r\n  if (request == \"TurnOff\") {\r\n    digitalWrite(TurnOff, HIGH);\r\n    webServer.send(200, \"text/plane\", request);\r\n    delay(3000);\r\n    digitalWrite(TurnOff, LOW);\r\n    return;\r\n  }\r\n  if (request == \"speedA\") {\r\n    if (PwmNow >= 178) {\r\n      webServer.send(200, \"text/plane\", \"已达到70%限速,无法继续加速\");\r\n    } else {\r\n      PwmNow = PwmNow + fadeAmount;\r\n      ledcWrite(channel1, PwmNow);\r\n      int temp=PwmNow*100/255;\r\n      String tempStr=String(temp)+\"%\";\r\n      webServer.send(200, \"text/plane\", tempStr);\r\n      Serial.println(\"speedA\");\r\n    }\r\n    return;\r\n  }\r\n  if (request == \"speedB\") {\r\n    if (PwmNow <= 77) {\r\n      webServer.send(200, \"text/plane\", \"已达到30%限速,无法继续减速\");\r\n    } else {\r\n      PwmNow = PwmNow - fadeAmount;\r\n      ledcWrite(channel1, PwmNow);\r\n      int temp=PwmNow*100/255;\r\n      String tempStr=String(temp)+\"%\";\r\n      webServer.send(200, \"text/plane\", tempStr);\r\n      Serial.println(\"speedB\");\r\n    }\r\n\r\n    return;\r\n  }\r\n  //HtmlSetWifi();\r\n}\r\n\r\nvoid ApStart() {\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n  String mymac=WiFi.macAddress();\r\n  Serial.println(mymac);\r\n  mymac.replace(\":\",\"\");\r\n  mymac.toLowerCase();\r\n  String temp1=mymac.substring(0,2);\r\n  String temp2=mymac.substring(10,12);\r\n  String ApPasswd=temp1+temp2;\r\n  Serial.println(ApPasswd);\r\n  //启动AP模式，并设置账号和密码\r\n  boolean result = WiFi.softAP(\"esp32WebServer192.168.168.2\", \"www.360xyz.cn\");\r\n  if (result) {\r\n    //输出 soft-ap ip地址\r\n    Serial.println(String(\"Soft-AP IP address = \") + WiFi.softAPIP());\r\n  }\r\n}\r\n//------- 展示页面-------\r\n\r\nvoid WebServerStart() {\r\n  //-----------------启动webserver网络服务--------------------\r\n  webServer.begin();              // 启用网络服务\r\n  webServer.on(\"/\", handleRoot);  // / 在HTTP中，表示请求首页服务，\r\n  webServer.on(\"/xyzgo\", xyzgo);\r\n  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid SetSPIFFS(String direction, String str) {\r\n  //写入数据\r\n  SPIFFS.remove(direction);\r\n  delay(500);\r\n  SPIFFS.begin();\r\n  File dataFile = SPIFFS.open(direction, \"w\");\r\n  dataFile.println(str);\r\n  dataFile.close();\r\n}\r\n//左轮前进\r\nvoid LeftFore() {\r\n  digitalWrite(LeftRetreat, LOW);\r\n  digitalWrite(LeftAdvance, HIGH);\r\n}\r\n//左轮后退\r\nvoid LeftAft() {\r\n  digitalWrite(LeftAdvance, LOW);\r\n  digitalWrite(LeftRetreat, HIGH);\r\n}\r\n//右轮前进\r\nvoid RightFore() {\r\n  digitalWrite(RightRetreat, LOW);\r\n  digitalWrite(RightAdvance, HIGH);\r\n}\r\n//右轮后退\r\nvoid RightAft() {\r\n  digitalWrite(RightAdvance, LOW);\r\n  digitalWrite(RightRetreat, HIGH);\r\n}\r\nvoid StopAllExec() {\r\n  digitalWrite(LeftAdvance, LOW);\r\n  digitalWrite(RightAdvance, LOW);\r\n  digitalWrite(LeftRetreat, LOW);\r\n  digitalWrite(RightRetreat, LOW);\r\n  digitalWrite(rise, LOW);\r\n  digitalWrite(decline, LOW);\r\n  delay(500);\r\n}\r\n//LED 闪烁\r\nvoid ledFlicker(){\r\n  digitalWrite(light22, HIGH);\r\n  delay(500);\r\n  digitalWrite(light22, LOW);\r\n  delay(500);\r\n  digitalWrite(light22, HIGH);\r\n  delay(500);\r\n  digitalWrite(light22, LOW);\r\n}','',1),
(198,4,'ESP8266 https GET','#include <ArduinoJson.h>\r\n#include <ESP8266WiFi.h>\r\n\r\nconst char* ssid = \"CMCC-j3vt\";\r\nconst char* password = \"qq282246101\";\r\n\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  delay(10);\r\n\r\n  Serial.println();\r\n  Serial.print(\"Connecting to \");\r\n  Serial.println(ssid);\r\n\r\n  WiFi.begin(ssid, password);\r\n\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n  }\r\n\r\n  Serial.println(\"\");\r\n  Serial.println(\"WiFi connected\");\r\n  Serial.println(\"IP address: \");\r\n  Serial.println(WiFi.localIP());\r\n}\r\nString MyHttpsGet(String host, String url) {\r\n  // Use WiFiClient class to create TCP connections\r\n  WiFiClientSecure client;\r\n  const int httpPort = 443;\r\n  client.setInsecure(); \r\n  if (!client.connect(host, httpPort)) { \r\n    Serial.println(\"connection failed\");\r\n    return \"\";\r\n  }\r\n\r\n  // This will send the request to the server\r\n  client.print(String(\"GET \") + url + \" HTTP/1.1\\r\\n\" +\r\n               \"Host: \" + host + \"\\r\\n\" +\r\n               \"Connection: close\\r\\n\\r\\n\");\r\n  String ss;\r\n  while (client.connected())\r\n  {\r\n    String line = client.readStringUntil(\'\\n\');\r\n    if (line.length() != 0) {\r\n      ss = ss + line;\r\n    }\r\n  }\r\n  return ss.substring(ss.lastIndexOf(\"Connection: close\") + 17);\r\n  client.stop();\r\n}\r\nvoid loop() {\r\n  String host = \"www.360xyz.cn\";\r\n  String url = \"/query/GetDate\";\r\n\r\n  String a = MyHttpsGet(host, url);\r\n  Serial.println(a);\r\n  delay(10000);\r\n}','',1),
(199,4,'esp32 https GET','String cat=String(\"\")+\"-----BEGIN CERTIFICATE-----\\r\\n\"+\r\n\"MIIEMjCCAxqgAwIBAgIBATANBgkqhkiG9w0BAQUFADB7MQswCQYDVQQGEwJHQjEb\\r\\n\"+\r\n\"MBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRAwDgYDVQQHDAdTYWxmb3JkMRow\\r\\n\"+\r\n\"GAYDVQQKDBFDb21vZG8gQ0EgTGltaXRlZDEhMB8GA1UEAwwYQUFBIENlcnRpZmlj\\r\\n\"+\r\n\"YXRlIFNlcnZpY2VzMB4XDTA0MDEwMTAwMDAwMFoXDTI4MTIzMTIzNTk1OVowezEL\\r\\n\"+\r\n\"MAkGA1UEBhMCR0IxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjEQMA4GA1UE\\r\\n\"+\r\n\"BwwHU2FsZm9yZDEaMBgGA1UECgwRQ29tb2RvIENBIExpbWl0ZWQxITAfBgNVBAMM\\r\\n\"+\r\n\"GEFBQSBDZXJ0aWZpY2F0ZSBTZXJ2aWNlczCCASIwDQYJKoZIhvcNAQEBBQADggEP\\r\\n\"+\r\n\"ADCCAQoCggEBAL5AnfRu4ep2hxxNRUSOvkbIgwadwSr+GB+O5AL686tdUIoWMQua\\r\\n\"+\r\n\"BtDFcCLNSS1UY8y2bmhGC1Pqy0wkwLxyTurxFa70VJoSCsN6sjNg4tqJVfMiWPPe\\r\\n\"+\r\n\"3M/vg4aijJRPn2jymJBGhCfHdr/jzDUsi14HZGWCwEiwqJH5YZ92IFCokcdmtet4\\r\\n\"+\r\n\"YgNW8IoaE+oxox6gmf049vYnMlhvB/VruPsUK6+3qszWY19zjNoFmag4qMsXeDZR\\r\\n\"+\r\n\"rOme9Hg6jc8P2ULimAyrL58OAd7vn5lJ8S3frHRNG5i1R8XlKdH5kBjHYpy+g8cm\\r\\n\"+\r\n\"ez6KJcfA3Z3mNWgQIJ2P2N7Sw4ScDV7oL8kCAwEAAaOBwDCBvTAdBgNVHQ4EFgQU\\r\\n\"+\r\n\"oBEKIz6W8Qfs4q8p74Klf9AwpLQwDgYDVR0PAQH/BAQDAgEGMA8GA1UdEwEB/wQF\\r\\n\"+\r\n\"MAMBAf8wewYDVR0fBHQwcjA4oDagNIYyaHR0cDovL2NybC5jb21vZG9jYS5jb20v\\r\\n\"+\r\n\"QUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNqA0oDKGMGh0dHA6Ly9jcmwuY29t\\r\\n\"+\r\n\"b2RvLm5ldC9BQUFDZXJ0aWZpY2F0ZVNlcnZpY2VzLmNybDANBgkqhkiG9w0BAQUF\\r\\n\"+\r\n\"AAOCAQEACFb8AvCb6P+k+tZ7xkSAzk/ExfYAWMymtrwUSWgEdujm7l3sAg9g1o1Q\\r\\n\"+\r\n\"GE8mTgHj5rCl7r+8dFRBv/38ErjHT1r0iWAFf2C3BUrz9vHCv8S5dIa2LX1rzNLz\\r\\n\"+\r\n\"Rt0vxuBqw8M0Ayx9lt1awg6nCpnBBYurDC/zXDrPbDdVCYfeU0BsWO/8tqtlbgT2\\r\\n\"+\r\n\"G9w84FoVxp7Z8VlIMCFlA2zs6SFz7JsDoeA3raAVGI/6ugLOpyypEBMs1OUIJqsi\\r\\n\"+\r\n\"l2D4kF501KKaU73yqWjgom7C12yxow+ev+to51byrvLjKzg6CYG1a4XXvi3tPxq3\\r\\n\"+\r\n\"smPi9WIsgtRqAEFQ8TmDn5XpNpaYbg==\\r\\n\"+\r\n\"-----END CERTIFICATE-----\";\r\nString MyHttpsGet(String url){\r\n  HTTPClient http;\r\n  String result;\r\n  http.begin(url,cat.c_str());\r\n  int httpCode=http.GET();\r\n  if(httpCode>0){\r\n     result=http.getString();\r\n  }\r\n  http.end();\r\n  return result;\r\n}','',1),
(200,4,'ESP32 多开一条线程','\r\n#include <Thread.h>\r\nThread myThread = Thread();\r\nvoid setup(void) {\r\n  myThread.onRun(niceCallback);\r\n  myThread.setInterval(10000);\r\n}\r\nvoid loop(void) {\r\n  if (myThread.shouldRun()) {\r\n    myThread.run();\r\n  }\r\n}\r\n\r\n\r\n\r\nvoid niceCallback() {\r\n  String HI = esp32getHI();\r\n  Serial.println(HI);\r\n  tft.setTextColor(TFT_WHITE, TFT_BLACK);\r\n  tft.setCursor(10, 10, 2);  //设置起始坐标(10, 10)，1 号字体\r\n  tft.println(HI);           //显示文字\r\n  millis();\r\n}','',1),
(205,5,'window 窗口创建使用','#include <windows.h>\r\n#include <commctrl.h>>\r\nUINT IDC_BUTTON_1=200;\r\n/* This is where all the input to the window goes to这是窗口的所有输入的位置 */\r\nLRESULT CALLBACK WndProc(HWND hwnd, UINT Message, WPARAM wParam, LPARAM lParam) {\r\n\r\n	switch(Message) {\r\n		//在创建窗口过程时 未显示窗口前 接收 WM_CREATE 这时可以创建控件 \r\n		case WM_CREATE: {\r\n			UINT IDC_BUTTON_1=200;\r\n			HWND hButtonWnd=CreateWindow(WC_BUTTON,\"确定\",WS_CHILD|BS_PUSHBUTTON|WS_VISIBLE,100,100,50,20,hwnd,(HMENU)IDC_BUTTON_1,NULL,NULL);\r\n\r\n		}\r\n		break;\r\n		//接收 来自button的点击事件 WM_COMMAND\r\n		case WM_COMMAND: {\r\n			UINT buttio_id=LOWORD(wParam);\r\n			UINT nCode=HIWORD(wParam);\r\n			if(nCode==BN_CLICKED && buttio_id==IDC_BUTTON_1) {\r\n				MessageBox(hwnd,\"弹出消息了\",\"tip\",MB_OK);\r\n			}\r\n		}\r\n		break;\r\n		/* Upon destruction, tell the main thread to stop 销毁时，告诉主线程停止*/\r\n		case WM_DESTROY: {\r\n			PostQuitMessage(0);\r\n			break;\r\n		}\r\n\r\n		/* All other messages (a lot of them) are processed using default procedures */\r\n		default:\r\n			return DefWindowProc(hwnd, Message, wParam, lParam);\r\n	}\r\n	return 0;\r\n}\r\n\r\n/* The \'main\' function of Win32 GUI programs: this is where execution starts */\r\nint WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {\r\n	WNDCLASSEX wc; /* A properties struct of our window  窗口的一个属性结构体*/\r\n	HWND hwnd; /* A \'HANDLE\', hence the H, or a pointer to our window 一个\'HANDLE\'，也就是H，或者一个指向窗口的指针*/\r\n	MSG msg; /* A temporary location for all messages 所有消息的临时位置*/\r\n\r\n	/* zero out the struct and set the stuff we want to modify 将结构归零并设置我们想要修改的内容 */\r\n	memset(&wc,0,sizeof(wc));\r\n	wc.cbSize		 = sizeof(WNDCLASSEX);\r\n	wc.lpfnWndProc	 = WndProc; /* This is where we will send messages to 这是我们要发送消息的地方*/\r\n	wc.hInstance	 = hInstance;\r\n	wc.hCursor		 = LoadCursor(NULL, IDC_ARROW);\r\n\r\n	/* White, COLOR_WINDOW is just a #define for a system color, try Ctrl+Clicking it 白色，COLOR_WINDOW只是一个系统颜色的#定义，尝试Ctrl+点击它*/\r\n	wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);\r\n	wc.lpszClassName = \"WindowClass\";\r\n	wc.hIcon		 = LoadIcon(NULL, IDI_APPLICATION); /* Load a standard icon 加载一个标准图标*/\r\n	wc.hIconSm		 = LoadIcon(NULL, IDI_APPLICATION); /* use the name \"A\" to use the project icon 使用名称“A”来使用项目图标*/\r\n	//MessageBox(NULL,\"111111\",\"标题\",MB_ICONEXCLAMATION|MB_OK);\r\n	if(!RegisterClassEx(&wc)) {\r\n		MessageBox(NULL, \"Window Registration Failed!\",\"Error!\",MB_ICONEXCLAMATION|MB_OK);\r\n		return 0;\r\n	}\r\n\r\n	hwnd = CreateWindowEx(WS_EX_CLIENTEDGE,\"WindowClass\",\"Caption123\",WS_VISIBLE|WS_OVERLAPPEDWINDOW,\r\n	                      CW_USEDEFAULT, /* x */\r\n	                      CW_USEDEFAULT, /* y */\r\n	                      640, /* width */\r\n	                      480, /* height */\r\n	                      NULL,NULL,hInstance,NULL);\r\n\r\n	if(hwnd == NULL) {\r\n		MessageBox(NULL, \"Window Creation Failed!\",\"Error!\",MB_ICONEXCLAMATION|MB_OK);\r\n		return 0;\r\n	}\r\n\r\n\r\n\r\n\r\n	/*\r\n		This is the heart of our program where all input is processed and\r\n		sent to WndProc. Note that GetMessage blocks code flow until it receives something, so\r\n		this loop will not produce unreasonably high CPU usage\r\n	*/\r\n	while(GetMessage(&msg, NULL, 0, 0) > 0) { /* If no error is received... */\r\n		TranslateMessage(&msg); /* Translate key codes to chars if present */\r\n		DispatchMessage(&msg); /* Send it to WndProc */\r\n	}\r\n	return msg.wParam;\r\n}','',1),
(206,5,'window窗口API开发一些常用函数','都是在WndProc 里面响应\r\nWM_CREATE  //窗口创建前\r\n\r\nWM_COMMAND  //窗口点击事件\r\nUINT nCode=HIWORD(wParam);\r\nnCode==BN_CLICKED\r\n//WS_CHILD子窗口\r\n//WS_VISIBLE 显示的意思\r\n\r\n//HMENU  UINT强制类型转换HMENU\r\nhwnd H= CreateWindow(窗口类型,控件名,样式(WS_CHILD|WS_VISIBLE),坐标x, 坐标y,宽,高,句柄,(HMENU)菜单(控件id),NULL,NULL );\r\n','',1),
(207,5,'c++类','#include <iostream>\r\nusing namespace std;\r\n\r\nclass Xyz\r\n{\r\n    int sz;\r\n    // 构造方法\r\npublic:\r\n    Xyz(int size)\r\n    {\r\n        sz = size;\r\n    }\r\n\r\npublic:\r\n    int Show()\r\n    {\r\n        return sz;\r\n    }\r\n\r\npublic:\r\n    string abc()\r\n    {\r\n        return \"Hello Worde\";\r\n    }\r\n    // 析构方法\r\npublic:\r\n    ~Xyz()\r\n    {\r\n    }\r\n};\r\n//main函数 是必须 放最后面 不然调用不了上面的类\r\nint main()\r\n{\r\n    Xyz abc(456);\r\n    cout << abc.abc();\r\n    return 0;\r\n}\r\n','',1),
(208,5,'c++引用 指针','#include <iostream>\r\nusing namespace std;\r\nint main()\r\n{\r\n    string a = \"123\";\r\n    // b变量是引用变量 a变量是本体\r\n    // 随着本体变量a的变化而变化\r\n    string &b = a;\r\n    a = \"321\";\r\n\r\n    // c是指针变量 存储的是a变量的内存地址\r\n    string *c = &a;\r\n    cout << b << endl;\r\n    cout << c << endl;\r\n    return 0;\r\n}\r\n输出结果 \r\n321\r\n0x12ebfffb90','',1),
(209,5,'c++ 获取当前时间','#include <ctime>\r\nusing namespace std;\r\nclass NowTime\r\n{\r\npublic:\r\n	int Time()\r\n	{\r\n		return time(0);\r\n	}\r\n\r\npublic:\r\n	string DateTime()\r\n	{\r\n		time_t now = time(0);\r\n		tm *p = localtime(&now);\r\n		int a = printf(\"%d-%d-%d %02d:%02d:%02d\\n\", 1900 + p->tm_year, 1 + p->tm_mon, p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);\r\n		return to_string(a);\r\n	}\r\n};','',1),
(210,2,'apache2+php7卸载重装','一、删除apache的相关包及配置\r\n    sudo apt-get autoremove apache*\r\n二、删除关联\r\n    sudo find /etc -name \"*apache*\" |xargs  rm -rf\r\n三、清除dept列表\r\n    sudo apt purge `dpkg -l | grep apache| awk \'{print $2}\' |tr \"\\n\" \" \"`\r\n\r\n一、删除php的相关包及配置\r\n    sudo apt-get autoremove php7*\r\n二、删除关联\r\n    sudo find /etc -name \"*php*\" |xargs  rm -rf\r\n三、清除dept列表\r\n    sudo apt purge `dpkg -l | grep php| awk \'{print $2}\' |tr \"\\n\" \" \"`\r\n\r\n\r\napt install apache2 -y\r\nvi /etc/apache2/ports.conf   可以修改端口\r\n\r\nsudo apt install php libapache2-mod-php  //apache 关联php\r\nsudo systemctl restart apache2\r\nsudo a2enmod rewrite  //apache2 rewrite\r\n\r\nvi /etc/apache2/sites-enabled/000-default.conf\r\n<VirtualHost *:80> 里添加\r\n\r\n     <Directory /var/www/html>\r\n       Options Indexes FollowSymLinks\r\n       AllowOverride All\r\n       Require all granted\r\n     </Directory>\r\n\r\napt install php -y\r\napt install php-gd\r\napt install php-mysqli\r\napt install php-mbstring\r\n','',1),
(211,2,'要取消phpMyAdmin最大上传2M的限制','修改 php.ini 文件：\r\n打开 php.ini 文件，该文件的位置可能因服务器配置而异。常见位置是在 PHP 安装目录下。\r\n找到并修改以下参数：\r\nupload_max_filesize：允许上传文件大小的最大值，默认为 2M。将其修改为你需要的大小，例如 10M（即 upload_max_filesize = 10M）。\r\npost_max_size：指通过表单 POST 给 PHP 的所能接收的最大值，包括表单里的所有值。默认为 8M。设置该值必须大于 upload_max_filesize，例如 post_max_size = 10M。\r\nfile_uploads：确认其值为 on，以确保允许通过 HTTP 上传文件。\r\nupload_tmp_dir：指定文件上传至服务器上存储临时文件的目录。如果不指定，将使用系统默认的临时目录。\r\n还可以考虑修改以下参数，以避免因页面执行时间或内存限制导致的问题：\r\nmax_execution_time：每个 PHP 页面运行的最大时间值（秒），默认 30 秒。可适当增大，例如 max_execution_time = 300。\r\nmax_input_time：每个 PHP 页面接收数据所需的最大时间，默认 60 秒。可适当增大，例如 max_input_time = 300。\r\nmemory_limit：每个 PHP 页面所占用的最大内存，默认 8M。需根据服务器实际情况进行调整，确保其大于 post_max_size，例如 memory_limit = 128M。\r\n修改完 php.ini 文件后，需要重启 Web 服务器（如 Apache 或 Nginx），使修改生效。','',1),
(212,3,'个别浏览器session不同步','火狐浏览器,post更改session不会同步,还有验证码都是失败','',1),
(213,4,'nodeMCU esp8266获取串口2数据并POST','#include <ESP8266WiFi.h>\r\n#include <ESP8266HTTPClient.h>\r\n#include <SoftwareSerial.h>\r\nWiFiClient wificlient;\r\n#define RX_PIN 13  // 接收引脚\r\n#define TX_PIN 15  // 发送引脚\r\nSoftwareSerial mySerial(RX_PIN, TX_PIN);\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  mySerial.begin(19200);\r\n  WiFi.begin(\"webserver\", \"www.nobiggie.cn\");\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  while (1) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      break;\r\n    }\r\n  }\r\n}\r\n\r\nvoid loop() {\r\n  if (mySerial.available()) {\r\n    String receivedData = \"\";\r\n    int i=1;\r\n    while (mySerial.available()) {\r\n      char c = mySerial.read();\r\n      receivedData += c;\r\n      i++;\r\n    }\r\n\r\n    if (i>10) {\r\n      Serial.println(\"单号数据\"+receivedData);\r\n      String postStr=\"number=\"+receivedData;\r\n      esp8266Post(\"http://192.168.7.254/api/scan\",postStr);\r\n    }\r\n  }\r\n}\r\nString esp8266Post(String url, String postStr) {\r\n  HTTPClient http;\r\n  String response;\r\n  http.begin(wificlient, url);\r\n  http.setTimeout(3000);\r\n  //需要传输的数据\r\n  char dataBuff[postStr.length()];\r\n  strcpy(dataBuff, postStr.c_str());\r\n  //头信息，需要的反注释就可以\r\n  //http.addHeader(\"Content-Type\", \"application/json;charset=utf-8\");\r\n  http.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n  int httpCode = http.POST(dataBuff);\r\n  if (httpCode == 200) {\r\n    response = http.getString();\r\n    return response;\r\n  } else {\r\n    return \"\";\r\n  }\r\n  http.end();\r\n}','',1),
(220,3,'float64转string','ff := 0.64\r\nstr := fmt.Sprintf(\"%.2f\", ff)','',1),
(221,3,'string转float64','    str := \"3.14\"\r\n    num, err := strconv.ParseFloat(str, 64)\r\n    if err!= nil {\r\n        // 处理转换错误\r\n        fmt.Println(\"转换出错:\", err)\r\n        return\r\n    }','',1),
(222,3,'int转string','str:= fmt.Sprintf(\"%d\", i)','',1),
(223,3,'string转int','    str := \"123\"\r\n    num, err := strconv.Atoi(str)\r\n    if err!= nil {\r\n        fmt.Println(\"转换出错:\", err)\r\n        return\r\n    }','',1),
(224,3,'bool转string','    b := true\r\n    str := fmt.Sprintf(\"%t\", b)','',1),
(225,3,'string转bool','str := \"true\"\r\nb, err := strconv.ParseBool(str)\r\nif err != nil {\r\n	fmt.Println(\"转换错误:\", err)\r\n	return\r\n}','',1),
(226,3,'websocket','func (ggg ServiceClass) Wss(ctx *gin.Context) {\r\n	if WebsocketLimiter.Allow() {\r\n		if ctx.Request.Method == \"GET\" {\r\n			var ws = websocket.Upgrader{\r\n				WriteBufferSize: 1028,\r\n				ReadBufferSize:  1028,\r\n				//跨域\r\n				CheckOrigin: func(r *http.Request) bool {\r\n					return true\r\n				},\r\n			}\r\n			var user, passwd, room string\r\n			webOs := true //默认网页登录\r\n			if ctx.Query(\"user\") != \"\" {\r\n				//fmt.Println(\"帐号用户\")\r\n				user = ctx.Query(\"user\")\r\n				passwd = ctx.Query(\"passwd\")\r\n				room = ctx.Query(\"room\")\r\n				if user == \"\" || passwd == \"\" || room == \"\" {\r\n					return\r\n				}\r\n				u := \"wQMh8OVTeE\"\r\n				//密码不是站长就验证  站长可以进任何房间\r\n				if passwd != u {\r\n					roomId := 0\r\n					bl1 := models.Db.Sql(\"SELECT id FROM WebsocketRoom WHERE name=?\", room).Row(&roomId)\r\n					if !bl1 {\r\n						return\r\n					}\r\n					//验证 帐号密码,房间room\r\n					openid := \"\"\r\n					b := models.Db.Sql(\"SELECT openid FROM Users WHERE user=? AND passwd=? AND roomid=?\", user, passwd, roomId).Row(&openid)\r\n					if !b {\r\n						return\r\n					}\r\n					if openid[len(openid)-10:] != passwd {\r\n						return\r\n					}\r\n				}\r\n			} else {\r\n				authorization := ctx.Request.Header[\"Authorization\"][0]\r\n				if authorization != \"\" {\r\n					webOs = false //非网页登录\r\n					//fmt.Println(\"单片机\")\r\n					arr, err7 := models.WebSystem.Parse_str(authorization)\r\n					if err7 != nil {\r\n						fmt.Println(\"单片机解析错误\")\r\n						return\r\n					}\r\n					user = arr[\"user\"]\r\n					passwd = arr[\"passwd\"]\r\n					room = arr[\"room\"]\r\n					if user == \"\" || passwd == \"\" || room == \"\" {\r\n						return\r\n					}\r\n				}\r\n				roomId := 0\r\n				bl1 := models.Db.Sql(\"SELECT id FROM WebsocketRoom WHERE name=?\", room).Row(&roomId)\r\n				if !bl1 {\r\n					return\r\n				}\r\n				//验证 帐号密码,房间room\r\n				passwdSql := \"\"\r\n				b := models.Db.Sql(\"SELECT id FROM Users WHERE user=? AND passwd=? AND roomid=?\", user, passwd, roomId).Row(&passwdSql)\r\n				if !b {\r\n					return\r\n				}\r\n			}\r\n			//正确 升级为websocket\r\n			conn, _ := ws.Upgrade(ctx.Writer, ctx.Request, nil)\r\n			//检查一下 自己组里有没有相同名字的,如果有把之前登录的同名用户下线\r\n			duplicationName := false //如果有同名用户 为 true\r\n			gUser := []string{}\r\n\r\n			if len(models.GroupFd[room]) != 0 {\r\n				for _, v := range models.GroupFd[room] {\r\n					if models.Clients[v].User == user {\r\n						duplicationName = true\r\n						//下线该用户名\r\n						models.Clients[v].Client.Close()\r\n\r\n						//重组在线成员列表\r\n						gFd := []uint32{}\r\n\r\n						for _, vv := range models.GroupFd[room] {\r\n							if v != vv {\r\n								gFd = append(gFd, vv)\r\n								gUser = append(gUser, models.Clients[vv].User)\r\n								//同时向在线该组成员 推送 用户重新上线消息\r\n								models.RWlock.Lock()\r\n								models.Clients[vv].Client.WriteMessage(websocket.TextMessage, []byte(\"SystemMessage: \"+user+\" 重新上线了\"))\r\n								models.RWlock.Unlock()\r\n							}\r\n						}\r\n						//得到去除同名的在线组成员 下一步把自己 加到该分组中\r\n						models.GroupFd[room] = gFd\r\n						models.GroupUser[room] = gUser\r\n						for _, v := range models.GroupFd[room] {\r\n							tmp := map[string][]string{}\r\n							//组成员赋值给 map[\"Group\"] 发送json\r\n							tmp[\"Group\"] = models.GroupUser[room]\r\n							models.Clients[v].Client.WriteJSON(tmp)\r\n						}\r\n\r\n						break\r\n					}\r\n				}\r\n				//加到 自己的fd 加到分组中 把自己的用户名 加到 在线组成员列表中\r\n				models.GroupFd[room] = append(models.GroupFd[room], models.Fd)\r\n				models.GroupUser[room] = append(models.GroupUser[room], user)\r\n			} else {\r\n				models.GroupFd[room] = []uint32{models.Fd}\r\n				models.GroupUser[room] = []string{user}\r\n			}\r\n			// 	//注册到系统 room fd user\r\n			client := models.WebsocketStruct{Client: conn, User: user, Fd: models.Fd, Room: room, WebOs: webOs}\r\n			models.RWlock.Lock()\r\n			models.Clients[models.Fd] = &client\r\n			models.RWlock.Unlock()\r\n			pongTimeOut := make(chan bool)\r\n\r\n			if len(models.GroupFd[room]) != 0 {\r\n				//推送在线组成员到每个成员中\r\n				//fmt.Println(models.GroupUser[room])\r\n				for _, v := range models.GroupFd[room] {\r\n					//不同名 向在该组在线成员推送 上线消息\r\n					if !duplicationName && v != models.Fd {\r\n						models.Clients[v].Client.WriteMessage(websocket.TextMessage, []byte(\"SystemMessage: \"+user+\" 上线了\"))\r\n					}\r\n					tmp := map[string][]string{}\r\n					tmp[\"Group\"] = models.GroupUser[room]\r\n					models.Clients[v].Client.WriteJSON(tmp)\r\n				}\r\n			}\r\n\r\n			//返回ping 向客户表示连接成功\r\n			if webOs {\r\n				conn.WriteMessage(websocket.PingMessage, []byte{})\r\n			}\r\n			//监听客户端close事件或者非常断开\r\n			conn.SetCloseHandler(func(code int, text string) error {\r\n				gFd := []uint32{}\r\n				for _, v := range models.GroupFd[room] {\r\n					if v != client.Fd {\r\n						gFd = append(gFd, v)\r\n					}\r\n				}\r\n				models.GroupFd[room] = gFd\r\n\r\n				gFd = []uint32{}\r\n				gUser = []string{}\r\n				//向组里广播该用户已经下线\r\n				for _, v := range models.GroupFd[room] {\r\n					models.Clients[v].Client.WriteMessage(websocket.TextMessage, []byte(\"system: \"+client.User+\"下线了\"))\r\n					if models.Clients[v].User != client.User {\r\n						gFd = append(gFd, v)\r\n						gUser = append(gUser, models.Clients[v].User)\r\n					}\r\n				}\r\n				//清除Client 连接\r\n				models.Clients[client.Fd].Client.Close()\r\n				//清除Clients 下这个成员的Fd 写列队互斥锁\r\n				models.RWlock.Lock()\r\n				delete(models.Clients, client.Fd)\r\n				models.RWlock.Unlock()\r\n				//组里有人下线就更新组成员\r\n				models.GroupFd[room] = gFd\r\n				models.GroupUser[room] = gUser\r\n				//然后逐个发送组成员 给组里的成员\r\n				for _, v := range models.GroupFd[room] {\r\n					tmp := map[string][]string{}\r\n					//组成员赋值给 map[\"Group\"] 发送json\r\n					tmp[\"Group\"] = models.GroupUser[room]\r\n					models.Clients[v].Client.WriteJSON(tmp)\r\n				}\r\n\r\n				return nil\r\n			})\r\n			if !webOs {\r\n				go func(fd uint32) {\r\n					breakBl := false //为true跳出循环\r\n					room := models.Clients[fd].Room\r\n					user := models.Clients[fd].User\r\n					ticker := time.NewTicker(time.Second * 20)\r\n					defer ticker.Stop()\r\n					for {\r\n						select {\r\n						case <-ticker.C:\r\n							//fmt.Println(\"每20秒发送ping\")\r\n							conn.WriteMessage(websocket.PingMessage, []byte{})\r\n						case <-pongTimeOut: //接受到pong超时chan\r\n							// 处理超时的情况\r\n							fmt.Println(\"发送 Ping 消息出错，客户端可能离线:\")\r\n							groupFdTmp := []uint32{}\r\n							for _, v := range models.GroupFd[room] {\r\n								if v != fd {\r\n									groupFdTmp = append(groupFdTmp, v)\r\n								}\r\n							}\r\n							models.GroupFd[room] = groupFdTmp\r\n							groupUserTmp := []string{}\r\n							for _, v := range models.GroupUser[room] {\r\n								if v != user {\r\n									groupUserTmp = append(groupUserTmp, v)\r\n								}\r\n							}\r\n							models.GroupUser[room] = groupUserTmp\r\n							//fmt.Println(models.GroupUser[room])\r\n							if len(models.GroupFd[room]) != 0 {\r\n								//向组里广播该用户已经下线\r\n								for _, v := range models.GroupFd[room] {\r\n									if models.Clients[v] == nil {\r\n										continue\r\n									}\r\n									//不同名 向在该组在线成员推送 上线消息\r\n									mm := make(map[string]string)\r\n									mm[\"date\"] = time.Now().Format(\"15:04:05\")\r\n									mm[\"message\"] = user + \" 下线了\"\r\n									mm[\"user\"] = \"system\"\r\n									models.RWlock.Lock()\r\n\r\n									models.Clients[v].Client.WriteJSON(mm)\r\n\r\n									models.RWlock.Unlock()\r\n									//更新组用户\r\n									tmp := map[string][]string{}\r\n									tmp[\"Group\"] = models.GroupUser[room]\r\n									models.RWlock.Lock()\r\n\r\n									models.Clients[v].Client.WriteJSON(tmp)\r\n\r\n									models.RWlock.Unlock()\r\n									if models.Clients[fd] != nil {\r\n										models.Clients[fd].Client.Close()\r\n									}\r\n\r\n									//fmt.Println(models.Clients)\r\n									//models.RWlock.Lock()\r\n									//delete(models.Clients, fd)\r\n									//models.RWlock.Unlock()\r\n									//fmt.Println(models.Clients)\r\n								}\r\n							}\r\n							breakBl = true\r\n						}\r\n						if breakBl {\r\n							fmt.Println(\"已经结束循环\")\r\n							break\r\n						}\r\n					}\r\n				}(models.Fd)\r\n				//不支持无阻塞 Pong 处理\r\n				conn.SetPongHandler(func(appData string) error {\r\n					//fmt.Println(\"单片机返回pong 消息是\" + appData)\r\n					//设置pong超时时间 因为这个只能生效一次,所以每次接受到pong都设置一次\r\n					conn.SetReadDeadline(time.Now().Add(time.Second * 50))\r\n					return nil\r\n				})\r\n\r\n			}\r\n\r\n			models.Fd++\r\n\r\n			for {\r\n				//第一个参数是数据类型,只是一个int,1代表String,2代表[]byte -1代表pong超时\r\n				//接收时是普通字符串。服务器返回给客户端是json\r\n				messageType, data, err := conn.ReadMessage()\r\n				if err != nil {\r\n					fmt.Println(\"err\")\r\n					if messageType == -1 { //pong超时\r\n						pongTimeOut <- true\r\n					}\r\n					return\r\n				}\r\n				mm := make(map[string]string)\r\n				if room == \"server\" {\r\n					mm[\"fd\"] = fmt.Sprintf(\"%d\", client.Fd)\r\n					mm[\"message\"] = string(data)\r\n					models.WebsocketMessageChan <- mm\r\n				} else {\r\n					mm[\"date\"] = time.Now().Format(\"15:04:05\")\r\n					str := string(data)\r\n					mm[\"message\"] = strings.Trim(str, \" \")\r\n					mm[\"user\"] = user\r\n					for _, v := range models.GroupFd[room] {\r\n						if client.Fd != v {\r\n							models.Clients[v].Client.WriteJSON(mm)\r\n						}\r\n					}\r\n					//admin 温湿度\r\n					if room == \"admin\" {\r\n						if strings.Contains(mm[\"message\"], \".\") && strings.Contains(mm[\"message\"], \",\") {\r\n							thStr := mm[\"message\"]\r\n							ii := strings.LastIndex(thStr, \",\")\r\n							tStr := thStr[0:ii]\r\n							hStr := thStr[ii+1:]\r\n							models.WebSystem.RequestPost(\"http://\"+models.Host+\"/wechat/door1publish\", \"msg=\"+tStr+hStr+\"&send=publish\")\r\n						}\r\n						if strings.Contains(mm[\"message\"], \"限制\") {\r\n							models.WebSystem.RequestPost(\"http://\"+models.Host+\"/wechat/door1publish\", \"msg=\"+mm[\"message\"]+\"&send=publish\")\r\n						}\r\n						if strings.Contains(mm[\"message\"], \"广告牌\") {\r\n							models.WebSystem.RequestPost(\"http://\"+models.Host+\"/wechat/door1publish\", \"msg=\"+mm[\"message\"]+\"&send=publish\")\r\n						}\r\n						if strings.Contains(mm[\"message\"], \"mydoor1.power1On\") {\r\n							msg := mm[\"message\"]\r\n							n := strings.Index(msg, \"mydoor1.power1On\")\r\n							if n != -1 {\r\n								t := msg[16:]\r\n								intt, _ := strconv.Atoi(t)\r\n								if intt != 0 {\r\n									if !Mydoor1power1On {\r\n										Mydoor1power1On = true\r\n										fmt.Println(\"定时开始\", intt)\r\n										go func() {\r\n											//定时intt 小时后关闭电源\r\n											timer := time.NewTimer(time.Duration(intt) * time.Hour)\r\n											for {\r\n												<-timer.C\r\n												timer.Stop()\r\n												Mydoor1power1On = false\r\n												models.WebsocketMessageChan <- map[string]string{\"message\": \"mydoor1.power1Off\"}\r\n												break\r\n											}\r\n										}()\r\n									}\r\n								}\r\n							}\r\n						}\r\n						if mm[\"message\"] == \"mydoor1.power1Off\" {\r\n							Mydoor1power1On = false\r\n						}\r\n					}\r\n				}\r\n			}\r\n\r\n		}\r\n	} else {\r\n		ctx.String(200, \"请求超过限流\")\r\n		return\r\n	}\r\n	ctx.String(200, \"\")\r\n}','',1),
(227,3,'gin 中间件 c.Next()原理','package main\r\n\r\nimport (\r\n	\"fmt\"\r\n\r\n	\"github.com/gin-gonic/gin\"\r\n)\r\n\r\n// 中间件函数\r\nfunc loggingMiddleware() gin.HandlerFunc {\r\n	return func(c *gin.Context) {\r\n		fmt.Println(\"loggingMiddleware start\")\r\n		c.Next()\r\n		fmt.Println(\"loggingMiddleware end\")\r\n	}\r\n}\r\n\r\nfunc main() {\r\n	r := gin.Default()\r\n	// 将中间件函数应用到需要共享处理逻辑的路由上\r\n	r.Use(loggingMiddleware())\r\n	r.GET(\"/\", func(c *gin.Context) {\r\n		fmt.Println(\"这里是c.Next()\")\r\n	})\r\n\r\n	r.Run(\":8080\")\r\n}\r\n\r\n\r\n//输出是\r\nloggingMiddleware start\r\n这里是c.Next()\r\nloggingMiddleware end','',1),
(228,4,'arduino 串口接收字符串整行输出','  if (Serial.available()) {\r\n    while (Serial.available()) {\r\n      String receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n      webSocket.sendTXT(receivedString);\r\n    }\r\n  }','',1),
(230,4,'esp01s websocket串口发送','#include <Arduino.h>\r\n#include <ESP8266WiFi.h>\r\n#include <WebSocketsClient.h>\r\n#include <ESP8266httpUpdate.h>\r\n#include <Hash.h>\r\n#include <ArduinoJson.h>\r\n\r\nWebSocketsClient webSocket;\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject jsonArr;\r\nWiFiClient wificlient;\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      Serial.printf(\"[WSc] Disconnected!\\n\");\r\n      break;\r\n    case WStype_CONNECTED:\r\n      {\r\n        Serial.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n\r\n        // send message to server when Connected\r\n        //webSocket.sendTXT(\"Connected\");\r\n      }\r\n      break;\r\n    case WStype_TEXT:\r\n      Serial.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      jsonArr = jsonBuffer.as<JsonObject>();\r\n      if (jsonArr[\"message\"] != \"\") {\r\n        String message = jsonArr[\"message\"];\r\n        if (message == \"updateBin\") {\r\n          while (1) {\r\n            ESPhttpUpdate.onStart(update_started);\r\n            ESPhttpUpdate.onEnd(update_finished);\r\n            ESPhttpUpdate.onProgress(update_progress);\r\n            ESPhttpUpdate.onError(update_error);\r\n            ESPhttpUpdate.update(wificlient, \"http://192.168.0.254/tool/ota/GolangCpp/scan.bin\");\r\n          }\r\n        }\r\n      }\r\n      // send message to server\r\n      // webSocket.sendTXT(\"message here\");\r\n      break;\r\n    case WStype_BIN:\r\n      Serial.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      hexdump(payload, length);\r\n\r\n      // send data to server\r\n      // webSocket.sendBIN(payload, length);\r\n      break;\r\n    case WStype_PING:\r\n      // pong will be send automatically\r\n      //Serial.printf(\"[WSc] get ping\\n\");\r\n      break;\r\n    case WStype_PONG:\r\n      // answer to a ping we send\r\n      Serial.printf(\"[WSc] get pong\\n\");\r\n      break;\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  // Serial.begin(921600);\r\n  Serial.begin(9600);\r\n\r\n  WiFi.begin(\"webserver\", \"www.nobiggie.cn\");\r\n\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n  }\r\n   Serial.printf(\"wifi OK\");\r\n  // server address, port and URL\r\n  webSocket.begin(\"192.168.0.254\", 80, \"/websocket\");\r\n\r\n  // event handler\r\n  webSocket.onEvent(webSocketEvent);\r\n\r\n  // use HTTP Basic Authorization this is optional remove if not needed\r\n  webSocket.setAuthorization(\"user=scan&passwd=efghijk&room=scan\");\r\n\r\n  // try ever 5000 again if connection has failed\r\n  webSocket.setReconnectInterval(5000);\r\n\r\n  // start heartbeat (optional)\r\n  // ping server every 15000 ms\r\n  // expect pong from server within 3000 ms\r\n  // consider connection disconnected if pong is not received 2 times\r\n  webSocket.enableHeartbeat(15000, 3000, 2);\r\n}\r\n\r\nvoid loop() {\r\n  webSocket.loop();\r\n  if (Serial.available()) {\r\n    while (Serial.available()) {\r\n      String receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n      webSocket.sendTXT(receivedString);\r\n    }\r\n  }\r\n}\r\nvoid update_started() {\r\n  Serial.println(\"CALLBACK:  HTTP update process started\");\r\n}\r\n\r\nvoid update_finished() {\r\n  Serial.println(\"CALLBACK:  HTTP update process finished\");\r\n}\r\nvoid update_progress(int cur, int total) {\r\n  Serial.printf(\"CALLBACK:  HTTP update process at %d of %d bytes...\\n\", cur, total);\r\n}\r\n\r\nvoid update_error(int err) {\r\n  Serial.printf(\"CALLBACK:  HTTP update fatal error code %d\\n\", err);\r\n}\r\n','',1),
(232,3,'date转time时间戳','	// 获取当前 UTC 时间保证UTC一样, 不然解析出来不准\r\n	nowP := time.Now().UTC()\r\n	timeStr := nowP.Format(\"2006-01-02 15:04:05\")\r\n	layout := \"2006-01-02 15:04:05\"\r\n\r\n	loc, _ := time.LoadLocation(\"UTC\")\r\n	t, err := time.ParseInLocation(layout, timeStr, loc)\r\n	if err != nil {\r\n		fmt.Println(\"解析时间错误:\", err)\r\n		return\r\n	}\r\n	old := t.Unix()\r\n	now := time.Now().UTC().Unix()\r\n	fmt.Println(old)\r\n	fmt.Println(now)','',1),
(233,3,'协程互斥锁','map 全局变量 如果不加锁,会产生抢夺全局变量报错\r\n\r\nvar mutex sync.Mutex //互斥锁 防止开启协程后 同时1个以上协程同时抢占全局变量报错\r\n// 互斥锁 变成非协程\r\nmutex.Lock //锁上\r\n协程都变成列队\r\nmutex.UnLock //解锁\r\n\r\nlock.Lock()\r\nfor i := 0; i < 1000; i++ {\r\n	globalVariable[\"root\"]++\r\n}\r\nlock.Unlock()\r\n上面代码等同于\r\nfor i := 0; i < 1000; i++ {\r\n	lock.Lock()\r\n	globalVariable[\"root\"]++\r\n	lock.Unlock()\r\n}\r\n\r\n\r\n//主要应用 于商品抢购 可以读并发协程，写锁列队\r\nvar mutex sync.RWMutex\r\n//RWMutex可以mutex.Lock //锁上\r\n//协程都变成列队，写时不能读\r\n//mutex.UnLock //解锁\r\nmutex.RLock()\r\n并发协程读操作 特别注意 这里不是协程都变成列队\r\n读时不能写\r\nmutex.RUnlock()\r\n\r\n','',1),
(236,1,'服务器有线固定ip设置','sudo vi /etc/dhcpcd.conf\r\ninterface eth0\r\nstatic ip_address=192.168.x.x/24\r\nstatic routers=192.168.x.x\r\nstatic domain_name_servers=192.168.x.x\r\n//如果是通过无线连接，可以将  interface eth0  改为  interface wlan0\r\n//1. 可以使用命令  sudo service dhcpcd restart  重启网络服务。\r\n2. 或者直接重启树莓派，使用命令  sudo reboot 。\r\n\r\n新版树莓派需要使用nmcli 你可以使用 nmcli con show 命令列出所有的网络连接\r\nnmcli con modify \"Wired connection 1\" ipv4.addresses 192.168.1.254/24\r\nnmcli con modify \"Wired connection 1\" ipv4.gateway 192.168.1.1\r\nnmcli con modify \"Wired connection 1\" ipv4.dns \"8.8.8.8,8.8.4.4\"\r\nnmcli con modify \"Wired connection 1\" ipv4.method manual\r\n请根据你的网络环境替换上述命令中的IP地址、网关和DNS服务器地址。如果你需要设置动态IP（即DHCP），可以将 ipv4.method 设置为 auto 而不是 manual','',1),
(237,1,'wifi连接路由器设置','一、配置无线网络\r\n使用 sudo nano /etc/wpa_supplicant/wpa_supplicant.conf 命令打开无线网络配置文件。\r\n在文件中添加以下内容：\r\n\r\nnetwork={\r\n    ssid=\"你的无线网络名称\"\r\n    psk=\"你的无线网络密码\"\r\n}\r\n\r\n二、设置网络优先级\r\n创建一个名为 /etc/network/interfaces.d/wlan0.cfg 的文件，用于配置无线网络接口：\r\n\r\nallow-hotplug wlan0\r\niface wlan0 inet dhcp\r\nwpa-conf /etc/wpa_supplicant/wpa_supplicant.conf\r\n修改 /etc/network/interfaces 文件，添加以下内容：\r\n\r\nsource-directory /etc/network/interfaces.d\r\n\r\n执行 sudo service networking restart 命令重启网络服务，使配置生效','',1),
(241,1,'docker','docker run -d --name mynginx -p 8080:80 nginx\r\ndocker exec -it mynginx /bin/bash  //进入docker内部\r\n//保存镜像\r\ndocker commit -a xyz -m \"名字\"\r\ndocker save -o mynginx.tar mynginx:v1.0\r\ndocker load -i mynginx.tar //导入镜像\r\n//挂载\r\ndocker run -d --name mynginx -p 8080:80 -v /var/www/html:/docker内部目录 nginx\r\n-v /var/www/html:/docker内部目录\r\n//分享社区\r\ndocker login\r\n\r\ndocker tag myngin:v1.0 账号/mynginx:v2.0 更改名字版本\r\n\r\n\r\n\r\n','',1),
(242,1,'无法启动无线','在终端中运行 rfkill list，查看所有被禁用的无线设备列表。如果 wlan0 被列出并显示为 Soft blocked: yes（软件禁用）或 Hard blocked: yes（硬件禁用），则需要解除禁用。\r\n sudo rfkill unblock wlan\r\n这将尝试解除对无线设备的软件禁用\r\n禁用的方法\r\n     sudo rfkill block wlan','',1),
(243,1,'关闭窗口 后台 apt install ','nohup apt install xxxx -y &\r\n使用ps aux | grep apt命令查看所有进程中包含 “apt” 关键字的进程','',1),
(244,4,'快递CAM','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <ArduinoJson.h>\r\n#include <WebSocketsClient.h>\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <HTTPUpdate.h>\r\n#include \"esp_camera.h\"\r\nString version = \"1.0\";\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject jsonArr;\r\n\r\nWebSocketsClient webSocket;\r\n\r\nWiFiClient wificlient;\r\nString localServerIP = \"192.168.0.254\";\r\nString upBinUrl = \"http://\" + localServerIP + \"/tool/ota/englishgolangcpp/dkcam.bin\";\r\n// 图像缓冲区大小\r\nconst int bufferSize = 1024 * 10;\r\nuint8_t buffer[bufferSize];\r\nString ssid = \"webserver\";                                             //wifi账号\r\nString passwd = \"www.nobiggie.cn\";                                           //wifi密码\r\n\r\nvoid hexdump(const void* mem, uint32_t len, uint8_t cols = 16) {\r\n  const uint8_t* src = (const uint8_t*)mem;\r\n  Serial.printf(\"\\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)\", (ptrdiff_t)src, len, len);\r\n  for (uint32_t i = 0; i < len; i++) {\r\n    if (i % cols == 0) {\r\n      Serial.printf(\"\\n[0x%08X] 0x%08X: \", (ptrdiff_t)src, i);\r\n    }\r\n    Serial.printf(\"%02X \", *src);\r\n    src++;\r\n  }\r\n  Serial.printf(\"\\n\");\r\n}\r\n\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      break;\r\n    case WStype_CONNECTED:\r\n      Serial.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n      break;\r\n    case WStype_TEXT:\r\n      Serial.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      jsonArr = jsonBuffer.as<JsonObject>();\r\n      if (jsonArr[\"message\"] != \"\") {\r\n        String user = jsonArr[\"user\"];\r\n        String date = jsonArr[\"date\"];\r\n        String message = jsonArr[\"message\"];\r\n        if (message == \"picture\") {\r\n          // 拍照并发送图片\r\n          camera_fb_t* fb = esp_camera_fb_get();\r\n          if (!fb) {\r\n            Serial.println(\"Failed to take picture\");\r\n            return;\r\n          }\r\n          size_t jpegSize = fb->len;\r\n          memcpy(buffer, fb->buf, jpegSize);\r\n          esp_camera_fb_return(fb);\r\n\r\n          WiFiClient client;\r\n          if (!client.connect(localServerIP.c_str(),80)) {\r\n            Serial.println(\"Connection failed\");\r\n            return;\r\n          }\r\n\r\n          // 构造 HTTP 请求头\r\n          client.printf(\"POST /dkimage HTTP/1.1\\r\\n\");\r\n          client.printf(\"Host: %s\\r\\n\", localServerIP);\r\n          client.printf(\"Content-Type: image/jpeg\\r\\n\");\r\n          client.printf(\"Content-Length: %d\\r\\n\", jpegSize);\r\n          client.printf(\"\\r\\n\");\r\n\r\n          // 发送图片数据\r\n          client.write(buffer, jpegSize);\r\n          client.flush();\r\n\r\n          Serial.println(\"Image sent\");\r\n        }\r\n        if (message == \"updateBin\") {\r\n          while (1) {\r\n            httpUpdate.onStart(update_started);\r\n            httpUpdate.onEnd(update_finished);\r\n            httpUpdate.onProgress(update_progress);\r\n            httpUpdate.onError(update_error);\r\n            t_httpUpdate_return ret = httpUpdate.update(wificlient, upBinUrl);\r\n            //t_httpUpdate_return ret = httpUpdate.update(wificlient, \"server\", 80, \"/file.bin\");\r\n            switch (ret) {\r\n              case HTTP_UPDATE_FAILED:\r\n                Serial.printf(\"HTTP_UPDATE_FAILED Error (%d): %s\\n\", httpUpdate.getLastError(), httpUpdate.getLastErrorString().c_str());\r\n                break;\r\n              case HTTP_UPDATE_NO_UPDATES:\r\n                Serial.println(\"HTTP_UPDATE_NO_UPDATES\");\r\n                break;\r\n              case HTTP_UPDATE_OK:\r\n                Serial.println(\"HTTP_UPDATE_OK\");\r\n                break;\r\n            }\r\n          }\r\n          espReboot();\r\n        }\r\n      }\r\n      // send message to server\r\n      // webSocket.sendTXT(\"message here\");\r\n      break;\r\n    case WStype_BIN:\r\n      //语音是以 []byte 传输\r\n      Serial.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      break;\r\n    case WStype_ERROR:\r\n    case WStype_FRAGMENT_TEXT_START:\r\n    case WStype_FRAGMENT_BIN_START:\r\n    case WStype_FRAGMENT:\r\n    case WStype_FRAGMENT_FIN:\r\n      break;\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  // 设置相机\r\n  camera_config_t config;\r\n  config.ledc_channel = LEDC_CHANNEL_0;\r\n  config.ledc_timer = LEDC_TIMER_0;\r\n  config.pin_d0 = 5;\r\n  config.pin_d1 = 18;\r\n  config.pin_d2 = 19;\r\n  config.pin_d3 = 21;\r\n  config.pin_d4 = 36;\r\n  config.pin_d5 = 39;\r\n  config.pin_d6 = 34;\r\n  config.pin_d7 = 35;\r\n  config.pin_xclk = 0;\r\n  config.pin_pclk = 22;\r\n  config.pin_vsync = 25;\r\n  config.pin_href = 23;\r\n  config.pin_sscb_sda = 26;\r\n  config.pin_sscb_scl = 27;\r\n  config.pin_pwdn = 32;\r\n  config.pin_reset = -1;\r\n  config.xclk_freq_hz = 20000000;\r\n  config.pixel_format = PIXFORMAT_JPEG;\r\n\r\n  // 初始化相机\r\n  esp_err_t err = esp_camera_init(&config);\r\n  if (err != ESP_OK) {\r\n    Serial.printf(\"Camera init failed with error 0x%x\", err);\r\n    return;\r\n  }\r\n  delay(300);\r\n  // 连接 Wi-Fi\r\n  WiFi.begin(\"webserver\", \"www.nobiggie.cn\");\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(1000);\r\n    Serial.println(\"Connecting to WiFi...\");\r\n  }\r\n  Serial.println(\"Connected to WiFi\");\r\n  //通知服务器 我正在重启,验证一下广告牌开关状态 10秒后我启动完成发送广告牌开关状态给我\r\n  webSocket.begin(localServerIP, 80, \"/websocket\");\r\n  webSocket.onEvent(webSocketEvent);\r\n  webSocket.setAuthorization(\"user=esp32camkd&passwd=efghijk&room=admin\");\r\n  webSocket.setReconnectInterval(5000);\r\n}\r\nvoid loop() {\r\n  webSocket.loop();\r\n}\r\n\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\n\r\nString esp32post(String url) {\r\n  HTTPClient http;\r\n  http.begin(wificlient, url);\r\n  http.setTimeout(3000);\r\n  String postStr = \"version=esp32test10\";\r\n  char dataBuff[postStr.length()];\r\n  strcpy(dataBuff, postStr.c_str());\r\n  http.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n  int httpResponseCode = http.POST(dataBuff);\r\n  if (httpResponseCode > 0) {\r\n    String response = http.getString();\r\n    return response;\r\n  }\r\n  return \"\";\r\n}\r\n\r\n\r\n\r\n//当升级开始时，打印日志\r\nvoid update_started() {\r\n  Serial.println(\"CALLBACK: HTTP update process started\");\r\n}\r\n//当升级结束时，打印日志\r\nvoid update_finished() {\r\n  Serial.println(\"CALLBACK: HTTP update process finished\");\r\n}\r\n//当升级中，打印日志\r\nvoid update_progress(int cur, int total) {\r\n  Serial.printf(\"CALLBACK: HTTP update process at %d of %d bytes...\\n\", cur, total);\r\n}\r\n//当升级失败时，打印日志\r\nvoid update_error(int err) {\r\n  Serial.printf(\"CALLBACK: HTTP update fatal error code %d\\n\", err);\r\n}\r\n','',1),
(245,4,'NodeMCU ESP32s 彩灯带变色，闪烁，亮度 ','#include <FastLED.h>\r\n\r\n#define TOUCH_PIN_BRIGHTNESS 13  // 触摸引脚13用于亮度调节\r\n#define TOUCH_PIN_FLASH 12       // 触摸引脚12用于闪烁模式切换\r\n#define TOUCH_PIN_COLOR 14       // 触摸引脚14用于彩带颜色切换\r\n#define LED_PIN 2                // LED串连接的引脚\r\n#define NUM_LEDS 30              // LED数量\r\nint mycolor = 0;\r\nCRGB leds[NUM_LEDS];      // 用于存储LED颜色数据的数组\r\nbool isFlashing = false;  // 闪烁模式标志\r\n                          //touch 最大65  手摸20以下 但是运行环境 触摸达不到20以下,20-50间,应该设置为50\r\nconst int ConstTouch = 50;\r\nvoid setup() {\r\n  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);  // 初始化LED串\r\n  pinMode(TOUCH_PIN_BRIGHTNESS, INPUT);                   // 设置触摸引脚为输入\r\n  pinMode(TOUCH_PIN_FLASH, INPUT);\r\n  pinMode(TOUCH_PIN_COLOR, INPUT);\r\n}\r\n\r\nvoid loop() {\r\n  int lhdu = touchRead(TOUCH_PIN_BRIGHTNESS);\r\n  delay(300);\r\n  // 亮度调节\r\n  if (lhdu < ConstTouch) {\r\n    if (FastLED.getBrightness() > 250) {\r\n      FastLED.setBrightness(50);\r\n    }\r\n    FastLED.setBrightness(FastLED.getBrightness() + 50);  // 增加亮度\r\n    delay(100);\r\n  }\r\n\r\n  // 闪烁模式切换\r\n  int sjuo = touchRead(TOUCH_PIN_FLASH);\r\n  delay(300);\r\n  if (sjuo < ConstTouch) {\r\n    isFlashing = !isFlashing;  // 切换闪烁模式状态\r\n    delay(100);\r\n  }\r\n\r\n  // 彩带颜色切换\r\n  int bmse = touchRead(TOUCH_PIN_COLOR);\r\n  delay(300);\r\n  if (bmse < ConstTouch) {\r\n    mycolor++;\r\n    if (mycolor > 2) {\r\n      mycolor = 0;\r\n    }\r\n    if (mycolor == 0) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(0, 255, 255)));  // 红色\r\n    }\r\n    if (mycolor == 1) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(85, 255, 255)));\r\n    }\r\n    if (mycolor == 2) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(170, 255, 255)));\r\n    }\r\n    // 这里可以添加切换不同颜色的逻辑\r\n    delay(100);\r\n  }\r\n\r\n  if (isFlashing) {\r\n    static bool state = false;\r\n    state = !state;\r\n    fill_solid(leds, NUM_LEDS, state ? CRGB::Black : CRGB(CHSV(100, 255, 255)));  // 闪烁效果\r\n    if (mycolor == 0) {\r\n      fill_solid(leds, NUM_LEDS, state ? CRGB::Black : CRGB(CHSV(0, 255, 255)));\r\n    }\r\n    if (mycolor == 1) {\r\n      fill_solid(leds, NUM_LEDS, state ? CRGB::Black : CRGB(CHSV(85, 255, 255)));\r\n    }\r\n    if (mycolor == 2) {\r\n      fill_solid(leds, NUM_LEDS, state ? CRGB::Black : CRGB(CHSV(170, 255, 255)));\r\n    }\r\n  } else {\r\n    // 常亮颜色\r\n    if (mycolor == 0) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(0, 255, 255)));  // 红色\r\n    }\r\n    if (mycolor == 1) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(85, 255, 255)));\r\n    }\r\n    if (mycolor == 2) {\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(170, 255, 255)));\r\n    }\r\n  }\r\n\r\n  FastLED.show();  // 显示当前颜色\r\n  delay(10);       // 延时以避免CPU过载\r\n}','',1),
(246,4,'esp32cam','开发板选用 ESP32 Wrover Module\r\n示例代码 -> ESP32 -> Camera -> WebServer\r\n\r\n注释 #define CAMERA_MODEL_ESP_EYE\r\n取消注释 #define CAMERA_MODEL_AI_THINKER\r\n111行改成 if (s->id.PID == OV2640_PID) {','',1),
(247,4,'esp32cam访问IP返回抓拍图片','#include \"esp_camera.h\"\r\n#include <WiFi.h>\r\n\r\n// Wi-Fi 连接信息\r\nconst char *ssid = \"webserver\";\r\nconst char *password = \"www.nobiggie.cn\";\r\n\r\n// 定义摄像头模块\r\n#define CAMERA_MODEL_AI_THINKER\r\n\r\n// 摄像头引脚定义\r\n#if defined(CAMERA_MODEL_AI_THINKER)\r\n#define PWDN_GPIO_NUM 32\r\n#define RESET_GPIO_NUM -1\r\n#define XCLK_GPIO_NUM 0\r\n#define SIOD_GPIO_NUM 26\r\n#define SIOC_GPIO_NUM 27\r\n#define Y9_GPIO_NUM 35\r\n#define Y8_GPIO_NUM 34\r\n#define Y7_GPIO_NUM 39\r\n#define Y6_GPIO_NUM 36\r\n#define Y5_GPIO_NUM 21\r\n#define Y4_GPIO_NUM 19\r\n#define Y3_GPIO_NUM 18\r\n#define Y2_GPIO_NUM 5\r\n#define VSYNC_GPIO_NUM 25\r\n#define HREF_GPIO_NUM 23\r\n#define PCLK_GPIO_NUM 22\r\n#endif\r\n\r\n// 摄像头配置\r\ncamera_config_t camera_config = {\r\n   .pin_pwdn = PWDN_GPIO_NUM,\r\n   .pin_reset = RESET_GPIO_NUM,\r\n   .pin_xclk = XCLK_GPIO_NUM,\r\n   .pin_sscb_sda = SIOD_GPIO_NUM,\r\n   .pin_sscb_scl = SIOC_GPIO_NUM,\r\n   .pin_d7 = Y9_GPIO_NUM,\r\n   .pin_d6 = Y8_GPIO_NUM,\r\n   .pin_d5 = Y7_GPIO_NUM,\r\n   .pin_d4 = Y6_GPIO_NUM,\r\n   .pin_d3 = Y5_GPIO_NUM,\r\n   .pin_d2 = Y4_GPIO_NUM,\r\n   .pin_d1 = Y3_GPIO_NUM,\r\n   .pin_d0 = Y2_GPIO_NUM,\r\n   .pin_vsync = VSYNC_GPIO_NUM,\r\n   .pin_href = HREF_GPIO_NUM,\r\n   .pin_pclk = PCLK_GPIO_NUM,\r\n   .xclk_freq_hz = 20000000,\r\n   .ledc_timer = LEDC_TIMER_0,\r\n   .ledc_channel = LEDC_CHANNEL_0,\r\n   .pixel_format = PIXFORMAT_JPEG,\r\n   .frame_size =FRAMESIZE_UXGA,//FRAMESIZE_VAG,\r\n   .jpeg_quality = 12,\r\n   .fb_count = 1\r\n};\r\n\r\nvoid startCameraServer() {\r\n    WiFiServer server(80);\r\n    WiFi.begin(ssid, password);\r\n\r\n    while (WiFi.status()!= WL_CONNECTED) {\r\n        delay(1000);\r\n        Serial.println(\"Connecting to WiFi...\");\r\n    }\r\n    Serial.println(\"Connected to WiFi\");\r\n    Serial.println(WiFi.localIP());\r\n\r\n    server.begin();\r\n\r\n    while (true) {\r\n        WiFiClient client = server.available();\r\n        if (client) {\r\n            Serial.println(\"New client connected\");\r\n            String currentLine = \"\";\r\n            while (client.connected()) {\r\n                if (client.available()) {\r\n                    char c = client.read();\r\n                    Serial.write(c);\r\n                    if (c == \'\\n\') {\r\n                        if (currentLine.length() == 0) {\r\n                            // 发送 HTTP 响应头\r\n                            client.println(\"HTTP/1.1 200 OK\");\r\n                            client.println(\"Content-Type: image/jpeg\");\r\n                            client.println(\"Connection: close\");\r\n                            client.println();\r\n\r\n                            // 获取摄像头图像并发送\r\n                            camera_fb_t *fb = esp_camera_fb_get();\r\n                            esp_camera_fb_return(fb); //清空一下，防止下次还是这个图片\r\n                            fb = esp_camera_fb_get();\r\n                            if (fb) {\r\n                                client.write(fb->buf, fb->len);\r\n                                esp_camera_fb_return(fb);\r\n                            }\r\n                            break;\r\n                        } else {\r\n                            currentLine = \"\";\r\n                        }\r\n                    } else if (c!= \'\\r\') {\r\n                        currentLine += c;\r\n                    }\r\n                }\r\n            }\r\n            client.stop();\r\n            Serial.println(\"Client disconnected\");\r\n        }\r\n    }\r\n}\r\n\r\nvoid setup() {\r\n    Serial.begin(115200);\r\n    // 初始化摄像头\r\n    esp_err_t err = esp_camera_init(&camera_config);\r\n    if (err!= ESP_OK) {\r\n        Serial.println(\"Camera init failed\");\r\n        return;\r\n    }\r\n    Serial.println(\"Camera init ok\");\r\n    startCameraServer();\r\n}\r\n\r\nvoid loop() {\r\n    // 无需在 loop 中执行其他操作，因为服务器在 setup 中的 while (true) 循环中持续运行\r\n}\r\n','',1),
(248,3,'微信机器人','package models\r\n\r\nimport (\r\n	\"fmt\"\r\n	\"io\"\r\n	\"log\"\r\n	\"net/http\"\r\n	\"net/url\"\r\n	\"os\"\r\n	\"regexp\"\r\n	\"strconv\"\r\n	\"strings\"\r\n\r\n	\"github.com/eatmoreapple/openwechat\"\r\n	\"github.com/joho/godotenv\"\r\n	\"github.com/tidwall/gjson\"\r\n)\r\n\r\ntype OpenChatZ struct {\r\n	ExceArr  map[int]string\r\n	Self     *openwechat.Self\r\n	Groups   openwechat.Groups\r\n	KdNumber int\r\n}\r\n\r\nfunc (ggg *OpenChatZ) Setup() *OpenChatZ {\r\n	ggg.ExceArr = map[int]string{\r\n		0: \"查物流\",\r\n		1: \"拦截\",\r\n		2: \"改地址\",\r\n		3: \"查重\",\r\n		4: \"催件\",\r\n		5: \"核实签收\",\r\n		6: \"破损\",\r\n		7: \"遗失\",\r\n	}\r\n	return ggg\r\n\r\n}\r\nfunc (ggg *OpenChatZ) hasThirteenConsecutiveDigits(s string) bool {\r\n	// 定义匹配连续13位数字的正则表达式\r\n	pattern := regexp.MustCompile(`\\d{13}`)\r\n	return pattern.MatchString(s)\r\n}\r\n\r\n/*\r\n*\r\n去除html标签，过滤html标签\r\n*/\r\nfunc (ggg *OpenChatZ) TrimHtml(src string) string {\r\n	//将HTML标签全转换成小写\r\n	re, _ := regexp.Compile(\"\\\\<[\\\\S\\\\s]+?\\\\>\")\r\n	src = re.ReplaceAllStringFunc(src, strings.ToLower)\r\n	//去除STYLE\r\n	re, _ = regexp.Compile(\"\\\\<style[\\\\S\\\\s]+?\\\\</style\\\\>\")\r\n	src = re.ReplaceAllString(src, \"\")\r\n	//去除SCRIPT\r\n	re, _ = regexp.Compile(\"\\\\<script[\\\\S\\\\s]+?\\\\</script\\\\>\")\r\n	src = re.ReplaceAllString(src, \"\")\r\n	//去除所有尖括号内的HTML代码，并换成换行符\r\n	re, _ = regexp.Compile(\"\\\\<[\\\\S\\\\s]+?\\\\>\")\r\n	src = re.ReplaceAllString(src, \"\\n\")\r\n	//去除连续的换行符\r\n	re, _ = regexp.Compile(\"\\\\s{2,}\")\r\n	src = re.ReplaceAllString(src, \"\\n\")\r\n	return strings.TrimSpace(src)\r\n}\r\n\r\n// 发送http post请求数据为form\r\nfunc (ggg *OpenChatZ) PostForm(url string, data url.Values) (string, error) {\r\n	resp, err := http.PostForm(url, data)\r\n	if err != nil {\r\n		return \"\", err\r\n	}\r\n	defer resp.Body.Close()\r\n	content, err := io.ReadAll(resp.Body)\r\n	if err != nil {\r\n		return \"\", err\r\n	}\r\n	return string(content), nil\r\n}\r\nfunc (ggg *OpenChatZ) SendTextGroupZ(groupName, msg string) {\r\n	// 遍历群组，找到目标群并发送消息\r\n	for _, group := range ggg.Groups {\r\n		fmt.Println(group.NickName)\r\n		if group.NickName == groupName {\r\n			// 发送文本消息\r\n			_, err := ggg.Self.SendTextToGroup(group, msg)\r\n			if err != nil {\r\n				fmt.Println(\"发送消息失败：\", err)\r\n			}\r\n		}\r\n	}\r\n}\r\nfunc (ggg *OpenChatZ) Start() {\r\n\r\n	err := godotenv.Load(\".env\")\r\n	myUID := \"\"\r\n	myNicname := \"\"\r\n\r\n	// 读取环境变量\r\n	api := os.Getenv(\"API_BASE\")\r\n	entId := os.Getenv(\"API_ENT_ID\")\r\n	replyType := os.Getenv(\"REPLY_TYPE\")\r\n	bot := openwechat.DefaultBot(openwechat.Desktop) // 桌面模式\r\n\r\n	// 注册消息处理函数\r\n	bot.MessageHandler = func(msg *openwechat.Message) {\r\n\r\n		if msg.IsText() {\r\n\r\n			log.Printf(\"收到消息：%s\\n\", msg.Content)\r\n\r\n			isSend := false\r\n			//群聊\r\n			if msg.IsSendByGroup() {\r\n				sender, err := msg.SenderInGroup()\r\n				if err == nil {\r\n					log.Println(\"消息发送者：\" + sender.NickName)\r\n				}\r\n				if strings.Contains(msg.Content, \"@\"+myNicname) {\r\n					msg.Content = strings.TrimSpace(strings.ReplaceAll(msg.Content, \"@\"+myNicname, \"\"))\r\n					isSend = true\r\n				} else {\r\n					isSend = false\r\n				}\r\n			} else {\r\n				//单聊\r\n				sender, err := msg.Sender()\r\n				if err == nil {\r\n					log.Println(\"消息发送者：\" + sender.NickName)\r\n				}\r\n				if replyType != \"GROUP\" {\r\n					isSend = true\r\n				}\r\n			}\r\n			//字符串装是否含有13位连续的数字\r\n			if ggg.hasThirteenConsecutiveDigits(msg.Content[0:13]) {\r\n				//如果输入单号错误为14位数字也不会触发\r\n				_, errInt := strconv.Atoi(msg.Content[13:14])\r\n				if errInt != nil {\r\n					tmpNumber := msg.Content[0:13]\r\n					tmpKdNumber, _ := strconv.Atoi(tmpNumber)\r\n					ggg.KdNumber = tmpKdNumber\r\n					selectTmp := []rune(msg.Content)\r\n					selectStr := string(selectTmp[0:22])\r\n					for _, v := range ggg.ExceArr {\r\n						if strings.Contains(selectStr, v) {\r\n							if v == \"查物流\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"拦截\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"改地址\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"查重\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"催件\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"核实签收\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"破损\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n							if v == \"遗失\" {\r\n								// 遍历群组，找到目标群并发送消息\r\n								ggg.SendTextGroupZ(\"群快递机器人助手研发\", \"接收到\"+tmpNumber+\" \"+v+\" 指令,准备执行命令\")\r\n								break\r\n							}\r\n\r\n						}\r\n					}\r\n				}\r\n			}\r\n			if isSend {\r\n				data := url.Values{}\r\n				data.Set(\"ent_id\", entId)\r\n				data.Set(\"visitor_id\", msg.MsgId)\r\n				data.Set(\"content\", msg.Content)\r\n				res, err := ggg.PostForm(api, data)\r\n				if err == nil {\r\n					r := gjson.Get(res, \"result.content\").String()\r\n					r = ggg.TrimHtml(r)\r\n					msg.ReplyText(r)\r\n				}\r\n			}\r\n\r\n		}\r\n	}\r\n	// 注册登陆二维码回调\r\n	bot.UUIDCallback = openwechat.PrintlnQrcodeUrl\r\n\r\n	// 登陆\r\n	if err := bot.Login(); err != nil {\r\n		fmt.Println(err)\r\n		return\r\n	}\r\n\r\n	// 获取登陆的用户\r\n	selfs, err := bot.GetCurrentUser()\r\n	if err != nil {\r\n		fmt.Println(err)\r\n		return\r\n	}\r\n	ggg.Self = selfs\r\n	// 获取所有的群组\r\n\r\n	group, err := ggg.Self.Groups()\r\n	fmt.Println(group.Count())\r\n	if err != nil {\r\n		fmt.Println(err)\r\n		return\r\n	}\r\n	ggg.Groups = group\r\n	// 遍历群组列表并打印群组名称和ID\r\n	for _, vv := range ggg.Groups {\r\n		fmt.Printf(\"群组名称: %s, 群组ID: %d\\n\", vv.NickName, vv.ID)\r\n	}\r\n\r\n	myUID = string(ggg.Self.ID())\r\n	myNicname = ggg.Self.NickName\r\n	log.Printf(\"当前登录用户：%s,uid：%s \\n\", myNicname, myUID)\r\n	// 获取所有的好友\r\n	//friends, err := self.Friends()\r\n	//fmt.Println(friends, err)\r\n\r\n	// 获取所有的群组\r\n	//groups, err := self.Groups()\r\n	//fmt.Println(groups, err)\r\n\r\n	// 阻塞主goroutine, 直到发生异常或者用户主动退出\r\n	bot.Block()\r\n\r\n}\r\n','',1),
(249,4,'180旋转超声波雷达','#include <Servo.h>\r\n#include <SPI.h>\r\n#include \"Ucglib.h\"\r\n\r\n#define trigPin 6\r\n#define echoPin 5\r\n#define ServoPin 3\r\nint Ymax = 240;\r\nint Xmax = 320;\r\nint D2 = 2;\r\nint Xcent = Xmax / 2;\r\nint base = 210;\r\nint scanline = 185;\r\n\r\nServo baseServo;\r\n//Ucglib_ILI9341_18x240x320_SWSPI ucg(/*sclk=*/ 13, /*data=*/ 11, /*cd=*/ 9, /*cs=*/ 10, /*reset=*/ 8);\r\nUcglib_ILI9341_18x240x320_HWSPI ucg(/*cd=*/9, /*cs=*/10, /*reset=*/8);\r\n\r\nvoid setup(void) {\r\n  ucg.begin(UCG_FONT_MODE_SOLID);\r\n  ucg.setRotate90();\r\n\r\n  pinMode(trigPin, OUTPUT);\r\n  pinMode(echoPin, INPUT);\r\n  Serial.begin(115200);\r\n  pinMode(D2, OUTPUT);\r\n  digitalWrite(D2, LOW);\r\n  baseServo.attach(ServoPin);\r\n\r\n\r\n  ucg.setFontMode(UCG_FONT_MODE_TRANSPARENT);\r\n  ucg.setColor(0, 0, 0, 0);\r\n  ucg.setColor(1, 0, 0, 0);\r\n  ucg.setColor(2, 0, 0, 0);\r\n  ucg.setColor(3, 0, 0, 0);\r\n  ucg.drawGradientBox(0, 0, 320, 240);\r\n  ucg.setPrintDir(0);\r\n  ucg.setColor(0, 5, 0);\r\n  ucg.setPrintPos(70, 120);\r\n  ucg.setFont(ucg_font_logisoso32_tf);\r\n  ucg.print(\"Mini Radar\");\r\n  ucg.setColor(0, 255, 0);\r\n  ucg.setPrintPos(70, 120);\r\n  ucg.print(\"Mini Radar\");\r\n  ucg.setFont(ucg_font_courB14_tf);\r\n  ucg.setColor(20, 255, 20);\r\n  ucg.setPrintPos(90, 200);\r\n  ucg.print(\"Testing...\");\r\n  baseServo.write(90);\r\n\r\n\r\n  for (int x = 0; x < 180; x += 5) {\r\n    baseServo.write(x);\r\n    delay(50);\r\n  }\r\n  ucg.print(\"OK!\");\r\n  delay(500);\r\n  ucg.setColor(0, 0, 0, 0);\r\n  ucg.setColor(1, 0, 0, 0);\r\n  ucg.setColor(2, 0, 0, 0);\r\n  ucg.setColor(3, 0, 0, 0);\r\n  ucg.drawGradientBox(0, 0, 320, 240);\r\n  delay(10);\r\n\r\n\r\n  //ucg.clearScreen();\r\n  cls();\r\n  ucg.setFontMode(UCG_FONT_MODE_SOLID);\r\n  ucg.setFont(ucg_font_helvR08_hr);  // or freedoomr10_tr\r\n}\r\n\r\nvoid cls() {\r\n\r\n  ucg.setColor(0, 0, 0, 0);\r\n  for (int s = 0; s < 240; s++) {\r\n    ucg.drawHLine(0, s, 320);\r\n    delay(1);\r\n  }\r\n\r\n  //ucg.drawBox(0, 0, 160, 60);\r\n}\r\n\r\nint calculateDistance() {\r\n  long duration;\r\n\r\n  digitalWrite(trigPin, LOW);\r\n  delayMicroseconds(2);\r\n\r\n  digitalWrite(trigPin, HIGH);\r\n  delayMicroseconds(10);\r\n  digitalWrite(trigPin, LOW);\r\n\r\n  duration = pulseIn(echoPin, HIGH);\r\n\r\n  return duration * 0.034 / 2;\r\n}\r\n\r\nvoid fix_font() {\r\n  ucg.setColor(0, 180, 0);\r\n  ucg.setPrintPos(144, 44);\r\n  ucg.print(\"1.00\");\r\n  ucg.setPrintPos(144, 100);\r\n  ucg.print(\"0.60\");\r\n  ucg.setPrintPos(144, 165);\r\n  ucg.print(\"0.30\");\r\n}\r\n\r\nvoid fix() {\r\n  ucg.setColor(0, 180, 0);\r\n\r\n  ucg.drawDisc(Xcent, base + 1, 3, UCG_DRAW_ALL);\r\n  ucg.drawCircle(Xcent, base + 1, 210, UCG_DRAW_UPPER_LEFT);\r\n  ucg.drawCircle(Xcent, base + 1, 210, UCG_DRAW_UPPER_RIGHT);\r\n  ucg.drawCircle(Xcent, base + 1, 135, UCG_DRAW_UPPER_LEFT);\r\n  ucg.drawCircle(Xcent, base + 1, 135, UCG_DRAW_UPPER_RIGHT);\r\n  ucg.drawCircle(Xcent, base + 1, 70, UCG_DRAW_UPPER_LEFT);\r\n  ucg.drawCircle(Xcent, base + 1, 70, UCG_DRAW_UPPER_RIGHT);\r\n  ucg.drawLine(0, base + 1, Xmax, base + 1);\r\n\r\n  ucg.setColor(0, 180, 0);\r\n\r\n  for (int i = 40; i < 300; i += 2) {\r\n    if (i % 10 == 0)\r\n      ucg.drawLine(185 * cos(radians(i)) + Xcent, base - 185 * sin(radians(i)), 205 * cos(radians(i)) + Xcent, base - 205 * sin(radians(i)));\r\n    else\r\n      ucg.drawLine(195 * cos(radians(i)) + Xcent, base - 195 * sin(radians(i)), 205 * cos(radians(i)) + Xcent, base - 205 * sin(radians(i)));\r\n  }\r\n\r\n  ucg.setColor(0, 200, 0);\r\n  ucg.drawLine(0, 0, 0, 36);\r\n  for (int i = 0; i < 5; i++) {\r\n    ucg.setColor(0, random(200) + 50, 0);\r\n    ucg.drawBox(2, i * 8, random(28) + 3, 6);\r\n  }\r\n\r\n  ucg.setColor(0, 180, 0);\r\n  ucg.drawFrame(292, 0, 28, 28);\r\n  ucg.setColor(0, 60, 0);\r\n  ucg.drawHLine(296, 0, 20);\r\n  ucg.drawVLine(292, 4, 20);\r\n  ucg.drawHLine(296, 52, 20);\r\n  ucg.drawVLine(318, 4, 20);\r\n\r\n  ucg.setColor(0, 220, 0);\r\n  ucg.drawBox(296, 4, 8, 8);\r\n  ucg.drawBox(296, 16, 8, 8);\r\n  ucg.drawBox(308, 16, 8, 8);\r\n  ucg.setColor(0, 100, 0);\r\n  ucg.drawBox(308, 4, 8, 8);\r\n\r\n  ucg.setColor(0, 90, 0);\r\n  ucg.drawTetragon(124, 220, 116, 230, 196, 230, 204, 220);\r\n  ucg.setColor(0, 160, 0);\r\n  ucg.drawTetragon(134, 220, 126, 230, 186, 230, 194, 220);\r\n  ucg.setColor(0, 210, 0);\r\n  ucg.drawTetragon(144, 220, 136, 230, 176, 230, 184, 220);\r\n}\r\n\r\nvoid loop(void) {\r\n\r\n  int distance;\r\n\r\n  fix();\r\n  fix_font();\r\n\r\n  for (int x = 180; x > 4; x -= 2) {\r\n\r\n    baseServo.write(x);\r\n\r\n\r\n    int f = x - 4;\r\n    ucg.setColor(0, 255, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    f += 2;\r\n    ucg.setColor(0, 128, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    f += 2;\r\n    ucg.setColor(0, 0, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    ucg.setColor(0, 200, 0);\r\n\r\n    distance = calculateDistance();\r\n\r\n\r\n    if (distance < 100) {\r\n      if (distance < 30) {\r\n        digitalWrite(D2, HIGH);\r\n      } else {\r\n        digitalWrite(D2, LOW);\r\n      }\r\n      ucg.setColor(255, 255, 0);\r\n      ucg.drawDisc(2.2 * distance * cos(radians(x)) + Xcent, -2.2 * distance * sin(radians(x)) + base, 1, UCG_DRAW_ALL);\r\n    } else {\r\n      digitalWrite(D2, LOW);\r\n      ucg.setColor(255, 255, 0);\r\n      ucg.drawDisc(208 * cos(radians(x)) + Xcent, -208 * sin(radians(x)) + base, 1, UCG_DRAW_ALL);\r\n    }\r\n\r\n\r\n\r\n    Serial.print(x);\r\n    Serial.print(\"    ,   \");\r\n    Serial.println(distance);\r\n\r\n\r\n    if (x > 70 and x < 110) fix_font();\r\n\r\n\r\n    ucg.setColor(255, 255, 0);\r\n    ucg.setPrintPos(20, 230);\r\n    ucg.print(\"DEG: \");\r\n    ucg.setPrintPos(54, 230);\r\n    ucg.print(x);\r\n    ucg.print(\"  \");\r\n    ucg.setPrintPos(240, 230);\r\n    ucg.print(\"     \");\r\n    ucg.print(distance);\r\n    ucg.print(\" cm    \");\r\n  }\r\n  //ucg.clearScreen();\r\n  delay(50);\r\n  cls();\r\n\r\n  fix();\r\n  fix_font();\r\n\r\n  for (int x = 1; x < 176; x += 2) {\r\n    baseServo.write(x);\r\n\r\n\r\n    int f = x + 4;\r\n    ucg.setColor(0, 255, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    f -= 2;\r\n    ucg.setColor(0, 128, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    f -= 2;\r\n    ucg.setColor(0, 0, 0);\r\n    ucg.drawLine(Xcent, base, scanline * cos(radians(f)) + Xcent, base - scanline * sin(radians(f)));\r\n    ucg.setColor(0, 200, 0);\r\n\r\n    distance = calculateDistance();\r\n\r\n\r\n    if (distance < 100) {\r\n      if (distance < 30) {\r\n        digitalWrite(D2, HIGH);\r\n      } else {\r\n        digitalWrite(D2, LOW);\r\n      }\r\n      ucg.setColor(255, 255, 0);\r\n      ucg.drawDisc(2.2 * distance * cos(radians(x)) + Xcent, -2.2 * distance * sin(radians(x)) + base, 1, UCG_DRAW_ALL);\r\n    } else {\r\n      digitalWrite(D2, LOW);\r\n      ucg.setColor(255, 255, 0);\r\n      ucg.drawDisc(208 * cos(radians(x)) + Xcent, -208 * sin(radians(x)) + base, 1, UCG_DRAW_ALL);\r\n    }\r\n\r\n\r\n    Serial.print(x);\r\n    Serial.print(\"    ,   \");\r\n    Serial.println(distance);\r\n\r\n    if (x > 70 and x < 110) fix_font();\r\n\r\n    ucg.setColor(255, 255, 0);\r\n    ucg.setPrintPos(20, 230);\r\n    ucg.print(\"DEG: \");\r\n    ucg.setPrintPos(54, 230);\r\n    ucg.print(x);\r\n    ucg.print(\"  \");\r\n    ucg.setPrintPos(240, 230);\r\n    ucg.print(\"     \");\r\n    ucg.print(distance);\r\n    ucg.print(\" cm    \");\r\n  }\r\n  //ucg.clearScreen(); //\r\n  delay(50);\r\n  cls();\r\n}','',1),
(250,1,'设置有线固定ip','nano /etc/rc.local\r\n在文件中的exit 0之前添加如下内容\r\nifconfig eth0 192.168.x.xxx  # 将xxx更改为你想要设置的IP地址','',1),
(251,3,'微信公从号gh_c4117ba50e6a','微信公从号gh_c4117ba50e6a','',1),
(252,1,'使用树莓派做WIFI路由器','机身温度会增高3度\r\n','',1),
(253,4,'arduino Uno TFT屏','#include <TFT.h>  // Arduino LCD library\r\n#include <SPI.h>\r\n#include <OneWire.h>\r\n#include <DallasTemperature.h>\r\n#include <Adafruit_Sensor.h>\r\n#include <DHT.h>\r\n#include <DHT_U.h>\r\n\r\n#define DHTPIN 7       // 数字引脚连接DHT传感器\r\n#define DHTTYPE DHT11  // DHT 11\r\nDHT_Unified dht(DHTPIN, DHTTYPE);\r\nfloat humidity;\r\nfloat temperature;\r\n\r\n#define cs 10\r\n#define dc 9\r\n#define rst 8\r\n\r\n// TFT屏SDA  接11\r\n// TFT屏SCL  接13\r\n// TFT屏VDD  接5V\r\n// TFT屏GND  接GND\r\n\r\n// 创建实例\r\nTFT TFTscreen = TFT(cs, dc, rst);\r\n\r\n// char array to print to the screen\r\nchar sensorPrintout[4];\r\nint D2 = 2;               //蜂鸣器\r\nint D4 = 4;               // 遥控LED灯\r\nint flameSensorPin = 12;  //火焰传感器\r\nbool firealarm = false;   //为false 代表火警关闭, 否知 代表火警响了\r\nString oldT = \"\";\r\nString oldH = \"\";\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  pinMode(D2, OUTPUT);\r\n  pinMode(D4, OUTPUT);\r\n  digitalWrite(D2, LOW);\r\n  digitalWrite(D4, LOW);\r\n  pinMode(flameSensorPin, INPUT);\r\n  // Put this line at the beginning of every sketch that uses the GLCD:\r\n  TFTscreen.begin();\r\n\r\n  //清除黑色背景的屏幕\r\n  TFTscreen.background(0, 0, 0);\r\n\r\n  // 将静态文本写入屏幕\r\n  // 设置字体颜色为白色\r\n  TFTscreen.stroke(255, 255, 255);\r\n  // 设置字体大小\r\n  TFTscreen.setTextSize(1.5);\r\n  TFTscreen.text(\"temperature:\\n \", 5, 25);\r\n  TFTscreen.text(\"humidity:\\n \", 20, 50);\r\n  TFTscreen.setTextSize(1.9);\r\n  dht.begin();\r\n  sensor_t sensor;\r\n  dht.temperature().getSensor(&sensor);\r\n  dht.humidity().getSensor(&sensor);\r\n}\r\n\r\nvoid loop() {\r\n  sensors_event_t event;\r\n  dht.temperature().getEvent(&event);\r\n  if (isnan(event.temperature)) {\r\n    Serial.println(F(\"Error reading temperature!\"));\r\n  } else {\r\n    temperature = event.temperature;\r\n  }\r\n  delay(500);\r\n  // Get humidity event and print its value.\r\n  dht.humidity().getEvent(&event);\r\n  if (isnan(event.relative_humidity)) {\r\n    Serial.println(F(\"Error reading humidity!\"));\r\n  } else {\r\n    humidity = event.relative_humidity;\r\n  }\r\n  // 读取火焰传感器的值，其返回值范围是0-1023（模拟输入值）\r\n\r\n\r\n  // 设置字体颜色\r\n  TFTscreen.stroke(255, 255, 255);\r\n  // print the sensor value\r\n  char tchar[20];\r\n  char hchar[20];\r\n  dtostrf(temperature, 4, 2, tchar);\r\n  dtostrf(humidity, 4, 0, hchar);\r\n  strcat(tchar, \" C\");\r\n  strcat(hchar, \" %\");\r\n  String tstr=String(tchar);\r\n  tstr.trim();\r\n  String hstr=String(hchar);\r\n  hstr.trim();\r\n  if (oldT != tstr || oldH != hstr) {  //如果 旧的 温度 湿度 有变化 就屏幕更新\r\n    // 把你刚写的文字擦掉\r\n    TFTscreen.stroke(0, 0, 0);\r\n    TFTscreen.text(oldT.c_str(), 90, 25);\r\n    TFTscreen.text(oldH.c_str(), 80, 50);\r\n    oldT = tstr;\r\n    oldH = hstr;\r\n    String tempT = oldT.substring(0, oldT.length() - 2);\r\n    String tempH = oldH.substring(0, oldH.length() - 2);\r\n    String result=tempT+\"&\"+tempH;\r\n    Serial.println(result);\r\n    Serial.write(result.c_str(), 8);  //串口输出\r\n    TFTscreen.stroke(255, 255, 255);\r\n    TFTscreen.text(tstr.c_str(), 90, 25);\r\n    TFTscreen.text(hstr.c_str(), 80, 50);\r\n  }\r\n\r\n  if (digitalRead(flameSensorPin) == LOW) {  // 如果检测到火焰，点亮LED灯\r\n    if (!firealarm) {       //如果上次是火警关闭状态\r\n      firealarm = true;\r\n      Serial.write(\"Fire alarm\", 10);  //串口输出\r\n      TFTscreen.text(\"Fire alarm\", 50, 80);\r\n      digitalWrite(D2, HIGH);  //蜂鸣器响\r\n    }\r\n  } else {\r\n    if (firealarm) {  //如果上次是火警响了,就把屏幕提示关闭\r\n      firealarm = false;\r\n      Serial.write(\"Fire stop\", 9);  //串口输出\r\n      TFTscreen.stroke(0, 0, 0);\r\n      TFTscreen.text(\"Fire alarm\", 50, 80);\r\n      // 如果没检测到火焰，熄灭LED灯\r\n      digitalWrite(D2, LOW);  //蜂鸣器关\r\n    }\r\n  }\r\n\r\n  if (Serial.available() > 0) {  //获取串口消息\r\n    String receivedString = \"\";\r\n    while (Serial.available()) {\r\n      receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n    }\r\n\r\n    Serial.println(receivedString);\r\n    // 在这里可以对读取到的数据进行处理\r\n    if (receivedString == \"on\") {          //串口接收到on\r\n      digitalWrite(D4, HIGH);              //LED 点亮\r\n    } else if (receivedString == \"off\") {  //串口接收到off\r\n      digitalWrite(D4, LOW);               //LED 熄灭\r\n    }\r\n  }\r\n}\r\n','',1),
(254,4,'esp01s 串口消息通过MQTT转发','#include <ESP8266WiFi.h>\r\n#include <ESP8266WebServer.h>\r\n#include <ESP8266HTTPClient.h>\r\n#include <Ticker.h>\r\n#include <PubSubClient.h>\r\n\r\nTicker myTicker;\r\nWiFiClient wificlient;\r\n\r\n//被读取的文件位置和名称\r\nString flash_wifi = \"/wifi.txt\";\r\nESP8266WebServer ESP8266webServer;  // 创建网络服务器对象,HTTP服务的标准端口为：80\r\nString ssid = \"\";\r\nString passwd = \"\";\r\nString html;\r\n\r\nconst char* mqtt_server = \"bemfa.com\";\r\nPubSubClient client(wificlient);\r\n//unsigned long lastMsg = 0;\r\n//#define MSG_BUFFER_SIZE (50)\r\n//char msg[MSG_BUFFER_SIZE];\r\nint value = 0;\r\nvoid setup(void) {\r\n  Serial.begin(115200);\r\n  delay(1000);\r\n  if (FlashConnetWifi() == false) {\r\n    HtmlSetWifi();\r\n  }\r\n  client.setServer(mqtt_server, 9501);\r\n  client.setCallback(callback);\r\n}\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n  if (Serial.available()) {\r\n    while (Serial.available()) {\r\n      String receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n      client.publish(\"esp8266outTopic\", receivedString.c_str()); //mqtt发送\r\n    }\r\n  }\r\n  // unsigned long now = millis();\r\n  // if (now - lastMsg > 2000) {\r\n  //   lastMsg = now;\r\n  //   ++value;\r\n  //   snprintf(msg, MSG_BUFFER_SIZE, \"hello world #%ld\", value);\r\n  //   Serial.print(\"Publish message: \");\r\n  //   Serial.println(msg);\r\n  //   client.publish(\"esp8266outTopic\", msg);\r\n  // }\r\n}\r\n\r\nvoid SetWifi() {\r\n  ssid = ESP8266webServer.arg(0);\r\n  passwd = ESP8266webServer.arg(1);\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.println(\"密码在前面\");\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* WifiPw = passwd.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      ESP8266webServer.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      ESP8266webServer.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  ESP8266webServer.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handlejsonStr() {\r\n  ESP8266webServer.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  ESP8266webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\nvoid ApStart() {\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n  //启动AP模式,并设置账号和密码\r\n  WiFi.softAP(\"192.168.168.2\", \"nobiggie.cn\");\r\n}\r\nvoid WebserverStart() {\r\n  ESP8266webServer.begin(80);               // 启用网络服务\r\n  ESP8266webServer.on(\"/\", handlejsonStr);  // / 在HTTP中,表示请求首页服务,\r\n  ESP8266webServer.on(\"/SetWifi\", SetWifi);\r\n  ESP8266webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid ForPing() {\r\n  //定时时间到了,先取消定时状态\r\n  myTicker.detach();\r\n  delay(500);\r\n  ESP.restart();\r\n}\r\n\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.disconnect();\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n    Serial.println(\"扫描不到网络\");\r\n  }\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result = dateFile.readString();\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* Ssid = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi->false\");\r\n  return false;\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApStart();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  myTicker.detach();\r\n  delay(200);\r\n  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  WebserverStart();\r\n  while (1) {\r\n    ESP8266webServer.handleClient();  // 检查网络服务请求并处理\r\n  }\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\nvoid callback(char* topic, byte* payload, unsigned int length) {\r\n  Serial.print(\"Message arrived [\");\r\n  Serial.print(topic);\r\n  Serial.print(\"] \");\r\n  String str;\r\n  for (int i = 0; i < length; i++) {\r\n    str += (char)payload[i];\r\n  }\r\n  Serial.println(str);\r\n  Serial.write(str.c_str(), length);  //串口输出\r\n}\r\nvoid reconnect() {\r\n  // Loop until we\'re reconnected\r\n  while (!client.connected()) {\r\n    Serial.print(\"Attempting MQTT connection...\");\r\n    // Create a random client ID\r\n    String clientId = \"07b120062a7972cf9831d7d9bc8012bc\";\r\n    //clientId += String(random(0xffff), HEX);\r\n    // Attempt to connect\r\n    if (client.connect(clientId.c_str())) {\r\n      Serial.println(\"connected\");\r\n      //发送\r\n      client.publish(\"esp8266outTopic\", \"hello world\");\r\n      //订阅\r\n      client.subscribe(\"esp8266inTopic\");\r\n    } else {\r\n      Serial.print(\"failed, rc=\");\r\n      Serial.print(client.state());\r\n      Serial.println(\" try again in 5 seconds\");\r\n      // Wait 5 seconds before retrying\r\n      delay(5000);\r\n    }\r\n  }\r\n}','',1),
(255,4,'获取串口整句消息','  if (Serial.available() > 0) {  //获取串口消息\r\n    String receivedString = \"\";\r\n    while (Serial.available()) {\r\n      receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n    }\r\n//receivedString 这个就是获取到的整句消息\r\n}','',1),
(256,3,'golang 图片旋转180度','package main\r\n\r\nimport (\r\n	\"image\"\r\n	\"image/jpeg\"\r\n	\"os\"\r\n)\r\n\r\n// rotateImage180函数用于将给定的图片旋转180度\r\nfunc rotateImage180(img image.Image) image.Image {\r\n	bounds := img.Bounds()\r\n	width := bounds.Max.X\r\n	height := bounds.Max.Y\r\n	rotated := image.NewRGBA(image.Rect(0, 0, width, height))\r\n	for y := 0; y < height; y++ {\r\n		for x := 0; x < width; x++ {\r\n			oldPos := image.Pt(x, y)\r\n			newX := width - x - 1\r\n			newY := height - y - 1\r\n			newPos := image.Pt(newX, newY)\r\n			rotated.Set(newPos.X, newPos.Y, img.At(oldPos.X, oldPos.Y))\r\n		}\r\n	}\r\n	return rotated\r\n}\r\n\r\nfunc main() {\r\n	// 打开原始图片文件\r\n	file, err := os.Open(\"1.jpeg\")\r\n	if err != nil {\r\n		panic(err)\r\n	}\r\n	defer file.Close()\r\n	img, err := jpeg.Decode(file)\r\n	if err != nil {\r\n		panic(err)\r\n	}\r\n	// 对图片进行180度旋转\r\n	rotatedImg := rotateImage180(img)\r\n	// 创建输出文件\r\n	outFile, err := os.Create(\"output.jpg\")\r\n	if err != nil {\r\n		panic(err)\r\n	}\r\n	defer outFile.Close()\r\n	// 将旋转后的图片编码并保存为新文件\r\n	err = jpeg.Encode(outFile, rotatedImg, &jpeg.Options{Quality: 100})\r\n	if err != nil {\r\n		panic(err)\r\n	}\r\n}\r\n','',1),
(258,3,'websocketRedis','package models\r\n\r\nimport (\r\n	\"context\"\r\n	\"encoding/json\"\r\n	\"fmt\"\r\n	\"runtime\"\r\n\r\n	\"github.com/go-redis/redis/v8\"\r\n)\r\n\r\ntype WebsocketRedisRoomZ struct {\r\n	Rdb    *redis.Client\r\n	Host   string\r\n	Passwd string\r\n	Ctx    context.Context\r\n}\r\n\r\nvar WsRedisRoom WebsocketRedisRoomZ\r\n\r\nfunc init() {\r\n	WsRedisRoom.Select(0) // 选择redis库\r\n}\r\n\r\n/*\r\ndb 连接哪个redis reids有16个数据库 0-15\r\n数据库0 是websocket使用\r\n*/\r\nfunc (ggg *WebsocketRedisRoomZ) Select(db int) *redis.Client {\r\n	if runtime.GOOS == \"windows\" {\r\n		ggg.Host = \"192.168.0.200\"\r\n	} else {\r\n		ggg.Host = \"localhost\"\r\n	}\r\n	ggg.Passwd = \"qq282246101\"\r\n	ggg.Rdb = redis.NewClient(&redis.Options{\r\n		Network:            \"\",\r\n		Addr:               ggg.Host + \":6379\",\r\n		Dialer:             nil,\r\n		OnConnect:          nil,\r\n		Password:           ggg.Passwd,\r\n		DB:                 db,\r\n		MaxRetries:         0,\r\n		MinRetryBackoff:    0,\r\n		MaxRetryBackoff:    0,\r\n		DialTimeout:        0,\r\n		ReadTimeout:        0,\r\n		WriteTimeout:       0,\r\n		PoolSize:           30,\r\n		MinIdleConns:       10,\r\n		MaxConnAge:         0,\r\n		PoolTimeout:        0,\r\n		IdleTimeout:        0,\r\n		IdleCheckFrequency: 0,\r\n		TLSConfig:          nil,\r\n	})\r\n	//检测连接是否success success返回 pong\r\n	ggg.Ctx = ggg.Rdb.Context()\r\n	_, errRedis := ggg.Rdb.Ping(ggg.Ctx).Result()\r\n	if errRedis != nil {\r\n		fmt.Println(errRedis.Error())\r\n		return nil\r\n	}\r\n	return ggg.Rdb\r\n}\r\n\r\n// 存储map[房间名]map[用户名]string{ connetID }到 Redis 哈希\r\nfunc (ggg *WebsocketRedisRoomZ) StoreNestedMapToRedisHash(data map[string]map[string]string) error {\r\n	for outerKey, innerMap := range data {\r\n		serializedInnerMap, err := json.Marshal(innerMap)\r\n		if err != nil {\r\n			return err\r\n		}\r\n		err = ggg.Rdb.HSet(ggg.Ctx, \"nested_map_key\", outerKey, string(serializedInnerMap)).Err()\r\n		if err != nil {\r\n			return err\r\n		}\r\n	}\r\n	return nil\r\n}\r\n\r\n// 读取所有websocket 房间数据\r\nfunc (ggg *WebsocketRedisRoomZ) RetrieveNestedMapFromRedisHash() (map[string]map[string]string, error) {\r\n	result := make(map[string]map[string]string)\r\n	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, \"nested_map_key\").Result()\r\n	if err != nil {\r\n		return nil, err\r\n	}\r\n	for outerKey, serializedInnerMap := range fields {\r\n		var innerMap map[string]string\r\n		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)\r\n		if err != nil {\r\n			return nil, err\r\n		}\r\n		result[outerKey] = innerMap\r\n	}\r\n	return result, err\r\n}\r\n\r\n// 通过 房间名,用户名 找到 connetID\r\nfunc (ggg *WebsocketRedisRoomZ) RetrieveValueByInnerKey(outerKey, innerKey string) (string, error) {\r\n	// 先从Redis获取整个嵌套map数据并还原\r\n	result := make(map[string]map[string]string)\r\n	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, \"nested_map_key\").Result()\r\n	if err != nil {\r\n		return \"\", err\r\n	}\r\n	for outerK, serializedInnerMap := range fields {\r\n		var innerMap map[string]string\r\n		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)\r\n		if err != nil {\r\n			return \"\", err\r\n		}\r\n		result[outerK] = innerMap\r\n	}\r\n\r\n	// 从还原后的map结构中查找指定内层键对应的值\r\n	innerMap, ok := result[outerKey]\r\n	if !ok {\r\n		return \"\", fmt.Errorf(\"外层键 %s 不存在\", outerKey)\r\n	}\r\n	value, ok := innerMap[innerKey]\r\n	if !ok {\r\n		return \"\", fmt.Errorf(\"内层键 %s 在外层键 %s 下不存在\", innerKey, outerKey)\r\n	}\r\n	return value, nil\r\n}\r\n\r\n// 清空redis库0下的所有key\r\nfunc (ggg *WebsocketRedisRoomZ) FlushDB0ByKeys() error {\r\n	keys, err := ggg.Rdb.Keys(ggg.Ctx, \"*\").Result()\r\n	if err != nil {\r\n		return err\r\n	}\r\n	if len(keys) == 0 {\r\n		return nil\r\n	}\r\n	_, err = ggg.Rdb.Del(ggg.Ctx, keys...).Result()\r\n	return err\r\n}\r\n\r\n// 通过 房间名,connetID 找到 用户名\r\nfunc (ggg *WebsocketRedisRoomZ) RetrieveInnerKeyByValue(outerKey, value string) (string, error) {\r\n	// 先从Redis获取整个嵌套map数据并还原\r\n	result := make(map[string]map[string]string)\r\n	fields, err := ggg.Rdb.HGetAll(ggg.Ctx, \"nested_map_key\").Result()\r\n	if err != nil {\r\n		return \"\", err\r\n	}\r\n	for outerK, serializedInnerMap := range fields {\r\n		var innerMap map[string]string\r\n		err = json.Unmarshal([]byte(serializedInnerMap), &innerMap)\r\n		if err != nil {\r\n			return \"\", err\r\n		}\r\n		result[outerK] = innerMap\r\n	}\r\n\r\n	// 从还原后的map结构中查找指定外层键对应的内层map\r\n	innerMap, ok := result[outerKey]\r\n	if !ok {\r\n		return \"\", fmt.Errorf(\"外层键 %s 不存在\", outerKey)\r\n	}\r\n\r\n	// 遍历内层map，查找值为value的键\r\n	for innerK, v := range innerMap {\r\n		if v == value {\r\n			return innerK, nil\r\n		}\r\n	}\r\n	return \"\", fmt.Errorf(\"未找到值为 %s 的内层键（外层键为 %s）\", value, outerKey)\r\n}\r\n','',1),
(259,2,'redis 命令行','redis-cli -h localhost -p 6379 -a password\r\nselect 0\r\nFLUSHDB 清空当前数据库。\r\n\r\nFLUSHALL 清空所有数据库。','',1),
(260,3,'websocket ROOM map','package main\r\n\r\nimport \"fmt\"\r\n\r\n// DataContainer结构体用于包含和操作map[string][]interface{}\r\ntype DataContainer struct {\r\n	Data map[string][]interface{}\r\n}\r\n\r\n// NewDataContainer创建DataContainer的实例\r\nfunc NewDataContainer() *DataContainer {\r\n	return &DataContainer{\r\n		Data: make(map[string][]interface{}),\r\n	}\r\n}\r\n\r\n// AppendToSlice向指定key对应的切片中追加元素\r\nfunc (dc *DataContainer) AppendToSlice(key string, elements ...interface{}) {\r\n	if _, ok := dc.Data[key]; !ok {\r\n		dc.Data[key] = []interface{}{}\r\n	}\r\n	dc.Data[key] = append(dc.Data[key], elements...)\r\n}\r\n\r\n// UpdateSliceElement更新指定key对应的切片中指定索引位置的元素\r\nfunc (dc *DataContainer) UpdateSliceElement(key string, index int, newElement interface{}) bool {\r\n	if slice, ok := dc.Data[key]; ok {\r\n		if index >= 0 && index < len(slice) {\r\n			slice[index] = newElement\r\n			dc.Data[key] = slice\r\n			return true\r\n		}\r\n	}\r\n	return false\r\n}\r\n\r\n// DeleteSliceElement删除指定key对应的切片中指定索引位置的元素\r\nfunc (dc *DataContainer) DeleteSliceElement(key string, index int) bool {\r\n	if slice, ok := dc.Data[key]; ok {\r\n		if index >= 0 && index < len(slice) {\r\n			dc.Data[key] = append(slice[:index], slice[index+1:]...)\r\n			return true\r\n		}\r\n	}\r\n	return false\r\n}\r\n\r\n// DeleteValue从指定key对应的切片中删除指定的interface{}值\r\nfunc (dc *DataContainer) DeleteValue(key string, target interface{}) bool {\r\n	if slice, ok := dc.Data[key]; ok {\r\n		var newSlice []interface{}\r\n		for _, element := range slice {\r\n			if element != target {\r\n				newSlice = append(newSlice, element)\r\n			}\r\n		}\r\n		if len(newSlice) < len(slice) {\r\n			dc.Data[key] = newSlice\r\n			return true\r\n		}\r\n	}\r\n	return false\r\n}\r\nfunc main() {\r\n	container := NewDataContainer()\r\n	// 向名为\"list1\"的切片中追加元素\r\n	container.AppendToSlice(\"list1\", 1, 2, 3, 2, 4)\r\n	container.AppendToSlice(\"list1\", 5)\r\n	container.AppendToSlice(\"list1\", \"aaaa\")\r\n	fmt.Println(\"After appending:\", container.Data)\r\n\r\n	// 删除\"list1\"切片中的值2\r\n	deleted := container.DeleteValue(\"list1\", \"aaaa\")\r\n	if deleted {\r\n		fmt.Println(\"After deleting value:\", container.Data)\r\n	}\r\n\r\n	fmt.Println(container.Data)\r\n}\r\n','',1),
(262,3,'websocket 服务器 工作原理','Mysql   账号注册,登录 (用户名,密码,房间,登录时间)\r\n\r\n// 结构体用于包含和操作map[string][]interface{}\r\ntype MapMapContainer struct {\r\n    Data  map[string][]interface{}\r\n    mutex sync.RWMutex\r\n}\r\nvar WebsocketRoomFd = MapMapContainer{}   // 房间号 对应的 客户端fd\r\nvar WebsocketRoomUser = MapMapContainer{} // 房间号 对应的 用户名\r\n//WebsocketRoomFd,WebsocketRoomUser 添加,删除时候同时进行\r\n\r\n\r\n连接池 WsConnetPool 全局变量\r\n// 通过Fd 找到conn是谁\r\ntype WsService struct {\r\n    Client *websocket.Conn\r\n    //哪个用户\r\n    User  string\r\n    Fd    uint32 //用户 websocket连接池 唯一标识\r\n    Room  string\r\n    WebOs bool //网页登录\r\n}\r\nvar WsConnetPool = make(map[uint32]*WsService) //websocket 连接池  连接池添加,更改,删除,操作 要使用互斥锁','',1),
(264,4,'ESP01S和DTS33A播报','#include <Arduino.h>\r\n#include <ESP8266WiFi.h>\r\n#include <WebSocketsClient.h>\r\n#include <ESP8266httpUpdate.h>\r\n#include <Hash.h>\r\n#include <ArduinoJson.h>\r\n\r\nWebSocketsClient webSocket;\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject jsonArr;\r\nWiFiClient wificlient;\r\nString version=\"1.0.0\";\r\n#define D2 2          //启动时为高电平才能启动,必须接一个ss8050 三极管 c极接3.3v b极接D2  e机接要控制高低电平的GPIO\r\nbool status = false;  //默认D2 低电平\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      Serial.printf(\"[WSc] Disconnected!\\n\");\r\n      break;\r\n    case WStype_CONNECTED:\r\n      {\r\n        Serial.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n\r\n        // send message to server when Connected\r\n        //webSocket.sendTXT(\"Connected\");\r\n      }\r\n      break;\r\n    case WStype_TEXT:\r\n      Serial.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      jsonArr = jsonBuffer.as<JsonObject>();\r\n      if (jsonArr[\"message\"] != \"\") {\r\n        String message = jsonArr[\"message\"];\r\n        if (message == \"updateBin\") {\r\n          while (1) {\r\n            ESPhttpUpdate.onStart(update_started);\r\n            ESPhttpUpdate.onEnd(update_finished);\r\n            ESPhttpUpdate.onProgress(update_progress);\r\n            ESPhttpUpdate.onError(update_error);\r\n            ESPhttpUpdate.update(wificlient, \"http://192.168.1.2/ota/broadcast.bin\");\r\n          }\r\n        }\r\n        if (message == \"version\") {\r\n          webSocket.sendTXT(version);\r\n        }\r\n        if (message == \"hibernation\") {\r\n          digitalWrite(D2, HIGH);  //播报休眠\r\n          status = true;\r\n        }\r\n      }\r\n      // send message to server\r\n      // webSocket.sendTXT(\"message here\");\r\n      break;\r\n    case WStype_BIN:\r\n      Serial.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      if (status == true) {     //D2高电平\r\n        digitalWrite(D2, LOW);  //播报工作\r\n        status = false;\r\n        delay(2000);\r\n      }\r\n      hexdump(payload, length);\r\n      Serial.write(payload, length);\r\n      // send data to server\r\n      // webSocket.sendBIN(payload, length);\r\n      break;\r\n    case WStype_PING:\r\n      // pong will be send automatically\r\n      //Serial.printf(\"[WSc] get ping\\n\");\r\n      break;\r\n    case WStype_PONG:\r\n      // answer to a ping we send\r\n      Serial.printf(\"[WSc] get pong\\n\");\r\n      break;\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  // Serial.begin(921600);\r\n  Serial.begin(115200);\r\n  pinMode(D2, OUTPUT);\r\n  digitalWrite(D2, LOW);\r\n  WiFi.begin(\"webserver\", \"www.nobiggie.cn\");\r\n\r\n  while (WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n  }\r\n  Serial.printf(\"wifi OK\");\r\n  // server address, port and URL\r\n  webSocket.begin(\"192.168.1.2\", 80, \"/websocket\");\r\n\r\n  // event handler\r\n  webSocket.onEvent(webSocketEvent);\r\n\r\n  // use HTTP Basic Authorization this is optional remove if not needed\r\n  webSocket.setAuthorization(\"user=broadcast&passwd=efghijk&room=root\");\r\n\r\n  // try ever 5000 again if connection has failed\r\n  webSocket.setReconnectInterval(5000);\r\n\r\n  // start heartbeat (optional)\r\n  // ping server every 15000 ms\r\n  // expect pong from server within 3000 ms\r\n  // consider connection disconnected if pong is not received 2 times\r\n  webSocket.enableHeartbeat(15000, 3000, 2);\r\n}\r\n\r\nvoid loop() {\r\n  webSocket.loop();\r\n}\r\nvoid update_started() {\r\n  Serial.println(\"CALLBACK:  HTTP update process started\");\r\n}\r\n\r\nvoid update_finished() {\r\n  Serial.println(\"CALLBACK:  HTTP update process finished\");\r\n}\r\nvoid update_progress(int cur, int total) {\r\n  Serial.printf(\"CALLBACK:  HTTP update process at %d of %d bytes...\\n\", cur, total);\r\n}\r\n\r\nvoid update_error(int err) {\r\n  Serial.printf(\"CALLBACK:  HTTP update fatal error code %d\\n\", err);\r\n}\r\n','',1),
(265,4,'temp','#include <OneWire.h>\r\n#include <DallasTemperature.h>\r\n#include <Adafruit_Sensor.h>\r\n#include <DHT.h>\r\n#include <DHT_U.h>\r\n\r\n#include <FastLED.h>\r\n#include \"MAX30105.h\"\r\n#include \"spo2_algorithm.h\"\r\n\r\n\r\n#define DHTPIN 4  // 数字引脚连接DHT传感器\r\n#define DHTTYPE DHT11  // DHT 11\r\nDHT_Unified dht(DHTPIN, DHTTYPE);\r\nfloat humidity;\r\nfloat temperature;\r\n\r\n#define LED_PIN 18    // LED串连接的引脚\r\n#define NUM_LEDS 16   // LED数量\r\nCRGB leds[NUM_LEDS];  // 用于存储LED颜色数据的数组\r\n\r\n\r\nMAX30105 particleSensor;\r\n// 存储红外光和红光数据的缓冲区\r\nuint32_t irBuffer[100];\r\nuint32_t redBuffer[100];\r\n// 测量得到的血氧饱和度和心率值\r\nint32_t spo2;\r\nint8_t validSPO2;\r\nint32_t heartRate;\r\nint8_t validHeartRate;\r\n\r\n// 引脚定义\r\nbyte pulseLED = 21;\r\nbyte readLED = 22;\r\nvoid setup() {\r\n  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);  // 初始化LED串\r\n\r\n  Serial.begin(115200);\r\n  dht.begin();\r\n  sensor_t sensor;\r\n  dht.temperature().getSensor(&sensor);\r\n  dht.humidity().getSensor(&sensor);\r\n\r\n  // 初始化传感器\r\n  if (!particleSensor.begin(Wire, I2C_SPEED_FAST)) {\r\n    Serial.println(\"MAX30105 was not found. Please check wiring/power.\");\r\n    while (1)\r\n      ;\r\n  }\r\n  // 设置传感器参数\r\n  byte ledBrightness =100;\r\n  byte sampleAverage = 4;\r\n  byte ledMode = 2;\r\n  byte sampleRate = 100;\r\n  int pulseWidth = 411;\r\n  int adcRange = 4096;\r\n  particleSensor.setup(ledBrightness, sampleAverage, ledMode, sampleRate, pulseWidth, adcRange);\r\n}\r\n\r\nvoid loop() {\r\n  sensors_event_t event;\r\n  dht.temperature().getEvent(&event);\r\n  if (isnan(event.temperature)) {\r\n    Serial.println(F(\"Error reading temperature!\"));\r\n  } else {\r\n    temperature = event.temperature;\r\n  }\r\n  delay(500);\r\n  // Get humidity event and print its value.\r\n  dht.humidity().getEvent(&event);\r\n  if (isnan(event.relative_humidity)) {\r\n    Serial.println(F(\"Error reading humidity!\"));\r\n  } else {\r\n    humidity = event.relative_humidity;\r\n  }\r\n  String th = String(temperature) + \",\" + String(humidity);\r\n  Serial.println(th);\r\n  if (temperature <= 5) {\r\n    //紫色\r\n    fill_solid(leds, NUM_LEDS, CRGB(CHSV(160, 255, 255)));\r\n    FastLED.show();\r\n    delay(100);\r\n  } else {\r\n    if (temperature <= 10) {\r\n      //靛\r\n      fill_solid(leds, NUM_LEDS, CRGB(CHSV(180, 255, 255)));\r\n      FastLED.show();\r\n      delay(100);\r\n    } else {\r\n      if (temperature <= 15) {\r\n        //蓝\r\n       fill_solid(leds, NUM_LEDS, CRGB(CHSV(192, 255, 255)));\r\n        FastLED.show();\r\n        delay(100);\r\n      } else {\r\n        if (temperature <= 20) {\r\n          //绿\r\n          fill_solid(leds, NUM_LEDS, CRGB(CHSV(96, 255, 255)));\r\n          FastLED.show();\r\n          delay(100);\r\n        } else {\r\n          if (temperature <= 25) {\r\n            //黄\r\n            fill_solid(leds, NUM_LEDS, CRGB(CHSV(32, 255, 255)));\r\n            FastLED.show();\r\n            delay(100);\r\n          } else {\r\n            if (temperature <= 30) {\r\n              //橙\r\n              fill_solid(leds, NUM_LEDS, CRGB(CHSV(24, 255, 255)));\r\n              FastLED.show();\r\n              delay(100);\r\n            } else {\r\n              //红\r\n              fill_solid(leds, NUM_LEDS, CRGB(CHSV(0, 255, 255)));\r\n              FastLED.show();\r\n              delay(100);\r\n            }\r\n          }\r\n        }\r\n      }\r\n    }\r\n  }\r\n\r\n  \r\n  /////// 读取 30 个样本\r\n  int bufferLength = 50;\r\n\r\n  for (byte i = 0; i < bufferLength; i++) {\r\n    while (particleSensor.available() == false) {\r\n      particleSensor.check();\r\n    }\r\n    redBuffer[i] = particleSensor.getRed();\r\n    irBuffer[i] = particleSensor.getIR();\r\n    particleSensor.nextSample();\r\n    //Serial.print(\"red=\");\r\n    //Serial.print(redBuffer[i], DEC);\r\n    //Serial.print(\", ir=\");\r\n    //Serial.println(irBuffer[i], DEC);\r\n  }\r\n  // 计算血氧饱和度和心率\r\n  maxim_heart_rate_and_oxygen_saturation(irBuffer, bufferLength, redBuffer, &spo2, &validSPO2, &heartRate, &validHeartRate);\r\n  // 输出结果\r\n  if (validHeartRate == 1 && validSPO2 == 1) {\r\n    int32_t temHR = heartRate / 2.4;\r\n    Serial.print(\"心率 \");\r\n    Serial.print(temHR);\r\n    Serial.print(\", 血氧 \");\r\n    Serial.println(spo2, DEC);\r\n\r\n   // Number1.print(temHR);\r\n   // Number2.print(spo2);\r\n\r\n  } else {\r\n    Serial.println(\"检测失败,请手指放好位置\");\r\n    //Number1.print(0);\r\n    //Number2.print(0);\r\n\r\n  }\r\n}\r\n','',1),
(266,4,'彩灯 FastLED库 使用','#include <FastLED.h>\r\n#define LED_PIN 18    // LED串连接的引脚\r\n#define NUM_LEDS 16   // LED数量\r\nCRGB leds[NUM_LEDS];  // 用于存储LED颜色数据的数组\r\nvoid setup() {\r\n  // put your setup code here, to run once:\r\n  FastLED.addLeds<WS2812, LED_PIN, GRB>(leds, NUM_LEDS);  // 初始化LED串\r\n\r\n  Serial.begin(115200);\r\n}\r\n\r\nvoid loop() {\r\n  // put your main code here, to run repeatedly:\r\n  //紫色\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(160, 255, 255)));\r\n  FastLED.show();\r\n  Serial.println(\"紫色\");\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(180, 255, 255)));\r\n  FastLED.show();\r\n  Serial.println(\"靛色\");\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(192, 255, 255)));\r\n  Serial.println(\"蓝色\");\r\n  FastLED.show();\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(96, 255, 255)));\r\n  FastLED.show();\r\n  Serial.println(\"绿色\");\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(32, 255, 255)));\r\n  FastLED.show();\r\n  Serial.println(\"黄色\");\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(24, 255, 255)));\r\n  FastLED.show();\r\n   Serial.println(\"橙色\");\r\n  delay(2000);\r\n  fill_solid(leds, NUM_LEDS, CRGB(CHSV(0, 255, 255)));\r\n  FastLED.show();\r\n  Serial.println(\"红色\");\r\n  delay(2000);\r\n}\r\n','',1),
(267,3,'golang 打包多个EXE文件生成一个文件','package main\r\n\r\nimport (\r\n	\"embed\"\r\n	\"io\"\r\n	\"log\"\r\n	\"os\"\r\n	\"os/exec\"\r\n	\"time\"\r\n)\r\n\r\n//go:embed ws.exe copyFile.exe\r\nvar exeFiles embed.FS\r\n\r\nfunc extractFile(fileName, outputName string) {\r\n	file, err := exeFiles.Open(fileName)\r\n	if err != nil {\r\n		log.Fatalf(\"无法打开嵌入的文件 %s: %v\", fileName, err)\r\n	}\r\n	defer file.Close()\r\n\r\n	outputFile, err := os.Create(outputName)\r\n	if err != nil {\r\n		log.Fatalf(\"无法创建输出文件 %s: %v\", outputName, err)\r\n	}\r\n	defer outputFile.Close()\r\n\r\n	_, err = io.Copy(outputFile, file)\r\n	if err != nil {\r\n		log.Fatalf(\"无法从 %s 复制文件内容到 %s: %v\", fileName, outputName, err)\r\n	}\r\n\r\n	log.Printf(\"%s 已成功提取到 %s.\", fileName, outputName)\r\n}\r\n\r\nfunc main() {\r\n	extractFile(\"ws.exe\", \"ws.exe\")\r\n	extractFile(\"copyFile.exe\", \"copyFile.exe\")\r\n	time.Sleep(time.Second * 1)\r\n	// 运行 extracted_abc.exe\r\n	cmd := exec.Command(\"copyFile.exe\")\r\n	err := cmd.Run()\r\n	if err != nil {\r\n		log.Fatalf(\"运行 copyFile.exe 时出错: %v\", err)\r\n	}\r\n	log.Println(\"extracted_abc.exe 已成功运行。\")\r\n	os.Remove(\"copyFile.exe\")\r\n	os.Remove(\"ws.exe\")\r\n}\r\n','',1),
(268,4,'沃柑吊针容量esp8266','\r\n#include <ESP8266WiFi.h>\r\n#include <ESP8266HTTPClient.h>\r\nWiFiClient wificlient;\r\nint d5 = 14;\r\nint d6 = 12;\r\nint d8 = 15;  //wifi 连接成gas指示灯\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n  pinMode(d5, INPUT_PULLUP);\r\n  pinMode(d6, OUTPUT);\r\n  pinMode(d8, OUTPUT);\r\n  digitalWrite(d6, LOW);\r\n  digitalWrite(d8, LOW);\r\n  WiFi.begin(\"webserver\", \"www.nobiggie.cn\");\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  while (1) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      break;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  digitalWrite(d8, HIGH);\r\n  delay(500);\r\n}\r\n\r\nvoid loop() {\r\n  // put your main code here, to run repeatedly:\r\n  if (digitalRead(d5) == LOW) {\r\n    String a = esp8266Get(\"http://192.168.1.2:8080/auth/delay\");\r\n    if (a.length() > 0) {\r\n      Serial.println(a);\r\n      int aint = a.toInt();\r\n      int aa = aint * 1000;\r\n      digitalWrite(D6, HIGH);\r\n      delay(aa);\r\n      digitalWrite(D6, LOW);\r\n    }\r\n  }\r\n}\r\nString esp8266Get(String string) {\r\n  HTTPClient http;\r\n  String response;\r\n  http.begin(wificlient, string);\r\n  http.setTimeout(2000);\r\n  int httpCode = http.GET();\r\n  // httpCode will be negative on error\r\n  if (httpCode == 200) {\r\n    response = http.getString();\r\n    return response;\r\n  } else {\r\n    return \"\";\r\n  }\r\n  http.end();\r\n}','',1),
(269,4,'esp01s websocket客户端测试用','/*\r\n * WebSocketClient.ino\r\n *\r\n *  Created on: 24.05.2015\r\n *\r\n */\r\n\r\n#include <Arduino.h>\r\n\r\n#include <ESP8266WiFi.h>\r\n#include <ESP8266WiFiMulti.h>\r\n\r\n#include <WebSocketsClient.h>\r\n#include <ArduinoJson.h>\r\n#include <Hash.h>\r\n\r\nESP8266WiFiMulti WiFiMulti;\r\nWebSocketsClient webSocket;\r\n\r\n#define USE_SERIAL Serial1\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject jsonArr;\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      USE_SERIAL.printf(\"[WSc] Disconnected!\\n\");\r\n      break;\r\n    case WStype_CONNECTED:\r\n      {\r\n        USE_SERIAL.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n\r\n        // send message to server when Connected\r\n        webSocket.sendTXT(\"Connected\");\r\n      }\r\n      break;\r\n    case WStype_TEXT:\r\n      USE_SERIAL.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      jsonArr = jsonBuffer.as<JsonObject>();\r\n      if (jsonArr[\"message\"] != \"\") {\r\n        String user = jsonArr[\"user\"];\r\n        String date = jsonArr[\"date\"];\r\n        String message = jsonArr[\"message\"];\r\n        Serial.println(message);\r\n        webSocket.sendTXT(message);\r\n      }\r\n      // send message to server\r\n      // webSocket.sendTXT(\"message here\");\r\n      break;\r\n    case WStype_BIN:\r\n      USE_SERIAL.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      hexdump(payload, length);\r\n\r\n      // send data to server\r\n      // webSocket.sendBIN(payload, length);\r\n      break;\r\n    case WStype_PING:\r\n      // pong will be send automatically\r\n      USE_SERIAL.printf(\"[WSc] get ping\\n\");\r\n      break;\r\n    case WStype_PONG:\r\n      // answer to a ping we send\r\n      USE_SERIAL.printf(\"[WSc] get pong\\n\");\r\n      break;\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  // USE_SERIAL.begin(921600);\r\n  USE_SERIAL.begin(115200);\r\n\r\n  //Serial.setDebugOutput(true);\r\n  USE_SERIAL.setDebugOutput(true);\r\n\r\n  USE_SERIAL.println();\r\n  USE_SERIAL.println();\r\n  USE_SERIAL.println();\r\n\r\n  for (uint8_t t = 4; t > 0; t--) {\r\n    USE_SERIAL.printf(\"[SETUP] BOOT WAIT %d...\\n\", t);\r\n    USE_SERIAL.flush();\r\n    delay(1000);\r\n  }\r\n\r\n  WiFiMulti.addAP(\"webserver\", \"www.nobiggie.cn\");\r\n\r\n  //WiFi.disconnect();\r\n  while (WiFiMulti.run() != WL_CONNECTED) {\r\n    delay(100);\r\n  }\r\n  Serial.println(\"wifi ok\");\r\n  // server address, port and URL\r\n  webSocket.begin(\"192.168.1.2\", 100, \"/websocket\");\r\n\r\n  // event handler\r\n  webSocket.onEvent(webSocketEvent);\r\n\r\n  // use HTTP Basic Authorization this is optional remove if not needed\r\n  webSocket.setAuthorization(\"user=mydoor1&passwd=efghijk&room=admin\");\r\n  // try ever 5000 again if connection has failed\r\n  webSocket.setReconnectInterval(5000);\r\n\r\n  // start heartbeat (optional)\r\n  // ping server every 15000 ms\r\n  // expect pong from server within 3000 ms\r\n  // consider connection disconnected if pong is not received 2 times\r\n  webSocket.enableHeartbeat(15000, 3000, 2);\r\n}\r\n\r\nvoid loop() {\r\n  webSocket.loop();\r\n}\r\n','',1),
(270,2,'合并表去重复','MySQL  遍历查询CommerceCustomer表下的 phone ，比对KuaiDiReceiver表.如果KuaiDiReceiver表没有对应的 phone字段值 就向 KuaiDiReceiver插入该数据，，sql 语句怎么写\r\n\r\nINSERT INTO KuaiDiReceiver (name,phone,address,date,quantity)\r\nSELECT name,phone,address,date,quantity\r\nFROM CommerceCustomer\r\nWHERE phone NOT IN (SELECT DISTINCT phone FROM KuaiDiReceiver);','',1),
(271,2,'查询表是否有重复的字段 phone','SELECT phone\r\nFROM KuaiDiReceiver\r\nGROUP BY phone\r\nHAVING COUNT(phone) > 1;','',1),
(272,3,'单协程多channel与多个协程多channel的区别','单协程下的多channel不能并发,只能一个channel执行完毕后在selelct{}下的另一个channel\r\n\r\n多个协程多channel就可以同时并行并发','',1),
(274,1,'mqtt服务器开启webscoket TLS微信小程序','sudo apt update\r\nsudo apt install mosquitto mosquitto-clients\r\nsudo systemctl start mosquitto\r\nsudo systemctl enable mosquitto\r\ncd /etc/mosquitto/certs\r\n把网站证书放到这里\r\nnano /etc/mosquitto/mosquitto.conf\r\n添加以下内容\r\n# 允许匿名访问，可根据需要修改\r\nallow_anonymous true\r\n\r\n# 监听 WebSocket 端口\r\nlistener 8083\r\nprotocol websockets\r\n\r\n# 监听 WebSocket TLS 端口\r\nlistener 8084\r\nprotocol websockets\r\ncafile /etc/mosquitto/certs/server.crt\r\ncertfile /etc/mosquitto/certs/server.crt\r\nkeyfile /etc/mosquitto/certs/server.key\r\n\r\nsudo systemctl restart mosquitto\r\n验证配置\r\n<!DOCTYPE html>\r\n<html lang=\"en\">\r\n\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\r\n    <title>MQTT WebSocket TLS Test</title>\r\n    <script src=\"https://unpkg.com/mqtt/dist/mqtt.min.js\"></script>\r\n</head>\r\n\r\n<body>\r\n    <script>\r\n        const options = {\r\n            protocol: \'wss\',\r\n            host: \'localhost\',\r\n            port: 8084,\r\n            rejectUnauthorized: false // 因为是自签名证书，这里暂时设置为 false\r\n        };\r\n\r\n        const client = mqtt.connect(options);\r\n\r\n        client.on(\'connect\', function () {\r\n            console.log(\'Connected to MQTT broker via WebSocket TLS\');\r\n            client.subscribe(\'test/topic\', function (err) {\r\n                if (!err) {\r\n                    client.publish(\'test/topic\', \'Hello, MQTT!\');\r\n                }\r\n            });\r\n        });\r\n\r\n        client.on(\'message\', function (topic, message) {\r\n            console.log(\'Received message:\', topic, message.toString());\r\n            client.end();\r\n        });\r\n    </script>\r\n</body>\r\n\r\n</html>','',1),
(275,4,'SIM900和A7670C使用MQTT','#include <Arduino.h>\r\n\r\n// MQTT服务器配置\r\nconst char* mqttServer = \"wulian.nobiggie.cn\";\r\nconst int mqttPort = 1883;\r\nconst char* clientId = \"alarm-101\";\r\nconst char* username = \"alarm-101\";\r\nconst char* password = \"abcdefg\";\r\nconst char* sutopic = \"water/alarm-101/control\";\r\nconst char* putopic = \"water/alarm-101/data\";\r\n\r\n// 定义引脚数组 分别代表水位 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%\r\nconst int pins[] = {39, 34, 35, 32, 33, 25, 26, 27, 14 ,13};\r\nconst int numPins = sizeof(pins) / sizeof(pins[0]);\r\n// 缓冲区和超时设置\r\nchar response[256];\r\nunsigned long timeout;\r\nint waterV = 0;\r\nbool mqttConnet = false;\r\nint i = 10;\r\nString connectCmd = \"\";\r\n\r\n\r\nString inputString = \"\";  // 用于存储接收到的数据\r\nbool breakbl = false;\r\nvoid setup() {\r\n  // 初始化串口通信\r\n  Serial.begin(115200);\r\n  // 设置水位检测引脚为输入模式\r\n  for (int i = 0; i < numPins; i++) {\r\n    pinMode(pins[i], INPUT);\r\n    delay(200);\r\n  }\r\n\r\n  Serial.println(\"开始启动GSM\");\r\n  delay(28000);  //等待GSM启动完成\r\n  // 连接MQTT服务器\r\n  connectCmd = \"AT+CMQTTCONNECT=0,\\\"tcp://\";\r\n  connectCmd += mqttServer;\r\n  connectCmd += \":\";\r\n  connectCmd += String(mqttPort);\r\n  connectCmd += \"\\\",60,1,\\\"\";\r\n  connectCmd += username;\r\n  connectCmd += \"\\\",\\\"\";\r\n  connectCmd += password;\r\n  connectCmd += \"\\\"\";\r\n  Serial.println(\"AT+CMQTTCFG=\\\"argtopic\\\",0,1,1\");\r\n  delay(500);\r\n}\r\nbool mConnet() {\r\n\r\n  // 启动MQTT\r\n  if (sendATCommand(\"AT+CMQTTSTART\", \"OK\", 5000)) {\r\n    // 设置MQTT账户\r\n    if (sendATCommand(\"AT+CMQTTACCQ=0,\\\"A7670C\\\"\", \"OK\", 5000)) {\r\n      //Serial.println(\"MQTT account set successfully\");\r\n      if (sendATCommand(connectCmd.c_str(), \"OK\", 10000)) {\r\n        return true;\r\n        Serial.println(\"Connected to MQTT server\");\r\n        int topicLen = String(sutopic).length();\r\n        String subFrameCmd = \"AT+CMQTTSUB=0,\" + String(topicLen) + \",0\";\r\n        //订阅主题\r\n        if (sendATCommand(subFrameCmd.c_str(), \">\", 2000)) {\r\n          if (sendATCommand(sutopic, \"OK\", 2000)) {\r\n            return true;\r\n            //     //Serial.println(\"Subscribed to topic successfully\");\r\n            //     const char* a = \"10\";\r\n            //     publishMQTTMessage(putopic, a);\r\n            //   } else {\r\n            //     Serial.println(\"Failed to send topic\");\r\n          }\r\n          return false;\r\n        } else {\r\n          Serial.println(\"Failed to start subscription\");\r\n          return false;\r\n        }\r\n      } else {\r\n\r\n        Serial.println(\"Failed to connect to MQTT server\");\r\n        return false;\r\n      }\r\n    } else {\r\n      Serial.println(\"Failed to set MQTT account\");\r\n      return false;\r\n    }\r\n  } else {\r\n    Serial.println(\"Failed to start MQTT\");\r\n    return false;\r\n  }\r\n}\r\nvoid loop() {\r\n  // 设置水位检测引脚为输入模式\r\n  for (int i = 0; i < numPins; i++) {\r\n    if (digitalRead(pins[i]) == HIGH) {\r\n      waterV = (i + 1) * 10;\r\n    }\r\n  }\r\n  if (mqttConnet) {\r\n    //i++;\r\n    String tmp = String(waterV);\r\n    waterV = 0;\r\n    publishMQTTMessage(putopic, tmp.c_str());\r\n  } else {\r\n    mqttConnet = mConnet();\r\n  }\r\n\r\n  // 可以在这里添加发布消息的代码\r\n  delay(20000);\r\n}\r\n\r\nbool sendATCommand(const char* command, const char* expectedResponse, unsigned long timeout) {\r\n  if (command != \"AT\") {\r\n    Serial.println(command);\r\n  } else {\r\n    Serial.print(\"[发送指令] \");\r\n    Serial.println(command);\r\n  }\r\n\r\n\r\n  // 清空接收缓冲区\r\n  while (Serial.available()) {\r\n    Serial.read();\r\n  }\r\n\r\n  String response = \"\";\r\n  unsigned long startTime = millis();\r\n\r\n  while (millis() - startTime < timeout) {\r\n    // 读取所有可用数据\r\n    while (Serial.available()) {\r\n      char c = Serial.read();\r\n      response += c;\r\n      delay(1);  // 防止数据丢失\r\n    }\r\n\r\n    // 检查是否包含预期响应（忽略大小写）\r\n    if (response.indexOf(expectedResponse) != -1) {\r\n      String bb = String(command);\r\n      if (bb.indexOf(\"AT+CMQTTPAYLOAD=\") != -1 || bb.indexOf(\"AT+\") == -1) {\r\n        return true;\r\n      }\r\n      //Serial.print(\"[指令成功] \");\r\n      return true;\r\n    }\r\n\r\n    // 检查是否包含错误\r\n    if (response.indexOf(\"ERROR\") != -1) {\r\n      Serial.print(\"[指令失败] \");\r\n      return false;\r\n    }\r\n\r\n    delay(10);  // 避免CPU占用过高\r\n  }\r\n\r\n  Serial.print(\"[指令超时] \");\r\n  return false;\r\n}\r\n\r\nbool publishMQTTMessage(const char* putopic, const char* payload) {\r\n  int topicLength = strlen(putopic);\r\n  int payloadLength = strlen(payload);\r\n\r\n  // 设置主题长度\r\n  String topicLenCmd = \"AT+CMQTTTOPIC=0,\" + String(topicLength);\r\n  if (!sendATCommand(topicLenCmd.c_str(), \">\", 5000)) {\r\n    Serial.print(\"[发布主题长度失败] \");\r\n    return false;\r\n  }\r\n\r\n  // 发送主题内容\r\n  //Serial.print(\"[发送主题] \");\r\n  Serial.println(putopic);\r\n  delay(500);\r\n  // 设置负载长度\r\n  String payloadLenCmd = \"AT+CMQTTPAYLOAD=0,\" + String(payloadLength);\r\n  if (!sendATCommand(payloadLenCmd.c_str(), \">\", 5000)) {\r\n    return false;\r\n  }\r\n\r\n  // 发送负载内容\r\n\r\n  if (!sendATCommand(payload, \"OK\", 1000)) {\r\n    return false;\r\n  }\r\n\r\n\r\n  String aa = \"AT+CMQTTPUB=0,0,\" + String(payloadLength);\r\n  // 发布消息 (QoS=0, 保留=0)\r\n  if (!sendATCommand(aa.c_str(), \"+CMQTTPUB: 0,0\", 8000)) {\r\n    return false;\r\n  }\r\n\r\n\r\n  return true;\r\n}\r\n','',1),
(277,4,'arduino微信小程序水位NODEMCU32S','#include <Arduino.h>\r\n\r\n// 定义引脚数组 分别代表水位 10% 20% 30% 40% 50% 60% 70% 80% 90% 100%\r\nconst int pins[] = { 25, 26, 15, 2, 0, 4, 16, 17, 21, 22 };\r\nconst int numPins = sizeof(pins) / sizeof(pins[0]);\r\n\r\n// MQTT配置参数\r\nconst char* mqttBroker = \"wulian.nobiggie.cn\";\r\nconst int mqttPort = 1883;\r\nconst char* mqttClientId = \"A7670C\";\r\nconst char* mqttUsername = \"aaaaaaa\";\r\nconst char* mqttPassword = \"abcdefg\";\r\nconst char* pubTopic = \"wulian/water\";\r\nconst char* subTopic = \"wulian/admin\";\r\n\r\n// 状态变量\r\nbool mqttConnected = false;\r\nunsigned long lastReconnectAttempt = 0;\r\nunsigned long lastPublishTime = 0;\r\nunsigned long lastWaterCheckTime = 0;\r\nconst unsigned long publishInterval = 5000;     // 发布间隔(毫秒)\r\nconst unsigned long waterCheckInterval = 1000;  // 水位检测间隔(毫秒)\r\n\r\n// 水位相关变量\r\nint currentWaterLevel = 0;\r\nint lastPublishedLevel = -1;          // 初始化为-1表示尚未发布过\r\nconst int levelChangeThreshold = 10;  // 水位变化超过此值才发布更新\r\n\r\n// 串口通信缓冲区和状态\r\n#define MAX_RESPONSE_LENGTH 256\r\nchar responseBuffer[MAX_RESPONSE_LENGTH];\r\nint responseIndex = 0;\r\nbool awaitingResponse = false;\r\nString currentCommand = \"\";\r\n\r\nvoid setup() {\r\n  Serial.begin(115200);\r\n\r\n  Serial.println(\"水位监测系统初始化中...\");\r\n\r\n  // 设置水位检测引脚为输入模式\r\n  for (int i = 0; i < numPins; i++) {\r\n    pinMode(pins[i], INPUT);\r\n  }\r\n\r\n  // 初始化模块\r\n  if (initModule()) {\r\n    Serial.println(\"模块初始化成功\");\r\n    // 连接MQTT服务器\r\n    reconnect();\r\n  } else {\r\n    Serial.println(\"模块初始化失败\");\r\n  }\r\n}\r\n\r\nvoid loop() {\r\n  // 处理串口响应\r\n  handleSerialResponses();\r\n\r\n  // 定期检测水位\r\n  if (millis() - lastWaterCheckTime > waterCheckInterval) {\r\n    lastWaterCheckTime = millis();\r\n    checkWaterLevel();\r\n  }\r\n\r\n  // 保持MQTT连接\r\n  if (!mqttConnected) {\r\n    unsigned long now = millis();\r\n    // 每5秒尝试重连\r\n    if (now - lastReconnectAttempt > 5000) {\r\n      lastReconnectAttempt = now;\r\n      reconnect();\r\n    }\r\n  } else {\r\n    // 当水位变化超过阈值时发布更新\r\n    if (shouldPublishUpdate()) {\r\n      publishWaterLevel();\r\n      lastPublishedLevel = currentWaterLevel;\r\n      Serial.print(\"已发布更新水位: \");\r\n      Serial.print(currentWaterLevel);\r\n      Serial.println(\"%\");\r\n    }\r\n  }\r\n}\r\n\r\n// 检测当前水位\r\nvoid checkWaterLevel() {\r\n  int newLevel = 0;\r\n\r\n  // 遍历所有引脚，找到最高水位\r\n  for (int i = 0; i < numPins; i++) {\r\n    if (digitalRead(pins[i]) == LOW) {\r\n      newLevel = (i + 1) * 10;  // 转换为百分比\r\n    }\r\n  }\r\n\r\n  // 如果水位发生变化，打印日志\r\n  if (newLevel != currentWaterLevel) {\r\n    Serial.print(\"水位更新: \");\r\n    Serial.print(currentWaterLevel);\r\n    Serial.print(\"% -> \");\r\n    Serial.print(newLevel);\r\n    Serial.println(\"%\");\r\n    currentWaterLevel = newLevel;\r\n  }\r\n}\r\n\r\n// 判断是否应该发布水位更新\r\nbool shouldPublishUpdate() {\r\n  // 如果还没有发布过任何水位信息，立即发布\r\n  if (lastPublishedLevel == -1) {\r\n    return true;\r\n  }\r\n\r\n  // 计算当前水位与上次发布水位的差值\r\n  int levelDifference = abs(currentWaterLevel - lastPublishedLevel);\r\n\r\n  // 当差值达到或超过阈值时发布更新\r\n  return (levelDifference >= levelChangeThreshold);\r\n}\r\n\r\n// 初始化模块\r\nbool initModule() {\r\n  // 重置模块\r\n  resetModule();\r\n\r\n  // 检查模块响应\r\n  if (!sendATCommand(\"AT\", \"OK\", 2000)) return false;\r\n\r\n  // 关闭回显\r\n  if (!sendATCommand(\"ATE0\", \"OK\", 2000)) return false;\r\n\r\n  // 检查SIM卡状态\r\n  if (!sendATCommand(\"AT+CPIN?\", \"READY\", 5000)) return false;\r\n\r\n  // 等待网络注册\r\n  if (!waitForNetworkRegistration()) return false;\r\n\r\n  // 配置MQTT主题参数模式\r\n  if (!sendATCommand(\"AT+CMQTTCFG=\\\"argtopic\\\",0,1,1\", \"OK\", 5000)) return false;\r\n\r\n  return true;\r\n}\r\n\r\n// 重置模块\r\nvoid resetModule() {\r\n  pinMode(9, OUTPUT);  // 假设复位引脚为D9\r\n  digitalWrite(9, LOW);\r\n  delay(200);\r\n  digitalWrite(9, HIGH);\r\n  delay(3000);  // 等待模块启动\r\n}\r\n\r\n// 等待网络注册\r\nbool waitForNetworkRegistration() {\r\n  Serial.println(\"等待网络注册...\");\r\n  unsigned long startTime = millis();\r\n\r\n  while (millis() - startTime < 30000) {  // 超时30秒\r\n    if (sendATCommand(\"AT+CREG?\", \"+CREG: 0,1\", 2000) || sendATCommand(\"AT+CREG?\", \"+CREG: 0,5\", 2000)) {\r\n      Serial.println(\"网络注册成功\");\r\n      return true;\r\n    }\r\n    delay(1000);\r\n  }\r\n\r\n  //Serial.println(\"网络注册超时\");\r\n  return false;\r\n}\r\n\r\n// 连接MQTT服务器和订阅主题\r\nbool reconnect() {\r\n  Serial.println(\"尝试连接MQTT服务器...\");\r\n\r\n  // 启动MQTT\r\n  if (!sendATCommand(\"AT+CMQTTSTART\", \"OK\", 5000)) return false;\r\n\r\n  // 设置客户端ID\r\n  String cmd = \"AT+CMQTTACCQ=0,\\\"\";\r\n  cmd += mqttClientId;\r\n  cmd += \"\\\"\";\r\n  if (!sendATCommand(cmd.c_str(), \"OK\", 5000)) return false;\r\n\r\n  // 连接到MQTT代理\r\n  cmd = \"AT+CMQTTCONNECT=0,\\\"tcp://\";\r\n  cmd += mqttBroker;\r\n  cmd += \":\";\r\n  cmd += String(mqttPort);\r\n  cmd += \"\\\",120,1,\\\"\";\r\n  cmd += mqttUsername;\r\n  cmd += \"\\\",\\\"\";\r\n  cmd += mqttPassword;\r\n  cmd += \"\\\"\";\r\n  if (!sendATCommand(cmd.c_str(), \"OK\", 15000)) return false;\r\n\r\n  // 订阅主题 - 按照AT指令序列实现\r\n  cmd = \"AT+CMQTTSUB=0,\";\r\n  cmd += String(strlen(subTopic));\r\n  cmd += \",0\";\r\n  if (!sendATCommand(cmd.c_str(), \"OK\", 5000)) return false;\r\n  if (!sendATCommand(subTopic, \"OK\", 5000)) return false;\r\n  delay(200);\r\n  Serial.println(subTopic);\r\n\r\n  mqttConnected = true;\r\n  //Serial.println(\"MQTT连接成功\");\r\n  return true;\r\n}\r\n\r\n// 发布水位信息\r\nbool publishWaterLevel() {\r\n  if (!mqttConnected) return false;\r\n\r\n  Serial.print(\"发布水位信息: \");\r\n  // 构建消息内容\r\n  String message = String(currentWaterLevel);\r\n\r\n  // 按照AT指令序列实现发布流程\r\n  // 设置发布主题\r\n  String cmd = \"AT+CMQTTTOPIC=0,\";\r\n  cmd += String(strlen(pubTopic));\r\n  delay(200);\r\n  Serial.println(pubTopic);\r\n\r\n  // 设置发布负载\r\n  cmd = \"AT+CMQTTPAYLOAD=0,\";\r\n  cmd += String(message.length());\r\n  //if (!sendATCommand(cmd.c_str(), \"OK\", 5000)) return false;\r\n  if (!sendATCommand(message.c_str(), \"OK\", 5000)) return false;\r\n\r\n  // 发布消息\r\n  cmd = \"AT+CMQTTPUB=0,1,\";\r\n  cmd += String(message.length());\r\n  return sendATCommand(cmd.c_str(), \"OK\", 5000);\r\n}\r\n\r\n// 处理串口响应\r\nvoid handleSerialResponses() {\r\n  static unsigned long lastCharTime = 0;\r\n\r\n  while (Serial.available()) {\r\n    char c = Serial.read();\r\n    lastCharTime = millis();\r\n\r\n    // 构建响应字符串\r\n    if (responseIndex < MAX_RESPONSE_LENGTH - 1) {\r\n      responseBuffer[responseIndex++] = c;\r\n      responseBuffer[responseIndex] = \'\\0\';\r\n    }\r\n\r\n    // 检查是否收到完整响应\r\n    if (strstr(responseBuffer, \"\\r\\nOK\\r\\n\") != NULL || strstr(responseBuffer, \"\\r\\nERROR\\r\\n\") != NULL) {\r\n\r\n      processResponse(responseBuffer);\r\n\r\n      // 清空缓冲区\r\n      memset(responseBuffer, 0, MAX_RESPONSE_LENGTH);\r\n      responseIndex = 0;\r\n      awaitingResponse = false;\r\n    }\r\n  }\r\n\r\n  // 超时处理\r\n  if (awaitingResponse && (millis() - lastCharTime > 5000)) {\r\n    Serial.println(\"响应超时\");\r\n    memset(responseBuffer, 0, MAX_RESPONSE_LENGTH);\r\n    responseIndex = 0;\r\n    awaitingResponse = false;\r\n  }\r\n}\r\n\r\n// 处理AT命令响应\r\nvoid processResponse(char* response) {\r\n  Serial.print(\"收到响应: \");\r\n  Serial.println(response);\r\n\r\n  // 在这里可以添加对特定响应的处理逻辑\r\n  if (strstr(response, \"+CMQTTRXMSG:\") != NULL) {\r\n    // 解析接收到的MQTT消息\r\n    processMqttMessage(response);\r\n  }\r\n}\r\n\r\n// 解析MQTT消息\r\nvoid processMqttMessage(char* response) {\r\n  // 实际应用中需要根据模块返回格式解析消息内容\r\n  // 这里只是简单示例\r\n  Serial.println(\"收到MQTT消息\");\r\n\r\n  // 提取消息内容的逻辑...\r\n}\r\n\r\n// 发送AT命令并等待响应\r\nbool sendATCommand(const char* command, const char* expectedResponse, unsigned long timeout) {\r\n  // 等待上一个命令完成\r\n  while (awaitingResponse) {\r\n    handleSerialResponses();\r\n    delay(10);\r\n  }\r\n\r\n\r\n  // 发送命令\r\n  Serial.println(command);\r\n\r\n  // 设置等待响应状态\r\n  awaitingResponse = true;\r\n  currentCommand = command;\r\n\r\n  // 等待响应\r\n  unsigned long startTime = millis();\r\n  while (awaitingResponse && (millis() - startTime < timeout)) {\r\n    handleSerialResponses();\r\n    delay(10);\r\n  }\r\n\r\n  // 检查响应\r\n  if (!awaitingResponse) {\r\n    // 检查是否包含预期响应\r\n    if (strstr(responseBuffer, expectedResponse) != NULL) {\r\n      return true;\r\n    }\r\n  }\r\n\r\n  return false;\r\n}','',1),
(278,4,'SIM900和A7670C使用HTTP和HTTPS','AT     =>OK\r\nAT+CGACT=1      =>+CME ERROR: operation not allowed\r\nAT+CRESET      =>OK  *ATREADY: 1  +CPIN: READY  SMS DONE  +CGEV: EPS PDN ACT 1  +CGEV: ME PDN ACT 8,1\r\nAT+CGDCONT=1,\"IP\",\"CMNET\"    =>OK\r\nAT+CGACT=1,1        =>OK   +CGEV: NW MODIFY 1,0\r\nAT+HTTPINIT     =>OK\r\nAT+HTTPPARA=\"URL\",\"https://www.nobiggie.cn/test/test\"     =>OK\r\nAT+HTTPACTION=0  //http  \r\nAT+HTTPREAD=0,500  //https\r\nOK\r\n+HTTPACTION: 0,200,32587\r\n\r\nAT+HTTPREAD=200     =>OK','',1),
(280,1,'windows自动ssh连接ip批处理','@echo off\r\ntitle SSH连接到服务器\r\necho 正在连接到 192.168.0.254...\r\nssh root@192.168.0.254\r\nif errorlevel 1 (\r\n    echo SSH连接失败！\r\n    echo 请确保：\r\n    echo   1. 已安装OpenSSH客户端（可通过Windows功能启用）\r\n    echo   2. 目标服务器IP地址正确\r\n    echo   3. 目标服务器SSH服务正在运行\r\n    pause\r\n)    ','',1),
(281,4,'arduino GPIO中断','#include <Arduino.h>\r\nconst int INTERRUPT_PIN_1 = 17;\r\nconst int INTERRUPT_PIN_2 = 16;\r\nconst int OUTPUT_PIN_1 = 25;\r\nconst int OUTPUT_PIN_2 = 26;\r\n\r\n// 函数声明\r\nvoid IRAM_ATTR handleInterrupt1();\r\nvoid IRAM_ATTR handleInterrupt2();\r\n\r\nvoid setup() {\r\n  // 初始化输出引脚\r\n  pinMode(OUTPUT_PIN_1, OUTPUT);\r\n  pinMode(OUTPUT_PIN_2, OUTPUT);\r\n\r\n  // 初始化为低电平\r\n  digitalWrite(OUTPUT_PIN_1, LOW);\r\n  digitalWrite(OUTPUT_PIN_2, LOW);\r\n\r\n  // digitalWrite(INTERRUPT_PIN_1, HIGH);\r\n  // digitalWrite(INTERRUPT_PIN_2, HIGH);\r\n  // 设置中断引脚为输入模式，启用上拉电阻\r\n  pinMode(INTERRUPT_PIN_1, INPUT_PULLUP);\r\n  pinMode(INTERRUPT_PIN_2, INPUT_PULLUP);\r\n\r\n  // 附加中断服务函数\r\n  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_1), handleInterrupt1, FALLING);\r\n  attachInterrupt(digitalPinToInterrupt(INTERRUPT_PIN_2), handleInterrupt2, FALLING);\r\n  /*参数说明：\r\n  第一个参数digitalPinToInterrupt(INTERRUPT_PIN_1)：将数字引脚号转换为中断号。\r\n  第二个参数handleInterrupt1：是中断触发后要执行的函数名，此函数不能带有参数，并且返回类型必须为void。\r\n  第三个参数RISING：用来确定中断的触发方式。\r\n  触发模式类型：\r\n  LOW：当引脚为低电平时，会持续触发中断。\r\n  CHANGE：只要引脚电平发生变化，无论是高变低还是低变高，都会触发中断。\r\n  RISING：引脚电平从低电平变为高电平时，触发中断。\r\n  FALLING：引脚电平从高电平变为低电平时，触发中断。\r\n  HIGH（仅适用于 Arduino SAMD 板）：当引脚为高电平时，会持续触发中断。*/\r\n  Serial.begin(115200);\r\n  Serial.println(\"系统初始化完成\");\r\n}\r\n\r\nvoid loop() {\r\n  // 主循环可以处理其他任务\r\n  // 中断会独立于主循环执行\r\n  delay(5000);\r\n  Serial.println(\"loop\");\r\n}\r\n\r\n// 中断服务函数1：当INTERRUPT_PIN_1检测到上升沿时触发\r\nvoid IRAM_ATTR handleInterrupt1() {\r\n  static unsigned long lastInterruptTime = 0;\r\n  unsigned long currentTime = millis();\r\n\r\n  // 消抖处理\r\n  if (currentTime - lastInterruptTime > 50) {\r\n    lastInterruptTime = currentTime;\r\n    // 翻转输出状态\r\n    digitalWrite(OUTPUT_PIN_1, !digitalRead(OUTPUT_PIN_1));\r\n  }\r\n   //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n\r\n}\r\n\r\n// 中断服务函数2：当INTERRUPT_PIN_2检测到上升沿时触发\r\nvoid IRAM_ATTR handleInterrupt2() {\r\n  static unsigned long lastInterruptTime = 0;\r\n  unsigned long currentTime = millis();\r\n\r\n  // 消抖处理\r\n  if (currentTime - lastInterruptTime > 50) {\r\n    lastInterruptTime = currentTime;\r\n    digitalWrite(OUTPUT_PIN_2, !digitalRead(OUTPUT_PIN_2));\r\n  }\r\n   //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n}','',1),
(284,4,'ESP32物联遥控大门与智能播报','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <WiFiClientSecure.h>\r\n#include <ArduinoJson.h>\r\n#include <WebSocketsClient.h>\r\n#include <ESP32httpUpdate.h>\r\n\r\n#include <OneWire.h>\r\n#include <DallasTemperature.h>\r\n#include <Adafruit_Sensor.h>\r\n#include <DHT.h>\r\n#include <DHT_U.h>\r\n\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include <Ticker.h>\r\n#include \"esp_sleep.h\"\r\nString version = \"1.24.3\";\r\nDynamicJsonDocument jsonBuffer(100);\r\nJsonObject jsonArr;\r\n\r\nWebSocketsClient webSocket;\r\nWebServer webserver;\r\nTicker myTicker;\r\nWiFiClient wificlient;\r\n\r\n#define doorOpen 22       //升\r\n#define doorClose 21      //降\r\n#define doorOpenEXTI 26   //升 中断\r\n#define doorCloseEXTI 25  //降 中断\r\n#define dhtPin 0          //温湿度传感器\r\n#define DHTTYPE DHT11     //传感器类型\r\nDHT_Unified dht(dhtPin, DHTTYPE);\r\n\r\n#define billboard1 4           //广告牌开关\r\n#define billboard2 13          //寄快递广告牌\r\nbool guangGaoPaiBool = false;  //guangGaoPaiBool为false代表没有开灯箱 true为已开启灯箱\r\n\r\n#define broadcast 2   //播报电源开关\r\n#define doorPower 15  //电源总开关\r\n\r\nbool xrty = false;\r\n//开关状态 10为上升上限  0为下降上限 5为可以上升可降\r\nint stroke = 5;\r\nString localServerIP = \"192.168.0.254\";\r\nString upBinUrl = \"http://\" + localServerIP + \"/tool/ota/GolangCpp/mydoor1.bin\";\r\n\r\nString localDateH = \"http://\" + localServerIP + \"/time/h\";  //获取小时\r\n\r\nString localDate = \"http://\" + localServerIP + \"/time/date\";  //获取时间\r\nString ssid = \"\";                                             //wifi账号\r\nString passwd = \"\";                                           //wifi密码\r\nString html;                                                  //存html\r\nString flash_wifi = \"/wifi.txt\";                              //被读取的文件位置和名称\r\nint wsTimeOut = 0;\r\nString UserExec;\r\n\r\nint execStatus = 9;     //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表\r\nbool scanBool = false;  //ture为开启扫描状态\r\n\r\n// 函数声明\r\nvoid IRAM_ATTR handleInterruptOpen();\r\nvoid IRAM_ATTR handleInterruptClose();\r\n\r\nvolatile bool extiOpen = false;   //true 代表上升中断触发了\r\nvolatile bool extiClose = false;  //true 代表下降中断触发了\r\n\r\nvolatile unsigned long extiTime = 0;\r\n\r\nvoid hexdump(const void* mem, uint32_t len, uint8_t cols = 16) {\r\n  const uint8_t* src = (const uint8_t*)mem;\r\n  Serial.printf(\"\\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)\", (ptrdiff_t)src, len, len);\r\n  for (uint32_t i = 0; i < len; i++) {\r\n    if (i % cols == 0) {\r\n      Serial.printf(\"\\n[0x%08X] 0x%08X: \", (ptrdiff_t)src, i);\r\n    }\r\n    Serial.printf(\"%02X \", *src);\r\n    src++;\r\n  }\r\n  Serial.printf(\"\\n\");\r\n}\r\n\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n  String tmpH;\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      digitalWrite(doorOpen, LOW);\r\n      digitalWrite(doorClose, LOW);\r\n      Serial.println(\"Disconnected 开关状态全部关闭一下\");\r\n\r\n      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测\r\n      if (digitalRead(doorOpenEXTI) == HIGH && stroke != 10) {\r\n        stroke = 10;\r\n        closeAll();\r\n        Serial.println(\"Disconnected  上限关闭\");\r\n      }\r\n      //查询是否下降 限制,及时把状态改好\r\n      if (digitalRead(doorCloseEXTI) == HIGH && stroke != 0) {\r\n        stroke = 0;\r\n        closeAll();\r\n        Serial.println(\"Disconnected  下限关闭\");\r\n      }\r\n      wsTimeOut++;\r\n      //无法连接远程WSS服务器15次就自动 启动网页控制\r\n      if (wsTimeOut > 30) {\r\n        closeAll();\r\n        delay(500);\r\n        webSocket.disconnect();\r\n        delay(500);\r\n        //计数器归0\r\n        wsTimeOut = 0;\r\n        ESP.restart();\r\n      }\r\n      break;\r\n    case WStype_CONNECTED:\r\n      Serial.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n      //计数器归0\r\n      wsTimeOut = 0;\r\n      delay(200);\r\n      //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测\r\n      if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n        stroke = 5;\r\n      } else {\r\n        if (digitalRead(doorOpenEXTI) == HIGH) {\r\n          stroke = 10;\r\n        } else {\r\n          stroke = 0;\r\n        }\r\n      }\r\n      tmpH = esp32get(localDateH);\r\n      if (tmpH.length() > 0) {\r\n        int dateL = tmpH.toInt();\r\n        if (dateL > 6 && dateL < 18) {\r\n          digitalWrite(billboard1, HIGH);  //连接成功 这个时间段打开广告牌\r\n        }\r\n      }\r\n      break;\r\n    case WStype_TEXT:\r\n      Serial.printf(\"[WSc] get text: %s\\n\", payload);\r\n      deserializeJson(jsonBuffer, payload);\r\n      jsonArr = jsonBuffer.as<JsonObject>();\r\n      if (jsonArr[\"message\"] != \"\") {\r\n        String user = jsonArr[\"user\"];\r\n        String date = jsonArr[\"date\"];\r\n        String message = jsonArr[\"message\"];\r\n        if (message == \"mydoor1.open\") {\r\n          xrty = false;\r\n          //限位开关导通为低电平 表明还没有达到限位,并且没有达到上升最大限制 能正常上升,断开为高电平表明达到限位了\r\n          if (digitalRead(doorOpenEXTI) == LOW && stroke != 10) {\r\n            //主电源接通\r\n            digitalWrite(doorPower, HIGH);\r\n            delay(200);\r\n            //开 上升\r\n            webSocket.sendTXT(\"正在开门 ...\");\r\n            digitalWrite(doorClose, LOW);\r\n            delay(50);\r\n            digitalWrite(doorOpen, HIGH);\r\n            UserExec = user + \" 开门 \" + date;\r\n            execStatus = 1;\r\n\r\n          } else {\r\n            webSocket.sendTXT(\"无效,上升达到限制\");\r\n          }\r\n          return;\r\n        }\r\n\r\n        if (message == \"mydoor1.stop\") {\r\n          Serial.println(\"停止\");\r\n          closeAll();\r\n          //如果不在上限也不在下限时  悬停设置\r\n          if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n            stroke = 5;\r\n            if (!xrty) {\r\n              xrty = true;\r\n              webSocket.sendTXT(\"悬停播报设置成功\");\r\n            } else {\r\n              webSocket.sendTXT(\"悬停\");\r\n            }\r\n            delay(200);\r\n          } else {\r\n            //不是上升上限就是下降上限\r\n            xrty = false;\r\n            //查询是否下降 限制,及时把状态改好\r\n            if (digitalRead(doorOpenEXTI) == HIGH) {\r\n              stroke = 10;\r\n              String tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n              String rez = \"开门达到限制 \" + tempstr;\r\n              webSocket.sendTXT(rez);\r\n            } else {\r\n              stroke = 0;\r\n              String tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n              String rez = \"关门达到限制 \" + tempstr;\r\n              webSocket.sendTXT(rez);\r\n            }\r\n          }\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.close\") {\r\n          xrty = false;\r\n          //限位开关导通时为低电平表明还没有达到限位,并且没到达到下降最大限制  能正常下降,断开为高电平表明达到限位了\r\n          if (digitalRead(doorCloseEXTI) == LOW && stroke != 0) {\r\n            //主电源接通\r\n            digitalWrite(doorPower, HIGH);\r\n            delay(200);\r\n            //关 下降\r\n            webSocket.sendTXT(\"正在关门 ...\");\r\n            digitalWrite(doorOpen, LOW);\r\n            delay(50);\r\n            digitalWrite(doorClose, HIGH);\r\n            UserExec = user + \" 关门 \" + date;\r\n            execStatus = 0;\r\n          } else {\r\n            webSocket.sendTXT(\"无效,关门达到限制\");\r\n          }\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.TemperatureHumidity\") {\r\n          float temperature;\r\n          float humidity;\r\n          sensors_event_t event;\r\n          dht.temperature().getEvent(&event);\r\n          if (isnan(event.temperature)) {\r\n            Serial.println(F(\"Error reading temperature!\"));\r\n          } else {\r\n            temperature = event.temperature;\r\n          }\r\n          delay(500);\r\n          // Get humidity event and print its value.\r\n          dht.humidity().getEvent(&event);\r\n          if (isnan(event.relative_humidity)) {\r\n            Serial.println(F(\"Error reading humidity!\"));\r\n          } else {\r\n            humidity = event.relative_humidity;\r\n          }\r\n          String th = String(temperature) + \",\" + String(int(humidity));\r\n          webSocket.sendTXT(th);\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.records\") {  //查看最后操作人\r\n          webSocket.sendTXT(UserExec);\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.billboard\") {\r\n          if (guangGaoPaiBool == false) {\r\n            digitalWrite(billboard1, HIGH);\r\n            guangGaoPaiBool = true;\r\n            if (user == \"系统任务\") {\r\n              String strtem = \"系统任务 广告牌1已开\";\r\n              webSocket.sendTXT(strtem);\r\n            } else {\r\n              String strtem = user + \" 广告牌1已开\";\r\n              webSocket.sendTXT(strtem);\r\n            }\r\n          } else {\r\n            digitalWrite(billboard1, LOW);\r\n            guangGaoPaiBool = false;\r\n            if (user == \"系统任务\") {\r\n              String strtem = \"系统任务 广告牌1已关\";\r\n              webSocket.sendTXT(strtem);\r\n            } else {\r\n              String strtem = user + \" 广告牌1已关\";\r\n              webSocket.sendTXT(strtem);\r\n            }\r\n          }\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.billboardOn1\") {\r\n          guangGaoPaiBool = true;\r\n          digitalWrite(billboard1, HIGH);\r\n          String strtem = \"系统任务 广告牌1灯已开\";\r\n          webSocket.sendTXT(strtem);\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.billboardOff1\") {\r\n          guangGaoPaiBool = false;\r\n          digitalWrite(billboard1, LOW);\r\n          String strtem = \"系统任务 广告牌1灯已关\";\r\n          webSocket.sendTXT(strtem);\r\n          return;\r\n        }\r\n        if (message == \"mydoor1.dormancy\") {  //休眠两个小时\r\n          espSleep();\r\n          return;\r\n        }\r\n\r\n        if (message == \"mydoor1.ScanOpen\") {  //扫码开\r\n          scanBool = true;\r\n          digitalWrite(broadcast, HIGH);\r\n        }\r\n        if (message == \"mydoor1.ScanClose\") {  //扫码关 按停止键也是会关的\r\n          scanBool = false;\r\n          digitalWrite(broadcast, LOW);\r\n        }\r\n\r\n        if (message == \"mydoor1.reboot\") {\r\n          ESP.restart();\r\n        }\r\n        if (message == \"mydoor1.version\") {\r\n          webSocket.sendTXT(version);\r\n        }\r\n        if (message == \"mydoor1.updateBin\") {\r\n          // 执行 OTA 更新前的准备工作\r\n          noInterrupts();                                         // 关闭全局中断\r\n          detachInterrupt(digitalPinToInterrupt(doorOpenEXTI));   // 禁用特定中断源（如果需要）\r\n          detachInterrupt(digitalPinToInterrupt(doorCloseEXTI));  // 禁用特定中断源（如果需要）\r\n          delay(200);\r\n          while (1) {\r\n            t_httpUpdate_return ret =ESPhttpUpdate.update(upBinUrl);\r\n            switch (ret) {\r\n              case HTTP_UPDATE_FAILED:  //更新失败\r\n                webSocket.sendTXT(\"更新失败\");\r\n                Serial.printf(\"HTTP_UPDATE_FAILD Error (%d): %s\", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());\r\n                break;\r\n              case HTTP_UPDATE_NO_UPDATES:  //无需更新\r\n                webSocket.sendTXT(\"无需更新\");\r\n                break;\r\n              case HTTP_UPDATE_OK:  //更新成功\r\n                webSocket.sendTXT(\"更新成功\");\r\n                Serial.println(\"HTTP_UPDATE_OK\");\r\n                break;\r\n            }\r\n\r\n            interrupts();  // 恢复全局中断\r\n            espReboot();\r\n          }\r\n        }\r\n        if (message.indexOf(\"mydoor1.billboardOn2\") != -1) {  //13脚高电平 广告牌2开启\r\n          digitalWrite(billboard2, HIGH);\r\n          String strtem = \"系统任务 广告牌2已开\";\r\n          webSocket.sendTXT(strtem);\r\n        }\r\n        if (message == \"mydoor1.billboardOff2\") {  //13脚低电平 广告牌2关闭\r\n          digitalWrite(billboard2, LOW);\r\n          String strtem = \"系统任务 广告牌2已关\";\r\n          webSocket.sendTXT(strtem);\r\n        }\r\n      }\r\n      // send message to server\r\n      // webSocket.sendTXT(\"message here\");\r\n      break;\r\n    case WStype_BIN:\r\n      //语音是以 []byte 传输\r\n      Serial.printf(\"[WSc] get binary length: %u\\n\", length);\r\n      //hexdump(payload, length);\r\n      //防止大门开启的时候,同时播报导致大门控制失灵 只有在上,下限的时候才播报\r\n      if (digitalRead(doorOpenEXTI) == HIGH) {\r\n        if (scanBool) {\r\n          Serial.write(payload, length);\r\n        } else {\r\n          bobao(payload, length);\r\n        }\r\n        return;\r\n      } else {\r\n        if (digitalRead(doorCloseEXTI) == HIGH) {\r\n          if (scanBool) {\r\n            Serial.write(payload, length);\r\n          } else {\r\n            bobao(payload, length);\r\n          }\r\n          return;\r\n        }\r\n      }\r\n\r\n      if (xrty) {\r\n        if (scanBool) {\r\n          Serial.write(payload, length);\r\n        } else {\r\n          bobao(payload, length);\r\n        }\r\n        return;\r\n      }\r\n\r\n      // send data to server\r\n      // webSocket.sendBIN(payload, length);\r\n      break;\r\n    case WStype_ERROR:\r\n    case WStype_FRAGMENT_TEXT_START:\r\n    case WStype_FRAGMENT_BIN_START:\r\n    case WStype_FRAGMENT:\r\n    case WStype_FRAGMENT_FIN:\r\n      break;\r\n  }\r\n}\r\n\r\nvoid setup() {\r\n  pinMode(doorOpen, OUTPUT);    //门上升\r\n  pinMode(doorClose, OUTPUT);   //门下降\r\n  pinMode(billboard1, OUTPUT);  //广播牌\r\n  pinMode(broadcast, OUTPUT);   //语音播报\r\n  pinMode(doorPower, OUTPUT);   //总电源\r\n  pinMode(billboard2, OUTPUT);\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  digitalWrite(billboard1, LOW);\r\n  digitalWrite(broadcast, LOW);\r\n  digitalWrite(doorPower, LOW);\r\n  digitalWrite(billboard2, LOW);\r\n\r\n  pinMode(doorOpenEXTI, INPUT_PULLUP);  //上拉电阻模式,默认高电平\r\n  pinMode(doorCloseEXTI, INPUT_PULLUP);\r\n\r\n  // 附加中断服务函数\r\n  attachInterrupt(digitalPinToInterrupt(doorOpenEXTI), handleInterruptOpen, FALLING);\r\n  attachInterrupt(digitalPinToInterrupt(doorCloseEXTI), handleInterruptClose, FALLING);\r\n  /*参数说明：\r\n  第一个参数digitalPinToInterrupt(INTERRUPT_PIN_1)：将数字引脚号转换为中断号。\r\n  第二个参数handleInterrupt1：是中断触发后要执行的函数名，此函数不能带有参数，并且返回类型必须为void。\r\n  第三个参数RISING：用来确定中断的触发方式。\r\n  触发模式类型：\r\n  LOW：当引脚为低电平时，会持续触发中断。\r\n  CHANGE：只要引脚电平发生变化，无论是高变低还是低变高，都会触发中断。\r\n  RISING：引脚电平从低电平变为高电平时，触发中断。\r\n  FALLING：引脚电平从高电平变为低电平时，触发中断。\r\n  HIGH（仅适用于 Arduino SAMD 板）：当引脚为高电平时，会持续触发中断。*/\r\n\r\n\r\n  Serial.begin(115200);\r\n\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    SPIFFS.begin();\r\n    File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n    dataFile.println(\"webserver&www.nobiggie.cn\");\r\n    dataFile.close();\r\n  }\r\n\r\n  //pinMode(flash_rst, INPUT_PULLUP);\r\n  delay(200);\r\n  dht.begin();\r\n  sensor_t sensor;\r\n  dht.temperature().getSensor(&sensor);\r\n  dht.humidity().getSensor(&sensor);\r\n  delay(500);\r\n  if (FlashConnetWifi() == false) {  //自动flash联网不成功 就启用 html配置wifi账号密码\r\n    HtmlSetWifi();\r\n  }\r\n  delay(300);\r\n\r\n  //通知服务器 我正在重启,验证一下广告牌开关状态 10秒后我启动完成发送广告牌开关状态给我\r\n  billboardPost();\r\n  webSocket.begin(localServerIP, 80, \"/websocket\");\r\n  webSocket.onEvent(webSocketEvent);\r\n  webSocket.setAuthorization(\"user=mydoor1&passwd=efghijk&room=door\");\r\n  webSocket.setReconnectInterval(5000);\r\n  if (digitalRead(doorOpenEXTI) == HIGH) {\r\n    stroke = 10;\r\n  }\r\n  if (digitalRead(doorCloseEXTI) == HIGH) {\r\n    stroke = 0;\r\n  }\r\n}\r\nvoid loop() {\r\n  webSocket.loop();\r\n\r\n\r\n  if (extiTime > 0) {\r\n    unsigned long currentTime = millis();\r\n    if ((extiTime - currentTime) > 1500) {\r\n      execStatus = 5;  //中断 正常触发模式开启\r\n      extiTime = 0;\r\n      webSocket.sendTXT(\"中断 正常触发模式开启\");\r\n      return;\r\n    }\r\n    if (extiOpen) {  //上升中断触发\r\n      xrty = false;\r\n      extiOpen = false;\r\n      if (execStatus == 1) {\r\n        stroke = 10;\r\n        execStatus = 9;  //执行状态为空\r\n        extiTime = 0;\r\n        digitalWrite(doorPower, LOW);  //断开电源接通\r\n        webSocket.sendTXT(\"正常模式 上升中断触发\");\r\n      }\r\n      return;\r\n    }\r\n    if (extiClose) {  //下降中断触发\r\n      xrty = false;\r\n      extiClose = false;\r\n      if (execStatus == 0) {\r\n        stroke = 0;\r\n        execStatus = 9;  //执行状态为空\r\n        extiTime = 0;\r\n        digitalWrite(doorPower, LOW);   //断开电源接通\r\n        digitalWrite(billboard1, LOW);  //关门就把广告灯箱关了\r\n        webSocket.sendTXT(\"正常模式 下降中断触发\");\r\n      }\r\n      return;\r\n    }\r\n  }\r\n  //即没有上限也没有下限状态\r\n\r\n  if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n    stroke = 5;\r\n    return;\r\n  }\r\n  if (digitalRead(doorOpenEXTI) == HIGH) {  //上升达到限制\r\n    if (stroke != 10) {                     //第一次 加一层保险 停止上升\r\n      digitalWrite(doorOpen, LOW);\r\n\r\n      webSocket.sendTXT(\"loop上升限制触发\");\r\n    }\r\n    stroke = 10;\r\n    return;\r\n  }\r\n  if (digitalRead(doorCloseEXTI) == HIGH) {  //下降达到限制\r\n    if (stroke != 0) {                       //第一次 加一层保险 停止下降\r\n      digitalWrite(doorClose, LOW);\r\n\r\n      webSocket.sendTXT(\"loop下降限制触发\");\r\n    }\r\n    stroke = 0;\r\n    return;\r\n  }\r\n}\r\n\r\n\r\nvoid IRAM_ATTR handleInterruptOpen() {\r\n  static unsigned long lastInterruptTime = 0;\r\n  unsigned long currentTime = millis();\r\n  if (execStatus == 1) {\r\n    if (extiTime == 0) {\r\n      extiTime = currentTime;\r\n    }\r\n  } else {\r\n    // 消抖处理\r\n    if (currentTime - lastInterruptTime > 20) {  //调到50有时候上升不能触发中断\r\n      lastInterruptTime = currentTime;\r\n      if (digitalRead(doorOpenEXTI) == HIGH) {\r\n        digitalWrite(doorOpen, LOW);\r\n        if (execStatus == 1) {           //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表\r\n          digitalWrite(doorPower, LOW);  //断开电源接通\r\n        }\r\n        //开门中断\r\n        extiOpen = true;\r\n        //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n\r\nvoid IRAM_ATTR handleInterruptClose() {\r\n  static unsigned long lastInterruptTime = 0;\r\n  unsigned long currentTime = millis();\r\n  if (execStatus == 0) {\r\n    if (extiTime == 0) {\r\n      extiTime = currentTime;\r\n    }\r\n  } else {\r\n    // 消抖处理\r\n    if (currentTime - lastInterruptTime > 20) {\r\n      lastInterruptTime = currentTime;\r\n      if (digitalRead(doorCloseEXTI) == HIGH) {\r\n        digitalWrite(doorClose, LOW);\r\n        if (execStatus == 0) {           //0关门 状态\r\n          digitalWrite(doorPower, LOW);  //断开电源接通\r\n        }\r\n        //关门中断\r\n        extiClose = true;\r\n        //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n      }\r\n    }\r\n  }\r\n}\r\n//关闭上升,下降,变压器主电源\r\nvoid closeAll() {\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  delay(200);\r\n  digitalWrite(doorPower, LOW);  //断开电源接通\r\n  scanBool = false;              //扫描状态关闭\r\n  digitalWrite(broadcast, LOW);  //扫描关\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\n//无网特殊状态下安全限位\r\nvoid html_whlie() {\r\n  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开\r\n  if (digitalRead(doorOpenEXTI) == HIGH || digitalRead(doorCloseEXTI) == HIGH) {\r\n    //关闭所有开关\r\n    closeAll();\r\n    Serial.println(\"达到上限\");\r\n    myTicker.detach();\r\n    delay(200);\r\n    ESP.restart();\r\n  }\r\n}\r\nvoid billboardPost() {\r\n  HTTPClient http;\r\n  String response;\r\n  String postStr = \"a=a\";\r\n  String tmp = \"http://\" + localServerIP + \"/time/bill-board\";\r\n  http.begin(wificlient, tmp);\r\n  http.setTimeout(500);\r\n  //需要传输的数据\r\n  char dataBuff[postStr.length()];\r\n  strcpy(dataBuff, postStr.c_str());\r\n  http.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n  http.POST(dataBuff);\r\n  http.end();\r\n}\r\n\r\nvoid espSleep() {\r\n  closeAll();\r\n  delay(500);\r\n  webSocket.sendTXT(\"mydoor1休眠两个半小时...\");\r\n  delay(200);\r\n  webSocket.disconnect();\r\n  delay(300);\r\n  WiFi.disconnect();\r\n  myTicker.detach();\r\n  delay(200);\r\n  //进入深睡眠状态，将在 3 小时后唤醒\r\n  esp_sleep_enable_timer_wakeup(10800000000);\r\n  esp_deep_sleep_start();\r\n}\r\nString esp32post(String url) {\r\n  HTTPClient http;\r\n  http.begin(wificlient, url);\r\n  http.setTimeout(3000);\r\n  String postStr = \"version=esp32test10\";\r\n  char dataBuff[postStr.length()];\r\n  strcpy(dataBuff, postStr.c_str());\r\n  http.addHeader(\"Content-Type\", \"application/x-www-form-urlencoded\");\r\n  int httpResponseCode = http.POST(dataBuff);\r\n  if (httpResponseCode > 0) {\r\n    String response = http.getString();\r\n    return response;\r\n  }\r\n  return \"\";\r\n}\r\nString esp32get(String urlstr) {\r\n  HTTPClient http;\r\n  String response;\r\n  http.begin(wificlient, urlstr);\r\n  http.setTimeout(3000);\r\n  int httpCode = http.GET();\r\n  // httpCode will be negative on error\r\n  if (httpCode == 200) {\r\n    response = http.getString();\r\n    return response;\r\n  } else {\r\n    return \"\";\r\n  }\r\n  http.end();\r\n}\r\n\r\nvoid SetWifi() {\r\n  ssid = webserver.arg(0);\r\n  passwd = webserver.arg(1);\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.println(\"密码在前面\");\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* WifiPw = passwd.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webserver.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      webserver.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webserver.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handlejsonArr() {\r\n  webserver.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webserver.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\nvoid ApStart() {\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n\r\n  //启动AP模式,并设置账号和密码\r\n  WiFi.softAP(\"192.168.168.2\", \"\");\r\n}\r\nvoid webserverStart() {\r\n  //-----------------启动webserver网络服务--------------------\r\n  webserver.begin(80);               // 启用网络服务\r\n  webserver.on(\"/\", handlejsonArr);  // / 在HTTP中,表示请求首页服务,\r\n  webserver.on(\"/SetWifi\", SetWifi);\r\n  webserver.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid ForPing() {\r\n  //定时时间到了,先取消定时状态\r\n  myTicker.detach();\r\n  closeAll();\r\n  delay(500);\r\n\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.disconnect();\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n    Serial.println(\"扫描不到网络\");\r\n  }\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result = dateFile.readString();\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* Ssid = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(Ssid);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(Ssid, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int wifiTimeout = 0;\r\n  for (wifiTimeout; wifiTimeout < 30; wifiTimeout++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi-> timeout\");\r\n  return false;\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApStart();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  myTicker.detach();\r\n  delay(200);\r\n  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  webserverStart();\r\n  while (1) {\r\n    webserver.handleClient();  // 检查网络服务请求并处理\r\n  }\r\n}\r\n\r\nvoid bobao(uint8_t* message, int len) {  //定时播报\r\n  if (!scanBool) {                       //如果不是 扫描状态 延迟25秒关闭播报开关\r\n    digitalWrite(broadcast, HIGH);\r\n    delay(2000);\r\n    //String data = \"020104C0\";\r\n    Serial.write(message, len);\r\n\r\n    delay(25000);\r\n    digitalWrite(broadcast, LOW);\r\n  }\r\n}','',1),
(285,4,'ESP32物联遥控大门与智能播报(中断)','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <PubSubClient.h>\r\n#include <HTTPUpdate.h>\r\n#include <OneWire.h>\r\n#include <DallasTemperature.h>\r\n#include <Adafruit_Sensor.h>\r\n#include <DHT.h>\r\n#include <DHT_U.h>\r\n#include <Ticker.h>\r\n#include <esp_sleep.h>\r\n#include <FS.h>\r\n#include <SPIFFS.h>\r\n#include <WebServer.h>\r\n#define doorOpen 22       //升\r\n#define doorClose 21      //降\r\n#define doorOpenEXTI 26   //升 中断\r\n#define doorCloseEXTI 25  //降 中断\r\n#define dhtPin 0          //温湿度传感器\r\n#define DHTTYPE DHT11     //传感器类型\r\nDHT_Unified dht(dhtPin, DHTTYPE);\r\n\r\n#define billboard1 4           //广告牌开关\r\n#define billboard2 13          //寄快递广告牌\r\nbool guangGaoPaiBool = false;  //guangGaoPaiBool为false代表没有开灯箱 true为已开启灯箱\r\n\r\n#define light 2       //灯\r\n#define gpio17 17     //播报电源开关\r\n#define doorPower 15  //电源总开关\r\nString version = \"1.24.7\";\r\nString ssid = \"\";                 //wifi账号\r\nString passwd = \"\";               //wifi密码\r\nString html;                      //存html\r\nString flash_wifi = \"/wifi.txt\";  //被读取的文件位置和名称\r\nWebServer webserver;\r\n\r\nvolatile int doorStroke = 5;   //记录门静止状态 处于什么位置 10为上限  0为下限 5为可以上升可降\r\nvolatile bool runIng = false;  //true 为电机正在运行\r\nvolatile int command = 2;      //此时执行的指令  0关门  1开门  2停止\r\n// 函数声明\r\nvoid IRAM_ATTR handleInterruptOpen();\r\nvoid IRAM_ATTR handleInterruptClose();\r\n\r\nvolatile bool extiOpen = false;   //true 代表上升中断触发了\r\nvolatile bool extiClose = false;  //true 代表下降中断触发了\r\nvolatile unsigned long extiTime = 0;\r\n\r\n\r\nWiFiClient wificlient;\r\n\r\nTicker myTicker;\r\n\r\nconst char* mqtt_server = \"192.168.0.254\";\r\nconst char* mqtt_username = \"door1\";\r\nconst char* mqtt_password = \"abcdefg\";\r\nconst char* subTopic = \"wifi/door1/control\";\r\nconst char* pubshTopic = \"wifi/door1/data\";\r\nPubSubClient client(wificlient);\r\n\r\nString upBinUrl = \"http://\" + String(mqtt_server) + \"/tool/ota/GolangCpp/mydoor1.bin\";\r\nvoid setup(void) {\r\n  pinMode(doorOpen, OUTPUT);    //门上升\r\n  pinMode(doorClose, OUTPUT);   //门下降\r\n  pinMode(billboard1, OUTPUT);  //广播牌\r\n  pinMode(gpio17, OUTPUT);      //语音播报\r\n  pinMode(light, OUTPUT);\r\n  pinMode(doorPower, OUTPUT);  //总电源\r\n  pinMode(billboard2, OUTPUT);\r\n\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  digitalWrite(billboard1, LOW);\r\n  digitalWrite(gpio17, LOW);\r\n  digitalWrite(light, LOW);\r\n  digitalWrite(doorPower, LOW);\r\n  digitalWrite(billboard2, LOW);\r\n\r\n  pinMode(doorOpenEXTI, INPUT_PULLUP);  //上拉电阻模式,默认高电平\r\n  pinMode(doorCloseEXTI, INPUT_PULLUP);\r\n\r\n  // 附加中断服务函数\r\n  attachInterrupt(digitalPinToInterrupt(doorOpenEXTI), handleInterruptOpen, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(doorCloseEXTI), handleInterruptClose, CHANGE);\r\n\r\n\r\n  Serial.begin(115200);\r\n  //pinMode(flash_rst, INPUT_PULLUP);\r\n  delay(200);\r\n  dht.begin();\r\n  sensor_t sensor;\r\n  dht.temperature().getSensor(&sensor);\r\n  dht.humidity().getSensor(&sensor);\r\n  delay(500);\r\n\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    SPIFFS.begin();\r\n    File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n    dataFile.println(\"webserver&www.nobiggie.cn\");\r\n    dataFile.close();\r\n  }\r\n\r\n  if (FlashConnetWifi() == false) {  //自动flash联网不成功 就启用 html配置wifi账号密码\r\n    HtmlSetWifi();\r\n  }\r\n  Serial.println(\"wifi ok\");\r\n  client.setServer(mqtt_server, 1883);\r\n  client.setCallback(mqttCallback);\r\n  delay(200);\r\n  if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n    doorStroke = 5;\r\n  } else {\r\n    if (digitalRead(doorOpenEXTI) == HIGH) {\r\n      doorStroke = 10;\r\n    }\r\n    if (digitalRead(doorCloseEXTI) == HIGH) {\r\n      doorStroke = 0;\r\n    }\r\n  }\r\n}\r\n\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    digitalWrite(doorOpen, LOW);\r\n    digitalWrite(doorClose, LOW);\r\n\r\n    //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测\r\n    if (digitalRead(doorOpenEXTI) == HIGH && doorStroke != 10) {\r\n      doorStroke = 10;\r\n      closeAll();\r\n      //Serial.println(\"Disconnected  上限关闭\");\r\n    }\r\n    //查询是否下降 限制,及时把状态改好\r\n    if (digitalRead(doorCloseEXTI) == HIGH && doorStroke != 0) {\r\n      doorStroke = 0;\r\n      closeAll();\r\n      //Serial.println(\"Disconnected  下限关闭\");\r\n    }\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n  if (extiOpen) {      //上升中断触发\r\n    extiOpen = false;  //复位\r\n    if (command == 1) {\r\n      doorStroke = 10;               //位置变为上限\r\n      command = 2;                   //命令变为停止\r\n      extiTime = 0;                  //防抖复位\r\n      digitalWrite(doorPower, LOW);  //断开电源接通\r\n      const char* tmp = \"上升中断触发了\";\r\n      client.publish(pubshTopic, tmp);\r\n    }\r\n    return;\r\n  }\r\n  if (extiClose) {      //下降中断触发\r\n    extiClose = false;  //复位\r\n    if (command == 0) {\r\n      doorStroke = 0;                 //位置变为下限\r\n      command = 2;                    //命令变为停止\r\n      extiTime = 0;                   //防抖复位\r\n      digitalWrite(doorPower, LOW);   //断开电源接通\r\n      digitalWrite(billboard1, LOW);  //关门就把广告灯箱关了\r\n      const char* tmp = \"下降中断触发了\";\r\n      client.publish(pubshTopic, tmp);\r\n    }\r\n    return;\r\n  }\r\n}\r\nvoid IRAM_ATTR handleInterruptOpen() {\r\n  unsigned long currentTime = millis();\r\n  if (command == 1) {     //上升命令\r\n    if (extiTime == 0) {  //上升中断 防抖 麻痹时间计数开始\r\n      extiTime = currentTime;\r\n    } else {\r\n      // 消抖处理\r\n      if (currentTime - extiTime > 1500) {\r\n        if (digitalRead(doorOpenEXTI) == HIGH) {\r\n          digitalWrite(doorOpen, LOW);\r\n          if (command == 1) {              //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表\r\n            digitalWrite(doorPower, LOW);  //断开电源接通\r\n          }\r\n          //开门中断\r\n          extiOpen = true;\r\n          //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n\r\nvoid IRAM_ATTR handleInterruptClose() {\r\n  unsigned long currentTime = millis();\r\n  if (command == 0) {\r\n    if (extiTime == 0) {\r\n      extiTime = currentTime;\r\n    } else {\r\n      // 消抖处理\r\n      if (currentTime - extiTime > 1500) {\r\n        if (digitalRead(doorCloseEXTI) == HIGH) {\r\n          digitalWrite(doorClose, LOW);\r\n          if (command == 0) {              //0关门 状态\r\n            digitalWrite(doorPower, LOW);  //断开电源接通\r\n          }\r\n          //关门中断\r\n          extiClose = true;\r\n          //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\nvoid mqttCallback(char* topic, byte* payload, unsigned int length) {\r\n  String msg;\r\n  for (int i = 0; i < length; i++) {\r\n    msg += (char)payload[i];\r\n  }\r\n  if (length > 0) {\r\n    if (msg == \"open\") {\r\n      if (doorStroke == 10) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,上升达到限制doorStroke == 10\";\r\n        client.publish(pubshTopic, tmp);\r\n      }\r\n      if (digitalRead(doorOpenEXTI) == HIGH) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,上升达到限制\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n\r\n      digitalWrite(doorPower, HIGH);  //主电源接通\r\n      delay(200);\r\n      //开 上升\r\n      const char* tmp = \"正在开门 ...\";\r\n      client.publish(pubshTopic, tmp);\r\n      digitalWrite(doorClose, LOW);\r\n      delay(50);\r\n      digitalWrite(doorOpen, HIGH);\r\n      runIng = true;\r\n      command = 1;\r\n\r\n      return;\r\n    }\r\n    if (msg == \"stop\") {\r\n      closeAll();\r\n      runIng = false;\r\n      command = 2;\r\n      String tempstr;\r\n      String rez;\r\n      //查询是否下降 限制,及时把状态改好\r\n      switch (doorStroke) {\r\n        case 10:\r\n          if (runIng) {  //如果在电机运行了按停止.说明已经在中间\r\n            doorStroke = 5;\r\n            client.publish(pubshTopic, \"悬停播报模式\");\r\n          } else {\r\n            tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n            rez = \"开门达到限制 \" + tempstr;\r\n            client.publish(pubshTopic, rez.c_str());\r\n          }\r\n          break;\r\n        case 0:\r\n          if (runIng) {\r\n            doorStroke = 5;\r\n            const char* tmp = \"悬停播报模式\";\r\n            client.publish(pubshTopic, tmp);\r\n          } else {\r\n            tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n            rez = \"关门达到限制 \" + tempstr;\r\n            client.publish(pubshTopic, rez.c_str());\r\n          }\r\n\r\n          break;\r\n        case 5:\r\n          rez = \"悬停播报模式\";\r\n          client.publish(pubshTopic, rez.c_str());\r\n          break;\r\n        default:\r\n          if (runIng) {\r\n            doorStroke = 5;\r\n            rez = \"悬停播报模式0\";\r\n            client.publish(pubshTopic, rez.c_str());\r\n          } else {\r\n            if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n              doorStroke = 5;\r\n              rez = \"悬停播报模式1\";\r\n              client.publish(pubshTopic, rez.c_str());\r\n            } else {\r\n              if (digitalRead(doorOpenEXTI) == HIGH) {\r\n                doorStroke = 10;\r\n                tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n                rez = \"开门达到限制1 \" + tempstr;\r\n                client.publish(pubshTopic, rez.c_str());\r\n              }\r\n              if (digitalRead(doorCloseEXTI) == HIGH) {\r\n                doorStroke = 0;\r\n                tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n                rez = \"关门达到限制1 \" + tempstr;\r\n                client.publish(pubshTopic, rez.c_str());\r\n              }\r\n            }\r\n          }\r\n      }\r\n      return;\r\n    }\r\n    if (msg == \"close\") {\r\n      //限位开关导通时为高电平达到限位\r\n      if (doorStroke == 0) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,关门达到限制doorStroke==0\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n      if (digitalRead(doorCloseEXTI) == HIGH) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,关门达到限制\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n      //主电源接通\r\n      digitalWrite(doorPower, HIGH);\r\n      delay(200);\r\n      //关 下降\r\n      const char* tmp = \"正在关门 ...\";\r\n      client.publish(pubshTopic, tmp);\r\n      digitalWrite(doorOpen, LOW);\r\n      digitalWrite(doorClose, HIGH);\r\n      runIng = true;\r\n      command = 0;\r\n      return;\r\n    }\r\n\r\n    if (msg == \"temperatureHumidity\") {\r\n      float temperature;\r\n      float humidity;\r\n      sensors_event_t event;\r\n      dht.temperature().getEvent(&event);\r\n      if (isnan(event.temperature)) {\r\n        Serial.println(F(\"Error reading temperature!\"));\r\n      } else {\r\n        temperature = event.temperature;\r\n      }\r\n      delay(500);\r\n      // Get humidity event and print its value.\r\n      dht.humidity().getEvent(&event);\r\n      if (isnan(event.relative_humidity)) {\r\n        Serial.println(F(\"Error reading humidity!\"));\r\n      } else {\r\n        humidity = event.relative_humidity;\r\n      }\r\n      String th = String(temperature) + \",\" + String(int(humidity));\r\n      client.publish(pubshTopic, th.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"billboard1:on\") {\r\n      guangGaoPaiBool = true;\r\n      digitalWrite(billboard1, HIGH);\r\n      String strtem = \"系统任务 广告牌1灯已开\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"billboard1:off\") {\r\n      guangGaoPaiBool = false;\r\n      digitalWrite(billboard1, LOW);\r\n      String strtem = \"系统任务 广告牌1灯已关\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"sleep\") {  //休眠3个小时\r\n      espSleep();\r\n      return;\r\n    }\r\n\r\n    if (msg == \"gpio17:on\") {  //播报开\r\n      digitalWrite(gpio17, HIGH);\r\n      String strtem = \"goip17已开\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"gpio17:off\") {  //播报关 按停止键也是会关的\r\n      digitalWrite(gpio17, LOW);\r\n      String strtem = \"gpio17已关\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n\r\n    if (msg == \"reboot\") {\r\n      ESP.restart();\r\n      return;\r\n    }\r\n    if (msg == \"version\") {\r\n      client.publish(pubshTopic, version.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"updateBin\") {\r\n      // 执行 OTA 更新前的准备工作\r\n      noInterrupts();                                         // 关闭全局中断\r\n      detachInterrupt(digitalPinToInterrupt(doorOpenEXTI));   // 禁用特定中断源（如果需要）\r\n      detachInterrupt(digitalPinToInterrupt(doorCloseEXTI));  // 禁用特定中断源（如果需要）\r\n      delay(200);\r\n      httpUpdate.onStart(update_started);\r\n      httpUpdate.onEnd(update_finished);\r\n      httpUpdate.onProgress(update_progress);\r\n      httpUpdate.onError(update_error);\r\n      t_httpUpdate_return ret = httpUpdate.update(wificlient, upBinUrl);\r\n\r\n      interrupts();  // 恢复全局中断\r\n      espReboot();\r\n    }\r\n    if (msg == \"billboard2:on\") {  //13脚高电平 广告牌2开启\r\n      digitalWrite(billboard2, HIGH);\r\n      const char* strtem = \"系统任务 广告牌2已开\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (msg == \"billboard2:off\") {  //13脚低电平 广告牌2关闭\r\n      digitalWrite(billboard2, LOW);\r\n      const char* tmp = \"系统任务 广告牌2已关\";\r\n      client.publish(pubshTopic, tmp);\r\n      return;\r\n    }\r\n    if (msg == \"light:on\") {\r\n      digitalWrite(light, HIGH);\r\n      const char* strtem = \"灯 已开\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (msg == \"light:off\") {\r\n      digitalWrite(light, LOW);\r\n      const char* strtem = \"灯 已关\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (!runIng) {\r\n      Serial.write((const uint8_t*)payload, length);\r\n    }\r\n    // client.publish(pub,str.c_str());\r\n  }\r\n}\r\n\r\nvoid reconnect() {\r\n  while (!client.connected()) {\r\n    String clientId = \"ESP32Client-\";\r\n    clientId += String(random(0xffff), HEX);\r\n    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空\r\n      //Serial.println(\"MQTT 连接成功\");\r\n      client.subscribe(subTopic);\r\n    } else {\r\n      Serial.print(\"连接失败, rc=\");\r\n      Serial.print(client.state());\r\n      delay(5000);\r\n    }\r\n  }\r\n}\r\n//关闭上升,下降,变压器主电源\r\nvoid closeAll() {\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  delay(200);\r\n  digitalWrite(doorPower, LOW);  //断开电源接通\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\nvoid espSleep() {\r\n  closeAll();\r\n  delay(200);\r\n  const char* tmp = \"door1进入休眠状态,3小时后醒...\";\r\n  client.publish(pubshTopic, tmp);\r\n  delay(200);\r\n  WiFi.disconnect();\r\n  myTicker.detach();\r\n  delay(200);\r\n  //进入深睡眠状态，将在 3 小时后唤醒\r\n  esp_sleep_enable_timer_wakeup(10800000000);\r\n  esp_deep_sleep_start();\r\n}\r\nvoid update_started() {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process started\");\r\n}\r\n\r\nvoid update_finished() {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process finished\");\r\n}\r\n\r\nvoid update_progress(int cur, int total) {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process\");\r\n}\r\n\r\nvoid update_error(int err) {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update fatal\");\r\n}\r\n\r\n//无网特殊状态下安全限位\r\nvoid html_whlie() {\r\n  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开\r\n  if (digitalRead(doorOpenEXTI) == HIGH || digitalRead(doorCloseEXTI) == HIGH) {\r\n    //关闭所有开关\r\n    closeAll();\r\n    Serial.println(\"达到上限\");\r\n    myTicker.detach();\r\n    delay(200);\r\n    ESP.restart();\r\n  }\r\n}\r\n\r\n\r\nvoid SetWifi() {\r\n  ssid = webserver.arg(0);\r\n  passwd = webserver.arg(1);\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.println(\"密码在前面\");\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* WifiPw = passwd.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webserver.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      webserver.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webserver.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handlejsonArr() {\r\n  webserver.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webserver.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\nvoid ApStart() {\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n\r\n  //启动AP模式,并设置账号和密码\r\n  WiFi.softAP(\"192.168.168.2\", \"\");\r\n}\r\nvoid webserverStart() {\r\n  //-----------------启动webserver网络服务--------------------\r\n  webserver.begin(80);               // 启用网络服务\r\n  webserver.on(\"/\", handlejsonArr);  // / 在HTTP中,表示请求首页服务,\r\n  webserver.on(\"/SetWifi\", SetWifi);\r\n  webserver.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid ForPing() {\r\n  //定时时间到了,先取消定时状态\r\n  myTicker.detach();\r\n  closeAll();\r\n  delay(500);\r\n\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.disconnect();\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n    Serial.println(\"扫描不到网络\");\r\n  }\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result = dateFile.readString();\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* Ssid = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(Ssid);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(Ssid, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int wifiTimeout = 0;\r\n  for (wifiTimeout; wifiTimeout < 30; wifiTimeout++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi-> timeout\");\r\n  return false;\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApStart();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  myTicker.detach();\r\n  delay(200);\r\n  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  webserverStart();\r\n  while (1) {\r\n    webserver.handleClient();  // 检查网络服务请求并处理\r\n  }\r\n}\r\n','',1),
(286,6,'什么是中断,以及使用方法','打个比方,\r\n你正在看电脑看动画片,这个时候有人打电话给你,这个时候你就暂停电脑的动画片播放,接电话,(接电话这个动作接中断)\r\nSTM32 中断   使用 PB11 和 PB10 作为中断下拉\r\n如果使用 PB10 做中断，那么 不能使用 PC10 ,PA10作为中断引脚 \r\n以此类推PB11 做中断，那么 不能使用 PC11 ,PA11作为中断引脚 \r\n点击cubeIDE 坐上 System Core->NVIC\r\n看到中间下方 有个 EXTI开头的就是中断  在后面打勾\r\nPreemption Priority  为抢占中断级 \r\nSub Priority   为释放中断级     这两个顾名思义，值越小 权利越高\r\n保存 生成代码\r\n在IED 左侧 Core ->Src 文件夹下会有一个?????_it.c\r\n打开文件  找到  void EXTI  开头的方法\r\n函数里会找到 \r\nvoid HAL_GPIO_EXTI_IRQHandler(uint16_t GPIO_Pin) 这个方法里\r\n HAL_GPIO_EXTI_Callback(GPIO_Pin); 这个就是我们要使用的中断回调方法\r\n\r\n回到main.c文件 \r\n在 int main(void) 方法上 使用\r\nvoid HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {}\r\n在这个方法里进行逻辑执行\r\n特别记住不能再中断里使用延迟\r\n\r\n','',1),
(287,6,'什么是看门狗','什么是看门狗','',1),
(289,6,'PWM','PWM','',1),
(290,6,'串口轮询发送与接收','Conectivity->USART1-> Mode (选 Asynchronous))\r\n使用 printf(\"串口打印 %s\\r\\n\",?);\r\nint __io_putchar(int ch) （这个方法在 Core->Src->syscalls.c）\r\nmain.c 创建方法\r\nint __io_putchar(int ch){\r\n HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 1000);\r\n return ch;\r\n}\r\n\r\nHAL_UART_Receive(&huart1,revbuf,sizeof(revbuf),HAL_MAX_DELAY);','',1),
(291,6,'串口中断收发','Conectivity->USART1-> Mode (选 Asynchronous))\r\n中间下方  点击 NVIC Settiings  ->  Enabled (打勾)\r\n保存生成代码\r\n\r\nHAL_UART_Receive_IT(&huart1, revbuf, sizeof(revbuf)); 必须 放在main方法 MX_USART1_UART_Init(); 后\r\n\r\n//下面这个方法必须声明在main 方法上面\r\nvoid HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){\r\n	if (huart->Instance == USART1) {\r\n		HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_14);\r\n		HAL_UART_Transmit_IT(&huart1, revbuf, sizeof(revbuf));\r\n		HAL_UART_Receive_IT(&huart1, revbuf, sizeof(revbuf));\r\n	}\r\n}','',1),
(292,6,'定时器分3种功能','定时器    就是一个计数器 分为向上，或向下计数\r\n基本定时器 （没有输入输出，只有定时中断，只支持向上计数）\r\n通用定时器 （具备基本定时器所有功能外， 还有多路独立通道，可用于输入铺货、输出比较）\r\n高级定时器  （具备通用定时器所有功能外，还有死区控制的互补信号输出，刹车输入（可用于电机控制，数字电源设计）\r\nF103 有 TIM1-TIM8\r\n在 STM32F103C8T6 中，TIM1 是高级定时器，TIM2  TIM3、TIM4 是通用定时器、\r\n基本定时器（TIM6和TIM7）\r\n通用定时器（TIME2到TIME5）\r\n高级定时器 （TIME1和TIME8）','',1),
(293,6,'基本定时器','72M（F）c8t6型号是8M 精准时钟->预分频器PSC(主要作用是使时钟变慢，是16为的从0-65535) \r\n->出来的时钟才是定时器频率（比如频率1hz 就是1秒，如果是1000hz 就是1毫秒） \r\n自动重装只ARR（就是计数器总计数值）\r\n-> 计数器CNT(溢出)->时间到\r\n定时时间（单位是秒）  T=(ARR+1)*(psc+1)/F(72m时钟源)\r\n\r\nTimers->TIME4->勾选Intemal clock\r\n中下方  Plesscaler(PSC-16bits value)  输入 7200-1   最大时钟是72M\r\nCounter Period 默认最大撒只是65535    这个时候要根据上面公式计算你的定时时间\r\n也就是说最大定时是6.5秒左右\r\n若你想实现60 分钟（3600 秒）的定时，仅凭单个 16 位定时器（如 STM32F103 的 TIM1-TIM4）无法直接完\r\n利用定时器的周期性中断，在中断服务函数中累计计数次数，达到目标时间后触发事件\r\n	 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){    放到main方法外面\r\n\r\n	 }\r\n\r\n\r\n               while (1) {｝ 上面 HAL_TIM_Base_Start_IT(&htim4); // 启动TIM4定时器并使能中断','',1),
(294,6,'uint8_t的意思','取值范围\r\n由于是8 位无符号数，其取值范围为：0 ~ 255\r\nu：表示无符号（Unsigned，即不存储负数，仅表示非负整数）。\r\nint：表示整数类型。\r\n8：表示该类型的位数为 8 位（Bit）。\r\n_t：表示这是通过typedef定义的类型别名。\r\n关键特性\r\n超出范围会导致溢出（Overflow），结果通常是取模运算后的余数（例如：255 + 1会溢出为0）。\r\n内存占用\r\n理论上占用1 字节（Byte，8 位）内存，但需注意：\r\n部分平台可能存在字节对齐等优化，但标准规定其宽度必须恰好为 8 位。\r\n与其他类型的区别\r\n对比int：\r\nint是编译器依赖的整数类型（通常为 16 位或 32 位），而uint8_t固定为 8 位。\r\nint是有符号类型（取值范围如 - 32768 ~ 32767），uint8_t是无符号类型。\r\n对比unsigned char：\r\n在多数平台上，uint8_t等同于unsigned char，但unsigned char属于字符类型，而uint8_t明确为整数类型，用途更清晰（例如用于表示字节数据）。\r\n典型应用场景\r\n存储字节数据\r\n例如：网络传输中的单个字节、文件读写中的字节流、硬件寄存器的 8 位数据等。\r\n表示无符号小整数\r\n如：枚举值、计数器（范围≤255）、颜色分量（RGB 值每个分量为 0~255）等。\r\n内存优化\r\n当需要严格控制变量占用内存（如嵌入式系统）时，使用固定宽度类型避免空间浪费。','',1),
(295,6,'通用定时器PWM','STM32F103C8T6\r\nTimers->TIM1\r\nClock Source -> Intemal Clock   \r\nChannel1  -> PWM Generation CH1  输出脚是 PA8\r\n往中下方看\r\nPrescaler(PSC-16 bits value) -> 8-1    //8M的\r\nCounter Period (AutoReload R....)   -> 500-1\r\nPWM Generation Channel1 下面的 Mode -> PWM mode 2\r\nCH Polarity ->High \r\n保存生成代码\r\nint main(void) {\r\n	uint8_t dir = 1; //方向\r\n	uint16_t val = 250;  //ccr 的值\r\n//在 MX_TIM1_Init(); 下面\r\nHAL_TIM_PWM_Start(&htim1, TIM_CHANNEL_1);\r\n	while (1) {\r\n		if (dir == 1) {\r\n			val++;\r\n		} else {\r\n			val--;\r\n		}\r\n		if (val >= 300) {\r\n			dir = 0;\r\n		}\r\n		if (val == 0) {\r\n			dir = 1;\r\n		}\r\n\r\n		__HAL_TIM_SET_COMPARE(&htim1,TIM_CHANNEL_1,val);\r\n		HAL_Delay(10);\r\n		/* USER CODE END WHILE */\r\n\r\n		/* USER CODE BEGIN 3 */\r\n	}\r\n\r\n\r\n','',1),
(296,4,'esp32WIFI下的MQTT','#include <WiFi.h>\r\n#include <PubSubClient.h>\r\n\r\nWiFiClient wificlient;\r\nString wifiSsid = \"webserver\";\r\nString wifiPasswd = \"www.nobiggie.cn\";\r\n\r\nconst char* mqtt_server = \"wulian.nobiggie.cn\";\r\nconst char* mqtt_username=\"aaaaaaa\";\r\nconst char* mqtt_password=\"abcdefg\";\r\nconst char* sub=\"wifi/card01\";\r\nconst char* pub=\"wifi/app01\";\r\nPubSubClient client(wificlient);\r\n\r\nvoid setup(void) {\r\n  Serial.begin(115200);\r\n  WiFi.begin(wifiSsid .c_str(), wifiPasswd .c_str());\r\n  WiFi.mode(WIFI_STA);\r\n\r\n  int ii = 0;\r\n  while (ii < 20 && WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    ii++;\r\n  }\r\n  Serial.println(\"wifi ok\");\r\n  client.setServer(mqtt_server, 1883);\r\n  client.setCallback(mqttCallback);\r\n}\r\n\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n\r\n  if (Serial.available()) {\r\n    String receivedString = Serial.readStringUntil(\'\\n\');\r\n    client.publish(\"wifi/app01\", receivedString.c_str());\r\n  }\r\n}\r\n\r\nvoid mqttCallback(char* topic, byte* payload, unsigned int length) {\r\n  String str;\r\n  for (int i = 0; i < length; i++) {\r\n    str += (char)payload[i];\r\n  }\r\n  str=\"收到消息:\"+str;\r\n  client.publish(pub,str.c_str());\r\n}\r\n\r\nvoid reconnect() {\r\n  while (!client.connected()) {\r\n    String clientId = \"ESP32Client-\";\r\n    clientId += String(random(0xffff), HEX);\r\n\r\n    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空\r\n      Serial.println(\"MQTT 连接成功\");\r\n      client.subscribe(sub);\r\n    } else {\r\n      Serial.print(\"连接失败, rc=\");\r\n      Serial.print(client.state());\r\n      delay(5000);\r\n    }\r\n  }\r\n}','',1),
(298,4,'遥控小车','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <PubSubClient.h>\r\n#include <ESP32Servo.h>\r\n\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include \"ESPmDNS.h\"\r\n#include <Ticker.h>\r\n\r\nServo myServo;\r\n\r\nconst int minPulse = 500;   // 最小脉宽(微秒)\r\nconst int maxPulse = 2500;  // 最大脉宽(微秒)\r\nint servoPin = 17;          //H4  默认使用，可根据实际连接修改\r\nconst int centerPos = 90;   // 舵机中心位置(对应物理90度)\r\n\r\nint baudRate = 115200;\r\n\r\nTicker myTicker;\r\nString flashWifi = \"/wifi.txt\";  //wifi密码存放文件\r\nWebServer webServer;\r\nString ssid;\r\nString passwd;\r\nString html;\r\n\r\nWiFiClient wificlient;\r\n\r\nconst char* mqtt_server = \"wulian.nobiggie.cn\";\r\nconst char* mqtt_username = \"ccccccc\";\r\nconst char* mqtt_password = \"abcdefg\";\r\nconst char* sub = \"wifi/car003/control\";\r\nconst char* pub = \"wifi/car003/status\";\r\nPubSubClient client(wificlient);\r\n\r\nint h8 = 21;  //车灯\r\n\r\n\r\nint pwmPin = 4;                   //PWM控制\r\nconst uint32_t pwmFreq = 1000;    // PWM频率1kHz（1-10kHz适合电机控制）\r\nconst uint8_t pwmResolution = 8;  // 8位分辨率（0-255）\r\n\r\nint PwmNow = 125;    //初始值 49%\r\nint fadeAmount = 5;  //累加数\r\n\r\nvoid setup(void) {\r\n  Serial.begin(baudRate);\r\n  pinMode(h8, OUTPUT);\r\n\r\n  pinMode(pwmPin, OUTPUT);\r\n\r\n  // 手动选择通道并绑定引脚\r\n  if (ledcAttach(pwmPin, pwmFreq, pwmResolution)) {\r\n    Serial.printf(\"PWM配置成功：频率=%dHz，分辨率=%dbits\\n\", pwmFreq, pwmResolution);\r\n  } else {\r\n    Serial.println(\"PWM配置失败，请检查参数或通道占用情况\");\r\n  }\r\n  ledcWrite(pwmPin, PwmNow);\r\n\r\n  if (FlashConnetWifi() == false) {\r\n    Serial.println(\"启动AP\");\r\n    HtmlSetWifi();\r\n  }\r\n  digitalWrite(h8, HIGH);\r\n  // 为舵机分配PWM定时器\r\n  ESP32PWM::allocateTimer(0);\r\n  myServo.setPeriodHertz(50);  // 标准舵机频率50Hz\r\n  delay(100);\r\n  // 初始化舵机连接\r\n  myServo.attach(servoPin, minPulse, maxPulse);\r\n\r\n  // 移动到中心位置\r\n  Serial.println(\"移动到中心位置\");\r\n  myServo.write(centerPos);\r\n  delay(500);\r\n  Serial.println(\"舵机已就绪\");\r\n\r\n  client.setServer(mqtt_server, 1883);\r\n  client.setCallback(mqttCallback);\r\n}\r\n\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n}\r\n\r\nvoid mqttCallback(char* topic, byte* payload, unsigned int length) {\r\n  String temp;\r\n  for (int i = 0; i < length; i++) {\r\n    temp += (char)payload[i];\r\n  }\r\n  Serial.println(temp);\r\n  if (temp.indexOf(\"forward\") != -1) {  //前进 66-100\r\n    int ln = temp.length();\r\n    int plusIndex = temp.indexOf(\"+\");\r\n    int angle = temp.substring(plusIndex + 1).toInt();\r\n    ledcWrite(pwmPin, angle);\r\n    return;\r\n  }\r\n  if (temp.indexOf(\"backward\") != -1) {  //后退 33-66\r\n    int ln = temp.length();\r\n    int plusIndex = temp.indexOf(\"+\");\r\n    int angle = temp.substring(plusIndex + 1).toInt();\r\n    ledcWrite(pwmPin, angle);\r\n    return;\r\n  }\r\n  if (temp.indexOf(\"left\") != -1) {  //left+转弯角度 0-45 即90度-48度\r\n    int ln = temp.length();\r\n    int plusIndex = temp.indexOf(\"+\");\r\n    int angle = temp.substring(plusIndex + 1).toInt();\r\n    angle = angle > 45 ? 45 : angle;\r\n    angle = 90 + angle;\r\n    rightGo(angle);\r\n    return;\r\n  }\r\n  if (temp.indexOf(\"right\") != -1) {  //right+转弯角度  0-45 即90度-135度\r\n    int ln = temp.length();\r\n    int plusIndex = temp.indexOf(\"+\");\r\n    int angle = temp.substring(plusIndex + 1).toInt();\r\n    angle = angle > 45 ? 45 : angle;\r\n    angle = 90 - angle;\r\n    leftGo(angle);\r\n    return;\r\n  }\r\n  if (temp == \"md\") {\r\n    aligning();\r\n  }\r\n  if (temp = \"stop\") {\r\n    aligning();\r\n  }\r\n\r\n\r\n  //temp = \"收到消息:\" + temp;\r\n  //client.publish(pub, temp.c_str());\r\n}\r\n\r\nvoid reconnect() {\r\n  while (!client.connected()) {\r\n    String clientId = \"ESP32Client-\";\r\n    clientId += String(random(0xffff), HEX);\r\n\r\n    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空\r\n      Serial.println(\"MQTT 连接成功\");\r\n      client.subscribe(sub);\r\n    } else {\r\n      Serial.print(\"连接失败, rc=\");\r\n      Serial.print(client.state());\r\n      delay(5000);\r\n    }\r\n  }\r\n}\r\n\r\nvoid leftGo(int angle) {\r\n  // 验证角度范围\r\n  if (angle < 45) angle = 45;\r\n  if (angle > 90) angle = 90;\r\n  myServo.write(angle);\r\n}\r\nvoid aligning() {\r\n  myServo.write(centerPos);  // 确保到达目标位置\r\n}\r\nvoid rightGo(int angle) {\r\n  if (angle < 90) angle = 90;\r\n  if (angle > 135) angle = 135;\r\n  myServo.write(angle);\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  apStart();\r\n  delay(1000);\r\n  Serial.println(\"start webserver\");\r\n  //5分后重启 防止停电后复电，路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式，所以这里只让AP激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  webserverStart();\r\n  while (1) {\r\n    webServer.handleClient();  //检查网络服务请求并处理\r\n  }\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  WiFi.disconnect();\r\n  Serial.begin(baudRate);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n  }\r\n}\r\nvoid setWifi() {\r\n  ssid = webServer.arg(0);\r\n  passwd = webServer.arg(1);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* Passwd = passwd.c_str();\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.begin(baudRate);\r\n  delay(500);\r\n  WiFi.begin(Ssid, Passwd);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webServer.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      webServer.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flashWifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n      Serial.println(\"HTML设置网络连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webServer.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handleRoot() {\r\n  webServer.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webServer.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\nvoid apStart() {\r\n  Serial.begin(baudRate);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n  //启动AP模式，并设置账号和密码\r\n  boolean result = WiFi.softAP(\"192.168.168.2\", \"192.168.168.2\");\r\n  if (result) {\r\n    Serial.println(String(\"Soft-AP IP address = \") + WiFi.softAPIP());\r\n  }\r\n}\r\nvoid webserverStart() {\r\n  webServer.begin(80);            // 启用网络服务\r\n  webServer.on(\"/\", handleRoot);  // / 在HTTP中，表示请求首页服务，\r\n  webServer.on(\"/setWifi\", setWifi);\r\n  webServer.onNotFound(handleNotFound);  // 当服务器没找到所需的服务，调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(baudRate);\r\n  delay(200);\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flashWifi) == false) {\r\n    Serial.println(\"NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flashWifi, \"r\");\r\n  String result;\r\n  for (int i = 0; i < dateFile.size(); i++) {\r\n    //Serial.print((char)dateFile.read());\r\n    result += (char)dateFile.read();\r\n  }\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* WifiName = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(WifiName);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(WifiName, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnet...\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"flash connet success\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"flash read error\");\r\n  return false;\r\n}','',1),
(299,4,'MQTT遥控大门与智能播报(中断)','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <PubSubClient.h>\r\n#include <HTTPUpdate.h>\r\n#include <OneWire.h>\r\n#include <DallasTemperature.h>\r\n#include <Adafruit_Sensor.h>\r\n#include <DHT.h>\r\n#include <DHT_U.h>\r\n#include <Ticker.h>\r\n#include <esp_sleep.h>\r\n#include <FS.h>\r\n#include <SPIFFS.h>\r\n#include <WebServer.h>\r\n#define doorOpen 22       //升\r\n#define doorClose 21      //降\r\n#define doorOpenEXTI 26   //升 中断\r\n#define doorCloseEXTI 25  //降 中断\r\n#define dhtPin 0          //温湿度传感器\r\n#define DHTTYPE DHT11     //传感器类型\r\nDHT_Unified dht(dhtPin, DHTTYPE);\r\n\r\n#define billboard1 4           //广告牌开关\r\n#define billboard2 13          //寄快递广告牌\r\nbool guangGaoPaiBool = false;  //guangGaoPaiBool为false代表没有开灯箱 true为已开启灯箱\r\n\r\n#define light 2       //灯\r\n#define broadcast 17  //播报电源开关\r\n#define doorPower 15  //电源总开关\r\nString version = \"1.24.7\";\r\nString ssid = \"\";                 //wifi账号\r\nString passwd = \"\";               //wifi密码\r\nString html;                      //存html\r\nString flash_wifi = \"/wifi.txt\";  //被读取的文件位置和名称\r\nWebServer webserver;\r\n\r\nvolatile int doorStroke = 5;   //记录门静止状态 处于什么位置 10为上限  0为下限 5为可以上升可降\r\nvolatile bool runIng = false;  //true 为电机正在运行\r\nvolatile int command = 2;      //此时执行的指令  0关门  1开门  2停止\r\n// 函数声明\r\nvoid IRAM_ATTR handleInterruptOpen();\r\nvoid IRAM_ATTR handleInterruptClose();\r\n\r\nvolatile bool extiOpen = false;   //true 代表上升中断触发了\r\nvolatile bool extiClose = false;  //true 代表下降中断触发了\r\nvolatile unsigned long extiTime = 0;\r\n\r\n\r\nWiFiClient wificlient;\r\n\r\nvolatile bool sayBool = false;  //ture为开启扫描状态\r\nTicker myTicker;\r\n\r\nconst char* mqtt_server = \"192.168.0.254\";\r\nconst char* mqtt_username = \"door1\";\r\nconst char* mqtt_password = \"abcdefg\";\r\nconst char* subTopic = \"wifi/door1/control\";\r\nconst char* pubshTopic = \"wifi/door1/data\";\r\nPubSubClient client(wificlient);\r\n\r\nString upBinUrl = \"http://\" + String(mqtt_server) + \"/tool/ota/GolangCpp/mydoor1.bin\";\r\nvoid setup(void) {\r\n  pinMode(doorOpen, OUTPUT);    //门上升\r\n  pinMode(doorClose, OUTPUT);   //门下降\r\n  pinMode(billboard1, OUTPUT);  //广播牌\r\n  pinMode(broadcast, OUTPUT);   //语音播报\r\n  pinMode(light, OUTPUT);\r\n  pinMode(doorPower, OUTPUT);  //总电源\r\n  pinMode(billboard2, OUTPUT);\r\n\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  digitalWrite(billboard1, LOW);\r\n  digitalWrite(broadcast, HIGH);\r\n  digitalWrite(light, LOW);\r\n  digitalWrite(doorPower, LOW);\r\n  digitalWrite(billboard2, LOW);\r\n\r\n  pinMode(doorOpenEXTI, INPUT_PULLUP);  //上拉电阻模式,默认高电平\r\n  pinMode(doorCloseEXTI, INPUT_PULLUP);\r\n\r\n  // 附加中断服务函数\r\n  attachInterrupt(digitalPinToInterrupt(doorOpenEXTI), handleInterruptOpen, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(doorCloseEXTI), handleInterruptClose, CHANGE);\r\n\r\n\r\n  Serial.begin(115200);\r\n  //pinMode(flash_rst, INPUT_PULLUP);\r\n  delay(200);\r\n  dht.begin();\r\n  sensor_t sensor;\r\n  dht.temperature().getSensor(&sensor);\r\n  dht.humidity().getSensor(&sensor);\r\n  delay(500);\r\n\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    SPIFFS.begin();\r\n    File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n    dataFile.println(\"webserver&www.nobiggie.cn\");\r\n    dataFile.close();\r\n  }\r\n\r\n  if (FlashConnetWifi() == false) {  //自动flash联网不成功 就启用 html配置wifi账号密码\r\n    HtmlSetWifi();\r\n  }\r\n  Serial.println(\"wifi ok\");\r\n  client.setServer(mqtt_server, 1883);\r\n  client.setCallback(mqttCallback);\r\n  delay(200);\r\n  if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n    doorStroke = 5;\r\n  } else {\r\n    if (digitalRead(doorOpenEXTI) == HIGH) {\r\n      doorStroke = 10;\r\n    }\r\n    if (digitalRead(doorCloseEXTI) == HIGH) {\r\n      doorStroke = 0;\r\n    }\r\n  }\r\n}\r\n\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    digitalWrite(doorOpen, LOW);\r\n    digitalWrite(doorClose, LOW);\r\n\r\n    //防止开启突然没有网络,lop循环检测不到状态 这里再进行检测\r\n    if (digitalRead(doorOpenEXTI) == HIGH && doorStroke != 10) {\r\n      doorStroke = 10;\r\n      closeAll();\r\n      //Serial.println(\"Disconnected  上限关闭\");\r\n    }\r\n    //查询是否下降 限制,及时把状态改好\r\n    if (digitalRead(doorCloseEXTI) == HIGH && doorStroke != 0) {\r\n      doorStroke = 0;\r\n      closeAll();\r\n      //Serial.println(\"Disconnected  下限关闭\");\r\n    }\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n  if (extiOpen) {      //上升中断触发\r\n    extiOpen = false;  //复位\r\n    if (command == 1) {\r\n      doorStroke = 10;               //位置变为上限\r\n      command = 2;                   //命令变为停止\r\n      extiTime = 0;                  //防抖复位\r\n      digitalWrite(doorPower, LOW);  //断开电源接通\r\n      const char* tmp = \"上升中断触发了\";\r\n      client.publish(pubshTopic, tmp);\r\n    }\r\n    return;\r\n  }\r\n  if (extiClose) {      //下降中断触发\r\n    extiClose = false;  //复位\r\n    if (command == 0) {\r\n      doorStroke = 0;                 //位置变为下限\r\n      command = 2;                    //命令变为停止\r\n      extiTime = 0;                   //防抖复位\r\n      digitalWrite(doorPower, LOW);   //断开电源接通\r\n      digitalWrite(billboard1, LOW);  //关门就把广告灯箱关了\r\n      const char* tmp = \"下降中断触发了\";\r\n      client.publish(pubshTopic, tmp);\r\n    }\r\n    return;\r\n  }\r\n}\r\nvoid IRAM_ATTR handleInterruptOpen() {\r\n  unsigned long currentTime = millis();\r\n  if (command == 1) {     //上升命令\r\n    if (extiTime == 0) {  //上升中断 防抖 麻痹时间计数开始\r\n      extiTime = currentTime;\r\n    } else {\r\n      // 消抖处理\r\n      if (currentTime - extiTime > 1500) {\r\n        if (digitalRead(doorOpenEXTI) == HIGH) {\r\n          digitalWrite(doorOpen, LOW);\r\n          if (command == 1) {              //此时执行中的状态  0关门  1开门  2停止 5中断正常触发模式  9什么也不代表\r\n            digitalWrite(doorPower, LOW);  //断开电源接通\r\n          }\r\n          //开门中断\r\n          extiOpen = true;\r\n          //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\n\r\n\r\nvoid IRAM_ATTR handleInterruptClose() {\r\n  unsigned long currentTime = millis();\r\n  if (command == 0) {\r\n    if (extiTime == 0) {\r\n      extiTime = currentTime;\r\n    } else {\r\n      // 消抖处理\r\n      if (currentTime - extiTime > 1500) {\r\n        if (digitalRead(doorCloseEXTI) == HIGH) {\r\n          digitalWrite(doorClose, LOW);\r\n          if (command == 0) {              //0关门 状态\r\n            digitalWrite(doorPower, LOW);  //断开电源接通\r\n          }\r\n          //关门中断\r\n          extiClose = true;\r\n          //中断服务程序（ISR）必须简短，避免调用耗时函数（如串口打印、网络通信），否则可能引发看门狗超时或系统重启\r\n        }\r\n      }\r\n    }\r\n  }\r\n}\r\nvoid mqttCallback(char* topic, byte* payload, unsigned int length) {\r\n  String msg;\r\n  for (int i = 0; i < length; i++) {\r\n    msg += (char)payload[i];\r\n  }\r\n  if (length > 0) {\r\n    if (msg == \"open\") {\r\n      if (doorStroke == 10) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,上升达到限制doorStroke == 10\";\r\n        client.publish(pubshTopic, tmp);\r\n      }\r\n      if (digitalRead(doorOpenEXTI) == HIGH) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,上升达到限制\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n\r\n      digitalWrite(doorPower, HIGH);  //主电源接通\r\n      delay(200);\r\n      //开 上升\r\n      const char* tmp = \"正在开门 ...\";\r\n      client.publish(pubshTopic, tmp);\r\n      digitalWrite(doorClose, LOW);\r\n      delay(50);\r\n      digitalWrite(doorOpen, HIGH);\r\n      runIng = true;\r\n      command = 1;\r\n\r\n      return;\r\n    }\r\n    if (msg == \"stop\") {\r\n      closeAll();\r\n      runIng = false;\r\n      command = 2;\r\n      String tempstr;\r\n      String rez;\r\n      //查询是否下降 限制,及时把状态改好\r\n      switch (doorStroke) {\r\n        case 10:\r\n          if (runIng) {  //如果在电机运行了按停止.说明已经在中间\r\n            doorStroke = 5;\r\n            client.publish(pubshTopic, \"悬停播报模式\");\r\n          } else {\r\n            tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n            rez = \"开门达到限制 \" + tempstr;\r\n            client.publish(pubshTopic, rez.c_str());\r\n          }\r\n          break;\r\n        case 0:\r\n          if (runIng) {\r\n            doorStroke = 5;\r\n            const char* tmp = \"悬停播报模式\";\r\n            client.publish(pubshTopic, tmp);\r\n          } else {\r\n            tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n            rez = \"关门达到限制 \" + tempstr;\r\n            client.publish(pubshTopic, rez.c_str());\r\n          }\r\n\r\n          break;\r\n        case 5:\r\n          rez = \"悬停播报模式\";\r\n          client.publish(pubshTopic, rez.c_str());\r\n          break;\r\n        default:\r\n          if (runIng) {\r\n            doorStroke = 5;\r\n            rez = \"悬停播报模式0\";\r\n            client.publish(pubshTopic, rez.c_str());\r\n          } else {\r\n            if (digitalRead(doorOpenEXTI) == LOW && digitalRead(doorCloseEXTI) == LOW) {\r\n              doorStroke = 5;\r\n              rez = \"悬停播报模式1\";\r\n              client.publish(pubshTopic, rez.c_str());\r\n            } else {\r\n              if (digitalRead(doorOpenEXTI) == HIGH) {\r\n                doorStroke = 10;\r\n                tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n                rez = \"开门达到限制1 \" + tempstr;\r\n                client.publish(pubshTopic, rez.c_str());\r\n              }\r\n              if (digitalRead(doorCloseEXTI) == HIGH) {\r\n                doorStroke = 0;\r\n                tempstr = guangGaoPaiBool == true ? \"灯箱亮\" : \"灯箱关\";\r\n                rez = \"关门达到限制1 \" + tempstr;\r\n                client.publish(pubshTopic, rez.c_str());\r\n              }\r\n            }\r\n          }\r\n      }\r\n      return;\r\n    }\r\n    if (msg == \"close\") {\r\n      //限位开关导通时为高电平达到限位\r\n      if (doorStroke == 0) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,关门达到限制doorStroke==0\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n      if (digitalRead(doorCloseEXTI) == HIGH) {\r\n        runIng = false;\r\n        command = 2;\r\n        const char* tmp = \"无效,关门达到限制\";\r\n        client.publish(pubshTopic, tmp);\r\n        return;\r\n      }\r\n      //主电源接通\r\n      digitalWrite(doorPower, HIGH);\r\n      delay(200);\r\n      //关 下降\r\n      const char* tmp = \"正在关门 ...\";\r\n      client.publish(pubshTopic, tmp);\r\n      digitalWrite(doorOpen, LOW);\r\n      digitalWrite(doorClose, HIGH);\r\n      runIng = true;\r\n      command = 0;\r\n      return;\r\n    }\r\n\r\n    if (msg == \"temperatureHumidity\") {\r\n      float temperature;\r\n      float humidity;\r\n      sensors_event_t event;\r\n      dht.temperature().getEvent(&event);\r\n      if (isnan(event.temperature)) {\r\n        Serial.println(F(\"Error reading temperature!\"));\r\n      } else {\r\n        temperature = event.temperature;\r\n      }\r\n      delay(500);\r\n      // Get humidity event and print its value.\r\n      dht.humidity().getEvent(&event);\r\n      if (isnan(event.relative_humidity)) {\r\n        Serial.println(F(\"Error reading humidity!\"));\r\n      } else {\r\n        humidity = event.relative_humidity;\r\n      }\r\n      String th = String(temperature) + \",\" + String(int(humidity));\r\n      client.publish(pubshTopic, th.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"billboard1:on\") {\r\n      guangGaoPaiBool = true;\r\n      digitalWrite(billboard1, HIGH);\r\n      String strtem = \"系统任务 广告牌1灯已开\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"billboard1:off\") {\r\n      guangGaoPaiBool = false;\r\n      digitalWrite(billboard1, LOW);\r\n      String strtem = \"系统任务 广告牌1灯已关\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"sleep\") {  //休眠3个小时\r\n      espSleep();\r\n      return;\r\n    }\r\n\r\n    if (msg == \"broadcast:on\") {  //播报开\r\n      sayBool = true;\r\n      digitalWrite(broadcast, LOW);\r\n      String strtem = \"播报已开\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"broadcast:off\") {  //播报关 按停止键也是会关的\r\n      sayBool = false;\r\n      digitalWrite(broadcast, HIGH);\r\n      String strtem = \"播报已关\";\r\n      client.publish(pubshTopic, strtem.c_str());\r\n      return;\r\n    }\r\n\r\n    if (msg == \"reboot\") {\r\n      ESP.restart();\r\n      return;\r\n    }\r\n    if (msg == \"version\") {\r\n      client.publish(pubshTopic, version.c_str());\r\n      return;\r\n    }\r\n    if (msg == \"updateBin\") {\r\n      // 执行 OTA 更新前的准备工作\r\n      noInterrupts();                                         // 关闭全局中断\r\n      detachInterrupt(digitalPinToInterrupt(doorOpenEXTI));   // 禁用特定中断源（如果需要）\r\n      detachInterrupt(digitalPinToInterrupt(doorCloseEXTI));  // 禁用特定中断源（如果需要）\r\n      delay(200);\r\n      httpUpdate.onStart(update_started);\r\n      httpUpdate.onEnd(update_finished);\r\n      httpUpdate.onProgress(update_progress);\r\n      httpUpdate.onError(update_error);\r\n      t_httpUpdate_return ret = httpUpdate.update(wificlient, upBinUrl);\r\n\r\n      interrupts();  // 恢复全局中断\r\n      espReboot();\r\n    }\r\n    if (msg == \"billboard2:on\") {  //13脚高电平 广告牌2开启\r\n      digitalWrite(billboard2, HIGH);\r\n      const char* strtem = \"系统任务 广告牌2已开\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (msg == \"billboard2:off\") {  //13脚低电平 广告牌2关闭\r\n      digitalWrite(billboard2, LOW);\r\n      const char* tmp = \"系统任务 广告牌2已关\";\r\n      client.publish(pubshTopic, tmp);\r\n      return;\r\n    }\r\n    if (msg == \"light:on\") {\r\n      digitalWrite(light, HIGH);\r\n      const char* strtem = \"灯 已开\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (msg == \"light:off\") {\r\n      digitalWrite(light, LOW);\r\n      const char* strtem = \"灯 已关\";\r\n      client.publish(pubshTopic, strtem);\r\n      return;\r\n    }\r\n    if (sayBool) {\r\n      if (!runIng) {\r\n        Serial.write((const uint8_t*)payload, length);\r\n      }\r\n      return;\r\n    }\r\n    // client.publish(pub,str.c_str());\r\n  }\r\n}\r\n\r\nvoid reconnect() {\r\n  while (!client.connected()) {\r\n    String clientId = \"ESP32Client-\";\r\n    clientId += String(random(0xffff), HEX);\r\n    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空\r\n      //Serial.println(\"MQTT 连接成功\");\r\n      client.subscribe(subTopic);\r\n    } else {\r\n      Serial.print(\"连接失败, rc=\");\r\n      Serial.print(client.state());\r\n      delay(5000);\r\n    }\r\n  }\r\n}\r\n//关闭上升,下降,变压器主电源\r\nvoid closeAll() {\r\n  digitalWrite(doorOpen, LOW);\r\n  digitalWrite(doorClose, LOW);\r\n  delay(200);\r\n  digitalWrite(doorPower, LOW);   //断开电源接通\r\n  sayBool = false;                //扫描状态关闭\r\n  digitalWrite(broadcast, HIGH);  //扫描关\r\n}\r\nvoid espReboot() {\r\n  ESP.restart();\r\n}\r\nvoid espSleep() {\r\n  closeAll();\r\n  delay(200);\r\n  const char* tmp = \"door1进入休眠状态,3小时后醒...\";\r\n  client.publish(pubshTopic, tmp);\r\n  delay(200);\r\n  WiFi.disconnect();\r\n  myTicker.detach();\r\n  delay(200);\r\n  //进入深睡眠状态，将在 3 小时后唤醒\r\n  esp_sleep_enable_timer_wakeup(10800000000);\r\n  esp_deep_sleep_start();\r\n}\r\nvoid update_started() {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process started\");\r\n}\r\n\r\nvoid update_finished() {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process finished\");\r\n}\r\n\r\nvoid update_progress(int cur, int total) {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update process\");\r\n}\r\n\r\nvoid update_error(int err) {\r\n  // client.publish(pubshTopic, \"CALLBACK:  HTTP update fatal\");\r\n}\r\n\r\n//无网特殊状态下安全限位\r\nvoid html_whlie() {\r\n  //操作运行时 上升达到限位了,并且上次状态不是上升上限, 并且执行状态是上升 限位开关断开\r\n  if (digitalRead(doorOpenEXTI) == HIGH || digitalRead(doorCloseEXTI) == HIGH) {\r\n    //关闭所有开关\r\n    closeAll();\r\n    Serial.println(\"达到上限\");\r\n    myTicker.detach();\r\n    delay(200);\r\n    ESP.restart();\r\n  }\r\n}\r\n\r\n\r\nvoid SetWifi() {\r\n  ssid = webserver.arg(0);\r\n  passwd = webserver.arg(1);\r\n  String sp = ssid + \"&\" + passwd;\r\n  Serial.println(sp);\r\n  Serial.println(\"密码在前面\");\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  const char* Ssid = ssid.c_str();\r\n  const char* WifiPw = passwd.c_str();\r\n  WiFi.begin(Ssid, WifiPw);\r\n  WiFi.mode(WIFI_STA);\r\n  delay(200);\r\n  Serial.println(\"wifi_connet\");\r\n  int ii = 0;\r\n  for (ii; ii < 20; ii++) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      webserver.send(200, \"text/plane\", \"Wifi密码正确,ESP准备重启\");\r\n      webserver.close();\r\n      delay(500);\r\n      WiFi.disconnect();\r\n      delay(500);\r\n\r\n      //写入数据\r\n      SPIFFS.format();\r\n      delay(500);\r\n      SPIFFS.begin();\r\n      File dataFile = SPIFFS.open(flash_wifi, \"w\");\r\n      dataFile.println(sp);\r\n      dataFile.close();\r\n\r\n      Serial.println(\"HTML连接成功\");\r\n      delay(500);\r\n      ESP.restart();\r\n    }\r\n  }\r\n  webserver.send(200, \"text/plane\", \"Wifi密码错误,请重新输入\");\r\n}\r\n//------- 展示页面-------\r\nvoid handlejsonArr() {\r\n  webserver.send(500, \"text/html\", html);\r\n}\r\nvoid handleNotFound(void) {\r\n  webserver.send(404, \"text/plain\", \"404-The page is not Found!\");\r\n}\r\n\r\nvoid ApStart() {\r\n  WiFi.disconnect();\r\n  delay(500);\r\n  Serial.begin(115200);\r\n  WiFi.mode(WIFI_AP);\r\n  IPAddress local_IP(192, 168, 168, 2);\r\n  IPAddress gateway(192, 168, 168, 1);\r\n  IPAddress subnet(255, 255, 255, 0);\r\n  WiFi.softAPConfig(local_IP, gateway, subnet);\r\n\r\n  //启动AP模式,并设置账号和密码\r\n  WiFi.softAP(\"192.168.168.2\", \"\");\r\n}\r\nvoid webserverStart() {\r\n  //-----------------启动webserver网络服务--------------------\r\n  webserver.begin(80);               // 启用网络服务\r\n  webserver.on(\"/\", handlejsonArr);  // / 在HTTP中,表示请求首页服务,\r\n  webserver.on(\"/SetWifi\", SetWifi);\r\n  webserver.onNotFound(handleNotFound);  // 当服务器没找到所需的服务,调用404函数\r\n  Serial.println(\"webserver started\");\r\n}\r\n\r\nvoid ForPing() {\r\n  //定时时间到了,先取消定时状态\r\n  myTicker.detach();\r\n  closeAll();\r\n  delay(500);\r\n\r\n  ESP.restart();\r\n}\r\n//扫描附件WIFI\r\nvoid WifiScan() {\r\n  Serial.begin(115200);\r\n  WiFi.disconnect();\r\n  WiFi.mode(WIFI_STA);\r\n  delay(100);\r\n  int n = WiFi.scanNetworks();\r\n  if (n > 0) {\r\n    Serial.println(n);\r\n    String a = \"附近WiFi <select id=\\\"ssid\\\">\";\r\n    for (int i = 0; i < n; i++) {\r\n      Serial.println(WiFi.SSID(i));\r\n      a = a + \"<option value=\\\"\" + WiFi.SSID(i) + \"\\\">\" + WiFi.SSID(i) + \"</option>\";\r\n      delay(50);\r\n    }\r\n    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>\";\r\n    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>\";\r\n    a = a + \"</select>\";\r\n    html = HtmlStart + a + HtmlEnd;\r\n  } else {\r\n    html = \"扫描不到网络\";\r\n    Serial.println(\"扫描不到网络\");\r\n  }\r\n}\r\nbool FlashConnetWifi() {\r\n  Serial.begin(115200);\r\n  delay(500);\r\n  //是否存在数据\r\n  SPIFFS.begin();\r\n  if (SPIFFS.exists(flash_wifi) == false) {\r\n    Serial.print(flash_wifi);\r\n    Serial.println(\" NOT FOUND\");\r\n    return false;\r\n  }\r\n\r\n  File dateFile = SPIFFS.open(flash_wifi, \"r\");\r\n  String result = dateFile.readString();\r\n  result.trim();\r\n  Serial.println(result);\r\n  int i = result.indexOf(\"&\");\r\n  String wifiname = result.substring(0, i);\r\n  String wifipw = result.substring(i + 1);\r\n  const char* Ssid = wifiname.c_str();\r\n  const char* WifiPw = wifipw.c_str();\r\n  Serial.println(Ssid);\r\n  Serial.println(WifiPw);\r\n  WiFi.begin(Ssid, WifiPw);\r\n  delay(500);\r\n  WiFi.mode(WIFI_STA);\r\n  Serial.println(\"FlashConnetWifi->connet\");\r\n  int wifiTimeout = 0;\r\n  for (wifiTimeout; wifiTimeout < 30; wifiTimeout++) {\r\n    delay(500);\r\n    Serial.print(\" . \");\r\n    if (WiFi.status() == WL_CONNECTED) {\r\n      Serial.println(\"FlashConnetWifi 闪存配置连接成功\");\r\n      return true;\r\n    }\r\n  }\r\n  Serial.println(\"FlashConnetWifi-> timeout\");\r\n  return false;\r\n}\r\n\r\nvoid HtmlSetWifi() {\r\n  WifiScan();\r\n  ApStart();\r\n  delay(1000);\r\n  Serial.println(\"启动网页\");\r\n  myTicker.detach();\r\n  delay(200);\r\n  //5分后重启 防止停电后复电,路由器连接WIFI速度慢于ESP导致ESP连接不到路由器进入无限AP模式,所以这里只让AP 激活5分钟后就重启\r\n  myTicker.once(300, espReboot);\r\n  delay(200);\r\n  webserverStart();\r\n  while (1) {\r\n    webserver.handleClient();  // 检查网络服务请求并处理\r\n  }\r\n}\r\n','',1),
(301,3,'mqtt 客户端类','package models\r\n\r\nimport (\r\n	\"bytes\"\r\n	\"fmt\"\r\n	\"log\"\r\n	\"sync\"\r\n	\"time\"\r\n\r\n	mqtt \"github.com/eclipse/paho.mqtt.golang\"\r\n)\r\n\r\ntype MqttClient struct {\r\n	Client        mqtt.Client\r\n	opts          *mqtt.ClientOptions\r\n	Username      string\r\n	Password      string\r\n	ClientId      string\r\n	Broker        string\r\n	Reconnecting  bool            // 是否正在重连\r\n	Subscriptions map[string]byte // 存储已订阅的主题和QoS\r\n	reconnectLock sync.Mutex\r\n}\r\n\r\nfunc (ggg *MqttClient) Setup() *MqttClient {\r\n	ggg.opts = mqtt.NewClientOptions().AddBroker(ggg.Broker) //tcp://192.168.1.2:1883\r\n	ggg.opts.SetUsername(ggg.Username)\r\n	ggg.opts.SetPassword(ggg.Password)\r\n	ggg.opts.SetClientID(ggg.ClientId)\r\n	ggg.opts.SetDefaultPublishHandler(func(c mqtt.Client, m mqtt.Message) {\r\n		log.Printf(\"[%s] 收到消息: 主题=%s, 内容=%s\\n\", ggg.Username, m.Topic(), m.Payload())\r\n	})\r\n	// 设置连接回调\r\n	ggg.opts.OnConnect = func(client mqtt.Client) {\r\n		log.Printf(\"设备 %s 连接成功\\n\", ggg.Username)\r\n		ggg.Reconnecting = false\r\n		// 重连后恢复订阅\r\n		ggg.reconnectLock.Lock()\r\n		defer ggg.reconnectLock.Unlock()\r\n		if ggg.Subscriptions != nil {\r\n			for topic, qos := range ggg.Subscriptions {\r\n				token := client.Subscribe(topic, qos, nil)\r\n				token.Wait()\r\n				if token.Error() != nil {\r\n					log.Printf(\"恢复订阅失败 %s: %v\\n\", topic, token.Error())\r\n				} else {\r\n					log.Printf(\"恢复订阅成功 %s\\n\", topic)\r\n				}\r\n			}\r\n		}\r\n	}\r\n\r\n	// 设置断开连接回调\r\n	ggg.opts.OnConnectionLost = func(client mqtt.Client, err error) {\r\n		log.Printf(\"设备 %s 连接丢失: %v\\n\", ggg.Username, err)\r\n		ggg.startReconnect()\r\n	}\r\n\r\n	ggg.Client = mqtt.NewClient(ggg.opts)\r\n	//连接并等待连接成功\r\n	if token := ggg.Client.Connect(); token.WaitTimeout(3 * time.Second) {\r\n		if token.Error() != nil {\r\n			return nil\r\n		}\r\n	} else {\r\n		return nil\r\n	}\r\n\r\n	return ggg\r\n}\r\nfunc (ggg *MqttClient) Publish(topic string, qos int, retained bool, payload interface{}) error {\r\n	if !ggg.Client.IsConnected() {\r\n		return fmt.Errorf(\"MQTT客户端未连接\")\r\n	}\r\n	// 校验 QoS 合法性\r\n	if qos < 0 || qos > 2 {\r\n		return fmt.Errorf(\"invalid QoS level: %d\", qos)\r\n	}\r\n\r\n	var pubPayload interface{}\r\n	switch p := payload.(type) {\r\n	case string:\r\n		pubPayload = p\r\n	case []byte:\r\n		pubPayload = p\r\n	case bytes.Buffer:\r\n		pubPayload = p.Bytes()\r\n	default:\r\n		return fmt.Errorf(\"unknown payload type\")\r\n	}\r\n\r\n	// 校验 payload 长度（最大 268,435,455 字节）\r\n	if b, ok := pubPayload.([]byte); ok {\r\n		if len(b) > 268435455 {\r\n			return fmt.Errorf(\"payload too large for MQTT publish\")\r\n		}\r\n	} else if s, ok := pubPayload.(string); ok {\r\n		if len(s) > 268435455 {\r\n			return fmt.Errorf(\"payload too large for MQTT publish\")\r\n		}\r\n	}\r\n\r\n	token := ggg.Client.Publish(topic, byte(qos), retained, payload)\r\n\r\n	// 等待发布结果，最多等待 3 秒\r\n	if !token.WaitTimeout(3 * time.Second) {\r\n		return fmt.Errorf(\"publish timeout\")\r\n	}\r\n\r\n	if err := token.Error(); err != nil {\r\n		return fmt.Errorf(\"publish failed: %v\", err)\r\n	}\r\n\r\n	return nil\r\n}\r\nfunc (ggg *MqttClient) Subscribe(topic string, qos int, callback mqtt.MessageHandler) error {\r\n	if !ggg.Client.IsConnected() {\r\n		return fmt.Errorf(\"MQTT客户端未连接\")\r\n	}\r\n	// 校验 QoS 合法性\r\n	if qos < 0 || qos > 2 {\r\n		return fmt.Errorf(\"invalid QoS level: %d\", qos)\r\n	}\r\n	// 如果没有提供回调函数，使用默认处理\r\n	if callback == nil {\r\n		callback = func(client mqtt.Client, msg mqtt.Message) {\r\n			opts1 := client.OptionsReader()\r\n			log.Printf(\"[%s] 收到订阅消息: 主题=%s, 内容=%s\\n\", opts1.Username(), msg.Topic(), msg.Payload())\r\n		}\r\n	}\r\n	if ggg.Subscriptions == nil {\r\n		ggg.Subscriptions = make(map[string]byte)\r\n	}\r\n	// 存储订阅信息以便重连后恢复\r\n	ggg.reconnectLock.Lock()\r\n	ggg.Subscriptions[topic] = byte(qos)\r\n	ggg.reconnectLock.Unlock()\r\n\r\n	token := ggg.Client.Subscribe(topic, byte(qos), callback)\r\n	if token.Wait() && token.Error() != nil {\r\n		return fmt.Errorf(\"订阅失败: %v\", token.Error())\r\n	}\r\n\r\n	return nil\r\n}\r\nfunc (m *MqttClient) Unsubscribe(topic string) error {\r\n	m.reconnectLock.Lock()\r\n	delete(m.Subscriptions, topic)\r\n	m.reconnectLock.Unlock()\r\n\r\n	token := m.Client.Unsubscribe(topic)\r\n	token.Wait()\r\n	return token.Error()\r\n}\r\nfunc (ggg *MqttClient) Disconnect() { // 断开连接\r\n	ggg.reconnectLock.Lock()\r\n	defer ggg.reconnectLock.Unlock()\r\n	ggg.Reconnecting = false\r\n\r\n	if ggg.Client.IsConnected() {\r\n		ggg.Client.Disconnect(250) // 优雅断开连接，等待250ms\r\n		log.Printf(\"客户端 %s 已断开连接\\n\", ggg.Username)\r\n	}\r\n}\r\n\r\n// 开始重连逻辑\r\nfunc (m *MqttClient) startReconnect() {\r\n	m.reconnectLock.Lock()\r\n	defer m.reconnectLock.Unlock()\r\n\r\n	// 防止重复启动重连\r\n	if m.Reconnecting {\r\n		return\r\n	}\r\n\r\n	m.Reconnecting = true\r\n\r\n	// 启动重连协程\r\n	go func() {\r\n		retryCount := 0\r\n		maxRetries := 10\r\n\r\n		for {\r\n			// 指数退避策略\r\n			retryCount++\r\n			d := 0\r\n			if retryCount*2 < 30 {\r\n				d = retryCount * 2\r\n			} else {\r\n				d = 30\r\n			}\r\n			backoffTime := time.Second * time.Duration(d)\r\n			log.Printf(\"重连尝试 %d/%d，%s 后重试\\n\", retryCount, maxRetries, backoffTime)\r\n			time.Sleep(backoffTime)\r\n\r\n			m.reconnectLock.Lock()\r\n			if !m.Reconnecting {\r\n				m.reconnectLock.Unlock()\r\n				return\r\n			}\r\n			m.reconnectLock.Unlock()\r\n\r\n			log.Printf(\"尝试重连到 %s...\\n\", m.Broker)\r\n\r\n			// 创建新客户端实例\r\n			m.Client = mqtt.NewClient(m.opts)\r\n			token := m.Client.Connect()\r\n			token.Wait()\r\n\r\n			if token.Error() == nil {\r\n				log.Printf(\"重连成功\\n\")\r\n				m.Reconnecting = false\r\n				return\r\n			}\r\n\r\n			log.Printf(\"重连失败: %v\\n\", token.Error())\r\n\r\n			if retryCount >= maxRetries {\r\n				log.Printf(\"达到最大重连次数，停止重连\\n\")\r\n				m.Reconnecting = false\r\n				return\r\n			}\r\n		}\r\n	}()\r\n}\r\n','',1),
(302,3,'电脑客户端服务器定时','package main\r\n\r\nimport (\r\n	\"log\"\r\n	\"net\"\r\n	\"os/exec\"\r\n	\"pcmqtt/models\"\r\n	\"strings\"\r\n	\"syscall\"\r\n	\"time\"\r\n	\"unsafe\"\r\n\r\n	mqtt \"github.com/eclipse/paho.mqtt.golang\"\r\n)\r\n\r\nvar windowsAlert = WindowsAlert{}\r\nvar mqttClient = models.MqttClient{}\r\n\r\nfunc main() {\r\n	mac := GetMac()\r\n	mac = strings.TrimSpace(mac)\r\n	mqttClient := models.MqttClient{\r\n		Broker:        \"tcp://192.168.0.254:1883\",\r\n		ClientId:      \"pcmqtt-\" + mac,\r\n		Username:      mac,\r\n		Password:      mac,\r\n		Reconnecting:  false,\r\n		Subscriptions: map[string]byte{\"computer/control\": 0},\r\n	}\r\n	mqttClient.Setup()\r\n	mqttClient.Subscribe(\"computer/control\", 0, func(client mqtt.Client, msg mqtt.Message) {\r\n		message := string(msg.Payload())\r\n		log.Println(message)\r\n		if message == mqttClient.Username+\":shutdown\" {\r\n			arg := []string{\"-s\", \"-t\", \"0\"}\r\n			cmd := exec.Command(\"shutdown\", arg...)\r\n			cmd.CombinedOutput()\r\n		} else {\r\n			go func() {\r\n				windowsAlert.ShowMessage(\"服务器提示\", message)\r\n			}()\r\n		}\r\n	})\r\n	for {\r\n	}\r\n}\r\nfunc GetMac() string {\r\n	netInterfaces, err := net.Interfaces()\r\n	if err != nil {\r\n		return \"\"\r\n	}\r\n	for _, i2 := range netInterfaces {\r\n		//fmt.Println(i2.Name)\r\n		mac := i2.HardwareAddr\r\n		//fmt.Println(mac)\r\n		return mac.String()\r\n	}\r\n	return \"\"\r\n}\r\n\r\ntype WindowsAlert struct {\r\n}\r\n\r\nfunc (ggg *WindowsAlert) StrPtr(s string) uintptr {\r\n	utf16Ptr, _ := syscall.UTF16PtrFromString(s)\r\n	return uintptr(unsafe.Pointer(utf16Ptr))\r\n}\r\nfunc (ggg *WindowsAlert) ShowMessage(title, text string) {\r\n	user32dll, _ := syscall.LoadLibrary(\"user32.dll\")\r\n	user32 := syscall.NewLazyDLL(\"user32.dll\")\r\n	MessageBoxW := user32.NewProc(\"MessageBoxW\")\r\n	defer syscall.FreeLibrary(user32dll)\r\n	MessageBoxW.Call(uintptr(0), ggg.StrPtr(text), ggg.StrPtr(title), uintptr(0))\r\n	time.Sleep(time.Second * 3)\r\n}\r\n','',1),
(303,3,'golang gin  接受json','type mqttJSON struct {\r\n	Id             int\r\n	Username       string\r\n	Password       string\r\n	PublishTopic   string\r\n	SubscribeTopic string\r\n	Nickname       string\r\n}\r\n\r\nfunc (ggg KeepInTouch) MqttUsersAdd(ctx *gin.Context) {\r\n	var mqttUsersJson = &mqttJSON{}\r\n	if err := ctx.ShouldBindJSON(&mqttUsersJson); err != nil {\r\n		ctx.String(200, \"接收失败\")\r\n		return\r\n	}\r\n	username := mqttUsersJson.Username\r\n	password := mqttUsersJson.Password\r\n	publishTopic := mqttUsersJson.PublishTopic\r\n	subscribeTopic := mqttUsersJson.SubscribeTopic\r\n	nickname := mqttUsersJson.Nickname\r\n	fmt.Println(username)\r\n结构体 里面的 变量 首字母必须要大写','',1),
(307,3,'mqttOverWs(mqttServer 结构体)','package models\r\n\r\nimport (\r\n	\"context\"\r\n	\"crypto/tls\"\r\n	\"errors\"\r\n	\"fmt\"\r\n	\"io\"\r\n	\"net\"\r\n	\"net/http\"\r\n	\"os\"\r\n	\"os/signal\"\r\n	\"strings\"\r\n	\"syscall\"\r\n	\"time\"\r\n\r\n	\"github.com/DrmagicE/gmqtt\"\r\n	_ \"github.com/DrmagicE/gmqtt/persistence\"\r\n	\"github.com/DrmagicE/gmqtt/pkg/packets\"\r\n	mqttServer \"github.com/DrmagicE/gmqtt/server\"\r\n	_ \"github.com/DrmagicE/gmqtt/topicalias/fifo\"\r\n)\r\n\r\ntype MqttServerZ struct {\r\n	CertFile string\r\n	KeyFile  string\r\n}\r\n\r\nvar postUrl = \"http://localhost/api/mqttserver_state\"\r\n\r\nfunc (ggg *MqttServerZ) MqttServerTLS() {\r\n	// 加载TLS证书和私钥\r\n	cert, err := tls.LoadX509KeyPair(ggg.CertFile, ggg.KeyFile)\r\n	if err != nil {\r\n		fmt.Printf(\"加载TLS证书失败: %v\\n\", err)\r\n		return\r\n	}\r\n\r\n	// 创建TLS配置\r\n	tlsConfig := &tls.Config{\r\n		Certificates: []tls.Certificate{cert},\r\n		ClientAuth:   tls.NoClientCert, // 单向认证\r\n		MinVersion:   tls.VersionTLS12, // 最低TLS版本\r\n	}\r\n\r\n	//创建TLS监听器，监听8083端口\r\n	ln, err := tls.Listen(\"tcp\", \":8883\", tlsConfig)\r\n	if err != nil {\r\n		fmt.Printf(\"创建TLS监听器失败: %v\\n\", err)\r\n		return\r\n	}\r\n	// 设置MQTT钩子\r\n	hooks := mqttServer.Hooks{\r\n		//账号密码认证\r\n		OnBasicAuth: func(ctx context.Context, client mqttServer.Client, req *mqttServer.ConnectRequest) error {\r\n			username := string(req.Connect.Username)\r\n			password := string(req.Connect.Password)\r\n			clientId := string(req.Connect.ClientID)\r\n			fmt.Println(username, password, clientId)\r\n			if Db.conn != nil {\r\n				fmt.Println(\"数据库连接成功\")\r\n				id := 0\r\n				Db.Sql(\"SELECT id FROM MqttServer WHERE username=? AND password=?\", username, password).Row(&id)\r\n				if id == 0 {\r\n					client.Close()\r\n					return fmt.Errorf(\"账号密码错误\")\r\n				} else {\r\n					client.ClientOptions().Username = username\r\n					ggg.RequestPost(postUrl, \"username=\"+username+\"&message=connect\")\r\n					return nil\r\n				}\r\n			} else {\r\n				client.Close()\r\n				fmt.Println(\"数据库连接失败\")\r\n				return fmt.Errorf(\"数据库连接失败\")\r\n			}\r\n\r\n		},\r\n		// 连接处理钩子（可访问完整的ConnectRequest）\r\n		OnConnected: func(ctx context.Context, client mqttServer.Client) {\r\n			fmt.Printf(\"客户端已连接: %s\\n\", client.ClientOptions().ClientID)\r\n		},\r\n\r\n		OnClosed: func(ctx context.Context, client mqttServer.Client, err error) {\r\n			fmt.Printf(\"客户端已断开: %s, 错误: %v\\n\", client.ClientOptions().Username, err)\r\n			ggg.RequestPost(postUrl, \"username=\"+client.ClientOptions().Username+\"&message=disconnect\")\r\n		},\r\n	}\r\n\r\n	// 创建MQTT服务器\r\n	srv := mqttServer.New(\r\n		mqttServer.WithTCPListener(ln),\r\n		mqttServer.WithHook(hooks),\r\n	)\r\n\r\n	// 添加保留消息\r\n	retainedService := srv.RetainedService()\r\n	retainedService.AddOrReplace(&gmqtt.Message{\r\n		QoS:      packets.Qos1,\r\n		Retained: true,\r\n		Topic:    \"a/b/c\",\r\n		Payload:  []byte(\"retained message\"),\r\n	})\r\n\r\n	// 启动服务器\r\n	go func() {\r\n		signalCh := make(chan os.Signal, 1)\r\n		signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)\r\n		<-signalCh\r\n		srv.Stop(context.Background())\r\n	}()\r\n\r\n	fmt.Println(\"MQTT服务器已启动，监听TLS端口: 8083\")\r\n	if err := srv.Run(); err != nil {\r\n		panic(fmt.Sprintf(\"服务器运行失败: %v\", err))\r\n	}\r\n}\r\nfunc (ggg *MqttServerZ) MqttServer() {\r\n\r\n	ln, err := net.Listen(\"tcp\", \":1883\")\r\n	if err != nil {\r\n		fmt.Printf(\"创建TLS监听器失败: %v\\n\", err)\r\n		return\r\n	}\r\n	defer ln.Close()\r\n	// 设置MQTT钩子\r\n	hooks := mqttServer.Hooks{\r\n		//账号密码认证\r\n		OnBasicAuth: func(ctx context.Context, client mqttServer.Client, req *mqttServer.ConnectRequest) error {\r\n			username := string(req.Connect.Username)\r\n			password := string(req.Connect.Password)\r\n			clientId := string(req.Connect.ClientID)\r\n			fmt.Println(username, password, clientId)\r\n			if Db.conn != nil {\r\n				fmt.Println(\"数据库连接成功\")\r\n				id := 0\r\n				Db.Sql(\"SELECT id FROM MqttServer WHERE username=? AND password=?\", username, password).Row(&id)\r\n				if id == 0 {\r\n					client.Close()\r\n					return fmt.Errorf(\"账号密码错误\")\r\n				} else {\r\n					// 保存用户名到客户端属性\r\n					client.ClientOptions().Username = username\r\n					ggg.RequestPost(postUrl, \"username=\"+username+\"&message=connect\")\r\n					return nil\r\n				}\r\n			} else {\r\n				client.Close()\r\n				fmt.Println(\"数据库连接失败\")\r\n				return fmt.Errorf(\"数据库连接失败\")\r\n			}\r\n		},\r\n		OnConnected: func(ctx context.Context, client mqttServer.Client) {\r\n			fmt.Printf(\"客户端已连接: %s\\n\", client.ClientOptions().ClientID)\r\n		},\r\n\r\n		OnClosed: func(ctx context.Context, client mqttServer.Client, err error) {\r\n			fmt.Printf(\"客户端已断开: %s, 错误: %v\\n\", client.ClientOptions().Username, err)\r\n			ggg.RequestPost(postUrl, \"username=\"+client.ClientOptions().Username+\"&message=disconnect\")\r\n		},\r\n	}\r\n\r\n	// 创建MQTT服务器\r\n	srv := mqttServer.New(\r\n		mqttServer.WithTCPListener(ln),\r\n		mqttServer.WithHook(hooks),\r\n	)\r\n\r\n	// 添加保留消息\r\n	retainedService := srv.RetainedService()\r\n	retainedService.AddOrReplace(&gmqtt.Message{\r\n		QoS:      packets.Qos1,\r\n		Retained: true,\r\n		Topic:    \"a/b/c\",\r\n		Payload:  []byte(\"retained message\"),\r\n	})\r\n\r\n	// 启动服务器\r\n	go func() {\r\n		signalCh := make(chan os.Signal, 1)\r\n		signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM)\r\n		<-signalCh\r\n		srv.Stop(context.Background())\r\n	}()\r\n\r\n	fmt.Println(\"MQTT服务器已启动，监听TLS端口: 1883\")\r\n	if err := srv.Run(); err != nil {\r\n		panic(fmt.Sprintf(\"服务器运行失败: %v\", err))\r\n	}\r\n}\r\nfunc (ggg *MqttServerZ) RequestPost(url string, KVstring string) (string, error) {\r\n	myhttp := &http.Client{}\r\n	myhttp.Timeout = time.Duration(3 * time.Second)\r\n	resp, err1 := myhttp.Post(url, \"application/x-www-form-urlencoded; charset=UTF-8\", strings.NewReader(KVstring))\r\n	if err1 != nil {\r\n		return \"\", errors.New(\"服务器未响应\")\r\n	}\r\n	defer resp.Body.Close()\r\n	body, err2 := io.ReadAll(resp.Body)\r\n	if err2 != nil {\r\n		return \"\", errors.New(\"读取数据fail\")\r\n	}\r\n	return string(body), nil\r\n}\r\nfunc IsValidMAC(mac string) bool {\r\n	_, err := net.ParseMAC(mac)\r\n	return err == nil\r\n}\r\n','',1),
(308,3,'mqttOverWs(mqttServer 启动)','// 启动MQTT服务器\r\n	go func() {\r\n		// 命令行参数\r\n		certFile := flag.String(\"cert\", \"wulian.nobiggie.cn_bundle.crt\", \"TLS证书文件路径\")\r\n		keyFile := flag.String(\"key\", \"wulian.nobiggie.cn.key\", \"TLS私钥文件路径\")\r\n		flag.Parse()\r\n		// 启动MQTT服务器\r\n		mqttServer := models.MqttServerZ{\r\n			CertFile: *certFile,\r\n			KeyFile:  *keyFile,\r\n		}\r\n		//mqttServer.MqttServerTLS()  //启动TLS服务器\r\n		mqttServer.MqttServer() //启动非TLS服务器\r\n	}()','',1),
(309,3,'mqttOverWs(mqtt解析 结构体)','package models\r\n\r\nimport (\r\n	\"encoding/binary\"\r\n	\"fmt\"\r\n)\r\n\r\n// MQTT解析\r\ntype MqttAnalysis struct {\r\n}\r\n\r\n// 解析MQTT剩余长度\r\nfunc (ggg *MqttAnalysis) DecodeRemainingLength(buf []byte) (int, int, error) {\r\n	multiplier := 1\r\n	value := 0\r\n	index := 0\r\n	for {\r\n		if index >= len(buf) {\r\n			return 0, 0, fmt.Errorf(\"剩余长度解析不完整\")\r\n		}\r\n		digit := int(buf[index])\r\n		value += (digit & 127) * multiplier\r\n		if multiplier > 128*128*128 {\r\n			return 0, 0, fmt.Errorf(\"剩余长度过大\")\r\n		}\r\n		multiplier *= 128\r\n		index++\r\n		if (digit & 128) == 0 {\r\n			break\r\n		}\r\n	}\r\n	return value, index, nil\r\n}\r\n\r\n// 解析MQTT CONNECT报文\r\nfunc (ggg *MqttAnalysis) ParseConnectPacket(data []byte) (map[string]string, error) {\r\n	result := make(map[string]string)\r\n	// 跳过固定报头和剩余长度\r\n	pos := 2\r\n	// 解析协议名\r\n	protocolLen := binary.BigEndian.Uint16(data[pos : pos+2])\r\n	pos += 2\r\n	protocolName := string(data[pos : pos+int(protocolLen)])\r\n	pos += int(protocolLen)\r\n	result[\"ProtocolName\"] = protocolName\r\n	// 解析协议级别\r\n	protocolLevel := data[pos]\r\n	pos++\r\n	result[\"ProtocolLevel\"] = fmt.Sprintf(\"%d\", protocolLevel)\r\n	// 解析连接标志\r\n	connectFlags := data[pos]\r\n	pos++\r\n	// 解析保持活动时间\r\n	keepAlive := binary.BigEndian.Uint16(data[pos : pos+2])\r\n	pos += 2\r\n	result[\"KeepAlive\"] = fmt.Sprintf(\"%d\", keepAlive)\r\n	// 解析客户端ID\r\n	clientIDLen := binary.BigEndian.Uint16(data[pos : pos+2])\r\n	pos += 2\r\n	clientID := string(data[pos : pos+int(clientIDLen)])\r\n	pos += int(clientIDLen)\r\n	result[\"ClientID\"] = clientID\r\n	// 解析用户名和密码\r\n	if connectFlags&0x80 != 0 {\r\n		usernameLen := binary.BigEndian.Uint16(data[pos : pos+2])\r\n		pos += 2\r\n		username := string(data[pos : pos+int(usernameLen)])\r\n		pos += int(usernameLen)\r\n		result[\"Username\"] = username\r\n	}\r\n\r\n	if connectFlags&0x40 != 0 {\r\n		passwordLen := binary.BigEndian.Uint16(data[pos : pos+2])\r\n		pos += 2\r\n		password := string(data[pos : pos+int(passwordLen)])\r\n		pos += int(passwordLen)\r\n		result[\"Password\"] = password\r\n	}\r\n\r\n	return result, nil\r\n}\r\n\r\n// 生成MQTT CONNACK报文\r\nfunc (ggg *MqttAnalysis) GenerateConnackPacket(sessionPresent bool, returnCode byte) []byte {\r\n	connack := make([]byte, 4)\r\n	connack[0] = 0x20 // 固定报头：CONNACK\r\n	connack[1] = 0x02 // 剩余长度\r\n	if sessionPresent {\r\n		connack[2] = 0x01 // 会话存在标志\r\n	} else {\r\n		connack[2] = 0x00 // 会话不存在标志\r\n	}\r\n	connack[3] = returnCode // 返回码\r\n	return connack\r\n}\r\n\r\n// 生成MQTT PUBLISH报文\r\nfunc (ggg *MqttAnalysis) GeneratePublishPacket(topic string, payload []byte, qos byte, retain bool, packetID uint16) ([]byte, error) {\r\n	// 验证参数有效性\r\n	if qos > 2 {\r\n		return nil, fmt.Errorf(\"无效的QoS级别: %d\", qos)\r\n	}\r\n\r\n	topicLength := len(topic)\r\n	if topicLength > 65535 {\r\n		return nil, fmt.Errorf(\"主题名长度超出限制\")\r\n	}\r\n\r\n	// 计算剩余长度\r\n	variableHeaderLength := 2 + topicLength // 主题长度字段(2字节) + 主题名\r\n	if qos > 0 {\r\n		variableHeaderLength += 2 // 包ID\r\n	}\r\n	remainingLength := variableHeaderLength + len(payload)\r\n\r\n	// 构建固定报头 (类型 + 标志位)\r\n	fixedHeader := byte(0x30) // PUBLISH类型\r\n	// 设置QoS级别\r\n	fixedHeader |= (qos << 1) & 0x06\r\n	// 设置保留标志\r\n	if retain {\r\n		fixedHeader |= 0x01\r\n	}\r\n\r\n	// 编码剩余长度\r\n	remainingBytes := ggg.EncodeRemainingLength(remainingLength)\r\n\r\n	// 构建可变报头\r\n	variableHeader := make([]byte, variableHeaderLength)\r\n	binary.BigEndian.PutUint16(variableHeader[0:2], uint16(topicLength))\r\n	copy(variableHeader[2:], topic)\r\n\r\n	// QoS>0时添加PacketID\r\n	if qos > 0 {\r\n		binary.BigEndian.PutUint16(variableHeader[2+topicLength:], packetID)\r\n	}\r\n\r\n	// 组合完整报文\r\n	message := make([]byte, 1+len(remainingBytes)+len(variableHeader)+len(payload))\r\n	message[0] = fixedHeader\r\n	copy(message[1:], remainingBytes)\r\n	copy(message[1+len(remainingBytes):], variableHeader)\r\n	copy(message[1+len(remainingBytes)+len(variableHeader):], payload)\r\n\r\n	return message, nil\r\n}\r\n\r\n// 构建MQTT PUBLISH消息\r\nfunc (ggg *MqttAnalysis) BuildPublishMessage(topic string, payload []byte, qos byte, retain bool) ([]byte, error) {\r\n	// 验证QoS值\r\n	if qos > 2 {\r\n		return nil, fmt.Errorf(\"无效的QoS级别: %d\", qos)\r\n	}\r\n\r\n	topicLength := len(topic) // 计算主题名长度\r\n	if topicLength > 65535 {\r\n		return nil, fmt.Errorf(\"主题名长度超出限制\")\r\n	}\r\n\r\n	// 计算剩余长度\r\n	// 剩余长度 = 主题名长度(2字节) + 主题名 + 可选PacketID(2字节) + 载荷长度\r\n	remainingLength := 2 + topicLength + len(payload)\r\n	if qos > 0 {\r\n		remainingLength += 2 // 包含PacketID\r\n	}\r\n\r\n	// 构建固定报头\r\n	fixedHeader := byte(0x30) // PUBLISH类型\r\n	// 设置QoS级别\r\n	fixedHeader |= (qos << 1) & 0x06\r\n	// 设置保留标志\r\n	if retain {\r\n		fixedHeader |= 0x01\r\n	}\r\n\r\n	// 编码剩余长度\r\n	remainingBytes := ggg.EncodeRemainingLength(remainingLength)\r\n\r\n	// 构建可变报头\r\n	variableHeader := make([]byte, 2+topicLength)\r\n	binary.BigEndian.PutUint16(variableHeader[0:2], uint16(topicLength))\r\n	copy(variableHeader[2:], topic)\r\n\r\n	// 如果QoS>0，添加PacketID\r\n	var packetIDBytes []byte\r\n	if qos > 0 {\r\n		packetIDBytes = make([]byte, 2)\r\n		// 这里应该使用唯一的PacketID，示例中使用固定值\r\n		binary.BigEndian.PutUint16(packetIDBytes, uint16(1234))\r\n	}\r\n\r\n	// 组装完整消息\r\n	message := make([]byte, 1+len(remainingBytes)+len(variableHeader)+len(packetIDBytes)+len(payload))\r\n	message[0] = fixedHeader\r\n	copy(message[1:], remainingBytes)\r\n	copy(message[1+len(remainingBytes):], variableHeader)\r\n	if qos > 0 {\r\n		copy(message[1+len(remainingBytes)+len(variableHeader):], packetIDBytes)\r\n	}\r\n	copy(message[1+len(remainingBytes)+len(variableHeader)+len(packetIDBytes):], payload)\r\n\r\n	return message, nil\r\n}\r\n\r\n// 生成MQTT SUBACK报文\r\nfunc (ggg *MqttAnalysis) GenerateSubackPacket(packetID uint16, returnCodes []byte) []byte {\r\n	// 计算剩余长度\r\n	remainingLength := 2 + len(returnCodes)\r\n\r\n	// 构建固定报头\r\n	fixedHeader := []byte{\r\n		0x90, // SUBACK报文类型\r\n		byte(remainingLength),\r\n	}\r\n\r\n	// 构建可变报头\r\n	variableHeader := make([]byte, 2)\r\n	binary.BigEndian.PutUint16(variableHeader, packetID)\r\n\r\n	// 组合完整报文\r\n	return append(append(fixedHeader, variableHeader...), returnCodes...)\r\n}\r\n\r\n// 编码剩余长度字段（MQTT协议特有变长编码）\r\nfunc (ggg *MqttAnalysis) EncodeRemainingLength(length int) []byte {\r\n	var result []byte\r\n	for {\r\n		digit := byte(length % 128)\r\n		length = length / 128\r\n		if length > 0 {\r\n			digit |= 0x80\r\n		}\r\n		result = append(result, digit)\r\n		if length == 0 {\r\n			break\r\n		}\r\n	}\r\n	return result\r\n}\r\n','',1),
(310,3,'mqttOverWs(ws桥接mqtt 结构体)','package models\r\n\r\nimport (\r\n	\"encoding/binary\"\r\n	\"errors\"\r\n	\"fmt\"\r\n	\"log\"\r\n	\"net\"\r\n	\"sync\"\r\n	\"time\"\r\n\r\n	mqtt \"github.com/eclipse/paho.mqtt.golang\"\r\n	\"github.com/gorilla/websocket\"\r\n)\r\n\r\n// MqttServerOrWs\r\ntype MqttServerOrWs struct {\r\n	Client     *websocket.Conn\r\n	User       string\r\n	Fd         uint32\r\n	ClientId   string\r\n	MqttClient MqttClientZ\r\n}\r\n\r\nvar (\r\n	MqttA        = &MqttAnalysis{}\r\n	WsConnetPool = make(map[uint32]*MqttServerOrWs) // WebSocket连接池\r\n	RWlock       = sync.RWMutex{}                   // 读写锁\r\n	MqttHost     = \"localhost:1883\"\r\n)\r\n\r\nfunc NewMqttServerOrWs(conn *websocket.Conn, fd uint32, mc MqttClientZ) *MqttServerOrWs {\r\n	client := &MqttServerOrWs{\r\n		Client:     conn,\r\n		User:       \"\",\r\n		Fd:         fd,\r\n		ClientId:   \"\",\r\n		MqttClient: mc,\r\n	}\r\n	return client\r\n}\r\n\r\nfunc (ggg *MqttServerOrWs) HandleMessages() {\r\n	bytesAdd := make([]byte, 0) //  用于存储读取的二进制数据\r\n	defer func() {\r\n		if r := recover(); r != nil {\r\n			log.Println(\"Recovered from panic:\", r)\r\n			// 确保所有锁都被释放\r\n			if RWlock.TryRLock() {\r\n				RWlock.RUnlock()\r\n			}\r\n			// 清理WebSocket连接池中的资源\r\n			if wsClient, exists := WsConnetPool[ggg.Fd]; exists {\r\n				fmt.Println(\"清理WebSocket连接\")\r\n				if wsClient.Client != nil {\r\n					wsClient.Client.Close()\r\n				}\r\n				delete(WsConnetPool, ggg.Fd)\r\n			}\r\n			// 清理MQTT客户端\r\n			if ggg.MqttClient.Client != nil {\r\n				ggg.MqttClient.Client.Disconnect(250) // 给MQTT客户端250ms时间完成操作\r\n				ggg.MqttClient.Client = nil\r\n			}\r\n			// 标记连接状态为关闭\r\n			ggg.Client = nil\r\n		}\r\n	}()\r\n	for {\r\n		if ggg.Client == nil {\r\n			RWlock.RUnlock()\r\n			log.Println(\"连接已关闭，退出处理循环\")\r\n			break\r\n		}\r\n		// 设置读取超时\r\n		if err := ggg.Client.SetReadDeadline(time.Now().Add(90 * time.Second)); err != nil {\r\n			if errors.Is(err, net.ErrClosed) {\r\n				log.Println(\"连接已关闭，退出处理循环1\")\r\n			} else {\r\n				log.Printf(\"设置读取超时失败: %v\", err)\r\n			}\r\n			ggg.clearup()\r\n			break\r\n		}\r\n		// 读取消息\r\n		RWlock.RLock()\r\n		if ggg.Client == nil {\r\n			RWlock.RUnlock()\r\n			log.Println(\"连接已关闭，退出处理循环\")\r\n			break\r\n		}\r\n		tmpClient := ggg.Client\r\n		RWlock.RUnlock()\r\n		// 读取消息\r\n		_, message, err := tmpClient.ReadMessage()\r\n\r\n		if err != nil {\r\n			// 清理连接\r\n			if websocket.IsUnexpectedCloseError(err, websocket.CloseGoingAway, websocket.CloseAbnormalClosure) {\r\n				log.Printf(\"意外关闭错误: %v\", err)\r\n				ggg.clearup()\r\n				break\r\n			}\r\n			continue\r\n		}\r\n\r\n		bytesAdd = append(bytesAdd, message...)\r\n		if len(bytesAdd) < 2 {\r\n			//固定报头 log.Println(\"MQTT报文不完整，继续读取\")\r\n			continue\r\n		}\r\n		// 解析报文类型\r\n		header := bytesAdd[0] >> 4\r\n		remainingLength := int(bytesAdd[1])\r\n\r\n		// 检查剩余长度是否足够解析可变报头\r\n		if len(bytesAdd) < 2+remainingLength {\r\n			//log.Println(\"MQTT报文不完整，继续读取\")\r\n			continue\r\n		}\r\n		//fmt.Printf(\"报文读取完整, 报文类型: %d 剩余长度:%d \\n\", packetType, remainingLength)\r\n		// 解析剩余长度\r\n		remainingLength, lengthBytes, err := MqttA.DecodeRemainingLength(bytesAdd[1:])\r\n		if err != nil {\r\n			log.Printf(\"解析剩余长度失败: %v\", err)\r\n			bytesAdd = make([]byte, 0)\r\n			continue\r\n		}\r\n		payloadStart := 1 + lengthBytes\r\n		// 处理不同类型的MQTT报文\r\n		switch header {\r\n		case 1: // CONNECT\r\n			connectData, err := MqttA.ParseConnectPacket(bytesAdd)\r\n			if err != nil {\r\n				log.Printf(\"解析CONNECT报文失败: %v\", err)\r\n				connack := MqttA.GenerateConnackPacket(false, 0x01) // 不支持的协议版本\r\n				ggg.Client.WriteMessage(websocket.BinaryMessage, connack)\r\n				bytesAdd = make([]byte, 0)\r\n				continue\r\n			}\r\n\r\n			//log.Printf(\"收到CONNECT报文: %v\", connectData)\r\n			ggg.ClientId = connectData[\"ClientID\"]\r\n			ggg.User = connectData[\"Username\"]\r\n			password := connectData[\"Password\"]\r\n			//log.Printf(\"账号:%s 密码:%s\", ggg.User, password, WsConnetPool)\r\n			bytesAdd = make([]byte, 0)\r\n			mqttClient := ggg.MqttClient.Connect(MqttHost, ggg.ClientId, ggg.User, password)\r\n			if mqttClient != nil {\r\n				fmt.Println(\"账号密码错误\")\r\n				connack := MqttA.GenerateConnackPacket(false, 0x01) //发送CONNACK响应 连接断开\r\n				ggg.Client.WriteMessage(websocket.BinaryMessage, connack)\r\n				return\r\n			}\r\n			connack := MqttA.GenerateConnackPacket(false, 0x00) //发送CONNACK响应 连接已接受\r\n			if err := ggg.Client.WriteMessage(websocket.BinaryMessage, connack); err != nil {\r\n				fmt.Printf(\"发送CONNACK失败: %v\", err)\r\n				return\r\n			}\r\n			log.Println(\"MQTT连接成功\")\r\n			RWlock.Lock()\r\n			WsConnetPool[ggg.Fd] = ggg\r\n			RWlock.Unlock()\r\n		case 3:\r\n			// 解析PUBLISH报文\r\n			pos := payloadStart\r\n			// 解析主题名长度\r\n			topicLength := binary.BigEndian.Uint16(bytesAdd[pos : pos+2])\r\n			pos += 2\r\n			// 解析主题名\r\n			topic := string(bytesAdd[pos : pos+int(topicLength)])\r\n			pos += int(topicLength)\r\n\r\n			// 解析包ID（如果QoS > 0）\r\n			var packetID uint16\r\n			qos := (header >> 1) & 0x03\r\n			if qos > 0 {\r\n				packetID = binary.BigEndian.Uint16(bytesAdd[pos : pos+2])\r\n			}\r\n			// 解析负载\r\n			payload := bytesAdd[pos : payloadStart+remainingLength]\r\n			log.Printf(\"收到 推送 报文: 主题:%s, QoS:%d, 包ID:%d, 内容=%s\", topic, qos, packetID, string(payload))\r\n\r\n			// 处理QoS确认\r\n			if qos == 1 {\r\n				// 发送PUBACK\r\n				puback := []byte{\r\n					0x40, // PUBACK报文类型\r\n					0x02, // 剩余长度\r\n					byte(packetID >> 8),\r\n					byte(packetID & 0xFF),\r\n				}\r\n				if err := ggg.Client.WriteMessage(websocket.BinaryMessage, puback); err != nil {\r\n					log.Printf(\"发送PUBACK失败: %v\", err)\r\n				}\r\n			} else if qos == 2 {\r\n				// TODO: 处理QoS 2流程\r\n				log.Println(\"收到QoS 2的PUBLISH，暂不处理\")\r\n			}\r\n\r\n			// 转发消息到订阅者\r\n			ggg.forwardMessageToSubscribers(topic, payload, 0)\r\n			bytesAdd = make([]byte, 0)\r\n\r\n		case 8: //订阅主题\r\n			// 解析SUBSCRIBE报文\r\n			pos := payloadStart\r\n			// 解析包ID\r\n			packetID := binary.BigEndian.Uint16(bytesAdd[pos : pos+2])\r\n			pos += 2\r\n			topic := \"\"\r\n			// 解析订阅列表\r\n			for pos < payloadStart+remainingLength {\r\n				// 解析主题过滤器长度\r\n				filterLength := binary.BigEndian.Uint16(bytesAdd[pos : pos+2])\r\n				pos += 2\r\n				// 解析主题过滤器\r\n				topic = string(bytesAdd[pos : pos+int(filterLength)])\r\n				pos += int(filterLength)\r\n				// 解析最大QoS\r\n				maxQoS := bytesAdd[pos] & 0x03\r\n				pos++\r\n				log.Printf(\"收到订阅报文: 包ID:%d, 主题:%s, QoS=%d\",\r\n					packetID, topic, maxQoS)\r\n				if ggg.MqttClient.Topic == \"\" { // 处理订阅逻辑\r\n					ggg.MqttClient.Topic = topic\r\n					ggg.ServerMsg()\r\n				}\r\n			}\r\n\r\n			suback := MqttA.GenerateSubackPacket(packetID, []byte{0x00}) // 发送订阅成功响应\r\n			if err := ggg.Client.WriteMessage(websocket.BinaryMessage, suback); err != nil {\r\n				log.Printf(\"发送SUBACK失败: %v\", err)\r\n			}\r\n			bytesAdd = make([]byte, 0)\r\n\r\n		case 12:\r\n			//fmt.Println(\"PINGREQ\", header)\r\n			pingresp := []byte{0xD0, 0x00} //发送PINGRESP响应 PINGRESP报文\r\n			if err := ggg.Client.WriteMessage(websocket.BinaryMessage, pingresp); err != nil {\r\n				log.Printf(\"发送PINGRESP失败: %v\", err)\r\n			}\r\n			bytesAdd = make([]byte, 0)\r\n\r\n		case 14: // DISCONNECT\r\n			log.Printf(\"收到DISCONNECT关闭连接报文: ClientID=%s\", ggg.ClientId)\r\n			connack := MqttA.GenerateConnackPacket(false, 0x01) //发送CONNACK响应 连接断开\r\n			RWlock.Lock()\r\n			if err := ggg.Client.WriteMessage(websocket.BinaryMessage, connack); err != nil {\r\n				log.Printf(\"发送DISCONNECT失败: %v\", err)\r\n			}\r\n			RWlock.Unlock()\r\n			ggg.clearup()\r\n			return\r\n\r\n		default:\r\n			log.Printf(\"未知MQTT报文类型: %d\", header)\r\n			bytesAdd = make([]byte, 0)\r\n		}\r\n	}\r\n}\r\n\r\n// 转发消息到订阅者\r\nfunc (ggg *MqttServerOrWs) forwardMessageToSubscribers(topic string, payload []byte, qos byte) {\r\n	RWlock.Lock()\r\n	if ggg.Client == nil {\r\n		RWlock.Unlock()\r\n		return\r\n	}\r\n	// 这里应该实现实际的消息转发逻辑\r\n	a, _ := MqttA.BuildPublishMessage(topic, payload, qos, false) // 生成MQTT PUBLISH报文\r\n	err := ggg.Client.WriteMessage(websocket.BinaryMessage, a)\r\n	if err != nil {\r\n		RWlock.Unlock()\r\n		log.Println(\"转发消息到订阅者失败\")\r\n		ggg.clearup()\r\n		return\r\n	}\r\n	err1 := ggg.MqttClient.PublishZ(topic, payload, 0, false)\r\n	if err1 != nil {\r\n		RWlock.Unlock()\r\n		log.Println(\"推送往MQTT服务器失败\", err1.Error())\r\n		ggg.clearup()\r\n		return\r\n	}\r\n	log.Println(\"推送往MQTT服务器成功\")\r\n	RWlock.Unlock()\r\n}\r\n\r\nfunc (ggg *MqttServerOrWs) ServerMsg() {\r\n	// 启动消息订阅处理\r\n	go func(ggg *MqttServerOrWs) {\r\n		// 处理goroutine内的panic\r\n		defer func() {\r\n			if err := recover(); err != nil {\r\n				log.Printf(\"MQTT订阅goroutine发生panic: %v\", err)\r\n				ggg.clearup()\r\n			}\r\n		}()\r\n\r\n		// 检查MQTT客户端是否已连接\r\n		if !ggg.MqttClient.Client.IsConnected() {\r\n			log.Println(\"MQTT客户端未连接，无法订阅主题\")\r\n			ggg.clearup()\r\n			return\r\n		}\r\n\r\n		// 执行订阅并处理错误\r\n		token := ggg.MqttClient.Client.Subscribe(ggg.MqttClient.Topic, 0, func(client mqtt.Client, msg mqtt.Message) {\r\n			// 处理回调函数内的panic\r\n			defer func() {\r\n				if err := recover(); err != nil {\r\n					log.Printf(\"MQTT消息处理回调发生panic: %v\", err)\r\n					ggg.clearup()\r\n				}\r\n			}()\r\n\r\n			// 构建并发送消息到WebSocket客户端\r\n			payload, err := MqttA.BuildPublishMessage(ggg.MqttClient.Topic, msg.Payload(), 0, false)\r\n\r\n			if err != nil {\r\n				log.Printf(\"构建发布消息失败: %v\", err)\r\n				ggg.clearup()\r\n				return\r\n			}\r\n\r\n			// 写入WebSocket并检查错误\r\n			if err := ggg.Client.WriteMessage(websocket.BinaryMessage, payload); err != nil {\r\n				log.Printf(\"发送消息到WebSocket失败: %v\", err)\r\n				// 通常发生错误时应该关闭连接\r\n				ggg.clearup()\r\n				return\r\n			}\r\n\r\n			log.Printf(\"收到订阅消息: %s\", string(msg.Payload()))\r\n		},\r\n		)\r\n\r\n		// 等待订阅完成并检查结果\r\n		if token.Wait() && token.Error() != nil {\r\n			log.Printf(\"订阅主题失败: %v\", token.Error())\r\n			ggg.clearup()\r\n			return\r\n		}\r\n\r\n		log.Printf(\"成功订阅主题: %s\", ggg.MqttClient.Topic)\r\n\r\n	}(ggg)\r\n\r\n}\r\nfunc (ggg *MqttServerOrWs) clearup() {\r\n	RWlock.Lock()\r\n	defer RWlock.Unlock()\r\n\r\n	if wsClient, exists := WsConnetPool[ggg.Fd]; exists {\r\n		if wsClient.Client != nil {\r\n			wsClient.Client.Close()\r\n		}\r\n		delete(WsConnetPool, ggg.Fd)\r\n	}\r\n	if ggg.MqttClient.Client != nil {\r\n		ggg.MqttClient.Client.Disconnect(0)\r\n	}\r\n}\r\n','',1),
(311,3,'mqttOverWs(ws桥接mqtt ctx)','package controllers\r\n\r\nimport (\r\n	\"fmt\"\r\n	\"log\"\r\n	\"net/http\"\r\n	\"wss/models\"\r\n\r\n	\"github.com/gin-gonic/gin\"\r\n	\"github.com/gorilla/websocket\"\r\n)\r\n\r\nvar proxy *models.MqttServerOrWs\r\nvar (\r\n	Fd      = uint32(1) // 连接唯一标识\r\n	mclient = models.MqttClientZ{}\r\n)\r\n\r\ntype WsServer struct {\r\n}\r\n\r\nfunc (ggg WsServer) Get(ctx *gin.Context) {\r\n	upgrader := websocket.Upgrader{\r\n		WriteBufferSize: 1024,\r\n		ReadBufferSize:  1024,\r\n		CheckOrigin: func(r *http.Request) bool {\r\n			return true\r\n		},\r\n	}\r\n\r\n	// 升级为WebSocket连接\r\n	conn, err := upgrader.Upgrade(ctx.Writer, ctx.Request, http.Header{\"Sec-WebSocket-Protocol\": []string{\"mqtt\"}})\r\n	if err != nil {\r\n		log.Printf(\"WebSocket升级失败: %v\", err)\r\n		return\r\n	}\r\n	// 注册新连接\r\n	fmt.Println(\"升级为WebSocket连接成功\")\r\n	proxy = models.NewMqttServerOrWs(conn, Fd, mclient)\r\n	models.RWlock.Lock()\r\n	fmt.Println(Fd)\r\n	models.WsConnetPool[Fd] = proxy\r\n	Fd++\r\n	models.RWlock.Unlock()\r\n	log.Printf(\"客户端已连接: Fd=%d\", proxy.Fd)\r\n	// 设置关闭处理函数\r\n	conn.SetCloseHandler(func(code int, text string) error {\r\n		log.Printf(\"客户端已断开连接: Fd=%d\", proxy.Fd)\r\n		fmt.Println(models.WsConnetPool)\r\n		if wsClient, exists := models.WsConnetPool[proxy.Fd]; exists {\r\n			if wsClient.Client != nil {\r\n				wsClient.Client.Close()\r\n			}\r\n			delete(models.WsConnetPool, proxy.Fd)\r\n\r\n		}\r\n		fmt.Println(\"WsConnetPool\", models.WsConnetPool)\r\n		return nil\r\n	})\r\n\r\n	go proxy.HandleMessages() // 读取并处理MQTT控制报文\r\n}\r\n','',1),
(312,3,'mqttOverWs(wsOverMqtt 启动)','	// 启动HTTP服务器\r\n	var r *gin.Engine\r\n	if runtime.GOOS == \"windows\" {\r\n		gin.SetMode(gin.DebugMode)\r\n		r = gin.Default()\r\n	} else {\r\n		gin.SetMode(gin.ReleaseMode)\r\n		r = gin.New()\r\n	}\r\n\r\n	store := cookie.NewStore([]byte(\"nobiggie\"))\r\n	store.Options(sessions.Options{\r\n		MaxAge: 28800,\r\n		Path:   \"/\",\r\n	})\r\n	r.Use(sessions.Sessions(\"mysession\", store))\r\n	routers.GinRouterSetup(r)\r\n	if runtime.GOOS == \"windows\" || runtime.GOARCH == \"arm64\" {\r\n		r.Run(\":8084\")\r\n	} else {\r\n		r.Use(func(ctx *gin.Context) {\r\n			secureMiddleware := secure.New(secure.Options{\r\n				SSLRedirect: true,\r\n				SSLHost:     \"localhost:8084\",\r\n			})\r\n			err := secureMiddleware.Process(ctx.Writer, ctx.Request)\r\n			if err != nil {\r\n				return\r\n			}\r\n			ctx.Next()\r\n		})\r\n		err7 := http.ListenAndServeTLS(\":8084\", fmt.Sprintf(\"%v\", \"wulian.nobiggie.cn_bundle.pem\"), fmt.Sprintf(\"%v\", \"wulian.nobiggie.cn.key\"), r)\r\n		if err7 != nil {\r\n			fmt.Println(err7.Error())\r\n		}\r\n	}','',1),
(313,7,'pdo static类','<?php\r\nnamespace myclass;\r\n\r\nclass Db {\r\n\r\n    private static $host = \"957lan.dnstoo.com\";\r\n    private static $user = \"jyan_f\";\r\n    private static $password = \"golang\";\r\n\r\n    private static $port = \"3306\";\r\n    private static $dbname = \"jyan\";\r\n    private static $instance = null;\r\n\r\n    // 获取PDO实例（单例模式）\r\n    private static function getConnection() {\r\n        if (self::$instance == null) {\r\n            $dsn = \"mysql:host=\".self::$host.\";port=\".self::$port.\";dbname=\".self::$dbname.\";charset=utf8mb4\";\r\n            self::$instance = new \\pdo($dsn, self::$user, self::$password);\r\n            self::$instance->setAttribute(\\PDO::ATTR_ERRMODE, \\PDO::ERRMODE_EXCEPTION);\r\n            self::$instance->setAttribute(\\PDO::ATTR_EMULATE_PREPARES, false);\r\n        }\r\n        return self::$instance;\r\n    }\r\n\r\n    // 查询单行数据\r\n    public static function Row($sql, array $arr = []): ?array {\r\n        $stmt = self::getConnection()->prepare($sql);\r\n        $stmt->execute($arr);\r\n        return $stmt->fetch(\\PDO::FETCH_ASSOC) ?: null;\r\n    }\r\n\r\n    // 查询多行数据\r\n    public static function Rows($sql, array $arr = []): ?array {\r\n        $stmt = self::getConnection()->prepare($sql);\r\n        $stmt->execute($arr);\r\n        $result = $stmt->fetchAll(\\PDO::FETCH_ASSOC);\r\n        return $result ?: null;\r\n    }\r\n\r\n    // 执行SQL语句（INSERT/UPDATE/DELETE）\r\n    public static function Exec($sql, array $arr = []): int {\r\n        $stmt = self::getConnection()->prepare($sql);\r\n        $stmt->execute($arr);\r\n        return $stmt->rowCount();\r\n    }\r\n\r\n    // 自动拼接INSERT语句并执行\r\n    public static function AutoInsert(string $table, array $arr): int {\r\n        if (empty($arr)) return 0;\r\n        \r\n        $keys = implode(\',\', array_keys($arr));\r\n        $placeholders = implode(\',\', array_fill(0, count($arr), \'?\'));\r\n        $sql = \"INSERT INTO $table ($keys) VALUES ($placeholders)\";\r\n        \r\n        $stmt = self::getConnection()->prepare($sql);\r\n        $stmt->execute(array_values($arr));\r\n        return $stmt->rowCount();\r\n    }\r\n\r\n    // 执行INSERT并返回最后插入ID\r\n    public static function lastInsertId(string $sql, array $arr = []): int {\r\n        $stmt = self::getConnection()->prepare($sql);\r\n        $stmt->execute($arr);\r\n        return (int)self::getConnection()->lastInsertId();\r\n    }\r\n\r\n    // 清空表数据（更高效的实现）\r\n    public static function deleteAll(string $table): bool {\r\n        if (empty($table)) return false;\r\n        \r\n        // 防止SQL注入，验证表名是否合法\r\n        if (!preg_match(\'/^[a-zA-Z0-9_]+$/\', $table)) {\r\n            return false;\r\n        }\r\n        \r\n        $stmt = self::getConnection()->prepare(\"DELETE FROM $table\");\r\n        return $stmt->execute();\r\n    }\r\n\r\n}','',1),
(314,7,'分页类','<?php\r\nnamespace myclass;\r\n\r\n\r\nclass Pages\r\n{\r\n    public $PageNo;     // 当前页\r\n    public $PageSize;   // 每页多少数据\r\n    public $TotalPage;  // 总共多少页\r\n    public $TotalCount; // 总共多少条数据\r\n    public $FirstPage;  // 第一页\r\n    public $LastPage;   // 最后一页\r\n    public $Url;        // 链接\r\n\r\n    public function __construct($PageNo, $PageSize, $TotalCount, $Url)\r\n    {\r\n        $this->PageNo = $PageNo;\r\n        $this->PageSize = $PageSize;\r\n        $this->TotalCount = $TotalCount;\r\n        $this->Url = $Url;\r\n    }\r\n\r\n    // 计算总页数\r\n    private function getPageCount()\r\n    {\r\n        if ($this->TotalCount == 0) {\r\n            $this->TotalPage = 1;\r\n        } else {\r\n            $this->TotalPage = ($this->TotalCount + $this->PageSize - 1) / $this->PageSize;\r\n            $this->TotalPage = intval($this->TotalPage);\r\n        }\r\n        $this->LastPage = $this->TotalPage;\r\n        $this->FirstPage = 1;\r\n        $this->execUrl();\r\n    }\r\n\r\n    // 格式化URL地址\r\n    private function execUrl()\r\n    {\r\n        if (strpos($this->Url, \'?\') !== false) {\r\n            $this->Url = $this->Url . \'&page=\';\r\n        } else {\r\n            $this->Url = $this->Url . \'?page=\';\r\n        }\r\n    }\r\n\r\n    // 获取URL\r\n    private function getUrl($page)\r\n    {\r\n        return sprintf(\"%s%d\", $this->Url, $page);\r\n    }\r\n\r\n    // 获取页码范围\r\n    private function getNumRange()\r\n    {\r\n        $maxVisiblePages = 3; // 最多显示的页码数\r\n\r\n        if ($this->TotalPage <= $maxVisiblePages) {\r\n            $k = 1;\r\n            $v = $this->TotalPage;\r\n        } else {\r\n            $k = $this->PageNo - intval(($maxVisiblePages-1)/2);\r\n            $v = $this->PageNo + intval($maxVisiblePages/2);\r\n\r\n            if ($k < 1) {\r\n                $k = 1;\r\n                $v = $maxVisiblePages;\r\n            } else if ($v > $this->TotalPage) {\r\n                $k = $this->TotalPage - $maxVisiblePages + 1;\r\n                $v = $this->TotalPage;\r\n            }\r\n        }\r\n\r\n        return array($k, $v);\r\n    }\r\n\r\n    // 获取分页起始位置\r\n    public function LimitStart()\r\n    {\r\n        return ($this->PageNo - 1) * $this->PageSize;\r\n    }\r\n\r\n    // 生成响应式分页HTML\r\npublic function Show()\r\n{\r\n    $this->getPageCount();\r\n\r\n    $style = \'\r\n        <link href=\"/static/css/font-awesome.css\" rel=\"stylesheet\">\r\n        <style>\r\n            /* 分页容器样式 */\r\n            .pagination-container {\r\n                display: flex;\r\n                justify-content: center;\r\n                margin: 1.5rem 0 1rem;\r\n            }\r\n            \r\n            /* 分页导航样式 */\r\n            .pagination {\r\n                display: inline-flex;\r\n                border-radius: 0.375rem;\r\n                box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);\r\n                padding: 1rem;\r\n                background-color: white;\r\n                border: 1px solid #e5e7eb;\r\n                font-size: 1.25rem; /* 字体大小调整为20px (1.25rem) */\r\n            }\r\n            \r\n            /* 分页项通用样式 */\r\n            .pagination-item {\r\n                padding: 0.375rem 0.75rem;\r\n                border-radius: 0.375rem;\r\n                border: 1px solid #d1d5db;\r\n                color: #4b5563;\r\n                text-decoration: none;\r\n                transition: all 0.2s;\r\n                margin-left: 0.25rem;\r\n            }\r\n            \r\n            /* 分页项悬停效果 */\r\n            .pagination-item:hover:not(.pagination-item-disabled) {\r\n                background-color: #f3f4f6;\r\n            }\r\n            \r\n            /* 当前激活页样式 */\r\n            .pagination-active {\r\n                background-color: #3b82f6; /* 主色调蓝色 */\r\n                color: white;\r\n                border-color: #3b82f6;\r\n            }\r\n            \r\n            /* 禁用状态样式 */\r\n            .pagination-item-disabled {\r\n                opacity: 0.5;\r\n                cursor: not-allowed;\r\n            }\r\n            \r\n            /* 禁用状态悬停效果 */\r\n            .pagination-item-disabled:hover {\r\n                background-color: transparent;\r\n            }\r\n            \r\n            /* 屏幕阅读器隐藏文本 */\r\n            .sr-only {\r\n                position: absolute;\r\n                width: 1px;\r\n                height: 1px;\r\n                padding: 0;\r\n                margin: -1px;\r\n                overflow: hidden;\r\n                clip: rect(0, 0, 0, 0);\r\n                white-space: nowrap;\r\n                border-width: 0;\r\n            }\r\n        </style>\r\n    \';\r\n\r\n    $buf = \'\r\n        <div class=\"pagination-container\">\r\n            <nav class=\"pagination\" aria-label=\"Pagination\">\r\n    \';\r\n\r\n    // 首页和上一页\r\n    if ($this->PageNo > 1) {\r\n        $buf .= sprintf(\'\r\n            <a href=\"%s\" class=\"pagination-item\">\r\n                <span class=\"sr-only\">首页</span>\r\n                <i class=\"fa fa-angle-double-left\"></i>\r\n            </a>\r\n            <a href=\"%s\" class=\"pagination-item\">\r\n                <span class=\"sr-only\">上一页</span>\r\n                <i class=\"fa fa-angle-left\"></i>\r\n            </a>\r\n        \', $this->getUrl(1), $this->getUrl($this->PageNo-1));\r\n    } else {\r\n        $buf .= \'\r\n            <span class=\"pagination-item pagination-item-disabled\">\r\n                <span class=\"sr-only\">首页</span>\r\n                <i class=\"fa fa-angle-double-left\"></i>\r\n            </span>\r\n            <span class=\"pagination-item pagination-item-disabled\">\r\n                <span class=\"sr-only\">上一页</span>\r\n                <i class=\"fa fa-angle-left\"></i>\r\n            </span>\r\n        \';\r\n    }\r\n\r\n    // 页码\r\n    list($k, $v) = $this->getNumRange();\r\n\r\n    // 中间页码\r\n    for ($i = $k; $i <= $v; $i++) {\r\n        if ($i == $this->PageNo) {\r\n            $buf .= sprintf(\'\r\n                <span class=\"pagination-item pagination-active\">\r\n                    %s\r\n                </span>\r\n            \', sprintf(\"%d\", $i));\r\n        } else {\r\n            $buf .= sprintf(\'\r\n                <a href=\"%s\" class=\"pagination-item\">\r\n                    %s\r\n                </a>\r\n            \', $this->getUrl($i), sprintf(\"%d\", $i));\r\n        }\r\n    }\r\n\r\n    // 下一页和尾页\r\n    if ($this->PageNo < $this->TotalPage) {\r\n        $buf .= sprintf(\'\r\n            <a href=\"%s\" class=\"pagination-item\">\r\n                <span class=\"sr-only\">下一页</span>\r\n                <i class=\"fa fa-angle-right\"></i>\r\n            </a>\r\n            <a href=\"%s\" class=\"pagination-item\">\r\n                <span class=\"sr-only\">尾页</span>\r\n                <i class=\"fa fa-angle-double-right\"></i>\r\n            </a>\r\n        \', $this->getUrl($this->PageNo+1), $this->getUrl($this->TotalPage));\r\n    } else {\r\n        $buf .= \'\r\n            <span class=\"pagination-item pagination-item-disabled\">\r\n                <span class=\"sr-only\">下一页</span>\r\n                <i class=\"fa fa-angle-right\"></i>\r\n            </span>\r\n            <span class=\"pagination-item pagination-item-disabled\">\r\n                <span class=\"sr-only\">尾页</span>\r\n                <i class=\"fa fa-angle-double-right\"></i>\r\n            </span>\r\n        \';\r\n    }\r\n\r\n    $buf .= \'\r\n            </nav>\r\n        </div>\r\n    \';\r\n\r\n    if ($this->TotalPage <= 1) {\r\n        return \"\";\r\n    }\r\n\r\n    return $style . $buf;\r\n}\r\n}\r\n\r\n//$pages = new \\myclass\\Pages($_GET[\"page\"],$pageSise,$lenArr,\'?page={page}\');\r\n $pages->Show()','',1),
(315,4,'挖掘机','#include <Arduino.h>\r\n#include <WiFi.h>\r\n#include <WiFiMulti.h>\r\n#include <WiFiClientSecure.h>\r\n#include <WebSocketsClient.h>\r\n#include <ESP32Servo.h>\r\n#include <WebServer.h>\r\n#include <HTTPClient.h>\r\n#include <FS.h>\r\n#include \"SPIFFS.h\"\r\n#include \"ESPmDNS.h\"\r\n#include <Ticker.h>\r\n\r\n// 全局变量定义\r\nWiFiMulti WiFiMulti;\r\nWebSocketsClient webSocket;\r\nServo myServoLeft;   // 左车轮舵机\r\nServo myServoRight;  // 右车轮舵机\r\nServo myBig;         // 大臂舵机（180度）\r\nServo mySmall;       // 小臂舵机（180度）\r\nServo myWadb;        // 挖斗舵机（180度）\r\nServo servoJiuf;     // 机身舵机（改为180度）\r\n\r\n// 舵机参数（统一180度舵机配置）\r\nconst int minPulse = 500;   // 最小脉宽(微秒)\r\nconst int maxPulse = 2500;  // 最大脉宽(微秒)\r\nint servoPinLeft = 5;       // 左车轮引脚\r\nint servoPinRight = 6;      // 右车轮引脚\r\nconst int stopPos = 90;     // 所有180度舵机的停止位置（中间点）\r\nint baudRate = 115200;\r\n\r\n// 引脚定义\r\nint bigUpper = 18;    // 大臂上限限位开关（180度）\r\nint bigLower = 8;     // 大臂下限限位开关（0度）\r\nint smallUpper = 39;  // 小臂上限限位开关（180度）\r\nint smallLower = 38;  // 小臂下限限位开关（0度）\r\nint wadbUpper = 41;   // 挖斗上限限位开关（180度）\r\nint wadbLower = 40;   // 挖斗下限限位开关（0度）\r\nint bigExc = 7;       // 大臂舵机引脚\r\nint smallExc = 17;    // 小臂舵机引脚\r\nint wadb = 16;        // 挖斗舵机引脚\r\nint fuselage = 4;     // 机身舵机引脚（180度）\r\n\r\n// 状态变量\r\nint bigstate = 2;                 // 0:下限  1:上限  2:可活动（大臂）\r\nint smallstate = 2;               // 小臂状态（同上）\r\nint wadbstate = 2;                // 挖斗状态（同上）\r\n\r\nint bigAngle = 90;                // 大臂当前角度（初始停止位置）\r\nint smallAngle = 90;              // 小臂当前角度（初始停止位置）\r\nint wadbAngle = 90;               // 挖斗当前角度（初始停止位置）\r\nint fuselageAngle = 90;           // 机身当前角度（初始停止位置，180度范围）\r\nunsigned long extiTimeBig = 0;    // 大臂中断防抖时间\r\nunsigned long extiTimeSmall = 0;  // 小臂中断防抖时间\r\nunsigned long extiTimeWadb = 0;   // 挖斗中断防抖时间\r\nunsigned long extiTimeFuselage = 0; // 机身边界防抖时间（若有机身限位）\r\nint commandBig = -1;              // 大臂命令（0:下降 1:上升 -1:无）\r\nint commandSmall = -1;            // 小臂命令（0:收缩 1:伸展 -1:无）\r\nint commandWadb = -1;             // 挖斗命令（0:收 1:开 -1:无）\r\nint commandFuselage = -1;         // 机身命令（0:左转 1:右转 -1:无）\r\nbool extiBigOpen = false;         // 大臂上限 触发标志\r\nbool extiBigClose = false;        // 大臂下限 触发标志\r\nbool extiSmallOpen = false;       // 小臂上限 触发标志\r\nbool extiSmallClose = false;      // 小臂下限 触发标志\r\nbool extiWadbOpen = false;        // 挖斗上限 触发标志\r\nbool extiWadbClose = false;       // 挖斗下限 触发标志\r\nbool extiFuselageLeft = false;    // 机身左边界触发（若有机身限位）\r\nbool extiFuselageRight = false;   // 机身后边界触发（若有机身限位）\r\n\r\n// WiFi与设备信息\r\nTicker myTicker;\r\nString flashWifi = \"/wifi.txt\";     // WiFi配置文件\r\nWebServer webServer(80);            // WebServer端口\r\nString ssid = \"webserver\";          // WiFi名称\r\nString passwd = \"www.nobiggie.cn\";  // WiFi密码\r\nString html;\r\nWiFiClient wificlient;\r\nString deviceName = \"DYZKTEST0016\";\r\n\r\n\r\n// 工具函数：十六进制打印\r\nvoid hexdump(const void* mem, uint32_t len, uint8_t cols = 16) {\r\n  const uint8_t* src = (const uint8_t*)mem;\r\n  Serial.printf(\"\\n[HEXDUMP] Address: 0x%08X len: 0x%X (%d)\", (ptrdiff_t)src, len, len);\r\n  for (uint32_t i = 0; i < len; i++) {\r\n    if (i % cols == 0) {\r\n      Serial.printf(\"\\n[0x%08X] 0x%08X: \", (ptrdiff_t)src, i);\r\n    }\r\n    Serial.printf(\"%02X \", *src);\r\n    src++;\r\n  }\r\n  Serial.printf(\"\\n\");\r\n}\r\n\r\n// WebSocket事件处理\r\nvoid webSocketEvent(WStype_t type, uint8_t* payload, size_t length) {\r\n  String msg;\r\n  switch (type) {\r\n    case WStype_DISCONNECTED:\r\n      Serial.printf(\"[WSc] Disconnected!\\n\");\r\n      // 断开连接时复位所有舵机到停止位置\r\n      myServoLeft.write(stopPos);\r\n      myServoRight.write(stopPos);\r\n      myBig.write(stopPos);\r\n      mySmall.write(stopPos);\r\n      myWadb.write(stopPos);\r\n      servoJiuf.write(stopPos);  // 机身回到中间停止位置\r\n      commandBig = -1;\r\n      commandSmall = -1;\r\n      commandWadb = -1;\r\n      commandFuselage = -1;\r\n      break;\r\n\r\n    case WStype_CONNECTED:\r\n      Serial.printf(\"[WSc] Connected to url: %s\\n\", payload);\r\n      webSocket.sendTXT(\"+NET_\" + deviceName + \"-\");  // 发送设备标识\r\n      break;\r\n\r\n    case WStype_TEXT:\r\n      for (int i = 0; i < length; i++) {\r\n        msg += (char)payload[i];\r\n      }\r\n      if (msg.indexOf(\"heart#\") != -1) break;  // 忽略心跳包\r\n      Serial.println(\"收到指令: \" + msg);\r\n\r\n      // 车轮控制（保持不变）\r\n      if (msg.indexOf(\"ltw#\") != -1) {  // 左前进\r\n        myServoLeft.write(130);\r\n      }\r\n      if (msg.indexOf(\"rtw#\") != -1) {  // 右前进\r\n        myServoRight.write(130);\r\n      }\r\n      if (msg.indexOf(\"lts#\") != -1) {  // 左后退\r\n        myServoLeft.write(50);\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"rts#\") != -1) {  // 右后退\r\n        myServoRight.write(50);\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"mix#\") != -1) {  // 停止移动\r\n        myServoLeft.write(stopPos);\r\n        myServoRight.write(stopPos);\r\n        break;\r\n      }\r\n\r\n      // 机身旋转控制（改为180度角度控制）\r\n      if (msg.indexOf(\"cztz#\") != -1) {  // 机身左转（未到左边界0度）\r\n        fuselageAngle = constrain(fuselageAngle - 1, 0, 360);  // 角度减小（左转）\r\n        servoJiuf.write(fuselageAngle);\r\n        commandFuselage = 0;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"czty#\") != -1) {  // 机身右转（未到右边界180度）\r\n        fuselageAngle = constrain(fuselageAngle + 1, 0, 360);  // 角度增大（右转）\r\n        servoJiuf.write(fuselageAngle);\r\n        commandFuselage = 1;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"js#0\") != -1) {  // 机身停止（回到中间90度）\r\n        servoJiuf.write(stopPos);\r\n        fuselageAngle = stopPos;\r\n        commandFuselage = -1;\r\n        break;\r\n      }\r\n\r\n      // 大臂控制（180度范围：0-180，90度停止）\r\n      if (msg.indexOf(\"dbs#\") != -1 && bigstate != 1) {  // 大臂升（未到180度上限）\r\n        bigAngle = constrain(bigAngle + 1, 0, 180);       // 限制角度在0-180\r\n        myBig.write(bigAngle);\r\n        commandBig = 1;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"dbx#\") != -1 && bigstate != 0) {  // 大臂降（未到0度下限）\r\n        bigAngle = constrain(bigAngle - 1, 0, 180);\r\n        myBig.write(bigAngle);\r\n        commandBig = 0;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"db#0\") != -1) {  // 大臂停（回到90度）\r\n        myBig.write(stopPos);\r\n        bigAngle = stopPos;\r\n        commandBig = -1;\r\n        break;\r\n      }\r\n\r\n      // 小臂控制（180度范围：0-180，90度停止）\r\n      if (msg.indexOf(\"xbs#\") != -1 && smallstate != 1) {  // 小臂伸（未到180度上限）\r\n        smallAngle = constrain(smallAngle + 1, 0, 180);\r\n        mySmall.write(smallAngle);\r\n        commandSmall = 1;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"xbx#\") != -1 && smallstate != 0) {  // 小臂缩（未到0度下限）\r\n        smallAngle = constrain(smallAngle - 1, 0, 180);\r\n        mySmall.write(smallAngle);\r\n        commandSmall = 0;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"xb#0\") != -1) {  // 小臂停（回到90度）\r\n        mySmall.write(stopPos);\r\n        smallAngle = stopPos;\r\n        commandSmall = -1;\r\n        break;\r\n      }\r\n\r\n      // 挖斗控制（180度范围：0-180，90度停止）\r\n      if (msg.indexOf(\"wds#\") != -1 && wadbstate != 1) {  // 挖斗开（未到180度上限）\r\n        wadbAngle = constrain(wadbAngle + 1, 0, 180);\r\n        myWadb.write(wadbAngle);\r\n        commandWadb = 1;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"wdx#\") != -1 && wadbstate != 0) {  // 挖斗收（未到0度下限）\r\n        wadbAngle = constrain(wadbAngle - 1, 0, 180);\r\n        myWadb.write(wadbAngle);\r\n        commandWadb = 0;\r\n        break;\r\n      }\r\n      if (msg.indexOf(\"wd#0\") != -1) {  // 挖斗停（回到90度）\r\n        myWadb.write(stopPos);\r\n        wadbAngle = stopPos;\r\n        commandWadb = -1;\r\n        break;\r\n      }\r\n      break;\r\n\r\n    // 其他事件类型处理（保持不变）\r\n    case WStype_BIN:\r\n      Serial.printf(\"[WSc] 收到二进制数据，长度: %u\\n\", length);\r\n      hexdump(payload, length);\r\n      break;\r\n    case WStype_ERROR:\r\n    case WStype_FRAGMENT_TEXT_START:\r\n    case WStype_FRAGMENT_BIN_START:\r\n    case WStype_FRAGMENT:\r\n    case WStype_FRAGMENT_FIN:\r\n      break;\r\n    case WStype_PING:\r\n      Serial.printf(\"[WSc] 收到PING\\n\");\r\n      break;\r\n    case WStype_PONG:\r\n      Serial.printf(\"[WSc] 收到PONG\\n\");\r\n      break;\r\n  }\r\n}\r\n\r\n// 大臂上限 中断处理\r\nvoid IRAM_ATTR handleInterruptBigUpper() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeBig > 50) {  // 50ms防抖\r\n    extiTimeBig = currentTime;\r\n    if (digitalRead(bigUpper) == LOW) {  // 限位触发（低电平有效）\r\n      bigstate = 1;                      // 标记为上限 \r\n      extiBigOpen = true;\r\n    }\r\n  }\r\n}\r\n\r\n// 大臂下限 中断处理\r\nvoid IRAM_ATTR handleInterruptBigLower() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeBig > 50) {  // 50ms防抖\r\n    extiTimeBig = currentTime;\r\n    if (digitalRead(bigLower) == LOW) {  // 限位触发\r\n      bigstate = 0;                      // 标记为下限 \r\n      extiBigClose = true;\r\n    }\r\n  }\r\n}\r\n\r\n// 小臂上限 中断处理\r\nvoid IRAM_ATTR handleInterruptSmallUpper() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeSmall > 50) {  // 50ms防抖\r\n    extiTimeSmall = currentTime;\r\n    if (digitalRead(smallUpper) == LOW) {  // 限位触发\r\n      smallstate = 1;                      // 标记为上限 \r\n      extiSmallOpen = true;\r\n    }\r\n  }\r\n}\r\n\r\n// 小臂下限 中断处理\r\nvoid IRAM_ATTR handleInterruptSmallLower() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeSmall > 50) {  // 50ms防抖\r\n    extiTimeSmall = currentTime;\r\n    if (digitalRead(smallLower) == LOW) {  // 限位触发\r\n      smallstate = 0;                      // 标记为下限 \r\n      extiSmallClose = true;\r\n    }\r\n  }\r\n}\r\n\r\n// 挖斗上限中断处理\r\nvoid IRAM_ATTR handleInterruptWadbUpper() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeWadb > 50) {  // 50ms防抖\r\n    extiTimeWadb = currentTime;\r\n    if (digitalRead(wadbUpper) == LOW) {  // 限位触发\r\n      wadbstate = 1;                      // 标记为上限 \r\n      extiWadbOpen = true;\r\n    }\r\n  }\r\n}\r\n\r\n// 挖斗下限 中断处理\r\nvoid IRAM_ATTR handleInterruptWadbLower() {\r\n  unsigned long currentTime = millis();\r\n  if (currentTime - extiTimeWadb > 50) {  // 50ms防抖\r\n    extiTimeWadb = currentTime;\r\n    if (digitalRead(wadbLower) == LOW) {  // 限位触发\r\n      wadbstate = 0;                      // 标记为下限 \r\n      extiWadbClose = true;\r\n    }\r\n  }\r\n}\r\n\r\n\r\n\r\nvoid setup() {\r\n  Serial.begin(baudRate);\r\n  for (uint8_t t = 4; t > 0; t--) {\r\n    Serial.printf(\"[SETUP] 启动等待 %d...\\n\", t);\r\n    delay(1000);\r\n  }\r\n\r\n  // 初始化SPIFFS（如需读取WiFi配置）\r\n  if (!SPIFFS.begin(true)) {\r\n    Serial.println(\"SPIFFS初始化失败\");\r\n  }\r\n\r\n  // 连接WiFi（保持不变）\r\n  WiFiMulti.addAP(ssid.c_str(), passwd.c_str());\r\n  Serial.print(\"连接WiFi...\");\r\n  while (WiFiMulti.run() != WL_CONNECTED) {\r\n    delay(100);\r\n    Serial.print(\".\");\r\n  }\r\n  Serial.println(\"\\nWiFi已连接，IP: \" + WiFi.localIP().toString());\r\n\r\n  // 舵机初始化（分配PWM定时器）\r\n  ESP32PWM::allocateTimer(0);\r\n  myServoLeft.setPeriodHertz(50);\r\n  myServoLeft.attach(servoPinLeft, minPulse, maxPulse);\r\n  myServoLeft.write(stopPos);\r\n\r\n  ESP32PWM::allocateTimer(1);\r\n  myServoRight.setPeriodHertz(50);\r\n  myServoRight.attach(servoPinRight, minPulse, maxPulse);\r\n  myServoRight.write(stopPos);\r\n\r\n  ESP32PWM::allocateTimer(2);  // 大臂（180度）\r\n  myBig.setPeriodHertz(50);\r\n  myBig.attach(bigExc, minPulse, maxPulse);\r\n  myBig.write(stopPos);  // 初始停止在90度\r\n\r\n  ESP32PWM::allocateTimer(3);  // 小臂（180度）\r\n  mySmall.setPeriodHertz(50);\r\n  mySmall.attach(smallExc, minPulse, maxPulse);\r\n  mySmall.write(stopPos);  // 初始停止在90度\r\n\r\n  ESP32PWM::allocateTimer(4);  // 挖斗（180度）\r\n  myWadb.setPeriodHertz(50);\r\n  myWadb.attach(wadb, minPulse, maxPulse);\r\n  myWadb.write(stopPos);  // 初始停止在90度\r\n\r\n  // 机身舵机初始化（改为180度控制，与其他舵机统一）\r\n  ESP32PWM::allocateTimer(5);\r\n  servoJiuf.setPeriodHertz(50);  // 50Hz舵机标准频率\r\n  servoJiuf.attach(fuselage, minPulse, maxPulse);  // 共用脉宽参数\r\n  servoJiuf.write(stopPos);  // 初始位置：中间90度（停止状态）\r\n  Serial.println(\"所有舵机初始化完成\");\r\n\r\n  // 限位开关初始化（上拉输入，低电平触发）\r\n  pinMode(bigUpper, INPUT_PULLUP);    // 对应180度上限\r\n  pinMode(bigLower, INPUT_PULLUP);    // 对应0度下限\r\n  pinMode(smallUpper, INPUT_PULLUP);  // 对应180度上限\r\n  pinMode(smallLower, INPUT_PULLUP);  // 对应0度下限\r\n  pinMode(wadbUpper, INPUT_PULLUP);   // 对应180度上限\r\n  pinMode(wadbLower, INPUT_PULLUP);   // 对应0度下限\r\n  // 若有机身限位开关，添加引脚初始化（示例）：\r\n  // pinMode(fuselageLeftPin, INPUT_PULLUP);   // 机身左边界（0度）\r\n  // pinMode(fuselageRightPin, INPUT_PULLUP);  // 机身后边界（180度）\r\n\r\n  // 绑定限位中断（机身限位若有则添加）\r\n  attachInterrupt(digitalPinToInterrupt(bigUpper), handleInterruptBigUpper, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(bigLower), handleInterruptBigLower, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(smallUpper), handleInterruptSmallUpper, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(smallLower), handleInterruptSmallLower, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(wadbUpper), handleInterruptWadbUpper, CHANGE);\r\n  attachInterrupt(digitalPinToInterrupt(wadbLower), handleInterruptWadbLower, CHANGE);\r\n  // 若有机身限位，添加中断绑定（示例）：\r\n  // attachInterrupt(digitalPinToInterrupt(fuselageLeftPin), handleInterruptFuselageLeft, CHANGE);\r\n  // attachInterrupt(digitalPinToInterrupt(fuselageRightPin), handleInterruptFuselageRight, CHANGE);\r\n  Serial.println(\"限位中断初始化完成\");\r\n\r\n  // WebSocket初始化（保持不变）\r\n  webSocket.beginSSL(\"www.dyzk520.com\", 443, \"/ws\");\r\n  webSocket.onEvent(webSocketEvent);\r\n  webSocket.setReconnectInterval(5000);  // 重连间隔5秒\r\n  Serial.println(\"WebSocket初始化完成\");\r\n}\r\n\r\nvoid loop() {\r\n  webSocket.loop();  // 处理WebSocket通信\r\n\r\n  // 发送心跳包（每800ms）\r\n  static unsigned long lastPingTime = 0;\r\n  if (millis() - lastPingTime >= 800) {\r\n    webSocket.sendTXT(\"+NET_\" + deviceName + \"&111111-\");\r\n    lastPingTime = millis();\r\n  }\r\n\r\n  // 处理大臂限位触发\r\n  if (extiBigOpen) {\r\n    Serial.println(\"大臂到达180度上限\");\r\n    bigAngle = 180;  // 强制设为最大角度\r\n    myBig.write(bigAngle);\r\n    commandBig = -1;  // 停止动作\r\n    extiBigOpen = false;\r\n  }\r\n  if (extiBigClose) {\r\n    Serial.println(\"大臂到达0度下限\");\r\n    bigAngle = 0;  // 强制设为最小角度\r\n    myBig.write(bigAngle);\r\n    commandBig = -1;  // 停止动作\r\n    extiBigClose = false;\r\n  }\r\n\r\n  // 处理小臂限位触发\r\n  if (extiSmallOpen) {\r\n    Serial.println(\"小臂到达180度上限\");\r\n    smallAngle = 180;  // 强制设为最大角度\r\n    mySmall.write(smallAngle);\r\n    commandSmall = -1;  // 停止动作\r\n    extiSmallOpen = false;\r\n  }\r\n  if (extiSmallClose) {\r\n    Serial.println(\"小臂到达0度下限\");\r\n    smallAngle = 0;  // 强制设为最小角度\r\n    mySmall.write(smallAngle);\r\n    commandSmall = -1;  // 停止动作\r\n    extiSmallClose = false;\r\n  }\r\n\r\n  // 处理挖斗限位触发\r\n  if (extiWadbOpen) {\r\n    Serial.println(\"挖斗到达180度上限\");\r\n    wadbAngle = 180;  // 强制设为最大角度\r\n    myWadb.write(wadbAngle);\r\n    commandWadb = -1;  // 停止动作\r\n    extiWadbOpen = false;\r\n  }\r\n  if (extiWadbClose) {\r\n    Serial.println(\"挖斗到达0度下限\");\r\n    wadbAngle = 0;  // 强制设为最小角度\r\n    myWadb.write(wadbAngle);\r\n    commandWadb = -1;  // 停止动作\r\n    extiWadbClose = false;\r\n  }\r\n\r\n  // 处理机身限位触发（若有机身限位可启用）\r\n  if (extiFuselageLeft) {\r\n    Serial.println(\"机身到达0度左边界\");\r\n    fuselageAngle = 0;  // 强制设为最小角度\r\n    servoJiuf.write(fuselageAngle);\r\n    commandFuselage = -1;  // 停止动作\r\n    extiFuselageLeft = false;\r\n  }\r\n  if (extiFuselageRight) {\r\n    Serial.println(\"机身到达180度右边界\");\r\n    fuselageAngle = 180;  // 强制设为最大角度\r\n    servoJiuf.write(fuselageAngle);\r\n    commandFuselage = -1;  // 停止动作\r\n    extiFuselageRight = false;\r\n  }\r\n}\r\n','',1),
(316,4,'扫码播报器','#include <ESP8266WiFi.h>\r\n#include <PubSubClient.h>\r\n#include <ESP8266httpUpdate.h>\r\n#include <Hash.h>\r\n#include <ArduinoJson.h>\r\n\r\nWiFiClient wificlient;\r\nString wifiSsid = \"webserver\";\r\nString wifiPasswd = \"www.nobiggie.cn\";\r\n\r\nconst char* mqtt_server = \"www.nobiggie.cn\";\r\nconst char* mqtt_username = \"scan\";\r\nconst char* mqtt_password = \"abcdefg\";\r\nconst char* subTopic = \"wifi/dts33a/data\";\r\nconst char* pubshTopic = \"wifi/scan/data\";\r\nPubSubClient client(wificlient);\r\n\r\nvoid setup(void) {\r\n  Serial.begin(115200);\r\n  WiFi.begin(wifiSsid.c_str(), wifiPasswd.c_str());\r\n  WiFi.mode(WIFI_STA);\r\n\r\n  int ii = 0;\r\n  while (ii < 20 && WiFi.status() != WL_CONNECTED) {\r\n    delay(500);\r\n    Serial.print(\".\");\r\n    ii++;\r\n  }\r\n  Serial.println(\"wifi ok\");\r\n  client.setServer(mqtt_server, 1883);\r\n  client.setCallback(mqttCallback);\r\n}\r\n\r\nvoid loop(void) {\r\n  if (!client.connected()) {\r\n    reconnect();\r\n  }\r\n  client.loop();\r\n\r\n  if (Serial.available()) {\r\n    while (Serial.available()) {\r\n      String receivedString = Serial.readStringUntil(\'\\n\');  // 读取直到遇到换行符\r\n      client.publish(pubshTopic, receivedString.c_str());\r\n    }\r\n  }\r\n}\r\n\r\nvoid mqttCallback(char* topic, byte* payload, unsigned int length) {\r\n  String str;\r\n  for (int i = 0; i < length; i++) {\r\n    str += (char)payload[i];\r\n  }\r\n  if (str == \"updateBin\") {\r\n    while (1) {\r\n      ESPhttpUpdate.onStart(update_started);\r\n      ESPhttpUpdate.onEnd(update_finished);\r\n      ESPhttpUpdate.onProgress(update_progress);\r\n      ESPhttpUpdate.onError(update_error);\r\n      ESPhttpUpdate.update(wificlient, \"http://www.nobiggie.cn/tool/ota/GolangCpp/scan.bin\");\r\n    }\r\n    return;\r\n  }\r\n  Serial.write((const uint8_t*)payload, length);\r\n}\r\n\r\nvoid reconnect() {\r\n  while (!client.connected()) {\r\n    String clientId = \"ESP32Client-\";\r\n    clientId += String(random(0xffff), HEX);\r\n\r\n    if (client.connect(clientId.c_str(), mqtt_username, mqtt_password)) {  // 无认证时留空\r\n      Serial.println(\"MQTT 连接成功\");\r\n      client.subscribe(subTopic);\r\n    } else {\r\n      Serial.print(\"连接失败, rc=\");\r\n      Serial.print(client.state());\r\n      delay(5000);\r\n    }\r\n  }\r\n}\r\nvoid update_started() {\r\n  Serial.println(\"CALLBACK:  HTTP update process started\");\r\n}\r\n\r\nvoid update_finished() {\r\n  Serial.println(\"CALLBACK:  HTTP update process finished\");\r\n}\r\nvoid update_progress(int cur, int total) {\r\n  Serial.printf(\"CALLBACK:  HTTP update process at %d of %d bytes...\\n\", cur, total);\r\n}\r\n\r\nvoid update_error(int err) {\r\n  Serial.printf(\"CALLBACK:  HTTP update fatal error code %d\\n\", err);\r\n}\r\n','',1),
(317,4,'A7670  4G模块打电话 过程','ATD15677817648;    //返回 OK\r\n    +CGEV: NW ACT 8,10\r\n    +CLCC: 1,0,2,0,0,\"15677817648\",129,\"\"\r\n    +CLCC: 1,0,3,0,0,\"15677817648\",129,\"\"\r\n VOICE CALL: BEGIN     //对方已经接通电话\r\n    +CLCC: 1,0,0,0,0,\"15677817648\",129,\"\"\r\n    +COLP: \"15677817648\",129\r\n\r\n    +RXDTMF: 1\r\n    +RXDTMF: 2\r\n    +RXDTMF: 3\r\n    +RXDTMF: 4\r\n    +RXDTMF: 5\r\n    +RXDTMF: #  //对方按键 12345#\r\n\r\n    +CGEV: NW DEACT 8,10\r\n    +CLCC: 1,0,6,0,0,\"15677817648\",129,\"\"\r\n VOICE CALL: END: 000009     //通话结束','',1);

--
-- Table structure for table `WisdomRepair`
--

CREATE TABLE `WisdomRepair` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(20) NOT NULL DEFAULT '',
  `userid` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=557 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

--
-- Dumping data for table `WisdomRepair`
--

INSERT INTO `WisdomRepair` VALUES
(1,'电磁炉',1),
(2,'电压力锅',1),
(3,'洗衣机',1),
(4,'微波炉',1),
(5,'电脑',1),
(6,'冰箱/空调/饮水机',1),
(7,'电风扇',1),
(8,'液晶电视/显示器',1),
(9,'燃油汽车/电动车',1),
(10,'餐饮小吃店',1),
(11,'电热燃气',1),
(12,'手机维修',1),
(13,'网址收藏',1),
(14,'医学常识',1);

--
-- Table structure for table `WisdomRepairReply`
--

CREATE TABLE `WisdomRepairReply` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `tid` int(11) NOT NULL DEFAULT 0,
  `title` varchar(64) NOT NULL DEFAULT 'null',
  `content` text NOT NULL,
  `imgtext` varchar(100) NOT NULL DEFAULT '',
  `userid` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8077 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

--
-- Dumping data for table `WisdomRepairReply`
--

INSERT INTO `WisdomRepairReply` VALUES
(107,1,'按键面板不显示,不通电','1.拆开,观察一下整体哪个部件有烧损的痕迹 \r\n2.测量保险丝开路 \r\n3.再测量IGBT管基极,集电极之间短路 \r\n4.拆下IGBT,在线测量整流桥,根据整流桥的原理可以在线测量,坏了更换 \r\n5.这个时候直接更换S8050,S8550三极管,如果是贴片的也必须要更换,直接更换0.3UF电容,避免再烧IGBT管 \r\n6.测量300V滤波电容两极是否存在短路,没有短路现象才能接通电源','',1),
(108,1,'电源板更换损坏','首先把300V直接电容正极断开,然后找到接大风扇的正负极接18V,找到7805输出极,拆下7805后5V电压接到该输出极,12V可以空着','',1),
(109,1,'按键时而失效,受潮失效','自来水刷子刷干净\r\n用电吹风吹干\r\n更换全部按键\r\n那就补焊一下每个焊点','',1),
(114,2,'维修时注意防护 非常重要','电压力锅和电饭锅，绝缘导线套里有石棉，不要去动它，粉尘极小看不见的，以够粉尘吸入有害','',1),
(115,2,'换板注意事项','交流220V直接出来接火线,另一根交流电源线经过发热盘一端的那根接地线,剩余一个接发热盘,两根交流电线接反会使发热盘不发热','',1),
(117,3,'开机就报警，显示故障代码','通常开机报警或者按启动就报警，都是水位开关问题。更换水位开关，检查连接线是否接触不良即可解决，如解决不了，再更换电脑板。','',1),
(118,3,'其他程序正常，不能脱水，脱水跟洗衣服一样慢','此故障为排水阀排水时候没有把离合器拉开故障，解决方法：调整离合器把手螺丝，把排水阀跟排水堵头之间距离调整短一点，把离合器把手拉开到位即可','',1),
(119,3,'其他程序正常，不能脱水，排水完了，等4分钟左右就报警，或者不报警','水位开关故障，解决方法：更换水位开关即可','',1),
(120,3,'进水，洗衣服正常，不能排水，不能脱水','排水阀烧坏，导致电脑板烧坏。解决方法：同时更换排水阀跟电脑板即可	\r\n删除\r\n','',1),
(121,3,'其他程序都正常，就是不能脱水，排水完了或者开始排水就报警，显示故','门开关坏，桶平衡开关坏。解决方法：短路电脑板门开关跟桶平衡开关插座即可（短路方法：找到电脑板对应插座，把插头插好在电脑板上面，在电脑板对应插座把线皮剥开，直接扭在一起即可）','',1),
(122,3,'一直洗衣服，不能到排水脱水程序','桶平衡故障，一般为衣服在桶里面偏一边问题。解决方法：短路电脑板桶平衡插座即可（短路方法：找到电脑板对应插座，把插头插好在电脑板上面，在电脑板对应插座把线皮剥开，直接扭在一起即可）','',1),
(123,3,'进水完了，洗衣服时候报警','一般桶平衡故障，其中LG.松下洗衣机有干簧管保护的，应该是盖子磁铁掉了，干簧管不能吸和故障，解决方法：调整桶平衡即可。干簧管问题的，把盖子上面的磁铁安装好即可解决。','',1),
(124,3,'开机没有按启动排水阀就打开，按启动后一边排水一边进水','排水阀坏，导致电脑板烧坏。解决方法：电脑板跟排水阀同时更换即可。','',1),
(125,3,'进水正常，洗衣服程序会排水时','此故障应该是有手搓功能洗衣机，排水牵引器都是三线的，是排水牵引器与排水阀堵头拉线距离太短问题。解决方法：调整离合器把手螺丝，让距离长一点即可，或者改单行程（把牵引器左右两边线并接，剪断接电脑板的手搓功能线即可解决故障。不需要更换电脑板','',1),
(126,3,'开机没有启动就转,进水完了，洗衣服一边转或者不能转','电脑板烧坏，电机局部短路或者烧坏，电容坏。解决方法：量电机阻值（正常阻值两个副绕组相加=主绕组），电机阻值正常，电容正常更换电脑板即可解决，如电机阻值不相等，需要电脑板跟电机。','',1),
(127,3,'打开电源，没有按启动就进水','水位开关故障，更换水位开关即可解决，如解决不了再更换电脑板','',1),
(128,3,'启动后进水不停不能洗衣服','水位开关坏，或者水位开关皮管漏气原因。解决方法：更换水位开关，用胶水密封皮管两头。如果还是解决不了，再更换电脑板','',1),
(129,3,'波轮式洗衣机 可以甩干排水','先测电容,再单独通电电机看是否转动,正反转,电机是正常的,更换电脑板故障排除','',1),
(130,3,'单筒波轮式洗衣机启动线圈和运行线圈','阻值一样，双筒不一样','',1),
(131,4,'微波炉通电不加热,或不通电','检测一下保险丝有没有断开,电阻测量微波管为1欧左右为正常,拆下高压二极管,用电阻200M档正向导通,在30M左右,反向为无穷大,为正常','',1),
(132,4,'微波炉电容','有时测量是正常的但其实是损坏了的,最好的办法是直接替换,测量正反向电容应该不差于0.003UF','',1),
(134,5,'电脑开10分钟左右突然断电','从散热不良方面考虑,如果换了散热风扇还是没有作用的话,请重新拔插一下CPU或更换CPU','',1),
(135,5,'电脑蓝屏死机','先从系统方面考虑,先重装一下系统,如果不行再考虑硬件问题 比如内存条有没有装好或者损坏','',1),
(136,5,'电脑改装通电后延时几秒自动开机,','开机器为5V供电,主板主供电插卡头朝上,从左边第四个黄线接5V正,第八根为黑线接负,继电器接机箱开关按钮','',1),
(137,5,'GHOST系统后不能从硬盘正常启动','首先看一下硬盘有没有拿来做移动U盘,事前装过装机PE系统,没有返还空间,','',1),
(145,1,'IGBT管测量好坏','电阻档200K,红接第三脚黑笔接中间脚,阻值在73K左右, 反向测量为无限大,为正常.电阻档200M档测正反电阻有跳动数字后变为无限大,为正常,如果导通为击穿损坏,第一脚和中间脚正反向电阻都为无限大为正常!','',1),
(147,2,'开机几秒后显示E4错误代码','一般为温度传感器损坏,还有一个可能就是压力开关断路了,正常时是导通的!','',1),
(148,8,'创维液晶电视能遥控开机,开机后有声音没图像没字符且灰屏!','首先 用遥控器试下能否待机,能待机遥控,问题应该不是主板\r\n\r\n用酒精清理一下排线,如还是不行故障可能出现在主板或显示屏驱动电路,因为有声音主板故障率不高,应该为屏驱动电路有问题,难修!','',1),
(149,1,'线圈盘中心感温二极管','阻值在73K左右为正常','',1),
(150,1,'2n3904用8050 2n3906用c8550替换','2n3904用8050 2n3906用c8550替换','',1),
(154,3,'波轮洗衣机一直进水且溢出，能单独排水','先用万用表测一下水位开关导线有无开路，在有装有水的状态下用牙签疏通一下水压导管进水处看到有明显水流出后，查看水压导管是否存在露气，插回水压导管','',1),
(157,1,'故障代码E2','拆开电磁炉,把锅盘感温器拆开不接,然后直接通电如果能正常,更换锅盘感温二极管故障排除','',1),
(181,1,'烧IGBT管,换后不加热几秒后面板闪烁','继续更换IGBT管两脚的稳压二极管,更新220V电源的2UF电容故障排除','',1),
(182,3,'滚筒洗衣机按键失灵','用电吹风加热某部位后就能正常使用,拆下电脑板对加热部位用酒精清洗,故障排除','',1),
(183,4,'微波炉不加热保险丝未断','拔开变压器初级,用万用表交流档测有无220V,如有220V测高压二极管,电容,更换微波管','',1),
(184,4,'微波炉测量变压器','检测时要把线先拔下来\r\n微波炉变压器的初级线圈阻值2欧,\r\n磁控管线圈电阻：约2Ω,\r\n高压保险丝对变压器外壳电阻250欧左右\r\n最后还应该检测 初级线圈,磁控管线圈都应该为无穷大才是正常的','',1),
(186,4,'微波炉电容好坏测量','用电容档测量是否跟标识容量一致,然后用电阻20M档测电阻会在1M逐渐上升至10M左右,正反向电阻测量效果都是这样且最大值都误差不大为正常','',1),
(187,4,'微波炉不加热','高压保险丝没有烧断,测高压二极管和电容都为正常,门开关正常(电阻档,一表笔放在220V输入保险丝那,另一表笔分别测量门开关),测量高压变压器输入端电压是否有220V,如果没有肯定是机械开关或电脑板问题,还有一种是有220v电压的也是机械或电脑板问题,里面接触不良引起的.','',1),
(189,5,'电脑屏幕重影','更换数据线','',1),
(190,3,'波轮洗衣机开机启动几秒后显示F1','为水位传感器问题!更换水位开关或水管','',1),
(191,5,'电脑时不时的卡，硬盘有声音','如果是装系统GHOST的时候没有停卡现像，有时候很正常，有时卡得动不了，请更换一个质量好的电脑电源！','',1),
(232,2,'电脑功能正常,但是不加热','仔细听一下按上煮饭键后,\r\n有没有听到继电器吸合的声音,\r\n如果有,再测一下连接发热盘的导航有没有开路,没有开路,更换继电器->更换驱动三极管','',1),
(233,1,'按键面板显示,按键无反应','先测量该按键连接铜泊线是否通,如通,酒精清洗一下集成电路各引脚,再补焊上一般都可以解决','',1),
(234,6,'空调加制冷剂','1.先查看空调制冷剂型号和量度\r\n2.先使压缩机运行\r\n3.打开制冷剂的伐门\r\n4.打开两通伐的阀门(排一下管里的空气,两次1秒开关操作)\r\n5.加注大概需要30分钟左右,当压力表 在0.5到0.6MP时为加注量合适(冬天压力会偏低,冬天小阀会有点结箱)\r\n6.关制冷剂阀门,关两通伐的阀门\r\n7.制冷剂过多时表现为,小阀和大阀都结箱,看表的压力不明显\r\n8.制冷剂不够,小阀结箱,压力表只有0.2MP左右','',1),
(235,6,'空调制冷剂过多放出制冷剂','1.不能直接放制冷剂,要连接表阀来放,防止冻伤手 2.放制冷剂时表阀变化不明显,所以只能通过观察,小阀大阀结箱的状态来控制放量 3.大阀,小阀箱冻水化状态时,说明制冷剂放出量合适了','',1),
(239,1,'开启3秒左右就停电','检查一下，cpu傍的5UF电容有没膨胀或直接拆下来测量','',1),
(249,6,'制冷原理','压缩机→冷凝器→节流(毛细管)→蒸发 ( 吸热 ) 的制冷循环\r\n压缩机一般由壳体、电动机、缸体、活塞、控制设备 ( 启动器和热保护器 ) 及冷却系统组成\r\n压缩机吸气管吸入低温低压的制冷剂气体\r\n压缩机排气管排出高温高压的制冷剂气体\r\n为制冷循环提供动力\r\n经过冷凝器散热变成高压中温\r\n经过毛细管 毛细管末端变为低压低温\r\n在蒸发器内 汽化时必须吸热，使冷媒水因失热而降低了温度\r\n又被制冷压缩机吸入压缩，这样周而复始\r\n','',1),
(250,6,'压缩机','压缩机一般由壳体、电动机、缸体、活塞、控制设备 ( 启动器和热保护器 ) 及冷却系统组成\r\n启动器基本上有两种，即重锤式和 PTC 式。其中后者较为先进\r\n冷却方式有油冷和自然冷却两种\r\n一般家用冰箱的压缩机是以单相交流电作为电源，压缩机功率较小，通常在 250W 以下','',1),
(251,6,'毛细管作用','毛细管供液量小，蒸发器内的制冷剂偏少，制冷效果差\r\n供液量过大，蒸发压力会很高，蒸发温度高、温度达不到相应的温度等级，严重的还会造成压缩机无法停机故障\r\n毛细管供液能力与尺寸有关。长度长、内径小，供液量越小,反之越大\r\n电冰箱中使用的毛细管，一般使用内径为0.5～1mm、长度2～4m的紫铜管。其尺寸主要由电冰箱采用的压缩机功率与其相应温度等级确定\r\n','',1),
(253,6,'压缩机好坏检测','排出雪种,拆下压缩机剪断低压和高压端,用手堵高压端如堵不住就是正常的\r\n还有交流电流法和电阻法可以判断压缩机是否正常!启动电容也要测量一下\r\n','',1),
(254,6,'打压检漏','用自身压缩机打压比较适合打低压管，不适合打高压管。在实际应用中一般仅打低压就够了，只要低压不漏便是漏高压。正常使用中的冰箱两侧同时漏的概率不高。\r\n   打低压的方法：\r\n1、割开回气管\r\n2、在回气管端接上压力表\r\n3、通电开机并观察表压，当表压到4至5kg时断电，此时表压会继续上升，约8至10kg才停止\r\n4、封死过滤器出口处毛细管\r\n5、观察表压即可','',1),
(256,6,'冰堵脏堵判断','断电并清理冰箱箱里使雪全部融化,一个小时以上\r\n再通电开机,如果刚开始一段时间制冷之后不制冷为冰堵\r\n一直不能制冷为脏堵','',1),
(257,6,'冰箱系统正常工作的压力','600高压0.5低压负0.05\r\n12高压0.9低压0.04\r\n以上为正常工作后的压力','',1),
(259,6,'焊堵检测方法','压力为0的状态下,接表,然后开启冰箱,运行30秒左右,停机过一时间看看压力表如果归0就是通的,为负压就是有焊堵','',1),
(264,6,'焊炬点火,停火顺序','1.点火,打开液化气瓶阀,打开氧气瓶阀,先打开焊枪燃气点火,慢慢调焊枪氧气到合适\r\n2.停火,先关焊枪氧气,再关液化瓶气阀,再关焊枪燃气开关,再关氧气瓶阀','',1),
(271,6,'冰箱维修步骤','1. 9成以上缺氟就是漏气\r\n2. 不管冰箱是否有雪种,切开干燥过滤器和毛细管焊接处(切开哦不要焊,安全)\r\n3. 启动压缩机吹干净从冷凝器里的雪种(安全方法)\r\n4. 焊封住毛细管,回气管焊接单向阀,然后连接压力表三通阀,连接外置打压机,打0.8MPA,观察两个小时左右\r\n5. 压缩机工艺管焊接单向阀(加雪种抽真空用)\r\n6. 压力正常直接外挂冷凝器,否则要开背修了!\r\n7. 焊接点要圆润光滑\r\n8. 充制冷剂时要慢慢的充!\r\n9. 30分钟内压缩机回气管必须要结露,不然很可能有,冰,脏,堵现象!\r\n10. 如果压缩机 出现嗡 停一秒 嗡 停一秒 反复这样肯定就是冰堵严重了!\r\n11. 冰堵就要连续带雪种抽两到三次真空,抽真空时用电热吹风吹一下干燥过滤器!\r\n12. 真空泵,抽真空最少20分钟左右','',1),
(272,6,'打压检漏','一般常用压缩机打压,也有用氮气打压,千万不能用氧气打压,氧气遇油会爆炸','',1),
(273,6,'空调压缩机参数','正常运行有5.3A交流电流\r\n空载电流在3.2A左右','',1),
(275,7,'风扇维修步骤','1. 在未通电状态下,用镙丝刀推动一下扇叶,看是否灵活!(卡住,电机就上机油)\r\n2. 手握风扇塑料,用手去感觉,是否点击开启后有轻微抖动,同时用耳朵注意听是否有交流声!(如有更换电容)\r\n3. 没有任何反应,拆开风扇,定时器开启,测量是否导通,(不导通更换定时器)\r\n4. 焊开电容一脚,用数字万用表测量一下电容值(电容容量正常,更换风扇电机)','',1),
(278,2,'时而按键失灵时而正常','更换78L05不行再更换470UF电解电容','',1),
(279,3,'波轮洗衣机边进水边自动放水','拆开底下管子，里面应该是有什么异物卡住了！','',1),
(280,7,'遥控风扇(遥控都正常,电机不转)','拆开电路板测量双向可控硅两脚是否短路!如短路更换可控硅\r\n通电测量交流电压,在操作123档时分别测量对应档位是否有220V电压\r\n对应三档档位中只能有一个档位有220V电压为电路正常,电路正常更换电机就好了','',1),
(290,5,'没有活动分区的固态硬盘','固态硬盘是新的GPT分区表格式，没有活动分区\r\n指定EPS一个分区盘付,然后用U修复把引导信息写到ESP分区','',1),
(291,1,'间断1秒反复加热','1.查检锅回路有无虚焊 \r\n2.查功率管\r\n更换->5UF->0.33UF->2UF->8050 8550三极管或贴片3904 3906                        ','',1),
(294,5,'启动电脑,CPU转了两秒就停了','重新擦干净一下内存条,重插一下CPU,不行直接更换电脑电源试下','',1),
(296,2,'当按下煮饭键后,显示板熄灭后无反应','供电主板上更换稳压二极管,或开关二极管,不行就把三极管全换了','',1),
(298,1,'开启一切显示都正常，就是不加热','更换开关二极管4148和18v稳压二极管，更换8050 8550,不行更换LM339','',1),
(300,1,'间断2秒反复加热','首先在插电源的时候仔细听一下,电磁炉有没有18v风扇运行两秒后停止 \r\n如果有,那就是CPU有问题了,不用再检测了','',1),
(302,1,'加热很慢','先更换电容5UF，故障依旧,更换0.33UF，2UF更换后故障依旧, \r\n更换推动管8050 8550三极管或贴片3904 3906 等三极管,看下周边有没有虚焊的电容,故障排除','',1),
(303,2,'感温电阻','阻值为74.5K 到100K','',1),
(305,1,'加热很慢(间歇性加热),按键开关时而失灵','按键开关是好的状态下 \r\n更换功率管(功率管线性不好,这种状态下测量不出好坏)','',1),
(307,5,'播放视频中不定时声音卡顿,振荡卡顿','更换电脑硬盘,为保护电脑硬盘养成完全关机后再拔电源','',1),
(308,2,'按键显示正常,不加热','在按下煮饭键后,仔细听一下没有有继电器吸合声,如没有,更换驱动三极管8050或继电器','',1),
(309,1,'E0故障代码','更换一下5UF/400V电容\r\n补焊大线圈电感\r\n不行的话 更换电源 IC 更换完成后\r\n用万用表测量一下5678脚对3.4脚的正反向电阻是否为短路，短路说明IC是坏的\r\n小功率电阻 200k 检查一下是否开路','',1),
(317,1,'E9故障代码','检查。检锅回路电阻。5uf/400v电容','',1),
(318,1,'能开机,无法关机','能开机说明面板单片机没有问题,(之前更换过单片机也一样),更换主板上78L05,故障排除','',1),
(319,1,'只能开关机,别的按键都失灵','电磁炉电感线圈可能脱焊了','',1),
(320,1,'加热一段时间后故障e3','更换8脚电源芯片','',1),
(321,1,'按开关键后，按键无效','仔细观察一下,开机后屏幕显示在哪个档位,故障原因为个别按键开关粘合,导致开机后按键失效','',1),
(322,1,'按开机键后E7','检测推动三极管周围的电阻,或三极管有问题,可以在线测量,(查看电阻阻值,在线测量如果阻值大于标识拆下来测量),','',1),
(323,5,'主机里面风扇呼呼响','风扇坏了，更换CPU风扇','',1),
(324,1,'面板按键失效','补焊->清洁->更换IC->更换78l05','',1),
(333,1,'按键有效果,但时而没有加热','测量功率管 发现 GE两脚踩有80欧,更好故障排除\r\n','',1),
(493,2,'旋钮带电源驱动定时器','插电就自动加热，触动压力开关动作后，跳到保压保温指示灯后，这时定时器电机才有供电，开始计时回旋，如中途压力不够会跳回加热，直到压力够了，又回保压保温，直到回到定时器起点，不会跳回加热为正常.更换定时器时按原来接线即可.','',1),
(497,3,'波轮洗衣机只一个方向转动','用钳表分别测量电脑板控制电机的两根控制线,在电流声间隔响声中,交流电流会随着一升一归0说明电脑板正常,更换电容故障排除','',1),
(521,1,'电磁炉漏电相关','电磁炉正常状态插电后没有按键启动,这时用220v电笔是不亮的,启动后用电笔测量会有一点暗亮是正常现像,如果很亮是真的漏电了','',1),
(523,9,'汽车发动机调气门','标准间隙排气的0.25毫米进气的0.20毫米\r\n顶筒的看筒内数字加+测量间隙再-标准间隙=新顶筒内数字厚度','',1),
(525,9,'发动机油水混合','有微型摄像机可以塞进火花塞查看气缸内部问题\r\n如果气门被顶,拆开发动机更换气门,调气门,活塞被顶坏了就更换活塞\r\n更换缸垫,更换水堵(很重要)','',1),
(527,9,'清洗废机油','用柴油或汽油清洗，化油器成本太高','',1),
(531,9,'更换发动机缸垫 ','直径大于缸头宽度的大砂轮片,厚度10毫米以上\r\n\r\n把缸头放水平整,从缸头 前双手用力向缸尾推\r\n\r\n再跑到缸头尾 双手用力向缸头前推,如此往复\r\n\r\n来回一共60次,即每边30次看到表面都被磨过即可\r\n\r\n如果是高温多次的缸体也要磨一次,往返一共30次即可','',1),
(533,9,'b12发动机凸轮轴固定标记','E0到E4代表排气\r\n1I到4I(不是11到41)为进气','',1),
(535,9,'汽车发动机调气门','标准间隙排气的0.25毫米进气的0.20毫米\r\n顶筒的看筒内数字加+测量间隙再-标准间隙=新顶筒内数字厚度\r\n排气最小不低于0.2毫米,最大不能超过0.4毫米\r\n进气最低就是有间隙就行,最大不能超过0.3毫米','',1),
(539,9,'发动机抖动','1.查看点火顺序连接是否正确\r\n2.缺缸运行检测,在运行的状态下,分别拔下连接火花塞的点火胶套,如果拔出后,发动机没有任何变化,此缸有问题\r\n3.在运行状态下,测量喷油嘴供电电源应有5v左右\r\n4.拔下喷油嘴,稍拔出点,在启动试机状态下,看喷油出口壁是否有油水印为正常','',1),
(541,9,'正常发动机关闭发动机后会有吸气声','正常发动机关闭发动机后会有吸气声','',1),
(545,9,'发动机启动咔咔声,有时启动几次才成功','两种原因\r\n1.电瓶老化,或者亏电\r\n2.启动器旁边的电磁开关或者继电器接触不良,拆下清理灰尘即可,时间太长的话连同启动马达一起清理灰尘','',1),
(547,9,'装正时链条时,注意事项','固定正时的螺丝不能拧太紧很容易断开\r\n正时没有对上千万不能转动曲轴,以免顶到气门\r\n装完后记得在正时链条和涨紧器上放机油润滑\r\n装好后,面朝正时链接条顺时针转动几圈,应有气缸气体压缩阻力感,能转动说明没有顶到气门,OK了','',1),
(549,9,'更换排气管','排气管连接是一个橡胶,拆时在橡胶上涂点机油,然后用长柄镙丝刀翘开','',1),
(551,9,'变速箱齿轮油不可用发动机油替换','手头上没齿轮油，变速箱加发动机机油暂时顶用最好不要超过50公里应马上换为齿轮油，这时排出来的油应该是黑色的了，如果是齿轮油几公里都是黄色的。','',1),
(557,9,' 更换水箱宝(防冷液)','1.水箱散热铝片下面有个塑料镙丝,逆时针拆转动取下来,排出里面的水\r\n\r\n2.节温器拆下排水排锈渣,然后装好(记得放点防油胶水)\r\n\r\n3.水箱加入水超过标记处两指\r\n\r\n4.找到排空气镙丝,逆时针松开排空气(会听到吱吱气流声)直到镙丝有水出来就拧紧\r\n\r\n5.空档启动车子20秒左右,然后再找到排空气镙丝,再次排空气','',1),
(563,9,'更换轮胎镙丝','有的车子前后轮镙丝是有区别的\r\n如果放不进镙丝的话,试下在镙丝头旁边切一刀,就可以放进去了\r\n后轮镙丝能直接拆拿出来的话,装上去时把轮盘后面4个镙丝拆了,用个长的镙丝或镙丝刀,从后面镙丝口伸进来敲好新镙丝','',1),
(567,9,'更换碟刹车片','1.把轮胎拆下后,把刹车油管固定的U型卡扣拆下 \r\n\r\n2.用一字镙丝刀,杠杆顶缩回油钳活塞,拧下固定刹车片镙丝,转动方向盘配合把油钳拆下\r\n\r\n3.把刹车片旧的拆下换新,装回油钳,拧紧镙丝 \r\n\r\n4.转动轮盘,如有卡紧现象,把排油镙母拧松放油,直到轮盘轻松转动为止,拧紧排油镙母,装回轮胎 \r\n\r\n5.在启动车子后踩刹车不太灵,踩很深才有点效果,管里有空气引起的.顶起车子,把排油镙母拧松滴油为止,把刹车油壶帽打开,然后启动车子,深踩5到6次刹车使刹车油跟空气一起喷出来,拧紧排油镙母','',1),
(569,9,'鼓刹片与碟刹片','一部分车，前碟刹后鼓刹，一般换两次碟刹后即第三次换碟刹时才连同鼓刹一起换。\r\n\r\n一般大货车都是鼓刹的多','',1),
(573,8,'液晶电视开机后约2分钟左右就自动黑屏','判断为电源升压电路故障或灯管问题,从故障发作时间很短判断是电路问题更换电源升压板后,故障排除','',1),
(577,3,'波轮洗衣机进水同时,排水管也出水','1.先在断电状态下倒两勺水进去,看排水口是否有水出来\r\n2.如果排水口这时有水出来,那么就是排水口有异物卡住了排水阀使它关不紧\r\n3.拧开排水阀塑料镙母,这个角度看不到里面的情况,用手机拍照看一下里面的异物.取出异物即可\r\n','',1),
(579,5,'usb供电不足','电脑电源老化更换好的电脑电源\r\n电源要选择CPU座旁有8口插座,即两条电源供电的,平常只要插一条也能点亮,但是如果供电不足最好是两根的都插','',1),
(581,5,'主机开机无任何反应','1.拔下电源开关线,万用表测量一下开关按键是否正常\r\n2.通电状态下,用镙丝刀直接短路主板电脑开关一,从个脚\r\n3.把BIOS电池取下,短接正负极放电,然后重新开机\r\n4.cpu拆了重装->更换电源->更换主板\r\n','',1),
(597,10,'油条利润计算','面粉/2元\r\n泡多源 15g(500g17元 每克0.034元)0.51元\r\n油条膨松剂 5g(400g 10元 每克0.025元) 0.125元\r\n食盐 8g \r\n色拉油 10g (500g 3元 每克0.006) 0.06元\r\n鸡蛋 0.5个 一个0.5元半个0.25元算\r\n油炸损耗3元\r\n面粉2元+1元<泡多源+膨松剂+食盐+色拉油+ 半个鸡蛋>=3元\r\n3元+油炸损耗3元=6元 每斤面至少要做出13根油条=13元\r\n13-6元=7元-1元电费燃气费=6\r\n一斤面至少要做出13根油条,不然是赚不到钱的','',1),
(599,10,'豆浆利润计算','大豆每斤3元左右\r\n1g水=1.166ml水\r\n1ml豆浆放0.08g白糖\r\n1杯豆浆放满是350ml留点空为320ml\r\n1斤(500g)大豆做9斤(4500g)豆浆\r\n那么4500g豆浆就是*1.166=5247ml\r\n5247ml / 320ml=16.39杯\r\n1杯子 1吸管费=0.1元成本费\r\n16杯=16元 -1.6元杯费=14.4元\r\n5247ml * 0.08=420g白糖(每斤4元算) =3.4元\r\n14.4-3.4=11 -3元/斤大豆=8元\r\n8-2元电费=6元利润\r\n一斤豆至少要做出9斤豆浆不然是赚不到钱的','',1),
(601,10,'豆浆与油条配对制作量','1斤大豆=16杯豆浆\r\n1斤面粉=13根油条\r\n2斤面=26根油条\r\n500g/16杯=31.25g 每杯需要的大豆量\r\n31.25g *26=812.5g /500= 1.625斤大豆','',1),
(603,6,'冰箱维修','1. 9成以上缺氟就是漏气\r\n2. 不管冰箱是否有雪种,距干燥过滤器8CM外切开毛细管(切开哦不要焊,安全)\r\n3. 启动压缩机吹干净从冷凝器里的雪种(安全方法)\r\n4. 封口钳封住低压路毛细管(封口钳能封住毛细管,别的封不紧)\r\n5. 启动自身压缩机抽真空20分钟左右,观察连接冷凝器刚切开的毛细管\r\n6. 如果有气出来说明是低压漏,如果没有气出来是高压漏(这个方法微漏不准确)\r\n7.留2个小时后把封号钳去掉让毛细管流通,这时有气流声说明低压正常\r\n8.压缩机连接冷凝器的管路不要割拆压缩机口,要留一段长度进行割焊(非常重要)','',1),
(605,6,'氧焊技巧','氧焊分三种火焰温度以小到大排列，碳化焰(三层火焰，中焰长），中性焰(三层火焰，短中焰锥形)，氧化焰(两层火焰，没有中焰且有强烈嘶嘶声)\r\n三种火焰又分大小，就是说虽然都是中性焰但是燃气，氧气开得大的中性焰温度比小的中性焰的温度差别很大(技巧就在这调大小中性焰)！\r\n焊接时用中性焰的中间火焰或内焰吹焊条，吹熔化的铜水，使均匀填在连接处，马上移开火焰','',1),
(607,1,'开启就报警且没有加热效果,面板灯有微闪','更换5UF','',1),
(748,11,'燃气热水器点火器停止点火后就熄灭','更换感应针,不行再更换煤气磁控阀','',1),
(890,9,'手动变速箱1档2档挂不进','检查挂档杆两线的固定镙母是否松动','',1),
(894,8,'更换背光灯条步骤与注意事项','仔细用电筒看一下屏幕表面，有内印不必修屏幕坏了\r\n液晶屏非常非常非常脆弱，一点不均匀平放都会使整个屏报废。\r\n拆开屏幕与主板连接线，\r\n准备一个可以平整而且无尘桌子（拆开后放屏幕用）\r\n把屏幕塑料框边的螺丝拆完\r\n带上塑料手套，防手摸到反光板弄脏了\r\n准备两条木条，屏幕左4分之1和右边4分之1分别竖放，用透明宽胶带粘屏幕跟木条，双手轻拉两木条取出屏幕','',1),
(898,4,'微波炉只有下面底盘不转别的都正常','更换220v转盘电机','',1),
(909,1,'插电有滴声且面板亮一下就熄，之后按键无效','把面板拆下来用水冲洗，刷子刷一下。\r\n电热吹风吹干\r\n把所有开关都更换。\r\n一般为开关潮湿漏电导致','',1),
(913,11,'燃气灶更换点火时注意事项','把两个风门关了，不然不易点火','',1),
(915,3,'波轮洗衣机，进水排水都正常，不能洗衣服','进水满后，有电机交替嗡嗡声就是不转，更换电机电容','',1),
(917,3,'滚筒,更换电脑板,进水没动作,开机几秒后报警','首先电脑板是新的可以排除问题\r\n进水阀没有动作.断电状态下测导线是否导通\r\n如导通以下操作\r\n查看门锁开关是否正常,可以短接的形式启动看\r\n短接开机正常,更换门锁开关\r\n','',1),
(941,8,'开机无图像，检修','拆开拔掉灯管连接线，用灯管测试仪加电，记得正负极别搞反，\r\n如果能点亮一般为电源板问题，不亮为灯管','',1),
(943,3,'维修滚筒洗衣机注意事项','更换电脑板前，一定要检查门锁是否短路，最后跟门锁一齐换了','',1),
(953,5,'笔记本电脑装系统时不认U盘','把读卡器换成usb3.0就行了','',1),
(964,1,'电磁炉线圈盘接法','螺旋外圈接功率管C极','',1),
(1127,7,'遥控电风扇不通电','测量105j400v降压电容,测量5.6v1w稳压二极管，如果保险丝烧断了。后路有短路，记得检查一下可控硅1-3脚是否短路','',1),
(1297,3,'滚筒洗衣机7脚门锁测量好坏','左4右3面向，5,6脚252欧。6,7脚248欧\r\n5,7脚应该是两个之合502殴左右','',1),
(1488,3,'滚筒洗衣机排水阀阻值','滚筒洗衣机排水阀阻值在170欧左右为正常，测到地电阻为无穷大为正常','',1),
(1489,3,'滚筒洗衣机三脚门锁检测好坏','1和3脚有200多欧，推开推卡，1和2脚导通为正常','',1),
(1490,5,'固态硬盘分区 esp msr分区方法','创建第一个分区时esp msr都默认\r\n然后在之后的分区c盘上装系统,装好系统\r\nUEFI修复一下,记得esp指定一个分区一般都是指派最后一个分区名\r\n\r\n也可以创建第一个分区时esp 不创建msr\r\n在C盘上装好系统,系统引导自动修复一下','',1),
(1701,11,'燃气热水器火烧黑旁边','用空压机吹通里面火路的灰尘','',1),
(2148,8,'更换灯条注意事项','找个大场地\r\n1.拆外壳\r\n2.拆排线\r\n3.贴胶布取屏幕\r\n4.拆荧光板\r\n5.单个测灯条,哪个坏换哪个\r\n6.过程反过来装回去','./public/upfiles/images/reply/QQ图片20210106082049.jpg',1),
(2610,5,'4线USB接线方法','接9线座，1排有5个的。多出的线算1 接线2345对应黑绿白红线','',1),
(2852,7,'风扇开几分钟就停 ，且是遥控风扇','查一下降压电容105j是否有1uf','',1),
(3089,1,'一通电没有按键就间断响','拆开把面板和主机连接线断开,通电看是否还响响,如果没有响,为面板按键受潮问题','',1),
(3090,7,'吊扇三线接法','测量阻值居中的那根黑线接220V一端,\r\n另一根非黄线再接220V另一端','',1),
(3093,9,'加油无力','查看汽车是不是外置汽油滤清器,\r\n内置的不需要更换,因为内置的在油箱里面,而且很大\r\n一般去大弄加油站8万公里左右换一次外置汽油滤清器\r\n小加油站6万公里左右就要换\r\n','',1),
(3097,9,'涡轮增压发动机注意事项','1.至少半合成机油以上\r\n2.涡轮不需要清理,清理不当反而容易损坏\r\n3.涡轮工作时温度800度到1000度的高温.好的机油可以更好的降温','',1),
(3321,11,'电子热水器不通电检修步骤','拆下漏电保护器单独测电压是否正常\r\n正常再测量一下发热管是否有几十欧阻值\r\n36欧 50欧 86欧 测对外金属外壳是否有短路或漏电现像\r\n如果正常，直接更换电脑板，看看显示板要不要换，一般都一起换','',1),
(3792,7,'遥控风扇按开机键转一下就停,按住不放也不转 ','更换5.6v1w稳压二极管         ','',1),
(3840,1,'间歇2秒循环报警','放锅上去间歇2秒左右报警，\r\n检查检锅电路，大电阻是否断开或者阻值变大\r\n功率管周边的大电阻焊下测一下阻值是否正常一般不超过1M阻值为正常','',1),
(3841,1,'不通电,保险丝没有烧','更换 IC 然后听一下IC旁边变压器是否有嘶嘶声，如有，焊下220v4.7uf测量阻值，电容阻值正常，直接更换变压器','',1),
(5313,11,'燃气热水器有打电火声,没着火','有电火声说明其它都正常就是煤气通道不通或未开,\r\n先更换电池,不行再更换煤气电磁阀','',1),
(6428,12,'手机屏开机一闪亮一下就熄灭 ','升压电感或升压电容问题\r\n手机升压电路  \r\n由 升压电感 IC 整流二极管 滤波电容组成','',1),
(6448,11,'燃气热水器不打火','手动短接看是否打火，如果打火更换水压膜片\r\n否则拔开插口用镙丝刀直接短接，\r\n如果打火更换水动开头\r\n否则更换点火器，如果更换点火器还是不打火\r\n测电池电压正常，那应该是电池盒里生锈了，更换电池盒','',1),
(6452,5,'查看系统是否是已久激活','运行 cmd\r\nslmgr.vbs -xpr','',1),
(6454,13,'https://github.com/','https://github.com/','',1),
(6456,13,'B站','https://www.bilibili.com/','',1),
(6458,13,'单机游戏网','https://www.3dmgame.com/','',1),
(6459,13,'有道翻译','https://fanyi.youdao.com/','',1),
(6460,13,'西西软件','https://www.cr173.com/','',1),
(6461,13,'php官网','https://www.php.net/','',1),
(6462,13,'风行影视','http://www.fun.tv/','',1),
(6463,13,'河池同城游','http://www.tcy365.com/hc/','',1),
(6464,13,'单片机arduino官网','https://www.arduino.cn/','',1),
(6465,13,'https://cn.vuejs.org/','https://cn.vuejs.org/','',1),
(6466,13,'改图宝','https://www.gaitubao.com/','',1),
(6468,13,'爱纯净系统','http://www.aichunjing.com/','',1),
(6470,5,'GHOST还原恢复系统失败','cmd cd 到GHOST程序目录下\r\n执行命令 Ghost64.exe -ntexact\r\n//必须是GHOST 12版本以上\r\n如果使用的PE系统中没有这个版本可以电脑手动下载后放到U盘里\r\n使用时cmd cd到存放位置 \r\n版本12.0.0.8006 使用正常(备份恢复时都是使用这个版本)','',1),
(6477,8,'屏幕出现规则横线纹','断开一边屏线,一边就能显示正常\r\n更换逻辑板\r\n','',1),
(6802,13,'树莓派系统官网','https://www.raspberrypi.com/software/operating-systems/#raspberry-pi-os-64-bit','',1),
(6804,4,'微波炉电脑板故障','按键与显示都正常就是不加热\r\n如果已经确定是电脑板问题,试一下更换电脑板上面的部份三极管,一般能解决问题','',1),
(6806,12,'更换充电尾插','拆开手机壳\r\n扭开螺丝\r\n取出充电小板\r\n固定小板\r\n热风枪吹\r\n吸锡带清理底坐\r\n酒精清洗底座\r\n放入锡膏\r\n装入新的插口\r\n热风枪吹\r\n如果有引脚粘连 用电烙铁吸锡带吸一下就行','',1),
(6809,2,'按键显示都正常就是不加热','开启后，听一下没有吸合的声音\r\n测量电压是否有12v\r\n如果没有12v 断开70L05输入脚后，电压12v左右正常\r\n说明CPU 或者 连接CPU 供电另一路分支出现问题\r\n直接断开压力开关传感器，开机看看是否报错E4\r\n如果没有报错测量连接CPU引脚到传感器是否正常连接\r\n如果正常 CPU坏了\r\n\r\n如果报错E4了 检查cpu供电引脚电源另一分支线路\r\n','',1),
(6812,11,'电热水器显示按键正常但不加热','拆开\r\n断开加热管连线，测量其电阻值\r\n100欧以内正常, 阻值越小其功率瓦数越大\r\n\r\n把继电器连接的加热管断开，通电测量继电器在开机加热状态下有没有220V电压(一般没有电压)\r\n直接更换继电器再测量，\r\n如果还是没有电压更换电源板','',1),
(6813,5,'电脑开不了机，或者更换主板点不亮','首先检查重启开关是否沾连，如果沾连直接导通，也会使主板点不亮','',1),
(6814,14,'小孩咳嗽 雾化+抗生素','小孩咳嗽 雾化+抗生素','',1),
(6815,14,'预防肺炎 ','1.喂完奶粉千万记得要让宝宝坐着喂，喂完十分钟后再让宝宝自由活动，不然吐奶就会呛到，引起肺炎\r\n2.正常情况下，新生儿的呼吸频率可达40-45次/分，1个月至一岁，宝宝呼吸频率30-40次/分，一岁至3岁，呼吸频率为25-30次/分，4-7岁，呼吸频率为20-25次/分，八岁以上，呼吸频率接近成人，18-20次/分。年龄越小，呼吸频率相对较快，小儿以腹式呼吸为主，成人以胸式呼吸为主\r\n3.发烧达到39度就要服用退烧药，千万记得这时要吃消炎药，消炎药是杀死细菌的卫兵，退烧药只能让你不能再烧下去不然烧坏了，消炎药最少吃三天，最多8天 \r\n4.头孢克肟和头孢哌酮舒巴坦钠的区别\r\n都是第三代头孢，区别在前者是口服，后者是输液\r\n5.小孩子呛到了记得及时拍背','',1),
(6816,14,'感冒感染区分','感冒分为病毒和细菌感染\r\n流青鼻涕为病毒感染 使用 利韦巴林\r\n流浓鼻涕为细菌感染 使用 头孢或阿莫西林，阿奇霉素\r\n','',1),
(6817,14,'胃不舒服 ','反酸 打咳 一般是幽门螺旋杆菌感染\r\n一般使用三联药物治疗  \r\n奥美拉挫 阿莫西林 克拉霉素\r\n坚持吃 最多能吃8天\r\n\r\n最好的方法是食疗 每天早晚各一大碗豆浆,最好不放糖,7天就好了','',1),
(6820,14,'儿童一般3岁以后不容易得肺炎','儿童3岁以后免疫功能逐渐增强,6岁时免疫功能基本与成人相似','',1),
(6821,14,'肺炎判断','1 呼吸频率判断 不同年龄频率不同,超过5次就有感染风险\r\n2 肚皮呼吸,有感染风险\r\n3.发烧,有感染风险','',1),
(6825,3,'美的波轮洗衣机F8故障代码','检测水位开关导管，水位开关，还有接口是否漏气','',1),
(6826,14,'拉肚子 用药方法 ','18岁以下使用蒙脱石散\r\n18岁以上可以使用诺氟沙星\r\n ','',1),
(6828,1,'一通电在没有任务按键操作下 报警 ','先更换4.7UF/400V电解电容\r\n一般都是大件问题或大件虚焊或者感温器问题，\r\n一个感温器在线圈盘中心，还有一个在场效应管附近','',1),
(6843,10,'豆腐圆','馅 配方 \r\n豆腐圆 每斤肉放1.33两糯米饭，1斤肉放0.02斤盐，葱花每斤肉放一两，每斤肉放0.012味精,柠檬叶看情况份量不定\r\n煎豆腐圆使用的豆油 \r\n为节约成本,用食用豆油3.5元/斤混合专用煎油 比例1比1','',1),
(7160,1,'控制面板按键无反应','测量5伏电压正常。\r\n更换按键开关。故障依旧。\r\n更换贴片芯片。故障依旧。\r\n更换瓷片电容101\r\n故障排除。','',1),
(7165,3,'波轮洗衣机一直进水不转','更换水位传感器\r\n更新时必须注意以下几点\r\n1.必须在排完水后更换\r\n2.更换时最好拿铜丝绑紧接口处让其密封好\r\n3.通水\r\n4.调水位开关上面那个镙帽可以调节水位对应相对电子档位，向外转是调低水位','',1),
(7166,13,'在线视频转换','https://convertio.co/zh/','',1),
(7167,3,'滚筒洗衣机运行10多分钟就停','滚筒洗衣机排水采用抽水方式\r\n如果不排水也会有这种现象\r\n测量220v两脚电阻正常180欧左右\r\n无穷大或过小就更换','',1),
(7497,9,'4冲程割草机 难启动或启动一下就停','更换 化油器','',1),
(7498,6,'饮水机不制冷','拆下制冷片测量电阻 例如tec1-2706 正向向电阻正常为 0欧和45欧左右，如果阻值差别很大更换，更换后还是一样，更换主板','',1),
(7504,13,'国家中小学智慧教育网','https://basic.smartedu.cn/','',1),
(7838,6,'冰箱封口焊接步骤注意事项','必须在断电停止运行的状态下进行封口焊接，因为运行的时候管道内是负压很危险的\r\n用封口钳压两道，在压第三道的时候，封口钳要一直留在那里等风封口焊接完再拿下来\r\n用割管器把封口钳前面20mm左右割开\r\n封口稍微往上抬，做好周围部件的防热，就可以用氧焊口','',1),
(7839,3,'甩干内桶呼呼响，内桶撞到内壁','更换4个平衡杆','',1),
(7840,13,'火狐浏览器视频缓存','浏览器上输入 about:cache\r\n\r\nC:\\Users\\Administrator\\AppData\\Local\\Mozilla\\Firefox\\Profiles\\6m23yd8k.default-release-1\\cache2 ','',1),
(7864,13,'http://www.huayunengxiang.com/','http://www.huayunengxiang.com/','',1),
(7872,13,'https://www.ktkkt2.com/','https://www.ktkkt2.com/','',1),
(7874,1,'电磁炉面板无显示5V供电不正常未烧保险','更换电源IC\r\n更换电源小变压器','',1),
(7896,13,'springboot框架在线生成','https://start.spring.io/','',1),
(7901,12,'360通话录音','手机识别码 0bda0bi0\r\n激活码212  有规律  b代表2 隔一个算一个。a代表1','',1),
(7905,3,'滚筒洗衣机通电就报警','看看显示面板 门锁有没有关闭\r\n门锁关闭了 更换 水位传感器 ','',1),
(7906,13,'另一个微信号','\r\n密码 qq@277791907','',1),
(7915,13,'在线图片转文字','http://imagetotxt.com/','',1),
(7918,13,'www.41114.tv','www.41114.tv','',1),
(7922,13,'arm64 mysql5.7','https://blog.csdn.net/qq_40875195/article/details/129215341','',1),
(7924,13,'https://hanime1.me/watch?v=37590','https://hanime1.me/watch?v=37590','',1),
(7925,13,'https://xanimu.com/','https://xanimu.com/','',1),
(7929,3,'波轮洗衣机桶不转,点击有交流声','用交流电流表测量是否有交替的交流电,\r\n有交替的交流电流,测量一下电容,\r\n电容容量没问题,把电容装回去,测量电机连接电容的两根线是否有电容容量,如果没有,查一下线路是否被老鼠咬\r\n如果两根线有电容容量,拆下电机接电容单独测试看看扭力够不够','',1),
(7933,9,'电动车电瓶保养','使用工具\r\n1.浓缩修复液  2 一字小螺丝刀 3 注射针管\r\n一 浓缩液用量 每个电瓶安数 / 2 \r\n比如12A的   12/2=6毫升  如果一个是电瓶有6个格，每个格要加6毫升,加完502胶水粘好盖子\r\n二 静放4小时,然后充电2小时,把点放完,然后一次性把点充满,再放电,做循环让修复液彻底循环\r\n','',1),
(7944,14,'过敏性咳嗽，咳嗽频率搞','富马酸酮替芬片','',1),
(7969,13,'redis window版下载地址','https://github.com/MicrosoftArchive/redis/releases','',1),
(7980,5,'电脑电源单独检测好坏','24口插脚凸面朝下,右下开始数第4根绿线短接随意黑线,\r\n电源风扇转动就是好的\r\n','',1),
(7982,5,'在不拆机状态下查看主板型号','Win+R 组合键\r\nmsinfo32 回车','',1),
(8013,14,'颈椎病会引起高血压','脖子僵硬,转头颈椎疼,是由于脖子后面左右两根神经不通,严重是会起床或躺下天旋地转血压升高\r\n注意事项\r\n冷天注意脖子保暖\r\n不要床上看手机,\r\n坐在电脑前30分钟,运动一下脖子,背部肌肉','',1),
(8014,5,'内存真假防伪方法','运行Thaiphoon.exe\r\n选择Read 下拉\r\nJEDEC DIMM Label 下面\r\n8GB 2Rx8 (2R开头为真) 1R是假内存-服务器拆机颗粒的','',1),
(8015,13,'香橙派3','http://www.orangepi.cn/html/hardWare/computerAndMicrocontrollers/service-and-support/Orange-Pi-Zero-3.html','',1),
(8021,5,'电脑开手机模拟器就蓝屏,必须开启CPU虚拟化','进入BIOS 启用SVM Mode','',1),
(8022,4,'烧坏220V保险管','把连接电容的线全拔掉,分别测量电容两个脚对外壳电阻,都为无穷大,\r\n重点测量门开关和微波发射管,如果使用几天才烧220V保险管,为微波炉发射管问题,一般为门开关不良,更换就可以了','',1),
(8030,14,'小孩不吃肉,抵抗力低','小孩不吃肉,抵抗力低,必须吃肉','',1),
(8062,3,'洗衣机进水阀电阻值','4k左右','',1),
(8063,8,'图像抖动','图像抖动,时间越久越明显\r\n一般是屏幕问题,屏里面应该有短路\r\n处理方法\r\n逻辑板,两个排线,有一根需要技改来维修\r\n如果维修中灰屏了,就是两边都有问题,无法修复','',1),
(8064,1,'电磁炉E5','功率管感温二极管(正常60k左右,有时候也需要换),补焊检锅传感器','',1),
(8065,5,'电脑编程系统必备','QQ拼音输入法\r\nQQ五笔\r\ncpu 温度检测\r\ngolang\r\narduino\r\nffmpeg\r\n桌面CMD\r\nFlashFXP\r\n火狐浏览器\r\nEdge\r\nQQ\r\n微信电脑版\r\n央视影音\r\n迅雷\r\nQQ游戏\r\n\r\nphp\r\nsublime\r\n','',1),
(8066,13,'ipv6网址检测','https://v6t.ipip.net/','',1),
(8067,5,'电脑电源好坏','用外接卡测试，\r\n3.3v应该有3.38v-3.4v\r\n5v 应该有 5.12v\r\n12v 应该有 12.12v','',1),
(8068,5,'电脑不定时蓝屏或死机','查看CPU温度都是0度，一般为CPU问题','',1),
(8069,9,'汽车防鼠','1.空调进气口使用铸工胶+网购橱柜铝合金网格(量好尺寸)\r\n2.车尾轮胎后面 车内气压泄压孔(用于关门时车内气压泄压)，使用金属片量好尺寸剪好打孔+铸工胶封好\r\n3.发动机撒点风油清可以驱鼠防止老鼠咬线路\r\n4.车里不要使用吸引老鼠的香水','',1),
(8070,8,'液晶电视开机背光闪1秒就灭','使用灯条测试仪能点亮灯条，这个情况不一定是主板问题，测量一下主板灯条供电电压正常100v左右，只后慢慢降到40v左右，说明主板正常,灯条问题','',1),
(8071,4,'微波炉微波管好坏检测','电阻测量\r\n1.测量灯丝电阻：微波管有两个灯丝引脚，正常情况下，这两根引脚之间的电阻应该很小，一般在 1Ω 左右\r\n2.常情况下，这个电阻值应该为无穷大，因为灯丝与外壳之间是绝缘的。如果测得有一定的电阻值，说明微波管的绝缘性能下降，存在漏电现象，也是损坏的表现','',1),
(8072,3,'洗衣机排水正常两分钟后E4','短接门开关\r\n不行就更换水位传感器','',1),
(8073,4,'微波炉接磁控管的两根线阻值也是0欧左右','微波炉接磁控管的两根线阻值也是0欧左右','',1),
(8074,4,'微波炉电容有一边对外壳电阻230欧左右','微波炉电容有一边对外壳电阻230欧左右','',1),
(8075,4,'微波炉烧保险','检查电容容量和电容电地电阻看看是否短路\r\n电容有一边对外壳电阻230欧左右\r\n如果完好,更换门开关','',1),
(8076,7,'遥控电风扇开机不通电','测一个 稳压二级管5.1v,在线就可以测量','',1);

--
-- Table structure for table `WisdomTools`
--

CREATE TABLE `WisdomTools` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category` varchar(32) NOT NULL DEFAULT '',
  `userid` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

--
-- Dumping data for table `WisdomTools`
--

INSERT INTO `WisdomTools` VALUES
(1,'家电维修',1),
(3,'制冷维修',1),
(4,'汽车维修',1),
(5,'智能家居',1),
(6,'车上必备',1),
(7,'外出必备',1),
(8,'水果工具',1),
(29,'必学技能',1);

--
-- Table structure for table `WisdomToolsReply`
--

CREATE TABLE `WisdomToolsReply` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `category` varchar(32) NOT NULL DEFAULT '',
  `name` varchar(32) NOT NULL DEFAULT '',
  `number` int(10) DEFAULT 0,
  `buy` float DEFAULT 0,
  `freight` float NOT NULL DEFAULT 0,
  `date` date NOT NULL,
  `userid` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1042 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;

--
-- Dumping data for table `WisdomToolsReply`
--

INSERT INTO `WisdomToolsReply` VALUES
(1,'汽车维修','40件套(内六角十二梅花)',1,46,0,'2019-09-07',1),
(3,'家电维修','电烙铁芯内热式',1,10,1,'2019-09-07',1),
(961,'家电维修','英爵伦电吹风',1,27,0,'2023-08-16',1),
(5,'家电维修','936焊台烙铁芯',3,12,1,'2019-09-07',1),
(6,'家电维修','6寸老虎钳',1,7,1,'2019-09-07',1),
(7,'家电维修','八角锤2.2斤',1,13,1,'2019-09-07',1),
(780,'家电维修','3斤松香',1,24,0,'2020-10-18',1),
(9,'家电维修','烙铁头936电焊台',1,5.5,1,'2019-09-07',1),
(10,'家电维修','汉邦936恒温电烙铁',1,59,0,'2019-09-07',1),
(838,'家电维修','802电动镙丝刀批头 ',34,3,0.5,'2022-04-19',1),
(12,'汽车维修','8寸一字镙丝刀',1,8.5,1,'2019-09-07',1),
(13,'家电维修','8寸十字镙丝刀',1,8.5,1,'2019-09-07',1),
(874,'家电维修','17寸双层工具箱',1,15,0,'2022-08-18',1),
(839,'家电维修','936烙铁头5个一套',1,13.5,0.5,'2022-04-19',1),
(17,'家电维修','十字镙丝刀200m',1,3.8,1,'2019-09-07',1),
(18,'家电维修','9v电子称充电电源',1,10,0,'2019-09-07',1),
(19,'家电维修','107吸锡器',2,9.6,1,'2019-09-07',1),
(20,'家电维修','10寸活动板手',1,12.8,1,'2019-09-07',1),
(21,'家电维修','铁皮剪8寸',1,11.8,0,'2019-09-07',1),
(22,'家电维修','热熔胶枪',1,14,0,'2019-09-07',1),
(23,'家电维修','电热熔枪胶棒',10,0.8,1,'2019-09-07',1),
(960,'智能家居','康夫电吹风',1,68,0,'2023-08-16',1),
(810,'智能家居',' 手机遥控电动卷闸门 ',1,180,0,'2021-06-18',1),
(26,'家电维修','12v2000瓦逆变器',1,350,0,'2019-09-07',1),
(836,'家电维修','焊锡丝500克',1,39,0,'2022-03-23',1),
(28,'家电维修','936焊台烙铁头',2,6,1,'2019-09-07',1),
(29,'汽车维修','保险丝30A',3,0.5,0,'2019-09-07',1),
(30,'汽车维修','保险丝20A ',3,0.5,0,'2019-09-07',1),
(31,'汽车维修','保险丝15A',5,0.5,0,'2019-09-07',1),
(32,'汽车维修','72齿弯柄棘轮61件套',1,210,0,'2019-09-07',1),
(33,'汽车维修','12/24脉冲充电器',1,92,0,'2019-09-07',1),
(34,'汽车维修','千斤顶(气压)',1,50,0,'2019-09-07',1),
(35,'制冷维修','飞越真空泵',1,265,0,'2019-09-07',1),
(36,'制冷维修','封口钳',1,17,0,'2019-09-07',1),
(37,'制冷维修','弯管器6/8/10m',1,28,0,'2019-09-07',1),
(38,'制冷维修','扩管器割管刀',1,32,1,'2019-09-07',1),
(39,'制冷维修','雪种万能开瓶',2,9,1,'2019-09-07',1),
(40,'制冷维修','钳形电流表(交流)',1,27,1,'2019-09-07',1),
(42,'制冷维修','500克硼砂',2,8,0,'2019-09-07',1),
(44,'制冷维修','2升液化瓶减压阀',1,16,0,'2019-09-07',1),
(45,'制冷维修','小型割管器',2,12,0,'2019-09-07',1),
(46,'制冷维修','2.5Mpa压力表头',2,7,0,'2019-09-07',1),
(868,'制冷维修','R134A汽车空调高压接头',1,8,0,'2022-08-13',1),
(48,'汽车维修','12寸活动板手',1,15.3,0,'2019-09-07',1),
(49,'家电维修','6寸尖嘴钳',1,9,0,'2019-09-07',1),
(50,'家电维修','6寸细尖嘴钳',1,12,0,'2019-09-07',1),
(51,'家电维修','尖嘴镊子',2,5,1,'2019-09-07',1),
(52,'家电维修','卷尺',1,3,0,'2019-09-08',1),
(53,'家电维修','802电动镙比刀',1,120,0,'2019-09-08',1),
(54,'家电维修','3米人字梯',1,330,0,'2019-09-08',1),
(55,'家电维修','三路单独开关插座',1,30,0,'2019-09-08',1),
(58,'智能家居','手机遥控定时充电插座',1,20,0,'2019-09-08',1),
(61,'智能家居','wifi路由器',1,98,0,'2019-09-08',1),
(62,'智能家居','夜视摄像头',1,168,0,'2019-09-08',1),
(1026,'智能家居','5700+16g+512g+显示器',1,2000,0,'2025-02-22',1),
(64,'智能家居','空调',1,3200,0,'2019-09-08',1),
(65,'智能家居','冰箱',1,1680,0,'2019-09-08',1),
(977,'智能家居','滚筒洗衣机',1,1500,0,'2024-05-28',1),
(67,'智能家居','电脑桌',1,168,0,'2019-09-08',1),
(68,'智能家居','厨房液化燃气',1,200,0,'2019-09-08',1),
(69,'智能家居','电磁炉',2,200,0,'2019-09-08',1),
(970,'智能家居','浴霸',1,70,0,'2023-12-06',1),
(71,'智能家居','沐浴露',1,30,0,'2019-09-08',1),
(73,'智能家居','洗发水',1,30,0,'2019-09-08',1),
(74,'智能家居','洗衣粉',1,20,0,'2019-09-08',1),
(75,'智能家居','.剃须刀',1,20,0,'2019-09-08',1),
(76,'智能家居','飞科理发器',1,100,0,'2019-09-08',1),
(814,'车上必备','三头剃须刀 ',1,19,0,'2021-06-18',1),
(78,'车上必备','纯净水',1,2,0,'2019-09-08',1),
(789,'车上必备','灭火器',1,15,0.5,'2020-12-28',1),
(788,'车上必备','灭火器',1,15,0.5,'2020-12-28',1),
(81,'车上必备','雨伞',1,10,0,'2019-09-08',1),
(887,'车上必备','360行车记录仪',1,300,0.5,'2023-04-29',1),
(83,'车上必备','千斤顶',1,50,0,'2019-09-08',1),
(84,'车上必备','几个大号塑料袋',1,1,0,'2019-09-08',1),
(85,'车上必备','三角板',1,0,0,'2019-09-08',1),
(86,'车上必备','车贴黑膜',1,100,0,'2019-09-08',1),
(89,'智能家居','拖鞋',1,20,0,'2019-09-08',1),
(88,'家电维修','网线钳-测试仪',1,25,0,'2019-09-08',1),
(90,'外出必备','纸巾',1,2,0,'2019-09-08',1),
(91,'外出必备','镜子',1,10,0,'2019-09-08',1),
(92,'外出必备','理发器',1,100,0,'2019-09-08',1),
(93,'外出必备','剃须刀',1,20,0,'2019-09-08',1),
(937,'外出必备','手机',1,3000,0,'2023-05-19',1),
(95,'外出必备','驾驶证',1,0,0,'2019-09-08',1),
(96,'外出必备','手机充电器',1,0,0,'2019-09-08',1),
(97,'外出必备','沐浴露',1,30,0,'2019-09-08',1),
(98,'外出必备','两套换洗',1,0,0,'2019-09-08',1),
(99,'外出必备','拖鞋',1,20,0,'2019-09-08',1),
(100,'汽车维修','拾取器(买工具箱附送)',1,0,0,'2019-09-08',1),
(758,'家电维修','晶思达数字万用表',1,80,0,'2020-05-27',1),
(857,'家电维修','洗衣机捶打扳手',1,50,0,'2022-07-28',1),
(942,'家电维修','4-12mm九层含古钻头',1,14.3,0,'2023-05-26',1),
(808,'智能家居','电动牙刷 充电能用一年',1,40,0,'2021-06-18',1),
(1012,'家电维修','电动扳手(伯特里)全铝',1,91.1,0,'2024-10-17',1),
(109,'水果工具','糖度仪0~32%',1,105,0,'2019-09-09',1),
(867,'餐饮小吃','双层保温桶',1,168,0,'2022-08-12',1),
(964,'家电维修','波轮洗衣机拉钩一对',1,4,0,'2023-08-22',1),
(116,'水果工具','打药管收纳轮(80)',2,110,0,'2019-09-09',1),
(114,'水果工具','打药喷杆',2,20,0,'2019-09-09',1),
(936,'遥控工具车','24VDC继电器6脚 C型',8,3,0,'2023-05-19',1),
(732,'水果工具','1寸18米水带',5,33,0,'2019-10-05',1),
(746,'餐饮小吃','测温仪-32到380度',1,38,0,'2019-11-23',1),
(743,'餐饮小吃','豆浆奶茶封口机',1,200,0,'2019-11-22',1),
(741,'餐饮小吃','不锈钢油炸盆60x40x15',1,78,0,'2019-11-21',1),
(742,'餐饮小吃','30L豆浆保温桶',1,105,0,'2019-11-22',1),
(739,'餐饮小吃','擀面杖直径3.2cm长度30cm',1,13.5,0,'2019-11-21',1),
(738,'餐饮小吃','擀面杖直径3.2cm长度30cm',1,13.5,0,'2019-11-21',1),
(737,'餐饮小吃','不锈钢油炸盆60x40x15',1,78,0,'2019-11-21',1),
(735,'车上必备','灭火器',1,70,0,'2019-11-19',1),
(736,'水果工具','汽油抽水泵',1,180,0,'2019-11-19',1),
(733,'汽车维修','黑旋封586汽车耐油温密封胶',1,10,0,'2019-10-18',1),
(730,'汽车维修','气门拆装油封钳',1,18,0,'2019-09-30',1),
(727,'汽车维修','气门拆装油封钳',1,18,0,'2019-09-30',1),
(728,'汽车维修','棕刚玉150*25*32(粒度80)',1,16,0,'2019-09-30',1),
(729,'汽车维修','棕刚玉150*25*32(粒度80)',1,16,0,'2019-09-30',1),
(724,'汽车维修','机油壳扳手',1,15,0,'2019-09-29',1),
(723,'汽车维修','机油壳扳手',1,15,0,'2019-09-29',1),
(722,'汽车维修','塞尺',1,6,0,'2019-09-28',1),
(721,'汽车维修','气门工具',1,43,0,'2019-09-28',1),
(720,'汽车维修','塞尺',1,6,0,'2019-09-27',1),
(719,'汽车维修','气门工具',1,43,0,'2019-09-27',1),
(717,'汽车维修','3吨卧式千斤顶(汽修专用)',1,334,0,'2019-09-12',1),
(716,'汽车维修','3吨卧式千斤顶(汽修专用)',1,334,0,'2019-09-12',1),
(714,'水果工具','30升酒壶(装柴油)',1,32,0,'2019-09-09',1),
(713,'水果工具','水箱2000斤',1,330,0,'2019-09-09',1),
(840,'水果工具','PVC软管1寸牛筋50米',2,169,0,'2022-04-28',1),
(711,'水果工具','50米抽刚电缆',1,45,0,'2019-09-09',1),
(710,'水果工具','至尊电子秤计价30kg',1,88,0,'2019-09-09',1),
(709,'水果工具','抽水管50米',1,120,0,'2019-09-09',1),
(708,'水果工具','风冷柴油机',1,800,0,'2019-09-09',1),
(707,'水果工具','打药管收纳轮(80)',2,110,0,'2019-09-09',1),
(706,'水果工具','打药喷杆',2,20,0,'2019-09-09',1),
(705,'水果工具','45cm大果剪',1,35,0,'2019-09-09',1),
(704,'水果工具','糖度仪0~32%',1,105,0,'2019-09-09',1),
(703,'水果工具','60v抽水泵300W',1,130,0,'2019-09-09',1),
(702,'水果工具','4冲程割草机',1,420,0,'2019-09-09',1),
(701,'水果工具','水箱540斤',1,150,0,'2019-09-09',1),
(700,'水果工具',' 水箱660斤',1,176,0,'2019-09-09',1),
(699,'水果工具','220v抽水机370w',1,188,0,'2019-09-09',1),
(698,'水果工具','200米打药管(65)',1,260,0,'2019-09-09',1),
(696,'外出必备','拖鞋',1,20,0,'2019-09-08',1),
(697,'水果工具','打药泵',1,400,0,'2019-09-09',1),
(695,'外出必备','两套换洗',1,0,0,'2019-09-08',1),
(694,'外出必备','沐浴露',1,30,0,'2019-09-08',1),
(693,'外出必备','手机充电器',1,0,0,'2019-09-08',1),
(692,'外出必备','驾驶证',1,0,0,'2019-09-08',1),
(691,'外出必备','手机',1,1500,0,'2019-09-08',1),
(690,'外出必备','剃须刀',1,20,0,'2019-09-08',1),
(689,'外出必备','理发器',1,100,0,'2019-09-08',1),
(688,'外出必备','镜子',1,10,0,'2019-09-08',1),
(687,'外出必备','纸巾',1,2,0,'2019-09-08',1),
(686,'车上必备','车贴黑膜',1,100,0,'2019-09-08',1),
(685,'车上必备','三角板',1,0,0,'2019-09-08',1),
(684,'车上必备','几个大号塑料袋',1,1,0,'2019-09-08',1),
(683,'车上必备','千斤顶',1,50,0,'2019-09-08',1),
(682,'车上必备','行车记录仪',1,368,0,'2019-09-08',1),
(681,'车上必备','雨伞',1,10,0,'2019-09-08',1),
(680,'车上必备','大功率手机充电器',1,40,0,'2019-09-08',1),
(679,'车上必备','.安全套',1,30,0,'2019-09-08',1),
(678,'车上必备','纯净水',1,2,0,'2019-09-08',1),
(677,'车上必备','.剃须刀',1,20,0,'2019-09-08',1),
(676,'智能家居','拖鞋',1,20,0,'2019-09-08',1),
(675,'智能家居','飞科理发器',1,100,0,'2019-09-08',1),
(674,'智能家居','.剃须刀',1,20,0,'2019-09-08',1),
(673,'智能家居','洗衣粉',1,20,0,'2019-09-08',1),
(672,'智能家居','洗发水',1,30,0,'2019-09-08',1),
(671,'智能家居','沐浴露',1,30,0,'2019-09-08',1),
(670,'智能家居','48寸液晶电视机',1,4300,0,'2019-09-08',1),
(669,'智能家居','电磁炉',2,200,0,'2019-09-08',1),
(668,'智能家居','厨房液化燃气',1,200,0,'2019-09-08',1),
(667,'智能家居','电脑桌',1,168,0,'2019-09-08',1),
(666,'智能家居','滚筒洗衣机',1,3500,0,'2019-09-08',1),
(665,'智能家居','冰箱',1,1680,0,'2019-09-08',1),
(664,'智能家居','空调',1,3200,0,'2019-09-08',1),
(663,'智能家居','8升电压力锅',1,330,0,'2019-09-08',1),
(662,'智能家居','夜视摄像头',1,168,0,'2019-09-08',1),
(661,'智能家居','wifi路由器',1,98,0,'2019-09-08',1),
(660,'智能家居','电脑',1,1700,0,'2019-09-08',1),
(659,'智能家居','台式电脑下载共享备份机',1,400,0,'2019-09-08',1),
(658,'智能家居','手机遥控定时充电插座',1,20,0,'2019-09-08',1),
(657,'智能家居','手机遥控电动卷闸门',1,50,0,'2019-09-08',1),
(656,'汽车维修','拾取器(买工具箱附送)',1,0,0,'2019-09-08',1),
(655,'汽车维修','12寸活动板手',1,15.3,0,'2019-09-07',1),
(654,'汽车维修','千斤顶(气压)',1,50,0,'2019-09-07',1),
(653,'汽车维修','12/24脉冲充电器',1,92,0,'2019-09-07',1),
(652,'汽车维修','72齿弯柄棘轮61件套',1,210,0,'2019-09-07',1),
(651,'汽车维修','保险丝15A',5,0.5,0,'2019-09-07',1),
(650,'汽车维修','保险丝20A ',3,0.5,0,'2019-09-07',1),
(649,'汽车维修','保险丝30A',3,0.5,0,'2019-09-07',1),
(718,'汽车维修','40件铁盒套(内6角12梅花)',1,46,0,'2019-09-12',1),
(648,'汽车维修','8寸一字镙丝刀',1,8.5,1,'2019-09-07',1),
(646,'制冷维修','加长点火棒',2,5,0,'2019-09-07',1),
(645,'制冷维修','2.5Mpa压力表头',2,7,0,'2019-09-07',1),
(643,'制冷维修','2升液化瓶减压阀',1,16,0,'2019-09-07',1),
(644,'制冷维修','小型割管器',2,12,0,'2019-09-07',1),
(642,'制冷维修','发泡枪',1,10,0,'2019-09-07',1),
(641,'制冷维修','500克硼砂',2,8,0,'2019-09-07',1),
(640,'制冷维修','2升焊炬双用氧气瓶',1,225,0,'2019-09-07',1),
(639,'制冷维修','钳形电流表(交流)',1,27,1,'2019-09-07',1),
(638,'制冷维修','雪种万能开瓶',2,9,1,'2019-09-07',1),
(637,'制冷维修','扩管器割管刀',1,32,1,'2019-09-07',1),
(636,'制冷维修','弯管器6/8/10m',1,28,0,'2019-09-07',1),
(635,'制冷维修','封口钳',1,17,0,'2019-09-07',1),
(633,'家电维修','数字万用表',1,40,0,'2019-09-08',1),
(634,'制冷维修','飞越真空泵',1,265,0,'2019-09-07',1),
(632,'家电维修','网线钳-测试仪',1,25,0,'2019-09-08',1),
(631,'家电维修','工作台(自焊接DIY)',1,50,0,'2019-09-08',1),
(630,'家电维修','三路单独开关插座',1,30,0,'2019-09-08',1),
(629,'家电维修','3米人字梯',1,330,0,'2019-09-08',1),
(628,'家电维修','802电动镙比刀',1,120,0,'2019-09-08',1),
(627,'家电维修','卷尺',1,3,0,'2019-09-08',1),
(626,'家电维修','尖嘴镊子',2,5,1,'2019-09-07',1),
(625,'家电维修','6寸细尖嘴钳',1,12,0,'2019-09-07',1),
(624,'家电维修','6寸尖嘴钳',1,9,0,'2019-09-07',1),
(623,'家电维修','936焊台烙铁头',2,6,1,'2019-09-07',1),
(622,'家电维修','焊锡丝350克',1,25,1,'2019-09-07',1),
(621,'家电维修','12v2000瓦逆变器',1,350,0,'2019-09-07',1),
(620,'家电维修','康夫电吹风',1,68,0,'2019-09-07',1),
(619,'家电维修','电热熔枪胶棒',10,0.8,1,'2019-09-07',1),
(618,'家电维修','热熔胶枪',1,14,0,'2019-09-07',1),
(617,'家电维修','铁皮剪8寸',1,11.8,0,'2019-09-07',1),
(616,'家电维修','10寸活动板手',1,12.8,1,'2019-09-07',1),
(615,'家电维修','107吸锡器',2,9.6,1,'2019-09-07',1),
(614,'家电维修','9v电子称充电电源',1,10,0,'2019-09-07',1),
(613,'家电维修','十字镙丝刀200m',1,3.8,1,'2019-09-07',1),
(612,'家电维修','电子元件盒',24,1.9,1,'2019-09-07',1),
(611,'家电维修','电钻炭刷一对',1,1,1,'2019-09-07',1),
(610,'家电维修','220v电钻',1,89,0,'2019-09-07',1),
(609,'家电维修','8寸十字镙丝刀',1,8.5,1,'2019-09-07',1),
(608,'家电维修','802电动镙丝刀批头',26,3,1,'2019-09-07',1),
(607,'家电维修','汉邦936恒温电烙铁',1,59,0,'2019-09-07',1),
(605,'家电维修','松香',11,0.6,1,'2019-09-07',1),
(604,'家电维修','八角锤2.2斤',1,13,1,'2019-09-07',1),
(603,'家电维修','6寸老虎钳',1,7,1,'2019-09-07',1),
(602,'家电维修','936焊台烙铁芯',3,12,1,'2019-09-07',1),
(601,'家电维修','直柄麻花钻 3MM',7,0.5,1,'2019-09-07',1),
(600,'家电维修','电烙铁芯内热式',1,10,1,'2019-09-07',1),
(747,'制冷维修','双公透明加液管',1,3.5,0.5,'2019-12-19',1),
(748,'制冷维修','公英透明加液管',1,0.5,0.5,'2019-12-19',1),
(750,'制冷维修','双公透明加液管',1,3.5,0.5,'2019-12-19',1),
(751,'制冷维修','公英透明加液管',1,3.5,0.5,'2019-12-19',1),
(752,'制冷维修','自制空压机',1,150,0.5,'2019-12-21',1),
(753,'汽车维修','黑色防锈漆1L',1,32,0,'2020-01-04',1),
(980,'水果工具','48-72v电动打药机',1,458,0,'2024-06-02',1),
(755,'水果工具','139汽油机泵头',1,170,0,'2020-05-11',1),
(756,'水果工具','斯尔特割草机发动机',1,240,0,'2020-05-25',1),
(764,'家电维修','液晶屏 背光灯测试仪',1,55,0,'2020-06-07',1),
(763,'家电维修','液晶屏 背光灯测试仪',1,55,0,'2020-06-07',1),
(766,'车上必备','30L汽油铁桶',1,55,0,'2020-06-16',1),
(767,'车上必备','30L汽油铁桶',1,55,0,'2020-06-16',1),
(768,'水果工具','6.8KW油锯',1,279,0,'2020-07-13',1),
(769,'水果工具','6.8KW油锯',1,279,0,'2020-07-13',1),
(770,'水果工具','大功率二冲程油锯',1,335,0,'2020-07-19',1),
(771,'水果工具','大功率二冲程油锯',1,335,0,'2020-07-19',1),
(773,'智能家居','卫星接收机(71台)',1,188,0,'2020-07-31',1),
(1001,'家电维修','水龙头断丝取出器',1,4,0,'2024-09-30',1),
(775,'智能家居','树莓派4b(4g内存)',1,430,0,'2020-09-23',1),
(776,'餐饮小吃','气温 湿度表',1,21,0,'2020-10-11',1),
(777,'餐饮小吃','气温 湿度表',1,21,0,'2020-10-11',1),
(781,'家电维修','3斤松香',1,24,0,'2020-10-18',1),
(779,'餐饮小吃','温度湿度表',1,21,0,'2020-10-15',1),
(782,'家电维修','20卷电工胶布',1,14,0,'2020-10-18',1),
(783,'家电维修','20卷电工胶布',1,14,0,'2020-10-18',1),
(785,'智能家居','三星256G内存卡-树莓派',1,194,0,'2020-10-30',1),
(786,'车上必备','安全套 ',1,22,0.5,'2020-12-28',1),
(787,'车上必备','安全套 ',1,22,0.5,'2020-12-28',1),
(791,'车上必备','剃须刀',1,10,0.5,'2020-12-28',1),
(792,'车上必备','30A汽车保险丝',2,0.5,0.5,'2020-12-28',1),
(793,'车上必备','30A汽车保险丝',2,0.5,0.5,'2020-12-28',1),
(794,'车上必备','墨镜或眼镜',1,10,0.5,'2020-12-28',1),
(795,'车上必备','墨镜或眼镜',1,10,0.5,'2020-12-28',1),
(991,'智能家居','304不锈钢50*50米桶',1,90,0,'2024-08-14',1),
(797,'水果工具','电动剪刀40cm',1,410,0,'2021-02-23',1),
(799,'水果工具','电动剪刀30cm(两电)',1,310,0,'2021-03-04',1),
(801,'水果工具','4冲程割草机(用于抽水)',1,324,0,'2021-03-06',1),
(830,'家电维修','元件盒子17.3x9.8x2.2',32,2,0.5,'2021-10-11',1),
(803,'家电维修','元件盒子17.3x9.8x2.2',30,2,0.5,'2021-04-18',1),
(804,'车上必备','洗车水管水枪',1,17,0,'2021-04-19',1),
(805,'车上必备','洗车水管水枪',1,17,0,'2021-04-19',1),
(807,'智能家居','7800+8+120g技术电脑',2,1200,0,'2021-05-21',1),
(809,'智能家居','电动牙刷 充电能用一年',1,40,0,'2021-06-18',1),
(811,'智能家居',' 手机遥控电动卷闸门 ',1,180,0,'2021-06-18',1),
(1007,'家电维修','电脑电源转接板',1,12,0,'2024-10-07',1),
(813,'智能家居','7800+8+240g技术电脑 ',1,1400,0,'2021-06-18',1),
(815,'车上必备','三头剃须刀 ',1,19,0,'2021-06-18',1),
(816,'车上必备','汽车座椅套',1,209,0,'2021-06-18',1),
(817,'车上必备','汽车座椅套',1,209,0,'2021-06-18',1),
(818,'家电维修','外热笔式电烙铁',1,20,0,'2021-08-10',1),
(819,'家电维修','外热笔式电烙铁',1,20,0,'2021-08-10',1),
(820,'车上必备','鼻毛剪刀',1,3,0,'2021-08-12',1),
(821,'车上必备','鼻毛剪刀',1,3,0,'2021-08-12',1),
(822,'车上必备','指甲剪',1,2,0,'2021-08-12',1),
(823,'车上必备','指甲剪',1,2,0,'2021-08-12',1),
(825,'车上必备','行车记录仪',1,120,0,'2021-08-12',1),
(827,'家电维修','元件盒子17.3x9.8x2.2',1,32,0.5,'2021-10-11',1),
(829,'家电维修','元件盒子17.3x9.8x2.2',32,2,0.5,'2021-10-11',1),
(831,'家电维修','元件盒子17.3x9.8x2.2',32,2,0.5,'2021-10-11',1),
(833,'家电维修','姚工热风枪850无数显',1,143,0.5,'2022-02-24',1),
(834,'家电维修','热风枪焬膏(安立信)30g',1,13.8,2,'2022-03-11',1),
(875,'家电维修','液晶电视维修吸盘',1,33,0,'2022-08-22',1),
(842,'家电维修','鹿仙子维修卡具',1,15,2,'2022-04-29',1),
(859,'家电维修','12号订书(摇头)',1,12,0.5,'2022-07-30',1),
(846,'智能家居','19寸液晶电脑显示器',1,200,0.5,'2022-05-05',1),
(1027,'智能家居','8750+8g+240g技术电脑',1,350,0,'2025-02-22',1),
(847,'智能家居','19寸液晶电脑显示器2',1,200,0.5,'2022-05-05',1),
(1032,'智能家居','24寸液晶电脑显示器（收购）',1,100,0,'2025-02-24',1),
(854,'家电维修','过塑机 齐心F131B',1,68,2,'2022-05-10',1),
(1028,'智能家居','7800+8g+240g技术电脑',1,330,0,'2025-02-22',1),
(856,'家电维修','卷尺3m',1,3,0,'2022-07-14',1),
(858,'制冷维修','R134A汽车空调低压接头',1,8,2,'2022-07-29',1),
(863,'家电维修','20寸工具箱',1,16,0,'2022-08-08',1),
(864,'家电维修','8寸一字螺丝刀',1,3,1,'2022-08-12',1),
(865,'家电维修','8寸十字螺丝刀',1,3,1,'2022-08-12',1),
(866,'家电维修','6寸薄爪拉马',1,42,0,'2022-08-12',1),
(870,'制冷维修','R410接头高',1,13,0,'2022-08-13',1),
(871,'制冷维修','R410接头低',1,13,0,'2022-08-13',1),
(872,'制冷维修','R22接头低',1,14,0,'2022-08-13',1),
(873,'制冷维修','R22接头高',1,14,0,'2022-08-13',1),
(876,'家电维修','液晶屏维修支架6个',1,37,0,'2022-09-09',1),
(877,'智能家居','消毒碗柜',1,207,0,'2022-09-24',1),
(878,'家电维修','A4切纸机',1,31,1,'2022-11-06',1),
(882,'智能家居','1.6米直径旋转圆桌',1,310,0,'2023-02-01',1),
(953,'智能家居','1.6米餐桌12个凳子',1,152,0,'2023-06-19',1),
(1010,'智能家居','300斤电子秤',2,130,0,'2024-10-13',1),
(886,'智能家居','搬运车60x40cm',1,65.5,0.5,'2023-03-01',1),
(919,'遥控工具车','禧玛诺18齿内变速齿轮',2,10,0,'2023-05-12',1),
(1023,'家电维修','铆钉枪',1,10,0,'2025-02-08',1),
(890,'遥控工具车','36V350W短轴带脚带齿轮',2,120,10,'2023-05-05',1),
(891,'遥控工具车','36VDC继电器6脚 C型',4,3.6,0,'2023-05-05',1),
(892,'遥控工具车','前轴28cm带隔套',2,9.5,0,'2023-05-05',1),
(915,'遥控工具车','沙滩车法兰',2,17,0,'2023-05-12',1),
(894,'遥控工具车','进口6202轴承',10,1,0,'2023-05-05',1),
(944,'遥控工具车','5-6-7-8寸重型万向轮底座',4,5.5,0,'2023-05-30',1),
(897,'遥控工具车','265*185*80mm单片机盒子',1,14,0,'2023-05-05',1),
(898,'遥控工具车','螺丝套8x40mm',20,0.7,0,'2023-05-05',1),
(899,'遥控工具车','中号焊接定位器',2,8,0,'2023-05-05',1),
(1024,'智能家居','鸡鸭拔毛器',1,39,0,'2025-02-10',1),
(901,'遥控工具车','万向轮',2,13,0,'2023-05-05',1),
(902,'遥控工具车','esp32底座',1,9,0,'2023-05-05',1),
(904,'遥控工具车','13X6.5-6轮胎带轮毂',2,75,0,'2023-05-05',1),
(905,'遥控工具车','两个电机底座（帮打孔）',2,13,0,'2023-05-05',1),
(967,'智能家居','360P1WIFI路由器',1,19,0,'2023-09-18',1),
(917,'遥控工具车','36v800W有刷控制器',1,41,6,'2023-05-12',1),
(911,'遥控工具车','油漆',1,20,0,'2023-05-12',1),
(912,'遥控工具车',' 焊条焊料',1,30,0,'2023-05-12',1),
(913,'遥控工具车','PCB板腐浊料',1,5,0,'2023-05-12',1),
(914,'遥控工具车','5-38v可调降压模块',1,2.5,1,'2023-05-12',1),
(918,'遥控工具车','PCB板钻头',1,2.5,0,'2023-05-12',1),
(921,'遥控工具车','6002轴承',4,1.82,0,'2023-05-13',1),
(927,'遥控工具车','30*56*3mm铁片',6,1,0,'2023-05-14',1),
(923,'遥控工具车','2芯纯铜2.5*1000mm',1,29,0,'2023-05-13',1),
(924,'遥控工具车','大型船型开关',1,4,0,'2023-05-14',1),
(930,'遥控工具车','36v12a充电器',1,18,0,'2023-05-18',1),
(926,'遥控工具车','20A紫铜卡扣10个',1,5,0,'2023-05-14',1),
(929,'遥控工具车','嘉立创打样5片PCB',1,54,0,'2023-05-18',1),
(943,'遥控工具车','34*60*3mm圆形铁片10个',1,6,0,'2023-05-30',1),
(932,'遥控工具车','法兰改装转动管',2,42,0,'2023-05-18',1),
(933,'遥控工具车','柳螺母头+10个M5',1,10,0,'2023-05-19',1),
(934,'遥控工具车','拿快递交通费',1,100,0,'2023-05-19',1),
(941,'遥控工具车','请帮电焊50',1,50,0,'2023-05-21',1),
(1011,'智能家居','诗贝康雾化机',1,68,0,'2024-10-13',1),
(947,'遥控工具车','30cm直径26mm+双9齿轮轴',1,28,0,'2023-05-30',1),
(992,'智能家居','18.3cm不锈钢水勺',1,7.6,0,'2024-08-25',1),
(952,'遥控工具车','8路模块配8键遥控器433',1,33,0,'2023-06-14',1),
(951,'遥控工具车','U型卡扣M20',1,6,1,'2023-06-12',1),
(968,'家电维修','电子元件15格盒子',40,2,0,'2023-11-27',1),
(1019,'家电维修','上门维修电动车',1,700,0,'2024-10-26',1),
(959,'制冷维修','空调加氟安全带',1,35,0,'2023-08-08',1),
(962,'家电维修','鹿仙子出锡辅助器',1,30,0,'2023-08-16',1),
(963,'车上必备','手机导航支架',1,3,0,'2023-08-20',1),
(965,'家电维修','晨洲岛12s语音数字万用表',1,44.1,0,'2023-08-24',1),
(966,'家电维修','金属锡线架',1,5,0,'2023-09-07',1),
(969,'智能家居','鑫悦电猫',1,58,0,'2023-12-02',1),
(1025,'智能家居','电动车定时充电器',1,14,0,'2025-02-13',1),
(982,'家电维修','21v电动螺丝刀电池',1,37,0,'2024-07-13',1),
(989,'智能家居','316不锈钢筷子',10,2.5,0,'2024-08-09',1),
(988,'家电维修','90cm磁铁条E36寸',1,14.25,0,'2024-07-31',1),
(993,'水果工具','40个吊针输液管',1,40,0,'2024-08-25',1),
(994,'水果工具','40个输液塑料瓶',1,22,0,'2024-08-25',1),
(995,'家电维修','U型端子紫铜管压线钳',1,31,0,'2024-08-27',1),
(996,'智能家居','代步电动车',1,700,0,'2024-09-01',1),
(1004,'智能家居','树莓派4b(2g内存+tf256g)',1,512,0,'2024-10-06',1),
(999,'智能家居','电动车雨棚',1,30,0,'2024-09-16',1),
(1002,'智能家居','一次性杯子取杯器(60个)',1,6,0,'2024-10-05',1),
(1003,'家电维修','监控警示牌',1,4.2,0,'2024-10-05',1),
(1009,'家电维修','轴承拉玛',1,14,0,'2024-10-11',1),
(1013,'家电维修','电动扳手转电钻螺丝刀',1,7.89,0,'2024-10-17',1),
(1014,'家电维修','万能取螺丝器',1,8,0,'2024-10-21',1),
(1015,'家电维修','电动扳手转角磨机转接头',1,3.8,0,'2024-10-22',1),
(1016,'家电维修','电动扳手20节电池',1,42,0,'2024-10-23',1),
(1017,'家电维修','绿林强磁防滑4件套螺丝头',1,5.6,0,'2024-10-24',1),
(1018,'家电维修','货架1.5x0.4x1.6',2,115,0,'2024-10-26',1),
(1021,'智能家居','弩式弹弓(带十字瞄准镜)',1,87,0,'2024-11-09',1),
(1031,'智能家居','爱普生L3256 打印复印扫描',1,779,0,'2025-02-24',1),
(1033,'家电维修','皮筋绑筋工具',1,7.3,0,'2025-02-24',1),
(1036,'智能家居','6L不锈钢内胆',2,49,0,'2025-04-26',1),
(1035,'智能家居','48v800瓦正弦波逆变器',1,155,0,'2025-04-26',1),
(1037,'必学技能','STA32全案例开发实战书',1,15,0,'2025-05-22',1),
(1038,'必学技能','golang',1,100,0,'2025-05-22',1),
(1039,'必学技能','arduino',1,100,0,'2025-05-22',1),
(1040,'必学技能','开车',1,5000,0,'2025-05-22',1),
(1041,'智能家居','康佳真空封口机',1,70,0,'2025-05-30',1);


