认识板子与开发环境 / Python上板发布 内容更新

用 MicroPython 玩 ESP32:刷固件、进 REPL、读一个按钮

固定MicroPython v1.29.0的S3 Octal固件,分清REPL、临时run和持久main.py;完整去抖按键例程、恢复步骤与同源Unix逻辑检查,实板明确待测。

读完能做什么
获得可下载的Python按钮程序,按REPL→临时执行→main.py持久化→RESET/冷启动的顺序完成自己的上板记录。
开始之前
理解GPIO、电源/共地/LED限流,能辨认S3 v1.1板与桥USB;具备基本Python语法。
所需器材
官方DevKitC-1 v1.1 / ESP32-S3-WROOM-1-N8R8、USB数据线、常开按钮、WP7113ID红LED、330Ω±5%0.25W电阻、面包板短线、电脑Python3.12.13、mpremote1.29.0、esptool5.3.0
版本组合
固件:MicroPython v1.29.0 / 2026-08-24 / ESP32_GENERIC_S3 SPIRAM_OCT官方镜像bootloader:IDF5.5.2 / DIO80MHz / 4MB header;保留原镜像参数电脑工具:Python3.12.13 / mpremote1.29.0 / esptool5.3.0逻辑检查:v1.29.0 mpy-cross mpy6.3 / Unix MicroPython同源helper
适用范围

固定S3 N8R8与官方SPIRAM_OCT v1.29.0镜像,GPIO5输入/GPIO4外接LED。源码和软件检查已完成;无板,未刷写、REPL或上电运行。

这篇验证到了哪一步

资料核对已通过
docs/esp32-evidence/micropython.md;固定官方源码/固件/工具核查版本:MicroPython v1.29.0验证日期:
构建 / 配置已通过
docs/esp32-evidence/micropython-builds.json;3份.py实际编译Exit0对象:Python字节码编译;官方S3镜像结构检查,非本站重建ESP32固件版本:mpy-cross v1.29.0 / official image-info esptool5.3.0验证日期:
仿真已通过
docs/esp32-evidence/logs/f09-unix-fixture.log;5组同源纯逻辑fixture,不模拟GPIO/ESP32/电气版本:MicroPython Unix v1.29.0验证日期:
本站实板待验证
没有ESP设备;未擦除/刷写/REPL/文件上传/RESET或冷启动按钮检查

构建通过只说明指定环境中的代码或配置通过检查;实物功能、功耗和稳定性以对应的实板记录为准。

01 / Python上板

先把Python程序运行在板上,再检查能否上电自启

在REPL输入1+1得到2,是解释器的一次交互;把main.py临时run成功,是一次RAM执行;把它写入板上文件系统并按RESET/重新上电后看到新标记,才证明持久程序被启动。这三件事要分别留下记录。

本篇提供完整GPIO5按键、GPIO4提示灯程序:稳定30ms才接受按下,一次按压计一次并切灯,保持按住不连发,上电按住需先释放。本站没有开发板,下面硬件步骤是完整待执行方法;已完成的是官方镜像检查、源码字节码编译和同源纯逻辑fixture。

做到这里,你应该看到
验收记录会分别列REPL、run、fs cp、RESET与断电再上电,不用一次终端输出代替全部。

参考:[3] v1.29.0启动链:raw软重置跳过main.py、Ctrl-C恢复[4] v1.29.0 mpremote:run/fs/repl/自动软重置

02 / Python上板

N8R8选Octal版,保留官方合并镜像

固定官方DevKitC-1 v1.1 WROOM-1-N8R8,固件用MicroPython v1.29.0(2026-08-24)的ESP32_GENERIC_S3 SPIRAM_OCT。官方S3下载页说明Octal PSRAM要选spiram-oct变体;在对应Releases区取完整.bin,不取preview、.app-bin或.uf2。版本锁保存完整URL、文件名、大小和SHA。

本轮实际下载并核对官方合并镜像1786704字节、SHA256=ab24eadfe3ef0e6ee38834d730e648def7cd82f3fa51ee0bbc59c29a6e1bd176;esptool5.3.0 image-info检查成功,S3 bootloader头显示DIO80MHz、4MB和IDF5.5.2。固定应用源配置另启用QIO80MHz与Octal PSRAM,启动层不同,不能把header读成整个应用只按DIO运行。

