1. 项目概述一个父子共创的电子记忆游戏几年前我儿子对电脑游戏很着迷但对屏幕背后的世界一无所知。为了让他理解“魔法”是如何发生的我决定和他一起动手做一个看得见、摸得着的电子玩具。我们的目标不是造一个多么复杂的设备而是创造一个能让他亲手焊接、亲手编程并且玩得开心的东西。最终我们选定了一个经典游戏的复刻版——一个需要记忆颜色序列的“记忆游戏机”。这个项目的核心非常简单模仿上世纪七八十年代风靡一时的“Simon”游戏。游戏机会亮起一个颜色的LED玩家需要按下对应颜色的按钮接着它会再随机亮起一个LED玩家则需要按顺序重复整个越来越长的颜色序列。听起来简单但序列变长后对记忆力的挑战可不小。我选择用Python作为编程语言主要是因为它语法清晰、接近自然语言对初学者非常友好。硬件上我们选用了一块ESP32开发板它功能强大、价格便宜而且自带Wi-Fi和蓝牙虽然我们这个项目用不上更重要的是它可以用一块锂电池供电让我们的游戏机摆脱电线真正“活”起来。整个项目只用了ESP32上的8个GPIO引脚4个控制LED4个读取按钮状态。为了增加点趣味性和信息反馈我们还加了一块小小的128x64像素的OLED屏幕用来显示分数和关卡。从一堆散乱的元器件、面包板上的原型到最后用3D打印外壳封装成一个可以揣在口袋里、带到任何地方玩的完整设备这个过程充满了乐趣和挑战。这个游戏机陪伴我们度过了整个假期它不仅是一个玩具更是一堂生动的物理、电子和编程入门课。下面我就把我们从零到一搭建这个“记忆大师”游戏机的完整过程、踩过的坑和收获的经验毫无保留地分享出来。2. 核心设计思路与方案选型2.1 为什么选择“记忆游戏”作为入门项目对于电子和编程的初学者尤其是孩子第一个项目的选择至关重要。它需要满足几个条件目标明确、反馈即时、有成就感并且能拆解成清晰的步骤。记忆游戏完美契合这些要求。它的游戏逻辑是线性的、可预测的生成序列 - 展示序列 - 等待输入 - 验证输入。这个循环对应到程序里就是几个清晰的函数模块。孩子能很容易地理解“这里该写一个让灯闪起来的函数”“那里该写一个检查按钮的函数”。当代码写完下载到板子上灯真的按写的逻辑亮起来按钮真的能控制游戏时那种“我创造了它”的成就感是无与伦比的。这比单纯在屏幕上打印“Hello World”要生动和有趣得多。2.2 硬件平台选型ESP32为何胜出在项目开始时我们考虑过几种主流微控制器Arduino Uno、Raspberry Pi Pico和ESP32。Arduino Uno经典资料极多。但它核心性能较弱需要额外的硬件才能实现无线功能而且原生开发环境Arduino IDE对Python支持不直接需要通过Firmata等协议增加了复杂度。Raspberry Pi Pico性价比高原生支持MicroPython一种针对微控制器的Python精简版本非常不错。但在当时其生态系统和社区资源相对于ESP32还是稍显年轻。ESP32这是我们最终的选择。理由如下双核处理器与充足内存比传统Arduino性能强得多能轻松处理游戏逻辑、OLED显示并为未来扩展比如添加声音留足余地。对MicroPython支持成熟有一个非常活跃的社区固件稳定库丰富。我们可以用纯Python来编程学习路径一致。集成Wi-Fi与蓝牙虽然本项目未使用但这意味着未来我们可以轻松升级项目比如做成联网对战版或者用手机蓝牙遥控项目的“可成长性”很好。广泛的电源管理特性它支持从锂电池直接供电并集成了充电管理电路。这意味着我们只需要一块常见的3.7V锂电池和一个USB口就能实现充电、供电一体化非常适合制作便携设备。注意市面上ESP32开发板型号繁多推荐选择像“ESP32 DevKitC V4”或“NodeMCU-32S”这类引脚引出完善、自带USB转串口芯片的版本会省去很多调试的麻烦。2.3 软件与工具链的搭建编程环境我们选择了Thonny。这是一个对初学者极其友好的Python IDE特别适合MicroPython开发。它的优势在于无缝连接硬件安装好驱动后可以像操作本地文件一样直接在IDE里选择串口连接ESP32上传、下载文件甚至使用交互式命令行REPL实时调试查看变量状态。简洁直观的界面没有复杂的配置孩子可以快速聚焦于写代码本身。内置包管理器可以方便地为ESP32安装额外的MicroPython库比如我们后面用到的OLED驱动库。开发流程大致是在Thonny中编写Python代码 - 通过USB线连接ESP32 - 将代码作为主脚本上传到ESP32的文件系统中 - 复位或上电后ESP32自动运行。这种“写-传-跑”的循环非常快速直观。3. 硬件电路设计与连接详解3.1 元器件清单与作用在画电路图之前我们先明确需要哪些“演员”主角 ESP32开发板1块系统大脑。LED发光二极管4个红、绿、蓝、黄各一。用于显示颜色序列。注意要选择合适颜色的散光LED视觉效果更柔和。按钮微动开关4个。用于玩家输入。建议选用带帽的12x12mm四脚微动开关手感清晰。电阻限流电阻4个每个LED串联一个。阻值计算是关键。ESP32的GPIO输出电压约为3.3V假设LED工作电流我们设定在10mA足够亮且安全不同颜色LED正向压降不同通常红色约1.8V蓝/绿/白约3.0V。以红色LED为例电阻值 R (3.3V - 1.8V) / 0.01A 150Ω。为方便采购统一使用220Ω电阻亮度稍减但完全可用且更安全。上拉电阻4个每个按钮接一个。ESP32的GPIO可以配置内部上拉电阻但为了电路稳定性和教学直观我们外部使用了10kΩ的电阻。它的作用是当按钮未按下时将GPIO引脚稳定地拉到高电平3.3V按下时引脚接地变为低电平。OLED显示屏128x641块。常用型号是SSD1306驱动的I2C接口屏幕。它只有4个引脚VCC, GND, SCL, SDA接线非常简洁。锂电池1块3.7V容量建议在500mAh以上。用于便携供电。电池充电/升压一体模块1个如TP4056升压。这是实现便携的关键。TP4056模块负责给锂电池安全充电升压部分将电池的3.7V稳定升压到5V供给ESP32。面包板、杜邦线公对公、公对母用于原型搭建。后续封装材料如3D打印外壳、螺丝、导线等。3.2 电路连接原理与实操接线图电路的核心逻辑是“输出控制LED输入读取按钮”。以下是接线明细表元件引脚/端连接到 ESP32 引脚说明红色LED阳极长脚通过220Ω电阻 - GPIO 23输出高电平时点亮阴极短脚GND绿色LED阳极通过220Ω电阻 - GPIO 22输出高电平时点亮阴极GND蓝色LED阳极通过220Ω电阻 - GPIO 21输出高电平时点亮阴极GND黄色LED阳极通过220Ω电阻 - GPIO 19输出高电平时点亮阴极GND红色按钮一脚GPIO 15配置为输入内部/外部上拉另一脚GND按下时GPIO15接地绿色按钮一脚GPIO 2配置为输入内部/外部上拉另一脚GND蓝色按钮一脚GPIO 4配置为输入内部/外部上拉另一脚GND黄色按钮一脚GPIO 18配置为输入内部/外部上拉另一脚GNDOLED屏幕VCC3.3V切勿接5V会烧毁GNDGNDSCLGPIO 25 (I2C时钟线)SDAGPIO 26 (I2C数据线)电源电池充电模块B电池-充电模块B-充电模块OUTESP32 VIN (或5V引脚)充电模块OUT-ESP32 GND充电模块USB口用Micro USB线连接电脑或充电器用于充电和初始编程重要提示在将任何元件连接到ESP32之前务必断开USB或电池供电。接线时优先完成GND地线的连接建立一个共同的参考点。LED的长脚阳极必须通过电阻连接正极短脚阴极接GND接反了不会亮。按钮的两脚没有正反之分。接线顺序建议先在面包板上完成电源部分ESP32、电池模块的连接并测试通电正常。然后逐一连接LED及其电阻每接好一个就写一段简单的测试代码如pin.value(1)验证是否能点亮。接着连接按钮同样写测试代码读取引脚状态。最后连接OLED屏幕。这种“分模块测试”的方法能让你在问题出现时快速定位避免所有线都缠在一起后无从下手。4. 软件编程从零到一的逻辑实现4.1 开发环境准备与固件烧录首先需要给ESP32“安装操作系统”也就是MicroPython固件。从MicroPython官网下载最新的ESP32通用固件.bin文件。安装ESP32的刷机工具如esptool.py可通过Python的pip安装。将ESP32通过USB连接电脑进入下载模式通常需要按住板上的“BOOT”按钮再按一下“EN”复位按钮然后松开“EN”再松开“BOOT”。使用命令行擦除原有固件并刷入新固件。命令类似esptool.py --chip esp32 --port COM3 erase_flash esptool.py --chip esp32 --port COM3 --baud 460800 write_flash -z 0x1000 esp32-xxx.bin请将COM3替换为你的实际串口号esp32-xxx.bin替换为你的固件文件名。刷写成功后打开Thonny在右下角选择解释器为“MicroPython (ESP32)”并选择正确的串口端口。连接后Shell交互窗口会出现提示符输入print(‘Hello, ESP32!’)测试成功则环境搭建完成。4.2 核心游戏逻辑的代码拆解游戏的主程序可以分解为几个核心函数我们采用面向过程的方式让逻辑更清晰。1. 硬件初始化 (init_hardware)这个函数负责配置所有用到的引脚。from machine import Pin, I2C import ssd1306 import time # 定义LED和按钮对应的引脚 led_pins [23, 22, 21, 19] button_pins [15, 2, 4, 18] # 初始化LED对象列表设置为输出模式 leds [Pin(pin, Pin.OUT) for pin in led_pins] # 初始化按钮对象列表设置为输入模式并启用内部上拉电阻 buttons [Pin(pin, Pin.IN, Pin.PULL_UP) for pin in button_pins] # 初始化OLED显示屏 (I2C) i2c I2C(0, sclPin(25), sdaPin(26), freq400000) oled ssd1306.SSD1306_I2C(128, 64, i2c) def init_hardware(): # 确保所有LED初始状态为熄灭低电平 for led in leds: led.value(0) # 清空OLED屏幕并显示欢迎信息 oled.fill(0) oled.text(Memory Game!, 10, 10) oled.text(Press any key, 15, 30) oled.show()实操心得Pin.PULL_UP启用了芯片内部的上述电阻这样我们就不必在外部为每个按钮焊接一个10k电阻了极大地简化了电路。这是ESP32等现代微控制器的便利之处。2. 游戏序列生成与展示 (generate_sequence,play_sequence)游戏的核心是生成一个随机序列并以视觉LED闪烁的方式播放给玩家。import urandom # 用于生成随机数 sequence [] current_level 1 MAX_LEVEL 20 # 游戏最大关卡数 def generate_sequence(length): 生成指定长度的随机序列每个元素是0-3的数字对应4种颜色 return [urandom.getrandbits(2) for _ in range(length)] # getrandbits(2)生成0-3的随机数 def play_sequence(seq, speed0.5): 播放序列点亮对应LED延时熄灭间隔 for color_index in seq: leds[color_index].value(1) # 点亮LED time.sleep(speed) # 保持点亮状态 leds[color_index].value(0) # 熄灭LED time.sleep(0.2) # 序列中每个颜色之间的间隔3. 玩家输入捕获与验证 (get_player_input,check_sequence)需要持续监听按钮并对比玩家的输入和游戏序列。def wait_for_button(): 等待并返回第一个被按下的按钮索引同时提供视觉反馈 pressed_index None while pressed_index is None: for i, btn in enumerate(buttons): if btn.value() 0: # 按钮按下时为低电平 leds[i].value(1) # 按下时对应LED亮起作为反馈 time.sleep(0.2) # 消抖兼反馈显示 leds[i].value(0) while btn.value() 0: # 等待按钮释放 time.sleep(0.05) pressed_index i break # 跳出for循环 time.sleep(0.01) # 短暂延时防止循环空跑耗电 return pressed_index def check_sequence(seq): 让玩家重复整个序列并逐项检查 for i, correct_color in enumerate(seq): player_color wait_for_button() if player_color ! correct_color: return False # 按错游戏结束 # 按对了可以给一个简短的成功提示比如快速闪烁一下对的灯 leds[player_color].value(1) time.sleep(0.1) leds[player_color].value(0) return True # 整个序列输入正确4. 游戏主循环与状态管理将以上函数串联起来并管理游戏状态开始、进行中、结束。def game_loop(): global sequence, current_level game_active True sequence generate_sequence(current_level) while game_active: # 1. 更新OLED显示当前关卡 oled.fill(0) oled.text(Level: {}.format(current_level), 20, 20) oled.show() # 2. 向玩家播放序列 play_sequence(sequence, speedmax(0.1, 0.6 - current_level*0.02)) # 速度随关卡递增 # 3. 获取并验证玩家输入 oled.text(Your turn!, 25, 40) oled.show() if check_sequence(sequence): # 玩家成功 oled.fill(0) oled.text(Correct!, 30, 25) oled.show() time.sleep(1) current_level 1 if current_level MAX_LEVEL: oled.fill(0) oled.text(You WIN!, 30, 25) oled.show() time.sleep(3) game_active False break sequence generate_sequence(current_level) # 生成更长的新序列 else: # 玩家失败 oled.fill(0) oled.text(Wrong! Score:{}.format(current_level-1), 10, 25) oled.show() # 失败动画所有LED快速闪烁几次 for _ in range(5): for led in leds: led.value(1) time.sleep(0.1) for led in leds: led.value(0) time.sleep(0.1) time.sleep(3) game_active False # 游戏结束回到初始状态 current_level 1 oled.fill(0) oled.text(Game Over, 25, 25) oled.text(Press to restart, 5, 45) oled.show() wait_for_button() # 等待任意按钮按下以重启5. 主程序入口最后在main.py文件中调用初始化函数并启动游戏循环。确保将主循环代码文件命名为main.py这样ESP32上电后会自动执行。# main.py if __name__ __main__: init_hardware() while True: game_loop()将main.py和依赖的ssd1306.py库文件通过Thonny上传到ESP32的根目录然后重启设备游戏就应该跑起来了5. 从原型到产品外壳设计与制作当面包板上的电路稳定运行后我们就开始考虑把它变成一个“产品”。这个过程能让孩子理解工程设计中的另一个重要环节——机械结构与外观设计。5.1 3D建模与设计考量我们使用免费的在线3D建模软件如Tinkercad进行设计。设计外壳时需要考虑以下几点固定与定位外壳需要留出精确的孔位来固定ESP32开发板、OLED屏幕、4个按钮和4个LED。LED的孔需要稍微大一点以便嵌入和扩散光线。按钮手感按钮帽要略高于外壳表面并且周围留有间隙防止卡住。我们在按钮下方设计了支撑柱确保按下时受力均匀。电池仓与充电口需要为锂电池设计一个容易放入取出的仓室并为充电模块的Micro USB口开一个访问孔。散热与组装ESP32在运行时会有轻微发热外壳顶部和底部我们设计了一些栅格状的通风孔。外壳设计为上下盖结构用螺丝固定方便后期维修和更换电池。美学和孩子一起选择颜色并在外壳上刻上游戏的名字“Memory Master”和他的名字缩写增加归属感。5.2 焊接与内部走线有了外壳就需要将面包板上的临时连接变为永久性的可靠连接。准备万用板根据外壳内部空间裁剪一块合适大小的洞洞板万用板。规划布局在板上大致摆放ESP32、按钮、LED、电阻等元件规划一个整洁的布局确保所有连接线最短且不会互相干扰。焊接这是锻炼孩子精细操作和耐心的好机会。从低矮的元件电阻开始焊起再到LED、按钮插座最后是连接排针和飞线。技巧使用助焊剂可以让焊接更顺畅。焊锡不要太多形成一个小圆锥形即可。每个焊点焊接时间不宜过长2-3秒以免烫坏元件或焊盘。飞线对于无法通过板子背面走线连接的引脚使用不同颜色的细导线如AWG30硅胶线进行连接。用热熔胶或扎带固定线束使其整洁牢固。避坑指南焊接LED和按钮时务必再次确认极性LED和引脚对应关系按钮。最好在焊接前用万用表的二极管档或通断档测试一下。一旦焊反拆下来会很麻烦可能损坏焊盘。5.3 总装与测试将所有模块组装进外壳先将焊好元件的万用板用螺丝固定在下壳内。将OLED屏幕对准视窗卡好并用少量热熔胶固定侧面。将按钮从外壳内侧装入孔位确保其能自由活动。将LED从内侧插入孔位可以使用一小段热缩管作为遮光罩防止LED之间串光。连接电池并妥善放置电池和充电模块用双面胶或尼龙扎带固定防止晃动。盖上上盖拧紧螺丝。总装完成后不要急于庆祝。进行全面的功能测试测试每个按钮是否都能正常触发且手感一致。测试每个LED是否能正常点亮亮度是否均匀。测试充电功能是否正常充电指示灯会亮。长时间运行游戏检查是否有过热或程序崩溃的情况。6. 项目总结与扩展思考经过这个完整的项目我儿子不仅学会了焊接、读电路图、3D设计基础更重要的是他理解了软件和硬件是如何协同工作的。他看到了一行行抽象的代码如何转化为具体的灯光闪烁和屏幕显示又如何通过物理按钮被控制。这种“创造-控制-反馈”的闭环体验是任何理论课程都无法替代的。项目过程中几个关键的收获点调试是常态代码第一次就完美运行的概率极低。学会使用print()输出变量状态或者用LED闪烁不同次数来指示程序运行到哪一步是嵌入式开发最基本的调试技能。电源管理的重要性当我们第一次用电池供电时发现屏幕偶尔会闪烁。后来意识到是电池电量不足时电压下降导致ESP32和屏幕工作不稳定。这引出了关于电压、电流和电池容量的一堂生动物理课。用户体验UX细节比如按钮按下时对应的LED要亮起给予反馈游戏失败或成功要有明确的声光提示我们后来加了一个有源蜂鸣器关卡提升时序列播放速度可以适当加快以增加难度。这些细节让游戏变得“好玩”。这个项目还有巨大的扩展潜力可以作为一个持续学习的平台增加声音加入一个无源蜂鸣器为每种颜色分配一个不同的音调实现真正的“Simon”游戏体验。难度与模式在OLED菜单上增加选择项比如调整速度、开启“残酷模式”错一次即结束、或者改变序列生成规则如禁止连续同色。数据记录利用ESP32的Flash存储保存最高分记录。无线化利用ESP32的Wi-Fi将得分上传到简单的网络服务器或者实现双人对战模式。这个小小的记忆游戏机最终远远超出了一个玩具的范畴。它是一把钥匙打开了一扇通往创造世界的大门。看到孩子从“消费者”转变为“创造者”时眼中的光芒我觉得所有投入的时间都无比值得。如果你也想为孩子或为自己开启这样一段旅程不妨就从这样一个具体而微的项目开始吧。