Tasmota 如何用 QEMU 在本地模拟 ESP32 固件启动并打开 WebUI
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
不需要 ESP32 硬件,也可以在本地把 Tasmota 固件跑起来并点开它的 WebUI。仓库的 tools/qemu/tasmota-qemu.sh 脚本完成了整条链路:用 PlatformIO 编译当前 Tasmota 检出中的 ESP32 固件、生成 QEMU 能识别的 flash 镜像、克隆并构建 Espressif 的 QEMU fork,最后引导固件——串口控制台始终可用;当使用支持 WiFi 的 fork 时,还能在浏览器里访问 WebUI。完整的操作说明在 tools/qemu/README.md。
前置条件
- amd64 宿主机:脚本面向 amd64 平台运行 QEMU(xtensa-softmmu)。
- 一份 Tasmota 源码检出:脚本编译的是当前检出(包括你的本地改动),不是重新克隆的副本。脚本以 platformio.ini 是否存在来判断检出有效。
- python3 和网络:脚本会在工作目录里创建虚拟环境并自行安装 PlatformIO;首次运行还要拉取编译工具链、克隆并构建 QEMU fork,比较慢,后续运行有缓存。
- QEMU 构建依赖(Debian/Ubuntu):
git build-essential ninja-build pkg-config python3-venv libglib2.0-dev libpixman-1-dev libgcrypt20-dev libslirp-dev flex bison。缺依赖时脚本会打印清单并提示用INSTALL_DEPS=1重跑——注意这会执行sudo apt-get update和系统级apt-get install,需要管理员权限,安装范围是上面列出的全部构建包。 - 两个有副作用的行为,运行前要知道:
NET_WIFI=1会把 WiFi 凭据写入tasmota/user_config_override.h的一个受管代码块中(用#undef/#define覆盖STA_SSID1、STA_PASS1等)。该块有明确的分隔标记且幂等——重复运行只重写这一块,文件其余部分保持不变。run步骤会自动终止针对同一工作目录先前启动的 QEMU 进程(只匹配命令行引用了本工作目录的qemu-system-xtensa),以释放端口转发。
构建产物、QEMU 构建和日志都放在仓库根目录下的tasmota-qemu/(gitignored),不会污染源码树。
最短路径:一条命令启动并打开 WebUI
从tools/qemu/目录执行(在仓库根目录执行时用tools/qemu/tasmota-qemu.sh前缀,效果相同,路径解析一致):
NET_WIFI=1 ./tasmota-qemu.sh # build -> image -> wifi QEMU -> boot, joins emulated AP # 固件加入模拟 AP 后,在宿主机浏览器访问: open http://localhost:8080不带参数时执行的是all命令,即按顺序执行build -> make-image -> qemu-setup -> run四步:
build:用 PlatformIO 编译$TASMOTA_ENV(NET_WIFI=1时自动切换为单核环境tasmota32solo1,定义见 platformio_tasmota_env32.ini);make-image:把build_output/firmware/<env>.factory.bin补零填充为 QEMU 接受的 flash 镜像(FLASH_SIZE仅支持2MB/4MB/8MB/16MB,默认4MB);qemu-setup:克隆并构建所选 QEMU fork(NET_WIFI=1时强制使用wifi变体);run:引导固件,串口输出始终可见,同时 tee 到tasmota-qemu/qemu.log。
run会打印 WebUI 地址(http://localhost:8080,即宿主机 8080 端口转发到 guest 的 80 端口)以及退出方式(Ctrl-A后按X)。
NET_WIFI=1 为什么能打开 WebUI
普通组合(默认tasmota32环境 + stock QEMU)下固件只能拿到串口控制台,WebUI 不会响应。要让网络可达,文档说明有三件事必须同时对齐,NET_WIFI=1一次性把它们全部设置好:
- 活过早期 IDF 初始化。完整双核的
tasmota32构建在 QEMU 下的esp_timer_impl_init中 panic(空中断向量);单核的tasmota32solo1构建不会,所以NET_WIFI=1选择它。 - 活过 WiFi 拉起。Tasmota 的 WiFi blobs 会访问 RF 寄存器(
0x6003xxxx),stock QEMU 不模拟这些寄存器(LoadStorePIFAddrError);wifi变体(Ebiroll/calib fork)模拟了它们,所以NET_WIFI=1强制QEMU_VARIANT=wifi。 - 拿到宿主机可达的 IP。该 fork 会广播一个名为
Open Wifi的开放 AP。NET_WIFI=1把 station 凭据写入tasmota/user_config_override.h的受管块,固件于是以 station 模式加入它、通过 slirp DHCP 获得地址(10.0.2.15),:8080 -> :80的端口转发才真正可达。
结果验证
- 串口控制台是第一个判据:
run启动后即可看到固件启动过程,全程记录在tasmota-qemu/qemu.log。 - Web 服务器起来之后(日志中出现
Web server active),NET_WIFI=1的后台监听器会自动代你下发SetOption128 1,解决下面一节提到的 referer 问题,UI 开箱即可点击。 - 此时在宿主机浏览器打开
http://localhost:8080,应能加载 Tasmota 控制台页面并点击进入子页面。如果 8080 不通,先确认 QEMU 仍在运行、日志中没有 panic。
只有/能加载、子页面报 ERR_EMPTY_RESPONSE
这是首次使用的预期现象,不是崩溃。原因:Tasmota 的 referer 检查只接受 host 为自身主机名或本地 IP(10.0.2.15)的Referer;经过宿主机端口转发时,浏览器发出的是Referer: http://localhost:8080/,host 是localhost,两者都不匹配,于是/之后的每个页面都被空响应丢弃。
NET_WIFI=1已自动处理。如果你是手动跑了一次普通run(例如全新镜像),需要手工执行:
./tasmota-qemu.sh fixui # sends SetOption128 1 (disable referer check)该设置会持久化到 flash。注意:重新make-image会擦除 flash,但下一次NET_WIFI=1运行会自动重新应用。
只想要串口控制台(无网络)
如果不需要 WebUI,只验证固件能否在模拟器中启动并观察串口输出,可以用 stock fork 的最快路径:
./tasmota-qemu.sh # stock fork, serial console only限制要清楚:stock fork 没有 WiFi 模拟(只有 OpenCores 以太网 MAC),Tasmota 不会拉起它认识的网络接口,所以 WebUI 和 MQTT 在网络侧都不可达。需要网络访问就用NET_WIFI=1。
启动 panic 时的排查
run以-no-reboot启动(guest 首次复位时 QEMU 直接退出而不是循环重启),并把串口 tee 到tasmota-qemu/qemu.log,panic 回溯因此不会滚走。符号化最后一条 panic:
./tasmota-qemu.sh decode # decodes tasmota-qemu/qemu.log ./tasmota-qemu.sh decode some-other.log # decodes a specific logdecode会去掉0xffffffff哨兵帧,取每个pc:sp对的 PC,再对匹配的firmware.elf跑addr2line。落在应用 flash(0x4018xxxx及以上)的帧最有价值;解析成??的地址在闭源 WiFi/BT blobs 里(无符号)——通常是固件触碰了模拟器不建模的射频硬件。
分不清问题是 QEMU 还是固件时,跑 smoketest:它用与 Tasmota 相同的 IDF/工具链构建一个最小 Arduino 应用并引导。最小应用能跑,说明 QEMU 本身没问题,问题在 Tasmota 的构建;同样 panic,则是 QEMU/IDF 基线起不来,应该修 QEMU 而不是 Tasmota。
常用环境变量
| 变量 | 默认值 | 含义 |
|---|---|---|
NET_WIFI | 0 | 1= 一次性网络配方(wifi fork +tasmota32solo1+ 模拟 AP 凭据) |
TASMOTA_ENV | tasmota32 | PlatformIO 环境(必须是 ESP32 环境) |
NET_WIFI_ENV | tasmota32solo1 | NET_WIFI=1使用的环境 |
QEMU_VARIANT | stock | stock(无 WiFi)或wifi(Ebiroll/calib fork) |
FLASH_SIZE | 4MB | 仅2MB/4MB/8MB/16MB |
WEB_PORT_HOST | 8080 | 转发到 guest:80的宿主机端口 |
INSTALL_DEPS | 0 | 1= 用 apt-get 安装 QEMU 构建依赖(需要 sudo) |
USE_SYSTEM_QEMU | 0 | 1= 直接使用 PATH 中的qemu-system-xtensa |
MINIMAL=1是可选分支:编译裁剪过的tasmota32safeboot环境,用于测试某个 Tasmota 驱动的初始化是否破坏 QEMU 启动,不是默认路径。
停止 QEMU 与已知限制
- 交互退出:
Ctrl-A然后X。 - 脚本之外也可以清理进程,但这会杀掉所有
qemu-system-xtensa进程(不限于本次运行),确认没有其他 QEMU 会话再用:for p in $(pgrep -f qemu-system-xtensa); do kill -9 $p; done。 - 已知限制:
wififork 比固件的 IDF 旧,其 RF 寄存器模型足以完成关联,但不是完整的 WiFi 栈;stock 构建下 WebUI 不响应,这是模拟器能力边界而非固件故障。
【免费下载链接】TasmotaAlternative firmware for ESP8266 and ESP32 based devices with easy configuration using webUI, OTA updates, automation using timers or rules, expandability and entirely local control over MQTT, HTTP, Serial or KNX. Full documentation at项目地址: https://gitcode.com/GitHub_Trending/ta/Tasmota
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考