ESP32-S3 MicroPython 全栈实战指南:刷机 · 点灯 · Wi-Fi 控制 · OTA 远程升级

从零到量产:MicroPython 开发环境、ESP32-S3 刷机、LED 控制、Wi-Fi 网页控制、OTA 无线烧录、MQTT 上云与一键远程升级全流程。
适用平台:Windows / macOS / Linux(命令以 Windows COMx 为例,macOS/Linux 为 /dev/tty*)

目录

  1. 硬件与软件准备
  2. 安装串口驱动
  3. 刷入 MicroPython 固件(刷机全流程)
  4. 开发工具:Thonny 与命令行
  5. 编写代码并烧录运行
  6. 实战:LED 控制
  7. 常见问题排查
  8. 常用命令速查表
  9. Wi-Fi 联网 + Web 控制 LED
  10. OTA 无线烧录
  11. MQTT 上云 + 手机 App 控灯
  12. OTA 自动 + 云端版本检查的一键升级流水线

1. 硬件与软件准备

硬件清单

物品说明
ESP32-S3 开发板如 ESP32-S3-DevKitC-1、ESP32-S3-WROOM-1 等
USB 数据线必须能传数据(很多充电线只能供电)
电脑Windows 10+/macOS/Linux
外接 LED + 220Ω 电阻(可选)用于外部 LED 实验
面包板 + 杜邦线(可选)外接电路用

软件清单

软件用途下载地址
Python 3.8+运行 esptool / 工具https://www.python.org/downloads/
esptool官方刷机工具pip install esptool
Thonny IDE图形化开发/烧录(推荐新手)https://thonny.org/
mpremote / ampy命令行文件管理pip install mpremote

2. 安装串口驱动

ESP32-S3 自带 USB-CDC,很多板子插上就能识别。若设备管理器出现黄色感叹号,安装对应驱动:

安装后,插入开发板,在「设备管理器 → 端口(COM 和 LPT)」中确认出现的端口号,例如 COM3。

macOS/Linux 用户用 ls /dev/tty.*(macOS)或 ls /dev/ttyUSB* /dev/ttyACM*(Linux)查看。

3. 刷入 MicroPython 固件(刷机全流程)

步骤 3.1 下载固件

前往官方下载页:https://micropython.org/download/ESP32_S3/

将固件保存到便于访问的目录,例如 C:\esp\firmware.bin。

步骤 3.2 让开发板进入下载模式

ESP32-S3 进入烧录模式的方式:

  1. 按住 BOOT 键不放;
  2. 按一下 RESET/EN 键;
  3. 松开 BOOT 键。
部分板子(带原生 USB-CDC)在断电再插电时会自动进入下载模式,无需按 BOOT。

步骤 3.3 擦除 Flash(首次必做)

# Windows(将 COM3 换成你的端口)
esptool.py --chip esp32s3 --port COM3 erase_flash

# macOS / Linux
esptool.py --chip esp32s3 --port /dev/ttyACM0 erase_flash

看到 Chip erase completed 即成功。

步骤 3.4 烧录固件

# Windows
esptool.py --chip esp32s3 --port COM3 --baud 460800 write_flash -z 0x0 C:\esp\firmware.bin

# macOS / Linux
esptool.py --chip esp32s3 --port /dev/ttyACM0 --baud 460800 write_flash -z 0x0 ./firmware.bin

参数说明:

看到 Hash of data verified / Leaving... 表示烧录完成。

步骤 3.5 验证

  1. 按下 RESET/EN 键重启开发板;
  2. 打开串口工具(Thonny / 串口助手),波特率 115200;
  3. 应看到类似提示符:

```

MicroPython v1.23.0 on 2024-06-02; ESP32-S3 module with ESP32S3

Type "help()" for more information.

>>>

```

出现 >>> 即代表 MicroPython 运行成功!

4. 开发工具:Thonny 与命令行

4.1 Thonny(推荐新手)

  1. 打开 Thonny → 运行 → 选择解释器;
  2. 解释器选 MicroPython (ESP32),端口选你的 COMx;
  3. 下方 Shell 出现 >>> 即可交互;
  4. 写代码后在编辑器按 Ctrl+S,选择 保存到 MicroPython 设备,文件名建议用 main.py(开机自启)。

4.2 命令行工具 mpremote(推荐进阶)

# 进入 REPL 交互
mpremote connect COM3 repl

# 运行本地脚本
mpremote connect COM3 run blink.py