物理8MB Flash也不等于这份固件的可用文件系统空间。这里保留官方镜像参数与分区,从地址0写完整合并.bin,不自行强改8MB header,也不沿用经典ESP32常见0x1000起点。官方固件从官方URL获取,本站下载包只放本教程源码与锁。

代码 / 配置
ESP32_GENERIC_S3-SPIRAM_OCT-20260824-v1.29.0.bin
https://micropython.org/resources/firmware/ESP32_GENERIC_S3-SPIRAM_OCT-20260824-v1.29.0.bin

参考:[1] 官方ESP32-S3下载:Octal变体、完整.bin与地址0[2] MicroPython v1.29.0正式发行[8] v1.29.0 S3固定板变体与SDK配置

03 / Python上板

确认端口与备份后,首次安装才擦除和写入

在独立目录用Python3.12.13建立venv,requirements固定mpremote1.29.0/esptool5.3.0;Windows改用.venv\Scripts内可执行文件。connect list后按插拔辨认板载USB-to-UART Bridge端口,蓝牙端口不能作为S3已连接证据。先只接板与USB,关闭占用串口的终端。

首次安装会替换原Arduino/ESPHome等固件;erase-flash清掉旧程序、文件与配置。先保留原工程、依赖和私有凭据;需要完整回退时,对已经确认物理8MB的本板先read-flash 0 0x800000保存私有备份。完整命令在README,不能把备份里的网络信息提交到公开仓库。

确认备份后按下方首次安装命令操作,PORT换真实桥端口。自动下载超时可按住BOOT、短按RESET、释放BOOT后重试;写完松开BOOT再RESET。115200是起步速度,未声称本站已刷写。无需修改eFuse或安全启动。

代码 / 配置
python3.12 -m venv .venv
.venv/bin/python -m pip install -r requirements.txt
.venv/bin/mpremote connect list
# 以下待实板执行;先按README备份,再明确选择目标PORT
.venv/bin/esptool --chip esp32s3 --port PORT --baud 115200 erase-flash
.venv/bin/esptool --chip esp32s3 --port PORT --baud 115200 write-flash --flash-mode keep --flash-freq keep --flash-size keep 0 ESP32_GENERIC_S3-SPIRAM_OCT-20260824-v1.29.0.bin
做到这里,你应该看到
先保存原项目/必要Flash备份,再记录镜像SHA、地址0和实际刷写结果;无板时不勾通过。

参考:[1] 官方ESP32-S3下载:Octal变体、完整.bin与地址0[4] v1.29.0 mpremote:run/fs/repl/自动软重置[9] DevKitC-1 v1.1固定板级资料:GPIO4/5与桥UART0

04 / Python上板

用REPL先辨认版本,再读按钮的1和0

执行mpremote connect PORT repl,桥UART0默认115200/8N1。出现>>>后查看sys.implementation与os.uname,确认板端v1.29.0和S3 Octal目标。Ctrl-]退出电脑终端;运行中的程序可用Ctrl-C回REPL,友好REPL内Ctrl-D软重置。S3桥接串口脚为GPIO43/44,不照抄经典ESP32的1/3。

断电接GPIO5/J1-5→常开按钮→GND,内部上拉,松开读1、按下读0。GPIO4/J1-4经330Ω±5%0.25W到WP7113ID阳极,阴极回地;板载GPIO38可寻址RGB不参与。F03原1kΩ可保留但更暗;厂家1.9V典型压降是在10mA条件,不能当本文实测值。

先用下方Pin读数确认接线,再进入完整程序。value()只给此刻电平,不做去抖;因此反复读取的0/1变化不是可靠机械按压次数。四脚按钮先断电用通断档确认两组。

代码 / 配置
import sys, os
print(sys.implementation)
print(os.uname())
print(1 + 1)
from machine import Pin
b = Pin(5, Pin.IN, Pin.PULL_UP)
print(b.value())  # 松开1;按住0,分别实际读取
做到这里,你应该看到
保存板端版本与松开/按下读数;不是把Unix解释器输出充当REPL结果。

参考:[4] v1.29.0 mpremote:run/fs/repl/自动软重置[5] v1.29.0 machine.Pin:输入/上拉/初始输出[9] DevKitC-1 v1.1固定板级资料:GPIO4/5与桥UART0[10] Kingbright WP7113ID Ver14A极性与电气测试条件

05 / Python上板

完整程序:纯逻辑和GPIO入口分开,但只维护一份规则

