Marlin 固件配置嵌入(Configuration Embedding)完全指南:从二进制逆向还原构建配置
【免费下载链接】MarlinMarlin is a firmware for RepRap 3D printers optimized for both 8 and 32 bit microcontrollers. Marlin supports all common platforms. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.项目地址: https://gitcode.com/GitHub_Trending/ma/Marlin
导读
从 Marlin 2.0.9.3 开始,固件可以将生成它时所用的全部配置项以压缩形式嵌入到固件二进制中,从而让任何拿到固件的人都能够还原出原始构建配置。本文围绕官方文档 ConfigEmbedding.md 展开,结合仓库中的构建脚本 signature.py、应用脚本 mc-apply.py 以及 G-code 源码,完整讲解该功能的启用方式、构建期原理、固件内导出(M503 C)与配置还原的完整操作流程,并深入解释其底层实现与限制条件。读完本文,你将能够在自己的 Marlin 构建中启用配置嵌入,并能从任意已刷入的固件二进制中提取并还原其完整构建配置。
一、什么是 Configuration Embedding
CONFIGURATION_EMBEDDING(配置嵌入)是 Marlin 提供的一项构建期功能:在编译固件时,把当时生效的所有配置项(来自Marlin/Configuration.h与Marlin/Configuration_adv.h中所有已启用的#define)提取出来,连同构建信息一起序列化为 JSON,再压缩为 ZIP 归档,最后以 C 数组的形式编译进固件二进制。
它的典型应用场景是:
- 调试与复现:拿到一台运行着未知配置固件的打印机,无需猜测参数,直接还原构建配置;
- 配置备份:固件本身就是配置的载体,即使丢失本地工程文件,也能从二进制中找回;
- 固件分发:厂商或贡献者分发预编译固件时,用户可自行提取其中的配置进行比对与定制。
该功能由Marlin/Configuration_adv.h中的开关控制,注释中明确写道:"Marlin will embed all settings in the firmware binary as compressed data. Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'."(Configuration_adv.h)。
二、构建期原理:配置如何被"嵌入"二进制
配置嵌入的完整流水线由构建系统在编译开始时自动执行,核心逻辑位于 signature.py 的compute_build_signature函数(L89 起)。整个流程可以概括为五步:
- 提取配置宏:脚本调用预处理器运行配置头文件,收集所有已启用的
#define及其值(L141-L162); - 过滤与归类:剔除内部宏(如以
__开头、函数式宏、BOARD_*板卡宏等),只保留真正来自Configuration.h/Configuration_adv.h的选项,并按@section分组(L171-L193); - 写出 JSON:将活跃配置项序列化为
marlin_config.json写入构建目录(默认.pio/build/<env>/),并附带__INITIAL_HASH与VERSION构建信息(L457-L493); - ZIP 压缩:使用
ZIP_DEFLATED算法、压缩级别 9 将 JSON 打包为.pio/build/mc.zip(compress_file); - 转换为 C 数组:将
mc.zip逐字节转换为Marlin/src/mczip.h中的const unsigned char mc_zip[] PROGMEM数组,随固件一同编译(L506-L520)。
值得注意的工程细节:脚本会对Configuration.h与Configuration_adv.h分别计算 SHA-256 哈希并拼接为__INITIAL_HASH;如果构建目录中已有相同哈希的marlin_config.json,会直接复用(跳过重复计算),实现增量构建优化(L112-L127)。
在固件侧,mczip.h中的数组通过PROGMEM存放在 Flash(而非 RAM),运行时由pgm_read_byte逐字节读取,最大限度节约宝贵的 RAM 资源。
三、如何启用 CONFIGURATION_EMBEDDING
在Marlin/Configuration_adv.h中取消注释该宏即可启用:
/** * Enable this option if you have more than ~3K of unused flash space. * Marlin will embed all settings in the firmware binary as compressed data. * Use 'M503 C' to write the settings out to the SD Card as 'mc.zip'. * See docs/ConfigEmbedding.md for details on how to use 'mc-apply.py'. */ #define CONFIGURATION_EMBEDDING(Configuration_adv.h)
3.1 Flash 空间要求
官方注释明确提示:启用前应确保目标板有约 3KB 以上的空闲 Flash 空间。配置数据以 ZIP 压缩后体积通常较小,但仍会占用固件容量,Flash 紧张的板子需要评估后再开启。
3.2 自动降级条件(AVR 与无 SD 介质)
并非所有平台都能使用该功能。在 Conditionals-4-adv.h 中定义了自动降级逻辑:
// AVR are (usually) too limited in resources to store the configuration into the binary #if ENABLED(CONFIGURATION_EMBEDDING) && !defined(FORCE_CONFIG_EMBED) && (defined(__AVR__) || !HAS_MEDIA || ANY(SDCARD_READONLY, DISABLE_M503)) #undef CONFIGURATION_EMBEDDING #define CANNOT_EMBED_CONFIGURATION defined(__AVR__) #endif也就是说,满足以下任一条件时CONFIGURATION_EMBEDDING会被自动取消:
- 目标为AVR 架构(如 Arduino Mega 2560 等 8 位平台,Flash 通常不足);
- 固件没有 SD 介质支持(
HAS_MEDIA为假); - 启用了
SDCARD_READONLY(只读 SD 卡,无法写入mc.zip); - 启用了
DISABLE_M503(禁用M503命令,也就无法导出)。
此时编译会在 Warnings.cpp 中打印警告:"Disabled CONFIGURATION_EMBEDDING because the target usually has less flash storage." 若确实要在这些平台上强行启用,可以额外定义FORCE_CONFIG_EMBED覆盖自动降级。同样,build_example 在构建示例工程时通过NO_CONFIGURATION_EMBEDDING_WARNING抑制mczip.h自身打印的#warning。
3.3 能力自检:M115 报告
启用后,固件会通过M115(固件信息)命令向宿主机报告CONFIG_EXPORT能力标志,便于上位机软件自动发现该特性(M115.cpp)。
四、从固件二进制导出配置:M503 C
配置导出在固件运行时完成,需要一张未写保护的 SD 卡插入打印机。
- 通过串口向打印机发送:
M503 C- 固件会将嵌入的
mc.zip写入 SD 卡根目录,并回显Configuration saved as 'mc.zip'。
4.1 M503 C 的源码实现
M503命令本身用于打印当前内存中的设置(EEPROM 中的运行参数),其实现位于 M500-M504.cpp:
/** * M503: print settings currently in memory * * S<bool> : Include / exclude header comments in the output. (Default: S1) * * With CONFIGURATION_EMBEDDING: * C<flag> : Save the full Marlin configuration to SD Card as "mc.zip" */ void GcodeSuite::M503() { (void)settings.report(!parser.boolval('S', true)); #if ENABLED(CONFIGURATION_EMBEDDING) if (parser.seen_test('C')) { MediaFile file; // Need to create the config size on the SD card MediaFile root = card.getroot(); if (file.open(&root, "mc.zip", O_WRITE|O_CREAT)) { bool success = true; for (uint16_t i = 0; success && i < sizeof(mc_zip); ++i) { const uint8_t c = pgm_read_byte(&mc_zip[i]); success = (file.write(c) == 1); } success = file.close() && success; if (success) SERIAL_ECHO_MSG("Configuration saved as 'mc.zip'"); } } #endif }关键点:
S<bool>参数控制输出是否包含头注释(默认S1),不影响导出功能;C参数触发导出:固件从mc_zip[]数组逐字节读出嵌入数据(pgm_read_byte从 Flash 读取),写入 SD 卡根目录下的mc.zip;- 写入前会创建文件(
O_WRITE|O_CREAT),若 SD 卡写保护或空间不足,导出会静默失败; - 该导出分支仅在
CONFIGURATION_EMBEDDING启用时编译。
4.2 导出文件内容
mc.zip解开后是一个marlin_config.json文件,其结构包括:
| 字段 | 含义 |
|---|---|
| 各配置项键值对 | 构建时所有活跃的#define名称与值(来自两个配置文件) |
__INITIAL_HASH | Configuration.h与Configuration_adv.h的 SHA-256 前缀哈希拼接,用于校验配置是否与本地一致 |
VERSION.DETAILED_BUILD_VERSION | 构建时定义的详细版本字符串 |
VERSION.STRING_DISTRIBUTION_DATE | 发行日期字符串(fork 通常不会修改,可作为回溯线索) |
VERSION.GIT_REF | git describe得到的提交引用(若在 Git 仓库内构建则存在) |
其中GIT_REF通过git describe --match=NeVeRmAtCh --always获取(L487-L491),失败时该字段会被省略。
五、还原配置:mc-apply.py 的使用
将mc.zip从 SD 卡拷贝到电脑上,建议放在 Marlin 仓库根目录下(脚本默认查找marlin_config.json,放在仓库根目录最省事)。然后依次执行:
$ git checkout -f $ unzip mc.zip $ python3 buildroot/share/PlatformIO/scripts/mc-apply.py5.1 各命令的作用
git checkout -f:强制丢弃仓库中所有本地未提交修改,把Marlin/Configuration.h与Marlin/Configuration_adv.h恢复为仓库原始状态,确保后续应用配置时不会与残留修改冲突。执行前请确认没有需要保留的本地改动!unzip mc.zip:解压出marlin_config.json(默认被脚本读取的文件名);python3 buildroot/share/PlatformIO/scripts/mc-apply.py:解析 JSON,逐项更新两个配置文件,使其与原始构建配置一致。
脚本执行时会先打印配置版本信息,随后对每个配置项执行"启用 / 禁用 / 设值"三类操作,并在修改前为Marlin/Configuration.h与Marlin/Configuration_adv.h自动生成.bak备份(如Configuration.h.bak.h、.bak1.h…),方便回退(mc-apply.py)。
5.2 值规范化规则
脚本将 JSON 中的每个值归一化为三类动作(normalize_value):
| JSON 中的值 | 动作 | 效果 |
|---|---|---|
"on"、"true"、True、""(空字符串) | enable | 在配置文件中启用该#define(去除注释) |
"off"、"false"、False | disable | 注释掉该#define |
| 其他任意值 | set | 启用该宏并设置对应值 |
5.3 命令行参数
mc-apply.py支持三个参数:
usage: mc-apply.py [-h] [--opt] [--verbose] [config_file]config_file:配置文件路径,默认marlin_config.json;--opt:不直接修改配置文件,而是生成一个选项设置脚本Marlin/apply_config.sh,其中包含opt_enable/opt_disable/opt_set形式的命令序列,便于在支持选项脚本的构建流程中批量应用(write_opt_file);--verbose/-v:详细日志级别(0–2),级别 1 打印主要步骤,级别 2 打印每一项的启用/禁用/设值动作。
5.4 指令(directives)处理
JSON 中可选的__directives__字段会在应用配置前被先行处理(process_directives),支持:
[disable]:先把两个配置文件中的所有#define全部注释掉,从"干净状态"开始应用;examples/<路径>或example/<路径>:从 Marlin 示例配置仓库抓取指定示例配置;http://.../https://...:从任意 URL 获取配置。
这些指令主要服务于以config.ini驱动的构建流程(参见 signature.py 中ini_use_config的说明),普通还原场景通常用不到。
六、Git 引用与版本回溯
脚本输出时还会打印配置中记录的版本信息,帮助定位固件来源:
- 若固件由主仓库构建,
VERSION.GIT_REF会给出构建时的 Git 引用(commit/tag),可据此检出对应源码; - 作为兜底,
STRING_DISTRIBUTION_DATE(发行日期字符串)通常不会被 fork 修改,即使拿不到 Git 引用,也能大致判断固件版本时间线。
注意:这两项信息只在嵌入式 JSON 中记录,并不保证一定存在——例如在非 Git 环境构建时GIT_REF可能缺失;因此它只能作为参考线索,不能作为绝对依据。
七、与配置文件导出的关系:CONFIG_EXPORT
配置嵌入并非 Marlin 唯一的配置导出途径。signature.py 的说明显示,通过构建时定义CONFIG_EXPORT还可以导出其他格式:
| 导出方式 | 产物 | 用途 |
|---|---|---|
CONFIG_EXPORT 1/101 | marlin_config.json | 与嵌入相同的 JSON 配置(不打包进固件) |
CONFIG_EXPORT 2/102 | config.ini | 按配置节分组的 INI 文件,可被ini_use_config驱动的构建流程直接引用 |
CONFIG_EXPORT 3/13 | schema.json/schema_grouped.json | 配置项 schema |
CONFIG_EXPORT 4 | schema.yml | YAML 格式 schema |
CONFIG_EXPORT 5/105 | Config-export.h | 精简的"纯#define"头文件,可改名Config.h直接用于构建 |
其中CONFIGURATION_EMBEDDING本质上是"导出 JSON + 压缩 + 内嵌固件"的组合,且M115报告中显示的CONFIG_EXPORT能力标志正是由CONFIGURATION_EMBEDDING驱动(M115.cpp)。
八、适用前提与限制
- 平台限制:AVR(8 位)平台默认不支持,除非定义
FORCE_CONFIG_EMBED;无 SD 介质、SDCARD_READONLY或DISABLE_M503时也会被自动禁用(Conditionals-4-adv.h); - Flash 开销:需要约 3KB 以上空闲 Flash;
mczip.h编译时会打印#warning,可用NO_CONFIGURATION_EMBEDDING_WARNING抑制; - SD 卡要求:导出
mc.zip时必须插入未写保护的 SD 卡; - 还原前务必提交或备份本地修改:
git checkout -f会无条件丢弃工作区改动; - 配置是"构建时快照":嵌入的是编译时的配置宏集合,运行时通过 EEPROM 保存的可调参数(如 PID、Z offset 等)不在其内,请结合
M503的标准输出查看; - 不可逆的细微差异:还原后的配置文件与原始文件在注释、排版上不会完全一致,但启用的选项与值一一对应,可视为等价配置。
结语
Configuration Embedding 让 Marlin 固件从"黑盒"变成了"自带说明书"的可审计对象:构建期自动提取配置、压缩内嵌,运行期通过M503 C导出,宿主机再借助 mc-apply.py 一键还原。无论是排查别人的固件问题、迁移打印机配置,还是为预编译固件提供可复现性,这都是一套实用且完整的闭环方案。结合 signature.py 与 M500-M504.cpp 的源码,你可以进一步定制导出格式(如config.ini、Config-export.h),将其融入自己的构建与发布流程。
【免费下载链接】MarlinMarlin is a firmware for RepRap 3D printers optimized for both 8 and 32 bit microcontrollers. Marlin supports all common platforms. Many commercial 3D printers come with Marlin installed. Check with your vendor if you need source code for your specific machine.项目地址: https://gitcode.com/GitHub_Trending/ma/Marlin
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考