- 物联网
- 嵌入式
【免费下载链接】nodemcu-firmware
Lua based interactive firmware for ESP8266, ESP8285 and ESP32
本指南面向首次接触 NodeMCU(基于 Lua 的 ESP8266 / ESP8285 / ESP32 交互式固件)的开发者,系统梳理从固件构建、烧录、代码上传到 Lua Flash Store(LFS)扩展的完整流程。读完你将掌握云端与本地构建固件的多种途径、跨平台烧录与上传工具链,以及把 Lua 代码移入 Flash 以释放 RAM 的进阶方案。
总体流程:三大步骤
NodeMCU 的上手过程由三个核心步骤组成,整个开发生命周期基本围绕它们展开:
- 构建固件:按需选择要包含的模块,编译出定制固件;
- 烧录固件:将固件写入芯片 Flash;
- 上传代码:把 Lua 源文件传送到设备并运行。
步骤 1 和 2 通常只执行一次;步骤 3 则会在你开发应用的过程中反复执行。当应用超出片上 RAM 容量时,可以使用 Lua Flash Store(LFS) 将 Lua 代码移入 Flash,从而为变量数据释放大量 RAM——因此如果你计划开发较大的应用,建议在步骤 1 构建固件时就启用 LFS。关于代码上传,LFS 也提供了与 SPIFFS 不同的方式(详见下文)。
提示:每个任务都有多种工具与操作系统组合可选。文中的彩色表格代表一条"被推荐的路径"——也就是最快通向成功的方案;需要更多细节时,跟随表格中的链接即可。
任务与操作系统选择矩阵
下表将三类任务(构建固件、烧录固件、上传代码)与 LFS 相关的衍生任务按操作系统(Windows / macOS / Linux)展开,蓝色标记(select)即推荐的入门路径:
| 任务 \ 操作系统 | Windows | macOS | Linux |
|---|---|---|---|
| 构建固件 | cloud builder(推荐) | cloud builder(推荐) | cloud builder(推荐) |
| Docker | Docker | Docker | |
| — | — | native(原生编译) | |
| 烧录固件 | NodeMCU PyFlasher(推荐) | NodeMCU PyFlasher(推荐) | — |
| esptool.py | esptool.py | esptool.py(推荐) | |
| 上传代码 | ESPlorer (Java)(推荐) | ESPlorer (Java) | ESPlorer (Java) |
| NodeMCU-Tool (Node.js) | NodeMCU-Tool (Node.js)(推荐) | NodeMCU-Tool (Node.js)(推荐) | |
| 构建启用 LFS 的固件 | cloud builder(推荐) | cloud builder(推荐) | cloud builder(推荐) |
| Docker | Docker | Docker | |
| — | — | native | |
| 构建 luac.cross | 使用 Terry 的 webservice 或 Docker 编译 LFS 镜像时无需本地构建 | 同左 | 同左 |
| native | native | native | |
| 从 release 下载 | — | — | |
| 将 Lua 编译为 LFS 镜像 | webservice(推荐) | webservice | webservice |
| Docker | Docker | Docker | |
| native | native | native | |
| 上传 LFS 镜像 | generic(推荐) | generic(推荐) | generic(推荐) |
如何阅读此表:以"刚接触 NodeMCU、使用 Windows、暂不使用 LFS"的场景为例,只需看 Windows 列的蓝色推荐项——在 cloud builder 上构建固件 → 用 NodeMCU PyFlasher 把固件写入设备 → 下载并运行依赖 Java 的 ESPlorer 将 Lua 文件上传到设备。
缺少合适的工具?本指南刻意将工具数量控制在最少,以便尽快带你进入 ESP8266 上的 Lua 编程;更多工具与 IDE 选型可以参考社区整理的资料(如 frightanic.com 的 Tools and IDEs 页面)。
以下各章并非需要逐节按顺序阅读,而是从上面矩阵中选取一个任务,再由表格链接跳转到对应章节。
Cloud Builder:云端构建固件
Cloud builder(nodemcu-build.com)允许你选择 NodeMCU 分支、所需模块以及少量其他配置项(例如是否启用 SSL)。构建完成后你会收到一封邮件,内含两个下载链接:
- 一个带浮点(floating point)支持的 NodeMCU 固件;
- 一个不带浮点支持的(即纯整数)固件。
官方推荐使用浮点构建版本。虽然整数版本存储变量时占用更少 RAM,但两者的运行时差异很小;浮点版本能正确处理非整数数值,可极大简化数值运算。Cloud builder 的 GUI 其余部分基本自解释,无需额外说明。
构建启用 LFS 的固件(For LFS)
- 展开 "LFS options" 面板;
- 选择一个 LFS 大小,64KB 通常已足够;
- 配置其余选项并开始构建。
注意:该服务并非 NodeMCU 团队官方维护,而是由团队某位成员以个人身份运营。
NodeMCU PyFlasher:图形化烧录工具
NodeMCU PyFlasher 是基于 Python、esptool.py 与 wxPython 的图形化烧录工具,提供 Windows 可执行 .exe 与 macOS 的 .dmg 安装包。Windows 与 macOS 上无需安装,其他平台的运行方式见其项目主页。
操作步骤:
- 安装 USB 转串口驱动——所需驱动取决于你使用的 ESP8266 模块或 USB 转串口芯片;
- 用 USB 线连接设备与电脑;
- 下载并启动 PyFlasher;
- 选择串口、浏览选择固件二进制文件,并设置烧录选项(flash options)。
注意:该工具同样并非官方 NodeMCU 出品,由 NodeMCU 团队某位成员以个人身份维护。
esptool.py:命令行烧录
esptool.py 最初是 ESP8266 社区项目,后由乐鑫(Espressif)接管,成为官方推荐的 ESP 系列芯片烧录方式。
操作步骤:
- 安装 USB 转串口驱动(同上,取决于模块型号);
- 安装 Python 2.7 或 Python >= 3.4(如果系统尚未安装);
- 用 USB 线连接设备与电脑;
- 安装 esptool:
$ pip install esptool # 同时会安装 pySerial 依赖- 烧录固件:
$ esptool.py --port <serial-port-of-ESP8266> --baud <baud-rate> write_flash -fm <flash-mode> 0x00000 <nodemcu-firmware>.binflash-mode 参数取值:大多数 ESP8266 ESP-01/07(512 KB 模块)使用qio;大多数 ESP32 与 ESP8266 ESP-12(>= 4 MB 模块)使用dio;ESP8285 必须使用dout。
波特率说明:默认波特率为 115200;多数硬件配置在 230400 下也可工作(取决于操作系统、驱动与模块);NodeMCU 与 WeMos 模块通常可稳定跑在 921600。
更多细节可查阅 esptool.py 的 GitHub 仓库文档。
ESPlorer:Java 图形化上传工具
ESPlorer 是一款基于 Java 的上传/交互工具,详情见其 GitHub 仓库。它是 Windows 用户上传代码的推荐选项(需已安装 Java 运行时)。
NodeMCU-Tool:Node.js 命令行工具
NodeMCU-Tool 依赖 Node.js,在代码上传与执行方面通常优于 ESPlorer,且维护非常活跃;当然我们也理解 Windows 用户普遍更偏好 GUI 而非命令行。
其核心能力包括:
- 从宿主机上传 Lua 文件到设备;
- 管理设备文件系统(删除、上传、下载等);
- 在 NodeMCU 上运行文件并通过 UART/串口显示输出。
快速上手:
- 安装 Node.js 与 NPM(如尚未安装);
- 全局安装 NodeMCU-Tool:
$ npm install nodemcu-tool -g- 验证安装:
$ nodemcu-tool --version- 上传 Lua 文件:
$ nodemcu-tool upload --port=/dev/ttyUSB0 helloworld.lua- 运行它:
$ nodemcu-tool run helloworld.lua注意:第 2 步安装工具时可能需要sudo前缀,必要时可追加--unsafe-perm标志。
Docker:本地构建与 LFS 编译
Docker NodeMCU build image 是在本地构建 NodeMCU 相关组件最便捷的方式,支持:
- 基于本地克隆源码与配置构建 NodeMCU 固件;
- 在本地将 Lua 文件交叉编译为 LFS 镜像。
详细说明见镜像的 README;可用的配置选项参见构建文档以及app/include/user_config.h中的注释。
构建启用 LFS 的固件(For LFS)
- 编辑
app/include/user_config.h,将#define LUA_FLASH_STORE 0x0这一行改为所需大小——注意该值必须是 4KB 的整数倍; - 其余按该镜像的常规流程构建。
从源码看,app/include/user_config.h默认定义#define LUA_FLASH_STORE 0x0,且注释明确指出"LFS 允许将 Lua 代码存入 Flash,Lua 虚拟机直接从 Flash 执行代码而无需 RAM 开销;可直接在系统分区表中配置 LFS,为保持向后兼容,LUA_FLASH_STORE 在未使用 NodeMCU 分区工具时定义默认分区大小"。分区编号在 app/include/user_config.h 中定义(NODEMCU_LFS0_PARTITION = 3、NODEMCU_LFS1_PARTITION = 4),固件启动时 app/user/user_main.c 会据此填充分区表。
注意:该 Docker 镜像并非官方 NodeMCU 出品,由团队某位成员以个人身份维护。
构建 luac.cross 交叉编译器
只有当你想自己把 Lua 文件编译为 LFS 镜像、且不使用 Docker 时,才需要本地编译一份luac.cross。构建产物位于仓库的app/lua/luac_cross/目录(含 Makefile 与 Windows 专用的 mingw32-Makefile.mak,以及 luac.c、lflashimg.c 等源码)。
注意:请务必获取与你固件配置匹配的 luac.cross——即浮点/整数、Lua 5.1/5.3、以及可能的 release 版本都要一致。
Windows
Windows 用户可在开发机上编译一份本地luac.cross可执行文件,需要:
- 下载当前 NodeMCU 源码(dev ZIP 或 release ZIP)并解压到本地目录(如
C:\nodemcu-firmware),选择与目标固件版本匹配的 master/dev 版本;若需要整数构建,编辑app/includes/user_config.h进行选择; - 选择偏好的工具链:
- Windows 10 + WSL:这是 Linux 环境,直接按下面的 Linux 构建说明操作;
- Cygwin 或 MinGW:对资源占用更小,适用于所有 Windows 版本,两者都是 GNU 编译器套件的 Windows 移植,可编译出原生 Windows 可执行文件。Cygwin 安装时在安装菜单勾选 Cygwin core +gcc-core+gnu make;MinGW 只需基础 C 构建环境(core GCC 与 mingw32-make)。二者都会创建包含相关 GCC 工具链路径的Cmd提示符。切换到 NodeMCU 固件根目录下的
app/lua/luac_cross后构建:Cygwin 中运行make,MinGW 中运行mingw32-make -f mingw32-Makefile.mak; - MS Visual Studio(免费社区版即可):打开仓库自带的 msvc/hosttools.sln 并构建,即可得到 Lua 5.1 的
luac.cross.exe。目前尚无 Lua 5.3 版本的 sln 工程文件。
- 拿到构建好的
luac.cross可执行文件后,即可用它把 Lua 代码编译为 LFS 镜像。建议将其移出 nodemcu-firmware 目录树,因为该目录此后不再需要。
Linux
- 确保已安装 "build essential" GCC 工具链;
- 下载当前 NodeMCU 源码(dev/release ZIP)并解压到本地目录,选择匹配的 master/dev 版本;需要整数构建时编辑
app/includes/user_config.h; - 切换到
app/lua/luac_cross子目录; - 运行
make构建可执行文件; - 构建完成后即可用于编译 LFS 镜像;同样可将其移出 nodemcu-firmware 目录树。
macOS
同 Linux 的流程。
将 Lua 编译为 LFS 镜像
选择放入 LFS 的 Lua 文件
最简单的做法:把项目所有 Lua 文件集中维护在宿主机上的单一目录中(这些文件随后由luac.cross编译成 LFS 镜像)。
例如,想从 LFS 运行 Telnet 与 FTP 服务器,可在项目目录中放入以下文件:
- lua_examples/lfs/_init.lua:LFS 辅助例程与函数(应始终包含);
- lua_examples/lfs/dummy_strings.lua:把常用字符串移入 LFS(应始终包含);
- lua_examples/lfs/lfs_fragments.lua:LFS 片段示例;
- 你自己项目中的 Telnet / FTP 等模块(通常替换掉示例文件)。
前两个文件务必保留,其余示例文件按需替换为你自己的项目文件——这些只是示例,你完全可以按应用需要修改或替换它们。
来看_init.lua的实际作用:lua_examples/lfs/_init.lua 是 SPIFFS 中init.lua的 LFS 对应物。在 Lua 5.1 下,它向全局环境_G添加一个LFS表,通过__index元方法解析 LFS 中的函数(如LFS.fred(params)直接执行模块fred的主函数),并实现只读属性LFS._time(luac.cross 执行时的 Unix 时间戳,可作为版本标识)、LFS._config(返回 lfs_base/lfs_mapped/lfs_size/fs_mapped/fs_size 等配置表)、LFS._list(LFS 模块列表);同时禁止对 LFS 的写入(__newindex抛出 "LFS is readonly" 错误)并禁用 Lua 5.0 风格的module以节省 RAM。在 Lua 5.3 下,LFS 表由 C 层实现直接填充,故跳过这段 Lua 代码。随后它把 LFS 接入package.loaders[3],使require "myModule"可直接解析 LFS 中的模块,并用优先 SPIFFS、回退 LFS 的方式替换了loadfile/dofile。
Terry 的 LFS Lua 交叉编译 Web 服务
先阅读选择 LFS 文件一节。
将包含全部 Lua 文件的ZIP 包上传到该 webservice,它会交叉编译出一个.img镜像,供上传到设备使用;同时支持浮点与整数两种固件变体的 LFS 镜像。更多细节见服务站点说明。
注意:该服务并非 NodeMCU 团队官方维护,由团队某位成员以个人身份运营。
使用 Docker 编译 LFS 镜像
先阅读选择 LFS 文件一节。
构建固件所用的同一 Docker 镜像也可以用来把 Lua 文件编译为 LFS 镜像:
$ cd <your-nodemcu-firmware-folder> $ docker run --rm -ti -v `pwd`:/opt/nodemcu-firmware -v {PathToLuaSourceFolder}:/opt/lua marcelstoer/nodemcu-build lfs-image在宿主机原生编译(Native on OS)
如果是在 Windows 上用 WSL / Cygwin 构建的 luac.cross,则应在对应命令行窗口(均使用bashshell)中操作;如果用的是 Visual Studio,则在 Windows cmd 窗口操作。
$ cd <project-dir> $ luac.cross -o lfs.img -f *.lua需要按实际情况调整img与lua路径,并确保luac.cross在$PATH搜索列表中。例如使用 WSL 且项目文件位于D:\myproject时,Lua 路径应写为/mnt/d/myproject/*.lua(Cygwin 下将mnt换成cygwin)。若无 Lua 编译错误,将生成lfs.img文件(必要时指定显式目录路径)。
也可以在你的~/bin目录放一个一行脚本,把这套命令封装起来方便复用。仓库的 tools/Makefile 展示了自动化做法:LFSimage目标会执行$(LUAC_CROSS) -f -o $(FSSOURCE)/LFS.img $(LFSSOURCES),用通配符收集的 Lua 源文件生成 LFS 镜像。
上传 LFS 镜像
编译好的 LFS 镜像文件(如lfs.img)像普通文件一样上传到设备文件系统(SPIFFS),上传方式与 Lua 文件完全相同(用 ESPlorer 或 NodeMCU-Tool)。仓库还提供了一个新示例 lua_examples/lfs/HTTP_OTA.lua——它从标准 Web 服务下载镜像:通过net.dns.resolve(host, doRequest)解析域名后发起 HTTP 请求,按content-length分块接收并写入文件,最终校验文件大小后调用node.flashreload(image)完成重载(示例中为无加密无签名的简单实现,但加载器会校验镜像文件是否为有效完整的 LFS 镜像)。
镜像上传到 SPIFFS 后,执行node.flashreload()命令,加载器会将其载入 Flash 并立即重启 ESP 模块以加载新 LFS——前提是镜像文件有效。如果文件无效,该调用会返回错误,因此重载代码应包含错误返回的处理逻辑。
从源码看,LFS 相关的 Lua API 集中在 app/modules/node.c:node.LFS.reload()实际调用luaL_lfsreload()(node.flashreload是其即将废弃的别名,会打印 deprecation 提示),node.LFS.get()/node.flashindex()调用luaL_pushlfsmodule()返回 LFS 函数引用,node.LFS.list()调用luaL_pushlfsmodules()返回模块列表。完整的 API 语义见 docs/modules/node.md。
编辑你的init.lua
init.lua是 NodeMCU 固件最先执行的文件,通常负责配置 WiFi 连接并启动主 Lua 应用。假设你已把上述_init文件编入 LFS,那么执行它会为 LFS 模块访问提供一套简洁 API:
- 可直接执行单个函数,例如
LFS.myfunc(a,b); - LFS 已进入 require 路径,因此
require 'myModule'可直接工作。
建议对_init代码做保护调用:pcall(node.LFS._init())并检查错误状态。更详细的描述参见 LFS 白皮书中的 Programming Techniques and Approaches。
最小化 LFS 示例
以下是最简 LFS 系统的构建与运行全流程:
- 使用启用 LFS 的固件:参考矩阵中 "Build LFS enabled firmware" 一节,把固件以常规方式烧录到 ESP8266;
- 构建 LFS 文件系统:用上述任一方式(最简单的是宿主机上执行
luac.cross -o lfs.img -f *.lua)生成镜像。务必包含一个内容为print("Hello ESP8266 world!")的hello_world.lua文件,且应把 lua_examples/lfs/_init.lua 编入镜像——它是集成 LFS 系统最便捷的方式。随后把lfs.img像普通文件一样下载到 ESP8266; - 让 ESP8266 认识 LFS:通过
node.LFS.reload("lfs.img")触发一次(或两次)复位,再调用node.LFS._init()更好地集成 LFS。登录设备后运行以下命令即可看到完整流程:
> > node.LFS.reload("lfs.img") -- node.LFS.reload() 会在这里触发一次或两次复位。 -- 调用 LFS 中的 hello_world。 > node.LFS.hello_world() Hello ESP8266 world! -- 完成! -- 再看一些更深入的洞察与辅助功能 -- 列出 LFS 中的模块。 > print(node.LFS.list) function: 3fff0728 > for k,v in pairs(node.LFS.list()) do print(k,v) end 1 dummy_strings 2 _init 3 hello_world -- 把 LFS 与 SPIFFS 集成 > node.LFS._init() -- 现在可以用 dofile 和 loadfile 从 SPIFFS 或 LFS 运行、加载文件。 > dofile("hello_world.lua") Hello ESP8266 world! -- require() 现在也以同样方式工作。 -- 如果 SPIFFS 里恰好有名为 "hello_world.lua" 的文件,将执行 SPIFFS 版本; -- 否则会回退查找 LFS。 -- _init.lua 还会设置一个全局 LFS 作为 node.LFS 的副本(向后兼容,未来可能移除)。 > print(LFS) table: 3fff06e0 >以上命令未使用任何错误校正,因为它们面向终端交互输入,错误会直观显现。
- 设置开机自启:配置 ESP8266 启动流程检查 LFS 镜像是否存在并运行所需模块。一旦通过运行 lua_examples/lfs/_init.lua 注册了 LFS 模块表,运行某个 LFS 模块就只是一行调用的事,例如
LFS.hello_world()。
要点总结:
node.LFS.reload()仅在 LFS 镜像更新时需重新执行;加载完成后 SPIFFS 中的原始文件不再被使用,可以删除;- 待 LFS 验证工作正常后,可再添加 lua_examples/lfs/dummy_strings.lua 这类模块,并辅以有效的错误检查。
关于dummy_strings.lua的原理:lua_examples/lfs/dummy_strings.lua 利用luac.cross -f生成的 ROM 字符串表——该表包含所有已加载模块引用的字符串。通过在 LFS 中放入一个引用目标字符串的"哑模块",即可把这些字符串预载入 ROM 表,应用使用这些字符串时不产生任何 RAM 或 Lua 垃圾回收(LGC)开销;该模块本身无需调用,编入镜像即生效。它预载的典型字符串包括"?.lc;?.lua"、"/\n;\n?\n!\n-"、"@init.lua"、"_G"、"require"、"node.task"等(开发调试时可用debug.getstrings'RAM'列出当前 RAM 字符串表,进而决定哪些字符串应加入 LFS)。
至此,你已经掌握了一条完整的 NodeMCU 开发链路:按需构建固件 → 选择顺手的工具烧录 → 上传并运行 Lua 代码;当应用规模变大、RAM 吃紧时,即可平滑切换到 LFS 方案,把代码固化到 Flash 中,为变量数据腾出宝贵的片上内存。
- 物联网
- 嵌入式
【免费下载链接】nodemcu-firmware
Lua based interactive firmware for ESP8266, ESP8285 and ESP32
相关推荐
如何使用NodeMCU Flasher:快速上手ESP8266固件烧录工具
如何使用NodeMCU Flasher:快速上手ESP8266固件烧录工具 NodeMCU Flasher是一款专为NodeMCU开发板和ESP8266芯片设计
桌面应用开发工具嵌入式物联网NodeMCU PyFlasher终极指南:三分钟快速上手的ESP8266固件烧录神器
NodeMCU PyFlasher终极指南:三分钟快速上手的ESP8266固件烧录神器 还在为ESP8266开发板的固件烧录而头疼吗?面对复杂的命令行参数和繁琐
物联网开发工具桌面应用嵌入式终极NodeMCU固件上传与配置指南:从入门到精通的完整教程
终极NodeMCU固件上传与配置指南:从入门到精通的完整教程 NodeMCU固件是一款基于Lua的交互式固件,专为ESP8266、ESP8285和ESP32等物
物联网嵌入式
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考