伍老师 发表于 昨天 09:34

ESP32-C5 FireBeetle 2 搭建离线版的课堂交互网页

ESP32-C5FireBeetle 2 搭建离线版的课堂交互网页注:此文参考卓尔大佬的文章,谢谢!1.1 产品简介FireBeetle 2 ESP32-C5 IO套装包括两部分:Firebeetle 2 ESP32-C5 开发板和其专用的IO扩展底板。IO扩展板方便快速连接各种传感器外设,让Firebeetle 2 ESP32-C5开发板到手即用,无需焊接。FireBeetle 2 ESP32-C5 是一款搭载乐鑫 ESP32-C5 模组的低功耗 IoT 开发板,面向智能家居和广泛物联网场景,集高性能计算、多协议支持与智能电源管理于一体,为各种部署需求提供高可靠性、高灵活性与长续航的解决方案。支持 2.4 & 5 GHz 双频 Wi-Fi 6ESP32-C5 是乐鑫首款支持2.4 G和5 G 双频Wi-Fi 6的芯片。相较于 2.4GHz 频段,5GHz 具备更高传输速率、更低延迟及更少干扰特性,可提供更稳定、更低延迟的无线连接。同时,Wi-Fi 6 技术通过 OFDMA 频分复用 和目标唤醒时间(TWT,Target Wake Time) 机制,显著提升网络容量并降低设备功耗,延长电池使用时间,让设备长久续航。FireBeetle 2 ESP32-C5 凭借双频 Wi-Fi 6 支持,可满足智能家居、物联网等场景对高吞吐与低功耗的双重需求。多协议融合,扩展无线连接性开发板支持 Wi-Fi、Thread、BLE、Zigbee 协议,可构建 Matter Wi-Fi/Thread 终端设备,实现跨平台智能家居设备互联。结合外部 MCU,还可作为 Thread 边界路由器、Matter 网关 或 Zigbee 网桥,覆盖复杂场景需求。高效电源管理,部署更灵活FireBeetle 2 ESP32-C5提供了高效和易用的电源管理,为各种部署需求提供可靠、灵活的供电解决方案。
[*]多元供电方式:支持 Type-C、5V DC 及太阳能输入对锂电池充电,解决无电源场景(如屋顶、阳台)的部署难题。
[*]太阳能优化PMIC(电源管理集成电路):采用太阳能电源管理模块 5V@1A同款太阳能电源管理芯片,最大限度的利用输入电源的电流输出能力,可最大化不同光照下的发电效率。
[*]智能监测与节能:集成电池电量监测功能,支持低电量预警;提供一组可控 3.3V 电源输出,可切断外接传感器供电以进一步降低功耗。
搭配专用IO扩展底板,无需焊接Firebeetle 2 ESP32-C5开发板推出专属的IO扩展板,其IO引脚全部引出,并且精心做了功能分区,方便直接快速连接各种传感器外设,真正让开发板做到了到手即用,无需焊接。2.1 打开arduino,配置环境环境配置参考:请点击下方链接查看添加板卡的详细步骤:https://wiki.dfrobot.com.cn/Add_ESP32_board_to_Arduino_IDE注意:仅esp32板卡环境 3.3.0-alpha1分支版本才支持ESP32-C5。2.2 输入代码#include <WiFi.h>
#include <WebServer.h>

// 手机热点配置(替换为您的热点SSID和密码)
const char* ssid = "jzai"; // 替换为手机热点的SSID
const char* password = "88998899"; // 替换为手机热点的密码

// 创建Web服务器对象,监听80端口(HTTP协议)
WebServer server(80);

