速度点击器 - 信用卡大小的反应时间游戏设备(ESP32-C3 MiNi开发板)
本项目制作了一个基于ESP32-C3开发板的便携式反应时间测试游戏机。游戏机包含4个带LED的按钮和一个0.96寸OLED显示屏。游戏开始时随机点亮一个LED,玩家需快速按下对应按钮,4轮结束后显示平均反应时间。硬件采用信用卡大小的ESP32-C3开发板,搭配OLED显示屏和触觉按钮,外壳通过3D打印完成。软件使用Arduino IDE开发,包含LED控制、按钮检测和OLED显示功能。该项目适合初
速度点击器 - 信用卡大小的反应时间游戏设备(ESP32-C3 MiNi开发板)
一、前言
这是我制作的第1个项目,较适合初学者,简单易上手。在此项目中,我将制作一个小型掌上游戏机,可用来测试反应能力。当游戏开始时,其中一个按钮内的LED会随机点亮。玩家必须尽快按下LED亮起的按钮,4次亮起后,将会显示最终的反应时间,每次需要按下的开关都是随机的。如果按下其他按钮,可以重新启动游戏。
二、硬件配置
1.ESP32-C3 MiNi开发板
ESP32C3SuperMini是一款基于 Espressif ESP32-C3 WiFi/蓝牙双模芯片的 IoT 迷你开发板。ESP32-C3 是一款32 位 RISC-V CPU,包含FPU(浮点单元),可进行32 位单精度运算,具有强大的计算能力。它具有出色的射频性能,支持IEEE 802.11 b/g/n WiFi和蓝牙 5 (LE)协议。该板附带外部天线,可增强无线应用的信号强度。它还具有小巧精致的外形并结合单面表面贴装设计。它配备了丰富的接口,有11个可用作PWM引脚的数字I/O和4个可用作ADC引脚的模拟I/O。它支持UART、I2C 和 SPI等四种串行接口。板上还有一个小的重置按钮和一个引导加载程序模式按钮。综合下来是一款高性价比的迷你开发板。

2.0.96寸蓝色4针IIC接口OLED模块显示屏(128*64点阵 SSD1315)
该显示器采用 OLED 技术,支持 SPI 和 I2C 通信来显示数据。它通过每个像素使用它的光。屏幕上几乎总共有128×64像素。在单色模式下,屏幕只有一种颜色,具体取决于 OLED 类型。由于 OLED 技术,它的显示屏始终处于明亮状态,不需要任何对比度调整。所有像素均可通过SSD1306驱动器控制。每个像素和通信信号都经过SSD1306。这种OLED显示器具有更高的图像质量、全视角、高亮度、更好的对比度、宽广的色彩范围、低功耗、更高效和可靠。主要应用于电脑显示器、手机、掌上游戏机、电视屏幕等数字显示设备。
3. 6x6x6毫米两脚微型瞬时触觉触摸按钮开关

4.LED灯



5.其他材料
杜邦线,USB线,3D打印机,焊烙铁,面包板。
三、制作步骤
(一)3D打印
1.通过百度网盘分享的文件:speed clicker.zip
链接: https://pan.baidu.com/s/16-OB0Q3KibAQcOC2GN239A
复制这段内容打开「百度网盘APP 即可获取」:
- button cap.stl-纽扣帽
- button tray.stl-按钮托盘
- front panel.stl-前板
- main body.stl-主体
- speed clicker.stl-速度点击器

(二)接线
| OLED | ESP32-C3 |
|---|---|
| GND | GND |
| VCC | 3V3 |
| SCL | D9 |
| SDA | D8 |
| OLED | ESP32-C3 |
| LED1-4 | ESP32-C3 |
|---|---|
| 负极 | GND |
| 正极 | D7-4 |
| 开关1-4 | ESP32-C3 |
|---|---|
| 负极 | GND |
| 正极 | D0-3 |
接线图如下
(三)上传代码
1. 【Arduino】Arduino IDE使用教程-超详细
链接: https://blog.csdn.net/as480133937/article/details/105331315
2. Arduino IDE安装
先到 Arduino 官方网站上下载最新版本的 Arduino IDE:
链接: Arduino - Home 



下载完成后,打开文件
直接下一步就行(建议存到C盘以外不用的盘)
3.Arduino IDE配置
中文模式


开发板安装(esp32)

