先把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/自动软重置
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配置
确认端口与备份后,首次安装才擦除和写入
在独立目录用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
用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极性与电气测试条件
完整程序:纯逻辑和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
先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的行为要另行区分。
- REPL保存版本与1+1结果,输入电平1/0各实读。
- 临时run做30次约0.2秒短按,每次释放至少0.2秒;每次只计一次,保持2秒不连发。
- 持久化后记录RESET与冷启动标记B,计数重置为0;上电按住先释放再按,应只计新的一次。
- 填写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
哪些已实际执行,哪些仍需拿到板再验收
本轮复用官方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
先救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
资料来源与版本说明
实读v1.29.0 Pin/time/启动链/mpremote及ESP32端口源码;官方合并镜像SHA与结构已检查,mpy-cross不是本站重建ESP32固件。资料核对日期:。资料核对、程序构建与实板测试分别记录,不互相替代。
- 官方ESP32-S3下载:Octal变体、完整.bin与地址0
- MicroPython v1.29.0正式发行
- v1.29.0启动链:raw软重置跳过main.py、Ctrl-C恢复
- v1.29.0 mpremote:run/fs/repl/自动软重置
- v1.29.0 machine.Pin:输入/上拉/初始输出
- v1.29.0 time:ticks_ms/ticks_diff/sleep_ms
- v1.29.0 ESP32 main.c:friendly启动与main.py
- v1.29.0 S3固定板变体与SDK配置
- DevKitC-1 v1.1固定板级资料:GPIO4/5与桥UART0
- Kingbright WP7113ID Ver14A极性与电气测试条件