Arduino 记忆力挑战游戏 (Simon 复刻版) 制作教程
零部件清单
| 组件 | 数量 | 规格/备注 |
|---|---|---|
| Arduino Uno R3 | 1 | 主控 |
| LED (红/绿/蓝/黄) | 各 1 | 共 4 色,直插 3mm/5mm |
| 330Ω 电阻 | 4 | LED 限流 |
| 10kΩ 电阻 | 4 | 按键下拉 |
| 轻触开关 (6x6mm) | 4 | 对应 4 个 LED |
| 无源蜂鸣器 | 1 | 接 D5,可选 |
| 全尺寸面包板、杜邦线 | 1 套 | 搭建电路 |
电路原理
每个「按键+LED」共用一个 Arduino 引脚,通过切换引脚模式实现双向复用:
- 输出模式 (OUTPUT):驱动 LED 亮/灭 (通过 330Ω 限流电阻)
- 输入模式 (INPUT):读取按键状态 (通过 10kΩ 下拉电阻,按下接 VCC → 读 HIGH)
电路拓扑 (每通道相同):
Arduino Pin (D2/D13/D10/D8)
│
├─[330Ω]─┬─[LED]─┐
│ │ │
│ GND GND
│
├─[10kΩ]─┐
│ │
└─[Button]─VCC (按下接通)
蜂鸣器接 D5 (PWM),一端接 GND,另一端接引脚,用 analogWrite() 产生方波发声。
游戏规则
- 上电后 3 秒延时,全灯慢闪 5 次 (500ms 间隔) → 游戏开始
- 系统随机点亮 1 个 LED (伴随蜂鸣),玩家须在 2 秒内按下对应按键
- 每轮成功,序列长度 +1,系统完整播放新序列,玩家重复
- 按错键、超时 (2秒)、按键顺序错误 → 判负,全灯快闪 50ms × 5
- 失败后回放最后正确序列 (可数分数),再慢闪 5 次进入下一局
- 最高分挑战:作者记录 22 级
核心代码逻辑解析
关键变量
#define PLAYER_WAIT_TIME 2000 // 玩家响应超时 (ms)
byte sequence[100]; // 存储随机序列 (引脚号)
byte curLen = 0; // 当前序列长度
byte inputCount = 0; // 本轮已正确输入数
byte expRd = 0; // 期望的按键引脚
bool btnDwn = false; // 按键按下标志 (防重复触发)
bool wait = false; // true=玩家回合, false=系统回合
bool resetFlag = false; // 判负标志
byte pins[] = {2, 13, 10, 8}; // 4通道引脚 (按键+LED共用)
byte noPins = 4; // 通道数
主循环状态机
void loop() {
if (!wait) { // ====== 系统回合 ======
setPinDirection(OUTPUT);
randomSeed(analogRead(A0));
sequence[curLen] = pins[random(0, noPins)]; // 新增随机步
curLen++;
playSequence(); // 顺序播放整个序列
beep(50);
wait = true; // 切换到玩家回合
inputTime = millis();
inputCount = 0;
}
else { // ====== 玩家回合 ======
setPinDirection(INPUT);
if (millis() - inputTime > PLAYER_WAIT_TIME) { DoLoseProcess(); return; }
if (!btnDwn) {
expRd = sequence[inputCount]; // 当前期望按键
// 检测错误按键
for (int i=0; i
辅助函数
setPinDirection(dir):批量设置 4 通道 INPUT/OUTPUTwriteAllPins(val):全灯同步亮/灭flash(freq):全灯闪烁 (系统回合慢闪/判负快闪)playSequence():遍历sequence[0..curLen-1]依次点亮对应 LED 500ms/间隔 250msbeep(freq):蜂鸣器短鸣Reset():重置所有变量 + 慢闪 5 次DoLoseProcess():快闪 → 延时 → 回放序列 → 延时 → Reset()
扩展玩法
1. 调整难度
修改 #define PLAYER_WAIT_TIME 2000 为 1000 (更难) 或 3000 (更易)。
2. 增加第 5/6 个通道
- 硬件:复制一套「按键+LED+330Ω+10kΩ」接到空闲引脚 (避开 D0/D1 串口)
- 代码修改:
byte noPins = 5; byte pins[] = {2, 13, 10, 8, 6}; // 新增 D6
3. 添加 LCD 显示分数/关卡
接 I2C LCD1602,在 DoLoseProcess() 显示 curLen - 1 作为最终分数。
4. 便携化制作
- PCB 设计:将面包板电路做成定制板,集成电池座、电源开关
- 外壳:3D 打印手持盒,预留按键帽、LED 透光柱
- 电源:1 节 18650 + TP4056 充电 + 5V 升压
常见问题
| 现象 | 原因 | 解决 |
|---|---|---|
| 某 LED 不亮 / 按键无响应 | 杜邦线接触不良 / 引脚定义错 / LED 极性反 | 用万用表测通断;对调同色 LED 排查;核对 pins[] 数组 |
| 上电即死循环/无法上传新代码 | 无 delay(3000) 导致 setup 立即跑逻辑占用串口 |
保留 setup() 首行 delay(3000);紧急时按住 Reset 再插 USB 上传空草图 |
| 蜂鸣器无声/太吵 | 无源蜂鸣器需 PWM 驱动 / 无限流电阻 | 确认 analogWrite(soundPin, 2) (占空比极低);串联 100Ω 降音量 |
| 随机序列不随机 | randomSeed() 熵源固定 |
用 analogRead(A0) 悬空引脚噪声作种子 |
完整代码 (可直接复制上传)
#define PLAYER_WAIT_TIME 2000
byte sequence[100];
byte curLen = 0;
byte inputCount = 0;
byte lastInput = 0;
byte expRd = 0;
bool btnDwn = false;
bool wait = false;
bool resetFlag = false;
byte soundPin = 5;
byte noPins = 4;
byte pins[] = {2, 13, 10, 8};
long inputTime = 0;
void setup() {
delay(3000);
Serial.begin(9600);
Reset();
}
void setPinDirection(byte dir) {
for (byte i = 0; i < noPins; i++) pinMode(pins[i], dir);
}
void writeAllPins(byte val) {
for (byte i = 0; i < noPins; i++) digitalWrite(pins[i], val);
}
void beep(byte freq) {
analogWrite(soundPin, 2); delay(freq);
analogWrite(soundPin, 0); delay(freq);
}
void flash(short freq) {
setPinDirection(OUTPUT);
for (int i = 0; i < 5; i++) {
writeAllPins(HIGH); beep(50); delay(freq);
writeAllPins(LOW); delay(freq);
}
}
void Reset() {
flash(500);
curLen = 0; inputCount = 0; lastInput = 0; expRd = 0;
btnDwn = false; wait = false; resetFlag = false;
}
void Lose() { flash(50); }
void playSequence() {
for (int i = 0; i < curLen; i++) {
digitalWrite(sequence[i], HIGH); delay(500);
digitalWrite(sequence[i], LOW); delay(250);
}
}
void DoLoseProcess() {
Lose(); delay(1000);
playSequence(); delay(1000);
Reset();
}
void loop() {
if (!wait) {
setPinDirection(OUTPUT);
randomSeed(analogRead(A0));
sequence[curLen] = pins[random(0, noPins)];
curLen++;
playSequence();
beep(50);
wait = true;
inputTime = millis();
} else {
setPinDirection(INPUT);
if (millis() - inputTime > PLAYER_WAIT_TIME) { DoLoseProcess(); return; }
if (!btnDwn) {
expRd = sequence[inputCount];
for (int i = 0; i < noPins; i++) {
if (pins[i] == expRd) continue;
if (digitalRead(pins[i]) == HIGH) {
lastInput = pins[i];
resetFlag = true; btnDwn = true;
}
}
}
if (digitalRead(expRd) == HIGH && !btnDwn) {
inputTime = millis();
inputCount++;
btnDwn = true;
} else {
if (btnDwn && digitalRead(lastInput) == LOW) {
btnDwn = false;
delay(20);
if (resetFlag) DoLoseProcess();
else if (inputCount == curLen) {
wait = false; inputCount = 0; delay(1500);
}
}
}
}
}
原理图下载
simonsays.fzz (Fritzing 格式,可导出 PCB/面包板视图)
这是一个经典的 Arduino 入门进阶项目,涵盖了状态机、随机数、非阻塞定时、引脚复用、去抖动等核心编程技巧。做成便携版送给孩子或朋友,是极佳的电子 DIY 礼物。更高级的乐高迷你版可参考文末 Instructables 链接。
制造工艺
- 用 Arduino 打造绚丽显示器 Ambilight 系统
- Arduino Nano 与 MPU-6050 构建实时陀螺仪游戏
- 互动陀螺仪 LED 显示:NeoPixel Ring + Arduino Pro Mini
- 用 Arduino UNO 和 OLED 显示屏制作经典 Pong 游戏:分步教程
- DIY Arduino USB 游戏手柄 — 自制高性能游戏手柄
- Arduino 触控打砖块:在 2.4 英寸 TFT LCD 上重现经典街机
- Pixie:基于 Arduino Pro Mini 的 NeoPixel 智能腕表 - 极简 DIY LED 时钟
- Pixel Chaser:Arduino Nano 单键 NeoPixel 追逐小游戏
- Arduino 自动化 Chrome 恐龙游戏 - LDR 光敏电阻 + Python 实现全自动跳跃
- 便携式远程监控摄像头机器人系统
- 智能手语手套:柔性传感器 + 加速度计 + 蓝牙将手势转文字/语音
- Arduino 合成乐音:从零实现 88 键钢琴全音域