想玩 ESP32 但手头没有开发板,或者板子还在快递路上,又或者你只是单纯想先跑通一个点灯程序找找感觉——Wokwi 这个在线模拟器基本能解决你所有的入门焦虑。它把 ESP32 开发板、LED、电阻、按钮、传感器甚至逻辑分析仪都搬进了浏览器,打开网页就能连线、写代码、点运行,几秒钟后就能看到虚拟 LED 亮起来。这套教程是零基础系列的第一篇,目标很明确:把开发环境搭起来,把第一个点灯实验跑通,让你在完全没有实物的情况下,建立起对 ESP32 开发流程的完整认知。不管你是刚接触嵌入式的学生,还是从纯软件转过来的开发者,跟着走一遍,后面再上手真板子会顺很多。
1. 为什么入门阶段用模拟器比直接买板子更划算
1.1 硬件入门最劝退的三个瞬间
我带过不少刚接触嵌入式的朋友,发现大家卡住的地方高度一致。第一个瞬间是板子买回来发现驱动装不上,设备管理器里一个黄色感叹号,串口就是不出来;第二个瞬间是好不容易连上了,烧录报错,什么"Failed to connect to ESP32: Timed out waiting for packet header",一查资料发现要按住 BOOT 键再点下载;第三个瞬间是代码烧进去了但灯不亮,开始怀疑是代码问题、板子问题还是线接错了,手边又没有第二块板子做对照。
这三个瞬间本质上都是"环境问题"和"硬件问题"混在一起,新手很难区分到底是哪一层出了错。而模拟器恰好把硬件层的不确定性全部消掉了——虚拟开发板不会坏、驱动不用装、串口不会冲突,你写的代码要么逻辑对要么逻辑错,反馈非常干净。
1.2 Wokwi 到底模拟了什么,没模拟什么
Wokwi 的定位是"电子电路与嵌入式代码的在线仿真"。它模拟的是 ESP32 芯片的指令执行、GPIO 电平变化、常见外设的行为(比如 LED、按钮、蜂鸣器、OLED 屏、温湿度传感器等),以及它们之间的连线关系。你在浏览器里拖一块 ESP32 DevKit,再拖一个 LED 和电阻,用虚拟导线连起来,然后写 Arduino 代码,点绿色运行按钮,就能看到 LED 按你的逻辑闪烁。
它没有模拟的部分也要心里有数:真实的射频性能、蓝牙配对过程、WiFi 实际吞吐、模拟量采集的噪声、电源纹波、时序抖动这些,模拟器给不了你。所以 Wokwi 适合验证逻辑、学习外设驱动写法、调试状态机,但不适合做射频相关或对时序极其敏感的项目。入门阶段这些限制完全不影响,反而帮你把注意力集中在代码逻辑上。
1.3 零成本试错的真正价值
我自己的习惯是,任何新外设的驱动代码,先在 Wokwi 上把逻辑跑通,再去真板子上验证。这样做的好处是,当真板子出问题时,我可以确定代码逻辑本身是对的,问题大概率出在接线、供电或硬件差异上,排查范围一下子缩小了一半。对于纯新手来说,这个习惯更值得养成——你不需要为每一次"我是不是把 GPIO 号写错了"的疑问去反复插拔 USB 线。
提示:Wokwi 免费账户可以创建公开项目,足够完成本教程所有内容。如果你后续要做私有项目,再考虑升级即可,入门阶段完全不用花钱。
2. 五分钟把 Wokwi 开发环境跑起来
2.1 打开即用的在线编辑器
Wokwi 的入口非常直接,浏览器访问 wokwi.com,注册一个账号(用邮箱或第三方登录都行),然后点 "New Project",选择 "ESP32" 模板。这时候你会看到一个分屏界面:左边是代码编辑器,默认已经有一段 Arduino 的 setup 和 loop 骨架;右边是电路图区域,默认放了一块 ESP32 DevKit 开发板。整个界面不需要安装任何东西,不需要配置编译器路径,不需要管 Python 环境,打开就能用。
这里有个细节值得说一下:Wokwi 的代码编译是在云端完成的,你点运行后,代码会被送到服务器编译成固件,再加载到浏览器里的虚拟芯片上执行。所以第一次运行会稍微慢一点,大概几秒钟,之后就很快了。如果你的网络环境访问境外站点不太顺畅,可能会遇到加载慢的情况,这时候可以换个时间段再试,或者用 Wokwi for VS Code 插件在本地编辑、云端仿真,体验会好一些。
2.2 认识默认工程里的三样东西
新建 ESP32 工程后,界面上有三个核心区域需要你认识清楚。
第一个是代码区,默认代码长这样:
void setup() { // put your setup code here, to run once: } void loop() { // put your main code here, to run repeatedly: }这是 Arduino 框架的标准结构,setup()里的代码上电后只执行一次,loop()里的代码会无限循环执行。ESP32 用 Arduino 框架开发的好处就是这套结构极其简单,新手不用管启动流程、时钟初始化、任务调度这些底层东西。
第二个是电路区,默认只有一块 ESP32 DevKit 板子,板子上标注了各个引脚的名字,比如 GPIO2、GPIO4、GND、3V3 等。你可以直接在这些引脚上连线。
第三个是顶部的控制栏,绿色三角形是运行,红色方块是停止,还有一个串口监视器按钮,点开可以看到代码里Serial.print输出的内容。这三个东西就是你接下来最常用的操作。
2.3 用 Wokwi for VS Code 在本地写代码
如果你更习惯在本地编辑器里写代码,Wokwi 提供了 VS Code 插件。安装方式是在 VS Code 扩展市场搜索 "Wokwi",安装后按F1,输入 "Wokwi: Request a new Wokwi license",按提示激活(免费版有功能限制但入门够用)。然后新建一个文件夹,创建diagram.json和sketch.ino两个文件,插件会自动识别并提供仿真按钮。
diagram.json是描述电路连接的文件,格式是 JSON,定义了板子和元件的类型、位置、连线关系。这个文件在网页版里是自动生成的,你在界面上拖拽连线,它就在后台更新。用 VS Code 插件时你需要手动写或者从网页版导出。入门阶段我建议先用网页版把电路搭好,再导出diagram.json到本地,这样最省事。
注意:VS Code 插件版的编译和仿真仍然依赖云端服务,所以本地编辑并不等于完全离线。如果你需要完全离线的方案,那得用 ESP-IDF 或 PlatformIO 配合本地工具链,那是另一个话题了,入门阶段不必折腾。
3. 第一个点灯实验:从连线到代码的完整链路
3.1 电路连线:LED 和电阻怎么接
点灯实验的电路极其简单,但接线逻辑必须搞清楚,否则后面做复杂项目会一直糊涂。你需要从元件库里拖出两个东西:一个 LED 和一个电阻。元件库在电路区上方的紫色 "+" 按钮里,搜索 "LED" 和 "Resistor" 就能找到。
接线方式是这样的:ESP32 的 GPIO2 引脚连到电阻的一端,电阻的另一端连到 LED 的正极(阳极),LED 的负极(阴极)连到 ESP32 的 GND 引脚。为什么中间要串一个电阻?因为 LED 是电流驱动器件,不加限流电阻直接接在 3.3V 和 GND 之间,电流会大到烧毁 LED(真实世界里)或触发模拟器的保护。电阻的作用是把电流限制在 LED 能承受的范围内。
限流电阻的阻值怎么算?ESP32 的 GPIO 输出高电平时是 3.3V,普通 LED 的正向压降大约是 2V,期望电流取 10mA 左右比较安全。根据欧姆定律,电阻值 = (3.3V - 2V) / 0.01A = 130Ω。实际选用 220Ω 或 330Ω 都可以,电流会小一些,LED 稍微暗一点但更安全。Wokwi 里默认的电阻是 220Ω,直接用就行。
在 Wokwi 里连线的方式是:点击一个引脚,再点击另一个引脚,就会生成一条导线。连线完成后,你可以点击导线中间调整走向,让它看起来整洁一些。这里有个新手常犯的错误:把 LED 的正负极接反了。LED 是有极性的,长脚是正极,短脚是负极,在 Wokwi 里 LED 元件上也有标记,接反了灯不会亮,但也不会报错,容易让人以为是代码问题。
3.2 代码编写:三行核心逻辑
电路连好后,把代码区的默认内容替换成下面这段:
#define LED_PIN 2 void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { digitalWrite(LED_PIN, HIGH); delay(1000); digitalWrite(LED_PIN, LOW); delay(1000); }逐行解释一下。#define LED_PIN 2是定义一个宏,把 GPIO2 这个引脚号起个名字叫 LED_PIN,后面代码里都用这个名字。这样做的好处是,如果以后你要换引脚,只改这一行就行,不用满代码找数字 2。pinMode(LED_PIN, OUTPUT)在 setup 里执行,作用是把这个引脚配置成输出模式,只有配置成输出,才能用 digitalWrite 去控制它输出高电平或低电平。
loop()里的四行是核心逻辑:先输出高电平(3.3V),LED 点亮;延时 1000 毫秒;再输出低电平(0V),LED 熄灭;再延时 1000 毫秒。因为 loop 会无限循环,所以效果就是 LED 每秒闪烁一次。delay(1000)的单位是毫秒,1000 毫秒就是 1 秒。
点绿色运行按钮,你应该能看到虚拟 LED 开始一闪一闪。如果没亮,先检查三件事:LED 正负极有没有接反、GPIO 号是不是写成了 2、电阻有没有接上。这三件事排查完基本就能解决。
3.3 用串口监视器观察程序运行状态
点灯只是第一步,学会看串口输出才是调试的关键。把代码改成这样:
#define LED_PIN 2 void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); Serial.println("ESP32 booted, LED blink start"); } void loop() { digitalWrite(LED_PIN, HIGH); Serial.println("LED ON"); delay(1000); digitalWrite(LED_PIN, LOW); Serial.println("LED OFF"); delay(1000); }Serial.begin(115200)初始化串口,波特率设成 115200,这是 ESP32 上最常用的速率。然后在关键位置加Serial.println,运行时点开串口监视器,就能看到 "LED ON" 和 "LED OFF" 交替出现。这个习惯非常重要——真实项目里你没法用眼睛看到程序内部状态,串口打印就是你的眼睛。Wokwi 的串口监视器还支持输入,你可以在代码里用Serial.read()读取你输入的内容,做交互式调试。
提示:Wokwi 串口监视器的波特率要和代码里
Serial.begin的参数一致,否则会显示乱码。这是新手非常容易忽略的一点,真板子上也一样。
4. 模拟器里踩过的坑和真板子上的对应关系
4.1 模拟器能跑但真板子不亮,问题出在哪
这是最常见的问题。在 Wokwi 上跑得好好的代码,烧到真板子上灯不亮,原因通常有几个。第一是引脚选择,ESP32 有些 GPIO 在启动时有特殊功能,比如 GPIO0 影响启动模式,GPIO6 到 GPIO11 连接了内部 Flash 不能随便用,GPIO34 到 GPIO39 只能做输入不能做输出。Wokwi 对这些限制的模拟不严格,你接在 GPIO34 上做输出它可能也让你跑,但真板子上就不行。所以选引脚时尽量用 GPIO2、GPIO4、GPIO5、GPIO12 到 GPIO19 这些通用引脚。
第二是 LED 极性,模拟器里接反了不亮但代码不报错,真板子上也一样,但真板子上你还得考虑 LED 的驱动电流够不够。ESP32 单个 GPIO 最大输出电流大约是 40mA,驱动普通小 LED 没问题,但如果你接的是大功率 LED 或者多个 LED 并联,就需要加三极管或驱动芯片了。
第三是供电问题,真板子用 USB 供电时,如果同时驱动多个外设,电流可能不够,导致板子反复重启。模拟器里不存在供电不足的问题,所以这一点在模拟阶段完全体会不到。
4.2 代码逻辑对但时序不对的排查思路
模拟器的时间是"理想时间",delay(1000)就是精确的 1000 毫秒。真板子上,delay的精度受晶振和中断影响,会有微小偏差,但一般项目感觉不到。真正需要注意的是,当你用millis()做非阻塞延时的时候,模拟器和真板子的行为基本一致,但如果你用了硬件定时器或者中断,模拟器的模拟程度就有限了。
我的经验是,任何涉及精确时序的代码,比如驱动 WS2812 灯带、DHT11 温湿度传感器这种对时序敏感的器件,模拟器上跑通了不代表真板子上一定行。Wokwi 对这类器件有专门的模拟模型,能帮你验证逻辑,但真板子上的时序余量还得自己实测。入门阶段先不用管这些,把 GPIO 控制和串口调试练熟就够了。
4.3 从模拟到实物的迁移检查清单
当你准备把 Wokwi 上的项目搬到真板子时,按这个清单过一遍,能省很多时间。
| 检查项 | 模拟器表现 | 真板子注意事项 |
|---|---|---|
| 引脚号 | 任意 GPIO 都能用 | 避开 GPIO6-11、34-39 做输出 |
| LED 极性 | 接反不亮不报错 | 同样不亮,需万用表确认 |
| 限流电阻 | 可省略也能亮 | 必须接,否则烧 LED 或 GPIO |
| 供电 | 无限供电 | USB 供电注意总电流 |
| 串口波特率 | 不匹配显示乱码 | 同样乱码,需一致 |
| 启动模式 | 无此概念 | GPIO0 拉低进下载模式 |
这张表建议截图存着,每次从模拟转实物时对照检查。尤其是引脚选择这一条,我见过太多人把 LED 接在 GPIO34 上然后怀疑人生。
5. 把点灯实验扩展成真正有用的练习
5.1 用按钮做输入控制 LED
只会输出不算完整,加上输入才形成闭环。从元件库拖一个按钮(Push Button)出来,一端接 GPIO4,另一端接 GND。代码改成这样:
#define LED_PIN 2 #define BTN_PIN 4 void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); pinMode(BTN_PIN, INPUT_PULLUP); } void loop() { int btnState = digitalRead(BTN_PIN); if (btnState == LOW) { digitalWrite(LED_PIN, HIGH); Serial.println("Button pressed, LED ON"); } else { digitalWrite(LED_PIN, LOW); } delay(50); }这里的关键是INPUT_PULLUP,它启用了 ESP32 内部的上拉电阻,这样按钮不按时引脚是 HIGH,按下时接地变成 LOW。如果不加上拉,引脚状态会浮动,读数不稳定。这个知识点在真板子上同样重要,很多新手按钮读数乱跳就是因为没加上拉或下拉。
5.2 用电位器模拟模拟量输入
ESP32 有 ADC(模数转换器),可以读取模拟电压。从元件库拖一个电位器(Potentiometer),两端分别接 3V3 和 GND,中间抽头接 GPIO34(注意这里用输入专用引脚正合适)。代码里用analogRead读取:
#define POT_PIN 34 void setup() { Serial.begin(115200); } void loop() { int val = analogRead(POT_PIN); Serial.print("ADC value: "); Serial.println(val); delay(200); }ESP32 的 ADC 默认是 12 位分辨率,读数范围 0 到 4095,对应 0 到 3.3V。转动电位器,串口监视器里的数值会跟着变。这个练习让你理解模拟量采集的基本流程,后面接光敏电阻、温度传感器都是同样的套路。
5.3 用 millis 替代 delay 做多任务
delay的问题是它会阻塞整个 loop,期间什么都不能做。真实项目里通常用millis()做非阻塞定时。把点灯代码改成这样:
#define LED_PIN 2 unsigned long previousMillis = 0; const long interval = 1000; bool ledState = false; void setup() { pinMode(LED_PIN, OUTPUT); } void loop() { unsigned long currentMillis = millis(); if (currentMillis - previousMillis >= interval) { previousMillis = currentMillis; ledState = !ledState; digitalWrite(LED_PIN, ledState); } // 这里可以放其他任务,不会被阻塞 }这个写法看起来比delay复杂,但它是所有实用项目的基础。用millis()记录上次执行时间,每次 loop 检查是否到了间隔,到了就执行并更新时间戳。这样 loop 可以快速循环,同时处理多个不同间隔的任务。我在实际项目里从来不用delay做超过 100ms 的延时,全部用millis()管理,这是从模拟阶段就该养成的习惯。
6. 入门之后往哪走:模拟器能陪你走多远
Wokwi 的元件库比很多人想象的丰富,除了 LED、按钮、电位器,还有 OLED 显示屏、LCD 字符屏、伺服电机、步进电机、超声波测距、红外接收、DHT22 温湿度、MPU6050 陀螺仪、SD 卡、蜂鸣器等等。你完全可以在模拟器上把 ESP32 的常用外设都玩一遍,把驱动代码和逻辑都验证好,再去买对应的实物模块。
我个人的学习路径是这样的:每接触一个新外设,先在 Wokwi 上找对应的元件,写最小驱动代码跑通,理解它的通信协议(I2C、SPI、UART、单总线等),然后再买实物验证。这样做的好处是,买回来的模块大概率一次点亮,因为代码逻辑已经在模拟器上验证过了,剩下的只是接线和供电问题。对于预算有限或者不想囤一堆用不上的模块的人来说,这个路径非常经济。
当然,模拟器替代不了真实世界的全部。WiFi 连接、蓝牙通信、低功耗管理、射频调试这些,最终还是要上真板子。但作为零基础入门的第一步,Wokwi 把门槛降到了最低,让你能把精力集中在编程逻辑和电子基础概念上,而不是被环境和硬件问题劝退。把这一篇的点灯、按钮、电位器、millis 四个练习都跑一遍,你对 ESP32 开发的整体感觉就建立起来了,后面再学 WiFi、蓝牙、传感器融合,都是在这个基础上叠加。
最后分享一个我在模拟器上养成的习惯:每个练习项目都导出保存一份diagram.json和代码,按外设类型建文件夹归档。时间长了你就有了一个自己的代码库,下次做新项目时直接翻出类似的改一改,效率比从头写高得多。这个习惯我从入门保持到现在,受益很大。