ESP32-S3 MicroPython 全栈实战指南:刷机 · 点灯 · Wi-Fi 控制 · OTA 远程升级
从零到量产:MicroPython 开发环境、ESP32-S3 刷机、LED 控制、Wi-Fi 网页控制、OTA 无线烧录、MQTT 上云与一键远程升级全流程。
适用平台:Windows / macOS / Linux(命令以 WindowsCOMx为例,macOS/Linux 为/dev/tty*)
目录
- 硬件与软件准备
- 安装串口驱动
- 刷入 MicroPython 固件(刷机全流程)
- 开发工具:Thonny 与命令行
- 编写代码并烧录运行
- 实战:LED 控制
- 常见问题排查
- 常用命令速查表
- Wi-Fi 联网 + Web 控制 LED
- OTA 无线烧录
- MQTT 上云 + 手机 App 控灯
- 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,很多板子插上就能识别。若设备管理器出现黄色感叹号,安装对应驱动:
- CH340 / CH343:https://www.wch.cn/downloads/CH341SER_EXE.html
- CP210x(Silicon Labs):https://www.silabs.com/developers/usb-to-uart-bridge-vcp-drivers
安装后,插入开发板,在「设备管理器 → 端口(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/
- 选择 Generic ESP32-S3 的最新
.bin固件(如ESP32_S3-20240602-v1.23.0.bin)。 - 若你的板子带 8MB / 16MB 以上 Flash 且想用完整功能,选择带文件系统支持的版本即可。
将固件保存到便于访问的目录,例如 C:\esp\firmware.bin。
步骤 3.2 让开发板进入下载模式
ESP32-S3 进入烧录模式的方式:
- 按住
BOOT键不放; - 按一下
RESET/EN键; - 松开
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
参数说明:
--chip esp32s3:指定芯片型号--baud 460800:波特率,越高越快-z:压缩写入0x0:固件烧录的起始地址(S3 必须是 0x0)
看到 Hash of data verified / Leaving... 表示烧录完成。
步骤 3.5 验证
- 按下
RESET/EN键重启开发板; - 打开串口工具(Thonny / 串口助手),波特率
115200; - 应看到类似提示符:
```
MicroPython v1.23.0 on 2024-06-02; ESP32-S3 module with ESP32S3
Type "help()" for more information.
>>>
```
出现 >>> 即代表 MicroPython 运行成功!
4. 开发工具:Thonny 与命令行
4.1 Thonny(推荐新手)
- 打开 Thonny →
运行 → 选择解释器; - 解释器选 MicroPython (ESP32),端口选你的
COMx; - 下方 Shell 出现
>>>即可交互; - 写代码后在编辑器按
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Ω 电阻接地):
- LED 长脚(阳极)→ GPIO 21
- LED 短脚(阴极)→ 220Ω 电阻 → GND
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)
运行:
- 把上面的脚本保存为
main.py烧录到板子(参见第 5 节); - 板子复位后,串口会打印
IP: 192.168.x.x; - 电脑/手机连同一个 Wi-Fi,浏览器打开
http://192.168.x.x; - 点「打开 / 关闭」即可远程控制 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)。
电脑端使用:
- 浏览器打开官方客户端:https://micropython.org/webrepl/
- 地址填
ws://192.168.x.x:8266(用板子 IP),输入刚才设的密码,点 Connect; - 左下角 Send a file 选
main.py→ 无线上传,等价于有线烧录; - 也可在网页底部 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 端口)。要点:
- 设备三元组:
ProductKey/DeviceName/DeviceSecret; - 用
hmac+hashlib计算 password(securemode=3+signmethod=hmacsha256); - 连接地址
PK.iot-as-mqtt.cn-shanghai.aliyuncs.com:8883,需ssl上下文; - 主题形如
/sys/PK/DN/thing/service/property/set。
这块签名细节较多,建议直接用各云厂商的 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:版本元数据main_1.0.0.py/main_1.1.0.py...:每个版本一份,文件名带版本号
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,板子立刻拉取云端最新版并重启——真正意义上的远程一键升级。
下一步建议
- 状态可视:把 LED 状态 + 传感器数据定时
publish到云端,做手机仪表盘; - 灰度发布:云端
version.json按设备分组下发不同版本,先小批再全量; - 文件系统:
open('data.txt','w')读写本地数据,配合ujson存配置; - 进阶外设:
machine.I2C/machine.SPI驱动 OLED、传感器; - 工程化:用
mpremote+ 脚本实现一键烧录与 OTA 下发。
祝你玩得开心,灯亮了就是第一步的胜利 💡