# 把文件传到板子
mpremote connect COM3 fs cp blink.py :blink.py

# 执行板子上的 main.py
mpremote connect COM3 run :main.py

# 软重启
mpremote connect COM3 reset

5. 编写代码并烧录运行

以最简单的「点灯」为例。在电脑上新建 blink.py:

import machine
import time

led = machine.Pin(48, machine.Pin.OUT)  # 板载 LED(见注释)

while True:
    led.value(1)      # 点亮
    time.sleep(0.5)
    led.value(0)      # 熄灭
    time.sleep(0.5)

运行方式 A(Thonny):Ctrl+S → 保存到 MicroPython 设备 → 命名 main.py → 板子复位即自动运行。

运行方式 B(命令行):

mpremote connect COM3 fs cp blink.py :main.py
mpremote connect COM3 reset
⚠️ 关于 GPIO 48:多数 ESP32-S3-DevKitC 板载 LED 在 GPIO 48。但不同板子引脚不同(有的用 GPIO 2 / 13 / 21)。请查阅你的板子丝印或原理图。外接 LED 见第 6 节。

6. 实战:LED 控制

6.1 板载 LED 闪烁

见第 5 节 blink.py。把 48 换成你板子的实际引脚即可。

6.2 外接 LED(最通用)

接线(低电平驱动,LED 长脚接 GPIO,短脚串 220Ω 电阻接地):

import machine
import time

led = machine.Pin(21, machine.Pin.OUT)

print("外置 LED 开始闪烁")
while True:
    led.on()          # 等价于 led.value(1)
    time.sleep(1)
    led.off()         # 等价于 led.value(0)
    time.sleep(1)

6.3 PWM 调光(呼吸灯效果)

用 machine.PWM 调节亮度:

import machine
import time

led = machine.PWM(machine.Pin(21))   # 外接 LED 接 GPIO21
led.freq(1000)                       # PWM 频率 1kHz

def breathe():
    # 从暗到亮
    for duty in range(0, 1024, 8):
        led.duty(duty)
        time.sleep_ms(5)
    # 从亮到暗
    for duty in range(1023, -1, -8):
        led.duty(duty)
        time.sleep_ms(5)

while True:
    breathe()
注:ESP32 的 duty 范围在 MicroPython 中默认是 0–1023(10 位)。

6.4 板载 RGB 彩灯(WS2812 / NeoPixel,常见 S3 板载)

很多 ESP32-S3 开发板(如 DevKitC-1 的某些版本、ESP32-S3-USB-OTG)板载一颗 WS2812 RGB LED,连接在 GPIO 48:

import machine
import neopixel
import time

PIN = 48          # 板载 RGB LED 引脚
N = 1             # LED 数量
np = neopixel.NeoPixel(machine.Pin(PIN), N)

def color(r, g, b):
    np[0] = (r, g, b)
    np.write()

while True:
    color(255, 0, 0)    # 红
    time.sleep(1)
    color(0, 255, 0)    # 绿
    time.sleep(1)
    color(0, 0, 255)    # 蓝
    time.sleep(1)
    color(0, 0, 0)      # 灭
    time.sleep(1)
若你的板载 RGB 不在 GPIO 48,请查原理图替换 PIN。neopixel 是 MicroPython 内置模块,无需额外安装。

6.5 用按键控制 LED(交互进阶)

外接一个按钮到 GPIO 0(S3 的 BOOT 键通常就在 GPIO 0,可直接复用):

import machine
import time

led = machine.Pin(21, machine.Pin.OUT)
button = machine.Pin(0, machine.Pin.IN, machine.Pin.PULL_UP)  # BOOT 键,按下为 0
state = 0

while True:
    if button.value() == 0:          # 按键按下
        state = 1 - state             # 翻转状态
        led.value(state)
        time.sleep(0.3)               # 简单防抖

7. 常见问题排查

现象原因 / 解决
A fatal error occurred: Failed to connect未进入下载模式 → 重做第 3.2 步(按住 BOOT 再 RESET)
PermissionError: [Errno 13] (Linux)串口权限不足 → sudo usermod -a -G dialout $USER 后重登
烧录后无 >>> 输出波特率不对 / 固件地址错 → 确认 0x0;用 115200 重新连
LED 不亮引脚号错 → 查板子丝印;外接 LED 注意极性、电阻
ImportError: no module named 'neopixel'用了普通 GPIO 教程却跑 RGB → 确认板载是 WS2812 且引脚正确
文件保存后不自动运行文件名必须是 main.py 才会开机自启