button_logic.py只接收按下布尔值和时间,连续稳定30ms才接受状态改变。首次已按住时armed关闭,稳定释放后才允许下一次新按;正常按下只返回一次True。主程序用time.ticks_diff处理回绕,sleep_ms(5)让出执行机会,不把轮询当成硬实时保证。

main.py先将GPIO4初始化LOW,再导入helper,缺文件时灯仍先保持关。每次有效按下递增RAM计数并切灯;Ctrl-C后回REPL,finally再次关灯。它没有自动重启错误循环,便于新手保留异常行号并修复;日志led_latch是GPIO写入状态,不能代替观察LED。

代码 / 配置
# ---- button_logic.py ----
# SPDX-License-Identifier: MIT
"""Pure logic used unchanged on ESP32 and in the Unix MicroPython fixture."""
class DebouncedButton:
    def __init__(self, held, now, ticks_diff, debounce_ms=30):
        self.raw = bool(held)
        self.stable = bool(held)
        self.armed = not held
        self.changed = now
        self.ticks_diff = ticks_diff
        self.debounce_ms = debounce_ms

    def update(self, held, now):
        held = bool(held)
        if held != self.raw:
            self.raw = held
            self.changed = now
        if self.raw != self.stable and self.ticks_diff(now, self.changed) >= self.debounce_ms:
            self.stable = self.raw
            if not self.stable:
                self.armed = True
            elif self.armed:
                self.armed = False
                return True
        return False

# ---- main.py ----
# SPDX-License-Identifier: MIT
# MicroPython v1.29.0 / ESP32_GENERIC_S3 SPIRAM_OCT / DevKitC-1 v1.1 N8R8.
from machine import Pin
import time

# Initialise the LED before importing our helper, so a missing file leaves it off.
led = Pin(4, Pin.OUT, value=0)
from button_logic import DebouncedButton

APP_MARKER = "F09-A"

def run():
    button = Pin(5, Pin.IN, Pin.PULL_UP)
    logic = DebouncedButton(button.value() == 0, time.ticks_ms(), time.ticks_diff)
    count = 0
    latch = 0
    print(APP_MARKER, "START GPIO5 button / GPIO4 LED; count=0; Ctrl-C stops")
    try:
        while True:
            if logic.update(button.value() == 0, time.ticks_ms()):
                count += 1
                latch = 1 - latch
                led.value(latch)
                print(APP_MARKER, "press_count=", count, "led_latch=", latch)
            time.sleep_ms(5)
    except KeyboardInterrupt:
        print(APP_MARKER, "KeyboardInterrupt; return to REPL")
    finally:
        led.value(0)
        print(APP_MARKER, "STOP led_latch=0")

if __name__ == "__main__":
    run()

参考:[5] v1.29.0 machine.Pin:输入/上拉/初始输出[6] v1.29.0 time:ticks_ms/ticks_diff/sleep_ms[7] v1.29.0 ESP32 main.c:friendly启动与main.py

06 / Python上板

先run临时试验,再fs cp并用RESET验收

查看板文件并备份已有main.py/boot.py,避免覆盖旧脚本。本例不写boot.py。先将button_logic.py复制到板上,再run主程序;run只发主文件内容到RAM,helper需要事先存好,板上原main.py仍不被替换。

mpremote的exec/fs/run通常会先停止原程序并在raw REPL软重置,本次跳过板上main.py;resume只是禁用自动软重置,并不是“运行main.py”命令。因此刚fs cp完没有看到START,不应立刻判断上传失败。

临时运行确认后,把main.py写到根目录,fs cat核对内容,进入repl并按物理RESET看F09-A START。再将APP_MARKER改F09-B,重新复制并RESET确认B,最后拔USB再上电确认B与计数从零开始。普通友好REPL的Ctrl-D也走正常启动;mpremote raw软重置跳过main的行为要另行区分。

  1. REPL保存版本与1+1结果,输入电平1/0各实读。
  2. 临时run做30次约0.2秒短按,每次释放至少0.2秒;每次只计一次,保持2秒不连发。
  3. 持久化后记录RESET与冷启动标记B,计数重置为0;上电按住先释放再按,应只计新的一次。
  4. 填写observations.csv的真实observed/pass/日志路径,不复制预期当结果。