// HTML内容(主题,保持与原代码一致)
const char* htmlContent = R"rawliteral(
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>打地鼠小游戏</title>
    <style>
      * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
            font-family: 'Arial Rounded MT Bold', 'Arial', sans-serif;
      }
      
      body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
            background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
            padding: 20px;
      }
      
      .game-container {
            width: 100%;
            max-width: 600px;
            background-color: rgba(255, 255, 255, 0.9);
            border-radius: 20px;
            padding: 25px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
            text-align: center;
      }
      
      h1 {
            color: #333;
            margin-bottom: 20px;
            font-size: 2.5rem;
            text-shadow: 2px 2px 0 #ffcc00;
      }
      
      .game-info {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
            font-size: 1.2rem;
            font-weight: bold;
      }
      
      .score, .timer {
            background-color: #ffcc00;
            padding: 10px 20px;
            border-radius: 10px;
            box-shadow: 0 4px 0 #e6b800;
      }
      
      .game-board {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 15px;
            margin-bottom: 25px;
      }
      
      .hole {
            width: 100%;
            aspect-ratio: 1;
            background-color: #8B4513;
            border-radius: 50%;
            position: relative;
            overflow: hidden;
            box-shadow: inset 0 10px 0 rgba(0, 0, 0, 0.3);
            cursor: pointer;
      }
      
      .mole {
            width: 80%;
            height: 80%;
            background-color: #8B4513;
            border-radius: 50%;
            position: absolute;
            bottom: -80%;
            left: 10%;
            transition: bottom 0.3s;
            cursor: pointer;
      }
      
      .mole::before {
            content: '';
            position: absolute;
            width: 40%;
            height: 40%;
            background-color: #333;
            border-radius: 50%;
            top: 20%;
            left: 30%;
      }
      
      .mole::after {
            content: '';
            position: absolute;
            width: 60%;
            height: 30%;
            background-color: #ff6666;
            border-radius: 50%;
            bottom: 10%;
            left: 20%;
      }
      
      .mole.up {
            bottom: 10%;
      }
      
      .controls {
            margin-top: 20px;
      }
      
      button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 12px 30px;
            font-size: 1.2rem;
            border-radius: 10px;
            cursor: pointer;
            transition: all 0.3s;
            box-shadow: 0 4px 0 #3d8b40;
      }
      
      button:hover {
            background-color: #45a049;
            transform: translateY(2px);
            box-shadow: 0 2px 0 #3d8b40;
      }
      
      button:active {
            transform: translateY(4px);
            box-shadow: 0 0 0 #3d8b40;
      }
      
      .game-over {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            justify-content: center;
            align-items: center;
            z-index: 10;
      }
      
      .game-over-content {
            background-color: white;
            padding: 30px;
            border-radius: 15px;
            text-align: center;
            max-width: 400px;
            width: 90%;
      }
      
      .game-over h2 {
            color: #ff3333;
            margin-bottom: 20px;
            font-size: 2rem;
      }
      
      .final-score {
            font-size: 1.5rem;
            margin-bottom: 20px;
      }
      
      @media (max-width: 500px) {
            .game-board {
                grid-template-columns: repeat(2, 1fr);
            }
            
            h1 {
                font-size: 2rem;
            }
      }
    </style>
