1. 为什么Thonny烧录ESP32总在第一步就卡住
很多人拿到ESP32开发板的第一反应是:装个Thonny,选对串口,点一下烧录按钮,等进度条走完就完事了。理想很丰满,现实是——插上板子,Thonny的串口下拉框里空空如也,或者好不容易识别到了,烧录到一半直接报错退出。更让人抓狂的是,有时候明明昨天还能正常烧录,今天换了根线就彻底不认了。
我前后用过ESP32、ESP32-S3、ESP32-C3这几款芯片,搭配Thonny烧录MicroPython固件的次数没有一百也有八十。踩过的坑从驱动装错版本到Flash分区表对不上,从USB线只有充电功能到板子进了下载模式却没退出来,几乎把能遇到的错误都碰了一遍。这篇文章就是把这些年攒下来的经验一次性倒出来,聚焦五个最高频的烧录错误,把每个错误的触发条件、排查路径和最终解法讲透。
Thonny本身是一个面向初学者的Python IDE,它对MicroPython的支持做得相当友好,内置了固件烧录向导,不需要你手动去记esptool.py那一长串参数。但友好归友好,它把很多底层细节藏起来了,一旦出问题,新手根本不知道从哪里下手。所以理解Thonny烧录背后的实际流程,比单纯记住“点哪个按钮”重要得多。
下面这张表先给你一个全局印象,五个错误分别对应什么现象、什么原因、大概怎么解:
| 错误编号 | 典型现象 | 核心原因 | 解决方向 |
|---|---|---|---|
| 错误一 | 串口列表为空或灰色不可选 | 驱动未装/装错、USB线仅供电 | 装CP210x或CH340驱动,换数据线 |
| 错误二 | 烧录进度卡在某个百分比后超时 | Flash大小选错、波特率过高 | 确认Flash容量,降低烧录波特率 |
| 错误三 | 烧录成功但REPL无响应 | 固件型号与芯片不匹配 | 选对ESP32/S3/C3对应固件 |
| 错误四 | 反复进入下载模式失败 | 自动复位电路不工作 | 手动按住BOOT再点烧录 |
| 错误五 | 烧录后板子不断重启 | 分区表或固件损坏 | 全片擦除后重新烧录 |
这张表建议你截图存着,遇到问题先对号入座,能省下大量瞎折腾的时间。接下来逐个拆解。
2. 错误一:串口列表空白,Thonny根本找不到板子
2.1 先确认是驱动问题还是线的问题
这是最最常见的第一个拦路虎。你插上ESP32开发板,打开Thonny,点右下角的状态栏或者菜单里的“运行”→“选择解释器”→“端口”下拉框,发现里面只有系统自带的COM1,或者干脆什么都没有。这时候别急着重装Thonny,问题几乎不可能出在Thonny本身。
ESP32开发板上负责USB转串口的芯片主要有两种:CP2102和CH340。CP2102是Silicon Labs家的,CH340是南京沁恒的。这两种芯片在Windows上都需要额外安装驱动,Windows 10之后的版本有时候能自动识别,但自动装的驱动版本往往有问题,表现为设备管理器里能看到一个带黄色感叹号的“未知设备”,或者识别成了别的串口设备但Thonny读不到。
排查顺序是这样的:先把板子插到电脑上,打开设备管理器(右键“此电脑”→“管理”→“设备管理器”),展开“端口(COM和LPT)”这一项。如果能看到类似“Silicon Labs CP210x USB to UART Bridge (COM3)”或者“USB-SERIAL CH340 (COM4)”的设备,说明驱动没问题,串口也被系统认出来了。这时候如果Thonny还是看不到,那大概率是Thonny的解释器没选对,或者你打开Thonny的时候板子还没插上。
如果设备管理器里根本没有“端口”这一项,或者出现的是带感叹号的未知设备,那就是驱动没装好。CP210x驱动去Silicon Labs官网下载,CH340驱动去沁恒官网下载。注意不要从各种第三方驱动站下载,那些打包的驱动经常夹带私货或者版本太老。
提示:装完驱动后一定要重新插拔一次USB线,或者直接在设备管理器里点“扫描检测硬件改动”,否则新驱动不会立即生效。
2.2 USB线缆的坑比你想象的大
驱动装好了,设备管理器里也能看到串口了,但Thonny的端口下拉框里就是没有这个COM号。这种情况我遇到过好几次,最后发现是USB线的问题。市面上大量USB线是“充电专用线”,内部只有电源正负两根线,根本没有数据线。这种线插上去,板子上的电源灯会亮,但电脑完全不会识别到USB设备。
怎么判断?换一根线。找一根你确定能传数据的线,比如手机原装数据线、移动硬盘附带的线,插上去试试。如果换了线就能识别,那问题就清楚了。我建议你专门准备一根“调试专用线”,用标签纸标记好,别跟充电线混在一起。这个习惯能帮你省下无数次“怎么又识别不到了”的困惑。
还有一种情况是USB口供电不足。有些台式机前面的USB口或者USB Hub供电能力弱,ESP32开发板加上外设之后电流不够,也会导致识别不稳定。解决办法是插到主板后置的USB口,或者用带独立供电的Hub。
2.3 Thonny解释器配置的正确姿势
假设驱动和线都没问题,设备管理器里能看到COM口,但Thonny里还是选不到。这时候检查Thonny的解释器设置:菜单栏“运行”→“选择解释器”,在弹出的窗口里,“解释器”那一栏要选“MicroPython (ESP32)”,然后下面的“端口”下拉框才会列出可用的串口。如果你选的是“MicroPython (generic)”或者“CircuitPython”,端口列表可能不会刷新。
选好端口之后,Thonny会尝试连接板子。如果板子上已经有MicroPython固件在运行,右下角会显示“MicroPython”和版本号。如果板子是空的或者固件损坏,Thonny会提示连接失败,但端口本身应该是可见的。端口可见但连接失败,那是固件问题,不是驱动问题,这两个要分清楚。
还有一个细节:Thonny在Windows上偶尔会出现端口列表不刷新的情况。如果你确定板子已经插好、驱动也正常,但下拉框里就是没有,可以先把解释器切到“MicroPython (generic)”,再切回“MicroPython (ESP32)”,强制它重新扫描一次。或者干脆关掉Thonny重新打开,通常就能看到新端口了。
3. 错误二:烧录进度条卡住或超时中断
3.1 Flash容量选错是最隐蔽的坑
Thonny的固件烧录向导里有一个步骤是选择Flash大小,选项通常有1MB、2MB、4MB、8MB、16MB。很多新手看到自己的板子标称是4MB Flash,就直接选4MB,结果烧录到一半卡死。问题出在:ESP32的Flash容量和实际可用的固件空间不是一回事,而且不同厂家的板子标注方式不一样。
更关键的是,MicroPython固件本身有不同的大小版本。比如ESP32的通用固件通常在1.5MB到2MB之间,如果你选的Flash容量小于固件实际大小,烧录到后面就会写不进去,进度条卡在某个百分比然后超时。反过来,如果你选的Flash容量大于实际硬件容量,烧录工具会尝试往不存在的地址写数据,同样会失败。
怎么确定板子的真实Flash大小?最可靠的方法是用esptool.py读芯片信息。在命令行里执行:
esptool.py --port COM3 flash_id把COM3换成你的实际端口号。这条命令会输出芯片型号、Flash制造商和容量。比如输出里看到“Detected flash size: 4MB”,那就是4MB。Thonny的烧录向导里就选4MB。如果读出来是2MB,就选2MB。不要凭板子商品页面的描述猜,以实际读出来的为准。
注意:有些ESP32-S3或C3的板子用的是内置Flash,esptool读出来的容量可能和标称不一致,这种情况下优先以esptool的输出为准。
3.2 波特率不是越高越好
Thonny烧录固件时默认的波特率通常是115200或者460800。有些人为了快,手动改成921600甚至更高,结果烧录频繁失败。波特率越高,对USB转串口芯片和线缆质量的要求就越高。CH340芯片在921600下很容易丢数据,CP2102稍微好一点但也不是绝对稳定。
我的经验是:烧录固件时用115200最稳,虽然慢一点,但一次成功的概率最高。烧录完成后,REPL交互可以用更高的波特率,比如115200或230400,这个不影响。Thonny的烧录向导里如果找不到波特率设置,可以在“运行”→“选择解释器”的高级选项里调整。
另外,烧录过程中不要插拔其他USB设备,不要运行大型软件,这些都可能干扰串口通信。我遇到过好几次烧录到90%多突然失败,最后发现是后台在跑Windows更新,占用了大量IO资源。
3.3 烧录地址和分区表的匹配问题
Thonny的烧录向导会自动处理烧录地址,通常MicroPython固件烧到0x1000偏移处。但如果你之前烧过其他固件,比如Arduino的bin文件或者ESP-IDF的固件,Flash里的分区表可能和MicroPython不兼容。这种情况下,即使烧录过程本身成功,板子启动后也会因为找不到正确的分区而反复重启。
解决办法是在烧录MicroPython之前,先做一次全片擦除。Thonny的烧录向导里有一个“擦除Flash”的选项,勾选它。或者手动用esptool执行:
esptool.py --port COM3 erase_flash擦除完成后再烧录MicroPython固件,这样能保证分区表是干净的。这个步骤我强烈建议每次都做,尤其是当你在这块板子上折腾过不同框架之后。多花十几秒擦除,能避免后面半小时的排查。
4. 错误三:烧录显示成功但REPL毫无反应
4.1 固件型号和芯片型号必须严格对应
这是另一个高频错误。ESP32家族现在有ESP32、ESP32-S2、ESP32-S3、ESP32-C3、ESP32-C6等多个型号,每个型号的MicroPython固件是不同的。Thonny的烧录向导里会让你选择固件文件,如果你下载的是ESP32的固件却烧到了ESP32-S3的板子上,烧录过程可能显示成功,但板子重启后REPL完全没反应,或者输出一堆乱码。
怎么确认自己的芯片型号?看板子上的金属屏蔽罩上印的字,通常会写“ESP32-S3”或者“ESP32-C3”。如果看不清,可以用esptool读:
esptool.py --port COM3 chip_id输出里会显示“Chip is ESP32-S3 (revision 0)”之类的信息。确认型号后,去MicroPython官网下载对应型号的固件。注意要下载“Firmware”而不是“Source code”,文件后缀是.bin。
还有一个细节:ESP32-S3和ESP32-C3有不同版本的固件,比如有些是针对Octal SPI Flash的,有些是针对Quad SPI Flash的。如果你烧录后REPL无响应,可以换一个版本的固件试试。通常官网会标注“for boards with Octal SPI”之类的说明。
4.2 烧录后的首次启动需要一点耐心
有些板子在烧录完MicroPython后,第一次启动会花比较长的时间,尤其是ESP32-S3,可能需要几秒钟才能初始化完文件系统并启动REPL。如果你烧录完立刻点Thonny的“停止/重启”按钮,可能因为板子还没准备好而连接失败。
正确的做法是:烧录完成后,先拔掉USB线,等两秒再插上。然后打开Thonny,选好端口,点“停止/重启”按钮(那个红色的停止图标)。如果板子正常,Thonny的Shell区域会出现MicroPython的版本信息和“>>>”提示符。如果还是没反应,按一下板子上的EN/RST按钮,再试一次。
如果反复尝试都不行,检查一下Thonny右下角的状态栏,看看它显示的是什么。如果显示“无法连接到COM3”之类的错误,那可能是端口被其他程序占用了。串口是独占资源,同一时间只能有一个程序打开它。如果你同时开着Arduino IDE、串口助手或者其他终端工具,先把它们关掉。
4.3 用esptool验证固件是否真的写进去了
Thonny的烧录进度条走完,不代表固件一定完整写入了。有时候USB通信中途出错,Thonny没检测到,但实际写入的数据是残缺的。这时候可以用esptool读一下Flash里的内容,验证固件是否完整:
esptool.py --port COM3 read_flash 0x1000 0x100000 firmware_dump.bin这条命令会把从0x1000开始的1MB内容读出来存成文件。然后用二进制比较工具对比一下读出来的文件和原始固件是否一致。如果一致,说明烧录没问题,问题出在别的地方;如果不一致,重新烧录。
这个方法稍微有点折腾,但当你怀疑烧录过程本身有问题时,它能给你一个确定的答案。我一般是在反复烧录失败、怀疑人生的时候用这招来定位问题。
5. 错误四:自动下载模式不生效,手动复位也进不去
5.1 ESP32的下载模式是怎么触发的
ESP32芯片有一个内置的Bootloader,上电时会检查GPIO0的电平。如果GPIO0为低电平,芯片进入下载模式,等待通过串口接收固件;如果GPIO0为高电平,芯片进入正常运行模式,执行Flash里的程序。
大部分ESP32开发板都设计了自动下载电路,用USB转串口芯片的DTR和RTS信号来控制EN和GPIO0。当Thonny开始烧录时,它会通过串口发送特定的信号序列,触发自动复位并进入下载模式。但这个自动电路有时候不靠谱,尤其是便宜的板子或者USB线质量差的时候。
如果自动下载失败,Thonny会报“Failed to connect to ESP32: Timed out waiting for packet header”之类的错误。这时候就需要手动进入下载模式。
5.2 手动进入下载模式的标准操作
手动操作很简单,但顺序很重要:
- 按住板子上的BOOT按钮(有些板子标的是GPIO0或者IO0)不松手。
- 按一下EN按钮(有些板子标的是RST或者RESET),然后松开EN按钮。
- 保持按住BOOT按钮,回到Thonny点击烧录。
- 看到Thonny开始烧录后,再松开BOOT按钮。
这个顺序的关键是:先按住BOOT,再点EN,然后保持BOOT按住直到烧录开始。很多人搞反了顺序,先点EN再按BOOT,结果芯片已经启动完了才拉低GPIO0,自然进不了下载模式。
提示:有些板子的BOOT按钮标注不明显,可能是一个很小的贴片按键。如果你找不到,可以查一下板子的原理图,确认GPIO0对应的按键位置。
5.3 自动复位电路失效的长期解决方案
如果你每次烧录都要手动按按钮,那太麻烦了。自动复位电路失效通常有两个原因:一是USB转串口芯片的DTR/RTS信号没有正确连接到EN和GPIO0;二是板子上的电容或电阻参数不对,导致复位时序不满足要求。
对于第一种情况,检查板子上CP2102或CH340的DTR、RTS引脚是否通过电阻连接到了EN和GPIO0。有些廉价板子为了省成本,省略了这两个电阻,导致自动复位完全不可用。这种板子只能手动按按钮,没有太好的办法。
对于第二种情况,可以尝试在EN引脚和地之间加一个0.1uF的电容,改善复位时序。但这个需要一定的焊接功底,新手不建议折腾。更实际的做法是换一块质量好一点的开发板,比如官方推荐的ESP32-DevKitC或者带有完整自动下载电路的第三方板子。
我个人的经验是:如果一块板子连续三次自动烧录都失败,而手动模式能成功,那基本可以判定是自动复位电路的问题。这种情况下,要么忍受手动操作,要么换板子。时间比板子值钱。
6. 错误五:烧录后板子无限重启或反复崩溃
6.1 看门狗复位和固件损坏的区分
烧录完MicroPython,板子上的LED闪烁或者串口输出一堆重复的启动信息,这是典型的无限重启现象。原因可能有两个:一是固件本身损坏或不完整,二是看门狗定时器被触发导致复位。
怎么区分?看串口输出的内容。如果输出的是MicroPython的启动信息,但每次都在同一个地方卡住然后重启,那大概率是固件问题。如果输出的是“rst:0x10 (RTCWDT_RTC_RESET)”之类的看门狗复位信息,那可能是代码里有死循环或者阻塞操作。
对于固件问题,最彻底的解决办法是全片擦除后重新烧录。前面提到的erase_flash命令在这里同样适用。擦除后不要急着烧录,先确认板子在没有固件的情况下是否能被esptool正常识别。如果能识别,说明硬件没问题,问题在固件。
6.2 分区表不匹配导致的启动失败
MicroPython固件自带分区表,定义了Flash的各个区域:Bootloader区、分区表区、应用程序区、文件系统区等。如果你之前烧过其他固件,比如ESP-IDF的默认分区表,它和MicroPython的分区表布局可能完全不同。这种情况下,即使MicroPython固件本身是完整的,芯片启动时也会因为找不到正确的分区而失败。
Thonny的烧录向导在烧录MicroPython时,会自动烧写配套的分区表和Bootloader。但前提是你勾选了“擦除Flash”选项。如果不勾选,旧的Bootloader和分区表可能残留,导致启动异常。
所以我的建议很明确:每次烧录MicroPython之前,都勾选“擦除Flash”。这个选项在Thonny的烧录向导里通常是一个复选框,标注为“Erase flash before installing”或者类似文字。多花十几秒擦除,能避免绝大多数启动问题。
6.3 电源不稳导致的随机复位
还有一个容易被忽略的原因:电源供电不足。ESP32在启动瞬间和WiFi工作时电流会突然增大,如果USB口供电能力不足,电压会跌落,导致芯片复位。这种复位是随机的,有时候能正常启动,有时候启动到一半就重启。
判断方法:换一个USB口,或者用带外部供电的USB Hub。如果换了供电之后问题消失,那就是电源问题。另外,如果你在板子上接了外设,比如屏幕、传感器、继电器,这些外设的功耗也要算进去。ESP32的WiFi峰值电流可以到500mA以上,加上外设很容易超过USB口的供电能力。
我实测过一个案例:一块ESP32-S3板子接了一个1.3寸TFT屏幕,用台式机前置USB口供电时频繁重启,换到后置USB口就稳定了。前置USB口经过延长线和Hub之后,线损和接触电阻导致实际到达板子的电压只有4.6V左右,低于ESP32的最低工作电压。这种问题用万用表量一下USB口的输出电压就能确认。
7. 几个能帮你省下大量时间的实操习惯
7.1 固定一套“烧录专用”的硬件组合
我强烈建议你固定一套烧录用的硬件:一根确定能传数据的USB线、一个确定供电充足的USB口、一块确定自动复位正常的板子。把这套组合固定下来,不要每次随手抓一根线就用。这样当你遇到问题时,可以快速排除线缆和供电的因素,把精力集中在固件和配置上。
具体做法:找一根质量好的短线(长度不超过50cm),用标签纸写上“ESP32调试专用”,跟板子放在一起。USB口固定用主板后置的那个,不要用前置或者Hub。这些细节看起来不起眼,但能帮你省下大量“怎么又不行了”的排查时间。
7.2 养成烧录前先擦除的习惯
前面反复提到擦除Flash的重要性,这里再强调一次。Thonny的烧录向导里,擦除选项有时候藏得比较深,需要展开高级选项才能看到。我的做法是:不管三七二十一,每次烧录前都先手动执行一次erase_flash,然后再用Thonny烧录。这样虽然多了一步,但能保证每次烧录的环境是干净的。
擦除命令很简单:
esptool.py --port COM3 erase_flash执行时间大概几秒钟,输出“Chip erase completed successfully”就说明擦除完成了。然后打开Thonny烧录,基本不会遇到分区表冲突的问题。
7.3 保留一份“已知可用”的固件备份
MicroPython的固件版本更新比较频繁,有时候新版本反而没有旧版本稳定。我建议你在第一次成功烧录并验证功能正常后,把那个固件文件备份到一个专门的文件夹里,命名带上日期和芯片型号,比如“esp32s3-micropython-20240601.bin”。以后如果新版本出问题,可以快速回退到已知可用的版本。
另外,Thonny的烧录向导里选择的固件文件路径会被记住,但如果你换了电脑或者重装了系统,这个记录就丢了。所以固件文件本身要存好,不要只依赖Thonny的记忆。
7.4 串口占用问题的快速排查
Thonny连不上板子,很多时候是因为串口被其他程序占用了。常见的“串口杀手”包括:Arduino IDE的串口监视器、各种串口调试助手、Putty、甚至另一个Thonny窗口。如果你在Windows上,可以通过设备管理器查看串口是否被占用,但更简单的方法是:关掉所有可能用到串口的程序,然后重新插拔板子。
在Linux或macOS上,可以用lsof命令查看哪个进程占用了串口:
lsof /dev/ttyUSB0把/dev/ttyUSB0换成你的实际设备名。如果有输出,说明有进程正在使用这个串口,根据输出的PID找到对应程序并关闭它。
8. 关于ESP32-S3和C3的额外注意事项
8.1 USB原生接口和UART接口的区别
ESP32-S3和ESP32-C3有些板子带有两个USB接口:一个是原生USB接口(直接连到芯片的GPIO19和GPIO20),一个是UART接口(通过CP2102或CH340转接)。这两个接口在Thonny里的表现不一样。
原生USB接口在烧录时通常更稳定,因为它不经过额外的USB转串口芯片。但原生USB接口需要芯片内部有正确的USB固件支持,如果芯片是空的或者固件损坏,原生USB接口可能无法被识别。这时候需要用UART接口来烧录。
我的建议是:如果板子同时有两个接口,优先用UART接口烧录,因为它的兼容性更好。烧录完成后,可以用原生USB接口来做REPL交互,速度更快。
8.2 S3和C3的Flash模式选择
ESP32-S3支持Quad SPI和Octal SPI两种Flash模式,对应的MicroPython固件也不同。如果你烧录后板子无法启动,可以尝试换另一种Flash模式的固件。Thonny的烧录向导里通常不会区分这个,需要你自己去MicroPython官网下载对应版本。
怎么知道板子用的是哪种Flash?看板子上的Flash芯片型号,或者查板子的原理图。一般来说,带有“N8R8”或“N16R8”字样的模组用的是Octal SPI Flash,需要下载对应的固件版本。
8.3 C3的USB Serial/JTAG功能
ESP32-C3有一个内置的USB Serial/JTAG控制器,可以通过USB接口直接烧录和调试,不需要额外的USB转串口芯片。但这个功能需要芯片内部有正确的固件支持。如果芯片是全新的或者固件被擦除了,USB Serial/JTAG可能无法工作,需要用UART接口来烧录。
Thonny在连接C3的USB Serial/JTAG时,端口名称通常显示为“USB JTAG/serial debug unit”之类的文字。如果看不到这个端口,检查一下板子的USB接口是否连接正确,以及芯片是否处于下载模式。
9. 当所有方法都失效时的终极排查流程
如果你按照上面的步骤都试过了,板子还是无法烧录,那可能需要更系统地排查。我整理了一个从外到内的排查流程,按顺序执行,基本能定位到问题所在:
| 步骤 | 检查项 | 操作方法 | 预期结果 |
|---|---|---|---|
| 1 | USB线和接口 | 换线、换USB口 | 设备管理器出现COM口 |
| 2 | 驱动 | 重装CP210x/CH340驱动 | 设备管理器无感叹号 |
| 3 | 芯片型号 | esptool chip_id | 输出正确的芯片型号 |
| 4 | Flash容量 | esptool flash_id | 输出正确的Flash大小 |
| 5 | 全片擦除 | esptool erase_flash | 擦除成功 |
| 6 | 固件匹配 | 核对固件型号和芯片型号 | 型号一致 |
| 7 | 手动下载模式 | 按住BOOT+点EN | 烧录开始 |
| 8 | 供电 | 换USB口或外供电 | 电压稳定在5V |
| 9 | 换板子 | 用另一块已知正常的板子 | 确认是板子问题还是环境问题 |
这个流程的核心逻辑是:先排除外部因素(线、驱动、供电),再确认芯片信息,然后确保烧录环境干净,最后才怀疑硬件本身。大部分问题在前三步就能解决,真正遇到硬件损坏的概率其实很低。
我遇到过一次怎么都烧不进去的情况,最后发现是板子上的Flash芯片虚焊了。这种属于硬件故障,只能换板子或者重新焊接。但这种情况非常少见,绝大多数烧录失败都是配置或环境问题。
10. 个人体会:烧录这件事,慢就是快
折腾ESP32和Thonny这些年,我最大的体会是:烧录这件事上,越想快越容易出错。急着点烧录、跳过擦除、用最高的波特率、随手抓一根线,这些“求快”的操作最后都会以失败告终,然后花更多时间排查。
反过来,每次烧录前花十几秒擦除Flash,用115200的波特率慢慢烧,确认线缆和供电没问题,看起来慢,但一次成功的概率极高。算总账的话,这种“慢”才是真正的快。
另外,遇到问题不要慌,按顺序排查。先看设备管理器,再看esptool的输出,然后检查固件型号和Flash容量。大部分错误信息其实已经告诉了你问题在哪里,只是新手容易被那一长串英文吓到。把错误信息复制出来搜一下,通常都能找到答案。
最后分享一个小技巧:如果你经常需要在多块ESP32板子之间切换,可以给每块板子贴一个标签,写上芯片型号和Flash容量。这样每次烧录时不用再去查,直接看标签就知道该选哪个固件、哪个Flash大小。这个习惯帮我省下了不少翻资料的时间。