代码 / 配置
.venv/bin/mpremote connect PORT fs ls :
# 若已有main.py先按README备份
.venv/bin/mpremote connect PORT fs cp button_logic.py :button_logic.py
.venv/bin/mpremote connect PORT run main.py
# 结束临时运行并关闭终端后:
.venv/bin/mpremote connect PORT fs cp main.py :main.py
.venv/bin/mpremote connect PORT fs cat :main.py
.venv/bin/mpremote connect PORT repl
# 此终端保持连接,按板RESET;再单独做断电重启
做到这里,你应该看到
文件持久化、RESET执行和冷启动执行各有独立记录;本站这些格仍待测。

参考:[3] v1.29.0启动链:raw软重置跳过main.py、Ctrl-C恢复[4] v1.29.0 mpremote:run/fs/repl/自动软重置[7] v1.29.0 ESP32 main.c:friendly启动与main.py

07 / Python上板

哪些已实际执行,哪些仍需拿到板再验收

本轮复用官方v1.29.0源码构建的mpy-cross,三份.py实际编译为mpy6.3字节码均Exit0;产物只放外部验证目录。官方S3.bin已SHA与image-info检查,但没有在本站重新编译那份ESP32固件,字节码编译也不会验证GPIO电气。

同一v1.29.0 Unix MicroPython实际运行logic_fixture.py,直接import固件同一button_logic.py,5组通过:30次抖动一次计数、保持不连发、上电按住、合成环形时钟回绕与30ms边界、短噪声拒绝。合成16位时钟是ticks_diff契约检查,非ESP32真实ticks周期;Unix端口不是S3仿真器。

电脑当前端口列表没有ESP设备,本站没有擦除、刷写、REPL、文件上传或上电按钮记录。构建与逻辑检查分开记在证据;完整板端步骤仍可继续执行,缺少实板不以假日志填空。

做到这里,你应该看到
mpy-cross与Unix原日志、源SHA可复核;硬件维度保持pending。

参考:[2] MicroPython v1.29.0正式发行[5] v1.29.0 machine.Pin:输入/上拉/初始输出[6] v1.29.0 time:ticks_ms/ticks_diff/sleep_ms[7] v1.29.0 ESP32 main.c:friendly启动与main.py

08 / Python上板

先救Python文件,再决定是否重刷固件

ImportError先fs ls并补传button_logic.py;SyntaxError保存行号、回未改版本再上传。计数异常先回REPL读1/0,查按钮两组/地线,再查去抖;增加sleep不能修反接。没有>>>先核桥接口、115200与BOOT释放,再RESET。

循环不退出时mpremote connect PORT repl,再Ctrl-C回>>>,先备份错误main.py后用os.rename改为main-disabled.py;普通Ctrl-D或RESET后应停在REPL。也可退出终端后fs cp备份,再fs rm :main.py移除已备份的启动文件。保留helper无碍,不先擦掉整个文件系统。

若改坏REPL接口或早期启动而始终无法中断,才回ROM下载模式按备份重新部署固件;首次擦除会丢文件。恢复原Arduino/ESPHome工程用原源码和精确环境烧录;整片备份只回同一板与同一容量。接下来按模块核对清单扩展外设,维持每个器件的电平、协议和版本记录。

代码 / 配置
import os
os.rename('main.py', 'main-disabled.py')
# 在REPL先备份,再改名;普通Ctrl-D/RESET后观察是否回到提示符

参考:[3] v1.29.0启动链:raw软重置跳过main.py、Ctrl-C恢复[4] v1.29.0 mpremote:run/fs/repl/自动软重置[9] DevKitC-1 v1.1固定板级资料:GPIO4/5与桥UART0

BACK TO THE SOURCE

资料来源与版本说明

实读v1.29.0 Pin/time/启动链/mpremote及ESP32端口源码;官方合并镜像SHA与结构已检查,mpy-cross不是本站重建ESP32固件。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。

  1. 官方ESP32-S3下载:Octal变体、完整.bin与地址0
  2. MicroPython v1.29.0正式发行
  3. v1.29.0启动链:raw软重置跳过main.py、Ctrl-C恢复
  4. v1.29.0 mpremote:run/fs/repl/自动软重置
  5. v1.29.0 machine.Pin:输入/上拉/初始输出
  6. v1.29.0 time:ticks_ms/ticks_diff/sleep_ms
  7. v1.29.0 ESP32 main.c:friendly启动与main.py
  8. v1.29.0 S3固定板变体与SDK配置
  9. DevKitC-1 v1.1固定板级资料:GPIO4/5与桥UART0
  10. Kingbright WP7113ID Ver14A极性与电气测试条件