8. 常用命令速查表

# 擦除
esptool.py --chip esp32s3 --port COM3 erase_flash

# 烧录
esptool.py --chip esp32s3 --port COM3 --baud 460800 write_flash -z 0x0 firmware.bin

# 查看端口(Windows PowerShell)
[System.IO.Ports.SerialPort]::getportnames()

# mpremote
mpremote connect COM3 repl                 # REPL
mpremote connect COM3 fs cp main.py :main.py  # 上传
mpremote connect COM3 run :main.py        # 运行
mpremote connect COM3 reset               # 重启

# 查看板子信息(REPL 内)
import sys; print(sys.implementation)      # 版本
import machine; print(machine.freq())      # CPU 频率

9. Wi-Fi 联网 + Web 控制 LED

让 ESP32-S3 连上 Wi-Fi,并起一个网页服务器,用浏览器点按钮就能远程开关 LED。无需插线,局域网内任意设备可访问。

9.1 连接 Wi-Fi

MicroPython 用 network 模块联网:

import network
import time

SSID = "你的WiFi名"
PASSWORD = "你的WiFi密码"

def connect_wifi():
    wlan = network.WLAN(network.STA_IF)   # 站点模式(连路由器)
    wlan.active(True)
    if not wlan.isconnected():
        print("正在连接", SSID, "...")
        wlan.connect(SSID, PASSWORD)
        # 等待连接,最多 15 秒
        for _ in range(30):
            if wlan.isconnected():
                break
            time.sleep(0.5)
    if wlan.isconnected():
        print("✅ 已连接,IP =", wlan.ifconfig()[0])
    else:
        print("❌ 连接失败,检查 SSID/密码")
    return wlan

connect_wifi()
wlan.ifconfig() 返回 (IP, 掩码, 网关, DNS)。记下这个 IP,等会儿要用浏览器访问它。
若连接不稳定,可把 wlan.config(pm=...) 关闭省电模式:wlan.config(pm=0xa11140)(关闭 Modem-sleep,提升稳定性)。

9.2 搭建 Web 服务器控制 LED

用标准库 socket 起一个极简 HTTP 服务(无需任何第三方库):

import network
import socket
import machine
import time

SSID = "你的WiFi名"
PASSWORD = "你的WiFi密码"

wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(SSID, PASSWORD)
while not wlan.isconnected():
    time.sleep(0.5)
print("IP:", wlan.ifconfig()[0])

led = machine.Pin(21, machine.Pin.OUT)   # 外接 LED 接 GPIO21

def web_page(state):
    return """<!DOCTYPE html><html><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>ESP32-S3 LED</title>
<style>body{font-family:sans-serif;text-align:center;margin-top:40px}
button{font-size:20px;padding:12px 28px;margin:8px;border-radius:10px;border:none;cursor:pointer}
.on{background:#16a34a;color:#fff}.off{background:#dc2626;color:#fff}</style>
</head><body>
<h1>ESP32-S3 LED 控制台</h1>
<p>当前状态:<b>%s</b></p>
<a href="/?led=on"><button class="on">打开</button></a>
<a href="/?led=off"><button class="off">关闭</button></a>
</body></html>""" % ("ON" if state else "OFF")

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind(('', 80))      # 监听 80 端口
s.listen(5)
print("Web 服务器已启动,浏览器访问 http://", wlan.ifconfig()[0])

while True:
    try:
        conn, addr = s.accept()
        request = conn.recv(1024).decode('utf-8')
        if "led=on" in request:
            led.on()
        elif "led=off" in request:
            led.off()
        state = led.value()
        conn.send("HTTP/1.1 200 OK\r\nContent-Type: text/html; charset=utf-8\r\n\r\n")
        conn.sendall(web_page(state).encode('utf-8'))
        conn.close()
    except Exception as e:
        print("错误:", e)

运行:

  1. 把上面的脚本保存为 main.py 烧录到板子(参见第 5 节);
  2. 板子复位后,串口会打印 IP: 192.168.x.x;
  3. 电脑/手机连同一个 Wi-Fi,浏览器打开 http://192.168.x.x;
  4. 点「打开 / 关闭」即可远程控制 LED ✅。
进阶:把 LED 改成板载 RGB(neopixel,见 6.4),用 ?r=255&g=0&b=0 传颜色参数即可做彩色网页调色盘。

10. OTA 无线烧录

「无线烧录」指不插 USB 线,通过 Wi-Fi 更新板子上的程序(甚至固件)。MicroPython 有三种常见方式:

