2025-9-23 13:54:11 [显示全部楼层]
5浏览
查看: 5|回复: 0

[M10项目] ESP32-C5——断网可用的简单群聊

[复制链接]
在某些特殊的场合之下,比如没有网络的大兴安岭,克拉玛利,比如不允许上网的某些研究所,比如在飞机上。那么我们能否连接热点,上网做一款线上简易群聊呢
基于Firebeete试用的ESP32-C5开发板以及其强大的wifi功能,我们基于其作为一个简单的服务器,提供热点连接,然后可以访问群留言聊天功能。
ESP32-C5——断网可用的简单群聊图2
废话不说,结果展示如下
ESP32-C5——断网可用的简单群聊图1
首先需要访问Esp32的热点,然后输入密码12345678,就可以使用啦,默认30秒更新一次,因为esp32能力有限,需要快速更新可以点击更新,多人使用也是可以的,这样可以聊起天来,哈哈哈哈哈,相当于一个简单群聊


手机端也可以使用,代码展示如下:
  1. // 针对ESP32-C5优化的包含部分
  2. #include <WiFi.h>
  3. #include <WebServer.h>
  4. #include <SPIFFS.h>
  5. // 使用ArduinoJson 6.x并进行内存优化
  6. #include <ArduinoJson.h>
  7. /* WiFi设置 */
  8. const char* ssid     = "ESP32-MessageBoard";
  9. const char* password = "12345678";
  10. /* IP地址设置 */
  11. IPAddress local_ip(192,168,1,1);
  12. IPAddress gateway(192,168,1,1);
  13. IPAddress subnet(255,255,255,0);
  14. // 创建Web服务器实例,使用标准HTTP端口80以便于浏览器访问
  15. WebServer msgServer(80);
  16. // 最多存储的留言数量 - 为ESP32-C5内存限制而减少
  17. #define MAX_MESSAGES 30
  18. // 留言结构体 - 优化字符串存储
  19. struct Message {
  20.   char author[32];  // 使用固定大小缓冲区替代String
  21.   char content[256];
  22.   char timestamp[16];
  23. };
  24. // 留言数组
  25. Message messages[MAX_MESSAGES];
  26. int messageCount = 0;
  27. // 函数声明
  28. void loadMessages();
  29. void saveMessages();
  30. String sendMessageBoardHTML();
  31. void setup() {
  32.   Serial.begin(115200);
  33.   
  34.   // 初始化SPIFFS,针对ESP32-C5进行错误处理
  35.   if(!SPIFFS.begin(true)){
  36.     Serial.println("SPIFFS挂载失败");
  37.     // 尝试替代初始化方法
  38.     if(!SPIFFS.begin(false)) {
  39.       Serial.println("SPIFFS挂载再次失败,继续执行但功能可能受限");
  40.     }
  41.   }
  42.   
  43.   // 从文件加载留言
  44.   loadMessages();
  45.   
  46.   // 设置WiFi接入点
  47.   WiFi.softAP(ssid, password);
  48.   WiFi.softAPConfig(local_ip, gateway, subnet);
  49.   delay(100);
  50.   
  51.   // 设置Web服务器路由
  52.   msgServer.on("/", HTTP_GET, handleRoot);
  53.   msgServer.on("/messages", HTTP_GET, handleGetMessages);
  54.   msgServer.on("/post", HTTP_POST, handlePostMessage);
  55.   msgServer.onNotFound(handleNotFound);
  56.   
  57.   // 启动服务器
  58.   msgServer.begin();
  59.   Serial.println("留言板HTTP服务器已启动在端口80");
  60.   Serial.print("IP地址: ");
  61.   Serial.println(WiFi.softAPIP());
  62. }
  63. void loop() {
  64.   msgServer.handleClient();
  65. }
  66. // 处理根路径请求
  67. void handleRoot() {
  68.   Serial.println("客户端已连接到留言板");
  69.   msgServer.send(200, "text/html", sendMessageBoardHTML());
  70. }
  71. void handleNotFound() {
  72.   msgServer.send(404, "text/plain", "找不到页面");
  73. }
  74. // 为ESP32-C5内存限制而减小JSON文档大小
  75. void handleGetMessages() {
  76.   // 使用StaticJsonDocument以获得更好的内存管理
  77.   StaticJsonDocument<3072> doc;
  78.   JsonArray array = doc.to<JsonArray>();
  79.   
  80.   for (int i = 0; i < messageCount; i++) {
  81.     JsonObject obj = array.createNestedObject();
  82.     obj["author"] = messages[i].author;
  83.     obj["content"] = messages[i].content;
  84.     obj["timestamp"] = messages[i].timestamp;
  85.   }
  86.   
  87.   String response;
  88.   serializeJson(doc, response);
  89.   msgServer.send(200, "application/json", response);
  90. }
  91. void handlePostMessage() {
  92.   if (msgServer.hasArg("plain")) {
  93.     String message = msgServer.arg("plain");
  94.     StaticJsonDocument<512> doc;
  95.     DeserializationError error = deserializeJson(doc, message);
  96.    
  97.     if (!error) {
  98.       const char* author = doc["author"];
  99.       const char* content = doc["content"];
  100.       
  101.       // 获取当前时间(由于ESP32没有实时时钟,这里使用运行时间)
  102.       unsigned long currentMillis = millis();
  103.       int seconds = currentMillis / 1000;
  104.       int minutes = seconds / 60;
  105.       int hours = minutes / 60;
  106.       
  107.       minutes = minutes % 60;
  108.       seconds = seconds % 60;
  109.       
  110.       char timestamp[16];
  111.       sprintf(timestamp, "%02d:%02d:%02d", hours, minutes, seconds);
  112.       
  113.       // 添加新留言 - 使用strncpy处理固定缓冲区
  114.       if (messageCount < MAX_MESSAGES) {
  115.         strncpy(messages[messageCount].author, author, sizeof(messages[messageCount].author) - 1);
  116.         messages[messageCount].author[sizeof(messages[messageCount].author) - 1] = '\0';
  117.         
  118.         strncpy(messages[messageCount].content, content, sizeof(messages[messageCount].content) - 1);
  119.         messages[messageCount].content[sizeof(messages[messageCount].content) - 1] = '\0';
  120.         
  121.         strncpy(messages[messageCount].timestamp, timestamp, sizeof(messages[messageCount].timestamp) - 1);
  122.         messages[messageCount].timestamp[sizeof(messages[messageCount].timestamp) - 1] = '\0';
  123.         
  124.         messageCount++;
  125.       } else {
  126.         // 如果留言已满,移除最旧的留言
  127.         for (int i = 0; i < MAX_MESSAGES - 1; i++) {
  128.           memcpy(&messages[i], &messages[i + 1], sizeof(Message));
  129.         }
  130.         
  131.         strncpy(messages[MAX_MESSAGES - 1].author, author, sizeof(messages[MAX_MESSAGES - 1].author) - 1);
  132.         messages[MAX_MESSAGES - 1].author[sizeof(messages[MAX_MESSAGES - 1].author) - 1] = '\0';
  133.         
  134.         strncpy(messages[MAX_MESSAGES - 1].content, content, sizeof(messages[MAX_MESSAGES - 1].content) - 1);
  135.         messages[MAX_MESSAGES - 1].content[sizeof(messages[MAX_MESSAGES - 1].content) - 1] = '\0';
  136.         
  137.         strncpy(messages[MAX_MESSAGES - 1].timestamp, timestamp, sizeof(messages[MAX_MESSAGES - 1].timestamp) - 1);
  138.         messages[MAX_MESSAGES - 1].timestamp[sizeof(messages[MAX_MESSAGES - 1].timestamp) - 1] = '\0';
  139.       }
  140.       
  141.       // 保存留言到文件
  142.       saveMessages();
  143.       
  144.       msgServer.send(200, "application/json", "{"status":"success"}");
  145.     } else {
  146.       msgServer.send(400, "application/json", "{"status":"error","message":"无效的JSON格式"}");
  147.     }
  148.   } else {
  149.     msgServer.send(400, "application/json", "{"status":"error","message":"没有提供数据"}");
  150.   }
  151. }
  152. // 保存留言到SPIFFS文件系统 - 针对ESP32-C5优化
  153. void saveMessages() {
  154.   File file = SPIFFS.open("/messages.json", FILE_WRITE);
  155.   if (!file) {
  156.     Serial.println("无法打开文件进行写入");
  157.     return;
  158.   }
  159.   
  160.   // 使用更小的JSON文档
  161.   StaticJsonDocument<3072> doc;
  162.   JsonArray array = doc.to<JsonArray>();
  163.   
  164.   // 分批处理
  165.   const int batchSize = 5;
  166.   int totalBatches = (messageCount + batchSize - 1) / batchSize;
  167.   
  168.   file.print("[");
  169.   
  170.   for (int batch = 0; batch < totalBatches; batch++) {
  171.     int startIdx = batch * batchSize;
  172.     int endIdx = min(startIdx + batchSize, messageCount);
  173.    
  174.     for (int i = startIdx; i < endIdx; i++) {
  175.       JsonObject obj = array.createNestedObject();
  176.       obj["author"] = messages[i].author;
  177.       obj["content"] = messages[i].content;
  178.       obj["timestamp"] = messages[i].timestamp;
  179.       
  180.       String jsonStr;
  181.       serializeJson(obj, jsonStr);
  182.       file.print(jsonStr);
  183.       
  184.       if (i < messageCount - 1) {
  185.         file.print(",");
  186.       }
  187.       
  188.       // 清除对象以便下次使用
  189.       array.clear();
  190.     }
  191.    
  192.     // 给ESP32一些时间处理
  193.     yield();
  194.   }
  195.   
  196.   file.print("]");
  197.   file.close();
  198.   Serial.println("留言保存完成");
  199. }
  200. // 从SPIFFS文件系统加载留言 - 针对ESP32-C5优化
  201. void loadMessages() {
  202.   if (!SPIFFS.exists("/messages.json")) {
  203.     Serial.println("留言文件不存在,将创建新文件");
  204.     return;
  205.   }
  206.   
  207.   File file = SPIFFS.open("/messages.json", FILE_READ);
  208.   if (!file) {
  209.     Serial.println("无法打开文件进行读取");
  210.     return;
  211.   }
  212.   
  213.   // 流式解析以减少内存使用
  214.   messageCount = 0;
  215.   
  216.   // 检查文件是否为空或不是有效的JSON
  217.   if (file.size() < 2) {
  218.     file.close();
  219.     return;
  220.   }
  221.   
  222.   // 使用JsonStreamingParser处理大文件
  223.   DynamicJsonDocument doc(512);
  224.   DeserializationError error;
  225.   
  226.   // 读取开始括号
  227.   char c = file.read();
  228.   if (c != '[') {
  229.     Serial.println("文件格式错误");
  230.     file.close();
  231.     return;
  232.   }
  233.   
  234.   // 读取每个留言对象
  235.   while (file.available() && messageCount < MAX_MESSAGES) {
  236.     // 查找对象的开始
  237.     while (file.available() && file.peek() != '{') {
  238.       file.read();
  239.     }
  240.    
  241.     if (!file.available()) break;
  242.    
  243.     // 读取对象
  244.     String jsonStr = "";
  245.     int braceCount = 0;
  246.     bool inString = false;
  247.     char prevChar = 0;
  248.    
  249.     do {
  250.       c = file.read();
  251.       jsonStr += c;
  252.       
  253.       if (c == '"' && prevChar != '\\') {
  254.         inString = !inString;
  255.       }
  256.       
  257.       if (!inString) {
  258.         if (c == '{') braceCount++;
  259.         if (c == '}') braceCount--;
  260.       }
  261.       
  262.       prevChar = c;
  263.     } while (file.available() && (braceCount > 0 || c != '}'));
  264.    
  265.     // 解析对象
  266.     error = deserializeJson(doc, jsonStr);
  267.    
  268.     if (!error) {
  269.       const char* author = doc["author"];
  270.       const char* content = doc["content"];
  271.       const char* timestamp = doc["timestamp"];
  272.       
  273.       if (author && content && timestamp) {
  274.         strncpy(messages[messageCount].author, author, sizeof(messages[messageCount].author) - 1);
  275.         messages[messageCount].author[sizeof(messages[messageCount].author) - 1] = '\0';
  276.         
  277.         strncpy(messages[messageCount].content, content, sizeof(messages[messageCount].content) - 1);
  278.         messages[messageCount].content[sizeof(messages[messageCount].content) - 1] = '\0';
  279.         
  280.         strncpy(messages[messageCount].timestamp, timestamp, sizeof(messages[messageCount].timestamp) - 1);
  281.         messages[messageCount].timestamp[sizeof(messages[messageCount].timestamp) - 1] = '\0';
  282.         
  283.         messageCount++;
  284.       }
  285.     }
  286.    
  287.     // 清除以便下一个对象
  288.     doc.clear();
  289.    
  290.     // 给ESP32一些时间处理
  291.     yield();
  292.   }
  293.   
  294.   file.close();
  295.   Serial.println("留言加载完成,共 " + String(messageCount) + " 条");
  296. }
  297. // 生成留言板HTML页面
  298. String sendMessageBoardHTML() {
  299.   String ptr = "<!DOCTYPE html>\n";
  300.   ptr += "<html lang='zh'>\n";
  301.   ptr += "<head>\n";
  302.   ptr += "  <meta charset='UTF-8'>\n";
  303.   ptr += "  <meta name='viewport' content='width=device-width, initial-scale=1.0'>\n";
  304.   ptr += "  <title>ESP32在线群聊</title>\n";
  305.   ptr += "  <style>\n";
  306.   ptr += "    body {\n";
  307.   ptr += "      font-family: Arial, sans-serif;\n";
  308.   ptr += "      max-width: 800px;\n";
  309.   ptr += "      margin: 0 auto;\n";
  310.   ptr += "      padding: 20px;\n";
  311.   ptr += "      background-color: #f5f5f5;\n";
  312.   ptr += "    }\n";
  313.   ptr += "    h1 {\n";
  314.   ptr += "      color: #333;\n";
  315.   ptr += "      text-align: center;\n";
  316.   ptr += "      margin-bottom: 30px;\n";
  317.   ptr += "    }\n";
  318.   ptr += "    .message-form {\n";
  319.   ptr += "      background-color: white;\n";
  320.   ptr += "      padding: 20px;\n";
  321.   ptr += "      border-radius: 8px;\n";
  322.   ptr += "      box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n";
  323.   ptr += "      margin-bottom: 30px;\n";
  324.   ptr += "    }\n";
  325.   ptr += "    .form-group {\n";
  326.   ptr += "      margin-bottom: 15px;\n";
  327.   ptr += "    }\n";
  328.   ptr += "    label {\n";
  329.   ptr += "      display: block;\n";
  330.   ptr += "      margin-bottom: 5px;\n";
  331.   ptr += "      font-weight: bold;\n";
  332.   ptr += "    }\n";
  333.   ptr += "    input, textarea {\n";
  334.   ptr += "      width: 100%;\n";
  335.   ptr += "      padding: 10px;\n";
  336.   ptr += "      border: 1px solid #ddd;\n";
  337.   ptr += "      border-radius: 4px;\n";
  338.   ptr += "      box-sizing: border-box;\n";
  339.   ptr += "    }\n";
  340.   ptr += "    textarea {\n";
  341.   ptr += "      height: 100px;\n";
  342.   ptr += "      resize: vertical;\n";
  343.   ptr += "    }\n";
  344.   ptr += "    button {\n";
  345.   ptr += "      background-color: #4CAF50;\n";
  346.   ptr += "      color: white;\n";
  347.   ptr += "      border: none;\n";
  348.   ptr += "      padding: 10px 15px;\n";
  349.   ptr += "      border-radius: 4px;\n";
  350.   ptr += "      cursor: pointer;\n";
  351.   ptr += "      font-size: 16px;\n";
  352.   ptr += "    }\n";
  353.   ptr += "    button:hover {\n";
  354.   ptr += "      background-color: #45a049;\n";
  355.   ptr += "    }\n";
  356.   ptr += "    .message-list {\n";
  357.   ptr += "      background-color: white;\n";
  358.   ptr += "      border-radius: 8px;\n";
  359.   ptr += "      box-shadow: 0 2px 4px rgba(0,0,0,0.1);\n";
  360.   ptr += "      overflow: hidden;\n";
  361.   ptr += "    }\n";
  362.   ptr += "    .message-item {\n";
  363.   ptr += "      padding: 15px 20px;\n";
  364.   ptr += "      border-bottom: 1px solid #eee;\n";
  365.   ptr += "    }\n";
  366.   ptr += "    .message-item:last-child {\n";
  367.   ptr += "      border-bottom: none;\n";
  368.   ptr += "    }\n";
  369.   ptr += "    .message-header {\n";
  370.   ptr += "      display: flex;\n";
  371.   ptr += "      justify-content: space-between;\n";
  372.   ptr += "      margin-bottom: 10px;\n";
  373.   ptr += "      font-size: 14px;\n";
  374.   ptr += "      color: #666;\n";
  375.   ptr += "    }\n";
  376.   ptr += "    .message-author {\n";
  377.   ptr += "      font-weight: bold;\n";
  378.   ptr += "      color: #333;\n";
  379.   ptr += "    }\n";
  380.   ptr += "    .message-time {\n";
  381.   ptr += "      color: #999;\n";
  382.   ptr += "    }\n";
  383.   ptr += "    .message-content {\n";
  384.   ptr += "      line-height: 1.5;\n";
  385.   ptr += "    }\n";
  386.   ptr += "    .refresh-btn {\n";
  387.   ptr += "      background-color: #2196F3;\n";
  388.   ptr += "      margin-bottom: 15px;\n";
  389.   ptr += "    }\n";
  390.   ptr += "    .refresh-btn:hover {\n";
  391.   ptr += "      background-color: #0b7dda;\n";
  392.   ptr += "    }\n";
  393.   ptr += "    .status {\n";
  394.   ptr += "      text-align: center;\n";
  395.   ptr += "      padding: 10px;\n";
  396.   ptr += "      margin-top: 10px;\n";
  397.   ptr += "      border-radius: 4px;\n";
  398.   ptr += "      display: none;\n";
  399.   ptr += "    }\n";
  400.   ptr += "    .status.success {\n";
  401.   ptr += "      background-color: #dff0d8;\n";
  402.   ptr += "      color: #3c763d;\n";
  403.   ptr += "    }\n";
  404.   ptr += "    .status.error {\n";
  405.   ptr += "      background-color: #f2dede;\n";
  406.   ptr += "      color: #a94442;\n";
  407.   ptr += "    }\n";
  408.   ptr += "    @media (max-width: 600px) {\n";
  409.   ptr += "      body {\n";
  410.   ptr += "        padding: 10px;\n";
  411.   ptr += "      }\n";
  412.   ptr += "      .message-form, .message-list {\n";
  413.   ptr += "        border-radius: 0;\n";
  414.   ptr += "      }\n";
  415.   ptr += "    }\n";
  416.   ptr += "  </style>\n";
  417.   ptr += "</head>\n";
  418.   ptr += "<body>\n";
  419.   ptr += "  <h1>ESP32在线群聊</h1>\n";
  420.   
  421.   ptr += "  <div class='message-form'>\n";
  422.   ptr += "    <div class='form-group'>\n";
  423.   ptr += "      <label for='author'>您的名字:</label>\n";
  424.   ptr += "      <input type='text' id='author' name='author' required>\n";
  425.   ptr += "    </div>\n";
  426.   ptr += "    <div class='form-group'>\n";
  427.   ptr += "      <label for='content'>群聊内容:</label>\n";
  428.   ptr += "      <textarea id='content' name='content' required></textarea>\n";
  429.   ptr += "    </div>\n";
  430.   ptr += "    <button type='button' id='submit-btn'>发布</button>\n";
  431.   ptr += "    <div id='status' class='status'></div>\n";
  432.   ptr += "  </div>\n";
  433.   
  434.   ptr += "  <button type='button' id='refresh-btn' class='refresh-btn'>刷新</button>\n";
  435.   
  436.   ptr += "  <div id='message-list' class='message-list'>\n";
  437.   ptr += "    <div class='message-item' style='text-align:center;color:#999;'>加载中...</div>\n";
  438.   ptr += "  </div>\n";
  439.   
  440.   ptr += "  <script>\n";
  441.   ptr += "    // DOM元素\n";
  442.   ptr += "    const authorInput = document.getElementById('author');\n";
  443.   ptr += "    const contentInput = document.getElementById('content');\n";
  444.   ptr += "    const submitBtn = document.getElementById('submit-btn');\n";
  445.   ptr += "    const refreshBtn = document.getElementById('refresh-btn');\n";
  446.   ptr += "    const messageList = document.getElementById('message-list');\n";
  447.   ptr += "    const statusDiv = document.getElementById('status');\n";
  448.   
  449.   ptr += "    // 保存用户名到本地存储\n";
  450.   ptr += "    if (localStorage.getItem('author')) {\n";
  451.   ptr += "      authorInput.value = localStorage.getItem('author');\n";
  452.   ptr += "    }\n";
  453.   
  454.   ptr += "    // 加载留言\n";
  455.   ptr += "    function loadMessages() {\n";
  456.   ptr += "      fetch('/messages')\n";
  457.   ptr += "        .then(response => response.json())\n";
  458.   ptr += "        .then(data => {\n";
  459.   ptr += "          messageList.innerHTML = '';\n";
  460.   ptr += "          \n";
  461.   ptr += "          if (data.length === 0) {\n";
  462.   ptr += "            messageList.innerHTML = '<div class="message-item" style="text-align:center;color:#999;">暂无留言</div>';\n";
  463.   ptr += "            return;\n";
  464.   ptr += "          }\n";
  465.   ptr += "          \n";
  466.   ptr += "          // 按时间倒序排列留言\n";
  467.   ptr += "          data.reverse().forEach(message => {\n";
  468.   ptr += "            const messageItem = document.createElement('div');\n";
  469.   ptr += "            messageItem.className = 'message-item';\n";
  470.   ptr += "            \n";
  471.   ptr += "            const messageHeader = document.createElement('div');\n";
  472.   ptr += "            messageHeader.className = 'message-header';\n";
  473.   ptr += "            \n";
  474.   ptr += "            const messageAuthor = document.createElement('span');\n";
  475.   ptr += "            messageAuthor.className = 'message-author';\n";
  476.   ptr += "            messageAuthor.textContent = message.author;\n";
  477.   ptr += "            \n";
  478.   ptr += "            const messageTime = document.createElement('span');\n";
  479.   ptr += "            messageTime.className = 'message-time';\n";
  480.   ptr += "            messageTime.textContent = message.timestamp;\n";
  481.   ptr += "            \n";
  482.   ptr += "            messageHeader.appendChild(messageAuthor);\n";
  483.   ptr += "            messageHeader.appendChild(messageTime);\n";
  484.   ptr += "            \n";
  485.   ptr += "            const messageContent = document.createElement('div');\n";
  486.   ptr += "            messageContent.className = 'message-content';\n";
  487.   ptr += "            messageContent.textContent = message.content;\n";
  488.   ptr += "            \n";
  489.   ptr += "            messageItem.appendChild(messageHeader);\n";
  490.   ptr += "            messageItem.appendChild(messageContent);\n";
  491.   ptr += "            \n";
  492.   ptr += "            messageList.appendChild(messageItem);\n";
  493.   ptr += "          });\n";
  494.   ptr += "        })\n";
  495.   ptr += "        .catch(error => {\n";
  496.   ptr += "          console.error('获取留言失败:', error);\n";
  497.   ptr += "          messageList.innerHTML = '<div class="message-item" style="text-align:center;color:#999;">获取留言失败</div>';\n";
  498.   ptr += "        });\n";
  499.   ptr += "    }\n";
  500.   
  501.   ptr += "    // 提交新留言\n";
  502.   ptr += "    function submitMessage() {\n";
  503.   ptr += "      const author = authorInput.value.trim();\n";
  504.   ptr += "      const content = contentInput.value.trim();\n";
  505.   ptr += "      \n";
  506.   ptr += "      if (!author) {\n";
  507.   ptr += "        showStatus('请输入您的名字', 'error');\n";
  508.   ptr += "        return;\n";
  509.   ptr += "      }\n";
  510.   ptr += "      \n";
  511.   ptr += "      if (!content) {\n";
  512.   ptr += "        showStatus('请输入消息内容', 'error');\n";
  513.   ptr += "        return;\n";
  514.   ptr += "      }\n";
  515.   ptr += "      \n";
  516.   ptr += "      // 保存用户名到本地存储\n";
  517.   ptr += "      localStorage.setItem('author', author);\n";
  518.   ptr += "      \n";
  519.   ptr += "      // 禁用提交按钮\n";
  520.   ptr += "      submitBtn.disabled = true;\n";
  521.   ptr += "      \n";
  522.   ptr += "      fetch('/post', {\n";
  523.   ptr += "        method: 'POST',\n";
  524.   ptr += "        headers: {\n";
  525.   ptr += "          'Content-Type': 'application/json',\n";
  526.   ptr += "        },\n";
  527.   ptr += "        body: JSON.stringify({ author, content })\n";
  528.   ptr += "      })\n";
  529.   ptr += "      .then(response => response.json())\n";
  530.   ptr += "      .then(data => {\n";
  531.   ptr += "        if (data.status === 'success') {\n";
  532.   ptr += "          showStatus('留言发布成功!', 'success');\n";
  533.   ptr += "          contentInput.value = '';\n";
  534.   ptr += "          loadMessages();\n";
  535.   ptr += "        } else {\n";
  536.   ptr += "          showStatus('留言发布失败: ' + (data.message || '未知错误'), 'error');\n";
  537.   ptr += "        }\n";
  538.   ptr += "      })\n";
  539.   ptr += "      .catch(error => {\n";
  540.   ptr += "        console.error('提交留言失败:', error);\n";
  541.   ptr += "        showStatus('提交留言失败,请稍后再试', 'error');\n";
  542.   ptr += "      })\n";
  543.   ptr += "      .finally(() => {\n";
  544.   ptr += "        submitBtn.disabled = false;\n";
  545.   ptr += "      });\n";
  546.   ptr += "    }\n";
  547.   
  548.   ptr += "    // 显示状态消息\n";
  549.   ptr += "    function showStatus(message, type) {\n";
  550.   ptr += "      statusDiv.textContent = message;\n";
  551.   ptr += "      statusDiv.className = 'status ' + type;\n";
  552.   ptr += "      statusDiv.style.display = 'block';\n";
  553.   ptr += "      \n";
  554.   ptr += "      setTimeout(() => {\n";
  555.   ptr += "        statusDiv.style.display = 'none';\n";
  556.   ptr += "      }, 3000);\n";
  557.   ptr += "    }\n";
  558.   
  559.   ptr += "    // 事件***\n";
  560.   ptr += "    submitBtn.addEventListener('click', submitMessage);\n";
  561.   ptr += "    refreshBtn.addEventListener('click', loadMessages);\n";
  562.   ptr += "    \n";
  563.   ptr += "    // 回车键提交\n";
  564.   ptr += "    contentInput.addEventListener('keypress', function(e) {\n";
  565.   ptr += "      if (e.key === 'Enter' && !e.shiftKey) {\n";
  566.   ptr += "        e.preventDefault();\n";
  567.   ptr += "        submitMessage();\n";
  568.   ptr += "      }\n";
  569.   ptr += "    });\n";
  570.   
  571.   ptr += "    // 初始加载留言\n";
  572.   ptr += "    loadMessages();\n";
  573.   
  574.   ptr += "    // 定时刷新留言(每30秒)\n";
  575.   ptr += "    setInterval(loadMessages, 30000);\n";
  576.   ptr += "  </script>\n";
  577.   ptr += "</body>\n";
  578.   ptr += "</html>\n";
  579.   
  580.   return ptr;
  581. }
复制代码
具体的arduino的配置可以看我的上一篇内容


您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

为本项目制作心愿单
购买心愿单
心愿单 编辑
[[wsData.name]]

硬件清单

  • [[d.name]]
btnicon
我也要做!
点击进入购买页面
上海智位机器人股份有限公司 沪ICP备09038501号-4 备案 沪公网安备31011502402448

© 2013-2025 Comsenz Inc. Powered by Discuz! X3.4 Licensed

mail