</head>
<body>
    <div class="game-container">
      <h1>打地鼠小游戏</h1>
      
      <div class="game-info">
            <div class="score">得分: <span id="score">0</span></div>
            <div class="timer">时间: <span id="time">30</span>秒</div>
      </div>
      
      <div class="game-board" id="gameBoard">
            <!-- 地鼠洞将通过JavaScript生成 -->
      </div>
      
      <div class="controls">
            <button id="startBtn">开始游戏</button>
      </div>
    </div>
   
    <div class="game-over" id="gameOver">
      <div class="game-over-content">
            <h2>游戏结束!</h2>
            <p class="final-score">你的得分: <span id="finalScore">0</span></p>
            <button id="restartBtn">再玩一次</button>
      </div>
    </div>

    <script>
      document.addEventListener('DOMContentLoaded', function() {
            // 游戏元素
            const gameBoard = document.getElementById('gameBoard');
            const scoreDisplay = document.getElementById('score');
            const timeDisplay = document.getElementById('time');
            const startBtn = document.getElementById('startBtn');
            const gameOverScreen = document.getElementById('gameOver');
            const finalScoreDisplay = document.getElementById('finalScore');
            const restartBtn = document.getElementById('restartBtn');
            
            // 游戏变量
            let score = 0;
            let timeLeft = 30;
            let gameActive = false;
            let timer;
            let moleTimer;
            
            // 创建地鼠洞
            for (let i = 0; i < 9; i++) {
                const hole = document.createElement('div');
                hole.className = 'hole';
               
                const mole = document.createElement('div');
                mole.className = 'mole';
                mole.dataset.id = i;
               
                hole.appendChild(mole);
                gameBoard.appendChild(hole);
               
                // 添加点击事件
                mole.addEventListener('click', hitMole);
            }
            
            const moles = document.querySelectorAll('.mole');
            
            // 开始游戏
            startBtn.addEventListener('click', startGame);
            
            // 重新开始游戏
            restartBtn.addEventListener('click', function() {
                gameOverScreen.style.display = 'none';
                startGame();
            });
            
            function startGame() {
                // 重置游戏状态
                score = 0;
                timeLeft = 30;
                gameActive = true;
                scoreDisplay.textContent = score;
                timeDisplay.textContent = timeLeft;
                startBtn.disabled = true;
               
                // 清除所有活动的地鼠
                moles.forEach(mole => {
                  mole.classList.remove('up');
                });
               
                // 开始计时
                timer = setInterval(function() {
                  timeLeft--;
                  timeDisplay.textContent = timeLeft;
                  
                  if (timeLeft <= 0) {
                        endGame();
                  }
                }, 1000);
               
                // 开始地鼠出现
                showRandomMole();
            }
            
            function showRandomMole() {
                if (!gameActive) return;
               
                // 随机选择一个地鼠
                const randomIndex = Math.floor(Math.random() * moles.length);
                const mole = moles;
               
                // 显示地鼠
                mole.classList.add('up');
               
                // 设置地鼠消失的时间(0.5-2秒之间随机)
                const hideTime = Math.random() * 1500 + 500;
               
                setTimeout(() => {
                  mole.classList.remove('up');
                  
                  // 如果游戏还在进行中,继续显示下一个地鼠
                  if (gameActive) {
                        showRandomMole();
                  }
                }, hideTime);
            }
            
            function hitMole(e) {
                if (!gameActive) return;
               
                // 确保点击的是地鼠而不是洞
                if (e.target.classList.contains('up')) {
                  // 增加分数
                  score++;
                  scoreDisplay.textContent = score;
                  
                  // 播放点击效果
                  e.target.style.backgroundColor = '#ff3333';
                  setTimeout(() => {
                        e.target.style.backgroundColor = '#8B4513';
                  }, 200);
                  
                  // 立即隐藏被点击的地鼠
                  e.target.classList.remove('up');
                }
            }
            
            function endGame() {
                gameActive = false;
                clearInterval(timer);
                startBtn.disabled = false;
               
                // 显示游戏结束画面
                finalScoreDisplay.textContent = score;
                gameOverScreen.style.display = 'flex';
            }
      });
    </script>
</body>
</html>
)rawliteral";

void handleRoot() {
// 发送HTTP响应,状态码200,内容类型为text/html
server.send(200, "text/html", htmlContent);
}

void setup() {
// 初始化串口,用于调试
Serial.begin(115200);
Serial.println("Starting ESP32-C5 Web Server...");

// 设置ESP32为STA模式,连接手机热点
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
Serial.print("Connecting to WiFi: ");
Serial.println(ssid);

// 等待连接,最多尝试20秒
int timeout = 20;
while (WiFi.status() != WL_CONNECTED && timeout > 0) {
    delay(1000);
    Serial.print(".");
    timeout--;
}

if (WiFi.status() == WL_CONNECTED) {
    Serial.println("\nWiFi Connected");
    Serial.print("IP Address: ");
    Serial.println(WiFi.localIP()); // 打印STA模式的IP地址
} else {
    Serial.println("\nFailed to connect to WiFi. Check SSID/password or signal strength.");
    return; // 连接失败,停止后续操作
}

// 设置Web服务器路由
server.on("/", handleRoot);
server.onNotFound([]() {
    server.send(404, "text/plain", "404: Page Not Found");
});

// 启动服务器
server.begin();
Serial.println("HTTP Server Started");
}

void loop() {
// 处理客户端HTTP请求
server.handleClient();
delay(10); // 短暂延时以确保稳定性
}3.1 查看效果无论是电脑还是手机只要接入同一个热点网络,那么它就可以通过查看串口监视中的IP进行连接游玩。https://lsky.555555.press:16666/i/2025/10/25/68fc26e750afd.pnghttps://lsky.555555.press:16666/i/2025/10/25/68fc26949d835.png4 接下来的方向我觉得可以搭建一个离线的小型服务器,这样学生的数据可以收集起来,方便老师统计。比如学生的答题,只要学生提交数据,老师这边就可以得到学生反馈。这有点类似学习平板的同步反馈。或者做个本地游戏。多人竞技游戏,支持2-4个玩家同时游戏,每个玩家连接自己的设备,实时显示玩家排名。这应该也是一个不错的方法。


页: [1]
查看完整版本: ESP32-C5 FireBeetle 2 搭建离线版的课堂交互网页