10.1 WebREPL(官方无线文件传输,最省事)

MicroPython 自带 WebREPL,可在浏览器里无线传文件、进 REPL。

板端启用(首次需在串口 REPL 里做一次):

import webrepl_setup
# 按提示输入:E(启用)→ 设置密码 → 选开机自启(y)→ 重启

重启后 WebREPL 会在后台运行(端口 8266)。

电脑端使用:

  1. 浏览器打开官方客户端:https://micropython.org/webrepl/
  2. 地址填 ws://192.168.x.x:8266(用板子 IP),输入刚才设的密码,点 Connect;
  3. 左下角 Send a file 选 main.py → 无线上传,等价于有线烧录;
  4. 也可在网页底部 REPL 直接敲代码。
注意:WebREPL 与第 9 节的 Web 服务器都占网络资源,调试时二选一即可;正式部署可在 main.py 里用 webrepl.start() 按需开启。

10.2 应用代码 OTA(自己拉取更新,适合量产/远程)

思路:板子联网后向一台 HTTP 服务器请求最新 main.py,下载写入文件系统,再重启生效。

服务器侧:把新版 main.py 放到任意能访问的 HTTP 地址(电脑起个静态服务即可):

# 在放有 main.py 的目录执行(电脑端 Python)
python -m http.server 8000
# 访问 http://电脑IP:8000/main.py 应能下载到文件

板子侧 ota_update():

import urequests
import uos
import machine

def ota_update(url, target="main.py"):
    print("下载:", url)
    r = urequests.get(url)
    if r.status_code != 200:
        print("下载失败,状态码:", r.status_code)
        r.close()
        return False
    tmp = target + ".tmp"
    with open(tmp, "w") as f:
        f.write(r.text)        # 写临时文件,避免下到一半破坏旧程序
    r.close()
    try:
        uos.remove(target)
    except OSError:
        pass
    uos.rename(tmp, target)    # 原子替换
    print("✅ 更新完成,重启中...")
    machine.reset()
    return True

# 用法:连好 Wi-Fi 后调用
# ota_update("http://192.168.1.50:8000/main.py")
建议把版本号写入文件(如 version.txt),更新前比对版本,仅在有新版时才拉取,省流量更稳。

10.3 固件 OTA(整包固件无线升级,需双分区)

如果要无线升级 MicroPython 固件本身(不只是应用代码),需用 ota 模块(来自 micropython-lib),且固件必须是带双 app 分区的构建版本:

import ota
import urequests
import machine

def firmware_ota(url):
    r = urequests.get(url)
    print("写入 OTA 分区...")
    ota.write('ota', r.content)   # 写入备用分区
    r.close()
    ota.switch()                  # 切换启动分区
    print("✅ 固件已更新,重启中...")
    machine.reset()

# firmware_ota("http://server/esp32s3-ota.bin")
⚠️ 前提:固件需编译为 OTA 镜像(含 ota_0 / ota_1 两个分区)。官方「Generic ESP32-S3」标准固件不含双分区,直接 OTA 会失败。需要的话可基于 MicroPython 源码用 make BOARD=... OTA=1 自行构建。普通玩家用 10.1 / 10.2 已完全够用。

11. MQTT 上云 + 手机 App 控灯

Web 控制只能局域网用,要「手机 4G 也能控灯」就得上云。MQTT 是 IoT 标配的轻量发布/订阅协议,配合公共 Broker 或私有云,手机装个 App 即可远程开关。

11.1 安装 MQTT 库

MicroPython 标准镜像不含 MQTT,用 mip(MicroPython 包管理器)装 umqtt.simple:

# 方式 A:mpremote(板子已联网)
mpremote connect COM3 mip install umqtt.simple

# 方式 B:板子 REPL 里直接装
import mip
mip.install("umqtt.simple")
若板子暂时无法联网装包,可在电脑上下载 umqtt/simple.py 后 mpremote fs cp simple.py :umqtt/simple.py 手动传上去。

11.2 先用公共 Broker 跑通

公共测试服务器 broker.emqx.io(无需账号),手机和板子都连它即可互通:

from umqtt.simple import MQTTClient
import network, machine, time

SSID, PASS = "你的WiFi名", "你的WiFi密码"
wlan = network.WLAN(network.STA_IF); wlan.active(True); wlan.connect(SSID, PASS)
while not wlan.isconnected():
    time.sleep(0.5)
print("IP:", wlan.ifconfig()[0])