其他开发板管理器地址:https://arduino.me/packages/esp32.json
安装完成后,重启Arduino IDE,即可在 菜单栏>工具>开发板中找到你使用的esp32开发板
端口(COM3)
插入开发板,点击“工具”中的“端口”,选择端口中自动显示出来的端口。
4.Arduino IDE使用
示例(Blink-闪烁代码)
先验证再上传 (插入开发板无错后)
5.代码上传
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
// 构造对象 连接到I2C(SDA、SCL引脚)的SSD1306声明
Adafruit_SSD1306 OLED(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define push buttons and LEDs pins
const int buttonPins[4] = {0,1,2,3};
const int ledPins[4] = {7,6,5,4};
unsigned long reactionTimes[4];
int currentRound = 0;
void setup() {
// Initialize buttons and LEDs
for (int i = 0; i < 4; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
pinMode(LED_BUILTIN, OUTPUT);
// OLED初始化
OLED.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
// OLED清除显示
OLED.clearDisplay();
// 设置字体颜色
OLED.setTextColor(WHITE);
// 设置字体大小
OLED.setTextSize(2);
// 设置字体位置
OLED.setCursor(0, 0);
// 显示字符串内容
OLED.println(" Press any");
OLED.println(" button to");
OLED.setTextSize(3);
OLED.setCursor(28, 38);
OLED.print("PLAY");
OLED.display();
// Wait for any button press to start the game
waitForAnyButtonPress();
// Start the countdown
startCountdown();
}
void loop(){
if (currentRound < 4) {
// Select a random LED to light up
int randomLED = random(0, 4);
digitalWrite(ledPins[randomLED], HIGH);
unsigned long startTime = millis();
while (!digitalRead(buttonPins[randomLED]) == LOW) {
// Waiting for the correct button press
}
unsigned long endTime = millis();
digitalWrite(ledPins[randomLED], LOW);
// Record the reaction time
reactionTimes[currentRound] = endTime - startTime;
currentRound++;
delay(500); // Wait 0.5 seconds before lighting up another LED
} else {
// Calculate and display the average reaction time
unsigned long totalTime = 0;
for (int i = 0; i < 4; i++) {
totalTime += reactionTimes[i];
}
unsigned long averageTime = totalTime / 4;
OLED.clearDisplay();
OLED.setCursor(15, 9);
OLED.setTextSize(2);
OLED.setTextColor(WHITE);
OLED.print("Avg.Time");
OLED.setTextSize(3);
OLED.setCursor(19, 31);
OLED.print(averageTime);
OLED.println("ms");
OLED.display();
// Wait for any button press to restart the game
waitForAnyButtonPress();
currentRound = 0; // Reset the game
startCountdown();
}
}
void waitForAnyButtonPress() {
bool buttonPressed = false;
while (!buttonPressed) {
for (int i = 0; i < 4; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
buttonPressed = true;
break;
}
}
}
}
void startCountdown() {
OLED.clearDisplay();
for (int i = 3; i > 0; i--) {
OLED.setCursor(41, 3);
OLED.setTextSize(8);
OLED.setTextColor(WHITE);
OLED.println(i);
OLED.display();
delay(1000);
OLED.clearDisplay();
}
OLED.setCursor(20, 4);
OLED.setTextSize(8);
OLED.setTextColor(WHITE);
OLED.println("GO");
OLED.display();
delay(1000);
OLED.clearDisplay();
}
代码讲解
- 代码结构与包含的库
包含的头文件:
#include <Arduino.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
分别引入了 Arduino 核心库、I2C 通信库以及用于控制 OLED 显示屏(基于 Adafruit 的库)的相关库,用于后续开发板基本功能、I2C 通信以及 OLED 显示操作。
宏定义部分:
#define SCREEN_WIDTH 128 // OLED display width, in pixels
#define SCREEN_HEIGHT 64 // OLED display height, in pixels
#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)
#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
定义了 OLED 显示屏的相关参数,如宽度、高度、复位引脚以及 I2C 地址等信息,用于正确初始化和配置 OLED 显示屏。
- 变量声明
// 构造对象 连接到I2C(SDA、SCL引脚)的SSD1306声明
Adafruit_SSD1306 OLED(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Define push buttons and LEDs pins
const int buttonPins[4] = {0,1,2,3};
const int ledPins[4] = {7,6,5,4};
unsigned long reactionTimes[4];
int currentRound = 0;
创建了 Adafruit_SSD1306 类型的对象 OLED,用于后续操作 OLED 显示屏。
定义了两个数组 buttonPins 和 ledPins,分别用于指定 4 个按钮和 4 个 LED 所连接的引脚编号。
reactionTimes 数组用于记录每一轮的反应时间,currentRound 变量用于跟踪当前游戏进行到的轮次。
3.setup函数(初始化函数)
引脚模式设置:
for (int i = 0; i < 4; i++) {
pinMode(buttonPins[i], INPUT_PULLUP);
pinMode(ledPins[i], OUTPUT);
digitalWrite(ledPins[i], LOW);
}
pinMode(LED_BUILTIN, OUTPUT);
通过循环将 4 个按钮引脚设置为上拉输入模式(默认高电平,按下为低电平),4 个 LED 引脚设置为输出模式并初始化为低电平(关闭状态),同时也设置了开发板内置 LED 引脚为输出模式。
OLED 初始化及显示设置:
OLED.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS);
OLED.clearDisplay();
OLED.setTextColor(WHITE);
OLED.setTextSize(2);
OLED.setCursor(0, 0);
OLED.println(" Press any");
OLED.println(" button to");
OLED.setTextSize(3);
OLED.setCursor(28, 38);
OLED.print("PLAY");
OLED.display();
初始化 OLED 显示屏,然后进行一系列显示设置,如清除屏幕内容、设置文本颜色为白色、设置字体大小、设置文本显示位置等,并在屏幕上显示提示玩家按下按钮开始游戏的相关文字信息,最后调用 display 函数将内容显示在屏幕上。
等待按钮按下开始游戏:
waitForAnyButtonPress();
startCountdown();
先调用 waitForAnyButtonPress 函数等待玩家按下任意一个按钮,之后调用 startCountdown 函数开始倒计时流程,进入游戏环节。
- loop函数(主循环函数)
游戏进行阶段(currentRound < 4 时):
if (currentRound < 4) {
// Select a random LED to light up
int randomLED = random(0, 4);
digitalWrite(ledPins[randomLED], HIGH);
unsigned long startTime = millis();
while (!digitalRead(buttonPins[randomLED]) == LOW) {
// Waiting for the correct button press
}
unsigned long endTime = millis();
digitalWrite(ledPins[randomLED], LOW);
// Record the reaction time
reactionTimes[currentRound] = endTime - startTime;
currentRound++;
delay(500); // Wait 0.5 seconds before lighting up another LED
}
在每一轮中,随机选择一个 LED(randomLED)点亮,记录点亮时刻(startTime),然后通过循环等待对应按钮被按下(按钮引脚变为低电平),记录按下按钮的时刻(endTime),计算并记录本轮反应时间到 reactionTimes 数组中,接着熄灭 LED,轮次加 1,并延迟 (delay)0.5 秒后进入下一轮。
游戏结束阶段(currentRound >= 4 时):
else {
// Calculate and display the average reaction time
unsigned long totalTime = 0;
for (int i = 0; i < 4; i++) {
totalTime += reactionTimes[i];
}
unsigned long averageTime = totalTime / 4;
OLED.clearDisplay();
OLED.setCursor(15, 9);
OLED.setTextSize(2);
OLED.setTextColor(WHITE);
OLED.print("Avg.Time");
OLED.setTextSize(3);
OLED.setCursor(19, 31);
OLED.print(averageTime);
OLED.println("ms");
OLED.display();
// Wait for any button press to restart the game
waitForAnyButtonPress();
currentRound = 0; // Reset the game
startCountdown();
}
当 4 轮游戏结束后,先计算出平均反应时间(averageTime),然后清空 OLED 屏幕,在屏幕上显示平均反应时间相关信息,接着等待玩家再次按下任意按钮重启游戏,重置轮次变量 currentRound 为 0,并重新开始倒计时进入下一轮游戏。
- waitForAnyButtonPress 函数
void waitForAnyButtonPress() {
bool buttonPressed = false;
while (!buttonPressed) {
for (int i = 0; i < 4; i++) {
if (digitalRead(buttonPins[i]) == LOW) {
buttonPressed = true;
break;
}
}
}
}
通过循环不断检测 4 个按钮引脚的电平状态,只要有一个按钮被按下(引脚变为低电平),就将 buttonPressed 标志变量设为 true,从而结束循环,实现等待任意按钮按下的功能。
- startCountdown函数
void startCountdown() {
OLED.clearDisplay();
for (int i = 3; i > 0; i--) {
OLED.setCursor(41, 3);
OLED.setTextSize(8);
OLED.setTextColor(WHITE);
OLED.println(i);
OLED.display();
delay(1000);
OLED.clearDisplay();
}
OLED.setCursor(20, 4);
OLED.setTextSize(8);
OLED.setTextColor(WHITE);
OLED.println("GO");
OLED.display();
delay(1000);
OLED.clearDisplay();
}
用于在 OLED 屏幕上实现倒计时显示功能,先清空屏幕,然后从数字 3 开始,每次在屏幕上显示一个数字,显示一秒后清空屏幕,依次显示 3、2、1,最后显示 “GO” 提示游戏开始,每个显示内容都停留一秒,完成倒计时提示后再次清空屏幕。
四、成品展示
速度点击器
五、过程总结
- 开始项目首先要了解所需要的器件,我刚开始时也是无从下手,对于相关的软硬件知识非常不了解。
了解他们的第一步,可以先从淘宝上进行拍照搜索,知道他们的具体名称,相关引脚及对应功能,熟悉后可以减少后续很多问题。 - 我是先从代码入手,将各部分代码依次进行试验,从LED的闪烁(这里我直接用的示例代码中的Blink进行上传,然后在面包板上连接观察电路),到OLED屏幕的设置(这里我卡了很久,一开始我用的是0.96寸八针OLED,对于它我没有具体了解,导致我后续搜索方向错误一直找不到相应图片和相关资料,了解引脚图及功能后又找不到其相应代码,出现了代码上传成功OLED却不显示字幕的情况,最后我选择使用了四针OLED,这回OLED屏显成功)如下图:

然后是按键控制开关(我先上传代码成功后,在面包板上简单连接电路)
按键控制开关
- 最后,各代码实验完毕后,我将总的电路连接并进行组装。一开始3D打印的主体壳太小,我就在Fusion360内将其拉伸了些,但是安装时发现按钮部位依旧不够适配,按下时不牢固总是会掉落,然后我就用热熔胶粘连了一下,但由于操作时动作幅度太大,一些引脚部位焊接不稳导致掉落。
- 最终,我决定自己画一个电路板(在嘉立创EDA),下图所示:



openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐
所有评论(0)