led = machine.Pin(21, machine.Pin.OUT)

SERVER    = "broker.emqx.io"
CLIENT_ID = "esp32s3_led_01"
TOPIC_SUB = b"home/led/control"     # 订阅:接收开关指令
TOPIC_PUB = b"home/led/state"       # 发布:上报当前状态

def sub_cb(topic, msg):
    print("收到:", topic, msg)
    if msg == b"on":
        led.on()
    elif msg == b"off":
        led.off()
    c.publish(TOPIC_PUB, b"on" if led.value() else b"off")

c = MQTTClient(CLIENT_ID, SERVER)
c.set_callback(sub_cb)
c.connect()
c.subscribe(TOPIC_SUB)
print("MQTT 已连接,等待指令...")

while True:
    c.check_msg()          # 非阻塞检查消息
    time.sleep(0.1)

手机端发布测试:安装 App「MQTT Dashboard」(Android) 或「MQTT Explorer」,连接服务器 broker.emqx.io,向主题 home/led/control 发送 on / off,灯即响应。

11.3 加断线重连(生产必加)

上面示例一旦掉线会卡死,实战包一层重连:

def mqtt_connect():
    c = MQTTClient(CLIENT_ID, SERVER)
    c.set_callback(sub_cb)
    c.connect()
    c.subscribe(TOPIC_SUB)
    return c

c = mqtt_connect()
while True:
    try:
        c.check_msg()
    except OSError:           # 断线
        print("掉线,重连中...")
        try:
            c = mqtt_connect()
        except Exception:
            time.sleep(3)
    time.sleep(0.1)

11.4 手机 App 控灯(三种玩法)

方案做法适合
MQTT Dashboard手机连同一 Broker,按钮绑定 home/led/control 发 on/off最快验证,免开发
Home Assistant板子发布 MQTT 自动发现配置,HA 里出现「灯」卡片,手机 HA App 控想做智能家居全家桶
微信小程序/自建小程序调云函数转发 MQTT想自己产品化

接入 Home Assistant 自动发现(在 connect 后发一条配置即可被 HA 自动识别):

import ujson
discovery = {
    "name": "ESP32-S3 LED",
    "unique_id": "esp32s3_led_01",
    "state_topic": "home/led/state",
    "command_topic": "home/led/control",
    "payload_on": "on", "payload_off": "off"
}
c.publish(b"homeassistant/light/esp32s3_led/config", ujson.dumps(discovery))

HA 侧启用 MQTT 集成后,无需手动配置即可看到这张灯卡片。

11.5 进阶:上云厂商(阿里云 IoT / 腾讯云 IoT)

上云厂商的 MQTT 要求签名鉴权(用户名、密码需按三元组 + 时间戳用 HMAC 计算,且多为 TLS 8883 端口)。要点:

这块签名细节较多,建议直接用各云厂商的 MicroPython 示例工程或 SDK,避免手算出错。若只是想「手机远程控灯」,11.2 的公共 Broker + MQTT Dashboard 已足够,无需上云。

12. OTA 自动 + 云端版本检查的一键升级流水线

把第 10.2 的应用 OTA 升级成工程化流水线:设备开机联网 → 查云端版本清单 → 比对本地版本 → 有新版就自动拉取、校验、替换、重启。配合第 11 节的 MQTT,还能用手机一键触发远程升级。

12.1 整体架构

[电脑 build_ota.py] --打包--> [HTTP 服务器]
                              ├── version.json   (latest / url / size / sha256)
                              └── main_1.2.0.py  (带版本号的程序)
                                       ▲
[ESP32-S3] 启动 → 读本地 version.txt → GET version.json → 比对 → 下载 → 校验 → 覆盖 main.py → 重启
                                       ▲
[手机 MQTT] ---- "upgrade" 指令 -----┘  (可选,触发立即检查)

12.2 云端版本清单与文件命名

服务器上准备一个目录(如 www/firmware/),存放:

version.json 示例:

{
  "latest": "1.2.0",
  "url": "http://192.168.1.50:8000/firmware/main_1.2.0.py",
  "size": 1856,
  "sha256": "a1b2c3...(程序文件的 sha256)"
}

12.3 板端:版本检查 + 安全 OTA

在 main.py 开头调用一次 check_and_update()。带 大小 + sha256 双重校验,下载失败绝不破坏旧程序:

import urequests, uos, machine, uhashlib

CURRENT_VERSION_FILE = "version.txt"
UPDATE_BASE = "http://192.168.1.50:8000/firmware"   # 改成你的服务器

def read_version():
    try:
        with open(CURRENT_VERSION_FILE) as f:
            return f.read().strip()
    except OSError:
        return "0.0.0"

def write_version(v):
    with open(CURRENT_VERSION_FILE, "w") as f:
        f.write(v)

def ver_tuple(v):
    return tuple(int(x) for x in v.split("."))

def check_and_update():
    local = read_version()
    print("当前版本:", local)
    try:
        r = urequests.get(UPDATE_BASE + "/version.json", timeout=8)
        info = r.json()
        r.close()
    except Exception as e:
        print("版本检查失败:", e)
        return False

    latest = info["latest"]
    if ver_tuple(latest) <= ver_tuple(local):
        print("已是最新,无需升级")
        return False

    print("发现新版本:", latest, "开始下载...")
    r = urequests.get(info["url"], timeout=20)
    data = r.content          # bytes
    r.close()

    # 校验大小
    if "size" in info and len(data) != info["size"]:
        print("❌ 大小不符,终止升级")
        return False
    # 校验 sha256
    if "sha256" in info:
        h = uhashlib.sha256(data).hexdigest()
        if h != info["sha256"]:
            print("❌ 校验失败,终止升级")
            return False

    # 原子替换:先写临时文件,成功后再覆盖
    tmp = "main.py.tmp"
    with open(tmp, "wb") as f:
        f.write(data)
    try:
        uos.remove("main.py")
    except OSError:
        pass
    uos.rename(tmp, "main.py")
    write_version(latest)
    print("✅ 升级到", latest, ",重启中...")
    machine.reset()

# 在 main.py 业务代码前调用:
# check_and_update()
防变砖建议:升级前把旧 main.py 备份成 main.py.bak,并在启动早期用 machine.WDT 看门狗——若新程序卡死不喂狗,自动复位回退。真正「变砖」只能串口重刷(见第 3 节),所以生产环境务必保留 WebREPL(10.1)作为兜底通道。

12.4 电脑端:一键打包脚本

用下面这个 build_ota.py 把新程序自动加版本号、算哈希、生成 version.json,再丢进 HTTP 服务器目录:

import hashlib, json, os, shutil, sys

SRC = "main.py"                       # 待发布的程序
OUT_DIR = "www/firmware"              # 静态服务器目录
HOST = "192.168.1.50"                # 你的服务器 IP
PORT = 8000

def bump(ver):
    a, b, c = map(int, ver.split("."))
    return f"{a}.{b}.{c+1}"

# 读当前最新版本
vf = os.path.join(OUT_DIR, "version.json")
cur = "0.0.0"
if os.path.exists(vf):
    cur = json.load(open(vf))["latest"]

new = bump(cur)
data = open(SRC, "rb").read()
name = f"main_{new}.py"
os.makedirs(OUT_DIR, exist_ok=True)
shutil.copy(SRC, os.path.join(OUT_DIR, name))

info = {
    "latest": new,
    "url": f"http://{HOST}:{PORT}/firmware/{name}",
    "size": len(data),
    "sha256": hashlib.sha256(data).hexdigest(),
}
json.dump(info, open(vf, "w"), indent=2)
print("已发布版本", new, "->", name)

发布流程:

# 1. 改好 main.py 后,运行打包脚本
python build_ota.py
# 2. 启动静态服务器(在 www/ 目录)
python -m http.server 8000 --directory www
# 3. 板子重启或触发检查,即自动拉取新版本

12.5 进阶:MQTT 一键远程升级(结合第 11 节)

在 MQTT 回调里收到 upgrade 指令就立即检查升级,手机发一条消息即可远程推送:

from umqtt.simple import MQTTClient
# ... 连接 MQTT(见 11.2)...

def sub_cb(topic, msg):
    if msg == b"upgrade":
        print("收到升级指令")
        check_and_update()     # 调用 12.3 的函数
    elif msg == b"on":
        led.on()
    elif msg == b"off":
        led.off()

c = MQTTClient("esp32s3_led_01", "broker.emqx.io")
c.set_callback(sub_cb)
c.connect()
c.subscribe(b"home/led/control")
while True:
    c.check_msg()
    time.sleep(0.1)

手机端用 MQTT Dashboard 向 home/led/control 发 upgrade,板子立刻拉取云端最新版并重启——真正意义上的远程一键升级。

下一步建议

祝你玩得开心,灯亮了就是第一步的胜利 💡